mirror of
https://github.com/btcsuite/btcd.git
synced 2025-03-27 02:37:10 +01:00
rpcclient+integration: add new method GetTxSpendingPrevOut
This commit is contained in:
parent
5f71df165e
commit
a033b0d6e7
2 changed files with 214 additions and 0 deletions
146
integration/chain_test.go
Normal file
146
integration/chain_test.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
//go:build rpctest
|
||||
// +build rpctest
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/integration/rpctest"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGetTxSpendingPrevOut checks that `GetTxSpendingPrevOut` behaves as
|
||||
// expected.
|
||||
// - an error is returned when invalid params are used.
|
||||
// - orphan tx is rejected.
|
||||
// - fee rate above the max is rejected.
|
||||
// - a mixed of both allowed and rejected can be returned in the same response.
|
||||
func TestGetTxSpendingPrevOut(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Boilerplate codetestDir to make a pruned node.
|
||||
btcdCfg := []string{"--rejectnonstd", "--debuglevel=debug"}
|
||||
r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup the node.
|
||||
require.NoError(t, r.SetUp(true, 100))
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, r.TearDown())
|
||||
})
|
||||
|
||||
// Create a tx and testing outpoints.
|
||||
tx := createTxInMempool(t, r)
|
||||
opInMempool := tx.TxIn[0].PreviousOutPoint
|
||||
opNotInMempool := wire.OutPoint{
|
||||
Hash: tx.TxHash(),
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
outpoints []wire.OutPoint
|
||||
expectedErr error
|
||||
expectedResult []*btcjson.GetTxSpendingPrevOutResult
|
||||
}{
|
||||
{
|
||||
// When no outpoints are provided, the method should
|
||||
// return an error.
|
||||
name: "empty outpoints",
|
||||
expectedErr: rpcclient.ErrInvalidParam,
|
||||
expectedResult: nil,
|
||||
},
|
||||
{
|
||||
// When there are outpoints provided, check the
|
||||
// expceted results are returned.
|
||||
name: "outpoints",
|
||||
outpoints: []wire.OutPoint{
|
||||
opInMempool, opNotInMempool,
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedResult: []*btcjson.GetTxSpendingPrevOutResult{
|
||||
{
|
||||
Txid: opInMempool.Hash.String(),
|
||||
Vout: opInMempool.Index,
|
||||
SpendingTxid: tx.TxHash().String(),
|
||||
},
|
||||
{
|
||||
Txid: opNotInMempool.Hash.String(),
|
||||
Vout: opNotInMempool.Index,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
results, err := r.Client.GetTxSpendingPrevOut(
|
||||
tc.outpoints,
|
||||
)
|
||||
|
||||
require.ErrorIs(err, tc.expectedErr)
|
||||
require.Len(results, len(tc.expectedResult))
|
||||
|
||||
// Check each item is returned as expected.
|
||||
for i, r := range results {
|
||||
e := tc.expectedResult[i]
|
||||
|
||||
require.Equal(e.Txid, r.Txid)
|
||||
require.Equal(e.Vout, r.Vout)
|
||||
require.Equal(e.SpendingTxid, r.SpendingTxid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// createTxInMempool creates a tx and puts it in the mempool.
|
||||
func createTxInMempool(t *testing.T, r *rpctest.Harness) *wire.MsgTx {
|
||||
// Create a fresh output for usage within the test below.
|
||||
const outputValue = btcutil.SatoshiPerBitcoin
|
||||
outputKey, testOutput, testPkScript, err := makeTestOutput(
|
||||
r, t, outputValue,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a new transaction with a lock-time past the current known
|
||||
// MTP.
|
||||
tx := wire.NewMsgTx(1)
|
||||
tx.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: *testOutput,
|
||||
})
|
||||
|
||||
// Fetch a fresh address from the harness, we'll use this address to
|
||||
// send funds back into the Harness.
|
||||
addr, err := r.NewAddress()
|
||||
require.NoError(t, err)
|
||||
|
||||
addrScript, err := txscript.PayToAddrScript(addr)
|
||||
require.NoError(t, err)
|
||||
|
||||
tx.AddTxOut(&wire.TxOut{
|
||||
PkScript: addrScript,
|
||||
Value: outputValue - 1000,
|
||||
})
|
||||
|
||||
sigScript, err := txscript.SignatureScript(
|
||||
tx, 0, testPkScript, txscript.SigHashAll, outputKey, true,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
tx.TxIn[0].SignatureScript = sigScript
|
||||
|
||||
// Send the tx.
|
||||
_, err = r.Client.SendRawTransaction(tx, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tx
|
||||
}
|
|
@ -1012,3 +1012,71 @@ func (c *Client) TestMempoolAccept(txns []*wire.MsgTx,
|
|||
|
||||
return c.TestMempoolAcceptAsync(txns, maxFeeRate).Receive()
|
||||
}
|
||||
|
||||
// FutureGetTxSpendingPrevOut is a future promise to deliver the result of a
|
||||
// GetTxSpendingPrevOut RPC invocation (or an applicable error).
|
||||
type FutureGetTxSpendingPrevOut chan *Response
|
||||
|
||||
// Receive waits for the Response promised by the future and returns the
|
||||
// response from GetTxSpendingPrevOut.
|
||||
func (r FutureGetTxSpendingPrevOut) Receive() (
|
||||
[]*btcjson.GetTxSpendingPrevOutResult, error) {
|
||||
|
||||
response, err := ReceiveFuture(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Unmarshal as an array of GetTxSpendingPrevOutResult items.
|
||||
var results []*btcjson.GetTxSpendingPrevOutResult
|
||||
|
||||
err = json.Unmarshal(response, &results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetTxSpendingPrevOutAsync returns an instance of a type that can be used to
|
||||
// get the result of the RPC at some future time by invoking the Receive
|
||||
// function on the returned instance.
|
||||
//
|
||||
// See GetTxSpendingPrevOut for the blocking version and more details.
|
||||
func (c *Client) GetTxSpendingPrevOutAsync(
|
||||
outpoints []wire.OutPoint) FutureGetTxSpendingPrevOut {
|
||||
|
||||
// Due to differences in the testmempoolaccept API for different
|
||||
// backends, we'll need to inspect our version and construct the
|
||||
// appropriate request.
|
||||
version, err := c.BackendVersion()
|
||||
if err != nil {
|
||||
return newFutureError(err)
|
||||
}
|
||||
|
||||
log.Debugf("GetTxSpendingPrevOutAsync: backend version %s", version)
|
||||
|
||||
// Exit early if the version is below 24.0.0.
|
||||
if version < BitcoindPre24 {
|
||||
err := fmt.Errorf("%w: %v", ErrBitcoindVersion, version)
|
||||
return newFutureError(err)
|
||||
}
|
||||
|
||||
// Exit early if an empty array of outpoints is provided.
|
||||
if len(outpoints) == 0 {
|
||||
err := fmt.Errorf("%w: no outpoints provided", ErrInvalidParam)
|
||||
return newFutureError(err)
|
||||
}
|
||||
|
||||
cmd := btcjson.NewGetTxSpendingPrevOutCmd(outpoints)
|
||||
|
||||
return c.SendCmd(cmd)
|
||||
}
|
||||
|
||||
// GetTxSpendingPrevOut returns the result from calling `gettxspendingprevout`
|
||||
// RPC.
|
||||
func (c *Client) GetTxSpendingPrevOut(outpoints []wire.OutPoint) (
|
||||
[]*btcjson.GetTxSpendingPrevOutResult, error) {
|
||||
|
||||
return c.GetTxSpendingPrevOutAsync(outpoints).Receive()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue