From f9da94e8d12e66acc43a13c438eac26afcd0b18a Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Sat, 24 May 2025 13:56:44 +0200 Subject: [PATCH 01/12] misc: fix formatting and typos --- lightning_client.go | 47 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/lightning_client.go b/lightning_client.go index 2cee931..6c36ab9 100644 --- a/lightning_client.go +++ b/lightning_client.go @@ -118,7 +118,8 @@ type LightningClient interface { opts ...ListTransactionsOption) ([]Transaction, error) // ListChannels retrieves all channels of the backing lnd node. - ListChannels(ctx context.Context, activeOnly, publicOnly bool) ([]ChannelInfo, error) + ListChannels(ctx context.Context, activeOnly, publicOnly bool) ( + []ChannelInfo, error) // PendingChannels returns a list of lnd's pending channels. PendingChannels(ctx context.Context) (*PendingChannels, error) @@ -128,8 +129,8 @@ type LightningClient interface { // ForwardingHistory makes a paginated call to our forwarding history // endpoint. - ForwardingHistory(ctx context.Context, - req ForwardingHistoryRequest) (*ForwardingHistoryResponse, error) + ForwardingHistory(ctx context.Context, req ForwardingHistoryRequest) ( + *ForwardingHistoryResponse, error) // ListInvoices makes a paginated call to our list invoices endpoint. ListInvoices(ctx context.Context, req ListInvoicesRequest) ( @@ -148,9 +149,8 @@ type LightningClient interface { // chanbackup.Multi payload. ChannelBackups(ctx context.Context) ([]byte, error) - // SubscribeChannelBackups allows a client to subscribe to the - // most up to date information concerning the state of all channel - // backups. + // SubscribeChannelBackups allows a client to subscribe to the most + // up-to-date information concerning the state of all channel backups. SubscribeChannelBackups(ctx context.Context) ( <-chan lnrpc.ChanBackupSnapshot, <-chan error, error) @@ -221,7 +221,8 @@ type LightningClient interface { includeChannels bool) (*NodeInfo, error) // DescribeGraph returns our view of the graph. - DescribeGraph(ctx context.Context, includeUnannounced bool) (*Graph, error) + DescribeGraph(ctx context.Context, includeUnannounced bool) (*Graph, + error) // SubscribeGraph allows a client to subscribe to gaph topology updates. SubscribeGraph(ctx context.Context) (<-chan *GraphTopologyUpdate, @@ -293,8 +294,7 @@ type LightningClient interface { // The returned signature string is zbase32 encoded and pubkey // recoverable, meaning that only the message digest and signature // are needed for verification. - SignMessage(ctx context.Context, data []byte) (string, - error) + SignMessage(ctx context.Context, data []byte) (string, error) // VerifyMessage verifies a signature over a msg. The signature must // be zbase32 encoded and signed by an active node in the resident @@ -359,9 +359,9 @@ type ChannelInfo struct { // Active indicates whether the channel is active. Active bool - // ChannelID holds the unique channel ID for the channel. The first 3 bytes - // are the block height, the next 3 the index within the block, and the last - // 2 bytes are the /output index for the channel. + // ChannelID holds the unique channel ID for the channel. The first 3 + // bytes are the block height, the next 3 the index within the block, + // and the last 2 bytes are the /output index for the channel. ChannelID uint64 // PubKeyBytes is the raw bytes of the public key of the remote node. @@ -765,7 +765,7 @@ const ( ForceCloseAnchorStateLost = ForceCloseAnchorState(lnrpc.PendingChannelsResponse_ForceClosedChannel_LOST) ) -// String provides the string represenetation of a close initiator. +// String provides the string representation of a close initiator. func (c Initiator) String() string { switch c { case InitiatorUnrecorded: @@ -2873,7 +2873,7 @@ type PaymentRequest struct { // Value is the value of the payment request in millisatoshis. Value lnwire.MilliSatoshi - /// Timestamp of the payment request. + // Timestamp of the payment request. Timestamp time.Time // Expiry is the time at which the payment request expires. @@ -3364,7 +3364,7 @@ type ChannelEdge struct { Node2Policy *RoutingPolicy } -// getRoutingPolicy converts an lnrpc.RoutingPolicy to RoutingPolicy. +// getRoutingPolicy converts a lnrpc.RoutingPolicy to RoutingPolicy. func getRoutingPolicy(policy *lnrpc.RoutingPolicy) *RoutingPolicy { if policy == nil { return nil @@ -3678,7 +3678,7 @@ func (s *lightningClient) SubscribeGraph(ctx context.Context) ( return updates, errChan, nil } -// getGraphTopologyUpdate converts an lnrpc.GraphTopologyUpdate to the higher +// getGraphTopologyUpdate converts a lnrpc.GraphTopologyUpdate to the higher // level GraphTopologyUpdate. func getGraphTopologyUpdate(update *lnrpc.GraphTopologyUpdate) ( *GraphTopologyUpdate, error) { @@ -3814,19 +3814,20 @@ func (s *lightningClient) NetworkInfo(ctx context.Context) (*NetworkInfo, // to start streaming. type InvoiceSubscriptionRequest struct { // If specified (non-zero), then we'll first start by sending out - // notifications for all added indexes with an add_index greater than this - // value. This allows callers to catch up on any events they missed while they - // weren't connected to the streaming RPC. + // notifications for all added indexes with an add_index greater than + // this value. This allows callers to catch up on any events they missed + // while they weren't connected to the streaming RPC. AddIndex uint64 // If specified (non-zero), then we'll first start by sending out - // notifications for all settled indexes with an settle_index greater than - // this value. This allows callers to catch up on any events they missed while - // they weren't connected to the streaming RPC. + // notifications for all settled indexes with a settle_index greater + // than this value. This allows callers to catch up on any events they + // missed while they weren't connected to the streaming RPC. SettleIndex uint64 } -// SubscribeInvoices subscribes a client to updates of newly added/settled invoices. +// SubscribeInvoices subscribes a client to updates of newly added/settled +// invoices. func (s *lightningClient) SubscribeInvoices(ctx context.Context, req InvoiceSubscriptionRequest) (<-chan *Invoice, <-chan error, error) { From 4611dc965028596617ca40a86cdae4e4ef85414d Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Fri, 23 May 2025 12:56:20 +0200 Subject: [PATCH 02/12] closechannel: add functional options --- lightning_client.go | 48 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/lightning_client.go b/lightning_client.go index 6c36ab9..6a3c33a 100644 --- a/lightning_client.go +++ b/lightning_client.go @@ -181,8 +181,9 @@ type LightningClient interface { // CloseChannel closes the channel provided. CloseChannel(ctx context.Context, channel *wire.OutPoint, - force bool, confTarget int32, deliveryAddr btcutil.Address) ( - chan CloseChannelUpdate, chan error, error) + force bool, confTarget int32, deliveryAddr btcutil.Address, + opts ...CloseChannelOption) (chan CloseChannelUpdate, + chan error, error) // UpdateChanPolicy updates the channel policy for the passed chanPoint. // If the chanPoint is nil, then the policy is applied for all existing @@ -3085,6 +3086,33 @@ func (p *ChannelClosedUpdate) CloseTxid() chainhash.Hash { return p.CloseTx } +// CloseChannelOption is a functional type for an option that modifies a +// CloseChannelRequest. +type CloseChannelOption func(r *lnrpc.CloseChannelRequest) + +// SatPerVbyte is an option for setting the fee rate of a CloseChannelRequest. +func SatPerVbyte(satPerVbyte chainfee.SatPerVByte) CloseChannelOption { + return func(r *lnrpc.CloseChannelRequest) { + r.SatPerVbyte = uint64(satPerVbyte) + } +} + +// MaxFeePerVbyte is an option for setting the maximum fee rate a closer is +// willing to pay on a CloseChannelRequest. +func MaxFeePerVbyte(maxFeePerVbyte chainfee.SatPerVByte) CloseChannelOption { + return func(r *lnrpc.CloseChannelRequest) { + r.MaxFeePerVbyte = uint64(maxFeePerVbyte) + } +} + +// WithNoWait is an option for setting the NoWait flag on an +// CloseChannelRequest. +func WithNoWait() CloseChannelOption { + return func(r *lnrpc.CloseChannelRequest) { + r.NoWait = true + } +} + // CloseChannel closes the channel provided, returning a channel that will send // a stream of close updates, and an error channel which will receive errors if // the channel close stream fails. This function starts a goroutine to consume @@ -3093,11 +3121,11 @@ func (p *ChannelClosedUpdate) CloseTxid() chainhash.Hash { // sending an EOF), we close the updates and error channel to signal that there // are no more updates to be sent. It takes an optional delivery address that // funds will be paid out to in the case where we cooperative close a channel -// that *does not* have an upfront shutdown addresss set. +// that *does not* have an upfront shutdown address set. func (s *lightningClient) CloseChannel(ctx context.Context, channel *wire.OutPoint, force bool, confTarget int32, - deliveryAddr btcutil.Address) (chan CloseChannelUpdate, chan error, - error) { + deliveryAddr btcutil.Address, opts ...CloseChannelOption) ( + chan CloseChannelUpdate, chan error, error) { var ( rpcCtx = s.adminMac.WithMacaroonAuth(ctx) @@ -3108,7 +3136,7 @@ func (s *lightningClient) CloseChannel(ctx context.Context, addrStr = deliveryAddr.String() } - stream, err := s.client.CloseChannel(rpcCtx, &lnrpc.CloseChannelRequest{ + closeChannelReq := &lnrpc.CloseChannelRequest{ ChannelPoint: &lnrpc.ChannelPoint{ FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ FundingTxidBytes: channel.Hash[:], @@ -3118,7 +3146,13 @@ func (s *lightningClient) CloseChannel(ctx context.Context, TargetConf: confTarget, Force: force, DeliveryAddress: addrStr, - }) + } + + for _, opt := range opts { + opt(closeChannelReq) + } + + stream, err := s.client.CloseChannel(rpcCtx, closeChannelReq) if err != nil { return nil, nil, err } From 6d65e0a659bc089cccf1211b2bd95a8a970278b3 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 2 Jun 2025 17:40:22 +0200 Subject: [PATCH 03/12] signer_client: correctly use 32-byte key for schnorr This was incorrect all along, seems like we haven't actually used the VerifySchnorr option in any project yet. --- signer_client.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/signer_client.go b/signer_client.go index 048db2c..acadd2d 100644 --- a/signer_client.go +++ b/signer_client.go @@ -502,6 +502,12 @@ func (s *signerClient) VerifyMessage(ctx context.Context, msg, sig []byte, opt(rpcIn) } + // If the signature is a Schnorr signature, we need to set the public + // key as the 32-byte x-only key, as mentioned in the RPC docs. + if rpcIn.IsSchnorrSig { + rpcIn.Pubkey = pubkey[1:] + } + rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) resp, err := s.client.VerifyMessage(rpcCtx, rpcIn) if err != nil { From aaf4f27ab618f300d54ce213c223f4ce5de85159 Mon Sep 17 00:00:00 2001 From: Andras Banki-Horvath Date: Wed, 11 Jun 2025 10:22:17 +0200 Subject: [PATCH 04/12] signer: allow passing nil keyLocator to DeriveSharedKey Previously if the `keyLocaltor` argument was unset `DeriveSharedKey` would crash. --- signer_client.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/signer_client.go b/signer_client.go index acadd2d..32919b8 100644 --- a/signer_client.go +++ b/signer_client.go @@ -533,10 +533,13 @@ func (s *signerClient) DeriveSharedKey(ctx context.Context, rpcIn := &signrpc.SharedKeyRequest{ EphemeralPubkey: ephemeralPubKey.SerializeCompressed(), - KeyLoc: &signrpc.KeyLocator{ + } + + if keyLocator != nil { + rpcIn.KeyLoc = &signrpc.KeyLocator{ KeyFamily: int32(keyLocator.Family), KeyIndex: int32(keyLocator.Index), - }, + } } rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) From 0bb2748df66ae7f110b2d0baa03fe956d7b1efac Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Tue, 24 Jun 2025 17:34:32 -0300 Subject: [PATCH 05/12] walletkit: add RPC GetTransaction https://lightning.engineering/api-docs/api/lnd/wallet-kit/get-transaction/ The RPC is available since LND 0.18.0. --- testdata/permissions.json | 8 ++++++++ walletkit_client.go | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/testdata/permissions.json b/testdata/permissions.json index e8ef641..e1d7873 100644 --- a/testdata/permissions.json +++ b/testdata/permissions.json @@ -1136,6 +1136,14 @@ } ] }, + "/walletrpc.WalletKit/GetTransaction": { + "permissions": [ + { + "entity": "onchain", + "action": "read" + } + ] + }, "/walletrpc.WalletKit/PendingSweeps": { "permissions": [ { diff --git a/walletkit_client.go b/walletkit_client.go index 682cdab..088d7c3 100644 --- a/walletkit_client.go +++ b/walletkit_client.go @@ -101,6 +101,10 @@ type WalletKitClient interface { addressType walletrpc.AddressType, change bool) (btcutil.Address, error) + // GetTransaction returns details for a transaction found in the wallet. + GetTransaction(ctx context.Context, + txid chainhash.Hash) (Transaction, error) + PublishTransaction(ctx context.Context, tx *wire.MsgTx, label string) error @@ -481,6 +485,26 @@ func (m *walletKitClient) NextAddr(ctx context.Context, accountName string, return addr, nil } +// GetTransaction returns details for a transaction found in the wallet. +func (m *walletKitClient) GetTransaction(ctx context.Context, + txid chainhash.Hash) (Transaction, error) { + + rpcCtx, cancel := context.WithTimeout(ctx, m.timeout) + defer cancel() + + rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) + + req := &walletrpc.GetTransactionRequest{ + Txid: txid.String(), + } + resp, err := m.client.GetTransaction(rpcCtx, req) + if err != nil { + return Transaction{}, err + } + + return unmarshallTransaction(resp) +} + func (m *walletKitClient) PublishTransaction(ctx context.Context, tx *wire.MsgTx, label string) error { From baeb3687035914f555e2c4e2c49c938866e5a37f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:44:03 +0000 Subject: [PATCH 06/12] build(deps): bump github.com/go-viper/mapstructure/v2 --- updated-dependencies: - dependency-name: github.com/go-viper/mapstructure/v2 dependency-version: 2.3.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 44d0587..1ac2424 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect diff --git a/go.sum b/go.sum index d835826..099194e 100644 --- a/go.sum +++ b/go.sum @@ -165,8 +165,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= From cb46084eaf2c350e9c3887a89bed26b30b64c766 Mon Sep 17 00:00:00 2001 From: Andras Banki-Horvath Date: Fri, 27 Jun 2025 20:08:29 +0200 Subject: [PATCH 07/12] lndclient: make notifier options exported (to help mocking) --- chainnotifier_client.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/chainnotifier_client.go b/chainnotifier_client.go index 0c7b088..37d7b74 100644 --- a/chainnotifier_client.go +++ b/chainnotifier_client.go @@ -13,36 +13,36 @@ import ( "google.golang.org/grpc" ) -// notifierOptions is a set of functional options that allow callers to further +// NotifierOptions is a set of functional options that allow callers to further // modify the type of chain even notifications they receive. -type notifierOptions struct { - // includeBlock if true, then the dispatched confirmation notification +type NotifierOptions struct { + // IncludeBlock if true, then the dispatched confirmation notification // will include the block that mined the transaction. - includeBlock bool + IncludeBlock bool - // reOrgChan if set, will be sent on if the transaction is re-organized + // ReOrgChan if set, will be sent on if the transaction is re-organized // out of the chain. This channel being set will also imply that we // don't cancel the notification listener after having received one // confirmation event. That means the caller manually needs to cancel // the passed in context to cancel being notified once the required // number of confirmations have been reached. - reOrgChan chan struct{} + ReOrgChan chan struct{} } // defaultNotifierOptions returns the set of default options for the notifier. -func defaultNotifierOptions() *notifierOptions { - return ¬ifierOptions{} +func DefaultNotifierOptions() *NotifierOptions { + return &NotifierOptions{} } // NotifierOption is a functional option that allows a caller to modify the // events received from the notifier. -type NotifierOption func(*notifierOptions) +type NotifierOption func(*NotifierOptions) // WithIncludeBlock is an optional argument that allows the caller to specify // that the block that mined a transaction should be included in the response. func WithIncludeBlock() NotifierOption { - return func(o *notifierOptions) { - o.includeBlock = true + return func(o *NotifierOptions) { + o.IncludeBlock = true } } @@ -53,8 +53,8 @@ func WithIncludeBlock() NotifierOption { // to cancel being notified once the required number of confirmations have been // reached. func WithReOrgChan(reOrgChan chan struct{}) NotifierOption { - return func(o *notifierOptions) { - o.reOrgChan = reOrgChan + return func(o *NotifierOptions) { + o.ReOrgChan = reOrgChan } } @@ -191,7 +191,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, optFuncs ...NotifierOption) (chan *chainntnfs.TxConfirmation, chan error, error) { - opts := defaultNotifierOptions() + opts := DefaultNotifierOptions() for _, optFunc := range optFuncs { optFunc(opts) } @@ -206,7 +206,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, NumConfs: uint32(numConfs), HeightHint: uint32(heightHint), Txid: txidSlice, - IncludeBlock: opts.includeBlock, + IncludeBlock: opts.IncludeBlock, }, ) if err != nil { @@ -238,7 +238,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, } var block *wire.MsgBlock - if opts.includeBlock { + if opts.IncludeBlock { block, err = decodeBlock( c.Conf.RawBlock, ) @@ -268,7 +268,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, // we don't return here, since we might want to // be informed about the new block we got // confirmed in after a re-org. - if opts.reOrgChan == nil { + if opts.ReOrgChan == nil { return } @@ -276,9 +276,9 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, // any additional information. But we only signal if the // caller requested to be notified about re-orgs. case *chainrpc.ConfEvent_Reorg: - if opts.reOrgChan != nil { + if opts.ReOrgChan != nil { select { - case opts.reOrgChan <- struct{}{}: + case opts.ReOrgChan <- struct{}{}: case <-ctx.Done(): return } From 543ff97c49a5a272b89c34448d860aa15d3ba1e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 07:57:57 +0000 Subject: [PATCH 08/12] build(deps): bump github.com/go-viper/mapstructure/v2 in /tools Bumps [github.com/go-viper/mapstructure/v2](https://github.com/go-viper/mapstructure) from 2.2.1 to 2.3.0. - [Release notes](https://github.com/go-viper/mapstructure/releases) - [Changelog](https://github.com/go-viper/mapstructure/blob/main/CHANGELOG.md) - [Commits](https://github.com/go-viper/mapstructure/compare/v2.2.1...v2.3.0) --- updated-dependencies: - dependency-name: github.com/go-viper/mapstructure/v2 dependency-version: 2.3.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 4b0e23b..530bcae 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -61,7 +61,7 @@ require ( github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.12.1 // indirect diff --git a/tools/go.sum b/tools/go.sum index 491f84e..e5364d8 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -191,8 +191,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= From 3e14d695c320fc3c8b22b905f88348297f709b72 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sat, 28 Jun 2025 18:25:11 -0300 Subject: [PATCH 09/12] GetInfo: add more fields to response Add fields: CommitHash, BestBlockHash, Color, NumPeers. --- lightning_client.go | 37 +++++++++++++++++++++++++++++++++---- lnd_services_test.go | 1 + 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/lightning_client.go b/lightning_client.go index 95c6e71..534f123 100644 --- a/lightning_client.go +++ b/lightning_client.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "errors" "fmt" + "image/color" "io" "sync" "time" @@ -16,6 +17,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" invpkg "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntypes" @@ -316,15 +318,27 @@ type Info struct { // Version is the version that lnd is running. Version string + // CommitHash is the SHA1 commit hash that the daemon is compiled with. + CommitHash string + // BlockHeight is the best block height that lnd has knowledge of. BlockHeight uint32 + // BestHeaderTimeStamp is the best block timestamp known to the wallet. + BestHeaderTimeStamp time.Time + + // BestBlockHash is the node's view of the hash of the best block. + BestBlockHash chainhash.Hash + // IdentityPubkey is our node's pubkey. IdentityPubkey [33]byte // Alias is our node's alias. Alias string + // Color is the color of the current node in RGB format. + Color color.RGBA + // Network is the network we are currently operating on. Network string @@ -339,9 +353,6 @@ type Info struct { // public channel graph. SyncedToGraph bool - // BestHeaderTimeStamp is the best block timestamp known to the wallet. - BestHeaderTimeStamp time.Time - // ActiveChannels is the number of active channels we have. ActiveChannels uint32 @@ -350,6 +361,9 @@ type Info struct { // PendingChannels is the number of pending channels we have. PendingChannels uint32 + + // NumPeers is the number of peers we connect to. + NumPeers uint32 } // ChannelInfo stores unpacked per-channel info. @@ -1419,19 +1433,34 @@ func newInfo(resp *lnrpc.GetInfoResponse) (*Info, error) { var pubKeyArray [33]byte copy(pubKeyArray[:], pubKey) + bestBlockHash, err := chainhash.NewHashFromStr(resp.BlockHash) + if err != nil { + return nil, fmt.Errorf("failed to parse BlockHash: %w", err) + } + + color, err := lncfg.ParseHexColor(resp.Color) + if err != nil { + return nil, fmt.Errorf("failed to parse color hex %q: %w", + resp.Color, err) + } + return &Info{ Version: resp.Version, + CommitHash: resp.CommitHash, BlockHeight: resp.BlockHeight, + BestHeaderTimeStamp: time.Unix(resp.BestHeaderTimestamp, 0), + BestBlockHash: *bestBlockHash, IdentityPubkey: pubKeyArray, Alias: resp.Alias, + Color: color, Network: resp.Chains[0].Network, Uris: resp.Uris, SyncedToChain: resp.SyncedToChain, SyncedToGraph: resp.SyncedToGraph, - BestHeaderTimeStamp: time.Unix(resp.BestHeaderTimestamp, 0), ActiveChannels: resp.NumActiveChannels, InactiveChannels: resp.NumInactiveChannels, PendingChannels: resp.NumPendingChannels, + NumPeers: resp.NumPeers, }, nil } diff --git a/lnd_services_test.go b/lnd_services_test.go index 401501b..708bbfc 100644 --- a/lnd_services_test.go +++ b/lnd_services_test.go @@ -197,6 +197,7 @@ func (l *lockLNDMock) GetInfo(ctx context.Context, _ *lnrpc.GetInfoRequest, return &lnrpc.GetInfoResponse{ Chains: []*lnrpc.Chain{{}}, + Color: "#112233", }, err } From 692014f74effe0d110fd7d0411c0bce584118573 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Tue, 8 Jul 2025 20:00:32 -0300 Subject: [PATCH 10/12] RegisterSpendNtfn: support reorg channel The corresponding API of LND already supports notifying the caller about a reorg. I reused NotifierOption's used for RegisterConfirmationsNtfn already to pass a channel notified upon a reorg affecting a reported confirmation. --- chainnotifier_client.go | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/chainnotifier_client.go b/chainnotifier_client.go index 37d7b74..63ed4c7 100644 --- a/chainnotifier_client.go +++ b/chainnotifier_client.go @@ -71,8 +71,9 @@ type ChainNotifierClient interface { chan error, error) RegisterSpendNtfn(ctx context.Context, - outpoint *wire.OutPoint, pkScript []byte, heightHint int32) ( - chan *chainntnfs.SpendDetail, chan error, error) + outpoint *wire.OutPoint, pkScript []byte, heightHint int32, + optFuncs ...NotifierOption) (chan *chainntnfs.SpendDetail, + chan error, error) } type chainNotifierClient struct { @@ -111,8 +112,18 @@ func (s *chainNotifierClient) RawClientWithMacAuth( } func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, - outpoint *wire.OutPoint, pkScript []byte, heightHint int32) ( - chan *chainntnfs.SpendDetail, chan error, error) { + outpoint *wire.OutPoint, pkScript []byte, heightHint int32, + optFuncs ...NotifierOption) (chan *chainntnfs.SpendDetail, chan error, + error) { + + opts := DefaultNotifierOptions() + for _, optFunc := range optFuncs { + optFunc(opts) + } + if opts.IncludeBlock { + return nil, nil, fmt.Errorf("option IncludeBlock is not " + + "supported by RegisterSpendNtfn") + } var rpcOutpoint *chainrpc.Outpoint if outpoint != nil { @@ -162,6 +173,18 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, return nil } + processReorg := func() { + if opts.ReOrgChan == nil { + return + } + + select { + case opts.ReOrgChan <- struct{}{}: + case <-ctx.Done(): + return + } + } + s.wg.Add(1) go func() { defer s.wg.Done() @@ -172,12 +195,35 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, return } - c, ok := spendEvent.Event.(*chainrpc.SpendEvent_Spend) - if ok { + switch c := spendEvent.Event.(type) { + case *chainrpc.SpendEvent_Spend: err := processSpendDetail(c.Spend) if err != nil { errChan <- err + + return } + + // If we're running in re-org aware mode, then + // we don't return here, since we might want to + // be informed about the new block we got + // confirmed in after a re-org. + if opts.ReOrgChan == nil { + return + } + + case *chainrpc.SpendEvent_Reorg: + processReorg() + + // Nil event, should never happen. + case nil: + errChan <- fmt.Errorf("spend event empty") + return + + // Unexpected type. + default: + errChan <- fmt.Errorf("spend event has " + + "unexpected type") return } } From 4eca8b789a76843f559fcc0ca0d5c2833b841e56 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Wed, 9 Jul 2025 03:01:57 -0300 Subject: [PATCH 11/12] listen for ctx expiry when sending spend and conf Avoid a deadlock in case the caller doesn't consume notifications about spending or confirmations after cancelling a call. --- chainnotifier_client.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/chainnotifier_client.go b/chainnotifier_client.go index 63ed4c7..4e589f6 100644 --- a/chainnotifier_client.go +++ b/chainnotifier_client.go @@ -159,7 +159,7 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, if err != nil { return err } - spendChan <- &chainntnfs.SpendDetail{ + spend := &chainntnfs.SpendDetail{ SpentOutPoint: &wire.OutPoint{ Hash: *outpointHash, Index: d.SpendingOutpoint.Index, @@ -170,7 +170,12 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, SpendingHeight: int32(d.SpendingHeight), } - return nil + select { + case spendChan <- spend: + return nil + case <-ctx.Done(): + return ctx.Err() + } } processReorg := func() { @@ -302,7 +307,7 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, return } - confChan <- &chainntnfs.TxConfirmation{ + conf := &chainntnfs.TxConfirmation{ BlockHeight: c.Conf.BlockHeight, BlockHash: blockHash, Tx: tx, @@ -310,6 +315,12 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, Block: block, } + select { + case confChan <- conf: + case <-ctx.Done(): + return + } + // If we're running in re-org aware mode, then // we don't return here, since we might want to // be informed about the new block we got From cf534c9968b94e7e1b067aab5963c658871e9942 Mon Sep 17 00:00:00 2001 From: Andras Banki-Horvath Date: Fri, 23 May 2025 10:21:43 +0200 Subject: [PATCH 12/12] walletkit: add SubmitPackage --- go.mod | 6 +++++ go.sum | 60 +++++++++++++++++++++++++++++++-------------- walletkit_client.go | 24 ++++++++++++++++++ 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 1ac2424..b001083 100644 --- a/go.mod +++ b/go.mod @@ -195,4 +195,10 @@ require ( // allows us to specify that as an option. replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display +replace github.com/lightningnetwork/lnd => github.com/bhandras/lnd v0.8.0-beta-rc3.0.20250523081420-f8b9f36a39d1 + +replace github.com/btcsuite/btcwallet => github.com/bhandras/btcwallet v0.11.1-0.20250507171803-0de1c46b1cfc + +replace github.com/btcsuite/btcd => github.com/bhandras/btcd v0.22.0-beta.0.20250507171227-f18160c86e92 + go 1.23.6 diff --git a/go.sum b/go.sum index 099194e..de31d1a 100644 --- a/go.sum +++ b/go.sum @@ -31,17 +31,15 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6 h1:8n9k3I7e8DkpdQ5YAP4j8ly/LSsbe6qX9vmVbrUGvVw= -github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6/go.mod h1:OmM4kFtB0klaG/ZqT86rQiyw/1iyXlJgc3UHClPhhbs= -github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/bhandras/btcd v0.22.0-beta.0.20250507171227-f18160c86e92 h1:/H5Dv5VoKqsXkI3iZc6D3xonibJS6YdPHgAa2XkFHi0= +github.com/bhandras/btcd v0.22.0-beta.0.20250507171227-f18160c86e92/go.mod h1:OmM4kFtB0klaG/ZqT86rQiyw/1iyXlJgc3UHClPhhbs= +github.com/bhandras/btcwallet v0.11.1-0.20250507171803-0de1c46b1cfc h1:RvT6udxYM857Kvj5fEkWhTo0wAT0t7R7oOgYSoLJOLY= +github.com/bhandras/btcwallet v0.11.1-0.20250507171803-0de1c46b1cfc/go.mod h1:PZ4WgE93vP5TBchtfrlvf5GT6P9ul0tM8rTH1BSYloo= +github.com/bhandras/lnd v0.8.0-beta-rc3.0.20250523081420-f8b9f36a39d1 h1:pTuSODRpgPrFi1BdgwK4SXmAOFQ7YVw/G7nqwVxcpOg= +github.com/bhandras/lnd v0.8.0-beta-rc3.0.20250523081420-f8b9f36a39d1/go.mod h1:1MLaoAsITndKIurbKLJqAPTO9ztZjkgDuSTZgyHaoVo= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= -github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= @@ -55,9 +53,6 @@ github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c h1:4HxD1lBUGUddhzg github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xeHN2y3TQ= github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 h1:oCjIcinPt7XQ644MP/22JcjYEC84qRc3bRBH0d7Hhd4= github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.13 h1:JGu+wrihQ0I00ODb3w92JtBPbrHxZhbcvU01O+e+lKw= -github.com/btcsuite/btcwallet v0.16.13/go.mod h1:H6dfoZcWPonM2wbVsR2ZBY0PKNZKdQyLAmnX8vL9JFA= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= @@ -72,9 +67,7 @@ github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JG github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= @@ -353,8 +346,6 @@ github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display h1:Y2WiPkBS github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= -github.com/lightningnetwork/lnd v0.19.0-beta h1:/8i2UdARiEpI2iAmPoSDcwZSSEuWqXyfsMxz/mLGbdw= -github.com/lightningnetwork/lnd v0.19.0-beta/go.mod h1:hu6zo1zcznx7nViiFlJY8qGDwwGw5LNLdGJ7ICz5Ysc= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= github.com/lightningnetwork/lnd/fn/v2 v2.0.8 h1:r2SLz7gZYQPVc3IZhU82M66guz3Zk2oY+Rlj9QN5S3g= @@ -412,13 +403,10 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= @@ -484,6 +472,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -491,6 +481,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -512,6 +505,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= @@ -580,6 +574,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -593,9 +590,10 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -615,6 +613,11 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -630,6 +633,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -661,12 +666,23 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -675,6 +691,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -693,6 +713,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/walletkit_client.go b/walletkit_client.go index 088d7c3..30dbdee 100644 --- a/walletkit_client.go +++ b/walletkit_client.go @@ -216,6 +216,14 @@ type WalletKitClient interface { // currently supported. ImportTaprootScript(ctx context.Context, tapscript *waddrmgr.Tapscript) (btcutil.Address, error) + + // SubmitPackage attempts to broadcast a transaction package, consisting + // of one or more parent transactions and exactly one child transaction. + // The package is submitted to the backend node's mempool atomically. + // This RPC is primarily used for Child-Pays-For-Parent (CPFP) fee + // bumping. + SubmitPackage(ctx context.Context, req *walletrpc.SubmitPackageRequest) ( + *walletrpc.SubmitPackageResponse, error) } type walletKitClient struct { @@ -1110,3 +1118,19 @@ func (m *walletKitClient) ImportTaprootScript(ctx context.Context, return p2trAddr, nil } + +// SubmitPackage attempts to broadcast a transaction package, consisting of one +// or more parent transactions and exactly one child transaction. The package is +// submitted to the backend node's mempool atomically. This RPC is primarily +// used for Child-Pays-For-Parent (CPFP) fee bumping. +func (m *walletKitClient) SubmitPackage(ctx context.Context, + req *walletrpc.SubmitPackageRequest) (*walletrpc.SubmitPackageResponse, + error) { + + rpcCtx, cancel := context.WithTimeout(ctx, m.timeout) + defer cancel() + + return m.client.SubmitPackage( + m.walletKitMac.WithMacaroonAuth(rpcCtx), req, + ) +}