From 5376097974cdfe0ca5abb90c48e45b076e5a85e9 Mon Sep 17 00:00:00 2001 From: wregulski Date: Wed, 9 Oct 2024 09:41:28 +0200 Subject: [PATCH 01/24] feat(SPV-898): replacements for old structures using new sdk-kit --- engine/action_transaction.go | 13 +++-- engine/beef_bump.go | 52 +++++++++---------- engine/beef_tx.go | 8 +-- engine/beef_tx_bytes.go | 17 +++--- engine/beef_tx_sorting.go | 26 +++++----- engine/beef_tx_sorting_test.go | 11 ++-- engine/ef_tx.go | 14 ++--- engine/examples/client/pike/main.go | 6 +-- engine/model_access_keys.go | 8 +-- engine/model_access_keys_test.go | 6 +-- engine/model_bump.go | 16 +++--- engine/pike/pike.go | 10 ++-- ...record_tx_strategy_internal_incoming_tx.go | 4 +- engine/script/template/evaluate.go | 22 ++++---- engine/tx_repository.go | 6 +-- engine/tx_sync_task.go | 4 +- engine/types/type42/linking_key.go | 12 ++--- engine/utils/destination_types.go | 37 +++++++------ engine/utils/destination_types_test.go | 12 ++--- engine/utils/encrypt.go | 14 ++--- engine/utils/fees.go | 12 +++-- engine/utils/keys.go | 32 ++++++------ engine/utils/keys_test.go | 14 ++--- engine/utils/merkletree.go | 43 +++++++++++++++ engine/utils/ripemd160.go | 28 ++++++++++ engine/utils/scripts.go | 34 ------------ engine/utils/scripts_test.go | 1 - engine/utils/utils.go | 7 +-- engine/utils/utils_test.go | 47 +++++++++-------- go.mod | 8 +-- go.sum | 16 ++---- server/middleware/signature_middleware.go | 38 +++++++++----- 32 files changed, 310 insertions(+), 268 deletions(-) create mode 100644 engine/utils/merkletree.go create mode 100644 engine/utils/ripemd160.go delete mode 100644 engine/utils/scripts.go delete mode 100644 engine/utils/scripts_test.go diff --git a/engine/action_transaction.go b/engine/action_transaction.go index e9515521..a723d74a 100644 --- a/engine/action_transaction.go +++ b/engine/action_transaction.go @@ -5,12 +5,11 @@ import ( "math" "time" + trx "github.com/bitcoin-sv/go-sdk/transaction" chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bc" - "github.com/libsv/go-bt/v2" ) // RecordTransaction will parse the outgoing transaction and save it into the Datastore @@ -19,8 +18,7 @@ import ( // draftID is the unique draft id from a previously started New() transaction (draft_transaction.ID) // opts are model options and can include "metadata" func (c *Client) RecordTransaction(ctx context.Context, xPubKey, txHex, draftID string, opts ...ModelOps) (*Transaction, error) { - - tx, err := bt.NewTxFromString(txHex) + tx, err := trx.NewTransactionFromHex(txHex) if err != nil { return nil, spverrors.ErrInvalidHex } @@ -122,12 +120,12 @@ func (c *Client) GetTransactionsByIDs(ctx context.Context, txIDs []string) ([]*T // GetTransactionByHex will get a transaction from the Datastore by its full hex string // uses GetTransaction func (c *Client) GetTransactionByHex(ctx context.Context, hex string) (*Transaction, error) { - tx, err := bt.NewTxFromString(hex) + tx, err := trx.NewTransactionFromHex(hex) if err != nil { return nil, spverrors.Wrapf(err, "failed to parse transaction hex: %s", hex) } - return c.GetTransaction(ctx, "", tx.TxID()) + return c.GetTransaction(ctx, "", tx.TxID().String()) } // GetTransactions will get all the transactions from the Datastore @@ -346,7 +344,8 @@ func (c *Client) RevertTransaction(ctx context.Context, id string) error { // HandleTxCallback will update the broadcast callback transaction info, like: block height, block hash, status, bump. func (c *Client) HandleTxCallback(ctx context.Context, callbackResp *chainmodels.TXInfo) error { logger := c.options.logger - bump, err := bc.NewBUMPFromStr(callbackResp.MerklePath) + bump, err := trx.NewMerklePathFromHex(callbackResp.MerklePath) + if err != nil { logger.Err(err).Msgf("failed to parse merkle path from broadcast callback - tx: %v", callbackResp) return spverrors.Wrapf(err, "failed to parse merkle path from broadcast callback - tx: %v", callbackResp) diff --git a/engine/beef_bump.go b/engine/beef_bump.go index 5ce2190a..2dd715f2 100644 --- a/engine/beef_bump.go +++ b/engine/beef_bump.go @@ -4,8 +4,8 @@ import ( "context" "sort" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) func calculateMergedBUMP(txs []*Transaction) (BUMPs, error) { @@ -55,8 +55,8 @@ func validateBumps(bumps BUMPs) error { return nil } -func prepareBEEFFactors(ctx context.Context, tx *Transaction, store TransactionGetter) ([]*bt.Tx, []*Transaction, error) { - btTxsNeededForBUMP, txsNeededForBUMP, err := initializeRequiredTxsCollection(tx) +func prepareBEEFFactors(ctx context.Context, tx *Transaction, store TransactionGetter) ([]*trx.Transaction, []*Transaction, error) { + sdkTxsNeededForBUMP, txsNeededForBUMP, err := initializeRequiredTxsCollection(tx) if err != nil { return nil, nil, err } @@ -72,32 +72,32 @@ func prepareBEEFFactors(ctx context.Context, tx *Transaction, store TransactionG } for _, inputTx := range inputTxs { - inputBtTx, err := bt.NewTxFromString(inputTx.Hex) + inputSDKTx, err := trx.NewTransactionFromHex(inputTx.Hex) if err != nil { - return nil, nil, spverrors.Wrapf(err, "cannot convert to bt.Tx from hex (tx.ID: %s)", inputTx.ID) + return nil, nil, spverrors.Wrapf(err, "cannot convert to SDK Tx from hex (tx.ID: %s)", inputTx.ID) } txsNeededForBUMP = append(txsNeededForBUMP, inputTx) - btTxsNeededForBUMP = append(btTxsNeededForBUMP, inputBtTx) + sdkTxsNeededForBUMP = append(sdkTxsNeededForBUMP, inputSDKTx) if inputTx.BUMP.BlockHeight == 0 && len(inputTx.BUMP.Path) == 0 { - parentBtTransactions, parentTransactions, err := checkParentTransactions(ctx, store, inputBtTx) + parentSDKTransactions, parentTransactions, err := checkParentTransactions(ctx, store, inputSDKTx) if err != nil { return nil, nil, err } txsNeededForBUMP = append(txsNeededForBUMP, parentTransactions...) - btTxsNeededForBUMP = append(btTxsNeededForBUMP, parentBtTransactions...) + sdkTxsNeededForBUMP = append(sdkTxsNeededForBUMP, parentSDKTransactions...) } } - return btTxsNeededForBUMP, txsNeededForBUMP, nil + return sdkTxsNeededForBUMP, txsNeededForBUMP, nil } -func checkParentTransactions(ctx context.Context, store TransactionGetter, btTx *bt.Tx) ([]*bt.Tx, []*Transaction, error) { - parentTxIDs := make([]string, 0, len(btTx.Inputs)) - for _, txIn := range btTx.Inputs { - parentTxIDs = append(parentTxIDs, txIn.PreviousTxIDStr()) +func checkParentTransactions(ctx context.Context, store TransactionGetter, sdkTx *trx.Transaction) ([]*trx.Transaction, []*Transaction, error) { + parentTxIDs := make([]string, 0, len(sdkTx.Inputs)) + for _, txIn := range sdkTx.Inputs { + parentTxIDs = append(parentTxIDs, txIn.SourceTXID.String()) } parentTxs, err := getRequiredTransactions(ctx, parentTxIDs, store) @@ -106,26 +106,26 @@ func checkParentTransactions(ctx context.Context, store TransactionGetter, btTx } validTxs := make([]*Transaction, 0, len(parentTxs)) - validBtTxs := make([]*bt.Tx, 0, len(parentTxs)) + validSDKTxs := make([]*trx.Transaction, 0, len(parentTxs)) for _, parentTx := range parentTxs { - parentBtTx, err := bt.NewTxFromString(parentTx.Hex) + parentSDKTx, err := trx.NewTransactionFromHex(parentTx.Hex) if err != nil { - return nil, nil, spverrors.Wrapf(err, "cannot convert to bt.Tx from hex (tx.ID: %s)", parentTx.ID) + return nil, nil, spverrors.Wrapf(err, "cannot convert to SDK Tx from hex (tx.ID: %s)", parentTx.ID) } validTxs = append(validTxs, parentTx) - validBtTxs = append(validBtTxs, parentBtTx) + validSDKTxs = append(validSDKTxs, parentSDKTx) if parentTx.BUMP.BlockHeight == 0 && len(parentTx.BUMP.Path) == 0 { - parentValidBtTxs, parentValidTxs, err := checkParentTransactions(ctx, store, parentBtTx) + parentValidSDKTxs, parentValidTxs, err := checkParentTransactions(ctx, store, parentSDKTx) if err != nil { return nil, nil, err } validTxs = append(validTxs, parentValidTxs...) - validBtTxs = append(validBtTxs, parentValidBtTxs...) + validSDKTxs = append(validSDKTxs, parentValidSDKTxs...) } } - return validBtTxs, validTxs, nil + return validSDKTxs, validTxs, nil } func getRequiredTransactions(ctx context.Context, txIDs []string, store TransactionGetter) ([]*Transaction, error) { @@ -157,17 +157,17 @@ func getMissingTxs(txIDs []string, foundTxs []*Transaction) []string { return missingTxIDs } -func initializeRequiredTxsCollection(tx *Transaction) ([]*bt.Tx, []*Transaction, error) { - var btTxsNeededForBUMP []*bt.Tx +func initializeRequiredTxsCollection(tx *Transaction) ([]*trx.Transaction, []*Transaction, error) { + var sdkTxsNeededForBUMP []*trx.Transaction var txsNeededForBUMP []*Transaction - processedBtTx, err := bt.NewTxFromString(tx.Hex) + processedSDKTx, err := trx.NewTransactionFromHex(tx.Hex) if err != nil { - return nil, nil, spverrors.Wrapf(err, "cannot convert processed tx to bt.Tx from hex (tx.ID: %s)", tx.ID) + return nil, nil, spverrors.Wrapf(err, "cannot convert processed tx to SDK Tx from hex (tx.ID: %s)", tx.ID) } - btTxsNeededForBUMP = append(btTxsNeededForBUMP, processedBtTx) + sdkTxsNeededForBUMP = append(sdkTxsNeededForBUMP, processedSDKTx) txsNeededForBUMP = append(txsNeededForBUMP, tx) - return btTxsNeededForBUMP, txsNeededForBUMP, nil + return sdkTxsNeededForBUMP, txsNeededForBUMP, nil } diff --git a/engine/beef_tx.go b/engine/beef_tx.go index b27f93cf..4276edc3 100644 --- a/engine/beef_tx.go +++ b/engine/beef_tx.go @@ -4,8 +4,8 @@ import ( "context" "encoding/hex" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) const maxBeefVer = uint32(0xFFFF) // value from BRC-62 @@ -13,7 +13,7 @@ const maxBeefVer = uint32(0xFFFF) // value from BRC-62 type beefTx struct { version uint32 bumps BUMPs - transactions []*bt.Tx + transactions []*trx.Transaction } // ToBeef generates BEEF Hex for transaction @@ -40,7 +40,7 @@ func ToBeef(ctx context.Context, tx *Transaction, store TransactionGetter) (stri return beefHex, nil } -func toBeefHex(bumps BUMPs, parentTxs []*bt.Tx) (string, error) { +func toBeefHex(bumps BUMPs, parentTxs []*trx.Transaction) (string, error) { beef, err := newBeefTx(1, bumps, parentTxs) if err != nil { return "", spverrors.Wrapf(err, "ToBeefHex() error") @@ -54,7 +54,7 @@ func toBeefHex(bumps BUMPs, parentTxs []*bt.Tx) (string, error) { return hex.EncodeToString(beefBytes), nil } -func newBeefTx(version uint32, bumps BUMPs, parentTxs []*bt.Tx) (*beefTx, error) { +func newBeefTx(version uint32, bumps BUMPs, parentTxs []*trx.Transaction) (*beefTx, error) { if version > maxBeefVer { return nil, spverrors.Newf("version above 0x%X", maxBeefVer) } diff --git a/engine/beef_tx_bytes.go b/engine/beef_tx_bytes.go index 71b107b2..76edfbd5 100644 --- a/engine/beef_tx_bytes.go +++ b/engine/beef_tx_bytes.go @@ -1,8 +1,9 @@ package engine import ( + trx "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/bitcoin-sv/go-sdk/util" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) var ( @@ -18,18 +19,18 @@ func (beefTx *beefTx) toBeefBytes() ([]byte, error) { // get beef bytes beefSize := 0 - ver := bt.LittleEndianBytes(beefTx.version, 4) + ver := util.LittleEndianBytes(beefTx.version, 4) ver[2] = 0xBE ver[3] = 0xEF beefSize += len(ver) - nBUMPS := bt.VarInt(len(beefTx.bumps)).Bytes() + nBUMPS := trx.VarInt(len(beefTx.bumps)).Bytes() beefSize += len(nBUMPS) bumps := beefTx.bumps.Bytes() beefSize += len(bumps) - nTransactions := bt.VarInt(uint64(len(beefTx.transactions))).Bytes() + nTransactions := trx.VarInt(uint64(len(beefTx.transactions))).Bytes() beefSize += len(nTransactions) transactions := make([][]byte, 0, len(beefTx.transactions)) @@ -55,13 +56,13 @@ func (beefTx *beefTx) toBeefBytes() ([]byte, error) { return buffer, nil } -func toBeefBytes(tx *bt.Tx, bumps BUMPs) []byte { +func toBeefBytes(tx *trx.Transaction, bumps BUMPs) []byte { txBeefBytes := tx.Bytes() bumpIdx := getBumpPathIndex(tx, bumps) if bumpIdx > -1 { txBeefBytes = append(txBeefBytes, hasBUMP) - txBeefBytes = append(txBeefBytes, bt.VarInt(bumpIdx).Bytes()...) + txBeefBytes = append(txBeefBytes, trx.VarInt(bumpIdx).Bytes()...) } else { txBeefBytes = append(txBeefBytes, hasNoBUMP) } @@ -69,12 +70,12 @@ func toBeefBytes(tx *bt.Tx, bumps BUMPs) []byte { return txBeefBytes } -func getBumpPathIndex(tx *bt.Tx, bumps BUMPs) int { +func getBumpPathIndex(tx *trx.Transaction, bumps BUMPs) int { bumpIndex := -1 for i, bump := range bumps { for _, path := range bump.Path[0] { - if path.Hash == tx.TxID() { + if path.Hash == tx.TxID().String() { bumpIndex = i } } diff --git a/engine/beef_tx_sorting.go b/engine/beef_tx_sorting.go index 4f4fa982..80557ec4 100644 --- a/engine/beef_tx_sorting.go +++ b/engine/beef_tx_sorting.go @@ -1,10 +1,10 @@ package engine -import "github.com/libsv/go-bt/v2" +import trx "github.com/bitcoin-sv/go-sdk/transaction" -func kahnTopologicalSortTransactions(transactions []*bt.Tx) []*bt.Tx { +func kahnTopologicalSortTransactions(transactions []*trx.Transaction) []*trx.Transaction { txByID, incomingEdgesMap, zeroIncomingEdgeQueue := prepareSortStructures(transactions) - result := make([]*bt.Tx, 0, len(transactions)) + result := make([]*trx.Transaction, 0, len(transactions)) for len(zeroIncomingEdgeQueue) > 0 { txID := zeroIncomingEdgeQueue[0] @@ -20,14 +20,14 @@ func kahnTopologicalSortTransactions(transactions []*bt.Tx) []*bt.Tx { return result } -func prepareSortStructures(dag []*bt.Tx) (txByID map[string]*bt.Tx, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) { +func prepareSortStructures(dag []*trx.Transaction) (txByID map[string]*trx.Transaction, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) { dagLen := len(dag) - txByID = make(map[string]*bt.Tx, dagLen) + txByID = make(map[string]*trx.Transaction, dagLen) incomingEdgesMap = make(map[string]int, dagLen) for _, tx := range dag { - txByID[tx.TxID()] = tx // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time - incomingEdgesMap[tx.TxID()] = 0 + txByID[tx.TxID().String()] = tx // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + incomingEdgesMap[tx.TxID().String()] = 0 } calculateIncomingEdges(incomingEdgesMap, txByID) @@ -36,11 +36,11 @@ func prepareSortStructures(dag []*bt.Tx) (txByID map[string]*bt.Tx, incomingEdge return } -func calculateIncomingEdges(inDegree map[string]int, txByID map[string]*bt.Tx) { +func calculateIncomingEdges(inDegree map[string]int, txByID map[string]*trx.Transaction) { for _, tx := range txByID { for _, input := range tx.Inputs { - inputUtxoTxID := input.PreviousTxIDStr() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time - if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in + inputUtxoTxID := input.SourceTXID.String() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in inDegree[inputUtxoTxID]++ } } @@ -59,9 +59,9 @@ func getTxWithZeroIncomingEdges(incomingEdgesMap map[string]int) []string { return zeroIncomingEdgeQueue } -func removeTxFromIncomingEdges(tx *bt.Tx, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) []string { +func removeTxFromIncomingEdges(tx *trx.Transaction, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) []string { for _, input := range tx.Inputs { - neighborID := input.PreviousTxIDStr() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + neighborID := input.SourceTXID.String() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time incomingEdgesMap[neighborID]-- if incomingEdgesMap[neighborID] == 0 { @@ -72,7 +72,7 @@ func removeTxFromIncomingEdges(tx *bt.Tx, incomingEdgesMap map[string]int, zeroI return zeroIncomingEdgeQueue } -func reverseInPlace(collection []*bt.Tx) { +func reverseInPlace(collection []*trx.Transaction) { for i, j := 0, len(collection)-1; i < j; i, j = i+1, j-1 { collection[i], collection[j] = collection[j], collection[i] } diff --git a/engine/beef_tx_sorting_test.go b/engine/beef_tx_sorting_test.go index 84a40003..8b6ac7a7 100644 --- a/engine/beef_tx_sorting_test.go +++ b/engine/beef_tx_sorting_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "testing" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/libsv/go-bt/v2" "github.com/stretchr/testify/assert" ) @@ -12,7 +13,7 @@ import ( func Test_kahnTopologicalSortTransaction(t *testing.T) { tCases := []struct { name string - expectedSortedTransactions []*bt.Tx + expectedSortedTransactions []*trx.Transaction }{ { name: "txs with necessary data only", @@ -40,7 +41,7 @@ func Test_kahnTopologicalSortTransaction(t *testing.T) { } } -func getTxsFromOldestToNewestWithNecessaryDataOnly() []*bt.Tx { +func getTxsFromOldestToNewestWithNecessaryDataOnly() []*trx.Transaction { // create related transactions from oldest to newest oldestTx := createTx() secondTx := createTx(oldestTx) @@ -53,7 +54,7 @@ func getTxsFromOldestToNewestWithNecessaryDataOnly() []*bt.Tx { newestTx := createTx(eightTx) - txsFromOldestToNewest := []*bt.Tx{ + txsFromOldestToNewest := []*trx.Transaction{ oldestTx, secondTx, thirdTx, @@ -68,7 +69,7 @@ func getTxsFromOldestToNewestWithNecessaryDataOnly() []*bt.Tx { return txsFromOldestToNewest } -func getTxsFromOldestToNewestWithUnnecessaryData() []*bt.Tx { +func getTxsFromOldestToNewestWithUnnecessaryData() []*trx.Transaction { unnecessaryParentTx1 := createTx() unnecessaryParentTx2 := createTx() unnecessaryParentTx3 := createTx() @@ -101,7 +102,7 @@ func getTxsFromOldestToNewestWithUnnecessaryData() []*bt.Tx { return txsFromOldestToNewest } -func createTx(inputsParents ...*bt.Tx) *bt.Tx { +func createTx(inputsParents ...*trx.Transaction) *bt.Tx { inputs := make([]*bt.Input, 0) for _, parent := range inputsParents { diff --git a/engine/ef_tx.go b/engine/ef_tx.go index 0cca5951..c2f94c94 100644 --- a/engine/ef_tx.go +++ b/engine/ef_tx.go @@ -4,7 +4,7 @@ import ( "context" "encoding/hex" - "github.com/libsv/go-bt/v2" + trx "github.com/bitcoin-sv/go-sdk/transaction" ) // ToEfHex generates Extended Format hex of transaction @@ -13,7 +13,7 @@ func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efH if btTx == nil { var err error - btTx, err = bt.NewTxFromString(tx.Hex) + btTx, err = trx.NewTransactionFromHex(tx.Hex) if err != nil { return "", false } @@ -36,11 +36,11 @@ func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efH return hex.EncodeToString(btTx.ExtendedBytes()), true } -func hydrate(ctx context.Context, tx *bt.Tx, store TransactionGetter) (ok bool) { +func hydrate(ctx context.Context, tx *trx.Transaction, store TransactionGetter) (ok bool) { txToGet := make([]string, 0, len(tx.Inputs)) for _, input := range tx.Inputs { - txToGet = append(txToGet, input.PreviousTxIDStr()) + txToGet = append(txToGet, input.SourceTXID.String()) } parentTxs, err := store.GetTransactionsByIDs(ctx, txToGet) @@ -52,15 +52,15 @@ func hydrate(ctx context.Context, tx *bt.Tx, store TransactionGetter) (ok bool) } for _, input := range tx.Inputs { - prevTxID := input.PreviousTxIDStr() + prevTxID := input.SourceTXID.String() pTx := find(parentTxs, func(tx *Transaction) bool { return tx.ID == prevTxID }) - pbtTx, err := bt.NewTxFromString((*pTx).Hex) + pbtTx, err := trx.NewTransactionFromHex((*pTx).Hex) if err != nil { return false } - o := pbtTx.Outputs[input.PreviousTxOutIndex] + o := pbtTx.Outputs[input.SourceTxOutIndex] input.PreviousTxSatoshis = o.Satoshis input.PreviousTxScript = o.LockingScript } diff --git a/engine/examples/client/pike/main.go b/engine/examples/client/pike/main.go index 341b1187..dd7cb96f 100644 --- a/engine/examples/client/pike/main.go +++ b/engine/examples/client/pike/main.go @@ -4,9 +4,9 @@ import ( "encoding/hex" "fmt" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/pike" "github.com/bitcoin-sv/spv-wallet/engine/script/template" - "github.com/libsv/go-bk/bec" ) func main() { @@ -17,7 +17,7 @@ func main() { if err != nil { panic(err) } - senderPubKey, err := bec.ParsePubKey(senderPublicKeyBytes, bec.S256()) + senderPubKey, err := ec.ParsePubKey(senderPublicKeyBytes) if err != nil { panic(err) } @@ -27,7 +27,7 @@ func main() { if err != nil { panic(err) } - receiverPubKey, err := bec.ParsePubKey(receiverPublicKeyBytes, bec.S256()) + receiverPubKey, err := ec.ParsePubKey(receiverPublicKeyBytes) if err != nil { panic(err) } diff --git a/engine/model_access_keys.go b/engine/model_access_keys.go index a2577ed4..49ea0a33 100644 --- a/engine/model_access_keys.go +++ b/engine/model_access_keys.go @@ -6,11 +6,11 @@ import ( "encoding/hex" "errors" + primitives "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/datastore" customTypes "github.com/bitcoin-sv/spv-wallet/engine/datastore/customtypes" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" ) // AccessKey is an object representing an access key model @@ -36,8 +36,8 @@ type AccessKey struct { // newAccessKey will start a new model func newAccessKey(xPubID string, opts ...ModelOps) *AccessKey { - privateKey, _ := bitcoin.CreatePrivateKey() - publicKey := hex.EncodeToString(privateKey.PubKey().SerialiseCompressed()) + privateKey, _ := primitives.NewPrivateKey() + publicKey := hex.EncodeToString(privateKey.PubKey().SerializeCompressed()) id := utils.Hash(publicKey) return &AccessKey{ @@ -47,7 +47,7 @@ func newAccessKey(xPubID string, opts ...ModelOps) *AccessKey { RevokedAt: customTypes.NullTime{NullTime: sql.NullTime{ Valid: false, }}, - Key: hex.EncodeToString(privateKey.Serialise()), + Key: hex.EncodeToString(privateKey.Serialize()), } } diff --git a/engine/model_access_keys_test.go b/engine/model_access_keys_test.go index 55348f52..9f0c24a1 100644 --- a/engine/model_access_keys_test.go +++ b/engine/model_access_keys_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + primitives "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,11 +21,11 @@ func Test_newAccessKey(t *testing.T) { assert.Equal(t, 64, len(key.GetID())) assert.Equal(t, 64, len(key.Key)) - privateKey, _ := bitcoin.PrivateKeyFromString(key.Key) + privateKey, _ := primitives.PrivateKeyFromHex(key.Key) assert.IsType(t, bec.PrivateKey{}, *privateKey) publicKey := privateKey.PubKey() assert.IsType(t, bec.PublicKey{}, *publicKey) - id := utils.Hash(hex.EncodeToString(publicKey.SerialiseCompressed())) + id := utils.Hash(hex.EncodeToString(publicKey.SerializeCompressed())) assert.Equal(t, id, key.ID) }) diff --git a/engine/model_bump.go b/engine/model_bump.go index 2f0a8252..80fb0f9a 100644 --- a/engine/model_bump.go +++ b/engine/model_bump.go @@ -9,10 +9,10 @@ import ( "reflect" "sort" + trx "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/bitcoin-sv/go-sdk/util" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bc" - "github.com/libsv/go-bt/v2" ) const maxBumpHeight = 64 @@ -164,7 +164,7 @@ func calculateMerkleRoot(baseLeaf BUMPLeaf, bump *BUMP) (string, error) { leftNode, rightNode := prepareNodes(baseLeaf, offset, *leafInPair, newOffset) - str, err := bc.MerkleTreeParentStr(leftNode, rightNode) + str, err := utils.MerkleTreeParentStr(leftNode, rightNode) if err != nil { return "", spverrors.Wrapf(err, "failed to calculate merkle tree parent for %s and %s", leftNode, rightNode) } @@ -237,7 +237,7 @@ func (bump *BUMP) Hex() string { func (bump *BUMP) bytesBuffer() *bytes.Buffer { var buff bytes.Buffer - buff.WriteString(hex.EncodeToString(bt.VarInt(bump.BlockHeight).Bytes())) + buff.WriteString(hex.EncodeToString(trx.VarInt(bump.BlockHeight).Bytes())) height := len(bump.Path) buff.WriteString(leadingZeroInt(height)) @@ -246,12 +246,12 @@ func (bump *BUMP) bytesBuffer() *bytes.Buffer { nodes := bump.Path[i] nLeafs := len(nodes) - buff.WriteString(hex.EncodeToString(bt.VarInt(nLeafs).Bytes())) + buff.WriteString(hex.EncodeToString(trx.VarInt(nLeafs).Bytes())) for _, n := range nodes { - buff.WriteString(hex.EncodeToString(bt.VarInt(n.Offset).Bytes())) + buff.WriteString(hex.EncodeToString(trx.VarInt(n.Offset).Bytes())) buff.WriteString(fmt.Sprintf("%02x", flags(n.TxID, n.Duplicate))) decodedHex, _ := hex.DecodeString(n.Hash) - buff.WriteString(hex.EncodeToString(bt.ReverseBytes(decodedHex))) + buff.WriteString(hex.EncodeToString(util.ReverseBytes(decodedHex))) } } return &buff @@ -339,7 +339,7 @@ func (bumps BUMPs) Value() (driver.Value, error) { return string(marshal), nil } -func bcBumpToBUMP(bcBump *bc.BUMP) BUMP { +func bcBumpToBUMP(bcBump *trx.MerklePath) BUMP { path := make([][]BUMPLeaf, len(bcBump.Path)) for i := range bcBump.Path { path[i] = make([]BUMPLeaf, len(bcBump.Path[i])) diff --git a/engine/pike/pike.go b/engine/pike/pike.go index f523e299..0500aee4 100644 --- a/engine/pike/pike.go +++ b/engine/pike/pike.go @@ -17,11 +17,11 @@ package pike import ( "fmt" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/script/template" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/types/type42" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bt/v2/bscript" ) // GenerateOutputsTemplate creates a Pike output template @@ -34,11 +34,11 @@ func GenerateOutputsTemplate(satoshis uint64) ([]*template.OutputTemplate, error } // GenerateLockingScriptsFromTemplates converts Pike outputs templates to scripts -func GenerateLockingScriptsFromTemplates(outputsTemplate []*template.OutputTemplate, senderPubKey, receiverPubKey *bec.PublicKey, reference string) ([]string, error) { +func GenerateLockingScriptsFromTemplates(outputsTemplate []*template.OutputTemplate, senderPubKey, receiverPubKey *ec.PublicKey, reference string) ([]string, error) { lockingScripts := make([]string, len(outputsTemplate)) for idx, output := range outputsTemplate { - templateScript, err := bscript.NewFromHexString(output.Script) + templateScript, err := script.NewFromHex(output.Script) if err != nil { return nil, spverrors.Wrapf(err, "error creating script from hex string") } @@ -53,7 +53,7 @@ func GenerateLockingScriptsFromTemplates(outputsTemplate []*template.OutputTempl return nil, spverrors.Wrapf(err, "error evaluating template script") } - finalScript := bscript.Script(scriptBytes) + finalScript := script.Script(scriptBytes) lockingScripts[idx] = finalScript.String() } diff --git a/engine/record_tx_strategy_internal_incoming_tx.go b/engine/record_tx_strategy_internal_incoming_tx.go index b24ec37f..612a3ef6 100644 --- a/engine/record_tx_strategy_internal_incoming_tx.go +++ b/engine/record_tx_strategy_internal_incoming_tx.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) type internalIncomingTx struct { @@ -36,7 +36,7 @@ func (strategy *internalIncomingTx) Validate() error { return spverrors.ErrEmptyTx } - if _, err := bt.NewTxFromString(strategy.Tx.Hex); err != nil { + if _, err := trx.NewTransactionFromHex(strategy.Tx.Hex); err != nil { return spverrors.ErrInvalidHex } diff --git a/engine/script/template/evaluate.go b/engine/script/template/evaluate.go index bab8c683..f999460e 100644 --- a/engine/script/template/evaluate.go +++ b/engine/script/template/evaluate.go @@ -1,11 +1,11 @@ package template import ( + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + script "github.com/bitcoin-sv/go-sdk/script" + "github.com/bitcoin-sv/go-sdk/script/interpreter" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/crypto" - "github.com/libsv/go-bt/v2/bscript" - "github.com/libsv/go-bt/v2/bscript/interpreter" + "github.com/bitcoin-sv/spv-wallet/engine/utils" ) // Evaluate processes a given Bitcoin script by parsing it, replacing certain opcodes @@ -18,8 +18,8 @@ import ( // // Returns: // - A byte array representing the evaluated script, or nil if an error occurs. -func Evaluate(script []byte, pubKey *bec.PublicKey) ([]byte, error) { - s := bscript.Script(script) +func Evaluate(scriptBytes []byte, pubKey *ec.PublicKey) ([]byte, error) { + s := script.Script(scriptBytes) parser := interpreter.DefaultOpcodeParser{} parsedScript, err := parser.Parse(&s) @@ -35,13 +35,13 @@ func Evaluate(script []byte, pubKey *bec.PublicKey) ([]byte, error) { } // Serialize the public key to compressed format - dPKBytes := pubKey.SerialiseCompressed() + dPKBytes := pubKey.SerializeCompressed() // Apply Hash160 (SHA-256 followed by RIPEMD-160) to the compressed public key - dPKHash := crypto.Hash160(dPKBytes) + dPKHash := utils.Hash160(dPKBytes) // Create a new script with the public key hash - newScript := new(bscript.Script) + newScript := new(script.Script) if err = newScript.AppendPushData(dPKHash); err != nil { return nil, spverrors.Wrapf(err, "failed to convert pubkeyhash value into opcodes") } @@ -56,9 +56,9 @@ func Evaluate(script []byte, pubKey *bec.PublicKey) ([]byte, error) { evaluated := make([]interpreter.ParsedOpcode, 0, len(parsedScript)) for _, op := range parsedScript { switch op.Value() { - case bscript.OpPUBKEYHASH: + case script.OpPUBKEYHASH: evaluated = append(evaluated, pkhParsed...) - case bscript.OpPUBKEY: + case script.OpPUBKEY: return nil, spverrors.Newf("OP_PUBKEY not supported yet") default: evaluated = append(evaluated, op) diff --git a/engine/tx_repository.go b/engine/tx_repository.go index 303bea26..cc5d0cea 100644 --- a/engine/tx_repository.go +++ b/engine/tx_repository.go @@ -4,9 +4,9 @@ import ( "context" "errors" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt" ) // getTransactionByID will get the model from a given transaction ID @@ -178,10 +178,10 @@ func getTransactionsCountInternal(ctx context.Context, conditions map[string]int } func getTransactionByHex(ctx context.Context, hex string, opts ...ModelOps) (*Transaction, error) { - btTx, err := bt.NewTxFromString(hex) + tx, err := trx.NewTransactionFromHex(hex) if err != nil { return nil, spverrors.Wrapf(err, "failed to parse transaction hex") } - return getTransactionByID(ctx, "", btTx.GetTxID(), opts...) + return getTransactionByID(ctx, "", tx.TxID().String(), opts...) } diff --git a/engine/tx_sync_task.go b/engine/tx_sync_task.go index 2fa65a82..e205ff4b 100644 --- a/engine/tx_sync_task.go +++ b/engine/tx_sync_task.go @@ -5,8 +5,8 @@ import ( "errors" "time" + "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bc" "github.com/rs/zerolog" ) @@ -109,7 +109,7 @@ func processSyncTransactions(ctx context.Context, client *Client) { continue } - bump, err := bc.NewBUMPFromStr(txInfo.MerklePath) + bump, err := transaction.NewMerklePathFromHex(txInfo.MerklePath) if err != nil { //ARC sometimes returns a TXStatus SEEN_ON_NETWORK, but with zero data logger.Warn().Err(err).Str("txID", txID).Msg("Cannot parse BUMP") diff --git a/engine/types/type42/linking_key.go b/engine/types/type42/linking_key.go index 85fc7e86..1e5a0863 100644 --- a/engine/types/type42/linking_key.go +++ b/engine/types/type42/linking_key.go @@ -5,8 +5,8 @@ import ( "crypto/sha256" "math/big" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bk/bec" ) // calculateHMAC calculates the HMAC of the provided public shared secret using a reference string. @@ -33,7 +33,7 @@ func calculateHMAC(pubSharedSecret []byte, message string) ([]byte, error) { // calculateLinkedPublicKey calculates the dedicated public key (dPK) using the HMAC result and the receiver's public key. // The HMAC result is used as a scalar to perform elliptic curve scalar multiplication and point addition. // Returns the resulting public key or an error if the calculation fails. -func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) (*bec.PublicKey, error) { +func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *ec.PublicKey) (*ec.PublicKey, error) { if len(hmacResult) == 0 { return nil, spverrors.Newf("HMAC result is empty") } @@ -43,7 +43,7 @@ func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) // Convert HMAC result to a big integer hn := new(big.Int).SetBytes(hmacResult) - curve := bec.S256() // Use secp256k1 curve + curve := ec.S256() // Use secp256k1 curve hn.Mod(hn, curve.Params().N) // Ensure the scalar is within the curve order // Perform scalar multiplication: hn * G @@ -58,7 +58,7 @@ func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) } // Create the dedicated public key - dPK := &bec.PublicKey{ + dPK := &ec.PublicKey{ Curve: curve, X: dedicatedPubKeyX, Y: dedicatedPubKeyY, @@ -68,7 +68,7 @@ func calculateLinkedPublicKey(hmacResult []byte, receiverPubKey *bec.PublicKey) // DeriveLinkedKey derives a child public key from the source public key and link it with public key // with use of invoiceNumber as reference of this derivation. -func DeriveLinkedKey(source *bec.PublicKey, linkPubKey *bec.PublicKey, invoiceNumber string) (*bec.PublicKey, error) { +func DeriveLinkedKey(source *ec.PublicKey, linkPubKey *ec.PublicKey, invoiceNumber string) (*ec.PublicKey, error) { if source == nil || linkPubKey == nil { return nil, spverrors.Newf("source or receiver public key is nil") } @@ -82,7 +82,7 @@ func DeriveLinkedKey(source *bec.PublicKey, linkPubKey *bec.PublicKey, invoiceNu } // Compute the shared secret - publicKeyBytes := source.SerialiseCompressed() + publicKeyBytes := source.SerializeCompressed() // Compute the HMAC result hmacResult, err := calculateHMAC(publicKeyBytes, invoiceNumber) diff --git a/engine/utils/destination_types.go b/engine/utils/destination_types.go index 047140ba..0ef2e5c7 100644 --- a/engine/utils/destination_types.go +++ b/engine/utils/destination_types.go @@ -4,25 +4,24 @@ import ( "encoding/hex" "regexp" - "github.com/bitcoinschema/go-bitcoin/v2" - bscript2 "github.com/libsv/go-bt/v2/bscript" + "github.com/bitcoin-sv/go-sdk/script" ) const ( // ScriptTypePubKey alias from bscript - ScriptTypePubKey = bscript2.ScriptTypePubKey + ScriptTypePubKey = script.ScriptTypePubKey // ScriptTypePubKeyHash alias from bscript - ScriptTypePubKeyHash = bscript2.ScriptTypePubKeyHash + ScriptTypePubKeyHash = script.ScriptTypePubKeyHash // ScriptTypeNullData alias from bscript - ScriptTypeNullData = bscript2.ScriptTypeNullData + ScriptTypeNullData = script.ScriptTypeNullData // ScriptTypeMultiSig alias from bscript - ScriptTypeMultiSig = bscript2.ScriptTypeMultiSig + ScriptTypeMultiSig = script.ScriptTypeMultiSig // ScriptTypeNonStandard alias from bscript - ScriptTypeNonStandard = bscript2.ScriptTypeNonStandard + ScriptTypeNonStandard = script.ScriptTypeNonStandard // ScriptHashType is the type for the deprecated script hash ScriptHashType = "scripthash" @@ -94,10 +93,11 @@ var ( // IsP2PK Check whether the given string is a p2pk output // This is the original destination type that was used in the first blocks by Satoshi func IsP2PK(lockingScript string) bool { - script, err := bscript2.NewFromHexString(lockingScript) + script, err := script.NewFromHex(lockingScript) if err != nil { return false } + return script.IsP2PK() } @@ -138,10 +138,11 @@ func IsRunJS(lockingScript string) bool { // IsMultiSig Check whether the given string is a multi-sig locking script func IsMultiSig(lockingScript string) bool { - script, err := bscript2.NewFromHexString(lockingScript) + script, err := script.NewFromHex(lockingScript) if err != nil { return false } + return script.IsMultiSigOut() } @@ -188,29 +189,31 @@ func GetDestinationTypeRegex(destType string) *regexp.Regexp { func GetAddressFromScript(lockingScript string) (address string) { scriptType := GetDestinationType(lockingScript) if scriptType == ScriptTypePubKeyHash { - address, _ = bitcoin.GetAddressFromScript(lockingScript) + decodedAddr, _ := script.NewAddressFromPublicKeyHash([]byte(lockingScript), true) + address = decodedAddr.AddressString } else if scriptType == ScriptTypePubKey { - s, err := bscript2.NewFromHexString(lockingScript) // no need for error check, if type is set, it should be valid + s, err := script.NewFromHex(lockingScript) // no need for error check, if type is set, it should be valid if err != nil { return "" } - var parts [][]byte - parts, err = bscript2.DecodeParts(*s) + var parts []*script.ScriptChunk + parts, err = script.DecodeScript(*s) if err != nil { return "" } - pubkeyHex := hex.EncodeToString(parts[0]) + pubkeyHex := hex.EncodeToString(parts[0].Data) - var addressScript *bscript2.Address - addressScript, err = bitcoin.GetAddressFromPubKeyString(pubkeyHex, len(parts[0]) <= 33) + var addressScript *script.Address + addressScript, err = script.NewAddressFromPublicKeyString(pubkeyHex, len(parts[0].Data) <= 33) if err == nil { address = addressScript.AddressString } } else if scriptType == ScriptTypeTokenStas { // stas is just a normal PubKeyHash with more data appended - address, _ = bitcoin.GetAddressFromScript(lockingScript[:50]) + decodedAddr, _ := script.NewAddressFromPublicKeyHash([]byte(lockingScript[:50]), true) + address = decodedAddr.AddressString // } else if scriptType == ScriptTypeTokenSensible { // sensible tokens do not have the receiving address in the token output, but in another output // sensible does not seem to be a utxo protocol, but an output protocol (all outputs of the tx matter) diff --git a/engine/utils/destination_types_test.go b/engine/utils/destination_types_test.go index bfadcd6a..38a19711 100644 --- a/engine/utils/destination_types_test.go +++ b/engine/utils/destination_types_test.go @@ -3,7 +3,7 @@ package utils import ( "testing" - bscript2 "github.com/libsv/go-bt/v2/bscript" + "github.com/bitcoin-sv/go-sdk/script" "github.com/stretchr/testify/assert" ) @@ -172,15 +172,15 @@ func TestGetDestinationType(t *testing.T) { t.Parallel() t.Run("no match - non standard", func(t *testing.T) { - assert.Equal(t, bscript2.ScriptTypeNonStandard, GetDestinationType("nope")) + assert.Equal(t, script.ScriptTypeNonStandard, GetDestinationType("nope")) }) t.Run("ScriptTypePubKey", func(t *testing.T) { - assert.Equal(t, bscript2.ScriptTypePubKey, GetDestinationType(p2pkHex)) + assert.Equal(t, script.ScriptTypePubKey, GetDestinationType(p2pkHex)) }) t.Run("ScriptTypePubKeyHash", func(t *testing.T) { - assert.Equal(t, bscript2.ScriptTypePubKeyHash, GetDestinationType(p2pkhHex)) + assert.Equal(t, script.ScriptTypePubKeyHash, GetDestinationType(p2pkhHex)) }) t.Run("ScriptHashType", func(t *testing.T) { @@ -192,11 +192,11 @@ func TestGetDestinationType(t *testing.T) { }) t.Run("op return - ScriptTypeNullData", func(t *testing.T) { - assert.Equal(t, bscript2.ScriptTypeNullData, GetDestinationType(opReturnHex)) + assert.Equal(t, script.ScriptTypeNullData, GetDestinationType(opReturnHex)) }) t.Run("multisig - ScriptTypeMultiSig", func(t *testing.T) { - assert.Equal(t, bscript2.ScriptTypeMultiSig, GetDestinationType(multisigHex)) + assert.Equal(t, script.ScriptTypeMultiSig, GetDestinationType(multisigHex)) }) t.Run("stas - ScriptTypeTokenStas", func(t *testing.T) { diff --git a/engine/utils/encrypt.go b/engine/utils/encrypt.go index ca104b08..ce732014 100644 --- a/engine/utils/encrypt.go +++ b/engine/utils/encrypt.go @@ -1,23 +1,23 @@ package utils import ( + ecies "github.com/bitcoin-sv/go-sdk/compat/ecies" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/bitcoinschema/go-bitcoin/v2" ) // Encrypt will encrypt the value using the encryption key func Encrypt(encryptionKey, encryptValue string) (string, error) { - // Get the keys seeded with the encryption key - privateKey, _, err := bitcoin.PrivateAndPublicKeys(encryptionKey) + privateKey, err := ec.PrivateKeyFromHex(encryptionKey) if err != nil { return "", spverrors.Wrapf(err, "error getting private keys from encryption key") } // Encrypt the private key var encryptedValue string - if encryptedValue, err = bitcoin.EncryptWithPrivateKey( - privateKey, encryptValue, + if encryptedValue, err = ecies.EncryptSingle( + encryptValue, privateKey, ); err != nil { return "", spverrors.Wrapf(err, "error encrypting data with private key") } @@ -27,6 +27,8 @@ func Encrypt(encryptionKey, encryptValue string) (string, error) { // Decrypt will take the data and decrypt using a char(64) key func Decrypt(encryptionKey, data string) (string, error) { - keyString, err := bitcoin.DecryptWithPrivateKeyString(encryptionKey, data) + // keyString, err := bitcoin.DecryptWithPrivateKeyString(encryptionKey, data) + privateKey, err := ec.PrivateKeyFromHex(encryptionKey) + keyString, err := ecies.DecryptSingle(data, privateKey) return keyString, spverrors.Wrapf(err, "error decrypting data with private key") } diff --git a/engine/utils/fees.go b/engine/utils/fees.go index 9ac75229..013ed7a1 100644 --- a/engine/utils/fees.go +++ b/engine/utils/fees.go @@ -3,12 +3,16 @@ package utils import ( "encoding/hex" "fmt" - - "github.com/libsv/go-bt/v2" ) -// FeeUnit fee unit imported from go-bt/v2 -type FeeUnit bt.FeeUnit +// Imported from deprecated go-bt library +// FeeUnit displays the amount of Satoshis needed +// for a specific amount of Bytes in a transaction +// see https://github.com/bitcoin-sv-specs/brfc-misc/tree/master/feespec +type FeeUnit struct { + Satoshis int `json:"satoshis"` // Fee in satoshis of the amount of Bytes + Bytes int `json:"bytes"` // Number of bytes that the Fee covers +} // IsLowerThan compare two fee units func (f *FeeUnit) IsLowerThan(other *FeeUnit) bool { diff --git a/engine/utils/keys.go b/engine/utils/keys.go index 44be0806..de3825c2 100644 --- a/engine/utils/keys.go +++ b/engine/utils/keys.go @@ -1,11 +1,11 @@ package utils import ( + bip32 "github.com/bitcoin-sv/go-sdk/compat/bip32" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/bip32" - "github.com/libsv/go-bt/v2/bscript" ) // DeriveChildKeyFromHex derive the child extended key from the hex string @@ -28,12 +28,12 @@ func DeriveChildKeyFromHex(hdKey *bip32.ExtendedKey, hexHash string) (*bip32.Ext } // DerivePublicKey will derive the internal and external address from a key -func DerivePublicKey(hdKey *bip32.ExtendedKey, chain uint32, num uint32) (*bec.PublicKey, error) { +func DerivePublicKey(hdKey *bip32.ExtendedKey, chain uint32, num uint32) (*ec.PublicKey, error) { if hdKey == nil { return nil, ErrHDKeyNil } - pubKeys, err := bitcoin.GetPublicKeysForPath(hdKey, num) + pubKeys, err := compat.GetPublicKeysForPath(hdKey, num) if err != nil { return nil, spverrors.Wrapf(err, "failed to derive public key") } @@ -50,7 +50,7 @@ func ValidateXPub(rawKey string) (*bip32.ExtendedKey, error) { } // Parse the xPub into an HD key - hdKey, err := bitcoin.GetHDKeyFromExtendedPublicKey(rawKey) + hdKey, err := compat.GetHDKeyFromExtendedPublicKey(rawKey) if err != nil { return nil, spverrors.Wrapf(err, "failed to parse xpub") } else if hdKey.String() != rawKey { // Sanity check (might not be needed) @@ -68,18 +68,18 @@ func DeriveAddress(hdKey *bip32.ExtendedKey, chain uint32, num uint32) (address } var child *bip32.ExtendedKey - if child, err = bitcoin.GetHDKeyByPath(hdKey, chain, num); err != nil { + if child, err = compat.GetHDKeyByPath(hdKey, chain, num); err != nil { return "", spverrors.Wrapf(err, "failed to derive child key") } - var pubKey *bec.PublicKey + var pubKey *ec.PublicKey if pubKey, err = child.ECPubKey(); err != nil { // Should never error since the previous method ensures a valid hdKey return "", spverrors.Wrapf(err, "failed to derive public key") } - var addressScript *bscript.Address - if addressScript, err = bitcoin.GetAddressFromPubKey(pubKey, true); err != nil { + var addressScript *script.Address + if addressScript, err = script.NewAddressFromPublicKey(pubKey, true); err != nil { // Should never error if the pubKeys are valid keys return "", spverrors.Wrapf(err, "failed to derive address") } @@ -97,7 +97,7 @@ func DeriveAddresses(hdKey *bip32.ExtendedKey, num uint32) (external, internal s // Derive the address var addresses []string - if addresses, err = bitcoin.GetAddressesForPath( + if addresses, err = compat.GetAddressesForPath( hdKey, num, ); err != nil { return @@ -110,7 +110,7 @@ func DeriveAddresses(hdKey *bip32.ExtendedKey, num uint32) (external, internal s } // DerivePrivateKeyFromHex will derive the private key from the extended key using the hex as the derivation paths -func DerivePrivateKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*bec.PrivateKey, error) { +func DerivePrivateKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*ec.PrivateKey, error) { if hdKey == nil { return nil, ErrHDKeyNil } @@ -120,7 +120,7 @@ func DerivePrivateKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*bec.P return nil, err } - var privKey *bec.PrivateKey + var privKey *ec.PrivateKey if privKey, err = childKey.ECPrivKey(); err != nil { return nil, spverrors.Wrapf(err, "failed to derive private key") } @@ -129,7 +129,7 @@ func DerivePrivateKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*bec.P } // DerivePublicKeyFromHex will derive the public key from the extended key using the hex as the derivation paths -func DerivePublicKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*bec.PublicKey, error) { +func DerivePublicKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*ec.PublicKey, error) { if hdKey == nil { return nil, ErrHDKeyNil } @@ -139,7 +139,7 @@ func DerivePublicKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*bec.Pu return nil, err } - var pubKey *bec.PublicKey + var pubKey *ec.PublicKey if pubKey, err = childKey.ECPubKey(); err != nil { return nil, spverrors.Wrapf(err, "failed to derive public key") } diff --git a/engine/utils/keys_test.go b/engine/utils/keys_test.go index 1f4e73aa..acc83cb6 100644 --- a/engine/utils/keys_test.go +++ b/engine/utils/keys_test.go @@ -3,7 +3,7 @@ package utils import ( "testing" - "github.com/libsv/go-bk/bip32" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,8 +14,7 @@ const ( // Test_DeriveAddresses will test the method DeriveAddresses() func Test_DeriveAddresses(t *testing.T) { - - xPub, errX := bip32.NewKeyFromString(testXPub) + xPub, errX := compat.NewKeyFromString(testXPub) require.NoError(t, errX) t.Run("DeriveAddresses 1", func(t *testing.T) { @@ -35,8 +34,7 @@ func Test_DeriveAddresses(t *testing.T) { // Test_DeriveAddress will test the method DeriveAddress() func Test_DeriveAddress(t *testing.T) { - - xPub, errX := bip32.NewKeyFromString(testXPub) + xPub, errX := compat.NewKeyFromString(testXPub) require.NoError(t, errX) t.Run("DeriveAddresses 1", func(t *testing.T) { @@ -62,8 +60,7 @@ func Test_DeriveAddress(t *testing.T) { // Benchmark_DeriveAddresses will benchmark the method DeriveAddresses() func Benchmark_DeriveAddresses(b *testing.B) { - - xPub, errX := bip32.NewKeyFromString(testXPub) + xPub, errX := compat.NewKeyFromString(testXPub) if errX != nil { b.Fail() } @@ -76,8 +73,7 @@ func Benchmark_DeriveAddresses(b *testing.B) { // Benchmark_DeriveAddress will benchmark the method DeriveAddress() func Benchmark_DeriveAddress(b *testing.B) { - - xPub, errX := bip32.NewKeyFromString(testXPub) + xPub, errX := compat.NewKeyFromString(testXPub) if errX != nil { b.Fail() } diff --git a/engine/utils/merkletree.go b/engine/utils/merkletree.go new file mode 100644 index 00000000..4bf9188f --- /dev/null +++ b/engine/utils/merkletree.go @@ -0,0 +1,43 @@ +package utils + +import ( + "encoding/hex" + + crypto "github.com/bitcoin-sv/go-sdk/primitives/hash" + "github.com/bitcoin-sv/go-sdk/util" +) + +// INFO: This function is moved to go-paymail from go-bc +// https://github.com/libsv/go-bc/blob/master/merkletreeparent.go +// try to use go-sdk implementation when available + +// MerkleTreeParentStr returns the Merkle Tree parent of two Merkle +// Tree children using hex strings instead of just bytes. +func MerkleTreeParentStr(leftNode, rightNode string) (string, error) { + l, err := hex.DecodeString(leftNode) + if err != nil { + return "", err + } + r, err := hex.DecodeString(rightNode) + if err != nil { + return "", err + } + + return hex.EncodeToString(merkleTreeParent(l, r)), nil +} + +// merkleTreeParent returns the Merkle Tree parent of two Merkle tree children. +func merkleTreeParent(leftNode, rightNode []byte) []byte { + // swap endianness before concatenating + l := util.ReverseBytes(leftNode) + r := util.ReverseBytes(rightNode) + + // concatenate leaves + concat := append(l, r...) + + // hash the concatenation + hash := crypto.Sha256d(concat) + + // swap endianness at the end and convert to hex string + return util.ReverseBytes(hash) +} diff --git a/engine/utils/ripemd160.go b/engine/utils/ripemd160.go new file mode 100644 index 00000000..ca64ac70 --- /dev/null +++ b/engine/utils/ripemd160.go @@ -0,0 +1,28 @@ +package utils + +import ( + "crypto/sha256" + + "golang.org/x/crypto/ripemd160" +) + +// NOTE: Temporary implementation - needs to be tracked in GO-SDK SPV-1035 + +// Sha256 hashes with SHA256 +func Sha256(b []byte) []byte { + data := sha256.Sum256(b) + return data[:] +} + +// Ripemd160 hashes with RIPEMD160 +func Ripemd160(b []byte) []byte { + ripe := ripemd160.New() + _, _ = ripe.Write(b[:]) + return ripe.Sum(nil) +} + +// Hash160 hashes with SHA256 and then hashes again with RIPEMD160. +func Hash160(b []byte) []byte { + hash := Sha256(b) + return Ripemd160(hash[:]) +} diff --git a/engine/utils/scripts.go b/engine/utils/scripts.go deleted file mode 100644 index b4f9a919..00000000 --- a/engine/utils/scripts.go +++ /dev/null @@ -1,34 +0,0 @@ -package utils - -import ( - "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bt/v2" - "github.com/libsv/go-bt/v2/bscript" - "github.com/libsv/go-bt/v2/sighash" -) - -// GetUnlockingScript will generate an unlocking script -func GetUnlockingScript(tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey) (*bscript.Script, error) { - sigHashFlags := sighash.AllForkID - - sigHash, err := tx.CalcInputSignatureHash(inputIndex, sigHashFlags) - if err != nil { - return nil, spverrors.Wrapf(err, "failed to calculate signature hash") - } - - var sig *bec.Signature - if sig, err = privateKey.Sign(sigHash); err != nil { - return nil, spverrors.Wrapf(err, "failed to sign transaction") - } - - pubKey := privateKey.PubKey().SerialiseCompressed() - signature := sig.Serialise() - - var script *bscript.Script - if script, err = bscript.NewP2PKHUnlockingScript(pubKey, signature, sigHashFlags); err != nil { - return nil, spverrors.Wrapf(err, "failed to create unlocking script") - } - - return script, nil -} diff --git a/engine/utils/scripts_test.go b/engine/utils/scripts_test.go deleted file mode 100644 index d4b585bf..00000000 --- a/engine/utils/scripts_test.go +++ /dev/null @@ -1 +0,0 @@ -package utils diff --git a/engine/utils/utils.go b/engine/utils/utils.go index 8b959684..d6e10bd0 100644 --- a/engine/utils/utils.go +++ b/engine/utils/utils.go @@ -13,8 +13,8 @@ import ( "math" "strconv" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) const ( @@ -83,11 +83,12 @@ func StringInSlice(a string, list []string) bool { // GetTransactionIDFromHex get the transaction ID from the given transaction hex func GetTransactionIDFromHex(hex string) (string, error) { - parsedTx, err := bt.NewTxFromString(hex) + parsedTx, err := trx.NewTransactionFromHex(hex) if err != nil { return "", spverrors.Wrapf(err, "failed to parse transaction hex") } - return parsedTx.TxID(), nil + + return parsedTx.TxID().String(), nil } // LittleEndianBytes64 returns a byte array in little endian from an unsigned integer of 64 bytes. diff --git a/engine/utils/utils_test.go b/engine/utils/utils_test.go index a3883d50..06b994bf 100644 --- a/engine/utils/utils_test.go +++ b/engine/utils/utils_test.go @@ -5,9 +5,10 @@ import ( "strings" "testing" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/bip32" + bip32 "github.com/bitcoin-sv/go-sdk/compat/bip32" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + primitives "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -115,7 +116,7 @@ func TestDeriveAddresses(t *testing.T) { // TestDerivePrivateKeyFromHex test the method DerivePrivateKeyFromHex func TestDerivePrivateKeyFromHex(t *testing.T) { - hdXpriv, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXpriv) + hdXpriv, _ := compat.GetHDKeyFromExtendedPublicKey(testXpriv) t.Run("empty key", func(t *testing.T) { _, err := DerivePrivateKeyFromHex(nil, testHash) @@ -125,20 +126,20 @@ func TestDerivePrivateKeyFromHex(t *testing.T) { t.Run("empty hex", func(t *testing.T) { key, err := DerivePrivateKeyFromHex(hdXpriv, "") require.NoError(t, err) - assert.Equal(t, privateKey0, hex.EncodeToString(key.Serialise())) + assert.Equal(t, privateKey0, hex.EncodeToString(key.Serialize())) }) t.Run("empty hex", func(t *testing.T) { key, err := DerivePrivateKeyFromHex(hdXpriv, testHash) require.NoError(t, err) - assert.Equal(t, privateKeyHash, hex.EncodeToString(key.Serialise())) + assert.Equal(t, privateKeyHash, hex.EncodeToString(key.Serialize())) }) } // TestDerivePublicKeyFromHex test the method DerivePublicKeyFromHex func TestDerivePublicKeyFromHex(t *testing.T) { - hdXpriv, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXpriv) - hdXpub, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXpub) + hdXpriv, _ := compat.GetHDKeyFromExtendedPublicKey(testXpriv) + hdXpub, _ := compat.GetHDKeyFromExtendedPublicKey(testXpub) t.Run("empty key", func(t *testing.T) { _, err := DerivePublicKeyFromHex(nil, testHash) @@ -148,29 +149,29 @@ func TestDerivePublicKeyFromHex(t *testing.T) { t.Run("priv empty hex", func(t *testing.T) { key, err := DerivePublicKeyFromHex(hdXpriv, "") require.NoError(t, err) - publicKey, _ := bitcoin.PubKeyFromPrivateKeyString(privateKey0, true) - assert.Equal(t, publicKey, hex.EncodeToString(key.SerialiseCompressed())) + privKey, _ := primitives.PrivateKeyFromWif(privateKey0) + assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) }) t.Run("pub empty hex", func(t *testing.T) { key, err := DerivePublicKeyFromHex(hdXpub, "") require.NoError(t, err) - publicKey, _ := bitcoin.PubKeyFromPrivateKeyString(privateKey0, true) - assert.Equal(t, publicKey, hex.EncodeToString(key.SerialiseCompressed())) + privKey, _ := primitives.PrivateKeyFromWif(privateKey0) + assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) }) t.Run("priv testHash hex", func(t *testing.T) { key, err := DerivePublicKeyFromHex(hdXpriv, testHash) require.NoError(t, err) - publicKey, _ := bitcoin.PubKeyFromPrivateKeyString(privateKeyHash, true) - assert.Equal(t, publicKey, hex.EncodeToString(key.SerialiseCompressed())) + privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) + assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) }) t.Run("pub testHash hex", func(t *testing.T) { key, err := DerivePublicKeyFromHex(hdXpub, testHash) require.NoError(t, err) - publicKey, _ := bitcoin.PubKeyFromPrivateKeyString(privateKeyHash, true) - assert.Equal(t, publicKey, hex.EncodeToString(key.SerialiseCompressed())) + privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) + assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) }) } @@ -212,7 +213,7 @@ func TestGetChildNumsFromHex(t *testing.T) { // TestDeriveChildKeyFromHex will test the method DeriveChildKeyFromHex() func TestDeriveChildKeyFromHex(t *testing.T) { t.Run("xpriv", func(t *testing.T) { - key, err := bitcoin.GenerateHDKeyFromString(testXpriv) + key, err := compat.GenerateHDKeyFromString(testXpriv) require.NoError(t, err) var childKey *bip32.ExtendedKey @@ -222,7 +223,7 @@ func TestDeriveChildKeyFromHex(t *testing.T) { }) t.Run("xpub", func(t *testing.T) { - key, err := bitcoin.GenerateHDKeyFromString(testXpub) + key, err := compat.GenerateHDKeyFromString(testXpub) require.NoError(t, err) var childKey *bip32.ExtendedKey @@ -232,7 +233,7 @@ func TestDeriveChildKeyFromHex(t *testing.T) { }) t.Run("xpriv => xpub", func(t *testing.T) { - key, err := bitcoin.GenerateHDKeyFromString(testXpriv) + key, err := compat.GenerateHDKeyFromString(testXpriv) require.NoError(t, err) var childKey *bip32.ExtendedKey @@ -254,26 +255,26 @@ func TestDerivePublicKey(t *testing.T) { require.NoError(t, err) t.Run("nil", func(t *testing.T) { - var pubKey *bec.PublicKey + var pubKey *ec.PublicKey pubKey, err = DerivePublicKey(nil, 0, 0) assert.ErrorIs(t, err, ErrHDKeyNil) assert.Nil(t, pubKey) }) t.Run("derive", func(t *testing.T) { - var pubKey *bec.PublicKey + var pubKey *ec.PublicKey pubKey, err = DerivePublicKey(hdKey, 0, 0) require.NoError(t, err) assert.Equal(t, "03d406421c2733d69a76147c67f8c2194857a2f088299ebf8f1c3790396aa70b4e", - hex.EncodeToString(pubKey.SerialiseCompressed()), + hex.EncodeToString(pubKey.SerializeCompressed()), ) pubKey, err = DerivePublicKey(hdKey, 1, 1) require.NoError(t, err) assert.Equal(t, "0263e4a3696fe4e5136536988169bc3fbec730b912ade7988c57098a47a81a0ae1", - hex.EncodeToString(pubKey.SerialiseCompressed()), + hex.EncodeToString(pubKey.SerializeCompressed()), ) }) } diff --git a/go.mod b/go.mod index 5e7c5d1e..88e88ecc 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,8 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/aws/aws-sdk-go v1.55.5 github.com/bitcoin-sv/go-broadcast-client v0.21.0 - github.com/bitcoin-sv/go-paymail v0.20.1 - github.com/bitcoin-sv/go-sdk v1.1.8 + github.com/bitcoin-sv/go-paymail v0.21.0 + github.com/bitcoin-sv/go-sdk v1.1.9 github.com/bitcoin-sv/spv-wallet/models v0.28.0 github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 github.com/bitcoinschema/go-map v0.2.0 @@ -30,10 +30,6 @@ require ( github.com/google/uuid v1.6.0 github.com/iancoleman/strcase v0.3.0 github.com/jarcoal/httpmock v1.3.1 - github.com/libsv/go-bc v0.1.29 - github.com/libsv/go-bk v0.1.6 - github.com/libsv/go-bt v1.0.8 - github.com/libsv/go-bt/v2 v2.2.5 github.com/mrz1836/go-cache v0.11.0 github.com/mrz1836/go-cachestore v0.5.0 github.com/mrz1836/go-logger v0.3.4 diff --git a/go.sum b/go.sum index d953ce8f..7cd51bdf 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitcoin-sv/go-broadcast-client v0.21.0 h1:QzDT0letnsQQ9rgVwpAuUN/WutjnBOftOiqSzvY796g= github.com/bitcoin-sv/go-broadcast-client v0.21.0/go.mod h1:BwLnKMTMbL9uSQSBV9NwveWgpohniZXz6tesikIG8JA= -github.com/bitcoin-sv/go-paymail v0.20.1 h1:mVTPo6YrOcbg44XTJRdNMduE/TG9sOvHbWpWhIUVVyE= -github.com/bitcoin-sv/go-paymail v0.20.1/go.mod h1:gsPYdngLUj/PA9gaW26V8OmpnMmUxdI/fe0V1TVol9A= -github.com/bitcoin-sv/go-sdk v1.1.8 h1:f/dDo+qBtjDvNmMagEiNihRS10ca93eNnZOoTG0HXk8= -github.com/bitcoin-sv/go-sdk v1.1.8/go.mod h1:NOAkJLbjqKOLuxJmb9ABG86ExTZp4HS8+iygiDIUps4= +github.com/bitcoin-sv/go-paymail v0.21.0 h1:xcyCWBpXG79Bek+uEC9HknPC9McFzafDKNRiHBi3110= +github.com/bitcoin-sv/go-paymail v0.21.0/go.mod h1:CzDCfKjxMI0Ve5Z4V7IuCUP+BXS4PuJ4A7TAQVbESmw= +github.com/bitcoin-sv/go-sdk v1.1.9 h1:N/LlZUMHNYKjEBuY72c3XSlzUI/q7IN34R0p6J0Qtjc= +github.com/bitcoin-sv/go-sdk v1.1.9/go.mod h1:NOAkJLbjqKOLuxJmb9ABG86ExTZp4HS8+iygiDIUps4= github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 h1:Sgh5Eb746Zck/46rFDrZZEXZWyO53fMuWYhNoZa1tck= github.com/bitcoinschema/go-bitcoin/v2 v2.0.5/go.mod h1:JjO1ivfZv6vhK0uAXzyH08AAHlzNMAfnyK1Fiv9r4ZA= github.com/bitcoinschema/go-bob v0.5.0 h1:Fjl60RuiQiUuZWLfXE8ETdcgmJBHYu9MYfbrbZU7yHs= @@ -181,14 +181,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libsv/go-bc v0.1.29 h1:w3ZnpZxLkTrjklwkr9x/y/xv0vJz5FVVZjd/gvtvGQs= -github.com/libsv/go-bc v0.1.29/go.mod h1:l6epTfcakN8YKId/hrpUzlu1QeT3ODF1MI3DeYhG1O8= -github.com/libsv/go-bk v0.1.6 h1:c9CiT5+64HRDbzxPl1v/oiFmbvWZTuUYqywCf+MBs/c= -github.com/libsv/go-bk v0.1.6/go.mod h1:khJboDoH18FPUaZlzRFKzlVN84d4YfdmlDtdX4LAjQA= -github.com/libsv/go-bt v1.0.8 h1:nWLLcnUm0dxNO3exqrL5jvAcTGkl0dsnBuQqB6+M6vQ= -github.com/libsv/go-bt v1.0.8/go.mod h1:yO023bNYLh5DwcOYl+ZqLAeTemoy6K+2UbQlIBMv+EQ= -github.com/libsv/go-bt/v2 v2.2.5 h1:VoggBLMRW9NYoFujqe5bSYKqnw5y+fYfufgERSoubog= -github.com/libsv/go-bt/v2 v2.2.5/go.mod h1:cV45+jDlPOLfhJLfpLmpQoWzrIvVth9Ao2ZO1f6CcqU= github.com/libsv/go-p2p v0.3.2 h1:O32CzkqM+jhSuleRHJln6JjL2pKH8aaRTx8lAfhIiic= github.com/libsv/go-p2p v0.3.2/go.mod h1:TENFxbTT/bfSfuiirjU6l+PfAWxwZgF8GYUxs5tzc/M= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= diff --git a/server/middleware/signature_middleware.go b/server/middleware/signature_middleware.go index b4754256..542ce3cc 100644 --- a/server/middleware/signature_middleware.go +++ b/server/middleware/signature_middleware.go @@ -2,19 +2,21 @@ package middleware import ( "bytes" + "encoding/base64" "fmt" "io" "strconv" "strings" "time" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + bsm "github.com/bitcoin-sv/go-sdk/compat/bsm" + "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models" "github.com/bitcoin-sv/spv-wallet/server/reqctx" - "github.com/bitcoinschema/go-bitcoin/v2" "github.com/gin-gonic/gin" - "github.com/libsv/go-bt/v2/bscript" ) // CheckSignatureMiddleware is a middleware that checks the signature of the request (if required) @@ -116,7 +118,7 @@ func (sa *sigAuth) checkRequirements(bodyContents string) error { // verifyWithXPub will verify the xPub key and the signature payload func (sa *sigAuth) verifyWithXPub(xPub string) error { - key, err := bitcoin.GetHDKeyFromExtendedPublicKey(xPub) + key, err := compat.GetHDKeyFromExtendedPublicKey(xPub) if err != nil { return spverrors.ErrInvalidSignature } @@ -125,15 +127,20 @@ func (sa *sigAuth) verifyWithXPub(xPub string) error { return spverrors.ErrInvalidSignature } - var address *bscript.Address - if address, err = bitcoin.GetAddressFromHDKey(key); err != nil { + var address *script.Address + if address, err = compat.GetAddressFromHDKey(key); err != nil { return spverrors.ErrInvalidSignature } message := sa.getSigningMessage(xPub) - if err = bitcoin.VerifyMessage( + sigBytes, err := base64.StdEncoding.DecodeString(sa.Signature) + if err != nil { + return spverrors.ErrInvalidSignature + } + + if err := bsm.VerifyMessage( address.AddressString, - sa.Signature, + sigBytes, message, ); err != nil { return spverrors.ErrInvalidSignature @@ -143,16 +150,19 @@ func (sa *sigAuth) verifyWithXPub(xPub string) error { // verifyWithAccessKey will verify the access key and the signature payload func (sa *sigAuth) verifyWithAccessKey(accessKey string) error { - address, err := bitcoin.GetAddressFromPubKeyString( - accessKey, true, - ) + address, err := script.NewAddressFromPublicKeyString(accessKey, true) + if err != nil { + return spverrors.ErrInvalidSignature + } + + sigBytes, err := base64.StdEncoding.DecodeString(sa.Signature) if err != nil { return spverrors.ErrInvalidSignature } - if err := bitcoin.VerifyMessage( + if err := bsm.VerifyMessage( address.AddressString, - sa.Signature, + sigBytes, sa.getSigningMessage(accessKey), ); err != nil { return spverrors.ErrInvalidSignature @@ -161,6 +171,6 @@ func (sa *sigAuth) verifyWithAccessKey(accessKey string) error { } // getSigningMessage will build the signing message string -func (sa *sigAuth) getSigningMessage(xPub string) string { - return fmt.Sprintf("%s%s%s%d", xPub, sa.AuthHash, sa.AuthNonce, sa.AuthTime) +func (sa *sigAuth) getSigningMessage(xPub string) []byte { + return []byte(fmt.Sprintf("%s%s%s%d", xPub, sa.AuthHash, sa.AuthNonce, sa.AuthTime)) } From a838b29eb47298f8087c0cc11643e8fbc8992750 Mon Sep 17 00:00:00 2001 From: wregulski Date: Wed, 9 Oct 2024 18:08:40 +0200 Subject: [PATCH 02/24] feat(SPV-898): part 2 replace structures to new sdk kit --- engine/action_transaction_test.go | 13 ++- engine/chainstate/transaction.go | 4 +- engine/chainstate/transaction_info.go | 4 +- engine/model_bump.go | 27 +++-- engine/model_destinations.go | 18 ++- engine/model_draft_transactions.go | 109 ++++++++---------- engine/model_draft_transactions_test.go | 44 +++---- engine/model_paymail_addresses.go | 21 ++-- engine/model_paymail_addresses_test.go | 31 ++--- engine/model_transaction_config.go | 46 ++++---- engine/model_transactions.go | 2 +- engine/models_test.go | 4 +- engine/pike/example_test.go | 6 +- engine/pike/pike_test.go | 6 +- engine/pike_service_provider.go | 7 +- engine/record_tx.go | 14 +-- ...record_tx_strategy_external_incoming_tx.go | 12 +- engine/record_tx_strategy_outgoing_tx.go | 10 +- engine/script/template/p2pkh.go | 12 +- engine/script/template/p2pkh_test.go | 32 ++--- engine/utils/scripts.go | 21 ++++ engine/utils/scripts_test.go | 1 + 22 files changed, 239 insertions(+), 205 deletions(-) create mode 100644 engine/utils/scripts.go create mode 100644 engine/utils/scripts_test.go diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index f6b8b728..4ce1a8c2 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -6,8 +6,8 @@ import ( "testing" broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bk/bip32" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -242,7 +242,7 @@ func Test_RecordTransaction(t *testing.T) { }) } -func initRevertTransactionData(t *testing.T, clientOpts ...ClientOps) (context.Context, ClientInterface, *Transaction, *bip32.ExtendedKey, func()) { +func initRevertTransactionData(t *testing.T, clientOpts ...ClientOps) (context.Context, ClientInterface, *Transaction, *compat.ExtendedKey, func()) { // this creates an xpub, destination and utxo ctx, client, deferMe := initSimpleTestCase(t, clientOpts...) @@ -269,8 +269,9 @@ func initRevertTransactionData(t *testing.T, clientOpts ...ClientOps) (context.C err = draftTransaction.Save(ctx) require.NoError(t, err) - var xPriv *bip32.ExtendedKey - xPriv, err = bip32.NewKeyFromString(testXPriv) + var xPriv *compat.ExtendedKey + // xPriv, err = bip32.NewKeyFromString(testXPriv) + xPriv, err = compat.NewKeyFromString(testXPriv) require.NoError(t, err) var hex string @@ -335,8 +336,8 @@ func BenchmarkAction_Transaction_recordTransaction(b *testing.B) { b.Fail() } - var xPriv *bip32.ExtendedKey - if xPriv, err = bip32.NewKeyFromString(testXPriv); err != nil { + var xPriv *compat.ExtendedKey + if xPriv, err = compat.NewKeyFromString(testXPriv); err != nil { return } diff --git a/engine/chainstate/transaction.go b/engine/chainstate/transaction.go index 00a1259a..8e24d3e3 100644 --- a/engine/chainstate/transaction.go +++ b/engine/chainstate/transaction.go @@ -7,8 +7,8 @@ import ( "time" "github.com/bitcoin-sv/go-broadcast-client/broadcast" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bc" ) // query will try ALL providers in order and return the first "valid" response based on requirements @@ -52,7 +52,7 @@ func queryBroadcastClient(ctx context.Context, client ClientInterface, id string return nil, spverrors.ErrTransactionIDMismatch } - bump, err := bc.NewBUMPFromStr(resp.BaseTxResponse.MerklePath) + bump, err := trx.NewMerklePathFromHex(resp.BaseTxResponse.MerklePath) if err != nil { return nil, spverrors.ErrBroadcastWrongBUMPResponse.Wrap(err) } diff --git a/engine/chainstate/transaction_info.go b/engine/chainstate/transaction_info.go index 36ffbdb0..f9e4b90e 100644 --- a/engine/chainstate/transaction_info.go +++ b/engine/chainstate/transaction_info.go @@ -2,7 +2,7 @@ package chainstate import ( "github.com/bitcoin-sv/go-broadcast-client/broadcast" - "github.com/libsv/go-bc" + trx "github.com/bitcoin-sv/go-sdk/transaction" ) // TransactionInfo is the universal information about the transaction found from a chain provider @@ -11,6 +11,6 @@ type TransactionInfo struct { BlockHeight int64 `json:"block_height"` // Block height of the transaction ID string `json:"id"` // Transaction ID (Hex) Provider string `json:"provider,omitempty"` // Provider is our internal source - BUMP *bc.BUMP `json:"bump,omitempty"` // Merkle proof in BUMP format + BUMP *trx.MerklePath `json:"bump,omitempty"` // Merkle proof in BUMP format TxStatus broadcast.TxStatus `json:"tx_status,omitempty"` // Status of the transaction } diff --git a/engine/model_bump.go b/engine/model_bump.go index 80fb0f9a..6838f7ee 100644 --- a/engine/model_bump.go +++ b/engine/model_bump.go @@ -339,24 +339,29 @@ func (bumps BUMPs) Value() (driver.Value, error) { return string(marshal), nil } -func bcBumpToBUMP(bcBump *trx.MerklePath) BUMP { - path := make([][]BUMPLeaf, len(bcBump.Path)) - for i := range bcBump.Path { - path[i] = make([]BUMPLeaf, len(bcBump.Path[i])) - for j, source := range bcBump.Path[i] { +func sdkMPToBUMP(sdkMerklePath *trx.MerklePath) BUMP { + path := make([][]BUMPLeaf, len(sdkMerklePath.Path)) + for i := range sdkMerklePath.Path { + path[i] = make([]BUMPLeaf, len(sdkMerklePath.Path[i])) + for j, source := range sdkMerklePath.Path[i] { leaf := BUMPLeaf{} + leaf.Offset = source.Offset + if source.Hash != nil { + leaf.Hash = source.Hash.String() + } - // All fields in bc.leaf are pointers, so we need to use SafeAssign to avoid dereferencing nil pointers - utils.SafeAssign(&leaf.Offset, source.Offset) - utils.SafeAssign(&leaf.Hash, source.Hash) - utils.SafeAssign(&leaf.TxID, source.Txid) - utils.SafeAssign(&leaf.Duplicate, source.Duplicate) + if source.Txid != nil { + leaf.TxID = *source.Txid + } + if source.Duplicate != nil { + leaf.Duplicate = *source.Duplicate + } path[i][j] = leaf } } return BUMP{ - BlockHeight: bcBump.BlockHeight, + BlockHeight: uint64(sdkMerklePath.BlockHeight), Path: path, } } diff --git a/engine/model_destinations.go b/engine/model_destinations.go index ad506f88..52220e9c 100644 --- a/engine/model_destinations.go +++ b/engine/model_destinations.go @@ -5,11 +5,12 @@ import ( "errors" "fmt" + "github.com/bitcoin-sv/go-sdk/script" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" "github.com/bitcoin-sv/spv-wallet/engine/cluster" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" ) // Destination is an object representing a BitCoin destination (address, script, etc) @@ -77,13 +78,18 @@ func newAddress(rawXpubKey string, chain, num uint32, opts ...ModelOps) (*Destin return nil, err } - // Set the locking script - if destination.LockingScript, err = bitcoin.ScriptFromAddress( - destination.Address, - ); err != nil { - return nil, spverrors.Wrapf(err, "failed to get locking script from address %s", destination.Address) + sc, err := script.NewAddressFromString(destination.Address) + if err != nil { + return nil, spverrors.Wrapf(err, "failed to parse string address to script %s", destination.Address) + } + + ls, err := p2pkh.Lock(sc) + if err != nil { + return nil, spverrors.Wrapf(err, "failed to get locking script from address %s", sc) } + destination.LockingScript = ls.String() + // Determine the type if the locking script is provided destination.Type = utils.GetDestinationType(destination.LockingScript) destination.ID = utils.Hash(destination.LockingScript) diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index 61df0126..12f3214c 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -3,21 +3,20 @@ package engine import ( "context" "crypto/rand" - "encoding/hex" "fmt" "math" "math/big" "time" "github.com/bitcoin-sv/go-paymail" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" + trx "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/bip32" - "github.com/libsv/go-bt/v2" - "github.com/libsv/go-bt/v2/bscript" "github.com/pkg/errors" ) @@ -247,8 +246,8 @@ func (m *DraftTransaction) createTransactionHex(ctx context.Context) (err error) } // Start a new transaction from the reservedUtxos - tx := bt.NewTx() - if err = tx.FromUTXOs(inputUtxos...); err != nil { + tx := trx.NewTransaction() + if err = tx.AddInputsFromUTXOs(inputUtxos...); err != nil { return } @@ -315,7 +314,7 @@ func (m *DraftTransaction) calculateAndSetFee(ctx context.Context, satoshisReser return nil } -func (m *DraftTransaction) prepareUtxos(ctx context.Context, opts []ModelOps, satoshisNeeded uint64) ([]*bt.UTXO, uint64, error) { +func (m *DraftTransaction) prepareUtxos(ctx context.Context, opts []ModelOps, satoshisNeeded uint64) ([]*trx.UTXO, uint64, error) { if m.Configuration.SendAllTo != nil { inputUtxos, satoshisReserved, err := m.prepareSendAllToUtxos(ctx, opts) if err != nil { @@ -332,7 +331,7 @@ func (m *DraftTransaction) prepareUtxos(ctx context.Context, opts []ModelOps, sa } // prepareSeparateUtxos will get user's utxos which will have the required amount of satoshi and then reserve and process them. -func (m *DraftTransaction) prepareSeparateUtxos(ctx context.Context, opts []ModelOps, satoshisNeeded uint64) ([]*bt.UTXO, uint64, error) { +func (m *DraftTransaction) prepareSeparateUtxos(ctx context.Context, opts []ModelOps, satoshisNeeded uint64) ([]*trx.UTXO, uint64, error) { // we can only include separate utxos (like tokens) when not using SendAllTo var includeUtxoSatoshis uint64 var err error @@ -379,7 +378,7 @@ func (m *DraftTransaction) prepareSeparateUtxos(ctx context.Context, opts []Mode } // prepareSendAllToUtxos will reserve and process all the user's utxos which will be sent to one address -func (m *DraftTransaction) prepareSendAllToUtxos(ctx context.Context, opts []ModelOps) ([]*bt.UTXO, uint64, error) { +func (m *DraftTransaction) prepareSendAllToUtxos(ctx context.Context, opts []ModelOps) ([]*trx.UTXO, uint64, error) { // todo should all utxos be sent to the SendAllTo address, not only the p2pkhs? spendableUtxos, err := getSpendableUtxos( ctx, m.XpubID, utils.ScriptTypePubKeyHash, nil, m.Configuration.FromUtxos, opts..., @@ -496,14 +495,14 @@ func (m *DraftTransaction) processUtxos(ctx context.Context, utxos []*Utxo) erro func (m *DraftTransaction) estimateSize() uint64 { size := defaultOverheadSize // version + nLockTime - inputSize := bt.VarInt(len(m.Configuration.Inputs)) + inputSize := trx.VarInt(len(m.Configuration.Inputs)) size += uint64(inputSize.Length()) for _, input := range m.Configuration.Inputs { size += utils.GetInputSizeForType(input.Type) } - outputSize := bt.VarInt(len(m.Configuration.Outputs)) + outputSize := trx.VarInt(len(m.Configuration.Outputs)) size += uint64(outputSize.Length()) for _, output := range m.Configuration.Outputs { @@ -522,12 +521,12 @@ func (m *DraftTransaction) estimateFee(unit *utils.FeeUnit, addToSize uint64) ui return uint64(math.Ceil(feeEstimate)) } -// addOutputs will add the given outputs to the bt.Tx -func (m *DraftTransaction) addOutputsToTx(tx *bt.Tx) (err error) { - var s *bscript.Script +// addOutputs will add the given outputs to the SDK Transaction +func (m *DraftTransaction) addOutputsToTx(tx *trx.Transaction) (err error) { + var s *script.Script for _, output := range m.Configuration.Outputs { for _, sc := range output.Scripts { - if s, err = bscript.NewFromHexString( + if s, err = script.NewFromHex( sc.Script, ); err != nil { return @@ -544,7 +543,7 @@ func (m *DraftTransaction) addOutputsToTx(tx *bt.Tx) (err error) { return spverrors.ErrInvalidOpReturnOutput } - tx.AddOutput(&bt.Output{ + tx.AddOutput(&trx.TransactionOutput{ LockingScript: s, Satoshis: 0, }) @@ -554,14 +553,14 @@ func (m *DraftTransaction) addOutputsToTx(tx *bt.Tx) (err error) { return spverrors.ErrOutputValueTooLow } - if err = tx.AddP2PKHOutputFromScript( - s, sc.Satoshis, - ); err != nil { - return - } + tx.AddOutput( + &trx.TransactionOutput{ + LockingScript: s, + Satoshis: sc.Satoshis, + }) } else { // add non-standard output script - tx.AddOutput(&bt.Output{ + tx.AddOutput(&trx.TransactionOutput{ LockingScript: s, Satoshis: sc.Satoshis, }) @@ -729,35 +728,32 @@ func (m *DraftTransaction) setChangeDestinations(ctx context.Context, numberOfDe return nil } -// getInputsFromUtxos this function transforms SPV Wallet utxos to bt.UTXOs -func (m *DraftTransaction) getInputsFromUtxos(reservedUtxos []*Utxo) ([]*bt.UTXO, uint64, error) { +// getInputsFromUtxos this function transforms SPV Wallet utxos to SDK UTXOs +func (m *DraftTransaction) getInputsFromUtxos(reservedUtxos []*Utxo) ([]*trx.UTXO, uint64, error) { // transform to bt.utxo and check if we have enough - inputUtxos := make([]*bt.UTXO, 0) + inputUtxos := make([]*trx.UTXO, 0) satoshisReserved := uint64(0) - var lockingScript *bscript.Script + var lockingScript *script.Script var err error for _, utxo := range reservedUtxos { - if lockingScript, err = bscript.NewFromHexString( + if lockingScript, err = script.NewFromHex( utxo.ScriptPubKey, ); err != nil { return nil, 0, spverrors.ErrInvalidLockingScript } - var txIDBytes []byte - if txIDBytes, err = hex.DecodeString( + utxo, err := trx.NewUTXO( utxo.TransactionID, - ); err != nil { - return nil, 0, spverrors.ErrInvalidTransactionID + utxo.OutputIndex, + lockingScript.String(), + utxo.Satoshis, + ) + if err != nil { + return nil, 0, err } - inputUtxos = append(inputUtxos, &bt.UTXO{ - TxID: txIDBytes, - Vout: utxo.OutputIndex, - Satoshis: utxo.Satoshis, - LockingScript: lockingScript, - SequenceNumber: bt.DefaultSequenceNumber, - }) + inputUtxos = append(inputUtxos, utxo) satoshisReserved += utxo.Satoshis } @@ -828,8 +824,8 @@ func (m *DraftTransaction) Migrate(client datastore.ClientInterface) error { // SignInputsWithKey will sign all the inputs using a key (string) (helper method) func (m *DraftTransaction) SignInputsWithKey(xPrivKey string) (signedHex string, err error) { // Decode the xPriv using the key - var xPriv *bip32.ExtendedKey - if xPriv, err = bip32.NewKeyFromString(xPrivKey); err != nil { + var xPriv *compat.ExtendedKey + if xPriv, err = compat.NewKeyFromString(xPrivKey); err != nil { return } @@ -837,10 +833,10 @@ func (m *DraftTransaction) SignInputsWithKey(xPrivKey string) (signedHex string, } // SignInputs will sign all the inputs using the given xPriv key -func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex string, err error) { +func (m *DraftTransaction) SignInputs(xPriv *compat.ExtendedKey) (signedHex string, err error) { // Start a bt draft transaction - var txDraft *bt.Tx - if txDraft, err = bt.NewTxFromString(m.Hex); err != nil { + var txDraft *trx.Transaction + if txDraft, err = trx.NewTransactionFromHex(m.Hex); err != nil { return } @@ -848,8 +844,8 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin for index, input := range m.Configuration.Inputs { // Get the locking script - var ls *bscript.Script - if ls, err = bscript.NewFromHexString( + var ls *script.Script + if ls, err = script.NewFromHex( input.Destination.LockingScript, ); err != nil { return @@ -858,7 +854,7 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin txDraft.Inputs[index].PreviousTxSatoshis = input.Satoshis // Derive the child key (chain) - var chainKey *bip32.ExtendedKey + var chainKey *compat.ExtendedKey if chainKey, err = xPriv.Child( input.Destination.Chain, ); err != nil { @@ -866,7 +862,7 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin } // Derive the child key (num) - var numKey *bip32.ExtendedKey + var numKey *compat.ExtendedKey if numKey, err = chainKey.Child( input.Destination.Num, ); err != nil { @@ -874,15 +870,13 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin } // Get the private key - var privateKey *bec.PrivateKey - if privateKey, err = bitcoin.GetPrivateKeyFromHDKey( + var privateKey *ec.PrivateKey + if privateKey, err = compat.GetPrivateKeyFromHDKey( numKey, ); err != nil { return } - - // Get the unlocking script - var s *bscript.Script + var s *p2pkh.P2PKH if s, err = utils.GetUnlockingScript( txDraft, uint32(index), privateKey, ); err != nil { @@ -890,11 +884,10 @@ func (m *DraftTransaction) SignInputs(xPriv *bip32.ExtendedKey) (signedHex strin } // Insert the locking script - if err = txDraft.InsertInputUnlockingScript( - uint32(index), s, - ); err != nil { - return + if txDraft.Inputs[index] == nil { + return "", spverrors.Newf("input with index %d not found in transaction draft %v", index, txDraft) } + txDraft.Inputs[index].UnlockingScriptTemplate = s } // Return the signed hex diff --git a/engine/model_draft_transactions_test.go b/engine/model_draft_transactions_test.go index 64752f9a..f69f0848 100644 --- a/engine/model_draft_transactions_test.go +++ b/engine/model_draft_transactions_test.go @@ -8,16 +8,17 @@ import ( "testing" "time" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + bsm "github.com/bitcoin-sv/go-sdk/compat/bsm" + trx "github.com/bitcoin-sv/go-sdk/transaction" + sighash "github.com/bitcoin-sv/go-sdk/transaction/sighash" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" xtester "github.com/bitcoin-sv/spv-wallet/engine/tester/paymailmock" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/bip32" - "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" - "github.com/libsv/go-bt/v2/sighash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -306,13 +307,13 @@ func TestDraftTransaction_createTransaction(t *testing.T) { assert.Equal(t, uint64((startingBalance - txAmount - expectedFee)), draftTransaction.Configuration.Outputs[1].Satoshis) assert.Equal(t, draftTransaction.Configuration.ChangeDestinations[0].LockingScript, draftTransaction.Configuration.Outputs[1].Scripts[0].Script) - var btTx *bt.Tx - btTx, err = bt.NewTxFromString(draftTransaction.Hex) + var btTx *trx.Transaction + btTx, err = trx.NewTransactionFromHex(draftTransaction.Hex) require.NoError(t, err) assert.Equal(t, 1, len(btTx.Inputs)) - assert.Equal(t, testTxID, hex.EncodeToString(btTx.Inputs[0].PreviousTxID())) - assert.Equal(t, uint32(0), btTx.Inputs[0].PreviousTxOutIndex) + assert.Equal(t, testTxID, btTx.Inputs[0].SourceTXID.String()) + assert.Equal(t, uint32(0), btTx.Inputs[0].SourceTxOutIndex) assert.Equal(t, 2, len(btTx.Outputs)) assert.Equal(t, uint64(txAmount), btTx.Outputs[0].Satoshis) @@ -928,7 +929,7 @@ func TestDraftTransaction_getInputsFromUtxos(t *testing.T) { require.NoError(t, err) assert.Equal(t, uint64(124235), satoshisReserved) assert.Equal(t, 1, len(inputUtxos)) - assert.Equal(t, testTxID, hex.EncodeToString((inputUtxos)[0].TxID)) + assert.Equal(t, testTxID, inputUtxos[0].TxID.String()) assert.Equal(t, uint32(123), (inputUtxos)[0].Vout) assert.Equal(t, testLockingScript, (inputUtxos)[0].LockingScript.String()) assert.Equal(t, uint64(124235), (inputUtxos)[0].Satoshis) @@ -957,12 +958,12 @@ func TestDraftTransaction_getInputsFromUtxos(t *testing.T) { assert.Equal(t, uint64(124235+52313), satoshisReserved) assert.Equal(t, 2, len(inputUtxos)) - assert.Equal(t, testTxID, hex.EncodeToString((inputUtxos)[0].TxID)) + assert.Equal(t, testTxID, inputUtxos[0].TxID.String()) assert.Equal(t, uint32(124), (inputUtxos)[0].Vout) assert.Equal(t, testLockingScript, (inputUtxos)[0].LockingScript.String()) assert.Equal(t, uint64(52313), (inputUtxos)[0].Satoshis) - assert.Equal(t, testTxID, hex.EncodeToString((inputUtxos)[1].TxID)) + assert.Equal(t, testTxID, inputUtxos[1].TxID.String()) assert.Equal(t, uint32(123), (inputUtxos)[1].Vout) assert.Equal(t, testLockingScript, (inputUtxos)[1].LockingScript.String()) assert.Equal(t, uint64(124235), (inputUtxos)[1].Satoshis) @@ -1037,7 +1038,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { }}, }, } - tx := bt.NewTx() + tx := trx.NewTransaction() err := draft.addOutputsToTx(tx) require.NoError(t, err) }) @@ -1053,7 +1054,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { }}, }, } - tx := bt.NewTx() + tx := trx.NewTransaction() err := draft.addOutputsToTx(tx) require.ErrorIs(t, err, spverrors.ErrOutputValueTooLow) assert.Len(t, tx.Outputs, 0) @@ -1070,7 +1071,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { }}, }, } - tx := bt.NewTx() + tx := trx.NewTransaction() err := draft.addOutputsToTx(tx) require.NoError(t, err) assert.Len(t, tx.Outputs, 1) @@ -1090,7 +1091,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { }}, }, } - tx := bt.NewTx() + tx := trx.NewTransaction() err := draft.addOutputsToTx(tx) require.NoError(t, err) assert.Len(t, tx.Outputs, 1) @@ -1110,7 +1111,7 @@ func TestDraftTransaction_addOutputsToTx(t *testing.T) { }}, }, } - tx := bt.NewTx() + tx := trx.NewTransaction() err := draft.addOutputsToTx(tx) require.ErrorIs(t, err, spverrors.ErrInvalidOpReturnOutput) }) @@ -1121,7 +1122,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { defer deferMe() xPrivString := "xprv9s21ZrQH143K31pvNoYNcRZjtdJXnNVEc5NmBbgJmEg27YWbZVL7jTLQhPELqAR7tcJTnF9AJLwVN5w3ABZvrfeDLm4vnBDw76bkx8a2NxK" - xPrivHD, err := bitcoin.GenerateHDKeyFromString(xPrivString) + xPrivHD, err := compat.GenerateHDKeyFromString(xPrivString) require.NoError(t, err) xPubHD, _ := xPrivHD.Neuter() xPubID := utils.Hash(xPubHD.String()) @@ -1148,7 +1149,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { // Get the private key var privateKey *bec.PrivateKey - if privateKey, err = bitcoin.GetPrivateKeyFromHDKey( + if privateKey, err = compat.GetPrivateKeyFromHDKey( numKey, ); err != nil { return @@ -1198,8 +1199,8 @@ func TestDraftTransaction_SignInputs(t *testing.T) { return } - var tx *bt.Tx - tx, err = bt.NewTxFromString(gotSignedHex) + var tx *trx.Transaction + tx, err = trx.NewTransactionFromHex(gotSignedHex) require.NoError(t, err) var ls *bscript.Script @@ -1214,8 +1215,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { require.NoError(t, err) assert.True(t, tx.Version > 0) for _, input := range tx.Inputs { - var unlocker string - unlocker, err = input.UnlockingScript.ToASM() + unlocker := input.UnlockingScript.ToASM() require.NoError(t, err) scriptParts := strings.Split(unlocker, " ") pubKey := hex.EncodeToString(privateKey.PubKey().SerialiseCompressed()) @@ -1227,7 +1227,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { var hash32 [32]byte copy(hash32[:], hash) var verified bool - verified, err = bitcoin.VerifyMessageDER(hash32, pubKey, scriptParts[0]) + verified, err = bsm.VerifyMessageDER(hash32, pubKey, scriptParts[0]) require.NoError(t, err) assert.True(t, verified) } diff --git a/engine/model_paymail_addresses.go b/engine/model_paymail_addresses.go index 9091bb70..aec9a3ff 100644 --- a/engine/model_paymail_addresses.go +++ b/engine/model_paymail_addresses.go @@ -7,11 +7,10 @@ import ( "fmt" "github.com/bitcoin-sv/go-paymail" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bip32" ) // PaymailAddress is an "external model example" - this model is not part of the standard models loaded and runtime @@ -38,7 +37,7 @@ type PaymailAddress struct { // Private fields externalXpubKeyDecrypted string - externalHdXpub *bip32.ExtendedKey + externalHdXpub *compat.ExtendedKey } // newPaymail create new paymail model @@ -128,13 +127,13 @@ func (m *PaymailAddress) setXPub(externalXpubDerivation uint32) error { m.XpubID = utils.Hash(m.rawXpubKey) // Derive the public key from string - xPub, err := bitcoin.GetHDKeyFromExtendedPublicKey(m.rawXpubKey) + xPub, err := compat.GetHDKeyFromExtendedPublicKey(m.rawXpubKey) if err != nil { return spverrors.Wrapf(err, "failed to load xPub key for paymail") } // Get the external public key - paymailExternalXpub, err := bitcoin.GetHDKeyByPath(xPub, utils.ChainExternal, externalXpubDerivation) + paymailExternalXpub, err := compat.GetHDKeyByPath(xPub, utils.ChainExternal, externalXpubDerivation) if err != nil { return spverrors.Wrapf(err, "failed to derive xPub key for paymail") } @@ -155,7 +154,7 @@ func (m *PaymailAddress) setXPub(externalXpubDerivation uint32) error { } // GetIdentityXpub will get the identity related to the xPub -func (m *PaymailAddress) GetIdentityXpub() (*bip32.ExtendedKey, error) { +func (m *PaymailAddress) GetIdentityXpub() (*compat.ExtendedKey, error) { // Get the external xPub (to derive the identity key) xPub, err := m.getExternalXpub() if err != nil { @@ -163,14 +162,14 @@ func (m *PaymailAddress) GetIdentityXpub() (*bip32.ExtendedKey, error) { } // Get the last possible key in the external key - child, err := bitcoin.GetHDKeyChild( + child, err := compat.GetHDKeyChild( xPub, uint32(utils.MaxInt32), ) return child, spverrors.Wrapf(err, "failed to generate identity xPub") } // getExternalXpub will get the external xPub -func (m *PaymailAddress) getExternalXpub() (*bip32.ExtendedKey, error) { +func (m *PaymailAddress) getExternalXpub() (*compat.ExtendedKey, error) { if m.externalHdXpub != nil { return m.externalHdXpub, nil } @@ -188,7 +187,7 @@ func (m *PaymailAddress) getExternalXpub() (*bip32.ExtendedKey, error) { } // Get the xPub - xPub, err := bitcoin.GetHDKeyFromExtendedPublicKey(m.externalXpubKeyDecrypted) + xPub, err := compat.GetHDKeyFromExtendedPublicKey(m.externalXpubKeyDecrypted) if err != nil { return nil, spverrors.Wrapf(err, "failed to get external xPub") } @@ -214,11 +213,11 @@ func (m *PaymailAddress) GetPubKey() (string, error) { return "", spverrors.Wrapf(err, "failed to get public key") } - return hex.EncodeToString(pubKey.SerialiseCompressed()), nil + return hex.EncodeToString(pubKey.SerializeCompressed()), nil } // GetNextXpub will get the next child xPub for external operations. -func (m *PaymailAddress) GetNextXpub(ctx context.Context) (*bip32.ExtendedKey, error) { +func (m *PaymailAddress) GetNextXpub(ctx context.Context) (*compat.ExtendedKey, error) { unlock, err := getWaitWriteLockForPaymail(ctx, m.client.Cachestore(), m.ID) defer unlock() if err != nil { diff --git a/engine/model_paymail_addresses_test.go b/engine/model_paymail_addresses_test.go index 6721230f..a91cd864 100644 --- a/engine/model_paymail_addresses_test.go +++ b/engine/model_paymail_addresses_test.go @@ -6,8 +6,9 @@ import ( "math/rand" "testing" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + script "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bip32" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,20 +27,20 @@ func TestNewPaymail(t *testing.T) { paymail := "paymail@tester.com" externalDerivationNum := randDerivationNum() - xPub, err := bitcoin.GetHDKeyFromExtendedPublicKey(testXpubAuth) + xPub, err := compat.GetHDKeyFromExtendedPublicKey(testXpubAuth) require.NoError(t, err) require.NotNil(t, xPub) // Get the external public key var paymailExternalKey *bip32.ExtendedKey - paymailExternalKey, err = bitcoin.GetHDKeyByPath( + paymailExternalKey, err = compat.GetHDKeyByPath( xPub, utils.ChainExternal, externalDerivationNum, ) require.NoError(t, err) require.NotNil(t, paymailExternalKey) var paymailIdentityKey *bip32.ExtendedKey - paymailIdentityKey, err = bitcoin.GetHDKeyChild(paymailExternalKey, uint32(utils.MaxInt32)) + paymailIdentityKey, err = compat.GetHDKeyChild(paymailExternalKey, uint32(utils.MaxInt32)) require.NoError(t, err) require.NotNil(t, paymailIdentityKey) @@ -106,16 +107,16 @@ func TestNewPaymail(t *testing.T) { assert.Equal(t, addressInternal, internal) assert.Equal(t, addressExternal, external) - childKeyChain0, _ := bitcoin.GetHDKeyChild(hdKey, 0) - childKeyChain01, _ := bitcoin.GetHDKeyChild(childKeyChain0, 1) + childKeyChain0, _ := compat.GetHDKeyChild(hdKey, 0) + childKeyChain01, _ := compat.GetHDKeyChild(childKeyChain0, 1) key0, _ := childKeyChain01.ECPubKey() - address0, _ := bitcoin.GetAddressFromPubKey(key0, true) + address0, _ := script.NewAddressFromPublicKey(key0, true) assert.Equal(t, addressExternal, address0.AddressString) - childKeyChain1, _ := bitcoin.GetHDKeyChild(hdKey, 1) - childKeyChain11, _ := bitcoin.GetHDKeyChild(childKeyChain1, 1) + childKeyChain1, _ := compat.GetHDKeyChild(hdKey, 1) + childKeyChain11, _ := compat.GetHDKeyChild(childKeyChain1, 1) key1, _ := childKeyChain11.ECPubKey() - address1, _ := bitcoin.GetAddressFromPubKey(key1, true) + address1, _ := script.NewAddressFromPublicKey(key1, true) assert.Equal(t, addressInternal, address1.AddressString) }) @@ -162,7 +163,7 @@ func TestNewPaymail(t *testing.T) { // verify the correctness of the derivation path expectedXpubDerivationPath := fmt.Sprintf("%d/%d/%d", utils.ChainExternal, derivationNumber, pm.XpubDerivationSeq) - masterXPub, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXPub) + masterXPub, _ := compat.GetHDKeyFromExtendedPublicKey(testXPub) expectedExternalXpub, err := masterXPub.DeriveChildFromPath(expectedXpubDerivationPath) require.NoError(t, err) @@ -198,7 +199,7 @@ func TestNewPaymail(t *testing.T) { // verify the correctness of the derivation path expectedXpubDerivationPath := fmt.Sprintf("%d/%d/%d", utils.ChainExternal, derivationNumber, initialDerivationSeq) - masterXPub, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXPub) + masterXPub, _ := compat.GetHDKeyFromExtendedPublicKey(testXPub) expectedHdPubKey, err := masterXPub.DeriveChildFromPath(expectedXpubDerivationPath) require.NoError(t, err) @@ -206,7 +207,7 @@ func TestNewPaymail(t *testing.T) { expectedPubKey, err := expectedHdPubKey.ECPubKey() require.NoError(t, err) - require.Equal(t, hex.EncodeToString(expectedPubKey.SerialiseCompressed()), firstPubKey) + require.Equal(t, hex.EncodeToString(expectedPubKey.SerializeCompressed()), firstPubKey) }) t.Run("RotatePubKey() test", func(t *testing.T) { @@ -239,7 +240,7 @@ func TestNewPaymail(t *testing.T) { require.NotEqual(t, firstPubKey, secondPubKey) externalXPubDerivationPath := fmt.Sprintf("%d/%d/%d", utils.ChainExternal, derivationNumber, pm.XpubDerivationSeq) - masterXPub, _ := bitcoin.GetHDKeyFromExtendedPublicKey(testXPub) + masterXPub, _ := compat.GetHDKeyFromExtendedPublicKey(testXPub) expectedExternalXpub, err := masterXPub.DeriveChildFromPath(externalXPubDerivationPath) require.NoError(t, err) @@ -247,7 +248,7 @@ func TestNewPaymail(t *testing.T) { expectedPubKey, err := expectedExternalXpub.ECPubKey() require.NoError(t, err) - require.Equal(t, hex.EncodeToString(expectedPubKey.SerialiseCompressed()), secondPubKey) + require.Equal(t, hex.EncodeToString(expectedPubKey.SerializeCompressed()), secondPubKey) }) t.Run("ExternalXPub and PubKey rotation test", func(t *testing.T) { diff --git a/engine/model_transaction_config.go b/engine/model_transaction_config.go index 302b25a7..d3c95e19 100644 --- a/engine/model_transaction_config.go +++ b/engine/model_transaction_config.go @@ -11,11 +11,12 @@ import ( "time" "github.com/bitcoin-sv/go-paymail" + "github.com/bitcoin-sv/go-sdk/script" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" paymailclient "github.com/bitcoin-sv/spv-wallet/engine/paymail" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" magic "github.com/bitcoinschema/go-map" - "github.com/libsv/go-bt/v2/bscript" ) // TransactionConfig is the configuration used to start a transaction @@ -276,9 +277,14 @@ func (t *TransactionOutput) processPaymailViaP2P(client paymailclient.ServiceCli // processAddressOutput will process an output for a standard Bitcoin Address Transaction func (t *TransactionOutput) processAddressOutput() (err error) { - // Create the script from the Bitcoin address - var s *bscript.Script - if s, err = bscript.NewP2PKHFromAddress(t.To); err != nil { + // Create the script from the Bitcoin parsedAddress + parsedAddress, err := script.NewAddressFromString(t.To) + if err != nil { + return + } + + var s *script.Script + if s, err = p2pkh.Lock(parsedAddress); err != nil { return } @@ -302,7 +308,7 @@ func (t *TransactionOutput) processScriptOutput() (err error) { } // check whether go-bt parses the script correctly - if _, err = bscript.NewFromHexString(t.Script); err != nil { + if _, err = script.NewFromHex(t.Script); err != nil { return } @@ -321,15 +327,15 @@ func (t *TransactionOutput) processScriptOutput() (err error) { // processOpReturnOutput will process an op_return output func (t *TransactionOutput) processOpReturnOutput() (err error) { - // Create the script from the Bitcoin address - var script string + // Create the sc from the Bitcoin address + var sc string if len(t.OpReturn.Hex) > 0 { // raw op_return output in hex - var s *bscript.Script - if s, err = bscript.NewFromHexString(t.OpReturn.Hex); err != nil { + var s *script.Script + if s, err = script.NewFromHex(t.OpReturn.Hex); err != nil { return } - script = s.String() + sc = s.String() } else if len(t.OpReturn.HexParts) > 0 { // hex strings of the op_return output bytesArray := make([][]byte, 0) @@ -340,24 +346,24 @@ func (t *TransactionOutput) processOpReturnOutput() (err error) { } bytesArray = append(bytesArray, b) } - s := &bscript.Script{} - _ = s.AppendOpcodes(bscript.OpFALSE, bscript.OpRETURN) + s := &script.Script{} + _ = s.AppendOpcodes(script.OpFALSE, script.OpRETURN) if err = s.AppendPushDataArray(bytesArray); err != nil { return } - script = s.String() + sc = s.String() } else if len(t.OpReturn.StringParts) > 0 { // strings for the op_return output bytesArray := make([][]byte, 0) for _, s := range t.OpReturn.StringParts { bytesArray = append(bytesArray, []byte(s)) } - s := &bscript.Script{} - _ = s.AppendOpcodes(bscript.OpFALSE, bscript.OpRETURN) + s := &script.Script{} + _ = s.AppendOpcodes(script.OpFALSE, script.OpRETURN) if err = s.AppendPushDataArray(bytesArray); err != nil { return } - script = s.String() + sc = s.String() } else if t.OpReturn.Map != nil { // strings for the map op_return bytesArray := [][]byte{ @@ -374,12 +380,12 @@ func (t *TransactionOutput) processOpReturnOutput() (err error) { bytesArray = append(bytesArray, []byte(value.(string))) } } - s := &bscript.Script{} - _ = s.AppendOpcodes(bscript.OpFALSE, bscript.OpRETURN) + s := &script.Script{} + _ = s.AppendOpcodes(script.OpFALSE, script.OpRETURN) if err = s.AppendPushDataArray(bytesArray); err != nil { return } - script = s.String() + sc = s.String() } else { return spverrors.ErrInvalidOpReturnOutput } @@ -389,7 +395,7 @@ func (t *TransactionOutput) processOpReturnOutput() (err error) { t.Scripts, &ScriptOutput{ Satoshis: t.Satoshis, - Script: script, + Script: sc, ScriptType: utils.ScriptTypeNullData, }, ) diff --git a/engine/model_transactions.go b/engine/model_transactions.go index 9b7e25b5..13547721 100644 --- a/engine/model_transactions.go +++ b/engine/model_transactions.go @@ -233,7 +233,7 @@ func (m *Transaction) getValues() (outputValue uint64, fee uint64) { // SetBUMP Converts from bc.BUMP to our BUMP struct in Transaction model func (m *Transaction) SetBUMP(bump *bc.BUMP) { if bump != nil { - m.BUMP = bcBumpToBUMP(bump) + m.BUMP = sdkMPToBUMP(bump) } else { m.client.Logger().Error().Msg("No BUMP found") } diff --git a/engine/models_test.go b/engine/models_test.go index 046df4b6..46f3d74a 100644 --- a/engine/models_test.go +++ b/engine/models_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/engine/datastore" - "github.com/bitcoinschema/go-bitcoin/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -110,7 +110,7 @@ func TestModel_GetModelTableName(t *testing.T) { func (ts *EmbeddedDBTestSuite) createXpubModels(tc *TestingClient, t *testing.T, number int) { for i := 0; i < number; i++ { - _, xPublicKey, err := bitcoin.GenerateHDKeyPair(bitcoin.SecureSeedLength) + _, xPublicKey, err := compat.GenerateHDKeyPair(compat.SecureSeedLength) require.NoError(t, err) xPub := newXpub(xPublicKey, append(tc.client.DefaultModelOptions(), New())...) xPub.CurrentBalance = 125000 diff --git a/engine/pike/example_test.go b/engine/pike/example_test.go index 27f6203e..7557b5d8 100644 --- a/engine/pike/example_test.go +++ b/engine/pike/example_test.go @@ -4,9 +4,9 @@ import ( "encoding/hex" "fmt" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/pike" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bk/bec" ) func Example_generateLockingScripts() { @@ -16,7 +16,7 @@ func Example_generateLockingScripts() { if err != nil { panic(err) } - senderPubKey, err := bec.ParsePubKey(senderPublicKeyBytes, bec.S256()) + senderPubKey, err := ec.ParsePubKey(senderPublicKeyBytes) if err != nil { panic(err) } @@ -26,7 +26,7 @@ func Example_generateLockingScripts() { if err != nil { panic(err) } - receiverPubKey, err := bec.ParsePubKey(receiverPublicKeyBytes, bec.S256()) + receiverPubKey, err := ec.ParsePubKey(receiverPublicKeyBytes) if err != nil { panic(err) } diff --git a/engine/pike/pike_test.go b/engine/pike/pike_test.go index 59ece394..4017610e 100644 --- a/engine/pike/pike_test.go +++ b/engine/pike/pike_test.go @@ -4,8 +4,8 @@ import ( "encoding/hex" "testing" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/script/template" - "github.com/libsv/go-bk/bec" assert "github.com/stretchr/testify/require" ) @@ -14,13 +14,13 @@ func TestGenerateLockingScriptsFromTemplates(t *testing.T) { senderPubKeyHex := "027c1404c3ecb034053e6dd90bc68f7933284559c7d0763367584195a8796d9b0e" senderPubKeyBytes, err := hex.DecodeString(senderPubKeyHex) assert.NoError(t, err) - senderPubKey, err := bec.ParsePubKey(senderPubKeyBytes, bec.S256()) + senderPubKey, err := ec.ParsePubKey(senderPubKeyBytes) assert.NoError(t, err) receiverPubKeyHex := "03a34e456deecb6e6e9237e63e5b7d045d1d2a456eb6be43de1ec4e9ac9a07b50d" receiverPubKeyBytes, err := hex.DecodeString(receiverPubKeyHex) assert.NoError(t, err) - receiverPubKey, err := bec.ParsePubKey(receiverPubKeyBytes, bec.S256()) + receiverPubKey, err := ec.ParsePubKey(receiverPubKeyBytes) assert.NoError(t, err) outputsTemplate := []*template.OutputTemplate{ diff --git a/engine/pike_service_provider.go b/engine/pike_service_provider.go index 720db407..9e7e665a 100644 --- a/engine/pike_service_provider.go +++ b/engine/pike_service_provider.go @@ -6,6 +6,7 @@ import ( "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/go-paymail/server" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/pike" "github.com/bitcoin-sv/spv-wallet/engine/script/template" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" @@ -136,7 +137,7 @@ func generateReferenceID() (string, error) { return referenceID, spverrors.Wrapf(err, "failed to generate reference id") } -func getPublicKeys(receiverPubKeyHex, senderPubKeyHex string) (*bec.PublicKey, *bec.PublicKey, error) { +func getPublicKeys(receiverPubKeyHex, senderPubKeyHex string) (*ec.PublicKey, *bec.PublicKey, error) { receiverPubKey, err := getPublicKey(receiverPubKeyHex) if err != nil { return nil, nil, err @@ -150,12 +151,12 @@ func getPublicKeys(receiverPubKeyHex, senderPubKeyHex string) (*bec.PublicKey, * return receiverPubKey, senderPubKey, nil } -func getPublicKey(pubKeyHex string) (*bec.PublicKey, error) { +func getPublicKey(pubKeyHex string) (*ec.PublicKey, error) { pubKeyBytes, err := hex.DecodeString(pubKeyHex) if err != nil { return nil, spverrors.Wrapf(err, "failed to decode public key hex") } - key, err := bec.ParsePubKey(pubKeyBytes, bec.S256()) + key, err := ec.ParsePubKey(pubKeyBytes) return key, spverrors.Wrapf(err, "failed to parse public key") } diff --git a/engine/record_tx.go b/engine/record_tx.go index 181679fb..53dbe65c 100644 --- a/engine/record_tx.go +++ b/engine/record_tx.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) type recordTxStrategy interface { @@ -45,9 +45,9 @@ func recordTransaction(ctx context.Context, c ClientInterface, strategy recordTx return } -func getOutgoingTxRecordStrategy(xPubKey string, btTx *bt.Tx, draftID string) (recordTxStrategy, error) { +func getOutgoingTxRecordStrategy(xPubKey string, sdkTx *trx.Transaction, draftID string) (recordTxStrategy, error) { rts := &outgoingTx{ - BtTx: btTx, + SDKTx: sdkTx, RelatedDraftID: draftID, XPubKey: xPubKey, } @@ -59,8 +59,8 @@ func getOutgoingTxRecordStrategy(xPubKey string, btTx *bt.Tx, draftID string) (r return rts, nil } -func getIncomingTxRecordStrategy(ctx context.Context, c ClientInterface, btTx *bt.Tx) (recordIncomingTxStrategy, error) { - tx, err := getTransactionByHex(ctx, btTx.String(), c.DefaultModelOptions()...) +func getIncomingTxRecordStrategy(ctx context.Context, c ClientInterface, sdkTx *trx.Transaction) (recordIncomingTxStrategy, error) { + tx, err := getTransactionByHex(ctx, sdkTx.String(), c.DefaultModelOptions()...) if err != nil { return nil, err } @@ -68,13 +68,13 @@ func getIncomingTxRecordStrategy(ctx context.Context, c ClientInterface, btTx *b var rts recordIncomingTxStrategy if tx != nil { - tx.parsedTx = btTx + tx.parsedTx = sdkTx rts = &internalIncomingTx{ Tx: tx, } } else { rts = &externalIncomingTx{ - BtTx: btTx, + SDKTx: sdkTx, } } diff --git a/engine/record_tx_strategy_external_incoming_tx.go b/engine/record_tx_strategy_external_incoming_tx.go index a4101540..7694db97 100644 --- a/engine/record_tx_strategy_external_incoming_tx.go +++ b/engine/record_tx_strategy_external_incoming_tx.go @@ -4,12 +4,12 @@ import ( "context" "fmt" - "github.com/libsv/go-bt/v2" + trx "github.com/bitcoin-sv/go-sdk/transaction" ) type externalIncomingTx struct { - BtTx *bt.Tx - txID string + SDKTx *trx.Transaction + txID string } func (strategy *externalIncomingTx) Name() string { @@ -34,7 +34,7 @@ func (strategy *externalIncomingTx) Execute(ctx context.Context, c ClientInterfa } func (strategy *externalIncomingTx) Validate() error { - if strategy.BtTx == nil { + if strategy.SDKTx == nil { return ErrMissingFieldHex } @@ -43,7 +43,7 @@ func (strategy *externalIncomingTx) Validate() error { func (strategy *externalIncomingTx) TxID() string { if strategy.txID == "" { - strategy.txID = strategy.BtTx.TxID() + strategy.txID = strategy.SDKTx.TxID().String() } return strategy.txID } @@ -54,7 +54,7 @@ func (strategy *externalIncomingTx) LockKey() string { func _createExternalTxToRecord(ctx context.Context, eTx *externalIncomingTx, c ClientInterface, opts []ModelOps) (*Transaction, error) { // Create NEW tx model - tx := txFromBtTx(eTx.BtTx, c.DefaultModelOptions(append(opts, New())...)...) + tx := txFromBtTx(eTx.SDKTx, c.DefaultModelOptions(append(opts, New())...)...) if !tx.TransactionBase.hasOneKnownDestination(ctx, c) { return nil, ErrNoMatchingOutputs diff --git a/engine/record_tx_strategy_outgoing_tx.go b/engine/record_tx_strategy_outgoing_tx.go index b324bfd2..1625ce8c 100644 --- a/engine/record_tx_strategy_outgoing_tx.go +++ b/engine/record_tx_strategy_outgoing_tx.go @@ -5,12 +5,12 @@ import ( "fmt" "slices" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2" ) type outgoingTx struct { - BtTx *bt.Tx + SDKTx *trx.Transaction RelatedDraftID string XPubKey string txID string @@ -71,7 +71,7 @@ func (strategy *outgoingTx) Execute(ctx context.Context, c ClientInterface, opts } func (strategy *outgoingTx) Validate() error { - if strategy.BtTx == nil { + if strategy.SDKTx == nil { return ErrMissingFieldHex } @@ -88,7 +88,7 @@ func (strategy *outgoingTx) Validate() error { func (strategy *outgoingTx) TxID() string { if strategy.txID == "" { - strategy.txID = strategy.BtTx.TxID() + strategy.txID = strategy.SDKTx.TxID().String() } return strategy.txID } @@ -100,7 +100,7 @@ func (strategy *outgoingTx) LockKey() string { func (strategy *outgoingTx) createOutgoingTxToRecord(ctx context.Context, c ClientInterface, opts []ModelOps) (*Transaction, error) { // Create NEW transaction model newOpts := c.DefaultModelOptions(append(opts, WithXPub(strategy.XPubKey), New())...) - tx := txFromBtTx(strategy.BtTx, newOpts...) + tx := txFromBtTx(strategy.SDKTx, newOpts...) tx.DraftID = strategy.RelatedDraftID if err := _hydrateOutgoingWithDraft(ctx, tx); err != nil { diff --git a/engine/script/template/p2pkh.go b/engine/script/template/p2pkh.go index afcb21d9..662f0bcb 100644 --- a/engine/script/template/p2pkh.go +++ b/engine/script/template/p2pkh.go @@ -5,8 +5,8 @@ import ( "encoding/hex" "sync" + script "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/libsv/go-bt/v2/bscript" ) var ( @@ -16,11 +16,11 @@ var ( func initScriptHex() { opcodes := []byte{ - bscript.OpDUP, - bscript.OpHASH160, - bscript.OpPUBKEYHASH, - bscript.OpEQUALVERIFY, - bscript.OpCHECKSIG, + script.OpDUP, + script.OpHASH160, + script.OpPUBKEYHASH, + script.OpEQUALVERIFY, + script.OpCHECKSIG, } // Convert opcodes to hexadecimal string diff --git a/engine/script/template/p2pkh_test.go b/engine/script/template/p2pkh_test.go index bb6ce3f5..45b1c3e3 100644 --- a/engine/script/template/p2pkh_test.go +++ b/engine/script/template/p2pkh_test.go @@ -4,9 +4,9 @@ import ( "encoding/hex" "testing" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/crypto" - "github.com/libsv/go-bt/bscript" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + crypto "github.com/bitcoin-sv/go-sdk/primitives/hash" + script "github.com/bitcoin-sv/go-sdk/script" assert "github.com/stretchr/testify/require" ) @@ -85,34 +85,34 @@ func TestEvaluate(t *testing.T) { pubKeyHex := "027c1404c3ecb034053e6dd90bc68f7933284559c7d0763367584195a8796d9b0e" pubKeyBytes, err := hex.DecodeString(pubKeyHex) assert.NoError(t, err) - mockPublicKey, err := bec.ParsePubKey(pubKeyBytes, bec.S256()) + mockPublicKey, err := ec.ParsePubKey(pubKeyBytes) assert.NoError(t, err) - mockPubKeyHash := crypto.Hash160(mockPublicKey.SerialiseCompressed()) + mockPubKeyHash := crypto.Hash160(mockPublicKey.SerializeCompressed()) t.Run("Valid Cases", func(t *testing.T) { validTests := []struct { name string script []byte - publicKey *bec.PublicKey + publicKey *ec.PublicKey expected []byte }{ { name: "valid script with OP_PUBKEYHASH", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEYHASH, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + script: []byte{script.OpDUP, script.OpHASH160, script.OpPUBKEYHASH, script.OpEQUALVERIFY, script.OpCHECKSIG}, publicKey: mockPublicKey, - expected: append([]byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpDATA20}, append(mockPubKeyHash, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG)...), + expected: append([]byte{script.OpDUP, script.OpHASH160, script.OpDATA20}, append(mockPubKeyHash, script.OpEQUALVERIFY, script.OpCHECKSIG)...), }, { name: "valid script without OP_PUBKEYHASH or OP_PUBKEY", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + script: []byte{script.OpDUP, script.OpHASH160, script.OpEQUALVERIFY, script.OpCHECKSIG}, publicKey: mockPublicKey, - expected: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + expected: []byte{script.OpDUP, script.OpHASH160, script.OpEQUALVERIFY, script.OpCHECKSIG}, }, { name: "script with OP_PUSHDATA1 and hex data matching PUBKEY and PUBKEYHASH opcodes", - script: []byte{bscript.OpPUSHDATA1, 1, bscript.OpPUBKEYHASH, bscript.OpADD, bscript.OpPUSHDATA1, 1, bscript.OpPUBKEY, bscript.OpEQUALVERIFY}, + script: []byte{script.OpPUSHDATA1, 1, script.OpPUBKEYHASH, script.OpADD, script.OpPUSHDATA1, 1, script.OpPUBKEY, script.OpEQUALVERIFY}, publicKey: mockPublicKey, - expected: []byte{bscript.OpPUSHDATA1, 1, bscript.OpPUBKEYHASH, bscript.OpADD, bscript.OpPUSHDATA1, 1, bscript.OpPUBKEY, bscript.OpEQUALVERIFY}, + expected: []byte{script.OpPUSHDATA1, 1, script.OpPUBKEYHASH, script.OpADD, script.OpPUSHDATA1, 1, script.OpPUBKEY, script.OpEQUALVERIFY}, }, { name: "empty script", @@ -122,9 +122,9 @@ func TestEvaluate(t *testing.T) { }, { name: "script with only valid push data", - script: []byte{bscript.OpPUSHDATA1, 2, 0xaa, 0xbb}, + script: []byte{script.OpPUSHDATA1, 2, 0xaa, 0xbb}, publicKey: mockPublicKey, - expected: []byte{bscript.OpPUSHDATA1, 2, 0xaa, 0xbb}, + expected: []byte{script.OpPUSHDATA1, 2, 0xaa, 0xbb}, }, } @@ -143,7 +143,7 @@ func TestEvaluate(t *testing.T) { invalidTests := []struct { name string script []byte - publicKey *bec.PublicKey + publicKey *ec.PublicKey }{ { name: "invalid script", @@ -152,7 +152,7 @@ func TestEvaluate(t *testing.T) { }, { name: "valid script with OP_PUBKEY", - script: []byte{bscript.OpDUP, bscript.OpHASH160, bscript.OpPUBKEY, bscript.OpEQUALVERIFY, bscript.OpCHECKSIG}, + script: []byte{script.OpDUP, script.OpHASH160, script.OpPUBKEY, script.OpEQUALVERIFY, script.OpCHECKSIG}, publicKey: mockPublicKey, }, } diff --git a/engine/utils/scripts.go b/engine/utils/scripts.go new file mode 100644 index 00000000..8584fa33 --- /dev/null +++ b/engine/utils/scripts.go @@ -0,0 +1,21 @@ +package utils + +import ( + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + trx "github.com/bitcoin-sv/go-sdk/transaction" + sighash "github.com/bitcoin-sv/go-sdk/transaction/sighash" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" +) + +// GetUnlockingScript will generate an unlocking script +func GetUnlockingScript(tx *trx.Transaction, inputIndex uint32, privateKey *ec.PrivateKey) (*p2pkh.P2PKH, error) { + sigHashFlags := sighash.AllForkID + + sc, err := p2pkh.Unlock(privateKey, &sigHashFlags) + if err != nil { + return nil, spverrors.Wrapf(err, "failed to create unlocking script") + } + + return sc, nil +} diff --git a/engine/utils/scripts_test.go b/engine/utils/scripts_test.go new file mode 100644 index 00000000..d4b585bf --- /dev/null +++ b/engine/utils/scripts_test.go @@ -0,0 +1 @@ +package utils From 83c6dd44363000b3aab2a8a38d41e92e805eaf9a Mon Sep 17 00:00:00 2001 From: wregulski Date: Wed, 9 Oct 2024 21:54:56 +0200 Subject: [PATCH 03/24] feat(SPV-898): remove libsv references completely --- engine/beef_tx_sorting_test.go | 24 +++---- engine/ef_tx.go | 16 +++-- engine/model_access_keys_test.go | 6 +- engine/model_destinations_test.go | 6 +- engine/model_draft_transactions.go | 6 +- engine/model_draft_transactions_test.go | 26 +++---- engine/model_paymail_addresses_test.go | 9 ++- engine/model_transactions.go | 31 ++++---- engine/model_transactions_test.go | 16 ++--- engine/paymail_service_provider.go | 31 ++++---- engine/pike_service_provider.go | 3 +- ...record_tx_strategy_external_incoming_tx.go | 2 +- engine/record_tx_strategy_outgoing_tx.go | 2 +- engine/spv_wallet_engine_test.go | 71 +++++++++---------- engine/tx_service.go | 5 +- engine/types/type42/linking_key_test.go | 30 ++++---- go.mod | 6 +- go.sum | 11 --- 18 files changed, 141 insertions(+), 160 deletions(-) diff --git a/engine/beef_tx_sorting_test.go b/engine/beef_tx_sorting_test.go index 8b6ac7a7..6ed590bf 100644 --- a/engine/beef_tx_sorting_test.go +++ b/engine/beef_tx_sorting_test.go @@ -6,7 +6,6 @@ import ( "testing" trx "github.com/bitcoin-sv/go-sdk/transaction" - "github.com/libsv/go-bt/v2" "github.com/stretchr/testify/assert" ) @@ -87,7 +86,7 @@ func getTxsFromOldestToNewestWithUnnecessaryData() []*trx.Transaction { newestTx := createTx(eightTx) - txsFromOldestToNewest := []*bt.Tx{ + txsFromOldestToNewest := []*trx.Transaction{ oldestTx, secondTx, thirdTx, @@ -102,25 +101,20 @@ func getTxsFromOldestToNewestWithUnnecessaryData() []*trx.Transaction { return txsFromOldestToNewest } -func createTx(inputsParents ...*trx.Transaction) *bt.Tx { - inputs := make([]*bt.Input, 0) - +func createTx(inputsParents ...*trx.Transaction) *trx.Transaction { + tx := trx.NewTransaction() for _, parent := range inputsParents { - in := bt.Input{} - in.PreviousTxIDAdd(parent.TxIDBytes()) - - inputs = append(inputs, &in) + tx.AddInput(&trx.TransactionInput{ + SourceTXID: parent.TxID(), + }) } - transaction := bt.NewTx() - transaction.Inputs = append(transaction.Inputs, inputs...) - - return transaction + return tx } -func shuffleTransactions(txs []*bt.Tx) []*bt.Tx { +func shuffleTransactions(txs []*trx.Transaction) []*trx.Transaction { n := len(txs) - result := make([]*bt.Tx, n) + result := make([]*trx.Transaction, n) copy(result, txs) for i := n - 1; i > 0; i-- { diff --git a/engine/ef_tx.go b/engine/ef_tx.go index c2f94c94..582ca0b5 100644 --- a/engine/ef_tx.go +++ b/engine/ef_tx.go @@ -2,7 +2,6 @@ package engine import ( "context" - "encoding/hex" trx "github.com/bitcoin-sv/go-sdk/transaction" ) @@ -21,7 +20,7 @@ func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efH needToHydrate := false for _, input := range btTx.Inputs { - if input.PreviousTxScript == nil { + if input.SourceTXID == nil { needToHydrate = true break } @@ -33,7 +32,12 @@ func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efH } } - return hex.EncodeToString(btTx.ExtendedBytes()), true + ef, err := btTx.EFHex() + if err != nil { + return "", false + } + + return ef, true } func hydrate(ctx context.Context, tx *trx.Transaction, store TransactionGetter) (ok bool) { @@ -61,8 +65,10 @@ func hydrate(ctx context.Context, tx *trx.Transaction, store TransactionGetter) } o := pbtTx.Outputs[input.SourceTxOutIndex] - input.PreviousTxSatoshis = o.Satoshis - input.PreviousTxScript = o.LockingScript + input.SetSourceTxOutput(&trx.TransactionOutput{ + Satoshis: o.Satoshis, + LockingScript: o.LockingScript, + }) } return true diff --git a/engine/model_access_keys_test.go b/engine/model_access_keys_test.go index 9f0c24a1..0891c796 100644 --- a/engine/model_access_keys_test.go +++ b/engine/model_access_keys_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" primitives "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bk/bec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,9 +22,9 @@ func Test_newAccessKey(t *testing.T) { assert.Equal(t, 64, len(key.Key)) privateKey, _ := primitives.PrivateKeyFromHex(key.Key) - assert.IsType(t, bec.PrivateKey{}, *privateKey) + assert.IsType(t, ec.PrivateKey{}, *privateKey) publicKey := privateKey.PubKey() - assert.IsType(t, bec.PublicKey{}, *publicKey) + assert.IsType(t, ec.PublicKey{}, *publicKey) id := utils.Hash(hex.EncodeToString(publicKey.SerializeCompressed())) assert.Equal(t, id, key.ID) }) diff --git a/engine/model_destinations_test.go b/engine/model_destinations_test.go index 64e48aff..75a6d229 100644 --- a/engine/model_destinations_test.go +++ b/engine/model_destinations_test.go @@ -4,11 +4,11 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/tester" "github.com/bitcoin-sv/spv-wallet/engine/utils" - bscript2 "github.com/libsv/go-bt/v2/bscript" "github.com/mrz1836/go-cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -47,7 +47,7 @@ func TestDestination_newDestination(t *testing.T) { assert.Equal(t, true, destination.IsNew()) assert.Equal(t, testScript, destination.LockingScript) assert.Equal(t, xPubID, destination.XpubID) - assert.Equal(t, bscript2.ScriptTypeNonStandard, destination.Type) + assert.Equal(t, script.ScriptTypeNonStandard, destination.Type) assert.Equal(t, testDestinationID, destination.GetID()) }) } @@ -84,7 +84,7 @@ func TestDestination_newAddress(t *testing.T) { // Check set locking script assert.Equal(t, testLockingScript, address.LockingScript) - assert.Equal(t, bscript2.ScriptTypePubKeyHash, address.Type) + assert.Equal(t, script.ScriptTypePubKeyHash, address.Type) assert.Equal(t, testAddressID, address.GetID()) }) } diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index 12f3214c..2ed1a0a0 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -850,8 +850,10 @@ func (m *DraftTransaction) SignInputs(xPriv *compat.ExtendedKey) (signedHex stri ); err != nil { return } - txDraft.Inputs[index].PreviousTxScript = ls - txDraft.Inputs[index].PreviousTxSatoshis = input.Satoshis + txDraft.Inputs[index].SetSourceTxOutput(&trx.TransactionOutput{ + Satoshis: input.Satoshis, + LockingScript: ls, + }) // Derive the child key (chain) var chainKey *compat.ExtendedKey diff --git a/engine/model_draft_transactions_test.go b/engine/model_draft_transactions_test.go index f69f0848..5d04e186 100644 --- a/engine/model_draft_transactions_test.go +++ b/engine/model_draft_transactions_test.go @@ -10,15 +10,14 @@ import ( compat "github.com/bitcoin-sv/go-sdk/compat/bip32" bsm "github.com/bitcoin-sv/go-sdk/compat/bsm" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" trx "github.com/bitcoin-sv/go-sdk/transaction" sighash "github.com/bitcoin-sv/go-sdk/transaction/sighash" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" xtester "github.com/bitcoin-sv/spv-wallet/engine/tester/paymailmock" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/bip32" - "github.com/libsv/go-bt/v2/bscript" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1132,7 +1131,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { require.NoError(t, err) // Derive the child key (chain) - var chainKey *bip32.ExtendedKey + var chainKey *compat.ExtendedKey if chainKey, err = xPrivHD.Child( 0, ); err != nil { @@ -1140,7 +1139,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { } // Derive the child key (num) - var numKey *bip32.ExtendedKey + var numKey *compat.ExtendedKey if numKey, err = chainKey.Child( 0, ); err != nil { @@ -1148,7 +1147,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { } // Get the private key - var privateKey *bec.PrivateKey + var privateKey *ec.PrivateKey if privateKey, err = compat.GetPrivateKeyFromHDKey( numKey, ); err != nil { @@ -1175,7 +1174,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { tests := []struct { name string config *TransactionConfig - xPriv *bip32.ExtendedKey + xPriv *compat.ExtendedKey wantErr assert.ErrorAssertionFunc }{ { @@ -1203,14 +1202,17 @@ func TestDraftTransaction_SignInputs(t *testing.T) { tx, err = trx.NewTransactionFromHex(gotSignedHex) require.NoError(t, err) - var ls *bscript.Script - if ls, err = bscript.NewFromHexString( + var ls *script.Script + if ls, err = script.NewFromHex( lockingScript, ); err != nil { return } - tx.Inputs[0].PreviousTxScript = ls - tx.Inputs[0].PreviousTxSatoshis = 12229 + + tx.Inputs[0].SetSourceTxOutput(&trx.TransactionOutput{ + LockingScript: ls, + Satoshis: 12229, + }) require.NoError(t, err) assert.True(t, tx.Version > 0) @@ -1218,7 +1220,7 @@ func TestDraftTransaction_SignInputs(t *testing.T) { unlocker := input.UnlockingScript.ToASM() require.NoError(t, err) scriptParts := strings.Split(unlocker, " ") - pubKey := hex.EncodeToString(privateKey.PubKey().SerialiseCompressed()) + pubKey := hex.EncodeToString(privateKey.PubKey().SerializeCompressed()) var hash []byte hash, err = tx.CalcInputSignatureHash(0, sighash.AllForkID) diff --git a/engine/model_paymail_addresses_test.go b/engine/model_paymail_addresses_test.go index a91cd864..caacad34 100644 --- a/engine/model_paymail_addresses_test.go +++ b/engine/model_paymail_addresses_test.go @@ -9,7 +9,6 @@ import ( compat "github.com/bitcoin-sv/go-sdk/compat/bip32" script "github.com/bitcoin-sv/go-sdk/script" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bk/bip32" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -32,14 +31,14 @@ func TestNewPaymail(t *testing.T) { require.NotNil(t, xPub) // Get the external public key - var paymailExternalKey *bip32.ExtendedKey + var paymailExternalKey *compat.ExtendedKey paymailExternalKey, err = compat.GetHDKeyByPath( xPub, utils.ChainExternal, externalDerivationNum, ) require.NoError(t, err) require.NotNil(t, paymailExternalKey) - var paymailIdentityKey *bip32.ExtendedKey + var paymailIdentityKey *compat.ExtendedKey paymailIdentityKey, err = compat.GetHDKeyChild(paymailExternalKey, uint32(utils.MaxInt32)) require.NoError(t, err) require.NotNil(t, paymailIdentityKey) @@ -73,7 +72,7 @@ func TestNewPaymail(t *testing.T) { err = Get(ctx, p2, conditions, false, 0, false) require.NoError(t, err) - var identityKey *bip32.ExtendedKey + var identityKey *compat.ExtendedKey identityKey, err = p2.GetIdentityXpub() require.NoError(t, err) require.NotNil(t, identityKey) @@ -301,7 +300,7 @@ func TestNewPaymail(t *testing.T) { } func randDerivationNum() uint32 { - rnd := rand.Int63n(int64(bip32.HardenedKeyStart)) + rnd := rand.Int63n(int64(compat.HardenedKeyStart)) if rnd < 0 { rnd = rnd * -1 } diff --git a/engine/model_transactions.go b/engine/model_transactions.go index 13547721..17165eb4 100644 --- a/engine/model_transactions.go +++ b/engine/model_transactions.go @@ -3,11 +3,10 @@ package engine import ( "context" + trx "github.com/bitcoin-sv/go-sdk/transaction" chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bc" - "github.com/libsv/go-bt/v2" ) // TransactionBase is the same fields share between multiple transaction models @@ -16,7 +15,7 @@ type TransactionBase struct { Hex string `json:"hex" toml:"hex" yaml:"hex" gorm:"<-:create;type:text;comment:This is the raw transaction hex"` // Private for internal use - parsedTx *bt.Tx `gorm:"-"` // The go-bt version of the transaction + parsedTx *trx.Transaction `gorm:"-"` // The GO-SDK version of the transaction } // TransactionDirection String describing the direction of the transaction (in / out) @@ -88,15 +87,15 @@ func emptyTx(opts ...ModelOps) *Transaction { // baseTxFromHex creates the standard transaction model base func baseTxFromHex(hex string, opts ...ModelOps) (*Transaction, error) { - var btTx *bt.Tx + var btTx *trx.Transaction var err error - if btTx, err = bt.NewTxFromString(hex); err != nil { + if btTx, err = trx.NewTransactionFromHex(hex); err != nil { return nil, spverrors.Wrapf(err, "error parsing transaction hex") } tx := emptyTx(opts...) - tx.ID = btTx.TxID() + tx.ID = btTx.TxID().String() tx.Hex = hex tx.parsedTx = btTx @@ -128,11 +127,11 @@ func newTransactionWithDraftID(txHex, draftID string, opts ...ModelOps) (*Transa return tx, nil } -func txFromBtTx(btTx *bt.Tx, opts ...ModelOps) *Transaction { +func txFromSDKTx(sdkTx *trx.Transaction, opts ...ModelOps) *Transaction { tx := emptyTx(opts...) - tx.ID = btTx.TxID() - tx.Hex = btTx.String() - tx.parsedTx = btTx + tx.ID = sdkTx.TxID().String() + tx.Hex = sdkTx.String() + tx.parsedTx = sdkTx return tx } @@ -183,13 +182,13 @@ func (m *Transaction) GetID() string { func (m *Transaction) setID() (err error) { // Parse the hex (if not already parsed) if m.TransactionBase.parsedTx == nil { - if m.TransactionBase.parsedTx, err = bt.NewTxFromString(m.Hex); err != nil { + if m.TransactionBase.parsedTx, err = trx.NewTransactionFromHex(m.Hex); err != nil { return } } // Set the true transaction ID - m.ID = m.TransactionBase.parsedTx.TxID() + m.ID = m.TransactionBase.parsedTx.TxID().String() return } @@ -211,7 +210,7 @@ func (m *Transaction) getValues() (outputValue uint64, fee uint64) { // todo: missing inputs in some tests? var inputValue uint64 for _, input := range m.TransactionBase.parsedTx.Inputs { - inputValue += input.PreviousTxSatoshis + inputValue += *input.SourceTxSatoshis() } if inputValue > 0 { @@ -231,9 +230,9 @@ func (m *Transaction) getValues() (outputValue uint64, fee uint64) { } // SetBUMP Converts from bc.BUMP to our BUMP struct in Transaction model -func (m *Transaction) SetBUMP(bump *bc.BUMP) { - if bump != nil { - m.BUMP = sdkMPToBUMP(bump) +func (m *Transaction) SetBUMP(mp *trx.MerklePath) { + if mp != nil { + m.BUMP = sdkMPToBUMP(mp) } else { m.client.Logger().Error().Msg("No BUMP found") } diff --git a/engine/model_transactions_test.go b/engine/model_transactions_test.go index 0167ddeb..4bc2575e 100644 --- a/engine/model_transactions_test.go +++ b/engine/model_transactions_test.go @@ -5,12 +5,12 @@ import ( "database/sql" "testing" + "github.com/bitcoin-sv/go-sdk/script" + trx "github.com/bitcoin-sv/go-sdk/transaction" "github.com/bitcoin-sv/spv-wallet/engine/datastore" customTypes "github.com/bitcoin-sv/spv-wallet/engine/datastore/customtypes" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bt/v2" - "github.com/libsv/go-bt/v2/bscript" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -645,12 +645,12 @@ func TestTransaction_Display(t *testing.T) { // TestTransaction_Save will test the method Save() func (ts *EmbeddedDBTestSuite) TestTransaction_Save() { - parsedTx, errP := bt.NewTxFromString(testTxHex) + parsedTx, errP := trx.NewTransactionFromHex(testTxHex) require.NoError(ts.T(), errP) require.NotNil(ts.T(), parsedTx) - var parsedInTx *bt.Tx - parsedInTx, errP = bt.NewTxFromString(testTx2Hex) + var parsedInTx *trx.Transaction + parsedInTx, errP = trx.NewTransactionFromHex(testTx2Hex) require.NoError(ts.T(), errP) require.NotNil(ts.T(), parsedInTx) @@ -729,7 +729,7 @@ func (ts *EmbeddedDBTestSuite) TestTransaction_Save() { require.NoError(t, err) require.NotNil(t, utxo) assert.Equal(t, xPub.GetID(), utxo.XpubID) - assert.Equal(t, bscript.ScriptTypePubKeyHash, utxo.Type) + assert.Equal(t, script.ScriptTypePubKeyHash, utxo.Type) assert.Equal(t, testTxScriptPubKey1, utxo.ScriptPubKey) assert.Empty(t, utxo.DraftID) assert.Empty(t, utxo.SpendingTxID) @@ -738,7 +738,7 @@ func (ts *EmbeddedDBTestSuite) TestTransaction_Save() { utxo2, err = getUtxo(tc.ctx, transaction.ID, 1, tc.client.DefaultModelOptions()...) assert.Nil(t, err) assert.Equal(t, xPub2.GetID(), utxo2.XpubID) - assert.Equal(t, bscript.ScriptTypePubKeyHash, utxo2.Type) + assert.Equal(t, script.ScriptTypePubKeyHash, utxo2.Type) assert.Equal(t, testTxScriptPubKey2, utxo2.ScriptPubKey) assert.Empty(t, utxo2.DraftID) assert.Empty(t, utxo2.SpendingTxID) @@ -777,7 +777,7 @@ func (ts *EmbeddedDBTestSuite) TestTransaction_Save() { require.NotNil(t, utxoIn) require.NoError(t, err) assert.Equal(t, xPub.GetID(), utxoIn.XpubID) - assert.Equal(t, bscript.ScriptTypePubKeyHash, utxoIn.Type) + assert.Equal(t, script.ScriptTypePubKeyHash, utxoIn.Type) assert.Equal(t, testTxInScriptPubKey, utxoIn.ScriptPubKey) assert.Empty(t, utxoIn.SpendingTxID) diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index d1b79090..3c4e32b8 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -8,12 +8,13 @@ import ( "github.com/bitcoin-sv/go-paymail/beef" "github.com/bitcoin-sv/go-paymail/server" "github.com/bitcoin-sv/go-paymail/spv" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" + trx "github.com/bitcoin-sv/go-sdk/transaction" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bt/v2" ) // PaymailDefaultServiceProvider is an interface for overriding the paymail actions in go-paymail/server @@ -142,7 +143,7 @@ func (p *PaymailDefaultServiceProvider) RecordTransaction(ctx context.Context, metadata[ReferenceIDField] = p2pTx.Reference // Record the transaction - btTx := buildBtTx(p2pTx) + btTx := buildSDKTx(p2pTx) rts, err := getIncomingTxRecordStrategy(ctx, p.client, btTx) if err != nil { return nil, err @@ -270,32 +271,34 @@ func createDestination(ctx context.Context, pm *PaymailAddress, opts ...ModelOps return dst, nil } -func createLockingScript(ecPubKey *bec.PublicKey) (lockingScript string, err error) { - bsvAddress, err := bitcoin.GetAddressFromPubKey(ecPubKey, true) +func createLockingScript(ecPubKey *ec.PublicKey) (lockingScript string, err error) { + bsvAddress, err := script.NewAddressFromPublicKey(ecPubKey, true) if err != nil { return } - address := bsvAddress.AddressString - lockingScript, err = bitcoin.ScriptFromAddress(address) + ls, err := p2pkh.Lock(bsvAddress) + lockingScript = ls.String() return } -func buildBtTx(p2pTx *paymail.P2PTransaction) *bt.Tx { +func buildSDKTx(p2pTx *paymail.P2PTransaction) *trx.Transaction { if p2pTx.DecodedBeef != nil { res := p2pTx.DecodedBeef.GetLatestTx() for _, input := range res.Inputs { - prevTxDt := find(p2pTx.DecodedBeef.Transactions, func(tx *beef.TxData) bool { return tx.Transaction.TxID() == input.PreviousTxIDStr() }) + prevTxDt := find(p2pTx.DecodedBeef.Transactions, func(tx *beef.TxData) bool { return tx.Transaction.TxID() == input.SourceTXID }) - o := (*prevTxDt).Transaction.Outputs[input.PreviousTxOutIndex] - input.PreviousTxSatoshis = o.Satoshis - input.PreviousTxScript = o.LockingScript + o := (*prevTxDt).Transaction.Outputs[input.SourceTxOutIndex] + input.SetSourceTxOutput(&trx.TransactionOutput{ + Satoshis: o.Satoshis, + LockingScript: o.LockingScript, + }) } return res } - res, _ := bt.NewTxFromString(p2pTx.Hex) + res, _ := trx.NewTransactionFromHex(p2pTx.Hex) return res } diff --git a/engine/pike_service_provider.go b/engine/pike_service_provider.go index 9e7e665a..61cf109c 100644 --- a/engine/pike_service_provider.go +++ b/engine/pike_service_provider.go @@ -11,7 +11,6 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/script/template" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "github.com/libsv/go-bk/bec" ) // PikeContactServiceProvider is an interface for handling the pike contact actions in go-paymail/server @@ -137,7 +136,7 @@ func generateReferenceID() (string, error) { return referenceID, spverrors.Wrapf(err, "failed to generate reference id") } -func getPublicKeys(receiverPubKeyHex, senderPubKeyHex string) (*ec.PublicKey, *bec.PublicKey, error) { +func getPublicKeys(receiverPubKeyHex, senderPubKeyHex string) (*ec.PublicKey, *ec.PublicKey, error) { receiverPubKey, err := getPublicKey(receiverPubKeyHex) if err != nil { return nil, nil, err diff --git a/engine/record_tx_strategy_external_incoming_tx.go b/engine/record_tx_strategy_external_incoming_tx.go index 7694db97..3777ccbe 100644 --- a/engine/record_tx_strategy_external_incoming_tx.go +++ b/engine/record_tx_strategy_external_incoming_tx.go @@ -54,7 +54,7 @@ func (strategy *externalIncomingTx) LockKey() string { func _createExternalTxToRecord(ctx context.Context, eTx *externalIncomingTx, c ClientInterface, opts []ModelOps) (*Transaction, error) { // Create NEW tx model - tx := txFromBtTx(eTx.SDKTx, c.DefaultModelOptions(append(opts, New())...)...) + tx := txFromSDKTx(eTx.SDKTx, c.DefaultModelOptions(append(opts, New())...)...) if !tx.TransactionBase.hasOneKnownDestination(ctx, c) { return nil, ErrNoMatchingOutputs diff --git a/engine/record_tx_strategy_outgoing_tx.go b/engine/record_tx_strategy_outgoing_tx.go index 1625ce8c..2fa5bc13 100644 --- a/engine/record_tx_strategy_outgoing_tx.go +++ b/engine/record_tx_strategy_outgoing_tx.go @@ -100,7 +100,7 @@ func (strategy *outgoingTx) LockKey() string { func (strategy *outgoingTx) createOutgoingTxToRecord(ctx context.Context, c ClientInterface, opts []ModelOps) (*Transaction, error) { // Create NEW transaction model newOpts := c.DefaultModelOptions(append(opts, WithXPub(strategy.XPubKey), New())...) - tx := txFromBtTx(strategy.SDKTx, newOpts...) + tx := txFromSDKTx(strategy.SDKTx, newOpts...) tx.DraftID = strategy.RelatedDraftID if err := _hydrateOutgoingWithDraft(ctx, tx); err != nil { diff --git a/engine/spv_wallet_engine_test.go b/engine/spv_wallet_engine_test.go index 759380b0..d755a322 100644 --- a/engine/spv_wallet_engine_test.go +++ b/engine/spv_wallet_engine_test.go @@ -7,16 +7,16 @@ import ( "github.com/DATA-DOG/go-sqlmock" broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" + compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" + "github.com/bitcoin-sv/go-sdk/script" + trx "github.com/bitcoin-sv/go-sdk/transaction" + sighash "github.com/bitcoin-sv/go-sdk/transaction/sighash" + "github.com/bitcoin-sv/go-sdk/transaction/template/p2pkh" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" "github.com/bitcoin-sv/spv-wallet/engine/tester" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bk/bec" - "github.com/libsv/go-bk/bip32" - "github.com/libsv/go-bt/v2" - "github.com/libsv/go-bt/v2/bscript" - "github.com/libsv/go-bt/v2/sighash" - "github.com/libsv/go-bt/v2/unlocker" "github.com/mrz1836/go-cache" "github.com/rafaeljusto/redigomock" "github.com/rs/zerolog" @@ -141,45 +141,46 @@ func CloseClient(ctx context.Context, t *testing.T, client ClientInterface) { // we need to create an interface for the unlocker type account struct { - PrivateKey *bec.PrivateKey + PrivateKey *ec.PrivateKey } // Unlocker get the correct un-locker for a given locking script. -func (a *account) Unlocker(context.Context, *bscript.Script) (bt.Unlocker, error) { - return &unlocker.Simple{ - PrivateKey: a.PrivateKey, - }, nil +func (a *account) Unlocker(context.Context, *script.Script) (*p2pkh.P2PKH, error) { + unlocker, err := p2pkh.Unlock(a.PrivateKey, nil) + if err != nil { + return nil, err + } + + return unlocker, nil } // CreateFakeFundingTransaction will create a valid (fake) transaction for funding -func CreateFakeFundingTransaction(t *testing.T, masterKey *bip32.ExtendedKey, +func CreateFakeFundingTransaction(t *testing.T, masterKey *compat.ExtendedKey, destinations []*Destination, satoshis uint64, ) string { // Create new tx - rawTx := bt.NewTx() - txErr := rawTx.From(testTxScriptSigID, 0, testTxScriptSigOut, satoshis+354) + rawTx := trx.NewTransaction() + txErr := rawTx.AddInputFrom(testTxScriptSigID, 0, testTxScriptSigOut, satoshis+354, nil) require.NoError(t, txErr) // Loop all destinations for _, destination := range destinations { - s, err := bscript.NewFromHexString(destination.LockingScript) + s, err := script.NewFromHex(destination.LockingScript) require.NoError(t, err) require.NotNil(t, s) - rawTx.AddOutput(&bt.Output{ + rawTx.AddOutput(&trx.TransactionOutput{ Satoshis: satoshis, LockingScript: s, }) } // Get private key - privateKey, err := bitcoin.GetPrivateKeyFromHDKey(masterKey) + privateKey, err := compat.GetPrivateKeyFromHDKey(masterKey) require.NoError(t, err) require.NotNil(t, privateKey) - // Sign the tx - myAccount := &account{PrivateKey: privateKey} - err = rawTx.FillAllInputs(context.Background(), myAccount) + err = rawTx.Sign() require.NoError(t, err) // Return the tx hex @@ -189,15 +190,15 @@ func CreateFakeFundingTransaction(t *testing.T, masterKey *bip32.ExtendedKey, // CreateNewXPub will create a new xPub and return all the information to use the xPub func CreateNewXPub(ctx context.Context, t *testing.T, engineClient ClientInterface, opts ...ModelOps, -) (*bip32.ExtendedKey, *Xpub, string) { +) (*compat.ExtendedKey, *Xpub, string) { // Generate a key pair - masterKey, err := bitcoin.GenerateHDKey(bitcoin.SecureSeedLength) + masterKey, err := compat.GenerateHDKey(compat.SecureSeedLength) require.NoError(t, err) require.NotNil(t, masterKey) // Get the raw string of the xPub var rawXPub string - rawXPub, err = bitcoin.GetExtendedPublicKey(masterKey) + rawXPub, err = compat.GetExtendedPublicKey(masterKey) require.NoError(t, err) require.NotNil(t, masterKey) @@ -211,21 +212,13 @@ func CreateNewXPub(ctx context.Context, t *testing.T, engineClient ClientInterfa } // GetUnlockingScript will get a locking script for valid fake transactions -func GetUnlockingScript(t *testing.T, tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey) *bscript.Script { - sh, err := tx.CalcInputSignatureHash(inputIndex, sighash.AllForkID) - require.NoError(t, err) - - var sig *bec.Signature - sig, err = privateKey.Sign(bt.ReverseBytes(sh)) - require.NoError(t, err) - require.NotNil(t, sig) +func GetUnlockingScript(tx *trx.Transaction, inputIndex uint32, privateKey *ec.PrivateKey) (*p2pkh.P2PKH, error) { + sigHashFlags := sighash.AllForkID - var s *bscript.Script - s, err = bscript.NewP2PKHUnlockingScript( - privateKey.PubKey().SerialiseCompressed(), sig.Serialise(), sighash.AllForkID, - ) - require.NoError(t, err) - require.NotNil(t, s) + sc, err := p2pkh.Unlock(privateKey, &sigHashFlags) + if err != nil { + return nil, spverrors.Wrapf(err, "failed to create unlocking script") + } - return s + return sc, nil } diff --git a/engine/tx_service.go b/engine/tx_service.go index 14b61146..eab228f3 100644 --- a/engine/tx_service.go +++ b/engine/tx_service.go @@ -2,7 +2,6 @@ package engine import ( "context" - "encoding/hex" "fmt" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" @@ -74,8 +73,8 @@ func (m *Transaction) _processInputs(ctx context.Context) (err error) { for index := range m.TransactionBase.parsedTx.Inputs { // todo: optimize this SQL SELECT to get all utxos in one query? if utxo, err = m.transactionService.getUtxo(ctx, - hex.EncodeToString(m.TransactionBase.parsedTx.Inputs[index].PreviousTxID()), - m.TransactionBase.parsedTx.Inputs[index].PreviousTxOutIndex, + m.TransactionBase.parsedTx.Inputs[index].SourceTXID.String(), + m.TransactionBase.parsedTx.Inputs[index].SourceTxOutIndex, opts..., ); err != nil { return diff --git a/engine/types/type42/linking_key_test.go b/engine/types/type42/linking_key_test.go index f9851ca1..49a25055 100644 --- a/engine/types/type42/linking_key_test.go +++ b/engine/types/type42/linking_key_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/libsv/go-bk/bec" + ec "github.com/bitcoin-sv/go-sdk/primitives/ec" assert "github.com/stretchr/testify/require" ) @@ -13,26 +13,26 @@ func TestDeriveLinkedKey(t *testing.T) { sourcePubKeyHex := "027c1404c3ecb034053e6dd90bc68f7933284559c7d0763367584195a8796d9b0e" sourcePubKeyBytes, err := hex.DecodeString(sourcePubKeyHex) assert.NoError(t, err) - sourcePubKey, err := bec.ParsePubKey(sourcePubKeyBytes, bec.S256()) + sourcePubKey, err := ec.ParsePubKey(sourcePubKeyBytes) assert.NoError(t, err) linkPubKeyHex := "03a34e456deecb6e6e9237e63e5b7d045d1d2a456eb6be43de1ec4e9ac9a07b50d" linkPubKeyBytes, err := hex.DecodeString(linkPubKeyHex) assert.NoError(t, err) - linkPubKey, err := bec.ParsePubKey(linkPubKeyBytes, bec.S256()) + linkPubKey, err := ec.ParsePubKey(linkPubKeyBytes) assert.NoError(t, err) - validHMAC, err := calculateHMAC(sourcePubKey.SerialiseCompressed(), "valid-invoice") + validHMAC, err := calculateHMAC(sourcePubKey.SerializeCompressed(), "valid-invoice") assert.NoError(t, err) validDerivedKey, err := calculateLinkedPublicKey(validHMAC, linkPubKey) assert.NoError(t, err) validTests := []struct { name string - source bec.PublicKey - linkPubKey bec.PublicKey + source ec.PublicKey + linkPubKey ec.PublicKey invoiceNumber string - expectedResult *bec.PublicKey + expectedResult *ec.PublicKey }{ { name: "valid case", @@ -56,8 +56,8 @@ func TestDeriveLinkedKey(t *testing.T) { errorTests := []struct { name string - source bec.PublicKey - linkPubKey bec.PublicKey + source ec.PublicKey + linkPubKey ec.PublicKey invoiceNumber string }{ { @@ -69,7 +69,7 @@ func TestDeriveLinkedKey(t *testing.T) { { name: "nil receiver public key", source: *sourcePubKey, - linkPubKey: bec.PublicKey{}, // Empty public key causing dedicated public key calculation to fail + linkPubKey: ec.PublicKey{}, // Empty public key causing dedicated public key calculation to fail invoiceNumber: "valid-invoice", }, } @@ -87,12 +87,12 @@ func TestDeriveLinkedKey(t *testing.T) { } // Helper function to parse a public key from a hex string -func mustParsePubKey(hexKey string) *bec.PublicKey { +func mustParsePubKey(hexKey string) *ec.PublicKey { keyBytes, err := hex.DecodeString(hexKey) if err != nil { panic(fmt.Sprintf("invalid hex key: %s", err)) } - key, err := bec.ParsePubKey(keyBytes, bec.S256()) + key, err := ec.ParsePubKey(keyBytes) if err != nil { panic(fmt.Sprintf("invalid public key: %s", err)) } @@ -103,10 +103,10 @@ func mustParsePubKey(hexKey string) *bec.PublicKey { func TestDeriveLinkedKeyCases(t *testing.T) { validTests := []struct { name string - source *bec.PublicKey - linkPubKey *bec.PublicKey + source *ec.PublicKey + linkPubKey *ec.PublicKey invoiceNumber string - expectedResult *bec.PublicKey + expectedResult *ec.PublicKey }{ { name: "case 1", diff --git a/go.mod b/go.mod index 88e88ecc..25a702c1 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/bitcoin-sv/go-paymail v0.21.0 github.com/bitcoin-sv/go-sdk v1.1.9 github.com/bitcoin-sv/spv-wallet/models v0.28.0 - github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 github.com/bitcoinschema/go-map v0.2.0 github.com/coocood/freecache v1.2.4 github.com/fergusstrange/embedded-postgres v1.29.0 @@ -67,8 +66,6 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bitcoinschema/go-bpu v0.2.0 // indirect - github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 // indirect - github.com/bitcoinsv/bsvutil v0.0.0-20181216182056-1d77cf353ea9 // indirect github.com/bsm/redislock v0.9.4 // indirect github.com/bytedance/sonic v1.12.0 // indirect github.com/capnm/sysinfo v0.0.0-20130621111458-5909a53897f3 // indirect @@ -102,7 +99,6 @@ require ( github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/libsv/go-p2p v0.3.2 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -135,7 +131,7 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.27.0 // indirect + golang.org/x/crypto v0.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.29.0 // indirect diff --git a/go.sum b/go.sum index 7cd51bdf..7e9cde83 100644 --- a/go.sum +++ b/go.sum @@ -22,18 +22,12 @@ github.com/bitcoin-sv/go-paymail v0.21.0 h1:xcyCWBpXG79Bek+uEC9HknPC9McFzafDKNRi github.com/bitcoin-sv/go-paymail v0.21.0/go.mod h1:CzDCfKjxMI0Ve5Z4V7IuCUP+BXS4PuJ4A7TAQVbESmw= github.com/bitcoin-sv/go-sdk v1.1.9 h1:N/LlZUMHNYKjEBuY72c3XSlzUI/q7IN34R0p6J0Qtjc= github.com/bitcoin-sv/go-sdk v1.1.9/go.mod h1:NOAkJLbjqKOLuxJmb9ABG86ExTZp4HS8+iygiDIUps4= -github.com/bitcoinschema/go-bitcoin/v2 v2.0.5 h1:Sgh5Eb746Zck/46rFDrZZEXZWyO53fMuWYhNoZa1tck= -github.com/bitcoinschema/go-bitcoin/v2 v2.0.5/go.mod h1:JjO1ivfZv6vhK0uAXzyH08AAHlzNMAfnyK1Fiv9r4ZA= github.com/bitcoinschema/go-bob v0.5.0 h1:Fjl60RuiQiUuZWLfXE8ETdcgmJBHYu9MYfbrbZU7yHs= github.com/bitcoinschema/go-bob v0.5.0/go.mod h1:bREtBvZuMKe3DX2oCUSE+LOuy9HDJnOC3qcWr0oLoy4= github.com/bitcoinschema/go-bpu v0.2.0 h1:/WI/F+ShIj50HUD1EBPPHTCBzSeiPDohy77W3Y8xx+s= github.com/bitcoinschema/go-bpu v0.2.0/go.mod h1:58mg4m1wlQY3TFfvlba7zRs5dTb4O2cqlzRWjcJPLuo= github.com/bitcoinschema/go-map v0.2.0 h1:piuYjUycLZWOFPG4Kj5dUGqLBTlrYbq7VNWQCuMhUTE= github.com/bitcoinschema/go-map v0.2.0/go.mod h1:fXRo0x/h3liZR+xdRc+dm5oa2SA+tTB2Q/CmPUVUkVY= -github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173 h1:2yTIV9u7H0BhRDGXH5xrAwAz7XibWJtX2dNezMeNsUo= -github.com/bitcoinsv/bsvd v0.0.0-20190609155523-4c29707f7173/go.mod h1:BZ1UcC9+tmcDEcdVXgpt13hMczwJxWzpAn68wNs7zRA= -github.com/bitcoinsv/bsvutil v0.0.0-20181216182056-1d77cf353ea9 h1:hFI8rT84FCA0FFy3cFrkW5Nz4FyNKlIdCvEvvTNySKg= -github.com/bitcoinsv/bsvutil v0.0.0-20181216182056-1d77cf353ea9/go.mod h1:p44KuNKUH5BC8uX4ONEODaHUR4+ibC8todEAOGQEJAM= github.com/bsm/redislock v0.7.2 h1:jggqOio8JyX9FJBKIfjF3fTxAu/v7zC5mAID9LveqG4= github.com/bsm/redislock v0.7.2/go.mod h1:kS2g0Yvlymc9Dz8V3iVYAtLAaSVruYbAFdYBDrmC5WU= github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls= @@ -181,8 +175,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libsv/go-p2p v0.3.2 h1:O32CzkqM+jhSuleRHJln6JjL2pKH8aaRTx8lAfhIiic= -github.com/libsv/go-p2p v0.3.2/go.mod h1:TENFxbTT/bfSfuiirjU6l+PfAWxwZgF8GYUxs5tzc/M= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -329,7 +321,6 @@ golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= @@ -379,7 +370,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -401,7 +391,6 @@ golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 5a7d45a9305c0df59936b595dd3040e47a6fd83a Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:16:02 +0200 Subject: [PATCH 04/24] fix(SPV-898): move fee unit to a bsv package --- engine/action_transaction_test.go | 3 +- engine/chainstate/broadcast_client_init.go | 5 +-- engine/chainstate/client.go | 6 ++-- engine/chainstate/client_options.go | 4 +-- engine/chainstate/interface.go | 4 +-- engine/chainstate/mock_const.go | 6 ++-- engine/utils/fees.go | 38 +++------------------- models/bsv/fee_unit.go | 32 ++++++++++++++++++ 8 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 models/bsv/fee_unit.go diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index 4ce1a8c2..d41d53f5 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -8,6 +8,7 @@ import ( broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -409,7 +410,7 @@ func initBenchmarkData(b *testing.B) (context.Context, ClientInterface, *Xpub, * } config := &TransactionConfig{ - FeeUnit: &utils.FeeUnit{ + FeeUnit: &bsv.FeeUnit{ Satoshis: 5, Bytes: 100, }, diff --git a/engine/chainstate/broadcast_client_init.go b/engine/chainstate/broadcast_client_init.go index 40704e15..9a1770fd 100644 --- a/engine/chainstate/broadcast_client_init.go +++ b/engine/chainstate/broadcast_client_init.go @@ -6,6 +6,7 @@ import ( "github.com/bitcoin-sv/go-broadcast-client/broadcast" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" ) func (c *Client) broadcastClientInit(ctx context.Context) error { @@ -27,9 +28,9 @@ func (c *Client) broadcastClientInit(ctx context.Context) error { return spverrors.Newf("no fee quotes returned from broadcast client") } c.options.logger.Info().Msgf("got %d fee quote(s) from broadcast client", len(feeQuotes)) - fees := make([]utils.FeeUnit, len(feeQuotes)) + fees := make([]bsv.FeeUnit, len(feeQuotes)) for index, fee := range feeQuotes { - fees[index] = utils.FeeUnit{ + fees[index] = bsv.FeeUnit{ Satoshis: int(fee.MiningFee.Satoshis), Bytes: int(fee.MiningFee.Bytes), } diff --git a/engine/chainstate/client.go b/engine/chainstate/client.go index cdad0fc7..8b5357d5 100644 --- a/engine/chainstate/client.go +++ b/engine/chainstate/client.go @@ -8,7 +8,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/logging" "github.com/bitcoin-sv/spv-wallet/engine/metrics" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/rs/zerolog" ) @@ -38,7 +38,7 @@ type ( network Network // Current network (mainnet, testnet, stn) queryTimeout time.Duration // Timeout for transaction query broadcastClient broadcast.Client // Broadcast client - feeUnit *utils.FeeUnit // The lowest fees among all miners + feeUnit *bsv.FeeUnit // The lowest fees among all miners feeQuotes bool // If set, feeUnit will be updated with fee quotes from miner's } @@ -112,7 +112,7 @@ func (c *Client) QueryTimeout() time.Duration { } // FeeUnit will return feeUnit -func (c *Client) FeeUnit() *utils.FeeUnit { +func (c *Client) FeeUnit() *bsv.FeeUnit { return c.options.config.feeUnit } diff --git a/engine/chainstate/client_options.go b/engine/chainstate/client_options.go index e5ec064b..52ecb5ea 100644 --- a/engine/chainstate/client_options.go +++ b/engine/chainstate/client_options.go @@ -5,7 +5,7 @@ import ( "github.com/bitcoin-sv/go-broadcast-client/broadcast" "github.com/bitcoin-sv/spv-wallet/engine/metrics" - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/rs/zerolog" ) @@ -102,7 +102,7 @@ func WithFeeQuotes(enabled bool) ClientOps { } // WithFeeUnit will set the fee unit -func WithFeeUnit(feeUnit *utils.FeeUnit) ClientOps { +func WithFeeUnit(feeUnit *bsv.FeeUnit) ClientOps { return func(c *clientOptions) { c.config.feeUnit = feeUnit } diff --git a/engine/chainstate/interface.go b/engine/chainstate/interface.go index 705d0b9d..42f7608e 100644 --- a/engine/chainstate/interface.go +++ b/engine/chainstate/interface.go @@ -6,7 +6,7 @@ import ( "time" "github.com/bitcoin-sv/go-broadcast-client/broadcast" - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" ) // HTTPInterface is the HTTP client interface @@ -38,5 +38,5 @@ type ClientInterface interface { IsDebug() bool Network() Network QueryTimeout() time.Duration - FeeUnit() *utils.FeeUnit + FeeUnit() *bsv.FeeUnit } diff --git a/engine/chainstate/mock_const.go b/engine/chainstate/mock_const.go index f7256aa0..ef7e66e6 100644 --- a/engine/chainstate/mock_const.go +++ b/engine/chainstate/mock_const.go @@ -1,6 +1,8 @@ package chainstate -import "github.com/bitcoin-sv/spv-wallet/engine/utils" +import ( + "github.com/bitcoin-sv/spv-wallet/models/bsv" +) const ( // Dummy transaction data @@ -11,7 +13,7 @@ const ( ) // MockDefaultFee is a mock default fee used for assertions -var MockDefaultFee = &utils.FeeUnit{ +var MockDefaultFee = &bsv.FeeUnit{ Satoshis: 1, Bytes: 1000, } diff --git a/engine/utils/fees.go b/engine/utils/fees.go index 013ed7a1..8d179dc7 100644 --- a/engine/utils/fees.go +++ b/engine/utils/fees.go @@ -2,41 +2,13 @@ package utils import ( "encoding/hex" - "fmt" -) - -// Imported from deprecated go-bt library -// FeeUnit displays the amount of Satoshis needed -// for a specific amount of Bytes in a transaction -// see https://github.com/bitcoin-sv-specs/brfc-misc/tree/master/feespec -type FeeUnit struct { - Satoshis int `json:"satoshis"` // Fee in satoshis of the amount of Bytes - Bytes int `json:"bytes"` // Number of bytes that the Fee covers -} -// IsLowerThan compare two fee units -func (f *FeeUnit) IsLowerThan(other *FeeUnit) bool { - return float64(f.Satoshis)/float64(f.Bytes) < float64(other.Satoshis)/float64(other.Bytes) -} - -// String returns the fee unit as a string -func (f *FeeUnit) String() string { - return fmt.Sprintf("FeeUnit(%d satoshis / %d bytes)", f.Satoshis, f.Bytes) -} - -// IsZero returns true if the fee unit suggest no fees (free) -func (f *FeeUnit) IsZero() bool { - return f.Satoshis == 0 -} - -// IsValid returns true if the Bytes in fee are greater than 0 -func (f *FeeUnit) IsValid() bool { - return f.Bytes > 0 -} + "github.com/bitcoin-sv/spv-wallet/models/bsv" +) // ValidFees filters out invalid fees from a list of fee units -func ValidFees(feeUnits []FeeUnit) []FeeUnit { - validFees := []FeeUnit{} +func ValidFees(feeUnits []bsv.FeeUnit) []bsv.FeeUnit { + validFees := []bsv.FeeUnit{} for _, fee := range feeUnits { if fee.IsValid() { validFees = append(validFees, fee) @@ -46,7 +18,7 @@ func ValidFees(feeUnits []FeeUnit) []FeeUnit { } // LowestFee get the lowest fee from a list of fee units, if defaultValue exists and none is found, return defaultValue -func LowestFee(feeUnits []FeeUnit, defaultValue *FeeUnit) *FeeUnit { +func LowestFee(feeUnits []bsv.FeeUnit, defaultValue *bsv.FeeUnit) *bsv.FeeUnit { validFees := ValidFees(feeUnits) if len(validFees) == 0 { return defaultValue diff --git a/models/bsv/fee_unit.go b/models/bsv/fee_unit.go new file mode 100644 index 00000000..5bd35079 --- /dev/null +++ b/models/bsv/fee_unit.go @@ -0,0 +1,32 @@ +package bsv + +import "fmt" + +// FeeUnit displays the amount of Satoshis neededz +// for a specific amount of Bytes in a transaction +// see https://github.com/bitcoin-sv-specs/brfc-misc/tree/master/feespec +// Imported from deprecated go-bt library +type FeeUnit struct { + Satoshis int `json:"satoshis"` // Fee in satoshis of the amount of Bytes + Bytes int `json:"bytes"` // Number of bytes that the Fee covers +} + +// IsLowerThan compare two fee units +func (f *FeeUnit) IsLowerThan(other *FeeUnit) bool { + return float64(f.Satoshis)/float64(f.Bytes) < float64(other.Satoshis)/float64(other.Bytes) +} + +// String returns the fee unit as a string +func (f *FeeUnit) String() string { + return fmt.Sprintf("FeeUnit(%d satoshis / %d bytes)", f.Satoshis, f.Bytes) +} + +// IsZero returns true if the fee unit suggest no fees (free) +func (f *FeeUnit) IsZero() bool { + return f.Satoshis == 0 +} + +// IsValid returns true if the Bytes in fee are greater than 0 +func (f *FeeUnit) IsValid() bool { + return f.Bytes > 0 +} From 18ef98dc6b58f4d9a7a31630b2eea89d5be48726 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:16:57 +0200 Subject: [PATCH 05/24] fix(SPV-898): replace utils package with bsv one on fee unit model --- engine/client_options.go | 6 +++--- engine/model_draft_transactions.go | 3 ++- engine/model_draft_transactions_test.go | 7 ++++--- engine/model_transaction_config.go | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/engine/client_options.go b/engine/client_options.go index 884301fe..621e9e8f 100644 --- a/engine/client_options.go +++ b/engine/client_options.go @@ -9,14 +9,14 @@ import ( "github.com/bitcoin-sv/go-broadcast-client/broadcast" "github.com/bitcoin-sv/go-paymail" "github.com/bitcoin-sv/go-paymail/server" - "github.com/bitcoin-sv/spv-wallet/engine/chain/models" + chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models" "github.com/bitcoin-sv/spv-wallet/engine/chainstate" "github.com/bitcoin-sv/spv-wallet/engine/cluster" "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/logging" "github.com/bitcoin-sv/spv-wallet/engine/metrics" "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/coocood/freecache" "github.com/go-redis/redis/v8" "github.com/go-resty/resty/v2" @@ -587,7 +587,7 @@ func WithFeeQuotes(enabled bool) ClientOps { } // WithFeeUnit will set the fee unit to use for broadcasting -func WithFeeUnit(feeUnit *utils.FeeUnit) ClientOps { +func WithFeeUnit(feeUnit *bsv.FeeUnit) ClientOps { return func(c *clientOptions) { c.chainstate.options = append(c.chainstate.options, chainstate.WithFeeUnit(feeUnit)) } diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index 2ed1a0a0..a772f947 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -17,6 +17,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/datastore" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/pkg/errors" ) @@ -515,7 +516,7 @@ func (m *DraftTransaction) estimateSize() uint64 { } // estimateFee will loop the inputs and outputs and estimate the required fee -func (m *DraftTransaction) estimateFee(unit *utils.FeeUnit, addToSize uint64) uint64 { +func (m *DraftTransaction) estimateFee(unit *bsv.FeeUnit, addToSize uint64) uint64 { size := m.estimateSize() + addToSize feeEstimate := float64(size) * (float64(unit.Satoshis) / float64(unit.Bytes)) return uint64(math.Ceil(feeEstimate)) diff --git a/engine/model_draft_transactions_test.go b/engine/model_draft_transactions_test.go index 5d04e186..ec8125e5 100644 --- a/engine/model_draft_transactions_test.go +++ b/engine/model_draft_transactions_test.go @@ -18,6 +18,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/spverrors" xtester "github.com/bitcoin-sv/spv-wallet/engine/tester/paymailmock" "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -435,7 +436,7 @@ func TestDraftTransaction_createTransaction(t *testing.T) { prepareAdditionalModels(ctx, t, client, false) draftTransaction, err := newDraftTransaction(testXPub, &TransactionConfig{ - FeeUnit: &utils.FeeUnit{ + FeeUnit: &bsv.FeeUnit{ Satoshis: 5, Bytes: 100, }, @@ -754,7 +755,7 @@ func TestDraftTransaction_setChangeDestination(t *testing.T) { ), Configuration: TransactionConfig{ ChangeDestinations: nil, - FeeUnit: &utils.FeeUnit{ + FeeUnit: &bsv.FeeUnit{ Satoshis: 5, Bytes: 10, }, @@ -781,7 +782,7 @@ func TestDraftTransaction_setChangeDestination(t *testing.T) { ), Configuration: TransactionConfig{ ChangeDestinations: nil, - FeeUnit: &utils.FeeUnit{ + FeeUnit: &bsv.FeeUnit{ Satoshis: 5, Bytes: 10, }, diff --git a/engine/model_transaction_config.go b/engine/model_transaction_config.go index d3c95e19..7d7831d2 100644 --- a/engine/model_transaction_config.go +++ b/engine/model_transaction_config.go @@ -16,6 +16,7 @@ import ( paymailclient "github.com/bitcoin-sv/spv-wallet/engine/paymail" "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" magic "github.com/bitcoinschema/go-map" ) @@ -28,7 +29,7 @@ type TransactionConfig struct { ChangeSatoshis uint64 `json:"change_satoshis" toml:"change_satoshis" yaml:"change_satoshis"` // The satoshis used for change ExpiresIn time.Duration `json:"expires_in" toml:"expires_in" yaml:"expires_in"` // The expiration time for the draft and utxos Fee uint64 `json:"fee" toml:"fee" yaml:"fee"` // The fee used for the transaction (auto generated) - FeeUnit *utils.FeeUnit `json:"fee_unit" toml:"fee_unit" yaml:"fee_unit"` // Fee unit to use (overrides chainstate if set) + FeeUnit *bsv.FeeUnit `json:"fee_unit" toml:"fee_unit" yaml:"fee_unit"` // Fee unit to use (overrides chainstate if set) FromUtxos []*UtxoPointer `json:"from_utxos" toml:"from_utxos" yaml:"from_utxos"` // Use these specific utxos for the transaction IncludeUtxos []*UtxoPointer `json:"include_utxos" toml:"include_utxos" yaml:"include_utxos"` // Include these utxos for the transaction, among others necessary if more is needed for fees Inputs []*TransactionInput `json:"inputs" toml:"inputs" yaml:"inputs"` // All transaction inputs From 2c9cf70ad9bb95034e3f7db5a2443f746a444694 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:27:03 +0200 Subject: [PATCH 06/24] fix(SPV-898): resolve linter issues --- engine/spv_wallet_engine_test.go | 15 --------------- engine/utils/encrypt.go | 7 ++----- engine/utils/fees_test.go | 21 +++++++++++---------- engine/utils/merkletree.go | 5 +++-- 4 files changed, 16 insertions(+), 32 deletions(-) diff --git a/engine/spv_wallet_engine_test.go b/engine/spv_wallet_engine_test.go index d755a322..57b19f5f 100644 --- a/engine/spv_wallet_engine_test.go +++ b/engine/spv_wallet_engine_test.go @@ -139,21 +139,6 @@ func CloseClient(ctx context.Context, t *testing.T, client ClientInterface) { require.NoError(t, client.Close(ctx)) } -// we need to create an interface for the unlocker -type account struct { - PrivateKey *ec.PrivateKey -} - -// Unlocker get the correct un-locker for a given locking script. -func (a *account) Unlocker(context.Context, *script.Script) (*p2pkh.P2PKH, error) { - unlocker, err := p2pkh.Unlock(a.PrivateKey, nil) - if err != nil { - return nil, err - } - - return unlocker, nil -} - // CreateFakeFundingTransaction will create a valid (fake) transaction for funding func CreateFakeFundingTransaction(t *testing.T, masterKey *compat.ExtendedKey, destinations []*Destination, satoshis uint64, diff --git a/engine/utils/encrypt.go b/engine/utils/encrypt.go index ce732014..9712dce8 100644 --- a/engine/utils/encrypt.go +++ b/engine/utils/encrypt.go @@ -15,10 +15,8 @@ func Encrypt(encryptionKey, encryptValue string) (string, error) { } // Encrypt the private key - var encryptedValue string - if encryptedValue, err = ecies.EncryptSingle( - encryptValue, privateKey, - ); err != nil { + encryptedValue, err := ecies.EncryptSingle(encryptValue, privateKey) + if err != nil { return "", spverrors.Wrapf(err, "error encrypting data with private key") } @@ -27,7 +25,6 @@ func Encrypt(encryptionKey, encryptValue string) (string, error) { // Decrypt will take the data and decrypt using a char(64) key func Decrypt(encryptionKey, data string) (string, error) { - // keyString, err := bitcoin.DecryptWithPrivateKeyString(encryptionKey, data) privateKey, err := ec.PrivateKeyFromHex(encryptionKey) keyString, err := ecies.DecryptSingle(data, privateKey) return keyString, spverrors.Wrapf(err, "error decrypting data with private key") diff --git a/engine/utils/fees_test.go b/engine/utils/fees_test.go index 2f6bf8d7..49b085f4 100644 --- a/engine/utils/fees_test.go +++ b/engine/utils/fees_test.go @@ -3,6 +3,7 @@ package utils import ( "testing" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" ) @@ -34,11 +35,11 @@ func TestGetOutputSizeForType(t *testing.T) { func TestIsLowerThan(t *testing.T) { t.Run("same satoshis, different bytes", func(t *testing.T) { - one := FeeUnit{ + one := bsv.FeeUnit{ Satoshis: 1, Bytes: 1000, } - two := FeeUnit{ + two := bsv.FeeUnit{ Satoshis: 1, Bytes: 20, } @@ -46,11 +47,11 @@ func TestIsLowerThan(t *testing.T) { assert.False(t, two.IsLowerThan(&one)) }) t.Run("same bytes, different satoshis", func(t *testing.T) { - one := FeeUnit{ + one := bsv.FeeUnit{ Satoshis: 1, Bytes: 20, } - two := FeeUnit{ + two := bsv.FeeUnit{ Satoshis: 2, Bytes: 20, } @@ -59,11 +60,11 @@ func TestIsLowerThan(t *testing.T) { }) t.Run("zero as bytes in denominator", func(t *testing.T) { - one := FeeUnit{ + one := bsv.FeeUnit{ Satoshis: 1, Bytes: 0, } - two := FeeUnit{ + two := bsv.FeeUnit{ Satoshis: 2, Bytes: 0, } @@ -73,8 +74,8 @@ func TestIsLowerThan(t *testing.T) { } func TestLowestFee(t *testing.T) { - initTest := func() (feeList []FeeUnit, defaultFee FeeUnit) { - feeList = []FeeUnit{ + initTest := func() (feeList []bsv.FeeUnit, defaultFee bsv.FeeUnit) { + feeList = []bsv.FeeUnit{ { Satoshis: 1, Bytes: 20, @@ -88,7 +89,7 @@ func TestLowestFee(t *testing.T) { Bytes: 20, }, } - defaultFee = FeeUnit{ + defaultFee = bsv.FeeUnit{ Satoshis: 4, Bytes: 20, } @@ -115,7 +116,7 @@ func TestLowestFee(t *testing.T) { t.Run("lowest fee as defaultValue", func(t *testing.T) { _, defaultFee := initTest() - feeList := []FeeUnit{} + feeList := []bsv.FeeUnit{} assert.Equal(t, defaultFee, *LowestFee(feeList, &defaultFee)) }) } diff --git a/engine/utils/merkletree.go b/engine/utils/merkletree.go index 4bf9188f..febdfeee 100644 --- a/engine/utils/merkletree.go +++ b/engine/utils/merkletree.go @@ -5,6 +5,7 @@ import ( crypto "github.com/bitcoin-sv/go-sdk/primitives/hash" "github.com/bitcoin-sv/go-sdk/util" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // INFO: This function is moved to go-paymail from go-bc @@ -16,11 +17,11 @@ import ( func MerkleTreeParentStr(leftNode, rightNode string) (string, error) { l, err := hex.DecodeString(leftNode) if err != nil { - return "", err + return "", spverrors.Wrapf(err, "error decoding left node of merkle tree parent") } r, err := hex.DecodeString(rightNode) if err != nil { - return "", err + return "", spverrors.Wrapf(err, "error decoding right node of merkle tree parent") } return hex.EncodeToString(merkleTreeParent(l, r)), nil From 299e5328430658eade1156d005f8f8947b4b5023 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:34:39 +0200 Subject: [PATCH 07/24] fix(SPV-898): resolve pending errors after feeunit move --- config/config_to_options.go | 3 ++- engine/model_draft_transactions.go | 2 +- mappings/fee_unit.go | 8 ++++---- mappings/fee_unit_old.go | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/config/config_to_options.go b/config/config_to_options.go index 601e2dea..fd5157f7 100644 --- a/config/config_to_options.go +++ b/config/config_to_options.go @@ -14,6 +14,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/metrics" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/go-redis/redis/v8" "github.com/go-resty/resty/v2" "github.com/mrz1836/go-cachestore" @@ -75,7 +76,7 @@ func (c *AppConfig) addFeeQuotes(options []engine.ClientOps) []engine.ClientOps options = append(options, engine.WithFeeQuotes(c.ARC.UseFeeQuotes)) if c.ARC.FeeUnit != nil { - options = append(options, engine.WithFeeUnit(&utils.FeeUnit{ + options = append(options, engine.WithFeeUnit(&bsv.FeeUnit{ Satoshis: c.ARC.FeeUnit.Satoshis, Bytes: c.ARC.FeeUnit.Bytes, })) diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index a772f947..90200e02 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -751,7 +751,7 @@ func (m *DraftTransaction) getInputsFromUtxos(reservedUtxos []*Utxo) ([]*trx.UTX utxo.Satoshis, ) if err != nil { - return nil, 0, err + return nil, 0, spverrors.Wrapf(err, "failed to create UTXO for transaction %s", m.GetID()) } inputUtxos = append(inputUtxos, utxo) diff --git a/mappings/fee_unit.go b/mappings/fee_unit.go index 577ad554..d02401ea 100644 --- a/mappings/fee_unit.go +++ b/mappings/fee_unit.go @@ -1,12 +1,12 @@ package mappings import ( - "github.com/bitcoin-sv/spv-wallet/engine/utils" + "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/bitcoin-sv/spv-wallet/models/response" ) // MapToFeeUnitContract will map the fee-unit model from spv-wallet to the spv-wallet-models contract -func MapToFeeUnitContract(fu *utils.FeeUnit) (fc *response.FeeUnit) { +func MapToFeeUnitContract(fu *bsv.FeeUnit) (fc *response.FeeUnit) { if fu == nil { return nil } @@ -18,12 +18,12 @@ func MapToFeeUnitContract(fu *utils.FeeUnit) (fc *response.FeeUnit) { } // MapFeeUnitModelToEngine will map the fee-unit model from spv-wallet-models to the spv-wallet contract -func MapFeeUnitModelToEngine(fu *response.FeeUnit) (fc *utils.FeeUnit) { +func MapFeeUnitModelToEngine(fu *response.FeeUnit) (fc *bsv.FeeUnit) { if fu == nil { return nil } - return &utils.FeeUnit{ + return &bsv.FeeUnit{ Satoshis: fu.Satoshis, Bytes: fu.Bytes, } diff --git a/mappings/fee_unit_old.go b/mappings/fee_unit_old.go index cc748690..d62aecd6 100644 --- a/mappings/fee_unit_old.go +++ b/mappings/fee_unit_old.go @@ -1,12 +1,12 @@ package mappings import ( - "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models" + "github.com/bitcoin-sv/spv-wallet/models/bsv" ) // MapToOldFeeUnitContract will map the fee-unit model from spv-wallet to the spv-wallet-models contract -func MapToOldFeeUnitContract(fu *utils.FeeUnit) (fc *models.FeeUnit) { +func MapToOldFeeUnitContract(fu *bsv.FeeUnit) (fc *models.FeeUnit) { if fu == nil { return nil } @@ -18,12 +18,12 @@ func MapToOldFeeUnitContract(fu *utils.FeeUnit) (fc *models.FeeUnit) { } // MapOldFeeUnitModelToEngine will map the fee-unit model from spv-wallet-models to the spv-wallet contract -func MapOldFeeUnitModelToEngine(fu *models.FeeUnit) (fc *utils.FeeUnit) { +func MapOldFeeUnitModelToEngine(fu *models.FeeUnit) (fc *bsv.FeeUnit) { if fu == nil { return nil } - return &utils.FeeUnit{ + return &bsv.FeeUnit{ Satoshis: fu.Satoshis, Bytes: fu.Bytes, } From 4d84659ac0bb5ab74f492f069bd84ade4f24ff0b Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:43:26 +0200 Subject: [PATCH 08/24] fix(SPV-898): avoid ineffectual assignment to err (ineffassign) --- engine/utils/encrypt.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/utils/encrypt.go b/engine/utils/encrypt.go index 9712dce8..80cef785 100644 --- a/engine/utils/encrypt.go +++ b/engine/utils/encrypt.go @@ -8,14 +8,17 @@ import ( // Encrypt will encrypt the value using the encryption key func Encrypt(encryptionKey, encryptValue string) (string, error) { + var err error + var privateKey *ec.PrivateKey // Get the keys seeded with the encryption key - privateKey, err := ec.PrivateKeyFromHex(encryptionKey) + privateKey, err = ec.PrivateKeyFromHex(encryptionKey) if err != nil { return "", spverrors.Wrapf(err, "error getting private keys from encryption key") } // Encrypt the private key - encryptedValue, err := ecies.EncryptSingle(encryptValue, privateKey) + var encryptedValue string + encryptedValue, err = ecies.EncryptSingle(encryptValue, privateKey) if err != nil { return "", spverrors.Wrapf(err, "error encrypting data with private key") } From 9d6cd362b1ad2223968a8e64d2d2f6d68ee61bb9 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 14:46:48 +0200 Subject: [PATCH 09/24] fix(SPV-898): wrap errors in decrypt func in a correct way --- engine/utils/encrypt.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/engine/utils/encrypt.go b/engine/utils/encrypt.go index 80cef785..59e2b442 100644 --- a/engine/utils/encrypt.go +++ b/engine/utils/encrypt.go @@ -9,16 +9,14 @@ import ( // Encrypt will encrypt the value using the encryption key func Encrypt(encryptionKey, encryptValue string) (string, error) { var err error - var privateKey *ec.PrivateKey // Get the keys seeded with the encryption key - privateKey, err = ec.PrivateKeyFromHex(encryptionKey) + privateKey, err := ec.PrivateKeyFromHex(encryptionKey) if err != nil { return "", spverrors.Wrapf(err, "error getting private keys from encryption key") } // Encrypt the private key - var encryptedValue string - encryptedValue, err = ecies.EncryptSingle(encryptValue, privateKey) + encryptedValue, err := ecies.EncryptSingle(encryptValue, privateKey) if err != nil { return "", spverrors.Wrapf(err, "error encrypting data with private key") } @@ -29,6 +27,14 @@ func Encrypt(encryptionKey, encryptValue string) (string, error) { // Decrypt will take the data and decrypt using a char(64) key func Decrypt(encryptionKey, data string) (string, error) { privateKey, err := ec.PrivateKeyFromHex(encryptionKey) + if err != nil { + return "", spverrors.Wrapf(err, "error getting private keys from encryption key") + } + keyString, err := ecies.DecryptSingle(data, privateKey) - return keyString, spverrors.Wrapf(err, "error decrypting data with private key") + if err != nil { + return "", spverrors.Wrapf(err, "error decrypting data with private key") + } + + return keyString, nil } From f21a2aea5274a7ff5578160d4c82f05886b674a3 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 15:28:50 +0200 Subject: [PATCH 10/24] temp(SPV-XXX): temporarily comment out failing tests --- engine/utils/destination_types.go | 16 +++++- engine/utils/destination_types_test.go | 46 ++++++++-------- engine/utils/utils_test.go | 76 +++++++++++++------------- 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/engine/utils/destination_types.go b/engine/utils/destination_types.go index 0ef2e5c7..04f72a3e 100644 --- a/engine/utils/destination_types.go +++ b/engine/utils/destination_types.go @@ -189,7 +189,12 @@ func GetDestinationTypeRegex(destType string) *regexp.Regexp { func GetAddressFromScript(lockingScript string) (address string) { scriptType := GetDestinationType(lockingScript) if scriptType == ScriptTypePubKeyHash { - decodedAddr, _ := script.NewAddressFromPublicKeyHash([]byte(lockingScript), true) + lsBytes, err := hex.DecodeString(lockingScript) + if err != nil { + return "" + } + + decodedAddr, _ := script.NewAddressFromPublicKeyHash(lsBytes, true) address = decodedAddr.AddressString } else if scriptType == ScriptTypePubKey { s, err := script.NewFromHex(lockingScript) // no need for error check, if type is set, it should be valid @@ -206,13 +211,18 @@ func GetAddressFromScript(lockingScript string) (address string) { pubkeyHex := hex.EncodeToString(parts[0].Data) var addressScript *script.Address - addressScript, err = script.NewAddressFromPublicKeyString(pubkeyHex, len(parts[0].Data) <= 33) + addressScript, err = script.NewAddressFromPublicKeyString(pubkeyHex, true) if err == nil { address = addressScript.AddressString } } else if scriptType == ScriptTypeTokenStas { // stas is just a normal PubKeyHash with more data appended - decodedAddr, _ := script.NewAddressFromPublicKeyHash([]byte(lockingScript[:50]), true) + lsBytes, err := hex.DecodeString(lockingScript[:50]) + if err != nil { + return "" + } + + decodedAddr, _ := script.NewAddressFromPublicKeyHash(lsBytes, true) address = decodedAddr.AddressString // } else if scriptType == ScriptTypeTokenSensible { // sensible tokens do not have the receiving address in the token output, but in another output diff --git a/engine/utils/destination_types_test.go b/engine/utils/destination_types_test.go index 38a19711..aa83d4aa 100644 --- a/engine/utils/destination_types_test.go +++ b/engine/utils/destination_types_test.go @@ -205,29 +205,29 @@ func TestGetDestinationType(t *testing.T) { } // TestGetAddressFromScript will test the method GetAddressFromScript() -func TestGetAddressFromScript(t *testing.T) { - t.Parallel() - - t.Run("p2pk", func(t *testing.T) { - assert.Equal(t, "1BYpPJHowiz9Qr6zsTzRXKNeej2RV2Av6H", GetAddressFromScript(p2pkHex)) - }) - - t.Run("p2pkh", func(t *testing.T) { - assert.Equal(t, "12kwBQPUnAMouxBBWRa5wsA6vC29soEdXT", GetAddressFromScript(p2pkhHex)) - }) - - t.Run("stas 1", func(t *testing.T) { - assert.Equal(t, "1AxScC72W9tyk1Enej6dBsVZNkkgAonk4H", GetAddressFromScript(stasHex)) - }) - - t.Run("stas 2", func(t *testing.T) { - assert.Equal(t, "1MXhcVvUz1LGSkoUFGkANHXkGCtrzFKHpA", GetAddressFromScript(stas2Hex)) - }) - - t.Run("unknown", func(t *testing.T) { - assert.Equal(t, "", GetAddressFromScript("invalid-or-unknown-script")) - }) -} +// func TestGetAddressFromScript(t *testing.T) { +// t.Parallel() + +// //t.Run("p2pk", func(t *testing.T) { +// // assert.Equal(t, "1BYpPJHowiz9Qr6zsTzRXKNeej2RV2Av6H", GetAddressFromScript(p2pkHex)) +// //}) + +// // t.Run("p2pkh", func(t *testing.T) { +// // assert.Equal(t, "12kwBQPUnAMouxBBWRa5wsA6vC29soEdXT", GetAddressFromScript(p2pkhHex)) +// // }) + +// // t.Run("stas 1", func(t *testing.T) { +// // assert.Equal(t, "1AxScC72W9tyk1Enej6dBsVZNkkgAonk4H", GetAddressFromScript(stasHex)) +// // }) +// // +// // t.Run("stas 2", func(t *testing.T) { +// // assert.Equal(t, "1MXhcVvUz1LGSkoUFGkANHXkGCtrzFKHpA", GetAddressFromScript(stas2Hex)) +// // }) +// // +// // t.Run("unknown", func(t *testing.T) { +// // assert.Equal(t, "", GetAddressFromScript("invalid-or-unknown-script")) +// // }) +// } func BenchmarkIsP2PKH(b *testing.B) { for i := 0; i < b.N; i++ { diff --git a/engine/utils/utils_test.go b/engine/utils/utils_test.go index 06b994bf..de69ecc7 100644 --- a/engine/utils/utils_test.go +++ b/engine/utils/utils_test.go @@ -8,7 +8,6 @@ import ( bip32 "github.com/bitcoin-sv/go-sdk/compat/bip32" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" ec "github.com/bitcoin-sv/go-sdk/primitives/ec" - primitives "github.com/bitcoin-sv/go-sdk/primitives/ec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -136,44 +135,45 @@ func TestDerivePrivateKeyFromHex(t *testing.T) { }) } +// TODO: Fix // TestDerivePublicKeyFromHex test the method DerivePublicKeyFromHex -func TestDerivePublicKeyFromHex(t *testing.T) { - hdXpriv, _ := compat.GetHDKeyFromExtendedPublicKey(testXpriv) - hdXpub, _ := compat.GetHDKeyFromExtendedPublicKey(testXpub) - - t.Run("empty key", func(t *testing.T) { - _, err := DerivePublicKeyFromHex(nil, testHash) - require.ErrorIs(t, err, ErrHDKeyNil) - }) - - t.Run("priv empty hex", func(t *testing.T) { - key, err := DerivePublicKeyFromHex(hdXpriv, "") - require.NoError(t, err) - privKey, _ := primitives.PrivateKeyFromWif(privateKey0) - assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) - }) - - t.Run("pub empty hex", func(t *testing.T) { - key, err := DerivePublicKeyFromHex(hdXpub, "") - require.NoError(t, err) - privKey, _ := primitives.PrivateKeyFromWif(privateKey0) - assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) - }) - - t.Run("priv testHash hex", func(t *testing.T) { - key, err := DerivePublicKeyFromHex(hdXpriv, testHash) - require.NoError(t, err) - privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) - assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) - }) - - t.Run("pub testHash hex", func(t *testing.T) { - key, err := DerivePublicKeyFromHex(hdXpub, testHash) - require.NoError(t, err) - privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) - assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) - }) -} +//func TestDerivePublicKeyFromHex(t *testing.T) { +// hdXpriv, _ := compat.GetHDKeyFromExtendedPublicKey(testXpriv) +// hdXpub, _ := compat.GetHDKeyFromExtendedPublicKey(testXpub) +// +// t.Run("empty key", func(t *testing.T) { +// _, err := DerivePublicKeyFromHex(nil, testHash) +// require.ErrorIs(t, err, ErrHDKeyNil) +// }) +// +// //t.Run("priv empty hex", func(t *testing.T) { +// // key, err := DerivePublicKeyFromHex(hdXpriv, "") +// // require.NoError(t, err) +// // privKey, _ := primitives.PrivateKeyFromWif(privateKey0) +// // assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) +// //}) +// // +// //t.Run("pub empty hex", func(t *testing.T) { +// // key, err := DerivePublicKeyFromHex(hdXpub, "") +// // require.NoError(t, err) +// // privKey, _ := primitives.PrivateKeyFromWif(privateKey0) +// // assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) +// //}) +// +// t.Run("priv testHash hex", func(t *testing.T) { +// key, err := DerivePublicKeyFromHex(hdXpriv, testHash) +// require.NoError(t, err) +// privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) +// assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) +// }) +// +// t.Run("pub testHash hex", func(t *testing.T) { +// key, err := DerivePublicKeyFromHex(hdXpub, testHash) +// require.NoError(t, err) +// privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) +// assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) +// }) +//} // TestGetChildNumsFromHex test the method GetChildNumsFromHex func TestGetChildNumsFromHex(t *testing.T) { From 27c50270ea7c557343a792b9723451fb33f60ad6 Mon Sep 17 00:00:00 2001 From: wregulski Date: Sat, 12 Oct 2024 15:39:00 +0200 Subject: [PATCH 11/24] temp(SPV-898): temporarily comment out failing test --- engine/action_transaction_test.go | 354 +++++++++++++++--------------- 1 file changed, 176 insertions(+), 178 deletions(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index d41d53f5..ae8f8750 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -5,190 +5,188 @@ import ( "fmt" "testing" - broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" - "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func Test_RevertTransaction(t *testing.T) { - bc := broadcast_client_mock.Builder(). - WithMockArc(broadcast_client_mock.MockNilQueryTxResp). - Build() - t.Run("revert transaction", func(t *testing.T) { - ctx, client, transaction, _, deferMe := initRevertTransactionData(t, WithBroadcastClient(bc)) - defer deferMe() - - // - // Revert the transaction - // - err := client.RevertTransaction(ctx, transaction.ID) - require.NoError(t, err) - - // check transaction was reverted - var tx *Transaction - tx, err = client.GetTransaction(ctx, testXPubID, transaction.ID) - require.NoError(t, err) - assert.Equal(t, transaction.ID, tx.ID) - assert.Len(t, tx.XpubInIDs, 1) // XpubInIDs should have been set to reverted - assert.Equal(t, "reverted", tx.XpubInIDs[0]) - assert.Len(t, tx.XpubOutIDs, 1) // XpubInIDs should have been set to reverted - assert.Equal(t, "reverted", tx.XpubOutIDs[0]) - assert.Len(t, tx.XpubOutputValue, 1) // XpubInIDs should have been set to reverted - assert.Equal(t, int64(0), tx.XpubOutputValue["reverted"]) - - // check the balance of the xpub - var xpub *Xpub - xpub, err = client.GetXpubByID(ctx, testXPubID) - require.NoError(t, err) - assert.Equal(t, uint64(100000), xpub.CurrentBalance) // 100000 was initial value - - // check utxos where reverted - var utxos []*Utxo - conditions := map[string]interface{}{ - xPubIDField: transaction.XPubID, - } - utxos, err = client.GetUtxos(ctx, nil, conditions, nil, client.DefaultModelOptions()...) - require.NoError(t, err) - assert.Len(t, utxos, 2) // only original - for _, utxo := range utxos { - if utxo.TransactionID == transaction.ID { - assert.True(t, utxo.SpendingTxID.Valid) - assert.Equal(t, "deleted", utxo.SpendingTxID.String) - } else { - assert.False(t, utxo.SpendingTxID.Valid) - assert.Equal(t, "", utxo.SpendingTxID.String) - } - } - }) - - t.Run("disallow revert spent transaction", func(t *testing.T) { - ctx, client, transaction, xPriv, deferMe := initRevertTransactionData(t) - defer deferMe() - - // we need a draft transaction, otherwise we cannot revert - draftTransaction, err := newDraftTransaction( - testXPub, &TransactionConfig{ - Outputs: []*TransactionOutput{{ - To: "1A1PjKqjWMNBzTVdcBru27EV1PHcXWc63W", // random address - Satoshis: 1000, - }}, - ChangeNumberOfDestinations: 1, - Sync: &SyncConfig{ - Broadcast: true, - BroadcastInstant: false, - PaymailP2P: false, - SyncOnChain: false, - }, - }, - append(client.DefaultModelOptions(), New())..., - ) - require.NoError(t, err) - - // this gets inputs etc. - err = draftTransaction.Save(ctx) - require.NoError(t, err) - - var hex string - hex, err = draftTransaction.SignInputs(xPriv) - require.NoError(t, err) - assert.NotEmpty(t, hex) - - var secondTransaction *Transaction - secondTransaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) - require.NoError(t, err) - assert.NotEmpty(t, secondTransaction) - - // - // Revert the transaction - // - err = client.RevertTransaction(ctx, transaction.ID) - require.Equal(t, "utxo of this transaction has been spent, cannot revert", err.Error()) - }) - - t.Run("revert spend to internal address", func(t *testing.T) { - ctx, client, _, xPriv, deferMe := initRevertTransactionData(t) - defer deferMe() - - testXPub2 := "xpub661MyMwAqRbcFGX8a3K99DKPZahQBj1z8DsMTE7gqKtYj9yaWv45nkjHYcWdwUcQkGdZMv62HVKNCF4MNqXK2oiRKcfSE7U7iu5hAcyMzUS" - xPub := newXpub(testXPub2, append(client.DefaultModelOptions(), New())...) - err := xPub.Save(ctx) - require.NoError(t, err) - - var destination *Destination - destination, err = xPub.getNewDestination(ctx, utils.ChainExternal, utils.ScriptTypePubKeyHash, client.DefaultModelOptions(New())...) - require.NoError(t, err) - require.NotNil(t, destination) - - err = destination.Save(ctx) - require.NoError(t, err) - - // we need a draft transaction, otherwise we cannot revert - draftTransaction, err := newDraftTransaction( - testXPub, &TransactionConfig{ - Outputs: []*TransactionOutput{{ - To: destination.Address, - Satoshis: 1000, - }}, - ChangeNumberOfDestinations: 1, - Sync: &SyncConfig{ - Broadcast: true, - BroadcastInstant: false, - PaymailP2P: false, - SyncOnChain: false, - }, - }, - append(client.DefaultModelOptions(), New())..., - ) - require.NoError(t, err) - - // this gets inputs etc. - err = draftTransaction.Save(ctx) - require.NoError(t, err) - - var hex string - hex, err = draftTransaction.SignInputs(xPriv) - require.NoError(t, err) - assert.NotEmpty(t, hex) - - var transaction *Transaction - transaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) - require.NoError(t, err) - assert.NotEmpty(t, transaction) - assert.Len(t, transaction.XpubOutIDs, 2) - assert.Equal(t, int64(1000), transaction.XpubOutputValue[xPub.ID]) - - xPub, err = client.GetXpub(ctx, testXPub2) - require.NoError(t, err) - assert.Equal(t, uint64(1000), xPub.CurrentBalance) - - var utxos []*Utxo - utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) - require.NoError(t, err) - assert.Len(t, utxos, 1) - assert.Equal(t, uint64(1000), utxos[0].Satoshis) - assert.False(t, utxos[0].SpendingTxID.Valid) - - // - // Revert the transaction - // - err = client.RevertTransaction(ctx, transaction.ID) - require.NoError(t, err) - - // check the destination xpub / utxos etc - xPub, err = client.GetXpub(ctx, testXPub2) - require.NoError(t, err) - assert.Equal(t, uint64(0), xPub.CurrentBalance) - - utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) - require.NoError(t, err) - assert.Len(t, utxos, 1) - assert.True(t, utxos[0].SpendingTxID.Valid) - assert.Equal(t, "deleted", utxos[0].SpendingTxID.String) - }) -} +//func Test_RevertTransaction(t *testing.T) { +// bc := broadcast_client_mock.Builder(). +// WithMockArc(broadcast_client_mock.MockNilQueryTxResp). +// Build() +// t.Run("revert transaction", func(t *testing.T) { +// ctx, client, transaction, _, deferMe := initRevertTransactionData(t, WithBroadcastClient(bc)) +// defer deferMe() +// +// // +// // Revert the transaction +// // +// err := client.RevertTransaction(ctx, transaction.ID) +// require.NoError(t, err) +// +// // check transaction was reverted +// var tx *Transaction +// tx, err = client.GetTransaction(ctx, testXPubID, transaction.ID) +// require.NoError(t, err) +// assert.Equal(t, transaction.ID, tx.ID) +// assert.Len(t, tx.XpubInIDs, 1) // XpubInIDs should have been set to reverted +// assert.Equal(t, "reverted", tx.XpubInIDs[0]) +// assert.Len(t, tx.XpubOutIDs, 1) // XpubInIDs should have been set to reverted +// assert.Equal(t, "reverted", tx.XpubOutIDs[0]) +// assert.Len(t, tx.XpubOutputValue, 1) // XpubInIDs should have been set to reverted +// assert.Equal(t, int64(0), tx.XpubOutputValue["reverted"]) +// +// // check the balance of the xpub +// var xpub *Xpub +// xpub, err = client.GetXpubByID(ctx, testXPubID) +// require.NoError(t, err) +// assert.Equal(t, uint64(100000), xpub.CurrentBalance) // 100000 was initial value +// +// // check utxos where reverted +// var utxos []*Utxo +// conditions := map[string]interface{}{ +// xPubIDField: transaction.XPubID, +// } +// utxos, err = client.GetUtxos(ctx, nil, conditions, nil, client.DefaultModelOptions()...) +// require.NoError(t, err) +// assert.Len(t, utxos, 2) // only original +// for _, utxo := range utxos { +// if utxo.TransactionID == transaction.ID { +// assert.True(t, utxo.SpendingTxID.Valid) +// assert.Equal(t, "deleted", utxo.SpendingTxID.String) +// } else { +// assert.False(t, utxo.SpendingTxID.Valid) +// assert.Equal(t, "", utxo.SpendingTxID.String) +// } +// } +// }) +// +// t.Run("disallow revert spent transaction", func(t *testing.T) { +// ctx, client, transaction, xPriv, deferMe := initRevertTransactionData(t) +// defer deferMe() +// +// // we need a draft transaction, otherwise we cannot revert +// draftTransaction, err := newDraftTransaction( +// testXPub, &TransactionConfig{ +// Outputs: []*TransactionOutput{{ +// To: "1A1PjKqjWMNBzTVdcBru27EV1PHcXWc63W", // random address +// Satoshis: 1000, +// }}, +// ChangeNumberOfDestinations: 1, +// Sync: &SyncConfig{ +// Broadcast: true, +// BroadcastInstant: false, +// PaymailP2P: false, +// SyncOnChain: false, +// }, +// }, +// append(client.DefaultModelOptions(), New())..., +// ) +// require.NoError(t, err) +// +// // this gets inputs etc. +// err = draftTransaction.Save(ctx) +// require.NoError(t, err) +// +// var hex string +// hex, err = draftTransaction.SignInputs(xPriv) +// require.NoError(t, err) +// assert.NotEmpty(t, hex) +// +// var secondTransaction *Transaction +// secondTransaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) +// require.NoError(t, err) +// assert.NotEmpty(t, secondTransaction) +// +// // +// // Revert the transaction +// // +// err = client.RevertTransaction(ctx, transaction.ID) +// require.Equal(t, "utxo of this transaction has been spent, cannot revert", err.Error()) +// }) +// +// t.Run("revert spend to internal address", func(t *testing.T) { +// ctx, client, _, xPriv, deferMe := initRevertTransactionData(t) +// defer deferMe() +// +// testXPub2 := "xpub661MyMwAqRbcFGX8a3K99DKPZahQBj1z8DsMTE7gqKtYj9yaWv45nkjHYcWdwUcQkGdZMv62HVKNCF4MNqXK2oiRKcfSE7U7iu5hAcyMzUS" +// xPub := newXpub(testXPub2, append(client.DefaultModelOptions(), New())...) +// err := xPub.Save(ctx) +// require.NoError(t, err) +// +// var destination *Destination +// destination, err = xPub.getNewDestination(ctx, utils.ChainExternal, utils.ScriptTypePubKeyHash, client.DefaultModelOptions(New())...) +// require.NoError(t, err) +// require.NotNil(t, destination) +// +// err = destination.Save(ctx) +// require.NoError(t, err) +// +// // we need a draft transaction, otherwise we cannot revert +// draftTransaction, err := newDraftTransaction( +// testXPub, &TransactionConfig{ +// Outputs: []*TransactionOutput{{ +// To: destination.Address, +// Satoshis: 1000, +// }}, +// ChangeNumberOfDestinations: 1, +// Sync: &SyncConfig{ +// Broadcast: true, +// BroadcastInstant: false, +// PaymailP2P: false, +// SyncOnChain: false, +// }, +// }, +// append(client.DefaultModelOptions(), New())..., +// ) +// require.NoError(t, err) +// +// // this gets inputs etc. +// err = draftTransaction.Save(ctx) +// require.NoError(t, err) +// +// var hex string +// hex, err = draftTransaction.SignInputs(xPriv) +// require.NoError(t, err) +// assert.NotEmpty(t, hex) +// +// var transaction *Transaction +// transaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) +// require.NoError(t, err) +// assert.NotEmpty(t, transaction) +// assert.Len(t, transaction.XpubOutIDs, 2) +// assert.Equal(t, int64(1000), transaction.XpubOutputValue[xPub.ID]) +// +// xPub, err = client.GetXpub(ctx, testXPub2) +// require.NoError(t, err) +// assert.Equal(t, uint64(1000), xPub.CurrentBalance) +// +// var utxos []*Utxo +// utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) +// require.NoError(t, err) +// assert.Len(t, utxos, 1) +// assert.Equal(t, uint64(1000), utxos[0].Satoshis) +// assert.False(t, utxos[0].SpendingTxID.Valid) +// +// // +// // Revert the transaction +// // +// err = client.RevertTransaction(ctx, transaction.ID) +// require.NoError(t, err) +// +// // check the destination xpub / utxos etc +// xPub, err = client.GetXpub(ctx, testXPub2) +// require.NoError(t, err) +// assert.Equal(t, uint64(0), xPub.CurrentBalance) +// +// utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) +// require.NoError(t, err) +// assert.Len(t, utxos, 1) +// assert.True(t, utxos[0].SpendingTxID.Valid) +// assert.Equal(t, "deleted", utxos[0].SpendingTxID.String) +// }) +//} func Test_RecordTransaction(t *testing.T) { ctx, client, deferMe := CreateTestSQLiteClient(t, false, true, withTaskManagerMockup()) From b989e1a49fcfa53f1ad1da8bf16f9fa6f51777c7 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 10:30:54 +0200 Subject: [PATCH 12/24] fix(SPV-898): fixing failing tests --- engine/model_draft_transactions.go | 7 +++- engine/model_draft_transactions_test.go | 4 +-- engine/model_transactions.go | 14 +++++--- engine/spverrors/definitions.go | 3 ++ engine/utils/destination_types.go | 33 ++++++++++++------ engine/utils/destination_types_test.go | 46 ++++++++++++------------- engine/utils/keys.go | 19 ---------- engine/utils/utils_test.go | 40 --------------------- 8 files changed, 65 insertions(+), 101 deletions(-) diff --git a/engine/model_draft_transactions.go b/engine/model_draft_transactions.go index 90200e02..74dc8e5d 100644 --- a/engine/model_draft_transactions.go +++ b/engine/model_draft_transactions.go @@ -751,7 +751,7 @@ func (m *DraftTransaction) getInputsFromUtxos(reservedUtxos []*Utxo) ([]*trx.UTX utxo.Satoshis, ) if err != nil { - return nil, 0, spverrors.Wrapf(err, "failed to create UTXO for transaction %s", m.GetID()) + return nil, 0, spverrors.ErrFailedToCreateUTXO.Wrap(err) } inputUtxos = append(inputUtxos, utxo) @@ -894,6 +894,11 @@ func (m *DraftTransaction) SignInputs(xPriv *compat.ExtendedKey) (signedHex stri } // Return the signed hex + err = txDraft.Sign() + if err != nil { + return "", spverrors.Wrapf(err, "failed to sign inputs on model %s", m.GetModelName()) + } + signedHex = txDraft.String() return } diff --git a/engine/model_draft_transactions_test.go b/engine/model_draft_transactions_test.go index ec8125e5..3abdd5a1 100644 --- a/engine/model_draft_transactions_test.go +++ b/engine/model_draft_transactions_test.go @@ -903,13 +903,13 @@ func TestDraftTransaction_getInputsFromUtxos(t *testing.T) { reservedUtxos := []*Utxo{{ UtxoPointer: UtxoPointer{ OutputIndex: 123, - TransactionID: "testTxID", + TransactionID: "invalidTxIDHex", }, Satoshis: 124235, ScriptPubKey: testLockingScript, }} inputUtxos, satoshisReserved, err := draftTransaction.getInputsFromUtxos(reservedUtxos) - require.ErrorIs(t, err, spverrors.ErrInvalidTransactionID) + require.ErrorIs(t, err, spverrors.ErrFailedToCreateUTXO) assert.Nil(t, inputUtxos) assert.Equal(t, uint64(0), satoshisReserved) }) diff --git a/engine/model_transactions.go b/engine/model_transactions.go index 17165eb4..87cc43dd 100644 --- a/engine/model_transactions.go +++ b/engine/model_transactions.go @@ -87,17 +87,17 @@ func emptyTx(opts ...ModelOps) *Transaction { // baseTxFromHex creates the standard transaction model base func baseTxFromHex(hex string, opts ...ModelOps) (*Transaction, error) { - var btTx *trx.Transaction + var sdkTx *trx.Transaction var err error - if btTx, err = trx.NewTransactionFromHex(hex); err != nil { + if sdkTx, err = trx.NewTransactionFromHex(hex); err != nil { return nil, spverrors.Wrapf(err, "error parsing transaction hex") } tx := emptyTx(opts...) - tx.ID = btTx.TxID().String() + tx.ID = sdkTx.TxID().String() tx.Hex = hex - tx.parsedTx = btTx + tx.parsedTx = sdkTx return tx, nil } @@ -207,9 +207,13 @@ func (m *Transaction) getValues() (outputValue uint64, fee uint64) { fee = m.draftTransaction.Configuration.Fee } else { // external transaction - // todo: missing inputs in some tests? var inputValue uint64 for _, input := range m.TransactionBase.parsedTx.Inputs { + sourceTxSato := input.SourceTxSatoshis() + if sourceTxSato == nil { + continue + } + inputValue += *input.SourceTxSatoshis() } diff --git a/engine/spverrors/definitions.go b/engine/spverrors/definitions.go index f6b5486c..15dc49ba 100644 --- a/engine/spverrors/definitions.go +++ b/engine/spverrors/definitions.go @@ -206,6 +206,9 @@ var ErrInvalidOpReturnOutput = models.SPVError{Message: "invalid op_return outpu // ErrInvalidLockingScript is when a locking script cannot be decoded var ErrInvalidLockingScript = models.SPVError{Message: "invalid locking script", StatusCode: 400, Code: "error-transaction-locking-script-invalid"} +// ErrFailedToCreateUTXO is when sdk method fails to create UTXO with provided data +var ErrFailedToCreateUTXO = models.SPVError{Message: "failed to create UTXO", StatusCode: 400, Code: "error-transaction-failed-to-create-utxo"} + // ErrOutputValueNotRecognized is when there is an invalid output value given, or missing value var ErrOutputValueNotRecognized = models.SPVError{Message: "output value is unrecognized", StatusCode: 400, Code: "error-transaction-output-value-unrecognized"} diff --git a/engine/utils/destination_types.go b/engine/utils/destination_types.go index 04f72a3e..f26f8644 100644 --- a/engine/utils/destination_types.go +++ b/engine/utils/destination_types.go @@ -186,18 +186,23 @@ func GetDestinationTypeRegex(destType string) *regexp.Regexp { } // GetAddressFromScript gets the destination address from the given locking script +// FIXME: add logger on this scope to have info about returned errors func GetAddressFromScript(lockingScript string) (address string) { scriptType := GetDestinationType(lockingScript) if scriptType == ScriptTypePubKeyHash { - lsBytes, err := hex.DecodeString(lockingScript) + s, err := script.NewFromHex(lockingScript) if err != nil { return "" } - decodedAddr, _ := script.NewAddressFromPublicKeyHash(lsBytes, true) - address = decodedAddr.AddressString + addresses, err := s.Addresses() + if err != nil || len(addresses) == 0 { + return "" + } + + address = addresses[0] } else if scriptType == ScriptTypePubKey { - s, err := script.NewFromHex(lockingScript) // no need for error check, if type is set, it should be valid + s, err := script.NewFromHex(lockingScript) if err != nil { return "" } @@ -208,22 +213,28 @@ func GetAddressFromScript(lockingScript string) (address string) { return "" } - pubkeyHex := hex.EncodeToString(parts[0].Data) + pubKeyHex := hex.EncodeToString(parts[0].Data) var addressScript *script.Address - addressScript, err = script.NewAddressFromPublicKeyString(pubkeyHex, true) - if err == nil { - address = addressScript.AddressString + addressScript, err = script.NewAddressFromPublicKeyString(pubKeyHex, true) + if err != nil { + return "" } + + address = addressScript.AddressString } else if scriptType == ScriptTypeTokenStas { // stas is just a normal PubKeyHash with more data appended - lsBytes, err := hex.DecodeString(lockingScript[:50]) + s, err := script.NewFromHex(lockingScript[:50]) if err != nil { return "" } - decodedAddr, _ := script.NewAddressFromPublicKeyHash(lsBytes, true) - address = decodedAddr.AddressString + addresses, err := s.Addresses() + if err != nil || len(addresses) == 0 { + return "" + } + + address = addresses[0] // } else if scriptType == ScriptTypeTokenSensible { // sensible tokens do not have the receiving address in the token output, but in another output // sensible does not seem to be a utxo protocol, but an output protocol (all outputs of the tx matter) diff --git a/engine/utils/destination_types_test.go b/engine/utils/destination_types_test.go index aa83d4aa..38a19711 100644 --- a/engine/utils/destination_types_test.go +++ b/engine/utils/destination_types_test.go @@ -205,29 +205,29 @@ func TestGetDestinationType(t *testing.T) { } // TestGetAddressFromScript will test the method GetAddressFromScript() -// func TestGetAddressFromScript(t *testing.T) { -// t.Parallel() - -// //t.Run("p2pk", func(t *testing.T) { -// // assert.Equal(t, "1BYpPJHowiz9Qr6zsTzRXKNeej2RV2Av6H", GetAddressFromScript(p2pkHex)) -// //}) - -// // t.Run("p2pkh", func(t *testing.T) { -// // assert.Equal(t, "12kwBQPUnAMouxBBWRa5wsA6vC29soEdXT", GetAddressFromScript(p2pkhHex)) -// // }) - -// // t.Run("stas 1", func(t *testing.T) { -// // assert.Equal(t, "1AxScC72W9tyk1Enej6dBsVZNkkgAonk4H", GetAddressFromScript(stasHex)) -// // }) -// // -// // t.Run("stas 2", func(t *testing.T) { -// // assert.Equal(t, "1MXhcVvUz1LGSkoUFGkANHXkGCtrzFKHpA", GetAddressFromScript(stas2Hex)) -// // }) -// // -// // t.Run("unknown", func(t *testing.T) { -// // assert.Equal(t, "", GetAddressFromScript("invalid-or-unknown-script")) -// // }) -// } +func TestGetAddressFromScript(t *testing.T) { + t.Parallel() + + t.Run("p2pk", func(t *testing.T) { + assert.Equal(t, "1BYpPJHowiz9Qr6zsTzRXKNeej2RV2Av6H", GetAddressFromScript(p2pkHex)) + }) + + t.Run("p2pkh", func(t *testing.T) { + assert.Equal(t, "12kwBQPUnAMouxBBWRa5wsA6vC29soEdXT", GetAddressFromScript(p2pkhHex)) + }) + + t.Run("stas 1", func(t *testing.T) { + assert.Equal(t, "1AxScC72W9tyk1Enej6dBsVZNkkgAonk4H", GetAddressFromScript(stasHex)) + }) + + t.Run("stas 2", func(t *testing.T) { + assert.Equal(t, "1MXhcVvUz1LGSkoUFGkANHXkGCtrzFKHpA", GetAddressFromScript(stas2Hex)) + }) + + t.Run("unknown", func(t *testing.T) { + assert.Equal(t, "", GetAddressFromScript("invalid-or-unknown-script")) + }) +} func BenchmarkIsP2PKH(b *testing.B) { for i := 0; i < b.N; i++ { diff --git a/engine/utils/keys.go b/engine/utils/keys.go index de3825c2..820863c1 100644 --- a/engine/utils/keys.go +++ b/engine/utils/keys.go @@ -127,22 +127,3 @@ func DerivePrivateKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*ec.Pr return privKey, nil } - -// DerivePublicKeyFromHex will derive the public key from the extended key using the hex as the derivation paths -func DerivePublicKeyFromHex(hdKey *bip32.ExtendedKey, hexString string) (*ec.PublicKey, error) { - if hdKey == nil { - return nil, ErrHDKeyNil - } - - childKey, err := DeriveChildKeyFromHex(hdKey, hexString) - if err != nil { - return nil, err - } - - var pubKey *ec.PublicKey - if pubKey, err = childKey.ECPubKey(); err != nil { - return nil, spverrors.Wrapf(err, "failed to derive public key") - } - - return pubKey, nil -} diff --git a/engine/utils/utils_test.go b/engine/utils/utils_test.go index de69ecc7..bad82973 100644 --- a/engine/utils/utils_test.go +++ b/engine/utils/utils_test.go @@ -135,46 +135,6 @@ func TestDerivePrivateKeyFromHex(t *testing.T) { }) } -// TODO: Fix -// TestDerivePublicKeyFromHex test the method DerivePublicKeyFromHex -//func TestDerivePublicKeyFromHex(t *testing.T) { -// hdXpriv, _ := compat.GetHDKeyFromExtendedPublicKey(testXpriv) -// hdXpub, _ := compat.GetHDKeyFromExtendedPublicKey(testXpub) -// -// t.Run("empty key", func(t *testing.T) { -// _, err := DerivePublicKeyFromHex(nil, testHash) -// require.ErrorIs(t, err, ErrHDKeyNil) -// }) -// -// //t.Run("priv empty hex", func(t *testing.T) { -// // key, err := DerivePublicKeyFromHex(hdXpriv, "") -// // require.NoError(t, err) -// // privKey, _ := primitives.PrivateKeyFromWif(privateKey0) -// // assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) -// //}) -// // -// //t.Run("pub empty hex", func(t *testing.T) { -// // key, err := DerivePublicKeyFromHex(hdXpub, "") -// // require.NoError(t, err) -// // privKey, _ := primitives.PrivateKeyFromWif(privateKey0) -// // assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) -// //}) -// -// t.Run("priv testHash hex", func(t *testing.T) { -// key, err := DerivePublicKeyFromHex(hdXpriv, testHash) -// require.NoError(t, err) -// privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) -// assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) -// }) -// -// t.Run("pub testHash hex", func(t *testing.T) { -// key, err := DerivePublicKeyFromHex(hdXpub, testHash) -// require.NoError(t, err) -// privKey, _ := primitives.PrivateKeyFromWif(privateKeyHash) -// assert.Equal(t, privKey.PublicKey, hex.EncodeToString(key.SerializeCompressed())) -// }) -//} - // TestGetChildNumsFromHex test the method GetChildNumsFromHex func TestGetChildNumsFromHex(t *testing.T) { t.Run("empty hex", func(t *testing.T) { From 1b8f7c38709b31d165d500346ce830296b06a921 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 10:39:14 +0200 Subject: [PATCH 13/24] rev(SPV-898): revert skipped test --- engine/action_transaction_test.go | 354 +++++++++++++++--------------- 1 file changed, 178 insertions(+), 176 deletions(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index ae8f8750..51377e5c 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -3,6 +3,8 @@ package engine import ( "context" "fmt" + broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" + "github.com/bitcoin-sv/spv-wallet/engine/utils" "testing" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" @@ -11,182 +13,182 @@ import ( "github.com/stretchr/testify/require" ) -//func Test_RevertTransaction(t *testing.T) { -// bc := broadcast_client_mock.Builder(). -// WithMockArc(broadcast_client_mock.MockNilQueryTxResp). -// Build() -// t.Run("revert transaction", func(t *testing.T) { -// ctx, client, transaction, _, deferMe := initRevertTransactionData(t, WithBroadcastClient(bc)) -// defer deferMe() -// -// // -// // Revert the transaction -// // -// err := client.RevertTransaction(ctx, transaction.ID) -// require.NoError(t, err) -// -// // check transaction was reverted -// var tx *Transaction -// tx, err = client.GetTransaction(ctx, testXPubID, transaction.ID) -// require.NoError(t, err) -// assert.Equal(t, transaction.ID, tx.ID) -// assert.Len(t, tx.XpubInIDs, 1) // XpubInIDs should have been set to reverted -// assert.Equal(t, "reverted", tx.XpubInIDs[0]) -// assert.Len(t, tx.XpubOutIDs, 1) // XpubInIDs should have been set to reverted -// assert.Equal(t, "reverted", tx.XpubOutIDs[0]) -// assert.Len(t, tx.XpubOutputValue, 1) // XpubInIDs should have been set to reverted -// assert.Equal(t, int64(0), tx.XpubOutputValue["reverted"]) -// -// // check the balance of the xpub -// var xpub *Xpub -// xpub, err = client.GetXpubByID(ctx, testXPubID) -// require.NoError(t, err) -// assert.Equal(t, uint64(100000), xpub.CurrentBalance) // 100000 was initial value -// -// // check utxos where reverted -// var utxos []*Utxo -// conditions := map[string]interface{}{ -// xPubIDField: transaction.XPubID, -// } -// utxos, err = client.GetUtxos(ctx, nil, conditions, nil, client.DefaultModelOptions()...) -// require.NoError(t, err) -// assert.Len(t, utxos, 2) // only original -// for _, utxo := range utxos { -// if utxo.TransactionID == transaction.ID { -// assert.True(t, utxo.SpendingTxID.Valid) -// assert.Equal(t, "deleted", utxo.SpendingTxID.String) -// } else { -// assert.False(t, utxo.SpendingTxID.Valid) -// assert.Equal(t, "", utxo.SpendingTxID.String) -// } -// } -// }) -// -// t.Run("disallow revert spent transaction", func(t *testing.T) { -// ctx, client, transaction, xPriv, deferMe := initRevertTransactionData(t) -// defer deferMe() -// -// // we need a draft transaction, otherwise we cannot revert -// draftTransaction, err := newDraftTransaction( -// testXPub, &TransactionConfig{ -// Outputs: []*TransactionOutput{{ -// To: "1A1PjKqjWMNBzTVdcBru27EV1PHcXWc63W", // random address -// Satoshis: 1000, -// }}, -// ChangeNumberOfDestinations: 1, -// Sync: &SyncConfig{ -// Broadcast: true, -// BroadcastInstant: false, -// PaymailP2P: false, -// SyncOnChain: false, -// }, -// }, -// append(client.DefaultModelOptions(), New())..., -// ) -// require.NoError(t, err) -// -// // this gets inputs etc. -// err = draftTransaction.Save(ctx) -// require.NoError(t, err) -// -// var hex string -// hex, err = draftTransaction.SignInputs(xPriv) -// require.NoError(t, err) -// assert.NotEmpty(t, hex) -// -// var secondTransaction *Transaction -// secondTransaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) -// require.NoError(t, err) -// assert.NotEmpty(t, secondTransaction) -// -// // -// // Revert the transaction -// // -// err = client.RevertTransaction(ctx, transaction.ID) -// require.Equal(t, "utxo of this transaction has been spent, cannot revert", err.Error()) -// }) -// -// t.Run("revert spend to internal address", func(t *testing.T) { -// ctx, client, _, xPriv, deferMe := initRevertTransactionData(t) -// defer deferMe() -// -// testXPub2 := "xpub661MyMwAqRbcFGX8a3K99DKPZahQBj1z8DsMTE7gqKtYj9yaWv45nkjHYcWdwUcQkGdZMv62HVKNCF4MNqXK2oiRKcfSE7U7iu5hAcyMzUS" -// xPub := newXpub(testXPub2, append(client.DefaultModelOptions(), New())...) -// err := xPub.Save(ctx) -// require.NoError(t, err) -// -// var destination *Destination -// destination, err = xPub.getNewDestination(ctx, utils.ChainExternal, utils.ScriptTypePubKeyHash, client.DefaultModelOptions(New())...) -// require.NoError(t, err) -// require.NotNil(t, destination) -// -// err = destination.Save(ctx) -// require.NoError(t, err) -// -// // we need a draft transaction, otherwise we cannot revert -// draftTransaction, err := newDraftTransaction( -// testXPub, &TransactionConfig{ -// Outputs: []*TransactionOutput{{ -// To: destination.Address, -// Satoshis: 1000, -// }}, -// ChangeNumberOfDestinations: 1, -// Sync: &SyncConfig{ -// Broadcast: true, -// BroadcastInstant: false, -// PaymailP2P: false, -// SyncOnChain: false, -// }, -// }, -// append(client.DefaultModelOptions(), New())..., -// ) -// require.NoError(t, err) -// -// // this gets inputs etc. -// err = draftTransaction.Save(ctx) -// require.NoError(t, err) -// -// var hex string -// hex, err = draftTransaction.SignInputs(xPriv) -// require.NoError(t, err) -// assert.NotEmpty(t, hex) -// -// var transaction *Transaction -// transaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) -// require.NoError(t, err) -// assert.NotEmpty(t, transaction) -// assert.Len(t, transaction.XpubOutIDs, 2) -// assert.Equal(t, int64(1000), transaction.XpubOutputValue[xPub.ID]) -// -// xPub, err = client.GetXpub(ctx, testXPub2) -// require.NoError(t, err) -// assert.Equal(t, uint64(1000), xPub.CurrentBalance) -// -// var utxos []*Utxo -// utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) -// require.NoError(t, err) -// assert.Len(t, utxos, 1) -// assert.Equal(t, uint64(1000), utxos[0].Satoshis) -// assert.False(t, utxos[0].SpendingTxID.Valid) -// -// // -// // Revert the transaction -// // -// err = client.RevertTransaction(ctx, transaction.ID) -// require.NoError(t, err) -// -// // check the destination xpub / utxos etc -// xPub, err = client.GetXpub(ctx, testXPub2) -// require.NoError(t, err) -// assert.Equal(t, uint64(0), xPub.CurrentBalance) -// -// utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) -// require.NoError(t, err) -// assert.Len(t, utxos, 1) -// assert.True(t, utxos[0].SpendingTxID.Valid) -// assert.Equal(t, "deleted", utxos[0].SpendingTxID.String) -// }) -//} +func Test_RevertTransaction(t *testing.T) { + bc := broadcast_client_mock.Builder(). + WithMockArc(broadcast_client_mock.MockNilQueryTxResp). + Build() + t.Run("revert transaction", func(t *testing.T) { + ctx, client, transaction, _, deferMe := initRevertTransactionData(t, WithBroadcastClient(bc)) + defer deferMe() + + // + // Revert the transaction + // + err := client.RevertTransaction(ctx, transaction.ID) + require.NoError(t, err) + + // check transaction was reverted + var tx *Transaction + tx, err = client.GetTransaction(ctx, testXPubID, transaction.ID) + require.NoError(t, err) + assert.Equal(t, transaction.ID, tx.ID) + assert.Len(t, tx.XpubInIDs, 1) // XpubInIDs should have been set to reverted + assert.Equal(t, "reverted", tx.XpubInIDs[0]) + assert.Len(t, tx.XpubOutIDs, 1) // XpubInIDs should have been set to reverted + assert.Equal(t, "reverted", tx.XpubOutIDs[0]) + assert.Len(t, tx.XpubOutputValue, 1) // XpubInIDs should have been set to reverted + assert.Equal(t, int64(0), tx.XpubOutputValue["reverted"]) + + // check the balance of the xpub + var xpub *Xpub + xpub, err = client.GetXpubByID(ctx, testXPubID) + require.NoError(t, err) + assert.Equal(t, uint64(100000), xpub.CurrentBalance) // 100000 was initial value + + // check utxos where reverted + var utxos []*Utxo + conditions := map[string]interface{}{ + xPubIDField: transaction.XPubID, + } + utxos, err = client.GetUtxos(ctx, nil, conditions, nil, client.DefaultModelOptions()...) + require.NoError(t, err) + assert.Len(t, utxos, 2) // only original + for _, utxo := range utxos { + if utxo.TransactionID == transaction.ID { + assert.True(t, utxo.SpendingTxID.Valid) + assert.Equal(t, "deleted", utxo.SpendingTxID.String) + } else { + assert.False(t, utxo.SpendingTxID.Valid) + assert.Equal(t, "", utxo.SpendingTxID.String) + } + } + }) + + t.Run("disallow revert spent transaction", func(t *testing.T) { + ctx, client, transaction, xPriv, deferMe := initRevertTransactionData(t) + defer deferMe() + + // we need a draft transaction, otherwise we cannot revert + draftTransaction, err := newDraftTransaction( + testXPub, &TransactionConfig{ + Outputs: []*TransactionOutput{{ + To: "1A1PjKqjWMNBzTVdcBru27EV1PHcXWc63W", // random address + Satoshis: 1000, + }}, + ChangeNumberOfDestinations: 1, + Sync: &SyncConfig{ + Broadcast: true, + BroadcastInstant: false, + PaymailP2P: false, + SyncOnChain: false, + }, + }, + append(client.DefaultModelOptions(), New())..., + ) + require.NoError(t, err) + + // this gets inputs etc. + err = draftTransaction.Save(ctx) + require.NoError(t, err) + + var hex string + hex, err = draftTransaction.SignInputs(xPriv) + require.NoError(t, err) + assert.NotEmpty(t, hex) + + var secondTransaction *Transaction + secondTransaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) + require.NoError(t, err) + assert.NotEmpty(t, secondTransaction) + + // + // Revert the transaction + // + err = client.RevertTransaction(ctx, transaction.ID) + require.Equal(t, "utxo of this transaction has been spent, cannot revert", err.Error()) + }) + + t.Run("revert spend to internal address", func(t *testing.T) { + ctx, client, _, xPriv, deferMe := initRevertTransactionData(t) + defer deferMe() + + testXPub2 := "xpub661MyMwAqRbcFGX8a3K99DKPZahQBj1z8DsMTE7gqKtYj9yaWv45nkjHYcWdwUcQkGdZMv62HVKNCF4MNqXK2oiRKcfSE7U7iu5hAcyMzUS" + xPub := newXpub(testXPub2, append(client.DefaultModelOptions(), New())...) + err := xPub.Save(ctx) + require.NoError(t, err) + + var destination *Destination + destination, err = xPub.getNewDestination(ctx, utils.ChainExternal, utils.ScriptTypePubKeyHash, client.DefaultModelOptions(New())...) + require.NoError(t, err) + require.NotNil(t, destination) + + err = destination.Save(ctx) + require.NoError(t, err) + + // we need a draft transaction, otherwise we cannot revert + draftTransaction, err := newDraftTransaction( + testXPub, &TransactionConfig{ + Outputs: []*TransactionOutput{{ + To: destination.Address, + Satoshis: 1000, + }}, + ChangeNumberOfDestinations: 1, + Sync: &SyncConfig{ + Broadcast: true, + BroadcastInstant: false, + PaymailP2P: false, + SyncOnChain: false, + }, + }, + append(client.DefaultModelOptions(), New())..., + ) + require.NoError(t, err) + + // this gets inputs etc. + err = draftTransaction.Save(ctx) + require.NoError(t, err) + + var hex string + hex, err = draftTransaction.SignInputs(xPriv) + require.NoError(t, err) + assert.NotEmpty(t, hex) + + var transaction *Transaction + transaction, err = client.RecordTransaction(ctx, testXPub, hex, draftTransaction.ID, client.DefaultModelOptions()...) + require.NoError(t, err) + assert.NotEmpty(t, transaction) + assert.Len(t, transaction.XpubOutIDs, 2) + assert.Equal(t, int64(1000), transaction.XpubOutputValue[xPub.ID]) + + xPub, err = client.GetXpub(ctx, testXPub2) + require.NoError(t, err) + assert.Equal(t, uint64(1000), xPub.CurrentBalance) + + var utxos []*Utxo + utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) + require.NoError(t, err) + assert.Len(t, utxos, 1) + assert.Equal(t, uint64(1000), utxos[0].Satoshis) + assert.False(t, utxos[0].SpendingTxID.Valid) + + // + // Revert the transaction + // + err = client.RevertTransaction(ctx, transaction.ID) + require.NoError(t, err) + + // check the destination xpub / utxos etc + xPub, err = client.GetXpub(ctx, testXPub2) + require.NoError(t, err) + assert.Equal(t, uint64(0), xPub.CurrentBalance) + + utxos, err = client.GetUtxosByXpubID(ctx, xPub.ID, nil, nil, nil) + require.NoError(t, err) + assert.Len(t, utxos, 1) + assert.True(t, utxos[0].SpendingTxID.Valid) + assert.Equal(t, "deleted", utxos[0].SpendingTxID.String) + }) +} func Test_RecordTransaction(t *testing.T) { ctx, client, deferMe := CreateTestSQLiteClient(t, false, true, withTaskManagerMockup()) From 10df9dc5fbc79d83487666cf3855237cfdec256c Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 11:07:55 +0200 Subject: [PATCH 14/24] chore(SPV-898): apply gci on action_transaction_test.go file --- engine/action_transaction_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index 51377e5c..eb32ecd8 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -3,9 +3,10 @@ package engine import ( "context" "fmt" + "testing" + broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" "github.com/bitcoin-sv/spv-wallet/engine/utils" - "testing" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/models/bsv" From 5de22f95f2a0374715961e5dc144e1cbd612b81a Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 11:12:09 +0200 Subject: [PATCH 15/24] chore(SPV-898): organize imports to match gci convention --- engine/action_transaction_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index eb32ecd8..dec26eb1 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -6,9 +6,9 @@ import ( "testing" broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" - "github.com/bitcoin-sv/spv-wallet/engine/utils" - compat "github.com/bitcoin-sv/go-sdk/compat/bip32" + + "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" From 583f2902eabea3bd5ae89f9b9bff573b85c1b195 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 12:30:54 +0200 Subject: [PATCH 16/24] fix(SPV-898): organize imports --- engine/action_transaction_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index dec26eb1..006f3c97 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -3,15 +3,13 @@ package engine import ( "context" "fmt" - "testing" - broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" - "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "testing" ) func Test_RevertTransaction(t *testing.T) { From 4c9a72b9c3339a1c524d2cf7adcf67b789286a4d Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 12:34:40 +0200 Subject: [PATCH 17/24] fix(SPV-898): proper order gci --- engine/action_transaction_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index 006f3c97..d41d53f5 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -3,13 +3,14 @@ package engine import ( "context" "fmt" + "testing" + broadcast_client_mock "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client-mock" compat "github.com/bitcoin-sv/go-sdk/compat/bip32" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_RevertTransaction(t *testing.T) { From 295560112b8f5f521ba71da14b5c0927d29d98ad Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 15:46:41 +0200 Subject: [PATCH 18/24] fix(SPV-898): codebase improvments - simplifications --- engine/action_transaction_test.go | 1 - engine/beef_tx_sorting.go | 6 +-- engine/ef_tx.go | 12 +++--- engine/model_destinations.go | 2 + engine/paymail_service_provider.go | 59 ++++++++++++++++++++---------- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/engine/action_transaction_test.go b/engine/action_transaction_test.go index d41d53f5..ed8dc942 100644 --- a/engine/action_transaction_test.go +++ b/engine/action_transaction_test.go @@ -271,7 +271,6 @@ func initRevertTransactionData(t *testing.T, clientOpts ...ClientOps) (context.C require.NoError(t, err) var xPriv *compat.ExtendedKey - // xPriv, err = bip32.NewKeyFromString(testXPriv) xPriv, err = compat.NewKeyFromString(testXPriv) require.NoError(t, err) diff --git a/engine/beef_tx_sorting.go b/engine/beef_tx_sorting.go index 80557ec4..f70e81fa 100644 --- a/engine/beef_tx_sorting.go +++ b/engine/beef_tx_sorting.go @@ -26,7 +26,7 @@ func prepareSortStructures(dag []*trx.Transaction) (txByID map[string]*trx.Trans incomingEdgesMap = make(map[string]int, dagLen) for _, tx := range dag { - txByID[tx.TxID().String()] = tx // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + txByID[tx.TxID().String()] = tx // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time incomingEdgesMap[tx.TxID().String()] = 0 } @@ -39,7 +39,7 @@ func prepareSortStructures(dag []*trx.Transaction) (txByID map[string]*trx.Trans func calculateIncomingEdges(inDegree map[string]int, txByID map[string]*trx.Transaction) { for _, tx := range txByID { for _, input := range tx.Inputs { - inputUtxoTxID := input.SourceTXID.String() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + inputUtxoTxID := input.SourceTXID.String() // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in inDegree[inputUtxoTxID]++ } @@ -61,7 +61,7 @@ func getTxWithZeroIncomingEdges(incomingEdgesMap map[string]int) []string { func removeTxFromIncomingEdges(tx *trx.Transaction, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) []string { for _, input := range tx.Inputs { - neighborID := input.SourceTXID.String() // TODO: perf -> In bt, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + neighborID := input.SourceTXID.String() // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time incomingEdgesMap[neighborID]-- if incomingEdgesMap[neighborID] == 0 { diff --git a/engine/ef_tx.go b/engine/ef_tx.go index 582ca0b5..e97e8774 100644 --- a/engine/ef_tx.go +++ b/engine/ef_tx.go @@ -8,18 +8,18 @@ import ( // ToEfHex generates Extended Format hex of transaction func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efHex string, ok bool) { - btTx := tx.parsedTx + sdkTx := tx.parsedTx - if btTx == nil { + if sdkTx == nil { var err error - btTx, err = trx.NewTransactionFromHex(tx.Hex) + sdkTx, err = trx.NewTransactionFromHex(tx.Hex) if err != nil { return "", false } } needToHydrate := false - for _, input := range btTx.Inputs { + for _, input := range sdkTx.Inputs { if input.SourceTXID == nil { needToHydrate = true break @@ -27,12 +27,12 @@ func ToEfHex(ctx context.Context, tx *Transaction, store TransactionGetter) (efH } if needToHydrate { - if ok := hydrate(ctx, btTx, store); !ok { + if ok := hydrate(ctx, sdkTx, store); !ok { return "", false } } - ef, err := btTx.EFHex() + ef, err := sdkTx.EFHex() if err != nil { return "", false } diff --git a/engine/model_destinations.go b/engine/model_destinations.go index 52220e9c..bd7402e6 100644 --- a/engine/model_destinations.go +++ b/engine/model_destinations.go @@ -274,6 +274,8 @@ func getDestinationWithCache(ctx context.Context, client ClientInterface, return destination, nil } + opts = append(opts, client.DefaultModelOptions()...) + // Get via ID, address or locking script if len(id) > 0 { destination, err = getDestinationByID( diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index 8b31026b..45e89527 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -142,8 +142,12 @@ func (p *PaymailDefaultServiceProvider) RecordTransaction(ctx context.Context, metadata[ReferenceIDField] = p2pTx.Reference // Record the transaction - btTx := buildSDKTx(p2pTx) - rts, err := getIncomingTxRecordStrategy(ctx, p.client, btTx) + sdkTx, err := buildSDKTx(p2pTx) + if err != nil { + return nil, err + } + + rts, err := getIncomingTxRecordStrategy(ctx, p.client, sdkTx) if err != nil { return nil, err } @@ -287,24 +291,39 @@ func createLockingScript(ecPubKey *ec.PublicKey) (lockingScript string, err erro return } -func buildSDKTx(p2pTx *paymail.P2PTransaction) *trx.Transaction { - if p2pTx.DecodedBeef != nil { - res := p2pTx.DecodedBeef.GetLatestTx() - for _, input := range res.Inputs { - prevTxDt := find(p2pTx.DecodedBeef.Transactions, func(tx *beef.TxData) bool { return tx.Transaction.TxID() == input.SourceTXID }) - - o := (*prevTxDt).Transaction.Outputs[input.SourceTxOutIndex] - input.SetSourceTxOutput(&trx.TransactionOutput{ - Satoshis: o.Satoshis, - LockingScript: o.LockingScript, - }) - } - - return res - } - - res, _ := trx.NewTransactionFromHex(p2pTx.Hex) - return res +func buildSDKTx(p2pTx *paymail.P2PTransaction) (*trx.Transaction, error) { + //if p2pTx.DecodedBeef != nil { + // res := p2pTx.DecodedBeef.GetLatestTx() + // for _, input := range res.Inputs { + // //prevTxDt := find(p2pTx.DecodedBeef.Transactions, func(tx *beef.TxData) bool { return tx.Transaction.TxID() == input.SourceTXID }) + // var prevTxDt *beef.TxData + // for _, transactionData := range p2pTx.DecodedBeef.Transactions { + // txID := transactionData.Transaction.TxID().String() // Get the current transaction's TxID + // // Check if this TxID matches the input source TXID + // if txID == input.SourceTXID.String() { + // prevTxDt = transactionData + // break + // } + // + // // Optional: Debugging log to check which TXIDs are being compared + // fmt.Printf("Checking TxID: %s against SourceTXID: %s\n", txID, input.SourceTXID) + // } + // + // o := (*prevTxDt).Transaction.Outputs[input.SourceTxOutIndex] + // input.SetSourceTxOutput(&trx.TransactionOutput{ + // Satoshis: o.Satoshis, + // LockingScript: o.LockingScript, + // }) + // } + + // return res + //} + + if p2pTx.Beef != "" { + return trx.NewTransactionFromBEEFHex(p2pTx.Beef) + } + + return trx.NewTransactionFromHex(p2pTx.Hex) } func saveBEEFTxInputs(ctx context.Context, c ClientInterface, dBeef *beef.DecodedBEEF) { From c7b88cfa6ebc144c4e445a8f875685cf6fcf5e80 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 15:59:10 +0200 Subject: [PATCH 19/24] fix(SPV-898): wrap external errors with local wrapper --- engine/paymail_service_provider.go | 44 ++++++++++-------------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index 45e89527..e04f6b2e 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -292,38 +292,22 @@ func createLockingScript(ecPubKey *ec.PublicKey) (lockingScript string, err erro } func buildSDKTx(p2pTx *paymail.P2PTransaction) (*trx.Transaction, error) { - //if p2pTx.DecodedBeef != nil { - // res := p2pTx.DecodedBeef.GetLatestTx() - // for _, input := range res.Inputs { - // //prevTxDt := find(p2pTx.DecodedBeef.Transactions, func(tx *beef.TxData) bool { return tx.Transaction.TxID() == input.SourceTXID }) - // var prevTxDt *beef.TxData - // for _, transactionData := range p2pTx.DecodedBeef.Transactions { - // txID := transactionData.Transaction.TxID().String() // Get the current transaction's TxID - // // Check if this TxID matches the input source TXID - // if txID == input.SourceTXID.String() { - // prevTxDt = transactionData - // break - // } - // - // // Optional: Debugging log to check which TXIDs are being compared - // fmt.Printf("Checking TxID: %s against SourceTXID: %s\n", txID, input.SourceTXID) - // } - // - // o := (*prevTxDt).Transaction.Outputs[input.SourceTxOutIndex] - // input.SetSourceTxOutput(&trx.TransactionOutput{ - // Satoshis: o.Satoshis, - // LockingScript: o.LockingScript, - // }) - // } - - // return res - //} - + var err error + var tx *trx.Transaction if p2pTx.Beef != "" { - return trx.NewTransactionFromBEEFHex(p2pTx.Beef) - } + tx, err = trx.NewTransactionFromBEEFHex(p2pTx.Beef) + if err != nil { + return nil, spverrors.Wrapf(err, "unable to create transaction from BEEF") + } - return trx.NewTransactionFromHex(p2pTx.Hex) + return tx, nil + } + tx, err = trx.NewTransactionFromHex(p2pTx.Hex) + if err != nil { + return nil, spverrors.Wrapf(err, "unable to create transaction from hex") + } + + return tx, err } func saveBEEFTxInputs(ctx context.Context, c ClientInterface, dBeef *beef.DecodedBEEF) { From 016515592fad0f0c3b91b8061d673b0212b234c6 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 16:01:20 +0200 Subject: [PATCH 20/24] fix(SPV-898): gci-ed engine/paymail_service_provider.go file --- engine/paymail_service_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index e04f6b2e..46aa6681 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -306,7 +306,7 @@ func buildSDKTx(p2pTx *paymail.P2PTransaction) (*trx.Transaction, error) { if err != nil { return nil, spverrors.Wrapf(err, "unable to create transaction from hex") } - + return tx, err } From cfe410191c9471e1e78f91080c28b7a8038e1ad5 Mon Sep 17 00:00:00 2001 From: wregulski Date: Mon, 14 Oct 2024 16:04:21 +0200 Subject: [PATCH 21/24] fix(SPV-898): return proper type --- engine/paymail_service_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/paymail_service_provider.go b/engine/paymail_service_provider.go index 46aa6681..6374283f 100644 --- a/engine/paymail_service_provider.go +++ b/engine/paymail_service_provider.go @@ -307,7 +307,7 @@ func buildSDKTx(p2pTx *paymail.P2PTransaction) (*trx.Transaction, error) { return nil, spverrors.Wrapf(err, "unable to create transaction from hex") } - return tx, err + return tx, nil } func saveBEEFTxInputs(ctx context.Context, c ClientInterface, dBeef *beef.DecodedBEEF) { From 86d8b2b46279bbd4183496ba7f98a9c4370e2819 Mon Sep 17 00:00:00 2001 From: wregulski Date: Tue, 15 Oct 2024 15:09:24 +0200 Subject: [PATCH 22/24] fix(SPV-898): code review fixes --- config/config_to_options.go | 16 ---------------- engine/beef_tx_bytes.go | 3 ++- engine/script/template/evaluate.go | 5 ++++- engine/utils/ripemd160.go | 8 ++++---- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/config/config_to_options.go b/config/config_to_options.go index fd5157f7..3cccf48d 100644 --- a/config/config_to_options.go +++ b/config/config_to_options.go @@ -14,7 +14,6 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/taskmanager" "github.com/bitcoin-sv/spv-wallet/engine/utils" "github.com/bitcoin-sv/spv-wallet/metrics" - "github.com/bitcoin-sv/spv-wallet/models/bsv" "github.com/go-redis/redis/v8" "github.com/go-resty/resty/v2" "github.com/mrz1836/go-cachestore" @@ -59,8 +58,6 @@ func (c *AppConfig) ToEngineOptions(logger zerolog.Logger) (options []engine.Cli return nil, err } - options = c.addFeeQuotes(options) - return options, nil } @@ -72,19 +69,6 @@ func (c *AppConfig) addHttpClientOpts(options []engine.ClientOps) []engine.Clien return append(options, engine.WithHTTPClient(client)) } -func (c *AppConfig) addFeeQuotes(options []engine.ClientOps) []engine.ClientOps { - options = append(options, engine.WithFeeQuotes(c.ARC.UseFeeQuotes)) - - if c.ARC.FeeUnit != nil { - options = append(options, engine.WithFeeUnit(&bsv.FeeUnit{ - Satoshis: c.ARC.FeeUnit.Satoshis, - Bytes: c.ARC.FeeUnit.Bytes, - })) - } - - return options -} - func (c *AppConfig) addUserAgentOpts(options []engine.ClientOps) []engine.ClientOps { return append(options, engine.WithUserAgent(c.GetUserAgent())) } diff --git a/engine/beef_tx_bytes.go b/engine/beef_tx_bytes.go index 76edfbd5..07bdc6e2 100644 --- a/engine/beef_tx_bytes.go +++ b/engine/beef_tx_bytes.go @@ -72,10 +72,11 @@ func toBeefBytes(tx *trx.Transaction, bumps BUMPs) []byte { func getBumpPathIndex(tx *trx.Transaction, bumps BUMPs) int { bumpIndex := -1 + txID := tx.TxID().String() for i, bump := range bumps { for _, path := range bump.Path[0] { - if path.Hash == tx.TxID().String() { + if path.Hash == txID { bumpIndex = i } } diff --git a/engine/script/template/evaluate.go b/engine/script/template/evaluate.go index f999460e..43d55bfc 100644 --- a/engine/script/template/evaluate.go +++ b/engine/script/template/evaluate.go @@ -38,7 +38,10 @@ func Evaluate(scriptBytes []byte, pubKey *ec.PublicKey) ([]byte, error) { dPKBytes := pubKey.SerializeCompressed() // Apply Hash160 (SHA-256 followed by RIPEMD-160) to the compressed public key - dPKHash := utils.Hash160(dPKBytes) + dPKHash, err := utils.Hash160(dPKBytes) + if err != nil { + return nil, spverrors.Wrapf(err, "failed to hash public key") + } // Create a new script with the public key hash newScript := new(script.Script) diff --git a/engine/utils/ripemd160.go b/engine/utils/ripemd160.go index ca64ac70..60580efc 100644 --- a/engine/utils/ripemd160.go +++ b/engine/utils/ripemd160.go @@ -15,14 +15,14 @@ func Sha256(b []byte) []byte { } // Ripemd160 hashes with RIPEMD160 -func Ripemd160(b []byte) []byte { +func Ripemd160(b []byte) ([]byte, error) { ripe := ripemd160.New() - _, _ = ripe.Write(b[:]) - return ripe.Sum(nil) + _, err := ripe.Write(b[:]) + return ripe.Sum(nil), err } // Hash160 hashes with SHA256 and then hashes again with RIPEMD160. -func Hash160(b []byte) []byte { +func Hash160(b []byte) ([]byte, error) { hash := Sha256(b) return Ripemd160(hash[:]) } From 5fcc8b4f8dd7cf2d559de1b05a44a6ddcf11abee Mon Sep 17 00:00:00 2001 From: Wojciech Regulski <48433067+wregulski@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:11:37 +0200 Subject: [PATCH 23/24] Update engine/beef_tx_sorting.go Co-authored-by: chris-4chain <152964795+chris-4chain@users.noreply.github.com> --- engine/beef_tx_sorting.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/beef_tx_sorting.go b/engine/beef_tx_sorting.go index f70e81fa..f32dcc94 100644 --- a/engine/beef_tx_sorting.go +++ b/engine/beef_tx_sorting.go @@ -26,8 +26,9 @@ func prepareSortStructures(dag []*trx.Transaction) (txByID map[string]*trx.Trans incomingEdgesMap = make(map[string]int, dagLen) for _, tx := range dag { - txByID[tx.TxID().String()] = tx // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time - incomingEdgesMap[tx.TxID().String()] = 0 + txID := tx.TxID().String() + txByID[txID] = tx + incomingEdgesMap[txID] = 0 } calculateIncomingEdges(incomingEdgesMap, txByID) From 810dc04728f8da782a7b4647320fc69b49cf75ff Mon Sep 17 00:00:00 2001 From: wregulski Date: Tue, 15 Oct 2024 15:16:22 +0200 Subject: [PATCH 24/24] fix(SPV-898): replacements for better performance --- engine/beef_tx_sorting.go | 6 +++--- engine/utils/merkletree.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/beef_tx_sorting.go b/engine/beef_tx_sorting.go index f32dcc94..dc655acc 100644 --- a/engine/beef_tx_sorting.go +++ b/engine/beef_tx_sorting.go @@ -40,8 +40,8 @@ func prepareSortStructures(dag []*trx.Transaction) (txByID map[string]*trx.Trans func calculateIncomingEdges(inDegree map[string]int, txByID map[string]*trx.Transaction) { for _, tx := range txByID { for _, input := range tx.Inputs { - inputUtxoTxID := input.SourceTXID.String() // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time - if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in + inputUtxoTxID := input.SourceTXID.String() + if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in inDegree[inputUtxoTxID]++ } } @@ -62,7 +62,7 @@ func getTxWithZeroIncomingEdges(incomingEdgesMap map[string]int) []string { func removeTxFromIncomingEdges(tx *trx.Transaction, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) []string { for _, input := range tx.Inputs { - neighborID := input.SourceTXID.String() // TODO: perf -> In GO-SDK, the TxID is calculated every time we try to get it, which means we hash the tx bytes twice each time. It's expensive operation - try to avoid calculation each time + neighborID := input.SourceTXID.String() incomingEdgesMap[neighborID]-- if incomingEdgesMap[neighborID] == 0 { diff --git a/engine/utils/merkletree.go b/engine/utils/merkletree.go index febdfeee..26f9de8e 100644 --- a/engine/utils/merkletree.go +++ b/engine/utils/merkletree.go @@ -8,7 +8,7 @@ import ( "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) -// INFO: This function is moved to go-paymail from go-bc +// INFO: This function is moved to spv-wallet from go-bc // https://github.com/libsv/go-bc/blob/master/merkletreeparent.go // try to use go-sdk implementation when available