Skip to content

Commit

Permalink
rhp4: implement RPC remove
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Oct 21, 2024
1 parent a033f8b commit dc1d8bc
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 114 deletions.
46 changes: 23 additions & 23 deletions rhp/v4/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ type (
Cost types.Currency `json:"cost"`
}

// RPCModifySectorsResult contains the result of executing the modify sectors RPC.
RPCModifySectorsResult struct {
// RPCRemoveSectorsResult contains the result of executing the remove sectors RPC.
RPCRemoveSectorsResult struct {
Revision types.V2FileContract `json:"revision"`
Cost types.Currency `json:"cost"`
}
Expand Down Expand Up @@ -309,55 +309,55 @@ func RPCVerifySector(ctx context.Context, t TransportClient, prices rhp4.HostPri
}, nil
}

// RPCModifySectors modifies sectors on the host.
func RPCModifySectors(ctx context.Context, t TransportClient, cs consensus.State, prices rhp4.HostPrices, sk types.PrivateKey, contract ContractRevision, actions []rhp4.ModifyAction) (RPCModifySectorsResult, error) {
req := rhp4.RPCModifySectorsRequest{
// RPCRemoveSectors modifies sectors on the host.
func RPCRemoveSectors(ctx context.Context, t TransportClient, cs consensus.State, prices rhp4.HostPrices, sk types.PrivateKey, contract ContractRevision, indices []uint64) (RPCRemoveSectorsResult, error) {
req := rhp4.RPCRemoveSectorsRequest{
ContractID: contract.ID,
Prices: prices,
Actions: actions,
Indices: indices,
}
req.ChallengeSignature = sk.SignHash(req.ChallengeSigHash(contract.Revision.RevisionNumber + 1))

s := t.DialStream(ctx)
defer s.Close()

if err := rhp4.WriteRequest(s, rhp4.RPCModifySectorsID, &req); err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to write request: %w", err)
if err := rhp4.WriteRequest(s, rhp4.RPCRemoveSectorsID, &req); err != nil {
return RPCRemoveSectorsResult{}, fmt.Errorf("failed to write request: %w", err)
}

numSectors := (contract.Revision.Filesize + rhp4.SectorSize - 1) / rhp4.SectorSize
var resp rhp4.RPCModifySectorsResponse
// numSectors := (contract.Revision.Filesize + rhp4.SectorSize - 1) / rhp4.SectorSize
var resp rhp4.RPCRemoveSectorsResponse
if err := rhp4.ReadResponse(s, &resp); err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to read response: %w", err)
} else if !rhp4.VerifyModifySectorsProof(actions, numSectors, resp.OldSubtreeHashes, resp.OldLeafHashes, contract.Revision.FileMerkleRoot, resp.NewMerkleRoot) {
return RPCModifySectorsResult{}, ErrInvalidProof
}
return RPCRemoveSectorsResult{}, fmt.Errorf("failed to read response: %w", err)
} /*else if !rhp4.VerifyModifySectorsProof(actions, numSectors, resp.OldSubtreeHashes, resp.OldLeafHashes, contract.Revision.FileMerkleRoot, resp.NewMerkleRoot) {
return RPCRemoveSectorsResult{}, ErrInvalidProof
}*/ // TODO: verify proof

revision, err := rhp4.ReviseForModifySectors(contract.Revision, prices, resp.NewMerkleRoot, actions)
revision, err := rhp4.ReviseForRemoveSectors(contract.Revision, prices, resp.NewMerkleRoot, len(indices))
if err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to revise contract: %w", err)
return RPCRemoveSectorsResult{}, fmt.Errorf("failed to revise contract: %w", err)
}

sigHash := cs.ContractSigHash(revision)
revision.RenterSignature = sk.SignHash(sigHash)

signatureResp := rhp4.RPCModifySectorsSecondResponse{
signatureResp := rhp4.RPCRemoveSectorsSecondResponse{
RenterSignature: revision.RenterSignature,
}
if err := rhp4.WriteResponse(s, &signatureResp); err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to write signature response: %w", err)
return RPCRemoveSectorsResult{}, fmt.Errorf("failed to write signature response: %w", err)
}

