moonbit-CryptoAssert

Multi-chain cryptographic assertion & formal verification library.

crypto
blockchain
assertion
formal-verification
proof-carrying
moon add Kali-Leo/moonbit-CryptoAssert@0.2.0
Download zip
Author
Version
0.2.0
License
Apache-2.0
Last updated
18 days ago
Downloads
15

Dependencies

README

#CryptoAssert — Cryptocurrency Compliance & Security Assertion Library

A production-grade, three-tier assertion template library for cryptocurrency compliance verification, bridging business logic, protocol specifications, and mathematical proofs through compile-time static dispatch and theorem-proven safety guarantees.


#Table of Contents

  1. Overview
  2. Architecture
  3. Project Structure
  4. Design Philosophy
  5. Layer 1: Formal Proof Layer
  6. Layer 2: Protocol Specification Layer
  7. Layer 2 (Extended): Cryptography Primitives
  8. Layer 2 (Extended): E2E Production Verifier
  9. Layer 2 (Extended): Lemma Runtime Bridge
  10. Layer 2 (Extended): BigMath & UInt256
  11. Layer 2 (Extended): Additional Modules
  12. Layer 3: Business Assertion Layer
  13. Type System Reference
  14. Trait Reference
  15. Error Model
  16. Quick Start
  17. API Reference
  18. Extending the Library
  19. Testing & Quality Assurance
  20. Security Considerations
  21. Build, Prove & Test
  22. CI/CD
  23. Comparison with Industry Alternatives
  24. License


#Overview

CryptoAssert eliminates the need for engineers to understand low-level cryptographic primitives when building cryptocurrency compliance checks. It provides a formally verified, type-safe abstraction over address validation, transaction verification, signature scheme compatibility, fee ratio enforcement, replay protection, contract safety, and cross-chain bridge security across 9 blockchain ecosystems and 5 signature schemes.

The library is organized in three layers, each with a distinct responsibility:

LayerDirectoryResponsibilityTrust Model
Proofproof/transfer_proof.mbtpMathematical conservation / replay / multisig theoremsSMT-solver verified (moon prove, Why3 + Z3), executed for real in CI
Protocolprotocol/Type definitions, traits, cryptographic primitives, production verifiers, lemma bridgesCompile-time trait resolution
Businessbusiness/Assertion functions with fn[V: Trait] dispatchStatic dispatch, zero NotImplemented

#Core Capabilities

  • Multi-chain address audit — 9 blockchain address formats (Bitcoin P2PKH/P2SH/SegWit/Taproot, Ethereum, EIP-55, Solana, Tron, Cosmos)
  • Signature scheme compatibility — 5 signature algorithms (ECDSA secp256k1, Ed25519, Sr25519, BLS12-381, SchnorrSecp256k1) cross-matched with address types
  • Real cryptographic verification — Production-grade ECDSA secp256k1 (FIPS 186-4), SHA-256, Keccak-256, Base58Check, Bech32/Bech32m, EIP-712, RLP
  • Transaction fund conservation — Multi-input/output balance verification with overflow protection
  • Replay attack prevention — Real per-transaction nonce / chain_id field validation (consumed-nonce replay and cross-chain replay both rejected), plus EIP-155 style signature binding: chain_id and nonce are serialized into the signing message, so a signed transaction whose chain or nonce is tampered with fails ECDSA verification
  • Smart contract safety audit — Mint/burn/pause/upgrade control verification
  • Cross-chain bridge audit — Multisig threshold verification (multisig_threshold-of-total_validators: zero / over-count / non-majority thresholds are rejected, sub-BFT thresholds warned), validator set size, amount boundaries, and chain ID validation
  • Sparse Merkle Tree verification — Inclusion and exclusion proofs with 256-layer precomputed nil-hash table
  • Formal theorem proving — 14 lemmas (plus 2 predicates) checked by moon prove via Why3 + Z3, 14 goals proved; the CI prove job fails if any lemma cannot be proved
  • Structured error model — 10 suberror variants with typed payloads


#Architecture

┌─────────────────────────────────────────────────────────────────┐ │ BUSINESS LAYER │ │ (business/) │ │ │ │ fn[V: Trait] assert_*(verifier: V, ...) -> Result raise E │ │ │ │ • address_assert.mbt (3 assertions) │ │ • security_assert.mbt (6 assertions) │ │ │ │ Consumers inject concrete Verifier implementations. │ │ Dispatch resolved at compile time — zero runtime overhead. │ └──────────────────────────┬──────────────────────────────────────┘ │ trait delegation ▼ ┌─────────────────────────────────────────────────────────────────┐ │ PROTOCOL LAYER │ │ (protocol/) │ │ │ │ ┌─ Traits & Specs ──────────────────────────────────────────┐ │ │ │ • AddressVerifier trait (6 methods) │ │ │ │ • TransactionVerifier trait (5 methods) │ │ │ │ • SecurityVerifier trait (5 methods) │ │ │ │ • DefaultVerifier struct (9 blockchain implementations) │ │ │ │ • SimpleSecurityVerifier (full SecurityVerifier impl) │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─ Cryptography Primitives ─────────────────────────────────┐ │ │ │ • ecdsa_secp256k1.mbt — ECDSA FIPS 186-4 + RFC 6979 │ │ │ │ • sha256.mbt — SHA-256 (FIPS 180-4) │ │ │ │ • keccak256.mbt — Keccak-256 (Ethereum) │ │ │ │ • base58.mbt — Base58 + Base58Check │ │ │ │ • bech32.mbt — Bech32 (BIP 173) + Bech32m │ │ │ │ • eip712.mbt — EIP-712 typed structured data │ │ │ │ • rlp.mbt — RLP decoder (Ethereum) │ │ │ │ • smt.mbt — Sparse Merkle Tree verifier │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─ Production Verifiers ────────────────────────────────────┐ │ │ │ • e2e_verifier.mbt — Full E2E transaction pipeline │ │ │ │ • transfer_runtime.mbt — Lemma → Runtime bridges │ │ │ │ • bigmath.mbt — BigInt amount operations │ │ │ │ • uint256.mbt — 256-bit unsigned integer │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ All traits use self: Self → compile-time static dispatch. │ │ No dyn dispatch, no vtable, no runtime NotImplemented. │ └──────────────────────────┬──────────────────────────────────────┘ │ mathematical invariants ▼ ┌─────────────────────────────────────────────────────────────────┐ │ PROOF LAYER │ │ (proof/transfer_proof.mbtp, "proof-enabled" package) │ │ │ │ 2 predicates: │ │ • fund_conservation_inv │ │ • transfer_state_invariant │ │ │ │ 14 lemmas (Why3/Z3 proven — one goal each, 14 goals total): │ │ • no_inflation_lemma • value_monotonic_lemma │ │ • overflow_safe_lemma • transfer_correctness_theorem │ │ • nonce_replay_rejection_lemma• chain_id_binding_lemma │ │ • multisig_quorum_intersection_lemma │ │ • model_balance_lemma • uint256_add_no_overflow_lemma │ │ • uint256_sub_no_underflow_lemma │ │ • uint256_mul_no_overflow_lemma │ │ • bridge_soundness_lemma • bridge_conservation_lemma │ │ • bigint_extended_conservation_lemma │ │ │ │ Each lemma carries explicit proof_assert steps. │ │ All theorems validated by `moon prove` (real execution in CI). │ └─────────────────────────────────────────────────────────────────┘


#Project Structure

