Agent index (llms.txt) — fetch this first, then the page it names.

Shielded pool

x/shielded is the private way to hold and move ORAMA. It is a shielded pool in the style of Zcash Orchard (orchard 0.15.5, "Ironwood" in Orama's naming): value goes in, moves between private notes with no visible sender, receiver or amount, and comes out again.

Read this first. The module is implemented and wired, but it fails closed. A node accepts a shielded bundle only if it has both proof verifiers: the in-process Orchard library (built with -tags orchardffi) and the pinned out-of-process orama-orchard-verifier binary. A plain make build produces a binary with neither, so it accepts no shielded transaction. Release builds (build-linux-amd64-full, build-linux-amd64-orchard) link both. On any node without them, only public sends work. The linux binaries have been built and linked but not yet run as a network. The two verifiers share the same upstream crate; a genuinely independent second implementation is an open item.

Using the pool is optional

Nothing forces value into the pool. A bank send of norama between plain accounts is an ordinary public payment, earnings can be withdrawn to a bank balance (MsgWithdrawEarnings) and sent in the open, and a user who never shields is a normal user of the chain. The pool is for people who want a payment to show no sender, recipient or amount.

The tools make that choice explicit and default to the private side. orama chain send <to> <amount> and the SDK's chain.transfer({ to, amount }) are private unless you pass --public or public: true, so a payment cannot become public by omission, and they never fall back to a public payment when the private one cannot be built. Building a shielded bundle needs the owner's shielded spending key (see Keys below), and the RootWallet cannot do that yet, so today a private send through the CLI stops with that explanation. A wallet or tool that holds the key builds the bundle itself and passes it to the SDK as its shielded option.

Messages

MessageWhat it does
MsgShieldedTransferA signer-less private transfer. The transaction must contain only this message, with no signature, no fee and no memo, and declare exactly action_gas x actions
MsgShieldMove bank balance into the pool
MsgShieldEarningsMove the signer's earnings into the pool
MsgUnshieldMove value out. The signature hash binds the signer and the target so a copy cannot be redirected

Unshield targets: BOND (the signer's own delegation), NODE_BOND (the signer's own x/nodes role bond), and FEE_TOPUP (the signer's own fee-only balance, at most 0.01 ORAMA per transaction, and the whole transaction fails if over). DEPOSIT and CONTRACT targets are refused: they are not linked.

Because ORAMA leaves the pool only into the owner's own bond or fee balance, the pool is not a way to pay someone else out of band.

Rules that protect the pool

  • +Turnstile. The pool can never pay out more than went in.
  • +24 hour cap. Net outflow is at most 2% of the pool (or 1 ORAMA if larger) per rolling 24 hours. The 2% is an ossified constant. Over-cap BOND and NODE_BOND unshields are queued and served once a window, pro rata, up to 1,000 payments per window. There is no cancel message.
  • +Nullifiers. Each spent note's nullifier is recorded in a separate append-only database next to the chain data. The app hash commits to a running SHA-256 of that set and a count, and the node refuses to start if the database does not match.
  • +One pool. Only vintage 1 and the native asset exist. There is no migration message and no multi-asset mode.

Parameters

ParameterValue
anchor_window_blocks14,400 (20 hours at the chain's 5 s block interval)
nullifier_fee0.001 ORAMA per nullifier, burned
action_gas250,000 (an unmeasured placeholder)
max_actions_per_bundle16
unshield_floor1 ORAMA
max_fee_topup0.01 ORAMA
queue_per_address_cap100 ORAMA
max_signerless_per_block64

A transfer's fee (its value balance) must be at least base fee x action_gas x actions + nullifier fee x actions. The base and nullifier parts are burned and any remainder is a tip to the proposer.

Proof sizes: a 1-action bundle is 5,985 bytes and a 2-action bundle 9,141 bytes. Verifying a 1-action bundle takes about 5.6 ms on an Apple M3 with 8 threads; Linux is not measured.

Keys

Wallet-side shielded keys are derived with ZIP-32 hardened-only derivation from a RootWallet branch of the seed (HKDF salt orama-shielded-v1). RootWallet does not use them yet, and its agent refuses shielded messages.

Not built

The CONTRACT unshield path and its adapter, DEPOSIT unshields, vintage migration, multi-asset pools, shielded delegation tokens, per-peer admission limits in the mempool, a measured gas price, and a real two-node state-sync test of the nullifier database.

Queries

Params, TreeState and NullifierSpent are public. Pools and Invariants are withheld from the gateway.

What the chain shows

A shielded transaction is public in these ways and no others (the explorer shows exactly this):

  • +Every accepted bundle emits a shielded_bundle event with its kind (transfer, shield, shield_earnings or unshield), its number of actions and its amount: the fee of a private transfer, the amount shielded, or the amount unshielded. A transaction the chain refuses emits none.
  • +A private transfer has no signer, no receiver and no amount. Its fee, its action count, the size of its proof, its block and its time are public.
  • +A shield, a shield of earnings and an unshield have a signer, because the signer's public balance, earnings or bond is on the other side. The signer and the amount are public; which notes an unshield spent and who owns the value inside the pool are not.
  • +The pool as a whole: its module account's bank balance (the pool plus any unshields waiting in the queue), TreeState (the note commitments added to the tree, one per action of every accepted bundle, and the nullifiers recorded, one per action), and Params. The chain indexer lists the transactions whose events name the pool's account, which is every shield, shield of earnings and unshield and every private transfer (each burns its fee from the pool). The turnstile is a rule of the module, checked by its invariant, which the gateway withholds.

An address's balance and its activity cover public funds only; value in the pool belongs to no address.