var hostSignature rhp4.RPCModifySectorsThirdResponse
var hostSignature rhp4.RPCRemoveSectorsThirdResponse
if err := rhp4.ReadResponse(s, &hostSignature); err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to read host signatures: %w", err)
return RPCRemoveSectorsResult{}, fmt.Errorf("failed to read host signatures: %w", err)
}
// validate the host signature
if !contract.Revision.HostPublicKey.VerifyHash(sigHash, hostSignature.HostSignature) {
return RPCModifySectorsResult{}, rhp4.ErrInvalidSignature
return RPCRemoveSectorsResult{}, rhp4.ErrInvalidSignature
}
// return the signed revision
return RPCModifySectorsResult{
return RPCRemoveSectorsResult{
Revision: revision,
Cost: contract.Revision.RenterOutput.Value.Sub(revision.RenterOutput.Value),
}, nil
Expand Down Expand Up @@ -495,7 +495,7 @@ func RPCSectorRoots(ctx context.Context, t TransportClient, cs consensus.State,
RenterSignature: revision.RenterSignature,
}

if err := req.Validate(contract.Revision.HostPublicKey, revision); err != nil {
if err := req.Validate(contract.Revision.HostPublicKey, revision, length); err != nil {
return RPCSectorRootsResult{}, fmt.Errorf("invalid request: %w", err)
}

Expand Down
77 changes: 29 additions & 48 deletions rhp/v4/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"bytes"
"context"
"io"
"maps"
"net"
"reflect"
"slices"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -177,7 +179,7 @@ func TestSettings(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -238,7 +240,7 @@ func TestFormContract(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -298,7 +300,7 @@ func TestFormContractBasis(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -356,7 +358,7 @@ func TestRPCRefresh(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -469,7 +471,7 @@ func TestRPCRenew(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -631,7 +633,7 @@ func TestAccounts(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -733,7 +735,7 @@ func TestReadWriteSector(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -830,7 +832,7 @@ func TestAppendSectors(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -948,7 +950,7 @@ func TestVerifySector(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -1024,7 +1026,7 @@ func TestVerifySector(t *testing.T) {
}
}

func TestRPCModifySectors(t *testing.T) {
func TestRPCRemoveSectors(t *testing.T) {
log := zaptest.NewLogger(t)
n, genesis := testutil.V2Network()
hostKey, renterKey := types.GeneratePrivateKey(), types.GeneratePrivateKey()
Expand All @@ -1042,7 +1044,7 @@ func TestRPCModifySectors(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -1130,44 +1132,23 @@ func TestRPCModifySectors(t *testing.T) {
assertRevision(t, appendResult.Revision, roots)
revision.Revision = appendResult.Revision

// swap two random sectors
swapA, swapB := frand.Uint64n(uint64(len(roots)/2)), frand.Uint64n(uint64(len(roots)/2))+uint64(len(roots)/2)
actions := []proto4.ModifyAction{
{Type: proto4.ActionSwap, A: swapA, B: swapB},
// randomly remove half the sectors
indices := make(map[uint64]bool)
var newRoots []types.Hash256
for len(indices) < len(roots)/2 {
i := frand.Uint64n(uint64(len(roots)))
indices[i] = true
}
modifyResult, err := rhp4.RPCModifySectors(context.Background(), transport, cs, settings.Prices, renterKey, revision, actions)
if err != nil {
t.Fatal(err)
}
roots[swapA], roots[swapB] = roots[swapB], roots[swapA]
assertRevision(t, modifyResult.Revision, roots)
revision.Revision = modifyResult.Revision

// delete the last 10 sectors
actions = []proto4.ModifyAction{
{Type: proto4.ActionTrim, N: uint64(len(roots) / 2)},
}
modifyResult, err = rhp4.RPCModifySectors(context.Background(), transport, cs, settings.Prices, renterKey, revision, actions)
if err != nil {
t.Fatal(err)
}
trimmed, roots := roots[len(roots)/2:], roots[:len(roots)/2]
assertRevision(t, modifyResult.Revision, roots)
revision.Revision = modifyResult.Revision

// update a random sector with one of the trimmed sectors
updateIdx := frand.Intn(len(roots))
trimmedIdx := frand.Intn(len(trimmed))
actions = []proto4.ModifyAction{
{Type: proto4.ActionUpdate, Root: trimmed[trimmedIdx], A: uint64(updateIdx)},
for i, root := range roots {
if !indices[uint64(i)] {
newRoots = append(newRoots, root)
}
}

modifyResult, err = rhp4.RPCModifySectors(context.Background(), transport, cs, settings.Prices, renterKey, revision, actions)
removeResult, err := rhp4.RPCRemoveSectors(context.Background(), transport, cs, settings.Prices, renterKey, revision, slices.Collect(maps.Keys(indices)))
if err != nil {
t.Fatal(err)
}
roots[updateIdx] = trimmed[trimmedIdx]
assertRevision(t, modifyResult.Revision, roots)
assertRevision(t, removeResult.Revision, newRoots)
}

func TestRPCSectorRoots(t *testing.T) {
Expand All @@ -1188,7 +1169,7 @@ func TestRPCSectorRoots(t *testing.T) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -1298,7 +1279,7 @@ func BenchmarkWrite(b *testing.B) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -1388,7 +1369,7 @@ func BenchmarkRead(b *testing.B) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down Expand Up @@ -1490,7 +1471,7 @@ func BenchmarkContractUpload(b *testing.B) {
MaxCollateral: types.Siacoins(10000),
MaxContractDuration: 1000,
MaxSectorDuration: 3 * 144,
MaxModifyActions: 100,
MaxSectorBatchSize: 100,
RemainingStorage: 100 * proto4.SectorSize,
TotalStorage: 100 * proto4.SectorSize,
Prices: proto4.HostPrices{
Expand Down
Loading

0 comments on commit dc1d8bc

Please sign in to comment.