After a block is finalized on a Sub-Chain, the Sub-Chain submits a cryptographic proof (hash and optional ZK proof) to the Main Chain. The Main Chain stores only the proof, never raw event data. Submission succeeds only after a signed MainChain block containing the proof is finalized, saved through durable SQL storage, and read back with its hash, Merkle root, and signature verified. SQLite uses WAL with synchronous=FULL for these commits. An unavailable or unsupported storage backend causes submission to fail.
Automatic submission runs after a Sub-Chain block is finalized when the configured time interval has elapsed and a newer block has not yet been submitted. The authenticated REST endpoint can also trigger submission.
Flow diagram
sequenceDiagram
autonumber
participant SC as 📦 SubChain
participant ZKP as 🔐 ZKProver
participant MC as 🔗 MainChain
SC->>SC: auto_submit_proof_if_needed()
SC->>SC: Check: chain length > 1 AND block finalized
alt ZK Proofs Enabled (HRC_ENABLE_ZK_PROOFS=true)
SC->>ZKP: generate_proof(old_state_root, new_state_root, block_index, events)
ZKP->>ZKP: Compute proof (Mock SHA-256; production backend unavailable)
ZKP-->>SC: ProofResult { proof: bytes, success: bool }
SC->>SC: Retry up to 3× with exponential backoff on failure
else ZK Proofs Disabled
SC->>SC: zk_proof = None
end
SC->>SC: _generate_default_proof_metadata()
SC->>MC: add_proof(sub_chain_name, proof_hash, metadata, zk_proof)
MC->>MC: Verify ZK proof (if enabled)
MC-->>SC: Proof queued
SC->>MC: Finalize signed proof block, save and read back from storage
MC-->>SC: Durable proof confirmed
SC->>SC: Record proof_submitted event
SC->>SC: Update last_proof_submission timestamp
Step-by-step breakdown
Step
Description
1. Trigger check
auto_submit_proof_if_needed() checks the elapsed interval and whether a newer finalized block exists.
2. ZK generation
If HRC_ENABLE_ZK_PROOFS=true: ZKProver computes over (old_state_root, new_state_root, events). Retries 3× with backoff
3. Proof metadata
_generate_default_proof_metadata() builds summary metadata; MainChain rejects forbidden detail fields inside nested dictionaries or lists and sanitizes accepted containers recursively before recording them
4. Main Chain write
MainChain.add_proof() verifies and queues the proof; the submission path finalizes and reads back the signed block from durable storage.
5. Record
Only after durable readback, Sub-Chain logs a proof_submitted event and updates last_proof_submission.
ZK proof modes
Mode
Mechanism
Use Case
mock
SHA-256 hash simulation
Development / Testing
production
Unimplemented proving/verifying backend
Unavailable
Configuration
Setting
Default
Description
HRC_ENABLE_ZK_PROOFS
false
Toggle ZK verification
HRC_ZK_MODE
mock
mock or production
HRC_ZK_REQUIRED_MAINCHAIN
false
Block proof submission if ZK fails
Error handling
Condition
Behavior
ZK proof generation fails
Retry up to 3× with exponential backoff; if HRC_ZK_REQUIRED_MAINCHAIN=true, abort
Main Chain write fails
Exception logged, last_proof_submission not updated; retry on next block
Durable storage is absent, unsupported, or readback fails
Submission returns False; a retry can persist an already queued or finalized proof without adding a duplicate.
Required ZK proof is missing or verification fails
MainChain.add_proof() returns False; no proof is recorded