molpha-verifier
Framework-independent Rust verifier for Molpha updates, compatible with Solana program and native Rust consumers.
The downstream Solana program (or any other consumer) owns registry account types and I/O. This crate only takes plain data — no Anchor, Pinocchio, or AccountInfo dependency — and verifies the same checks as the EVM Validator reference path.
What it verifies
Given an AttestationPayload (or combined Attestation), a SchnorrSignature, and the signing nodes' secp256k1 pubkeys, verification:
- Rejects an invalid / zero aggregate scalar
s - Enforces
popcount(signers_bitmap) ≥ signatures_required - Re-derives the deterministic selection bitmap and requires
signers ⊆ selection - Reconstructs the coalition key
Σ X_ifrom ordered signer pubkeys - Hashes the EVM-compatible message (
MOLPHA_MESSAGE_V1domain) oversource_id, registry version, threshold, signers bitmap, rawvaluebytes, and canonical timestamp - Recovers the commitment address via the Schnorr→ECDSA trick and matches
commitment_addr
Optional helpers resolve ordered signers from a plain RegistryView + NodeEntry slice, including previous-version remove-transition remapping.
AttestationPayload
Content fields carried with the attestation:
| Field | Type | Notes |
|---|---|---|
value |
[u8; 32] |
Attested value; hashed as-is into the message |
source_id |
[u8; 32] |
Source identifier (often ASCII-padded) |
registry_version |
u32 |
Registry snapshot referenced by the attestation |
signatures_required |
u32 |
Threshold encoded in the payload |
canonical_timestamp |
u64 |
Round timestamp; used in selection seed |
Attestation
Combines [AttestationPayload] with [SchnorrSignature] for wire-format decode/encode when the borsh feature is enabled.
SchnorrSignature
Aggregate signature material, passed separately from the payload:
| Field | Type | Notes |
|---|---|---|
agg_sig_s |
[u8; 32] |
Aggregate Schnorr scalar s |
commitment_addr |
[u8; 20] |
Ethereum address of nonce point R |
signers_bitmap |
[u8; 32] |
EVM uint256 bitmap (big-endian) |
Install
[]
= "0.3"
# With Borsh support for wire-format decode/encode:
# molpha-verifier = { version = "0.3", features = ["borsh"] }
Usage
Already-resolved signers
use ;
// `ordered_signers`: one (x, y) per set bit of `attestation.signature.signers_bitmap`,
// in ascending bit-index order (same order as EVM Validator.verify).
verify_attestation?;
Compressed (33-byte) pubkeys: verify_attestation_compressed.
Registry-resolved path
use ;
verify_attestation_resolved?;
The caller must owner-check and deserialize accounts; this crate only validates indices / versions and runs crypto.
Dispute path
For instructions that verify an aggregate signature over an arbitrary message hash (slash / dispute semantics):
use verify_aggregate_over_hash;
// Ok(true) = valid, Ok(false) = invalid (slashable), Err = malformed input
let valid = verify_aggregate_over_hash?;
Registry-resolved variant: verify_aggregate_over_hash_resolved.
Modules
| Module | Role |
|---|---|
payload |
Plain AttestationPayload, SchnorrSignature, and Attestation structs |
verify |
High-level verify, coalition reconstruction, dispute helpers |
onchain |
Signer resolution over RegistryView / NodeEntry |
selection |
Deterministic selection bitmap (MOLPHA_SELECTION_V1) |
message |
EVM-compatible message hash (MOLPHA_MESSAGE_V1) |
bitmap |
u256 bitmap helpers and group sampling |
coalition |
secp256k1 point sum accumulator |
scalar |
Schnorr→ECDSA inputs, ETH address from pubkey |
state |
Framework-agnostic registry / node view types |
error |
AttestationError — map at the program call boundary |
Features
| Feature | Effect |
|---|---|
| (default) | Pure verification; no Borsh |
borsh |
Derive Borsh on AttestationPayload, SchnorrSignature, and Attestation |
thiserror |
Display and std::error::Error on AttestationError for off-chain tooling |
Development
# End-to-end example (Borsh decode + compressed-pubkey verify)
License
MIT — see LICENSE.