diff --git a/api/api.go b/api/api.go index 91ae59f6..a64aedce 100644 --- a/api/api.go +++ b/api/api.go @@ -41,8 +41,8 @@ type ( SignTransaction(txn *types.Transaction, toSign []types.Hash256, cf types.CoveredFields) // v2 - FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (consensus.State, []int, error) - SignV2Inputs(state consensus.State, txn *types.V2Transaction, toSign []int) + FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (types.ChainIndex, []int, error) + SignV2Inputs(txn *types.V2Transaction, toSign []int) } // Settings updates and retrieves the host's settings @@ -134,7 +134,7 @@ type ( AddPoolTransactions(txns []types.Transaction) (known bool, err error) UnconfirmedParents(txn types.Transaction) []types.Transaction AddV2PoolTransactions(basis types.ChainIndex, txns []types.V2Transaction) (known bool, err error) - V2UnconfirmedParents(txn types.V2Transaction) []types.V2Transaction + V2TransactionSet(basis types.ChainIndex, txn types.V2Transaction) (types.ChainIndex, []types.V2Transaction, error) } // Webhooks manages webhooks diff --git a/api/endpoints.go b/api/endpoints.go index c8db41ee..a0e52b6a 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -469,19 +469,23 @@ func (a *api) handlePOSTWalletSend(jc jape.Context) { }, } // fund and sign transaction - state, toSign, err := a.wallet.FundV2Transaction(&txn, req.Amount.Add(minerFee), false) + basis, toSign, err := a.wallet.FundV2Transaction(&txn, req.Amount.Add(minerFee), false) if !a.checkServerError(jc, "failed to fund transaction", err) { return } - a.wallet.SignV2Inputs(state, &txn, toSign) - txnset := append(a.chain.V2UnconfirmedParents(txn), txn) + a.wallet.SignV2Inputs(&txn, toSign) + basis, txnset, err := a.chain.V2TransactionSet(basis, txn) + if !a.checkServerError(jc, "failed to create transaction set", err) { + a.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) + return + } // verify the transaction and add it to the transaction pool - if _, err := a.chain.AddV2PoolTransactions(state.Index, txnset); !a.checkServerError(jc, "failed to add v2 transaction set", err) { + if _, err := a.chain.AddV2PoolTransactions(basis, txnset); !a.checkServerError(jc, "failed to add v2 transaction set", err) { a.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) return } // broadcast the transaction - a.syncer.BroadcastV2TransactionSet(state.Index, txnset) + a.syncer.BroadcastV2TransactionSet(basis, txnset) jc.Encode(txn.ID()) } } diff --git a/host/contracts/manager.go b/host/contracts/manager.go index ce19bd07..cedb2747 100644 --- a/host/contracts/manager.go +++ b/host/contracts/manager.go @@ -43,8 +43,8 @@ type ( FundTransaction(txn *types.Transaction, amount types.Currency, useUnconfirmed bool) ([]types.Hash256, error) SignTransaction(txn *types.Transaction, toSign []types.Hash256, cf types.CoveredFields) - FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (consensus.State, []int, error) - SignV2Inputs(state consensus.State, txn *types.V2Transaction, toSign []int) + FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (types.ChainIndex, []int, error) + SignV2Inputs(txn *types.V2Transaction, toSign []int) } // A StorageManager stores and retrieves sectors. diff --git a/host/contracts/manager_test.go b/host/contracts/manager_test.go index 08fa600f..47c6c65c 100644 --- a/host/contracts/manager_test.go +++ b/host/contracts/manager_test.go @@ -60,14 +60,14 @@ func formV2Contract(t *testing.T, cm *chain.Manager, c *contracts.Manager, w *wa FileContracts: []types.V2FileContract{fc}, } - cs, toSign, err := w.FundV2Transaction(&txn, fundAmount, false) + basis, toSign, err := w.FundV2Transaction(&txn, fundAmount, false) if err != nil { t.Fatal("failed to fund transaction:", err) } - w.SignV2Inputs(cs, &txn, toSign) + w.SignV2Inputs(&txn, toSign) formationSet := contracts.V2FormationTransactionSet{ TransactionSet: []types.V2Transaction{txn}, - Basis: cs.Index, + Basis: basis, } if broadcast { @@ -1049,11 +1049,11 @@ func TestV2ContractLifecycle(t *testing.T) { {Value: fundAmount, Address: fc.HostOutput.Address}, }, } - cs, toSign, err := node.Wallet.FundV2Transaction(&setupTxn, fundAmount, false) + basis, toSign, err := node.Wallet.FundV2Transaction(&setupTxn, fundAmount, false) if err != nil { t.Fatal("failed to fund transaction:", err) } - node.Wallet.SignV2Inputs(cs, &setupTxn, toSign) + node.Wallet.SignV2Inputs(&setupTxn, toSign) renewalTxn := types.V2Transaction{ SiacoinInputs: []types.V2SiacoinInput{ @@ -1068,9 +1068,9 @@ func TestV2ContractLifecycle(t *testing.T) { }, }, } - node.Wallet.SignV2Inputs(cs, &renewalTxn, []int{0}) + node.Wallet.SignV2Inputs(&renewalTxn, []int{0}) renewalTxnSet := contracts.V2FormationTransactionSet{ - Basis: cs.Index, + Basis: basis, TransactionSet: []types.V2Transaction{setupTxn, renewalTxn}, } if _, err := cm.AddV2PoolTransactions(renewalTxnSet.Basis, renewalTxnSet.TransactionSet); err != nil { @@ -1149,14 +1149,14 @@ func TestV2ContractLifecycle(t *testing.T) { FileContracts: []types.V2FileContract{fc}, } - cs, toSign, err := w.FundV2Transaction(&txn, fundAmount, false) + basis, toSign, err := w.FundV2Transaction(&txn, fundAmount, false) if err != nil { t.Fatal("failed to fund transaction:", err) } - w.SignV2Inputs(cs, &txn, toSign) + w.SignV2Inputs(&txn, toSign) formationSet := contracts.V2FormationTransactionSet{ TransactionSet: []types.V2Transaction{txn}, - Basis: cs.Index, + Basis: basis, } contractID := txn.V2FileContractID(txn.ID(), 0) // corrupt the formation set to trigger a rejection diff --git a/host/contracts/update.go b/host/contracts/update.go index 1ef1bbfa..49f5d9ba 100644 --- a/host/contracts/update.go +++ b/host/contracts/update.go @@ -324,19 +324,19 @@ func (cm *Manager) ProcessActions(index types.ChainIndex) error { MinerFee: fee, FileContractRevisions: []types.V2FileContractRevision{fcr}, } - cs, toSign, err := cm.wallet.FundV2Transaction(&revisionTxn, fee, false) // TODO: true + basis, toSign, err := cm.wallet.FundV2Transaction(&revisionTxn, fee, false) // TODO: true if err != nil { log.Error("failed to fund transaction", zap.Error(err)) continue } - cm.wallet.SignV2Inputs(cs, &revisionTxn, toSign) + cm.wallet.SignV2Inputs(&revisionTxn, toSign) revisionTxnSet := []types.V2Transaction{revisionTxn} - if _, err := cm.chain.AddV2PoolTransactions(cs.Index, revisionTxnSet); err != nil { + if _, err := cm.chain.AddV2PoolTransactions(basis, revisionTxnSet); err != nil { log.Error("failed to add transaction set to pool", zap.Error(err)) continue } - cm.syncer.BroadcastV2TransactionSet(cs.Index, revisionTxnSet) + cm.syncer.BroadcastV2TransactionSet(basis, revisionTxnSet) log.Debug("broadcast transaction", zap.Stringer("transactionID", revisionTxn.ID())) } @@ -370,24 +370,24 @@ func (cm *Manager) ProcessActions(index types.ChainIndex) error { {Address: cm.wallet.Address(), Value: fee}, }, } - cs, toSign, err := cm.wallet.FundV2Transaction(&setupTxn, fee, false) // TODO: true + basis, toSign, err := cm.wallet.FundV2Transaction(&setupTxn, fee, false) // TODO: true if err != nil { log.Error("failed to fund resolution transaction", zap.Error(err)) continue } - cm.wallet.SignV2Inputs(cs, &setupTxn, toSign) + cm.wallet.SignV2Inputs(&setupTxn, toSign) resolutionTxn := types.V2Transaction{ MinerFee: fee, SiacoinInputs: []types.V2SiacoinInput{{Parent: setupTxn.EphemeralSiacoinOutput(0)}}, FileContractResolutions: []types.V2FileContractResolution{resolution}, } - cm.wallet.SignV2Inputs(cs, &resolutionTxn, []int{0}) + cm.wallet.SignV2Inputs(&resolutionTxn, []int{0}) resolutionTxnSet := []types.V2Transaction{setupTxn, resolutionTxn} - if _, err := cm.chain.AddV2PoolTransactions(cs.Index, resolutionTxnSet); err != nil { + if _, err := cm.chain.AddV2PoolTransactions(basis, resolutionTxnSet); err != nil { log.Error("failed to add resolution transaction to pool", zap.Error(err)) continue } - cm.syncer.BroadcastV2TransactionSet(cs.Index, resolutionTxnSet) + cm.syncer.BroadcastV2TransactionSet(basis, resolutionTxnSet) log.Debug("broadcast transaction", zap.String("transactionID", resolutionTxn.ID().String())) } @@ -400,12 +400,12 @@ func (cm *Manager) ProcessActions(index types.ChainIndex) error { {Address: cm.wallet.Address(), Value: fee}, }, } - cs, toSign, err := cm.wallet.FundV2Transaction(&setupTxn, fee, false) // TODO: true + basis, toSign, err := cm.wallet.FundV2Transaction(&setupTxn, fee, false) // TODO: true if err != nil { log.Error("failed to fund resolution transaction", zap.Error(err)) continue } - cm.wallet.SignV2Inputs(cs, &setupTxn, toSign) + cm.wallet.SignV2Inputs(&setupTxn, toSign) resolutionTxn := types.V2Transaction{ MinerFee: fee, SiacoinInputs: []types.V2SiacoinInput{ @@ -420,15 +420,15 @@ func (cm *Manager) ProcessActions(index types.ChainIndex) error { }, }, } - cm.wallet.SignV2Inputs(cs, &resolutionTxn, []int{0}) + cm.wallet.SignV2Inputs(&resolutionTxn, []int{0}) resolutionTxnSet := []types.V2Transaction{setupTxn, resolutionTxn} - if _, err := cm.chain.AddV2PoolTransactions(cs.Index, resolutionTxnSet); err != nil { + if _, err := cm.chain.AddV2PoolTransactions(basis, resolutionTxnSet); err != nil { cm.wallet.ReleaseInputs(nil, resolutionTxnSet) log.Error("failed to add resolution transaction to pool", zap.Error(err)) continue } - cm.syncer.BroadcastV2TransactionSet(cs.Index, resolutionTxnSet) + cm.syncer.BroadcastV2TransactionSet(basis, resolutionTxnSet) log.Debug("broadcast transaction", zap.String("transactionID", resolutionTxn.ID().String())) } diff --git a/host/settings/announce.go b/host/settings/announce.go index c8e85410..9d7f5041 100644 --- a/host/settings/announce.go +++ b/host/settings/announce.go @@ -71,9 +71,12 @@ func (m *ConfigManager) Announce() error { if err != nil { return fmt.Errorf("failed to fund transaction: %w", err) } - m.wallet.SignV2Inputs(basis, &txn, toSign) - txnset := append(m.chain.V2UnconfirmedParents(txn), txn) - if _, err := m.chain.AddV2PoolTransactions(cs.Index, txnset); err != nil { + m.wallet.SignV2Inputs(&txn, toSign) + basis, txnset, err := m.chain.V2TransactionSet(basis, txn) + if err != nil { + m.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) + return fmt.Errorf("failed to create transaction set: %w", err) + } else if _, err := m.chain.AddV2PoolTransactions(basis, txnset); err != nil { m.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) return fmt.Errorf("failed to add transaction to pool: %w", err) } diff --git a/host/settings/settings.go b/host/settings/settings.go index 7e8188b4..8e957a9c 100644 --- a/host/settings/settings.go +++ b/host/settings/settings.go @@ -52,7 +52,7 @@ type ( UnconfirmedParents(txn types.Transaction) []types.Transaction AddPoolTransactions([]types.Transaction) (known bool, err error) - V2UnconfirmedParents(txn types.V2Transaction) []types.V2Transaction + V2TransactionSet(types.ChainIndex, types.V2Transaction) (types.ChainIndex, []types.V2Transaction, error) AddV2PoolTransactions(types.ChainIndex, []types.V2Transaction) (known bool, err error) } @@ -69,8 +69,8 @@ type ( FundTransaction(txn *types.Transaction, amount types.Currency, useUnconfirmed bool) ([]types.Hash256, error) SignTransaction(txn *types.Transaction, toSign []types.Hash256, cf types.CoveredFields) - FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (consensus.State, []int, error) - SignV2Inputs(state consensus.State, txn *types.V2Transaction, toSign []int) + FundV2Transaction(txn *types.V2Transaction, amount types.Currency, useUnconfirmed bool) (types.ChainIndex, []int, error) + SignV2Inputs(txn *types.V2Transaction, toSign []int) } // Alerts registers global alerts.