crypto_assert/ ├── moon.mod # Package manifest (Kali-Leo/moonbit-CryptoAssert v0.2.0) ├── moon.pkg # Root package declaration ├── README.md # This file ├── LICENSE # Apache 2.0 ├── proposal.md # Project proposal (OSC 2026) ├── acceptance_review.md # OSC 2026 acceptance self-review ├── .gitignore │ ├── proof/ # ═══ Formal Proof Layer ═══ │ ├── moon.pkg # options("proof-enabled": true) │ └── transfer_proof.mbtp # 2 predicates + 14 lemmas, proved by `moon prove` │ ├── examples/ # ═══ Runnable Examples ═══ │ ├── moon.pkg # Executable package │ └── main.mbt # `moon run examples --target native` (run in CI) │ ├── protocol/ # ═══ Protocol Layer ═══ │ ├── moon.pkg # Package config │ │ │ │ ┌── Traits & Specs ──────────────────────────────────────┐ │ ├── assert_result.mbt # AssertionResult enum, suberror AssertError (10 variants) │ ├── address_spec.mbt # AddressVerifier trait, DefaultVerifier, AddressType (9), │ │ # SignatureScheme (5), HashScheme (6), PrecisionSpec, │ │ # AddressFormatSpec, ChecksumType, AddressLengthRange │ ├── transaction_spec.mbt # TransactionVerifier trait, TransactionSpec, TxInputSpec, │ │ # TxOutputSpec, FeeSpec, TokenTransferSpec, │ │ # TxValidationCode (9), TxStatus (5) │ ├── security_spec.mbt # SecurityVerifier trait, SecurityAssertResult, │ │ # ReplayProtectionSpec, TokenSafetySpec, BridgeSpec, │ │ # ContractSafetyMode (5) │ ├── simple_security_verifier.mbt # SimpleSecurityVerifier — full SecurityVerifier impl │ │ # with BigInt-based numerical checks │ │ │ │ ┌── Cryptography Primitives ─────────────────────────────┐ │ ├── ecdsa_secp256k1.mbt # ECDSA over secp256k1 (FIPS 186-4) │ │ # with RFC 6979 deterministic nonce │ ├── sha256.mbt # SHA-256 (FIPS 180-4), SHA-256d │ ├── keccak256.mbt # Keccak-256 (Ethereum, distinct from SHA3-256) │ ├── base58.mbt # Base58 + Base58Check (Bitcoin alphabet, O(1) lookup) │ ├── bech32.mbt # Bech32 (BIP 173) + Bech32m (BIP 350) + Cosmos verify │ ├── eip712.mbt # EIP-712 typed structured data hashing │ ├── rlp.mbt # RLP decoder (Ethereum Yellow Paper Appendix B) │ ├── smt.mbt # Sparse Merkle Tree (256-layer, precomputed nil-hashes) │ │ │ │ ┌── Production Verifiers & Bridges ──────────────────────┐ │ ├── e2e_verifier.mbt # E2E production verifier — full tx pipeline, │ │ # ECDSA signing & verification, fund conservation, │ │ # replay protection, contract/bridge audit │ ├── transfer_runtime.mbt # Lemma → Runtime bridges (6 lemma-corresponding functions) │ ├── bigmath.mbt # BigInt amount parsing & operations (4 functions) │ ├── uint256.mbt # UInt256 — 256-bit unsigned integer (add/sub/mul/div) │ │ │ │ ┌── Tests ───────────────────────────────────────────────┐ │ ├── spec_easy_test.mbt # Unit tests │ └── spec_difficult_test.mbt # Integration tests │ └── business/ # ═══ Business Assertion Layer ═══ ├── moon.pkg # Package config ├── address_assert.mbt # 3 assertion functions: address, sig-compat, tx-sig ├── security_assert.mbt # 6 assertion functions: balance, replay, amount, fee, │ # contract-safety, bridge-security └── business_test.mbt # QuickCheck property bombardment

Line counts (implementation only):

FileLinesPurpose
protocol/e2e_verifier.mbt656E2E production verifier + TransactionVerifier impl
protocol/ecdsa_secp256k1.mbt443ECDSA secp256k1 FIPS 186-4 + RFC 6979
protocol/simple_security_verifier.mbt439SecurityVerifier full implementation
proof/transfer_proof.mbtp432Formal theorem proofs (2 predicates + 14 lemmas)
protocol/address_spec.mbt3769 address types, 5 sig schemes, 6 hash schemes + DefaultVerifier
protocol/uint256.mbt325UInt256 — 256-bit unsigned integer arithmetic
protocol/bech32.mbt233Bech32/Bech32m + Cosmos verify
protocol/eip712.mbt229EIP-712 typed structured data hashing
protocol/transfer_runtime.mbt225Lemma → Runtime bridges
examples/main.mbt204Runnable examples (executed by CI)
protocol/keccak256.mbt193Keccak-256 (Ethereum)
protocol/rlp.mbt177RLP decoder (Ethereum Yellow Paper)
protocol/smt.mbt171Sparse Merkle Tree verifier
protocol/sha256.mbt169SHA-256 (FIPS 180-4)
protocol/base58.mbt161Base58 + Base58Check
protocol/transaction_spec.mbt104Transaction trait + 6 struct types
protocol/bigmath.mbt96BigInt amount parsing & operations
protocol/security_spec.mbt755 trait methods + 4 struct types
protocol/assert_result.mbt22Error domain model (10 suberror variants)
business/security_assert.mbt1576 security assertion functions
business/address_assert.mbt1503 address/signature assertion functions
Total (non-test)~5,037


#Design Philosophy

#1. Static Dispatch over Dynamic Dispatch

All traits use self: Self parameters and fn[V: Trait] generic syntax. This guarantees the compiler resolves every method call at compile time. There is no vtable lookup, no dyn dispatch, and no runtime NotImplemented error state.

In a smart-contract context, a runtime "not implemented" branch is not a usability nuisance — it is a remote code execution vulnerability. By eliminating this class of errors entirely through the type system, CryptoAssert removes an entire attack surface.

#2. Theorem-Proven Invariants

The proof/transfer_proof.mbtp file uses MoonBit's first-class formal verification (.mbtp proof files in a proof-enabled package) to encode mathematical invariants about cryptocurrency transfers, replay protection, and bridge multisig quorums. Running moon prove translates them to WhyML and hands each goal to the Why3 platform backed by the Z3 SMT solver — 14 goals, all mechanically proved. The CI prove job runs this for real on every push and fails the build if any lemma cannot be proved.

Having a machine-checked proof obligation wired into CI is a capability that mainstream blockchain ecosystems (Solidity, Rust/CosmWasm, Go/Cosmos SDK) do not offer natively.

#3. Trait-Based Extension

The library ships with DefaultVerifier, SimpleSecurityVerifier, and E2ETransactionVerifier, but users can implement their own verifiers by implementing the AddressVerifier, TransactionVerifier, and SecurityVerifier traits. The compiler enforces completeness — missing any method is a compilation error, not a runtime panic.

#4. Structured Error Model

All errors use MoonBit's suberror mechanism (checked error subtypes). Each error variant carries structured payload data that enables programmatic error handling without string parsing. The 10 error variants cover the entire error space of cryptocurrency assertion failures.

#5. Separation of Proof and Runtime

The proof layer (proof/transfer_proof.mbtp) defines mathematical theorems, while the runtime bridge (protocol/transfer_runtime.mbt) provides corresponding verification functions. Each runtime function references its corresponding lemma via proof_require in docstrings, creating a traceable link between formal verification and production code. Facts that the proof model cannot supply for free (see the modeling note in Layer 1) are enforced at runtime with BigInt checks.


#Layer 1: Formal Proof Layer

File: proof/transfer_proof.mbtp — a dedicated package enabled for proving via proof/moon.pkg:

options( "proof-enabled": true, )

This layer defines and proves mathematical theorems about cryptocurrency transfers, replay protection, and bridge multisig quorums using moon prove, which translates the .mbtp file to WhyML and invokes the Why3 verification platform backed by an SMT solver (Z3, CVC5, or Alt-Ergo).

# Prerequisites: Why3 + at least one SMT solver on PATH # WHY3DATA / WHY3LIB → why3 --print-datadir / --print-libdir # Z3PATH → optional explicit z3 binary location moon prove # Kali-Leo/moonbit-CryptoAssert/proof # Succeeded: 14 goals proved # Summary: # 1 of 1 packages proved

