From 5416ca466f0420c86157f339f38d945a346aa33d Mon Sep 17 00:00:00 2001 From: arkadiuszos4chain Date: Wed, 22 Nov 2023 11:28:58 +0100 Subject: [PATCH] feat(BUX-172): implement SPV for every unmined tx in BEEF --- definitions.go | 2 +- p2p_beef_tx.go | 64 +++++++-------- p2p_beef_tx_test.go | 49 ++++++------ server/p2p_receive_transaction.go | 2 +- ...p_receive_transaction_request_processor.go | 2 +- spv/merkle_path_validation.go | 25 +++--- spv/scripts_validation.go | 8 +- spv/spv.go | 77 ++++++++++--------- spv/spv_test.go | 6 +- 9 files changed, 125 insertions(+), 110 deletions(-) diff --git a/definitions.go b/definitions.go index 2016f87..c4c4880 100644 --- a/definitions.go +++ b/definitions.go @@ -84,5 +84,5 @@ type AddressInformation struct { // of Merkle Roots inclusion in the longest chain. type MerkleRootConfirmationRequestItem struct { MerkleRoot string `json:"merkleRoot"` - BlockHeight int32 `json:"blockHeight"` + BlockHeight uint64 `json:"blockHeight"` } diff --git a/p2p_beef_tx.go b/p2p_beef_tx.go index 0abaf96..b1ff3ff 100644 --- a/p2p_beef_tx.go +++ b/p2p_beef_tx.go @@ -27,11 +27,15 @@ const ( type TxData struct { Transaction *bt.Tx - PathIndex *bt.VarInt + BumpIndex *bt.VarInt txID string } +func (td *TxData) Unmined() bool { + return td.BumpIndex == nil +} + func (td *TxData) GetTxID() string { if len(td.txID) == 0 { td.txID = td.Transaction.TxID() @@ -41,9 +45,30 @@ func (td *TxData) GetTxID() string { } type DecodedBEEF struct { - BUMPs BUMPs - InputsTxData []*TxData - ProcessedTxData *bt.Tx + BUMPs BUMPs + Transactions []*TxData +} + +func DecodeBEEF(beefHex string) (*DecodedBEEF, error) { + beefBytes, err := extractBytesWithoutVersionAndMarker(beefHex) + if err != nil { + return nil, err + } + + bumps, remainingBytes, err := decodeBUMPs(beefBytes) + if err != nil { + return nil, err + } + + transactions, err := decodeTransactionsWithPathIndexes(remainingBytes) + if err != nil { + return nil, err + } + + return &DecodedBEEF{ + BUMPs: bumps, + Transactions: transactions, + }, nil } // GetMerkleRoots will calculate the merkle roots for the BUMPs in the BEEF transaction @@ -57,7 +82,7 @@ func (dBeef *DecodedBEEF) GetMerkleRootsRequest() ([]MerkleRootConfirmationReque } request := MerkleRootConfirmationRequestItem{ - BlockHeight: int32(bump.BlockHeight), + BlockHeight: bump.BlockHeight, MerkleRoot: merkleRoot, } merkleRootsRequest = append(merkleRootsRequest, request) @@ -66,31 +91,8 @@ func (dBeef *DecodedBEEF) GetMerkleRootsRequest() ([]MerkleRootConfirmationReque return merkleRootsRequest, nil } -func DecodeBEEF(beefHex string) (*DecodedBEEF, error) { - beefBytes, err := extractBytesWithoutVersionAndMarker(beefHex) - if err != nil { - return nil, err - } - - bumps, remainingBytes, err := decodeBUMPs(beefBytes) - if err != nil { - return nil, err - } - - transactions, err := decodeTransactionsWithPathIndexes(remainingBytes) - if err != nil { - return nil, err - } - - // get the last transaction as the processed transaction - it should be the last one because of khan's ordering - processedTx := transactions[len(transactions)-1] - transactions = transactions[:len(transactions)-1] - - return &DecodedBEEF{ - BUMPs: bumps, - InputsTxData: transactions, - ProcessedTxData: processedTx.Transaction, - }, nil +func (d *DecodedBEEF) GetLatestTx() *bt.Tx { + return d.Transactions[len(d.Transactions)-1].Transaction // get the last transaction as the processed transaction - it should be the last one because of khan's ordering } func decodeBUMPs(beefBytes []byte) ([]BUMP, []byte, error) { @@ -239,7 +241,7 @@ func decodeTransactionsWithPathIndexes(bytes []byte) ([]*TxData, error) { transactions = append(transactions, &TxData{ Transaction: tx, - PathIndex: pathIndex, + BumpIndex: pathIndex, }) } diff --git a/p2p_beef_tx_test.go b/p2p_beef_tx_test.go index 5becdeb..bfe29b9 100644 --- a/p2p_beef_tx_test.go +++ b/p2p_beef_tx_test.go @@ -15,7 +15,6 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) { hexStream string expectedDecodedBEEF *DecodedBEEF pathIndexForTheOldestInput *bt.VarInt - expectedError error }{ { name: "valid BEEF with 1 CMP and 1 input transaction", @@ -50,7 +49,7 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) { }, }, }, - InputsTxData: []*TxData{ + Transactions: []*TxData{ { Transaction: &bt.Tx{ Version: 1, @@ -70,25 +69,29 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) { }, }, }, - PathIndex: func(v bt.VarInt) *bt.VarInt { return &v }(0x0), + BumpIndex: func(v bt.VarInt) *bt.VarInt { return &v }(0x0), }, - }, - ProcessedTxData: &bt.Tx{ - Version: 1, - LockTime: 0, - Inputs: []*bt.Input{ - { - PreviousTxSatoshis: 0, - PreviousTxOutIndex: 0, - SequenceNumber: 4294967295, - PreviousTxScript: nil, - }, - }, - Outputs: []*bt.Output{ - { - Satoshis: 26172, - LockingScript: bscript.NewFromBytes([]byte("76a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac")), + + { + Transaction: &bt.Tx{ + Version: 1, + LockTime: 0, + Inputs: []*bt.Input{ + { + PreviousTxSatoshis: 0, + PreviousTxOutIndex: 0, + SequenceNumber: 4294967295, + PreviousTxScript: nil, + }, + }, + Outputs: []*bt.Output{ + { + Satoshis: 26172, + LockingScript: bscript.NewFromBytes([]byte("76a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac")), + }, + }, }, + BumpIndex: nil, }, }, }, @@ -104,9 +107,9 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) { decodedBEEF, err := DecodeBEEF(beef) // then - assert.Equal(t, tc.expectedError, err, "expected error %v, but got %v", tc.expectedError, err) + assert.Nil(t, err) - assert.Equal(t, len(tc.expectedDecodedBEEF.InputsTxData), len(decodedBEEF.InputsTxData), "expected %v inputs, but got %v", len(tc.expectedDecodedBEEF.InputsTxData), len(decodedBEEF.InputsTxData)) + assert.Equal(t, len(tc.expectedDecodedBEEF.Transactions), len(decodedBEEF.Transactions), "expected %v inputs, but got %v", len(tc.expectedDecodedBEEF.Transactions), len(decodedBEEF.Transactions)) assert.Equal(t, len(tc.expectedDecodedBEEF.BUMPs), len(decodedBEEF.BUMPs), "expected %v BUMPs, but got %v", len(tc.expectedDecodedBEEF.BUMPs), len(decodedBEEF.BUMPs)) @@ -115,9 +118,7 @@ func TestDecodeBEEF_DecodeBEEF_HappyPaths(t *testing.T) { assert.Equal(t, bump.Path, decodedBEEF.BUMPs[i].Path, "expected equal BUMPPaths for %v BUMP, expected: %v but got %v", i, bump, len(decodedBEEF.BUMPs[i].Path)) } - assert.NotNil(t, decodedBEEF.ProcessedTxData, "expected original transaction to be not nil") - - assert.Equal(t, tc.expectedDecodedBEEF.InputsTxData[0].PathIndex, decodedBEEF.InputsTxData[0].PathIndex, "expected path index for the oldest input to be %v, but got %v", tc.expectedDecodedBEEF.InputsTxData[0].PathIndex, decodedBEEF.InputsTxData[0].PathIndex) + assert.Equal(t, tc.expectedDecodedBEEF.Transactions[0].BumpIndex, decodedBEEF.Transactions[0].BumpIndex, "expected path index for the oldest input to be %v, but got %v", tc.expectedDecodedBEEF.Transactions[0].BumpIndex, decodedBEEF.Transactions[0].BumpIndex) }) } } diff --git a/server/p2p_receive_transaction.go b/server/p2p_receive_transaction.go index f0cf3d3..3f80fbb 100644 --- a/server/p2p_receive_transaction.go +++ b/server/p2p_receive_transaction.go @@ -88,7 +88,7 @@ func (c *Configuration) p2pReceiveBeefTx(w http.ResponseWriter, req *http.Reques panic("empty beef after parsing!") } - err := spv.ExecuteSimplifiedPaymentVerification(dBeef, c.actions) + err := spv.ExecuteSimplifiedPaymentVerification(req.Context(), dBeef, c.actions) if err != nil { ErrorResponse(w, ErrorSimplifiedPaymentVerification, err.Error(), http.StatusExpectationFailed) return diff --git a/server/p2p_receive_transaction_request_processor.go b/server/p2p_receive_transaction_request_processor.go index 7146088..ffd0e9b 100644 --- a/server/p2p_receive_transaction_request_processor.go +++ b/server/p2p_receive_transaction_request_processor.go @@ -83,7 +83,7 @@ func getProcessedTxData(payload *p2pReceiveTxReqPayload, format p2pPayloadFormat return nil, nil, &processingError{&parseError{ErrorInvalidParameter, errorMsg}, http.StatusBadRequest} } - processedTx = beefData.ProcessedTxData + processedTx = beefData.GetLatestTx() default: panic("Unexpected transaction format!") diff --git a/spv/merkle_path_validation.go b/spv/merkle_path_validation.go index 0cb0cea..1848863 100644 --- a/spv/merkle_path_validation.go +++ b/spv/merkle_path_validation.go @@ -8,8 +8,8 @@ import ( "github.com/libsv/go-bt/v2" ) -func verifyMerkleRoots(dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) error { - if err := ensureInputTransactionsArePresentInBump(dBeef); err != nil { +func verifyMerkleRoots(ctx context.Context, dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) error { + if err := ensureInputTransactionsArePresentInBump(dBeef.GetLatestTx(), dBeef); err != nil { return err } @@ -18,7 +18,7 @@ func verifyMerkleRoots(dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) return err } - err = provider.VerifyMerkleRoots(context.Background(), merkleRoots) + err = provider.VerifyMerkleRoots(ctx, merkleRoots) if err != nil { return err } @@ -26,11 +26,11 @@ func verifyMerkleRoots(dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) return nil } -func ensureInputTransactionsArePresentInBump(dBeef *paymail.DecodedBEEF) error { +func ensureInputTransactionsArePresentInBump(tx *bt.Tx, dBeef *paymail.DecodedBEEF) error { - for _, input := range dBeef.ProcessedTxData.Inputs { + for _, input := range tx.Inputs { - minedAcestors := findMinedAncestors(input, dBeef.InputsTxData) + minedAcestors := findMinedAncestors(input, dBeef.Transactions, 0) if len(minedAcestors) == 0 { return errors.New("invalid BUMP - input mined ancestor is not present in BUMPs") } @@ -46,28 +46,33 @@ func ensureInputTransactionsArePresentInBump(dBeef *paymail.DecodedBEEF) error { return nil } -func findMinedAncestors(input *bt.Input, parentTxs []*paymail.TxData) []*paymail.TxData { +func findMinedAncestors(input *bt.Input, parentTxs []*paymail.TxData, depth uint) []*paymail.TxData { + if depth > 64 { + return []*paymail.TxData{} + } + depth++ + parent := findParentForInput(input, parentTxs) if parent == nil { // oh oh- end of hierarchy return []*paymail.TxData{} } - if parent.PathIndex != nil { // mined parent + if !parent.Unmined() { return []*paymail.TxData{parent} } ancestors := make([]*paymail.TxData, 0) for _, in := range parent.Transaction.Inputs { - ancestors = append(ancestors, findMinedAncestors(in, parentTxs)...) + ancestors = append(ancestors, findMinedAncestors(in, parentTxs, depth)...) } return ancestors } func existsInBumps(ancestorTx *paymail.TxData, bumps paymail.BUMPs) bool { - bumpIdx := int(*ancestorTx.PathIndex) // TODO: disscuss max value of index + bumpIdx := int(*ancestorTx.BumpIndex) parentTxID := ancestorTx.GetTxID() if len(bumps) > bumpIdx { diff --git a/spv/scripts_validation.go b/spv/scripts_validation.go index cc71913..805c1e5 100644 --- a/spv/scripts_validation.go +++ b/spv/scripts_validation.go @@ -9,14 +9,14 @@ import ( "github.com/bitcoin-sv/go-paymail" ) -func validateScripts(dBeef *paymail.DecodedBEEF) error { - for i, input := range dBeef.ProcessedTxData.Inputs { - inputParentTx := findParentForInput(input, dBeef.InputsTxData) +func validateScripts(tx *bt.Tx, inputTxs []*paymail.TxData) error { + for i, input := range tx.Inputs { + inputParentTx := findParentForInput(input, inputTxs) if inputParentTx == nil { return errors.New("invalid parent transactions, no matching trasactions for input") } - err := verifyScripts(dBeef.ProcessedTxData, inputParentTx.Transaction, i) + err := verifyScripts(tx, inputParentTx.Transaction, i) if err != nil { return errors.New("invalid script") } diff --git a/spv/spv.go b/spv/spv.go index 7e6bd4b..ce5f96d 100644 --- a/spv/spv.go +++ b/spv/spv.go @@ -16,24 +16,38 @@ type MerkleRootVerifier interface { } // ExecuteSimplifiedPaymentVerification executes the SPV for decoded BEEF tx -func ExecuteSimplifiedPaymentVerification(dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) error { +func ExecuteSimplifiedPaymentVerification(ctx context.Context, dBeef *paymail.DecodedBEEF, provider MerkleRootVerifier) error { - err := validateSatoshisSum(dBeef) - if err != nil { - return err - } + for _, txDt := range dBeef.Transactions { + tx := txDt.Transaction - err = validateLockTime(dBeef) - if err != nil { - return err - } + if len(tx.Outputs) == 0 { + return errors.New("invalid output, no outputs") + } - err = validateScripts(dBeef) - if err != nil { - return err + if len(tx.Inputs) == 0 { + return errors.New("invalid input, no inputs") + } + + err := validateLockTime(tx) + if err != nil { + return err + } + + if txDt.Unmined() { + err = validateSatoshisSum(tx, dBeef.Transactions) + if err != nil { + return err + } + + err = validateScripts(tx, dBeef.Transactions) + if err != nil { + return err + } + } } - err = verifyMerkleRoots(dBeef, provider) + err := verifyMerkleRoots(ctx, dBeef, provider) if err != nil { return err } @@ -41,19 +55,24 @@ func ExecuteSimplifiedPaymentVerification(dBeef *paymail.DecodedBEEF, provider M return nil } -func validateSatoshisSum(dBeef *paymail.DecodedBEEF) error { - if len(dBeef.ProcessedTxData.Outputs) == 0 { - return errors.New("invalid output, no outputs") - } - - if len(dBeef.ProcessedTxData.Inputs) == 0 { - return errors.New("invalid input, no inputs") +func validateLockTime(tx *bt.Tx) error { + if tx.LockTime == 0 { + for _, input := range tx.Inputs { + if input.SequenceNumber != 0xffffffff { + return errors.New("unexpected transaction with nSequence") + } + } + } else { + return errors.New("unexpected transaction with nLockTime") } + return nil +} +func validateSatoshisSum(tx *bt.Tx, inputTxs []*paymail.TxData) error { inputSum, outputSum := uint64(0), uint64(0) - for _, input := range dBeef.ProcessedTxData.Inputs { - inputParentTx := findParentForInput(input, dBeef.InputsTxData) + for _, input := range tx.Inputs { + inputParentTx := findParentForInput(input, inputTxs) if inputParentTx == nil { return errors.New("invalid parent transactions, no matching trasactions for input") @@ -61,26 +80,14 @@ func validateSatoshisSum(dBeef *paymail.DecodedBEEF) error { inputSum += inputParentTx.Transaction.Outputs[input.PreviousTxOutIndex].Satoshis } - for _, output := range dBeef.ProcessedTxData.Outputs { + for _, output := range tx.Outputs { outputSum += output.Satoshis } if inputSum <= outputSum { return errors.New("invalid input and output sum, outputs can not be larger than inputs") } - return nil -} -func validateLockTime(dBeef *paymail.DecodedBEEF) error { - if dBeef.ProcessedTxData.LockTime == 0 { - for _, input := range dBeef.ProcessedTxData.Inputs { - if input.SequenceNumber != 0xffffffff { - return errors.New("unexpected transaction with nSequence") - } - } - } else { - return errors.New("unexpected transaction with nLockTime") - } return nil } diff --git a/spv/spv_test.go b/spv/spv_test.go index c5adb57..a413252 100644 --- a/spv/spv_test.go +++ b/spv/spv_test.go @@ -17,7 +17,7 @@ func TestExecuteSimplifiedPaymentVerification_ValidTransaction_Success(t *testin require.Nil(t, err) t.Run("SPV on valid beef", func(t *testing.T) { - require.Nil(t, ExecuteSimplifiedPaymentVerification(validDecodedBeef, new(mockServiceProvider))) + require.Nil(t, ExecuteSimplifiedPaymentVerification(context.Background(), validDecodedBeef, new(mockServiceProvider))) }) } @@ -29,7 +29,7 @@ func TestExecuteSimplifiedPaymentVerification_CorruptedTransaction_ReturnError(t const nlockTime = "0100beef01fe4e6d0c001002fd9c67028ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160cfd9d6700db1332728830a58c83a5970dcd111a575a585b43b0492361ea8082f41668f8bd01fdcf3300e568706954aae516ef6df7b5db7828771a1f3fcf1b6d65389ec8be8c46057a3c01fde6190001a6028d13cc988f55c8765e3ffcdcfc7d5185a8ebd68709c0adbe37b528557b01fdf20c001cc64f09a217e1971cabe751b925f246e3c2a8e145c49be7b831eaea3e064d7501fd7806009ccf122626a20cdb054877ef3f8ae2d0503bb7a8704fdb6295b3001b5e8876a101fd3d0300aeea966733175ff60b55bc77edcb83c0fce3453329f51195e5cbc7a874ee47ad01fd9f0100f67f50b53d73ffd6e84c02ee1903074b9a5b2ac64c508f7f26349b73cca9d7e901ce006ce74c7beed0c61c50dda8b578f0c0dc5a393e1f8758af2fb65edf483afcaa68016600e32475e17bdd141d62524d0005989dd1db6ca92c6af70791b0e4802be4c5c8c1013200b88162f494f26cc3a1a4a7dcf2829a295064e93b3dbb2f72e21a73522869277a011800a938d3f80dd25b6a3a80e450403bf7d62a1068e2e4b13f0656c83f764c55bb77010d006feac6e4fea41c37c508b5bfdc00d582f6e462e6754b338c95b448df37bd342c010700bf5448356be23b2b9afe53d00cee047065bbc16d0bbcc5f80aa8c1b509e45678010200c2e37431a437ee311a737aecd3caae1213db353847f33792fd539e380bdb4d440100005d5aef298770e2702448af2ce014f8bfcded5896df5006a44b5f1b6020007aeb01010091484f513003fcdb25f336b9b56dafcb05fbc739593ab573a2c6516b344ca5320201000000027b0a1b12c7c9e48015e78d3a08a4d62e439387df7e0d7a810ebd4af37661daaa000000006a47304402207d972759afba7c0ffa6cfbbf39a31c2aeede1dae28d8841db56c6dd1197d56a20220076a390948c235ba8e72b8e43a7b4d4119f1a81a77032aa6e7b7a51be5e13845412103f78ec31cf94ca8d75fb1333ad9fc884e2d489422034a1efc9d66a3b72eddca0fffffffff7f36874f858fb43ffcf4f9e3047825619bad0e92d4b9ad4ba5111d1101cbddfe010000006a473044022043f048043d56eb6f75024808b78f18808b7ab45609e4c4c319e3a27f8246fc3002204b67766b62f58bf6f30ea608eaba76b8524ed49f67a90f80ac08a9b96a6922cd41210254a583c1c51a06e10fab79ddf922915da5f5c1791ef87739f40cb68638397248ffffffff03e8030000000000001976a914b08f70bc5010fb026de018f19e7792385a146b4a88acf3010000000000001976a9147d48635f889372c3da12d75ce246c59f4ab907ed88acf7000000000000001976a914b8fbd58685b6920d8f9a8f1b274d8696708b51b088ac00000000010001000000018ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160c000000006a473044022052b71d3f9701e29419a0feb77f4eed2d1eeba113806c66956a4516531a5c8e7d022056ac7694a79ad45c105d28954034e71d30a7ec9d4dd0782098cedd32f8952a4a412102b0c8980f5d2cab77c92c68ac46442feba163a9d306913f6a34911fc618c3c4e7ffffffff01c8000000000000001976a9148a8c4546a95e6fc8d18076a9980d59fd882b4e6988ac9f86010000" const nlockNseq = "0100beef01fe4e6d0c001002fd9c67028ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160cfd9d6700db1332728830a58c83a5970dcd111a575a585b43b0492361ea8082f41668f8bd01fdcf3300e568706954aae516ef6df7b5db7828771a1f3fcf1b6d65389ec8be8c46057a3c01fde6190001a6028d13cc988f55c8765e3ffcdcfc7d5185a8ebd68709c0adbe37b528557b01fdf20c001cc64f09a217e1971cabe751b925f246e3c2a8e145c49be7b831eaea3e064d7501fd7806009ccf122626a20cdb054877ef3f8ae2d0503bb7a8704fdb6295b3001b5e8876a101fd3d0300aeea966733175ff60b55bc77edcb83c0fce3453329f51195e5cbc7a874ee47ad01fd9f0100f67f50b53d73ffd6e84c02ee1903074b9a5b2ac64c508f7f26349b73cca9d7e901ce006ce74c7beed0c61c50dda8b578f0c0dc5a393e1f8758af2fb65edf483afcaa68016600e32475e17bdd141d62524d0005989dd1db6ca92c6af70791b0e4802be4c5c8c1013200b88162f494f26cc3a1a4a7dcf2829a295064e93b3dbb2f72e21a73522869277a011800a938d3f80dd25b6a3a80e450403bf7d62a1068e2e4b13f0656c83f764c55bb77010d006feac6e4fea41c37c508b5bfdc00d582f6e462e6754b338c95b448df37bd342c010700bf5448356be23b2b9afe53d00cee047065bbc16d0bbcc5f80aa8c1b509e45678010200c2e37431a437ee311a737aecd3caae1213db353847f33792fd539e380bdb4d440100005d5aef298770e2702448af2ce014f8bfcded5896df5006a44b5f1b6020007aeb01010091484f513003fcdb25f336b9b56dafcb05fbc739593ab573a2c6516b344ca5320201000000027b0a1b12c7c9e48015e78d3a08a4d62e439387df7e0d7a810ebd4af37661daaa000000006a47304402207d972759afba7c0ffa6cfbbf39a31c2aeede1dae28d8841db56c6dd1197d56a20220076a390948c235ba8e72b8e43a7b4d4119f1a81a77032aa6e7b7a51be5e13845412103f78ec31cf94ca8d75fb1333ad9fc884e2d489422034a1efc9d66a3b72eddca0fffffffff7f36874f858fb43ffcf4f9e3047825619bad0e92d4b9ad4ba5111d1101cbddfe010000006a473044022043f048043d56eb6f75024808b78f18808b7ab45609e4c4c319e3a27f8246fc3002204b67766b62f58bf6f30ea608eaba76b8524ed49f67a90f80ac08a9b96a6922cd41210254a583c1c51a06e10fab79ddf922915da5f5c1791ef87739f40cb68638397248ffffffff03e8030000000000001976a914b08f70bc5010fb026de018f19e7792385a146b4a88acf3010000000000001976a9147d48635f889372c3da12d75ce246c59f4ab907ed88acf7000000000000001976a914b8fbd58685b6920d8f9a8f1b274d8696708b51b088ac00000000010001000000018ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160c000000006b483045022100e3da33b9f50ee8c4a2383d92bbfc9cc19367f1c227a8b7c08d11eecfc8ec01f702202ba54cdbf766dfd314c87e29557d18db2c451502a6a23acafa1c970b816f431c412102b0c8980f5d2cab77c92c68ac46442feba163a9d306913f6a34911fc618c3c4e70f27000001c8000000000000001976a9148a8c4546a95e6fc8d18076a9980d59fd882b4e6988ac9f86010000" const nseq = "0100beef01fe4e6d0c001002fd9c67028ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160cfd9d6700db1332728830a58c83a5970dcd111a575a585b43b0492361ea8082f41668f8bd01fdcf3300e568706954aae516ef6df7b5db7828771a1f3fcf1b6d65389ec8be8c46057a3c01fde6190001a6028d13cc988f55c8765e3ffcdcfc7d5185a8ebd68709c0adbe37b528557b01fdf20c001cc64f09a217e1971cabe751b925f246e3c2a8e145c49be7b831eaea3e064d7501fd7806009ccf122626a20cdb054877ef3f8ae2d0503bb7a8704fdb6295b3001b5e8876a101fd3d0300aeea966733175ff60b55bc77edcb83c0fce3453329f51195e5cbc7a874ee47ad01fd9f0100f67f50b53d73ffd6e84c02ee1903074b9a5b2ac64c508f7f26349b73cca9d7e901ce006ce74c7beed0c61c50dda8b578f0c0dc5a393e1f8758af2fb65edf483afcaa68016600e32475e17bdd141d62524d0005989dd1db6ca92c6af70791b0e4802be4c5c8c1013200b88162f494f26cc3a1a4a7dcf2829a295064e93b3dbb2f72e21a73522869277a011800a938d3f80dd25b6a3a80e450403bf7d62a1068e2e4b13f0656c83f764c55bb77010d006feac6e4fea41c37c508b5bfdc00d582f6e462e6754b338c95b448df37bd342c010700bf5448356be23b2b9afe53d00cee047065bbc16d0bbcc5f80aa8c1b509e45678010200c2e37431a437ee311a737aecd3caae1213db353847f33792fd539e380bdb4d440100005d5aef298770e2702448af2ce014f8bfcded5896df5006a44b5f1b6020007aeb01010091484f513003fcdb25f336b9b56dafcb05fbc739593ab573a2c6516b344ca5320201000000027b0a1b12c7c9e48015e78d3a08a4d62e439387df7e0d7a810ebd4af37661daaa000000006a47304402207d972759afba7c0ffa6cfbbf39a31c2aeede1dae28d8841db56c6dd1197d56a20220076a390948c235ba8e72b8e43a7b4d4119f1a81a77032aa6e7b7a51be5e13845412103f78ec31cf94ca8d75fb1333ad9fc884e2d489422034a1efc9d66a3b72eddca0fffffffff7f36874f858fb43ffcf4f9e3047825619bad0e92d4b9ad4ba5111d1101cbddfe010000006a473044022043f048043d56eb6f75024808b78f18808b7ab45609e4c4c319e3a27f8246fc3002204b67766b62f58bf6f30ea608eaba76b8524ed49f67a90f80ac08a9b96a6922cd41210254a583c1c51a06e10fab79ddf922915da5f5c1791ef87739f40cb68638397248ffffffff03e8030000000000001976a914b08f70bc5010fb026de018f19e7792385a146b4a88acf3010000000000001976a9147d48635f889372c3da12d75ce246c59f4ab907ed88acf7000000000000001976a914b8fbd58685b6920d8f9a8f1b274d8696708b51b088ac00000000010001000000018ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160c000000006b483045022100bcae2985c92e421e25b271d565398bf0fe0ef870d62d4945a1589933ad9f0af102207f9fd4ad14ac0c5507ca317e64231b9ba1f2de1e8a7b13520cccee3e5f0650e4412102b0c8980f5d2cab77c92c68ac46442feba163a9d306913f6a34911fc618c3c4e70f27000001c8000000000000001976a9148a8c4546a95e6fc8d18076a9980d59fd882b4e6988ac0000000000" - const bump = "0100beef01fe4e6d0c001002fd909002088a382ec07a8cf47c6158b68e5822852362102d8571482d1257e0b7527e1882fd91900065cb01218f2506bb51155d243e4d6b32d69d1b5f2221c52e26963cfd8cf7283201fd4948008d7a44ae384797b0ae84db0c857e8c1083425d64d09ef8bc5e2e9d270677260501fd25240060f38aa33631c8d70adbac1213e7a5b418c90414e919e3a12ced63dd152fd85a01fd1312005ff132ee64a7a0c79150a29f66ef861e552d3a05b47d6303f5d8a2b2a09bc61501fd080900cc0baf21cf06b9439dfe05dce9bdb14ddc2ca2d560b1138296ef5769851a84b301fd85040063ccb26232a6e1d3becdb47a0f19a67a562b754e8894155b3ae7bba10335ce5101fd430200e153fc455a0f2c8372885c11af70af904dcf44740b9ebf3b3e5b2234cce550bc01fd20010077d5ea69d1dcc379dde65d6adcebde1838190118a8fae928c037275e78bd87910191000263e4f31684a25169857f2788aeef603504931f92585f02c4c9e023b2aa43d1014900de72292e0b3e5eeacfa2b657bf4d46c885559b081ee78632a99b318c1148d85c01250068a5f831ca99b9e7f3720920d6ea977fd2ab52b83d1a6567dafa4c8cafd941ed0113006a0b91d83f9056b702d6a8056af6365c7da626fc3818b815dd4b0de22d05450f0108009876ce56b68545a75859e93d200bdde7880d46f39384818b259ed847a9664ddf010500990bc5e95cacbc927b5786ec39a183f983fe160d52829cf47521c7eb369771c30103004fe794e50305f590b6010a51d050bf47dfeaabfdb949c5ee0673f577a59537d70100004dad44a358aea4d8bc1917912539901f5ae44e07a4748e1a9d3018814b0759d00201000000027b0a1b12c7c9e48015e78d3a08a4d62e439387df7e0d7a810ebd4af37661daaa000000006a47304402207d972759afba7c0ffa6cfbbf39a31c2aeede1dae28d8841db56c6dd1197d56a20220076a390948c235ba8e72b8e43a7b4d4119f1a81a77032aa6e7b7a51be5e13845412103f78ec31cf94ca8d75fb1333ad9fc884e2d489422034a1efc9d66a3b72eddca0fffffffff7f36874f858fb43ffcf4f9e3047825619bad0e92d4b9ad4ba5111d1101cbddfe010000006a473044022043f048043d56eb6f75024808b78f18808b7ab45609e4c4c319e3a27f8246fc3002204b67766b62f58bf6f30ea608eaba76b8524ed49f67a90f80ac08a9b96a6922cd41210254a583c1c51a06e10fab79ddf922915da5f5c1791ef87739f40cb68638397248ffffffff03e8030000000000001976a914b08f70bc5010fb026de018f19e7792385a146b4a88acf3010000000000001976a9147d48635f889372c3da12d75ce246c59f4ab907ed88acf7000000000000001976a914b8fbd58685b6920d8f9a8f1b274d8696708b51b088ac000000000001000000018ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160c000000006a47304402204a04841f6f626d30e21200e1c404ea80e319b643fe86f08e709413a89a493a4b022038a2e3e25a813d8d540c1a572fa8ec5fa2d2434bcea78d17902dcccddcc1c9484121028fd1afeee81361e801800afb264e35cdce3037ec6f7dc4f1d1eaba7ad519c948ffffffff01c8000000000000001976a9148ce2d21f9a75e98600be76b25b91c4fef6b40bcd88ac0000000000" + const bump = "0100beef01fe4e6d0c001002fd909002088a382ec07a8cf47c6158b68e5822852362102d8571482d1257e0b7527e1882fd91900065cb01218f2506bb51155d243e4d6b32d69d1b5f2221c52e26963cfd8cf7283201fd4948008d7a44ae384797b0ae84db0c857e8c1083425d64d09ef8bc5e2e9d270677260501fd25240060f38aa33631c8d70adbac1213e7a5b418c90414e919e3a12ced63dd152fd85a01fd1312005ff132ee64a7a0c79150a29f66ef861e552d3a05b47d6303f5d8a2b2a09bc61501fd080900cc0baf21cf06b9439dfe05dce9bdb14ddc2ca2d560b1138296ef5769851a84b301fd85040063ccb26232a6e1d3becdb47a0f19a67a562b754e8894155b3ae7bba10335ce5101fd430200e153fc455a0f2c8372885c11af70af904dcf44740b9ebf3b3e5b2234cce550bc01fd20010077d5ea69d1dcc379dde65d6adcebde1838190118a8fae928c037275e78bd87910191000263e4f31684a25169857f2788aeef603504931f92585f02c4c9e023b2aa43d1014900de72292e0b3e5eeacfa2b657bf4d46c885559b081ee78632a99b318c1148d85c01250068a5f831ca99b9e7f3720920d6ea977fd2ab52b83d1a6567dafa4c8cafd941ed0113006a0b91d83f9056b702d6a8056af6365c7da626fc3818b815dd4b0de22d05450f0108009876ce56b68545a75859e93d200bdde7880d46f39384818b259ed847a9664ddf010500990bc5e95cacbc927b5786ec39a183f983fe160d52829cf47521c7eb369771c30103004fe794e50305f590b6010a51d050bf47dfeaabfdb949c5ee0673f577a59537d70100004dad44a358aea4d8bc1917912539901f5ae44e07a4748e1a9d3018814b0759d00201000000027b0a1b12c7c9e48015e78d3a08a4d62e439387df7e0d7a810ebd4af37661daaa000000006a47304402207d972759afba7c0ffa6cfbbf39a31c2aeede1dae28d8841db56c6dd1197d56a20220076a390948c235ba8e72b8e43a7b4d4119f1a81a77032aa6e7b7a51be5e13845412103f78ec31cf94ca8d75fb1333ad9fc884e2d489422034a1efc9d66a3b72eddca0fffffffff7f36874f858fb43ffcf4f9e3047825619bad0e92d4b9ad4ba5111d1101cbddfe010000006a473044022043f048043d56eb6f75024808b78f18808b7ab45609e4c4c319e3a27f8246fc3002204b67766b62f58bf6f30ea608eaba76b8524ed49f67a90f80ac08a9b96a6922cd41210254a583c1c51a06e10fab79ddf922915da5f5c1791ef87739f40cb68638397248ffffffff03e8030000000000001976a914b08f70bc5010fb026de018f19e7792385a146b4a88acf3010000000000001976a9147d48635f889372c3da12d75ce246c59f4ab907ed88acf7000000000000001976a914b8fbd58685b6920d8f9a8f1b274d8696708b51b088ac00000000010001000000018ae36502fdc82837319362c488fb9cb978e064daf600bbfc48389663fc5c160c000000006a47304402204a04841f6f626d30e21200e1c404ea80e319b643fe86f08e709413a89a493a4b022038a2e3e25a813d8d540c1a572fa8ec5fa2d2434bcea78d17902dcccddcc1c9484121028fd1afeee81361e801800afb264e35cdce3037ec6f7dc4f1d1eaba7ad519c948ffffffff01c8000000000000001976a9148ce2d21f9a75e98600be76b25b91c4fef6b40bcd88ac0000000000" tcs := []struct { name string @@ -76,7 +76,7 @@ func TestExecuteSimplifiedPaymentVerification_CorruptedTransaction_ReturnError(t require.Nil(t, err) //when - err = ExecuteSimplifiedPaymentVerification(validDecodedBeef, new(mockServiceProvider)) + err = ExecuteSimplifiedPaymentVerification(context.Background(), validDecodedBeef, new(mockServiceProvider)) require.NotNil(t, err) //then