Skip to content

Commit d7d47d0

Browse files
authored
Merge pull request #1264 from ellemouton/submitpackage
chain: add SubmitPackage to the chain.Interface
2 parents 41c1654 + 11063ac commit d7d47d0

5 files changed

Lines changed: 65 additions & 0 deletions

File tree

chain/bitcoind_client.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,30 @@ func (c *BitcoindClient) TestMempoolAccept(txns []*wire.MsgTx,
257257
return c.chainConn.client.TestMempoolAccept(txns, maxFeeRate)
258258
}
259259

260+
// SubmitPackage submits a package of related transactions (topologically
261+
// sorted, parents first and child last) to bitcoind's mempool for atomic
262+
// validation and acceptance via the submitpackage RPC. This is what allows a
263+
// zero-fee v3/TRUC parent to be accepted when paired with a fee-paying CPFP
264+
// child, which sendrawtransaction (single-tx) rejects.
265+
//
266+
// maxFeeRate is the optional per-tx fee-rate ceiling in BTC/kvB (pass a
267+
// pointer to 0 to disable the limit for high-feerate CPFP children).
268+
//
269+
// NOTE: This is part of the chain.Interface interface.
270+
func (c *BitcoindClient) SubmitPackage(txns []*wire.MsgTx,
271+
maxFeeRate *float64) (*btcjson.SubmitPackageResult, error) {
272+
273+
// The rpcclient SubmitPackage exposes a maxBurnAmount limit too, which
274+
// we don't surface on chain.Interface; pass nil to use the node
275+
// default.
276+
result, err := c.chainConn.client.SubmitPackage(txns, maxFeeRate, nil)
277+
if err != nil {
278+
return nil, c.MapRPCErr(err)
279+
}
280+
281+
return result, nil
282+
}
283+
260284
// Notifications returns a channel to retrieve notifications from.
261285
//
262286
// NOTE: This is part of the chain.Interface interface.

chain/btcd.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,19 @@ func (c *RPCClient) BackEnd() string {
204204
return "btcd"
205205
}
206206

207+
// SubmitPackage is unimplemented for the btcd backend. btcd defines the
208+
// submitpackage JSON-RPC command type but its RPC server registers no handler
209+
// for it, so a RawRequest would fail with a method-not-found error. Returning
210+
// ErrUnimplemented avoids advertising support that does not exist; a btcd
211+
// server-side submitpackage handler must land before this can do anything.
212+
//
213+
// NOTE: This is part of the chain.Interface interface.
214+
func (c *RPCClient) SubmitPackage(_ []*wire.MsgTx,
215+
_ *float64) (*btcjson.SubmitPackageResult, error) {
216+
217+
return nil, ErrUnimplemented
218+
}
219+
207220
// Start attempts to establish a client connection with the remote server.
208221
// If successful, handler goroutines are started to process notifications
209222
// sent by the server. After a limited number of connection attempts, this

chain/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type Interface interface {
5454
Notifications() <-chan interface{}
5555
BackEnd() string
5656
TestMempoolAccept([]*wire.MsgTx, float64) ([]*btcjson.TestMempoolAcceptResult, error)
57+
SubmitPackage(txns []*wire.MsgTx,
58+
maxFeeRate *float64) (*btcjson.SubmitPackageResult, error)
5759
MapRPCErr(err error) error
5860
}
5961

chain/neutrino.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,25 @@ func (s *NeutrinoClient) TestMempoolAccept(txns []*wire.MsgTx,
239239
return nil, ErrUnimplemented
240240
}
241241

242+
// SubmitPackage is unimplemented for the neutrino backend, mirroring
243+
// TestMempoolAccept: a light client has no mempool and cannot run the
244+
// submitpackage RPC, so it can neither validate nor atomically accept a
245+
// package, nor report whether one was accepted.
246+
//
247+
// A zero-fee parent paired with a fee-paying CPFP child can still reach the
248+
// network over neutrino by broadcasting each transaction individually (parents
249+
// first) via SendRawTransaction and relying on a peer's P2P 1p1c package
250+
// relay. That is a best-effort propagation step with no acceptance signal,
251+
// distinct from submitpackage's submit-for-acceptance contract, so it is left
252+
// to the caller rather than conflated with this method.
253+
//
254+
// NOTE: This is part of the chain.Interface interface.
255+
func (s *NeutrinoClient) SubmitPackage(_ []*wire.MsgTx,
256+
_ *float64) (*btcjson.SubmitPackageResult, error) {
257+
258+
return nil, ErrUnimplemented
259+
}
260+
242261
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
243262
// addresses of interest. For each requested block, the corresponding compact
244263
// filter will first be checked for matches, skipping those that do not report

wallet/mock.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ func (m *mockChainClient) TestMempoolAccept(txns []*wire.MsgTx,
103103
return nil, nil
104104
}
105105

106+
// SubmitPackage is part of the chain.Interface interface.
107+
func (m *mockChainClient) SubmitPackage(txns []*wire.MsgTx,
108+
maxFeeRate *float64) (*btcjson.SubmitPackageResult, error) {
109+
110+
return &btcjson.SubmitPackageResult{}, nil
111+
}
112+
106113
func (m *mockChainClient) MapRPCErr(err error) error {
107114
return nil
108115
}

0 commit comments

Comments
 (0)