Skip to main content
This is the reference for Molpha’s signing math: how the eligible signer set is derived, how signatures aggregate, and the exact pipeline every verifier runs. The Solana, EVM and Starknet verifiers and the molpha-verifier crate all implement it identically.

The verification pipeline

The message has no chainId, so one signature is valid on every chain. Payload fields and encoding are described in Attestations.

Node selection

Each round, a pseudo-random subset of the registry version’s nodes (the selection set) is eligible to sign. It is derived only from values in the payload and the registry snapshot, so every verifier reproduces it independently.
  • nodeCount and redundancyBuffer are those of the payload’s registryVersion. Changing the buffer publishes a new registry version, so selection for an existing version never changes.
  • The buffer widens the eligible set so a round can reach its quorum when some selected nodes are offline.
  • Selection reads only whole seconds. All rounds of one source and registry version within the same second share one selection set.
  • The caller does not choose the timestamp; the gateway stamps it. See Security model for what that does and does not protect.

Derivation algorithm

deriveWithoutReplacement(selectionSeed, nodeCount, groupSize) samples without replacement:
  1. PRF: keccak256(seed ‖ SELECTION_DOMAIN ‖ counter). Each 256-bit digest yields eight big-endian uint32 limbs.
  2. Bias rejection: a limb is rejected when limb ≥ floor((2³² − 1) / nodeCount) · nodeCount. An accepted limb maps to limb % nodeCount, skipping positions already chosen.
  3. Complement path: when groupSize > nodeCount / 2, the algorithm samples the nodeCount − groupSize excluded nodes and complements the mask.
The complement path is why verify cost on Starknet is not strictly linear in signer count.

Bitmaps

Bit i is the node at 0-based index i of the registry version. A registry holds at most 256 nodes, one 256-bit word.

Plain-sum aggregation

Molpha aggregates Schnorr signatures over secp256k1 by plain summation, with mandatory proof of possession at registration. It is not MuSig2: there are no delinearization coefficients and no precomputed aggregate key. The verification key for a round is the sum of the actual signers’ public keys, recomputed by the verifier from the registry snapshot. The signer set cannot be forged:
  • signersBitmap is inside the signed message, so the signature commits to exactly which keys took part.
  • A valid aggregate over keys that did not all contribute would require breaking the discrete logarithm, given that every key has a proof of possession.
  • Signers must fall inside the selection set, so registered nodes cannot sign rounds they were not selected for.

Proof of possession

Plain-sum aggregation is open to rogue-key attacks unless every key is proven to belong to its registrant. Each chain checks a Schnorr proof of possession before a key enters any registry snapshot. The Solana labels are raw ASCII, not hashed. On Solana a signing key can be registered only once (Secp256k1KeyClaim). EVM and Starknet reject a zero signature or commitment, off-curve keys and s >= Q. The proof binds one deployment, so nodes produce one proof per chain. Proof of possession is checked only at registration, never in verify, so attestation signatures stay chain-agnostic.

Domain separators

The first three are identical in every implementation. Solana uses its own proof-of-possession labels, shown above. The Solana program also uses raw ASCII labels for epoch tickets (MOLPHA_TICKET_V1, MOLPHA_TICKET_ID_V1, MOLPHA_DISPATCH_V1); see Epoch settlement.

The off-chain round

The node network and the gateways run the signing session: nonce commitments, message transport and retries. Whatever the transport, a verifier accepts only an aggregate that:
  • covers (value, sourceId, registryVersion, signaturesRequired, timestamp, signersBitmap) under MOLPHA_MESSAGE_V1;
  • comes from at least signaturesRequired signers, all inside the selection set;
  • is a plain-sum aggregate over exactly the keys in signersBitmap.