Skip to content

Commit

Permalink
fix(rhp4): Include storage cost in renter renewal cost
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Dec 10, 2024
1 parent f356bb7 commit 3698294
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 1 deletion.
3 changes: 2 additions & 1 deletion rhp/v4/rhp.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,8 @@ 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)
contractCost := r.NewContract.HostOutput.Value.Sub(r.NewContract.TotalCollateral) // (contract price + storage cost + locked collateral) - locked collateral
renter = r.NewContract.RenterOutput.Value.Add(contractCost).Add(minerFee).Add(cs.V2FileContractTax(r.NewContract)).Sub(r.RenterRollover)
host = r.NewContract.TotalCollateral.Sub(r.HostRollover)
return
}
Expand Down
212 changes: 212 additions & 0 deletions rhp/v4/rhp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rhp
import (
"testing"

"go.sia.tech/core/consensus"
"go.sia.tech/core/types"
)

Expand All @@ -19,3 +20,214 @@ func TestMinRenterAllowance(t *testing.T) {
t.Fatalf("expected %v, got %v", expected, minAllowance)
}
}

func TestRenewalCost(t *testing.T) {
const (
initialProofHeight = 1000
initialExpiration = initialProofHeight + ProofWindow

renewalHeight = 150
extension = 10
renewalProofHeight = initialProofHeight + extension
renewalExpiration = renewalProofHeight + ProofWindow
renewalDuration = renewalExpiration - renewalHeight
)
cs := consensus.State{}
prices := HostPrices{
ContractPrice: types.NewCurrency64(100),
Collateral: types.NewCurrency64(200),
StoragePrice: types.NewCurrency64(300),
IngressPrice: types.NewCurrency64(400),
EgressPrice: types.NewCurrency64(500),
FreeSectorPrice: types.NewCurrency64(600),
}
renterKey, hostKey := types.GeneratePrivateKey().PublicKey(), types.GeneratePrivateKey().PublicKey()

type testCase struct {
Description string
Modify func(*types.V2FileContract, *RPCRenewContractParams)
RenterCost types.Currency
HostCost types.Currency
}

cases := []testCase{
{
Description: "empty",
Modify: func(*types.V2FileContract, *RPCRenewContractParams) {},
RenterCost: prices.ContractPrice,
},
{
Description: "no storage",
Modify: func(rev *types.V2FileContract, p *RPCRenewContractParams) {
p.Allowance = rev.RenterOutput.Value.Add(types.Siacoins(20))
p.Collateral = rev.TotalCollateral.Add(types.Siacoins(10))
},
RenterCost: types.Siacoins(20).Add(prices.ContractPrice),
HostCost: types.Siacoins(10),
},
{
Description: "no storage - no renter rollover",
Modify: func(rev *types.V2FileContract, p *RPCRenewContractParams) {
p.Allowance = rev.RenterOutput.Value.Add(types.Siacoins(20))
p.Collateral = rev.TotalCollateral.Add(types.Siacoins(10))
// transfer all of the renter funds to the host so the renter will need to put up the entire allowance
rev.HostOutput.Value, rev.RenterOutput.Value = rev.HostOutput.Value.Add(rev.RenterOutput.Value), types.ZeroCurrency
},
RenterCost: types.Siacoins(320).Add(prices.ContractPrice),
HostCost: types.Siacoins(10),
},
{
Description: "renewed storage - no additional funds",
Modify: func(rev *types.V2FileContract, p *RPCRenewContractParams) {
// add storage
rev.Capacity = SectorSize
rev.Filesize = SectorSize
},
RenterCost: prices.ContractPrice.Add(prices.StoragePrice.Mul64(SectorSize).Mul64(extension)), // storage cost is calculated for just the extension
HostCost: types.ZeroCurrency, // collateral lock up is less than rollover
},
{
Description: "renewed storage",
Modify: func(rev *types.V2FileContract, p *RPCRenewContractParams) {
// add storage
rev.Capacity = SectorSize
rev.Filesize = SectorSize

// adjust the renewal params
p.Allowance = rev.RenterOutput.Value.Add(types.Siacoins(20))
p.Collateral = rev.TotalCollateral.Add(types.Siacoins(10))
},
RenterCost: types.Siacoins(20).Add(prices.ContractPrice).Add(prices.StoragePrice.Mul64(SectorSize).Mul64(extension)), // storage cost is calculated for just the extension
HostCost: types.Siacoins(10).Add(prices.Collateral.Mul64(SectorSize).Mul64(renewalDuration)), // collateral is calculated for the full duration
},
{
Description: "renewed storage - no renter rollover",
Modify: func(rev *types.V2FileContract, p *RPCRenewContractParams) {
// adjust the renewal params
p.Allowance = rev.RenterOutput.Value.Add(types.Siacoins(20))
p.Collateral = rev.TotalCollateral.Add(types.Siacoins(10))

// add storage
rev.Capacity = SectorSize
rev.Filesize = SectorSize
// transfer all the renter funds to the host so the renter will need to put up more allowance
rev.HostOutput.Value, rev.RenterOutput.Value = rev.HostOutput.Value.Add(rev.RenterOutput.Value), types.ZeroCurrency

},

Check failure on line 116 in rhp/v4/rhp_test.go

View workflow job for this annotation

GitHub Actions / test / test (1.23, macos-latest)

unnecessary trailing newline (whitespace)
RenterCost: types.Siacoins(320).Add(prices.ContractPrice).Add(prices.StoragePrice.Mul64(SectorSize).Mul64(extension)), // storage cost is calculated for just the extension
HostCost: types.Siacoins(10).Add(prices.Collateral.Mul64(SectorSize).Mul64(renewalDuration)), // collateral is calculated for the full duration
},
}
for _, tc := range cases {
t.Run(tc.Description, func(t *testing.T) {
contract, _ := NewContract(prices, RPCFormContractParams{
RenterPublicKey: renterKey,
RenterAddress: types.StandardAddress(renterKey),
Allowance: types.Siacoins(300),
Collateral: types.Siacoins(400),
ProofHeight: initialProofHeight,
}, hostKey, types.StandardAddress(hostKey))

params := RPCRenewContractParams{
ProofHeight: renewalProofHeight,
}
tc.Modify(&contract, &params)

prices.TipHeight = renewalHeight
renewal, _ := RenewContract(contract, prices, params)
tax := cs.V2FileContractTax(renewal.NewContract)
renter, host := RenewalCost(cs, prices, renewal, types.ZeroCurrency)
if !renter.Equals(tc.RenterCost.Add(tax)) {
t.Errorf("expected renter cost %v, got %v", tc.RenterCost, renter.Sub(tax))
} else if !host.Equals(tc.HostCost) {
t.Errorf("expected host cost %v, got %v", tc.HostCost, host)
}
})
}
}

