diff --git a/wallet/wallet.go b/wallet/wallet.go index 1624911..8a39f48 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -400,11 +400,14 @@ func (sw *SingleAddressWallet) SignTransaction(txn *types.Transaction, toSign [] } } -// FundTransactionV2 adds siacoin inputs worth at least amount to the provided +// FundV2Transaction adds siacoin inputs worth at least amount to the provided // transaction. If necessary, a change output will also be added. The inputs // will not be available to future calls to FundTransaction unless ReleaseInputs // is called. -func (sw *SingleAddressWallet) FundTransactionV2(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (consensus.State, []types.Hash256, error) { +// +// The returned consensus state should be used to calculate the input signature +// hash and as the basis for AddV2PoolTransactions. +func (sw *SingleAddressWallet) FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (consensus.State, []int, error) { sw.mu.Lock() defer sw.mu.Unlock() @@ -421,38 +424,33 @@ func (sw *SingleAddressWallet) FundTransactionV2(txn *types.V2Transaction, amoun }) } - toSign := make([]types.Hash256, len(selected)) - for i, sce := range selected { + toSign := make([]int, 0, len(selected)) + for _, sce := range selected { + toSign = append(toSign, len(txn.SiacoinInputs)) txn.SiacoinInputs = append(txn.SiacoinInputs, types.V2SiacoinInput{ Parent: sce, }) - toSign[i] = types.Hash256(sce.ID) sw.locked[sce.ID] = time.Now().Add(sw.cfg.ReservationDuration) } return sw.cm.TipState(), toSign, nil } -// SignTransactionV2 adds a signature to each of the specified inputs. -func (sw *SingleAddressWallet) SignTransactionV2(state consensus.State, txn *types.V2Transaction, toSign []types.Hash256) { - sw.mu.Lock() - defer sw.mu.Unlock() - - signMap := make(map[types.Hash256]bool) - for _, id := range toSign { - signMap[id] = true +// SignV2Inputs adds a signature to each of the specified siacoin inputs. +func (sw *SingleAddressWallet) SignV2Inputs(state consensus.State, txn *types.V2Transaction, toSign []int) { + if len(toSign) == 0 { + return } - policy := types.SpendPolicy{Type: types.PolicyTypeUnlockConditions(types.StandardUnlockConditions(sw.priv.PublicKey()))} - for i, se := range txn.SiacoinInputs { - if !signMap[types.Hash256(se.Parent.ID)] { - continue - } + sw.mu.Lock() + defer sw.mu.Unlock() - sigHash := state.InputSigHash(*txn) + policy := sw.SpendPolicy() + sigHash := state.InputSigHash(*txn) + for _, i := range toSign { txn.SiacoinInputs[i].SatisfiedPolicy = types.SatisfiedPolicy{ Policy: policy, - Signatures: []types.Signature{sw.priv.SignHash(sigHash)}, + Signatures: []types.Signature{sw.SignHash(sigHash)}, } } } @@ -462,6 +460,16 @@ func (sw *SingleAddressWallet) Tip() (types.ChainIndex, error) { return sw.store.Tip() } +// SpendPolicy returns the wallet's default spend policy. +func (sw *SingleAddressWallet) SpendPolicy() types.SpendPolicy { + return types.SpendPolicy{Type: types.PolicyTypeUnlockConditions(sw.UnlockConditions())} +} + +// SignHash signs the hash with the wallet's private key. +func (sw *SingleAddressWallet) SignHash(h types.Hash256) types.Signature { + return sw.priv.SignHash(h) +} + // UnconfirmedTransactions returns all unconfirmed transactions relevant to the // wallet. func (sw *SingleAddressWallet) UnconfirmedTransactions() (annotated []Event, err error) { diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index cfd7a81..6f15d00 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -884,11 +884,11 @@ func TestWalletV2(t *testing.T) { } // fund and sign the transaction - state, toSign, err := w.FundTransactionV2(&v2Txn, types.Siacoins(100), false) + state, toSignV2, err := w.FundV2Transaction(&v2Txn, types.Siacoins(100), false) if err != nil { t.Fatal(err) } - w.SignTransactionV2(state, &v2Txn, toSign) + w.SignV2Inputs(state, &v2Txn, toSignV2) // add the transaction to the pool if _, err := cm.AddV2PoolTransactions(state.Index, []types.V2Transaction{v2Txn}); err != nil { @@ -992,7 +992,7 @@ func TestReorgV2(t *testing.T) { } // try funding the transaction, expect it to fail since the outputs are immature - _, _, err = w.FundTransactionV2(&txn, initialReward, false) + _, _, err = w.FundV2Transaction(&txn, initialReward, false) if !errors.Is(err, wallet.ErrNotEnoughFunds) { t.Fatal("expected ErrNotEnoughFunds, got", err) } @@ -1024,11 +1024,11 @@ func TestReorgV2(t *testing.T) { } // fund and sign the transaction - state, toSign, err := w.FundTransactionV2(&txn, initialReward, false) + state, toSign, err := w.FundV2Transaction(&txn, initialReward, false) if err != nil { t.Fatal(err) } - w.SignTransactionV2(state, &txn, toSign) + w.SignV2Inputs(state, &txn, toSign) // check that wallet now has no spendable balance assertBalance(t, w, types.ZeroCurrency, initialReward, types.ZeroCurrency, types.ZeroCurrency) @@ -1100,11 +1100,11 @@ func TestReorgV2(t *testing.T) { {Address: types.VoidAddress, Value: initialReward}, }, } - state, toSign, err = w.FundTransactionV2(&txn2, initialReward, false) + state, toSign, err = w.FundV2Transaction(&txn2, initialReward, false) if err != nil { t.Fatal(err) } - w.SignTransactionV2(state, &txn2, toSign) + w.SignV2Inputs(state, &txn2, toSign) // release the inputs to construct a double spend w.ReleaseInputs(nil, []types.V2Transaction{txn2}) @@ -1114,11 +1114,11 @@ func TestReorgV2(t *testing.T) { {Address: types.VoidAddress, Value: initialReward.Div64(2)}, }, } - state, toSign, err = w.FundTransactionV2(&txn1, initialReward.Div64(2), false) + state, toSign, err = w.FundV2Transaction(&txn1, initialReward.Div64(2), false) if err != nil { t.Fatal(err) } - w.SignTransactionV2(state, &txn1, toSign) + w.SignV2Inputs(state, &txn1, toSign) // add the first transaction to the pool if _, err := cm.AddV2PoolTransactions(state.Index, []types.V2Transaction{txn1}); err != nil {