diff --git a/rhp/v4/rhp.go b/rhp/v4/rhp.go index 379ef03b..f5814278 100644 --- a/rhp/v4/rhp.go +++ b/rhp/v4/rhp.go @@ -3,7 +3,6 @@ package rhp import ( "bytes" "encoding/hex" - "errors" "fmt" "io" "time" @@ -549,52 +548,6 @@ func MinRenterAllowance(hp HostPrices, duration uint64, collateral types.Currenc return hp.StoragePrice.Mul64(duration).Mul(maxCollateralBytes).Mul64(9).Div64(10) // 10% buffer } -// ValidateFormContractParams checks the given contract parameters for validity. -func ValidateFormContractParams(hs HostSettings, tip types.ChainIndex, req RPCFormContractParams) error { - hp := hs.Prices - expirationHeight := req.ProofHeight + proofWindow - duration := expirationHeight - hp.TipHeight - minRenterAllowance := MinRenterAllowance(hp, duration, req.Collateral) - - switch { - case expirationHeight <= hp.TipHeight: // must be validated against tip instead of prices - return errors.New("contract expiration height is in the past") - case req.Allowance.IsZero(): - return errors.New("allowance is zero") - case req.Collateral.Cmp(hs.MaxCollateral) > 0: - return fmt.Errorf("collateral %v exceeds max collateral %v", req.Collateral, hs.MaxCollateral) - case duration > hs.MaxContractDuration: - return fmt.Errorf("contract duration %v exceeds max duration %v", duration, hs.MaxContractDuration) - case req.Allowance.Cmp(minRenterAllowance) < 0: - return fmt.Errorf("allowance %v is less than minimum %v for collateral", req.Allowance, minRenterAllowance) - default: - return nil - } -} - -// ValidateRenewContractParams checks the given renew parameters for validity. -func ValidateRenewContractParams(hs HostSettings, tip types.ChainIndex, req RPCRenewContractParams) error { - hp := hs.Prices - expirationHeight := req.ProofHeight + proofWindow - duration := expirationHeight - hp.TipHeight - minRenterAllowance := MinRenterAllowance(hp, duration, req.Collateral) - - switch { - case expirationHeight <= tip.Height: // must be validated against tip instead of prices - return errors.New("contract expiration height is in the past") - case req.Allowance.IsZero(): - return errors.New("allowance is zero") - case req.Collateral.Cmp(hs.MaxCollateral) > 0: - return fmt.Errorf("collateral %v exceeds max collateral %v", req.Collateral, hs.MaxCollateral) - case duration > hs.MaxContractDuration: - return fmt.Errorf("contract duration %v exceeds max duration %v", duration, hs.MaxContractDuration) - case req.Allowance.Cmp(minRenterAllowance) < 0: - return fmt.Errorf("allowance %v is less than minimum %v for collateral", req.Allowance, minRenterAllowance) - default: - return nil - } -} - // NewRenewal creates a contract renewal from an existing contract revision func NewRenewal(fc types.V2FileContract, prices HostPrices, rp RPCRenewContractParams) (renewal types.V2FileContractRenewal) { expirationHeight := rp.ProofHeight + proofWindow diff --git a/rhp/v4/validation.go b/rhp/v4/validation.go new file mode 100644 index 00000000..a0b7d3e0 --- /dev/null +++ b/rhp/v4/validation.go @@ -0,0 +1,119 @@ +package rhp + +import ( + "errors" + "fmt" + + "go.sia.tech/core/types" +) + +// Validate validates a read sector request. +func (req *RPCReadSectorRequest) Validate() error { + switch { + case req.Length == 0: + return errors.New("length must be greater than 0") + case req.Offset+req.Length > SectorSize: + return errors.New("read request exceeds sector bounds") + case (req.Offset+req.Length)%LeafSize != 0: + return errors.New("read request must be segment aligned") + } + return nil +} + +// Validate validates a write sector request. +func (req *RPCWriteSectorRequest) Validate() error { + switch { + case req.Duration == 0: + return errors.New("duration must be greater than 0") + case len(req.Sector) == 0: + return errors.New("sector must not be empty") + case len(req.Sector)%LeafSize != 0: + return errors.New("sector must be segment aligned") + } + return nil +} + +// Validate validates a sector roots request. Signatures are not validated. +func (req *RPCSectorRootsRequest) Validate(fc types.V2FileContract) error { + contractSectors := fc.Filesize / SectorSize + + switch { + case req.Length == 0: + return errors.New("length must be greater than 0") + case req.Length+req.Offset > contractSectors: + return fmt.Errorf("read request range exceeds contract sectors: %d > %d", req.Length+req.Offset, contractSectors) + } + return nil +} + +// Validate validates a form contract request. Prices are not validated +func (req *RPCFormContractRequest) Validate(hs HostSettings, tip types.ChainIndex) error { + // validate the request fields + switch { + case req.MinerFee.IsZero(): + return errors.New("miner fee must be greater than 0") + case req.Basis != (types.ChainIndex{}): + return errors.New("basis must be set") + case len(req.RenterInputs) == 0: + return errors.New("renter inputs must not be empty") + } + + // validate the contract fields + hp := hs.Prices + expirationHeight := req.Contract.ProofHeight + proofWindow + duration := expirationHeight - hp.TipHeight + // calculate the minimum allowance required for the contract based on the + // host's locked collateral and the contract duration + minRenterAllowance := MinRenterAllowance(hp, duration, req.Contract.Collateral) + + switch { + case expirationHeight <= tip.Height: // must be validated against tip instead of prices + return errors.New("contract expiration height is in the past") + case req.Contract.Allowance.IsZero(): + return errors.New("allowance is zero") + case req.Contract.Collateral.Cmp(hs.MaxCollateral) > 0: + return fmt.Errorf("collateral %v exceeds max collateral %v", req.Contract.Collateral, hs.MaxCollateral) + case duration > hs.MaxContractDuration: + return fmt.Errorf("contract duration %v exceeds max duration %v", duration, hs.MaxContractDuration) + case req.Contract.Allowance.Cmp(minRenterAllowance) < 0: + return fmt.Errorf("allowance %v is less than minimum %v for collateral", req.Contract.Allowance, minRenterAllowance) + default: + return nil + } +} + +// Validate validates a renew contract request. Prices are not validated +func (req *RPCRenewContractRequest) Validate(hs HostSettings, tip types.ChainIndex) error { + // validate the request fields + switch { + case req.MinerFee.IsZero(): + return errors.New("miner fee must be greater than 0") + case req.Basis != (types.ChainIndex{}): + return errors.New("basis must be set") + case len(req.RenterInputs) == 0: + return errors.New("renter inputs must not be empty") + } + + // validate the contract fields + hp := hs.Prices + expirationHeight := req.Renewal.ProofHeight + proofWindow + duration := expirationHeight - hp.TipHeight + // calculate the minimum allowance required for the contract based on the + // host's locked collateral and the contract duration + minRenterAllowance := MinRenterAllowance(hp, duration, req.Renewal.Collateral) + + switch { + case expirationHeight <= tip.Height: // must be validated against tip instead of prices + return errors.New("contract expiration height is in the past") + case req.Renewal.Allowance.IsZero(): + return errors.New("allowance is zero") + case req.Renewal.Collateral.Cmp(hs.MaxCollateral) > 0: + return fmt.Errorf("collateral %v exceeds max collateral %v", req.Renewal.Collateral, hs.MaxCollateral) + case duration > hs.MaxContractDuration: + return fmt.Errorf("contract duration %v exceeds max duration %v", duration, hs.MaxContractDuration) + case req.Renewal.Allowance.Cmp(minRenterAllowance) < 0: + return fmt.Errorf("allowance %v is less than minimum %v for collateral", req.Renewal.Allowance, minRenterAllowance) + default: + return nil + } +}