diff --git a/chain/bitcoind_client.go b/chain/bitcoind_client.go index 4082b729df..a2537da617 100644 --- a/chain/bitcoind_client.go +++ b/chain/bitcoind_client.go @@ -1400,3 +1400,11 @@ func (c *BitcoindClient) filterTx(txDetails *btcutil.Tx, return true, rec, nil } + +// LookupInputMempoolSpend returns the transaction hash and true if the given +// input is found being spent in mempool, otherwise it returns nil and false. +func (c *BitcoindClient) LookupInputMempoolSpend(op wire.OutPoint) ( + chainhash.Hash, bool) { + + return c.chainConn.events.LookupInputSpend(op) +} diff --git a/chain/bitcoind_events_test.go b/chain/bitcoind_events_test.go index fc9f8b8f8c..fe8eda0cd4 100644 --- a/chain/bitcoind_events_test.go +++ b/chain/bitcoind_events_test.go @@ -67,6 +67,9 @@ func TestBitcoindEvents(t *testing.T) { // mempool. btcClient = setupBitcoind(t, addr, test.rpcPolling) testNotifySpentMempool(t, miner1, btcClient) + + // Test looking up mempool for input spent. + testLookupInputMempoolSpend(t, miner1, btcClient) }) } } @@ -214,6 +217,46 @@ func testNotifySpentMempool(t *testing.T, miner *rpctest.Harness, } } +// testLookupInputMempoolSpend tests that LookupInputMempoolSpend returns the +// correct tx hash and whether the input has been spent in the mempool. +func testLookupInputMempoolSpend(t *testing.T, miner *rpctest.Harness, + client *BitcoindClient) { + + require := require.New(t) + + script, _, err := randPubKeyHashScript() + require.NoError(err) + + // Create a test tx. + tx, err := miner.CreateTransaction( + []*wire.TxOut{{Value: 1000, PkScript: script}}, 5, false, + ) + require.NoError(err) + + // Lookup the input in mempool. + op := tx.TxIn[0].PreviousOutPoint + txid, found := client.LookupInputMempoolSpend(op) + + // Expect that the input has not been spent in the mempool. + require.False(found) + require.Zero(txid) + + // Send the tx which will put it in the mempool. + _, err = client.SendRawTransaction(tx, true) + require.NoError(err) + + // Lookup the input again should return the spending tx. + // + // NOTE: We need to wait for the tx to propagate to the mempool. + require.Eventually(func() bool { + txid, found = client.LookupInputMempoolSpend(op) + return found + }, 5*time.Second, 100*time.Millisecond) + + // Check the expected txid is returned. + require.Equal(tx.TxHash(), txid) +} + // testReorg tests that the given BitcoindClient correctly responds to a chain // re-org. func testReorg(t *testing.T, miner1, miner2 *rpctest.Harness, diff --git a/chain/btcd.go b/chain/btcd.go index 4ddb183c40..d36b91884d 100644 --- a/chain/btcd.go +++ b/chain/btcd.go @@ -465,3 +465,11 @@ func (c *RPCClient) POSTClient() (*rpcclient.Client, error) { configCopy.HTTPPostMode = true return rpcclient.New(&configCopy, nil) } + +// LookupInputMempoolSpend returns the transaction hash and true if the given +// input is found being spent in mempool, otherwise it returns nil and false. +func (c *RPCClient) LookupInputMempoolSpend(op wire.OutPoint) ( + chainhash.Hash, bool) { + + return getTxSpendingPrevOut(op, c.Client) +}