From c4ae45fc97b51d248c3c2b652541edc0f6c0b194 Mon Sep 17 00:00:00 2001 From: Zuoyuan Zhao Date: Mon, 16 Jun 2025 17:17:30 -0500 Subject: [PATCH 1/5] added subscriber tests --- Cargo.lock | 1 + crates/arpa-node/Cargo.toml | 3 +- crates/arpa-node/build.rs | 4 +- .../test-contract/MockNodeRegistry.sol | 36 - .../MockAdapter.sol | 53 ++ .../MockController.sol | 50 ++ .../mock_contracts/MockControllerRelayer.sol | 41 + .../src/mock_contracts/MockCoordinator.sol | 108 +++ .../src/mock_contracts/MockNodeRegistry.sol | 172 +++++ crates/arpa-node/src/subscriber/block.rs | 233 ++++++ .../arpa-node/src/subscriber/in_grouping.rs | 315 ++++++++ crates/arpa-node/src/subscriber/mod.rs | 250 ++++++ .../arpa-node/src/subscriber/post_grouping.rs | 573 ++++++++++++++ .../src/subscriber/post_success_grouping.rs | 266 ++++++- .../arpa-node/src/subscriber/pre_grouping.rs | 197 +++++ .../randomness_signature_aggregation.rs | 619 ++++++++++++++- .../ready_to_handle_randomness_task.rs | 722 ++++++++++++++++++ .../subscriber/schedule_node_activation.rs | 367 +++++++++ 18 files changed, 3967 insertions(+), 43 deletions(-) delete mode 100644 crates/arpa-node/src/listener/test-contract/MockNodeRegistry.sol rename crates/arpa-node/src/{listener/test-contract => mock_contracts}/MockAdapter.sol (53%) rename crates/arpa-node/src/{listener/test-contract => mock_contracts}/MockController.sol (75%) create mode 100644 crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol create mode 100644 crates/arpa-node/src/mock_contracts/MockCoordinator.sol create mode 100644 crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol diff --git a/Cargo.lock b/Cargo.lock index 328fe955..94f1e809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -591,6 +591,7 @@ dependencies = [ "chrono", "dkg-core", "ethers", + "ethers-core", "futures", "glob", "gumdrop", diff --git a/crates/arpa-node/Cargo.toml b/crates/arpa-node/Cargo.toml index e4081f69..d8a31ca6 100644 --- a/crates/arpa-node/Cargo.toml +++ b/crates/arpa-node/Cargo.toml @@ -75,5 +75,6 @@ check-latest = { version = "1.0.2", default-features = false, features = [ unittest = [] [dev-dependencies] -arpa-node = { path = ".", features = [] } +arpa-node = { path = ".", features = ["unittest"] } regex = "1.11.1" +ethers-core.workspace = true diff --git a/crates/arpa-node/build.rs b/crates/arpa-node/build.rs index 370d6720..464becea 100644 --- a/crates/arpa-node/build.rs +++ b/crates/arpa-node/build.rs @@ -10,11 +10,11 @@ const SOLIDITY_EXTENSION: &str = "sol"; #[cfg(feature = "unittest")] const OUTPUT_DIR: &str = "./src/test_contracts"; #[cfg(feature = "unittest")] -const CONTRACT_DIR: &str = "./src/listener/test-contract"; +const CONTRACT_DIR: &str = "./src/mock_contracts"; fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=proto"); - println!("cargo:rerun-if-changed=src/listener/test-contract"); + println!("cargo:rerun-if-changed=src/mock_contracts"); let mut prost_build = prost_build::Config::new(); prost_build.btree_map(["members"]); diff --git a/crates/arpa-node/src/listener/test-contract/MockNodeRegistry.sol b/crates/arpa-node/src/listener/test-contract/MockNodeRegistry.sol deleted file mode 100644 index da64b732..00000000 --- a/crates/arpa-node/src/listener/test-contract/MockNodeRegistry.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface INodeRegistry { - struct Node { - address idAddress; - bytes dkgPublicKey; - bool isEigenlayerNode; - bool state; - uint256 pendingUntilBlock; - } - - function getNode(address nodeAddress) external view returns (Node memory); -} - -contract MockNodeRegistry is INodeRegistry { - mapping(address => Node) public nodes; - - function registerNode(address idAddress, bytes memory dkgPublicKey, bool isEigenlayerNode) external { - nodes[idAddress] = Node({ - idAddress: idAddress, - dkgPublicKey: dkgPublicKey, - isEigenlayerNode: isEigenlayerNode, - state: false, - pendingUntilBlock: 0 - }); - } - - function setNodeState(address idAddress, bool state) external { - nodes[idAddress].state = state; - } - - function getNode(address nodeAddress) public view override(INodeRegistry) returns (Node memory) { - return nodes[nodeAddress]; - } -} \ No newline at end of file diff --git a/crates/arpa-node/src/listener/test-contract/MockAdapter.sol b/crates/arpa-node/src/mock_contracts/MockAdapter.sol similarity index 53% rename from crates/arpa-node/src/listener/test-contract/MockAdapter.sol rename to crates/arpa-node/src/mock_contracts/MockAdapter.sol index 1c418f78..b7b90a29 100644 --- a/crates/arpa-node/src/listener/test-contract/MockAdapter.sol +++ b/crates/arpa-node/src/mock_contracts/MockAdapter.sol @@ -28,7 +28,20 @@ contract MockAdapter is IRequestTypeBase, IAdapter { uint256 estimatedPayment ); + event RandomnessRequestResult( + bytes32 indexed requestId, + uint32 indexed groupIndex, + address committer, + address[] participantMembers, + uint256 randomness, + uint256 payment, + uint256 flatFee, + bool success + ); + mapping(bytes32 => bytes32) public _requestCommitments; + mapping(bytes32 => bool) public shouldRevert; + mapping(bytes32 => bool) public shouldRevertWithCustomError; function emitRandomnessRequest( bytes32 requestId, @@ -65,4 +78,44 @@ contract MockAdapter is IRequestTypeBase, IAdapter { function setRequestCommitment(bytes32 requestId, bytes32 commitment) public { _requestCommitments[requestId] = commitment; } + + function setShouldRevert(bytes32 requestId, bool _shouldRevert) public { + shouldRevert[requestId] = _shouldRevert; + } + + function setShouldRevertWithCustomError(bytes32 requestId, bool _shouldRevertWithCustomError) public { + shouldRevertWithCustomError[requestId] = _shouldRevertWithCustomError; + } + + function fulfillRandomness( + uint32 groupIndex, + bytes32 requestId, + uint256 signature, + bytes calldata /* requestDetail */, + bytes calldata /* partialSignatures */ + ) public { + if (shouldRevertWithCustomError[requestId]) { + revert("CustomTestError"); + } + + if (shouldRevert[requestId]) { + revert("TestRevert"); + } + + delete _requestCommitments[requestId]; + + address[] memory participantMembers = new address[](1); + participantMembers[0] = msg.sender; + + emit RandomnessRequestResult( + requestId, + groupIndex, + msg.sender, + participantMembers, + uint256(keccak256(abi.encode(signature))), + 1000, + 100, + true + ); + } } \ No newline at end of file diff --git a/crates/arpa-node/src/listener/test-contract/MockController.sol b/crates/arpa-node/src/mock_contracts/MockController.sol similarity index 75% rename from crates/arpa-node/src/listener/test-contract/MockController.sol rename to crates/arpa-node/src/mock_contracts/MockController.sol index 5e7b2751..02274b1e 100644 --- a/crates/arpa-node/src/listener/test-contract/MockController.sol +++ b/crates/arpa-node/src/mock_contracts/MockController.sol @@ -30,14 +30,30 @@ interface IController { uint256[4] publicKey; } + struct CommitDkgParams { + uint256 groupIndex; + uint256 groupEpoch; + bytes publicKey; + bytes partialPublicKey; + address[] disqualifiedNodes; + } + function getGroup(uint256 groupIndex) external view returns (Group memory); + + function commitDkg(CommitDkgParams memory params) external; + + function getCoordinator(uint256 groupIndex) external view returns (address); } contract MockController is IController { mapping(uint256 => Group) private groups; + mapping(uint256 => address) private _coordinators; address public nodeRegistryAddress; address public adapterAddress; + bool public shouldSucceed = true; + string public failureMessage = ""; + event DkgTask( uint256 indexed globalEpoch, uint256 indexed groupIndex, @@ -49,6 +65,12 @@ contract MockController is IController { address coordinatorAddress ); + event CommitDkgSuccess( + uint256 indexed groupIndex, + uint256 indexed groupEpoch, + address indexed committer + ); + constructor(address _nodeRegistryAddress) { nodeRegistryAddress = _nodeRegistryAddress; adapterAddress = address(0); @@ -124,6 +146,14 @@ contract MockController is IController { return groups[groupIndex]; } + function setCoordinator(uint256 groupIndex, address coordinator) external { + _coordinators[groupIndex] = coordinator; + } + + function getCoordinator(uint256 groupIndex) public view override(IController) returns (address) { + return _coordinators[groupIndex]; + } + function emitDkgTaskEvent( uint256 globalEpoch, uint256 groupIndex, @@ -149,4 +179,24 @@ contract MockController is IController { function setAdapterAddress(address _adapterAddress) external { adapterAddress = _adapterAddress; } + + function commitDkg(CommitDkgParams memory params) external override(IController) { + if (!shouldSucceed) { + revert(failureMessage); + } + + emit CommitDkgSuccess( + params.groupIndex, + params.groupEpoch, + msg.sender + ); + } + + function setShouldSucceed(bool _shouldSucceed) external { + shouldSucceed = _shouldSucceed; + } + + function setFailureMessage(string memory _message) external { + failureMessage = _message; + } } \ No newline at end of file diff --git a/crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol b/crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol new file mode 100644 index 00000000..e10abf79 --- /dev/null +++ b/crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IControllerRelayer { + function relayGroup(uint256 chainId, uint256 groupIndex) external; +} + +contract MockControllerRelayer is IControllerRelayer { + bool public shouldSucceed = true; + string public failureMessage = ""; + + event GroupRelayed( + uint256 epoch, + uint256 indexed groupIndex, + uint256 indexed groupEpoch, + address indexed committer + ); + + constructor() {} + + function relayGroup(uint256 chainId, uint256 groupIndex) external override { + if (!shouldSucceed) { + revert(failureMessage); + } + + emit GroupRelayed( + 1, // mock epoch + groupIndex, // actual groupIndex + 1, // mock groupEpoch + msg.sender // actual committer + ); + } + + function setShouldSucceed(bool _shouldSucceed) external { + shouldSucceed = _shouldSucceed; + } + + function setFailureMessage(string memory _message) external { + failureMessage = _message; + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/mock_contracts/MockCoordinator.sol b/crates/arpa-node/src/mock_contracts/MockCoordinator.sol new file mode 100644 index 00000000..96b105d2 --- /dev/null +++ b/crates/arpa-node/src/mock_contracts/MockCoordinator.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract MockCoordinator { + uint256 public dkgThreshold; + bytes[] public dkgKeys; + address[] public participants; + bytes[] public sharesData; + bytes[] public responsesData; + bytes[] public justificationsData; + int8 public currentPhase = 0; + + constructor(uint256 _threshold) { + dkgThreshold = _threshold; + } + + function setDkgKeys(uint256 _threshold, bytes[] memory _keys) external { + dkgThreshold = _threshold; + dkgKeys = _keys; + } + + function setParticipants(address[] memory _participants) external { + participants = _participants; + } + + function setShares(bytes[] memory _shares) external { + sharesData = _shares; + } + + function setResponses(bytes[] memory _responses) external { + responsesData = _responses; + } + + function setJustifications(bytes[] memory _justifications) external { + justificationsData = _justifications; + } + + function setCurrentPhase(int8 _phase) external { + currentPhase = _phase; + } + + function getDkgKeys() external view returns (uint256, bytes[] memory) { + return (dkgThreshold, dkgKeys); + } + + function getParticipants() external view returns (address[] memory) { + return participants; + } + + function getShares() external view returns (bytes[] memory) { + return sharesData; + } + + function getResponses() external view returns (bytes[] memory) { + return responsesData; + } + + function getJustifications() external view returns (bytes[] memory) { + return justificationsData; + } + + function inPhase() external view returns (int8) { + return currentPhase; + } + + function setupTestScenario( + uint256 _threshold, + address[] memory _participants, + bytes[] memory _keys, + bytes[] memory _shares, + bytes[] memory _responses, + bytes[] memory _justifications + ) external { + dkgThreshold = _threshold; + participants = _participants; + dkgKeys = _keys; + sharesData = _shares; + responsesData = _responses; + justificationsData = _justifications; + currentPhase = 1; // 默认开始Phase 1 + } + + function clearAllData() external { + delete dkgKeys; + delete participants; + delete sharesData; + delete responsesData; + delete justificationsData; + currentPhase = 0; + dkgThreshold = 0; + } + + function getDataStats() external view returns ( + uint256 keysCount, + uint256 participantsCount, + uint256 sharesCount, + uint256 responsesCount, + uint256 justificationsCount + ) { + return ( + dkgKeys.length, + participants.length, + sharesData.length, + responsesData.length, + justificationsData.length + ); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol b/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol new file mode 100644 index 00000000..952c4464 --- /dev/null +++ b/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ISignatureUtils { + struct SignatureWithSaltAndExpiry { + bytes signature; + bytes32 salt; + uint256 expiry; + } +} + +interface INodeRegistry { + struct Node { + address idAddress; + bytes dkgPublicKey; + bool isEigenlayerNode; + bool state; + uint256 pendingUntilBlock; + } + + function getNode(address nodeAddress) external view returns (Node memory); + function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) external; + function getNodeRegistryConfig() external view returns ( + address controllerContractAddress, + address stakingContractAddress, + address serviceManagerContractAddress, + uint256 nativeNodeStakingAmount, + uint256 eigenlayerNodeStakingAmount, + uint256 pendingBlockAfterQuit + ); +} + +contract MockAVSDirectory { + bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = + keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); + + bytes32 private constant _TYPE_HASH = + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); + + function calculateOperatorAVSRegistrationDigestHash( + address operator, + address avs, + bytes32 salt, + uint256 expiry + ) public view returns (bytes32) { + return _calculateSignableDigest( + keccak256(abi.encode(OPERATOR_AVS_REGISTRATION_TYPEHASH, operator, avs, salt, expiry)) + ); + } + + function _calculateSignableDigest(bytes32 structHash) internal view returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash)); + } + + function _domainSeparator() internal view returns (bytes32) { + return keccak256(abi.encode( + _TYPE_HASH, + keccak256("MockAVSDirectory"), + block.chainid, + address(this) + )); + } +} + +contract MockServiceManager { + address public avsDirectory; + + constructor(address _avsDirectory) { + avsDirectory = _avsDirectory; + } +} + +contract MockNodeRegistry is INodeRegistry { + struct Config { + address controllerContractAddress; + address stakingContractAddress; + address serviceManagerContractAddress; + uint256 nativeNodeStakingAmount; + uint256 eigenlayerNodeStakingAmount; + uint256 pendingBlockAfterQuit; + } + + mapping(address => Node) public nodes; + Config private _config; + + event NodeActivated(address indexed nodeAddress, uint256 groupIndex); + + error NodeNotRegistered(); + error NodeAlreadyActive(); + error NodeStillPending(uint256 pendingUntilBlock); + + constructor( + address _controllerAddress, + address _stakingAddress, + address _serviceManagerAddress + ) { + _config = Config({ + controllerContractAddress: _controllerAddress, + stakingContractAddress: _stakingAddress, + serviceManagerContractAddress: _serviceManagerAddress, + nativeNodeStakingAmount: 100 ether, + eigenlayerNodeStakingAmount: 1000 ether, + pendingBlockAfterQuit: 1000 + }); + } + + function registerNode(address idAddress, bytes memory dkgPublicKey, bool isEigenlayerNode) external { + nodes[idAddress] = Node({ + idAddress: idAddress, + dkgPublicKey: dkgPublicKey, + isEigenlayerNode: isEigenlayerNode, + state: false, + pendingUntilBlock: 0 + }); + } + + function setNodeState(address idAddress, bool state) external { + nodes[idAddress].state = state; + } + + function getNode(address nodeAddress) public view override(INodeRegistry) returns (Node memory) { + return nodes[nodeAddress]; + } + + function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) + external + override(INodeRegistry) + { + Node storage node = nodes[msg.sender]; + + if (node.idAddress != msg.sender) { + revert NodeNotRegistered(); + } + + if (node.state) { + revert NodeAlreadyActive(); + } + + if (node.pendingUntilBlock > block.number) { + revert NodeStillPending(node.pendingUntilBlock); + } + + node.state = true; + + uint256 groupIndex = 1; + + emit NodeActivated(msg.sender, groupIndex); + } + + function getNodeRegistryConfig() + public + view + override(INodeRegistry) + returns ( + address controllerContractAddress, + address stakingContractAddress, + address serviceManagerContractAddress, + uint256 nativeNodeStakingAmount, + uint256 eigenlayerNodeStakingAmount, + uint256 pendingBlockAfterQuit + ) + { + return ( + _config.controllerContractAddress, + _config.stakingContractAddress, + _config.serviceManagerContractAddress, + _config.nativeNodeStakingAmount, + _config.eigenlayerNodeStakingAmount, + _config.pendingBlockAfterQuit + ); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/block.rs b/crates/arpa-node/src/subscriber/block.rs index f928af7b..ec63cb9f 100644 --- a/crates/arpa-node/src/subscriber/block.rs +++ b/crates/arpa-node/src/subscriber/block.rs @@ -60,3 +60,236 @@ impl Subscriber for BlockSubscriber { } impl DebuggableSubscriber for BlockSubscriber {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + event::{new_block::NewBlock, types::Topic, Event}, + queue::event_queue::EventQueue, + }; + use arpa_dal::{BlockInfoHandler, BlockInfoFetcher, BlockInfoUpdater}; + use std::sync::Arc; + use tokio::sync::RwLock; + + #[derive(Debug)] + struct MockBlockInfoHandler { + chain_id: usize, + block_height: Arc>, + block_time: usize, + } + + impl MockBlockInfoHandler { + fn new(chain_id: usize, initial_block_height: usize, block_time: usize) -> Self { + Self { + chain_id, + block_height: Arc::new(RwLock::new(initial_block_height)), + block_time, + } + } + } + + impl BlockInfoFetcher for MockBlockInfoHandler { + fn get_chain_id(&self) -> usize { + self.chain_id + } + + fn get_block_height(&self) -> usize { + futures::executor::block_on(async { *self.block_height.read().await }) + } + + fn get_block_time(&self) -> usize { + self.block_time + } + } + + impl BlockInfoUpdater for MockBlockInfoHandler { + fn set_block_height(&mut self, block_height: usize) { + if let Ok(mut height) = self.block_height.try_write() { + *height = block_height; + } + } + } + + impl BlockInfoHandler for MockBlockInfoHandler {} + + #[tokio::test] + async fn test_block_subscriber_creation() { + let chain_id = 1; + let block_cache = Arc::new(RwLock::new( + Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + + assert_eq!(subscriber.chain_id, chain_id); + } + + #[tokio::test] + async fn test_notify_updates_block_height() { + let chain_id = 1; + let initial_height = 100; + let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); + let block_cache = Arc::new(RwLock::new( + Box::new(mock_handler) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + + let new_block_height = 150; + let new_block_event = NewBlock::new(chain_id, new_block_height); + + let result = subscriber + .notify(Topic::NewBlock(chain_id), &new_block_event) + .await; + + assert!(result.is_ok()); + + let cache_guard = block_cache.read().await; + let current_height = cache_guard.get_block_height(); + assert_eq!(current_height, new_block_height); + } + + #[tokio::test] + async fn test_notify_with_different_chain_id() { + let chain_id = 1; + let different_chain_id = 2; + let initial_height = 100; + let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); + let block_cache = Arc::new(RwLock::new( + Box::new(mock_handler) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + + let new_block_event = NewBlock::new(chain_id, 150); + let result = subscriber + .notify(Topic::NewBlock(different_chain_id), &new_block_event) + .await; + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscribe_registers_with_event_queue() { + let chain_id = 1; + let block_cache = Arc::new(RwLock::new( + Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_multiple_notify_calls() { + let chain_id = 1; + let initial_height = 100; + let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); + let block_cache = Arc::new(RwLock::new( + Box::new(mock_handler) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + + let first_block = NewBlock::new(chain_id, 150); + let result1 = subscriber + .notify(Topic::NewBlock(chain_id), &first_block) + .await; + assert!(result1.is_ok()); + + let second_block = NewBlock::new(chain_id, 200); + let result2 = subscriber + .notify(Topic::NewBlock(chain_id), &second_block) + .await; + assert!(result2.is_ok()); + + let cache_guard = block_cache.read().await; + let final_height = cache_guard.get_block_height(); + assert_eq!(final_height, 200); + } + + #[tokio::test] + async fn test_debuggable_subscriber_trait() { + let chain_id = 1; + let block_cache = Arc::new(RwLock::new( + Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache, eq); + let _: &dyn DebuggableSubscriber = &subscriber; + } + + #[tokio::test] + #[should_panic] + async fn test_notify_with_wrong_event_type() { + let chain_id = 1; + let block_cache = Arc::new(RwLock::new( + Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber = BlockSubscriber::new(chain_id, block_cache, eq); + + #[derive(Debug)] + struct WrongEvent; + + impl Event for WrongEvent { + fn topic(&self) -> Topic { + Topic::NewBlock(1) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + } + + impl DebuggableEvent for WrongEvent {} + + let wrong_event = WrongEvent; + let _result = subscriber + .notify(Topic::NewBlock(chain_id), &wrong_event) + .await; + } + + mod integration_tests { + use super::*; + + #[tokio::test] + async fn test_block_subscriber_integration() { + let chain_id = 1; + let mock_handler = MockBlockInfoHandler::new(chain_id, 0, 12); + + let block_cache = Arc::new(RwLock::new( + Box::new(mock_handler) as Box + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + + let subscriber_for_subscribe = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + subscriber_for_subscribe.subscribe().await; + let subscriber = BlockSubscriber::new(chain_id, block_cache.clone(), eq.clone()); + + let heights = vec![10, 20, 30, 40, 50]; + + for height in heights.iter() { + let new_block = NewBlock::new(chain_id, *height); + + let result = subscriber + .notify(Topic::NewBlock(chain_id), &new_block) + .await; + + assert!(result.is_ok()); + } + + let cache_guard = block_cache.read().await; + let final_height = cache_guard.get_block_height(); + assert_eq!(final_height, 50); + } + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/in_grouping.rs b/crates/arpa-node/src/subscriber/in_grouping.rs index c989226a..c836be88 100644 --- a/crates/arpa-node/src/subscriber/in_grouping.rs +++ b/crates/arpa-node/src/subscriber/in_grouping.rs @@ -383,3 +383,318 @@ impl DebuggableSubscriber for InGroupingSubscriber { } + +#[cfg(feature = "unittest")] +mod tests { + use super::*; + use crate::{ + event::{run_dkg::RunDKG, types::Topic}, + queue::event_queue::EventQueue, + scheduler::dynamic::SimpleDynamicTaskScheduler, + test_contracts::{ + mockcontroller::{deploy_mock_controller_with_args, get_mock_controller_at}, + mockcoordinator::deploy_mock_coordinator_with_args + }, + }; + use arpa_core::{Config, DKGStatus, GeneralMainChainIdentity, DKGTask}; + use crate::test_contracts::mockcontroller::MockController; + use arpa_dal::{ + cache::{InMemoryGroupInfoCache, InMemoryNodeInfoCache}, + GroupInfoHandler, NodeInfoHandler, + }; + use ethers::prelude::*; + use rand::thread_rng; + use std::sync::Arc; + use threshold_bls::{schemes::bn254::G2Curve, group::Element}; + use tokio::sync::RwLock; + + const WS_ENDPOINT: &str = "ws://localhost:8545"; + const HTTP_ENDPOINT: &str = "http://localhost:8545"; + + async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { + let anvil = ethers::utils::Anvil::new().spawn(); + let ws_provider = Arc::new(Provider::::connect(anvil.ws_endpoint()).await.expect("Failed to connect to anvil")); + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet = wallet.with_chain_id(anvil.chain_id()); + (anvil, ws_provider, wallet) + } + + async fn deploy_controller(ws_provider: Arc>, wallet: LocalWallet) -> Address { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let controller_address = deploy_mock_controller_with_args(client.clone(), Address::random()).await.unwrap(); + let controller_contract: MockController, LocalWallet>> = + get_mock_controller_at(controller_address, client.clone()); + controller_contract.set_should_succeed(true).send().await.unwrap().await.unwrap(); + controller_address + } + + async fn deploy_coordinator(ws_provider: Arc>, wallet: LocalWallet, threshold: u64) -> Address { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + deploy_mock_coordinator_with_args(client.clone(), U256::from(threshold)).await.unwrap() + } + + async fn create_chain_identity( + chain_id: u64, + wallet: LocalWallet, + ws_provider: Arc>, + ws_endpoint: String, + controller_address: Address, + ) -> Arc>> { + let config = Config::default(); + let general_chain_identity = GeneralMainChainIdentity::new( + chain_id.try_into().unwrap(), + wallet.clone(), + ws_provider.clone(), + ws_endpoint, + controller_address, + Address::random(), + Address::random(), + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + async fn setup_node_cache(id_address: Address) -> NodeResult>>>> { + let node_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryNodeInfoCache::::new(id_address)), + )); + { + let mut node_cache_write = node_cache.write().await; + node_cache_write.set_node_rpc_endpoint(HTTP_ENDPOINT.to_string()).await?; + let private_key = ::Scalar::rand(&mut thread_rng()); + let mut public_key = ::Point::one(); + public_key.mul(&private_key); + node_cache_write.set_dkg_key_pair(private_key, public_key).await?; + } + Ok(node_cache) + } + + async fn setup_group_cache( + id_address: Address, + chain_id: usize, + group_index: usize, + epoch: usize, + size: usize, + threshold: usize, + member_addresses: Vec
, + dkg_status: DKGStatus, + ) -> NodeResult>>>> { + let group_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryGroupInfoCache::::new(id_address)), + )); + { + let mut group_cache_write = group_cache.write().await; + let dkg_task = arpa_core::DKGTask { + group_index, + epoch, + size, + threshold, + assignment_block_height: 100, + members: member_addresses.clone(), + coordinator_address: Address::random(), + }; + group_cache_write.save_task_info(chain_id, dkg_task).await?; + group_cache_write.update_dkg_status(group_index, epoch, dkg_status).await?; + } + Ok(group_cache) + } + + fn create_schedulers() -> (Arc>, Arc>) { + ( + Arc::new(RwLock::new(EventQueue::new())), + Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())) + ) + } + + #[tokio::test] + async fn test_in_grouping_subscriber_creation() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; + let id_address = Address::random(); + let node_cache = setup_node_cache(id_address).await.unwrap(); + let group_cache = setup_group_cache( + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::random(), Address::random()], + DKGStatus::InPhase, + ).await.unwrap(); + let (eq, ts) = create_schedulers(); + let subscriber = InGroupingSubscriber::new(chain_identity, node_cache, group_cache, eq, ts, 1000); + assert_eq!(subscriber.dkg_wait_for_phase_interval_millis, 1000); + } + + #[tokio::test] + async fn test_dkg_handler_creation() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; + let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; + let id_address = Address::random(); + let node_cache = setup_node_cache(id_address).await.unwrap(); + let group_cache = setup_group_cache( + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::random(), Address::random()], + DKGStatus::InPhase, + ).await.unwrap(); + let handler = AllInOneDKGHandler::new(|| thread_rng(), chain_identity, node_cache, group_cache.clone(), 1000); + let task = DKGTask { + group_index: 1, + epoch: 1, + size: 3, + threshold: 2, + members: vec![id_address, Address::random(), Address::random()], + assignment_block_height: 100, + coordinator_address, + }; + assert_eq!(task.group_index, 1); + assert_eq!(task.epoch, 1); + assert_eq!(task.size, 3); + assert_eq!(task.threshold, 2); + assert_eq!(handler.dkg_wait_for_phase_interval_millis, 1000); + } + + #[tokio::test] + async fn test_subscriber_notify() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; + let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; + let id_address = Address::random(); + let node_cache = setup_node_cache(id_address).await.unwrap(); + let group_cache = setup_group_cache( + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::random(), Address::random()], + DKGStatus::InPhase, + ).await.unwrap(); + let (eq, ts) = create_schedulers(); + let subscriber = InGroupingSubscriber::new(chain_identity, node_cache, group_cache, eq, ts, 1000); + let task = DKGTask { + group_index: 1, + epoch: 1, + size: 3, + threshold: 2, + members: vec![id_address, Address::random(), Address::random()], + assignment_block_height: 100, + coordinator_address, + }; + let run_dkg_event = RunDKG { dkg_task: task }; + let result = subscriber.notify(Topic::RunDKG, &run_dkg_event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_subscribe() { + let id_address = Address::random(); + let node_cache = setup_node_cache(id_address).await.unwrap(); + let group_cache = setup_group_cache( + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::random(), Address::random()], + DKGStatus::InPhase, + ).await.unwrap(); + let (eq, ts) = create_schedulers(); + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let controller_address = Address::random(); + let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; + let subscriber = InGroupingSubscriber::new(chain_identity, node_cache, group_cache, eq.clone(), ts, 1000); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_group_cache_operations() { + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, 1, 2, 2, 5, 3, + vec![id_address, Address::random(), Address::random(), Address::random(), Address::random()], + DKGStatus::InPhase, + ).await.unwrap(); + { + let group_cache_read = group_cache.read().await; + assert_eq!(group_cache_read.get_index().unwrap(), 2); + assert_eq!(group_cache_read.get_epoch().unwrap(), 2); + assert_eq!(group_cache_read.get_size().unwrap(), 5); + assert_eq!(group_cache_read.get_threshold().unwrap(), 3); + assert_eq!(group_cache_read.get_self_id_address().unwrap(), id_address); + assert_eq!(group_cache_read.get_dkg_status().unwrap(), DKGStatus::InPhase); + } + { + let mut group_cache_write = group_cache.write().await; + let result = group_cache_write.update_dkg_status(2, 2, DKGStatus::CommitSuccess).await; + assert!(result.is_ok()); + } + { + let group_cache_read = group_cache.read().await; + assert_eq!(group_cache_read.get_dkg_status().unwrap(), DKGStatus::CommitSuccess); + } + } + + #[tokio::test] + async fn test_node_cache_operations() { + let id_address = Address::random(); + let node_cache = setup_node_cache(id_address).await.unwrap(); + { + let node_cache_read = node_cache.read().await; + assert_eq!(node_cache_read.get_id_address().unwrap(), id_address); + assert_eq!(node_cache_read.get_node_rpc_endpoint().unwrap(), HTTP_ENDPOINT); + assert!(node_cache_read.get_dkg_private_key().is_ok()); + assert!(node_cache_read.get_dkg_public_key().is_ok()); + } + { + let mut node_cache_write = node_cache.write().await; + let new_endpoint = "http://localhost:9545".to_string(); + let result = node_cache_write.set_node_rpc_endpoint(new_endpoint.clone()).await; + assert!(result.is_ok()); + } + { + let node_cache_read = node_cache.read().await; + assert_eq!(node_cache_read.get_node_rpc_endpoint().unwrap(), "http://localhost:9545"); + } + } + + #[tokio::test] + async fn test_dkg_task_with_different_statuses() { + let id_address = Address::random(); + let statuses = vec![DKGStatus::None, DKGStatus::InPhase, DKGStatus::WaitForPostProcess, DKGStatus::CommitSuccess]; + for (i, status) in statuses.iter().enumerate() { + let group_cache = setup_group_cache( + id_address, 1, i + 1, 1, 3, 2, + vec![id_address, Address::random(), Address::random()], + *status, + ).await.unwrap(); + let group_cache_read = group_cache.read().await; + assert_eq!(group_cache_read.get_index().unwrap(), i + 1); + assert_eq!(group_cache_read.get_dkg_status().unwrap(), *status); + } + } + + #[tokio::test] + async fn test_multiple_group_members() { + let id_address = Address::random(); + let member_addresses = vec![id_address, Address::random(), Address::random(), Address::random(), Address::random()]; + let group_cache = setup_group_cache( + id_address, 1, 1, 1, 5, 3, + member_addresses.clone(), + DKGStatus::InPhase, + ).await.unwrap(); + let group_cache_read = group_cache.read().await; + assert_eq!(group_cache_read.get_size().unwrap(), 5); + assert_eq!(group_cache_read.get_threshold().unwrap(), 3); + let members = group_cache_read.get_members().unwrap(); + assert_eq!(members.len(), 5); + assert!(members.contains_key(&id_address)); + } + + #[tokio::test] + async fn test_contract_integration() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; + let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; + assert_ne!(controller_address, Address::zero()); + assert_ne!(coordinator_address, Address::zero()); + let chain_identity_read = chain_identity.read().await; + chain_identity_read.build_controller_client(); + chain_identity_read.build_coordinator_client(coordinator_address); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/mod.rs b/crates/arpa-node/src/subscriber/mod.rs index 7c65a307..6624dd03 100644 --- a/crates/arpa-node/src/subscriber/mod.rs +++ b/crates/arpa-node/src/subscriber/mod.rs @@ -23,3 +23,253 @@ pub trait Subscriber { async fn subscribe(self); } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + error::NodeResult, + event::{types::Topic, Event}, + }; + use async_trait::async_trait; + use std::any::Any; + + #[derive(Debug, Clone)] + struct MockEvent { + topic: Topic, + data: String, + } + + impl MockEvent { + fn new(topic: Topic, data: String) -> Self { + Self { topic, data } + } + } + + impl Event for MockEvent { + fn topic(&self) -> Topic { + self.topic.clone() + } + + fn as_any(&self) -> &dyn Any { + self + } + } + + impl DebuggableEvent for MockEvent {} + + #[derive(Debug)] + struct MockSubscriber { + notify_count: std::sync::Arc, + subscribe_called: std::sync::Arc, + last_topic: std::sync::Arc>>, + } + + impl MockSubscriber { + fn new() -> Self { + Self { + notify_count: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)), + subscribe_called: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), + last_topic: std::sync::Arc::new(std::sync::Mutex::new(None)), + } + } + + fn get_notify_count(&self) -> usize { + self.notify_count.load(std::sync::atomic::Ordering::SeqCst) + } + + fn is_subscribe_called(&self) -> bool { + self.subscribe_called.load(std::sync::atomic::Ordering::SeqCst) + } + + fn get_last_topic(&self) -> Option { + self.last_topic.lock().unwrap().clone() + } + } + + #[async_trait] + impl Subscriber for MockSubscriber { + async fn notify(&self, topic: Topic, _payload: &dyn DebuggableEvent) -> NodeResult<()> { + self.notify_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + *self.last_topic.lock().unwrap() = Some(topic); + Ok(()) + } + + async fn subscribe(self) { + self.subscribe_called.store(true, std::sync::atomic::Ordering::SeqCst); + } + } + + impl DebuggableSubscriber for MockSubscriber {} + + #[derive(Debug)] + struct ErrorSubscriber { + should_error: bool, + } + + impl ErrorSubscriber { + fn new(should_error: bool) -> Self { + Self { should_error } + } + } + + #[async_trait] + impl Subscriber for ErrorSubscriber { + async fn notify(&self, _topic: Topic, _payload: &dyn DebuggableEvent) -> NodeResult<()> { + if self.should_error { + Err(crate::error::NodeError::InvalidTaskType.into()) + } else { + Ok(()) + } + } + + async fn subscribe(self) {} + } + + impl DebuggableSubscriber for ErrorSubscriber {} + + #[tokio::test] + async fn test_subscriber_notify_success() { + let subscriber = MockSubscriber::new(); + let event = MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let result = subscriber.notify(Topic::NewBlock(1), &event).await; + assert!(result.is_ok()); + assert_eq!(subscriber.get_notify_count(), 1); + assert_eq!(subscriber.get_last_topic(), Some(Topic::NewBlock(1))); + } + + #[tokio::test] + async fn test_subscriber_notify_multiple_times() { + let subscriber = MockSubscriber::new(); + let event1 = MockEvent::new(Topic::NewBlock(1), "test_data1".to_string()); + let event2 = MockEvent::new(Topic::NewBlock(2), "test_data2".to_string()); + let result1 = subscriber.notify(Topic::NewBlock(1), &event1).await; + let result2 = subscriber.notify(Topic::NewBlock(2), &event2).await; + assert!(result1.is_ok()); + assert!(result2.is_ok()); + assert_eq!(subscriber.get_notify_count(), 2); + assert_eq!(subscriber.get_last_topic(), Some(Topic::NewBlock(2))); + } + + #[tokio::test] + async fn test_subscriber_notify_error() { + let subscriber = ErrorSubscriber::new(true); + let event = MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let result = subscriber.notify(Topic::NewBlock(1), &event).await; + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_subscriber_subscribe() { + let subscriber = MockSubscriber::new(); + assert!(!subscriber.is_subscribe_called()); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_debuggable_event_trait() { + let event = MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let _: &dyn Event = &event; + let _: &dyn std::fmt::Debug = &event; + let _: &dyn DebuggableEvent = &event; + let debug_str = format!("{:?}", event); + assert!(debug_str.contains("MockEvent")); + } + + #[tokio::test] + async fn test_debuggable_subscriber_trait() { + let subscriber = MockSubscriber::new(); + let _: &dyn Subscriber = &subscriber; + let _: &dyn std::fmt::Debug = &subscriber; + let _: &dyn DebuggableSubscriber = &subscriber; + let debug_str = format!("{:?}", subscriber); + assert!(debug_str.contains("MockSubscriber")); + } + + #[tokio::test] + async fn test_event_topic_consistency() { + let topic = Topic::NewBlock(1); + let event = MockEvent::new(topic.clone(), "test_data".to_string()); + assert_eq!(event.topic(), topic); + } + + #[tokio::test] + async fn test_event_as_any() { + let event = MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let any_ref = event.as_any(); + let downcast_result = any_ref.downcast_ref::(); + assert!(downcast_result.is_some()); + let downcast_event = downcast_result.unwrap(); + assert_eq!(downcast_event.data, "test_data"); + } + + #[tokio::test] + async fn test_different_topics() { + let subscriber = MockSubscriber::new(); + let topics = vec![Topic::NewBlock(1), Topic::NewBlock(2)]; + for (i, topic) in topics.iter().enumerate() { + let event = MockEvent::new(topic.clone(), format!("data_{}", i)); + let result = subscriber.notify(topic.clone(), &event).await; + assert!(result.is_ok()); + } + assert_eq!(subscriber.get_notify_count(), topics.len()); + } + + #[tokio::test] + async fn test_trait_object_usage() { + let subscriber: Box = Box::new(MockSubscriber::new()); + let event: Box = Box::new(MockEvent::new(Topic::NewBlock(1), "boxed_data".to_string())); + let result = subscriber.notify(Topic::NewBlock(1), event.as_ref()).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_send_sync_requirements() { + let subscriber = MockSubscriber::new(); + let event = MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let handle = tokio::spawn(async move { + subscriber.notify(Topic::NewBlock(1), &event).await + }); + let result = handle.await.unwrap(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_many_notifications() { + let subscriber = MockSubscriber::new(); + let count = 1000; + for i in 0..count { + let event = MockEvent::new(Topic::NewBlock(1), format!("data_{}", i)); + let result = subscriber.notify(Topic::NewBlock(1), &event).await; + assert!(result.is_ok()); + } + assert_eq!(subscriber.get_notify_count(), count); + } + + #[cfg(test)] + mod integration_tests { + use super::*; + + #[tokio::test] + async fn test_multiple_subscribers_with_same_event() { + let subscriber1 = tests::MockSubscriber::new(); + let subscriber2 = tests::MockSubscriber::new(); + let event = tests::MockEvent::new(Topic::NewBlock(1), "shared_data".to_string()); + let result1 = subscriber1.notify(Topic::NewBlock(1), &event).await; + let result2 = subscriber2.notify(Topic::NewBlock(1), &event).await; + assert!(result1.is_ok()); + assert!(result2.is_ok()); + assert_eq!(subscriber1.get_notify_count(), 1); + assert_eq!(subscriber2.get_notify_count(), 1); + } + + #[tokio::test] + async fn test_subscriber_lifecycle() { + let subscriber = tests::MockSubscriber::new(); + let event = tests::MockEvent::new(Topic::NewBlock(1), "test_data".to_string()); + let notify_result = subscriber.notify(Topic::NewBlock(1), &event).await; + assert!(notify_result.is_ok()); + subscriber.subscribe().await; + } + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/post_grouping.rs b/crates/arpa-node/src/subscriber/post_grouping.rs index 8c2ac97b..d95b3d2c 100644 --- a/crates/arpa-node/src/subscriber/post_grouping.rs +++ b/crates/arpa-node/src/subscriber/post_grouping.rs @@ -227,3 +227,576 @@ impl DebuggableSubscriber for PostGroupingSubscriber { } + +#[cfg(feature = "unittest")] +mod tests { + use super::*; + use crate::{ + event::{dkg_post_process::DKGPostProcess, types::Topic}, + queue::event_queue::EventQueue, + scheduler::dynamic::SimpleDynamicTaskScheduler, + test_contracts::{ + mockcontroller::{deploy_mock_controller_with_args, get_mock_controller_at}, + mockcontrollerrelayer::{deploy_mock_controller_relayer, get_mock_controller_relayer_at} + }, + }; + use arpa_core::{ + Config, DKGStatus, GeneralMainChainIdentity, Group, Member, PLACEHOLDER_ADDRESS + }; + use crate::test_contracts::{ + mockcontroller::MockController, + mockcontrollerrelayer::MockControllerRelayer + }; + use arpa_dal::{ + cache::InMemoryGroupInfoCache, + GroupInfoHandler, + }; + use ethers::prelude::*; + use std::{collections::BTreeMap, sync::Arc}; + use threshold_bls::schemes::bn254::G2Curve; + use tokio::sync::RwLock; + + const DEFAULT_CHAIN_ID: u64 = 1; + const DEFAULT_GROUP_INDEX: usize = 1; + const DEFAULT_EPOCH: usize = 1; + const DEFAULT_GROUP_SIZE: usize = 3; + const DEFAULT_THRESHOLD: usize = 2; + const DEFAULT_BLOCK_HEIGHT: usize = 100; + const DEFAULT_RPC_ENDPOINT: &str = "http://localhost:8545"; + const DEFAULT_WS_ENDPOINT: &str = "ws://localhost:8545"; + const DEFAULT_SUPPORTED_CHAINS: [usize; 2] = [2, 3]; + + async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { + let anvil = ethers::utils::Anvil::new().spawn(); + let ws_provider = Arc::new( + Provider::::connect(anvil.ws_endpoint()) + .await + .expect("Failed to connect to anvil") + ); + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet = wallet.with_chain_id(anvil.chain_id()); + (anvil, ws_provider, wallet) + } + + async fn deploy_controller( + ws_provider: Arc>, + wallet: LocalWallet + ) -> (Address, MockController, LocalWallet>>) { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let controller_address = deploy_mock_controller_with_args(client.clone(), Address::random()).await.unwrap(); + let controller_contract = get_mock_controller_at(controller_address, client.clone()); + (controller_address, controller_contract) + } + + async fn deploy_controller_relayer( + ws_provider: Arc>, + wallet: LocalWallet + ) -> (Address, MockControllerRelayer, LocalWallet>>) { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let relayer_address = deploy_mock_controller_relayer(client.clone()).await.unwrap(); + let relayer_contract = get_mock_controller_relayer_at(relayer_address, client.clone()); + (relayer_address, relayer_contract) + } + + async fn create_chain_identity( + chain_id: u64, + wallet: LocalWallet, + ws_provider: Arc>, + ws_endpoint: String, + controller_address: Address, + relayer_address: Address, + ) -> Arc>> { + let config = Config::default(); + let general_chain_identity = GeneralMainChainIdentity::new( + chain_id.try_into().unwrap(), + wallet.clone(), + ws_provider.clone(), + ws_endpoint, + controller_address, + relayer_address, + Address::random(), + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + async fn setup_group_cache( + id_address: Address, + group_index: usize, + epoch: usize, + size: usize, + threshold: usize, + member_addresses: Vec
, + dkg_status: DKGStatus, + state: bool, + ) -> NodeResult>>>> { + let group_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryGroupInfoCache::::new(id_address)), + )); + + { + let mut group_cache_write = group_cache.write().await; + let dkg_task = arpa_core::DKGTask { + group_index, + epoch, + size, + threshold, + assignment_block_height: DEFAULT_BLOCK_HEIGHT, + members: member_addresses.clone(), + coordinator_address: Address::random(), + }; + + group_cache_write.save_task_info(0, dkg_task).await?; + group_cache_write.update_dkg_status(group_index, epoch, dkg_status).await?; + + let mut group = Group:: { + index: group_index, + epoch, + size, + threshold, + state, + public_key: None, + members: BTreeMap::new(), + committers: vec![], + c: std::marker::PhantomData, + }; + + for (i, addr) in member_addresses.iter().enumerate() { + group.members.insert(*addr, Member { + index: i, + dkg_index: Some(i), + id_address: *addr, + rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), + partial_public_key: None, + }); + } + + group_cache_write.sync_up_members(group_index, epoch, group.members).await?; + } + + Ok(group_cache) + } + + fn create_schedulers() -> (Arc>, Arc>) { + ( + Arc::new(RwLock::new(EventQueue::new())), + Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())) + ) + } + + fn create_test_group( + group_index: usize, + epoch: usize, + state: bool, + member_addresses: Vec
+ ) -> Group { + let mut members = BTreeMap::new(); + for (i, addr) in member_addresses.iter().enumerate() { + members.insert(*addr, Member { + index: i, + dkg_index: Some(i), + id_address: *addr, + rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), + partial_public_key: None, + }); + } + + Group { + index: group_index, + epoch, + size: member_addresses.len(), + threshold: (member_addresses.len() * 2 / 3) + 1, + state, + public_key: None, + members, + committers: member_addresses, + c: std::marker::PhantomData, + } + } + + async fn create_test_setup() -> ( + Arc>>, + Arc>>>, + Address, + ) { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, _controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + (chain_identity, group_cache, id_address) + } + + #[tokio::test] + async fn test_post_grouping_subscriber_creation() { + let (chain_identity, group_cache, _id_address) = create_test_setup().await; + let (eq, ts) = create_schedulers(); + let supported_chains = DEFAULT_SUPPORTED_CHAINS.to_vec(); + + let subscriber = PostGroupingSubscriber::new( + chain_identity, + supported_chains.clone(), + group_cache, + eq, + ts, + ); + + assert_eq!(subscriber.supported_relayed_chains, supported_chains); + } + + #[tokio::test] + async fn test_dkg_post_process_handler_creation() { + let (chain_identity, group_cache, _id_address) = create_test_setup().await; + let supported_chains = DEFAULT_SUPPORTED_CHAINS.to_vec(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: supported_chains.clone(), + group_cache, + c: PhantomData, + }; + + assert_eq!(handler.supported_relayed_chains, supported_chains); + } + + #[tokio::test] + async fn test_post_process_handler_with_no_coordinator() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), PLACEHOLDER_ADDRESS).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_post_process_handler_with_coordinator_success() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let coordinator_addr = Address::random(); + controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); + controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); + relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_post_process_handler_with_inactive_group() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let coordinator_addr = Address::random(); + controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); + controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); + relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + false, + ).await.unwrap(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, false, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_post_process_handler_with_contract_failure() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let coordinator_addr = Address::random(); + controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); + controller.set_should_succeed(false).send().await.unwrap().await.unwrap(); + controller.set_failure_message("Controller failure".to_string()).send().await.unwrap().await.unwrap(); + relayer.set_should_succeed(false).send().await.unwrap().await.unwrap(); + relayer.set_failure_message("Relayer failure".to_string()).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_notify() { + let (chain_identity, group_cache, id_address) = create_test_setup().await; + let (eq, ts) = create_schedulers(); + + let subscriber = PostGroupingSubscriber::new( + chain_identity, + DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + eq, + ts, + ); + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let post_process_event = DKGPostProcess { + group_index: DEFAULT_GROUP_INDEX, + group_epoch: DEFAULT_EPOCH, + group, + }; + + let result = subscriber.notify(Topic::DKGPostProcess, &post_process_event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_subscribe() { + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + let (eq, ts) = create_schedulers(); + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + Address::random(), + Address::random(), + ).await; + + let subscriber = PostGroupingSubscriber::new( + chain_identity, + DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + eq.clone(), + ts, + ); + + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_multiple_relayed_chains() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let coordinator_addr = Address::random(); + controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); + controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); + relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::WaitForPostProcess, + true, + ).await.unwrap(); + + let multiple_chains = vec![2, 3, 4, 5, 6]; + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: multiple_chains.clone(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_dkg_status_update_failure() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let (controller_address, _controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; + let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + DEFAULT_CHAIN_ID, + wallet, + ws_provider, + DEFAULT_WS_ENDPOINT.to_string(), + controller_address, + relayer_address, + ).await; + + let id_address = Address::random(); + let group_cache = setup_group_cache( + id_address, + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + DKGStatus::None, + true, + ).await.unwrap(); + + let handler = GeneralDKGPostProcessHandler { + chain_identity, + supported_relayed_chains: DEFAULT_SUPPORTED_CHAINS.to_vec(), + group_cache, + c: PhantomData, + }; + + let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + assert!(result.is_ok()); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/post_success_grouping.rs b/crates/arpa-node/src/subscriber/post_success_grouping.rs index 17768a66..6f8d019f 100644 --- a/crates/arpa-node/src/subscriber/post_success_grouping.rs +++ b/crates/arpa-node/src/subscriber/post_success_grouping.rs @@ -92,7 +92,6 @@ impl Subscriber return Err(NodeError::DKGGroupingTwisted); } - // sync up the members in the group if !self .group_cache .write() @@ -110,8 +109,6 @@ impl Subscriber ) ); } - - // save the committers and update the state of the group self.group_cache .write() .await @@ -145,3 +142,266 @@ impl DebuggableSubscriber for PostSuccessGroupingSubscriber { } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + event::{dkg_success::DKGSuccess, types::Topic, Event}, + queue::event_queue::EventQueue, + }; + use arpa_core::{Group, Member, DKGTask}; + use arpa_dal::{GroupInfoHandler, cache::InMemoryGroupInfoCache}; + use ethers_core::types::Address; + use std::{ + any::Any, + collections::BTreeMap, + marker::PhantomData, + sync::Arc, + }; + use threshold_bls::schemes::bn254::G2Curve; + use tokio::sync::RwLock; + + const TEST_GROUP_INDEX: usize = 1; + const TEST_EPOCH: usize = 1; + const TEST_SIZE: usize = 3; + const TEST_THRESHOLD: usize = 2; + const TEST_CHAIN_ID: usize = 0; + const TEST_ASSIGNMENT_BLOCK_HEIGHT: usize = 100; + const TEST_RPC_ENDPOINT: &str = "http://localhost:8545"; + + fn create_test_group(id_address: Address, has_public_key: bool) -> Group { + let mut members = BTreeMap::new(); + members.insert(id_address, Member { + index: 0, + dkg_index: Some(0), + id_address, + rpc_endpoint: Some(TEST_RPC_ENDPOINT.to_string()), + partial_public_key: Some(G2Curve::point()), + }); + + Group { + index: TEST_GROUP_INDEX, + epoch: TEST_EPOCH, + size: TEST_SIZE, + threshold: TEST_THRESHOLD, + state: true, + public_key: if has_public_key { Some(G2Curve::point()) } else { None }, + members, + committers: vec![id_address], + c: PhantomData, + } + } + + fn create_test_dkg_success( + chain_id: usize, + id_address: Address, + has_public_key: bool, + ) -> DKGSuccess { + DKGSuccess { + chain_id, + id_address, + group: create_test_group(id_address, has_public_key), + } + } + + fn create_group_cache(id_address: Address) -> Arc>>> { + Arc::new(RwLock::new(Box::new(InMemoryGroupInfoCache::::new(id_address)))) + } + + fn create_event_queue() -> Arc> { + Arc::new(RwLock::new(EventQueue::new())) + } + + async fn setup_group_cache_with_group( + id_address: Address, + group: Group, + ) -> Arc>>> { + let group_cache = create_group_cache(id_address); + { + let mut cache = group_cache.write().await; + let dkg_task = DKGTask { + group_index: group.index, + epoch: group.epoch, + size: group.size, + threshold: group.threshold, + members: group.members.keys().copied().collect(), + assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, + coordinator_address: Address::random(), + }; + cache.save_task_info(TEST_CHAIN_ID, dkg_task).await.unwrap(); + } + group_cache + } + + #[tokio::test] + async fn test_post_success_grouping_subscriber_creation() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + assert!(format!("{:?}", subscriber).contains("PostSuccessGroupingSubscriber")); + } + + #[tokio::test] + async fn test_notify_successful_flow() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + + { + let mut cache = group_cache.write().await; + let dkg_task = DKGTask { + group_index: TEST_GROUP_INDEX, + epoch: TEST_EPOCH, + size: TEST_SIZE, + threshold: TEST_THRESHOLD, + members: vec![id_address], + assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, + coordinator_address: Address::random(), + }; + cache.save_task_info(TEST_CHAIN_ID, dkg_task).await.unwrap(); + } + + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + let dkg_success = create_test_dkg_success(1, id_address, true); + let result = subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + + match result { + Ok(_) => println!("Success!"), + Err(e) => println!("Error: {:?}", e), + } + } + + #[tokio::test] + async fn test_notify_node_not_in_group_error() { + let id_address = Address::random(); + let different_address = Address::random(); + let group = create_test_group(id_address, true); + let group_cache = setup_group_cache_with_group(id_address, group).await; + let eq = create_event_queue(); + + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + let dkg_success = create_test_dkg_success(1, different_address, true); + let result = subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + + assert!(result.is_err()); + if let Err(e) = result { + println!("Error type: {:?}", e); + } + } + + #[tokio::test] + async fn test_notify_different_public_key_error() { + let id_address = Address::random(); + let group = create_test_group(id_address, true); + let group_cache = setup_group_cache_with_group(id_address, group).await; + let eq = create_event_queue(); + + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + let dkg_success = create_test_dkg_success(1, id_address, false); + let result = subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + + assert!(result.is_err()); + if let Err(e) = result { + println!("Error type: {:?}", e); + } + } + + #[tokio::test] + async fn test_subscribe() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq.clone()); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_debuggable_subscriber_trait() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + + let _: &dyn DebuggableSubscriber = &subscriber; + let _: &dyn Subscriber = &subscriber; + } + + #[tokio::test] + #[should_panic] + async fn test_notify_with_wrong_event_type() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + + #[derive(Debug)] + struct WrongEvent; + + impl Event for WrongEvent { + fn topic(&self) -> Topic { + Topic::DKGSuccess + } + + fn as_any(&self) -> &dyn Any { + self + } + } + + impl DebuggableEvent for WrongEvent {} + + let wrong_event = WrongEvent; + let _result = subscriber.notify(Topic::DKGSuccess, &wrong_event).await; + } + + #[tokio::test] + async fn test_multiple_notifications() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + + for i in 0..3 { + let dkg_success = create_test_dkg_success(1, id_address, true); + let result = subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + println!("Notification {}: {:?}", i, result); + } + } + + #[tokio::test] + async fn test_with_different_chain_ids() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + + let chain_ids = vec![1, 2, 3]; + for chain_id in chain_ids { + let dkg_success = create_test_dkg_success(chain_id, id_address, true); + let result = subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + println!("Chain ID {}: {:?}", chain_id, result); + } + } + + #[cfg(test)] + mod integration_tests { + use super::*; + + #[tokio::test] + async fn test_full_workflow() { + let id_address = Address::random(); + let group_cache = create_group_cache(id_address); + let eq = create_event_queue(); + + let subscriber = PostSuccessGroupingSubscriber::new(group_cache.clone(), eq.clone()); + subscriber.subscribe().await; + + let test_subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); + let dkg_success = create_test_dkg_success(1, id_address, true); + let result = test_subscriber.notify(Topic::DKGSuccess, &dkg_success).await; + + println!("Full workflow result: {:?}", result); + } + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/pre_grouping.rs b/crates/arpa-node/src/subscriber/pre_grouping.rs index 1c437cc2..f30a0bbf 100644 --- a/crates/arpa-node/src/subscriber/pre_grouping.rs +++ b/crates/arpa-node/src/subscriber/pre_grouping.rs @@ -110,3 +110,200 @@ impl DebuggableSubscriber for PreGroupingSubscriber { } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + event::{new_dkg_task::NewDKGTask, run_dkg::RunDKG, types::Topic, Event}, + queue::{event_queue::EventQueue, EventPublisher}, + }; + use arpa_core::DKGTask; + use arpa_dal::{GroupInfoHandler, cache::InMemoryGroupInfoCache}; + use ethers_core::types::Address; + use std::{any::Any, sync::Arc}; + use threshold_bls::schemes::bn254::G2Curve; + use tokio::sync::RwLock; + + const CHAIN_ID: usize = 1; + const GROUP_SIZE: usize = 3; + const THRESHOLD: usize = 2; + const ASSIGNMENT_BLOCK_HEIGHT: usize = 100; + const SELF_INDEX: usize = 0; + + fn create_test_dkg_task(group_index: usize, epoch: usize) -> DKGTask { + DKGTask { + group_index, + epoch, + size: GROUP_SIZE, + threshold: THRESHOLD, + members: vec![Address::random(), Address::random(), Address::random()], + assignment_block_height: ASSIGNMENT_BLOCK_HEIGHT, + coordinator_address: Address::random(), + } + } + + fn create_test_new_dkg_task(group_index: usize, epoch: usize) -> NewDKGTask { + NewDKGTask { + chain_id: CHAIN_ID, + dkg_task: create_test_dkg_task(group_index, epoch), + self_index: SELF_INDEX, + } + } + + fn create_subscriber() -> (PreGroupingSubscriber, Arc>) { + let id_address = Address::random(); + let group_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryGroupInfoCache::::new(id_address)), + )); + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = PreGroupingSubscriber::new(group_cache, eq.clone()); + (subscriber, eq) + } + + #[tokio::test] + async fn test_pre_grouping_subscriber_creation() { + let (subscriber, _) = create_subscriber(); + assert!(format!("{:?}", subscriber).contains("PreGroupingSubscriber")); + } + + #[tokio::test] + async fn test_notify_with_different_group_index() { + let (subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(1, 1); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_notify_with_different_epoch() { + let (subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(0, 1); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_notify_with_same_group_index_and_epoch() { + let (subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(0, 0); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_publish_functionality() { + let (subscriber, _) = create_subscriber(); + let run_dkg_event = RunDKG { + dkg_task: create_test_dkg_task(1, 1), + }; + subscriber.publish(run_dkg_event).await; + } + + #[tokio::test] + async fn test_subscribe() { + let (subscriber, _) = create_subscriber(); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_event_publisher_trait() { + let (subscriber, _) = create_subscriber(); + let _: &dyn EventPublisher = &subscriber; + } + + #[tokio::test] + async fn test_debuggable_subscriber_trait() { + let (subscriber, _) = create_subscriber(); + let _: &dyn DebuggableSubscriber = &subscriber; + let _: &dyn Subscriber = &subscriber; + } + + #[tokio::test] + #[should_panic] + async fn test_notify_with_wrong_event_type() { + let (subscriber, _) = create_subscriber(); + + #[derive(Debug)] + struct WrongEvent; + + impl Event for WrongEvent { + fn topic(&self) -> Topic { + Topic::NewDKGTask + } + fn as_any(&self) -> &dyn Any { + self + } + } + + impl DebuggableEvent for WrongEvent {} + + let wrong_event = WrongEvent; + let _result = subscriber.notify(Topic::NewDKGTask, &wrong_event).await; + } + + #[tokio::test] + async fn test_multiple_notifications() { + let (subscriber, _) = create_subscriber(); + for i in 0..3 { + let new_dkg_task = create_test_new_dkg_task(i + 1, i + 1); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + assert!(result.is_ok()); + } + } + + #[tokio::test] + async fn test_error_handling_in_notify() { + let (subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(1, 1); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + match result { + Ok(_) => println!("Notification succeeded"), + Err(e) => println!("Notification failed with error: {:?}", e), + } + } + + #[tokio::test] + async fn test_concurrent_notifications() { + let (subscriber, _) = create_subscriber(); + let subscriber = Arc::new(subscriber); + let mut handles = vec![]; + + for i in 0..5 { + let subscriber_clone = subscriber.clone(); + let handle = tokio::spawn(async move { + let new_dkg_task = create_test_new_dkg_task(i + 1, i + 1); + subscriber_clone.notify(Topic::NewDKGTask, &new_dkg_task).await + }); + handles.push(handle); + } + + for handle in handles { + let result = handle.await.unwrap(); + println!("Concurrent notification result: {:?}", result); + } + } + + #[cfg(test)] + mod integration_tests { + use super::*; + + #[tokio::test] + async fn test_full_workflow() { + let (subscriber, _) = create_subscriber(); + subscriber.subscribe().await; + let (test_subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(1, 1); + let result = test_subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + println!("Full workflow result: {:?}", result); + } + + #[tokio::test] + async fn test_publish_and_notify_integration() { + let (subscriber, _) = create_subscriber(); + let new_dkg_task = create_test_new_dkg_task(1, 1); + let result = subscriber.notify(Topic::NewDKGTask, &new_dkg_task).await; + assert!(result.is_ok()); + } + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs index e590b7da..e0e24c87 100644 --- a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs +++ b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs @@ -149,7 +149,6 @@ impl FulfillRandomnessHandler for GeneralFulfillRandomnessHandler ) .await?; - // TODO add a special retry mechanism for gas price too high self.randomness_signature_cache .write() .await @@ -415,3 +414,621 @@ where ::Error: Sync + Send, { } + +#[cfg(feature = "unittest")] +mod tests { + use super::*; + use crate::{ + event::{ready_to_fulfill_randomness_task::ReadyToFulfillRandomnessTask, types::Topic}, + queue::event_queue::EventQueue, + scheduler::dynamic::SimpleDynamicTaskScheduler, + test_contracts::mockadapter::{deploy_mock_adapter, get_mock_adapter_at}, + }; + use arpa_core::{Config, GeneralMainChainIdentity, RandomnessTask, PartialSignature}; + use arpa_dal::{ + cache::{InMemoryBlockInfoCache, InMemorySignatureResultCache}, + BlockInfoHandler, BlockInfoUpdater, SignatureResultCacheHandler + }; + use ethers::prelude::*; + use std::{collections::BTreeMap, sync::Arc}; + use threshold_bls::{poly::Eval, schemes::bn254::{G2Curve, G2Scheme}}; + use tokio::sync::RwLock; + + const TEST_CHAIN_ID: u64 = 1; + const TEST_BLOCK_HEIGHT: usize = 1000; + const TEST_BLOCK_TIME: usize = 12; + const TEST_SUBSCRIPTION_ID: u64 = 1; + const TEST_GROUP_INDEX: u32 = 1; + const TEST_SEED: usize = 12345; + const TEST_REQUEST_CONFIRMATIONS: u16 = 6; + const TEST_CALLBACK_GAS_LIMIT: u32 = 100000; + const TEST_CALLBACK_MAX_GAS_PRICE: usize = 20000000000; + const TEST_ASSIGNMENT_BLOCK_HEIGHT: usize = 100; + const TEST_THRESHOLD: usize = 2; + const HIGH_BLOCK_HEIGHT: usize = 100000; + const LOW_GAS_PRICE: usize = 1; + const SIGNATURE_SIZE: usize = 32; + const RANDOMNESS_SIZE: usize = 96; + const NUM_SIGNERS: usize = 3; + const WS_ENDPOINT: &str = "ws://localhost:8545"; + + async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { + let anvil = ethers::utils::Anvil::new().spawn(); + let ws_provider = Provider::::connect(anvil.ws_endpoint()) + .await.expect("Failed to connect to anvil"); + let ws_provider = Arc::new(ws_provider); + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet = wallet.with_chain_id(anvil.chain_id()); + (anvil, ws_provider, wallet) + } + + async fn deploy_adapter(ws_provider: Arc>, wallet: LocalWallet) -> Address { + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + deploy_mock_adapter(client.clone()).await.unwrap() + } + + async fn create_chain_identity( + chain_id: u64, + wallet: LocalWallet, + ws_provider: Arc>, + ws_endpoint: String, + adapter_address: Address, + ) -> Arc>> { + let config = Config::default(); + let general_chain_identity = GeneralMainChainIdentity::new( + chain_id.try_into().unwrap(), + wallet.clone(), + ws_provider.clone(), + ws_endpoint, + Address::random(), + Address::random(), + adapter_address, + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + fn create_block_cache( + chain_id: usize, + block_height: usize, + block_time: usize, + ) -> Arc>> { + let mut cache = InMemoryBlockInfoCache::new(chain_id, block_time); + cache.set_block_height(block_height); + Arc::new(RwLock::new(Box::new(cache))) + } + + fn create_signature_cache() -> Arc>>> { + let cache = InMemorySignatureResultCache::new(); + Arc::new(RwLock::new(Box::new(cache))) + } + + fn create_schedulers() -> (Arc>, Arc>) { + ( + Arc::new(RwLock::new(EventQueue::new())), + Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())) + ) + } + + fn create_test_randomness_task(request_id: Vec) -> RandomnessTask { + let mut full_request_id = vec![0u8; 32]; + let copy_len = std::cmp::min(request_id.len(), 32); + full_request_id[..copy_len].copy_from_slice(&request_id[..copy_len]); + + RandomnessTask { + request_id: full_request_id, + subscription_id: TEST_SUBSCRIPTION_ID, + group_index: TEST_GROUP_INDEX, + request_type: arpa_core::RandomnessRequestType::Randomness, + params: vec![], + requester: Address::random(), + seed: U256::from(TEST_SEED), + request_confirmations: TEST_REQUEST_CONFIRMATIONS, + callback_gas_limit: TEST_CALLBACK_GAS_LIMIT, + callback_max_gas_price: U256::from(TEST_CALLBACK_MAX_GAS_PRICE), + assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, + } + } + + fn create_test_partial_signatures() -> BTreeMap { + let mut partial_signatures = BTreeMap::new(); + + for i in 0..NUM_SIGNERS { + let addr = Address::random(); + let eval = Eval { + value: vec![i as u8; SIGNATURE_SIZE], + index: i as u32, + }; + let serialized = bincode::serialize(&eval).unwrap(); + partial_signatures.insert(addr, PartialSignature { + index: i, + signed_partial_signature: serialized, + }); + } + partial_signatures + } + + async fn setup_signature_cache_with_task( + cache: Arc>>>, + randomness_task: RandomnessTask, + partial_signatures: BTreeMap, + committed_times: usize, + ) { + cache.write().await.add( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1, 2, 3], + TEST_THRESHOLD, + ).await.unwrap(); + + for (address, partial_sig) in partial_signatures { + cache.write().await.add_partial_signature( + randomness_task.request_id.clone(), + address, + partial_sig.index, + partial_sig.signed_partial_signature, + ).await.unwrap(); + } + + for _ in 0..committed_times { + cache.write().await.incr_committed_times(&randomness_task.request_id).await.unwrap(); + } + } + + async fn create_signature_cache_with_task( + randomness_task: RandomnessTask, + partial_signatures: BTreeMap, + committed_times: usize, + ) -> Arc>>> { + let cache = create_signature_cache(); + setup_signature_cache_with_task(cache.clone(), randomness_task, partial_signatures, committed_times).await; + cache + } + + #[tokio::test] + async fn test_subscriber_creation() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let signature_cache = create_signature_cache(); + let (eq, ts) = create_schedulers(); + let id_address = Address::random(); + + let subscriber = RandomnessSignatureAggregationSubscriber::::new( + TEST_CHAIN_ID.try_into().unwrap(), id_address, chain_identity, block_cache, signature_cache, eq, ts, + ); + + assert_eq!(subscriber.chain_id, TEST_CHAIN_ID as usize); + assert_eq!(subscriber.id_address, id_address); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_creation() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let signature_cache = create_signature_cache(); + let id_address = Address::random(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache, + pc: PhantomData, + }; + + assert_eq!(handler.id_address, id_address); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_task_not_pending() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::zero().into()).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; RANDOMNESS_SIZE], + partial_signatures, + ).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_task_expired() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, HIGH_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; RANDOMNESS_SIZE], + partial_signatures, + ).await; + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_gas_price_too_high() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let mut randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + randomness_task.callback_max_gas_price = U256::from(LOW_GAS_PRICE); + + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; RANDOMNESS_SIZE], + partial_signatures, + ).await; + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_success() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + adapter_contract.set_should_revert(request_id.into(), false).send().await.unwrap().await.unwrap(); + adapter_contract.set_should_revert_with_custom_error(request_id.into(), false).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; SIGNATURE_SIZE], + partial_signatures, + ).await; + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_transaction_failed() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + adapter_contract.set_should_revert(request_id.into(), true).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; SIGNATURE_SIZE], + partial_signatures, + ).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_fulfill_randomness_handler_custom_error() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); + let client = Arc::new(client); + let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), partial_signatures.clone(), 0, + ).await; + let id_address = Address::random(); + + let request_id = H256::from_slice(&randomness_task.request_id); + adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + adapter_contract.set_should_revert_with_custom_error(request_id.into(), true).send().await.unwrap().await.unwrap(); + + let handler = GeneralFulfillRandomnessHandler { + id_address, + chain_identity, + block_cache, + randomness_signature_cache: signature_cache.clone(), + pc: PhantomData, + }; + + let result = handler.handle( + randomness_task.group_index as usize, + randomness_task.clone(), + vec![1; SIGNATURE_SIZE], + partial_signatures, + ).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_notify_with_faulty_task() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let (eq, ts) = create_schedulers(); + let id_address = Address::random(); + + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + + let signature_cache = create_signature_cache_with_task( + randomness_task.clone(), + partial_signatures.clone(), + DEFAULT_MAX_RANDOMNESS_FULFILLMENT_ATTEMPTS, + ).await; + + let subscriber = RandomnessSignatureAggregationSubscriber::::new( + TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + ); + + let result_cache = RandomnessResultCache { + group_index: randomness_task.group_index as usize, + randomness_task: randomness_task.clone(), + message: vec![1, 2, 3], + threshold: TEST_THRESHOLD, + partial_signatures, + committed_times: DEFAULT_MAX_RANDOMNESS_FULFILLMENT_ATTEMPTS, + }; + + let event = ReadyToFulfillRandomnessTask { + chain_id: TEST_CHAIN_ID.try_into().unwrap(), + tasks: vec![result_cache], + }; + + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_notify_with_valid_task() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let signature_cache = create_signature_cache(); + let (eq, ts) = create_schedulers(); + let id_address = Address::random(); + + let subscriber = RandomnessSignatureAggregationSubscriber::::new( + TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + ); + + let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); + let partial_signatures = create_test_partial_signatures(); + + let result_cache = RandomnessResultCache { + group_index: randomness_task.group_index as usize, + randomness_task: randomness_task.clone(), + message: vec![1, 2, 3], + threshold: TEST_THRESHOLD, + partial_signatures, + committed_times: 0, + }; + + let event = ReadyToFulfillRandomnessTask { + chain_id: TEST_CHAIN_ID as usize, + tasks: vec![result_cache], + }; + + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_subscriber_subscribe() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let signature_cache = create_signature_cache(); + let (eq, ts) = create_schedulers(); + let id_address = Address::random(); + + let subscriber = RandomnessSignatureAggregationSubscriber::::new( + TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache, eq.clone(), ts, + ); + + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_subscriber_notify_with_multiple_tasks() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, + ).await; + + let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let signature_cache = create_signature_cache(); + let (eq, ts) = create_schedulers(); + let id_address = Address::random(); + + let subscriber = RandomnessSignatureAggregationSubscriber::::new( + TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + ); + + let mut tasks = Vec::new(); + for i in 0..NUM_SIGNERS { + let randomness_task = create_test_randomness_task(vec![i as u8, i as u8+1, i as u8+2, i as u8+3]); + let partial_signatures = create_test_partial_signatures(); + + let result_cache = RandomnessResultCache { + group_index: randomness_task.group_index as usize, + randomness_task: randomness_task.clone(), + message: vec![i as u8, i as u8+1, i as u8+2], + threshold: TEST_THRESHOLD, + partial_signatures, + committed_times: 0, + }; + + tasks.push(result_cache); + } + + let event = ReadyToFulfillRandomnessTask { chain_id: TEST_CHAIN_ID as usize, tasks }; + + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + assert!(result.is_ok()); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs index 26be03fe..9424a073 100644 --- a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs +++ b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs @@ -406,3 +406,725 @@ where ::Error: Sync + Send, { } + +#[cfg(feature = "unittest")] +mod tests { + use super::*; + use crate::{ + algorithm::bls::SimpleBLSCore, committer::CommitterClient, event::{ready_to_handle_randomness_task::ReadyToHandleRandomnessTask, types::Topic}, queue::event_queue::EventQueue, scheduler::dynamic::SimpleDynamicTaskScheduler + }; + use arpa_core::{ExponentialBackoffRetryDescriptor, RandomnessTask}; + use arpa_dal::{ + cache::{InMemoryGroupInfoCache, InMemoryBLSTasksQueue, InMemorySignatureResultCache, RandomnessResultCache}, + BLSTasksHandler, GroupInfoHandler, SignatureResultCacheHandler, + }; + use std::{sync::Arc, time::Duration}; + use threshold_bls::schemes::bn254::{G2Curve, G2Scheme}; + use tokio::sync::RwLock; + use tonic::{transport::Server, Request, Response, Status}; + use crate::rpc_stub::committer::{ + committer_service_server::{CommitterService, CommitterServiceServer}, + CommitPartialSignatureRequest, CommitPartialSignatureReply, + committer_service_client::CommitterServiceClient, + commit_partial_signature_request::BlsTaskType, + }; + + const DEFAULT_BASE_PORT: u16 = 50051; + const DEFAULT_RETRY_BASE: u64 = 1000; + const DEFAULT_RETRY_FACTOR: u64 = 2; + const DEFAULT_RETRY_ATTEMPTS: usize = 3; + const DEFAULT_GROUP_SIZE: usize = 3; + const DEFAULT_THRESHOLD: usize = 2; + const DEFAULT_GROUP_INDEX: u32 = 1; + const DEFAULT_EPOCH: usize = 1; + const DEFAULT_CHAIN_ID: usize = 1; + const DEFAULT_BLOCK_HEIGHT: usize = 100; + const DEFAULT_SUBSCRIPTION_ID: u64 = 1; + const DEFAULT_CONFIRMATIONS: u16 = 6; + const DEFAULT_GAS_LIMIT: u32 = 200000; + const DEFAULT_GAS_PRICE: u64 = 1000000000; + const DEFAULT_SEED: u64 = 12345; + + #[derive(Debug, Clone)] + pub struct MockCommitterService { + pub should_accept: bool, + pub response_delay: Option, + pub call_count: Arc>, + pub received_requests: Arc>>, + } + + impl MockCommitterService { + pub fn new(should_accept: bool) -> Self { + Self { + should_accept, + response_delay: None, + call_count: Arc::new(std::sync::Mutex::new(0)), + received_requests: Arc::new(std::sync::Mutex::new(Vec::new())), + } + } + + pub fn with_delay(mut self, delay: Duration) -> Self { + self.response_delay = Some(delay); + self + } + + pub fn get_call_count(&self) -> usize { + *self.call_count.lock().unwrap() + } + + pub fn get_received_requests(&self) -> Vec { + self.received_requests.lock().unwrap().clone() + } + } + + #[tonic::async_trait] + impl CommitterService for MockCommitterService { + async fn commit_partial_signature( + &self, + request: Request, + ) -> Result, Status> { + *self.call_count.lock().unwrap() += 1; + self.received_requests.lock().unwrap().push(request.get_ref().clone()); + + if let Some(delay) = self.response_delay { + tokio::time::sleep(delay).await; + } + + Ok(Response::new(CommitPartialSignatureReply { + result: self.should_accept, + })) + } + } + + #[derive(Debug, Clone)] + pub struct MockCommitterClient { + pub id_address: Address, + pub committer_id_address: Address, + pub committer_endpoint: String, + pub grpc_client: Arc>>, + } + + impl MockCommitterClient { + pub async fn new( + id_address: Address, + committer_id_address: Address, + server_address: String, + ) -> Result> { + let channel = tonic::transport::Channel::from_shared(server_address.clone())? + .connect() + .await?; + + Ok(Self { + id_address, + committer_id_address, + committer_endpoint: server_address, + grpc_client: Arc::new(tokio::sync::Mutex::new(CommitterServiceClient::new(channel))), + }) + } + + pub async fn commit_partial_signature( + &self, + chain_id: usize, + task_type: arpa_core::BLSTaskType, + request_id: Vec, + message: Vec, + partial_signature: Vec, + ) -> crate::error::NodeResult { + let grpc_task_type = match task_type { + arpa_core::BLSTaskType::Randomness => BlsTaskType::Randomness, + arpa_core::BLSTaskType::GroupRelay => BlsTaskType::GroupRelay, + arpa_core::BLSTaskType::GroupRelayConfirmation => BlsTaskType::GroupRelayConfirmation, + }; + + let request = CommitPartialSignatureRequest { + id_address: format!("{:?}", self.id_address), + chain_id: chain_id as u32, + task_type: grpc_task_type as i32, + request_id, + message, + partial_signature, + }; + + let response = self.grpc_client.lock().await + .commit_partial_signature(request) + .await + .map_err(|e| crate::error::NodeError::RpcResponseError(e))?; + Ok(response.into_inner().result) + } + } + + impl CommitterClient for MockCommitterClient { + fn get_id_address(&self) -> Address { + self.id_address + } + + fn get_committer_id_address(&self) -> Address { + self.committer_id_address + } + + fn get_committer_endpoint(&self) -> &str { + &self.committer_endpoint + } + + fn build( + _id_address: Address, + _committer_id_address: Address, + _committer_endpoint: String, + _commit_partial_signature_retry_descriptor: ExponentialBackoffRetryDescriptor, + ) -> Self { + panic!("Use MockCommitterClient::new() instead for async construction") + } + } + + fn default_retry_descriptor() -> ExponentialBackoffRetryDescriptor { + ExponentialBackoffRetryDescriptor { + base: DEFAULT_RETRY_BASE, + factor: DEFAULT_RETRY_FACTOR, + max_attempts: DEFAULT_RETRY_ATTEMPTS, + use_jitter: false, + } + } + + async fn start_mock_grpc_server( + service: MockCommitterService, + port: u16, + ) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + let addr = format!("127.0.0.1:{}", port).parse().unwrap(); + if let Err(e) = Server::builder() + .add_service(CommitterServiceServer::new(service)) + .serve(addr) + .await + { + eprintln!("Mock gRPC server error: {}", e); + } + }) + } + + async fn setup_group_cache_with_secret( + id_address: Address, + chain_id: usize, + group_index: usize, + epoch: usize, + size: usize, + threshold: usize, + member_addresses: Vec
, + ) -> NodeResult>>>> { + let group_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryGroupInfoCache::::new(id_address)), + )); + { + let mut group_cache_write = group_cache.write().await; + + let dkg_task = arpa_core::DKGTask { + group_index, + epoch, + size, + threshold, + assignment_block_height: DEFAULT_BLOCK_HEIGHT, + members: member_addresses.clone(), + coordinator_address: Address::random(), + }; + + group_cache_write.save_task_info(chain_id, dkg_task).await?; + group_cache_write.update_dkg_status(group_index, epoch, arpa_core::DKGStatus::InPhase).await?; + + use rand::thread_rng; + use threshold_bls::{group::Element, sig::Share}; + use dkg_core::primitives::{DKGOutput, Group as DKGGroup, Node}; + use threshold_bls::poly::{PublicPoly, Idx}; + + let secret_scalar = ::Scalar::rand(&mut thread_rng()); + + let mut dkg_nodes = Vec::new(); + for (i, &_addr) in member_addresses.iter().enumerate() { + let mut node_public_key = ::Point::one(); + let node_secret = ::Scalar::rand(&mut thread_rng()); + node_public_key.mul(&node_secret); + dkg_nodes.push(Node::new(i as Idx, node_public_key)); + } + + let qual = DKGGroup { + nodes: dkg_nodes, + threshold: threshold, + }; + + let public = PublicPoly::::new(threshold - 1); + let self_index = member_addresses + .iter() + .position(|&addr| addr == id_address) + .unwrap_or(0) as Idx; + + let share = Share { + index: self_index, + private: secret_scalar, + }; + + let dkg_output = DKGOutput { + qual, + public, + share, + disqualified_node_indices: vec![], + }; + + group_cache_write.save_successful_output(group_index, epoch, dkg_output).await?; + + let committers = member_addresses.iter().take(2).cloned().collect::>(); + group_cache_write.save_committers(group_index, epoch, committers).await?; + } + Ok(group_cache) + } + + async fn setup_group_cache_simple( + id_address: Address, + chain_id: usize, + group_index: usize, + epoch: usize, + size: usize, + threshold: usize, + member_addresses: Vec
, + ) -> NodeResult>>>> { + let group_cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryGroupInfoCache::::new(id_address)), + )); + + let dkg_task = arpa_core::DKGTask { + group_index, + epoch, + size, + threshold, + assignment_block_height: DEFAULT_BLOCK_HEIGHT, + members: member_addresses.clone(), + coordinator_address: Address::random(), + }; + + { + let mut group_cache_write = group_cache.write().await; + group_cache_write.save_task_info(chain_id, dkg_task).await?; + + let committers = member_addresses.iter().take(2).cloned().collect::>(); + group_cache_write.save_committers(group_index, epoch, committers).await?; + group_cache_write.update_dkg_status(group_index, epoch, arpa_core::DKGStatus::InPhase).await?; + } + Ok(group_cache) + } + + async fn setup_randomness_tasks_cache( + tasks: Vec, + ) -> Arc>>> { + let cache: Arc>>> = Arc::new(RwLock::new( + Box::new(InMemoryBLSTasksQueue::::new()), + )); + + { + let mut cache_write = cache.write().await; + for task in tasks { + cache_write.add(task).await.unwrap(); + } + } + cache + } + + async fn setup_randomness_signature_cache() -> Arc>>> { + Arc::new(RwLock::new( + Box::new(InMemorySignatureResultCache::::new()), + )) + } + + fn create_test_randomness_task() -> RandomnessTask { + RandomnessTask { + request_id: vec![1, 2, 3, 4], + subscription_id: DEFAULT_SUBSCRIPTION_ID, + group_index: DEFAULT_GROUP_INDEX, + request_type: arpa_core::RandomnessRequestType::Randomness, + params: vec![], + requester: Address::random(), + seed: ethers::types::U256::from(DEFAULT_SEED), + request_confirmations: DEFAULT_CONFIRMATIONS, + callback_gas_limit: DEFAULT_GAS_LIMIT, + callback_max_gas_price: ethers::types::U256::from(DEFAULT_GAS_PRICE), + assignment_block_height: DEFAULT_BLOCK_HEIGHT, + } + } + + fn create_schedulers() -> (Arc>, Arc>) { + ( + Arc::new(RwLock::new(EventQueue::new())), + Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())), + ) + } + + #[tokio::test] + async fn test_randomness_task_subscriber_creation() { + let id_address = Address::random(); + + let group_cache = setup_group_cache_simple( + id_address, + DEFAULT_CHAIN_ID, + DEFAULT_GROUP_INDEX as usize, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + ).await.unwrap(); + + let randomness_tasks_cache = setup_randomness_tasks_cache(vec![]).await; + let randomness_signature_cache = setup_randomness_signature_cache().await; + let (eq, ts) = create_schedulers(); + + let subscriber = ReadyToHandleRandomnessTaskSubscriber::::new( + DEFAULT_CHAIN_ID, + id_address, + group_cache, + randomness_tasks_cache, + randomness_signature_cache, + eq, + ts, + default_retry_descriptor(), + ); + + assert_eq!(subscriber.chain_id, DEFAULT_CHAIN_ID); + assert_eq!(subscriber.id_address, id_address); + } + + #[tokio::test] + async fn test_mock_grpc_committer_client_success() { + let service = MockCommitterService::new(true); + let port = DEFAULT_BASE_PORT; + + let _server_handle = start_mock_grpc_server(service.clone(), port).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + let id_address = Address::random(); + let committer_id_address = Address::random(); + let server_address = format!("http://127.0.0.1:{}", port); + + let mock_client = MockCommitterClient::new( + id_address, + committer_id_address, + server_address, + ).await.expect("Failed to create mock client"); + + let result = mock_client + .commit_partial_signature( + DEFAULT_CHAIN_ID, + arpa_core::BLSTaskType::Randomness, + vec![1, 2, 3], + vec![4, 5, 6], + vec![7, 8, 9], + ) + .await; + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), true); + assert_eq!(mock_client.get_committer_id_address(), committer_id_address); + + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!(service.get_call_count(), 1); + + let received_requests = service.get_received_requests(); + assert_eq!(received_requests.len(), 1); + assert_eq!(received_requests[0].chain_id, DEFAULT_CHAIN_ID as u32); + assert_eq!(received_requests[0].task_type, BlsTaskType::Randomness as i32); + assert_eq!(received_requests[0].request_id, vec![1, 2, 3]); + assert_eq!(received_requests[0].message, vec![4, 5, 6]); + assert_eq!(received_requests[0].partial_signature, vec![7, 8, 9]); + } + + #[tokio::test] + async fn test_mock_grpc_committer_client_rejection() { + let service = MockCommitterService::new(false); + let port = DEFAULT_BASE_PORT + 1; + + let _server_handle = start_mock_grpc_server(service.clone(), port).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + let id_address = Address::random(); + let committer_id_address = Address::random(); + let server_address = format!("http://127.0.0.1:{}", port); + + let mock_client = MockCommitterClient::new( + id_address, + committer_id_address, + server_address, + ).await.expect("Failed to create mock client"); + + let result = mock_client + .commit_partial_signature( + DEFAULT_CHAIN_ID, + arpa_core::BLSTaskType::Randomness, + vec![1, 2, 3], + vec![4, 5, 6], + vec![7, 8, 9], + ) + .await; + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), false); + + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!(service.get_call_count(), 1); + } + + #[tokio::test] + async fn test_mock_grpc_committer_with_delay() { + let service = MockCommitterService::new(true) + .with_delay(Duration::from_millis(100)); + let port = DEFAULT_BASE_PORT + 2; + + let _server_handle = start_mock_grpc_server(service.clone(), port).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + let id_address = Address::random(); + let committer_id_address = Address::random(); + let server_address = format!("http://127.0.0.1:{}", port); + + let mock_client = MockCommitterClient::new( + id_address, + committer_id_address, + server_address, + ).await.expect("Failed to create mock client"); + + let start_time = std::time::Instant::now(); + + let result = mock_client + .commit_partial_signature( + DEFAULT_CHAIN_ID, + arpa_core::BLSTaskType::Randomness, + vec![1, 2, 3], + vec![4, 5, 6], + vec![7, 8, 9], + ) + .await; + + let elapsed = start_time.elapsed(); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), true); + assert!(elapsed >= Duration::from_millis(100)); + } + + #[tokio::test] + async fn test_multiple_task_types() { + let service = MockCommitterService::new(true); + let port = DEFAULT_BASE_PORT + 3; + + let _server_handle = start_mock_grpc_server(service.clone(), port).await; + tokio::time::sleep(Duration::from_millis(100)).await; + + let id_address = Address::random(); + let committer_id_address = Address::random(); + let server_address = format!("http://127.0.0.1:{}", port); + + let mock_client = MockCommitterClient::new( + id_address, + committer_id_address, + server_address, + ).await.expect("Failed to create mock client"); + + let task_types = vec![ + arpa_core::BLSTaskType::Randomness, + arpa_core::BLSTaskType::GroupRelay, + arpa_core::BLSTaskType::GroupRelayConfirmation, + ]; + + for (i, task_type) in task_types.iter().enumerate() { + let result = mock_client + .commit_partial_signature( + DEFAULT_CHAIN_ID, + task_type.clone(), + vec![i as u8], + vec![i as u8 + 10], + vec![i as u8 + 20], + ) + .await; + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), true); + } + + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!(service.get_call_count(), 3); + + let received_requests = service.get_received_requests(); + assert_eq!(received_requests.len(), 3); + + assert_eq!(received_requests[0].task_type, BlsTaskType::Randomness as i32); + assert_eq!(received_requests[1].task_type, BlsTaskType::GroupRelay as i32); + assert_eq!(received_requests[2].task_type, BlsTaskType::GroupRelayConfirmation as i32); + } + + #[tokio::test] + async fn test_subscriber_notify_with_mock_grpc() { + let id_address = Address::random(); + let tasks = vec![create_test_randomness_task()]; + + let group_cache = setup_group_cache_with_secret( + id_address, + DEFAULT_CHAIN_ID, + DEFAULT_GROUP_INDEX as usize, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + ).await.unwrap(); + + let randomness_tasks_cache = setup_randomness_tasks_cache(tasks.clone()).await; + let randomness_signature_cache = setup_randomness_signature_cache().await; + let (eq, ts) = create_schedulers(); + + let subscriber = ReadyToHandleRandomnessTaskSubscriber::::new( + DEFAULT_CHAIN_ID, + id_address, + group_cache, + randomness_tasks_cache, + randomness_signature_cache, + eq, + ts, + default_retry_descriptor(), + ); + + let event = ReadyToHandleRandomnessTask { chain_id: DEFAULT_CHAIN_ID, tasks }; + + let result = subscriber + .notify(Topic::ReadyToHandleRandomnessTask(DEFAULT_CHAIN_ID), &event) + .await; + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_partial_signature_generation() { + let id_address = Address::random(); + let task = create_test_randomness_task(); + + let group_cache = setup_group_cache_with_secret( + id_address, + DEFAULT_CHAIN_ID, + DEFAULT_GROUP_INDEX as usize, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, Address::random(), Address::random()], + ).await.unwrap(); + + let actual_seed = [ + &arpa_core::u256_to_vec(&task.seed)[..], + &arpa_core::u256_to_vec(ðers::types::U256::from(task.assignment_block_height))[..], + ] + .concat(); + + let group_cache_guard = group_cache.read().await; + let secret_share = group_cache_guard.get_secret_share().unwrap(); + let partial_signature_result = SimpleBLSCore::::partial_sign( + secret_share, + &actual_seed, + ); + + assert!(partial_signature_result.is_ok()); + } + + #[tokio::test] + async fn test_signature_cache_operations() { + let randomness_signature_cache = setup_randomness_signature_cache().await; + let task = create_test_randomness_task(); + let message = vec![1, 2, 3, 4]; + + let result = randomness_signature_cache + .write() + .await + .add(DEFAULT_GROUP_INDEX as usize, task.clone(), message, DEFAULT_THRESHOLD) + .await; + + assert!(result.is_ok()); + + let contains_result = randomness_signature_cache + .read() + .await + .contains(&task.request_id) + .await; + + assert!(contains_result.is_ok()); + assert!(contains_result.unwrap()); + } + + #[tokio::test] + async fn test_group_cache_committer_operations() { + let id_address = Address::random(); + let committer_address = Address::random(); + let non_committer_address = Address::random(); + + let group_cache = setup_group_cache_simple( + id_address, + DEFAULT_CHAIN_ID, + DEFAULT_GROUP_INDEX as usize, + DEFAULT_EPOCH, + DEFAULT_GROUP_SIZE, + DEFAULT_THRESHOLD, + vec![id_address, committer_address, non_committer_address], + ).await.unwrap(); + + let result_id = group_cache.read().await.is_committer(id_address); + let result_committer = group_cache.read().await.is_committer(committer_address); + let result_non_committer = group_cache.read().await.is_committer(non_committer_address); + + assert!(result_id.is_ok()); + assert!(result_committer.is_ok()); + assert!(result_non_committer.is_ok()); + } + + #[tokio::test] + async fn test_multiple_grpc_clients() { + let service1 = MockCommitterService::new(true); + let service2 = MockCommitterService::new(false); + let port1 = DEFAULT_BASE_PORT + 4; + let port2 = DEFAULT_BASE_PORT + 5; + + let _server_handle1 = start_mock_grpc_server(service1.clone(), port1).await; + let _server_handle2 = start_mock_grpc_server(service2.clone(), port2).await; + + tokio::time::sleep(Duration::from_millis(100)).await; + + let client1 = MockCommitterClient::new( + Address::random(), + Address::random(), + format!("http://127.0.0.1:{}", port1), + ).await.unwrap(); + + let client2 = MockCommitterClient::new( + Address::random(), + Address::random(), + format!("http://127.0.0.1:{}", port2), + ).await.unwrap(); + + let result1 = client1 + .commit_partial_signature( + DEFAULT_CHAIN_ID, + arpa_core::BLSTaskType::Randomness, + vec![1], + vec![2], + vec![3], + ) + .await; + + let result2 = client2 + .commit_partial_signature( + DEFAULT_CHAIN_ID, + arpa_core::BLSTaskType::Randomness, + vec![4], + vec![5], + vec![6], + ) + .await; + + assert!(result1.is_ok()); + assert_eq!(result1.unwrap(), true); + + assert!(result2.is_ok()); + assert_eq!(result2.unwrap(), false); + + tokio::time::sleep(Duration::from_millis(50)).await; + assert_eq!(service1.get_call_count(), 1); + assert_eq!(service2.get_call_count(), 1); + } +} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/schedule_node_activation.rs b/crates/arpa-node/src/subscriber/schedule_node_activation.rs index 0103d796..1487351a 100644 --- a/crates/arpa-node/src/subscriber/schedule_node_activation.rs +++ b/crates/arpa-node/src/subscriber/schedule_node_activation.rs @@ -129,3 +129,370 @@ impl DebuggableSubscriber for NodeActivationSubscriber { } + +#[cfg(feature = "unittest")] +mod tests { + use super::*; + use crate::{ + event::{node_activation::NodeActivation, types::Topic}, + queue::event_queue::EventQueue, + test_contracts::{ + mockavsdirectory::{deploy_mock_a_v_s_directory, get_mock_a_v_s_directory_at}, + mocknoderegistry::{deploy_mock_node_registry_with_args, get_mock_node_registry_at, MockNodeRegistry}, + mockservicemanager::{deploy_mock_service_manager_with_args, get_mock_service_manager_at}, + }, + }; + use arpa_contract_client::contract_stub::node_registry::NodeActivatedFilter; + use arpa_core::{Config, GeneralMainChainIdentity}; + use ethers::prelude::*; + use std::sync::Arc; + use threshold_bls::schemes::bn254::G2Curve; + use tokio::sync::RwLock; + + const TEST_DKG_KEY: &[u8] = b"test_dkg_key"; + const TEST_SALT: [u8; 32] = [1u8; 32]; + const TEST_EXPIRY: u64 = 1000; + const FIRST_GROUP_INDEX: u64 = 1; + + async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { + let anvil = ethers::utils::Anvil::new().spawn(); + let ws_provider = Arc::new(Provider::::connect(anvil.ws_endpoint()).await.unwrap()); + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet = wallet.with_chain_id(anvil.chain_id()); + (anvil, ws_provider, wallet) + } + + async fn setup_mock_contracts_with_dependencies( + ws_provider: Arc>, + wallet: LocalWallet, + ) -> (Address, Address, Address) { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let avs_directory_address = deploy_mock_a_v_s_directory(client.clone()).await.unwrap(); + let service_manager_address = deploy_mock_service_manager_with_args(client.clone(), avs_directory_address).await.unwrap(); + let node_registry_address = deploy_mock_node_registry_with_args( + client.clone(), + (Address::random(), Address::random(), service_manager_address) + ).await.unwrap(); + (node_registry_address, service_manager_address, avs_directory_address) + } + + async fn setup_node_registry_with_node_and_dependencies( + ws_provider: Arc>, + wallet: LocalWallet, + node_address: Address, + is_eigenlayer: bool, + ) -> (Address, Address, Address, MockNodeRegistry, LocalWallet>>) { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let (registry_address, service_manager_address, avs_directory_address) = + setup_mock_contracts_with_dependencies(ws_provider, wallet).await; + let registry_contract = get_mock_node_registry_at(registry_address, client); + registry_contract + .register_node(node_address, TEST_DKG_KEY.to_vec().into(), is_eigenlayer) + .send().await.unwrap().await.unwrap(); + (registry_address, service_manager_address, avs_directory_address, registry_contract) + } + + async fn deploy_node_registry(ws_provider: Arc>, wallet: LocalWallet) -> Address { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + deploy_mock_node_registry_with_args( + client, + (Address::random(), Address::random(), Address::random()) + ).await.unwrap() + } + + async fn create_chain_identity( + chain_id: u64, + wallet: LocalWallet, + ws_provider: Arc>, + ws_endpoint: String, + node_registry_address: Address, + ) -> Arc>> { + let config = Config::default(); + let general_chain_identity = GeneralMainChainIdentity::new( + chain_id.try_into().unwrap(), + wallet.clone(), + ws_provider.clone(), + ws_endpoint, + Address::random(), + Address::random(), + node_registry_address, + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + async fn setup_node_registry_with_node( + ws_provider: Arc>, + wallet: LocalWallet, + node_address: Address, + is_eigenlayer: bool, + ) -> (Address, MockNodeRegistry, LocalWallet>>) { + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let registry_address = deploy_mock_node_registry_with_args( + client.clone(), + (Address::random(), Address::random(), Address::random()) + ).await.unwrap(); + let registry_contract = get_mock_node_registry_at(registry_address, client); + registry_contract + .register_node(node_address, TEST_DKG_KEY.to_vec().into(), is_eigenlayer) + .send().await.unwrap().await.unwrap(); + (registry_address, registry_contract) + } + + #[tokio::test] + async fn test_node_activation_subscriber_creation() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; + let chain_identity = create_chain_identity( + 1, + wallet, + ws_provider, + "ws://localhost:8545".to_string(), + node_registry_address, + ).await; + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + assert!(format!("{:?}", subscriber).contains("NodeActivationSubscriber")); + } + + #[tokio::test] + async fn test_successful_eigenlayer_node_activation() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + let (node_registry_address, service_manager_address, avs_directory_address, registry_contract) = + setup_node_registry_with_node_and_dependencies( + ws_provider.clone(), wallet.clone(), node_address, true + ).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet.clone(), ws_provider.clone(), + anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: true, + node_registry_address, + }; + + let node_before = registry_contract.get_node(node_address).call().await.unwrap(); + assert!(!node_before.state); + + let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); + let config = registry_contract.get_node_registry_config().call().await.unwrap(); + assert_eq!(config.2, service_manager_address); + + let service_manager_contract = get_mock_service_manager_at(service_manager_address, client.clone()); + let returned_avs_directory = service_manager_contract.avs_directory().call().await.unwrap(); + assert_eq!(returned_avs_directory, avs_directory_address); + + let avs_directory_contract = get_mock_a_v_s_directory_at(avs_directory_address, client); + let test_hash = avs_directory_contract + .calculate_operator_avs_registration_digest_hash( + node_address, service_manager_address, TEST_SALT, TEST_EXPIRY.into() + ) + .call().await.unwrap(); + assert_ne!(test_hash, [0u8; 32]); + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + + let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + assert!(node_after.state); + assert!(node_after.is_eigenlayer_node); + } + + #[tokio::test] + async fn test_successful_native_staking_node_activation() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + + let (node_registry_address, registry_contract) = setup_node_registry_with_node( + ws_provider.clone(), wallet.clone(), node_address, false + ).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: false, + node_registry_address, + }; + + let node_before = registry_contract.get_node(node_address).call().await.unwrap(); + assert!(!node_before.state); + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + + let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + assert!(node_after.state); + assert!(!node_after.is_eigenlayer_node); + } + + #[tokio::test] + async fn test_node_activation_already_active_error() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + + let (node_registry_address, registry_contract) = setup_node_registry_with_node( + ws_provider.clone(), wallet.clone(), node_address, false + ).await; + + registry_contract.set_node_state(node_address, true).send().await.unwrap().await.unwrap(); + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: false, + node_registry_address, + }; + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_node_activation_not_registered_error() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: false, + node_registry_address, + }; + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_node_activation_event_emission() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + + let (node_registry_address, _registry_contract) = setup_node_registry_with_node( + ws_provider.clone(), wallet.clone(), node_address, false + ).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider.clone(), anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: false, + node_registry_address, + }; + + let event_filter = MockNodeRegistry::new(node_registry_address, ws_provider.clone()) + .event::(); + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + + let events = event_filter.from_block(0).query().await.unwrap(); + assert_eq!(events.len(), 1); + assert_eq!(events[0].node_address, node_address); + assert_eq!(events[0].group_index, U256::from(FIRST_GROUP_INDEX)); + } + + #[tokio::test] + async fn test_subscriber_subscribe() { + let (_anvil, ws_provider, wallet) = setup_anvil().await; + let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; + let chain_identity = create_chain_identity( + 1, wallet, ws_provider, "ws://localhost:8545".to_string(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq.clone()); + subscriber.subscribe().await; + } + + #[tokio::test] + async fn test_different_chain_ids() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + + let (node_registry_address, _registry_contract) = setup_node_registry_with_node( + ws_provider.clone(), wallet.clone(), node_address, false + ).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let test_chain_ids = vec![1, 137, 42161, 10]; + + for chain_id in test_chain_ids { + let activation_event = NodeActivation { + chain_id, + is_eigenlayer: false, + node_registry_address, + }; + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + } + } + + #[tokio::test] + async fn test_node_activation_with_pending_status() { + let (anvil, ws_provider, wallet) = setup_anvil().await; + let node_address = wallet.address(); + + let (node_registry_address, registry_contract) = setup_node_registry_with_node( + ws_provider.clone(), wallet.clone(), node_address, false + ).await; + + let chain_identity = create_chain_identity( + anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address + ).await; + + let eq = Arc::new(RwLock::new(EventQueue::new())); + let subscriber = NodeActivationSubscriber::new(chain_identity, eq); + + let activation_event = NodeActivation { + chain_id: anvil.chain_id() as usize, + is_eigenlayer: false, + node_registry_address, + }; + + let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + assert!(result.is_ok()); + + let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + assert!(node_after.state); + } +} \ No newline at end of file From caf56ba87b3b8bf73ef7a94c0e0681c1ddec640f Mon Sep 17 00:00:00 2001 From: Zuoyuan Zhao Date: Mon, 16 Jun 2025 17:20:41 -0500 Subject: [PATCH 2/5] clean up --- crates/arpa-node/src/mock_contracts/MockCoordinator.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/arpa-node/src/mock_contracts/MockCoordinator.sol b/crates/arpa-node/src/mock_contracts/MockCoordinator.sol index 96b105d2..14e756c7 100644 --- a/crates/arpa-node/src/mock_contracts/MockCoordinator.sol +++ b/crates/arpa-node/src/mock_contracts/MockCoordinator.sol @@ -77,7 +77,7 @@ contract MockCoordinator { sharesData = _shares; responsesData = _responses; justificationsData = _justifications; - currentPhase = 1; // 默认开始Phase 1 + currentPhase = 1; } function clearAllData() external { From aaba1f57151e257e4c733edba76906eee0c7967d Mon Sep 17 00:00:00 2001 From: Zuoyuan Zhao Date: Tue, 17 Jun 2025 18:05:46 -0500 Subject: [PATCH 3/5] fixed bug --- .../src/listener/schedule_node_activation.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/arpa-node/src/listener/schedule_node_activation.rs b/crates/arpa-node/src/listener/schedule_node_activation.rs index 98b6b15b..b03997b8 100644 --- a/crates/arpa-node/src/listener/schedule_node_activation.rs +++ b/crates/arpa-node/src/listener/schedule_node_activation.rs @@ -122,7 +122,7 @@ mod tests { use crate::subscriber::{DebuggableEvent, DebuggableSubscriber, Subscriber}; use crate::test_contracts::mockcontroller::deploy_with_args_and_get_mock_controller; use crate::test_contracts::mocknoderegistry::{ - deploy_and_get_mock_node_registry, MockNodeRegistry, + deploy_with_args_and_get_mock_node_registry, MockNodeRegistry }; use ethers::middleware::SignerMiddleware; use ethers::signers::{LocalWallet, Signer}; @@ -274,7 +274,14 @@ mod tests { ) -> Result<(Address, Address), Box> { println!("Deploying mock contracts..."); - let node_registry = deploy_and_get_mock_node_registry(client.clone()).await?; + let node_registry = deploy_with_args_and_get_mock_node_registry( + client.clone(), + ( + Address::random(), + Address::random(), + Address::random(), + ) + ).await?; let node_registry_address = node_registry.address(); println!( "Node Registry contract deployed at: {}", From fa260cb659996a878d313d928eb7f90ea0fa0e79 Mon Sep 17 00:00:00 2001 From: Zuoyuan Zhao Date: Wed, 22 Oct 2025 22:40:52 -0500 Subject: [PATCH 4/5] fixing conflicts --- crates/arpa-node/Cargo.toml | 1 - crates/arpa-node/src/subscriber/block.rs | 4 +- .../arpa-node/src/subscriber/in_grouping.rs | 2 +- .../arpa-node/src/subscriber/post_grouping.rs | 2 +- .../randomness_signature_aggregation.rs | 2 +- .../ready_to_handle_randomness_task.rs | 2 +- .../subscriber/schedule_node_activation.rs | 425 +++++++++--------- .../test-contract/MockAVSDirectory.json | 1 + .../test-contract/MockAVSDirectory.sol | 34 ++ .../test-contract/MockServiceManager.sol | 10 + 10 files changed, 274 insertions(+), 209 deletions(-) create mode 100644 crates/arpa-node/test-contract/MockAVSDirectory.json create mode 100644 crates/arpa-node/test-contract/MockAVSDirectory.sol create mode 100644 crates/arpa-node/test-contract/MockServiceManager.sol diff --git a/crates/arpa-node/Cargo.toml b/crates/arpa-node/Cargo.toml index 86c50a88..a6bc333b 100644 --- a/crates/arpa-node/Cargo.toml +++ b/crates/arpa-node/Cargo.toml @@ -75,4 +75,3 @@ check-latest = { version = "1.0.2", default-features = false, features = [ alloy = { workspace = true, features = ["full", "provider-anvil-node"] } arpa-node = { path = ".", features = [] } regex = "1.11.1" -ethers-core.workspace = true diff --git a/crates/arpa-node/src/subscriber/block.rs b/crates/arpa-node/src/subscriber/block.rs index a707be00..977dc9f0 100644 --- a/crates/arpa-node/src/subscriber/block.rs +++ b/crates/arpa-node/src/subscriber/block.rs @@ -90,8 +90,8 @@ mod tests { } impl BlockInfoFetcher for MockBlockInfoHandler { - fn get_chain_id(&self) -> usize { - self.chain_id + fn get_chain_id(&self) -> u64 { + self.chain_id.try_into().unwrap() } fn get_block_height(&self) -> usize { diff --git a/crates/arpa-node/src/subscriber/in_grouping.rs b/crates/arpa-node/src/subscriber/in_grouping.rs index 44fb13ec..989e37d0 100644 --- a/crates/arpa-node/src/subscriber/in_grouping.rs +++ b/crates/arpa-node/src/subscriber/in_grouping.rs @@ -383,7 +383,7 @@ impl DebuggableSubscriber { } -#[cfg(feature = "unittest")] +#[cfg(test)] mod tests { use super::*; use crate::{ diff --git a/crates/arpa-node/src/subscriber/post_grouping.rs b/crates/arpa-node/src/subscriber/post_grouping.rs index 09b1eee8..41fdf554 100644 --- a/crates/arpa-node/src/subscriber/post_grouping.rs +++ b/crates/arpa-node/src/subscriber/post_grouping.rs @@ -227,7 +227,7 @@ impl DebuggableSubscriber { } -#[cfg(feature = "unittest")] +#[cfg(test)] mod tests { use super::*; use crate::{ diff --git a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs index 96959a40..401424e2 100644 --- a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs +++ b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs @@ -415,7 +415,7 @@ where { } -#[cfg(feature = "unittest")] +#[cfg(test)] mod tests { use super::*; use crate::{ diff --git a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs index b4b27ea4..c9b6b311 100644 --- a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs +++ b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs @@ -407,7 +407,7 @@ where { } -#[cfg(feature = "unittest")] +#[cfg(test)] mod tests { use super::*; use crate::{ diff --git a/crates/arpa-node/src/subscriber/schedule_node_activation.rs b/crates/arpa-node/src/subscriber/schedule_node_activation.rs index 8e5ba716..6bee742d 100644 --- a/crates/arpa-node/src/subscriber/schedule_node_activation.rs +++ b/crates/arpa-node/src/subscriber/schedule_node_activation.rs @@ -122,21 +122,19 @@ impl DebuggableSubscriber { } -#[cfg(feature = "unittest")] +#[cfg(test)] mod tests { use super::*; use crate::{ event::{node_activation::NodeActivation, types::Topic}, queue::event_queue::EventQueue, - test_contracts::{ - mockavsdirectory::{deploy_mock_a_v_s_directory, get_mock_a_v_s_directory_at}, - mocknoderegistry::{deploy_mock_node_registry_with_args, get_mock_node_registry_at, MockNodeRegistry}, - mockservicemanager::{deploy_mock_service_manager_with_args, get_mock_service_manager_at}, - }, }; - use arpa_contract_client::contract_stub::node_registry::NodeActivatedFilter; - use arpa_core::{Config, GeneralMainChainIdentity}; - use ethers::prelude::*; + use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::primitives::{Address, U256}; + use alloy::providers::{Provider, WsConnect}; + use alloy::signers::local::PrivateKeySigner; + use alloy::sol; + use arpa_core::{build_client, Config, GeneralMainChainIdentity, ProviderClientWithSigner}; use std::sync::Arc; use threshold_bls::schemes::bn254::G2Curve; use tokio::sync::RwLock; @@ -146,104 +144,125 @@ mod tests { const TEST_EXPIRY: u64 = 1000; const FIRST_GROUP_INDEX: u64 = 1; - async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { - let anvil = ethers::utils::Anvil::new().spawn(); - let ws_provider = Arc::new(Provider::::connect(anvil.ws_endpoint()).await.unwrap()); - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let wallet = wallet.with_chain_id(anvil.chain_id()); - (anvil, ws_provider, wallet) + sol! { + #[sol(rpc)] + MockAVSDirectory, + "test-contract/MockAVSDirectory.json" } - async fn setup_mock_contracts_with_dependencies( - ws_provider: Arc>, - wallet: LocalWallet, - ) -> (Address, Address, Address) { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let avs_directory_address = deploy_mock_a_v_s_directory(client.clone()).await.unwrap(); - let service_manager_address = deploy_mock_service_manager_with_args(client.clone(), avs_directory_address).await.unwrap(); - let node_registry_address = deploy_mock_node_registry_with_args( - client.clone(), - (Address::random(), Address::random(), service_manager_address) - ).await.unwrap(); - (node_registry_address, service_manager_address, avs_directory_address) + sol! { + #[sol(rpc)] + MockServiceManager, + "test-contract/MockServiceManager.json" } - async fn setup_node_registry_with_node_and_dependencies( - ws_provider: Arc>, - wallet: LocalWallet, - node_address: Address, - is_eigenlayer: bool, - ) -> (Address, Address, Address, MockNodeRegistry, LocalWallet>>) { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let (registry_address, service_manager_address, avs_directory_address) = - setup_mock_contracts_with_dependencies(ws_provider, wallet).await; - let registry_contract = get_mock_node_registry_at(registry_address, client); - registry_contract - .register_node(node_address, TEST_DKG_KEY.to_vec().into(), is_eigenlayer) - .send().await.unwrap().await.unwrap(); - (registry_address, service_manager_address, avs_directory_address, registry_contract) + sol! { + #[sol(rpc)] + MockNodeRegistry, + "test-contract/MockNodeRegistry.json" } - async fn deploy_node_registry(ws_provider: Arc>, wallet: LocalWallet) -> Address { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - deploy_mock_node_registry_with_args( - client, - (Address::random(), Address::random(), Address::random()) - ).await.unwrap() + struct TestEnvironment { + _anvil: AnvilInstance, + client: ProviderClientWithSigner, + wallet: PrivateKeySigner, + chain_id: u64 } - async fn create_chain_identity( - chain_id: u64, - wallet: LocalWallet, - ws_provider: Arc>, - ws_endpoint: String, - node_registry_address: Address, - ) -> Arc>> { - let config = Config::default(); - let general_chain_identity = GeneralMainChainIdentity::new( - chain_id.try_into().unwrap(), - wallet.clone(), - ws_provider.clone(), - ws_endpoint, - Address::random(), - Address::random(), - node_registry_address, - config.get_time_limits().contract_transaction_retry_descriptor, - config.get_time_limits().contract_view_retry_descriptor, - None, - ); - Arc::new(RwLock::new(Box::new(general_chain_identity))) - } + impl TestEnvironment { + async fn new() -> Self { + let anvil = Anvil::new().spawn(); + let ws_connect = WsConnect::new(anvil.ws_endpoint()); + let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); + let chain_id = anvil.chain_id(); + let client = build_client(wallet.clone(), chain_id, ws_connect) + .await + .unwrap(); - async fn setup_node_registry_with_node( - ws_provider: Arc>, - wallet: LocalWallet, - node_address: Address, - is_eigenlayer: bool, - ) -> (Address, MockNodeRegistry, LocalWallet>>) { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let registry_address = deploy_mock_node_registry_with_args( - client.clone(), - (Address::random(), Address::random(), Address::random()) - ).await.unwrap(); - let registry_contract = get_mock_node_registry_at(registry_address, client); - registry_contract - .register_node(node_address, TEST_DKG_KEY.to_vec().into(), is_eigenlayer) - .send().await.unwrap().await.unwrap(); - (registry_address, registry_contract) + TestEnvironment { + _anvil: anvil, + client, + wallet, + chain_id + } + } + + async fn deploy_mock_avs_directory(client: ProviderClientWithSigner,) -> Address { + let contract = MockAVSDirectory::deploy(client.clone()).await; + *contract.address() + } + + async fn deploy_mock_service_manager(&self, avs_directory: Address) -> Address { + let contract = MockServiceManager::deploy(client.clone()).await; + *contract.address() + } + + async fn deploy_mock_node_registry( + &self, + service_manager: Address, + ) -> (Address, MockNodeRegistry::MockNodeRegistryInstance) { + let contract = MockNodeRegistry::deploy( + &self.client.clone() + ) + .await + .unwrap(); + (*contract.address(), contract) + } + + async fn setup_full_contracts(&self) -> (Address, Address, Address, MockNodeRegistry::MockNodeRegistryInstance) { + let avs_directory = self.deploy_mock_avs_directory().await; + let service_manager = self.deploy_mock_service_manager(avs_directory).await; + let (node_registry, registry_instance) = + self.deploy_mock_node_registry(service_manager).await; + (node_registry, service_manager, avs_directory, registry_instance) + } + + async fn register_node( + &self, + registry: &MockNodeRegistry::MockNodeRegistryInstance, + node_address: Address, + is_eigenlayer: bool, + ) { + registry + .registerNode(node_address, TEST_DKG_KEY.to_vec().into(), is_eigenlayer) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + } + + async fn create_chain_identity( + &self, + node_registry_address: Address, + ) -> Arc>> { + let config = Config::default(); + let ws_connect = WsConnect::new(self.ws_endpoint.clone()); + let general_chain_identity = GeneralMainChainIdentity::new( + self.chain_id.try_into().unwrap(), + self.wallet.clone(), + ws_connect, + self.client.clone(), + self.ws_endpoint.clone(), + Address::ZERO, // controller + Address::ZERO, // controller_relayer + node_registry_address, + config + .get_time_limits() + .contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } } #[tokio::test] async fn test_node_activation_subscriber_creation() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; - let chain_identity = create_chain_identity( - 1, - wallet, - ws_provider, - "ws://localhost:8545".to_string(), - node_registry_address, - ).await; + let env = TestEnvironment::new().await; + let (node_registry_address, _, _, _) = env.setup_full_contracts().await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); assert!(format!("{:?}", subscriber).contains("NodeActivationSubscriber")); @@ -251,178 +270,182 @@ mod tests { #[tokio::test] async fn test_successful_eigenlayer_node_activation() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); - let (node_registry_address, service_manager_address, avs_directory_address, registry_contract) = - setup_node_registry_with_node_and_dependencies( - ws_provider.clone(), wallet.clone(), node_address, true - ).await; - - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet.clone(), ws_provider.clone(), - anvil.ws_endpoint(), node_registry_address - ).await; + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, service_manager_address, avs_directory_address, registry_contract) = + env.setup_full_contracts().await; + env.register_node(®istry_contract, node_address, true) + .await; + + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: true, node_registry_address, }; - let node_before = registry_contract.get_node(node_address).call().await.unwrap(); + // Check node state before activation + let node_before = registry_contract.getNode(node_address).call().await.unwrap(); assert!(!node_before.state); - - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let config = registry_contract.get_node_registry_config().call().await.unwrap(); - assert_eq!(config.2, service_manager_address); - - let service_manager_contract = get_mock_service_manager_at(service_manager_address, client.clone()); - let returned_avs_directory = service_manager_contract.avs_directory().call().await.unwrap(); + + // Verify service manager and avs directory setup + let service_manager = MockServiceManager::new(service_manager_address, &env.client); + let returned_avs_directory = service_manager.avsDirectory().call().await.unwrap()._0; assert_eq!(returned_avs_directory, avs_directory_address); - - let avs_directory_contract = get_mock_a_v_s_directory_at(avs_directory_address, client); - let test_hash = avs_directory_contract - .calculate_operator_avs_registration_digest_hash( - node_address, service_manager_address, TEST_SALT, TEST_EXPIRY.into() + + let avs_directory = MockAVSDirectory::new(avs_directory_address, &env.client); + let test_hash = avs_directory + .calculateOperatorAVSRegistrationDigestHash( + node_address, + service_manager_address, + TEST_SALT.into(), + U256::from(TEST_EXPIRY), ) - .call().await.unwrap(); - assert_ne!(test_hash, [0u8; 32]); + .call() + .await + .unwrap() + ._0; + assert_ne!(test_hash, [0u8; 32].into()); - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); - let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + let node_after = registry_contract.getNode(node_address).call().await.unwrap(); assert!(node_after.state); - assert!(node_after.is_eigenlayer_node); + assert!(node_after.isEigenlayerNode); } #[tokio::test] async fn test_successful_native_staking_node_activation() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); - - let (node_registry_address, registry_contract) = setup_node_registry_with_node( - ws_provider.clone(), wallet.clone(), node_address, false - ).await; + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, _, _, registry_contract) = env.setup_full_contracts().await; - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address - ).await; + env.register_node(®istry_contract, node_address, false) + .await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: false, node_registry_address, }; - let node_before = registry_contract.get_node(node_address).call().await.unwrap(); + let node_before = registry_contract.getNode(node_address).call().await.unwrap(); assert!(!node_before.state); - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); - let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + let node_after = registry_contract.getNode(node_address).call().await.unwrap(); assert!(node_after.state); - assert!(!node_after.is_eigenlayer_node); + assert!(!node_after.isEigenlayerNode); } #[tokio::test] async fn test_node_activation_already_active_error() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, _, _, registry_contract) = env.setup_full_contracts().await; - let (node_registry_address, registry_contract) = setup_node_registry_with_node( - ws_provider.clone(), wallet.clone(), node_address, false - ).await; + env.register_node(®istry_contract, node_address, false) + .await; - registry_contract.set_node_state(node_address, true).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address - ).await; + // Set node as active + registry_contract + .setNodeState(node_address, true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: false, node_registry_address, }; - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_node_activation_not_registered_error() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address - ).await; + let env = TestEnvironment::new().await; + let (node_registry_address, _, _, _) = env.setup_full_contracts().await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: false, node_registry_address, }; - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_node_activation_event_emission() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, _, _, registry_contract) = env.setup_full_contracts().await; - let (node_registry_address, _registry_contract) = setup_node_registry_with_node( - ws_provider.clone(), wallet.clone(), node_address, false - ).await; - - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider.clone(), anvil.ws_endpoint(), node_registry_address - ).await; + env.register_node(®istry_contract, node_address, false) + .await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: false, node_registry_address, }; - let event_filter = MockNodeRegistry::new(node_registry_address, ws_provider.clone()) - .event::(); - - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); - let events = event_filter.from_block(0).query().await.unwrap(); + // Query for NodeActivated events + let event_filter = registry_contract.NodeActivated_filter().from_block(0); + let events = event_filter.query().await.unwrap(); assert_eq!(events.len(), 1); - assert_eq!(events[0].node_address, node_address); - assert_eq!(events[0].group_index, U256::from(FIRST_GROUP_INDEX)); + assert_eq!(events[0].nodeAddress, node_address); + assert_eq!(events[0].groupIndex, U256::from(FIRST_GROUP_INDEX)); } #[tokio::test] async fn test_subscriber_subscribe() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let node_registry_address = deploy_node_registry(ws_provider.clone(), wallet.clone()).await; - let chain_identity = create_chain_identity( - 1, wallet, ws_provider, "ws://localhost:8545".to_string(), node_registry_address - ).await; + let env = TestEnvironment::new().await; + let (node_registry_address, _, _, _) = env.setup_full_contracts().await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq.clone()); @@ -431,17 +454,14 @@ mod tests { #[tokio::test] async fn test_different_chain_ids() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, _, _, registry_contract) = env.setup_full_contracts().await; - let (node_registry_address, _registry_contract) = setup_node_registry_with_node( - ws_provider.clone(), wallet.clone(), node_address, false - ).await; - - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address - ).await; + env.register_node(®istry_contract, node_address, false) + .await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); @@ -454,37 +474,38 @@ mod tests { node_registry_address, }; - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); } } #[tokio::test] async fn test_node_activation_with_pending_status() { - let (anvil, ws_provider, wallet) = setup_anvil().await; - let node_address = wallet.address(); - - let (node_registry_address, registry_contract) = setup_node_registry_with_node( - ws_provider.clone(), wallet.clone(), node_address, false - ).await; + let env = TestEnvironment::new().await; + let node_address = env.wallet.address(); + let (node_registry_address, _, _, registry_contract) = env.setup_full_contracts().await; - let chain_identity = create_chain_identity( - anvil.chain_id(), wallet, ws_provider, anvil.ws_endpoint(), node_registry_address - ).await; + env.register_node(®istry_contract, node_address, false) + .await; + let chain_identity = env.create_chain_identity(node_registry_address).await; let eq = Arc::new(RwLock::new(EventQueue::new())); let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: anvil.chain_id() as usize, + chain_id: env.chain_id as usize, is_eigenlayer: false, node_registry_address, }; - let result = subscriber.notify(Topic::NodeActivation, &activation_event).await; + let result = subscriber + .notify(Topic::NodeActivation, &activation_event) + .await; assert!(result.is_ok()); - let node_after = registry_contract.get_node(node_address).call().await.unwrap(); + let node_after = registry_contract.getNode(node_address).call().await.unwrap(); assert!(node_after.state); } } \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockAVSDirectory.json b/crates/arpa-node/test-contract/MockAVSDirectory.json new file mode 100644 index 00000000..f657ae5b --- /dev/null +++ b/crates/arpa-node/test-contract/MockAVSDirectory.json @@ -0,0 +1 @@ +{"contracts":{"MockAVSDirectory.sol:MockAVSDirectory":{"abi":[{"inputs":[],"name":"OPERATOR_AVS_REGISTRATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"avs","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"calculateOperatorAVSRegistrationDigestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}],"bin":"6080604052348015600f57600080fd5b5061025c8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a1060c881461003b578063d79aceab14610060575b600080fd5b61004e6100493660046101e4565b610087565b60405190815260200160405180910390f35b61004e7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290526000906100fe9060c00160405160208183030381529060405280519060200120610107565b95945050505050565b600061018b604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a59181019190915246606082015230608082015260009060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101df57600080fd5b919050565b600080600080608085870312156101fa57600080fd5b610203856101c8565b9350610211602086016101c8565b9396939550505050604082013591606001359056fea2646970667358221220dbd3a7787db0b0f15c8be1882e760026205840ed4366838ebb349e23df7964cc64736f6c634300081b0033","bin-runtime":"608060405234801561001057600080fd5b50600436106100365760003560e01c8063a1060c881461003b578063d79aceab14610060575b600080fd5b61004e6100493660046101e4565b610087565b60405190815260200160405180910390f35b61004e7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290526000906100fe9060c00160405160208183030381529060405280519060200120610107565b95945050505050565b600061018b604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a59181019190915246606082015230608082015260009060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101df57600080fd5b919050565b600080600080608085870312156101fa57600080fd5b610203856101c8565b9350610211602086016101c8565b9396939550505050604082013591606001359056fea2646970667358221220dbd3a7787db0b0f15c8be1882e760026205840ed4366838ebb349e23df7964cc64736f6c634300081b0033","metadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"OPERATOR_AVS_REGISTRATION_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"name\":\"calculateOperatorAVSRegistrationDigestHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"MockAVSDirectory.sol\":\"MockAVSDirectory\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[]},\"sources\":{\"MockAVSDirectory.sol\":{\"keccak256\":\"0xcdb7ea8e372ba79a263c09b789f2ae208d0be87147372d9213ea8f1e1bc44f41\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c8869a37deee832ad1e59a5e7d330251ffb02396f181c8de4f516b2bceb9ab\",\"dweb:/ipfs/QmfAGeyoBBXyPcvUSaqEhJpg3HNqo1vURokurzebDvjvyZ\"]}},\"version\":1}","srcmap":"57:1123:0:-:0;;;;;;;;;;;;;;;;;;;","srcmap-runtime":"57:1123:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;402:339;;;;;;:::i;:::-;;:::i;:::-;;;763:25:1;;;751:2;736:18;402:339:0;;;;;;;89:164;;159:94;89:164;;402:339;648:75;;;159:94;648:75;;;1058:25:1;-1:-1:-1;;;;;1119:55:1;;;1099:18;;;1092:83;;;;1211:55;;;1191:18;;;1184:83;1283:18;;;1276:34;;;1326:19;;;1319:35;;;574:7:0;;600:134;;1030:19:1;;648:75:0;;;;;;;;;;;;638:86;;;;;;600:24;:134::i;:::-;593:141;402:339;-1:-1:-1;;;;;402:339:0:o;751:181::-;828:7;893:18;1029:141;;;311:80;1029:141;;;1993:25:1;1077:29:0;2034:18:1;;;2027:34;;;;1120:13:0;2077:18:1;;;2070:34;1155:4:0;2120:18:1;;;2113:83;993:7:0;;1965:19:1;;1029:141:0;;;;;;;;;;;;1019:152;;;;;;1012:159;;942:236;;893:18;864:60;;-1:-1:-1;;;864:60:0;;;1623:27:1;1666:11;;;1659:27;;;;1702:12;;;1695:28;;;1739:12;;864:60:0;;;;;;;;;;;;854:71;;;;;;847:78;;751:181;;;:::o;14:196:1:-;82:20;;-1:-1:-1;;;;;131:54:1;;121:65;;111:93;;200:1;197;190:12;111:93;14:196;;;:::o;215:397::-;301:6;309;317;325;378:3;366:9;357:7;353:23;349:33;346:53;;;395:1;392;385:12;346:53;418:29;437:9;418:29;:::i;:::-;408:39;;466:38;500:2;489:9;485:18;466:38;:::i;:::-;215:397;;456:48;;-1:-1:-1;;;;551:2:1;536:18;;523:32;;602:2;587:18;574:32;;215:397::o"}},"sourceList":["MockAVSDirectory.sol"],"version":"0.8.27+commit.40a35a09.Linux.g++"} diff --git a/crates/arpa-node/test-contract/MockAVSDirectory.sol b/crates/arpa-node/test-contract/MockAVSDirectory.sol new file mode 100644 index 00000000..ac9e3c33 --- /dev/null +++ b/crates/arpa-node/test-contract/MockAVSDirectory.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract MockAVSDirectory { + bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = + keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); + + bytes32 private constant _TYPE_HASH = + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); + + function calculateOperatorAVSRegistrationDigestHash( + address operator, + address avs, + bytes32 salt, + uint256 expiry + ) public view returns (bytes32) { + return _calculateSignableDigest( + keccak256(abi.encode(OPERATOR_AVS_REGISTRATION_TYPEHASH, operator, avs, salt, expiry)) + ); + } + + function _calculateSignableDigest(bytes32 structHash) internal view returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash)); + } + + function _domainSeparator() internal view returns (bytes32) { + return keccak256(abi.encode( + _TYPE_HASH, + keccak256("MockAVSDirectory"), + block.chainid, + address(this) + )); + } +} \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockServiceManager.sol b/crates/arpa-node/test-contract/MockServiceManager.sol new file mode 100644 index 00000000..1d68adb7 --- /dev/null +++ b/crates/arpa-node/test-contract/MockServiceManager.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract MockServiceManager { + address public avsDirectory; + + constructor(address _avsDirectory) { + avsDirectory = _avsDirectory; + } +} \ No newline at end of file From 403ada40f55e4f1402b453482fb054c493e26201 Mon Sep 17 00:00:00 2001 From: Zuoyuan Zhao Date: Sun, 26 Oct 2025 17:30:59 -0500 Subject: [PATCH 5/5] finished subscriber update --- crates/arpa-node/build.rs | 2 +- .../src/listener/schedule_node_activation.rs | 2 +- .../src/mock_contracts/MockAdapter.sol | 72 -- .../src/mock_contracts/MockController.sol | 137 ---- .../src/mock_contracts/MockNodeRegistry.sol | 172 ----- crates/arpa-node/src/subscriber/block.rs | 20 +- .../arpa-node/src/subscriber/in_grouping.rs | 241 ++++--- .../arpa-node/src/subscriber/post_grouping.rs | 672 +++++++++++------- .../src/subscriber/post_success_grouping.rs | 30 +- .../arpa-node/src/subscriber/pre_grouping.rs | 10 +- .../randomness_signature_aggregation.rs | 435 +++++++----- .../ready_to_handle_randomness_task.rs | 83 ++- .../subscriber/schedule_node_activation.rs | 62 +- .../arpa-node/test-contract/IController.sol | 46 ++ .../test-contract/MockAVSDirectory.json | 158 +++- .../arpa-node/test-contract/MockAdapter.json | 372 +++++++++- .../arpa-node/test-contract/MockAdapter.sol | 61 +- .../test-contract/MockController.json | 564 ++++++++++++++- .../test-contract/MockController.sol | 169 +++-- .../test-contract/MockControllerRelayer.json | 123 ++++ .../MockControllerRelayer.sol | 0 .../test-contract/MockCoordinator.json | 393 ++++++++++ .../MockCoordinator.sol | 0 .../test-contract/MockNodeRegistry.json | 318 ++++++++- .../test-contract/MockNodeRegistry.sol | 108 ++- .../test-contract/MockServiceManager.json | 111 +++ 26 files changed, 3249 insertions(+), 1112 deletions(-) delete mode 100644 crates/arpa-node/src/mock_contracts/MockAdapter.sol delete mode 100644 crates/arpa-node/src/mock_contracts/MockController.sol delete mode 100644 crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol create mode 100644 crates/arpa-node/test-contract/IController.sol create mode 100644 crates/arpa-node/test-contract/MockControllerRelayer.json rename crates/arpa-node/{src/mock_contracts => test-contract}/MockControllerRelayer.sol (100%) create mode 100644 crates/arpa-node/test-contract/MockCoordinator.json rename crates/arpa-node/{src/mock_contracts => test-contract}/MockCoordinator.sol (100%) create mode 100644 crates/arpa-node/test-contract/MockServiceManager.json diff --git a/crates/arpa-node/build.rs b/crates/arpa-node/build.rs index d57572b4..1ffb9112 100644 --- a/crates/arpa-node/build.rs +++ b/crates/arpa-node/build.rs @@ -5,7 +5,7 @@ const PROTO_DIR: &str = "proto"; fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=proto"); - println!("cargo:rerun-if-changed=src/mock_contracts"); + println!("cargo:rerun-if-changed=test-contract"); let mut prost_build = tonic_prost_build::Config::new(); prost_build.btree_map(["members"]); diff --git a/crates/arpa-node/src/listener/schedule_node_activation.rs b/crates/arpa-node/src/listener/schedule_node_activation.rs index a3abbac1..f66ceec3 100644 --- a/crates/arpa-node/src/listener/schedule_node_activation.rs +++ b/crates/arpa-node/src/listener/schedule_node_activation.rs @@ -277,7 +277,7 @@ mod tests { ) -> Result<(Address, Address), Box> { println!("Deploying mock contracts..."); - let node_registry = MockNodeRegistry::deploy(client.clone()).await?; + let node_registry = MockNodeRegistry::deploy(client.clone(), Address::ZERO, Address::ZERO, Address::ZERO).await?; let node_registry_address = *node_registry.address(); println!( "Node Registry contract deployed at: {}", diff --git a/crates/arpa-node/src/mock_contracts/MockAdapter.sol b/crates/arpa-node/src/mock_contracts/MockAdapter.sol deleted file mode 100644 index ea75540b..00000000 --- a/crates/arpa-node/src/mock_contracts/MockAdapter.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -contract MockAdapter { - enum RequestType { - Randomness, - RandomWords, - Shuffling - } - event RandomnessRequest( - bytes32 indexed requestId, - uint64 indexed subId, - uint32 indexed groupIndex, - RequestType requestType, - bytes params, - address sender, - uint256 seed, - uint16 requestConfirmations, - uint32 callbackGasLimit, - uint256 callbackMaxGasPrice, - uint256 estimatedPayment - ); - - event RandomnessRequestResult( - bytes32 indexed requestId, - uint32 indexed groupIndex, - address committer, - address[] participantMembers, - uint256 randomness, - uint256 payment, - uint256 flatFee, - bool success - ); - - mapping(bytes32 => bytes32) public _requestCommitments; - - function emitRandomnessRequest( - bytes32 requestId, - uint64 subId, - uint32 groupIndex, - RequestType requestType, - bytes calldata params, - address sender, - uint256 seed, - uint16 requestConfirmations, - uint32 callbackGasLimit, - uint256 callbackMaxGasPrice, - uint256 estimatedPayment - ) external { - emit RandomnessRequest( - requestId, - subId, - groupIndex, - requestType, - params, - sender, - seed, - requestConfirmations, - callbackGasLimit, - callbackMaxGasPrice, - estimatedPayment - ); - } - - function getPendingRequestCommitment(bytes32 requestId) public view returns (bytes32) { - return _requestCommitments[requestId]; - } - - function setRequestCommitment(bytes32 requestId, bytes32 commitment) public { - _requestCommitments[requestId] = commitment; - } -} diff --git a/crates/arpa-node/src/mock_contracts/MockController.sol b/crates/arpa-node/src/mock_contracts/MockController.sol deleted file mode 100644 index ed9ffb7b..00000000 --- a/crates/arpa-node/src/mock_contracts/MockController.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -contract MockController { - mapping(uint256 => Group) private groups; - address public nodeRegistryAddress; - address public adapterAddress; - - struct Member { - address nodeIdAddress; - uint256[4] partialPublicKey; - } - - struct CommitResult { - uint256 groupEpoch; - uint256[4] publicKey; - address[] disqualifiedNodes; - } - - struct CommitCache { - address[] nodeIdAddress; - CommitResult commitResult; - } - - struct Group { - uint256 index; - uint256 epoch; - uint256 size; - uint256 threshold; - Member[] members; - address[] committers; - CommitCache[] commitCacheList; - bool isStrictlyMajorityConsensusReached; - uint256[4] publicKey; - } - - event DkgTask( - uint256 indexed globalEpoch, - uint256 indexed groupIndex, - uint256 indexed groupEpoch, - uint256 size, - uint256 threshold, - address[] members, - uint256 assignmentBlockHeight, - address coordinatorAddress - ); - - constructor(address _nodeRegistryAddress) { - nodeRegistryAddress = _nodeRegistryAddress; - adapterAddress = address(0); - } - - function getControllerConfig() - external - view - returns ( - address nodeRegistryContractAddress, - address adapterContractAddress, - uint256 disqualifiedNodePenaltyAmount, - uint256 defaultNumberOfCommitters, - uint256 defaultDkgPhaseDuration, - uint256 groupMaxCapacity, - uint256 idealNumberOfGroups, - uint256 dkgPostProcessReward - ) - { - return ( - nodeRegistryAddress, - adapterAddress, - 1000, // disqualifiedNodePenaltyAmount - 5, // defaultNumberOfCommitters - 100, // defaultDkgPhaseDuration - 10, // groupMaxCapacity - 3, // idealNumberOfGroups - 100 // dkgPostProcessReward - ); - } - - function setGroup( - uint256 groupIndex, - uint256 epoch, - uint256 size, - uint256 threshold, - bool isStrictlyMajorityConsensusReached, - uint256[4] memory publicKey, - address[] memory memberAddresses - ) external { - Group storage group = groups[groupIndex]; - group.index = groupIndex; - group.epoch = epoch; - group.size = size; - group.threshold = threshold; - group.isStrictlyMajorityConsensusReached = isStrictlyMajorityConsensusReached; - group.publicKey = publicKey; - - delete group.members; - for (uint256 i = 0; i < memberAddresses.length; i++) { - uint256[4] memory emptyPartialPublicKey; - group.members.push(Member({nodeIdAddress: memberAddresses[i], partialPublicKey: emptyPartialPublicKey})); - } - - group.committers = memberAddresses; - } - - function setMemberPartialPublicKey(uint256 groupIndex, uint256 memberIndex, uint256[4] memory partialPublicKey) - external - { - groups[groupIndex].members[memberIndex].partialPublicKey = partialPublicKey; - } - - function setCommitters(uint256 groupIndex, address[] memory committerAddresses) external { - groups[groupIndex].committers = committerAddresses; - } - - function getGroup(uint256 groupIndex) public view returns (Group memory) { - return groups[groupIndex]; - } - - function emitDkgTaskEvent( - uint256 globalEpoch, - uint256 groupIndex, - uint256 groupEpoch, - uint256 size, - uint256 threshold, - address[] memory members, - uint256 assignmentBlockHeight, - address coordinatorAddress - ) external { - emit DkgTask( - globalEpoch, groupIndex, groupEpoch, size, threshold, members, assignmentBlockHeight, coordinatorAddress - ); - } - - function setAdapterAddress(address _adapterAddress) external { - adapterAddress = _adapterAddress; - } -} diff --git a/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol b/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol deleted file mode 100644 index 952c4464..00000000 --- a/crates/arpa-node/src/mock_contracts/MockNodeRegistry.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface ISignatureUtils { - struct SignatureWithSaltAndExpiry { - bytes signature; - bytes32 salt; - uint256 expiry; - } -} - -interface INodeRegistry { - struct Node { - address idAddress; - bytes dkgPublicKey; - bool isEigenlayerNode; - bool state; - uint256 pendingUntilBlock; - } - - function getNode(address nodeAddress) external view returns (Node memory); - function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) external; - function getNodeRegistryConfig() external view returns ( - address controllerContractAddress, - address stakingContractAddress, - address serviceManagerContractAddress, - uint256 nativeNodeStakingAmount, - uint256 eigenlayerNodeStakingAmount, - uint256 pendingBlockAfterQuit - ); -} - -contract MockAVSDirectory { - bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH = - keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)"); - - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); - - function calculateOperatorAVSRegistrationDigestHash( - address operator, - address avs, - bytes32 salt, - uint256 expiry - ) public view returns (bytes32) { - return _calculateSignableDigest( - keccak256(abi.encode(OPERATOR_AVS_REGISTRATION_TYPEHASH, operator, avs, salt, expiry)) - ); - } - - function _calculateSignableDigest(bytes32 structHash) internal view returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash)); - } - - function _domainSeparator() internal view returns (bytes32) { - return keccak256(abi.encode( - _TYPE_HASH, - keccak256("MockAVSDirectory"), - block.chainid, - address(this) - )); - } -} - -contract MockServiceManager { - address public avsDirectory; - - constructor(address _avsDirectory) { - avsDirectory = _avsDirectory; - } -} - -contract MockNodeRegistry is INodeRegistry { - struct Config { - address controllerContractAddress; - address stakingContractAddress; - address serviceManagerContractAddress; - uint256 nativeNodeStakingAmount; - uint256 eigenlayerNodeStakingAmount; - uint256 pendingBlockAfterQuit; - } - - mapping(address => Node) public nodes; - Config private _config; - - event NodeActivated(address indexed nodeAddress, uint256 groupIndex); - - error NodeNotRegistered(); - error NodeAlreadyActive(); - error NodeStillPending(uint256 pendingUntilBlock); - - constructor( - address _controllerAddress, - address _stakingAddress, - address _serviceManagerAddress - ) { - _config = Config({ - controllerContractAddress: _controllerAddress, - stakingContractAddress: _stakingAddress, - serviceManagerContractAddress: _serviceManagerAddress, - nativeNodeStakingAmount: 100 ether, - eigenlayerNodeStakingAmount: 1000 ether, - pendingBlockAfterQuit: 1000 - }); - } - - function registerNode(address idAddress, bytes memory dkgPublicKey, bool isEigenlayerNode) external { - nodes[idAddress] = Node({ - idAddress: idAddress, - dkgPublicKey: dkgPublicKey, - isEigenlayerNode: isEigenlayerNode, - state: false, - pendingUntilBlock: 0 - }); - } - - function setNodeState(address idAddress, bool state) external { - nodes[idAddress].state = state; - } - - function getNode(address nodeAddress) public view override(INodeRegistry) returns (Node memory) { - return nodes[nodeAddress]; - } - - function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) - external - override(INodeRegistry) - { - Node storage node = nodes[msg.sender]; - - if (node.idAddress != msg.sender) { - revert NodeNotRegistered(); - } - - if (node.state) { - revert NodeAlreadyActive(); - } - - if (node.pendingUntilBlock > block.number) { - revert NodeStillPending(node.pendingUntilBlock); - } - - node.state = true; - - uint256 groupIndex = 1; - - emit NodeActivated(msg.sender, groupIndex); - } - - function getNodeRegistryConfig() - public - view - override(INodeRegistry) - returns ( - address controllerContractAddress, - address stakingContractAddress, - address serviceManagerContractAddress, - uint256 nativeNodeStakingAmount, - uint256 eigenlayerNodeStakingAmount, - uint256 pendingBlockAfterQuit - ) - { - return ( - _config.controllerContractAddress, - _config.stakingContractAddress, - _config.serviceManagerContractAddress, - _config.nativeNodeStakingAmount, - _config.eigenlayerNodeStakingAmount, - _config.pendingBlockAfterQuit - ); - } -} \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/block.rs b/crates/arpa-node/src/subscriber/block.rs index 977dc9f0..1db8409f 100644 --- a/crates/arpa-node/src/subscriber/block.rs +++ b/crates/arpa-node/src/subscriber/block.rs @@ -74,13 +74,13 @@ mod tests { #[derive(Debug)] struct MockBlockInfoHandler { - chain_id: usize, + chain_id: u64, block_height: Arc>, block_time: usize, } impl MockBlockInfoHandler { - fn new(chain_id: usize, initial_block_height: usize, block_time: usize) -> Self { + fn new(chain_id: u64, initial_block_height: usize, block_time: usize) -> Self { Self { chain_id, block_height: Arc::new(RwLock::new(initial_block_height)), @@ -115,7 +115,7 @@ mod tests { #[tokio::test] async fn test_block_subscriber_creation() { - let chain_id = 1; + let chain_id = 1u64; let block_cache = Arc::new(RwLock::new( Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box )); @@ -128,7 +128,7 @@ mod tests { #[tokio::test] async fn test_notify_updates_block_height() { - let chain_id = 1; + let chain_id = 1u64; let initial_height = 100; let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); let block_cache = Arc::new(RwLock::new( @@ -154,7 +154,7 @@ mod tests { #[tokio::test] async fn test_notify_with_different_chain_id() { - let chain_id = 1; + let chain_id = 1u64; let different_chain_id = 2; let initial_height = 100; let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); @@ -175,7 +175,7 @@ mod tests { #[tokio::test] async fn test_subscribe_registers_with_event_queue() { - let chain_id = 1; + let chain_id = 1u64; let block_cache = Arc::new(RwLock::new( Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box )); @@ -187,7 +187,7 @@ mod tests { #[tokio::test] async fn test_multiple_notify_calls() { - let chain_id = 1; + let chain_id = 1u64; let initial_height = 100; let mock_handler = MockBlockInfoHandler::new(chain_id, initial_height, 12); let block_cache = Arc::new(RwLock::new( @@ -216,7 +216,7 @@ mod tests { #[tokio::test] async fn test_debuggable_subscriber_trait() { - let chain_id = 1; + let chain_id = 1u64; let block_cache = Arc::new(RwLock::new( Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box )); @@ -229,7 +229,7 @@ mod tests { #[tokio::test] #[should_panic] async fn test_notify_with_wrong_event_type() { - let chain_id = 1; + let chain_id = 1u64; let block_cache = Arc::new(RwLock::new( Box::new(MockBlockInfoHandler::new(chain_id, 100, 12)) as Box )); @@ -263,7 +263,7 @@ mod tests { #[tokio::test] async fn test_block_subscriber_integration() { - let chain_id = 1; + let chain_id = 1u64; let mock_handler = MockBlockInfoHandler::new(chain_id, 0, 12); let block_cache = Arc::new(RwLock::new( diff --git a/crates/arpa-node/src/subscriber/in_grouping.rs b/crates/arpa-node/src/subscriber/in_grouping.rs index 989e37d0..5baabe3d 100644 --- a/crates/arpa-node/src/subscriber/in_grouping.rs +++ b/crates/arpa-node/src/subscriber/in_grouping.rs @@ -359,8 +359,6 @@ impl Subscriber for InGroup let cache_epoch = group_cache.clone().read().await.get_epoch().unwrap_or(0); cache_index != task_group_index || cache_epoch != task_epoch - //NodeError::GroupIndexObsolete(cache_index) - //NodeError::GroupEpochObsolete(cache_epoch) } }, 2000, @@ -389,70 +387,109 @@ mod tests { use crate::{ event::{run_dkg::RunDKG, types::Topic}, queue::event_queue::EventQueue, - scheduler::dynamic::SimpleDynamicTaskScheduler, - test_contracts::{ - mockcontroller::{deploy_mock_controller_with_args, get_mock_controller_at}, - mockcoordinator::deploy_mock_coordinator_with_args - }, + scheduler::dynamic::SimpleDynamicTaskScheduler, }; - use arpa_core::{Config, DKGStatus, GeneralMainChainIdentity, DKGTask}; - use crate::test_contracts::mockcontroller::MockController; + use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::primitives::{Address, U256}; + use alloy::providers::WsConnect; + use alloy::signers::local::PrivateKeySigner; + use alloy::sol; + use arpa_core::{build_client, Config, DKGStatus, DKGTask, GeneralMainChainIdentity, ProviderClientWithSigner}; use arpa_dal::{ cache::{InMemoryGroupInfoCache, InMemoryNodeInfoCache}, GroupInfoHandler, NodeInfoHandler, }; - use ethers::prelude::*; use rand::thread_rng; use std::sync::Arc; - use threshold_bls::{schemes::bn254::G2Curve, group::Element}; + use threshold_bls::{group::Element, schemes::bn254::G2Curve}; use tokio::sync::RwLock; - const WS_ENDPOINT: &str = "ws://localhost:8545"; - const HTTP_ENDPOINT: &str = "http://localhost:8545"; - - async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { - let anvil = ethers::utils::Anvil::new().spawn(); - let ws_provider = Arc::new(Provider::::connect(anvil.ws_endpoint()).await.expect("Failed to connect to anvil")); - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let wallet = wallet.with_chain_id(anvil.chain_id()); - (anvil, ws_provider, wallet) - } - - async fn deploy_controller(ws_provider: Arc>, wallet: LocalWallet) -> Address { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let controller_address = deploy_mock_controller_with_args(client.clone(), Address::random()).await.unwrap(); - let controller_contract: MockController, LocalWallet>> = - get_mock_controller_at(controller_address, client.clone()); - controller_contract.set_should_succeed(true).send().await.unwrap().await.unwrap(); - controller_address + sol! { + #[sol(rpc)] + MockController, + "test-contract/MockController.json" } - async fn deploy_coordinator(ws_provider: Arc>, wallet: LocalWallet, threshold: u64) -> Address { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - deploy_mock_coordinator_with_args(client.clone(), U256::from(threshold)).await.unwrap() + sol! { + #[sol(rpc)] + MockCoordinator, + "test-contract/MockCoordinator.json" } - async fn create_chain_identity( + struct TestEnvironment { + _anvil: AnvilInstance, + client: ProviderClientWithSigner, + wallet: PrivateKeySigner, chain_id: u64, - wallet: LocalWallet, - ws_provider: Arc>, ws_endpoint: String, - controller_address: Address, - ) -> Arc>> { - let config = Config::default(); - let general_chain_identity = GeneralMainChainIdentity::new( - chain_id.try_into().unwrap(), - wallet.clone(), - ws_provider.clone(), - ws_endpoint, - controller_address, - Address::random(), - Address::random(), - config.get_time_limits().contract_transaction_retry_descriptor, - config.get_time_limits().contract_view_retry_descriptor, - None, - ); - Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + impl TestEnvironment { + async fn new() -> Self { + let anvil = Anvil::new().spawn(); + let ws_endpoint = anvil.ws_endpoint(); + let ws_connect = WsConnect::new(&ws_endpoint); + let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); + let chain_id = anvil.chain_id(); + let client = build_client(wallet.clone(), chain_id, ws_connect) + .await + .unwrap(); + + TestEnvironment { + _anvil: anvil, + client, + wallet, + chain_id, + ws_endpoint, + } + } + + async fn deploy_controller(&self) -> (Address, MockController::MockControllerInstance) { + let contract = MockController::deploy(self.client.clone(), Address::ZERO) + .await + .unwrap(); + + let _receipt = contract + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + (*contract.address(), contract) + } + + async fn deploy_coordinator(&self, threshold: u64) -> (Address, MockCoordinator::MockCoordinatorInstance) { + let contract = MockCoordinator::deploy(self.client.clone(), U256::from(threshold)) + .await + .unwrap(); + + (*contract.address(), contract) + } + + async fn create_chain_identity( + &self, + controller_address: Address, + ) -> Arc>> { + let config = Config::default(); + let ws_connect = WsConnect::new(&self.ws_endpoint); + let general_chain_identity = GeneralMainChainIdentity::new( + self.chain_id.try_into().unwrap(), + self.wallet.clone(), + ws_connect, + self.client.clone(), + self.ws_endpoint.clone(), + controller_address, + Address::ZERO, + Address::ZERO, + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } } async fn setup_node_cache(id_address: Address) -> NodeResult>>>> { @@ -461,7 +498,7 @@ mod tests { )); { let mut node_cache_write = node_cache.write().await; - node_cache_write.set_node_rpc_endpoint(HTTP_ENDPOINT.to_string()).await?; + node_cache_write.set_node_rpc_endpoint("http://localhost:8545".to_string()).await?; let private_key = ::Scalar::rand(&mut thread_rng()); let mut public_key = ::Point::one(); public_key.mul(&private_key); @@ -485,14 +522,14 @@ mod tests { )); { let mut group_cache_write = group_cache.write().await; - let dkg_task = arpa_core::DKGTask { + let dkg_task = DKGTask { group_index, epoch, size, threshold, assignment_block_height: 100, members: member_addresses.clone(), - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; group_cache_write.save_task_info(chain_id, dkg_task).await?; group_cache_write.update_dkg_status(group_index, epoch, dkg_status).await?; @@ -509,14 +546,14 @@ mod tests { #[tokio::test] async fn test_in_grouping_subscriber_creation() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, _) = env.deploy_controller().await; + let chain_identity = env.create_chain_identity(controller_address).await; + let id_address = Address::ZERO; let node_cache = setup_node_cache(id_address).await.unwrap(); let group_cache = setup_group_cache( - id_address, 1, 1, 1, 3, 2, - vec![id_address, Address::random(), Address::random()], + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::InPhase, ).await.unwrap(); let (eq, ts) = create_schedulers(); @@ -526,15 +563,15 @@ mod tests { #[tokio::test] async fn test_dkg_handler_creation() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; - let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, _) = env.deploy_controller().await; + let (coordinator_address, _) = env.deploy_coordinator(2).await; + let chain_identity = env.create_chain_identity(controller_address).await; + let id_address = Address::ZERO; let node_cache = setup_node_cache(id_address).await.unwrap(); let group_cache = setup_group_cache( - id_address, 1, 1, 1, 3, 2, - vec![id_address, Address::random(), Address::random()], + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::InPhase, ).await.unwrap(); let handler = AllInOneDKGHandler::new(|| thread_rng(), chain_identity, node_cache, group_cache.clone(), 1000); @@ -543,7 +580,7 @@ mod tests { epoch: 1, size: 3, threshold: 2, - members: vec![id_address, Address::random(), Address::random()], + members: vec![id_address, Address::ZERO, Address::ZERO], assignment_block_height: 100, coordinator_address, }; @@ -556,15 +593,15 @@ mod tests { #[tokio::test] async fn test_subscriber_notify() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; - let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, _) = env.deploy_controller().await; + let (coordinator_address, _) = env.deploy_coordinator(2).await; + let chain_identity = env.create_chain_identity(controller_address).await; + let id_address = Address::ZERO; let node_cache = setup_node_cache(id_address).await.unwrap(); let group_cache = setup_group_cache( - id_address, 1, 1, 1, 3, 2, - vec![id_address, Address::random(), Address::random()], + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::InPhase, ).await.unwrap(); let (eq, ts) = create_schedulers(); @@ -574,7 +611,7 @@ mod tests { epoch: 1, size: 3, threshold: 2, - members: vec![id_address, Address::random(), Address::random()], + members: vec![id_address, Address::ZERO, Address::ZERO], assignment_block_height: 100, coordinator_address, }; @@ -585,27 +622,27 @@ mod tests { #[tokio::test] async fn test_subscriber_subscribe() { - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let controller_address = Address::ZERO; + let chain_identity = env.create_chain_identity(controller_address).await; + let id_address = Address::ZERO; let node_cache = setup_node_cache(id_address).await.unwrap(); let group_cache = setup_group_cache( - id_address, 1, 1, 1, 3, 2, - vec![id_address, Address::random(), Address::random()], + id_address, 1, 1, 1, 3, 2, + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::InPhase, ).await.unwrap(); let (eq, ts) = create_schedulers(); - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let controller_address = Address::random(); - let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; let subscriber = InGroupingSubscriber::new(chain_identity, node_cache, group_cache, eq.clone(), ts, 1000); subscriber.subscribe().await; } #[tokio::test] async fn test_group_cache_operations() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = setup_group_cache( - id_address, 1, 2, 2, 5, 3, - vec![id_address, Address::random(), Address::random(), Address::random(), Address::random()], + id_address, 1, 2, 2, 5, 3, + vec![id_address, Address::ZERO, Address::ZERO, Address::ZERO, Address::ZERO], DKGStatus::InPhase, ).await.unwrap(); { @@ -630,12 +667,12 @@ mod tests { #[tokio::test] async fn test_node_cache_operations() { - let id_address = Address::random(); + let id_address = Address::ZERO; let node_cache = setup_node_cache(id_address).await.unwrap(); { let node_cache_read = node_cache.read().await; assert_eq!(node_cache_read.get_id_address().unwrap(), id_address); - assert_eq!(node_cache_read.get_node_rpc_endpoint().unwrap(), HTTP_ENDPOINT); + assert_eq!(node_cache_read.get_node_rpc_endpoint().unwrap(), "http://localhost:8545"); assert!(node_cache_read.get_dkg_private_key().is_ok()); assert!(node_cache_read.get_dkg_public_key().is_ok()); } @@ -653,12 +690,12 @@ mod tests { #[tokio::test] async fn test_dkg_task_with_different_statuses() { - let id_address = Address::random(); + let id_address = Address::ZERO; let statuses = vec![DKGStatus::None, DKGStatus::InPhase, DKGStatus::WaitForPostProcess, DKGStatus::CommitSuccess]; for (i, status) in statuses.iter().enumerate() { let group_cache = setup_group_cache( - id_address, 1, i + 1, 1, 3, 2, - vec![id_address, Address::random(), Address::random()], + id_address, 1, i + 1, 1, 3, 2, + vec![id_address, Address::ZERO, Address::ZERO], *status, ).await.unwrap(); let group_cache_read = group_cache.read().await; @@ -669,10 +706,16 @@ mod tests { #[tokio::test] async fn test_multiple_group_members() { - let id_address = Address::random(); - let member_addresses = vec![id_address, Address::random(), Address::random(), Address::random(), Address::random()]; + let id_address = Address::ZERO; + let member_addresses = vec![ + id_address, + Address::from([1u8; 20]), + Address::from([2u8; 20]), + Address::from([3u8; 20]), + Address::from([4u8; 20]) + ]; let group_cache = setup_group_cache( - id_address, 1, 1, 1, 5, 3, + id_address, 1, 1, 1, 5, 3, member_addresses.clone(), DKGStatus::InPhase, ).await.unwrap(); @@ -686,12 +729,12 @@ mod tests { #[tokio::test] async fn test_contract_integration() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let controller_address = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let coordinator_address = deploy_coordinator(ws_provider.clone(), wallet.clone(), 2).await; - let chain_identity = create_chain_identity(1, wallet, ws_provider, WS_ENDPOINT.to_string(), controller_address).await; - assert_ne!(controller_address, Address::zero()); - assert_ne!(coordinator_address, Address::zero()); + let env = TestEnvironment::new().await; + let (controller_address, _) = env.deploy_controller().await; + let (coordinator_address, _) = env.deploy_coordinator(2).await; + let chain_identity = env.create_chain_identity(controller_address).await; + assert_ne!(controller_address, Address::ZERO); + assert_ne!(coordinator_address, Address::ZERO); let chain_identity_read = chain_identity.read().await; chain_identity_read.build_controller_client(); chain_identity_read.build_coordinator_client(coordinator_address); diff --git a/crates/arpa-node/src/subscriber/post_grouping.rs b/crates/arpa-node/src/subscriber/post_grouping.rs index 41fdf554..a82e4fe1 100644 --- a/crates/arpa-node/src/subscriber/post_grouping.rs +++ b/crates/arpa-node/src/subscriber/post_grouping.rs @@ -93,7 +93,6 @@ impl DKGPostProcessHandler let chain_id = self.chain_identity.read().await.get_chain_id(); - // sync up the members in the group if !self .group_cache .write() @@ -234,91 +233,109 @@ mod tests { event::{dkg_post_process::DKGPostProcess, types::Topic}, queue::event_queue::EventQueue, scheduler::dynamic::SimpleDynamicTaskScheduler, - test_contracts::{ - mockcontroller::{deploy_mock_controller_with_args, get_mock_controller_at}, - mockcontrollerrelayer::{deploy_mock_controller_relayer, get_mock_controller_relayer_at} - }, }; + use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::primitives::{Address, U256}; + use alloy::providers::WsConnect; + use alloy::signers::local::PrivateKeySigner; + use alloy::sol; use arpa_core::{ - Config, DKGStatus, GeneralMainChainIdentity, Group, Member, PLACEHOLDER_ADDRESS + build_client, Config, DKGStatus, GeneralMainChainIdentity, Group, Member, + ProviderClientWithSigner, PLACEHOLDER_ADDRESS }; - use crate::test_contracts::{ - mockcontroller::MockController, - mockcontrollerrelayer::MockControllerRelayer - }; - use arpa_dal::{ - cache::InMemoryGroupInfoCache, - GroupInfoHandler, - }; - use ethers::prelude::*; + use arpa_dal::{cache::InMemoryGroupInfoCache, GroupInfoHandler}; use std::{collections::BTreeMap, sync::Arc}; use threshold_bls::schemes::bn254::G2Curve; use tokio::sync::RwLock; - const DEFAULT_CHAIN_ID: u64 = 1; const DEFAULT_GROUP_INDEX: usize = 1; const DEFAULT_EPOCH: usize = 1; const DEFAULT_GROUP_SIZE: usize = 3; const DEFAULT_THRESHOLD: usize = 2; const DEFAULT_BLOCK_HEIGHT: usize = 100; const DEFAULT_RPC_ENDPOINT: &str = "http://localhost:8545"; - const DEFAULT_WS_ENDPOINT: &str = "ws://localhost:8545"; - const DEFAULT_SUPPORTED_CHAINS: [usize; 2] = [2, 3]; + const DEFAULT_SUPPORTED_CHAINS: [u64; 2] = [2, 3]; - async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { - let anvil = ethers::utils::Anvil::new().spawn(); - let ws_provider = Arc::new( - Provider::::connect(anvil.ws_endpoint()) - .await - .expect("Failed to connect to anvil") - ); - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let wallet = wallet.with_chain_id(anvil.chain_id()); - (anvil, ws_provider, wallet) + sol! { + #[sol(ignore_unlinked)] + #[sol(rpc)] + MockController, + "test-contract/MockController.json" } - async fn deploy_controller( - ws_provider: Arc>, - wallet: LocalWallet - ) -> (Address, MockController, LocalWallet>>) { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let controller_address = deploy_mock_controller_with_args(client.clone(), Address::random()).await.unwrap(); - let controller_contract = get_mock_controller_at(controller_address, client.clone()); - (controller_address, controller_contract) + sol! { + #[sol(ignore_unlinked)] + #[sol(rpc)] + MockControllerRelayer, + "test-contract/MockControllerRelayer.json" } - async fn deploy_controller_relayer( - ws_provider: Arc>, - wallet: LocalWallet - ) -> (Address, MockControllerRelayer, LocalWallet>>) { - let client = Arc::new(SignerMiddleware::new((*ws_provider).clone(), wallet.clone())); - let relayer_address = deploy_mock_controller_relayer(client.clone()).await.unwrap(); - let relayer_contract = get_mock_controller_relayer_at(relayer_address, client.clone()); - (relayer_address, relayer_contract) - } - - async fn create_chain_identity( + struct TestEnvironment { + _anvil: AnvilInstance, + client: ProviderClientWithSigner, + wallet: PrivateKeySigner, chain_id: u64, - wallet: LocalWallet, - ws_provider: Arc>, ws_endpoint: String, - controller_address: Address, - relayer_address: Address, - ) -> Arc>> { - let config = Config::default(); - let general_chain_identity = GeneralMainChainIdentity::new( - chain_id.try_into().unwrap(), - wallet.clone(), - ws_provider.clone(), - ws_endpoint, - controller_address, - relayer_address, - Address::random(), - config.get_time_limits().contract_transaction_retry_descriptor, - config.get_time_limits().contract_view_retry_descriptor, - None, - ); - Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + impl TestEnvironment { + async fn new() -> Self { + let anvil = Anvil::new().spawn(); + let ws_endpoint = anvil.ws_endpoint(); + let ws_connect = WsConnect::new(&ws_endpoint); + let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); + let chain_id = anvil.chain_id(); + let client = build_client(wallet.clone(), chain_id, ws_connect) + .await + .unwrap(); + + TestEnvironment { + _anvil: anvil, + client, + wallet, + chain_id, + ws_endpoint, + } + } + + async fn deploy_mock_controller(&self) -> (Address, MockController::MockControllerInstance) { + let contract = MockController::deploy(self.client.clone(), Address::ZERO) + .await + .unwrap(); + (*contract.address(), contract) + } + + async fn deploy_mock_controller_relayer(&self) -> (Address, MockControllerRelayer::MockControllerRelayerInstance) { + let contract = MockControllerRelayer::deploy(self.client.clone()) + .await + .unwrap(); + (*contract.address(), contract) + } + + async fn create_chain_identity( + &self, + controller_address: Address, + relayer_address: Address, + ) -> Arc>> { + let config = Config::default(); + let ws_connect = WsConnect::new(&self.ws_endpoint); + let general_chain_identity = GeneralMainChainIdentity::new( + self.chain_id.try_into().unwrap(), + self.wallet.clone(), + ws_connect, + self.client.clone(), + self.ws_endpoint.clone(), + controller_address, + relayer_address, + Address::ZERO, + config + .get_time_limits() + .contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } } async fn setup_group_cache( @@ -344,11 +361,13 @@ mod tests { threshold, assignment_block_height: DEFAULT_BLOCK_HEIGHT, members: member_addresses.clone(), - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; group_cache_write.save_task_info(0, dkg_task).await?; - group_cache_write.update_dkg_status(group_index, epoch, dkg_status).await?; + group_cache_write + .update_dkg_status(group_index, epoch, dkg_status) + .await?; let mut group = Group:: { index: group_index, @@ -363,43 +382,54 @@ mod tests { }; for (i, addr) in member_addresses.iter().enumerate() { - group.members.insert(*addr, Member { - index: i, - dkg_index: Some(i), - id_address: *addr, - rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), - partial_public_key: None, - }); + group.members.insert( + *addr, + Member { + index: i, + dkg_index: Some(i), + id_address: *addr, + rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), + partial_public_key: None, + }, + ); } - group_cache_write.sync_up_members(group_index, epoch, group.members).await?; + group_cache_write + .sync_up_members(group_index, epoch, group.members) + .await?; } Ok(group_cache) } - fn create_schedulers() -> (Arc>, Arc>) { + fn create_schedulers() -> ( + Arc>, + Arc>, + ) { ( Arc::new(RwLock::new(EventQueue::new())), - Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())) + Arc::new(RwLock::new(SimpleDynamicTaskScheduler::new())), ) } fn create_test_group( - group_index: usize, - epoch: usize, - state: bool, - member_addresses: Vec
+ group_index: usize, + epoch: usize, + state: bool, + member_addresses: Vec
, ) -> Group { let mut members = BTreeMap::new(); for (i, addr) in member_addresses.iter().enumerate() { - members.insert(*addr, Member { - index: i, - dkg_index: Some(i), - id_address: *addr, - rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), - partial_public_key: None, - }); + members.insert( + *addr, + Member { + index: i, + dkg_index: Some(i), + id_address: *addr, + rpc_endpoint: Some(DEFAULT_RPC_ENDPOINT.to_string()), + partial_public_key: None, + }, + ); } Group { @@ -420,30 +450,27 @@ mod tests { Arc>>>, Address, ) { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, _controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, _controller) = env.deploy_mock_controller().await; + let (relayer_address, _relayer) = env.deploy_mock_controller_relayer().await; + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); (chain_identity, group_cache, id_address) } @@ -482,32 +509,36 @@ mod tests { #[tokio::test] async fn test_post_process_handler_with_no_coordinator() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), PLACEHOLDER_ADDRESS).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, controller) = env.deploy_mock_controller().await; + let (relayer_address, _relayer) = env.deploy_mock_controller_relayer().await; + + controller + .setCoordinator(U256::from(DEFAULT_GROUP_INDEX), PLACEHOLDER_ADDRESS) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); let handler = GeneralDKGPostProcessHandler { chain_identity, @@ -516,42 +547,69 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_post_process_handler_with_coordinator_success() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let coordinator_addr = Address::random(); - controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); - controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); - relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, controller) = env.deploy_mock_controller().await; + let (relayer_address, relayer) = env.deploy_mock_controller_relayer().await; + + let coordinator_addr = Address::ZERO; + controller + .setCoordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + controller + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + relayer + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); let handler = GeneralDKGPostProcessHandler { chain_identity, @@ -560,42 +618,69 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_post_process_handler_with_inactive_group() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let coordinator_addr = Address::random(); - controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); - controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); - relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, controller) = env.deploy_mock_controller().await; + let (relayer_address, relayer) = env.deploy_mock_controller_relayer().await; + + let coordinator_addr = Address::ZERO; + controller + .setCoordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + controller + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + relayer + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, false, - ).await.unwrap(); + ) + .await + .unwrap(); let handler = GeneralDKGPostProcessHandler { chain_identity, @@ -604,44 +689,87 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, false, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + false, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_post_process_handler_with_contract_failure() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let coordinator_addr = Address::random(); - controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); - controller.set_should_succeed(false).send().await.unwrap().await.unwrap(); - controller.set_failure_message("Controller failure".to_string()).send().await.unwrap().await.unwrap(); - relayer.set_should_succeed(false).send().await.unwrap().await.unwrap(); - relayer.set_failure_message("Relayer failure".to_string()).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, controller) = env.deploy_mock_controller().await; + let (relayer_address, relayer) = env.deploy_mock_controller_relayer().await; + + let coordinator_addr = Address::ZERO; + controller + .setCoordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + controller + .setShouldSucceed(false) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + controller + .setFailureMessage("Controller failure".to_string()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + relayer + .setShouldSucceed(false) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + relayer + .setFailureMessage("Relayer failure".to_string()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); let handler = GeneralDKGPostProcessHandler { chain_identity, @@ -650,8 +778,15 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } @@ -668,41 +803,45 @@ mod tests { ts, ); - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); let post_process_event = DKGPostProcess { group_index: DEFAULT_GROUP_INDEX, group_epoch: DEFAULT_EPOCH, group, }; - let result = subscriber.notify(Topic::DKGPostProcess, &post_process_event).await; + let result = subscriber + .notify(Topic::DKGPostProcess, &post_process_event) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_subscriber_subscribe() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); let (eq, ts) = create_schedulers(); - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - Address::random(), - Address::random(), - ).await; + let env = TestEnvironment::new().await; + let chain_identity = env + .create_chain_identity(Address::ZERO, Address::ZERO) + .await; let subscriber = PostGroupingSubscriber::new( chain_identity, @@ -717,35 +856,55 @@ mod tests { #[tokio::test] async fn test_multiple_relayed_chains() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let coordinator_addr = Address::random(); - controller.set_coordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr).send().await.unwrap().await.unwrap(); - controller.set_should_succeed(true).send().await.unwrap().await.unwrap(); - relayer.set_should_succeed(true).send().await.unwrap().await.unwrap(); - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, controller) = env.deploy_mock_controller().await; + let (relayer_address, relayer) = env.deploy_mock_controller_relayer().await; + + let coordinator_addr = Address::ZERO; + controller + .setCoordinator(U256::from(DEFAULT_GROUP_INDEX), coordinator_addr) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + controller + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + relayer + .setShouldSucceed(true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::WaitForPostProcess, true, - ).await.unwrap(); + ) + .await + .unwrap(); let multiple_chains = vec![2, 3, 4, 5, 6]; let handler = GeneralDKGPostProcessHandler { @@ -755,37 +914,41 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } #[tokio::test] async fn test_dkg_status_update_failure() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let (controller_address, _controller) = deploy_controller(ws_provider.clone(), wallet.clone()).await; - let (relayer_address, _relayer) = deploy_controller_relayer(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - DEFAULT_CHAIN_ID, - wallet, - ws_provider, - DEFAULT_WS_ENDPOINT.to_string(), - controller_address, - relayer_address, - ).await; - - let id_address = Address::random(); + let env = TestEnvironment::new().await; + let (controller_address, _controller) = env.deploy_mock_controller().await; + let (relayer_address, _relayer) = env.deploy_mock_controller_relayer().await; + + let chain_identity = env + .create_chain_identity(controller_address, relayer_address) + .await; + + let id_address = Address::ZERO; let group_cache = setup_group_cache( id_address, DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], DKGStatus::None, true, - ).await.unwrap(); + ) + .await + .unwrap(); let handler = GeneralDKGPostProcessHandler { chain_identity, @@ -794,8 +957,15 @@ mod tests { c: PhantomData, }; - let group = create_test_group(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, true, vec![id_address, Address::random(), Address::random()]); - let result = handler.handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group).await; + let group = create_test_group( + DEFAULT_GROUP_INDEX, + DEFAULT_EPOCH, + true, + vec![id_address, Address::ZERO, Address::ZERO], + ); + let result = handler + .handle(DEFAULT_GROUP_INDEX, DEFAULT_EPOCH, group) + .await; assert!(result.is_ok()); } } \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/post_success_grouping.rs b/crates/arpa-node/src/subscriber/post_success_grouping.rs index d9fbc9d3..18249523 100644 --- a/crates/arpa-node/src/subscriber/post_success_grouping.rs +++ b/crates/arpa-node/src/subscriber/post_success_grouping.rs @@ -151,8 +151,8 @@ mod tests { queue::event_queue::EventQueue, }; use arpa_core::{Group, Member, DKGTask}; + use alloy::primitives::{Address}; use arpa_dal::{GroupInfoHandler, cache::InMemoryGroupInfoCache}; - use ethers_core::types::Address; use std::{ any::Any, collections::BTreeMap, @@ -194,7 +194,7 @@ mod tests { } fn create_test_dkg_success( - chain_id: usize, + chain_id: u64, id_address: Address, has_public_key: bool, ) -> DKGSuccess { @@ -227,7 +227,7 @@ mod tests { threshold: group.threshold, members: group.members.keys().copied().collect(), assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; cache.save_task_info(TEST_CHAIN_ID, dkg_task).await.unwrap(); } @@ -236,7 +236,7 @@ mod tests { #[tokio::test] async fn test_post_success_grouping_subscriber_creation() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); @@ -245,7 +245,7 @@ mod tests { #[tokio::test] async fn test_notify_successful_flow() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); @@ -258,7 +258,7 @@ mod tests { threshold: TEST_THRESHOLD, members: vec![id_address], assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; cache.save_task_info(TEST_CHAIN_ID, dkg_task).await.unwrap(); } @@ -275,8 +275,8 @@ mod tests { #[tokio::test] async fn test_notify_node_not_in_group_error() { - let id_address = Address::random(); - let different_address = Address::random(); + let id_address = Address::ZERO; + let different_address = Address::ZERO; let group = create_test_group(id_address, true); let group_cache = setup_group_cache_with_group(id_address, group).await; let eq = create_event_queue(); @@ -293,7 +293,7 @@ mod tests { #[tokio::test] async fn test_notify_different_public_key_error() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group = create_test_group(id_address, true); let group_cache = setup_group_cache_with_group(id_address, group).await; let eq = create_event_queue(); @@ -310,7 +310,7 @@ mod tests { #[tokio::test] async fn test_subscribe() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq.clone()); @@ -319,7 +319,7 @@ mod tests { #[tokio::test] async fn test_debuggable_subscriber_trait() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); @@ -331,7 +331,7 @@ mod tests { #[tokio::test] #[should_panic] async fn test_notify_with_wrong_event_type() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); @@ -357,7 +357,7 @@ mod tests { #[tokio::test] async fn test_multiple_notifications() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); @@ -371,7 +371,7 @@ mod tests { #[tokio::test] async fn test_with_different_chain_ids() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); let subscriber = PostSuccessGroupingSubscriber::new(group_cache, eq); @@ -390,7 +390,7 @@ mod tests { #[tokio::test] async fn test_full_workflow() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = create_group_cache(id_address); let eq = create_event_queue(); diff --git a/crates/arpa-node/src/subscriber/pre_grouping.rs b/crates/arpa-node/src/subscriber/pre_grouping.rs index cb908e09..a2a1fa97 100644 --- a/crates/arpa-node/src/subscriber/pre_grouping.rs +++ b/crates/arpa-node/src/subscriber/pre_grouping.rs @@ -120,12 +120,12 @@ mod tests { }; use arpa_core::DKGTask; use arpa_dal::{GroupInfoHandler, cache::InMemoryGroupInfoCache}; - use ethers_core::types::Address; + use alloy::primitives::{Address}; use std::{any::Any, sync::Arc}; use threshold_bls::schemes::bn254::G2Curve; use tokio::sync::RwLock; - const CHAIN_ID: usize = 1; + const CHAIN_ID: u64 = 1; const GROUP_SIZE: usize = 3; const THRESHOLD: usize = 2; const ASSIGNMENT_BLOCK_HEIGHT: usize = 100; @@ -137,9 +137,9 @@ mod tests { epoch, size: GROUP_SIZE, threshold: THRESHOLD, - members: vec![Address::random(), Address::random(), Address::random()], + members: vec![Address::ZERO, Address::ZERO, Address::ZERO], assignment_block_height: ASSIGNMENT_BLOCK_HEIGHT, - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, } } @@ -152,7 +152,7 @@ mod tests { } fn create_subscriber() -> (PreGroupingSubscriber, Arc>) { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache: Arc>>> = Arc::new(RwLock::new( Box::new(InMemoryGroupInfoCache::::new(id_address)), )); diff --git a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs index 401424e2..f80327f6 100644 --- a/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs +++ b/crates/arpa-node/src/subscriber/randomness_signature_aggregation.rs @@ -422,21 +422,27 @@ mod tests { event::{ready_to_fulfill_randomness_task::ReadyToFulfillRandomnessTask, types::Topic}, queue::event_queue::EventQueue, scheduler::dynamic::SimpleDynamicTaskScheduler, - test_contracts::mockadapter::{deploy_mock_adapter, get_mock_adapter_at}, }; - use arpa_core::{Config, GeneralMainChainIdentity, RandomnessTask, PartialSignature}; + use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::primitives::{Address, U256, FixedBytes}; + use alloy::providers::WsConnect; + use alloy::signers::local::PrivateKeySigner; + use alloy::sol; + use arpa_core::{ + build_client, Config, GeneralMainChainIdentity, RandomnessTask, PartialSignature, + ProviderClientWithSigner, + }; use arpa_dal::{ - cache::{InMemoryBlockInfoCache, InMemorySignatureResultCache}, - BlockInfoHandler, BlockInfoUpdater, SignatureResultCacheHandler + cache::{InMemoryBlockInfoCache, InMemorySignatureResultCache}, + BlockInfoHandler, BlockInfoUpdater, SignatureResultCacheHandler, }; - use ethers::prelude::*; use std::{collections::BTreeMap, sync::Arc}; use threshold_bls::{poly::Eval, schemes::bn254::{G2Curve, G2Scheme}}; use tokio::sync::RwLock; const TEST_CHAIN_ID: u64 = 1; const TEST_BLOCK_HEIGHT: usize = 1000; - const TEST_BLOCK_TIME: usize = 12; + const TEST_BLOCK_TIME: usize = 1000; const TEST_SUBSCRIPTION_ID: u64 = 1; const TEST_GROUP_INDEX: u32 = 1; const TEST_SEED: usize = 12345; @@ -450,49 +456,73 @@ mod tests { const SIGNATURE_SIZE: usize = 32; const RANDOMNESS_SIZE: usize = 96; const NUM_SIGNERS: usize = 3; - const WS_ENDPOINT: &str = "ws://localhost:8545"; - - async fn setup_anvil() -> (ethers::utils::AnvilInstance, Arc>, LocalWallet) { - let anvil = ethers::utils::Anvil::new().spawn(); - let ws_provider = Provider::::connect(anvil.ws_endpoint()) - .await.expect("Failed to connect to anvil"); - let ws_provider = Arc::new(ws_provider); - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let wallet = wallet.with_chain_id(anvil.chain_id()); - (anvil, ws_provider, wallet) - } - async fn deploy_adapter(ws_provider: Arc>, wallet: LocalWallet) -> Address { - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - deploy_mock_adapter(client.clone()).await.unwrap() + sol! { + #[sol(rpc)] + MockAdapter, + "test-contract/MockAdapter.json" } - async fn create_chain_identity( + struct TestEnvironment { + _anvil: AnvilInstance, + client: ProviderClientWithSigner, + wallet: PrivateKeySigner, chain_id: u64, - wallet: LocalWallet, - ws_provider: Arc>, ws_endpoint: String, - adapter_address: Address, - ) -> Arc>> { - let config = Config::default(); - let general_chain_identity = GeneralMainChainIdentity::new( - chain_id.try_into().unwrap(), - wallet.clone(), - ws_provider.clone(), - ws_endpoint, - Address::random(), - Address::random(), - adapter_address, - config.get_time_limits().contract_transaction_retry_descriptor, - config.get_time_limits().contract_view_retry_descriptor, - None, - ); - Arc::new(RwLock::new(Box::new(general_chain_identity))) + } + + impl TestEnvironment { + async fn new() -> Self { + let anvil = Anvil::new().spawn(); + let ws_endpoint = anvil.ws_endpoint(); + let ws_connect = WsConnect::new(&ws_endpoint); + let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); + let chain_id = anvil.chain_id(); + let client = build_client(wallet.clone(), chain_id, ws_connect) + .await + .unwrap(); + + TestEnvironment { + _anvil: anvil, + client, + wallet, + chain_id, + ws_endpoint, + } + } + + async fn deploy_mock_adapter(&self) -> (Address, MockAdapter::MockAdapterInstance) { + let contract = MockAdapter::deploy(self.client.clone()) + .await + .unwrap(); + (*contract.address(), contract) + } + + async fn create_chain_identity( + &self, + adapter_address: Address, + ) -> Arc>> { + let config = Config::default(); + let ws_connect = WsConnect::new(&self.ws_endpoint); + let general_chain_identity = GeneralMainChainIdentity::new( + self.chain_id.try_into().unwrap(), + self.wallet.clone(), + ws_connect, + self.client.clone(), + self.ws_endpoint.clone(), + Address::ZERO, + Address::ZERO, + adapter_address, + config.get_time_limits().contract_transaction_retry_descriptor, + config.get_time_limits().contract_view_retry_descriptor, + None, + ); + Arc::new(RwLock::new(Box::new(general_chain_identity))) + } } fn create_block_cache( - chain_id: usize, + chain_id: u64, block_height: usize, block_time: usize, ) -> Arc>> { @@ -519,16 +549,16 @@ mod tests { full_request_id[..copy_len].copy_from_slice(&request_id[..copy_len]); RandomnessTask { - request_id: full_request_id, + request_id: full_request_id, subscription_id: TEST_SUBSCRIPTION_ID, group_index: TEST_GROUP_INDEX, request_type: arpa_core::RandomnessRequestType::Randomness, params: vec![], - requester: Address::random(), + requester: Address::ZERO, seed: U256::from(TEST_SEED), request_confirmations: TEST_REQUEST_CONFIRMATIONS, callback_gas_limit: TEST_CALLBACK_GAS_LIMIT, - callback_max_gas_price: U256::from(TEST_CALLBACK_MAX_GAS_PRICE), + callback_max_gas_price: TEST_CALLBACK_MAX_GAS_PRICE as u128, assignment_block_height: TEST_ASSIGNMENT_BLOCK_HEIGHT, } } @@ -537,15 +567,15 @@ mod tests { let mut partial_signatures = BTreeMap::new(); for i in 0..NUM_SIGNERS { - let addr = Address::random(); + let addr = Address::ZERO; let eval = Eval { - value: vec![i as u8; SIGNATURE_SIZE], + value: vec![i as u8; SIGNATURE_SIZE], index: i as u32, }; let serialized = bincode::serialize(&eval).unwrap(); partial_signatures.insert(addr, PartialSignature { index: i, - signed_partial_signature: serialized, + signed_partial_signature: serialized, }); } partial_signatures @@ -590,38 +620,32 @@ mod tests { #[tokio::test] async fn test_subscriber_creation() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let signature_cache = create_signature_cache(); let (eq, ts) = create_schedulers(); - let id_address = Address::random(); + let id_address = Address::ZERO; let subscriber = RandomnessSignatureAggregationSubscriber::::new( TEST_CHAIN_ID.try_into().unwrap(), id_address, chain_identity, block_cache, signature_cache, eq, ts, ); - assert_eq!(subscriber.chain_id, TEST_CHAIN_ID as usize); + assert_eq!(subscriber.chain_id, TEST_CHAIN_ID); assert_eq!(subscriber.id_address, id_address); } #[tokio::test] async fn test_fulfill_randomness_handler_creation() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let signature_cache = create_signature_cache(); - let id_address = Address::random(); + let id_address = Address::ZERO; let handler = GeneralFulfillRandomnessHandler { id_address, @@ -636,27 +660,26 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_task_not_pending() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); + let id_address = Address::ZERO; - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::zero().into()).send().await.unwrap().await.unwrap(); + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + adapter.setRequestCommitment(request_id, FixedBytes::<32>::ZERO) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -677,27 +700,28 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_task_expired() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; - - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, HIGH_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, HIGH_BLOCK_HEIGHT, TEST_BLOCK_TIME); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); + let id_address = Address::ZERO; - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + let mut commitment = [0u8; 32]; + commitment[31] = 1; + adapter.setRequestCommitment(request_id, FixedBytes::<32>::from(commitment)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -719,28 +743,29 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_gas_price_too_high() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let mut randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); - randomness_task.callback_max_gas_price = U256::from(LOW_GAS_PRICE); + let id_address = Address::ZERO; + randomness_task.callback_max_gas_price = LOW_GAS_PRICE as u128; - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + let mut commitment = [0u8; 32]; + commitment[31] = 1; + adapter.setRequestCommitment(request_id, FixedBytes::<32>::from(commitment)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -762,29 +787,44 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_success() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; - - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); + let id_address = Address::ZERO; - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); - adapter_contract.set_should_revert(request_id.into(), false).send().await.unwrap().await.unwrap(); - adapter_contract.set_should_revert_with_custom_error(request_id.into(), false).send().await.unwrap().await.unwrap(); + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + let mut commitment = [0u8; 32]; + commitment[31] = 1; + adapter.setRequestCommitment(request_id, FixedBytes::<32>::from(commitment)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + adapter.setShouldRevert(request_id, false) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + adapter.setShouldRevertWithCustomError(request_id, false) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -806,27 +846,36 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_transaction_failed() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); - adapter_contract.set_should_revert(request_id.into(), true).send().await.unwrap().await.unwrap(); + let id_address = Address::ZERO; + + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + let mut commitment = [0u8; 32]; + commitment[31] = 1; + adapter.setRequestCommitment(request_id, FixedBytes::<32>::from(commitment)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + adapter.setShouldRevert(request_id, true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -847,28 +896,36 @@ mod tests { #[tokio::test] async fn test_fulfill_randomness_handler_custom_error() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let client = SignerMiddleware::new((*ws_provider).clone(), wallet.clone()); - let client = Arc::new(client); - let adapter_contract = get_mock_adapter_at(adapter_address, client.clone()); + let env = TestEnvironment::new().await; + let (adapter_address, adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; - - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); let signature_cache = create_signature_cache_with_task( randomness_task.clone(), partial_signatures.clone(), 0, ).await; - let id_address = Address::random(); + let id_address = Address::ZERO; - let request_id = H256::from_slice(&randomness_task.request_id); - adapter_contract.set_request_commitment(request_id.into(), H256::from_low_u64_be(1).into()).send().await.unwrap().await.unwrap(); - adapter_contract.set_should_revert_with_custom_error(request_id.into(), true).send().await.unwrap().await.unwrap(); + let request_id = FixedBytes::<32>::from_slice(&randomness_task.request_id); + let mut commitment = [0u8; 32]; + commitment[31] = 1; + adapter.setRequestCommitment(request_id, FixedBytes::<32>::from(commitment)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + adapter.setShouldRevertWithCustomError(request_id, true) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let handler = GeneralFulfillRandomnessHandler { id_address, @@ -889,16 +946,13 @@ mod tests { #[tokio::test] async fn test_subscriber_notify_with_faulty_task() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let (eq, ts) = create_schedulers(); - let id_address = Address::random(); + let id_address = Address::ZERO; let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); let partial_signatures = create_test_partial_signatures(); @@ -910,7 +964,7 @@ mod tests { ).await; let subscriber = RandomnessSignatureAggregationSubscriber::::new( - TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + TEST_CHAIN_ID, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, ); let result_cache = RandomnessResultCache { @@ -919,7 +973,7 @@ mod tests { message: vec![1, 2, 3], threshold: TEST_THRESHOLD, partial_signatures, - committed_times: DEFAULT_MAX_RANDOMNESS_FULFILLMENT_ATTEMPTS, + committed_times: DEFAULT_MAX_RANDOMNESS_FULFILLMENT_ATTEMPTS, }; let event = ReadyToFulfillRandomnessTask { @@ -927,26 +981,23 @@ mod tests { tasks: vec![result_cache], }; - let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID), &event).await; assert!(result.is_ok()); } #[tokio::test] async fn test_subscriber_notify_with_valid_task() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let signature_cache = create_signature_cache(); let (eq, ts) = create_schedulers(); - let id_address = Address::random(); + let id_address = Address::ZERO; let subscriber = RandomnessSignatureAggregationSubscriber::::new( - TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + TEST_CHAIN_ID, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, ); let randomness_task = create_test_randomness_task(vec![1, 2, 3, 4]); @@ -962,30 +1013,27 @@ mod tests { }; let event = ReadyToFulfillRandomnessTask { - chain_id: TEST_CHAIN_ID as usize, + chain_id: TEST_CHAIN_ID, tasks: vec![result_cache], }; - let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID), &event).await; assert!(result.is_ok()); } #[tokio::test] async fn test_subscriber_subscribe() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let signature_cache = create_signature_cache(); let (eq, ts) = create_schedulers(); - let id_address = Address::random(); + let id_address = Address::ZERO; let subscriber = RandomnessSignatureAggregationSubscriber::::new( - TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache, eq.clone(), ts, + TEST_CHAIN_ID, id_address, chain_identity, block_cache, signature_cache, eq.clone(), ts, ); subscriber.subscribe().await; @@ -993,20 +1041,17 @@ mod tests { #[tokio::test] async fn test_subscriber_notify_with_multiple_tasks() { - let (_anvil, ws_provider, wallet) = setup_anvil().await; - let adapter_address = deploy_adapter(ws_provider.clone(), wallet.clone()).await; - - let chain_identity = create_chain_identity( - TEST_CHAIN_ID, wallet, ws_provider, WS_ENDPOINT.to_string(), adapter_address, - ).await; + let env = TestEnvironment::new().await; + let (adapter_address, _adapter) = env.deploy_mock_adapter().await; + let chain_identity = env.create_chain_identity(adapter_address).await; - let block_cache = create_block_cache(TEST_CHAIN_ID as usize, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); + let block_cache = create_block_cache(TEST_CHAIN_ID, TEST_BLOCK_HEIGHT, TEST_BLOCK_TIME); let signature_cache = create_signature_cache(); let (eq, ts) = create_schedulers(); - let id_address = Address::random(); + let id_address = Address::ZERO; let subscriber = RandomnessSignatureAggregationSubscriber::::new( - TEST_CHAIN_ID as usize, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, + TEST_CHAIN_ID, id_address, chain_identity, block_cache, signature_cache.clone(), eq, ts, ); let mut tasks = Vec::new(); @@ -1026,9 +1071,9 @@ mod tests { tasks.push(result_cache); } - let event = ReadyToFulfillRandomnessTask { chain_id: TEST_CHAIN_ID as usize, tasks }; + let event = ReadyToFulfillRandomnessTask { chain_id: TEST_CHAIN_ID, tasks }; - let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID as usize), &event).await; + let result = subscriber.notify(Topic::ReadyToFulfillRandomnessTask(TEST_CHAIN_ID), &event).await; assert!(result.is_ok()); } } \ No newline at end of file diff --git a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs index c9b6b311..462b650d 100644 --- a/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs +++ b/crates/arpa-node/src/subscriber/ready_to_handle_randomness_task.rs @@ -411,8 +411,13 @@ where mod tests { use super::*; use crate::{ - algorithm::bls::SimpleBLSCore, committer::CommitterClient, event::{ready_to_handle_randomness_task::ReadyToHandleRandomnessTask, types::Topic}, queue::event_queue::EventQueue, scheduler::dynamic::SimpleDynamicTaskScheduler + algorithm::bls::SimpleBLSCore, + committer::CommitterClient, + event::{ready_to_handle_randomness_task::ReadyToHandleRandomnessTask, types::Topic}, + queue::event_queue::EventQueue, + scheduler::dynamic::SimpleDynamicTaskScheduler }; + use alloy::primitives::{Address, U256}; use arpa_core::{ExponentialBackoffRetryDescriptor, RandomnessTask}; use arpa_dal::{ cache::{InMemoryGroupInfoCache, InMemoryBLSTasksQueue, InMemorySignatureResultCache, RandomnessResultCache}, @@ -437,7 +442,7 @@ mod tests { const DEFAULT_THRESHOLD: usize = 2; const DEFAULT_GROUP_INDEX: u32 = 1; const DEFAULT_EPOCH: usize = 1; - const DEFAULT_CHAIN_ID: usize = 1; + const DEFAULT_CHAIN_ID: u64 = 1; const DEFAULT_BLOCK_HEIGHT: usize = 100; const DEFAULT_SUBSCRIPTION_ID: u64 = 1; const DEFAULT_CONFIRMATIONS: u16 = 6; @@ -623,7 +628,7 @@ mod tests { threshold, assignment_block_height: DEFAULT_BLOCK_HEIGHT, members: member_addresses.clone(), - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; group_cache_write.save_task_info(chain_id, dkg_task).await?; @@ -695,7 +700,7 @@ mod tests { threshold, assignment_block_height: DEFAULT_BLOCK_HEIGHT, members: member_addresses.clone(), - coordinator_address: Address::random(), + coordinator_address: Address::ZERO, }; { @@ -738,11 +743,11 @@ mod tests { group_index: DEFAULT_GROUP_INDEX, request_type: arpa_core::RandomnessRequestType::Randomness, params: vec![], - requester: Address::random(), - seed: ethers::types::U256::from(DEFAULT_SEED), + requester: Address::ZERO, + seed: U256::from(DEFAULT_SEED), request_confirmations: DEFAULT_CONFIRMATIONS, callback_gas_limit: DEFAULT_GAS_LIMIT, - callback_max_gas_price: ethers::types::U256::from(DEFAULT_GAS_PRICE), + callback_max_gas_price: DEFAULT_GAS_PRICE as u128, assignment_block_height: DEFAULT_BLOCK_HEIGHT, } } @@ -756,16 +761,16 @@ mod tests { #[tokio::test] async fn test_randomness_task_subscriber_creation() { - let id_address = Address::random(); + let id_address = Address::ZERO; let group_cache = setup_group_cache_simple( id_address, - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, DEFAULT_GROUP_INDEX as usize, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], ).await.unwrap(); let randomness_tasks_cache = setup_randomness_tasks_cache(vec![]).await; @@ -795,8 +800,8 @@ mod tests { let _server_handle = start_mock_grpc_server(service.clone(), port).await; tokio::time::sleep(Duration::from_millis(100)).await; - let id_address = Address::random(); - let committer_id_address = Address::random(); + let id_address = Address::ZERO; + let committer_id_address = Address::ZERO; let server_address = format!("http://127.0.0.1:{}", port); let mock_client = MockCommitterClient::new( @@ -807,7 +812,7 @@ mod tests { let result = mock_client .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, arpa_core::BLSTaskType::Randomness, vec![1, 2, 3], vec![4, 5, 6], @@ -839,8 +844,8 @@ mod tests { let _server_handle = start_mock_grpc_server(service.clone(), port).await; tokio::time::sleep(Duration::from_millis(100)).await; - let id_address = Address::random(); - let committer_id_address = Address::random(); + let id_address = Address::ZERO; + let committer_id_address = Address::ZERO; let server_address = format!("http://127.0.0.1:{}", port); let mock_client = MockCommitterClient::new( @@ -851,7 +856,7 @@ mod tests { let result = mock_client .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, arpa_core::BLSTaskType::Randomness, vec![1, 2, 3], vec![4, 5, 6], @@ -875,8 +880,8 @@ mod tests { let _server_handle = start_mock_grpc_server(service.clone(), port).await; tokio::time::sleep(Duration::from_millis(100)).await; - let id_address = Address::random(); - let committer_id_address = Address::random(); + let id_address = Address::ZERO; + let committer_id_address = Address::ZERO; let server_address = format!("http://127.0.0.1:{}", port); let mock_client = MockCommitterClient::new( @@ -889,7 +894,7 @@ mod tests { let result = mock_client .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, arpa_core::BLSTaskType::Randomness, vec![1, 2, 3], vec![4, 5, 6], @@ -912,8 +917,8 @@ mod tests { let _server_handle = start_mock_grpc_server(service.clone(), port).await; tokio::time::sleep(Duration::from_millis(100)).await; - let id_address = Address::random(); - let committer_id_address = Address::random(); + let id_address = Address::ZERO; + let committer_id_address = Address::ZERO; let server_address = format!("http://127.0.0.1:{}", port); let mock_client = MockCommitterClient::new( @@ -931,7 +936,7 @@ mod tests { for (i, task_type) in task_types.iter().enumerate() { let result = mock_client .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, task_type.clone(), vec![i as u8], vec![i as u8 + 10], @@ -956,17 +961,17 @@ mod tests { #[tokio::test] async fn test_subscriber_notify_with_mock_grpc() { - let id_address = Address::random(); + let id_address = Address::ZERO; let tasks = vec![create_test_randomness_task()]; let group_cache = setup_group_cache_with_secret( id_address, - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, DEFAULT_GROUP_INDEX as usize, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], ).await.unwrap(); let randomness_tasks_cache = setup_randomness_tasks_cache(tasks.clone()).await; @@ -995,22 +1000,22 @@ mod tests { #[tokio::test] async fn test_partial_signature_generation() { - let id_address = Address::random(); + let id_address = Address::ZERO; let task = create_test_randomness_task(); let group_cache = setup_group_cache_with_secret( id_address, - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, DEFAULT_GROUP_INDEX as usize, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, DEFAULT_THRESHOLD, - vec![id_address, Address::random(), Address::random()], + vec![id_address, Address::ZERO, Address::ZERO], ).await.unwrap(); let actual_seed = [ &arpa_core::u256_to_vec(&task.seed)[..], - &arpa_core::u256_to_vec(ðers::types::U256::from(task.assignment_block_height))[..], + &arpa_core::u256_to_vec(&U256::from(task.assignment_block_height))[..], ] .concat(); @@ -1050,13 +1055,13 @@ mod tests { #[tokio::test] async fn test_group_cache_committer_operations() { - let id_address = Address::random(); - let committer_address = Address::random(); - let non_committer_address = Address::random(); + let id_address = Address::ZERO; + let committer_address = Address::ZERO; + let non_committer_address = Address::ZERO; let group_cache = setup_group_cache_simple( id_address, - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, DEFAULT_GROUP_INDEX as usize, DEFAULT_EPOCH, DEFAULT_GROUP_SIZE, @@ -1086,20 +1091,20 @@ mod tests { tokio::time::sleep(Duration::from_millis(100)).await; let client1 = MockCommitterClient::new( - Address::random(), - Address::random(), + Address::ZERO, + Address::ZERO, format!("http://127.0.0.1:{}", port1), ).await.unwrap(); let client2 = MockCommitterClient::new( - Address::random(), - Address::random(), + Address::ZERO, + Address::ZERO, format!("http://127.0.0.1:{}", port2), ).await.unwrap(); let result1 = client1 .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, arpa_core::BLSTaskType::Randomness, vec![1], vec![2], @@ -1109,7 +1114,7 @@ mod tests { let result2 = client2 .commit_partial_signature( - DEFAULT_CHAIN_ID, + DEFAULT_CHAIN_ID as usize, arpa_core::BLSTaskType::Randomness, vec![4], vec![5], diff --git a/crates/arpa-node/src/subscriber/schedule_node_activation.rs b/crates/arpa-node/src/subscriber/schedule_node_activation.rs index 6bee742d..3a352819 100644 --- a/crates/arpa-node/src/subscriber/schedule_node_activation.rs +++ b/crates/arpa-node/src/subscriber/schedule_node_activation.rs @@ -130,8 +130,8 @@ mod tests { queue::event_queue::EventQueue, }; use alloy::node_bindings::{Anvil, AnvilInstance}; - use alloy::primitives::{Address, U256}; - use alloy::providers::{Provider, WsConnect}; + use alloy::primitives::{Address, U256, B256}; + use alloy::providers::{WsConnect}; use alloy::signers::local::PrivateKeySigner; use alloy::sol; use arpa_core::{build_client, Config, GeneralMainChainIdentity, ProviderClientWithSigner}; @@ -157,6 +157,7 @@ mod tests { } sol! { + #[sol(ignore_unlinked)] #[sol(rpc)] MockNodeRegistry, "test-contract/MockNodeRegistry.json" @@ -166,13 +167,16 @@ mod tests { _anvil: AnvilInstance, client: ProviderClientWithSigner, wallet: PrivateKeySigner, - chain_id: u64 + chain_id: u64, + ws_endpoint: String, } + impl TestEnvironment { async fn new() -> Self { let anvil = Anvil::new().spawn(); - let ws_connect = WsConnect::new(anvil.ws_endpoint()); + let ws_endpoint = anvil.ws_endpoint(); + let ws_connect = WsConnect::new(&ws_endpoint); let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); let chain_id = anvil.chain_id(); let client = build_client(wallet.clone(), chain_id, ws_connect) @@ -183,17 +187,18 @@ mod tests { _anvil: anvil, client, wallet, - chain_id + chain_id, + ws_endpoint, } } - async fn deploy_mock_avs_directory(client: ProviderClientWithSigner,) -> Address { - let contract = MockAVSDirectory::deploy(client.clone()).await; + async fn deploy_mock_avs_directory(&self,) -> Address { + let contract = MockAVSDirectory::deploy(self.client.clone()).await.unwrap(); *contract.address() } async fn deploy_mock_service_manager(&self, avs_directory: Address) -> Address { - let contract = MockServiceManager::deploy(client.clone()).await; + let contract = MockServiceManager::deploy(self.client.clone(), avs_directory).await.unwrap(); *contract.address() } @@ -202,10 +207,14 @@ mod tests { service_manager: Address, ) -> (Address, MockNodeRegistry::MockNodeRegistryInstance) { let contract = MockNodeRegistry::deploy( - &self.client.clone() + self.client.clone(), + Address::ZERO, + Address::ZERO, + service_manager ) .await .unwrap(); + (*contract.address(), contract) } @@ -238,15 +247,15 @@ mod tests { node_registry_address: Address, ) -> Arc>> { let config = Config::default(); - let ws_connect = WsConnect::new(self.ws_endpoint.clone()); + let ws_connect = WsConnect::new(&self.ws_endpoint); let general_chain_identity = GeneralMainChainIdentity::new( self.chain_id.try_into().unwrap(), self.wallet.clone(), ws_connect, self.client.clone(), self.ws_endpoint.clone(), - Address::ZERO, // controller - Address::ZERO, // controller_relayer + Address::ZERO, + Address::ZERO, node_registry_address, config .get_time_limits() @@ -283,19 +292,17 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: true, node_registry_address, }; - // Check node state before activation let node_before = registry_contract.getNode(node_address).call().await.unwrap(); assert!(!node_before.state); - // Verify service manager and avs directory setup let service_manager = MockServiceManager::new(service_manager_address, &env.client); - let returned_avs_directory = service_manager.avsDirectory().call().await.unwrap()._0; - assert_eq!(returned_avs_directory, avs_directory_address); + let returned_avs_directory = service_manager.avsDirectory().call().await.unwrap().0; + assert_eq!(Address::from(returned_avs_directory), avs_directory_address); let avs_directory = MockAVSDirectory::new(avs_directory_address, &env.client); let test_hash = avs_directory @@ -308,8 +315,9 @@ mod tests { .call() .await .unwrap() - ._0; - assert_ne!(test_hash, [0u8; 32].into()); + .0; + + assert_ne!(test_hash, B256::ZERO); let result = subscriber .notify(Topic::NodeActivation, &activation_event) @@ -335,7 +343,7 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: false, node_registry_address, }; @@ -362,7 +370,6 @@ mod tests { env.register_node(®istry_contract, node_address, false) .await; - // Set node as active registry_contract .setNodeState(node_address, true) .send() @@ -377,7 +384,7 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: false, node_registry_address, }; @@ -398,7 +405,7 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: false, node_registry_address, }; @@ -423,7 +430,7 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: false, node_registry_address, }; @@ -433,12 +440,11 @@ mod tests { .await; assert!(result.is_ok()); - // Query for NodeActivated events let event_filter = registry_contract.NodeActivated_filter().from_block(0); let events = event_filter.query().await.unwrap(); assert_eq!(events.len(), 1); - assert_eq!(events[0].nodeAddress, node_address); - assert_eq!(events[0].groupIndex, U256::from(FIRST_GROUP_INDEX)); + assert_eq!(events[0].0.nodeAddress, node_address); + assert_eq!(events[0].0.groupIndex, U256::from(FIRST_GROUP_INDEX)); } #[tokio::test] @@ -495,7 +501,7 @@ mod tests { let subscriber = NodeActivationSubscriber::new(chain_identity, eq); let activation_event = NodeActivation { - chain_id: env.chain_id as usize, + chain_id: env.chain_id, is_eigenlayer: false, node_registry_address, }; diff --git a/crates/arpa-node/test-contract/IController.sol b/crates/arpa-node/test-contract/IController.sol new file mode 100644 index 00000000..6e659ae0 --- /dev/null +++ b/crates/arpa-node/test-contract/IController.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IController { + struct Member { + address nodeIdAddress; + uint256[4] partialPublicKey; + } + + struct CommitResult { + uint256 groupEpoch; + uint256[4] publicKey; + address[] disqualifiedNodes; + } + + struct CommitCache { + address[] nodeIdAddress; + CommitResult commitResult; + } + + struct Group { + uint256 index; + uint256 epoch; + uint256 size; + uint256 threshold; + Member[] members; + address[] committers; + CommitCache[] commitCacheList; + bool isStrictlyMajorityConsensusReached; + uint256[4] publicKey; + } + + struct CommitDkgParams { + uint256 groupIndex; + uint256 groupEpoch; + bytes publicKey; + bytes partialPublicKey; + address[] disqualifiedNodes; + } + + function getGroup(uint256 groupIndex) external view returns (Group memory); + + function commitDkg(CommitDkgParams memory params) external; + + function getCoordinator(uint256 groupIndex) external view returns (address); +} \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockAVSDirectory.json b/crates/arpa-node/test-contract/MockAVSDirectory.json index f657ae5b..5a03c1cf 100644 --- a/crates/arpa-node/test-contract/MockAVSDirectory.json +++ b/crates/arpa-node/test-contract/MockAVSDirectory.json @@ -1 +1,157 @@ -{"contracts":{"MockAVSDirectory.sol:MockAVSDirectory":{"abi":[{"inputs":[],"name":"OPERATOR_AVS_REGISTRATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"avs","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"calculateOperatorAVSRegistrationDigestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}],"bin":"6080604052348015600f57600080fd5b5061025c8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a1060c881461003b578063d79aceab14610060575b600080fd5b61004e6100493660046101e4565b610087565b60405190815260200160405180910390f35b61004e7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290526000906100fe9060c00160405160208183030381529060405280519060200120610107565b95945050505050565b600061018b604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a59181019190915246606082015230608082015260009060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101df57600080fd5b919050565b600080600080608085870312156101fa57600080fd5b610203856101c8565b9350610211602086016101c8565b9396939550505050604082013591606001359056fea2646970667358221220dbd3a7787db0b0f15c8be1882e760026205840ed4366838ebb349e23df7964cc64736f6c634300081b0033","bin-runtime":"608060405234801561001057600080fd5b50600436106100365760003560e01c8063a1060c881461003b578063d79aceab14610060575b600080fd5b61004e6100493660046101e4565b610087565b60405190815260200160405180910390f35b61004e7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290526000906100fe9060c00160405160208183030381529060405280519060200120610107565b95945050505050565b600061018b604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a59181019190915246606082015230608082015260009060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101df57600080fd5b919050565b600080600080608085870312156101fa57600080fd5b610203856101c8565b9350610211602086016101c8565b9396939550505050604082013591606001359056fea2646970667358221220dbd3a7787db0b0f15c8be1882e760026205840ed4366838ebb349e23df7964cc64736f6c634300081b0033","metadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"OPERATOR_AVS_REGISTRATION_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"name\":\"calculateOperatorAVSRegistrationDigestHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"MockAVSDirectory.sol\":\"MockAVSDirectory\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[]},\"sources\":{\"MockAVSDirectory.sol\":{\"keccak256\":\"0xcdb7ea8e372ba79a263c09b789f2ae208d0be87147372d9213ea8f1e1bc44f41\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c8869a37deee832ad1e59a5e7d330251ffb02396f181c8de4f516b2bceb9ab\",\"dweb:/ipfs/QmfAGeyoBBXyPcvUSaqEhJpg3HNqo1vURokurzebDvjvyZ\"]}},\"version\":1}","srcmap":"57:1123:0:-:0;;;;;;;;;;;;;;;;;;;","srcmap-runtime":"57:1123:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;402:339;;;;;;:::i;:::-;;:::i;:::-;;;763:25:1;;;751:2;736:18;402:339:0;;;;;;;89:164;;159:94;89:164;;402:339;648:75;;;159:94;648:75;;;1058:25:1;-1:-1:-1;;;;;1119:55:1;;;1099:18;;;1092:83;;;;1211:55;;;1191:18;;;1184:83;1283:18;;;1276:34;;;1326:19;;;1319:35;;;574:7:0;;600:134;;1030:19:1;;648:75:0;;;;;;;;;;;;638:86;;;;;;600:24;:134::i;:::-;593:141;402:339;-1:-1:-1;;;;;402:339:0:o;751:181::-;828:7;893:18;1029:141;;;311:80;1029:141;;;1993:25:1;1077:29:0;2034:18:1;;;2027:34;;;;1120:13:0;2077:18:1;;;2070:34;1155:4:0;2120:18:1;;;2113:83;993:7:0;;1965:19:1;;1029:141:0;;;;;;;;;;;;1019:152;;;;;;1012:159;;942:236;;893:18;864:60;;-1:-1:-1;;;864:60:0;;;1623:27:1;1666:11;;;1659:27;;;;1702:12;;;1695:28;;;1739:12;;864:60:0;;;;;;;;;;;;854:71;;;;;;847:78;;751:181;;;:::o;14:196:1:-;82:20;;-1:-1:-1;;;;;131:54:1;;121:65;;111:93;;200:1;197;190:12;111:93;14:196;;;:::o;215:397::-;301:6;309;317;325;378:3;366:9;357:7;353:23;349:33;346:53;;;395:1;392;385:12;346:53;418:29;437:9;418:29;:::i;:::-;408:39;;466:38;500:2;489:9;485:18;466:38;:::i;:::-;215:397;;456:48;;-1:-1:-1;;;;551:2:1;536:18;;523:32;;602:2;587:18;574:32;;215:397::o"}},"sourceList":["MockAVSDirectory.sol"],"version":"0.8.27+commit.40a35a09.Linux.g++"} +{ + "abi": [ + { + "inputs": [], + "name": "OPERATOR_AVS_REGISTRATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "avs", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + } + ], + "name": "calculateOperatorAVSRegistrationDigestHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x6080604052348015600e575f5ffd5b506102528061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063a1060c8814610038578063d79aceab1461005d575b5f5ffd5b61004b6100463660046101dd565b610084565b60405190815260200160405180910390f35b61004b7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290525f906100fa9060c00160405160208183030381529060405280519060200120610103565b95945050505050565b5f610185604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a5918101919091524660608201523060808201525f9060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101d8575f5ffd5b919050565b5f5f5f5f608085870312156101f0575f5ffd5b6101f9856101c2565b9350610207602086016101c2565b9396939550505050604082013591606001359056fea26469706673582212201bb5b69d81dc4e6e7a6bbea304a87080c309b9e588f0d6a74bef5f9b9fb0878864736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063a1060c8814610038578063d79aceab1461005d575b5f5ffd5b61004b6100463660046101dd565b610084565b60405190815260200160405180910390f35b61004b7fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd81565b604080517fda2c89bafdd34776a2b8bb9c83c82f419e20cc8c67207f70edd58249b92661bd60208201526001600160a01b038087169282019290925290841660608201526080810183905260a081018290525f906100fa9060c00160405160208183030381529060405280519060200120610103565b95945050505050565b5f610185604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660208201527fb90ba67d9f9e2595b7716388c2098bad993c6f9bf167b3949b5ceb5cbd4111a5918101919091524660608201523060808201525f9060a00160405160208183030381529060405280519060200120905090565b60405161190160f01b6020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b80356001600160a01b03811681146101d8575f5ffd5b919050565b5f5f5f5f608085870312156101f0575f5ffd5b6101f9856101c2565b9350610207602086016101c2565b9396939550505050604082013591606001359056fea26469706673582212201bb5b69d81dc4e6e7a6bbea304a87080c309b9e588f0d6a74bef5f9b9fb0878864736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"OPERATOR_AVS_REGISTRATION_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"avs\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"name\":\"calculateOperatorAVSRegistrationDigestHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"MockAVSDirectory.sol\":\"MockAVSDirectory\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[]},\"sources\":{\"MockAVSDirectory.sol\":{\"keccak256\":\"0xcdb7ea8e372ba79a263c09b789f2ae208d0be87147372d9213ea8f1e1bc44f41\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c8869a37deee832ad1e59a5e7d330251ffb02396f181c8de4f516b2bceb9ab\",\"dweb:/ipfs/QmfAGeyoBBXyPcvUSaqEhJpg3HNqo1vURokurzebDvjvyZ\"]}},\"version\":1}", + "metadata": { + "compiler": { + "version": "0.8.27+commit.40a35a09" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [], + "name": "OPERATOR_AVS_REGISTRATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "avs", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + } + ], + "name": "calculateOperatorAVSRegistrationDigestHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "MockAVSDirectory.sol": "MockAVSDirectory" + }, + "evmVersion": "cancun", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 300 + }, + "remappings": [] + }, + "sources": { + "MockAVSDirectory.sol": { + "keccak256": "0xcdb7ea8e372ba79a263c09b789f2ae208d0be87147372d9213ea8f1e1bc44f41", + "license": "MIT", + "urls": [ + "bzz-raw://30c8869a37deee832ad1e59a5e7d330251ffb02396f181c8de4f516b2bceb9ab", + "dweb:/ipfs/QmfAGeyoBBXyPcvUSaqEhJpg3HNqo1vURokurzebDvjvyZ" + ] + } + }, + "version": 1 + }, + "id": 1 +} diff --git a/crates/arpa-node/test-contract/MockAdapter.json b/crates/arpa-node/test-contract/MockAdapter.json index 3d9aa4ea..746a3c56 100644 --- a/crates/arpa-node/test-contract/MockAdapter.json +++ b/crates/arpa-node/test-contract/MockAdapter.json @@ -1 +1,371 @@ -{"abi":[{"type":"function","name":"_requestCommitments","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"emitRandomnessRequest","inputs":[{"name":"requestId","type":"bytes32","internalType":"bytes32"},{"name":"subId","type":"uint64","internalType":"uint64"},{"name":"groupIndex","type":"uint32","internalType":"uint32"},{"name":"requestType","type":"uint8","internalType":"enum MockAdapter.RequestType"},{"name":"params","type":"bytes","internalType":"bytes"},{"name":"sender","type":"address","internalType":"address"},{"name":"seed","type":"uint256","internalType":"uint256"},{"name":"requestConfirmations","type":"uint16","internalType":"uint16"},{"name":"callbackGasLimit","type":"uint32","internalType":"uint32"},{"name":"callbackMaxGasPrice","type":"uint256","internalType":"uint256"},{"name":"estimatedPayment","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getPendingRequestCommitment","inputs":[{"name":"requestId","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"setRequestCommitment","inputs":[{"name":"requestId","type":"bytes32","internalType":"bytes32"},{"name":"commitment","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"RandomnessRequest","inputs":[{"name":"requestId","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"subId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"groupIndex","type":"uint32","indexed":true,"internalType":"uint32"},{"name":"requestType","type":"uint8","indexed":false,"internalType":"enum MockAdapter.RequestType"},{"name":"params","type":"bytes","indexed":false,"internalType":"bytes"},{"name":"sender","type":"address","indexed":false,"internalType":"address"},{"name":"seed","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"requestConfirmations","type":"uint16","indexed":false,"internalType":"uint16"},{"name":"callbackGasLimit","type":"uint32","indexed":false,"internalType":"uint32"},{"name":"callbackMaxGasPrice","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"estimatedPayment","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false}],"bytecode":{"object":"0x608060405234801561001057600080fd5b506103be806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80631565034c14610051578063561949031461008357806368c50d52146100a3578063b3af18b4146100c5575b600080fd5b61007161005f36600461013b565b60009081526020819052604090205490565b60405190815260200160405180910390f35b61007161009136600461013b565b60006020819052908152604090205481565b6100c36100b1366004610154565b60009182526020829052604090912055565b005b6100c36100d3366004610210565b8963ffffffff168b67ffffffffffffffff168d7fd26299589dd9197a8dc30a0fa17b0fe7dd432bc3441aa5f5631ea1e14c1af7448c8c8c8c8c8c8c8c8c604051610125999897969594939291906102f0565b60405180910390a4505050505050505050505050565b60006020828403121561014d57600080fd5b5035919050565b6000806040838503121561016757600080fd5b50508035926020909101359150565b803563ffffffff8116811461018a57600080fd5b919050565b80356003811061018a57600080fd5b60008083601f8401126101b057600080fd5b50813567ffffffffffffffff8111156101c857600080fd5b6020830191508360208285010111156101e057600080fd5b9250929050565b80356001600160a01b038116811461018a57600080fd5b803561ffff8116811461018a57600080fd5b6000806000806000806000806000806000806101608d8f03121561023357600080fd5b8c359b5060208d013567ffffffffffffffff8116811461025257600080fd5b9a5061026060408e01610176565b995061026e60608e0161018f565b985067ffffffffffffffff60808e0135111561028957600080fd5b6102998e60808f01358f0161019e565b90985096506102aa60a08e016101e7565b955060c08d013594506102bf60e08e016101fe565b93506102ce6101008e01610176565b92506101208d013591506101408d013590509295989b509295989b509295989b565b600061010060038c1061031357634e487b7160e01b600052602160045260246000fd5b8b8352806020840152898184015250610120898b8285013760008a84018201526001600160a01b0389166040840152601f19601f8b01168301019050866060830152610365608083018761ffff169052565b63ffffffff851660a083015260c082019390935260e0015297965050505050505056fea26469706673582212207d4b9ed03db6758ebaac596c9c5f990d60c583ed61ec02f4bf25ccb7e6c02ddd64736f6c63430008120033","sourceMap":"57:1542:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631565034c14610051578063561949031461008357806368c50d52146100a3578063b3af18b4146100c5575b600080fd5b61007161005f36600461013b565b60009081526020819052604090205490565b60405190815260200160405180910390f35b61007161009136600461013b565b60006020819052908152604090205481565b6100c36100b1366004610154565b60009182526020829052604090912055565b005b6100c36100d3366004610210565b8963ffffffff168b67ffffffffffffffff168d7fd26299589dd9197a8dc30a0fa17b0fe7dd432bc3441aa5f5631ea1e14c1af7448c8c8c8c8c8c8c8c8c604051610125999897969594939291906102f0565b60405180910390a4505050505050505050505050565b60006020828403121561014d57600080fd5b5035919050565b6000806040838503121561016757600080fd5b50508035926020909101359150565b803563ffffffff8116811461018a57600080fd5b919050565b80356003811061018a57600080fd5b60008083601f8401126101b057600080fd5b50813567ffffffffffffffff8111156101c857600080fd5b6020830191508360208285010111156101e057600080fd5b9250929050565b80356001600160a01b038116811461018a57600080fd5b803561ffff8116811461018a57600080fd5b6000806000806000806000806000806000806101608d8f03121561023357600080fd5b8c359b5060208d013567ffffffffffffffff8116811461025257600080fd5b9a5061026060408e01610176565b995061026e60608e0161018f565b985067ffffffffffffffff60808e0135111561028957600080fd5b6102998e60808f01358f0161019e565b90985096506102aa60a08e016101e7565b955060c08d013594506102bf60e08e016101fe565b93506102ce6101008e01610176565b92506101208d013591506101408d013590509295989b509295989b509295989b565b600061010060038c1061031357634e487b7160e01b600052602160045260246000fd5b8b8352806020840152898184015250610120898b8285013760008a84018201526001600160a01b0389166040840152601f19601f8b01168301019050866060830152610365608083018761ffff169052565b63ffffffff851660a083015260c082019390935260e0015297965050505050505056fea26469706673582212207d4b9ed03db6758ebaac596c9c5f990d60c583ed61ec02f4bf25ccb7e6c02ddd64736f6c63430008120033","sourceMap":"57:1542:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1315:140;;;;;;:::i;:::-;1392:7;1418:30;;;;;;;;;;;;1315:140;;;;345:25:3;;;333:2;318:18;1315:140:0;;;;;;;550:54;;;;;;:::i;:::-;;;;;;;;;;;;;;;1461:136;;;;;;:::i;:::-;1547:19;:30;;;;;;;;;;;:43;1461:136;;;611:698;;;;;;:::i;:::-;1072:10;999:303;;1053:5;999:303;;1030:9;999:303;1096:11;1121:6;;1141;1161:4;1179:20;1213:16;1243:19;1276:16;999:303;;;;;;;;;;;;;;:::i;:::-;;;;;;;;611:698;;;;;;;;;;;;:::o;14:180:3:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;-1:-1:-1;165:23:3;;14:180;-1:-1:-1;14:180:3:o;381:248::-;449:6;457;510:2;498:9;489:7;485:23;481:32;478:52;;;526:1;523;516:12;478:52;-1:-1:-1;;549:23:3;;;619:2;604:18;;;591:32;;-1:-1:-1;381:248:3:o;634:163::-;701:20;;761:10;750:22;;740:33;;730:61;;787:1;784;777:12;730:61;634:163;;;:::o;802:152::-;879:20;;928:1;918:12;;908:40;;944:1;941;934:12;959:347;1010:8;1020:6;1074:3;1067:4;1059:6;1055:17;1051:27;1041:55;;1092:1;1089;1082:12;1041:55;-1:-1:-1;1115:20:3;;1158:18;1147:30;;1144:50;;;1190:1;1187;1180:12;1144:50;1227:4;1219:6;1215:17;1203:29;;1279:3;1272:4;1263:6;1255;1251:19;1247:30;1244:39;1241:59;;;1296:1;1293;1286:12;1241:59;959:347;;;;;:::o;1311:196::-;1379:20;;-1:-1:-1;;;;;1428:54:3;;1418:65;;1408:93;;1497:1;1494;1487:12;1512:159;1579:20;;1639:6;1628:18;;1618:29;;1608:57;;1661:1;1658;1651:12;1676:1249;1845:6;1853;1861;1869;1877;1885;1893;1901;1909;1917;1925:7;1934;1988:3;1976:9;1967:7;1963:23;1959:33;1956:53;;;2005:1;2002;1995:12;1956:53;2041:9;2028:23;2018:33;;2101:2;2090:9;2086:18;2073:32;2145:18;2138:5;2134:30;2127:5;2124:41;2114:69;;2179:1;2176;2169:12;2114:69;2202:5;-1:-1:-1;2226:37:3;2259:2;2244:18;;2226:37;:::i;:::-;2216:47;;2282;2325:2;2314:9;2310:18;2282:47;:::i;:::-;2272:57;;2379:18;2372:3;2361:9;2357:19;2344:33;2341:57;2338:77;;;2411:1;2408;2401:12;2338:77;2450:85;2527:7;2519:3;2508:9;2504:19;2491:33;2480:9;2476:49;2450:85;:::i;:::-;2554:8;;-1:-1:-1;2581:8:3;-1:-1:-1;2608:39:3;2642:3;2627:19;;2608:39;:::i;:::-;2598:49;;2694:3;2683:9;2679:19;2666:33;2656:43;;2718:38;2751:3;2740:9;2736:19;2718:38;:::i;:::-;2708:48;;2775:38;2808:3;2797:9;2793:19;2775:38;:::i;:::-;2765:48;;2861:3;2850:9;2846:19;2833:33;2822:44;;2914:3;2903:9;2899:19;2886:33;2875:44;;1676:1249;;;;;;;;;;;;;;:::o;3256:1127::-;3579:4;3608:3;3641:1;3633:6;3630:13;3620:144;;3686:10;3681:3;3677:20;3674:1;3667:31;3721:4;3718:1;3711:15;3749:4;3746:1;3739:15;3620:144;3791:6;3780:9;3773:25;3834:2;3829;3818:9;3814:18;3807:30;3873:6;3868:2;3857:9;3853:18;3846:34;;3899:3;3952:6;3944;3939:2;3928:9;3924:18;3911:48;4008:1;3979:22;;;3975:31;;3968:42;-1:-1:-1;;;;;2996:54:3;;4132:2;4117:18;;2984:67;-1:-1:-1;;4071:2:3;4050:15;;4046:29;4031:45;;4027:54;;-1:-1:-1;4172:6:3;4167:2;4156:9;4152:18;4145:34;4188:46;4229:3;4218:9;4214:19;4206:6;3138;3127:18;3115:31;;3062:90;4188:46;3233:10;3222:22;;4284:3;4269:19;;3210:35;4320:3;4305:19;;4298:35;;;;4364:3;4349:19;4342:35;3256:1127;;-1:-1:-1;;;;;;;3256:1127:3:o","linkReferences":{}},"methodIdentifiers":{"_requestCommitments(bytes32)":"56194903","emitRandomnessRequest(bytes32,uint64,uint32,uint8,bytes,address,uint256,uint16,uint32,uint256,uint256)":"b3af18b4","getPendingRequestCommitment(bytes32)":"1565034c","setRequestCommitment(bytes32,bytes32)":"68c50d52"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"groupIndex\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"enum MockAdapter.RequestType\",\"name\":\"requestType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"callbackMaxGasPrice\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"estimatedPayment\",\"type\":\"uint256\"}],\"name\":\"RandomnessRequest\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"groupIndex\",\"type\":\"uint32\"},{\"internalType\":\"enum MockAdapter.RequestType\",\"name\":\"requestType\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"callbackMaxGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"estimatedPayment\",\"type\":\"uint256\"}],\"name\":\"emitRandomnessRequest\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"getPendingRequestCommitment\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"commitment\",\"type\":\"bytes32\"}],\"name\":\"setRequestCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"test/mock/MockAdapter.sol\":\"MockAdapter\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[\":Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/\",\":Staking-v0.1/=lib/Staking-v0.1/src/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":fx-portal/=lib/fx-portal/contracts/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"]},\"sources\":{\"test/mock/MockAdapter.sol\":{\"keccak256\":\"0x4cac908426179b5465b1f2f3c55e77957c0eccbcd5999987e59295f7a98fe37a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://daa8aeef6f05922c93d7b5ba4f0f4d05745823f3d8b8cecb934ce977617379ad\",\"dweb:/ipfs/QmSS67iCNrQpnk83ivDVDuF1CmP19utjGoJuyHsQ3GSgWg\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32","indexed":true},{"internalType":"uint64","name":"subId","type":"uint64","indexed":true},{"internalType":"uint32","name":"groupIndex","type":"uint32","indexed":true},{"internalType":"enum MockAdapter.RequestType","name":"requestType","type":"uint8","indexed":false},{"internalType":"bytes","name":"params","type":"bytes","indexed":false},{"internalType":"address","name":"sender","type":"address","indexed":false},{"internalType":"uint256","name":"seed","type":"uint256","indexed":false},{"internalType":"uint16","name":"requestConfirmations","type":"uint16","indexed":false},{"internalType":"uint32","name":"callbackGasLimit","type":"uint32","indexed":false},{"internalType":"uint256","name":"callbackMaxGasPrice","type":"uint256","indexed":false},{"internalType":"uint256","name":"estimatedPayment","type":"uint256","indexed":false}],"type":"event","name":"RandomnessRequest","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"_requestCommitments","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint64","name":"subId","type":"uint64"},{"internalType":"uint32","name":"groupIndex","type":"uint32"},{"internalType":"enum MockAdapter.RequestType","name":"requestType","type":"uint8"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"internalType":"uint32","name":"callbackGasLimit","type":"uint32"},{"internalType":"uint256","name":"callbackMaxGasPrice","type":"uint256"},{"internalType":"uint256","name":"estimatedPayment","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"emitRandomnessRequest"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getPendingRequestCommitment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"setRequestCommitment"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/","Staking-v0.1/=lib/Staking-v0.1/src/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","fx-portal/=lib/fx-portal/contracts/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/"],"optimizer":{"enabled":true,"runs":300},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"test/mock/MockAdapter.sol":"MockAdapter"},"evmVersion":"paris","libraries":{}},"sources":{"test/mock/MockAdapter.sol":{"keccak256":"0x4cac908426179b5465b1f2f3c55e77957c0eccbcd5999987e59295f7a98fe37a","urls":["bzz-raw://daa8aeef6f05922c93d7b5ba4f0f4d05745823f3d8b8cecb934ce977617379ad","dweb:/ipfs/QmSS67iCNrQpnk83ivDVDuF1CmP19utjGoJuyHsQ3GSgWg"],"license":"MIT"}},"version":1},"id":0} \ No newline at end of file +{ + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "uint32", + "name": "groupIndex", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "enum IRequestTypeBase.RequestType", + "name": "requestType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "params", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "callbackMaxGasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "estimatedPayment", + "type": "uint256" + } + ], + "name": "RandomnessRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint32", + "name": "groupIndex", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "committer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "participantMembers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "flatFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "name": "RandomnessRequestResult", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "_requestCommitments", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "groupIndex", + "type": "uint32" + }, + { + "internalType": "enum IRequestTypeBase.RequestType", + "name": "requestType", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "params", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "callbackMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "estimatedPayment", + "type": "uint256" + } + ], + "name": "emitRandomnessRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "groupIndex", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "signature", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "fulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "name": "getPendingRequestCommitment", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "commitment", + "type": "bytes32" + } + ], + "name": "setRequestCommitment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "_shouldRevert", + "type": "bool" + } + ], + "name": "setShouldRevert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "_shouldRevertWithCustomError", + "type": "bool" + } + ], + "name": "setShouldRevertWithCustomError", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "shouldRevert", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "shouldRevertWithCustomError", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x6080604052348015600e575f5ffd5b506107968061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610090575f3560e01c80638cffc3b7116100635780638cffc3b714610119578063993d491714610146578063b1a0dcea14610178578063b3af18b41461019a578063cecaffd6146101ad575f5ffd5b8063130288f5146100945780631565034c146100a957806356194903146100db57806368c50d52146100fa575b5f5ffd5b6100a76100a236600461041d565b6101da565b005b6100c86100b73660046104af565b5f9081526020819052604090205490565b6040519081526020015b60405180910390f35b6100c86100e93660046104af565b5f6020819052908152604090205481565b6100a76101083660046104c6565b5f9182526020829052604090912055565b6100a76101273660046104e6565b5f91825260026020526040909120805460ff1916911515919091179055565b6101686101543660046104af565b60026020525f908152604090205460ff1681565b60405190151581526020016100d2565b6101686101863660046104af565b60016020525f908152604090205460ff1681565b6100a76101a836600461054d565b610358565b6100a76101bb3660046104e6565b5f91825260016020526040909120805460ff1916911515919091179055565b5f8681526002602052604090205460ff161561022f5760405162461bcd60e51b815260206004820152600f60248201526e21bab9ba37b6aa32b9ba22b93937b960891b60448201526064015b60405180910390fd5b5f8681526001602052604090205460ff161561027a5760405162461bcd60e51b815260206004820152600a60248201526915195cdd14995d995c9d60b21b6044820152606401610226565b5f868152602081815260408083208390558051600180825281830190925291828101908036833701905050905033815f815181106102ba576102ba610629565b60200260200101906001600160a01b031690816001600160a01b0316815250508763ffffffff16877f6fc1bcb4bd7d5fdf4017f746304b61ae4472496256e906c6bb44682aa2c9a6d833848a60405160200161031891815260200190565b60408051601f198184030181529082905280516020909101206103469392916103e89060649060019061063d565b60405180910390a35050505050505050565b8963ffffffff168b67ffffffffffffffff168d7fd26299589dd9197a8dc30a0fa17b0fe7dd432bc3441aa5f5631ea1e14c1af7448c8c8c8c8c8c8c8c8c6040516103aa999897969594939291906106c2565b60405180910390a4505050505050505050505050565b803563ffffffff811681146103d3575f5ffd5b919050565b5f5f83601f8401126103e8575f5ffd5b50813567ffffffffffffffff8111156103ff575f5ffd5b602083019150836020828501011115610416575f5ffd5b9250929050565b5f5f5f5f5f5f5f60a0888a031215610433575f5ffd5b61043c886103c0565b96506020880135955060408801359450606088013567ffffffffffffffff811115610465575f5ffd5b6104718a828b016103d8565b909550935050608088013567ffffffffffffffff811115610490575f5ffd5b61049c8a828b016103d8565b989b979a50959850939692959293505050565b5f602082840312156104bf575f5ffd5b5035919050565b5f5f604083850312156104d7575f5ffd5b50508035926020909101359150565b5f5f604083850312156104f7575f5ffd5b823591506020830135801515811461050d575f5ffd5b809150509250929050565b8035600381106103d3575f5ffd5b80356001600160a01b03811681146103d3575f5ffd5b803561ffff811681146103d3575f5ffd5b5f5f5f5f5f5f5f5f5f5f5f5f6101608d8f031215610569575f5ffd5b8c359b5060208d013567ffffffffffffffff81168114610587575f5ffd5b9a5061059560408e016103c0565b99506105a360608e01610518565b985067ffffffffffffffff60808e013511156105bd575f5ffd5b6105cd8e60808f01358f016103d8565b90985096506105de60a08e01610526565b955060c08d013594506105f360e08e0161053c565b93506106026101008e016103c0565b9b9e9a9d50989b979a96999598509396929591949193505061012082013591610140013590565b634e487b7160e01b5f52603260045260245ffd5b5f60c082016001600160a01b038916835260c0602084015280885180835260e08501915060208a0192505f5b818110156106905783516001600160a01b0316835260209384019390920191600101610669565b505080925050508560408301528460608301528360808301526106b760a083018415159052565b979650505050505050565b5f60038b106106df57634e487b7160e01b5f52602160045260245ffd5b8a8252610100602083015288610100830152888a6101208401375f6101208a84010152610120601f19601f8b0116830101905061072760408301896001600160a01b03169052565b86606083015261073d608083018761ffff169052565b63ffffffff851660a083015260c082019390935260e0015297965050505050505056fea26469706673582212204e7b539f868e40176daa7ce798145ed3fdc789035b371ecd796e76c12b2241af64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b5060043610610090575f3560e01c80638cffc3b7116100635780638cffc3b714610119578063993d491714610146578063b1a0dcea14610178578063b3af18b41461019a578063cecaffd6146101ad575f5ffd5b8063130288f5146100945780631565034c146100a957806356194903146100db57806368c50d52146100fa575b5f5ffd5b6100a76100a236600461041d565b6101da565b005b6100c86100b73660046104af565b5f9081526020819052604090205490565b6040519081526020015b60405180910390f35b6100c86100e93660046104af565b5f6020819052908152604090205481565b6100a76101083660046104c6565b5f9182526020829052604090912055565b6100a76101273660046104e6565b5f91825260026020526040909120805460ff1916911515919091179055565b6101686101543660046104af565b60026020525f908152604090205460ff1681565b60405190151581526020016100d2565b6101686101863660046104af565b60016020525f908152604090205460ff1681565b6100a76101a836600461054d565b610358565b6100a76101bb3660046104e6565b5f91825260016020526040909120805460ff1916911515919091179055565b5f8681526002602052604090205460ff161561022f5760405162461bcd60e51b815260206004820152600f60248201526e21bab9ba37b6aa32b9ba22b93937b960891b60448201526064015b60405180910390fd5b5f8681526001602052604090205460ff161561027a5760405162461bcd60e51b815260206004820152600a60248201526915195cdd14995d995c9d60b21b6044820152606401610226565b5f868152602081815260408083208390558051600180825281830190925291828101908036833701905050905033815f815181106102ba576102ba610629565b60200260200101906001600160a01b031690816001600160a01b0316815250508763ffffffff16877f6fc1bcb4bd7d5fdf4017f746304b61ae4472496256e906c6bb44682aa2c9a6d833848a60405160200161031891815260200190565b60408051601f198184030181529082905280516020909101206103469392916103e89060649060019061063d565b60405180910390a35050505050505050565b8963ffffffff168b67ffffffffffffffff168d7fd26299589dd9197a8dc30a0fa17b0fe7dd432bc3441aa5f5631ea1e14c1af7448c8c8c8c8c8c8c8c8c6040516103aa999897969594939291906106c2565b60405180910390a4505050505050505050505050565b803563ffffffff811681146103d3575f5ffd5b919050565b5f5f83601f8401126103e8575f5ffd5b50813567ffffffffffffffff8111156103ff575f5ffd5b602083019150836020828501011115610416575f5ffd5b9250929050565b5f5f5f5f5f5f5f60a0888a031215610433575f5ffd5b61043c886103c0565b96506020880135955060408801359450606088013567ffffffffffffffff811115610465575f5ffd5b6104718a828b016103d8565b909550935050608088013567ffffffffffffffff811115610490575f5ffd5b61049c8a828b016103d8565b989b979a50959850939692959293505050565b5f602082840312156104bf575f5ffd5b5035919050565b5f5f604083850312156104d7575f5ffd5b50508035926020909101359150565b5f5f604083850312156104f7575f5ffd5b823591506020830135801515811461050d575f5ffd5b809150509250929050565b8035600381106103d3575f5ffd5b80356001600160a01b03811681146103d3575f5ffd5b803561ffff811681146103d3575f5ffd5b5f5f5f5f5f5f5f5f5f5f5f5f6101608d8f031215610569575f5ffd5b8c359b5060208d013567ffffffffffffffff81168114610587575f5ffd5b9a5061059560408e016103c0565b99506105a360608e01610518565b985067ffffffffffffffff60808e013511156105bd575f5ffd5b6105cd8e60808f01358f016103d8565b90985096506105de60a08e01610526565b955060c08d013594506105f360e08e0161053c565b93506106026101008e016103c0565b9b9e9a9d50989b979a96999598509396929591949193505061012082013591610140013590565b634e487b7160e01b5f52603260045260245ffd5b5f60c082016001600160a01b038916835260c0602084015280885180835260e08501915060208a0192505f5b818110156106905783516001600160a01b0316835260209384019390920191600101610669565b505080925050508560408301528460608301528360808301526106b760a083018415159052565b979650505050505050565b5f60038b106106df57634e487b7160e01b5f52602160045260245ffd5b8a8252610100602083015288610100830152888a6101208401375f6101208a84010152610120601f19601f8b0116830101905061072760408301896001600160a01b03169052565b86606083015261073d608083018761ffff169052565b63ffffffff851660a083015260c082019390935260e0015297965050505050505056fea26469706673582212204e7b539f868e40176daa7ce798145ed3fdc789035b371ecd796e76c12b2241af64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "metadata": {}, + "id": 1 +} diff --git a/crates/arpa-node/test-contract/MockAdapter.sol b/crates/arpa-node/test-contract/MockAdapter.sol index ea75540b..b7b90a29 100644 --- a/crates/arpa-node/test-contract/MockAdapter.sol +++ b/crates/arpa-node/test-contract/MockAdapter.sol @@ -1,12 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -contract MockAdapter { +interface IRequestTypeBase { enum RequestType { Randomness, RandomWords, Shuffling } +} + +interface IAdapter { + function getPendingRequestCommitment(bytes32 requestId) external view returns (bytes32); +} + +contract MockAdapter is IRequestTypeBase, IAdapter { event RandomnessRequest( bytes32 indexed requestId, uint64 indexed subId, @@ -33,7 +40,9 @@ contract MockAdapter { ); mapping(bytes32 => bytes32) public _requestCommitments; - + mapping(bytes32 => bool) public shouldRevert; + mapping(bytes32 => bool) public shouldRevertWithCustomError; + function emitRandomnessRequest( bytes32 requestId, uint64 subId, @@ -61,12 +70,52 @@ contract MockAdapter { estimatedPayment ); } - - function getPendingRequestCommitment(bytes32 requestId) public view returns (bytes32) { + + function getPendingRequestCommitment(bytes32 requestId) public view override(IAdapter) returns (bytes32) { return _requestCommitments[requestId]; } - + function setRequestCommitment(bytes32 requestId, bytes32 commitment) public { _requestCommitments[requestId] = commitment; } -} + + function setShouldRevert(bytes32 requestId, bool _shouldRevert) public { + shouldRevert[requestId] = _shouldRevert; + } + + function setShouldRevertWithCustomError(bytes32 requestId, bool _shouldRevertWithCustomError) public { + shouldRevertWithCustomError[requestId] = _shouldRevertWithCustomError; + } + + function fulfillRandomness( + uint32 groupIndex, + bytes32 requestId, + uint256 signature, + bytes calldata /* requestDetail */, + bytes calldata /* partialSignatures */ + ) public { + if (shouldRevertWithCustomError[requestId]) { + revert("CustomTestError"); + } + + if (shouldRevert[requestId]) { + revert("TestRevert"); + } + + delete _requestCommitments[requestId]; + + address[] memory participantMembers = new address[](1); + participantMembers[0] = msg.sender; + + emit RandomnessRequestResult( + requestId, + groupIndex, + msg.sender, + participantMembers, + uint256(keccak256(abi.encode(signature))), + 1000, + 100, + true + ); + } +} \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockController.json b/crates/arpa-node/test-contract/MockController.json index f98e1dbf..e3c31149 100644 --- a/crates/arpa-node/test-contract/MockController.json +++ b/crates/arpa-node/test-contract/MockController.json @@ -1 +1,563 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"_nodeRegistryAddress","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"adapterAddress","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"emitDkgTaskEvent","inputs":[{"name":"globalEpoch","type":"uint256","internalType":"uint256"},{"name":"groupIndex","type":"uint256","internalType":"uint256"},{"name":"groupEpoch","type":"uint256","internalType":"uint256"},{"name":"size","type":"uint256","internalType":"uint256"},{"name":"threshold","type":"uint256","internalType":"uint256"},{"name":"members","type":"address[]","internalType":"address[]"},{"name":"assignmentBlockHeight","type":"uint256","internalType":"uint256"},{"name":"coordinatorAddress","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getControllerConfig","inputs":[],"outputs":[{"name":"nodeRegistryContractAddress","type":"address","internalType":"address"},{"name":"adapterContractAddress","type":"address","internalType":"address"},{"name":"disqualifiedNodePenaltyAmount","type":"uint256","internalType":"uint256"},{"name":"defaultNumberOfCommitters","type":"uint256","internalType":"uint256"},{"name":"defaultDkgPhaseDuration","type":"uint256","internalType":"uint256"},{"name":"groupMaxCapacity","type":"uint256","internalType":"uint256"},{"name":"idealNumberOfGroups","type":"uint256","internalType":"uint256"},{"name":"dkgPostProcessReward","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getGroup","inputs":[{"name":"groupIndex","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"tuple","internalType":"struct MockController.Group","components":[{"name":"index","type":"uint256","internalType":"uint256"},{"name":"epoch","type":"uint256","internalType":"uint256"},{"name":"size","type":"uint256","internalType":"uint256"},{"name":"threshold","type":"uint256","internalType":"uint256"},{"name":"members","type":"tuple[]","internalType":"struct MockController.Member[]","components":[{"name":"nodeIdAddress","type":"address","internalType":"address"},{"name":"partialPublicKey","type":"uint256[4]","internalType":"uint256[4]"}]},{"name":"committers","type":"address[]","internalType":"address[]"},{"name":"commitCacheList","type":"tuple[]","internalType":"struct MockController.CommitCache[]","components":[{"name":"nodeIdAddress","type":"address[]","internalType":"address[]"},{"name":"commitResult","type":"tuple","internalType":"struct MockController.CommitResult","components":[{"name":"groupEpoch","type":"uint256","internalType":"uint256"},{"name":"publicKey","type":"uint256[4]","internalType":"uint256[4]"},{"name":"disqualifiedNodes","type":"address[]","internalType":"address[]"}]}]},{"name":"isStrictlyMajorityConsensusReached","type":"bool","internalType":"bool"},{"name":"publicKey","type":"uint256[4]","internalType":"uint256[4]"}]}],"stateMutability":"view"},{"type":"function","name":"nodeRegistryAddress","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"setAdapterAddress","inputs":[{"name":"_adapterAddress","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setCommitters","inputs":[{"name":"groupIndex","type":"uint256","internalType":"uint256"},{"name":"committerAddresses","type":"address[]","internalType":"address[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setGroup","inputs":[{"name":"groupIndex","type":"uint256","internalType":"uint256"},{"name":"epoch","type":"uint256","internalType":"uint256"},{"name":"size","type":"uint256","internalType":"uint256"},{"name":"threshold","type":"uint256","internalType":"uint256"},{"name":"isStrictlyMajorityConsensusReached","type":"bool","internalType":"bool"},{"name":"publicKey","type":"uint256[4]","internalType":"uint256[4]"},{"name":"memberAddresses","type":"address[]","internalType":"address[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setMemberPartialPublicKey","inputs":[{"name":"groupIndex","type":"uint256","internalType":"uint256"},{"name":"memberIndex","type":"uint256","internalType":"uint256"},{"name":"partialPublicKey","type":"uint256[4]","internalType":"uint256[4]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"DkgTask","inputs":[{"name":"globalEpoch","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"groupIndex","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"groupEpoch","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"size","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"threshold","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"members","type":"address[]","indexed":false,"internalType":"address[]"},{"name":"assignmentBlockHeight","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"coordinatorAddress","type":"address","indexed":false,"internalType":"address"}],"anonymous":false}],"bytecode":{"object":"0x608060405234801561001057600080fd5b50604051610e9b380380610e9b83398101604081905261002f9161005b565b600180546001600160a01b039092166001600160a01b031992831617905560028054909116905561008b565b60006020828403121561006d57600080fd5b81516001600160a01b038116811461008457600080fd5b9392505050565b610e018061009a6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80636971f096116100665780636971f09614610103578063736eede514610116578063ceb6065414610146578063d11b8e6814610166578063fec10aa9146101b957600080fd5b80630fc68a0c146100985780631bb1fd28146100ad57806326a93abe146100c057806366c9aba6146100d3575b600080fd5b6100ab6100a6366004610906565b6101cc565b005b6100ab6100bb36600461098e565b610218565b6100ab6100ce366004610a43565b610240565b6002546100e6906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100ab610111366004610ad2565b610362565b6100ab610124366004610b08565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b610159610154366004610b2a565b6103ae565b6040516100fa9190610c99565b600154600254604080516001600160a01b0393841681529290911660208301526103e89082015260056060820152606460808201819052600a60a0830152600360c083015260e0820152610100016100fa565b6001546100e6906001600160a01b031681565b8587897fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee8888888888604051610206959493929190610d4e565b60405180910390a45050505050505050565b600082815260208181526040909120825161023b926005909201918401906106bb565b505050565b600087815260208190526040902087815560018101879055600281018690556003810185905560078101805460ff191685151517905561028560088201846004610720565b5061029460048201600061074e565b60005b8251811015610341576102a8610772565b8260040160405180604001604052808685815181106102c9576102c9610d8e565b6020908102919091018101516001600160a01b039081168352918101859052835460018082018655600095865294829020845160059092020180546001600160a01b031916919093161782558201519192909161032a918301906004610720565b50505050808061033990610da4565b915050610297565b50815161035790600583019060208501906106bb565b505050505050505050565b80600080858152602001908152602001600020600401838154811061038957610389610d8e565b90600052602060002090600502016001019060046103a8929190610720565b50505050565b6103b6610790565b6000828152602081815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101561048d5760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b8154815260200190600101908083116104625750505050508152505081526020019060010190610414565b505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156104ee57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116104d0575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101561066557838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561059857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161057a575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b8154815260200190600101908083116105d457505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561064957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161062b575b505050505081525050815250508152602001906001019061051c565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b815481526020019060010190808311610698575050505050815250509050919050565b828054828255906000526020600020908101928215610710579160200282015b8281111561071057825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906106db565b5061071c9291506107e3565b5090565b8260048101928215610710579160200282015b82811115610710578251825591602001919060010190610733565b508054600082556005029060005260206000209081019061076f91906107f8565b50565b60405180608001604052806004906020820280368337509192915050565b604051806101200160405280600081526020016000815260200160008152602001600081526020016060815260200160608152602001606081526020016000151581526020016107de610772565b905290565b5b8082111561071c57600081556001016107e4565b8082111561071c5780546001600160a01b0319168155600060018201819055600282018190556003820181905560048201556005016107f8565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461085f57600080fd5b919050565b600082601f83011261087557600080fd5b8135602067ffffffffffffffff8083111561089257610892610832565b8260051b604051601f19603f830116810181811084821117156108b7576108b7610832565b6040529384528581018301938381019250878511156108d557600080fd5b83870191505b848210156108fb576108ec82610848565b835291830191908301906108db565b979650505050505050565b600080600080600080600080610100898b03121561092357600080fd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff81111561095d57600080fd5b6109698b828c01610864565b93505060c0890135915061097f60e08a01610848565b90509295985092959890939650565b600080604083850312156109a157600080fd5b82359150602083013567ffffffffffffffff8111156109bf57600080fd5b6109cb85828601610864565b9150509250929050565b600082601f8301126109e657600080fd5b6040516080810181811067ffffffffffffffff82111715610a0957610a09610832565b604052806080840185811115610a1e57600080fd5b845b81811015610a38578035835260209283019201610a20565b509195945050505050565b6000806000806000806000610140888a031215610a5f57600080fd5b8735965060208801359550604088013594506060880135935060808801358015158114610a8b57600080fd5b9250610a9a8960a08a016109d5565b915061012088013567ffffffffffffffff811115610ab757600080fd5b610ac38a828b01610864565b91505092959891949750929550565b600080600060c08486031215610ae757600080fd5b8335925060208401359150610aff85604086016109d5565b90509250925092565b600060208284031215610b1a57600080fd5b610b2382610848565b9392505050565b600060208284031215610b3c57600080fd5b5035919050565b8060005b60048110156103a8578151845260209384019390910190600101610b47565b600081518084526020808501945080840160005b83811015610bb457815180516001600160a01b03168852830151610ba084890182610b43565b5060a0969096019590820190600101610b7a565b509495945050505050565b600081518084526020808501945080840160005b83811015610bb45781516001600160a01b031687529582019590820190600101610bd3565b600081518084526020808501808196508360051b8101915082860160005b85811015610c8c578284038952815160408151818752610c3882880182610bbf565b90508783015192508681038888015260c08351825288840151610c5d8a840182610b43565b508284015193508060a0830152610c7681830185610bbf565b9c89019c97505050928601925050600101610c16565b5091979650505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015161018060a0840152610cdf6101a0840182610b66565b905060a0840151601f19808584030160c0860152610cfd8383610bbf565b925060c08601519150808584030160e086015250610d1b8282610bf8565b91505060e0840151610100610d338186018315159052565b8501519050610d46610120850182610b43565b509392505050565b85815284602082015260a060408201526000610d6d60a0830186610bbf565b90508360608301526001600160a01b03831660808301529695505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201610dc457634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5275654e678068bd7262d977c899e65387d270e28fddbb0056b3e8d4ee8489364736f6c63430008120033","sourceMap":"57:3972:1:-:0;;;1113:138;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1165:19;:42;;-1:-1:-1;;;;;1165:42:1;;;-1:-1:-1;;;;;;1165:42:1;;;;;;1217:14;:27;;;;;;;57:3972;;14:290:3;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;179:16;;-1:-1:-1;;;;;224:31:3;;214:42;;204:70;;270:1;267;260:12;204:70;293:5;14:290;-1:-1:-1;;;14:290:3:o;:::-;57:3972:1;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c80636971f096116100665780636971f09614610103578063736eede514610116578063ceb6065414610146578063d11b8e6814610166578063fec10aa9146101b957600080fd5b80630fc68a0c146100985780631bb1fd28146100ad57806326a93abe146100c057806366c9aba6146100d3575b600080fd5b6100ab6100a6366004610906565b6101cc565b005b6100ab6100bb36600461098e565b610218565b6100ab6100ce366004610a43565b610240565b6002546100e6906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100ab610111366004610ad2565b610362565b6100ab610124366004610b08565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b610159610154366004610b2a565b6103ae565b6040516100fa9190610c99565b600154600254604080516001600160a01b0393841681529290911660208301526103e89082015260056060820152606460808201819052600a60a0830152600360c083015260e0820152610100016100fa565b6001546100e6906001600160a01b031681565b8587897fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee8888888888604051610206959493929190610d4e565b60405180910390a45050505050505050565b600082815260208181526040909120825161023b926005909201918401906106bb565b505050565b600087815260208190526040902087815560018101879055600281018690556003810185905560078101805460ff191685151517905561028560088201846004610720565b5061029460048201600061074e565b60005b8251811015610341576102a8610772565b8260040160405180604001604052808685815181106102c9576102c9610d8e565b6020908102919091018101516001600160a01b039081168352918101859052835460018082018655600095865294829020845160059092020180546001600160a01b031916919093161782558201519192909161032a918301906004610720565b50505050808061033990610da4565b915050610297565b50815161035790600583019060208501906106bb565b505050505050505050565b80600080858152602001908152602001600020600401838154811061038957610389610d8e565b90600052602060002090600502016001019060046103a8929190610720565b50505050565b6103b6610790565b6000828152602081815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101561048d5760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b8154815260200190600101908083116104625750505050508152505081526020019060010190610414565b505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156104ee57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116104d0575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101561066557838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561059857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161057a575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b8154815260200190600101908083116105d457505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561064957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161062b575b505050505081525050815250508152602001906001019061051c565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b815481526020019060010190808311610698575050505050815250509050919050565b828054828255906000526020600020908101928215610710579160200282015b8281111561071057825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906106db565b5061071c9291506107e3565b5090565b8260048101928215610710579160200282015b82811115610710578251825591602001919060010190610733565b508054600082556005029060005260206000209081019061076f91906107f8565b50565b60405180608001604052806004906020820280368337509192915050565b604051806101200160405280600081526020016000815260200160008152602001600081526020016060815260200160608152602001606081526020016000151581526020016107de610772565b905290565b5b8082111561071c57600081556001016107e4565b8082111561071c5780546001600160a01b0319168155600060018201819055600282018190556003820181905560048201556005016107f8565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461085f57600080fd5b919050565b600082601f83011261087557600080fd5b8135602067ffffffffffffffff8083111561089257610892610832565b8260051b604051601f19603f830116810181811084821117156108b7576108b7610832565b6040529384528581018301938381019250878511156108d557600080fd5b83870191505b848210156108fb576108ec82610848565b835291830191908301906108db565b979650505050505050565b600080600080600080600080610100898b03121561092357600080fd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff81111561095d57600080fd5b6109698b828c01610864565b93505060c0890135915061097f60e08a01610848565b90509295985092959890939650565b600080604083850312156109a157600080fd5b82359150602083013567ffffffffffffffff8111156109bf57600080fd5b6109cb85828601610864565b9150509250929050565b600082601f8301126109e657600080fd5b6040516080810181811067ffffffffffffffff82111715610a0957610a09610832565b604052806080840185811115610a1e57600080fd5b845b81811015610a38578035835260209283019201610a20565b509195945050505050565b6000806000806000806000610140888a031215610a5f57600080fd5b8735965060208801359550604088013594506060880135935060808801358015158114610a8b57600080fd5b9250610a9a8960a08a016109d5565b915061012088013567ffffffffffffffff811115610ab757600080fd5b610ac38a828b01610864565b91505092959891949750929550565b600080600060c08486031215610ae757600080fd5b8335925060208401359150610aff85604086016109d5565b90509250925092565b600060208284031215610b1a57600080fd5b610b2382610848565b9392505050565b600060208284031215610b3c57600080fd5b5035919050565b8060005b60048110156103a8578151845260209384019390910190600101610b47565b600081518084526020808501945080840160005b83811015610bb457815180516001600160a01b03168852830151610ba084890182610b43565b5060a0969096019590820190600101610b7a565b509495945050505050565b600081518084526020808501945080840160005b83811015610bb45781516001600160a01b031687529582019590820190600101610bd3565b600081518084526020808501808196508360051b8101915082860160005b85811015610c8c578284038952815160408151818752610c3882880182610bbf565b90508783015192508681038888015260c08351825288840151610c5d8a840182610b43565b508284015193508060a0830152610c7681830185610bbf565b9c89019c97505050928601925050600101610c16565b5091979650505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015161018060a0840152610cdf6101a0840182610b66565b905060a0840151601f19808584030160c0860152610cfd8383610bbf565b925060c08601519150808584030160e086015250610d1b8282610bf8565b91505060e0840151610100610d338186018315159052565b8501519050610d46610120850182610b43565b509392505050565b85815284602082015260a060408201526000610d6d60a0830186610bbf565b90508360608301526001600160a01b03831660808301529695505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201610dc457634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5275654e678068bd7262d977c899e65387d270e28fddbb0056b3e8d4ee8489364736f6c63430008120033","sourceMap":"57:3972:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3470:441;;;;;;:::i;:::-;;:::i;:::-;;3187:156;;;;;;:::i;:::-;;:::i;2061:889::-;;;;;;:::i;:::-;;:::i;173:29::-;;;;;-1:-1:-1;;;;;173:29:1;;;;;;-1:-1:-1;;;;;4240:55:3;;;4222:74;;4210:2;4195:18;173:29:1;;;;;;;;2956:225;;;;;;:::i;:::-;;:::i;3917:110::-;;;;;;:::i;:::-;3988:14;:32;;-1:-1:-1;;;;;;3988:32:1;-1:-1:-1;;;;;3988:32:1;;;;;;;;;;3917:110;3349:115;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;1257:798::-;1737:19;;1770:14;;1257:798;;;-1:-1:-1;;;;;1737:19:1;;;9631:34:3;;1770:14:1;;;;9696:2:3;9681:18;;9674:43;1798:4:1;9733:18:3;;;9726:34;1849:1:1;9791:2:3;9776:18;;9769:34;1893:3:1;9834::3;9819:19;;9812:35;;;1937:2:1;9878:3:3;9863:19;;9856:35;1973:1:1;9922:3:3;9907:19;;9900:35;9966:3;9951:19;;9944:35;9557:3;9542:19;1257:798:1;9227:758:3;133:34:1;;;;;-1:-1:-1;;;;;133:34:1;;;3470:441;3815:10;3803;3790:11;3769:135;3827:4;3833:9;3844:7;3853:21;3876:18;3769:135;;;;;;;;;;:::i;:::-;;;;;;;;3470:441;;;;;;;;:::o;3187:156::-;3286:6;:18;;;;;;;;;;;:50;;;;:29;;;;;:50;;;;:::i;:::-;;3187:156;;:::o;2061:889::-;2332:19;2354:18;;;;;;;;;;2382:24;;;2416:11;;;:19;;;2445:10;;;:17;;;2472:15;;;:27;;;2509:40;;;:77;;-1:-1:-1;;2509:77:1;;;;;;;2596:27;:15;;;2614:9;2596:27;;:::i;:::-;-1:-1:-1;2634:20:1;2641:13;;;;2634:20;:::i;:::-;2669:9;2664:235;2688:15;:22;2684:1;:26;2664:235;;;2731:39;;:::i;:::-;2784:5;:13;;2803:84;;;;;;;;2826:15;2842:1;2826:18;;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;2803:84:1;;;;;;;;;;;2784:104;;;;;;;;-1:-1:-1;2784:104:1;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2784:104:1;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;2717:182;2712:3;;;;;:::i;:::-;;;;2664:235;;;-1:-1:-1;2909:34:1;;;;:16;;;;:34;;;;;:::i;:::-;;2322:628;2061:889;;;;;;;:::o;2956:225::-;3158:16;3099:6;:18;3106:10;3099:18;;;;;;;;;;;:26;;3126:11;3099:39;;;;;;;;:::i;:::-;;;;;;;;;;;:56;;:75;;;;;;;:::i;:::-;;2956:225;;;:::o;3349:115::-;3408:12;;:::i;:::-;3439:6;:18;;;;;;;;;;;3432:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3439:18;;3432:25;;;;;;;;3439:6;3432:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3432:25:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3432:25:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3432:25:1;;;;;;;;;;;;;;;;-1:-1:-1;;;3432:25:1;;;-1:-1:-1;;3432:25:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3432:25:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;3432:25:1;;;-1:-1:-1;3432:25:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3349:115;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:127:3;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:196;214:20;;-1:-1:-1;;;;;263:54:3;;253:65;;243:93;;332:1;329;322:12;243:93;146:196;;;:::o;347:908::-;401:5;454:3;447:4;439:6;435:17;431:27;421:55;;472:1;469;462:12;421:55;508:6;495:20;534:4;557:18;594:2;590;587:10;584:36;;;600:18;;:::i;:::-;646:2;643:1;639:10;678:2;672:9;741:2;737:7;732:2;728;724:11;720:25;712:6;708:38;796:6;784:10;781:22;776:2;764:10;761:18;758:46;755:72;;;807:18;;:::i;:::-;843:2;836:22;893:18;;;969:15;;;965:24;;;927:15;;;;-1:-1:-1;1001:15:3;;;998:35;;;1029:1;1026;1019:12;998:35;1065:2;1057:6;1053:15;1042:26;;1077:148;1093:6;1088:3;1085:15;1077:148;;;1159:23;1178:3;1159:23;:::i;:::-;1147:36;;1203:12;;;;1110;;;;1077:148;;;1243:6;347:908;-1:-1:-1;;;;;;;347:908:3:o;1260:835::-;1407:6;1415;1423;1431;1439;1447;1455;1463;1516:3;1504:9;1495:7;1491:23;1487:33;1484:53;;;1533:1;1530;1523:12;1484:53;1569:9;1556:23;1546:33;;1626:2;1615:9;1611:18;1598:32;1588:42;;1677:2;1666:9;1662:18;1649:32;1639:42;;1728:2;1717:9;1713:18;1700:32;1690:42;;1779:3;1768:9;1764:19;1751:33;1741:43;;1835:3;1824:9;1820:19;1807:33;1863:18;1855:6;1852:30;1849:50;;;1895:1;1892;1885:12;1849:50;1918:61;1971:7;1962:6;1951:9;1947:22;1918:61;:::i;:::-;1908:71;;;2026:3;2015:9;2011:19;1998:33;1988:43;;2050:39;2084:3;2073:9;2069:19;2050:39;:::i;:::-;2040:49;;1260:835;;;;;;;;;;;:::o;2100:416::-;2193:6;2201;2254:2;2242:9;2233:7;2229:23;2225:32;2222:52;;;2270:1;2267;2260:12;2222:52;2306:9;2293:23;2283:33;;2367:2;2356:9;2352:18;2339:32;2394:18;2386:6;2383:30;2380:50;;;2426:1;2423;2416:12;2380:50;2449:61;2502:7;2493:6;2482:9;2478:22;2449:61;:::i;:::-;2439:71;;;2100:416;;;;;:::o;2521:648::-;2571:5;2624:3;2617:4;2609:6;2605:17;2601:27;2591:55;;2642:1;2639;2632:12;2591:55;2675:2;2669:9;2717:3;2709:6;2705:16;2787:6;2775:10;2772:22;2751:18;2739:10;2736:34;2733:62;2730:88;;;2798:18;;:::i;:::-;2834:2;2827:22;2869:6;2910:3;2898:16;;2926:15;;;2923:35;;;2954:1;2951;2944:12;2923:35;2978:6;2993:146;3009:6;3004:3;3001:15;2993:146;;;3077:17;;3065:30;;3124:4;3115:14;;;;3026;2993:146;;;-1:-1:-1;3157:6:3;;2521:648;-1:-1:-1;;;;;2521:648:3:o;3174:897::-;3332:6;3340;3348;3356;3364;3372;3380;3433:3;3421:9;3412:7;3408:23;3404:33;3401:53;;;3450:1;3447;3440:12;3401:53;3486:9;3473:23;3463:33;;3543:2;3532:9;3528:18;3515:32;3505:42;;3594:2;3583:9;3579:18;3566:32;3556:42;;3645:2;3634:9;3630:18;3617:32;3607:42;;3699:3;3688:9;3684:19;3671:33;3747:5;3740:13;3733:21;3726:5;3723:32;3713:60;;3769:1;3766;3759:12;3713:60;3792:5;-1:-1:-1;3816:54:3;3862:7;3856:3;3841:19;;3816:54;:::i;:::-;3806:64;;3921:3;3910:9;3906:19;3893:33;3949:18;3941:6;3938:30;3935:50;;;3981:1;3978;3971:12;3935:50;4004:61;4057:7;4048:6;4037:9;4033:22;4004:61;:::i;:::-;3994:71;;;3174:897;;;;;;;;;;:::o;4307:361::-;4407:6;4415;4423;4476:3;4464:9;4455:7;4451:23;4447:33;4444:53;;;4493:1;4490;4483:12;4444:53;4529:9;4516:23;4506:33;;4586:2;4575:9;4571:18;4558:32;4548:42;;4609:53;4654:7;4649:2;4638:9;4634:18;4609:53;:::i;:::-;4599:63;;4307:361;;;;;:::o;4673:186::-;4732:6;4785:2;4773:9;4764:7;4760:23;4756:32;4753:52;;;4801:1;4798;4791:12;4753:52;4824:29;4843:9;4824:29;:::i;:::-;4814:39;4673:186;-1:-1:-1;;;4673:186:3:o;4864:180::-;4923:6;4976:2;4964:9;4955:7;4951:23;4947:32;4944:52;;;4992:1;4989;4982:12;4944:52;-1:-1:-1;5015:23:3;;4864:180;-1:-1:-1;4864:180:3:o;5049:326::-;5142:5;5165:1;5175:194;5189:4;5186:1;5183:11;5175:194;;;5248:13;;5236:26;;5285:4;5309:12;;;;5344:15;;;;5209:1;5202:9;5175:194;;5380:640;5439:3;5477:5;5471:12;5504:6;5499:3;5492:19;5530:4;5559:2;5554:3;5550:12;5543:19;;5596:2;5589:5;5585:14;5617:1;5627:368;5641:6;5638:1;5635:13;5627:368;;;5700:13;;5742:9;;-1:-1:-1;;;;;5738:58:3;5726:71;;5836:11;;5830:18;5861:52;5900:12;;;5830:18;5861:52;:::i;:::-;-1:-1:-1;5942:4:3;5933:14;;;;;5970:15;;;;5663:1;5656:9;5627:368;;;-1:-1:-1;6011:3:3;;5380:640;-1:-1:-1;;;;;5380:640:3:o;6025:484::-;6078:3;6116:5;6110:12;6143:6;6138:3;6131:19;6169:4;6198:2;6193:3;6189:12;6182:19;;6235:2;6228:5;6224:14;6256:1;6266:218;6280:6;6277:1;6274:13;6266:218;;;6345:13;;-1:-1:-1;;;;;6341:62:3;6329:75;;6424:12;;;;6459:15;;;;6302:1;6295:9;6266:218;;6514:1294;6578:3;6616:5;6610:12;6643:6;6638:3;6631:19;6669:4;6710:2;6705:3;6701:12;6735:11;6762;6755:18;;6812:6;6809:1;6805:14;6798:5;6794:26;6782:38;;6854:2;6847:5;6843:14;6875:1;6885:897;6899:6;6896:1;6893:13;6885:897;;;6970:5;6964:4;6960:16;6955:3;6948:29;7006:6;7000:13;7036:4;7079:2;7073:9;7108:2;7102:4;7095:16;7138:57;7191:2;7185:4;7181:13;7167:12;7138:57;:::i;:::-;7124:71;;7244:2;7240;7236:11;7230:18;7208:40;;7295:4;7287:6;7283:17;7278:2;7272:4;7268:13;7261:40;7324:4;7362:14;7356:21;7348:6;7341:37;7439:2;7423:14;7419:23;7413:30;7456:57;7509:2;7501:6;7497:15;7481:14;7456:57;:::i;:::-;;7574:2;7558:14;7554:23;7548:30;7526:52;;7617:2;7610:4;7602:6;7598:17;7591:29;7641:61;7698:2;7690:6;7686:15;7670:14;7641:61;:::i;:::-;7760:12;;;;7633:69;-1:-1:-1;;;7725:15:3;;;;-1:-1:-1;;6921:1:3;6914:9;6885:897;;;-1:-1:-1;7798:4:3;;6514:1294;-1:-1:-1;;;;;;;6514:1294:3:o;7909:1313::-;8082:2;8071:9;8064:21;8127:6;8121:13;8116:2;8105:9;8101:18;8094:41;8189:2;8181:6;8177:15;8171:22;8166:2;8155:9;8151:18;8144:50;8248:2;8240:6;8236:15;8230:22;8225:2;8214:9;8210:18;8203:50;8308:2;8300:6;8296:15;8290:22;8284:3;8273:9;8269:19;8262:51;8045:4;8360:3;8352:6;8348:16;8342:23;8402:6;8396:3;8385:9;8381:19;8374:35;8432:69;8496:3;8485:9;8481:19;8467:12;8432:69;:::i;:::-;8418:83;;8550:3;8542:6;8538:16;8532:23;8578:2;8574:7;8646:2;8634:9;8626:6;8622:22;8618:31;8612:3;8601:9;8597:19;8590:60;8673:52;8718:6;8702:14;8673:52;:::i;:::-;8659:66;;8774:3;8766:6;8762:16;8756:23;8734:45;;8844:2;8832:9;8824:6;8820:22;8816:31;8810:3;8799:9;8795:19;8788:60;;8871:63;8927:6;8911:14;8871:63;:::i;:::-;8857:77;;;8983:3;8975:6;8971:16;8965:23;9007:3;9019:51;9066:2;9055:9;9051:18;9035:14;7883:13;7876:21;7864:34;;7813:91;9019:51;9107:15;;9101:22;;-1:-1:-1;9132:61:3;9188:3;9173:19;;9101:22;9132:61;:::i;:::-;-1:-1:-1;9210:6:3;7909:1313;-1:-1:-1;;;7909:1313:3:o;9990:597::-;10281:6;10270:9;10263:25;10324:6;10319:2;10308:9;10304:18;10297:34;10367:3;10362:2;10351:9;10347:18;10340:31;10244:4;10388:57;10440:3;10429:9;10425:19;10417:6;10388:57;:::i;:::-;10380:65;;10481:6;10476:2;10465:9;10461:18;10454:34;-1:-1:-1;;;;;10529:6:3;10525:55;10519:3;10508:9;10504:19;10497:84;9990:597;;;;;;;;:::o;10592:127::-;10653:10;10648:3;10644:20;10641:1;10634:31;10684:4;10681:1;10674:15;10708:4;10705:1;10698:15;10724:232;10763:3;10784:17;;;10781:140;;10843:10;10838:3;10834:20;10831:1;10824:31;10878:4;10875:1;10868:15;10906:4;10903:1;10896:15;10781:140;-1:-1:-1;10948:1:3;10937:13;;10724:232::o","linkReferences":{}},"methodIdentifiers":{"adapterAddress()":"66c9aba6","emitDkgTaskEvent(uint256,uint256,uint256,uint256,uint256,address[],uint256,address)":"0fc68a0c","getControllerConfig()":"d11b8e68","getGroup(uint256)":"ceb60654","nodeRegistryAddress()":"fec10aa9","setAdapterAddress(address)":"736eede5","setCommitters(uint256,address[])":"1bb1fd28","setGroup(uint256,uint256,uint256,uint256,bool,uint256[4],address[])":"26a93abe","setMemberPartialPublicKey(uint256,uint256,uint256[4])":"6971f096"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_nodeRegistryAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"globalEpoch\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"groupEpoch\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"members\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"assignmentBlockHeight\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"DkgTask\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"adapterAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"globalEpoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"groupEpoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"members\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"assignmentBlockHeight\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"emitDkgTaskEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getControllerConfig\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"nodeRegistryContractAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"adapterContractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"disqualifiedNodePenaltyAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"defaultNumberOfCommitters\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"defaultDkgPhaseDuration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"groupMaxCapacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"idealNumberOfGroups\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"dkgPostProcessReward\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"}],\"name\":\"getGroup\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"nodeIdAddress\",\"type\":\"address\"},{\"internalType\":\"uint256[4]\",\"name\":\"partialPublicKey\",\"type\":\"uint256[4]\"}],\"internalType\":\"struct MockController.Member[]\",\"name\":\"members\",\"type\":\"tuple[]\"},{\"internalType\":\"address[]\",\"name\":\"committers\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"address[]\",\"name\":\"nodeIdAddress\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"groupEpoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256[4]\",\"name\":\"publicKey\",\"type\":\"uint256[4]\"},{\"internalType\":\"address[]\",\"name\":\"disqualifiedNodes\",\"type\":\"address[]\"}],\"internalType\":\"struct MockController.CommitResult\",\"name\":\"commitResult\",\"type\":\"tuple\"}],\"internalType\":\"struct MockController.CommitCache[]\",\"name\":\"commitCacheList\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isStrictlyMajorityConsensusReached\",\"type\":\"bool\"},{\"internalType\":\"uint256[4]\",\"name\":\"publicKey\",\"type\":\"uint256[4]\"}],\"internalType\":\"struct MockController.Group\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nodeRegistryAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_adapterAddress\",\"type\":\"address\"}],\"name\":\"setAdapterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"committerAddresses\",\"type\":\"address[]\"}],\"name\":\"setCommitters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isStrictlyMajorityConsensusReached\",\"type\":\"bool\"},{\"internalType\":\"uint256[4]\",\"name\":\"publicKey\",\"type\":\"uint256[4]\"},{\"internalType\":\"address[]\",\"name\":\"memberAddresses\",\"type\":\"address[]\"}],\"name\":\"setGroup\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"groupIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"memberIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256[4]\",\"name\":\"partialPublicKey\",\"type\":\"uint256[4]\"}],\"name\":\"setMemberPartialPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"test/mock/MockController.sol\":\"MockController\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[\":Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/\",\":Staking-v0.1/=lib/Staking-v0.1/src/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":fx-portal/=lib/fx-portal/contracts/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"]},\"sources\":{\"test/mock/MockController.sol\":{\"keccak256\":\"0x75a6fd1067d50fca998bdb718afeb498b37f0d775d67f6e163c813c9bbf8f92e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d8d53cb3b18e614c692a1a9f5d73965e843efeb2ace2d61203c749040a4c71da\",\"dweb:/ipfs/QmXuyWuV2hZxFsW1HFtuAzQcd4YSrixweM2cwYf5SeKEFd\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_nodeRegistryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"globalEpoch","type":"uint256","indexed":true},{"internalType":"uint256","name":"groupIndex","type":"uint256","indexed":true},{"internalType":"uint256","name":"groupEpoch","type":"uint256","indexed":true},{"internalType":"uint256","name":"size","type":"uint256","indexed":false},{"internalType":"uint256","name":"threshold","type":"uint256","indexed":false},{"internalType":"address[]","name":"members","type":"address[]","indexed":false},{"internalType":"uint256","name":"assignmentBlockHeight","type":"uint256","indexed":false},{"internalType":"address","name":"coordinatorAddress","type":"address","indexed":false}],"type":"event","name":"DkgTask","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"adapterAddress","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"globalEpoch","type":"uint256"},{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"groupEpoch","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"address[]","name":"members","type":"address[]"},{"internalType":"uint256","name":"assignmentBlockHeight","type":"uint256"},{"internalType":"address","name":"coordinatorAddress","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"emitDkgTaskEvent"},{"inputs":[],"stateMutability":"view","type":"function","name":"getControllerConfig","outputs":[{"internalType":"address","name":"nodeRegistryContractAddress","type":"address"},{"internalType":"address","name":"adapterContractAddress","type":"address"},{"internalType":"uint256","name":"disqualifiedNodePenaltyAmount","type":"uint256"},{"internalType":"uint256","name":"defaultNumberOfCommitters","type":"uint256"},{"internalType":"uint256","name":"defaultDkgPhaseDuration","type":"uint256"},{"internalType":"uint256","name":"groupMaxCapacity","type":"uint256"},{"internalType":"uint256","name":"idealNumberOfGroups","type":"uint256"},{"internalType":"uint256","name":"dkgPostProcessReward","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"stateMutability":"view","type":"function","name":"getGroup","outputs":[{"internalType":"struct MockController.Group","name":"","type":"tuple","components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"struct MockController.Member[]","name":"members","type":"tuple[]","components":[{"internalType":"address","name":"nodeIdAddress","type":"address"},{"internalType":"uint256[4]","name":"partialPublicKey","type":"uint256[4]"}]},{"internalType":"address[]","name":"committers","type":"address[]"},{"internalType":"struct MockController.CommitCache[]","name":"commitCacheList","type":"tuple[]","components":[{"internalType":"address[]","name":"nodeIdAddress","type":"address[]"},{"internalType":"struct MockController.CommitResult","name":"commitResult","type":"tuple","components":[{"internalType":"uint256","name":"groupEpoch","type":"uint256"},{"internalType":"uint256[4]","name":"publicKey","type":"uint256[4]"},{"internalType":"address[]","name":"disqualifiedNodes","type":"address[]"}]}]},{"internalType":"bool","name":"isStrictlyMajorityConsensusReached","type":"bool"},{"internalType":"uint256[4]","name":"publicKey","type":"uint256[4]"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"nodeRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"_adapterAddress","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setAdapterAddress"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"address[]","name":"committerAddresses","type":"address[]"}],"stateMutability":"nonpayable","type":"function","name":"setCommitters"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"bool","name":"isStrictlyMajorityConsensusReached","type":"bool"},{"internalType":"uint256[4]","name":"publicKey","type":"uint256[4]"},{"internalType":"address[]","name":"memberAddresses","type":"address[]"}],"stateMutability":"nonpayable","type":"function","name":"setGroup"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"memberIndex","type":"uint256"},{"internalType":"uint256[4]","name":"partialPublicKey","type":"uint256[4]"}],"stateMutability":"nonpayable","type":"function","name":"setMemberPartialPublicKey"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/","Staking-v0.1/=lib/Staking-v0.1/src/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","fx-portal/=lib/fx-portal/contracts/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/"],"optimizer":{"enabled":true,"runs":300},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"test/mock/MockController.sol":"MockController"},"evmVersion":"paris","libraries":{}},"sources":{"test/mock/MockController.sol":{"keccak256":"0x75a6fd1067d50fca998bdb718afeb498b37f0d775d67f6e163c813c9bbf8f92e","urls":["bzz-raw://d8d53cb3b18e614c692a1a9f5d73965e843efeb2ace2d61203c749040a4c71da","dweb:/ipfs/QmXuyWuV2hZxFsW1HFtuAzQcd4YSrixweM2cwYf5SeKEFd"],"license":"MIT"}},"version":1},"id":1} \ No newline at end of file +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_nodeRegistryAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "committer", + "type": "address" + } + ], + "name": "CommitDkgSuccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "globalEpoch", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "size", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assignmentBlockHeight", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "coordinatorAddress", + "type": "address" + } + ], + "name": "DkgTask", + "type": "event" + }, + { + "inputs": [], + "name": "adapterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "partialPublicKey", + "type": "bytes" + }, + { + "internalType": "address[]", + "name": "disqualifiedNodes", + "type": "address[]" + } + ], + "internalType": "struct IController.CommitDkgParams", + "name": "params", + "type": "tuple" + } + ], + "name": "commitDkg", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "globalEpoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "assignmentBlockHeight", + "type": "uint256" + }, + { + "internalType": "address", + "name": "coordinatorAddress", + "type": "address" + } + ], + "name": "emitDkgTaskEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "failureMessage", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getControllerConfig", + "outputs": [ + { + "internalType": "address", + "name": "nodeRegistryContractAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "adapterContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "disqualifiedNodePenaltyAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "defaultNumberOfCommitters", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "defaultDkgPhaseDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupMaxCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "idealNumberOfGroups", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dkgPostProcessReward", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + } + ], + "name": "getCoordinator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + } + ], + "name": "getGroup", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "nodeIdAddress", + "type": "address" + }, + { + "internalType": "uint256[4]", + "name": "partialPublicKey", + "type": "uint256[4]" + } + ], + "internalType": "struct IController.Member[]", + "name": "members", + "type": "tuple[]" + }, + { + "internalType": "address[]", + "name": "committers", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "nodeIdAddress", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "publicKey", + "type": "uint256[4]" + }, + { + "internalType": "address[]", + "name": "disqualifiedNodes", + "type": "address[]" + } + ], + "internalType": "struct IController.CommitResult", + "name": "commitResult", + "type": "tuple" + } + ], + "internalType": "struct IController.CommitCache[]", + "name": "commitCacheList", + "type": "tuple[]" + }, + { + "internalType": "bool", + "name": "isStrictlyMajorityConsensusReached", + "type": "bool" + }, + { + "internalType": "uint256[4]", + "name": "publicKey", + "type": "uint256[4]" + } + ], + "internalType": "struct IController.Group", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nodeRegistryAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_adapterAddress", + "type": "address" + } + ], + "name": "setAdapterAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "committerAddresses", + "type": "address[]" + } + ], + "name": "setCommitters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "coordinator", + "type": "address" + } + ], + "name": "setCoordinator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_message", + "type": "string" + } + ], + "name": "setFailureMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isStrictlyMajorityConsensusReached", + "type": "bool" + }, + { + "internalType": "uint256[4]", + "name": "publicKey", + "type": "uint256[4]" + }, + { + "internalType": "address[]", + "name": "memberAddresses", + "type": "address[]" + } + ], + "name": "setGroup", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "memberIndex", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "partialPublicKey", + "type": "uint256[4]" + } + ], + "name": "setMemberPartialPublicKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_shouldSucceed", + "type": "bool" + } + ], + "name": "setShouldSucceed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shouldSucceed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x6003805460ff60a01b1916600160a01b17905560a06040525f608090815260049061002a9082610119565b50348015610036575f5ffd5b50604051611626380380611626833981016040819052610055916101d3565b600280546001600160a01b039092166001600160a01b0319928316179055600380549091169055610200565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806100a957607f821691505b6020821081036100c757634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561011457805f5260205f20601f840160051c810160208510156100f25750805b601f840160051c820191505b81811015610111575f81556001016100fe565b50505b505050565b81516001600160401b0381111561013257610132610081565b610146816101408454610095565b846100cd565b6020601f821160018114610178575f83156101615750848201515b5f19600385901b1c1916600184901b178455610111565b5f84815260208120601f198516915b828110156101a75787850151825560209485019460019092019101610187565b50848210156101c457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f602082840312156101e3575f5ffd5b81516001600160a01b03811681146101f9575f5ffd5b9392505050565b6114198061020d5f395ff3fe608060405234801561000f575f5ffd5b50600436106100fb575f3560e01c80639e31ddb611610093578063d11b8e6811610063578063d11b8e6814610284578063e37eb96c146102d9578063e77deb2c146102ec578063fec10aa914610310575f5ffd5b80639e31ddb6146101e8578063b8c4d6ec14610214578063bb95b9d414610229578063ceb6065414610264575f5ffd5b806342424d6f116100ce57806342424d6f1461014d57806366c9aba6146101925780636971f096146101a5578063736eede5146101b8575f5ffd5b80630fc68a0c146100ff5780631bb1fd281461011457806326a93abe14610127578063368271ee1461013a575b5f5ffd5b61011261010d366004610b78565b610323565b005b610112610122366004610bfa565b61036f565b610112610135366004610cb8565b610396565b610112610148366004610d93565b6104ad565b61017561015b366004610de0565b5f908152600160205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020015b60405180910390f35b600354610175906001600160a01b031681565b6101126101b3366004610df7565b6104bd565b6101126101c6366004610e2a565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6101126101f6366004610e4a565b60038054911515600160a01b0260ff60a01b19909216919091179055565b61021c610505565b6040516101899190610e63565b610112610237366004610e98565b5f9182526001602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b610277610272366004610de0565b610591565b6040516101899190611024565b60025460038054604080516001600160a01b0394851681529390911660208401526103e89083015260056060830152606460808301819052600a60a084015260c083019190915260e082015261010001610189565b6101126102e73660046110f6565b610892565b60035461030090600160a01b900460ff1681565b6040519015158152602001610189565b600254610175906001600160a01b031681565b8587897fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee888888888860405161035d9594939291906111cc565b60405180910390a45050505050505050565b5f828152602081815260409091208251610391926005909201918401906108fd565b505050565b5f87815260208190526040902087815560018101879055600281018690556003810185905560078101805460ff19168515151790556103da60088201846004610960565b506103e8600482015f61098e565b5f5b825181101561048c576103fb6109af565b82600401604051806040016040528086858151811061041c5761041c61120b565b6020908102919091018101516001600160a01b0390811683529181018590528354600180820186555f95865294829020845160059092020180546001600160a01b031916919093161782558201519192909161047c918301906004610960565b5050600190920191506103ea9050565b5081516104a290600583019060208501906108fd565b505050505050505050565b60046104b982826112a2565b5050565b805f5f8581526020019081526020015f2060040183815481106104e2576104e261120b565b905f5260205f2090600502016001019060046104ff929190610960565b50505050565b600480546105129061121f565b80601f016020809104026020016040519081016040528092919081815260200182805461053e9061121f565b80156105895780601f1061056057610100808354040283529160200191610589565b820191905f5260205f20905b81548152906001019060200180831161056c57829003601f168201915b505050505081565b6105996109cd565b5f828152602081815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101561066e575f8481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831161064357505050505081525050815260200190600101906105f6565b505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116106af575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020015f905b8282101561083c578382905f5260205f2090600702016040518060400160405290815f820180548060200260200160405190810160405280929190818152602001828054801561077157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610753575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b8154815260200190600101908083116107ad57505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561082057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610802575b50505050508152505081525050815260200190600101906106fa565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831161086f575050505050815250509050919050565b600354600160a01b900460ff166108c757600460405162461bcd60e51b81526004016108be919061135d565b60405180910390fd5b60208101518151604051339291907fb6d83871231a16b87dc25f4380b04f133cde70e9335e505a082e351eb3c0d9ec905f90a450565b828054828255905f5260205f20908101928215610950579160200282015b8281111561095057825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019061091b565b5061095c929150610a1b565b5090565b8260048101928215610950579160200282015b82811115610950578251825591602001919060010190610973565b5080545f8255600502905f5260205f20908101906109ac9190610a2f565b50565b60405180608001604052806004906020820280368337509192915050565b6040518061012001604052805f81526020015f81526020015f81526020015f81526020016060815260200160608152602001606081526020015f15158152602001610a166109af565b905290565b5b8082111561095c575f8155600101610a1c565b8082111561095c5780546001600160a01b03191681555f6001820181905560028201819055600382018190556004820155600501610a2f565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715610a9f57610a9f610a68565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610ace57610ace610a68565b604052919050565b80356001600160a01b0381168114610aec575f5ffd5b919050565b5f82601f830112610b00575f5ffd5b813567ffffffffffffffff811115610b1a57610b1a610a68565b8060051b610b2a60208201610aa5565b91825260208185018101929081019086841115610b45575f5ffd5b6020860192505b83831015610b6e57610b5d83610ad6565b825260209283019290910190610b4c565b9695505050505050565b5f5f5f5f5f5f5f5f610100898b031215610b90575f5ffd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff811115610bc9575f5ffd5b610bd58b828c01610af1565b93505060c08901359150610beb60e08a01610ad6565b90509295985092959890939650565b5f5f60408385031215610c0b575f5ffd5b82359150602083013567ffffffffffffffff811115610c28575f5ffd5b610c3485828601610af1565b9150509250929050565b80358015158114610aec575f5ffd5b5f82601f830112610c5c575f5ffd5b6040516080810167ffffffffffffffff81118282101715610c7f57610c7f610a68565b604052806080840185811115610c93575f5ffd5b845b81811015610cad578035835260209283019201610c95565b509195945050505050565b5f5f5f5f5f5f5f610140888a031215610ccf575f5ffd5b87359650602088013595506040880135945060608801359350610cf460808901610c3e565b9250610d038960a08a01610c4d565b915061012088013567ffffffffffffffff811115610d1f575f5ffd5b610d2b8a828b01610af1565b91505092959891949750929550565b5f5f67ffffffffffffffff841115610d5457610d54610a68565b50601f8301601f1916602001610d6981610aa5565b915050828152838383011115610d7d575f5ffd5b828260208301375f602084830101529392505050565b5f60208284031215610da3575f5ffd5b813567ffffffffffffffff811115610db9575f5ffd5b8201601f81018413610dc9575f5ffd5b610dd884823560208401610d3a565b949350505050565b5f60208284031215610df0575f5ffd5b5035919050565b5f5f5f60c08486031215610e09575f5ffd5b8335925060208401359150610e218560408601610c4d565b90509250925092565b5f60208284031215610e3a575f5ffd5b610e4382610ad6565b9392505050565b5f60208284031215610e5a575f5ffd5b610e4382610c3e565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f60408385031215610ea9575f5ffd5b82359150610eb960208401610ad6565b90509250929050565b805f5b60048110156104ff578151845260209384019390910190600101610ec5565b5f8151808452602084019350602083015f5b82811015610f385781516001600160a01b03815116875260208101519050610f216020880182610ec2565b5060a0959095019460209190910190600101610ef6565b5093949350505050565b5f8151808452602084019350602083015f5b82811015610f385781516001600160a01b0316865260209586019590910190600101610f54565b5f82825180855260208501945060208160051b830101602085015f5b8381101561101857601f198584030188528151805160408552610fbd6040860182610f42565b9050602082015191508481036020860152815181526020820151610fe46020830182610ec2565b506040820151915060c060a082015261100060c0820183610f42565b60209a8b019a90955093909301925050600101610f97565b50909695505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201525f608083015161018060a08401526110696101a0840182610ee4565b905060a0840151601f198483030160c08501526110868282610f42565b91505060c0840151601f198483030160e08501526110a48282610f7b565b91505060e08401516110bb61010085018215159052565b506101008401516110d0610120850182610ec2565b509392505050565b5f82601f8301126110e7575f5ffd5b610e4383833560208501610d3a565b5f60208284031215611106575f5ffd5b813567ffffffffffffffff81111561111c575f5ffd5b820160a0818503121561112d575f5ffd5b611135610a7c565b8135815260208083013590820152604082013567ffffffffffffffff81111561115c575f5ffd5b611168868285016110d8565b604083015250606082013567ffffffffffffffff811115611187575f5ffd5b611193868285016110d8565b606083015250608082013567ffffffffffffffff8111156111b2575f5ffd5b6111be86828501610af1565b608083015250949350505050565b85815284602082015260a060408201525f6111ea60a0830186610f42565b90508360608301526001600160a01b03831660808301529695505050505050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061123357607f821691505b60208210810361125157634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561039157805f5260205f20601f840160051c8101602085101561127c5750805b601f840160051c820191505b8181101561129b575f8155600101611288565b5050505050565b815167ffffffffffffffff8111156112bc576112bc610a68565b6112d0816112ca845461121f565b84611257565b6020601f821160018114611302575f83156112eb5750848201515b5f19600385901b1c1916600184901b17845561129b565b5f84815260208120601f198516915b828110156113315787850151825560209485019460019092019101611311565b508482101561134e57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f5f835461136e8161121f565b806020860152600182165f811461138c57600181146113a857610cad565b60ff1983166040870152604082151560051b8701019350610cad565b865f5260205f205f5b838110156113d0578154888201604001526001909101906020016113b1565b870160400194505050919594505050505056fea264697066735822122039425e9b1165e299360925cb358b5f3eb6d4bfeb9812485ab2275ddb65f109ea64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b50600436106100fb575f3560e01c80639e31ddb611610093578063d11b8e6811610063578063d11b8e6814610284578063e37eb96c146102d9578063e77deb2c146102ec578063fec10aa914610310575f5ffd5b80639e31ddb6146101e8578063b8c4d6ec14610214578063bb95b9d414610229578063ceb6065414610264575f5ffd5b806342424d6f116100ce57806342424d6f1461014d57806366c9aba6146101925780636971f096146101a5578063736eede5146101b8575f5ffd5b80630fc68a0c146100ff5780631bb1fd281461011457806326a93abe14610127578063368271ee1461013a575b5f5ffd5b61011261010d366004610b78565b610323565b005b610112610122366004610bfa565b61036f565b610112610135366004610cb8565b610396565b610112610148366004610d93565b6104ad565b61017561015b366004610de0565b5f908152600160205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020015b60405180910390f35b600354610175906001600160a01b031681565b6101126101b3366004610df7565b6104bd565b6101126101c6366004610e2a565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6101126101f6366004610e4a565b60038054911515600160a01b0260ff60a01b19909216919091179055565b61021c610505565b6040516101899190610e63565b610112610237366004610e98565b5f9182526001602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b610277610272366004610de0565b610591565b6040516101899190611024565b60025460038054604080516001600160a01b0394851681529390911660208401526103e89083015260056060830152606460808301819052600a60a084015260c083019190915260e082015261010001610189565b6101126102e73660046110f6565b610892565b60035461030090600160a01b900460ff1681565b6040519015158152602001610189565b600254610175906001600160a01b031681565b8587897fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee888888888860405161035d9594939291906111cc565b60405180910390a45050505050505050565b5f828152602081815260409091208251610391926005909201918401906108fd565b505050565b5f87815260208190526040902087815560018101879055600281018690556003810185905560078101805460ff19168515151790556103da60088201846004610960565b506103e8600482015f61098e565b5f5b825181101561048c576103fb6109af565b82600401604051806040016040528086858151811061041c5761041c61120b565b6020908102919091018101516001600160a01b0390811683529181018590528354600180820186555f95865294829020845160059092020180546001600160a01b031916919093161782558201519192909161047c918301906004610960565b5050600190920191506103ea9050565b5081516104a290600583019060208501906108fd565b505050505050505050565b60046104b982826112a2565b5050565b805f5f8581526020019081526020015f2060040183815481106104e2576104e261120b565b905f5260205f2090600502016001019060046104ff929190610960565b50505050565b600480546105129061121f565b80601f016020809104026020016040519081016040528092919081815260200182805461053e9061121f565b80156105895780601f1061056057610100808354040283529160200191610589565b820191905f5260205f20905b81548152906001019060200180831161056c57829003601f168201915b505050505081565b6105996109cd565b5f828152602081815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101561066e575f8481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831161064357505050505081525050815260200190600101906105f6565b505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116106af575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020015f905b8282101561083c578382905f5260205f2090600702016040518060400160405290815f820180548060200260200160405190810160405280929190818152602001828054801561077157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610753575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b8154815260200190600101908083116107ad57505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561082057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610802575b50505050508152505081525050815260200190600101906106fa565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831161086f575050505050815250509050919050565b600354600160a01b900460ff166108c757600460405162461bcd60e51b81526004016108be919061135d565b60405180910390fd5b60208101518151604051339291907fb6d83871231a16b87dc25f4380b04f133cde70e9335e505a082e351eb3c0d9ec905f90a450565b828054828255905f5260205f20908101928215610950579160200282015b8281111561095057825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019061091b565b5061095c929150610a1b565b5090565b8260048101928215610950579160200282015b82811115610950578251825591602001919060010190610973565b5080545f8255600502905f5260205f20908101906109ac9190610a2f565b50565b60405180608001604052806004906020820280368337509192915050565b6040518061012001604052805f81526020015f81526020015f81526020015f81526020016060815260200160608152602001606081526020015f15158152602001610a166109af565b905290565b5b8082111561095c575f8155600101610a1c565b8082111561095c5780546001600160a01b03191681555f6001820181905560028201819055600382018190556004820155600501610a2f565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715610a9f57610a9f610a68565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610ace57610ace610a68565b604052919050565b80356001600160a01b0381168114610aec575f5ffd5b919050565b5f82601f830112610b00575f5ffd5b813567ffffffffffffffff811115610b1a57610b1a610a68565b8060051b610b2a60208201610aa5565b91825260208185018101929081019086841115610b45575f5ffd5b6020860192505b83831015610b6e57610b5d83610ad6565b825260209283019290910190610b4c565b9695505050505050565b5f5f5f5f5f5f5f5f610100898b031215610b90575f5ffd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff811115610bc9575f5ffd5b610bd58b828c01610af1565b93505060c08901359150610beb60e08a01610ad6565b90509295985092959890939650565b5f5f60408385031215610c0b575f5ffd5b82359150602083013567ffffffffffffffff811115610c28575f5ffd5b610c3485828601610af1565b9150509250929050565b80358015158114610aec575f5ffd5b5f82601f830112610c5c575f5ffd5b6040516080810167ffffffffffffffff81118282101715610c7f57610c7f610a68565b604052806080840185811115610c93575f5ffd5b845b81811015610cad578035835260209283019201610c95565b509195945050505050565b5f5f5f5f5f5f5f610140888a031215610ccf575f5ffd5b87359650602088013595506040880135945060608801359350610cf460808901610c3e565b9250610d038960a08a01610c4d565b915061012088013567ffffffffffffffff811115610d1f575f5ffd5b610d2b8a828b01610af1565b91505092959891949750929550565b5f5f67ffffffffffffffff841115610d5457610d54610a68565b50601f8301601f1916602001610d6981610aa5565b915050828152838383011115610d7d575f5ffd5b828260208301375f602084830101529392505050565b5f60208284031215610da3575f5ffd5b813567ffffffffffffffff811115610db9575f5ffd5b8201601f81018413610dc9575f5ffd5b610dd884823560208401610d3a565b949350505050565b5f60208284031215610df0575f5ffd5b5035919050565b5f5f5f60c08486031215610e09575f5ffd5b8335925060208401359150610e218560408601610c4d565b90509250925092565b5f60208284031215610e3a575f5ffd5b610e4382610ad6565b9392505050565b5f60208284031215610e5a575f5ffd5b610e4382610c3e565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f60408385031215610ea9575f5ffd5b82359150610eb960208401610ad6565b90509250929050565b805f5b60048110156104ff578151845260209384019390910190600101610ec5565b5f8151808452602084019350602083015f5b82811015610f385781516001600160a01b03815116875260208101519050610f216020880182610ec2565b5060a0959095019460209190910190600101610ef6565b5093949350505050565b5f8151808452602084019350602083015f5b82811015610f385781516001600160a01b0316865260209586019590910190600101610f54565b5f82825180855260208501945060208160051b830101602085015f5b8381101561101857601f198584030188528151805160408552610fbd6040860182610f42565b9050602082015191508481036020860152815181526020820151610fe46020830182610ec2565b506040820151915060c060a082015261100060c0820183610f42565b60209a8b019a90955093909301925050600101610f97565b50909695505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201525f608083015161018060a08401526110696101a0840182610ee4565b905060a0840151601f198483030160c08501526110868282610f42565b91505060c0840151601f198483030160e08501526110a48282610f7b565b91505060e08401516110bb61010085018215159052565b506101008401516110d0610120850182610ec2565b509392505050565b5f82601f8301126110e7575f5ffd5b610e4383833560208501610d3a565b5f60208284031215611106575f5ffd5b813567ffffffffffffffff81111561111c575f5ffd5b820160a0818503121561112d575f5ffd5b611135610a7c565b8135815260208083013590820152604082013567ffffffffffffffff81111561115c575f5ffd5b611168868285016110d8565b604083015250606082013567ffffffffffffffff811115611187575f5ffd5b611193868285016110d8565b606083015250608082013567ffffffffffffffff8111156111b2575f5ffd5b6111be86828501610af1565b608083015250949350505050565b85815284602082015260a060408201525f6111ea60a0830186610f42565b90508360608301526001600160a01b03831660808301529695505050505050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061123357607f821691505b60208210810361125157634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561039157805f5260205f20601f840160051c8101602085101561127c5750805b601f840160051c820191505b8181101561129b575f8155600101611288565b5050505050565b815167ffffffffffffffff8111156112bc576112bc610a68565b6112d0816112ca845461121f565b84611257565b6020601f821160018114611302575f83156112eb5750848201515b5f19600385901b1c1916600184901b17845561129b565b5f84815260208120601f198516915b828110156113315787850151825560209485019460019092019101611311565b508482101561134e57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f5f835461136e8161121f565b806020860152600182165f811461138c57600181146113a857610cad565b60ff1983166040870152604082151560051b8701019350610cad565b865f5260205f205f5b838110156113d0578154888201604001526001909101906020016113b1565b870160400194505050919594505050505056fea264697066735822122039425e9b1165e299360925cb358b5f3eb6d4bfeb9812485ab2275ddb65f109ea64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "metadata": {}, + "id": 1 +} diff --git a/crates/arpa-node/test-contract/MockController.sol b/crates/arpa-node/test-contract/MockController.sol index ed9ffb7b..5141a5e8 100644 --- a/crates/arpa-node/test-contract/MockController.sol +++ b/crates/arpa-node/test-contract/MockController.sol @@ -1,39 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -contract MockController { +import "./IController.sol"; + +contract MockController is IController { mapping(uint256 => Group) private groups; + mapping(uint256 => address) private _coordinators; address public nodeRegistryAddress; address public adapterAddress; - - struct Member { - address nodeIdAddress; - uint256[4] partialPublicKey; - } - - struct CommitResult { - uint256 groupEpoch; - uint256[4] publicKey; - address[] disqualifiedNodes; - } - - struct CommitCache { - address[] nodeIdAddress; - CommitResult commitResult; - } - - struct Group { - uint256 index; - uint256 epoch; - uint256 size; - uint256 threshold; - Member[] members; - address[] committers; - CommitCache[] commitCacheList; - bool isStrictlyMajorityConsensusReached; - uint256[4] publicKey; - } - + + bool public shouldSucceed = true; + string public failureMessage = ""; + event DkgTask( uint256 indexed globalEpoch, uint256 indexed groupIndex, @@ -44,43 +22,45 @@ contract MockController { uint256 assignmentBlockHeight, address coordinatorAddress ); - + + event CommitDkgSuccess( + uint256 indexed groupIndex, + uint256 indexed groupEpoch, + address indexed committer + ); + constructor(address _nodeRegistryAddress) { nodeRegistryAddress = _nodeRegistryAddress; adapterAddress = address(0); } - - function getControllerConfig() - external - view - returns ( - address nodeRegistryContractAddress, - address adapterContractAddress, - uint256 disqualifiedNodePenaltyAmount, - uint256 defaultNumberOfCommitters, - uint256 defaultDkgPhaseDuration, - uint256 groupMaxCapacity, - uint256 idealNumberOfGroups, - uint256 dkgPostProcessReward - ) - { + + function getControllerConfig() external view returns ( + address nodeRegistryContractAddress, + address adapterContractAddress, + uint256 disqualifiedNodePenaltyAmount, + uint256 defaultNumberOfCommitters, + uint256 defaultDkgPhaseDuration, + uint256 groupMaxCapacity, + uint256 idealNumberOfGroups, + uint256 dkgPostProcessReward + ) { return ( nodeRegistryAddress, adapterAddress, - 1000, // disqualifiedNodePenaltyAmount - 5, // defaultNumberOfCommitters - 100, // defaultDkgPhaseDuration - 10, // groupMaxCapacity - 3, // idealNumberOfGroups - 100 // dkgPostProcessReward + 1000, // disqualifiedNodePenaltyAmount + 5, // defaultNumberOfCommitters + 100, // defaultDkgPhaseDuration + 10, // groupMaxCapacity + 3, // idealNumberOfGroups + 100 // dkgPostProcessReward ); } - + function setGroup( - uint256 groupIndex, - uint256 epoch, - uint256 size, - uint256 threshold, + uint256 groupIndex, + uint256 epoch, + uint256 size, + uint256 threshold, bool isStrictlyMajorityConsensusReached, uint256[4] memory publicKey, address[] memory memberAddresses @@ -92,30 +72,46 @@ contract MockController { group.threshold = threshold; group.isStrictlyMajorityConsensusReached = isStrictlyMajorityConsensusReached; group.publicKey = publicKey; - + delete group.members; - for (uint256 i = 0; i < memberAddresses.length; i++) { + for (uint i = 0; i < memberAddresses.length; i++) { uint256[4] memory emptyPartialPublicKey; - group.members.push(Member({nodeIdAddress: memberAddresses[i], partialPublicKey: emptyPartialPublicKey})); + group.members.push(Member({ + nodeIdAddress: memberAddresses[i], + partialPublicKey: emptyPartialPublicKey + })); } - + group.committers = memberAddresses; } - - function setMemberPartialPublicKey(uint256 groupIndex, uint256 memberIndex, uint256[4] memory partialPublicKey) - external - { + + function setMemberPartialPublicKey( + uint256 groupIndex, + uint256 memberIndex, + uint256[4] memory partialPublicKey + ) external { groups[groupIndex].members[memberIndex].partialPublicKey = partialPublicKey; } - - function setCommitters(uint256 groupIndex, address[] memory committerAddresses) external { + + function setCommitters( + uint256 groupIndex, + address[] memory committerAddresses + ) external { groups[groupIndex].committers = committerAddresses; } - - function getGroup(uint256 groupIndex) public view returns (Group memory) { + + function getGroup(uint256 groupIndex) public view override(IController) returns (Group memory) { return groups[groupIndex]; } - + + function setCoordinator(uint256 groupIndex, address coordinator) external { + _coordinators[groupIndex] = coordinator; + } + + function getCoordinator(uint256 groupIndex) public view override(IController) returns (address) { + return _coordinators[groupIndex]; + } + function emitDkgTaskEvent( uint256 globalEpoch, uint256 groupIndex, @@ -127,11 +123,38 @@ contract MockController { address coordinatorAddress ) external { emit DkgTask( - globalEpoch, groupIndex, groupEpoch, size, threshold, members, assignmentBlockHeight, coordinatorAddress + globalEpoch, + groupIndex, + groupEpoch, + size, + threshold, + members, + assignmentBlockHeight, + coordinatorAddress ); } - + function setAdapterAddress(address _adapterAddress) external { adapterAddress = _adapterAddress; } -} + + function commitDkg(CommitDkgParams memory params) external override(IController) { + if (!shouldSucceed) { + revert(failureMessage); + } + + emit CommitDkgSuccess( + params.groupIndex, + params.groupEpoch, + msg.sender + ); + } + + function setShouldSucceed(bool _shouldSucceed) external { + shouldSucceed = _shouldSucceed; + } + + function setFailureMessage(string memory _message) external { + failureMessage = _message; + } +} \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockControllerRelayer.json b/crates/arpa-node/test-contract/MockControllerRelayer.json new file mode 100644 index 00000000..7aa56e82 --- /dev/null +++ b/crates/arpa-node/test-contract/MockControllerRelayer.json @@ -0,0 +1,123 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "groupEpoch", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "committer", + "type": "address" + } + ], + "name": "GroupRelayed", + "type": "event" + }, + { + "inputs": [], + "name": "failureMessage", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + } + ], + "name": "relayGroup", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_message", + "type": "string" + } + ], + "name": "setFailureMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_shouldSucceed", + "type": "bool" + } + ], + "name": "setShouldSucceed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shouldSucceed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x5f805460ff19166001908117825560a060405260809182529061002290826100cc565b5034801561002e575f5ffd5b50610186565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061005c57607f821691505b60208210810361007a57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156100c757805f5260205f20601f840160051c810160208510156100a55750805b601f840160051c820191505b818110156100c4575f81556001016100b1565b50505b505050565b81516001600160401b038111156100e5576100e5610034565b6100f9816100f38454610048565b84610080565b6020601f82116001811461012b575f83156101145750848201515b5f19600385901b1c1916600184901b1784556100c4565b5f84815260208120601f198516915b8281101561015a578785015182556020948501946001909201910161013a565b508482101561017757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b61051f806101935f395ff3fe608060405234801561000f575f5ffd5b5060043610610055575f3560e01c8063368271ee146100595780639e31ddb61461006e578063b8c4d6ec1461008e578063ddc393dd146100ac578063e77deb2c146100bf575b5f5ffd5b61006c6100673660046101f5565b6100db565b005b61006c61007c3660046102a8565b5f805460ff1916911515919091179055565b6100966100eb565b6040516100a391906102ce565b60405180910390f35b61006c6100ba366004610303565b610177565b5f546100cb9060ff1681565b60405190151581526020016100a3565b60016100e782826103a7565b5050565b600180546100f890610323565b80601f016020809104026020016040519081016040528092919081815260200182805461012490610323565b801561016f5780601f106101465761010080835404028352916020019161016f565b820191905f5260205f20905b81548152906001019060200180831161015257829003601f168201915b505050505081565b5f5460ff166101a457600160405162461bcd60e51b815260040161019b9190610462565b60405180910390fd5b6040516001808252339183907ff8767d2ca0028e8ef4268cfb13b6e4f8c257b781b0dc13704a0204b2ad33acf19060200160405180910390a45050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610205575f5ffd5b813567ffffffffffffffff81111561021b575f5ffd5b8201601f8101841361022b575f5ffd5b803567ffffffffffffffff811115610245576102456101e1565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610274576102746101e1565b60405281815282820160200186101561028b575f5ffd5b816020840160208301375f91810160200191909152949350505050565b5f602082840312156102b8575f5ffd5b813580151581146102c7575f5ffd5b9392505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f60408385031215610314575f5ffd5b50508035926020909101359150565b600181811c9082168061033757607f821691505b60208210810361035557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156103a257805f5260205f20601f840160051c810160208510156103805750805b601f840160051c820191505b8181101561039f575f815560010161038c565b50505b505050565b815167ffffffffffffffff8111156103c1576103c16101e1565b6103d5816103cf8454610323565b8461035b565b6020601f821160018114610407575f83156103f05750848201515b5f19600385901b1c1916600184901b17845561039f565b5f84815260208120601f198516915b828110156104365787850151825560209485019460019092019101610416565b508482101561045357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f5f835461047381610323565b806020860152600182165f811461049157600181146104ad576104de565b60ff1983166040870152604082151560051b87010193506104de565b865f5260205f205f5b838110156104d5578154888201604001526001909101906020016104b6565b87016040019450505b50919594505050505056fea2646970667358221220942970d25db035be21dd107b73f5689313711cc2300e91ac3a58446e9cef943164736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b5060043610610055575f3560e01c8063368271ee146100595780639e31ddb61461006e578063b8c4d6ec1461008e578063ddc393dd146100ac578063e77deb2c146100bf575b5f5ffd5b61006c6100673660046101f5565b6100db565b005b61006c61007c3660046102a8565b5f805460ff1916911515919091179055565b6100966100eb565b6040516100a391906102ce565b60405180910390f35b61006c6100ba366004610303565b610177565b5f546100cb9060ff1681565b60405190151581526020016100a3565b60016100e782826103a7565b5050565b600180546100f890610323565b80601f016020809104026020016040519081016040528092919081815260200182805461012490610323565b801561016f5780601f106101465761010080835404028352916020019161016f565b820191905f5260205f20905b81548152906001019060200180831161015257829003601f168201915b505050505081565b5f5460ff166101a457600160405162461bcd60e51b815260040161019b9190610462565b60405180910390fd5b6040516001808252339183907ff8767d2ca0028e8ef4268cfb13b6e4f8c257b781b0dc13704a0204b2ad33acf19060200160405180910390a45050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610205575f5ffd5b813567ffffffffffffffff81111561021b575f5ffd5b8201601f8101841361022b575f5ffd5b803567ffffffffffffffff811115610245576102456101e1565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610274576102746101e1565b60405281815282820160200186101561028b575f5ffd5b816020840160208301375f91810160200191909152949350505050565b5f602082840312156102b8575f5ffd5b813580151581146102c7575f5ffd5b9392505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f5f60408385031215610314575f5ffd5b50508035926020909101359150565b600181811c9082168061033757607f821691505b60208210810361035557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156103a257805f5260205f20601f840160051c810160208510156103805750805b601f840160051c820191505b8181101561039f575f815560010161038c565b50505b505050565b815167ffffffffffffffff8111156103c1576103c16101e1565b6103d5816103cf8454610323565b8461035b565b6020601f821160018114610407575f83156103f05750848201515b5f19600385901b1c1916600184901b17845561039f565b5f84815260208120601f198516915b828110156104365787850151825560209485019460019092019101610416565b508482101561045357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f5f835461047381610323565b806020860152600182165f811461049157600181146104ad576104de565b60ff1983166040870152604082151560051b87010193506104de565b865f5260205f205f5b838110156104d5578154888201604001526001909101906020016104b6565b87016040019450505b50919594505050505056fea2646970667358221220942970d25db035be21dd107b73f5689313711cc2300e91ac3a58446e9cef943164736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "metadata": {}, + "id": 1 +} diff --git a/crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol b/crates/arpa-node/test-contract/MockControllerRelayer.sol similarity index 100% rename from crates/arpa-node/src/mock_contracts/MockControllerRelayer.sol rename to crates/arpa-node/test-contract/MockControllerRelayer.sol diff --git a/crates/arpa-node/test-contract/MockCoordinator.json b/crates/arpa-node/test-contract/MockCoordinator.json new file mode 100644 index 00000000..5f862eaa --- /dev/null +++ b/crates/arpa-node/test-contract/MockCoordinator.json @@ -0,0 +1,393 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "clearAllData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPhase", + "outputs": [ + { + "internalType": "int8", + "name": "", + "type": "int8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "dkgKeys", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "dkgThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDataStats", + "outputs": [ + { + "internalType": "uint256", + "name": "keysCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "participantsCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sharesCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "responsesCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "justificationsCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDkgKeys", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getJustifications", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getParticipants", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getResponses", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getShares", + "outputs": [ + { + "internalType": "bytes[]", + "name": "", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inPhase", + "outputs": [ + { + "internalType": "int8", + "name": "", + "type": "int8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "justificationsData", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "participants", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "responsesData", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int8", + "name": "_phase", + "type": "int8" + } + ], + "name": "setCurrentPhase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "_keys", + "type": "bytes[]" + } + ], + "name": "setDkgKeys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "_justifications", + "type": "bytes[]" + } + ], + "name": "setJustifications", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_participants", + "type": "address[]" + } + ], + "name": "setParticipants", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "_responses", + "type": "bytes[]" + } + ], + "name": "setResponses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "_shares", + "type": "bytes[]" + } + ], + "name": "setShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "_participants", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "_keys", + "type": "bytes[]" + }, + { + "internalType": "bytes[]", + "name": "_shares", + "type": "bytes[]" + }, + { + "internalType": "bytes[]", + "name": "_responses", + "type": "bytes[]" + }, + { + "internalType": "bytes[]", + "name": "_justifications", + "type": "bytes[]" + } + ], + "name": "setupTestScenario", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "sharesData", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x60806040526006805460ff191690553480156018575f5ffd5b506040516110fb3803806110fb833981016040819052603591603b565b5f556051565b5f60208284031215604a575f5ffd5b5051919050565b61109d8061005e5f395ff3fe608060405234801561000f575f5ffd5b506004361061015b575f3560e01c80639e174653116100c3578063cc5ef00911610088578063e004af5c11610063578063e004af5c14610314578063f24e816e14610327578063fb54cde11461033a575f5ffd5b8063cc5ef009146102f1578063d6b6cf20146102f9578063d73fe0aa1461030c575f5ffd5b80639e17465314610298578063a2012097146102ab578063b0ef8179146102b3578063bfeb1f3f146102c8578063c2704024146102db575f5ffd5b806342ab089e116101235780635aa68ac0116100fe5780635aa68ac01461025d57806361e1d923146102725780636489dc9014610285575f5ffd5b806342ab089e146101ec5780634e3874a01461021057806353c0fefd14610226575f5ffd5b8063055ad42e1461015f5780630d03e7be146101825780631ab78c3e14610197578063221f9511146101b757806335c1d349146101c1575b5f5ffd5b60065461016b905f0b81565b6040515f9190910b81526020015b60405180910390f35b610195610190366004610bd3565b61034d565b005b6101aa6101a5366004610c0d565b610364565b6040516101799190610c52565b6006545f0b61016b565b6101d46101cf366004610c0d565b61040a565b6040516001600160a01b039091168152602001610179565b6101956101fa366004610c6b565b6006805460ff191660ff92909216919091179055565b610218610432565b604051610179929190610ce4565b600154600254600354600454600554604080519586526020860194909452928401919091526060830152608082015260a001610179565b61026561050f565b6040516101799190610cfc565b610195610280366004610bd3565b61056f565b6101aa610293366004610c0d565b610582565b6101956102a6366004610db6565b610591565b61019561060e565b6102bb610654565b6040516101799190610ea1565b6101956102d6366004610bd3565b610728565b6102e35f5481565b604051908152602001610179565b6102bb61073b565b610195610307366004610eb3565b610806565b6102bb610822565b610195610322366004610ef7565b6108ed565b6101aa610335366004610c0d565b610900565b6101aa610348366004610c0d565b61090f565b805161036090600390602084019061091e565b5050565b60058181548110610373575f80fd5b905f5260205f20015f91509050805461038b90610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546103b790610f29565b80156104025780601f106103d957610100808354040283529160200191610402565b820191905f5260205f20905b8154815290600101906020018083116103e557829003601f168201915b505050505081565b60028181548110610419575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f60605f54600180805480602002602001604051908101604052809291908181526020015f905b82821015610501578382905f5260205f2001805461047690610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546104a290610f29565b80156104ed5780601f106104c4576101008083540402835291602001916104ed565b820191905f5260205f20905b8154815290600101906020018083116104d057829003601f168201915b505050505081526020019060010190610459565b505050509050915091509091565b6060600280548060200260200160405190810160405280929190818152602001828054801561056557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610547575b5050505050905090565b805161036090600490602084019061091e565b60038181548110610373575f80fd5b5f86905584516105a8906002906020880190610972565b5083516105bc90600190602087019061091e565b5082516105d090600390602086019061091e565b5081516105e490600490602085019061091e565b5080516105f890600590602084019061091e565b50506006805460ff191660011790555050505050565b61061960015f6109de565b61062460025f6109fc565b61062f60035f6109de565b61063a60045f6109de565b61064560055f6109de565b6006805460ff191690555f8055565b60606005805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461069490610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546106c090610f29565b801561070b5780601f106106e25761010080835404028352916020019161070b565b820191905f5260205f20905b8154815290600101906020018083116106ee57829003601f168201915b505050505081526020019060010190610677565b50505050905090565b805161036090600590602084019061091e565b60606004805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461077b90610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546107a790610f29565b80156107f25780601f106107c9576101008083540402835291602001916107f2565b820191905f5260205f20905b8154815290600101906020018083116107d557829003601f168201915b50505050508152602001906001019061075e565b5f829055805161081d90600190602084019061091e565b505050565b60606003805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461086290610f29565b80601f016020809104026020016040519081016040528092919081815260200182805461088e90610f29565b80156108d95780601f106108b0576101008083540402835291602001916108d9565b820191905f5260205f20905b8154815290600101906020018083116108bc57829003601f168201915b505050505081526020019060010190610845565b8051610360906002906020840190610972565b60018181548110610373575f80fd5b60048181548110610373575f80fd5b828054828255905f5260205f20908101928215610962579160200282015b8281111561096257825182906109529082610fac565b509160200191906001019061093c565b5061096e929150610a17565b5090565b828054828255905f5260205f209081019282156109d2579160200282015b828111156109d2578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190610990565b5061096e929150610a33565b5080545f8255905f5260205f20908101906109f99190610a17565b50565b5080545f8255905f5260205f20908101906109f99190610a33565b8082111561096e575f610a2a8282610a47565b50600101610a17565b5b8082111561096e575f8155600101610a34565b508054610a5390610f29565b5f825580601f10610a62575050565b601f0160209004905f5260205f20908101906109f99190610a33565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610abb57610abb610a7e565b604052919050565b5f67ffffffffffffffff821115610adc57610adc610a7e565b5060051b60200190565b5f82601f830112610af5575f5ffd5b8135610b08610b0382610ac3565b610a92565b8082825260208201915060208360051b860101925085831115610b29575f5ffd5b602085015b83811015610bc957803567ffffffffffffffff811115610b4c575f5ffd5b8601603f81018813610b5c575f5ffd5b602081013567ffffffffffffffff811115610b7957610b79610a7e565b610b8c601f8201601f1916602001610a92565b8181526040838301018a1015610ba0575f5ffd5b816040840160208301375f60208383010152808652505050602083019250602081019050610b2e565b5095945050505050565b5f60208284031215610be3575f5ffd5b813567ffffffffffffffff811115610bf9575f5ffd5b610c0584828501610ae6565b949350505050565b5f60208284031215610c1d575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f610c646020830184610c24565b9392505050565b5f60208284031215610c7b575f5ffd5b8135805f0b8114610c64575f5ffd5b5f82825180855260208501945060208160051b830101602085015f5b83811015610cd857601f19858403018852610cc2838351610c24565b6020988901989093509190910190600101610ca6565b50909695505050505050565b828152604060208201525f610c056040830184610c8a565b602080825282518282018190525f918401906040840190835b81811015610d3c5783516001600160a01b0316835260209384019390920191600101610d15565b509095945050505050565b5f82601f830112610d56575f5ffd5b8135610d64610b0382610ac3565b8082825260208201915060208360051b860101925085831115610d85575f5ffd5b602085015b83811015610bc95780356001600160a01b0381168114610da8575f5ffd5b835260209283019201610d8a565b5f5f5f5f5f5f60c08789031215610dcb575f5ffd5b86359550602087013567ffffffffffffffff811115610de8575f5ffd5b610df489828a01610d47565b955050604087013567ffffffffffffffff811115610e10575f5ffd5b610e1c89828a01610ae6565b945050606087013567ffffffffffffffff811115610e38575f5ffd5b610e4489828a01610ae6565b935050608087013567ffffffffffffffff811115610e60575f5ffd5b610e6c89828a01610ae6565b92505060a087013567ffffffffffffffff811115610e88575f5ffd5b610e9489828a01610ae6565b9150509295509295509295565b602081525f610c646020830184610c8a565b5f5f60408385031215610ec4575f5ffd5b82359150602083013567ffffffffffffffff811115610ee1575f5ffd5b610eed85828601610ae6565b9150509250929050565b5f60208284031215610f07575f5ffd5b813567ffffffffffffffff811115610f1d575f5ffd5b610c0584828501610d47565b600181811c90821680610f3d57607f821691505b602082108103610f5b57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561081d57805f5260205f20601f840160051c81016020851015610f865750805b601f840160051c820191505b81811015610fa5575f8155600101610f92565b5050505050565b815167ffffffffffffffff811115610fc657610fc6610a7e565b610fda81610fd48454610f29565b84610f61565b6020601f82116001811461100c575f8315610ff55750848201515b5f19600385901b1c1916600184901b178455610fa5565b5f84815260208120601f198516915b8281101561103b578785015182556020948501946001909201910161101b565b508482101561105857868401515f19600387901b60f8161c191681555b50505050600190811b0190555056fea2646970667358221220f5d4e2a06c139358152f64837888a161758bc0bf7f2dce8e32715815163235aa64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608060405234801561000f575f5ffd5b506004361061015b575f3560e01c80639e174653116100c3578063cc5ef00911610088578063e004af5c11610063578063e004af5c14610314578063f24e816e14610327578063fb54cde11461033a575f5ffd5b8063cc5ef009146102f1578063d6b6cf20146102f9578063d73fe0aa1461030c575f5ffd5b80639e17465314610298578063a2012097146102ab578063b0ef8179146102b3578063bfeb1f3f146102c8578063c2704024146102db575f5ffd5b806342ab089e116101235780635aa68ac0116100fe5780635aa68ac01461025d57806361e1d923146102725780636489dc9014610285575f5ffd5b806342ab089e146101ec5780634e3874a01461021057806353c0fefd14610226575f5ffd5b8063055ad42e1461015f5780630d03e7be146101825780631ab78c3e14610197578063221f9511146101b757806335c1d349146101c1575b5f5ffd5b60065461016b905f0b81565b6040515f9190910b81526020015b60405180910390f35b610195610190366004610bd3565b61034d565b005b6101aa6101a5366004610c0d565b610364565b6040516101799190610c52565b6006545f0b61016b565b6101d46101cf366004610c0d565b61040a565b6040516001600160a01b039091168152602001610179565b6101956101fa366004610c6b565b6006805460ff191660ff92909216919091179055565b610218610432565b604051610179929190610ce4565b600154600254600354600454600554604080519586526020860194909452928401919091526060830152608082015260a001610179565b61026561050f565b6040516101799190610cfc565b610195610280366004610bd3565b61056f565b6101aa610293366004610c0d565b610582565b6101956102a6366004610db6565b610591565b61019561060e565b6102bb610654565b6040516101799190610ea1565b6101956102d6366004610bd3565b610728565b6102e35f5481565b604051908152602001610179565b6102bb61073b565b610195610307366004610eb3565b610806565b6102bb610822565b610195610322366004610ef7565b6108ed565b6101aa610335366004610c0d565b610900565b6101aa610348366004610c0d565b61090f565b805161036090600390602084019061091e565b5050565b60058181548110610373575f80fd5b905f5260205f20015f91509050805461038b90610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546103b790610f29565b80156104025780601f106103d957610100808354040283529160200191610402565b820191905f5260205f20905b8154815290600101906020018083116103e557829003601f168201915b505050505081565b60028181548110610419575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f60605f54600180805480602002602001604051908101604052809291908181526020015f905b82821015610501578382905f5260205f2001805461047690610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546104a290610f29565b80156104ed5780601f106104c4576101008083540402835291602001916104ed565b820191905f5260205f20905b8154815290600101906020018083116104d057829003601f168201915b505050505081526020019060010190610459565b505050509050915091509091565b6060600280548060200260200160405190810160405280929190818152602001828054801561056557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610547575b5050505050905090565b805161036090600490602084019061091e565b60038181548110610373575f80fd5b5f86905584516105a8906002906020880190610972565b5083516105bc90600190602087019061091e565b5082516105d090600390602086019061091e565b5081516105e490600490602085019061091e565b5080516105f890600590602084019061091e565b50506006805460ff191660011790555050505050565b61061960015f6109de565b61062460025f6109fc565b61062f60035f6109de565b61063a60045f6109de565b61064560055f6109de565b6006805460ff191690555f8055565b60606005805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461069490610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546106c090610f29565b801561070b5780601f106106e25761010080835404028352916020019161070b565b820191905f5260205f20905b8154815290600101906020018083116106ee57829003601f168201915b505050505081526020019060010190610677565b50505050905090565b805161036090600590602084019061091e565b60606004805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461077b90610f29565b80601f01602080910402602001604051908101604052809291908181526020018280546107a790610f29565b80156107f25780601f106107c9576101008083540402835291602001916107f2565b820191905f5260205f20905b8154815290600101906020018083116107d557829003601f168201915b50505050508152602001906001019061075e565b5f829055805161081d90600190602084019061091e565b505050565b60606003805480602002602001604051908101604052809291908181526020015f905b8282101561071f578382905f5260205f2001805461086290610f29565b80601f016020809104026020016040519081016040528092919081815260200182805461088e90610f29565b80156108d95780601f106108b0576101008083540402835291602001916108d9565b820191905f5260205f20905b8154815290600101906020018083116108bc57829003601f168201915b505050505081526020019060010190610845565b8051610360906002906020840190610972565b60018181548110610373575f80fd5b60048181548110610373575f80fd5b828054828255905f5260205f20908101928215610962579160200282015b8281111561096257825182906109529082610fac565b509160200191906001019061093c565b5061096e929150610a17565b5090565b828054828255905f5260205f209081019282156109d2579160200282015b828111156109d2578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190610990565b5061096e929150610a33565b5080545f8255905f5260205f20908101906109f99190610a17565b50565b5080545f8255905f5260205f20908101906109f99190610a33565b8082111561096e575f610a2a8282610a47565b50600101610a17565b5b8082111561096e575f8155600101610a34565b508054610a5390610f29565b5f825580601f10610a62575050565b601f0160209004905f5260205f20908101906109f99190610a33565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610abb57610abb610a7e565b604052919050565b5f67ffffffffffffffff821115610adc57610adc610a7e565b5060051b60200190565b5f82601f830112610af5575f5ffd5b8135610b08610b0382610ac3565b610a92565b8082825260208201915060208360051b860101925085831115610b29575f5ffd5b602085015b83811015610bc957803567ffffffffffffffff811115610b4c575f5ffd5b8601603f81018813610b5c575f5ffd5b602081013567ffffffffffffffff811115610b7957610b79610a7e565b610b8c601f8201601f1916602001610a92565b8181526040838301018a1015610ba0575f5ffd5b816040840160208301375f60208383010152808652505050602083019250602081019050610b2e565b5095945050505050565b5f60208284031215610be3575f5ffd5b813567ffffffffffffffff811115610bf9575f5ffd5b610c0584828501610ae6565b949350505050565b5f60208284031215610c1d575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f610c646020830184610c24565b9392505050565b5f60208284031215610c7b575f5ffd5b8135805f0b8114610c64575f5ffd5b5f82825180855260208501945060208160051b830101602085015f5b83811015610cd857601f19858403018852610cc2838351610c24565b6020988901989093509190910190600101610ca6565b50909695505050505050565b828152604060208201525f610c056040830184610c8a565b602080825282518282018190525f918401906040840190835b81811015610d3c5783516001600160a01b0316835260209384019390920191600101610d15565b509095945050505050565b5f82601f830112610d56575f5ffd5b8135610d64610b0382610ac3565b8082825260208201915060208360051b860101925085831115610d85575f5ffd5b602085015b83811015610bc95780356001600160a01b0381168114610da8575f5ffd5b835260209283019201610d8a565b5f5f5f5f5f5f60c08789031215610dcb575f5ffd5b86359550602087013567ffffffffffffffff811115610de8575f5ffd5b610df489828a01610d47565b955050604087013567ffffffffffffffff811115610e10575f5ffd5b610e1c89828a01610ae6565b945050606087013567ffffffffffffffff811115610e38575f5ffd5b610e4489828a01610ae6565b935050608087013567ffffffffffffffff811115610e60575f5ffd5b610e6c89828a01610ae6565b92505060a087013567ffffffffffffffff811115610e88575f5ffd5b610e9489828a01610ae6565b9150509295509295509295565b602081525f610c646020830184610c8a565b5f5f60408385031215610ec4575f5ffd5b82359150602083013567ffffffffffffffff811115610ee1575f5ffd5b610eed85828601610ae6565b9150509250929050565b5f60208284031215610f07575f5ffd5b813567ffffffffffffffff811115610f1d575f5ffd5b610c0584828501610d47565b600181811c90821680610f3d57607f821691505b602082108103610f5b57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561081d57805f5260205f20601f840160051c81016020851015610f865750805b601f840160051c820191505b81811015610fa5575f8155600101610f92565b5050505050565b815167ffffffffffffffff811115610fc657610fc6610a7e565b610fda81610fd48454610f29565b84610f61565b6020601f82116001811461100c575f8315610ff55750848201515b5f19600385901b1c1916600184901b178455610fa5565b5f84815260208120601f198516915b8281101561103b578785015182556020948501946001909201910161101b565b508482101561105857868401515f19600387901b60f8161c191681555b50505050600190811b0190555056fea2646970667358221220f5d4e2a06c139358152f64837888a161758bc0bf7f2dce8e32715815163235aa64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "metadata": {}, + "id": 1 +} diff --git a/crates/arpa-node/src/mock_contracts/MockCoordinator.sol b/crates/arpa-node/test-contract/MockCoordinator.sol similarity index 100% rename from crates/arpa-node/src/mock_contracts/MockCoordinator.sol rename to crates/arpa-node/test-contract/MockCoordinator.sol diff --git a/crates/arpa-node/test-contract/MockNodeRegistry.json b/crates/arpa-node/test-contract/MockNodeRegistry.json index 822bd2a5..c1dc30d8 100644 --- a/crates/arpa-node/test-contract/MockNodeRegistry.json +++ b/crates/arpa-node/test-contract/MockNodeRegistry.json @@ -1 +1,317 @@ -{"abi":[{"type":"function","name":"getNode","inputs":[{"name":"nodeAddress","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct MockNodeRegistry.Node","components":[{"name":"idAddress","type":"address","internalType":"address"},{"name":"dkgPublicKey","type":"bytes","internalType":"bytes"},{"name":"isEigenlayerNode","type":"bool","internalType":"bool"},{"name":"state","type":"bool","internalType":"bool"},{"name":"pendingUntilBlock","type":"uint256","internalType":"uint256"}]}],"stateMutability":"view"},{"type":"function","name":"nodes","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"idAddress","type":"address","internalType":"address"},{"name":"dkgPublicKey","type":"bytes","internalType":"bytes"},{"name":"isEigenlayerNode","type":"bool","internalType":"bool"},{"name":"state","type":"bool","internalType":"bool"},{"name":"pendingUntilBlock","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"registerNode","inputs":[{"name":"idAddress","type":"address","internalType":"address"},{"name":"dkgPublicKey","type":"bytes","internalType":"bytes"},{"name":"isEigenlayerNode","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setNodeState","inputs":[{"name":"idAddress","type":"address","internalType":"address"},{"name":"state","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b50610759806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063189a5a1714610051578063933ff7b51461007e5780639d20904814610093578063f56f705f146100b3575b600080fd5b61006461005f3660046103ab565b6100f5565b604051610075959493929190610413565b60405180910390f35b61009161008c366004610478565b6101bd565b005b6100a66100a13660046103ab565b610276565b604051610075919061054a565b6100916100c13660046105a7565b6001600160a01b03909116600090815260208190526040902060020180549115156101000261ff0019909216919091179055565b600060208190529081526040902080546001820180546001600160a01b039092169291610121906105da565b80601f016020809104026020016040519081016040528092919081815260200182805461014d906105da565b801561019a5780601f1061016f5761010080835404028352916020019161019a565b820191906000526020600020905b81548152906001019060200180831161017d57829003601f168201915b505050506002830154600390930154919260ff8082169361010090920416915085565b6040805160a0810182526001600160a01b0385811680835260208084018781528615158587015260006060860181905260808601819052928352908290529390208251815473ffffffffffffffffffffffffffffffffffffffff19169216919091178155915190919060018201906102359082610663565b506040820151600282018054606085015115156101000261ff00199315159390931661ffff1990911617919091179055608090910151600390910155505050565b6040805160a08082018352600080835260606020808501829052848601839052908401829052608084018290526001600160a01b0386811683528282529185902085519384019095528454909116825260018401805493949293918401916102dd906105da565b80601f0160208091040260200160405190810160405280929190818152602001828054610309906105da565b80156103565780601f1061032b57610100808354040283529160200191610356565b820191906000526020600020905b81548152906001019060200180831161033957829003601f168201915b5050509183525050600282015460ff80821615156020840152610100909104161515604082015260039091015460609091015292915050565b80356001600160a01b03811681146103a657600080fd5b919050565b6000602082840312156103bd57600080fd5b6103c68261038f565b9392505050565b6000815180845260005b818110156103f3576020818501810151868301820152016103d7565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b038616815260a06020820152600061043560a08301876103cd565b941515604083015250911515606083015260809091015292915050565b634e487b7160e01b600052604160045260246000fd5b803580151581146103a657600080fd5b60008060006060848603121561048d57600080fd5b6104968461038f565b9250602084013567ffffffffffffffff808211156104b357600080fd5b818601915086601f8301126104c757600080fd5b8135818111156104d9576104d9610452565b604051601f8201601f19908116603f0116810190838211818310171561050157610501610452565b8160405282815289602084870101111561051a57600080fd5b82602086016020830137600060208483010152809650505050505061054160408501610468565b90509250925092565b602081526001600160a01b0382511660208201526000602083015160a0604084015261057960c08401826103cd565b9050604084015115156060840152606084015115156080840152608084015160a08401528091505092915050565b600080604083850312156105ba57600080fd5b6105c38361038f565b91506105d160208401610468565b90509250929050565b600181811c908216806105ee57607f821691505b60208210810361060e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561065e57600081815260208120601f850160051c8101602086101561063b5750805b601f850160051c820191505b8181101561065a57828155600101610647565b5050505b505050565b815167ffffffffffffffff81111561067d5761067d610452565b6106918161068b84546105da565b84610614565b602080601f8311600181146106c657600084156106ae5750858301515b600019600386901b1c1916600185901b17855561065a565b600085815260208120601f198616915b828110156106f5578886015182559484019460019091019084016106d6565b50858210156107135787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220942266b53c23c3fad0b806424c63f5baae65c2394bbfc3a3376d10c3ede2f2dc64736f6c63430008120033","sourceMap":"57:813:2:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061004c5760003560e01c8063189a5a1714610051578063933ff7b51461007e5780639d20904814610093578063f56f705f146100b3575b600080fd5b61006461005f3660046103ab565b6100f5565b604051610075959493929190610413565b60405180910390f35b61009161008c366004610478565b6101bd565b005b6100a66100a13660046103ab565b610276565b604051610075919061054a565b6100916100c13660046105a7565b6001600160a01b03909116600090815260208190526040902060020180549115156101000261ff0019909216919091179055565b600060208190529081526040902080546001820180546001600160a01b039092169291610121906105da565b80601f016020809104026020016040519081016040528092919081815260200182805461014d906105da565b801561019a5780601f1061016f5761010080835404028352916020019161019a565b820191906000526020600020905b81548152906001019060200180831161017d57829003601f168201915b505050506002830154600390930154919260ff8082169361010090920416915085565b6040805160a0810182526001600160a01b0385811680835260208084018781528615158587015260006060860181905260808601819052928352908290529390208251815473ffffffffffffffffffffffffffffffffffffffff19169216919091178155915190919060018201906102359082610663565b506040820151600282018054606085015115156101000261ff00199315159390931661ffff1990911617919091179055608090910151600390910155505050565b6040805160a08082018352600080835260606020808501829052848601839052908401829052608084018290526001600160a01b0386811683528282529185902085519384019095528454909116825260018401805493949293918401916102dd906105da565b80601f0160208091040260200160405190810160405280929190818152602001828054610309906105da565b80156103565780601f1061032b57610100808354040283529160200191610356565b820191906000526020600020905b81548152906001019060200180831161033957829003601f168201915b5050509183525050600282015460ff80821615156020840152610100909104161515604082015260039091015460609091015292915050565b80356001600160a01b03811681146103a657600080fd5b919050565b6000602082840312156103bd57600080fd5b6103c68261038f565b9392505050565b6000815180845260005b818110156103f3576020818501810151868301820152016103d7565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b038616815260a06020820152600061043560a08301876103cd565b941515604083015250911515606083015260809091015292915050565b634e487b7160e01b600052604160045260246000fd5b803580151581146103a657600080fd5b60008060006060848603121561048d57600080fd5b6104968461038f565b9250602084013567ffffffffffffffff808211156104b357600080fd5b818601915086601f8301126104c757600080fd5b8135818111156104d9576104d9610452565b604051601f8201601f19908116603f0116810190838211818310171561050157610501610452565b8160405282815289602084870101111561051a57600080fd5b82602086016020830137600060208483010152809650505050505061054160408501610468565b90509250925092565b602081526001600160a01b0382511660208201526000602083015160a0604084015261057960c08401826103cd565b9050604084015115156060840152606084015115156080840152608084015160a08401528091505092915050565b600080604083850312156105ba57600080fd5b6105c38361038f565b91506105d160208401610468565b90509250929050565b600181811c908216806105ee57607f821691505b60208210810361060e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561065e57600081815260208120601f850160051c8101602086101561063b5750805b601f850160051c820191505b8181101561065a57828155600101610647565b5050505b505050565b815167ffffffffffffffff81111561067d5761067d610452565b6106918161068b84546105da565b84610614565b602080601f8311600181146106c657600084156106ae5750858301515b600019600386901b1c1916600185901b17855561065a565b600085815260208120601f198616915b828110156106f5578886015182559484019460019091019084016106d6565b50858210156107135787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220942266b53c23c3fad0b806424c63f5baae65c2394bbfc3a3376d10c3ede2f2dc64736f6c63430008120033","sourceMap":"57:813:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;89:37;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;299:334;;;;;;:::i;:::-;;:::i;:::-;;754:114;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;639:109::-;;;;;;:::i;:::-;-1:-1:-1;;;;;711:16:2;;;:5;:16;;;;;;;;;;:22;;:30;;;;;;;-1:-1:-1;;711:30:2;;;;;;;;;639:109;89:37;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;89:37:2;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;89:37:2;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;89:37:2;:::o;299:334::-;428:198;;;;;;;;-1:-1:-1;;;;;428:198:2;;;;;;;;;;;;;;;;;;;;-1:-1:-1;428:198:2;;;;;;;;;;;;409:16;;;;;;;;;;:217;;;;-1:-1:-1;;409:217:2;;;;;;;;;;;428:198;;409:16;-1:-1:-1;409:217:2;;;;;;;:::i;:::-;-1:-1:-1;409:217:2;;;;;;;;;;;;;;;;;-1:-1:-1;;409:217:2;;;;;;;-1:-1:-1;;409:217:2;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;299:334:2:o;754:114::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;843:18:2;;;;;;;;;;;;836:25;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;836:25:2;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;836:25:2;;;-1:-1:-1;;836:25:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;754:114;-1:-1:-1;;754:114:2:o;14:196:3:-;82:20;;-1:-1:-1;;;;;131:54:3;;121:65;;111:93;;200:1;197;190:12;111:93;14:196;;;:::o;215:186::-;274:6;327:2;315:9;306:7;302:23;298:32;295:52;;;343:1;340;333:12;295:52;366:29;385:9;366:29;:::i;:::-;356:39;215:186;-1:-1:-1;;;215:186:3:o;406:422::-;447:3;485:5;479:12;512:6;507:3;500:19;537:1;547:162;561:6;558:1;555:13;547:162;;;623:4;679:13;;;675:22;;669:29;651:11;;;647:20;;640:59;576:12;547:162;;;551:3;754:1;747:4;738:6;733:3;729:16;725:27;718:38;817:4;810:2;806:7;801:2;793:6;789:15;785:29;780:3;776:39;772:50;765:57;;;406:422;;;;:::o;833:573::-;-1:-1:-1;;;;;1084:6:3;1080:55;1069:9;1062:74;1172:3;1167:2;1156:9;1152:18;1145:31;1043:4;1193:45;1233:3;1222:9;1218:19;1210:6;1193:45;:::i;:::-;1281:14;;1274:22;1269:2;1254:18;;1247:50;-1:-1:-1;1340:14:3;;1333:22;1328:2;1313:18;;1306:50;1387:3;1372:19;;;1365:35;1185:53;833:573;-1:-1:-1;;833:573:3:o;1411:127::-;1472:10;1467:3;1463:20;1460:1;1453:31;1503:4;1500:1;1493:15;1527:4;1524:1;1517:15;1543:160;1608:20;;1664:13;;1657:21;1647:32;;1637:60;;1693:1;1690;1683:12;1708:1063;1791:6;1799;1807;1860:2;1848:9;1839:7;1835:23;1831:32;1828:52;;;1876:1;1873;1866:12;1828:52;1899:29;1918:9;1899:29;:::i;:::-;1889:39;;1979:2;1968:9;1964:18;1951:32;2002:18;2043:2;2035:6;2032:14;2029:34;;;2059:1;2056;2049:12;2029:34;2097:6;2086:9;2082:22;2072:32;;2142:7;2135:4;2131:2;2127:13;2123:27;2113:55;;2164:1;2161;2154:12;2113:55;2200:2;2187:16;2222:2;2218;2215:10;2212:36;;;2228:18;;:::i;:::-;2303:2;2297:9;2271:2;2357:13;;-1:-1:-1;;2353:22:3;;;2377:2;2349:31;2345:40;2333:53;;;2401:18;;;2421:22;;;2398:46;2395:72;;;2447:18;;:::i;:::-;2487:10;2483:2;2476:22;2522:2;2514:6;2507:18;2562:7;2557:2;2552;2548;2544:11;2540:20;2537:33;2534:53;;;2583:1;2580;2573:12;2534:53;2639:2;2634;2630;2626:11;2621:2;2613:6;2609:15;2596:46;2684:1;2679:2;2674;2666:6;2662:15;2658:24;2651:35;2705:6;2695:16;;;;;;;2730:35;2761:2;2750:9;2746:18;2730:35;:::i;:::-;2720:45;;1708:1063;;;;;:::o;2776:681::-;2947:2;2936:9;2929:21;-1:-1:-1;;;;;2996:6:3;2990:13;2986:62;2981:2;2970:9;2966:18;2959:90;2910:4;3096:2;3088:6;3084:15;3078:22;3136:4;3131:2;3120:9;3116:18;3109:32;3164:51;3210:3;3199:9;3195:19;3181:12;3164:51;:::i;:::-;3150:65;;3283:2;3275:6;3271:15;3265:22;3258:30;3251:38;3246:2;3235:9;3231:18;3224:66;3359:2;3351:6;3347:15;3341:22;3334:30;3327:38;3321:3;3310:9;3306:19;3299:67;3422:3;3414:6;3410:16;3404:23;3397:4;3386:9;3382:20;3375:53;3445:6;3437:14;;;2776:681;;;;:::o;3462:254::-;3527:6;3535;3588:2;3576:9;3567:7;3563:23;3559:32;3556:52;;;3604:1;3601;3594:12;3556:52;3627:29;3646:9;3627:29;:::i;:::-;3617:39;;3675:35;3706:2;3695:9;3691:18;3675:35;:::i;:::-;3665:45;;3462:254;;;;;:::o;3721:380::-;3800:1;3796:12;;;;3843;;;3864:61;;3918:4;3910:6;3906:17;3896:27;;3864:61;3971:2;3963:6;3960:14;3940:18;3937:38;3934:161;;4017:10;4012:3;4008:20;4005:1;3998:31;4052:4;4049:1;4042:15;4080:4;4077:1;4070:15;3934:161;;3721:380;;;:::o;4231:544::-;4332:2;4327:3;4324:11;4321:448;;;4368:1;4393:5;4389:2;4382:17;4438:4;4434:2;4424:19;4508:2;4496:10;4492:19;4489:1;4485:27;4479:4;4475:38;4544:4;4532:10;4529:20;4526:47;;;-1:-1:-1;4567:4:3;4526:47;4622:2;4617:3;4613:12;4610:1;4606:20;4600:4;4596:31;4586:41;;4677:82;4695:2;4688:5;4685:13;4677:82;;;4740:17;;;4721:1;4710:13;4677:82;;;4681:3;;;4321:448;4231:544;;;:::o;4951:1348::-;5075:3;5069:10;5102:18;5094:6;5091:30;5088:56;;;5124:18;;:::i;:::-;5153:96;5242:6;5202:38;5234:4;5228:11;5202:38;:::i;:::-;5196:4;5153:96;:::i;:::-;5304:4;;5368:2;5357:14;;5385:1;5380:662;;;;6086:1;6103:6;6100:89;;;-1:-1:-1;6155:19:3;;;6149:26;6100:89;-1:-1:-1;;4908:1:3;4904:11;;;4900:24;4896:29;4886:40;4932:1;4928:11;;;4883:57;6202:81;;5350:943;;5380:662;4178:1;4171:14;;;4215:4;4202:18;;-1:-1:-1;;5416:20:3;;;5533:236;5547:7;5544:1;5541:14;5533:236;;;5636:19;;;5630:26;5615:42;;5728:27;;;;5696:1;5684:14;;;;5563:19;;5533:236;;;5537:3;5797:6;5788:7;5785:19;5782:201;;;5858:19;;;5852:26;-1:-1:-1;;5941:1:3;5937:14;;;5953:3;5933:24;5929:37;5925:42;5910:58;5895:74;;5782:201;-1:-1:-1;;;;;6029:1:3;6013:14;;;6009:22;5996:36;;-1:-1:-1;4951:1348:3:o","linkReferences":{}},"methodIdentifiers":{"getNode(address)":"9d209048","nodes(address)":"189a5a17","registerNode(address,bytes,bool)":"933ff7b5","setNodeState(address,bool)":"f56f705f"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"nodeAddress\",\"type\":\"address\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"idAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"dkgPublicKey\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"isEigenlayerNode\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"state\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"pendingUntilBlock\",\"type\":\"uint256\"}],\"internalType\":\"struct MockNodeRegistry.Node\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nodes\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"idAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"dkgPublicKey\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"isEigenlayerNode\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"state\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"pendingUntilBlock\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"idAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"dkgPublicKey\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"isEigenlayerNode\",\"type\":\"bool\"}],\"name\":\"registerNode\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"idAddress\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"state\",\"type\":\"bool\"}],\"name\":\"setNodeState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"test/mock/MockNodeRegistry.sol\":\"MockNodeRegistry\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[\":Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/\",\":Staking-v0.1/=lib/Staking-v0.1/src/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":fx-portal/=lib/fx-portal/contracts/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"]},\"sources\":{\"test/mock/MockNodeRegistry.sol\":{\"keccak256\":\"0xf3a00a6ae8d2a2483feebf642a046149e01ff99bed84370e45fdc0d6ddd8e09d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://17b217d6acd201674935535a6fe14f7cb9c0e47b7b4a5bbcad52b06a2472704a\",\"dweb:/ipfs/QmSYrFbboZy1N7hL2U2eWL3CZwU1FeB9AdwZNAWeAeKQci\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"nodeAddress","type":"address"}],"stateMutability":"view","type":"function","name":"getNode","outputs":[{"internalType":"struct MockNodeRegistry.Node","name":"","type":"tuple","components":[{"internalType":"address","name":"idAddress","type":"address"},{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"},{"internalType":"bool","name":"isEigenlayerNode","type":"bool"},{"internalType":"bool","name":"state","type":"bool"},{"internalType":"uint256","name":"pendingUntilBlock","type":"uint256"}]}]},{"inputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function","name":"nodes","outputs":[{"internalType":"address","name":"idAddress","type":"address"},{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"},{"internalType":"bool","name":"isEigenlayerNode","type":"bool"},{"internalType":"bool","name":"state","type":"bool"},{"internalType":"uint256","name":"pendingUntilBlock","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"idAddress","type":"address"},{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"},{"internalType":"bool","name":"isEigenlayerNode","type":"bool"}],"stateMutability":"nonpayable","type":"function","name":"registerNode"},{"inputs":[{"internalType":"address","name":"idAddress","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"stateMutability":"nonpayable","type":"function","name":"setNodeState"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["Randcast-User-Contract/=lib/Randcast-User-Contract/contracts/","Staking-v0.1/=lib/Staking-v0.1/src/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","fx-portal/=lib/fx-portal/contracts/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/"],"optimizer":{"enabled":true,"runs":300},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"test/mock/MockNodeRegistry.sol":"MockNodeRegistry"},"evmVersion":"paris","libraries":{}},"sources":{"test/mock/MockNodeRegistry.sol":{"keccak256":"0xf3a00a6ae8d2a2483feebf642a046149e01ff99bed84370e45fdc0d6ddd8e09d","urls":["bzz-raw://17b217d6acd201674935535a6fe14f7cb9c0e47b7b4a5bbcad52b06a2472704a","dweb:/ipfs/QmSYrFbboZy1N7hL2U2eWL3CZwU1FeB9AdwZNAWeAeKQci"],"license":"MIT"}},"version":1},"id":2} \ No newline at end of file +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_controllerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_stakingAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_serviceManagerAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "NodeAlreadyActive", + "type": "error" + }, + { + "inputs": [], + "name": "NodeNotRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "pendingUntilBlock", + "type": "uint256" + } + ], + "name": "NodeStillPending", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "nodeAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "groupIndex", + "type": "uint256" + } + ], + "name": "NodeActivated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "nodeAddress", + "type": "address" + } + ], + "name": "getNode", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "idAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "dkgPublicKey", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "isEigenlayerNode", + "type": "bool" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "pendingUntilBlock", + "type": "uint256" + } + ], + "internalType": "struct INodeRegistry.Node", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNodeRegistryConfig", + "outputs": [ + { + "internalType": "address", + "name": "controllerContractAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "serviceManagerContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nativeNodeStakingAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "eigenlayerNodeStakingAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pendingBlockAfterQuit", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + } + ], + "internalType": "struct ISignatureUtils.SignatureWithSaltAndExpiry", + "name": "assetAccountSignature", + "type": "tuple" + } + ], + "name": "nodeActivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nodes", + "outputs": [ + { + "internalType": "address", + "name": "idAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "dkgPublicKey", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "isEigenlayerNode", + "type": "bool" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "pendingUntilBlock", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "idAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "dkgPublicKey", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "isEigenlayerNode", + "type": "bool" + } + ], + "name": "registerNode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "idAddress", + "type": "address" + }, + { + "internalType": "bool", + "name": "state", + "type": "bool" + } + ], + "name": "setNodeState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": { + "object": "0x608060405234801561000f575f5ffd5b50604051610a7f380380610a7f83398101604081905261002e916100e1565b6040805160c0810182526001600160a01b03948516808252938516602082018190529290941690840181905268056bc75e2d6310000060608501819052683635c9adc5dea00000608086018190526103e860a0909601869052600180546001600160a01b0319908116909617905560028054861690941790935560038054909416909117909255600491909155600555600655610121565b80516001600160a01b03811681146100dc575f5ffd5b919050565b5f5f5f606084860312156100f3575f5ffd5b6100fc846100c6565b925061010a602085016100c6565b9150610118604085016100c6565b90509250925092565b6109518061012e5f395ff3fe608060405234801561000f575f5ffd5b506004361061006f575f3560e01c80639d2090481161004d5780639d209048146100c8578063e40e744b146100e8578063f56f705f1461013b575f5ffd5b8063189a5a17146100735780638d2f3e6b146100a0578063933ff7b5146100b5575b5f5ffd5b61008661008136600461051a565b61017c565b604051610097959493929190610568565b60405180910390f35b6100b36100ae366004610645565b610241565b005b6100b36100c33660046106f5565b610331565b6100db6100d636600461051a565b6103e9565b604051610097919061074f565b600154600254600354600454600554600654604080516001600160a01b039788168152958716602087015295909316948401949094526060830152608082019290925260a081019190915260c001610097565b6100b36101493660046107ab565b6001600160a01b039091165f90815260208190526040902060020180549115156101000261ff0019909216919091179055565b5f60208190529081526040902080546001820180546001600160a01b0390921692916101a7906107dc565b80601f01602080910402602001604051908101604052809291908181526020018280546101d3906107dc565b801561021e5780601f106101f55761010080835404028352916020019161021e565b820191905f5260205f20905b81548152906001019060200180831161020157829003601f168201915b505050506002830154600390930154919260ff8082169361010090920416915085565b335f818152602081905260409020805490916001600160a01b039091161461027b576040516229eaad60e31b815260040160405180910390fd5b6002810154610100900460ff16156102a6576040516324a228bb60e21b815260040160405180910390fd5b43816003015411156102dc5780600301546040516363525acb60e11b81526004016102d391815260200190565b60405180910390fd5b60028101805461ff00191661010017905560405160019033907ffc97cd9154b40031874ef09a9436a4b60052e4dcf40f21b1258be265fac4a397906103249084815260200190565b60405180910390a2505050565b6040805160a0810182526001600160a01b038581168083526020808401878152861515858701525f6060860181905260808601819052928352908290529390208251815473ffffffffffffffffffffffffffffffffffffffff19169216919091178155915190919060018201906103a89082610860565b506040820151600282018054606085015115156101000261ff00199315159390931661ffff1990911617919091179055608090910151600390910155505050565b6040805160a080820183525f80835260606020808501829052848601839052908401829052608084018290526001600160a01b03868116835282825291859020855193840190955284549091168252600184018054939492939184019161044f906107dc565b80601f016020809104026020016040519081016040528092919081815260200182805461047b906107dc565b80156104c65780601f1061049d576101008083540402835291602001916104c6565b820191905f5260205f20905b8154815290600101906020018083116104a957829003601f168201915b5050509183525050600282015460ff80821615156020840152610100909104161515604082015260039091015460609091015292915050565b80356001600160a01b0381168114610515575f5ffd5b919050565b5f6020828403121561052a575f5ffd5b610533826104ff565b9392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b6001600160a01b038616815260a060208201525f61058960a083018761053a565b941515604083015250911515606083015260809091015292915050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126105c9575f5ffd5b813567ffffffffffffffff8111156105e3576105e36105a6565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610612576106126105a6565b604052818152838201602001851015610629575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f60208284031215610655575f5ffd5b813567ffffffffffffffff81111561066b575f5ffd5b82016060818503121561067c575f5ffd5b6040516060810167ffffffffffffffff8111828210171561069f5761069f6105a6565b604052813567ffffffffffffffff8111156106b8575f5ffd5b6106c4868285016105ba565b8252506020820135602082015260408201356040820152809250505092915050565b80358015158114610515575f5ffd5b5f5f5f60608486031215610707575f5ffd5b610710846104ff565b9250602084013567ffffffffffffffff81111561072b575f5ffd5b610737868287016105ba565b925050610746604085016106e6565b90509250925092565b602081526001600160a01b0382511660208201525f602083015160a0604084015261077d60c084018261053a565b9050604084015115156060840152606084015115156080840152608084015160a08401528091505092915050565b5f5f604083850312156107bc575f5ffd5b6107c5836104ff565b91506107d3602084016106e6565b90509250929050565b600181811c908216806107f057607f821691505b60208210810361080e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561085b57805f5260205f20601f840160051c810160208510156108395750805b601f840160051c820191505b81811015610858575f8155600101610845565b50505b505050565b815167ffffffffffffffff81111561087a5761087a6105a6565b61088e8161088884546107dc565b84610814565b6020601f8211600181146108c0575f83156108a95750848201515b5f19600385901b1c1916600184901b178455610858565b5f84815260208120601f198516915b828110156108ef57878501518255602094850194600190920191016108cf565b508482101561090c57868401515f19600387901b60f8161c191681555b50505050600190811b0190555056fea26469706673582212209ac785113c2664bc891e513dc9d3e6d80b78ceb911f5277bae2f300dfc42ac3c64736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"MockNodeRegistry.sol\":\"ISignatureUtils\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[]},\"sources\":{\"MockNodeRegistry.sol\":{\"keccak256\":\"0x4752187fb612cc3c1376603342d2fe8831599834eecf090ac7aea28dbe320cd6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://967d19c4efe9beb90747fc431523fa06c1bce4e7f68cf32d0908a4b4bffbb745\",\"dweb:/ipfs/QmcF2NPreGEoysjgGhVtKAUKfp5rWxBGfo2Rh2xwZUt5qX\"]}},\"version\":1}", + "metadata": { + "compiler": { + "version": "0.8.27+commit.40a35a09" + }, + "language": "Solidity", + "output": { + "abi": [], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "MockNodeRegistry.sol": "ISignatureUtils" + }, + "evmVersion": "cancun", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 300 + }, + "remappings": [] + }, + "sources": { + "MockNodeRegistry.sol": { + "keccak256": "0x4752187fb612cc3c1376603342d2fe8831599834eecf090ac7aea28dbe320cd6", + "license": "MIT", + "urls": [ + "bzz-raw://967d19c4efe9beb90747fc431523fa06c1bce4e7f68cf32d0908a4b4bffbb745", + "dweb:/ipfs/QmcF2NPreGEoysjgGhVtKAUKfp5rWxBGfo2Rh2xwZUt5qX" + ] + } + }, + "version": 1 + }, + "id": 1 +} diff --git a/crates/arpa-node/test-contract/MockNodeRegistry.sol b/crates/arpa-node/test-contract/MockNodeRegistry.sol index 2a8bf54c..32ee2491 100644 --- a/crates/arpa-node/test-contract/MockNodeRegistry.sol +++ b/crates/arpa-node/test-contract/MockNodeRegistry.sol @@ -1,9 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -contract MockNodeRegistry { - mapping(address => Node) public nodes; +interface ISignatureUtils { + struct SignatureWithSaltAndExpiry { + bytes signature; + bytes32 salt; + uint256 expiry; + } +} +interface INodeRegistry { struct Node { address idAddress; bytes dkgPublicKey; @@ -11,6 +17,52 @@ contract MockNodeRegistry { bool state; uint256 pendingUntilBlock; } + + function getNode(address nodeAddress) external view returns (Node memory); + function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) external; + function getNodeRegistryConfig() external view returns ( + address controllerContractAddress, + address stakingContractAddress, + address serviceManagerContractAddress, + uint256 nativeNodeStakingAmount, + uint256 eigenlayerNodeStakingAmount, + uint256 pendingBlockAfterQuit + ); +} + +contract MockNodeRegistry is INodeRegistry { + struct Config { + address controllerContractAddress; + address stakingContractAddress; + address serviceManagerContractAddress; + uint256 nativeNodeStakingAmount; + uint256 eigenlayerNodeStakingAmount; + uint256 pendingBlockAfterQuit; + } + + mapping(address => Node) public nodes; + Config private _config; + + event NodeActivated(address indexed nodeAddress, uint256 groupIndex); + + error NodeNotRegistered(); + error NodeAlreadyActive(); + error NodeStillPending(uint256 pendingUntilBlock); + + constructor( + address _controllerAddress, + address _stakingAddress, + address _serviceManagerAddress + ) { + _config = Config({ + controllerContractAddress: _controllerAddress, + stakingContractAddress: _stakingAddress, + serviceManagerContractAddress: _serviceManagerAddress, + nativeNodeStakingAmount: 100 ether, + eigenlayerNodeStakingAmount: 1000 ether, + pendingBlockAfterQuit: 1000 + }); + } function registerNode(address idAddress, bytes memory dkgPublicKey, bool isEigenlayerNode) external { nodes[idAddress] = Node({ @@ -26,7 +78,55 @@ contract MockNodeRegistry { nodes[idAddress].state = state; } - function getNode(address nodeAddress) public view returns (Node memory) { + function getNode(address nodeAddress) public view override(INodeRegistry) returns (Node memory) { return nodes[nodeAddress]; } -} + + function nodeActivate(ISignatureUtils.SignatureWithSaltAndExpiry memory assetAccountSignature) + external + override(INodeRegistry) + { + Node storage node = nodes[msg.sender]; + + if (node.idAddress != msg.sender) { + revert NodeNotRegistered(); + } + + if (node.state) { + revert NodeAlreadyActive(); + } + + if (node.pendingUntilBlock > block.number) { + revert NodeStillPending(node.pendingUntilBlock); + } + + node.state = true; + + uint256 groupIndex = 1; + + emit NodeActivated(msg.sender, groupIndex); + } + + function getNodeRegistryConfig() + public + view + override(INodeRegistry) + returns ( + address controllerContractAddress, + address stakingContractAddress, + address serviceManagerContractAddress, + uint256 nativeNodeStakingAmount, + uint256 eigenlayerNodeStakingAmount, + uint256 pendingBlockAfterQuit + ) + { + return ( + _config.controllerContractAddress, + _config.stakingContractAddress, + _config.serviceManagerContractAddress, + _config.nativeNodeStakingAmount, + _config.eigenlayerNodeStakingAmount, + _config.pendingBlockAfterQuit + ); + } +} \ No newline at end of file diff --git a/crates/arpa-node/test-contract/MockServiceManager.json b/crates/arpa-node/test-contract/MockServiceManager.json new file mode 100644 index 00000000..f8f4a6f4 --- /dev/null +++ b/crates/arpa-node/test-contract/MockServiceManager.json @@ -0,0 +1,111 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_avsDirectory", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "avsDirectory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x6080604052348015600e575f5ffd5b50604051610112380380610112833981016040819052602b91604e565b5f80546001600160a01b0319166001600160a01b03929092169190911790556079565b5f60208284031215605d575f5ffd5b81516001600160a01b03811681146072575f5ffd5b9392505050565b608d806100855f395ff3fe6080604052348015600e575f5ffd5b50600436106026575f3560e01c80636b3aa72e14602a575b5f5ffd5b5f54603b906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f3fea2646970667358221220436ef968b576911c4bbc003b5bd55757c61730b754b3d3c757b4fdd7ab7d1d0564736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c80636b3aa72e14602a575b5f5ffd5b5f54603b906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f3fea2646970667358221220436ef968b576911c4bbc003b5bd55757c61730b754b3d3c757b4fdd7ab7d1d0564736f6c634300081b0033", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": {}, + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_avsDirectory\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"avsDirectory\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"MockServiceManager.sol\":\"MockServiceManager\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":300},\"remappings\":[]},\"sources\":{\"MockServiceManager.sol\":{\"keccak256\":\"0x9c9807bf9a19b4d1d86094ea0be6b2a0829d5b0cf6840f953449788286523b3e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://62bc8ee5fa7fd899db2a87e650a41f0b3aa65d8326e2d44663909acf8344a521\",\"dweb:/ipfs/QmaXevm89unDQVfhfKCMHyVuZt6yhiPRNvs14hW2dndYYb\"]}},\"version\":1}", + "metadata": { + "compiler": { + "version": "0.8.27+commit.40a35a09" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_avsDirectory", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "avsDirectory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "MockServiceManager.sol": "MockServiceManager" + }, + "evmVersion": "cancun", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs" + }, + "optimizer": { + "enabled": true, + "runs": 300 + }, + "remappings": [] + }, + "sources": { + "MockServiceManager.sol": { + "keccak256": "0x9c9807bf9a19b4d1d86094ea0be6b2a0829d5b0cf6840f953449788286523b3e", + "license": "MIT", + "urls": [ + "bzz-raw://62bc8ee5fa7fd899db2a87e650a41f0b3aa65d8326e2d44663909acf8344a521", + "dweb:/ipfs/QmaXevm89unDQVfhfKCMHyVuZt6yhiPRNvs14hW2dndYYb" + ] + } + }, + "version": 1 + }, + "id": 1 +}