The CI prove job executes exactly this and asserts 1 of 1 packages proved / 14 goals proved — a lemma that fails to prove fails the build. (Sanity-checked during development: adding a deliberately false lemma makes moon prove exit non-zero.)

#Modeling Note (read this before trusting any proof)

MoonBit's current proof prelude idealizes UInt64 as unbounded mathematical integers: the range fact 0 ≤ x ≤ 2⁶⁴−1 is not an implicit axiom. This file therefore follows two disciplines:

  1. Every lemma that depends on non-negativity or upper bounds states those facts as explicit proof_require premises — real input constraints, never the conclusion restated as a premise.
  2. Range facts are enforced at runtime by BigInt checks (check_fee_nonnegative_bigint, transfer_runtime.mbt, bigmath.mbt).

The earlier fee_nonnegative_lemma (⊢ fee ≥ 0 with no premises) is unprovable under this model, and worse, it entered the axiom stack of subsequent goals and could make them vacuously "provable". It has been removed; fee non-negativity is a runtime BigInt check instead.

#Predicates

PredicateSignatureMathematical Meaning
fund_conservation_inv(total_in, total_out, fee: UInt64)total_in ≡ total_out + fee
transfer_state_invariant(pre_total, post_total: UInt64)pre_total ≡ post_total

#Lemmas & Theorems (14 goals, all proved)

All premises listed below are the actual proof_require clauses in proof/transfer_proof.mbtp.

Fund conservation family

LemmaPremises (proof_require)Conclusion (proof_ensure)
no_inflation_lemmafund_conservation_invfee ≥ 0total_in ≥ total_out
value_monotonic_lemmain1 ≥ in2 ∧ both ≥ out + fee(in1 − out − fee) ≥ (in2 − out − fee)
overflow_safe_lemmaa ≥ 0b ≥ 0a ≤ max − ba + b ≥ aa + b ≥ ba + b ≤ max
transfer_correctness_theoremfund_conservation_invfee ≥ 0No inflation + state invariant

Replay protection family (backs check_replay_protection)

LemmaPremisesConclusion
nonce_replay_rejection_lemmaexpected == current + 1replayed ≤ currentreplayed ≠ expected — a consumed nonce can never pass the equality check
chain_id_binding_lemmaexpected_chain_id ≥ 1tx_chain_id == expected_chain_idtx_chain_id ≥ 1 — a tx that passes the chain-ID check is never chain-unbound

Bridge multisig family (backs check_bridge_security)

LemmaPremisesConclusion
multisig_quorum_intersection_lemmatotal ≥ 1threshold ≤ total3·threshold ≥ 2·total + 12·threshold ≥ total + 1threshold ≥ 1 — any two BFT quorums intersect, so the bridge cannot sign two conflicting messages

UInt256 limb bounds & account model

LemmaPremisesConclusion
model_balance_lemmalocked ≥ 0available ≥ 0total == locked + availabletotal ≥ lockedtotal ≥ available
uint256_add_no_overflow_lemmalimbs ≥ 0 ∧ carry_in ≤ 1a3 ≤ (max−1) − b3sum ≥ each addend ∧ sum ≤ max
uint256_sub_no_underflow_lemmaa3 > b3a3 ≥ b3
uint256_mul_no_overflow_lemmalimbs ≥ 0 ∧ both ≤ 2³²−1 ∧ carry == 0a3·b3 ≥ 0a3·b3 ≤ max

Proof ↔ runtime bridge family

LemmaPremisesConclusion
bridge_soundness_lemmafund_conservation_inv ∧ all values in [0, max]in == out + fee lifts losslessly to BigInt
bridge_conservation_lemmafund_conservation_invfee ≥ 0out ≤ max − feein ≥ outin == out + fee
bigint_extended_conservation_lemmafund_conservation_invfee ≥ 0out ≤ max − feein ≥ outin == out + fee

Each lemma body contains explicit proof_assert statements that guide the SMT solver through the logical derivation. The proofs are verifiable by running:

moon prove

#Why This Matters in Production

Without formal proof, a developer writes assert(total_in == total_out + fee) and hopes it is correct. Here, the SMT solver checks each lemma for all values satisfying its stated premises — not for a finite sample of test inputs. The guarantee is exactly as strong as the premises are honest: range facts are stated explicitly (see the modeling note above), and the runtime BigInt bridges enforce them on real data. Together this eliminates entire categories of bugs — inflation attacks, conservation violations, nonce-replay acceptance, and under-thresholded bridge multisigs — before code reaches production.


#Layer 2: Protocol Specification Layer

Directory: protocol/

The protocol layer defines what must be verified — the type contracts, trait interfaces, and the reference DefaultVerifier implementation.

#AddressVerifier Trait

pub trait AddressVerifier {
address_format_spec(self: Self, AddressType) -> AddressFormatSpec
valid_address_length_range(self: Self, AddressType) -> AddressLengthRange
validate_checksum(self: Self, AddressType, String) -> Bool
address_precision(self: Self, SignatureScheme) -> PrecisionSpec
hash_output_length(self: Self, HashScheme) -> UInt
is_valid_hash_size(self: Self, Bytes, HashScheme) -> Bool
}

All 9 address types supported by DefaultVerifier:

Address TypeChainPrefixLength RangeChecksum
BtcP2pkhBitcoin126–35 charsBase58Check
BtcP2shBitcoin326–35 charsBase58Check
BtcBech32Bitcoin SegWitbc126–42 charsBech32
BtcBech32mBitcoin Taprootbc1p26–42 charsBech32m
EthEthereum0x42 charsNone
EthEip55Ethereum (EIP-55)0x42 charsEIP-55
SolanaSolana(none)32–44 charsNone
TronTronT26–35 charsBase58Check
CosmosCosmoscosmos124–45 charsBech32

#TransactionVerifier Trait

pub trait TransactionVerifier {
validate_transaction(self: Self, TransactionSpec) -> TxValidationCode
verify_signature(self: Self, SignatureVerifySpec) -> Bool
compute_txid(self: Self, TransactionSpec) -> String
estimate_fee(self: Self, TransactionSpec, String) -> FeeSpec
validate_token_transfer(self: Self, TokenTransferSpec) -> TxValidationCode
}

#SecurityVerifier Trait

pub trait SecurityVerifier {
check_replay_protection(self: Self, TransactionSpec, ReplayProtectionSpec) -> SecurityAssertResult
check_contract_safety(self: Self, TokenSafetySpec) -> SecurityAssertResult
check_bridge_security(self: Self, BridgeSpec) -> SecurityAssertResult
validate_amount_range(self: Self, String, String, String) -> SecurityAssertResult
check_fee_ratio(self: Self, String, String, String) -> SecurityAssertResult
}


#Layer 2 (Extended): Cryptography Primitives

CryptoAssert includes production-grade implementations of core cryptographic primitives used across major blockchain ecosystems.

#ECDSA secp256k1 — protocol/ecdsa_secp256k1.mbt (443 lines)

Full FIPS 186-4 ECDSA implementation over the secp256k1 curve:

  • Elliptic curve: y² = x³ + 7 over F_p with SEC 2 secp256k1 parameters
  • Key generation: Deterministic private key derivation via SHA-256
  • Signing: ECDSA signature with RFC 6979 deterministic nonce (HMAC-SHA256 based)
  • Verification: Full ECDSA verification with curve point validation
  • Low-s enforcement: BIP 62 compliant low-s values
  • Point operations: Point addition, doubling, scalar multiplication (double-and-add)
  • All big integer arithmetic uses MoonBit BigInt for arbitrary precision

// Generate a key pair — returns Option, unwrap with match
let (priv_hex, pub_hex) = match @protocol.generate_key_pair(seed_bytes) {
Some(pair) => pair
None => abort("key generation failed")
}

// Sign a message — returns Option
let signature = @protocol.ecdsa_sign(sha256(message), private_key_bytes)

