Skip to content
hopper
Get started
Start / styx-ferry-messaging

Styx ferry messaging

Forward-secret messaging and VSL ferry proof control plane using Hopper crypto, precompile checks, and verifier CPI.

Hopper port of the Styx ferry and messaging control plane. The sample keeps Signal-style X3DH, Double Ratchet state, and XChaCha20-Poly1305 payload encryption in the client protocol, while Hopper enforces the deterministic on-chain boundaries: signed prekeys, bounded message envelopes, proof input checks, and verifier CPI.

What Hopper checks

  • The ferry config stores a pinned verifier program, a Keccak-derived BN254 field domain separator, and aggregate message/prekey/proof counters.
  • publish_prekey_bundle requires an Ed25519 precompile sibling instruction and checks that its offset table points at the owner key, signed prekey, and signed-prekey signature supplied to Hopper.
  • send_ratchet_message accepts only non-empty bounded ciphertext, updates a monotonic counter, and emits the ratchet key plus sealed-message hash for off-chain delivery/indexing.
  • submit_zk_ferry accepts exactly the Styx 513-byte v2 proof envelope, checks public input domain/base-fee/fee-tier fields, and CPIs into the pinned verifier program.

Instruction Surface

Tag Handler Purpose
0 init_config Create the ferry config, derive the Styx VSL domain separator, and pin a verifier program.
1 publish_prekey_bundle Publish a user prekey bundle after verifying the signed prekey with an Ed25519 precompile sibling instruction.
2 refresh_prekey_bundle Rotate the one-time-prekey root and update the bounded prekey count.
3 init_thread Create a sender-to-recipient ratchet thread.
4 send_ratchet_message Emit a bounded ciphertext envelope and update monotonic ratchet state.
5 submit_zk_ferry Validate the Styx proof envelope, check fee/domain public inputs, and CPI into the verifier.

VSL Proof Boundary

The proof format is versioned and fixed-size:

byte 0        proof version, currently 2
bytes 1..257  proof bytes consumed by the verifier
bytes 257..513 eight 32-byte public inputs

Public inputs are read in this order: root, nullifier, output commitment 0, output commitment 1, asset id, domain separator, fee tier id, and base fee. The domain separator must equal Hopper's config value, the base fee must match the configured lamports value, and the fee tier must stay within the program cap.

Local Checks

cargo check -p hopper-styx-ferry
cargo run -q -p hopper-cli -- solana-check --manifest-path examples/hopper-styx-ferry/Cargo.toml

Build with the Solana SBF toolchain when you want to deploy the sample:

cargo build-sbf -- -p hopper-styx-ferry
solana program deploy target/deploy/hopper_styx_ferry.so --url devnet