Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
chore: bring back tests; add better comment
Browse files Browse the repository at this point in the history
  • Loading branch information
arkadiuszos4chain committed Oct 23, 2023
1 parent 321db32 commit 5788912
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 98 deletions.
1 change: 1 addition & 0 deletions beef_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"fmt"

"github.com/libsv/go-bt/v2"
)

Expand Down
3 changes: 2 additions & 1 deletion beef_tx_bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bux

import (
"errors"

"github.com/libsv/go-bt/v2"
)

Expand Down Expand Up @@ -76,7 +77,7 @@ func getCompountedMarklePathIndex(tx *bt.Tx, compountedPaths CMPSlice) int {

for i, cmp := range compountedPaths {
for txID := range cmp[0] {
if txID == tx.TxID() { // TODO: perf
if txID == tx.TxID() {
pathIdx = i
}
}
Expand Down
6 changes: 3 additions & 3 deletions beef_tx_sorting.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func prepareSortStructures(dag []*bt.Tx) (txByID map[string]*bt.Tx, incomingEdge
incomingEdgesMap = make(map[string]int, dagLen)

for _, tx := range dag {
txByID[tx.TxID()] = tx // TODO: perf
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 calulation each time
incomingEdgesMap[tx.TxID()] = 0
}

Expand All @@ -39,7 +39,7 @@ func prepareSortStructures(dag []*bt.Tx) (txByID map[string]*bt.Tx, incomingEdge
func calculateIncomingEdges(inDegree map[string]int, txByID map[string]*bt.Tx) {
for _, tx := range txByID {
for _, input := range tx.Inputs {
inputUtxoTxID := input.PreviousTxIDStr() // TODO: perf
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 calulation each time
if _, ok := txByID[inputUtxoTxID]; ok { // transaction can contains inputs we are not interested in
inDegree[inputUtxoTxID]++
}
Expand All @@ -61,7 +61,7 @@ func getTxWithZeroIncomingEdges(incomingEdgesMap map[string]int) []string {

func removeTxFromIncomingEdges(tx *bt.Tx, incomingEdgesMap map[string]int, zeroIncomingEdgeQueue []string) []string {
for _, input := range tx.Inputs {
neighborID := input.PreviousTxIDStr() // TODO: perf
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 calulation each time
incomingEdgesMap[neighborID]--

if incomingEdgesMap[neighborID] == 0 {
Expand Down
224 changes: 130 additions & 94 deletions beef_tx_sorting_test.go
Original file line number Diff line number Diff line change
@@ -1,96 +1,132 @@
package bux

//func Test_kahnTopologicalSortTransaction(t *testing.T) {
// // create related transactions from oldest to newest
// txsFromOldestToNewest := []*Transaction{
// createTx("0"),
// createTx("1", "0"),
// createTx("2", "1"),
// createTx("3", "2", "1"),
// createTx("4", "3", "1"),
// createTx("5", "3", "2"),
// createTx("6", "4", "2", "0"),
// createTx("7", "6", "5", "3", "1"),
// createTx("8", "7"),
// }
//
// txsFromOldestToNewestWithUnnecessaryInputs := []*Transaction{
// createTx("0"),
// createTx("1", "0"),
// createTx("2", "1", "101", "102"),
// createTx("3", "2", "1"),
// createTx("4", "3", "1"),
// createTx("5", "3", "2", "100"),
// createTx("6", "4", "2", "0"),
// createTx("7", "6", "5", "3", "1", "103", "105", "106"),
// createTx("8", "7"),
// }
//
// tCases := []struct {
// name string
// expectedSortedTransactions []*Transaction
// }{{
// name: "txs with necessary data only",
// expectedSortedTransactions: txsFromOldestToNewest,
// },
// {
// name: "txs with inputs from other txs",
// expectedSortedTransactions: txsFromOldestToNewestWithUnnecessaryInputs,
// },
// }
//
// for _, tc := range tCases {
// t.Run(fmt.Sprint("sort from oldest to newest ", tc.name), func(t *testing.T) {
// // given
// unsortedTxs := shuffleTransactions(tc.expectedSortedTransactions)
//
// // when
// sortedGraph := kahnTopologicalSortTransactions(unsortedTxs)
//
// // then
// for i, tx := range txsFromOldestToNewest {
// assert.Equal(t, tx.ID, sortedGraph[i].ID)
// }
// })
// }
//}
//
//func createTx(txID string, inputsTxIDs ...string) *Transaction {
// inputs := make([]*TransactionInput, 0)
// for _, inTxID := range inputsTxIDs {
// in := &TransactionInput{
// Utxo: Utxo{
// UtxoPointer: UtxoPointer{
// TransactionID: inTxID,
// },
// },
// }
//
// inputs = append(inputs, in)
// }
//
// transaction := &Transaction{
// draftTransaction: &DraftTransaction{
// Configuration: TransactionConfig{
// Inputs: inputs,
// },
// },
// }
//
// transaction.ID = txID
//
// return transaction
//}
//
//func shuffleTransactions(txs []*Transaction) []*Transaction {
// n := len(txs)
// result := make([]*Transaction, n)
// copy(result, txs)
//
// for i := n - 1; i > 0; i-- {
// j := rand.Intn(i + 1)
// result[i], result[j] = result[j], result[i]
// }
//
// return result
//}
import (
"fmt"
"math/rand"
"testing"

"github.com/libsv/go-bt/v2"
"github.com/stretchr/testify/assert"
)

func Test_kahnTopologicalSortTransaction(t *testing.T) {

tCases := []struct {
name string
expectedSortedTransactions []*bt.Tx
}{
{
name: "txs with necessary data only",
expectedSortedTransactions: getTxsFromOldestToNewestWithNecessaryDataOnly(),
},
{
name: "txs with inputs from other txs",
expectedSortedTransactions: getTxsFromOldestToNewestWithUnecessaryData(),
},
}

for _, tc := range tCases {
t.Run(fmt.Sprint("sort from oldest to newest ", tc.name), func(t *testing.T) {
// given
unsortedTxs := shuffleTransactions(tc.expectedSortedTransactions)

// when
sortedGraph := kahnTopologicalSortTransactions(unsortedTxs)

// then
for i, tx := range tc.expectedSortedTransactions {
assert.Equal(t, tx.TxID(), sortedGraph[i].TxID())
}
})
}
}

func getTxsFromOldestToNewestWithNecessaryDataOnly() []*bt.Tx {
// create related transactions from oldest to newest
oldestTx := createTx()
secondTx := createTx(oldestTx)
thirdTx := createTx(secondTx)
fourthTx := createTx(thirdTx, secondTx)
fifthTx := createTx(fourthTx, secondTx)
sixthTx := createTx(fourthTx, thirdTx)
seventhTx := createTx(fifthTx, thirdTx, oldestTx)
eightTx := createTx(seventhTx, sixthTx, fourthTx, secondTx)

newestTx := createTx(eightTx)

txsFromOldestToNewest := []*bt.Tx{
oldestTx,
secondTx,
thirdTx,
fourthTx,
fifthTx,
sixthTx,
seventhTx,
eightTx,
newestTx,
}

return txsFromOldestToNewest
}

func getTxsFromOldestToNewestWithUnecessaryData() []*bt.Tx {
unnecessaryParentTx_1 := createTx()
unnecessaryParentTx_2 := createTx()
unnecessaryParentTx_3 := createTx()
unnecessaryParentTx_4 := createTx()

// create related transactions from oldest to newest
oldestTx := createTx()
secondTx := createTx(oldestTx)
thirdTx := createTx(secondTx)
fourthTx := createTx(thirdTx, secondTx, unnecessaryParentTx_1, unnecessaryParentTx_4)
fifthTx := createTx(fourthTx, secondTx)
sixthTx := createTx(fourthTx, thirdTx, unnecessaryParentTx_3, unnecessaryParentTx_2, unnecessaryParentTx_1)
seventhTx := createTx(fifthTx, thirdTx, oldestTx)
eightTx := createTx(seventhTx, sixthTx, fourthTx, secondTx, unnecessaryParentTx_1)

newestTx := createTx(eightTx)

txsFromOldestToNewest := []*bt.Tx{
oldestTx,
secondTx,
thirdTx,
fourthTx,
fifthTx,
sixthTx,
seventhTx,
eightTx,
newestTx,
}

return txsFromOldestToNewest
}

func createTx(inputsParents ...*bt.Tx) *bt.Tx {
inputs := make([]*bt.Input, 0)

for _, parent := range inputsParents {
in := bt.Input{}
in.PreviousTxIDAdd(parent.TxIDBytes())

inputs = append(inputs, &in)
}

transaction := bt.NewTx()
transaction.Inputs = append(transaction.Inputs, inputs...)

return transaction
}

func shuffleTransactions(txs []*bt.Tx) []*bt.Tx {
n := len(txs)
result := make([]*bt.Tx, n)
copy(result, txs)

for i := n - 1; i > 0; i-- {
j := rand.Intn(i + 1)
result[i], result[j] = result[j], result[i]
}

return result
}

0 comments on commit 5788912

Please sign in to comment.