// Verify a signature
let valid = @protocol.ecdsa_verify(message_hash, signature, public_key_bytes)

#SHA-256 — protocol/sha256.mbt (169 lines)

FIPS 180-4 compliant SHA-256 implementation with explicit 32-bit masking for cross-platform consistency (wasm32, wasm64, native targets). Includes sha256d (double SHA-256, Bitcoin standard).

#Keccak-256 — protocol/keccak256.mbt (193 lines)

Keccak-256 implementation for Ethereum compatibility. Keccak-256 is not SHA3-256 — they differ in padding. This implementation uses:
  • Keccak-f[1600] permutation (24 rounds)
  • State: 1600 bits = 25 × 64-bit lanes
  • Rate: 1088 bits = 136 bytes
  • FixedArray temporaries to minimize GC pressure inside the round loop
  • Includes keccak256_to_nibbles for EIP-55 address checksumming
  • Verified against official known-answer vectors (keccak256("") = c5d24601…, keccak256("abc") = 4e03657a…) in the test suite

#Base58 / Base58Check — protocol/base58.mbt (161 lines)

Bitcoin-compatible Base58 encoding with:
  • Bitcoin alphabet (excludes 0, O, I, l)
  • O(1) character lookup via precomputed 128-slot table
  • base58_check_verify — full Base58Check checksum validation (SHA-256d)
  • base58_check_validate — address validation with version byte and payload length checks

#Bech32 / Bech32m — protocol/bech32.mbt (233 lines)

BIP 173 and BIP 350 compliant Bech32/Bech32m implementation:
  • Bech32 (SegWit v0, checksum constant = 1)
  • Bech32m (Taproot, checksum constant = 0x2bc830a3)
  • BCH polynomial division with generator coefficients
  • cosmos_verify for Cosmos/IBC ecosystem addresses
  • Case-insensitive lookup with mixed-case rejection

#EIP-712 — protocol/eip712.mbt (229 lines)

Ethereum EIP-712 typed structured data hashing:
  • compute_type_hash — keccak256(encodeType(def))
  • encode_and_hash_struct — structHash = keccak256(typeHash || padded fields)
  • compute_eip712_message_hash — messageHash = keccak256(\x19\x01 || domainHash || structHash)
  • Field type support: address, uint256, bytes32, string, bool
  • parse_type_definition with bracket-aware field splitting
  • Left-padding to 32 bytes per EIP-712 spec

#RLP — protocol/rlp.mbt (177 lines)

Ethereum Yellow Paper Appendix B RLP decoder:
  • Single-pass cursor traversal (no redundant slicing)
  • Supports short/long strings and short/long lists
  • rlp_decode, rlp_as_string, rlp_as_list API
  • bytes_to_hex helper for RLP-to-hex conversion

#Sparse Merkle Tree — protocol/smt.mbt (171 lines)

256-layer Sparse Merkle Tree verifier using Keccak-256:
  • SMTProof struct with side nodes, bit mask, and value
  • verify_smt_inclusion — verify key/value exists under root hash
  • verify_smt_exclusion — verify key does not exist in tree
  • Precomputed 257-entry nil-hash table (zero-leaf to depth-256)
  • Proof validation: checks that non-empty side nodes match expected nil-hashes


#Layer 2 (Extended): E2E Production Verifier

File: protocol/e2e_verifier.mbt (656 lines)

The E2E production verifier provides a complete, production-ready transaction verification pipeline implementing the TransactionVerifier trait with real ECDSA secp256k1 operations.

#Transaction Signing & Verification

// Sign a transaction with ECDSA secp256k1 — returns Option
let signed_tx = match @protocol.sign_transaction(tx, private_key_hex) {
Some(t) => t
None => abort("signing failed")
}

// Verify a signed transaction's signature.
// The signing message serializes txid, chain_id, nonce, inputs, outputs and
// fee — tampering with chain_id or nonce after signing invalidates the
// signature (EIP-155 style binding).
let valid = @protocol.verify_signed_transaction(signed_tx)

#Fund Conservation

// BigInt-based fund conservation verification (no overflow limit)
let conserved = @protocol.verify_fund_conservation(tx)

#Replay Protection

// verify_replay_protection(tx, expected_chain_id, min_locktime,
// expected_nonce? : UInt64? = None)
let result = @protocol.verify_replay_protection(
tx,
1, // expected_chain_id — tx.chain_id must equal this (0 disables the match)
0, // min_locktime
expected_nonce=Some(7UL), // next valid account nonce
)
// Returns: Safe | Warning(msg) | Unsafe(msg)
// Real field checks:
// tx.chain_id missing / zero / ≠ expected_chain_id → Unsafe (cross-chain replay)
// tx.nonce missing → Unsafe
// tx.nonce < expected_nonce → Unsafe (consumed-nonce replay)
// tx.nonce > expected_nonce → Warning (nonce gap)

#Contract Safety Audit

let result = @protocol.audit_contract_safety(
has_mint, mint_controlled, has_pause, safety_modes
)

#Bridge Security Audit

let result = @protocol.audit_bridge_security(
contract_address,
target_chain_id,
min_amount,
max_amount,
min_validators,
multisig_threshold, // required signatures
total_validators, // validator set size
)
// Multisig threshold rules (see multisig_quorum_intersection_lemma):
// threshold == 0 / > total / not a majority (2t ≤ n) → Unsafe
// below BFT quorum ⌊2n/3⌋+1 (3t < 2n+1) → Warning

#Full Transaction Validation Pipeline

let result = @protocol.full_tx_validation_pipeline(
tx,
expected_chain_id,
min_locktime,
amount_min,
amount_max,
fee_max_ratio,
expected_nonce=Some(7UL), // optional
)
// Pipeline:
// 1. ECDSA signature verification
// 2. Fund conservation check
// 3. Replay protection (real chain_id / nonce checks)
// 4. Amount range validation (all outputs)
// 5. Fee ratio check

#TransactionVerifier Trait Implementation

E2ETransactionVerifier implements all 5 TransactionVerifier methods:

MethodImplementation
validate_transactionSignature → Fund conservation → Input/output validation
verify_signatureECDSA secp256k1 verification from SignatureVerifySpec
compute_txidSHA-256 of serialized tx (version, inputs, outputs, locktime, chain_id, nonce, fee)
estimate_feeRate-per-vbyte × virtual size (low/medium/high/default strategies)
validate_token_transferNon-empty from/to, positive amount, chain ID, precision check


#Layer 2 (Extended): Lemma Runtime Bridge

File: protocol/transfer_runtime.mbt (225 lines)

The lemma runtime bridge maps each formal lemma from proof/transfer_proof.mbtp to a runtime-callable verification function. Each function:

  1. Implements a runtime check exactly equivalent to its corresponding lemma
  2. References the lemma name in docstrings via proof_require
  3. Uses BigInt internally for overflow-safe comparisons

Runtime FunctionCorresponding LemmaDescription
check_fund_conservationfund_conservation_invtotal_in == total_out + fee
check_overflow_safeoverflow_safe_lemmaa + b ≥ a and a + b ≥ b
check_fee_nonnegative(runtime-only — the old fee_nonnegative_lemma is unprovable under the idealized-integer model and was removed)fee ≥ 0 (BigInt version)
check_no_inflationno_inflation_lemmatotal_in ≥ total_out
check_value_monotonicvalue_monotonic_lemmaMonotonicity of (in − out − fee)
check_transfer_state_invarianttransfer_state_invariantpre_total == post_total
check_transfer_correctnesstransfer_correctness_theoremComposite: no inflation + state invariant
verify_transfer_balance_chainConservation lemma familyMulti-input/output balance chain with overflow guards
check_transfer_bigintConservation lemma familyBigInt version — recommended for production


#Layer 2 (Extended): BigMath & UInt256

#BigMath — protocol/bigmath.mbt (96 lines)

BigInt-based amount parsing and operations designed for Ethereum's 18-decimal precision, where UInt64 overflows above ~18.44 ETH:

