Merge pull request #605 from hieblmi/xpub-service

pull/615/head
George Tsagkarelis 9 months ago committed by GitHub
commit a0933160b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -292,6 +292,21 @@ var setParamsCommand = cli.Command{
"autoloop loop out, set to \"default\" in " +
"order to revert to default behavior.",
},
cli.StringFlag{
Name: "account",
Usage: "the name of the account to generate a new " +
"address from. You can list the names of " +
"valid accounts in your backing lnd " +
"instance with \"lncli wallet accounts list\".",
Value: "",
},
cli.StringFlag{
Name: "account_addr_type",
Usage: "the address type of the extended public key " +
"specified in account. Currently only " +
"pay-to-taproot-pubkey(p2tr) is supported",
Value: "p2tr",
},
cli.Uint64Flag{
Name: "autobudget",
Usage: "the maximum amount of fees in satoshis that " +
@ -445,11 +460,35 @@ func setParams(ctx *cli.Context) error {
flagSet = true
}
if ctx.IsSet("destaddr") {
switch {
case ctx.IsSet("destaddr") && ctx.IsSet("account"):
return fmt.Errorf("cannot set destaddr and account at the " +
"same time")
case ctx.IsSet("destaddr"):
params.AutoloopDestAddress = ctx.String("destaddr")
params.Account = ""
flagSet = true
case ctx.IsSet("account") != ctx.IsSet("account_addr_type"):
return liquidity.ErrAccountAndAddrType
case ctx.IsSet("account"):
params.Account = ctx.String("account")
params.AutoloopDestAddress = ""
flagSet = true
}
if ctx.IsSet("account_addr_type") {
switch ctx.String("account_addr_type") {
case "p2tr":
params.AccountAddrType = looprpc.AddressType_TAPROOT_PUBKEY
default:
return fmt.Errorf("unknown account address type")
}
}
if ctx.IsSet("autobudgetrefreshperiod") {
params.AutoloopBudgetRefreshPeriodSec =
uint64(ctx.Duration("autobudgetrefreshperiod").Seconds())

@ -39,6 +39,21 @@ var loopOutCommand = cli.Command{
"should be sent to, if let blank the funds " +
"will go to lnd's wallet",
},
cli.StringFlag{
Name: "account",
Usage: "the name of the account to generate a new " +
"address from. You can list the names of " +
"valid accounts in your backing lnd " +
"instance with \"lncli wallet accounts list\".",
Value: "",
},
cli.StringFlag{
Name: "account_addr_type",
Usage: "the address type of the extended public key " +
"specified in account. Currently only " +
"pay-to-taproot-pubkey(p2tr) is supported",
Value: "p2tr",
},
cli.Uint64Flag{
Name: "amt",
Usage: "the amount in satoshis to loop out",
@ -101,7 +116,7 @@ func loopOut(ctx *cli.Context) error {
}
// Parse outgoing channel set. Don't string split if the flag is empty.
// Otherwise strings.Split returns a slice of length one with an empty
// Otherwise, strings.Split returns a slice of length one with an empty
// element.
var outgoingChanSet []uint64
if ctx.IsSet("channel") {
@ -122,14 +137,41 @@ func loopOut(ctx *cli.Context) error {
return err
}
if ctx.IsSet("addr") && ctx.IsSet("account") {
return fmt.Errorf("cannot set --addr and --account at the " +
"same time. Please specify only one source for a new " +
"address to sweep the loop amount to")
}
var destAddr string
var account string
switch {
case ctx.IsSet("addr"):
destAddr = ctx.String("addr")
case ctx.IsSet("account"):
account = ctx.String("account")
case args.Present():
destAddr = args.First()
}
if ctx.IsSet("account") != ctx.IsSet("account_addr_type") {
return fmt.Errorf("cannot set account without specifying " +
"account address type and vice versa")
}
var accountAddrType looprpc.AddressType
if ctx.IsSet("account_addr_type") {
switch ctx.String("account_addr_type") {
case "p2tr":
accountAddrType = looprpc.AddressType_TAPROOT_PUBKEY
default:
return fmt.Errorf("unknown account address type")
}
}
client, cleanup, err := getClient(ctx)
if err != nil {
return err
@ -191,6 +233,8 @@ func loopOut(ctx *cli.Context) error {
resp, err := client.LoopOut(context.Background(), &looprpc.LoopOutRequest{
Amt: int64(amt),
Dest: destAddr,
Account: account,
AccountAddrType: accountAddrType,
MaxMinerFee: int64(limits.maxMinerFee),
MaxPrepayAmt: int64(limits.maxPrepayAmt),
MaxSwapFee: int64(limits.maxSwapFee),

@ -157,6 +157,15 @@ var (
// set together are specified.
ErrExclusiveRules = errors.New("channel and peer rules must be " +
"exclusive")
// ErrAmbiguousDestAddr is returned when a destination address and
// a extended public key account is set.
ErrAmbiguousDestAddr = errors.New("ambiguous destination address")
// ErrAccountAndAddrType indicates if an account is set but the
// account address type is not or vice versa.
ErrAccountAndAddrType = errors.New("account and address type have " +
"to be both either set or unset")
)
// Config contains the external functionality required to run the

@ -154,9 +154,15 @@ func (b *loopOutBuilder) buildSwap(ctx context.Context, pubkey route.Vertex,
request.Label = labels.EasyAutoloopLabel(swap.TypeOut)
}
account := ""
addrType := walletrpc.AddressType_WITNESS_PUBKEY_HASH
if len(params.Account) > 0 {
account = params.Account
addrType = params.AccountAddrType
}
addr, err := b.cfg.Lnd.WalletKit.NextAddr(
ctx, "", walletrpc.AddressType_WITNESS_PUBKEY_HASH,
false,
ctx, account, addrType, false,
)
if err != nil {
return nil, err

@ -3,6 +3,7 @@ package liquidity
import (
"errors"
"fmt"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"strings"
"time"
@ -42,10 +43,18 @@ type Parameters struct {
// Autoloop enables automatic dispatch of swaps.
Autoloop bool
// DestAddr is the address to be used for sweeping the on-chain HTLC that
// is related with a loop out.
// DestAddr is the address to be used for sweeping the on-chain HTLC
// that is related with a loop out.
DestAddr btcutil.Address
// An alternative destination address source for the swap. This field
// represents the name of the account in the backing lnd instance.
// Refer to lnd's wallet import functions for reference.
Account string
// The address type of the account specified in the account field.
AccountAddrType walletrpc.AddressType
// AutoFeeBudget is the total amount we allow to be spent on
// automatically dispatched swaps. Once this budget has been used, we
// will stop dispatching swaps until the budget is refreshed.
@ -227,6 +236,21 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo,
return ErrZeroInFlight
}
// Destination address and account cannot be set at the same time.
if p.DestAddr != nil && len(p.DestAddr.String()) > 0 &&
len(p.Account) > 0 {
return ErrAmbiguousDestAddr
}
// If an account is specified the respective address type must be
// specified as well, or both must be unset.
if len(p.Account) == 0 !=
(p.AccountAddrType == walletrpc.AddressType_UNKNOWN) {
return ErrAccountAndAddrType
}
err := validateRestrictions(server, &p.ClientRestrictions)
if err != nil {
return err
@ -376,13 +400,20 @@ func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters,
if req.AutoloopDestAddress == "default" {
destaddr = nil
} else {
destaddr, err = btcutil.DecodeAddress(req.AutoloopDestAddress, nil)
destaddr, err = btcutil.DecodeAddress(
req.AutoloopDestAddress, nil,
)
if err != nil {
return nil, err
}
}
}
addrType := walletrpc.AddressType_UNKNOWN
if req.AccountAddrType == clientrpc.AddressType_TAPROOT_PUBKEY {
addrType = walletrpc.AddressType_TAPROOT_PUBKEY
}
params := &Parameters{
FeeLimit: feeLimit,
SweepConfTarget: req.SweepConfTarget,
@ -393,6 +424,8 @@ func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters,
int64(req.AutoloopBudgetLastRefresh), 0,
),
DestAddr: destaddr,
Account: req.Account,
AccountAddrType: addrType,
AutoFeeBudget: btcutil.Amount(req.AutoloopBudgetSat),
MaxAutoInFlight: int(req.AutoMaxInFlight),
ChannelRules: make(
@ -487,6 +520,16 @@ func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters,
destaddr = cfg.DestAddr.String()
}
var addrType clientrpc.AddressType
switch cfg.AccountAddrType {
case walletrpc.AddressType_TAPROOT_PUBKEY:
addrType = clientrpc.AddressType_TAPROOT_PUBKEY
default:
addrType = clientrpc.AddressType_ADDRESS_TYPE_UNKNOWN
}
rpcCfg := &clientrpc.LiquidityParameters{
SweepConfTarget: cfg.SweepConfTarget,
FailureBackoffSec: uint64(cfg.FailureBackOff.Seconds()),
@ -512,6 +555,8 @@ func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters,
HtlcConfTarget: cfg.HtlcConfTarget,
EasyAutoloop: cfg.EasyAutoloop,
EasyAutoloopLocalTargetSat: uint64(cfg.EasyAutoloopTarget),
Account: cfg.Account,
AccountAddrType: addrType,
}
switch f := cfg.FeeLimit.(type) {

@ -83,10 +83,10 @@ type swapClientServer struct {
mainCtx context.Context
}
// LoopOut initiates an loop out swap with the given parameters. The call
// returns after the swap has been set up with the swap server. From that point
// onwards, progress can be tracked via the LoopOutStatus stream that is
// returned from Monitor().
// LoopOut initiates a loop out swap with the given parameters. The call returns
// after the swap has been set up with the swap server. From that point onwards,
// progress can be tracked via the LoopOutStatus stream that is returned from
// Monitor().
func (s *swapClientServer) LoopOut(ctx context.Context,
in *clientrpc.LoopOutRequest) (
*clientrpc.SwapResponse, error) {
@ -94,9 +94,49 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
log.Infof("Loop out request received")
var sweepAddr btcutil.Address
if in.Dest == "" {
var err error
//nolint:lll
switch {
case in.Dest != "" && in.Account != "":
return nil, fmt.Errorf("destination address and external " +
"account address cannot be set at the same time")
case in.Dest != "":
// Decode the client provided destination address for the loop
// out sweep.
sweepAddr, err = btcutil.DecodeAddress(
in.Dest, s.lnd.ChainParams,
)
if err != nil {
return nil, fmt.Errorf("decode address: %v", err)
}
case in.Account != "" && in.AccountAddrType == clientrpc.AddressType_ADDRESS_TYPE_UNKNOWN:
return nil, liquidity.ErrAccountAndAddrType
case in.Account != "":
// Derive a new receiving address from the stated account.
addrType, err := toWalletAddrType(in.AccountAddrType)
if err != nil {
return nil, err
}
// Check if account with address type exists.
if !s.accountExists(ctx, in.Account, addrType) {
return nil, fmt.Errorf("the provided account does " +
"not exist")
}
sweepAddr, err = s.lnd.WalletKit.NextAddr(
ctx, in.Account, addrType, false,
)
if err != nil {
return nil, fmt.Errorf("NextAddr from account error: "+
"%v", err)
}
default:
// Generate sweep address if none specified.
var err error
sweepAddr, err = s.lnd.WalletKit.NextAddr(
context.Background(), "",
walletrpc.AddressType_WITNESS_PUBKEY_HASH, false,
@ -104,14 +144,6 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
if err != nil {
return nil, fmt.Errorf("NextAddr error: %v", err)
}
} else {
var err error
sweepAddr, err = btcutil.DecodeAddress(
in.Dest, s.lnd.ChainParams,
)
if err != nil {
return nil, fmt.Errorf("decode address: %v", err)
}
}
sweepConfTarget, err := validateLoopOutRequest(
@ -174,6 +206,38 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
return resp, nil
}
// accountExists returns true if account under the address type exists in the
// backing lnd instance and false otherwise.
func (s *swapClientServer) accountExists(ctx context.Context, account string,
addrType walletrpc.AddressType) bool {
accounts, err := s.lnd.WalletKit.ListAccounts(ctx, account, addrType)
if err != nil {
return false
}
for _, a := range accounts {
if a.Name == account {
return true
}
}
return false
}
func toWalletAddrType(addrType clientrpc.AddressType) (walletrpc.AddressType,
error) {
switch addrType {
case clientrpc.AddressType_TAPROOT_PUBKEY:
return walletrpc.AddressType_TAPROOT_PUBKEY, nil
default:
return walletrpc.AddressType_UNKNOWN,
fmt.Errorf("unknown address type")
}
}
func (s *swapClientServer) marshallSwap(loopSwap *loop.SwapInfo) (
*clientrpc.SwapStatus, error) {
@ -1072,7 +1136,8 @@ func validateLoopOutRequest(ctx context.Context, lnd lndclient.LightningClient,
// Check that the provided destination address is a supported
// address format.
switch sweepAddr.(type) {
case *btcutil.AddressWitnessScriptHash,
case *btcutil.AddressTaproot,
*btcutil.AddressWitnessScriptHash,
*btcutil.AddressWitnessPubKeyHash,
*btcutil.AddressScriptHash,
*btcutil.AddressPubKeyHash:

File diff suppressed because it is too large Load Diff

@ -212,6 +212,29 @@ message LoopOutRequest {
triggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI).
*/
string initiator = 14;
/*
An alternative destination address source for the swap. This field
represents the name of the account in the backing lnd instance.
Refer to lnd's wallet import functions for reference.
*/
string account = 15;
/*
The address type of the account specified in the account field.
*/
AddressType account_addr_type = 16;
}
/*
`AddressType` has to be one of:
- `unknown`: Unknown address type
- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 1)
*/
enum AddressType {
ADDRESS_TYPE_UNKNOWN = 0;
TAPROOT_PUBKEY = 1;
}
message LoopInRequest {
@ -970,6 +993,18 @@ message LiquidityParameters {
autoloop to determine how much liquidity should be maintained in channels.
*/
uint64 easy_autoloop_local_target_sat = 22;
/*
An alternative destination address source for the swap. This field
represents the name of the account in the backing lnd instance.
Refer to lnd's wallet import functions for reference.
*/
string account = 23;
/*
The address type of the account specified in the account field.
*/
AddressType account_addr_type = 24;
}
enum LiquidityRuleType {

@ -492,6 +492,16 @@
}
},
"definitions": {
"looprpcAddressType": {
"type": "string",
"enum": [
"ADDRESS_TYPE_UNKNOWN",
"TAPROOT_PUBKEY"
],
"default": "ADDRESS_TYPE_UNKNOWN",
"description": "- `unknown`: Unknown address type\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 1)",
"title": "`AddressType` has to be one of:"
},
"looprpcAutoReason": {
"type": "string",
"enum": [
@ -763,6 +773,14 @@
"type": "string",
"format": "uint64",
"description": "The local balance target size, expressed in satoshis. This is used by easy\nautoloop to determine how much liquidity should be maintained in channels."
},
"account": {
"type": "string",
"description": "An alternative destination address source for the swap. This field\nrepresents the name of the account in the backing lnd instance.\nRefer to lnd's wallet import functions for reference."
},
"account_addr_type": {
"$ref": "#/definitions/looprpcAddressType",
"description": "The address type of the account specified in the account field."
}
}
},
@ -944,6 +962,14 @@
"initiator": {
"type": "string",
"description": "An optional identification string that will be appended to the user agent\nstring sent to the server to give information about the usage of loop. This\ninitiator part is meant for user interfaces to add their name to give the\nfull picture of the binary used (loopd, LiT) and the method used for\ntriggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI)."
},
"account": {
"type": "string",
"description": "An alternative destination address source for the swap. This field\nrepresents the name of the account in the backing lnd instance.\nRefer to lnd's wallet import functions for reference."
},
"account_addr_type": {
"$ref": "#/definitions/looprpcAddressType",
"description": "The address type of the account specified in the account field."
}
}
},
@ -1020,7 +1046,7 @@
},
"id": {
"type": "string",
"description": "The l402 Id of the token."
"description": "The l402 ID of the token."
}
}
},

@ -1,20 +1,19 @@
# Loop Client Release Notes
This file tracks release notes for the loop client.
### Developers:
* When new features are added to the repo, a short description of the feature should be added under the "Next Release" heading.
* This should be done in the same PR as the change so that our release notes stay in sync!
### Release Manager:
* All of the items under the "Next Release" heading should be included in the release notes.
* As part of the PR that bumps the client version, cut everything below the 'Next Release' heading.
* These notes can either be pasted in a temporary doc, or you can get them from the PR diff once it is merged.
* The notes are just a guideline as to the changes that have been made since the last release, they can be updated.
* Once the version bump PR is merged and tagged, add the release notes to the tag on GitHub.
## Next release
#### New Features
This new feature enables loop out and autoloop out operations to sweep htlcs to addresses generated from an extened public key.
A precondition for using the loop client in this fashion is onboarding a xpub account in the backing lnd instance, e.g.
`lncli wallet accounts import xpub... my_loop_account --address_type p2tr --master_key_fingerprint 0df50`.
Loop outs can then be instructed to sweep to a new derived address from the specified account, e.g:
`loop out --amt 10000000 --account my_loop_account --address_type p2tr`.
To use this functionality with autoloop out one has to set the backing lnd account and address type via liquidity parameters for autoloop, e.g.
`loop --network regtest setparams --autoloop=true --account=my_loop_account --account_addr_type=p2tr ...`
#### Breaking Changes

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: common.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.28.1
// protoc v3.6.1
// source: server.proto

Loading…
Cancel
Save