Rotate your revocable key
What this lets you do
Rotate your account's revocable key to a new revocableKeyIndex. Because the revocable key participates in authorization material, rotating it limits the blast radius if it's compromised, without forcing you to re-deposit or re-derive your other keys.
Constraints & limits
- One on-chain transaction,
Keystore.updateAuthPolicy, taking the old and newauthDigestplus a Merkle proof of your existing keystore leaf. It preserves the bound nullifying-key hash and swaps only the revocable-keyauthDigest. - Doesn't migrate or re-create your existing notes. They stay spendable, but only with your current key bundle: a spend proves your current keystore leaf, so after rotating you use the new revocable key, not the old one. The nullifying key and the notes themselves don't change.
- The index is monotonically increasing. You can't roll back to a previous index without re-rotating forward.
- For a fresh account, the canonical starting value is
0x0(no rotation performed yet).
What it unlocks next
- Post-rotation, your
ProtocolKeysbundle must include the newrevocableKeyIndexwhen building a session. - Compromise-response workflow: detect the leak, rotate, then revoke / blacklist the old auth material.
How to use it
import { CryptoService } from "@privacy-pools-v2/sdk";
import { keccak256, toBytes } from "viem";
// The SDK does not ship an EIP-712 payload builder; construct the
// Signature-Based Derivation typed data yourself. It must match the
// payload signed at registration byte-for-byte.
const signerAddress = walletClient.account.address;
const addressHash = keccak256(toBytes(signerAddress));
const purpose =
"This signature is used to deterministically derive application-specific secrets from your master seed. It is not a transaction and will not cost any gas.";
const signature = await walletClient.signTypedData({
domain: {
name: "Standardized Secret Derivation",
version: "1",
verifyingContract: "0x0000000000000000000000000000000000000000",
salt: keccak256(toBytes(APP_IDENTIFIER)), // exported by the SDK
},
types: {
SecretDerivation: [
{ name: "purpose", type: "string" },
{ name: "addressHash", type: "bytes32" },
],
},
primaryType: "SecretDerivation",
message: { purpose, addressHash },
});
const { newRevocableKeyIndex } = await session.rotateRevocableKey({
signature,
signerAddress,
addressHash,
});
// The live session already applied the rotation internally. Future
// session builds must re-derive the key bundle at the new index: the
// revocable key is derived per-index, so the pre-rotation bundle no
// longer matches the on-chain auth digest.
const rotatedKeys = new CryptoService().deriveKeysFromSignature({
signature,
signerAddress,
addressHash,
revocableKeyIndex: newRevocableKeyIndex,
});
const newSession = await PoolSessionBuilder.fromConfig({
...baseConfig,
protocolKeys: { ...rotatedKeys, revocableKeyIndex: newRevocableKeyIndex },
}).create();
The SDK derives the next revocable key from the signature and returns the new index. Persist newRevocableKeyIndex, and on later session builds re-derive the key bundle at that index; reusing a pre-rotation privateRevocableKey with the new index fails every proof.
Behind the scenes
SDK call
session.rotateRevocableKey(...)Contract method
Keystore.updateAuthPolicy(oldAuthDigest, newAuthDigest, siblingNodes): rotation, not setAuthPolicy (which is first-time registration)State change Your account's auth policy on Keystore is updated and revocableKeyIndex advances. The on-chain nullifying-key hash is unchanged.