FunctionDescription
parse_bigint_amountParse decimal string to BigInt (arbitrary length)
bigint_fund_conservationtotal_in == total_out + fee
bigint_no_inflationtotal_in >= total_out after conservation check
bigint_transfer_correctnessComposite correctness check
sum_bigint_amountsAccumulate amount strings with error propagation
bigint_verify_transfer_balance_chainFull multi-input/output verification

#UInt256 — protocol/uint256.mbt (325 lines)

Full 256-bit unsigned integer implementation (4 × UInt64 limbs, little-endian):

OperationSignatureDescription
from_uint64(UInt64) -> UInt256Construct from 64-bit value
add(UInt256, UInt256) -> (UInt256, Bool)Addition with overflow flag
sub(UInt256, UInt256) -> (UInt256, Bool)Subtraction with underflow flag
mul(UInt256, UInt256) -> (UInt256, Bool)Long multiplication with overflow detection
div_mod(UInt256, UInt256) -> (UInt256, UInt256)?Division with remainder
to_string(UInt256) -> StringDecimal string representation
from_string(String) -> UInt256?Parse from decimal string
is_zero(UInt256) -> BoolZero check


#Layer 2 (Extended): Additional Modules

#SimpleSecurityVerifier — protocol/simple_security_verifier.mbt (439 lines)

A complete, production-ready SecurityVerifier implementation with BigInt-based numerical validation. Construct it with @protocol.SimpleSecurityVerifier::new().

  • Replay protection: real tx.chain_id validation (missing / zero / mismatch vs expected_chain_idUnsafe), real tx.nonce validation (missing → Unsafe; < expected_nonceUnsafe replay; > expected_nonceWarning gap), locktime requirement, nonce-reuse warning
  • Contract safety: Mint control audit, burn control verification, pause capability with ownership/timelock guards, single-owner pattern detection
  • Bridge security: multisig threshold verification (multisig_threshold-of-total_validators: zero / over-count / non-majority → Unsafe, sub-BFT ⌊2n/3⌋+1 → Warning, backed by multisig_quorum_intersection_lemma), validator set size analysis, amount range consistency, address format validation, chain ID validation
  • Amount range: BigInt-based min/max boundary checks with structured results
  • Fee ratio: Three-tier classification (Normal/Elevated/Excessive) with scaled BigInt comparison

All parse failures convert to SecurityAssertResult::Unsafe — no panics at runtime.


#Layer 3: Business Assertion Layer

Directory: business/

The business layer provides how to verify — concrete assertion functions that consume trait implementations and return structured results or raise typed errors.

#Address & Signature Assertions (address_assert.mbt)

FunctionSignatureDescription
assert_address_formatfn[V: AddressVerifier](V, String, AddressType) -> AssertionResult raise AssertErrorValidates address string against chain format spec
assert_sig_scheme_compatiblefn(SignatureScheme, AddressType) -> AssertionResult raise AssertErrorPure function; checks mathematical compatibility
assert_tx_signaturefn[V: TransactionVerifier](V, TransactionSpec) -> AssertionResult raise AssertErrorDelegates to verifier for full ECDSA signature check

#Security Assertions (security_assert.mbt)

FunctionSignatureDescription
assert_transaction_balancefn(TransactionSpec) -> AssertionResult raise AssertErrorFund conservation: Σinputs ≡ Σoutputs + fee
assert_replay_protectionfn[V: SecurityVerifier](V, TransactionSpec, ReplayProtectionSpec) -> AssertionResult raise AssertErrorReal nonce/chain_id value checks + timestamp
assert_amount_in_rangefn[V: SecurityVerifier](V, String, String, String) -> AssertionResult raise AssertErrorValidates amount ∈ [min, max]
assert_fee_within_ratiofn[V: SecurityVerifier](V, String, String, String) -> AssertionResult raise AssertErrorValidates fee/amount ≤ max_ratio
assert_contract_safetyfn[V: SecurityVerifier](V, TokenSafetySpec) -> AssertionResult raise AssertErrorChecks mint/burn/pause/upgrade controls
assert_bridge_securityfn[V: SecurityVerifier](V, BridgeSpec) -> AssertionResult raise AssertErrorChecks multisig threshold, validator set, amount bounds, chain ID

#Balance Verification

The assert_transaction_balance function directly corresponds to the formally proven fund_conservation_inv predicate (proof/transfer_proof.mbtp). It performs:

  1. Parse all input/output amount strings with overflow detection.
  2. Accumulate with overflow guard: a + b ≥ a check at each addition.
  3. Verify total_in ≡ total_out + fee with overflow check on total_out + fee.
  4. Raise AmountImbalance with exact values if conservation fails.

This is the runtime embodiment of the compile-time-proven theorem.


#Type System Reference

#Enumerated Types

#AddressType — 9 blockchain address formats

VariantChainDescription
BtcP2pkhBitcoinPay-to-Public-Key-Hash
BtcP2shBitcoinPay-to-Script-Hash
BtcBech32BitcoinSegWit (native)
BtcBech32mBitcoinTaproot
EthEthereum40-char hex (lowercase)
EthEip55EthereumEIP-55 mixed-case checksum
SolanaSolanaEd25519 base58
TronTronBase58Check
CosmosCosmosBech32

#SignatureScheme — 5 cryptographic signature algorithms

VariantCurve / SchemePrimary Use
EcdsaSecp256k1secp256k1Bitcoin, Ethereum, Tron
Ed25519Edwards 25519Solana, Cosmos, Tron
Sr25519Schnorr/RistrettoPolkadot, Substrate
Bls12381BLS12-381Filecoin, Ethereum 2.0
SchnorrSecp256k1secp256k1 SchnorrBitcoin (BIP-340)

#HashScheme — 6 hash algorithms

VariantOutput (bytes)Common Use
Sha25632Bitcoin, general
Keccak25632Ethereum
Blake2b25632Zcash, general
Blake2s25632Lightweight
Ripemd16020Bitcoin addresses
Sha256d32Bitcoin (double SHA-256)

#ChecksumType — 4 checksum strategies

VariantDescription
NoneNo checksum (e.g., raw Ethereum hex)
Base58CheckBitcoin-style 4-byte checksum
Bech32BCH-encoded checksum
Eip55Mixed-case hex (Ethereum)

#TxStatus — 5 transaction lifecycle states

Pending, Confirmed, Failed, Dropped, Unknown

#TxValidationCode — 9 transaction validation outcomes

Valid, InvalidSignature, InvalidInput, InvalidOutput, InsufficientFee, DoubleSpend, Expired, AmountMismatch, ChainIdMismatch

#SecurityAssertResult — 3-tier security verdict

Safe, Warning(String), Unsafe(String)

#ContractSafetyMode — 5 smart contract safety controls

Owned, TimeLock, Pausable, Upgradeable, RateLimited

#Struct Types

