From 85151108ca8d00028e029bcbca65335fddc6eaef Mon Sep 17 00:00:00 2001 From: lukechampine Date: Thu, 10 Oct 2024 18:10:38 -0400 Subject: [PATCH] rhp/v4: Implement Merkle proofs --- rhp/v4/rpc.go | 30 ++++++++++++++++++++---------- rhp/v4/server.go | 36 ++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/rhp/v4/rpc.go b/rhp/v4/rpc.go index d0d9bc2..17da549 100644 --- a/rhp/v4/rpc.go +++ b/rhp/v4/rpc.go @@ -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{} @@ -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) + 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), @@ -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) { + return RPCVerifySectorResult{}, ErrInvalidProof } - // TODO: validate proof return RPCVerifySectorResult{ Cost: prices.RPCVerifySectorCost(), }, nil @@ -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) { + 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) if err != nil { return RPCModifySectorsResult{}, fmt.Errorf("failed to revise contract: %w", err) } @@ -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) { + 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) } @@ -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 @@ -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, diff --git a/rhp/v4/server.go b/rhp/v4/server.go index 6a70671..20963ee 100644 --- a/rhp/v4/server.go +++ b/rhp/v4/server.go @@ -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(§or, start, end) return rhp4.WriteResponse(stream, &rhp4.RPCReadSectorResponse{ Sector: segment, - Proof: nil, // TODO implement proof + Proof: proof, }) } @@ -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)) @@ -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) @@ -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) } @@ -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) } @@ -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, }) } @@ -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(§or, 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) }