func TestRefreshCost(t *testing.T) {
const initialProofHeight = 1000

cs := consensus.State{}
prices := HostPrices{
ContractPrice: types.NewCurrency64(100),
Collateral: types.NewCurrency64(200),
StoragePrice: types.NewCurrency64(300),
IngressPrice: types.NewCurrency64(400),
EgressPrice: types.NewCurrency64(500),
FreeSectorPrice: types.NewCurrency64(600),
}
renterKey, hostKey := types.GeneratePrivateKey().PublicKey(), types.GeneratePrivateKey().PublicKey()

type testCase struct {
Description string
Modify func(*types.V2FileContract)
}

cases := []testCase{
{
Description: "no storage",
Modify: func(rev *types.V2FileContract) {},
},
{
Description: "no storage - no renter rollover",
Modify: func(rev *types.V2FileContract) {
// transfer all of the renter funds to the host so the renter rolls over nothing
rev.HostOutput.Value, rev.RenterOutput.Value = rev.HostOutput.Value.Add(rev.RenterOutput.Value), types.ZeroCurrency
},
},
{
Description: "renewed storage",
Modify: func(rev *types.V2FileContract) {
// add storage
rev.Capacity = SectorSize
rev.Filesize = SectorSize
},
},
{
Description: "renewed storage - no renter rollover",
Modify: func(rev *types.V2FileContract) {
// add storage
rev.Capacity = SectorSize
rev.Filesize = SectorSize
// transfer all the renter funds to the host
rev.HostOutput.Value, rev.RenterOutput.Value = rev.HostOutput.Value.Add(rev.RenterOutput.Value), types.ZeroCurrency

},

Check failure on line 197 in rhp/v4/rhp_test.go

View workflow job for this annotation

GitHub Actions / test / test (1.23, macos-latest)

unnecessary trailing newline (whitespace)
},
}

// the actual cost to the renter and host should always be the additional allowance and collateral
// on top of the existing contract costs
additionalAllowance, additionalCollateral := types.Siacoins(20), types.Siacoins(10)
renterCost := additionalAllowance.Add(prices.ContractPrice)
hostCost := additionalCollateral

for _, tc := range cases {
t.Run(tc.Description, func(t *testing.T) {
contract, _ := NewContract(prices, RPCFormContractParams{
RenterPublicKey: renterKey,
RenterAddress: types.StandardAddress(renterKey),
Allowance: types.Siacoins(300),
Collateral: types.Siacoins(400),
ProofHeight: initialProofHeight,
}, hostKey, types.StandardAddress(hostKey))

params := RPCRefreshContractParams{
Allowance: additionalAllowance,
Collateral: additionalCollateral,
}
tc.Modify(&contract)

refresh, _ := RefreshContract(contract, prices, params)
tax := cs.V2FileContractTax(refresh.NewContract)
renter, host := RefreshCost(cs, prices, refresh, types.ZeroCurrency)
if !renter.Equals(renterCost.Add(tax)) {
t.Errorf("expected renter cost %v, got %v", renterCost, renter.Sub(tax))
} else if !host.Equals(hostCost) {
t.Errorf("expected host cost %v, got %v", hostCost, host)
}
})
}
}

0 comments on commit 3698294

Please sign in to comment.