StructFieldsPurpose
AddressLengthRangemin_chars: UInt, max_chars: UInt, byte_len: UIntCharacter-level and byte-level length constraints
AddressFormatSpecty, length, checksum_type, prefix_req, charset, descriptionComplete address format descriptor
PrecisionSpecdecimals: UInt, unit_vals: String, symbol: StringToken decimal precision
TxInputSpectxid, vout, script_sig, amount_strTransaction input descriptor
TxOutputSpecaddress, amount_str, script_pubkey, is_changeTransaction output descriptor
FeeSpecrate_str, total_str, unit_descFee rate and total
TransactionSpecversion, inputs[], outputs[], locktime, fee, txid, nonce: UInt64?, chain_id: UInt?, sig_scheme, signature_hex?, public_key_hex?Complete transaction descriptor; nonce/chain_id are serialized into the signing message and txid
TokenTransferSpecfrom, to, contract, amount_str, chain_id, precisionERC-20 style token transfer
SignatureVerifySpecmessage_hex, signature_hex, public_key_hex, schemeSignature verification request
ReplayProtectionSpecrequire_nonce, expected_nonce: UInt64?, require_chain_id, expected_chain_id: UInt, require_timestamp, max_nonce_reuseReplay attack guard config with real expected-value checks
TokenSafetySpechas_mint, mint_controlled, has_burn, has_pause, safety_modes[]Token security audit spec
BridgeSpeccontract_address, target_chain_id, min_amount_str, max_amount_str, has_validator_set, min_validators, multisig_threshold, total_validatorsCross-chain bridge validator & multisig config
SMTProofside_nodes: Array[Bytes], bit_mask: FixedArray[Bool], value: BytesSparse Merkle Tree proof
Eip712Fieldname: String, ty: StringEIP-712 typed data field
Eip712TypeDefinitionname: String, fields: Array[Eip712Field]EIP-712 type definition
UInt256v0, v1, v2, v3: UInt64256-bit unsigned integer (4 × 64-bit limbs)
RlpItemString(Bytes) or List(Array[RlpItem])RLP-encoded item
ECPointx: BigInt, y: BigIntElliptic curve point (secp256k1)


#Trait Reference

#AddressVerifier

MethodReturnsDescription
address_format_spec(self, AddressType)AddressFormatSpecGet format spec for address type
valid_address_length_range(self, AddressType)AddressLengthRangeGet char/byte length bounds
validate_checksum(self, AddressType, String)BoolVerify address checksum
address_precision(self, SignatureScheme)PrecisionSpecGet native currency precision
hash_output_length(self, HashScheme)UIntGet hash output byte length
is_valid_hash_size(self, Bytes, HashScheme)BoolCheck hash size matches scheme

#TransactionVerifier

MethodReturnsDescription
validate_transaction(self, TransactionSpec)TxValidationCodeValidate entire transaction
verify_signature(self, SignatureVerifySpec)BoolVerify cryptographic signature
compute_txid(self, TransactionSpec)StringCompute transaction ID
estimate_fee(self, TransactionSpec, String)FeeSpecEstimate transaction fee
validate_token_transfer(self, TokenTransferSpec)TxValidationCodeValidate token transfer

#SecurityVerifier

MethodReturnsDescription
check_replay_protection(self, TransactionSpec, ReplayProtectionSpec)SecurityAssertResultCheck replay attack guards
check_contract_safety(self, TokenSafetySpec)SecurityAssertResultAudit token contract safety
check_bridge_security(self, BridgeSpec)SecurityAssertResultAudit cross-chain bridge config
validate_amount_range(self, String, String, String)SecurityAssertResultCheck amount ∈ [min, max]
check_fee_ratio(self, String, String, String)SecurityAssertResultCheck fee/amount ratio


#Error Model

All errors use MoonBit's suberror mechanism — checked error subtypes that the compiler enforces at call sites. Each variant carries domain-specific payload data.

#suberror AssertError — 10 variants

VariantPayloadWhen Raised
InvalidPrefix(String, String) — address, expected prefixAddress prefix mismatch
InvalidLength(String, UInt, UInt) — address, min, maxAddress string length out of range
InvalidCharacter(String, Char) — address, illegal charCharacter not in allowed charset
ChecksumMismatch(String) — addressChecksum validation failure
AmountImbalance(String, String, String) — total_in, total_out, feeFund conservation violation
IncompatibleScheme(SignatureScheme, AddressType)Signature scheme incompatible with address type
SignatureVerificationFailed(String) — txidCryptographic signature check failed
NumericParseError(String) — parse detailAmount string parsing or overflow
HashLengthMismatch(UInt, UInt) — actual, expectedHash output size mismatch
SecurityCheckFailed(String) — reasonGeneric security check failure

#Handling Errors

let verifier = @protocol.DefaultVerifier::new()
let result = try @business.assert_address_format(
verifier,
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", // official EIP-55 test vector
@protocol.AddressType::EthEip55,
) catch {
@protocol.AssertError::InvalidPrefix(address, expected_prefix) =>
// wrong prefix — e.g., missing "0x"
...
@protocol.AssertError::InvalidLength(address, min, max) =>
// address too short or too long
...
@protocol.AssertError::InvalidCharacter(address, c) =>
// illegal character in address
...
@protocol.AssertError::ChecksumMismatch(address) =>
// checksum validation failed
...
_ =>
// unexpected error
...
}


#Quick Start

#Prerequisites

  • MoonBit toolchain (latest stable)
  • Why3 and Z3 (only required for moon prove; set WHY3DATA/WHY3LIB from why3 --print-datadir / why3 --print-libdir, optionally Z3PATH)

#Run the Examples

Every snippet below is excerpted from the runnable example program in examples/main.mbt, which CI compiles and runs on every push (asserting on its output), so the documentation can never drift from the published API again:

moon run examples --target native

#Adding CryptoAssert to Your Project

moon add Kali-Leo/moonbit-CryptoAssert

Or add to moon.mod manually:

import { "Kali-Leo/moonbit-CryptoAssert@0.2.0", }

Then in your moon.pkg:

import {
"Kali-Leo/moonbit-CryptoAssert/protocol",
"Kali-Leo/moonbit-CryptoAssert/business",
}

#Example 1: Address Format Validation

/// Validate an Ethereum EIP-55 checksummed address
/// (from examples/main.mbt, section [5])
let verifier = @protocol.DefaultVerifier::new()
try {
let _ = @business.assert_address_format(
verifier,
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", // official EIP-55 vector
@protocol.AddressType::EthEip55,
)
println("checksummed address → Valid")
} catch {
_ => println("checksummed address → rejected")
}
// A corrupted (all-lowercase) form of the same address is rejected with
// ChecksumMismatch. Note: the widely-circulated example address
// 0x742d35Cc… actually has an INVALID EIP-55 checksum — this library now
// correctly rejects it.

#Example 2: Signature Scheme Compatibility (Pure Function)

/// ECDSA + Bitcoin P2PKH is compatible; BLS12-381 + Bitcoin is NOT
try {
let ok = @business.assert_sig_scheme_compatible(
@protocol.SignatureScheme::EcdsaSecp256k1,
@protocol.AddressType::BtcP2pkh,
)
// ok == AssertionResult::Valid
let _ = @business.assert_sig_scheme_compatible(
@protocol.SignatureScheme::Bls12381,
@protocol.AddressType::BtcP2pkh,
)
} catch {
@protocol.AssertError::IncompatibleScheme(_s, _a) =>
// _s == Bls12381, _a == BtcP2pkh
println("incompatible pair rejected")
_ => println("unexpected error")
}

#Example 3: E2E Production Transaction Pipeline

/// Full E2E validation: sign → verify sig → fund conservation → replay → fee
/// (from examples/main.mbt, section [4])
let result = @protocol.full_tx_validation_pipeline(
signed_tx,
1, // expected_chain_id
0, // min_locktime
"1", // amount_min
"100000000000000000000", // amount_max
"5", // fee_max_ratio (%)
expected_nonce=Some(7UL),
)
// result == Safe | Warning(msg) | Unsafe(msg)

#Example 4: ECDSA Signature Generation & Verification

/// (from examples/main.mbt, sections [1] and [4])
/// Generate key pair — Option, unwrap with match
let seed : Bytes = b"cryptoassert-demo-seed-32bytes!!"
let (priv_hex, pub_hex) = match @protocol.generate_key_pair(seed) {
Some(pair) => pair
None => abort("key generation failed")
}

/// Sign a transaction (tx carries nonce: Some(7UL), chain_id: Some(1)) — Option
let signed_tx = match @protocol.sign_transaction(tx, priv_hex) {
Some(t) => t
None => abort("signing failed")
}

/// Verify the signed transaction
let valid = @protocol.verify_signed_transaction(signed_tx)
// valid == true

/// The signature binds chain_id and nonce: re-targeting the same signed tx to
/// another chain makes verification fail
// verify_signed_transaction(tx_with_chain_id_56) == false

