diff --git a/.changeset/reduced_host_sector_hashing_in_rpcwritesector.md b/.changeset/reduced_host_sector_hashing_in_rpcwritesector.md new file mode 100644 index 00000000..3f22d6a8 --- /dev/null +++ b/.changeset/reduced_host_sector_hashing_in_rpcwritesector.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +# Reduced host sector hashing in RPCWriteSector. \ No newline at end of file diff --git a/rhp/v4/rpc_test.go b/rhp/v4/rpc_test.go index 01efae36..4b8a2dcf 100644 --- a/rhp/v4/rpc_test.go +++ b/rhp/v4/rpc_test.go @@ -1706,14 +1706,16 @@ func TestAccounts(t *testing.T) { cs := cm.TipState() - var sector *[proto4.SectorSize]byte - if err := ss.StoreSector(types.Hash256{1}, sector, 0); err != nil { + sector := [proto4.SectorSize]byte{1} + subtrees := proto4.CachedSectorSubtrees(§or) + root := proto4.MetaRoot(subtrees) + if err := ss.StoreSector(root, §or, subtrees, 0); err != nil { t.Fatal(err) } // test operations against unknown account token := proto4.NewAccountToken(renterKey, hostKey.PublicKey()) - _, err = rhp4.RPCVerifySector(context.Background(), transport, settings.Prices, token, types.Hash256{1}) + _, err = rhp4.RPCVerifySector(context.Background(), transport, settings.Prices, token, root) if err == nil || !strings.Contains(err.Error(), proto4.ErrNotEnoughFunds.Error()) { t.Fatal(err) } @@ -1772,7 +1774,7 @@ func TestAccounts(t *testing.T) { // drain account and try using it _ = c.DebitAccount(account, proto4.Usage{RPC: accountFundAmount}) - _, err = rhp4.RPCVerifySector(context.Background(), transport, settings.Prices, token, types.Hash256{1}) + _, err = rhp4.RPCVerifySector(context.Background(), transport, settings.Prices, token, root) if err == nil || !strings.Contains(err.Error(), proto4.ErrNotEnoughFunds.Error()) { t.Fatal(err) } diff --git a/rhp/v4/server.go b/rhp/v4/server.go index bd06bbc3..1dde0944 100644 --- a/rhp/v4/server.go +++ b/rhp/v4/server.go @@ -81,8 +81,8 @@ type ( HasSector(root types.Hash256) (bool, error) // ReadSector retrieves a segment of a sector by its root and returns the data and a proof for the segment. ReadSector(root types.Hash256, offset, length uint64) ([]byte, []types.Hash256, error) - // StoreSector writes a sector to disk - StoreSector(root types.Hash256, data *[rhp4.SectorSize]byte, expiration uint64) error + // StoreSector writes a sector to disk. + StoreSector(root types.Hash256, data *[rhp4.SectorSize]byte, subtrees []types.Hash256, expiration uint64) error } // A RevisionState pairs a contract revision with its sector roots. @@ -240,26 +240,21 @@ func (s *Server) handleRPCWriteSector(stream net.Conn) error { } prices := req.Prices - sr := io.LimitReader(stream, int64(req.DataLength)) - if req.DataLength < rhp4.SectorSize { - // if the data is less than a full sector, the reader needs to be padded - // with zeros to calculate the sector root - padding := int64(rhp4.SectorSize - req.DataLength) - sr = io.MultiReader(sr, io.LimitReader(zeros, padding)) - } - buf := newSectorBuffer() - root, err := rhp4.ReadSectorRoot(io.TeeReader(sr, buf)) - if err != nil { + if _, err := io.CopyN(buf, stream, int64(req.DataLength)); err != nil { return errorDecodingError("failed to read sector data: %v", err) } usage := prices.RPCWriteSectorCost(req.DataLength) - if err = s.contractor.DebitAccount(req.Token.Account, usage); err != nil { + if err := s.contractor.DebitAccount(req.Token.Account, usage); err != nil { return fmt.Errorf("failed to debit account: %w", err) } - if err := s.sectors.StoreSector(root, buf.Bytes(), prices.TipHeight+rhp4.TempSectorDuration); err != nil { + subtrees := rhp4.CachedSectorSubtrees(buf.Bytes()) + // compute the root from the subtrees instead of hashing the sector again. + root := rhp4.MetaRoot(subtrees) + + if err := s.sectors.StoreSector(root, buf.Bytes(), subtrees, prices.TipHeight+rhp4.TempSectorDuration); err != nil { return fmt.Errorf("failed to store sector: %w", err) } return rhp4.WriteResponse(stream, &rhp4.RPCWriteSectorResponse{ diff --git a/testutil/host.go b/testutil/host.go index 95d4a5ba..06fb968b 100644 --- a/testutil/host.go +++ b/testutil/host.go @@ -72,7 +72,7 @@ func (es *EphemeralSectorStore) ReadSector(root types.Hash256, offset, length ui } // StoreSector stores a sector in the EphemeralSectorStore. -func (es *EphemeralSectorStore) StoreSector(root types.Hash256, sector *[proto4.SectorSize]byte, _ uint64) error { +func (es *EphemeralSectorStore) StoreSector(root types.Hash256, sector *[proto4.SectorSize]byte, _ []types.Hash256, _ uint64) error { es.mu.Lock() defer es.mu.Unlock() es.sectors[root] = sector