Reference protocol implementation for April Gate — tamper-evident cold chain verification on Solana.
This repository contains the protocol types, simulation tooling, a Solana client library, and a demo binary that exercises the full pipeline from sensor readings through BLS-aggregated consensus to on-chain submission on Solana devnet. The on-chain program lives in coldchain-programs; the production consensus engine, ZK proof circuits, and hardware firmware are part of April Gate's proprietary stack and are not in this repository.
Five simulated sensors, each producing a hardware-style signed temperature attestation per round. Attestations are aggregated into a single BLS12-381 signature, and the round's slot-owning device submits two on-chain artifacts atomically in a single transaction:
- A Memo with human-readable JSON — a preview of what will eventually be stored encrypted on Filecoin and viewable through the April Gate web portal by authorized parties.
- A
device_registry::submit_proofcall writing a 32-byte commitment to the slot owner's on-chain assignment — the production-shaped tamper-evident record.
The memo JSON references the production proof PDA and includes the commitment hex, so anyone reading the human-readable record can navigate to the production proof account and verify that the on-chain commitment field matches.
Across the demo, five distinct device keypairs each take their turn as the on-chain submitter — the round-robin slot rotation made concrete in chain history.
What you can verify on-chain after running the demo:
- Five sensor node identities participating in each round
- A single BLS aggregate signature representing all five (binding off-chain evidence)
- Median temperature consensus (Byzantine-robust to faulty readings)
- Round-robin slot rotation distributing submission across five distinct devices
- For each round, a
Proofaccount on thedevice_registryprogram with the round's commitment, anchored to the slot owner'sDeviceAssignment
┌─────────────────────────────────────────────────────────────────┐
│ consensus crate │
│ Protocol types · TemperatureSource trait · BLS aggregation │
│ Median consensus · Slot scheduling · Canonical message format │
└─────────────────────────────────────────────────────────────────┘
▲ ▲
│ │
┌────────┴──────────────┐ ┌──────────┴──────────────┐
│ simulation crate │ │ coldchain-engine │
│ │ │ (PRIVATE) │
│ SimSensor: software │ │ │
│ BLS keys + synthetic │ │ Production sensor │
│ readings │ │ with ATECC608 hardware │
│ │ │ BFT engine, ZK proofs │
└───────────────────────┘ └─────────────────────────┘
▲
│ ┌─────────────────────────┐
│ │ chain-client crate │
│ │ │
│ │ PDA derivation │
│ │ Anchor ix builders │
│ │ Account decoders │
│ │ devnet.json schema │
│ └────────┬────────────────┘
│ │
│ ┌────────┴────────────────┐
│ │ bin/setup-devnet │
│ │ │
│ │ Idempotent provisioner:│
│ │ generates keys, │
│ │ registers devices, │
│ │ creates shipments, │
│ │ assigns devices, │
│ │ writes devnet.json │
│ └─────────────────────────┘
│ │
┌────────┴───────────────────────────────────────┴────────────────┐
│ bin/sim-cluster · bin/sim-cluster-full-demo │
│ │
│ sim-cluster: 5-round happy demo on the happy shipment. │
│ sim-cluster-full-demo: full arc (happy → mid-demo reassignment │
│ → degradation → quorum collapse), exercising the architecture │
│ end to end. │
│ │
│ Both atomically submit Memo (human-readable) + submit_proof │
│ (commitment) per round when consensus is achievable. │
└─────────────────────────────────────────────────────────────────┘
- Protocol types —
SensorReading,SignedAttestation,AggregatedProof TemperatureSourcetrait — uniform abstraction across simulated and hardware sensors- Median consensus — Byzantine-robust aggregation across signed readings
- BLS12-381 primitives — signature and public-key aggregation via
blst - Round-robin slot scheduling — simple deterministic rotation
- Canonical signing message format — used for both ECDSA (production) and BLS (demo)
- Anchor instruction builders — PDA derivation and
device_registryinstruction construction with noanchor-clientdependency, keeping build times reasonable - Idempotent devnet provisioning — one command brings devnet to a known-good state
- Dual on-chain submission — Memo (human-readable preview) plus production-shaped
submit_proof, atomically per round
- Position-aware tampering detection — using known relative sensor placements within a shipment; patent application in preparation
- Hardware-to-BLS bridge — derives BLS signing capability from ATECC608 hardware identity
- Slot-owner election with failover — deterministic recovery when designated submitter is offline
- Clock synchronization — across offline sensor meshes (NTP-on-reconnect + mesh time consensus)
- ZK proof circuit design — binding device identity, shipment, and range constraints into a single succinct proof
- Filecoin/Solana coordination — Merkle structure linking on-chain anchors to off-chain encrypted archives
- Production sensor firmware —
no_stdRust on Pico W with ATECC608 driver, DS18B20 reading, WiFi management - Encrypted Memo summaries — privacy-preserving on-chain summaries decryptable only by authorized parties (operator, insurer, regulator)
- Rust 1.85+ (the version April Gate's Solana toolchain ships with)
- Solana CLI configured for devnet
- A Solana wallet with at least 0.5 SOL on devnet (covers payer rent, device funding, and per-round transaction fees)
cargo build --release
cargo testTests should pass across the consensus, simulation, and chain-client crates.
cargo run --release -p sim-clusterFive rounds of five sensors each, with full output showing readings, consensus, BLS aggregation, and the computed on-chain commitment. No transactions submitted.
The live run is a two-step flow. Step 1 is one-time setup; step 2 is the demo itself.
There are two demo binaries — pick one:
sim-cluster— simple happy-path demo. 5 rounds, all sensors healthy. For a quick look at the consensus + on-chain submission pipeline.sim-cluster-full-demo— full architecture arc. 5 happy rounds (including a transient outlier event consensus invisibly absorbs), then mid-demo reassignment to a new shipment (ENTER prompt for live control), then 8 rounds covering graceful degradation, threshold-limit operation, and quorum collapse. For a thorough end-to-end demonstration.
# 1. One-time setup. Converges devnet to a known target state:
# - 5 device authorities under keys/
# - 5 devices registered on the device_registry program
# - 2 shipments created (happy + faulty)
# - All devices currently assigned to the happy shipment
# Idempotent — safe to re-run if anything fails partway.
cargo run --release -p setup-devnet -- \
--wallet ~/.config/solana/AprilGate/wallet.json
# 2a. Run the simple happy demo (devices stay on happy shipment).
cargo run --release -p sim-cluster -- --submit
# OR
# 2b. Run the full demo. Reassigns devices to the faulty shipment
# mid-run. Re-run setup-devnet afterward if you want to run any
# demo again — it resets devices back to the happy shipment.
cargo run --release -p sim-cluster-full-demo -- --submitEach round prints a Solscan transaction link plus a direct link to the new Proof account.
After setup-devnet finishes, the repo contains keys/device_01.json … keys/device_05.json (gitignored) and devnet.json (gitignored).
The on-chain program enforces two properties by design — both load-bearing for tamper-evidence:
- Device authority is immutable after registration. Once a
DeviceRegistryPDA is created, itsauthorityfield is set forever. The keypair that registered it is the only key that can ever assign/reassign that device, sign on its behalf, or end its assignments. Hardware-rooted identity in production; the same property in demo. - Shipment status is monotonic.
Created → InTransit → Delivered → Closed. Once a shipment passesInTransit, it cannot accept new assignments. You can't retroactively add sensors to a delivered shipment.
These properties produce two recoverable failure modes worth knowing about:
You'll see this from setup-devnet:
device_01 on-chain authority mismatch:
PDA: <addr>
on-chain: <old pubkey>
local: <new pubkey>
The on-chain device is locked to a keypair you no longer have. Fix:
chain-client/src/lib.rs — bump DEMO_NODE_DST (e.g. "april-gate-demo-node-v2" → "-v3")
simulation/src/lib.rs — match the same string in derive_demo_node_id
cargo build && cargo run -p setup-devnet -- --wallet <PATH>
This is the demo equivalent of replacing hardware in production. The previous on-chain devices stay valid (their historical proofs remain), they're just no longer signable by you.
You'll see this from setup-devnet:
shipment 'happy' PDA <addr> is in 'Delivered' state —
does not accept new device assignments.
Usually caused by prior test runs that exercised the full lifecycle. Fix:
chain-client/src/lib.rs — bump HAPPY_SHIPMENT_NONCE and FAULTY_SHIPMENT_NONCE
(e.g. 1001/1002 → 2001/2002)
cargo build && cargo run -p setup-devnet -- --wallet <PATH>
This is the demo equivalent of starting a new shipment with new manifest IDs in production. The previous shipments stay valid; you're just creating new state alongside them.
A future iteration of
setup-devnetwill detect these conditions and auto-increment, removing the manual constant bump. For the current hackathon-grade demo, the manual recovery path is two minutes and teaches the architecture honestly: the chain is monotonic; recovery means moving forward, never rewriting.
For each round, sim-cluster sends one transaction containing two instructions. Clicking the printed Solscan link shows:
The Memo program log — (hackathon convenience!) human-readable JSON the way it will appear in production previews before the encrypted-Filecoin path is wired in:
Program log: Memo (len 305): "{\"round\":0,\"timestamp\":1746464567,
\"temp_x100\":415,\"participants\":5,\"nodes\":[\"82ef683e\",\"84458f29\",
\"439e018b\",\"c904bd6e\",\"6e3cf4b5\"],
\"agg_sig_fingerprint\":\"a27cb164b8...\",
\"proof_pda\":\"4xPb…vQz\",
\"commitment_hex\":\"7f3a…c012\"}"
A newly-created Proof account at proof_pda — the production-shaped tamper-evident record. The account holds:
- The 32-byte commitment binding the full off-chain
AggregatedProof(including the BLS aggregate signature and pubkey) - The sequence number within its assignment
- The submitter (the slot owner's device keypair)
- The timestamp
Mutated DeviceAssignment and Shipment accounts — proof_count incremented on each.
The on-chain commitment field on the Proof account equals the commitment_hex value in the Memo JSON of the same transaction. That's the bridge from "human-readable preview" to "tamper-evident production record": one inspection path runs from the memo, through the named proof PDA, to the cryptographic anchor.
In production, the memo path is replaced by an encrypted summary readable only by authorized parties (operator, insurer, regulator), with the full sensor evidence archived to Filecoin. The Proof::commitment field provides the tamper-evidence binding.
.
├── Cargo.toml Workspace config
│
├── consensus/ Public protocol types and primitives
│ ├── Cargo.toml
│ └── src/lib.rs
│
├── simulation/ Simulated sensors for demo
│ ├── Cargo.toml
│ └── src/lib.rs
│
├── chain-client/ Solana client library
│ ├── Cargo.toml PDA derivation · Anchor ix builders
│ └── src/lib.rs Account decoders · devnet.json schema
│
└── bin/
├── sim-cluster/ Simple happy demo
│ ├── Cargo.toml
│ └── src/main.rs
│
├── sim-cluster-full-demo/ Full architecture arc
│ ├── Cargo.toml (happy → reassignment → degradation
│ └── src/main.rs → quorum collapse)
│
└── setup-devnet/ Devnet provisioning utility
├── Cargo.toml
└── src/main.rs
Apache 2.0
Built for the Solana Frontier Hackathon — Colosseum, May 2026.