Limonata v0.3.6 - encrypted-mempool concentration guard (MANDATORY coordinated upgrade at height 1,650,000)
LatestLimonata v0.3.6 - encrypted-mempool concentration guard
MANDATORY coordinated upgrade at height 1,650,000. Every validator must be running v0.3.6 by that
height or it will stop producing blocks. This is a consensus-breaking change gated by an on-chain
governance proposal; do not upgrade early and do not upgrade late.
Built from commit effa377d.
What changes
The encrypted mempool's committee concentration guard becomes two-clause, behind a new parameter so
that replaying history stays byte-identical.
- Confidentiality clause. The threshold becomes
t = floor(2S/3) + n, so no coalition holding two
thirds or less of committee stake can ever reach it. The previous single-clause form
(t = floor(2S/3) + 1) did not have that property once points were weighted by stake. - Strand clause. The guard fails closed if any single operator holds
S - t + 1points or more,
which is the point at which that operator alone could withhold its share and prevent decryption. - One-way ratchet. Once the gate flags are on,
UpdateParamscannot turn them back off.
The practical effect is that the encrypted mempool becomes usable. The single-clause guard is closed
against realistic committees - not by accident but structurally - which is why encrypted submission
does not currently work on the live testnet. The two-clause form opens it to a committee that is
actually decentralized, and keeps it closed to one that is not.
Replay safety
Chains that have no record of the new parameter behave exactly as before: the legacy threshold, no
strand clause, byte-for-byte identical results. The flag is flipped by the upgrade handler
encmempool-strict-concentration-v1, and DefaultParams() has it on so new chains are born correct.
One subtlety is worth stating because it cost us a rehearsal to find. An earlier build read the new
parameter through a metered store read. The returned value was replay-inert, but the gas was not:
any historically rejected SubmitEncrypted transaction would have recorded a different gas_used,
diverging LastResultsHash and breaking sync-from-genesis. Static analysis and fuzzing both passed;
only a real devnet upgrade followed by a full resync caught it. The shipped build threads the flag in
from callers that already hold the loaded parameters, and the ratchet reads through a throwaway
infinite gas meter. Replay-inert return values are not the same thing as replay-inert gas.
Verifying this release
curl -sfLO https://github.com/Limonata-Blockchain/limonata/releases/download/limonata-v0.3.6/limonatad-linux-amd64.tar.gz
curl -sfLO https://github.com/Limonata-Blockchain/limonata/releases/download/limonata-v0.3.6/SHA256SUMS.txt
curl -sfLO https://github.com/Limonata-Blockchain/limonata/releases/download/limonata-v0.3.6/SHA256SUMS.txt.asc
curl -sfLO https://github.com/Limonata-Blockchain/limonata/releases/download/limonata-v0.3.6/limonata-release-signing-key.asc
gpg --import limonata-release-signing-key.asc
gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt
tar xzf limonatad-linux-amd64.tar.gz
./limonatad version --long | head -3 # expect version v0.3.6, commit effa377d
The signing key is A45380198F390AF69126AE12E4ECEC477C1735FB
(Limonata Foundation, release signing).
This archive is larger than v0.3.5's because the binary keeps its debug symbols, which makes a stack
trace readable if a validator does halt. Behaviour is unaffected.
Upgrading
With cosmovisor, the upgrade info in the governance plan points at this archive and the swap is
automatic. Manually: let the node halt at 1,650,000, replace the binary, restart.
Also in this governance proposal
The same proposal recovers the expired IBC light client 07-tendermint-0, which restores transfer
channel-0 to Osmosis. That action is unrelated to the mempool change and executes when the proposal
passes, not at the upgrade height. Both messages apply atomically - governance applies both or
neither.