Skip to content

Commit

Permalink
rhp/v4: Implement Merkle proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
lukechampine committed Oct 10, 2024
1 parent a799184 commit 8515110
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 22 deletions.
30 changes: 20 additions & 10 deletions rhp/v4/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var (
// ErrInvalidRoot is returned when RPCWrite returns a sector root that does
// not match the expected value.
ErrInvalidRoot = errors.New("invalid root")
// ErrInvalidProof is returned when an RPC returns an invalid Merkle proof.
ErrInvalidProof = errors.New("invalid proof")
)

var zeros = zeroReader{}
Expand Down Expand Up @@ -216,12 +218,13 @@ func RPCReadSector(ctx context.Context, t TransportClient, prices rhp4.HostPrice
return RPCReadSectorResult{}, fmt.Errorf("failed to read response: %w", err)
}

// TODO: verify proof
n, err := io.Copy(w, io.LimitReader(s, int64(resp.DataLength)))
if err != nil {
rpv := rhp4.NewRangeProofVerifier(offset, length)

Check failure on line 221 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

undefined: rhp4.NewRangeProofVerifier
if n, err := rpv.ReadFrom(io.TeeReader(io.LimitReader(s, int64(resp.DataLength)), w)); err != nil {
return RPCReadSectorResult{}, fmt.Errorf("failed to read data: %w", err)
} else if n != int64(resp.DataLength) {
return RPCReadSectorResult{}, io.ErrUnexpectedEOF
} else if !rpv.Verify(resp.Proof, root) {
return RPCReadSectorResult{}, ErrInvalidProof
}
return RPCReadSectorResult{
Cost: prices.RPCReadSectorCost(length),
Expand Down Expand Up @@ -297,9 +300,10 @@ func RPCVerifySector(ctx context.Context, t TransportClient, prices rhp4.HostPri
var resp rhp4.RPCVerifySectorResponse
if err := callSingleRoundtripRPC(ctx, t, rhp4.RPCVerifySectorID, &req, &resp); err != nil {
return RPCVerifySectorResult{}, err
} else if !rhp4.VerifyLeafProof(resp.Proof, resp.Leaf, req.LeafIndex, root) {

Check failure on line 303 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

undefined: rhp4.VerifyLeafProof
return RPCVerifySectorResult{}, ErrInvalidProof
}

// TODO: validate proof
return RPCVerifySectorResult{
Cost: prices.RPCVerifySectorCost(),
}, nil
Expand All @@ -321,14 +325,15 @@ func RPCModifySectors(ctx context.Context, t TransportClient, cs consensus.State
return RPCModifySectorsResult{}, fmt.Errorf("failed to write request: %w", err)
}

numSectors := (contract.Revision.Filesize + rhp4.SectorSize - 1) / rhp4.SectorSize
var resp rhp4.RPCModifySectorsResponse
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) {

Check failure on line 332 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

undefined: rhp4.VerifyModifySectorsProof

Check failure on line 332 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.OldSubtreeHashes undefined (type "go.sia.tech/core/rhp/v4".RPCModifySectorsResponse has no field or method OldSubtreeHashes)

Check failure on line 332 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.OldLeafHashes undefined (type "go.sia.tech/core/rhp/v4".RPCModifySectorsResponse has no field or method OldLeafHashes)

Check failure on line 332 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.NewMerkleRoot undefined (type "go.sia.tech/core/rhp/v4".RPCModifySectorsResponse has no field or method NewMerkleRoot)
return RPCModifySectorsResult{}, ErrInvalidProof
}

// TODO: verify proof
root := resp.Proof[len(resp.Proof)-1]
revision, err := rhp4.ReviseForModifySectors(contract.Revision, prices, root, actions)
revision, err := rhp4.ReviseForModifySectors(contract.Revision, prices, resp.NewMerkleRoot, actions)

Check failure on line 336 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.NewMerkleRoot undefined (type "go.sia.tech/core/rhp/v4".RPCModifySectorsResponse has no field or method NewMerkleRoot)
if err != nil {
return RPCModifySectorsResult{}, fmt.Errorf("failed to revise contract: %w", err)
}
Expand Down Expand Up @@ -386,9 +391,12 @@ func RPCAppendSectors(ctx context.Context, t TransportClient, cs consensus.State
appended = append(appended, roots[i])
}
}
numSectors := (contract.Revision.Filesize + rhp4.SectorSize - 1) / rhp4.SectorSize
if !rhp4.VerifyAppendSectorsProof(numSectors, resp.SubtreeRoots, appended, contract.Revision.FileMerkleRoot, resp.NewMerkleRoot) {

Check failure on line 395 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

undefined: rhp4.VerifyAppendSectorsProof

Check failure on line 395 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.SubtreeRoots undefined (type "go.sia.tech/core/rhp/v4".RPCAppendSectorsResponse has no field or method SubtreeRoots)

Check failure on line 395 in rhp/v4/rpc.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

resp.NewMerkleRoot undefined (type "go.sia.tech/core/rhp/v4".RPCAppendSectorsResponse has no field or method NewMerkleRoot)
return RPCAppendSectorsResult{}, ErrInvalidProof
}

root := resp.Proof[len(resp.Proof)-1]
revision, err := rhp4.ReviseForAppendSectors(contract.Revision, prices, root, uint64(len(appended)))
revision, err := rhp4.ReviseForAppendSectors(contract.Revision, prices, resp.NewMerkleRoot, uint64(len(appended)))
if err != nil {
return RPCAppendSectorsResult{}, fmt.Errorf("failed to revise contract: %w", err)
}
Expand Down Expand Up @@ -491,9 +499,12 @@ func RPCSectorRoots(ctx context.Context, t TransportClient, cs consensus.State,
return RPCSectorRootsResult{}, fmt.Errorf("invalid request: %w", err)
}

numSectors := (contract.Revision.Filesize + rhp4.SectorSize - 1) / rhp4.SectorSize
var resp rhp4.RPCSectorRootsResponse
if err := callSingleRoundtripRPC(ctx, t, rhp4.RPCSectorRootsID, &req, &resp); err != nil {
return RPCSectorRootsResult{}, err
} else if !rhp4.VerifySectorRootsProof(resp.Proof, resp.Roots, numSectors, offset, offset+length, contract.Revision.FileMerkleRoot) {
return RPCSectorRootsResult{}, ErrInvalidProof
}

// validate host signature
Expand All @@ -502,7 +513,6 @@ func RPCSectorRoots(ctx context.Context, t TransportClient, cs consensus.State,
}
revision.HostSignature = resp.HostSignature

// TODO: validate proof
return RPCSectorRootsResult{
Revision: revision,
Roots: resp.Roots,
Expand Down
36 changes: 24 additions & 12 deletions rhp/v4/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,13 @@ func (s *Server) handleRPCReadSector(stream net.Conn) error {
}

segment := sector[req.Offset : req.Offset+req.Length]
start := (req.Offset / rhp4.LeafSize)
end := (req.Offset + req.Length + rhp4.LeafSize - 1) / rhp4.LeafSize
proof := rhp4.BuildSectorProof(&sector, start, end)

return rhp4.WriteResponse(stream, &rhp4.RPCReadSectorResponse{
Sector: segment,
Proof: nil, // TODO implement proof
Proof: proof,
})
}

Expand Down Expand Up @@ -286,10 +289,10 @@ func (s *Server) handleRPCModifySectors(stream net.Conn) error {
}
roots = roots[:len(roots)-int(action.N)]
case rhp4.ActionUpdate:
if action.A >= uint64(len(roots)) {
if action.N >= uint64(len(roots)) {
return errorBadRequest("update index %v exceeds sector count %v", action.A, len(roots))
}
roots[action.A] = action.Root
roots[action.N] = action.Root
case rhp4.ActionSwap:
if action.A >= uint64(len(roots)) || action.B >= uint64(len(roots)) {
return errorBadRequest("swap indices %v and %v exceed sector count %v", action.A, action.B, len(roots))
Expand All @@ -300,8 +303,11 @@ func (s *Server) handleRPCModifySectors(stream net.Conn) error {
}
}

treeHashes, leafHashes := rhp4.BuildModifySectorsProof(req.Actions, state.Roots)
resp := rhp4.RPCModifySectorsResponse{
Proof: []types.Hash256{rhp4.MetaRoot(roots)}, // TODO implement proof
OldSubtreeHashes: treeHashes,
OldLeafHashes: leafHashes,
NewMerkleRoot: rhp4.MetaRoot(roots),
}
if err := rhp4.WriteResponse(stream, &resp); err != nil {
return fmt.Errorf("failed to write response: %w", err)
Expand All @@ -311,7 +317,7 @@ func (s *Server) handleRPCModifySectors(stream net.Conn) error {
return errorDecodingError("failed to read renter signature response: %v", err)
}

revision, err := rhp4.ReviseForModifySectors(fc, prices, resp.Proof[len(resp.Proof)-1], req.Actions)
revision, err := rhp4.ReviseForModifySectors(fc, prices, resp.NewMerkleRoot, req.Actions)
if err != nil {
return fmt.Errorf("failed to revise contract: %w", err)
}
Expand Down Expand Up @@ -373,15 +379,17 @@ func (s *Server) handleRPCAppendSectors(stream net.Conn) error {
appended++
}

subtreeRoots, newRoot := rhp4.BuildAppendProof(state.Roots, roots[len(state.Roots):])
resp := rhp4.RPCAppendSectorsResponse{
Accepted: accepted,
Proof: []types.Hash256{rhp4.MetaRoot(roots)}, // TODO implement proof
Accepted: accepted,
SubtreeRoots: subtreeRoots,
NewMerkleRoot: newRoot,
}
if err := rhp4.WriteResponse(stream, &resp); err != nil {
return fmt.Errorf("failed to write response: %w", err)
}

revision, err := rhp4.ReviseForAppendSectors(fc, req.Prices, resp.Proof[len(resp.Proof)-1], appended)
revision, err := rhp4.ReviseForAppendSectors(fc, req.Prices, newRoot, appended)
if err != nil {
return fmt.Errorf("failed to revise contract: %w", err)
}
Expand Down Expand Up @@ -509,10 +517,13 @@ func (s *Server) handleRPCSectorRoots(stream net.Conn) error {
return fmt.Errorf("failed to revise contract: %w", err)
}

roots := state.Roots[req.Offset : req.Offset+req.Length]
proof := rhp4.BuildSectorRootsProof(state.Roots, req.Offset, req.Offset+req.Length)

// send the response
return rhp4.WriteResponse(stream, &rhp4.RPCSectorRootsResponse{
Proof: nil, // TODO: proof
Roots: state.Roots,
Proof: proof,
Roots: roots,
HostSignature: revision.HostSignature,
})
}
Expand Down Expand Up @@ -1011,9 +1022,10 @@ func (s *Server) handleRPCVerifySector(stream net.Conn) error {
return rhp4.NewRPCError(rhp4.ErrorCodeBadRequest, err.Error())
}

// TODO: build proof
proof := rhp4.BuildSectorProof(&sector, req.LeafIndex, req.LeafIndex+1)
resp := rhp4.RPCVerifySectorResponse{
Leaf: ([64]byte)(sector[rhp4.LeafSize*req.LeafIndex:]),
Proof: proof,
Leaf: ([64]byte)(sector[rhp4.LeafSize*req.LeafIndex:]),
}
return rhp4.WriteResponse(stream, &resp)
}
Expand Down

0 comments on commit 8515110

Please sign in to comment.