Skip to content

Commit

Permalink
Merge pull request #149 from SiaFoundation/nate/server-release-utxos
Browse files Browse the repository at this point in the history
Release host utxos when formation, refresh, or renew fails
  • Loading branch information
n8maninger authored Jan 12, 2025
2 parents edda581 + b6af4d2 commit 0d63b63
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

# Release locked host UTXOs if contract formation, renewal, or refresh fails
38 changes: 33 additions & 5 deletions rhp/v4/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,13 +566,21 @@ func (s *Server) handleRPCFormContract(stream net.Conn) error {
})
}

var broadcast bool
// fund the host collateral
basis, toSign, err := s.wallet.FundV2Transaction(&formationTxn, hostCost, true)
if errors.Is(err, wallet.ErrNotEnoughFunds) {
return rhp4.ErrHostFundError
} else if err != nil {
return fmt.Errorf("failed to fund transaction: %w", err)
}
defer func() {
if broadcast {
return
}
// release the inputs if the transaction is not going to be broadcast
s.wallet.ReleaseInputs(nil, []types.V2Transaction{formationTxn})
}()
// sign the transaction inputs
s.wallet.SignV2Inputs(&formationTxn, toSign)
// send the host inputs to the renter
Expand Down Expand Up @@ -633,7 +641,6 @@ func (s *Server) handleRPCFormContract(stream net.Conn) error {
} else if _, err = s.chain.AddV2PoolTransactions(basis, formationSet); err != nil {
return errorBadRequest("failed to broadcast formation transaction: %v", err)
}
s.syncer.BroadcastV2TransactionSet(basis, formationSet)

// add the contract to the contractor
err = s.contractor.AddV2Contract(TransactionSet{
Expand All @@ -643,6 +650,9 @@ func (s *Server) handleRPCFormContract(stream net.Conn) error {
if err != nil {
return fmt.Errorf("failed to add contract: %w", err)
}
// broadcast the finalized contract formation set
broadcast = true // set broadcast so the UTXOs will not be released if the renter happens to disconnect before receiving the last response
s.syncer.BroadcastV2TransactionSet(basis, formationSet)

// send the finalized transaction set to the renter
return rhp4.WriteResponse(stream, &rhp4.RPCFormContractThirdResponse{
Expand Down Expand Up @@ -713,12 +723,20 @@ func (s *Server) handleRPCRefreshContract(stream net.Conn) error {
return fmt.Errorf("failed to get contract element: %w", err)
}

var broadcast bool
basis, toSign, err := s.wallet.FundV2Transaction(&renewalTxn, hostCost, true)
if errors.Is(err, wallet.ErrNotEnoughFunds) {
return rhp4.ErrHostFundError
} else if err != nil {
return fmt.Errorf("failed to fund transaction: %w", err)
}
defer func() {
if broadcast {
return
}
// release the locked UTXOs if the transaction is not going to be broadcast
s.wallet.ReleaseInputs(nil, []types.V2Transaction{renewalTxn})
}()

// update renter inputs to reflect our chain state
if basis != req.Basis {
Expand Down Expand Up @@ -799,8 +817,6 @@ func (s *Server) handleRPCRefreshContract(stream net.Conn) error {
} else if _, err = s.chain.AddV2PoolTransactions(basis, renewalSet); err != nil {
return errorBadRequest("failed to broadcast renewal set: %v", err)
}
// broadcast the transaction set
s.syncer.BroadcastV2TransactionSet(basis, renewalSet)

// add the contract to the contractor
err = s.contractor.RenewV2Contract(TransactionSet{
Expand All @@ -811,6 +827,9 @@ func (s *Server) handleRPCRefreshContract(stream net.Conn) error {
return fmt.Errorf("failed to add contract: %w", err)
}

broadcast = true // set broadcast so the UTXOs will not be released if the renter happens to disconnect before receiving the last response
s.syncer.BroadcastV2TransactionSet(basis, renewalSet)

// send the finalized transaction set to the renter
return rhp4.WriteResponse(stream, &rhp4.RPCRefreshContractThirdResponse{
Basis: basis,
Expand Down Expand Up @@ -882,12 +901,20 @@ func (s *Server) handleRPCRenewContract(stream net.Conn) error {
return fmt.Errorf("failed to get contract element: %w", err)
}

var broadcast bool
basis, toSign, err := s.wallet.FundV2Transaction(&renewalTxn, hostCost, true)
if errors.Is(err, wallet.ErrNotEnoughFunds) {
return rhp4.ErrHostFundError
} else if err != nil {
return fmt.Errorf("failed to fund transaction: %w", err)
}
defer func() {
if broadcast {
return
}
// release the locked UTXOs if the transaction is not going to be broadcast
s.wallet.ReleaseInputs(nil, []types.V2Transaction{renewalTxn})
}()

// update renter inputs to reflect our chain state
if basis != req.Basis {
Expand Down Expand Up @@ -968,8 +995,6 @@ func (s *Server) handleRPCRenewContract(stream net.Conn) error {
} else if _, err = s.chain.AddV2PoolTransactions(basis, renewalSet); err != nil {
return errorBadRequest("failed to broadcast renewal set: %v", err)
}
// broadcast the transaction set
s.syncer.BroadcastV2TransactionSet(basis, renewalSet)

// add the contract to the contractor
err = s.contractor.RenewV2Contract(TransactionSet{
Expand All @@ -980,6 +1005,9 @@ func (s *Server) handleRPCRenewContract(stream net.Conn) error {
return fmt.Errorf("failed to add contract: %w", err)
}

broadcast = true // set broadcast so the UTXOs will not be released if the renter happens to disconnect before receiving the last response
s.syncer.BroadcastV2TransactionSet(basis, renewalSet)

// send the finalized transaction set to the renter
return rhp4.WriteResponse(stream, &rhp4.RPCRenewContractThirdResponse{
Basis: basis,
Expand Down

0 comments on commit 0d63b63

Please sign in to comment.