From 15c7f181b5d4f513ed7720beecae23cc97b9c403 Mon Sep 17 00:00:00 2001 From: PJ Date: Wed, 21 Feb 2024 16:13:20 +0100 Subject: [PATCH 1/2] wallet: do not use immature outputs to fund a txn with --- wallet/wallet.go | 11 +++++++++++ wallet/wallet_test.go | 32 +++++++++++++++++++------------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index f5ab36d..70195b8 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -274,6 +274,17 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty return nil, err } + cs := sw.cm.TipState() + + // filter out immature elements + filtered := elements[:0] + for _, sce := range elements { + if cs.Index.Height >= sce.MaturityHeight { + filtered = append(filtered, sce) + } + } + elements = filtered + tpoolSpent := make(map[types.Hash256]bool) tpoolUtxos := make(map[types.Hash256]types.SiacoinElement) for _, txn := range sw.cm.PoolTransactions() { diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index d2ecf5e..6c1ef6b 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -79,10 +79,27 @@ func TestWallet(t *testing.T) { } // check that the wallet has an immature balance - if checkBalance(w, types.ZeroCurrency, types.ZeroCurrency, initialReward, types.ZeroCurrency); err != nil { + if err := checkBalance(w, types.ZeroCurrency, types.ZeroCurrency, initialReward, types.ZeroCurrency); err != nil { t.Fatal(err) } + // create a transaction that splits the wallet's balance into 20 outputs + txn := types.Transaction{ + SiacoinOutputs: make([]types.SiacoinOutput, 20), + } + for i := range txn.SiacoinOutputs { + txn.SiacoinOutputs[i] = types.SiacoinOutput{ + Value: initialReward.Div64(20), + Address: w.Address(), + } + } + + // try funding the transaction, expect it to fail since the outputs are immature + _, err = w.FundTransaction(&txn, initialReward, false) + if err != wallet.ErrNotEnoughFunds { + t.Fatal("expected ErrNotEnoughFunds, got", err) + } + // mine until the payout matures tip := cm.TipState() target := tip.MaturityHeight() + 1 @@ -94,7 +111,7 @@ func TestWallet(t *testing.T) { } // check that one payout has matured - if checkBalance(w, initialReward, initialReward, types.ZeroCurrency, types.ZeroCurrency); err != nil { + if err := checkBalance(w, initialReward, initialReward, types.ZeroCurrency, types.ZeroCurrency); err != nil { t.Fatal(err) } @@ -116,17 +133,6 @@ func TestWallet(t *testing.T) { t.Fatalf("expected miner payout, got %v", events[0].Source) } - // split the wallet's balance into 20 outputs - txn := types.Transaction{ - SiacoinOutputs: make([]types.SiacoinOutput, 20), - } - for i := range txn.SiacoinOutputs { - txn.SiacoinOutputs[i] = types.SiacoinOutput{ - Value: initialReward.Div64(20), - Address: w.Address(), - } - } - // fund and sign the transaction toSign, err := w.FundTransaction(&txn, initialReward, false) if err != nil { From 28f4ee40fe9430d50ec95088543e8bd481b23ab8 Mon Sep 17 00:00:00 2001 From: PJ Date: Wed, 21 Feb 2024 18:09:08 +0100 Subject: [PATCH 2/2] wallet: add maturity check to loop --- wallet/wallet.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/wallet/wallet.go b/wallet/wallet.go index 70195b8..8c1d21f 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -274,17 +274,6 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty return nil, err } - cs := sw.cm.TipState() - - // filter out immature elements - filtered := elements[:0] - for _, sce := range elements { - if cs.Index.Height >= sce.MaturityHeight { - filtered = append(filtered, sce) - } - } - elements = filtered - tpoolSpent := make(map[types.Hash256]bool) tpoolUtxos := make(map[types.Hash256]types.SiacoinElement) for _, txn := range sw.cm.PoolTransactions() { @@ -305,10 +294,11 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty sw.mu.Lock() defer sw.mu.Unlock() - // remove locked and spent outputs + // remove immature, locked and spent outputs + cs := sw.cm.TipState() utxos := make([]types.SiacoinElement, 0, len(elements)) for _, sce := range elements { - if time.Now().Before(sw.locked[sce.ID]) || tpoolSpent[sce.ID] { + if time.Now().Before(sw.locked[sce.ID]) || tpoolSpent[sce.ID] || cs.Index.Height < sce.MaturityHeight { continue } utxos = append(utxos, sce.SiacoinElement)