lntest: adds test for list addresses
This commit is contained in:
1 parent
2f20012af6
commit
3105d6a2ec
3 files changed
+191
No files matched your search
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@@ -1673,3 +1674,151 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
t.Fatalf("sweep attempt should fail")
|
||||
}
|
||||
}
|
||||
|
||||
// testListAddresses tests that we get all the addresses and their
|
||||
// corresponding balance correctly.
|
||||
func testListAddresses(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
// First, we'll make a new node - Alice, which will be generating
|
||||
// new addresses.
|
||||
alice := net.NewNode(t.t, "Alice", nil)
|
||||
defer shutdownAndAssert(net, t, alice)
|
||||
|
||||
// Next, we'll give Alice exactly 1 utxo of 1 BTC.
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice)
|
||||
|
||||
type addressDetails struct {
|
||||
Balance int64
|
||||
Type walletrpc.AddressType
|
||||
}
|
||||
|
||||
// A map of generated address and its balance.
|
||||
generatedAddr := make(map[string]addressDetails)
|
||||
|
||||
// Create an address generated from internal keys.
|
||||
keyLoc := &walletrpc.KeyReq{KeyFamily: 123}
|
||||
keyDesc, err := alice.WalletKitClient.DeriveNextKey(ctxb, keyLoc)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Hex Encode the public key.
|
||||
pubkeyString := hex.EncodeToString(keyDesc.RawKeyBytes)
|
||||
|
||||
// Create a p2tr address.
|
||||
resp, err := alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
||||
Type: lnrpc.AddressType_TAPROOT_PUBKEY,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
generatedAddr[resp.Address] = addressDetails{
|
||||
Balance: 200_000,
|
||||
Type: walletrpc.AddressType_TAPROOT_PUBKEY,
|
||||
}
|
||||
|
||||
// Create a p2wkh address.
|
||||
resp, err = alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
||||
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
generatedAddr[resp.Address] = addressDetails{
|
||||
Balance: 300_000,
|
||||
Type: walletrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||
}
|
||||
|
||||
// Create a np2wkh address.
|
||||
resp, err = alice.NewAddress(ctxb, &lnrpc.NewAddressRequest{
|
||||
Type: lnrpc.AddressType_NESTED_PUBKEY_HASH,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
generatedAddr[resp.Address] = addressDetails{
|
||||
Balance: 400_000,
|
||||
Type: walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH,
|
||||
}
|
||||
|
||||
for addr, addressDetail := range generatedAddr {
|
||||
_, err := alice.SendCoins(ctxb, &lnrpc.SendCoinsRequest{
|
||||
Addr: addr,
|
||||
Amount: addressDetail.Balance,
|
||||
SpendUnconfirmed: true,
|
||||
})
|
||||
require.NoError(t.t, err)
|
||||
}
|
||||
|
||||
mineBlocks(t, net, 1, 3)
|
||||
|
||||
// Get all the accounts except LND's custom accounts.
|
||||
addressLists, err := alice.WalletKitClient.ListAddresses(
|
||||
ctxb, &walletrpc.ListAddressesRequest{},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
foundAddresses := 0
|
||||
for _, addressList := range addressLists.AccountWithAddresses {
|
||||
addresses := addressList.Addresses
|
||||
derivationPath, err := parseDerivationPath(
|
||||
addressList.DerivationPath,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Should not get an account with KeyFamily - 123.
|
||||
require.NotEqual(
|
||||
t.t, uint32(keyLoc.KeyFamily), derivationPath[2],
|
||||
)
|
||||
|
||||
for _, address := range addresses {
|
||||
if _, ok := generatedAddr[address.Address]; ok {
|
||||
addrDetails := generatedAddr[address.Address]
|
||||
require.Equal(
|
||||
t.t, addrDetails.Balance,
|
||||
address.Balance,
|
||||
)
|
||||
require.Equal(
|
||||
t.t, addrDetails.Type,
|
||||
addressList.AddressType,
|
||||
)
|
||||
foundAddresses++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t.t, len(generatedAddr), foundAddresses)
|
||||
foundAddresses = 0
|
||||
|
||||
// Get all the accounts (including LND's custom accounts).
|
||||
addressLists, err = alice.WalletKitClient.ListAddresses(
|
||||
ctxb, &walletrpc.ListAddressesRequest{
|
||||
ShowCustomAccounts: true,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
for _, addressList := range addressLists.AccountWithAddresses {
|
||||
addresses := addressList.Addresses
|
||||
derivationPath, err := parseDerivationPath(
|
||||
addressList.DerivationPath,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
for _, address := range addresses {
|
||||
// Check if the KeyFamily in derivation path is 123.
|
||||
if uint32(keyLoc.KeyFamily) == derivationPath[2] {
|
||||
// For LND's custom accounts, the address
|
||||
// represents the public key.
|
||||
pubkey := address.Address
|
||||
require.Equal(t.t, pubkeyString, pubkey)
|
||||
} else if _, ok := generatedAddr[address.Address]; ok {
|
||||
addrDetails := generatedAddr[address.Address]
|
||||
require.Equal(
|
||||
t.t, addrDetails.Balance,
|
||||
address.Balance,
|
||||
)
|
||||
require.Equal(
|
||||
t.t, addrDetails.Type,
|
||||
addressList.AddressType,
|
||||
)
|
||||
foundAddresses++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(t.t, len(generatedAddr), foundAddresses)
|
||||
}
|
||||
@@ -12,6 +12,10 @@ var allTestCases = []*testCase{
|
||||
name: "sweep coins",
|
||||
test: testSweepAllCoins,
|
||||
},
|
||||
{
|
||||
name: "list addresses",
|
||||
test: testListAddresses,
|
||||
},
|
||||
{
|
||||
name: "recovery info",
|
||||
test: testGetRecoveryInfo,
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
@@ -494,6 +496,42 @@ func getOutputIndex(t *harnessTest, miner *lntest.HarnessMiner,
|
||||
return p2trOutputIndex
|
||||
}
|
||||
|
||||
// parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice
|
||||
// of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_
|
||||
// added to the numbers.
|
||||
func parseDerivationPath(path string) ([]uint32, error) {
|
||||
path = strings.TrimSpace(path)
|
||||
if len(path) == 0 {
|
||||
return nil, fmt.Errorf("path cannot be empty")
|
||||
}
|
||||
if !strings.HasPrefix(path, "m/") {
|
||||
return nil, fmt.Errorf("path must start with m/")
|
||||
}
|
||||
|
||||
// Just the root key, no path was provided. This is valid but not useful
|
||||
// in most cases.
|
||||
rest := strings.ReplaceAll(path, "m/", "")
|
||||
if rest == "" {
|
||||
return []uint32{}, nil
|
||||
}
|
||||
|
||||
parts := strings.Split(rest, "/")
|
||||
indices := make([]uint32, len(parts))
|
||||
for i := 0; i < len(parts); i++ {
|
||||
part := parts[i]
|
||||
if strings.Contains(parts[i], "'") {
|
||||
part = strings.TrimRight(parts[i], "'")
|
||||
}
|
||||
parsed, err := strconv.ParseInt(part, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse part \"%s\": "+
|
||||
"%v", part, err)
|
||||
}
|
||||
indices[i] = uint32(parsed)
|
||||
}
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// acceptChannel is used to accept a single channel that comes across. This
|
||||
// should be run in a goroutine and is used to test nodes with the zero-conf
|
||||
// feature bit.
|
||||
|
||||
Reference in new issue
Block a user