Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(SPV 1112): replace toBeefBytes with sdk BEEFHex #760

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions conv/convert_primitives.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ func Int64ToUint32(value int64) (uint32, error) {
return uint32(value), nil
}

// Uint64ToUint32 will convert a uint64 to a uint32
func Uint64ToUint32(value uint64) (uint32, error) {
if value > math.MaxUint32 {
return 0, spverrors.ErrInvalidUint32
}
return uint32(value), nil
}

// Uint32ToInt64 will convert a uint32 to an int64 (safe as uint32 fits into int64)
func Uint32ToInt64(value uint32) int64 {
return int64(value)
Expand Down
48 changes: 0 additions & 48 deletions engine/beef_bump.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,11 @@ package engine

import (
"context"
"sort"

trx "github.com/bitcoin-sv/go-sdk/transaction"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

func calculateMergedBUMP(txs []*Transaction) (BUMPs, error) {
bumps := make(map[uint64][]BUMP)
mergedBUMPs := make(BUMPs, 0)

for _, tx := range txs {
if tx.BUMP.BlockHeight == 0 || len(tx.BUMP.Path) == 0 {
continue
}

bumps[tx.BlockHeight] = append(bumps[tx.BlockHeight], tx.BUMP)
}

// ensure that BUMPs are sorted by block height and will always be put in beef in the same order
mapKeys := make([]uint64, 0, len(bumps))
for k := range bumps {
mapKeys = append(mapKeys, k)
}
sort.Slice(mapKeys, func(i, j int) bool { return mapKeys[i] < mapKeys[j] })

for _, k := range mapKeys {
bump, err := CalculateMergedBUMP(bumps[k])
if err != nil {
return nil, spverrors.Wrapf(err, "failed to calculate merged BUMP")
}
if bump == nil {
continue
}
mergedBUMPs = append(mergedBUMPs, bump)
}

return mergedBUMPs, nil
}

func validateBumps(bumps BUMPs) error {
if len(bumps) == 0 {
return spverrors.Newf("empty bump paths slice")
}

for _, p := range bumps {
if len(p.Path) == 0 {
return spverrors.Newf("one of bump path is empty")
}
}

return nil
}

func prepareBEEFFactors(ctx context.Context, tx *Transaction, store TransactionGetter) ([]*trx.Transaction, []*Transaction, error) {
sdkTxsNeededForBUMP, txsNeededForBUMP, err := initializeRequiredTxsCollection(tx)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions engine/beef_fixtures.go

Large diffs are not rendered by default.

106 changes: 60 additions & 46 deletions engine/beef_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ package engine

import (
"context"
"encoding/hex"

chainhash "github.com/bitcoin-sv/go-sdk/chainhash"
trx "github.com/bitcoin-sv/go-sdk/transaction"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

const maxBeefVer = uint32(0xFFFF) // value from BRC-62

type beefTx struct {
version uint32
bumps BUMPs
transactions []*trx.Transaction
}

// ToBeef generates BEEF Hex for transaction
func ToBeef(ctx context.Context, tx *Transaction, store TransactionGetter) (string, error) {
if err := hydrateTransaction(ctx, tx); err != nil {
Expand All @@ -27,49 +19,18 @@ func ToBeef(ctx context.Context, tx *Transaction, store TransactionGetter) (stri
return "", spverrors.Wrapf(err, "prepareBUMPFactors() error")
}

bumps, err := calculateMergedBUMP(bumpFactors)
if err != nil {
return "", err
}
sortedTxs := kahnTopologicalSortTransactions(bumpBtFactors)
beefHex, err := toBeefHex(bumps, sortedTxs)
if err != nil {
return "", spverrors.Wrapf(err, "ToBeef() error")
}

return beefHex, nil
}

func toBeefHex(bumps BUMPs, parentTxs []*trx.Transaction) (string, error) {
beef, err := newBeefTx(1, bumps, parentTxs)
err = setMerklePathsFromBUMPs(bumpBtFactors, bumpFactors)
if err != nil {
return "", spverrors.Wrapf(err, "ToBeefHex() error")
return "", spverrors.Wrapf(err, "SetMerklePathsFromBUMPs() error")
}
populateSourceTransactions(bumpBtFactors)

beefBytes, err := beef.toBeefBytes()
trxHex, err := bumpBtFactors[0].BEEFHex()
if err != nil {
return "", spverrors.Wrapf(err, "ToBeefHex() error")
}

return hex.EncodeToString(beefBytes), nil
}

func newBeefTx(version uint32, bumps BUMPs, parentTxs []*trx.Transaction) (*beefTx, error) {
if version > maxBeefVer {
return nil, spverrors.Newf("version above 0x%X", maxBeefVer)
}

if err := validateBumps(bumps); err != nil {
return nil, err
}

beef := &beefTx{
version: version,
bumps: bumps,
transactions: parentTxs,
return "", spverrors.Wrapf(err, "BEEFHex() error")
}

return beef, nil
return trxHex, nil
}

func hydrateTransaction(ctx context.Context, tx *Transaction) error {
Expand All @@ -87,3 +48,56 @@ func hydrateTransaction(ctx context.Context, tx *Transaction) error {

return nil
}

func setMerklePathsFromBUMPs(bumpBtFactors []*trx.Transaction, bumpFactors []*Transaction) error {
for i := range bumpBtFactors {
if bumpFactors[i].BUMP.BlockHeight != 0 {
merklePath, err := buildMerklePathFromBUMP(&bumpFactors[i].BUMP)
if err != nil {
return err
}
bumpBtFactors[i].MerklePath = merklePath
}
}
return nil
}

func populateSourceTransactions(transactions []*trx.Transaction) {
transactionMap := make(map[chainhash.Hash]*trx.Transaction)
for _, tx := range transactions {
txID := *tx.TxID()
transactionMap[txID] = tx
}

visited := make(map[chainhash.Hash]bool)
stack := make([]*trx.Transaction, 0, len(transactions))

for _, tx := range transactions {
txID := *tx.TxID()
if visited[txID] {
continue
}
stack = append(stack, tx)

for len(stack) > 0 {
currentTx := stack[len(stack)-1]
stack = stack[:len(stack)-1]
currentTxID := *currentTx.TxID()

if visited[currentTxID] {
continue
}
visited[currentTxID] = true

for _, input := range currentTx.Inputs {
if input.SourceTXID != nil {
sourceTxID := *input.SourceTXID
if sourceTransaction, exists := transactionMap[sourceTxID]; exists {
input.SourceTransaction = sourceTransaction
stack = append(stack, sourceTransaction)
}
}
}
}
}
}
91 changes: 0 additions & 91 deletions engine/beef_tx_bytes.go

This file was deleted.

80 changes: 0 additions & 80 deletions engine/beef_tx_sorting.go

This file was deleted.

Loading
Loading