/// Verify fund conservation
let conserved = @protocol.verify_fund_conservation(signed_tx)
// conserved == true

#Example 5: Replay Protection — Real nonce / chain ID Checks

/// (from examples/main.mbt, section [2]; signed_tx carries
/// nonce: Some(7UL), chain_id: Some(1))

/// Legitimate: chain matches, nonce equals the account's next nonce
let ok = @protocol.verify_replay_protection(
signed_tx, 1, 0, expected_nonce=Some(7UL),
)
// ok == Safe

/// Replay attack: account nonce has advanced to 8 — replaying the old
/// nonce-7 transaction is rejected
let replayed = @protocol.verify_replay_protection(
signed_tx, 1, 0, expected_nonce=Some(8UL),
)
// replayed == Unsafe("Nonce 7 already consumed (account nonce is 8) — replay detected")

/// Cross-chain replay: tx is bound to chain 1, submitted to chain 56
let wrong_chain = @protocol.verify_replay_protection(signed_tx, 56, 0)
// wrong_chain == Unsafe("Chain ID mismatch: tx carries 1, expected 56 — cross-chain replay")

#Example 6: Bridge Multisig Audit

/// (from examples/main.mbt, section [3])
/// 7-of-9 satisfies the BFT quorum ⌊2n/3⌋+1 = 7 → Safe
let safe_bridge = @protocol.audit_bridge_security(
"0x1234567890abcdef1234567890abcdef12345678",
137, "1000", "100000000000",
7, // min validators
7, // multisig threshold
9, // total validators
)
// safe_bridge == Safe

/// 5-of-9 is a majority but below the BFT quorum → Warning
/// 4-of-9 is not even a majority — minority collusion could move funds → Unsafe

/// BigInt version — no overflow limit, safe for Ethereum-scale amounts
/// check_transfer_bigint(input_amounts, output_amounts, fee_str) — positional
let conserved = @protocol.check_transfer_bigint(
["1000000000000000000000", "500000000000000000000"],
["1499000000000000000000", "1000000000000000000"],
"1000000000000000000",
)
// conserved == true (1500 = 1499 + 1 + 1)

#Example 8: Sparse Merkle Tree Verification

/// Verify inclusion proof
let valid = @protocol.verify_smt_inclusion(
root_hash,
key_bytes,
value_bytes,
proof,
)

/// Verify exclusion proof (key not in tree)
let absent = @protocol.verify_smt_exclusion(
root_hash,
key_bytes,
proof,
)


#API Reference

The sections above (Type System Reference, Trait Reference, and Layer 3: Business Assertion Layer) constitute the complete API catalog for this library. For an interactive HTML API reference with cross-linked types and search, run:

moon doc

This generates browsable documentation in the _build/doc/ directory.


#Extending the Library

#Implementing a Custom AddressVerifier

To add support for a new blockchain (e.g., Polkadot), implement all 6 methods of the AddressVerifier trait:

struct PolkadotVerifier {}

impl @protocol.AddressVerifier for PolkadotVerifier with
address_format_spec(self, ty) {
match ty {
// ... implement all 9 AddressType variants
}
}

// Also required:
// valid_address_length_range(self, ty)
// validate_checksum(self, ty, address)
// address_precision(self, scheme)
// hash_output_length(self, scheme)
// is_valid_hash_size(self, hash, scheme)

The compiler will not compile if any method is missing. This is enforced statically — no runtime NotImplemented error can ever occur.

#Adding New Chain Support

The recommended workflow:

  1. Implement AddressVerifier for your chain's address format.
  2. Implement TransactionVerifier for your chain's transaction structure.
  3. Implement SecurityVerifier for your chain's security model.
  4. Write QuickCheck property tests (see business/business_test.mbt for patterns).
  5. Optionally, extend transfer_proof.mbtp with chain-specific invariants and prove them with moon prove.


#Testing & Quality Assurance

#Test Coverage

Test FileDescriptionCategory
protocol/spec_easy_test.mbtEnum counts, struct construction, SecurityAssertResult variants, Keccak-256 known-answer vectorsUnit
protocol/spec_difficult_test.mbtMulti-input/output TX, ERC-20 transfer, BTC/SAT precision, security specs, bridge config, EIP-55/Cosmos address format, SignatureVerifySpec, replay-protection behavior (consumed nonce / nonce gap / chain-ID mismatch / zero chain-ID), bridge multisig thresholds (non-majority / sub-BFT / over-count / zero / BFT-pass), E2E sign→verify→pipeline integration incl. chain_id/nonce signature-bindingIntegration
business/business_test.mbtQuickCheck property bombardment on signature scheme × address type compatibility, EIP-55 official-vector validationProperty

#Test Execution Summary

  • 90 tests total across all test files
  • All 90 tests pass with moon test --target native and moon test --target wasm-gc
  • moon check --deny-warn passes with zero warnings

#QuickCheck Property Bombardment

The business test suite uses moonbitlang/quickcheck@0.14.0 to systematically verify properties through randomized input generation:

TestRoundsDescription
Compatible pairs200 eachAll 11 compatible (scheme, address_type) pairs
Incompatible pairs200 eachAll 34 incompatible pairs (5×9−11=34)
Error payload correctnessAssertionVerify IncompatibleScheme carries correct (scheme, addr)
Deterministic / idempotence500Same input → same output (impurity guard)

Total: 9,100+ property check rounds across the full 5×9 compatibility matrix.

#Test Execution

# Type checking moon check # Run all tests moon test --target native # Strict mode moon test --deny-warn # Update test snapshots moon test --update # Run formal theorem proofs (requires Why3 + Z3) moon prove


#Security Considerations

#Compile-Time Guarantees

  1. Zero NotImplemented: All traits use self: Self with fn[V: Trait] syntax. The compiler rejects any incomplete trait implementation at compile time. This eliminates a class of vulnerabilities where runtime "not implemented" branches could be exploited in smart contract contexts.

  2. Overflow Protection: The overflow_safe_lemma is proven by Z3 (under its explicit non-negativity and bound premises — see the Layer 1 modeling note) and enforced at runtime in bridging functions. Every accumulation is guarded by overflow checks.

  3. Fund Conservation: The conservation lemmas are verified by the SMT solver for all values satisfying their stated proof_require premises. Both UInt64 and BigInt runtime implementations are provided; the BigInt path enforces the range premises on real data.

#Numeric Domain: UInt64 vs BigInt

The current formal proofs operate on UInt64 (max ≈ 1.84 × 10¹⁹), which is sufficient for Bitcoin (Satoshi), Solana (Lamport), and most UTXO-based chains where individual UTXO values fit within 64 bits.

For Ethereum and EVM-compatible chains that use 256-bit integers, the library provides:

  • bigmath.mbt: BigInt-based amount operations (no overflow limit)
  • uint256.mbt: Full 256-bit unsigned integer (add/sub/mul/div)
  • check_transfer_bigint: Production-recommended BigInt verification

#Supported Cryptographic Primitives

PrimitiveImplementationStandard
ECDSA secp256k1450 lines, real curve opsFIPS 186-4
SHA-256169 linesFIPS 180-4
Keccak-256187 lines, Keccak-f[1600]Ethereum
Base58Check161 lines, O(1) lookupBitcoin
Bech32/Bech32m233 lines, BCH polynomialBIP 173/350
EIP-712230 lines, structHash + messageHashEthereum
RLP177 lines, single-pass cursorEthereum Yellow Paper
SMT171 lines, 256-layer, precomputed nil-hashesSparse Merkle Tree

#Attack Surface Analysis

