mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-24 14:50:40 +01:00
Find and replace all nolint instances refering to the `lll` linter and replace with `ll` which is the name of our custom version of the `lll` linter which can be used to ignore log lines during linting. The next commit will do the configuration of the custom linter and disable the default one.
631 lines
16 KiB
Go
631 lines
16 KiB
Go
package btcwallet
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/btcutil/psbt"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
netParams = &chaincfg.RegressionNetParams
|
|
testValue int64 = 345678
|
|
testCSVTimeout uint32 = 2016
|
|
|
|
testCommitSecretBytes, _ = hex.DecodeString(
|
|
"9f1f0db609718cf70c580aec6a0e570c3f086ec85a2a6119295b1d64240d" +
|
|
"aca5",
|
|
)
|
|
testCommitSecret, testCommitPoint = btcec.PrivKeyFromBytes(
|
|
testCommitSecretBytes,
|
|
)
|
|
|
|
remoteRevocationBasePubKeyBytes, _ = hex.DecodeString(
|
|
"02baf067bfd1a6cf7229c7c459b106d384ad33e948ea1d561f2034475ff1" +
|
|
"7359fb",
|
|
)
|
|
remoteRevocationBasePubKey, _ = btcec.ParsePubKey(
|
|
remoteRevocationBasePubKeyBytes,
|
|
)
|
|
|
|
testTweakSingle, _ = hex.DecodeString(
|
|
"020143a30cf6b71ca2af01efbd1758a04b4c7f5c2299f2ea63a8a6b58107" +
|
|
"63b1ed",
|
|
)
|
|
)
|
|
|
|
// testInputType is a type that represents different types of inputs that are
|
|
// signed within a PSBT.
|
|
type testInputType uint8
|
|
|
|
const (
|
|
plainP2WKH testInputType = 0
|
|
tweakedP2WKH testInputType = 1
|
|
nestedP2WKH testInputType = 2
|
|
singleKeyP2WSH testInputType = 3
|
|
singleKeyDoubleTweakedP2WSH testInputType = 4
|
|
)
|
|
|
|
func (i testInputType) keyPath() []uint32 {
|
|
switch i {
|
|
case nestedP2WKH:
|
|
return []uint32{
|
|
hardenedKey(waddrmgr.KeyScopeBIP0049Plus.Purpose),
|
|
hardenedKey(0),
|
|
hardenedKey(0),
|
|
0, 0,
|
|
}
|
|
|
|
case singleKeyP2WSH:
|
|
return []uint32{
|
|
hardenedKey(keychain.BIP0043Purpose),
|
|
hardenedKey(netParams.HDCoinType),
|
|
hardenedKey(uint32(keychain.KeyFamilyPaymentBase)),
|
|
0, 7,
|
|
}
|
|
|
|
case singleKeyDoubleTweakedP2WSH:
|
|
return []uint32{
|
|
hardenedKey(keychain.BIP0043Purpose),
|
|
hardenedKey(netParams.HDCoinType),
|
|
hardenedKey(uint32(keychain.KeyFamilyDelayBase)),
|
|
0, 9,
|
|
}
|
|
|
|
default:
|
|
return []uint32{
|
|
hardenedKey(waddrmgr.KeyScopeBIP0084.Purpose),
|
|
hardenedKey(0),
|
|
hardenedKey(0),
|
|
0, 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i testInputType) output(t *testing.T,
|
|
privKey *btcec.PrivateKey) (*wire.TxOut, []byte) {
|
|
|
|
var (
|
|
addr btcutil.Address
|
|
witnessScript []byte
|
|
err error
|
|
)
|
|
switch i {
|
|
case plainP2WKH:
|
|
h := btcutil.Hash160(privKey.PubKey().SerializeCompressed())
|
|
addr, err = btcutil.NewAddressWitnessPubKeyHash(h, netParams)
|
|
require.NoError(t, err)
|
|
|
|
case tweakedP2WKH:
|
|
privKey = input.TweakPrivKey(privKey, testTweakSingle)
|
|
|
|
h := btcutil.Hash160(privKey.PubKey().SerializeCompressed())
|
|
addr, err = btcutil.NewAddressWitnessPubKeyHash(h, netParams)
|
|
require.NoError(t, err)
|
|
|
|
case nestedP2WKH:
|
|
h := btcutil.Hash160(privKey.PubKey().SerializeCompressed())
|
|
witnessAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
|
h, netParams,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
witnessProgram, err := txscript.PayToAddrScript(witnessAddr)
|
|
require.NoError(t, err)
|
|
|
|
addr, err = btcutil.NewAddressScriptHash(
|
|
witnessProgram, netParams,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
case singleKeyP2WSH:
|
|
// We're simulating a delay-to-self script which we're going to
|
|
// spend through the time lock path. We don't actually need to
|
|
// know the private key of the remote revocation base key.
|
|
revokeKey := input.DeriveRevocationPubkey(
|
|
remoteRevocationBasePubKey, testCommitPoint,
|
|
)
|
|
witnessScript, err = input.CommitScriptToSelf(
|
|
testCSVTimeout, privKey.PubKey(), revokeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
h := sha256.Sum256(witnessScript)
|
|
addr, err = btcutil.NewAddressWitnessScriptHash(h[:], netParams)
|
|
require.NoError(t, err)
|
|
|
|
case singleKeyDoubleTweakedP2WSH:
|
|
// We're simulating breaching a remote party's delay-to-self
|
|
// output which we're going to spend through the revocation
|
|
// path. In that case the self key is the other party's self key
|
|
// and, we only know the revocation base private key and commit
|
|
// secret.
|
|
revokeKey := input.DeriveRevocationPubkey(
|
|
privKey.PubKey(), testCommitPoint,
|
|
)
|
|
witnessScript, err = input.CommitScriptToSelf(
|
|
testCSVTimeout, remoteRevocationBasePubKey, revokeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
h := sha256.Sum256(witnessScript)
|
|
addr, err = btcutil.NewAddressWitnessScriptHash(h[:], netParams)
|
|
require.NoError(t, err)
|
|
|
|
default:
|
|
t.Fatalf("invalid input type")
|
|
}
|
|
|
|
pkScript, err := txscript.PayToAddrScript(addr)
|
|
require.NoError(t, err)
|
|
return &wire.TxOut{
|
|
Value: testValue,
|
|
PkScript: pkScript,
|
|
}, witnessScript
|
|
}
|
|
|
|
func (i testInputType) decorateInput(t *testing.T, privKey *btcec.PrivateKey,
|
|
in *psbt.PInput) {
|
|
|
|
switch i {
|
|
case tweakedP2WKH:
|
|
in.Unknowns = []*psbt.Unknown{{
|
|
Key: PsbtKeyTypeInputSignatureTweakSingle,
|
|
Value: testTweakSingle,
|
|
}}
|
|
|
|
case nestedP2WKH:
|
|
h := btcutil.Hash160(privKey.PubKey().SerializeCompressed())
|
|
witnessAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
|
h, netParams,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
witnessProgram, err := txscript.PayToAddrScript(witnessAddr)
|
|
require.NoError(t, err)
|
|
in.RedeemScript = witnessProgram
|
|
|
|
case singleKeyDoubleTweakedP2WSH:
|
|
in.Unknowns = []*psbt.Unknown{{
|
|
Key: PsbtKeyTypeInputSignatureTweakDouble,
|
|
Value: testCommitSecret.Serialize(),
|
|
}}
|
|
}
|
|
}
|
|
|
|
func (i testInputType) finalize(t *testing.T, packet *psbt.Packet) {
|
|
in := &packet.Inputs[0]
|
|
sigBytes := in.PartialSigs[0].Signature
|
|
|
|
var witnessStack wire.TxWitness
|
|
switch i {
|
|
case singleKeyP2WSH:
|
|
witnessStack = make([][]byte, 3)
|
|
witnessStack[0] = sigBytes
|
|
witnessStack[1] = nil
|
|
witnessStack[2] = in.WitnessScript
|
|
|
|
case singleKeyDoubleTweakedP2WSH:
|
|
// Place a 1 as the first item in the evaluated witness stack to
|
|
// force script execution to the revocation clause.
|
|
witnessStack = make([][]byte, 3)
|
|
witnessStack[0] = sigBytes
|
|
witnessStack[1] = []byte{1}
|
|
witnessStack[2] = in.WitnessScript
|
|
|
|
default:
|
|
// The PSBT finalizer knows what to do if we're not using a
|
|
// custom script.
|
|
err := psbt.MaybeFinalizeAll(packet)
|
|
require.NoError(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
var err error
|
|
in.FinalScriptWitness, err = serializeTxWitness(witnessStack)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// serializeTxWitness return the wire witness stack into raw bytes.
|
|
func serializeTxWitness(txWitness wire.TxWitness) ([]byte, error) {
|
|
var witnessBytes bytes.Buffer
|
|
err := psbt.WriteTxWitness(&witnessBytes, txWitness)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error serializing witness: %w", err)
|
|
}
|
|
|
|
return witnessBytes.Bytes(), nil
|
|
}
|
|
|
|
// TestSignPsbt tests the PSBT signing functionality.
|
|
func TestSignPsbt(t *testing.T) {
|
|
w, _ := newTestWallet(t, netParams, seedBytes)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
inputType testInputType
|
|
}{{
|
|
name: "plain P2WKH",
|
|
inputType: plainP2WKH,
|
|
}, {
|
|
name: "tweaked P2WKH",
|
|
inputType: tweakedP2WKH,
|
|
}, {
|
|
name: "nested P2WKH",
|
|
inputType: nestedP2WKH,
|
|
}, {
|
|
name: "single key P2WSH",
|
|
inputType: singleKeyP2WSH,
|
|
}, {
|
|
name: "single key double tweaked P2WSH",
|
|
inputType: singleKeyDoubleTweakedP2WSH,
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
// This is the private key we're going to sign with.
|
|
privKey, err := w.deriveKeyByBIP32Path(tc.inputType.keyPath())
|
|
require.NoError(t, err)
|
|
|
|
txOut, witnessScript := tc.inputType.output(t, privKey)
|
|
|
|
// Create the reference transaction that has the input that is
|
|
// going to be spent by our PSBT.
|
|
refTx := wire.NewMsgTx(2)
|
|
refTx.AddTxIn(&wire.TxIn{})
|
|
refTx.AddTxOut(txOut)
|
|
|
|
// Create the unsigned spend transaction that is going to be the
|
|
// main content of our PSBT.
|
|
spendTx := wire.NewMsgTx(2)
|
|
spendTx.LockTime = testCSVTimeout
|
|
spendTx.AddTxIn(&wire.TxIn{
|
|
PreviousOutPoint: wire.OutPoint{
|
|
Hash: refTx.TxHash(),
|
|
Index: 0,
|
|
},
|
|
Sequence: testCSVTimeout,
|
|
})
|
|
spendTx.AddTxOut(txOut)
|
|
|
|
// Convert it to a PSBT now and add all required signing
|
|
// metadata to it.
|
|
packet, err := psbt.NewFromUnsignedTx(spendTx)
|
|
require.NoError(t, err)
|
|
packet.Inputs[0].WitnessScript = witnessScript
|
|
packet.Inputs[0].SighashType = txscript.SigHashAll
|
|
packet.Inputs[0].WitnessUtxo = refTx.TxOut[0]
|
|
packet.Inputs[0].Bip32Derivation = []*psbt.Bip32Derivation{{
|
|
PubKey: privKey.PubKey().SerializeCompressed(),
|
|
Bip32Path: tc.inputType.keyPath(),
|
|
}}
|
|
tc.inputType.decorateInput(t, privKey, &packet.Inputs[0])
|
|
|
|
// Let the wallet do its job. We expect to be the only signer
|
|
// for this PSBT, so we'll be able to finalize it later.
|
|
signedInputs, err := w.SignPsbt(packet)
|
|
require.NoError(t, err)
|
|
|
|
// We expect one signed input at index 0.
|
|
require.Len(t, signedInputs, 1)
|
|
require.EqualValues(t, 0, signedInputs[0])
|
|
|
|
// If the witness stack needs to be assembled, give the caller
|
|
// the option to do that now.
|
|
tc.inputType.finalize(t, packet)
|
|
|
|
finalTx, err := psbt.Extract(packet)
|
|
require.NoError(t, err)
|
|
|
|
vm, err := txscript.NewEngine(
|
|
refTx.TxOut[0].PkScript, finalTx, 0,
|
|
txscript.StandardVerifyFlags, nil, nil,
|
|
refTx.TxOut[0].Value,
|
|
txscript.NewCannedPrevOutputFetcher(
|
|
refTx.TxOut[0].PkScript, refTx.TxOut[0].Value,
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
require.NoError(t, vm.Execute())
|
|
}
|
|
}
|
|
|
|
// TestEstimateInputWeight tests that we correctly estimate the weight of a
|
|
// PSBT input if it supplies all required information.
|
|
func TestEstimateInputWeight(t *testing.T) {
|
|
genScript := func(f func([]byte) ([]byte, error)) []byte {
|
|
pkScript, _ := f([]byte{})
|
|
return pkScript
|
|
}
|
|
|
|
var (
|
|
witnessScaleFactor = blockchain.WitnessScaleFactor
|
|
p2trScript, _ = txscript.PayToTaprootScript(
|
|
&input.TaprootNUMSKey,
|
|
)
|
|
dummyLeaf = txscript.TapLeaf{
|
|
LeafVersion: txscript.BaseLeafVersion,
|
|
Script: []byte("some bitcoin script"),
|
|
}
|
|
dummyLeafHash = dummyLeaf.TapHash()
|
|
)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
in *psbt.PInput
|
|
|
|
expectedErr error
|
|
expectedErrString string
|
|
|
|
// expectedWitnessWeight is the expected weight of the content
|
|
// of the witness of the input (without the base input size that
|
|
// is constant for all types of inputs).
|
|
expectedWitnessWeight int
|
|
}{{
|
|
name: "empty input",
|
|
in: &psbt.PInput{},
|
|
expectedErr: ErrInputMissingUTXOInfo,
|
|
}, {
|
|
name: "empty pkScript",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{},
|
|
},
|
|
expectedErr: ErrUnsupportedScript,
|
|
}, {
|
|
name: "nested p2wpkh input",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: genScript(input.GenerateP2SH),
|
|
},
|
|
},
|
|
expectedWitnessWeight: input.P2WKHWitnessSize +
|
|
input.NestedP2WPKHSize*witnessScaleFactor,
|
|
}, {
|
|
name: "p2wpkh input",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: genScript(input.WitnessPubKeyHash),
|
|
},
|
|
},
|
|
expectedWitnessWeight: input.P2WKHWitnessSize,
|
|
}, {
|
|
name: "p2wsh input",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: genScript(input.WitnessScriptHash),
|
|
},
|
|
},
|
|
expectedErr: ErrScriptSpendFeeEstimationUnsupported,
|
|
}, {
|
|
name: "p2tr with no derivation info",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: p2trScript,
|
|
},
|
|
},
|
|
expectedErrString: "cannot sign for taproot input " +
|
|
"without taproot BIP0032 derivation info",
|
|
}, {
|
|
name: "p2tr key spend",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: p2trScript,
|
|
},
|
|
SighashType: txscript.SigHashSingle,
|
|
TaprootBip32Derivation: []*psbt.TaprootBip32Derivation{
|
|
{},
|
|
},
|
|
},
|
|
//nolint:ll
|
|
expectedWitnessWeight: input.TaprootKeyPathCustomSighashWitnessSize,
|
|
}, {
|
|
name: "p2tr script spend",
|
|
in: &psbt.PInput{
|
|
WitnessUtxo: &wire.TxOut{
|
|
PkScript: p2trScript,
|
|
},
|
|
TaprootBip32Derivation: []*psbt.TaprootBip32Derivation{
|
|
{
|
|
LeafHashes: [][]byte{
|
|
dummyLeafHash[:],
|
|
},
|
|
},
|
|
},
|
|
TaprootLeafScript: []*psbt.TaprootTapLeafScript{
|
|
{
|
|
LeafVersion: dummyLeaf.LeafVersion,
|
|
Script: dummyLeaf.Script,
|
|
},
|
|
},
|
|
},
|
|
expectedErr: ErrScriptSpendFeeEstimationUnsupported,
|
|
}}
|
|
|
|
// The non-witness weight for a TX with a single input.
|
|
nonWitnessWeight := input.BaseTxSize + 1 + 1 + input.InputSize
|
|
|
|
// The base weight of a witness TX.
|
|
baseWeight := (nonWitnessWeight * witnessScaleFactor) +
|
|
input.WitnessHeaderSize
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
estimator := input.TxWeightEstimator{}
|
|
err := EstimateInputWeight(tc.in, &estimator)
|
|
|
|
if tc.expectedErr != nil {
|
|
require.Error(tt, err)
|
|
require.ErrorIs(tt, err, tc.expectedErr)
|
|
|
|
return
|
|
}
|
|
|
|
if tc.expectedErrString != "" {
|
|
require.Error(tt, err)
|
|
require.Contains(
|
|
tt, err.Error(), tc.expectedErrString,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
require.NoError(tt, err)
|
|
|
|
require.EqualValues(
|
|
tt, baseWeight+tc.expectedWitnessWeight,
|
|
estimator.Weight(),
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBip32DerivationFromKeyDesc tests that we can correctly extract a BIP32
|
|
// derivation path from a key descriptor.
|
|
func TestBip32DerivationFromKeyDesc(t *testing.T) {
|
|
privKey, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
keyDesc keychain.KeyDescriptor
|
|
coinType uint32
|
|
expectedPath string
|
|
expectedBip32Path []uint32
|
|
}{
|
|
{
|
|
name: "testnet multi-sig family",
|
|
keyDesc: keychain.KeyDescriptor{
|
|
PubKey: privKey.PubKey(),
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamilyMultiSig,
|
|
Index: 123,
|
|
},
|
|
},
|
|
coinType: chaincfg.TestNet3Params.HDCoinType,
|
|
expectedPath: "m/1017'/1'/0'/0/123",
|
|
expectedBip32Path: []uint32{
|
|
hardenedKey(keychain.BIP0043Purpose),
|
|
hardenedKey(chaincfg.TestNet3Params.HDCoinType),
|
|
hardenedKey(uint32(keychain.KeyFamilyMultiSig)),
|
|
0, 123,
|
|
},
|
|
},
|
|
{
|
|
name: "mainnet watchtower family",
|
|
keyDesc: keychain.KeyDescriptor{
|
|
PubKey: privKey.PubKey(),
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamilyTowerSession,
|
|
Index: 456,
|
|
},
|
|
},
|
|
coinType: chaincfg.MainNetParams.HDCoinType,
|
|
expectedPath: "m/1017'/0'/8'/0/456",
|
|
expectedBip32Path: []uint32{
|
|
hardenedKey(keychain.BIP0043Purpose),
|
|
hardenedKey(chaincfg.MainNetParams.HDCoinType),
|
|
hardenedKey(
|
|
uint32(keychain.KeyFamilyTowerSession),
|
|
),
|
|
0, 456,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
d, trD, path := Bip32DerivationFromKeyDesc(
|
|
tc.keyDesc, tc.coinType,
|
|
)
|
|
require.NoError(tt, err)
|
|
|
|
require.Equal(tt, tc.expectedPath, path)
|
|
require.Equal(tt, tc.expectedBip32Path, d.Bip32Path)
|
|
require.Equal(tt, tc.expectedBip32Path, trD.Bip32Path)
|
|
|
|
serializedKey := tc.keyDesc.PubKey.SerializeCompressed()
|
|
require.Equal(tt, serializedKey, d.PubKey)
|
|
require.Equal(tt, serializedKey[1:], trD.XOnlyPubKey)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBip32DerivationFromAddress tests that we can correctly extract a BIP32
|
|
// derivation path from an address.
|
|
func TestBip32DerivationFromAddress(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
addrType lnwallet.AddressType
|
|
expectedAddr string
|
|
expectedPath string
|
|
expectedBip32Path []uint32
|
|
expectedPubKey string
|
|
}{
|
|
{
|
|
name: "p2wkh",
|
|
addrType: lnwallet.WitnessPubKey,
|
|
expectedAddr: firstAddress,
|
|
expectedPath: "m/84'/0'/0'/0/0",
|
|
expectedBip32Path: []uint32{
|
|
hardenedKey(waddrmgr.KeyScopeBIP0084.Purpose),
|
|
hardenedKey(0), hardenedKey(0), 0, 0,
|
|
},
|
|
expectedPubKey: firstAddressPubKey,
|
|
},
|
|
{
|
|
name: "p2tr",
|
|
addrType: lnwallet.TaprootPubkey,
|
|
expectedAddr: firstAddressTaproot,
|
|
expectedPath: "m/86'/0'/0'/0/0",
|
|
expectedBip32Path: []uint32{
|
|
hardenedKey(waddrmgr.KeyScopeBIP0086.Purpose),
|
|
hardenedKey(0), hardenedKey(0), 0, 0,
|
|
},
|
|
expectedPubKey: firstAddressTaprootPubKey,
|
|
},
|
|
}
|
|
|
|
w, _ := newTestWallet(t, netParams, seedBytes)
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
addr, err := w.NewAddress(
|
|
tc.addrType, false, lnwallet.DefaultAccountName,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.expectedAddr, addr.String())
|
|
|
|
addrInfo, err := w.AddressInfo(addr)
|
|
require.NoError(t, err)
|
|
managedAddr, ok := addrInfo.(waddrmgr.ManagedPubKeyAddress)
|
|
require.True(t, ok)
|
|
|
|
d, trD, path, err := Bip32DerivationFromAddress(managedAddr)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.expectedPath, path)
|
|
require.Equal(t, tc.expectedBip32Path, d.Bip32Path)
|
|
require.Equal(t, tc.expectedBip32Path, trD.Bip32Path)
|
|
}
|
|
}
|