Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions crates/cli/src/ciphernode/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use alloy::primitives::U256;
use anyhow::{bail, Result};

use super::context::ChainContext;
use super::utils::{format_amount, parse_amount, parse_u256_list};
use super::utils::{format_amount, parse_amount};

pub(crate) async fn register(ctx: &ChainContext) -> Result<()> {
let receipt = ctx
Expand All @@ -26,11 +26,10 @@ pub(crate) async fn register(ctx: &ChainContext) -> Result<()> {
Ok(())
}

pub(crate) async fn deregister(ctx: &ChainContext, siblings: Vec<String>) -> Result<()> {
let proof = parse_u256_list(&siblings)?;
pub(crate) async fn deregister(ctx: &ChainContext) -> Result<()> {
let receipt = ctx
.bonding()
.deregisterOperator(proof)
.deregisterOperator()
.send()
.await?
.get_receipt()
Expand Down
11 changes: 3 additions & 8 deletions crates/cli/src/ciphernode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,8 @@ pub enum CiphernodeCommands {
#[command(flatten)]
chain: ChainArgs,
},
/// Request deregistration by providing the IMT proof siblings
/// Request deregistration from the bonding registry
Deregister {
#[arg(long = "proof", value_delimiter = ',', value_name = "NODE")]
sibling_nodes: Vec<String>,
#[command(flatten)]
chain: ChainArgs,
},
Expand Down Expand Up @@ -145,12 +143,9 @@ pub async fn execute(command: CiphernodeCommands, config: &AppConfig) -> Result<
let ctx = ChainContext::new(config, chain.selection()).await?;
lifecycle::register(&ctx).await?
}
CiphernodeCommands::Deregister {
chain,
sibling_nodes,
} => {
CiphernodeCommands::Deregister { chain } => {
let ctx = ChainContext::new(config, chain.selection()).await?;
lifecycle::deregister(&ctx, sibling_nodes).await?
lifecycle::deregister(&ctx).await?
}
CiphernodeCommands::Activate { chain } => {
let ctx = ChainContext::new(config, chain.selection()).await?;
Expand Down
20 changes: 0 additions & 20 deletions crates/cli/src/ciphernode/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,6 @@ pub(crate) fn parse_amount(value: &str, decimals: u8) -> Result<U256> {
Ok(result)
}

pub(crate) fn parse_u256_list(values: &[String]) -> Result<Vec<U256>> {
values
.iter()
.filter(|s| !s.trim().is_empty())
.map(|value| parse_u256(value))
.collect()
}

fn parse_u256(value: &str) -> Result<U256> {
let trimmed = value.trim();
if let Some(hex) = trimmed
.strip_prefix("0x")
.or_else(|| trimmed.strip_prefix("0X"))
{
U256::from_str_radix(hex, 16).context("Invalid hex value")
} else {
U256::from_str(trimmed).context("Invalid decimal value")
}
}

pub(crate) async fn ensure_allowance(
ctx: &ChainContext,
token: Address,
Expand Down
7 changes: 2 additions & 5 deletions docs/pages/ciphernode-operators/exits-and-slashing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,12 @@ To safely exit the ciphernode registry:

### Step 1: Deregister

Request deregistration with your IMT proof siblings:
Request deregistration:

```bash
enclave ciphernode deregister --proof 0x123...,0x456...,0x789...
enclave ciphernode deregister
```
Comment thread
hmzakhalid marked this conversation as resolved.

> Save your sibling proof from the `CiphernodeAdded` event when you first register. You'll need it
> to deregister.

### Step 2: Wait for Exit Delay

Assets enter a queue with a delay period (7 days on Sepolia):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,7 @@
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256[]",
"name": "siblingNodes",
"type": "uint256[]"
}
],
"inputs": [],
"name": "deregisterOperator",
"outputs": [],
"stateMutability": "nonpayable",
Expand Down Expand Up @@ -946,5 +940,5 @@
"deployedLinkReferences": {},
"immutableReferences": {},
"inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol",
"buildInfoId": "solc-0_8_28-8c7470711e670529b6d5512dcaf5896399b39260"
"buildInfoId": "solc-0_8_28-16cefaab22a5b0c22d849bfd93e340ea85b6ef79"
}
Original file line number Diff line number Diff line change
Expand Up @@ -637,11 +637,6 @@
"internalType": "address",
"name": "node",
"type": "address"
},
{
"internalType": "uint256[]",
"name": "siblingNodes",
"type": "uint256[]"
}
],
"name": "removeCiphernode",
Expand Down Expand Up @@ -787,5 +782,5 @@
"deployedLinkReferences": {},
"immutableReferences": {},
"inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol",
"buildInfoId": "solc-0_8_28-8c7470711e670529b6d5512dcaf5896399b39260"
"buildInfoId": "solc-0_8_28-16cefaab22a5b0c22d849bfd93e340ea85b6ef79"
}
Original file line number Diff line number Diff line change
Expand Up @@ -1263,5 +1263,5 @@
"deployedLinkReferences": {},
"immutableReferences": {},
"inputSourceName": "project/contracts/interfaces/IEnclave.sol",
"buildInfoId": "solc-0_8_28-8c7470711e670529b6d5512dcaf5896399b39260"
"buildInfoId": "solc-0_8_28-16cefaab22a5b0c22d849bfd93e340ea85b6ef79"
}
Original file line number Diff line number Diff line change
Expand Up @@ -934,5 +934,5 @@
"deployedLinkReferences": {},
"immutableReferences": {},
"inputSourceName": "project/contracts/interfaces/ISlashingManager.sol",
"buildInfoId": "solc-0_8_28-e31106e21ee0b1a0de522e91c17cb752d4a7fb4e"
"buildInfoId": "solc-0_8_28-16cefaab22a5b0c22d849bfd93e340ea85b6ef79"
}
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,8 @@ interface IBondingRegistry {

/**
* @notice Deregister as an operator and remove from IMT
* @param siblingNodes Sibling node proofs for IMT removal
* @dev Requires operator to provide sibling nodes for immediate IMT removal
*/
function deregisterOperator(uint256[] calldata siblingNodes) external;
function deregisterOperator() external;

/**
* @notice Increase operator's ticket balance by depositing tokens
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,7 @@ interface ICiphernodeRegistry {

/// @notice Remove a ciphernode from the registry
/// @param node Address of the ciphernode to remove
/// @param siblingNodes Array of sibling node indices for tree operations
function removeCiphernode(
address node,
uint256[] calldata siblingNodes
) external;
function removeCiphernode(address node) external;

/// @notice Initiates the committee selection process for a specified E3.
/// @dev This function MUST revert when not called by the Enclave contract.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,7 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable {
}

/// @inheritdoc IBondingRegistry
function deregisterOperator(
uint256[] calldata siblingNodes
) external noExitInProgress(msg.sender) {
function deregisterOperator() external noExitInProgress(msg.sender) {
Operator storage op = operators[msg.sender];
require(op.registered, NotRegistered());

Expand Down Expand Up @@ -353,7 +351,7 @@ contract BondingRegistry is IBondingRegistry, OwnableUpgradeable {
}

// CiphernodeRegistry already emits an event when a ciphernode is removed
registry.removeCiphernode(msg.sender, siblingNodes);
registry.removeCiphernode(msg.sender);

emit CiphernodeDeregistrationRequested(msg.sender, op.exitUnlocksAt);
_updateOperatorStatus(msg.sender);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
InternalLeanIMT,
LeanIMTData
} from "@zk-kit/lean-imt.sol/InternalLeanIMT.sol";
InternalLazyIMT,
LazyIMTData
} from "@zk-kit/lazy-imt.sol/InternalLazyIMT.sol";

/**
* @title CiphernodeRegistryOwnable
* @notice Ownable implementation of the ciphernode registry with IMT-based membership tracking
* @dev Manages ciphernode registration, committee selection, and integrates with bonding registry
*/
contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {
using InternalLeanIMT for LeanIMTData;
using InternalLazyIMT for LazyIMTData;

////////////////////////////////////////////////////////////
// //
Expand Down Expand Up @@ -55,8 +55,17 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {
/// their tickets to be a part of the committee.
uint256 public sortitionSubmissionWindow;

/// @notice Depth of the LazyIMT tree
uint8 public constant TREE_DEPTH = 20;
Comment thread
ctrlc03 marked this conversation as resolved.

/// @notice Incremental Merkle Tree (IMT) containing all registered ciphernodes
LeanIMTData public ciphernodes;
LazyIMTData public ciphernodes;

/// @notice Tracks whether a ciphernode is enabled in the registry
mapping(address => bool) public ciphernodeEnabled;

/// @notice Tracks the tree leaf index for each ciphernode
mapping(address => uint40) public ciphernodeTreeIndex;

/// @notice Maps E3 ID to the IMT root at the time of committee request
mapping(uint256 e3Id => uint256 root) public roots;
Expand Down Expand Up @@ -212,6 +221,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {
require(_owner != address(0), ZeroAddress());

__Ownable_init(msg.sender);
ciphernodes._init(TREE_DEPTH);
setSortitionSubmissionWindow(_submissionWindow);
if (_owner != owner()) transferOwnership(_owner);
}
Expand Down Expand Up @@ -302,29 +312,33 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {
return;
}

uint160 ciphernode = uint160(node);
ciphernodes._insert(ciphernode);
uint40 index = ciphernodes.numberOfLeaves;
ciphernodes._insert(uint160(node));
ciphernodeEnabled[node] = true;
ciphernodeTreeIndex[node] = index;
numCiphernodes++;
emit CiphernodeAdded(
node,
ciphernodes._indexOf(ciphernode),
index,
numCiphernodes,
ciphernodes.size
ciphernodes.numberOfLeaves
);
}

/// @inheritdoc ICiphernodeRegistry
function removeCiphernode(
address node,
uint256[] calldata siblingNodes
) external onlyOwnerOrBondingVault {
function removeCiphernode(address node) external onlyOwnerOrBondingVault {
require(isEnabled(node), CiphernodeNotEnabled(node));

uint160 ciphernode = uint160(node);
uint256 index = ciphernodes._indexOf(ciphernode);
ciphernodes._remove(ciphernode, siblingNodes);
uint40 index = ciphernodeTreeIndex[node];
ciphernodes._update(0, index);
ciphernodeEnabled[node] = false;
numCiphernodes--;
emit CiphernodeRemoved(node, index, numCiphernodes, ciphernodes.size);
emit CiphernodeRemoved(
node,
index,
numCiphernodes,
ciphernodes.numberOfLeaves
);
}

////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -497,13 +511,13 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {

/// @inheritdoc ICiphernodeRegistry
function isEnabled(address node) public view returns (bool) {
return ciphernodes._has(uint160(node));
return ciphernodeEnabled[node];
}

/// @notice Returns the current root of the ciphernode IMT
/// @return Current IMT root
function root() public view returns (uint256) {
return (ciphernodes._root());
return ciphernodes._root(TREE_DEPTH);
}

/// @notice Returns the IMT root at the time a committee was requested
Expand All @@ -525,7 +539,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {
/// @notice Returns the current size of the ciphernode IMT
/// @return Size of the IMT
function treeSize() public view returns (uint256) {
return ciphernodes.size;
return ciphernodes.numberOfLeaves;
}

/// @notice Returns the address of the bonding registry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry {
function addCiphernode(address) external pure {}

// solhint-disable-next-line no-empty-blocks
function removeCiphernode(address, uint256[] calldata) external pure {}
function removeCiphernode(address) external pure {}

function publishCommittee(
uint256,
Expand Down Expand Up @@ -183,7 +183,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry {
function addCiphernode(address) external pure {}

// solhint-disable-next-line no-empty-blocks
function removeCiphernode(address, uint256[] calldata) external pure {}
function removeCiphernode(address) external pure {}

function publishCommittee(
uint256,
Expand Down
2 changes: 0 additions & 2 deletions packages/enclave-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
ciphernodeAdminAdd,
ciphernodeMintTokens,
ciphernodeRemove,
ciphernodeSiblings,
updateSubmissionWindow,
} from "./tasks/ciphernode";
import {
Expand Down Expand Up @@ -92,7 +91,6 @@ const config: HardhatUserConfig = {
ciphernodeAdminAdd,
ciphernodeMintTokens,
ciphernodeRemove,
ciphernodeSiblings,
requestCommittee,
publishPlaintext,
publishCiphertext,
Expand Down
4 changes: 1 addition & 3 deletions packages/enclave-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@
"ciphernode:admin-add": "hardhat ciphernode:admin-add",
"ciphernode:mint-tokens": "hardhat ciphernode:mint-tokens",
"ciphernode:remove": "hardhat ciphernode:remove",
"ciphernode:siblings": "hardhat ciphernode:siblings",
"committee:new": "hardhat committee:new",
"lint": "solhint --disc --max-warnings 10 \"contracts/**/*.sol\"",
"prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yml}\"",
Expand All @@ -188,8 +187,7 @@
},
"dependencies": {
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@zk-kit/lean-imt.sol": "2.0.1",
"@zk-kit/lean-imt": "2.2.4",
"@zk-kit/lazy-imt.sol": "2.0.0-beta.12",
"js-yaml": "^4.1.1",
"poseidon-solidity": "0.0.5"
},
Expand Down
Loading
Loading