ClassMitigationMechanism
Integer overflow (inflation)Provenoverflow_safe_lemma + Z3 + runtime guard
Integer overflow (fee bypass)Provenfund_conservation_inv + Z3
Incomplete trait implPreventedCompile-time trait completeness check
Address spoofing (wrong prefix)CaughtInvalidPrefix error
Address spoofing (invalid char)CaughtInvalidCharacter error
Checksum bypassCaughtChecksumMismatch error
Signature scheme mismatchCaughtIncompatibleScheme error
Double-spendCaughtTxValidationCode::DoubleSpend
Replay attack (consumed nonce)CaughtReal tx.nonce vs expected_nonce check + nonce_replay_rejection_lemma
Replay attack (cross-chain)CaughtReal tx.chain_id vs expected_chain_id check + EIP-155 style signature binding
Cross-chain bridge exploitCaughtmultisig_threshold-of-total_validators verification (majority + BFT) + min_validators check
Fake signature (hash-as-sig)PreventedReal ECDSA secp256k1 verification

#Production Hardening Recommendations

  1. Use BigInt for high-value transfers — Prefer check_transfer_bigint over UInt64-based functions for Ethereum and EVM-compatible chains.

  2. Use a secure random number generator — The QuickCheck LCG in tests is deterministic by design. Production key generation should use OS-provided CSPRNGs.

  3. Add chain-specific invariants — Extend proof/transfer_proof.mbtp with chain-specific theorems (e.g., staking conservation, slashing invariants).

  4. Audit custom verifiers — While the trait system guarantees completeness, the semantic correctness of custom SecurityVerifier implementations is the integrator's responsibility.


#Build, Prove & Test

#Development Commands

# ─── Type Checking ────────────────────────────────── moon check # Full project type check moon check --deny-warn # Strict mode # ─── Testing ──────────────────────────────────────── moon test --target native # Run all 90 tests moon test --deny-warn # Strict mode moon test --update # Update test snapshots # ─── Formal Theorem Proving (Why3 + Z3 required) ─── moon prove # Verify all 14 lemmas in proof/transfer_proof.mbtp # ─── Build & Run Examples ─────────────────────────── moon build # Build the package moon run examples --target native # Run the executable examples moon build --target wasm-gc # Build for wasm-gc backend # ─── Documentation ────────────────────────────────── moon doc # Generate API documentation # ─── Formatting ───────────────────────────────────── moon fmt # Format all source files # ─── Publishing ───────────────────────────────────── moon publish --dry-run # Check publish readiness # ─── Full Validation Pipeline ─────────────────────── moon check --deny-warn && moon prove && moon test --target native && moon run examples --target native


#CI/CD

The project uses GitHub Actions for continuous integration:

#CI Pipeline (.github/workflows/ci.yml) — 4 jobs, all real

JobWhat it runsFailure condition
checkmoon check --deny-warn + moon fmt && git diff --exit-codeAny warning or unformatted file
buildmoon build + moon run examples --target native, then greps the output for key results (signature valid: true, replayed old nonce → Unsafe, 7-of-9 (BFT quorum) → Safe, …)Build error, runtime error, or missing expected output
testmoon test -v --target native + moon test --target wasm-gcAny of the 90 tests failing
proveInstalls Why3 (apt) + Z3 4.15.3 (GitHub release), sets WHY3DATA/WHY3LIB/Z3PATH, runs moon prove and asserts 1 of 1 packages proved / 14 goals provedAny lemma failing to prove, or the proof run not actually executing

#Published Package

  • Package: Kali-Leo/moonbit-CryptoAssert v0.2.0
  • Registry: mooncakes.io
  • Repository: GitHub
  • License: Apache 2.0


#Comparison with Industry Alternatives

FeatureCryptoAssert (MoonBit)OpenZeppelin (Solidity)CosmWasm (Rust)Cosmos SDK (Go)
Formal proof (SMT)moon prove (Why3/Z3)✗ (requires Echidna/Certora)✗ (external tools)
Compile-time trait completeness✓ (zero NotImplemented)✗ (runtime revert)✓ (trait bounds)✗ (interface checks)
Static dispatch✗ (dynamic calls)✓ (monomorphization)✗ (interface dispatch)
Structured errors✓ (10 suberror variants)Partial (custom errors)✓ (thiserror/anyhow)✗ (string errors)
Property-based testing✓ (QuickCheck built-in)Partial (Foundry fuzz)✓ (proptest)
Multi-chain coverage9 chainsEthereum ecosystemCosmos ecosystemCosmos ecosystem
Proof-carrying code
Real ECDSA secp256k1✓ (450 lines, FIPS 186-4)✓ (via precompiles)✓ (k256 crate)✓ (tendermint)
EIP-712 support
Sparse Merkle Tree✓ (ICS-23)
Lemma→Runtime bridge
Gas optimizationN/A (off-chain)Critical concernModerate concernModerate concern

Key differentiator: CryptoAssert ships proof-carrying code — mathematical theorems about fund conservation, replay protection, and bridge multisig quorums are verified by an SMT solver via moon prove on every CI run, with the honest modeling premises documented alongside. It also provides the broadest suite of built-in cryptographic primitives (ECDSA, SHA-256, Keccak-256, Base58, Bech32, EIP-712, RLP, SMT) among multi-chain assertion libraries.


#Changelog

#0.2.0 (2026-07-30)

Response to the OSC 2026 acceptance review — every finding addressed with code, proofs, and CI enforcement:

Core capability completion
  • Real replay protection: TransactionSpec gains nonce : UInt64? and chain_id : UInt?; ReplayProtectionSpec gains expected_nonce / expected_chain_id. check_replay_protection now validates the actual field values (missing/zero/mismatched chain ID → Unsafe; missing nonce → Unsafe; consumed nonce → Unsafe; nonce gap → Warning). chain_id and nonce are serialized into the signing message and txid, so signatures cryptographically bind chain and sequence number.
  • Real bridge multisig auditing: BridgeSpec gains multisig_threshold / total_validators; check_bridge_security rejects zero, over-count, and non-majority thresholds and warns below the BFT quorum ⌊2n/3⌋+1.

Formal proofs actually execute
  • Proofs moved to a dedicated proof-enabled package (proof/); moon prove translates them to WhyML and runs Why3 + Z3 for real — 14 goals proved, and a false lemma makes the build fail. The CI prove job asserts the goal count.
  • Lemma set made honest under the idealized-integer model (explicit range premises; unsound fee_nonnegative_lemma removed) and extended with nonce_replay_rejection_lemma, chain_id_binding_lemma, and multisig_quorum_intersection_lemma backing the new runtime checks.

Cryptography fixes
  • Fixed Keccak-256 ρ+π permutation table pairing — the implementation now passes official known-answer vectors, which makes EIP-55 checksum validation actually work (previously it rejected every valid address); validated against the official EIP-55 test vectors.
  • Fixed address charset validation incorrectly checking prefix characters (0x, bc1, cosmos1), which mis-rejected all prefixed addresses.
  • Unified the business-layer signing-message serialization with the protocol layer (previously assert_tx_signature could never verify a transaction signed by sign_transaction).

Docs, examples & CI
  • New runnable examples/ package mirroring the README Quick Start; CI builds and runs it and asserts on its output.
  • README fully synchronized with the published API (all snippets compile against 0.2.0).
  • CI expanded to 4 real jobs: strict check + format, build + run examples, test (native + wasm-gc), prove (Why3/Z3 with asserted goal count).
  • Test suite grown from 62 to 90 tests.

Breaking changes
  • TransactionSpec, ReplayProtectionSpec, BridgeSpec construction requires the new fields; verify_replay_protection, audit_bridge_security, and full_tx_validation_pipeline have new signatures (hence 0.2.0).


#License

Apache 2.0 License — see LICENSE


#Contributing

  1. Implement missing trait methods — the compiler will tell you which ones.
  2. Add new chain support by implementing all three traits.
  3. Extend proof/transfer_proof.mbtp with chain-specific invariants and prove them.
  4. Add tests for new compatibility pairs or cryptographic primitives.
  5. Run moon check --deny-warn && moon prove && moon test --target native before submitting.


Built with MoonBit · Proven with Why3/Z3 via moon prove · Tested with QuickCheck

Making cryptocurrency compliance verification accessible, provably safe, and production-ready.