Rework shielded transactions (with crypto in WASM)#2477
Merged
sam0x17 merged 91 commits intodevnet-readyfrom Mar 3, 2026
Merged
Rework shielded transactions (with crypto in WASM)#2477sam0x17 merged 91 commits intodevnet-readyfrom
sam0x17 merged 91 commits intodevnet-readyfrom
Conversation
JohnReedV
approved these changes
Mar 3, 2026
sam0x17
approved these changes
Mar 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Companion PR to opentensor/polkadot-sdk#6 — supersedes #2441.
Reworks the MEV Shield from a store-then-decrypt model to a decrypt-at-proposal model,
with all cryptographic operations running entirely in WASM — no custom host functions required.
Previously, encrypted transactions were stored on-chain (
Submissions) and decrypted by the blockauthor in a separate step, leaving a window where decrypted transactions sat in the pool before
inclusion. Now, encrypted transactions travel through the pool as opaque ciphertext and the block
proposer decrypts them inline during block building, including the inner extrinsic in the same
block. There is no point where a decrypted transaction is visible before it lands in a finalized
block.
Encryption uses ML-KEM-768 + XChaCha20-Poly1305 with per-block ephemeral keys rotated via an
inherent. The node passes the raw decapsulation key bytes to the runtime API, which performs
ML-KEM decapsulation and AEAD decryption in pure WASM — eliminating the need for custom host
functions (
stp-iohas been removed) and allowing the shield to be activated with a runtime-onlyupgrade.
Breaking changes for clients
This PR introduces breaking changes that require client-side updates.
Encrypted message format
The ciphertext payload now includes the next public key hash as a prefix. The new wire format is:
key_hash:xxhash128(NextKey)— the 16-byte hash of the ML-KEM public key used for encryption. Clients must compute this from theNextKeythey encrypt against.kem_len: 2-byte little-endian length of the KEM ciphertext that follows.kem_ct: ML-KEM-768 encapsulation ciphertext (typically 1088 bytes).nonce: 24-byte random nonce for XChaCha20-Poly1305.aead_ct: The authenticated ciphertext containing the encoded inner extrinsic.submit_encryptedAPI changeThe old unused
commitmentparameter has been removed. The new signature is:New transaction extension:
CheckShieldedTxValidityA new transaction extension validates encrypted messages at two levels:
FailedShieldedTxParsingerror.key_hashmatches eitherCurrentKeyorNextKey. If it doesn't match, the transaction is dropped silently — the block proposer returns anInvalidtransaction error without a custom error code because the block proposer does not propagate the inner error code of the extension.This means clients should expect:
FailedShieldedTxParsingerror if the ciphertext format is wrong.Invaliderror (no specific code) if thekey_hashdoesn't match any active key — e.g. when using a stale or wrong key.Rollout plan
Since all crypto runs in WASM, non-validator nodes do not need a binary upgrade — the shield-related node code (
ShieldKeystore, key rotation, proposer decryption) is gated behindrole.is_authorityand the block proposer. Community nodes receive the new behaviour automatically via the runtime upgrade.ShieldKeystorefor key management and rotation, and the block proposer gains the ability to pass decapsulation keys to the runtime.pallet-shieldand theShieldApiruntime API. The shield is fully operational once the runtime is upgraded — no host function registration required.Architecture
All cryptographic operations (ML-KEM-768 decapsulation, XChaCha20-Poly1305 decryption) run
entirely in WASM. The node side is responsible only for key generation, storage, and rotation;
the raw decapsulation key bytes are passed to the runtime API as a parameter.
stc-shield)MemoryShieldKeystorewith ML-KEM-768CurrentKey/NextKeytry_decode_shielded_txparses wrappertry_unshield_tx— pureml-kemcratetry_unshield_tx— purechacha20poly1305crateChanges
pallet-shield(reworked)announce_next_keyinherent: rotatesCurrentKey <- NextKeyeach block and publishes the next block author's ML-KEM public keysubmit_encryptedextrinsic: accepts an encrypted ciphertext wrapper — the block proposer decrypts and includes the inner extrinsic in the same blocktry_decode_shielded_tx/try_unshield_tx: runtime helpers called by the block proposer via theShieldApiruntime APIunshieldfunction: performs ML-KEM-768 decapsulation + XChaCha20-Poly1305 AEAD decryption entirely in WASM, usingml-kemandchacha20poly1305crates directlyCheckShieldedTxValiditytransaction extension: pool validation checks ciphertext structure; block import additionally validateskey_hashagainstCurrentKey/NextKeyFindAuthorstrait for resolving current and next block authorSubmissions,KeyHashByBlock)stp-io(removed)stp-iocrate (custom host functions for ML-KEM and AEAD) has been deleted entirely. All crypto now runs in WASM viapallet-shield.Node integration
ShieldKeystorecreated inservice.rsand threaded through to the proposer and inherent providersShieldInherentDataProvideradded to Aura consensus configurationKeyRotationServicegenerates a new ML-KEM keypair on each own-block importSubtensorHostFunctionsremoved from the client executor — no longer neededRuntime integration
ShieldApiruntime API implemented —try_unshield_txacceptsdec_key_bytes: Vec<u8>and decrypts in WASMCheckShieldedTxValidityadded to the transaction extension pipelineTests
try_decode_shielded_tx,try_unshield_tx(with in-WASM decryption), depth-limit protection, inherent creation,CheckShieldedTxValidityextension (key_hash matching, malformed rejection, pool vs in-block source)E2E (branch:
setup-e2e)14 tests across 3 files covering 3-node and 6-node topologies:
Key rotation (3-node)
Encrypted transactions (3-node)
Scaling (6-node)
Edge cases