From af7c48b09fd741238edcec6c5393e0c39fd2cc06 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 18 Dec 2024 14:33:52 +0100 Subject: [PATCH 1/4] fix(rhp): fix panic in handleRPCFormContract --- rhp/v4/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rhp/v4/server.go b/rhp/v4/server.go index 3936878..c05a04a 100644 --- a/rhp/v4/server.go +++ b/rhp/v4/server.go @@ -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 From b3acfca374c48a99ba38bf6a8b1d1d28b3118563 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 18 Dec 2024 15:01:28 +0100 Subject: [PATCH 2/4] fix handleRPCFormContract --- rhp/v4/rpc_test.go | 44 +++++++++++++++++++++++++++++++++++++++----- rhp/v4/server.go | 6 +++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/rhp/v4/rpc_test.go b/rhp/v4/rpc_test.go index 3911dd1..0f72ad4 100644 --- a/rhp/v4/rpc_test.go +++ b/rhp/v4/rpc_test.go @@ -3,6 +3,7 @@ package rhp_test import ( "bytes" "context" + "math" "net" "reflect" "strings" @@ -266,10 +267,44 @@ func TestFormContractBasis(t *testing.T) { n, genesis := testutil.V2Network() hostKey, renterKey := types.GeneratePrivateKey(), types.GeneratePrivateKey() - cm, s, w := startTestNode(t, n, genesis) + cm, s, _ := startTestNode(t, n, genesis) + + // create a separate wallet that doesn't automatically sync with the test node + wCS, wState, err := chain.NewDBStore(chain.NewMemDB(), n, genesis) + if err != nil { + t.Fatal(err) + } + wCM := chain.NewManager(wCS, wState) + wStore := testutil.NewEphemeralWalletStore() + w, err := wallet.NewSingleAddressWallet(renterKey, wCM, wStore) + if err != nil { + t.Fatal(err) + } // fund the wallet - mineAndSync(t, cm, w.Address(), int(n.MaturityDelay+20), w) + mineAndSync(t, cm, w.Address(), int(n.MaturityDelay+20)) + + // manually sync wallet's chain manager until one block before the tip + blocks, _, err := cm.BlocksForHistory([]types.BlockID{genesis.ID()}, cm.Tip().Height-1) + if err != nil { + t.Fatal(err) + } else if err := wCM.AddBlocks(blocks); err != nil { + t.Fatal(err) + } + rus, aus, err := wCM.UpdatesSince(types.ChainIndex{}, math.MaxInt32) + if err != nil { + t.Fatal(err) + } + err = wStore.UpdateChainState(func(tx wallet.UpdateTx) error { + return w.UpdateChainState(tx, rus, aus) + }) + if err != nil { + t.Fatal(err) + } + balance, err := w.Balance() + if err != nil { + t.Fatal(err) + } sr := testutil.NewEphemeralSettingsReporter() sr.Update(proto4.HostSettings{ @@ -299,12 +334,11 @@ func TestFormContractBasis(t *testing.T) { } 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{ RenterPublicKey: renterKey.PublicKey(), RenterAddress: w.Address(), - Allowance: renterAllowance, - Collateral: hostCollateral, + Allowance: balance.Confirmed.Mul64(96).Div64(100), // almost the whole balance to force as many inputs as possible + Collateral: types.ZeroCurrency, ProofHeight: cm.Tip().Height + 50, }) if err != nil { diff --git a/rhp/v4/server.go b/rhp/v4/server.go index c05a04a..25e99f8 100644 --- a/rhp/v4/server.go +++ b/rhp/v4/server.go @@ -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 From e30377336603b4ce8eefbb27c2b9a824890e1d95 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 18 Dec 2024 17:18:01 +0100 Subject: [PATCH 3/4] clean up TestFormContractBasis --- rhp/v4/rpc_test.go | 66 ++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/rhp/v4/rpc_test.go b/rhp/v4/rpc_test.go index 0f72ad4..e3c9890 100644 --- a/rhp/v4/rpc_test.go +++ b/rhp/v4/rpc_test.go @@ -3,7 +3,6 @@ package rhp_test import ( "bytes" "context" - "math" "net" "reflect" "strings" @@ -267,50 +266,36 @@ func TestFormContractBasis(t *testing.T) { n, genesis := testutil.V2Network() hostKey, renterKey := types.GeneratePrivateKey(), types.GeneratePrivateKey() - cm, s, _ := startTestNode(t, n, genesis) + cm1, s, w1 := startTestNode(t, n, genesis) + cm2, _, w2 := startTestNode(t, n, genesis) - // create a separate wallet that doesn't automatically sync with the test node - wCS, wState, err := chain.NewDBStore(chain.NewMemDB(), n, genesis) - if err != nil { - t.Fatal(err) - } - wCM := chain.NewManager(wCS, wState) - wStore := testutil.NewEphemeralWalletStore() - w, err := wallet.NewSingleAddressWallet(renterKey, wCM, wStore) - if err != nil { - t.Fatal(err) - } - - // fund the wallet - mineAndSync(t, cm, w.Address(), int(n.MaturityDelay+20)) + // fund both wallets + mineAndSync(t, cm1, w2.Address(), int(n.MaturityDelay+20)) + mineAndSync(t, cm1, w1.Address(), int(n.MaturityDelay+20)) - // manually sync wallet's chain manager until one block before the tip - blocks, _, err := cm.BlocksForHistory([]types.BlockID{genesis.ID()}, cm.Tip().Height-1) + // 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) - } else if err := wCM.AddBlocks(blocks); err != nil { - t.Fatal(err) } - rus, aus, err := wCM.UpdatesSince(types.ChainIndex{}, math.MaxInt32) - if err != nil { - t.Fatal(err) + var blocks []types.Block + for _, cau := range applied { + blocks = append(blocks, cau.Block) } - err = wStore.UpdateChainState(func(tx wallet.UpdateTx) error { - return w.UpdateChainState(tx, rus, aus) - }) - if err != nil { + if err := cm2.AddBlocks(blocks); err != nil { t.Fatal(err) } - balance, err := w.Balance() - if 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, @@ -324,29 +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} - 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(), + 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: cm.Tip().Height + 50, + 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") From 9f94d7c4bfba7175726ac7bd03d6db1d58ba2441 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 18 Dec 2024 17:19:00 +0100 Subject: [PATCH 4/4] fix handleRPCRefreshContract and handleRPCRenewContract --- rhp/v4/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rhp/v4/server.go b/rhp/v4/server.go index 25e99f8..a64b51d 100644 --- a/rhp/v4/server.go +++ b/rhp/v4/server.go @@ -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) @@ -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)