From af563bb1c6aba8432ab88f7afd872055a5b47bce Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 15 Mar 2022 12:47:10 +0100 Subject: [PATCH] lnd: replicate btcwallet's key derivation exactly There's this special case in lnd's wallet (btcwallet) where the coin type and account keys are always serialized as a string and encrypted, which actually fixes the key padding issue that makes the difference between DeriveNonStandard and Derive. To replicate lnd's behavior exactly, we need to serialize and de-serialize the extended key at the coin type and account level (depth = 2 or depth = 3). This does not apply to the default account (id = 0) because that is always derived directly. --- lnd/hdkeychain.go | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lnd/hdkeychain.go b/lnd/hdkeychain.go index 330ff29..0ba7c75 100644 --- a/lnd/hdkeychain.go +++ b/lnd/hdkeychain.go @@ -27,15 +27,34 @@ const ( func DeriveChildren(key *hdkeychain.ExtendedKey, path []uint32) ( *hdkeychain.ExtendedKey, error) { - var ( - currentKey = key - err error - ) + var currentKey = key for _, pathPart := range path { - currentKey, err = currentKey.DeriveNonStandard(pathPart) + derivedKey, err := currentKey.DeriveNonStandard(pathPart) if err != nil { return nil, err } + + // There's this special case in lnd's wallet (btcwallet) where + // the coin type and account keys are always serialized as a + // string and encrypted, which actually fixes the key padding + // issue that makes the difference between DeriveNonStandard and + // Derive. To replicate lnd's behavior exactly, we need to + // serialize and de-serialize the extended key at the coin type + // and account level (depth = 2 or depth = 3). This does not + // apply to the default account (id = 0) because that is always + // derived directly. + depth := derivedKey.Depth() + keyID := pathPart - hdkeychain.HardenedKeyStart + if (depth == 3 && keyID != 0) || depth == 2 { + currentKey, err = hdkeychain.NewKeyFromString( + derivedKey.String(), + ) + if err != nil { + return nil, err + } + } else { + currentKey = derivedKey + } } return currentKey, nil }