mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-20 02:27:21 +01:00
Merge pull request #2661 from Roasbeef/last-unused-addr
lnwallet+lnrpc: add ability to fetch the last unused wallet addr
This commit is contained in:
commit
63273e195e
1145
lnrpc/rpc.pb.go
1145
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load Diff
@ -900,6 +900,8 @@ message ListUnspentResponse {
|
||||
enum AddressType {
|
||||
WITNESS_PUBKEY_HASH = 0;
|
||||
NESTED_PUBKEY_HASH = 1;
|
||||
UNUSED_WITNESS_PUBKEY_HASH = 2;
|
||||
UNUSED_NESTED_PUBKEY_HASH = 3;
|
||||
}
|
||||
|
||||
message NewAddressRequest {
|
||||
|
@ -793,7 +793,9 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"WITNESS_PUBKEY_HASH",
|
||||
"NESTED_PUBKEY_HASH"
|
||||
"NESTED_PUBKEY_HASH",
|
||||
"UNUSED_WITNESS_PUBKEY_HASH",
|
||||
"UNUSED_NESTED_PUBKEY_HASH"
|
||||
],
|
||||
"default": "WITNESS_PUBKEY_HASH"
|
||||
}
|
||||
@ -1289,7 +1291,9 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"WITNESS_PUBKEY_HASH",
|
||||
"NESTED_PUBKEY_HASH"
|
||||
"NESTED_PUBKEY_HASH",
|
||||
"UNUSED_WITNESS_PUBKEY_HASH",
|
||||
"UNUSED_NESTED_PUBKEY_HASH"
|
||||
],
|
||||
"default": "WITNESS_PUBKEY_HASH",
|
||||
"description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)",
|
||||
|
@ -254,6 +254,29 @@ func (b *BtcWallet) NewAddress(t lnwallet.AddressType, change bool) (btcutil.Add
|
||||
return b.wallet.NewAddress(defaultAccount, keyScope)
|
||||
}
|
||||
|
||||
// LastUnusedAddress returns the last *unused* address known by the wallet. An
|
||||
// address is unused if it hasn't received any payments. This can be useful in
|
||||
// UIs in order to continually show the "freshest" address without having to
|
||||
// worry about "address inflation" caused by continual refreshing. Similar to
|
||||
// NewAddress it can derive a specified address type, and also optionally a
|
||||
// change address.
|
||||
func (b *BtcWallet) LastUnusedAddress(addrType lnwallet.AddressType) (
|
||||
btcutil.Address, error) {
|
||||
|
||||
var keyScope waddrmgr.KeyScope
|
||||
|
||||
switch addrType {
|
||||
case lnwallet.WitnessPubKey:
|
||||
keyScope = waddrmgr.KeyScopeBIP0084
|
||||
case lnwallet.NestedWitnessPubKey:
|
||||
keyScope = waddrmgr.KeyScopeBIP0049Plus
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown address type")
|
||||
}
|
||||
|
||||
return b.wallet.CurrentAddress(defaultAccount, keyScope)
|
||||
}
|
||||
|
||||
// IsOurAddress checks if the passed address belongs to this wallet
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
|
@ -150,6 +150,14 @@ type WalletController interface {
|
||||
// p2wsh, etc.
|
||||
NewAddress(addrType AddressType, change bool) (btcutil.Address, error)
|
||||
|
||||
// LastUnusedAddress returns the last *unused* address known by the
|
||||
// wallet. An address is unused if it hasn't received any payments.
|
||||
// This can be useful in UIs in order to continually show the
|
||||
// "freshest" address without having to worry about "address inflation"
|
||||
// caused by continual refreshing. Similar to NewAddress it can derive
|
||||
// a specified address type. By default, this is a non-change address.
|
||||
LastUnusedAddress(addrType AddressType) (btcutil.Address, error)
|
||||
|
||||
// IsOurAddress checks if the passed address belongs to this wallet
|
||||
IsOurAddress(a btcutil.Address) bool
|
||||
|
||||
|
@ -2165,6 +2165,62 @@ func testChangeOutputSpendConfirmation(r *rpctest.Harness,
|
||||
}
|
||||
}
|
||||
|
||||
// testLastUnusedAddr tests that the LastUnusedAddress returns the address if
|
||||
// it isn't used, and also that once the address becomes used, then it's
|
||||
// properly rotated.
|
||||
func testLastUnusedAddr(miner *rpctest.Harness,
|
||||
alice, bob *lnwallet.LightningWallet, t *testing.T) {
|
||||
|
||||
if _, err := miner.Node.Generate(1); err != nil {
|
||||
t.Fatalf("unable to generate block: %v", err)
|
||||
}
|
||||
|
||||
// We'll repeat this test for each address type to ensure they're all
|
||||
// rotated properly.
|
||||
addrTypes := []lnwallet.AddressType{
|
||||
lnwallet.WitnessPubKey, lnwallet.NestedWitnessPubKey,
|
||||
}
|
||||
for _, addrType := range addrTypes {
|
||||
addr1, err := alice.LastUnusedAddress(addrType)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get addr: %v", err)
|
||||
}
|
||||
addr2, err := alice.LastUnusedAddress(addrType)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get addr: %v", err)
|
||||
}
|
||||
|
||||
// If we generate two addresses back to back, then we should
|
||||
// get the same addr, as none of them have been used yet.
|
||||
if addr1.String() != addr2.String() {
|
||||
t.Fatalf("addresses changed w/o use: %v vs %v", addr1, addr2)
|
||||
}
|
||||
|
||||
// Next, we'll have Bob pay to Alice's new address. This should
|
||||
// trigger address rotation at the backend wallet.
|
||||
addrScript, err := txscript.PayToAddrScript(addr1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to convert addr to script: %v", err)
|
||||
}
|
||||
feeRate := lnwallet.SatPerKWeight(2500)
|
||||
output := &wire.TxOut{
|
||||
Value: 1000000,
|
||||
PkScript: addrScript,
|
||||
}
|
||||
sendCoins(t, miner, bob, alice, output, feeRate)
|
||||
|
||||
// If we make a new address, then it should be brand new, as
|
||||
// the prior address has been used.
|
||||
addr3, err := alice.LastUnusedAddress(addrType)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get addr: %v", err)
|
||||
}
|
||||
if addr1.String() == addr3.String() {
|
||||
t.Fatalf("address should have changed but didn't")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type walletTestCase struct {
|
||||
name string
|
||||
test func(miner *rpctest.Harness, alice, bob *lnwallet.LightningWallet,
|
||||
@ -2219,6 +2275,10 @@ var walletTests = []walletTestCase{
|
||||
name: "test cancel non-existent reservation",
|
||||
test: testCancelNonExistentReservation,
|
||||
},
|
||||
{
|
||||
name: "last unused addr",
|
||||
test: testLastUnusedAddr,
|
||||
},
|
||||
{
|
||||
name: "reorg wallet balance",
|
||||
test: testReorgWalletBalance,
|
||||
|
5
mock.go
5
mock.go
@ -226,6 +226,11 @@ func (m *mockWalletController) NewAddress(addrType lnwallet.AddressType,
|
||||
m.rootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams)
|
||||
return addr, nil
|
||||
}
|
||||
func (*mockWalletController) LastUnusedAddress(addrType lnwallet.AddressType) (
|
||||
btcutil.Address, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
41
rpcserver.go
41
rpcserver.go
@ -932,17 +932,42 @@ func (r *rpcServer) NewAddress(ctx context.Context,
|
||||
|
||||
// Translate the gRPC proto address type to the wallet controller's
|
||||
// available address types.
|
||||
var addrType lnwallet.AddressType
|
||||
var (
|
||||
addr btcutil.Address
|
||||
err error
|
||||
)
|
||||
switch in.Type {
|
||||
case lnrpc.AddressType_WITNESS_PUBKEY_HASH:
|
||||
addrType = lnwallet.WitnessPubKey
|
||||
case lnrpc.AddressType_NESTED_PUBKEY_HASH:
|
||||
addrType = lnwallet.NestedWitnessPubKey
|
||||
}
|
||||
addr, err = r.server.cc.wallet.NewAddress(
|
||||
lnwallet.WitnessPubKey, false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := r.server.cc.wallet.NewAddress(addrType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
case lnrpc.AddressType_NESTED_PUBKEY_HASH:
|
||||
addr, err = r.server.cc.wallet.NewAddress(
|
||||
lnwallet.NestedWitnessPubKey, false,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH:
|
||||
addr, err = r.server.cc.wallet.LastUnusedAddress(
|
||||
lnwallet.WitnessPubKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case lnrpc.AddressType_UNUSED_NESTED_PUBKEY_HASH:
|
||||
addr, err = r.server.cc.wallet.LastUnusedAddress(
|
||||
lnwallet.NestedWitnessPubKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rpcsLog.Infof("[newaddress] addr=%v", addr.String())
|
||||
|
Loading…
Reference in New Issue
Block a user