diff --git a/internal/node/transactionpool.go b/internal/node/transactionpool.go index c5582a757..5713c1d12 100644 --- a/internal/node/transactionpool.go +++ b/internal/node/transactionpool.go @@ -2,6 +2,7 @@ package node import ( "errors" + "slices" "go.sia.tech/core/types" "go.sia.tech/renterd/bus" @@ -41,7 +42,18 @@ func (tp txpool) AcceptTransactionSet(txns []types.Transaction) error { } func (tp txpool) UnconfirmedParents(txn types.Transaction) ([]types.Transaction, error) { - pool := tp.Transactions() + return unconfirmedParents(txn, tp.Transactions()), nil +} + +func (tp txpool) Subscribe(subscriber modules.TransactionPoolSubscriber) { + tp.tp.TransactionPoolSubscribe(subscriber) +} + +func (tp txpool) Close() error { + return tp.tp.Close() +} + +func unconfirmedParents(txn types.Transaction, pool []types.Transaction) []types.Transaction { outputToParent := make(map[types.SiacoinOutputID]*types.Transaction) for i, txn := range pool { for j := range txn.SiacoinOutputs { @@ -49,24 +61,23 @@ func (tp txpool) UnconfirmedParents(txn types.Transaction) ([]types.Transaction, } } var parents []types.Transaction + txnsToCheck := []*types.Transaction{&txn} seen := make(map[types.TransactionID]bool) - for _, sci := range txn.SiacoinInputs { - if parent, ok := outputToParent[sci.ParentID]; ok { - if txid := parent.ID(); !seen[txid] { - seen[txid] = true - parents = append(parents, *parent) + for len(txnsToCheck) > 0 { + nextTxn := txnsToCheck[0] + txnsToCheck = txnsToCheck[1:] + for _, sci := range nextTxn.SiacoinInputs { + if parent, ok := outputToParent[sci.ParentID]; ok { + if txid := parent.ID(); !seen[txid] { + seen[txid] = true + parents = append(parents, *parent) + txnsToCheck = append(txnsToCheck, parent) + } } } } - return parents, nil -} - -func (tp txpool) Subscribe(subscriber modules.TransactionPoolSubscriber) { - tp.tp.TransactionPoolSubscribe(subscriber) -} - -func (tp txpool) Close() error { - return tp.tp.Close() + slices.Reverse(parents) + return parents } func NewTransactionPool(tp modules.TransactionPool) bus.TransactionPool { diff --git a/internal/node/transactionpool_test.go b/internal/node/transactionpool_test.go new file mode 100644 index 000000000..c24e2c190 --- /dev/null +++ b/internal/node/transactionpool_test.go @@ -0,0 +1,40 @@ +package node + +import ( + "reflect" + "testing" + + "go.sia.tech/core/types" +) + +func TestUnconfirmedParents(t *testing.T) { + grandparent := types.Transaction{ + SiacoinOutputs: []types.SiacoinOutput{{}}, + } + parent := types.Transaction{ + SiacoinInputs: []types.SiacoinInput{ + { + ParentID: grandparent.SiacoinOutputID(0), + }, + }, + SiacoinOutputs: []types.SiacoinOutput{{}}, + } + txn := types.Transaction{ + SiacoinInputs: []types.SiacoinInput{ + { + ParentID: parent.SiacoinOutputID(0), + }, + }, + SiacoinOutputs: []types.SiacoinOutput{{}}, + } + pool := []types.Transaction{grandparent, parent} + + parents := unconfirmedParents(txn, pool) + if len(parents) != 2 { + t.Fatalf("expected 2 parents, got %v", len(parents)) + } else if !reflect.DeepEqual(parents[0], grandparent) { + t.Fatalf("expected grandparent") + } else if !reflect.DeepEqual(parents[1], parent) { + t.Fatalf("expected parent") + } +}