Sign in

    moonappend

    Bounded append-only Merkle log with inclusion and consistency proofs

    merkle
    append-only
    consistency-proof
    rfc9162
    Download zip
    Version
    0.1.0-alpha.1
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    33

    #MoonAppend

    MoonBit 的追加日志 Merkle 证明库。核心能力是生成并验证两个历史快照之间的 一致性证明:在可信旧根已保存的前提下,检查新的承诺是否保留了旧记录及其顺序。 另提供单条记录的包含证明、增量追加和历史前缀快照。

    仓库:ShiChengxu-creater/MoonAppend。 当前 0.1.0-alpha.1 用于初验,尚未发布到 Mooncakes。初验入口见 INITIAL_ACCEPTANCE.md。

    #运行内存示例

    工具链:moon 0.1.20260920、moonc 0.10.14+7d59c7ec9,依赖锁定 moonbitlang/x@0.4.44。native 使用 C 工具链,本地已能调用 MSVC;JS 需要 Node.js; wasm-gc 使用 MoonBit 自带 moonrun。独立参考脚本需要 Python 3.12+ 和 Go 1.22.7+, CI 使用 Python 3.12、Node.js 22、Go 1.27.2。

    在仓库根目录逐条运行,失败即停止:

    moon update moon run examples/memory --target wasm-gc

    输出:

    growth 3 -> 7: VERIFIED rewritten history: ROOT_MISMATCH release-2 inclusion: VERIFIED changed leaf: ROOT_MISMATCH

    示例先保存 3 条发布记录的旧快照,正常追加到 7 条;再用改写旧记录后生成的新证明, 验证它无法匹配原先保存的旧根。所有计算都在内存中,不需要网络或文件服务。

    #库接口

    模块名为 ShiChengxu/moonappend。初验源码消费者通过本地 moon.work 使用, 不能把尚未发布的 moon add 命令当作可用安装方式。公开 API 见 pkg.generated.mbti。

    let log = @moonappend.MemoryLog::new()
    ignore(log.append(b"first record"))
    let saved = log.snapshot()
    ignore(log.append(b"second record"))
    let grown = log.snapshot()
    let proof = log.prove_consistency(1UL, 2UL)
    assert_true(@moonappend.verify_consistency(saved, grown, proof).is_verified())

    构造和追加可能抛出 AppendError,应在可抛出错误的函数或 try/catch 中使用。 可编译的完整例子在 examples/memory。证明构造器验证元数据及路径限额,并复制节点数组; 对外返回的节点数组不会泄露内部可变存储。Hash32 包含不可变字节。

    #初验能力与边界

    能力alpha 状态
    SHA-256、叶/节点域分离、规范非二次幂树已实现
    有界内存追加、历史前缀根、快照已实现
    包含证明生成与严格验证已实现
    一致性证明生成与严格验证已实现
    无文件系统示例、独立 Python/Go 对照、三后端 CI已提供;具体运行状态见初验记录
    严格 JSON、文件 CLI、其余场景集成留待终验
    Mooncakes 发布、从包仓库安装尚未进行

    树形和证明算法限定为 RFC 9162 §2.1 的 SHA-256 配置,不是完整 CT、X.509 或 SCT 实现。

    默认生成器最多 65,536 叶,累计载荷最多 64 MiB,可通过 Limits::new 收紧。 内存只保存哈希和完整子树,不保存载荷;限额不是进程总内存保证。 验证器支持到 2^63−1 的逻辑树大小,无须分配该规模的树,路径最多 128 节点。 文本调用方必须显式确定 UTF-8 等字节编码,库不 trim、排序、换行转换或正规化。

    old_size == 0 返回 NO_PRIOR_HISTORY;相等大小要求空证明且根相同。 VERIFIED 只表示输入承诺间的关系符合证明。调用方必须独立取得可信快照并完成身份认证; 库不证明记录真实、时间可信、日志完整披露或运营方从不分叉。MemoryLog 是单线程可变对象, 并发访问和持久化由上层负责。详见 docs/ALGORITHMS.md。

    #验证

    对 native、wasm-gc、js 分别执行:

    moon fmt --check moon check --target wasm-gc --deny-warn moon build --target wasm-gc --deny-warn moon test --target wasm-gc --deny-warn python tools/verify_reference.py --target wasm-gc --go go python tools/verify_consumer.py --target wasm-gc

    把命令中的目标替换为另外两个后端。Go 参考源码已锁定提交并保留许可证, -mod=vendor 方式不需要访问模块代理;它只服务开发验收,不参与产品运行时。 go_vectors_test.mbt 是外部生成的测试夹具,与手写实现分别统计。

    CI 运行同样的检查、构建、测试、场景、独立对照和消费者测试,并检查生成向量可重复性、 公开 API 无漂移、参考来源校验值与至少 10 次 Git 提交。

    #来源与许可

    Apache-2.0。实现及参考范围见 THIRD_PARTY.md, AI 协作说明见 AI_USAGE.md。生态差异和复查记录见 docs/TOPIC_RECHECK.md。本项目不宣称发明 Merkle 算法或生态中绝无同类。

    AppendError

    pub(all) suberror AppendError {
    InvalidHashLength(Int)
    InvalidHex
    InvalidSize
    InvalidIndex
    InvalidLimits
    LeafLimit
    ByteLimit
    InvalidProofLength
    } derive(Eq,
    Debug
    )

    Invalid construction, bounds or resource request. Proof mismatch is a result.

    AppendError::equal

    fn AppendError::equal(AppendError, AppendError) -> Bool

    AppendError::not_equal

    fn AppendError::not_equal(x : AppendError, y : AppendError) -> Bool

    ConsistencyProof

    pub struct ConsistencyProof {
    // private fields
    } derive(Eq,
    Debug
    )

    ConsistencyProof::equal

    ConsistencyProof::new

    fn ConsistencyProof::new(old_size : UInt64, new_size : UInt64, nodes : Array[Hash32]) -> ConsistencyProof raise AppendError

    ConsistencyProof::new_size

    fn ConsistencyProof::new_size(self : ConsistencyProof) -> UInt64

    ConsistencyProof::nodes

    ConsistencyProof::not_equal

    fn ConsistencyProof::not_equal(x : ConsistencyProof, y : ConsistencyProof) -> Bool

    ConsistencyProof::old_size

    fn ConsistencyProof::old_size(self : ConsistencyProof) -> UInt64

    Hash32

    pub struct Hash32 {
    // private fields
    } derive(Eq,
    Debug
    )

    A validated SHA-256 digest. Internal byte storage is not exposed.

    Hash32::bytes

    fn Hash32::bytes(self : Hash32) -> Bytes

    Hash32::equal

    fn Hash32::equal(Hash32, Hash32) -> Bool

    Hash32::from_bytes

    fn Hash32::from_bytes(value : Bytes) -> Hash32 raise AppendError

    Hash32::from_hex

    fn Hash32::from_hex(value : String) -> Hash32 raise AppendError

    Strict lowercase 64-digit hexadecimal encoding.

    Hash32::hex

    fn Hash32::hex(self : Hash32) -> String

    Hash32::not_equal

    fn Hash32::not_equal(x : Hash32, y : Hash32) -> Bool

    Hash32::to_repr

    InclusionProof

    pub struct InclusionProof {
    // private fields
    } derive(Eq,
    Debug
    )

    InclusionProof::equal

    InclusionProof::leaf_index

    fn InclusionProof::leaf_index(self : InclusionProof) -> UInt64

    InclusionProof::new

    fn InclusionProof::new(index : UInt64, size : UInt64, nodes : Array[Hash32]) -> InclusionProof raise AppendError

    Copy the caller's array so later mutation cannot alter this proof.

    InclusionProof::nodes

    InclusionProof::not_equal

    fn InclusionProof::not_equal(x : InclusionProof, y : InclusionProof) -> Bool

    InclusionProof::tree_size

    fn InclusionProof::tree_size(self : InclusionProof) -> UInt64

    Limits

    pub struct Limits {
    // private fields
    }

    Generation limits may only tighten the absolute alpha caps.

    Limits::max_leaves

    fn Limits::max_leaves(self : Limits) -> Int

    Limits::max_total_bytes

    fn Limits::max_total_bytes(self : Limits) -> Int

    Limits::new

    fn Limits::new(max_leaves? : Int, max_total_bytes? : Int) -> Limits raise AppendError

    MemoryLog

    pub struct MemoryLog {
    // private fields
    }

    In-memory complete-subtree store. Payloads are not retained after hashing.

    MemoryLog::append

    fn MemoryLog::append(self : MemoryLog, leaf : Bytes) -> UInt64 raise AppendError

    Validation precedes mutation: a rejected append leaves the log unchanged.

    MemoryLog::input_bytes

    fn MemoryLog::input_bytes(self : MemoryLog) -> Int

    MemoryLog::new

    fn MemoryLog::new(limits? : Limits) -> MemoryLog

    MemoryLog::prove_consistency

    fn MemoryLog::prove_consistency(self : MemoryLog, old_size : UInt64, new_size : UInt64) -> ConsistencyProof raise AppendError

    MemoryLog::prove_inclusion

    fn MemoryLog::prove_inclusion(self : MemoryLog, index : UInt64, size : UInt64) -> InclusionProof raise AppendError

    MemoryLog::size

    fn MemoryLog::size(self : MemoryLog) -> UInt64

    MemoryLog::snapshot

    fn MemoryLog::snapshot(self : MemoryLog) -> Snapshot

    MemoryLog::snapshot_at

    fn MemoryLog::snapshot_at(self : MemoryLog, size : UInt64) -> Snapshot raise AppendError

    Snapshot

    pub struct Snapshot {
    // private fields
    } derive(Eq,
    Debug
    )

    An unsigned, unauthenticated commitment. Trust is supplied by the caller.

    Snapshot::equal

    fn Snapshot::equal(Snapshot, Snapshot) -> Bool

    Snapshot::new

    fn Snapshot::new(size : UInt64, root : Hash32) -> Snapshot raise AppendError

    Snapshot::not_equal

    fn Snapshot::not_equal(x : Snapshot, y : Snapshot) -> Bool

    Snapshot::root

    fn Snapshot::root(self : Snapshot) -> Hash32

    Snapshot::size

    fn Snapshot::size(self : Snapshot) -> UInt64

    Snapshot::to_repr

    Verification

    pub(all) enum Verification {
    Verified
    Rejected(String)
    NoPriorHistory
    } derive(Eq,
    Debug
    )

    This result does not authenticate the supplied snapshots.

    Verification::code

    fn Verification::code(self : Verification) -> String

    Verification::equal

    Verification::is_verified

    fn Verification::is_verified(self : Verification) -> Bool

    Verification::not_equal

    fn Verification::not_equal(x : Verification, y : Verification) -> Bool

    MAX_TREE_SIZE

    let MAX_TREE_SIZE : UInt64

    empty_root

    fn empty_root() -> Hash32

    HASH(empty); distinct from the hash of an empty leaf.

    hash_leaf

    fn hash_leaf(data : Bytes) -> Hash32

    hash_node

    fn hash_node(left : Hash32, right : Hash32) -> Hash32

    tree_root

    fn tree_root(leaves : Array[Bytes], limits? : Limits) -> Hash32 raise AppendError

    Bounded reference-style construction. Leaf order is never sorted or padded.

    verify_consistency

    fn verify_consistency(trusted_old : Snapshot, trusted_new : Snapshot, proof : ConsistencyProof) -> Verification

    Verify preservation of an old committed prefix. Snapshots are not signed here.

    verify_inclusion

    fn verify_inclusion(trusted : Snapshot, index : UInt64, leaf : Bytes, proof : InclusionProof) -> Verification

    The expected snapshot and index must come from the caller, not the proof.