Message Submit Pipeline
Every message goes through a multi-stage validation pipeline before being included in a block.
Pipeline stages
Client → gRPC SubmitMessage
│
├─ 1. Verify hash + signature
│ BLAKE3(data) == hash
│ Ed25519.verify(signature, hash, signer)
│
├─ 2. Structural validation
│ Field sizes, non-empty constraints, enum validity
│ (no state lookups)
│
├─ 3. Signer authorization pre-check
│ Verify signer is a registered key for data.mid
│ (skipped for KEY_ADD/KEY_REMOVE/OWNERSHIP_TRANSFER — relay-injected)
│
├─ 4. Network validation
│ Message network matches the node's configured network
│
├─ 5. Mempool admission
│ Deduplication (by message hash)
│ Capacity check (default: 100,000)
│ Timestamp window (10 min past, 30 sec future)
│
├─ [Mempool] ──── Consensus proposes block ────
│
├─ 6. Block execution
│ Serial account pre-pass (KEY_ADD, KEY_REMOVE, OWNERSHIP_TRANSFER,
│ ACCOUNT_DATA, VERIFICATION_ADD/REMOVE, PROJECT_CREATE, PROJECT_REMOVE, FORK)
│ Parallel project execution (grouped by project_id)
│ Full state validation (authorization, CAS checks, etc.)
│
└─ 7. Finalization
State diffs applied to base store
Block built and stored
Messages broadcast to subscribers
Rejection Points
Messages can be rejected at any stage. Each stage returns a specific error:
| Stage | Example Errors |
|---|---|
| 1. Verification | Hash mismatch, invalid signature |
| 2. Structural | Missing body, invalid field length |
| 3. Pre-check | Signer not registered for MID |
| 4. Network | Wrong network (e.g., testnet message to devnet node) |
| 5. Mempool | Duplicate message, mempool full, timestamp out of window |
| 6. Execution | Unauthorized, CAS mismatch, project not found |
Stages 1-5 happen synchronously on the submit RPC. Stage 6 happens asynchronously during block execution — if a message fails execution, it's silently dropped (the block proceeds without it).
Subscriber Notifications
Messages are broadcast to SubscribeMessages subscribers only after consensus finalization (stage 7), not on submit. This ensures subscribers see the canonical committed order and never see messages that fail execution.
Timestamp Validation
The mempool enforces a timestamp window:
- Maximum age: 10 minutes in the past (configurable via
max_timestamp_age_secs) - Maximum drift: 30 seconds in the future (configurable via
max_timestamp_drift_secs)
This prevents replay of old messages and rejects messages with clock skew beyond the tolerance window.