mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
PART A — manifest STATE ROOT (the >1-validator prerequisite M0 omitted):
- state.Root() hashes the committed manifest keyspace deterministically via the
zapdb prefix iterator NewIteratorWithStartAndPrefix(nil, prefixManifest),
folding each entry length-prefixed in lexicographic key order. Mirrors
dexvm/state/state.go:395 StateHash, narrowed to the one keyspace.
- VM.computeStateRoot folds blockHash+height with state.Root() (dexvm/vm.go:1260);
ProcessBlock stamps BlockResult.StateRoot (dexvm/vm.go:584).
- The root travels in the block HEADER (new 32B field after parentID; block id
commits to it). Block.Verify recomputes it and REJECTS a mismatch
(errStateRootMismatch) + Aborts staged writes — the safety gate dexvm computed
but never compared.
- Tests: determinism across write order, change-sensitivity (object set / etag /
size / fileIds), Verify-rejects-tampered-root.
PART B — S3 OBJECT path, on-chain-metadata / off-chain-blob split, end to end
(schain/object/):
- Store.PutObject streams the blob to a Volume (OFF chain) -> fid, then commits
only PutManifest{bucket,object,[fid],size,etag} to the VM through a real block
(ON chain). GetObject reads the manifest back and reconstructs from the volume.
- object.Volume is the seam; MemVolume satisfies it for M1 with a fid shape
mirroring hanzo/s3 needle.FileId and etag = base64(md5(blob)) (= ContentMd5).
- M2 PLUG POINT (object/volume.go): swap MemVolume for a github.com/hanzoai/s3
s3/operation.SubmitFiles adapter; nothing in the VM or object.Store changes.
- Test proves blob bytes appear in NO accepted block, the block stays
manifest-small, the manifest IS on chain, and GET byte-reconstructs.
luxfi packages only; zapdb-native; no protobuf in state; SDKROOT needed on macOS.
56 lines
2.8 KiB
Go
56 lines
2.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// Package object wires the S3 OBJECT path to the S-Chain storage VM, proving the
|
|
// on-chain-metadata / off-chain-blob split end to end:
|
|
//
|
|
// - On PUT, the blob bytes are streamed to a VOLUME server (a needle write) —
|
|
// OFF chain. The volume returns a file id (fid) per blob. The blob NEVER
|
|
// enters a block.
|
|
// - The small MANIFEST {bucket, object, fileIds, size, etag} is submitted as a
|
|
// PutManifest tx to the S-Chain VM — ON chain. Only this consensus-sized
|
|
// metadata becomes block state.
|
|
// - On GET, the manifest is read back from the VM (GetManifest), the fids it
|
|
// names are streamed from the volume, and the original blob is reconstructed.
|
|
//
|
|
// The Volume interface below is the SEAM. For M1 it is satisfied by a faithful
|
|
// in-memory stub (memvolume.go) whose Write/Read mirror the hanzo/s3 volume
|
|
// contract: Write(blob) -> fid, Read(fid) -> blob. For M2, the real hanzo/s3
|
|
// volume client (github.com/hanzoai/s3 s3/operation.SubmitFiles, which assigns a
|
|
// volume + streams the needle and returns SubmitResult.Fid) plugs in HERE, behind
|
|
// this identical interface — see the M2 PLUG POINT note on Volume. Keeping the
|
|
// seam in chains/schain (rather than importing the hanzoai/s3 module into the
|
|
// luxfi/chains module) respects the org/module boundary while proving the exact
|
|
// data model the real client will satisfy.
|
|
package object
|
|
|
|
import "errors"
|
|
|
|
var (
|
|
// ErrBlobNotFound is returned by Volume.Read when no blob is stored under fid.
|
|
ErrBlobNotFound = errors.New("object: blob not found in volume")
|
|
// ErrEmptyBlob rejects a PutObject with no bytes (M1 stores at least one fid).
|
|
ErrEmptyBlob = errors.New("object: empty blob")
|
|
)
|
|
|
|
// Volume is the OFF-CHAIN blob store seam. It is the minimal projection of the
|
|
// hanzo/s3 volume server the object path needs: write bytes, get a file id back;
|
|
// read a file id, get the bytes back. The fid is opaque to this package — it is
|
|
// the volume's own address (the SeaweedFS-style "volumeId,needleIdCookie" string
|
|
// the real s3 volume returns).
|
|
//
|
|
// M2 PLUG POINT: replace the in-memory stub (NewMemVolume) with a thin adapter
|
|
// over github.com/hanzoai/s3 s3/operation.SubmitFiles:
|
|
//
|
|
// Write(blob) -> SubmitFiles(...).Fid (assign volume + stream needle)
|
|
// Read(fid) -> GET http://<volume>/<fid> (stream needle bytes back)
|
|
//
|
|
// No code in object.go / the VM changes — only this interface's implementation.
|
|
type Volume interface {
|
|
// Write streams blob to the volume (OFF chain) and returns its file id.
|
|
Write(blob []byte) (fid string, err error)
|
|
// Read streams the blob bytes for fid back from the volume. Returns
|
|
// ErrBlobNotFound if no such blob exists.
|
|
Read(fid string) (blob []byte, err error)
|
|
}
|