Multi-chain cryptographic assertion & formal verification library.
Dependencies
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.
| Layer | Directory | Responsibility | Trust Model |
|---|---|---|---|
| Proof | proof/transfer_proof.mbtp | Mathematical conservation / replay / multisig theorems | SMT-solver verified (moon prove, Why3 + Z3), executed for real in CI |
| Protocol | protocol/ | Type definitions, traits, cryptographic primitives, production verifiers, lemma bridges | Compile-time trait resolution |
| Business | business/ | Assertion functions with fn[V: Trait] dispatch | Static dispatch, zero NotImplemented |
┌─────────────────────────────────────────────────────────────────┐
│ 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). │
└─────────────────────────────────────────────────────────────────┘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| File | Lines | Purpose |
|---|---|---|
| protocol/e2e_verifier.mbt | 656 | E2E production verifier + TransactionVerifier impl |
| protocol/ecdsa_secp256k1.mbt | 443 | ECDSA secp256k1 FIPS 186-4 + RFC 6979 |
| protocol/simple_security_verifier.mbt | 439 | SecurityVerifier full implementation |
| proof/transfer_proof.mbtp | 432 | Formal theorem proofs (2 predicates + 14 lemmas) |
| protocol/address_spec.mbt | 376 | 9 address types, 5 sig schemes, 6 hash schemes + DefaultVerifier |
| protocol/uint256.mbt | 325 | UInt256 — 256-bit unsigned integer arithmetic |
| protocol/bech32.mbt | 233 | Bech32/Bech32m + Cosmos verify |
| protocol/eip712.mbt | 229 | EIP-712 typed structured data hashing |
| protocol/transfer_runtime.mbt | 225 | Lemma → Runtime bridges |
| examples/main.mbt | 204 | Runnable examples (executed by CI) |
| protocol/keccak256.mbt | 193 | Keccak-256 (Ethereum) |
| protocol/rlp.mbt | 177 | RLP decoder (Ethereum Yellow Paper) |
| protocol/smt.mbt | 171 | Sparse Merkle Tree verifier |
| protocol/sha256.mbt | 169 | SHA-256 (FIPS 180-4) |
| protocol/base58.mbt | 161 | Base58 + Base58Check |
| protocol/transaction_spec.mbt | 104 | Transaction trait + 6 struct types |
| protocol/bigmath.mbt | 96 | BigInt amount parsing & operations |
| protocol/security_spec.mbt | 75 | 5 trait methods + 4 struct types |
| protocol/assert_result.mbt | 22 | Error domain model (10 suberror variants) |
| business/security_assert.mbt | 157 | 6 security assertion functions |
| business/address_assert.mbt | 150 | 3 address/signature assertion functions |
| Total (non-test) | ~5,037 | — |
options(
"proof-enabled": true,
)# 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| Predicate | Signature | Mathematical 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 |
| Lemma | Premises (proof_require) | Conclusion (proof_ensure) |
|---|---|---|
| no_inflation_lemma | fund_conservation_inv ∧ fee ≥ 0 | total_in ≥ total_out |
| value_monotonic_lemma | in1 ≥ in2 ∧ both ≥ out + fee | (in1 − out − fee) ≥ (in2 − out − fee) |
| overflow_safe_lemma | a ≥ 0 ∧ b ≥ 0 ∧ a ≤ max − b | a + b ≥ a ∧ a + b ≥ b ∧ a + b ≤ max |
| transfer_correctness_theorem | fund_conservation_inv ∧ fee ≥ 0 | No inflation + state invariant |
| Lemma | Premises | Conclusion |
|---|---|---|
| nonce_replay_rejection_lemma | expected == current + 1 ∧ replayed ≤ current | replayed ≠ expected — a consumed nonce can never pass the equality check |
| chain_id_binding_lemma | expected_chain_id ≥ 1 ∧ tx_chain_id == expected_chain_id | tx_chain_id ≥ 1 — a tx that passes the chain-ID check is never chain-unbound |
| Lemma | Premises | Conclusion |
|---|---|---|
| multisig_quorum_intersection_lemma | total ≥ 1 ∧ threshold ≤ total ∧ 3·threshold ≥ 2·total + 1 | 2·threshold ≥ total + 1 ∧ threshold ≥ 1 — any two BFT quorums intersect, so the bridge cannot sign two conflicting messages |
| Lemma | Premises | Conclusion |
|---|---|---|
| model_balance_lemma | locked ≥ 0 ∧ available ≥ 0 ∧ total == locked + available | total ≥ locked ∧ total ≥ available |
| uint256_add_no_overflow_lemma | limbs ≥ 0 ∧ carry_in ≤ 1 ∧ a3 ≤ (max−1) − b3 | sum ≥ each addend ∧ sum ≤ max |
| uint256_sub_no_underflow_lemma | a3 > b3 | a3 ≥ b3 |
| uint256_mul_no_overflow_lemma | limbs ≥ 0 ∧ both ≤ 2³²−1 ∧ carry == 0 | a3·b3 ≥ 0 ∧ a3·b3 ≤ max |
| Lemma | Premises | Conclusion |
|---|---|---|
| bridge_soundness_lemma | fund_conservation_inv ∧ all values in [0, max] | in == out + fee lifts losslessly to BigInt |
| bridge_conservation_lemma | fund_conservation_inv ∧ fee ≥ 0 ∧ out ≤ max − fee | in ≥ out ∧ in == out + fee |
| bigint_extended_conservation_lemma | fund_conservation_inv ∧ fee ≥ 0 ∧ out ≤ max − fee | in ≥ out ∧ in == out + fee |
moon provepub 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
}| Address Type | Chain | Prefix | Length Range | Checksum |
|---|---|---|---|---|
| BtcP2pkh | Bitcoin | 1 | 26–35 chars | Base58Check |
| BtcP2sh | Bitcoin | 3 | 26–35 chars | Base58Check |
| BtcBech32 | Bitcoin SegWit | bc1 | 26–42 chars | Bech32 |
| BtcBech32m | Bitcoin Taproot | bc1p | 26–42 chars | Bech32m |
| Eth | Ethereum | 0x | 42 chars | None |
| EthEip55 | Ethereum (EIP-55) | 0x | 42 chars | EIP-55 |
| Solana | Solana | (none) | 32–44 chars | None |
| Tron | Tron | T | 26–35 chars | Base58Check |
| Cosmos | Cosmos | cosmos1 | 24–45 chars | Bech32 |
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
}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
}// 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)// 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)// BigInt-based fund conservation verification (no overflow limit)
let conserved = @protocol.verify_fund_conservation(tx)// 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)let result = @protocol.audit_contract_safety(
has_mint, mint_controlled, has_pause, safety_modes
)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) → Warninglet 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| Method | Implementation |
|---|---|
| validate_transaction | Signature → Fund conservation → Input/output validation |
| verify_signature | ECDSA secp256k1 verification from SignatureVerifySpec |
| compute_txid | SHA-256 of serialized tx (version, inputs, outputs, locktime, chain_id, nonce, fee) |
| estimate_fee | Rate-per-vbyte × virtual size (low/medium/high/default strategies) |
| validate_token_transfer | Non-empty from/to, positive amount, chain ID, precision check |
| Runtime Function | Corresponding Lemma | Description |
|---|---|---|
| check_fund_conservation | fund_conservation_inv | total_in == total_out + fee |
| check_overflow_safe | overflow_safe_lemma | a + 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_inflation | no_inflation_lemma | total_in ≥ total_out |
| check_value_monotonic | value_monotonic_lemma | Monotonicity of (in − out − fee) |
| check_transfer_state_invariant | transfer_state_invariant | pre_total == post_total |
| check_transfer_correctness | transfer_correctness_theorem | Composite: no inflation + state invariant |
| verify_transfer_balance_chain | Conservation lemma family | Multi-input/output balance chain with overflow guards |
| check_transfer_bigint | Conservation lemma family | BigInt version — recommended for production |
| Function | Description |
|---|---|
| parse_bigint_amount | Parse decimal string to BigInt (arbitrary length) |
| bigint_fund_conservation | total_in == total_out + fee |
| bigint_no_inflation | total_in >= total_out after conservation check |
| bigint_transfer_correctness | Composite correctness check |
| sum_bigint_amounts | Accumulate amount strings with error propagation |
| bigint_verify_transfer_balance_chain | Full multi-input/output verification |
| Operation | Signature | Description |
|---|---|---|
| from_uint64 | (UInt64) -> UInt256 | Construct 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) -> String | Decimal string representation |
| from_string | (String) -> UInt256? | Parse from decimal string |
| is_zero | (UInt256) -> Bool | Zero check |
| Function | Signature | Description |
|---|---|---|
| assert_address_format | fn[V: AddressVerifier](V, String, AddressType) -> AssertionResult raise AssertError | Validates address string against chain format spec |
| assert_sig_scheme_compatible | fn(SignatureScheme, AddressType) -> AssertionResult raise AssertError | Pure function; checks mathematical compatibility |
| assert_tx_signature | fn[V: TransactionVerifier](V, TransactionSpec) -> AssertionResult raise AssertError | Delegates to verifier for full ECDSA signature check |
| Function | Signature | Description |
|---|---|---|
| assert_transaction_balance | fn(TransactionSpec) -> AssertionResult raise AssertError | Fund conservation: Σinputs ≡ Σoutputs + fee |
| assert_replay_protection | fn[V: SecurityVerifier](V, TransactionSpec, ReplayProtectionSpec) -> AssertionResult raise AssertError | Real nonce/chain_id value checks + timestamp |
| assert_amount_in_range | fn[V: SecurityVerifier](V, String, String, String) -> AssertionResult raise AssertError | Validates amount ∈ [min, max] |
| assert_fee_within_ratio | fn[V: SecurityVerifier](V, String, String, String) -> AssertionResult raise AssertError | Validates fee/amount ≤ max_ratio |
| assert_contract_safety | fn[V: SecurityVerifier](V, TokenSafetySpec) -> AssertionResult raise AssertError | Checks mint/burn/pause/upgrade controls |
| assert_bridge_security | fn[V: SecurityVerifier](V, BridgeSpec) -> AssertionResult raise AssertError | Checks multisig threshold, validator set, amount bounds, chain ID |
| Variant | Chain | Description |
|---|---|---|
| BtcP2pkh | Bitcoin | Pay-to-Public-Key-Hash |
| BtcP2sh | Bitcoin | Pay-to-Script-Hash |
| BtcBech32 | Bitcoin | SegWit (native) |
| BtcBech32m | Bitcoin | Taproot |
| Eth | Ethereum | 40-char hex (lowercase) |
| EthEip55 | Ethereum | EIP-55 mixed-case checksum |
| Solana | Solana | Ed25519 base58 |
| Tron | Tron | Base58Check |
| Cosmos | Cosmos | Bech32 |
| Variant | Curve / Scheme | Primary Use |
|---|---|---|
| EcdsaSecp256k1 | secp256k1 | Bitcoin, Ethereum, Tron |
| Ed25519 | Edwards 25519 | Solana, Cosmos, Tron |
| Sr25519 | Schnorr/Ristretto | Polkadot, Substrate |
| Bls12381 | BLS12-381 | Filecoin, Ethereum 2.0 |
| SchnorrSecp256k1 | secp256k1 Schnorr | Bitcoin (BIP-340) |
| Variant | Output (bytes) | Common Use |
|---|---|---|
| Sha256 | 32 | Bitcoin, general |
| Keccak256 | 32 | Ethereum |
| Blake2b256 | 32 | Zcash, general |
| Blake2s256 | 32 | Lightweight |
| Ripemd160 | 20 | Bitcoin addresses |
| Sha256d | 32 | Bitcoin (double SHA-256) |
| Variant | Description |
|---|---|
| None | No checksum (e.g., raw Ethereum hex) |
| Base58Check | Bitcoin-style 4-byte checksum |
| Bech32 | BCH-encoded checksum |
| Eip55 | Mixed-case hex (Ethereum) |
| Struct | Fields | Purpose |
|---|---|---|
| AddressLengthRange | min_chars: UInt, max_chars: UInt, byte_len: UInt | Character-level and byte-level length constraints |
| AddressFormatSpec | ty, length, checksum_type, prefix_req, charset, description | Complete address format descriptor |
| PrecisionSpec | decimals: UInt, unit_vals: String, symbol: String | Token decimal precision |
| TxInputSpec | txid, vout, script_sig, amount_str | Transaction input descriptor |
| TxOutputSpec | address, amount_str, script_pubkey, is_change | Transaction output descriptor |
| FeeSpec | rate_str, total_str, unit_desc | Fee rate and total |
| TransactionSpec | version, 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 |
| TokenTransferSpec | from, to, contract, amount_str, chain_id, precision | ERC-20 style token transfer |
| SignatureVerifySpec | message_hex, signature_hex, public_key_hex, scheme | Signature verification request |
| ReplayProtectionSpec | require_nonce, expected_nonce: UInt64?, require_chain_id, expected_chain_id: UInt, require_timestamp, max_nonce_reuse | Replay attack guard config with real expected-value checks |
| TokenSafetySpec | has_mint, mint_controlled, has_burn, has_pause, safety_modes[] | Token security audit spec |
| BridgeSpec | contract_address, target_chain_id, min_amount_str, max_amount_str, has_validator_set, min_validators, multisig_threshold, total_validators | Cross-chain bridge validator & multisig config |
| SMTProof | side_nodes: Array[Bytes], bit_mask: FixedArray[Bool], value: Bytes | Sparse Merkle Tree proof |
| Eip712Field | name: String, ty: String | EIP-712 typed data field |
| Eip712TypeDefinition | name: String, fields: Array[Eip712Field] | EIP-712 type definition |
| UInt256 | v0, v1, v2, v3: UInt64 | 256-bit unsigned integer (4 × 64-bit limbs) |
| RlpItem | String(Bytes) or List(Array[RlpItem]) | RLP-encoded item |
| ECPoint | x: BigInt, y: BigInt | Elliptic curve point (secp256k1) |
| Method | Returns | Description |
|---|---|---|
| address_format_spec(self, AddressType) | AddressFormatSpec | Get format spec for address type |
| valid_address_length_range(self, AddressType) | AddressLengthRange | Get char/byte length bounds |
| validate_checksum(self, AddressType, String) | Bool | Verify address checksum |
| address_precision(self, SignatureScheme) | PrecisionSpec | Get native currency precision |
| hash_output_length(self, HashScheme) | UInt | Get hash output byte length |
| is_valid_hash_size(self, Bytes, HashScheme) | Bool | Check hash size matches scheme |
| Method | Returns | Description |
|---|---|---|
| validate_transaction(self, TransactionSpec) | TxValidationCode | Validate entire transaction |
| verify_signature(self, SignatureVerifySpec) | Bool | Verify cryptographic signature |
| compute_txid(self, TransactionSpec) | String | Compute transaction ID |
| estimate_fee(self, TransactionSpec, String) | FeeSpec | Estimate transaction fee |
| validate_token_transfer(self, TokenTransferSpec) | TxValidationCode | Validate token transfer |
| Method | Returns | Description |
|---|---|---|
| check_replay_protection(self, TransactionSpec, ReplayProtectionSpec) | SecurityAssertResult | Check replay attack guards |
| check_contract_safety(self, TokenSafetySpec) | SecurityAssertResult | Audit token contract safety |
| check_bridge_security(self, BridgeSpec) | SecurityAssertResult | Audit cross-chain bridge config |
| validate_amount_range(self, String, String, String) | SecurityAssertResult | Check amount ∈ [min, max] |
| check_fee_ratio(self, String, String, String) | SecurityAssertResult | Check fee/amount ratio |
| Variant | Payload | When Raised |
|---|---|---|
| InvalidPrefix | (String, String) — address, expected prefix | Address prefix mismatch |
| InvalidLength | (String, UInt, UInt) — address, min, max | Address string length out of range |
| InvalidCharacter | (String, Char) — address, illegal char | Character not in allowed charset |
| ChecksumMismatch | (String) — address | Checksum validation failure |
| AmountImbalance | (String, String, String) — total_in, total_out, fee | Fund conservation violation |
| IncompatibleScheme | (SignatureScheme, AddressType) | Signature scheme incompatible with address type |
| SignatureVerificationFailed | (String) — txid | Cryptographic signature check failed |
| NumericParseError | (String) — parse detail | Amount string parsing or overflow |
| HashLengthMismatch | (UInt, UInt) — actual, expected | Hash output size mismatch |
| SecurityCheckFailed | (String) — reason | Generic security check failure |
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
...
}moon run examples --target nativemoon add Kali-Leo/moonbit-CryptoAssertimport {
"Kali-Leo/moonbit-CryptoAssert@0.2.0",
}import {
"Kali-Leo/moonbit-CryptoAssert/protocol",
"Kali-Leo/moonbit-CryptoAssert/business",
}/// 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./// 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")
}/// 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)/// (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/// (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")/// (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)/// 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,
)moon docstruct 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)| Test File | Description | Category |
|---|---|---|
| protocol/spec_easy_test.mbt | Enum counts, struct construction, SecurityAssertResult variants, Keccak-256 known-answer vectors | Unit |
| protocol/spec_difficult_test.mbt | Multi-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-binding | Integration |
| business/business_test.mbt | QuickCheck property bombardment on signature scheme × address type compatibility, EIP-55 official-vector validation | Property |
| Test | Rounds | Description |
|---|---|---|
| Compatible pairs | 200 each | All 11 compatible (scheme, address_type) pairs |
| Incompatible pairs | 200 each | All 34 incompatible pairs (5×9−11=34) |
| Error payload correctness | Assertion | Verify IncompatibleScheme carries correct (scheme, addr) |
| Deterministic / idempotence | 500 | Same input → same output (impurity guard) |
# 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| Primitive | Implementation | Standard |
|---|---|---|
| ECDSA secp256k1 | 450 lines, real curve ops | FIPS 186-4 |
| SHA-256 | 169 lines | FIPS 180-4 |
| Keccak-256 | 187 lines, Keccak-f[1600] | Ethereum |
| Base58Check | 161 lines, O(1) lookup | Bitcoin |
| Bech32/Bech32m | 233 lines, BCH polynomial | BIP 173/350 |
| EIP-712 | 230 lines, structHash + messageHash | Ethereum |
| RLP | 177 lines, single-pass cursor | Ethereum Yellow Paper |
| SMT | 171 lines, 256-layer, precomputed nil-hashes | Sparse Merkle Tree |
| Class | Mitigation | Mechanism |
|---|---|---|
| Integer overflow (inflation) | Proven | overflow_safe_lemma + Z3 + runtime guard |
| Integer overflow (fee bypass) | Proven | fund_conservation_inv + Z3 |
| Incomplete trait impl | Prevented | Compile-time trait completeness check |
| Address spoofing (wrong prefix) | Caught | InvalidPrefix error |
| Address spoofing (invalid char) | Caught | InvalidCharacter error |
| Checksum bypass | Caught | ChecksumMismatch error |
| Signature scheme mismatch | Caught | IncompatibleScheme error |
| Double-spend | Caught | TxValidationCode::DoubleSpend |
| Replay attack (consumed nonce) | Caught | Real tx.nonce vs expected_nonce check + nonce_replay_rejection_lemma |
| Replay attack (cross-chain) | Caught | Real tx.chain_id vs expected_chain_id check + EIP-155 style signature binding |
| Cross-chain bridge exploit | Caught | multisig_threshold-of-total_validators verification (majority + BFT) + min_validators check |
| Fake signature (hash-as-sig) | Prevented | Real ECDSA secp256k1 verification |
# ─── 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| Job | What it runs | Failure condition |
|---|---|---|
| check | moon check --deny-warn + moon fmt && git diff --exit-code | Any warning or unformatted file |
| build | moon 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 |
| test | moon test -v --target native + moon test --target wasm-gc | Any of the 90 tests failing |
| prove | Installs Why3 (apt) + Z3 4.15.3 (GitHub release), sets WHY3DATA/WHY3LIB/Z3PATH, runs moon prove and asserts 1 of 1 packages proved / 14 goals proved | Any lemma failing to prove, or the proof run not actually executing |
| Feature | CryptoAssert (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 coverage | 9 chains | Ethereum ecosystem | Cosmos ecosystem | Cosmos 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 optimization | N/A (off-chain) | Critical concern | Moderate concern | Moderate concern |
Multi-chain cryptographic assertion & formal verification library.
Dependencies