From 50650d054ed86281d6ce7a81f0d70c30f74f52cc Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 10 Dec 2018 18:24:04 -0800 Subject: [PATCH] chainntnfs: include transaction in confirmation details --- chainntnfs/bitcoindnotify/bitcoind.go | 49 ++++++++++++++++++-------- chainntnfs/btcdnotify/btcd.go | 50 ++++++++++++++++++--------- chainntnfs/interface.go | 3 ++ chainntnfs/neutrinonotify/neutrino.go | 1 + chainntnfs/txnotifier.go | 1 + chainntnfs/txnotifier_test.go | 10 ++++++ 6 files changed, 83 insertions(+), 31 deletions(-) diff --git a/chainntnfs/bitcoindnotify/bitcoind.go b/chainntnfs/bitcoindnotify/bitcoind.go index 19465e32..10551e57 100644 --- a/chainntnfs/bitcoindnotify/bitcoind.go +++ b/chainntnfs/bitcoindnotify/bitcoind.go @@ -1,6 +1,8 @@ package bitcoindnotify import ( + "bytes" + "encoding/hex" "errors" "fmt" "strings" @@ -462,7 +464,7 @@ func (b *BitcoindNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // If the transaction has some or all of its confirmations required, // then we may be able to dispatch it immediately. - tx, err := b.chainConn.GetRawTransactionVerbose(txid) + rawTxRes, err := b.chainConn.GetRawTransactionVerbose(txid) if err != nil { // If the transaction lookup was successful, but it wasn't found // within the index itself, then we can exit early. We'll also @@ -483,18 +485,18 @@ func (b *BitcoindNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // Make sure we actually retrieved a transaction that is included in a // block. If not, the transaction must be unconfirmed (in the mempool), // and we'll return TxFoundMempool together with a nil TxConfirmation. - if tx.BlockHash == "" { + if rawTxRes.BlockHash == "" { return nil, chainntnfs.TxFoundMempool, nil } // As we need to fully populate the returned TxConfirmation struct, // grab the block in which the transaction was confirmed so we can // locate its exact index within the block. - blockHash, err := chainhash.NewHashFromStr(tx.BlockHash) + blockHash, err := chainhash.NewHashFromStr(rawTxRes.BlockHash) if err != nil { return nil, chainntnfs.TxNotFoundIndex, fmt.Errorf("unable to get block hash %v for "+ - "historical dispatch: %v", tx.BlockHash, err) + "historical dispatch: %v", rawTxRes.BlockHash, err) } block, err := b.chainConn.GetBlockVerbose(blockHash) @@ -506,23 +508,39 @@ func (b *BitcoindNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // If the block was obtained, locate the transaction's index within the // block so we can give the subscriber full confirmation details. - targetTxidStr := txid.String() + txidStr := txid.String() for txIndex, txHash := range block.Tx { - if txHash == targetTxidStr { - details := &chainntnfs.TxConfirmation{ - BlockHash: blockHash, - BlockHeight: uint32(block.Height), - TxIndex: uint32(txIndex), - } - return details, chainntnfs.TxFoundIndex, nil + if txHash != txidStr { + continue } + + // Deserialize the hex-encoded transaction to include it in the + // confirmation details. + rawTx, err := hex.DecodeString(rawTxRes.Hex) + if err != nil { + return nil, chainntnfs.TxFoundIndex, + fmt.Errorf("unable to deserialize tx %v: %v", + txHash, err) + } + var tx wire.MsgTx + if err := tx.Deserialize(bytes.NewReader(rawTx)); err != nil { + return nil, chainntnfs.TxFoundIndex, + fmt.Errorf("unable to deserialize tx %v: %v", + txHash, err) + } + + return &chainntnfs.TxConfirmation{ + Tx: &tx, + BlockHash: blockHash, + BlockHeight: uint32(block.Height), + TxIndex: uint32(txIndex), + }, chainntnfs.TxFoundIndex, nil } // We return an error because we should have found the transaction // within the block, but didn't. - return nil, chainntnfs.TxNotFoundIndex, - fmt.Errorf("unable to locate tx %v in block %v", txid, - blockHash) + return nil, chainntnfs.TxNotFoundIndex, fmt.Errorf("unable to locate "+ + "tx %v in block %v", txid, blockHash) } // confDetailsManually looks up whether a transaction/output script has already @@ -568,6 +586,7 @@ func (b *BitcoindNotifier) confDetailsManually(confRequest chainntnfs.ConfReques } return &chainntnfs.TxConfirmation{ + Tx: tx, BlockHash: blockHash, BlockHeight: height, TxIndex: uint32(txIndex), diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go index 8f7fe57f..1486f18b 100644 --- a/chainntnfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -1,6 +1,8 @@ package btcdnotify import ( + "bytes" + "encoding/hex" "errors" "fmt" "strings" @@ -514,7 +516,7 @@ func (b *BtcdNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // If the transaction has some or all of its confirmations required, // then we may be able to dispatch it immediately. - tx, err := b.chainConn.GetRawTransactionVerbose(txid) + rawTxRes, err := b.chainConn.GetRawTransactionVerbose(txid) if err != nil { // If the transaction lookup was successful, but it wasn't found // within the index itself, then we can exit early. We'll also @@ -535,20 +537,19 @@ func (b *BtcdNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // Make sure we actually retrieved a transaction that is included in a // block. If not, the transaction must be unconfirmed (in the mempool), // and we'll return TxFoundMempool together with a nil TxConfirmation. - if tx.BlockHash == "" { + if rawTxRes.BlockHash == "" { return nil, chainntnfs.TxFoundMempool, nil } // As we need to fully populate the returned TxConfirmation struct, // grab the block in which the transaction was confirmed so we can // locate its exact index within the block. - blockHash, err := chainhash.NewHashFromStr(tx.BlockHash) + blockHash, err := chainhash.NewHashFromStr(rawTxRes.BlockHash) if err != nil { return nil, chainntnfs.TxNotFoundIndex, fmt.Errorf("unable to get block hash %v for "+ - "historical dispatch: %v", tx.BlockHash, err) + "historical dispatch: %v", rawTxRes.BlockHash, err) } - block, err := b.chainConn.GetBlockVerbose(blockHash) if err != nil { return nil, chainntnfs.TxNotFoundIndex, @@ -558,23 +559,39 @@ func (b *BtcdNotifier) confDetailsFromTxIndex(txid *chainhash.Hash, // If the block was obtained, locate the transaction's index within the // block so we can give the subscriber full confirmation details. - targetTxidStr := txid.String() + txidStr := txid.String() for txIndex, txHash := range block.Tx { - if txHash == targetTxidStr { - details := &chainntnfs.TxConfirmation{ - BlockHash: blockHash, - BlockHeight: uint32(block.Height), - TxIndex: uint32(txIndex), - } - return details, chainntnfs.TxFoundIndex, nil + if txHash != txidStr { + continue } + + // Deserialize the hex-encoded transaction to include it in the + // confirmation details. + rawTx, err := hex.DecodeString(rawTxRes.Hex) + if err != nil { + return nil, chainntnfs.TxFoundIndex, + fmt.Errorf("unable to deserialize tx %v: %v", + txHash, err) + } + var tx wire.MsgTx + if err := tx.Deserialize(bytes.NewReader(rawTx)); err != nil { + return nil, chainntnfs.TxFoundIndex, + fmt.Errorf("unable to deserialize tx %v: %v", + txHash, err) + } + + return &chainntnfs.TxConfirmation{ + Tx: &tx, + BlockHash: blockHash, + BlockHeight: uint32(block.Height), + TxIndex: uint32(txIndex), + }, chainntnfs.TxFoundIndex, nil } // We return an error because we should have found the transaction // within the block, but didn't. - return nil, chainntnfs.TxNotFoundIndex, - fmt.Errorf("unable to locate tx %v in block %v", txid, - blockHash) + return nil, chainntnfs.TxNotFoundIndex, fmt.Errorf("unable to locate "+ + "tx %v in block %v", txid, blockHash) } // confDetailsManually looks up whether a transaction/output script has already @@ -621,6 +638,7 @@ func (b *BtcdNotifier) confDetailsManually(confRequest chainntnfs.ConfRequest, } return &chainntnfs.TxConfirmation{ + Tx: tx, BlockHash: blockHash, BlockHeight: height, TxIndex: uint32(txIndex), diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index 4b4c38ac..13b49471 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -150,6 +150,9 @@ type TxConfirmation struct { // TxIndex is the index within the block of the ultimate confirmed // transaction. TxIndex uint32 + + // Tx is the transaction for which the notification was requested for. + Tx *wire.MsgTx } // ConfirmationEvent encapsulates a confirmation notification. With this struct, diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index dc7ad2c1..b63dee0c 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -563,6 +563,7 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ } return &chainntnfs.TxConfirmation{ + Tx: tx.MsgTx(), BlockHash: blockHash, BlockHeight: scanHeight, TxIndex: uint32(i), diff --git a/chainntnfs/txnotifier.go b/chainntnfs/txnotifier.go index 662963a7..a6a39779 100644 --- a/chainntnfs/txnotifier.go +++ b/chainntnfs/txnotifier.go @@ -1226,6 +1226,7 @@ func (n *TxNotifier) filterTx(tx *btcutil.Tx, blockHash *chainhash.Hash, blockHeight, blockHash) details := &TxConfirmation{ + Tx: tx.MsgTx(), BlockHash: blockHash, BlockHeight: blockHeight, TxIndex: uint32(tx.Index()), diff --git a/chainntnfs/txnotifier_test.go b/chainntnfs/txnotifier_test.go index bc28f8f3..aa5e8892 100644 --- a/chainntnfs/txnotifier_test.go +++ b/chainntnfs/txnotifier_test.go @@ -256,6 +256,7 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) { BlockHash: block1.Hash(), BlockHeight: 11, TxIndex: 0, + Tx: &tx1, } assertConfDetails(t, txConf, &expectedConf) default: @@ -327,6 +328,7 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) { BlockHash: block1.Hash(), BlockHeight: 11, TxIndex: 1, + Tx: &tx2, } assertConfDetails(t, txConf, &expectedConf) default: @@ -386,6 +388,7 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) { BlockHash: &chainntnfs.ZeroHash, BlockHeight: 9, TxIndex: 1, + Tx: &tx1, } err := n.UpdateConfDetails(ntfn1.ConfRequest, &txConf1) if err != nil { @@ -419,6 +422,7 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) { BlockHash: &chainntnfs.ZeroHash, BlockHeight: 9, TxIndex: 2, + Tx: &tx2, } err = n.UpdateConfDetails(ntfn2.ConfRequest, &txConf2) if err != nil { @@ -1344,6 +1348,7 @@ func TestTxNotifierConfReorg(t *testing.T) { BlockHash: block3.Hash(), BlockHeight: 12, TxIndex: 0, + Tx: &tx2, } assertConfDetails(t, txConf, &expectedConf) default: @@ -1374,6 +1379,7 @@ func TestTxNotifierConfReorg(t *testing.T) { BlockHash: block3.Hash(), BlockHeight: 12, TxIndex: 1, + Tx: &tx3, } assertConfDetails(t, txConf, &expectedConf) default: @@ -2106,6 +2112,10 @@ func assertConfDetails(t *testing.T, result, expected *chainntnfs.TxConfirmation t.Fatalf("Incorrect tx index in confirmation details: "+ "expected %d, got %d", expected.TxIndex, result.TxIndex) } + if result.Tx.TxHash() != expected.Tx.TxHash() { + t.Fatalf("expected tx hash %v, got %v", expected.Tx.TxHash(), + result.Tx.TxHash()) + } } func assertSpendDetails(t *testing.T, result, expected *chainntnfs.SpendDetail) {