Skip to content

Commit

Permalink
Merge pull request #145 from SiaFoundation/chris/fix-panic-form-contract
Browse files Browse the repository at this point in the history
fix(v4): Fix panic when forming contracts with different basis than the host
  • Loading branch information
n8maninger authored Dec 18, 2024
2 parents b5e84c0 + 9f94d7c commit b02d670
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 23 deletions.
52 changes: 38 additions & 14 deletions rhp/v4/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,16 +266,36 @@ func TestFormContractBasis(t *testing.T) {
n, genesis := testutil.V2Network()
hostKey, renterKey := types.GeneratePrivateKey(), types.GeneratePrivateKey()

cm, s, w := startTestNode(t, n, genesis)
cm1, s, w1 := startTestNode(t, n, genesis)
cm2, _, w2 := startTestNode(t, n, genesis)

// fund the wallet
mineAndSync(t, cm, w.Address(), int(n.MaturityDelay+20), w)
// fund both wallets
mineAndSync(t, cm1, w2.Address(), int(n.MaturityDelay+20))
mineAndSync(t, cm1, w1.Address(), int(n.MaturityDelay+20))

// manually sync the second wallet just before tip
_, applied, err := cm1.UpdatesSince(types.ChainIndex{}, int(w1.Tip().Height-5))
if err != nil {
t.Fatal(err)
}
var blocks []types.Block
for _, cau := range applied {
blocks = append(blocks, cau.Block)
}
if err := cm2.AddBlocks(blocks); err != nil {
t.Fatal(err)
}

// wait for the second wallet to sync
for cm2.Tip() != w2.Tip() {
time.Sleep(time.Millisecond)
}

sr := testutil.NewEphemeralSettingsReporter()
sr.Update(proto4.HostSettings{
Release: "test",
AcceptingContracts: true,
WalletAddress: w.Address(),
WalletAddress: w1.Address(),
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
RemainingStorage: 100 * proto4.SectorSize,
Expand All @@ -289,30 +309,34 @@ func TestFormContractBasis(t *testing.T) {
},
})
ss := testutil.NewEphemeralSectorStore()
c := testutil.NewEphemeralContractor(cm)
c := testutil.NewEphemeralContractor(cm1)

transport := testRenterHostPair(t, hostKey, cm, s, w, c, sr, ss, zap.NewNop())
transport := testRenterHostPair(t, hostKey, cm1, s, w1, c, sr, ss, zap.NewNop())

settings, err := rhp4.RPCSettings(context.Background(), transport)
if err != nil {
t.Fatal(err)
}

fundAndSign := &fundAndSign{w, renterKey}
renterAllowance, hostCollateral := types.Siacoins(100), types.Siacoins(200)
result, err := rhp4.RPCFormContract(context.Background(), transport, cm, fundAndSign, cm.TipState(), settings.Prices, hostKey.PublicKey(), settings.WalletAddress, proto4.RPCFormContractParams{
balance, err := w2.Balance()
if err != nil {
t.Fatal(err)
}

fundAndSign := &fundAndSign{w2, renterKey}
result, err := rhp4.RPCFormContract(context.Background(), transport, cm2, fundAndSign, cm1.TipState(), settings.Prices, hostKey.PublicKey(), settings.WalletAddress, proto4.RPCFormContractParams{
RenterPublicKey: renterKey.PublicKey(),
RenterAddress: w.Address(),
Allowance: renterAllowance,
Collateral: hostCollateral,
ProofHeight: cm.Tip().Height + 50,
RenterAddress: w2.Address(),
Allowance: balance.Confirmed.Mul64(96).Div64(100), // almost the whole balance to force as many inputs as possible
Collateral: types.ZeroCurrency,
ProofHeight: cm1.Tip().Height + 50,
})
if err != nil {
t.Fatal(err)
}

// verify the transaction set is valid
if known, err := cm.AddV2PoolTransactions(result.FormationSet.Basis, result.FormationSet.Transactions); err != nil {
if known, err := cm1.AddV2PoolTransactions(result.FormationSet.Basis, result.FormationSet.Transactions); err != nil {
t.Fatal(err)
} else if !known {
t.Fatal("expected transaction set to be known")
Expand Down
18 changes: 9 additions & 9 deletions rhp/v4/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,14 +585,14 @@ func (s *Server) handleRPCFormContract(stream net.Conn) error {

// update renter input basis to reflect our funding basis
if basis != req.Basis {
hostInputs := formationTxn.SiacoinInputs[len(formationTxn.SiacoinInputs)-len(req.RenterInputs)]
formationTxn.SiacoinInputs = formationTxn.SiacoinInputs[:len(formationTxn.SiacoinInputs)-len(req.RenterInputs)]
hostInputs := formationTxn.SiacoinInputs[len(req.RenterInputs):]
formationTxn.SiacoinInputs = formationTxn.SiacoinInputs[:len(req.RenterInputs)]
txnset, err := s.chain.UpdateV2TransactionSet([]types.V2Transaction{formationTxn}, req.Basis, basis)
if err != nil {
return errorBadRequest("failed to update renter inputs from %q to %q: %v", req.Basis, basis, err)
}
formationTxn = txnset[0]
formationTxn.SiacoinInputs = append(formationTxn.SiacoinInputs, hostInputs)
formationTxn.SiacoinInputs = append(formationTxn.SiacoinInputs, hostInputs...)
}

// read the renter's signatures
Expand All @@ -611,8 +611,8 @@ func (s *Server) handleRPCFormContract(stream net.Conn) error {
formationTxn.FileContracts[0].RenterSignature = renterSigResp.RenterContractSignature

// add the renter signatures to the transaction
for i := range formationTxn.SiacoinInputs[:len(req.RenterInputs)] {
formationTxn.SiacoinInputs[i].SatisfiedPolicy = renterSigResp.RenterSatisfiedPolicies[i]
for i, policy := range renterSigResp.RenterSatisfiedPolicies {
formationTxn.SiacoinInputs[i].SatisfiedPolicy = policy
}

// add our signature to the contract
Expand Down Expand Up @@ -722,8 +722,8 @@ func (s *Server) handleRPCRefreshContract(stream net.Conn) error {

// update renter inputs to reflect our chain state
if basis != req.Basis {
hostInputs := renewalTxn.SiacoinInputs[len(renewalTxn.SiacoinInputs)-len(req.RenterInputs):]
renewalTxn.SiacoinInputs = renewalTxn.SiacoinInputs[:len(renewalTxn.SiacoinInputs)-len(req.RenterInputs)]
hostInputs := renewalTxn.SiacoinInputs[len(req.RenterInputs):]
renewalTxn.SiacoinInputs = renewalTxn.SiacoinInputs[:len(req.RenterInputs)]
updated, err := s.chain.UpdateV2TransactionSet([]types.V2Transaction{renewalTxn}, req.Basis, basis)
if err != nil {
return errorBadRequest("failed to update renter inputs from %q to %q: %v", req.Basis, basis, err)
Expand Down Expand Up @@ -891,8 +891,8 @@ func (s *Server) handleRPCRenewContract(stream net.Conn) error {

// update renter inputs to reflect our chain state
if basis != req.Basis {
hostInputs := renewalTxn.SiacoinInputs[len(renewalTxn.SiacoinInputs)-len(req.RenterInputs):]
renewalTxn.SiacoinInputs = renewalTxn.SiacoinInputs[:len(renewalTxn.SiacoinInputs)-len(req.RenterInputs)]
hostInputs := renewalTxn.SiacoinInputs[len(req.RenterInputs):]
renewalTxn.SiacoinInputs = renewalTxn.SiacoinInputs[:len(req.RenterInputs)]
updated, err := s.chain.UpdateV2TransactionSet([]types.V2Transaction{renewalTxn}, req.Basis, basis)
if err != nil {
return errorBadRequest("failed to update renter inputs from %q to %q: %v", req.Basis, basis, err)
Expand Down

0 comments on commit b02d670

Please sign in to comment.