diff --git a/.changeset/have_renewalcost_take_into_account_cost_of_existing_storage.md b/.changeset/have_renewalcost_take_into_account_cost_of_existing_storage.md new file mode 100644 index 00000000..6badad8c --- /dev/null +++ b/.changeset/have_renewalcost_take_into_account_cost_of_existing_storage.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +# Have RenewalCost take into account cost of existing storage diff --git a/rhp/v4/rhp.go b/rhp/v4/rhp.go index 3e454c4d..c39e7212 100644 --- a/rhp/v4/rhp.go +++ b/rhp/v4/rhp.go @@ -581,9 +581,10 @@ func ContractCost(cs consensus.State, p HostPrices, fc types.V2FileContract, min } // RenewalCost calculates the cost to the host and renter for renewing a contract. -func RenewalCost(cs consensus.State, p HostPrices, r types.V2FileContractRenewal, minerFee types.Currency) (renter, host types.Currency) { - renter = r.NewContract.RenterOutput.Value.Add(p.ContractPrice).Add(minerFee).Add(cs.V2FileContractTax(r.NewContract)).Sub(r.RenterRollover) - host = r.NewContract.TotalCollateral.Sub(r.HostRollover) +func RenewalCost(cs consensus.State, p HostPrices, r types.V2FileContractRenewal, minerFee types.Currency, prevExpirationHeight uint64) (renter, host types.Currency) { + storageCost := p.StoragePrice.Mul64(r.NewContract.Filesize).Mul64(r.NewContract.ExpirationHeight - prevExpirationHeight) + renter = r.NewContract.RenterOutput.Value.Add(storageCost).Add(p.ContractPrice).Add(minerFee).Add(cs.V2FileContractTax(r.NewContract)).Sub(r.RenterRollover) + host = r.NewContract.HostOutput.Value.Add(r.NewContract.TotalCollateral).Sub(r.HostRollover) return } diff --git a/rhp/v4/rhp_test.go b/rhp/v4/rhp_test.go index ae4f0f00..0204bdb0 100644 --- a/rhp/v4/rhp_test.go +++ b/rhp/v4/rhp_test.go @@ -3,6 +3,7 @@ package rhp import ( "testing" + "go.sia.tech/core/consensus" "go.sia.tech/core/types" ) @@ -19,3 +20,119 @@ func TestMinRenterAllowance(t *testing.T) { t.Fatalf("expected %v, got %v", expected, minAllowance) } } + +func TestRenewalCost(t *testing.T) { + // contract for renewal + contract := types.V2FileContract{ + Capacity: 1000 * SectorSize, + Filesize: 900 * SectorSize, + FileMerkleRoot: types.Hash256{1, 1, 1}, + ProofHeight: 500000, // block 500k + ExpirationHeight: 500000 + 1000, // 1000 block window + RenterOutput: types.SiacoinOutput{Value: types.Siacoins(300)}, + HostOutput: types.SiacoinOutput{Value: types.Siacoins(400)}, + MissedHostValue: types.Siacoins(700), + TotalCollateral: types.Siacoins(100), + RevisionNumber: 99999999, + } + + cs := consensus.State{} + prices := HostPrices{ + TipHeight: 40000, + ContractPrice: types.NewCurrency64(100), + Collateral: types.NewCurrency64(200), + StoragePrice: types.NewCurrency64(300), + IngressPrice: types.NewCurrency64(400), + EgressPrice: types.NewCurrency64(500), + FreeSectorPrice: types.NewCurrency64(600), + } + + // renew contract + renewal, _ := RenewContract(contract, prices, RPCRenewContractParams{ + Allowance: contract.RenterOutput.Value.Add(types.Siacoins(20)), // 20 SC more than renter output + Collateral: contract.MissedHostValue.Add(types.Siacoins(10)), // 10 SC more than before + ProofHeight: contract.ExpirationHeight + 1000, + }) + + minerFee := types.NewCurrency64(700) + prevExpirationHeight := uint64(900) // 100 blocks before + + // assert expected results + renter, host := RenewalCost(cs, prices, renewal, minerFee, prevExpirationHeight) + expectedRenter := types.NewCurrency(12531552842177053476, 3317658) + expectedHost := types.NewCurrency(7783457943256563812, 71557343) + if !renter.Equals(expectedRenter) { + t.Fatalf("expected %v, got %v", expectedRenter, renter) + } else if !host.Equals(expectedHost) { + t.Fatalf("expected %v, got %v", expectedHost, host) + } + + // make sure the sums match + fc := renewal.NewContract + inputSum := renter.Add(host) + outputSum := fc.RenterOutput.Value. + Add(fc.HostOutput.Value). + Add(cs.V2FileContractTax(fc)). + Add(minerFee) + if !inputSum.Equals(outputSum) { + t.Fatalf("expected %v, got %v", inputSum, outputSum) + } +} + +func TestRefreshCost(t *testing.T) { + // contract for renewal + contract := types.V2FileContract{ + Capacity: 1000 * SectorSize, + Filesize: 900 * SectorSize, + FileMerkleRoot: types.Hash256{1, 1, 1}, + ProofHeight: 500000, // block 500k + ExpirationHeight: 500000 + 1000, // 1000 block window + RenterOutput: types.SiacoinOutput{Value: types.Siacoins(300)}, + HostOutput: types.SiacoinOutput{Value: types.Siacoins(400)}, + MissedHostValue: types.Siacoins(700), + TotalCollateral: types.Siacoins(1000), + RevisionNumber: 99999999, + } + + cs := consensus.State{} + prices := HostPrices{ + TipHeight: 40000, + ContractPrice: types.NewCurrency64(100), + Collateral: types.NewCurrency64(200), + StoragePrice: types.NewCurrency64(300), + IngressPrice: types.NewCurrency64(400), + EgressPrice: types.NewCurrency64(500), + FreeSectorPrice: types.NewCurrency64(600), + } + + // renew contract + renewal, _ := RefreshContract(contract, prices, RPCRefreshContractParams{ + Allowance: contract.RenterOutput.Value.Add(types.Siacoins(20)), // 20 SC more than renter output + Collateral: contract.TotalCollateral.Add(types.Siacoins(10)), + }) + + minerFee := types.NewCurrency64(700) + + // assert expected results + renter, host := RefreshCost(cs, prices, renewal, minerFee) + expectedRenter := types.NewCurrency(10700203959496213284, 21749095) + expectedHost := types.NewCurrency(13106743224624480256, 54752209) + if !renter.Equals(expectedRenter) { + t.Fatalf("expected %v, got %v", expectedRenter, renter) + } else if !host.Equals(expectedHost) { + t.Fatalf("expected %v, got %v", expectedHost, host) + } + + // make sure the sums match + fc := renewal.NewContract + inputSum := renter.Add(host) + outputSum := fc.RenterOutput.Value. + Add(fc.HostOutput.Value). + Add(cs.V2FileContractTax(fc)). + Add(minerFee). + Sub(renewal.RenterRollover). + Sub(renewal.HostRollover) + if !inputSum.Equals(outputSum) { + t.Fatalf("expected %v, got %v", inputSum, outputSum) + } +}