diff --git a/config/LagrangeService.json b/config/LagrangeService.json index 1f69bb8..10cd3cd 100644 --- a/config/LagrangeService.json +++ b/config/LagrangeService.json @@ -1,7 +1,7 @@ { "strategies": [ { - "strategy_address": "0xdEe9De63F5E5db89cfCcC550ABAaDF61A48E7225", + "strategy_address": "0x91E333A3d61862B1FE976351cf0F3b30aff1D202", "strategy_name": "Wrapped Ether", "multiplier": 1000000000 } @@ -21,7 +21,7 @@ } ], "settlement": { - "opt_l2outputoracle": "0x4200000000000000000000000000000000000016", - "arb_outbox": "0x45Af9Ed1D03703e480CE7d328fB684bb67DA5049" + "opt_l2outputoracle": "0x4200000000000000000000000000000000000016", + "arb_outbox": "0x45Af9Ed1D03703e480CE7d328fB684bb67DA5049" } -} +} \ No newline at end of file diff --git a/script/Add_Quorum.s.sol b/script/Add_Quorum.s.sol index b9a693e..5223ed5 100644 --- a/script/Add_Quorum.s.sol +++ b/script/Add_Quorum.s.sol @@ -28,8 +28,8 @@ contract AddQuorum is Script, Test { string memory deployLGRData = vm.readFile(deployedLGRPath); string memory configData = vm.readFile(configPath); - LagrangeService lagrangeService = LagrangeService( - stdJson.readAddress(deployLGRData, ".addresses.lagrangeService") + LagrangeCommittee committee = LagrangeCommittee( + stdJson.readAddress(deployLGRData, ".addresses.lagrangeCommittee") ); // add strategy multipliers to lagrange service @@ -52,7 +52,7 @@ contract AddQuorum is Script, Test { }); } - lagrangeService.addStrategiesConsideredAndMultipliers( + committee.addStrategiesConsideredAndMultipliers( 1, newStrategiesConsideredAndMultipliers ); diff --git a/script/Deploy_LGR.s.sol b/script/Deploy_LGR.s.sol index 6e72959..3023547 100644 --- a/script/Deploy_LGR.s.sol +++ b/script/Deploy_LGR.s.sol @@ -28,7 +28,7 @@ import {IL2OutputOracle} from "src/mock/optimism/IL2OutputOracle.sol"; contract Deploy is Script, Test { string public deployDataPath = string(bytes("script/output/deployed_mock.json")); - //string(bytes("script/output/M1_deployment_data.json")); + //string(bytes("script/output/M1_deployment_data.json")); string public poseidonDataPath = string(bytes("script/output/deployed_poseidon.json")); string public serviceDataPath = @@ -68,7 +68,7 @@ contract Deploy is Script, Test { // deploy proxy admin for ability to upgrade proxy contracts proxyAdmin = new ProxyAdmin(); - + // deploy upgradeable proxy contracts emptyContract = new EmptyContract(); lagrangeCommittee = LagrangeCommittee( @@ -101,23 +101,30 @@ contract Deploy is Script, Test { // deploy implementation contracts string memory poseidonData = vm.readFile(poseidonDataPath); - lagrangeCommitteeImp = new LagrangeCommittee(lagrangeService); + lagrangeCommitteeImp = new LagrangeCommittee( + lagrangeService, + lagrangeServiceManager, + IStrategyManager(strategyManagerAddress) + ); lagrangeServiceManagerImp = new LagrangeServiceManager( - ISlasher(slasherAddress) + ISlasher(slasherAddress), + lagrangeCommittee, + lagrangeService ); - + lagrangeServiceImp = new LagrangeService( - lagrangeServiceManager, lagrangeCommittee, - IStrategyManager(strategyManagerAddress) + lagrangeServiceManager + ); + + outbox = new Outbox(); + IL2OutputOracle opt_L2OutputOracle = IL2OutputOracle( + stdJson.readAddress(configData, ".settlement.opt_l2outputoracle") ); - - outbox = new Outbox(); - IL2OutputOracle opt_L2OutputOracle = IL2OutputOracle(stdJson.readAddress(configData, ".settlement.opt_l2outputoracle")); - //L2OutputOracle l2oo = new L2OutputOracle(); - //IL2OutputOracle opt_L2OutputOracle = IL2OutputOracle(l2oo.address); - //IOutbox arb_Outbox = IOutbox(stdJson.readAddress(configData, ".settlement.arb_outbox")); - IOutbox arb_Outbox = IOutbox(address(outbox)); + //L2OutputOracle l2oo = new L2OutputOracle(); + //IL2OutputOracle opt_L2OutputOracle = IL2OutputOracle(l2oo.address); + //IOutbox arb_Outbox = IOutbox(stdJson.readAddress(configData, ".settlement.arb_outbox")); + IOutbox arb_Outbox = IOutbox(address(outbox)); // deploy evidence verifier arbitrumVerifier = new ArbitrumVerifier(outbox); diff --git a/script/Init_Committee.s.sol b/script/Init_Committee.s.sol index 7e4237e..2697de6 100644 --- a/script/Init_Committee.s.sol +++ b/script/Init_Committee.s.sol @@ -17,7 +17,7 @@ contract InitCommittee is Script, Test { string public configPath = string(bytes("config/LagrangeService.json")); struct InitialChains { - uint256 chainId; + uint32 chainId; string chainName; uint256 epochPeriod; uint256 freezeDuration; @@ -32,12 +32,9 @@ contract InitCommittee is Script, Test { LagrangeCommittee lagrangeCommittee = LagrangeCommittee( stdJson.readAddress(deployLGRData, ".addresses.lagrangeCommittee") ); - + // initialize the lagrange committee - bytes memory initChains = stdJson.parseRaw( - configData, - ".chains" - ); + bytes memory initChains = stdJson.parseRaw(configData, ".chains"); InitialChains[] memory initialChains = abi.decode( initChains, (InitialChains[]) @@ -48,7 +45,7 @@ contract InitCommittee is Script, Test { initialChains[i].chainId, initialChains[i].epochPeriod, initialChains[i].freezeDuration - ); + ); } vm.stopBroadcast(); diff --git a/script/Redeploy_LGR_Committee.s.sol b/script/Redeploy_LGR_Committee.s.sol index 9d78262..a21c82c 100644 --- a/script/Redeploy_LGR_Committee.s.sol +++ b/script/Redeploy_LGR_Committee.s.sol @@ -3,8 +3,11 @@ pragma solidity ^0.8.12; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IStrategyManager} from "eigenlayer-contracts/interfaces/IStrategyManager.sol"; + import {LagrangeCommittee} from "src/protocol/LagrangeCommittee.sol"; import {LagrangeService} from "src/protocol/LagrangeService.sol"; +import {LagrangeServiceManager} from "src/protocol/LagrangeServiceManager.sol"; import "forge-std/Script.sol"; import "forge-std/Test.sol"; @@ -12,25 +15,55 @@ import "forge-std/Test.sol"; contract Deploy is Script, Test { string public deployDataPath = string(bytes("script/output/deployed_goerli.json")); + string public mockDataPath = + string(bytes("script/output/deployed_mock.json")); // Lagrange Contracts ProxyAdmin public proxyAdmin; LagrangeCommittee public lagrangeCommittee; LagrangeCommittee public lagrangeCommitteeImp; LagrangeService public lagrangeService; + LagrangeServiceManager public lagrangeServiceManager; function run() public { string memory deployData = vm.readFile(deployDataPath); + string memory mockData = vm.readFile(mockDataPath); + + address strategyManagerAddress = stdJson.readAddress( + mockData, + ".addresses.strategyManager" + ); vm.startBroadcast(msg.sender); // deploy proxy admin for ability to upgrade proxy contracts - proxyAdmin = ProxyAdmin(stdJson.readAddress(deployData, ".lagrange.addresses.proxyAdmin")); - lagrangeService = LagrangeService(stdJson.readAddress(deployData, ".lagrange.addresses.lagrangeService")); - lagrangeCommittee = LagrangeCommittee(stdJson.readAddress(deployData, ".lagrange.addresses.lagrangeCommittee")); - + proxyAdmin = ProxyAdmin( + stdJson.readAddress(deployData, ".lagrange.addresses.proxyAdmin") + ); + lagrangeService = LagrangeService( + stdJson.readAddress( + deployData, + ".lagrange.addresses.lagrangeService" + ) + ); + lagrangeCommittee = LagrangeCommittee( + stdJson.readAddress( + deployData, + ".lagrange.addresses.lagrangeCommittee" + ) + ); + lagrangeServiceManager = LagrangeServiceManager( + stdJson.readAddress( + deployData, + ".lagrange.addresses.lagrangeServiceManager" + ) + ); // deploy implementation contracts - lagrangeCommitteeImp = new LagrangeCommittee(lagrangeService); + lagrangeCommitteeImp = new LagrangeCommittee( + lagrangeService, + lagrangeServiceManager, + IStrategyManager(strategyManagerAddress) + ); // upgrade proxy contracts proxyAdmin.upgrade( diff --git a/script/Remove_Quorum.s.sol b/script/Remove_Quorum.s.sol index 4b794f9..b5f7037 100644 --- a/script/Remove_Quorum.s.sol +++ b/script/Remove_Quorum.s.sol @@ -20,15 +20,15 @@ contract RemoveQuorum is Script, Test { string memory deployLGRData = vm.readFile(deployedLGRPath); - LagrangeService lagrangeService = LagrangeService( - stdJson.readAddress(deployLGRData, ".addresses.lagrangeService") + LagrangeCommittee committee = LagrangeCommittee( + stdJson.readAddress(deployLGRData, ".addresses.lagrangeCommittee") ); - + IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = IStrategy(0x91E333A3d61862B1FE976351cf0F3b30aff1D202); uint256[] memory indexes = new uint256[](1); indexes[0] = 0; - lagrangeService.removeStrategiesConsideredAndMultipliers( + committee.removeStrategiesConsideredAndMultipliers( 1, strategies, indexes diff --git a/script/output/deployed_lgr.json b/script/output/deployed_lgr.json index b0f7744..ede8756 100644 --- a/script/output/deployed_lgr.json +++ b/script/output/deployed_lgr.json @@ -1,11 +1,11 @@ { "addresses": { - "lagrangeCommittee": "0xC1dCca9f209BD61a154e85980dcaff3203e27c69", - "lagrangeCommitteeImp": "0x5439b84e75C18319D085d5a0d075e6d554Dc6A5a", - "lagrangeService": "0x4e61b4f5C24441C5f7c1e912595794a06C14906f", - "lagrangeServiceImp": "0x559dc7De2842dbf9255224327310ec2Bd10911d7", - "lagrangeServiceManager": "0xB98bE9328734DDBa1d3F1DA2715C53a6ca1015f6", - "lagrangeServiceManagerImp": "0x3CA882c83d3bE68C2654f23773c3f8DBE91751d7", - "proxyAdmin": "0xe76f31442D911A4f7D0792E0AC815De8F02350a0" + "lagrangeCommittee": "0x923d8ADAAa6e52c485293cD48EE56F7BFAD85cd4", + "lagrangeCommitteeImp": "0x75B96311d8040c0F0d543ED5dc57b8Aa8492ffEF", + "lagrangeService": "0xF824C350EA9501234a731B01B8EC6E660e069c7F", + "lagrangeServiceImp": "0x98f07aB2d35638B79582b250C01444cEce0E517A", + "lagrangeServiceManager": "0x181e2559779316009409f9C9F6d47C323B5720F0", + "lagrangeServiceManagerImp": "0xBbE77E34F41aBf10bc3B408D975c38F15592AF2d", + "proxyAdmin": "0x0C601f8E4776bD47e1c460547c457B4A5EE93faA" } } \ No newline at end of file diff --git a/script/output/deployed_mock.json b/script/output/deployed_mock.json index dd63c2b..6dcf79c 100644 --- a/script/output/deployed_mock.json +++ b/script/output/deployed_mock.json @@ -1 +1 @@ -{"addresses":{"delegationManager":"0xE93DCb1e6D17Fd3178700FfE8DB8715cAd6E51F0","slasher":"0x82172b246D7e1A0Be30806119071c1CC83d0dDA2","strategy":"0xdEe9De63F5E5db89cfCcC550ABAaDF61A48E7225","strategyManager":"0x43727F38be62e5f0B163e5d725b4e78284165455"}} \ No newline at end of file +{"addresses":{"delegationManager":"0xbB9dDB1020F82F93e45DA0e2CFbd27756DA36956","slasher":"0x189d4f39f158664185DBB101791F47485B5bE993","strategy":"0x91E333A3d61862B1FE976351cf0F3b30aff1D202","strategyManager":"0x2f947E51B9A7cF1d6651D0a568261673233ba42b"}} \ No newline at end of file diff --git a/script/output/deployed_poseidon.json b/script/output/deployed_poseidon.json index 89e7552..9424e2b 100644 --- a/script/output/deployed_poseidon.json +++ b/script/output/deployed_poseidon.json @@ -1,8 +1,8 @@ { - "1": "0x22a80E4C09ACd1FD19cdDDD8F020255Ee619dFF0", - "2": "0xe60D87B752Bd1E6b60eA02AD9F5fd65d2f279F12", - "3": "0x3887f9e588Ec904b35e55B98C3746df485D3c25b", - "4": "0x6cC86DA97a0BFd83700EAf4DF66af8A63135B2BC", - "5": "0x0Bf14bD6270174FacD565878C6ad3C7d0bBc65B9", - "6": "0xC74D628EA658902D72Ac04Eabe62452b5Cd73365" + "1": "0xfd312Cf68d8cDC70bAB7AfDccBae9f2D0F9bA291", + "2": "0x8c6dB7d2F49c56aFe04A790d215E4dC6D7451d72", + "3": "0x11b59cE6E2b4509218bf45AF3582dC7E2a1e8a57", + "4": "0xf32358f5C8FFfCF1a7bDb58b270a082abb7Ba1A6", + "5": "0x660A09d2776fB3A4F4c2da6eBAa66996748Ec061", + "6": "0xa6045E9a60517Cf29435663a7f47f425e6020D53" } \ No newline at end of file diff --git a/src/interfaces/ILagrangeCommittee.sol b/src/interfaces/ILagrangeCommittee.sol index 54ed762..cb92cdb 100644 --- a/src/interfaces/ILagrangeCommittee.sol +++ b/src/interfaces/ILagrangeCommittee.sol @@ -1,21 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; -interface ILagrangeCommittee { - struct OperatorStatus { - uint256 amount; - bytes blsPubKey; - uint32 serveUntilBlock; - bool slashed; - } +struct OperatorStatus { + uint256 amount; + bytes blsPubKey; + uint32 serveUntilBlock; + uint32 chainID; + bool slashed; +} - /// Leaf in Lagrange State Committee Trie - struct CommitteeLeaf { - address addr; - uint256 stake; - bytes blsPubKey; - } +struct OperatorUpdate { + address operator; + uint8 updateType; +} +interface ILagrangeCommittee { struct CommitteeDef { uint256 startBlock; uint256 duration; @@ -29,14 +28,24 @@ interface ILagrangeCommittee { } function getServeUntilBlock(address operator) external returns (uint32); - - function setSlashed(address operator, uint256 chainID, bool slashed) external; + + function setSlashed(address operator) external; + function getSlashed(address operator) external returns (bool); - - function getCommittee(uint256 chainID, uint256 blockNumber) external returns (CommitteeData memory, uint256); - function addOperator(address operator, uint256 chainID, bytes memory blsPubKey, uint256 stake, uint32 serveUntilBlock) external; + function getCommittee( + uint32 chainID, + uint256 blockNumber + ) external returns (CommitteeData memory, uint256); - function update(uint256 chainID, uint256 epochNumber) external; -} + function addOperator( + address operator, + bytes memory blsPubKey, + uint32 chainID, + uint32 serveUntilBlock + ) external; + function updateOperator(OperatorUpdate memory opUpdate) external; + + function update(uint32 chainID, uint256 epochNumber) external; +} diff --git a/src/interfaces/ILagrangeService.sol b/src/interfaces/ILagrangeService.sol index dd987cc..781008c 100644 --- a/src/interfaces/ILagrangeService.sol +++ b/src/interfaces/ILagrangeService.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.12; interface ILagrangeService { function register( - uint256 chainID, + uint32 chainID, bytes memory _blsPubKey, uint32 serveUntilBlock ) external; diff --git a/src/library/ArbitrumVerifier.sol b/src/library/ArbitrumVerifier.sol index 59ba77a..02774bd 100644 --- a/src/library/ArbitrumVerifier.sol +++ b/src/library/ArbitrumVerifier.sol @@ -6,7 +6,6 @@ import "solidity-rlp/contracts/Helper.sol"; import "../mock/arbitrum/IOutbox.sol"; contract ArbitrumVerifier is Common { - IOutbox ArbOutbox; using RLPReader for RLPReader.RLPItem; @@ -18,31 +17,37 @@ contract ArbitrumVerifier is Common { } function verifyArbBlock( - bytes memory rlpData, + bytes memory rlpData, uint256 comparisonNumber, - bytes32 comparisonBlockHash, - bytes calldata headerProof, + bytes32 comparisonBlockHash, + bytes calldata headerProof, uint256 chainID ) external view returns (bool) { - RLPReader.RLPItem[] memory decoded = checkAndDecodeRLP(rlpData, comparisonBlockHash); - RLPReader.RLPItem memory extraDataItem = decoded[Common.BLOCK_HEADER_EXTRADATA_INDEX]; - RLPReader.RLPItem memory blockNumberItem = decoded[Common.BLOCK_HEADER_NUMBER_INDEX]; + RLPReader.RLPItem[] memory decoded = checkAndDecodeRLP( + rlpData, + comparisonBlockHash + ); + RLPReader.RLPItem memory extraDataItem = decoded[ + Common.BLOCK_HEADER_EXTRADATA_INDEX + ]; + RLPReader.RLPItem memory blockNumberItem = decoded[ + Common.BLOCK_HEADER_NUMBER_INDEX + ]; uint number = blockNumberItem.toUint(); - + bytes32 extraData = bytes32(extraDataItem.toUintStrict()); //TODO Maybe toUint() - please test this specifically with several cases. bytes32 l2Hash = ArbOutbox.roots(extraData); if (l2Hash == bytes32(0)) { // No such confirmed node... TODO determine how these should be handled return false; } - + //bool hashCheck = l2hash == comparisonBlockHash; //bool numberCheck = number == comparisonNumber; //bool res = hashCheck && numberCheck; - //bool res = true; + //bool res = true; // Verify Proof bool res = true; return res; } - } diff --git a/src/library/Common.sol b/src/library/Common.sol index 1cd76ee..f4662a2 100644 --- a/src/library/Common.sol +++ b/src/library/Common.sol @@ -11,11 +11,17 @@ contract Common { uint public constant BLOCK_HEADER_NUMBER_INDEX = 8; uint public constant BLOCK_HEADER_EXTRADATA_INDEX = 12; - function checkAndDecodeRLP(bytes memory rlpData, bytes32 comparisonBlockHash) public pure returns (RLPReader.RLPItem[] memory) { + function checkAndDecodeRLP( + bytes memory rlpData, + bytes32 comparisonBlockHash + ) public pure returns (RLPReader.RLPItem[] memory) { bytes32 blockHash = keccak256(rlpData); - require(blockHash == comparisonBlockHash, "Hash of RLP data diverges from comparison block hash"); + require( + blockHash == comparisonBlockHash, + "Hash of RLP data diverges from comparison block hash" + ); RLPReader.RLPItem[] memory decoded = rlpData.toRlpItem().toList(); - return decoded; + return decoded; } function _verifyRawHeaderSequence( @@ -43,7 +49,7 @@ contract Common { bytes memory rlpData, bytes32 comparisonBlockHash, uint256 chainID - ) public pure returns (bool) { + ) internal pure returns (bool) { // Verify Block Number RLPReader.RLPItem[] memory decoded = checkAndDecodeRLP( rlpData, diff --git a/src/library/EvidenceVerifier.sol b/src/library/EvidenceVerifier.sol index 4093c95..10243df 100644 --- a/src/library/EvidenceVerifier.sol +++ b/src/library/EvidenceVerifier.sol @@ -31,15 +31,15 @@ contract EvidenceVerifier is Common { OptimismVerifier OptVerify; ArbitrumVerifier ArbVerify; - + function setArbAddr(ArbitrumVerifier _arb) public { - ArbVerify = _arb; + ArbVerify = _arb; } function setOptAddr(OptimismVerifier _opt) public { - OptVerify = _opt; + OptVerify = _opt; } - + function getArbAddr() public view returns (address) /*onlyOwner*/ { return address(ArbVerify); } @@ -61,18 +61,22 @@ contract EvidenceVerifier is Common { bytes32 comparisonBlockHash, uint256 chainID ) public pure returns (bool) { - bool res = _verifyBlockNumber(comparisonNumber, rlpData, comparisonBlockHash, chainID); + bool res = _verifyBlockNumber( + comparisonNumber, + rlpData, + comparisonBlockHash, + chainID + ); bool success = false; if (chainID == CHAIN_ID_ARBITRUM_NITRO) { -// (success, checkpoint) = verifyArbBlock(); + // (success, checkpoint) = verifyArbBlock(); } else if (chainID == CHAIN_ID_OPTIMISM_BEDROCK) { -// (success, checkpoint) = verifyOptBlock(); - } - if (!success) { + // (success, checkpoint) = verifyOptBlock(); } + if (!success) {} return res; } - + function toUint(bytes memory src) internal pure returns (uint) { uint value; for (uint i = 0; i < src.length; i++) { diff --git a/src/library/HermezHelpers.sol b/src/library/HermezHelpers.sol index 19a2aad..a07510b 100644 --- a/src/library/HermezHelpers.sol +++ b/src/library/HermezHelpers.sol @@ -34,6 +34,7 @@ contract PoseidonUnit5 { contract PoseidonUnit6 { function poseidon(uint256[6] memory) public pure returns (uint256) {} } + /** * @dev Rollup helper functions */ @@ -92,23 +93,20 @@ contract HermezHelpers { _insPoseidonUnit6 = PoseidonUnit6(_poseidon6Elements); } - function _hash1Elements(uint256[1] memory inputs) - internal - view - returns (uint256) - { + function _hash1Elements( + uint256[1] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit1.poseidon(inputs); } + /** * @dev Hash poseidon for 2 elements * @param inputs Poseidon input array of 2 elements * @return Poseidon hash */ - function _hash2Elements(uint256[2] memory inputs) - internal - view - returns (uint256) - { + function _hash2Elements( + uint256[2] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit2.poseidon(inputs); } @@ -117,11 +115,9 @@ contract HermezHelpers { * @param inputs Poseidon input array of 3 elements * @return Poseidon hash */ - function _hash3Elements(uint256[3] memory inputs) - internal - view - returns (uint256) - { + function _hash3Elements( + uint256[3] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit3.poseidon(inputs); } @@ -130,40 +126,34 @@ contract HermezHelpers { * @param inputs Poseidon input array of 4 elements * @return Poseidon hash */ - function _hash4Elements(uint256[4] memory inputs) - internal - view - returns (uint256) - { + function _hash4Elements( + uint256[4] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit4.poseidon(inputs); } - function _hash5Elements(uint256[5] memory inputs) - internal - view - returns (uint256) - { + function _hash5Elements( + uint256[5] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit5.poseidon(inputs); } - function _hash6Elements(uint256[6] memory inputs) - internal - view - returns (uint256) - { + function _hash6Elements( + uint256[6] memory inputs + ) internal view returns (uint256) { return _insPoseidonUnit6.poseidon(inputs); } + /** * @dev Hash poseidon for sparse merkle tree nodes * @param left Input element array * @param right Input element array * @return Poseidon hash */ - function _hashNode(uint256 left, uint256 right) - internal - view - returns (uint256) - { + function _hashNode( + uint256 left, + uint256 right + ) internal view returns (uint256) { uint256[2] memory inputs; inputs[0] = left; inputs[1] = right; @@ -176,11 +166,10 @@ contract HermezHelpers { * @param value Input element array * @return Poseidon hash1 */ - function _hashFinalNode(uint256 key, uint256 value) - internal - view - returns (uint256) - { + function _hashFinalNode( + uint256 key, + uint256 value + ) internal view returns (uint256) { uint256[3] memory inputs; inputs[0] = key; inputs[1] = value; @@ -261,7 +250,7 @@ contract HermezHelpers { uint256 e = float >> 35; // never overflow, max "e" value is 32 - uint256 exp = 10**e; + uint256 exp = 10 ** e; // never overflow, max "fix" value is 1023 * 10^32 uint256 fix = m * exp; @@ -325,20 +314,18 @@ contract HermezHelpers { "HermezHelpers::_checkSig: INVALID_S_VALUE" ); - bytes32 encodeData = - keccak256( - abi.encode( - AUTHORISE_TYPEHASH, - HERMEZ_NETWORK_HASH, - ACCOUNT_CREATION_HASH, - babyjub - ) - ); + bytes32 encodeData = keccak256( + abi.encode( + AUTHORISE_TYPEHASH, + HERMEZ_NETWORK_HASH, + ACCOUNT_CREATION_HASH, + babyjub + ) + ); - bytes32 messageDigest = - keccak256( - abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), encodeData) - ); + bytes32 messageDigest = keccak256( + abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), encodeData) + ); address ethAddress = ecrecover(messageDigest, v, r, s); @@ -356,11 +343,9 @@ contract HermezHelpers { * @return ptr ptr to the call data position where the actual data starts * @return len Length of the data */ - function _getCallData(uint256 posParam) - internal - pure - returns (uint256 ptr, uint256 len) - { + function _getCallData( + uint256 posParam + ) internal pure returns (uint256 ptr, uint256 len) { assembly { let pos := add(4, mul(posParam, 32)) ptr := add(calldataload(pos), 4) @@ -394,9 +379,10 @@ contract HermezHelpers { * @param _preBytes bytes storage * @param _postBytes Bytes array memory */ - function _concatStorage(bytes storage _preBytes, bytes memory _postBytes) - internal - { + function _concatStorage( + bytes storage _preBytes, + bytes memory _postBytes + ) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot @@ -419,120 +405,120 @@ contract HermezHelpers { // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) - case 2 { - // Since the new array still fits in the slot, we just need to - // update the contents of the slot. - // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length - sstore( - _preBytes.slot, - // all the modifications to the slot are inside this - // next block + case 2 { + // Since the new array still fits in the slot, we just need to + // update the contents of the slot. + // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length + sstore( + _preBytes.slot, + // all the modifications to the slot are inside this + // next block + add( + // we can just add to the slot contents because the + // bytes we want to change are the LSBs + fslot, add( - // we can just add to the slot contents because the - // bytes we want to change are the LSBs - fslot, - add( - mul( - div( - // load the bytes from memory - mload(add(_postBytes, 0x20)), - // zero all bytes to the right - exp(0x100, sub(32, mlength)) - ), - // and now shift left the number of bytes to - // leave space for the length in the slot - exp(0x100, sub(32, newlength)) + mul( + div( + // load the bytes from memory + mload(add(_postBytes, 0x20)), + // zero all bytes to the right + exp(0x100, sub(32, mlength)) ), - // increase length by the double of the memory - // bytes length - mul(mlength, 2) - ) - ) - ) - } - case 1 { - // The stored value fits in the slot, but the combined value - // will exceed it. - // get the keccak hash to get the contents of the array - mstore(0x0, _preBytes.slot) - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) - - // save new length - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) - - // The contents of the _postBytes array start 32 bytes into - // the structure. Our first read should obtain the `submod` - // bytes that can fit into the unused space in the last word - // of the stored array. To get this, we read 32 bytes starting - // from `submod`, so the data we read overlaps with the array - // contents by `submod` bytes. Masking the lowest-order - // `submod` bytes allows us to add that value directly to the - // stored value. - - let submod := sub(32, slength) - let mc := add(_postBytes, submod) - let end := add(_postBytes, mlength) - let mask := sub(exp(0x100, submod), 1) - - sstore( - sc, - add( - and( - fslot, - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 + // and now shift left the number of bytes to + // leave space for the length in the slot + exp(0x100, sub(32, newlength)) ), - and(mload(mc), mask) + // increase length by the double of the memory + // bytes length + mul(mlength, 2) ) ) + ) + } + case 1 { + // The stored value fits in the slot, but the combined value + // will exceed it. + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes.slot) + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) + + // save new length + sstore(_preBytes.slot, add(mul(newlength, 2), 1)) + + // The contents of the _postBytes array start 32 bytes into + // the structure. Our first read should obtain the `submod` + // bytes that can fit into the unused space in the last word + // of the stored array. To get this, we read 32 bytes starting + // from `submod`, so the data we read overlaps with the array + // contents by `submod` bytes. Masking the lowest-order + // `submod` bytes allows us to add that value directly to the + // stored value. + + let submod := sub(32, slength) + let mc := add(_postBytes, submod) + let end := add(_postBytes, mlength) + let mask := sub(exp(0x100, submod), 1) + + sstore( + sc, + add( + and( + fslot, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 + ), + and(mload(mc), mask) + ) + ) - for { - mc := add(mc, 0x20) - sc := add(sc, 1) - } lt(mc, end) { - sc := add(sc, 1) - mc := add(mc, 0x20) - } { - sstore(sc, mload(mc)) - } + for { + mc := add(mc, 0x20) + sc := add(sc, 1) + } lt(mc, end) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { + sstore(sc, mload(mc)) + } - mask := exp(0x100, sub(mc, end)) + mask := exp(0x100, sub(mc, end)) - sstore(sc, mul(div(mload(mc), mask), mask)) - } - default { - // get the keccak hash to get the contents of the array - mstore(0x0, _preBytes.slot) - // Start copying to the last used word of the stored array. - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) - - // save new length - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) - - // Copy over the first `submod` bytes of the new data as in - // case 1 above. - let slengthmod := mod(slength, 32) - let mlengthmod := mod(mlength, 32) - let submod := sub(32, slengthmod) - let mc := add(_postBytes, submod) - let end := add(_postBytes, mlength) - let mask := sub(exp(0x100, submod), 1) - - sstore(sc, add(sload(sc), and(mload(mc), mask))) - - for { - sc := add(sc, 1) - mc := add(mc, 0x20) - } lt(mc, end) { - sc := add(sc, 1) - mc := add(mc, 0x20) - } { - sstore(sc, mload(mc)) - } - - mask := exp(0x100, sub(mc, end)) - - sstore(sc, mul(div(mload(mc), mask), mask)) + sstore(sc, mul(div(mload(mc), mask), mask)) + } + default { + // get the keccak hash to get the contents of the array + mstore(0x0, _preBytes.slot) + // Start copying to the last used word of the stored array. + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) + + // save new length + sstore(_preBytes.slot, add(mul(newlength, 2), 1)) + + // Copy over the first `submod` bytes of the new data as in + // case 1 above. + let slengthmod := mod(slength, 32) + let mlengthmod := mod(mlength, 32) + let submod := sub(32, slengthmod) + let mc := add(_postBytes, submod) + let end := add(_postBytes, mlength) + let mask := sub(exp(0x100, submod), 1) + + sstore(sc, add(sload(sc), and(mload(mc), mask))) + + for { + sc := add(sc, 1) + mc := add(mc, 0x20) + } lt(mc, end) { + sc := add(sc, 1) + mc := add(mc, 0x20) + } { + sstore(sc, mload(mc)) } + + mask := exp(0x100, sub(mc, end)) + + sstore(sc, mul(div(mload(mc), mask), mask)) + } } } } diff --git a/src/library/OptimismVerifier.sol b/src/library/OptimismVerifier.sol index 3e317b6..4852ab4 100644 --- a/src/library/OptimismVerifier.sol +++ b/src/library/OptimismVerifier.sol @@ -19,8 +19,10 @@ contract OptimismVerifier is Common { uint128 timestamp; uint128 l2BlockNumber; } - - function getOutputHash(bytes32[4] calldata outputProof) public view returns (bytes32) { + + function getOutputHash( + bytes32[4] calldata outputProof + ) public view returns (bytes32) { bytes32 comparisonProof = keccak256( abi.encode( outputProof[0], @@ -31,15 +33,22 @@ contract OptimismVerifier is Common { ); return comparisonProof; } - + function verifyOutputProof( uint256 comparisonNumber, bytes32 comparisonBlockHash, bytes32[4] calldata outputProof -// bytes calldata headerProof, - ) external view returns (bool) { + ) + external + view + returns ( + // bytes calldata headerProof, + bool + ) + { // 1. get next output root - Types.OutputProposal memory outputProposal = L2OutputOracle.getL2OutputAfter(comparisonNumber); + Types.OutputProposal memory outputProposal = L2OutputOracle + .getL2OutputAfter(comparisonNumber); // 2. Derive output root from result bytes32 outputRoot = outputProposal.outputRoot; // 3. Verify independently generated proof against diff --git a/src/protocol/LagrangeCommittee.sol b/src/protocol/LagrangeCommittee.sol index 9b4a8a1..2ca03c6 100644 --- a/src/protocol/LagrangeCommittee.sol +++ b/src/protocol/LagrangeCommittee.sol @@ -4,6 +4,10 @@ pragma solidity ^0.8.12; import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {IStrategyManager} from "eigenlayer-contracts/interfaces/IStrategyManager.sol"; +import {IServiceManager} from "eigenlayer-contracts/interfaces/IServiceManager.sol"; +import {VoteWeigherBase} from "eigenlayer-contracts/middleware/VoteWeigherBase.sol"; + import "../library/HermezHelpers.sol"; import "../library/EvidenceVerifier.sol"; import "../interfaces/ILagrangeCommittee.sol"; @@ -13,8 +17,13 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, HermezHelpers, + VoteWeigherBase, ILagrangeCommittee { + uint8 public constant UPDATE_TYPE_REGISTER = 1; + uint8 public constant UPDATE_TYPE_AMOUNT_CHANGE = 2; + uint8 public constant UPDATE_TYPE_UNREGISTER = 3; + ILagrangeService public immutable service; // Active Committee @@ -22,29 +31,26 @@ contract LagrangeCommittee is // Frozen Committee - Next "Current" Committee uint256 public constant COMMITTEE_NEXT_1 = 1; - // ChainID => Address - mapping(uint256 => address[]) public addedAddrs; - mapping(uint256 => address[]) public removedAddrs; + // ChainID => OperatorUpdate[] + mapping(uint32 => OperatorUpdate[]) public updatedOperators; // ChainID => Committee - mapping(uint256 => CommitteeDef) public CommitteeParams; + mapping(uint32 => CommitteeDef) public committeeParams; // ChainID => Epoch => CommitteeData - mapping(uint256 => mapping(uint256 => CommitteeData)) public Committees; + mapping(uint32 => mapping(uint256 => CommitteeData)) public committees; /* Live Committee Data */ - // ChainID => Committee Map Length - mapping(uint256 => uint256) public CommitteeMapLength; - // ChainID => Committee Leaf Hash - mapping(uint256 => uint256[]) public CommitteeMapKeys; - // ChainID => Committee Leaf Hash => Committee Leaf - mapping(uint256 => mapping(uint256 => CommitteeLeaf)) public CommitteeMap; // ChainID => Merkle Nodes - mapping(uint256 => uint256[]) public CommitteeLeaves; + mapping(uint32 => uint256[]) public committeeLeaves; + // ChainID => Address => CommitteeLeaf Index + mapping(uint32 => mapping(address => uint32)) public committeeLeavesMap; + // ChainID => Address[] + mapping(uint32 => address[]) public committeeAddrs; mapping(address => OperatorStatus) public operators; // ChainID => Epoch check if committee tree has been updated - mapping(uint256 => uint256) public updatedEpoch; + mapping(uint32 => uint256) public updatedEpoch; // Event fired on initialization of a new committee event InitCommittee( @@ -59,12 +65,24 @@ contract LagrangeCommittee is modifier onlyService() { require( msg.sender == address(service), - "Only service can call this function." + "Only Lagrange service can call this function." + ); + _; + } + + modifier onlyServiceManager() { + require( + msg.sender == address(serviceManager), + "Only Lagrange service manager can call this function." ); _; } - constructor(ILagrangeService _service) { + constructor( + ILagrangeService _service, + IServiceManager _serviceManager, + IStrategyManager _strategyManager + ) VoteWeigherBase(_strategyManager, _serviceManager, 5) { service = _service; _disableInitializers(); } @@ -92,25 +110,22 @@ contract LagrangeCommittee is // Initialize new committee. function _initCommittee( - uint256 chainID, + uint32 chainID, uint256 _duration, uint256 freezeDuration ) internal { require( - CommitteeParams[chainID].startBlock == 0, + committeeParams[chainID].startBlock == 0, "Committee has already been initialized." ); - CommitteeParams[chainID] = CommitteeDef( + committeeParams[chainID] = CommitteeDef( block.number, _duration, freezeDuration ); - Committees[chainID][0] = CommitteeData(0, 0, 0); - - CommitteeMapKeys[chainID] = new uint256[](0); - CommitteeMapLength[chainID] = 0; - CommitteeLeaves[chainID] = new uint256[](0); + committees[chainID][0] = CommitteeData(0, 0, 0); + committeeLeaves[chainID] = new uint256[](0); emit InitCommittee(chainID, _duration, freezeDuration); } @@ -119,62 +134,29 @@ contract LagrangeCommittee is return operators[operator].serveUntilBlock; } - function setSlashed( - address operator, - uint256 chainID, - bool slashed - ) public onlyService { - operators[operator].slashed = slashed; - removedAddrs[chainID].push(operator); - } - - function getSlashed(address operator) public view returns (bool) { - return operators[operator].slashed; - } - - // Remove address from committee map for chainID, update keys and length/height - function _removeCommitteeAddr(uint256 chainID, address addr) internal { - for (uint256 i = 0; i < CommitteeMapKeys[chainID].length; i++) { - uint256 _i = CommitteeMapKeys[chainID][i]; - uint256 _if = CommitteeMapKeys[chainID][ - CommitteeMapKeys[chainID].length - 1 - ]; - if (CommitteeMap[chainID][_i].addr == addr) { - CommitteeMap[chainID][_i] = CommitteeMap[chainID][_if]; - CommitteeMap[chainID][_if] = CommitteeLeaf(address(0), 0, ""); - - CommitteeMapKeys[chainID][i] = CommitteeMapKeys[chainID][ - CommitteeMapKeys[chainID].length - 1 - ]; - CommitteeMapKeys[chainID].pop; - CommitteeMapLength[chainID]--; - } + function updateOperator( + OperatorUpdate memory opUpdate + ) external onlyServiceManager { + if (opUpdate.updateType == UPDATE_TYPE_AMOUNT_CHANGE) { + operators[opUpdate.operator].amount = weightOfOperator( + opUpdate.operator, + 1 + ); } + updatedOperators[operators[opUpdate.operator].chainID].push(opUpdate); } - // Add address to committee (NEXT_2) trie - function _committeeAdd( - uint256 chainID, - address addr, - uint256 stake, - bytes memory _blsPubKey - ) internal { - require( - CommitteeParams[chainID].startBlock > 0, - "A committee for this chain ID has not been initialized." - ); + function setSlashed(address operator) external onlyService { + operators[operator].slashed = true; + } - CommitteeLeaf memory cleaf = CommitteeLeaf(addr, stake, _blsPubKey); - uint256 lhash = getLeafHash(cleaf); - CommitteeMap[chainID][lhash] = cleaf; - CommitteeMapKeys[chainID].push(lhash); - CommitteeMapLength[chainID]++; - CommitteeLeaves[chainID].push(lhash); + function getSlashed(address operator) public view returns (bool) { + return operators[operator].slashed; } // Returns chain's committee current and next roots at a given block. function getCommittee( - uint256 chainID, + uint32 chainID, uint256 blockNumber ) public @@ -183,24 +165,24 @@ contract LagrangeCommittee is { uint256 epochNumber = getEpochNumber(chainID, blockNumber); uint256 nextEpoch = getEpochNumber(chainID, blockNumber + 1); - currentCommittee = Committees[chainID][epochNumber]; - nextRoot = Committees[chainID][nextEpoch].root; + currentCommittee = committees[chainID][epochNumber]; + nextRoot = committees[chainID][nextEpoch].root; } // Computes and returns "next" committee root. function getNext1CommitteeRoot( - uint256 chainID + uint32 chainID ) public view returns (uint256) { - if (CommitteeLeaves[chainID].length == 0) { + if (committeeLeaves[chainID].length == 0) { return _hash2Elements([uint256(0), uint256(0)]); - } else if (CommitteeLeaves[chainID].length == 1) { - return CommitteeLeaves[chainID][0]; + } else if (committeeLeaves[chainID].length == 1) { + return committeeLeaves[chainID][0]; } // Calculate limit uint256 _lim = 2; uint256 height = 1; - uint256 dataLength = CommitteeLeaves[chainID].length; + uint256 dataLength = committeeLeaves[chainID].length; while (_lim < dataLength) { _lim *= 2; ++height; @@ -212,8 +194,8 @@ contract LagrangeCommittee is uint256 i = 1; for (; i < dataLength; i += 2) { _h = 0; - right = CommitteeLeaves[chainID][i]; - branches[0] = CommitteeLeaves[chainID][i - 1]; + right = committeeLeaves[chainID][i]; + branches[0] = committeeLeaves[chainID][i - 1]; while ((i >> _h) & 1 == 1) { right = _hash2Elements([branches[_h], right]); _h++; @@ -222,7 +204,7 @@ contract LagrangeCommittee is } if (i == dataLength) { - branches[0] = CommitteeLeaves[chainID][i - 1]; + branches[0] = committeeLeaves[chainID][i - 1]; _h = 0; right = 0; while ((i >> _h) & 1 == 1) { @@ -248,20 +230,21 @@ contract LagrangeCommittee is } // Recalculates committee root (next_1) - function _compCommitteeRoot(uint256 chainID, uint256 epochNumber) internal { + function _compCommitteeRoot(uint32 chainID, uint256 epochNumber) internal { uint256 nextRoot = getNext1CommitteeRoot(chainID); // Update roots uint256 nextEpoch = epochNumber + COMMITTEE_NEXT_1; - Committees[chainID][nextEpoch].height = CommitteeMapLength[chainID]; - Committees[chainID][nextEpoch].root = nextRoot; - Committees[chainID][nextEpoch] - .totalVotingPower = _getTotalVotingPower(chainID); - } - + committees[chainID][nextEpoch].height = committeeLeaves[chainID].length; + committees[chainID][nextEpoch].root = nextRoot; + committees[chainID][nextEpoch].totalVotingPower = _getTotalVotingPower( + chainID + ); + } + // Initializes a new committee, and optionally associates addresses with it. function registerChain( - uint256 chainID, + uint32 chainID, uint256 epochPeriod, uint256 freezeDuration ) public onlyOwner { @@ -272,33 +255,35 @@ contract LagrangeCommittee is // Adds address stake data and flags it for committee addition function addOperator( address operator, - uint256 chainID, bytes memory blsPubKey, - uint256 stake, + uint32 chainID, uint32 serveUntilBlock ) public onlyService { - addedAddrs[chainID].push(operator); + uint96 stakeAmount = weightOfOperator(operator, 1); operators[operator] = OperatorStatus( - stake, + stakeAmount, blsPubKey, serveUntilBlock, + chainID, false ); } function isUpdatable( - uint256 epochNumber, - uint256 chainID + uint32 chainID, + uint256 epochNumber ) public view returns (bool) { - uint256 epochEnd = epochNumber * CommitteeParams[chainID].duration + CommitteeParams[chainID].startBlock; - uint256 freezeDuration = CommitteeParams[chainID].freezeDuration; + uint256 epochEnd = epochNumber * + committeeParams[chainID].duration + + committeeParams[chainID].startBlock; + uint256 freezeDuration = committeeParams[chainID].freezeDuration; return block.number > epochEnd - freezeDuration; } // If applicable, updates committee based on staking, unstaking, and slashing. - function update(uint256 chainID, uint256 epochNumber) public { + function update(uint32 chainID, uint256 epochNumber) public { require( - isUpdatable(epochNumber, chainID), + isUpdatable(chainID, epochNumber), "Block number is prior to committee freeze window." ); @@ -307,88 +292,125 @@ contract LagrangeCommittee is _update(chainID, epochNumber); } - function _update(uint256 chainID, uint256 epochNumber) internal { - for (uint256 i = 0; i < addedAddrs[chainID].length; i++) { - address addedAddr = addedAddrs[chainID][i]; - OperatorStatus memory op = operators[addedAddr]; - _committeeAdd(chainID, addedAddr, op.amount, op.blsPubKey); - } - for (uint256 i = 0; i < removedAddrs[chainID].length; i++) { - _removeCommitteeAddr(chainID, removedAddrs[chainID][i]); + function _update(uint32 chainID, uint256 epochNumber) internal { + for (uint256 i = 0; i < updatedOperators[chainID].length; i++) { + OperatorUpdate memory opUpdate = updatedOperators[chainID][i]; + if (opUpdate.updateType == UPDATE_TYPE_REGISTER) { + _registerOperator(opUpdate.operator); + } else if (opUpdate.updateType == UPDATE_TYPE_AMOUNT_CHANGE) { + _updateAmount(opUpdate.operator); + } else if (opUpdate.updateType == UPDATE_TYPE_UNREGISTER) { + _unregisterOperator(opUpdate.operator); + } } _compCommitteeRoot(chainID, epochNumber); updatedEpoch[chainID] = epochNumber; - - delete addedAddrs[chainID]; - delete removedAddrs[chainID]; + delete updatedOperators[chainID]; emit UpdateCommittee( chainID, - bytes32(Committees[chainID][epochNumber + COMMITTEE_NEXT_1].root) + bytes32(committees[chainID][epochNumber + COMMITTEE_NEXT_1].root) ); } + function _registerOperator(address operator) internal { + uint32 chainID = operators[operator].chainID; + uint32 leafIndex = uint32(committeeLeaves[chainID].length); + committeeLeaves[chainID].push(getLeafHash(operator)); + committeeLeavesMap[chainID][operator] = leafIndex; + committeeAddrs[chainID].push(operator); + } + + function _updateAmount(address operator) internal { + uint32 chainID = operators[operator].chainID; + uint256 leafIndex = committeeLeavesMap[chainID][operator]; + committeeLeaves[chainID][leafIndex] = getLeafHash(operator); + } + + function _unregisterOperator(address operator) internal { + uint32 chainID = operators[operator].chainID; + uint32 leafIndex = uint32(committeeLeavesMap[chainID][operator]); + uint256 lastIndex = committeeLeaves[chainID].length - 1; + address lastAddr = committeeAddrs[chainID][lastIndex]; + + committeeLeaves[chainID][leafIndex] = committeeLeaves[chainID][ + lastIndex + ]; + committeeLeavesMap[chainID][lastAddr] = leafIndex; + committeeLeaves[chainID].pop(); + committeeAddrs[chainID].pop(); + } + // Computes epoch number for a chain's committee at a given block function getEpochNumber( - uint256 chainID, + uint32 chainID, uint256 blockNumber ) public view returns (uint256) { - uint256 startBlockNumber = CommitteeParams[chainID].startBlock; - uint256 epochPeriod = CommitteeParams[chainID].duration; + uint256 startBlockNumber = committeeParams[chainID].startBlock; + uint256 epochPeriod = committeeParams[chainID].duration; return (blockNumber - startBlockNumber) / epochPeriod + 1; } // Returns cumulative strategy shares for opted in addresses function _getTotalVotingPower( - uint256 chainID + uint32 chainID ) internal view returns (uint256) { uint256 total = 0; - for (uint256 i = 0; i < CommitteeMapLength[chainID]; i++) { - total += CommitteeMap[chainID][CommitteeMapKeys[chainID][i]].stake; + for (uint256 i = 0; i < committeeAddrs[chainID].length; i++) { + total += operators[committeeAddrs[chainID][i]].amount; } return total; } - function getLeafSlices(CommitteeLeaf memory leaf) public pure returns (uint96[11] memory slices) { + function getLeafHash(address opAddr) public view returns (uint256) { + uint96[11] memory slices; + OperatorStatus memory opStatus = operators[opAddr]; for (uint i = 0; i < 8; i++) { bytes memory addr = new bytes(12); for (uint j = 0; j < 12; j++) { - addr[j] = leaf.blsPubKey[(i*12)+j]; + addr[j] = opStatus.blsPubKey[(i * 12) + j]; } - bytes12 addr_chunk = bytes12(addr); - slices[i] = uint96(addr_chunk); + bytes12 addrChunk = bytes12(addr); + slices[i] = uint96(addrChunk); } - bytes memory addr_bytes = abi.encodePacked(leaf.addr, uint128(leaf.stake)); + bytes memory addrBytes = abi.encodePacked( + opAddr, + uint128(opStatus.amount) + ); for (uint i = 0; i < 3; i++) { bytes memory addr = new bytes(12); for (uint j = 0; j < 12; j++) { - addr[j] = addr_bytes[(i*12)+j]; + addr[j] = addrBytes[(i * 12) + j]; } - bytes12 addr_chunk = bytes12(addr); - slices[i+8] = uint96(addr_chunk); + bytes12 addrChunk = bytes12(addr); + slices[i + 8] = uint96(addrChunk); } - } - function getLeafHash( - CommitteeLeaf memory cleaf - ) public view returns (uint256) { - uint96[11] memory leaf_slices = getLeafSlices(cleaf); - - return _hash2Elements([_hash6Elements([ - uint256(leaf_slices[0]), - uint256(leaf_slices[1]), - uint256(leaf_slices[2]), - uint256(leaf_slices[3]), - uint256(leaf_slices[4]), - uint256(leaf_slices[5]) - ]), _hash5Elements([ - uint256(leaf_slices[6]), - uint256(leaf_slices[7]), - uint256(leaf_slices[8]), - uint256(leaf_slices[9]), - uint256(leaf_slices[10]) - ])]); + return + _hash2Elements( + [ + _hash6Elements( + [ + uint256(slices[0]), + uint256(slices[1]), + uint256(slices[2]), + uint256(slices[3]), + uint256(slices[4]), + uint256(slices[5]) + ] + ), + _hash5Elements( + [ + uint256(slices[6]), + uint256(slices[7]), + uint256(slices[8]), + uint256(slices[9]), + uint256(slices[10]) + ] + ) + ] + ); } } diff --git a/src/protocol/LagrangeService.sol b/src/protocol/LagrangeService.sol index 9faeb84..2a21a19 100644 --- a/src/protocol/LagrangeService.sol +++ b/src/protocol/LagrangeService.sol @@ -2,8 +2,6 @@ pragma solidity ^0.8.12; import {IServiceManager} from "eigenlayer-contracts/interfaces/IServiceManager.sol"; -import {IStrategyManager} from "eigenlayer-contracts/interfaces/IStrategyManager.sol"; -import {VoteWeigherBase} from "eigenlayer-contracts/middleware/VoteWeigherBase.sol"; import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; @@ -16,10 +14,14 @@ contract LagrangeService is Initializable, OwnableUpgradeable, ILagrangeService, - EvidenceVerifier, - VoteWeigherBase + EvidenceVerifier { + uint256 public constant UPDATE_TYPE_REGISTER = 1; + uint256 public constant UPDATE_TYPE_AMOUNT_CHANGE = 2; + uint256 public constant UPDATE_TYPE_UNREGISTER = 3; + ILagrangeCommittee public immutable committee; + IServiceManager public immutable serviceManager; event OperatorRegistered(address operator, uint32 serveUntilBlock); @@ -38,11 +40,11 @@ contract LagrangeService is ); constructor( - IServiceManager _serviceManager, ILagrangeCommittee _committee, - IStrategyManager _strategyManager - ) VoteWeigherBase(_strategyManager, _serviceManager, 5) { + IServiceManager _serviceManager + ) { committee = _committee; + serviceManager = _serviceManager; _disableInitializers(); } @@ -53,21 +55,13 @@ contract LagrangeService is /// Add the operator to the service. // Only unfractinalized WETH strategy shares assumed for stake amount function register( - uint256 chainID, + uint32 chainID, bytes memory _blsPubKey, uint32 serveUntilBlock ) external { - uint96 stakeAmount = weightOfOperator(msg.sender, 1); - require(stakeAmount > 0, "The stake amount is zero"); - + // NOTE: Please ensure that the order of the following two lines remains unchanged + committee.addOperator(msg.sender, _blsPubKey, chainID, serveUntilBlock); serviceManager.recordFirstStakeUpdate(msg.sender, serveUntilBlock); - committee.addOperator( - msg.sender, - chainID, - _blsPubKey, - stakeAmount, - serveUntilBlock - ); emit OperatorRegistered(msg.sender, serveUntilBlock); } @@ -104,7 +98,7 @@ contract LagrangeService is evidence.chainID ) ) { - _freezeOperator(evidence.operator, evidence.chainID); + _freezeOperator(evidence.operator); } if ( @@ -117,7 +111,7 @@ contract LagrangeService is evidence.chainID ) ) { - _freezeOperator(evidence.operator, evidence.chainID); + _freezeOperator(evidence.operator); } //_freezeOperator(evidence.operator,evidence.chainID); // TODO what is this for (no condition)? @@ -151,7 +145,8 @@ contract LagrangeService is chainID ) && blockHash == correctBlockHash; } -/* + + /* function verifyRawHeaderSequence(bytes32 latestHash, bytes[] calldata sequence) public view returns (bool) { return _verifyRawHeaderSequence(latestHash, sequence); } @@ -163,7 +158,7 @@ contract LagrangeService is bytes32 correctNextCommitteeRoot, bytes32 nextCommitteeRoot, uint256 blockNumber, - uint256 chainID + uint32 chainID ) internal returns (bool) { ( ILagrangeCommittee.CommitteeData memory currentCommittee, @@ -184,9 +179,9 @@ contract LagrangeService is } /// Slash the given operator - function _freezeOperator(address operator, uint256 chainID) internal { + function _freezeOperator(address operator) internal { + committee.setSlashed(operator); serviceManager.freezeOperator(operator); - committee.setSlashed(operator, chainID, true); emit OperatorSlashed(operator); } diff --git a/src/protocol/LagrangeServiceManager.sol b/src/protocol/LagrangeServiceManager.sol index f2fa750..fe6c48c 100644 --- a/src/protocol/LagrangeServiceManager.sol +++ b/src/protocol/LagrangeServiceManager.sol @@ -7,38 +7,69 @@ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISlasher} from "eigenlayer-contracts/interfaces/ISlasher.sol"; import {IServiceManager} from "eigenlayer-contracts/interfaces/IServiceManager.sol"; -//import "../interfaces/ILagrangeCommittee.sol"; +import {ILagrangeCommittee, OperatorUpdate} from "../interfaces/ILagrangeCommittee.sol"; +import {ILagrangeService} from "../interfaces/ILagrangeService.sol"; contract LagrangeServiceManager is Initializable, OwnableUpgradeable, IServiceManager { + uint8 public constant UPDATE_TYPE_REGISTER = 1; + uint8 public constant UPDATE_TYPE_AMOUNT_CHANGE = 2; + uint8 public constant UPDATE_TYPE_UNREGISTER = 3; + ISlasher public immutable slasher; + ILagrangeCommittee public immutable committee; + ILagrangeService public immutable service; uint32 public taskNumber = 0; uint32 public latestServeUntilBlock = 0; - constructor(ISlasher _slasher) { + modifier onlyService() { + require( + msg.sender == address(service), + "Only Lagrange service can call this function." + ); + _; + } + + constructor( + ISlasher _slasher, + ILagrangeCommittee _committee, + ILagrangeService _service + ) { slasher = _slasher; + committee = _committee; + service = _service; _disableInitializers(); } - function initialize( - address initialOwner - ) external initializer { + function initialize(address initialOwner) external initializer { _transferOwnership(initialOwner); } // slash the given operator - function freezeOperator(address operator) external { + function freezeOperator(address operator) external onlyService { + committee.updateOperator( + OperatorUpdate({ + operator: operator, + updateType: UPDATE_TYPE_UNREGISTER + }) + ); slasher.freezeOperator(operator); } function recordFirstStakeUpdate( address operator, uint32 serveUntilBlock - ) external { + ) external onlyService { + committee.updateOperator( + OperatorUpdate({ + operator: operator, + updateType: UPDATE_TYPE_REGISTER + }) + ); slasher.recordFirstStakeUpdate(operator, serveUntilBlock); } @@ -48,6 +79,12 @@ contract LagrangeServiceManager is uint32 serveUntilBlock, uint256 prevElement ) external { + committee.updateOperator( + OperatorUpdate({ + operator: operator, + updateType: UPDATE_TYPE_AMOUNT_CHANGE + }) + ); slasher.recordStakeUpdate( operator, updateBlock, @@ -59,14 +96,25 @@ contract LagrangeServiceManager is function recordLastStakeUpdateAndRevokeSlashingAbility( address operator, uint32 serveUntilBlock - ) external { + ) external onlyService { + committee.updateOperator( + OperatorUpdate({ + operator: operator, + updateType: UPDATE_TYPE_UNREGISTER + }) + ); slasher.recordLastStakeUpdateAndRevokeSlashingAbility( operator, serveUntilBlock ); } - function owner() public view override(OwnableUpgradeable, IServiceManager) returns (address) { + function owner() + public + view + override(OwnableUpgradeable, IServiceManager) + returns (address) + { return OwnableUpgradeable.owner(); } } diff --git a/test/e2e/test_committee_goerli.js b/test/e2e/test_committee_goerli.js index b49bd95..beea82c 100644 --- a/test/e2e/test_committee_goerli.js +++ b/test/e2e/test_committee_goerli.js @@ -18,12 +18,17 @@ const arbChainID = 421613; const optChainID = 420; -contract.getEpochNumber(arbChainID, 9372848).then((epoch) => { +contract.getEpochNumber(arbChainID, 9377423).then((epoch) => { console.log("Arb epoch: ", epoch); }); +contract.getCommittee(arbChainID, 9377423).then((current) => { + console.log("Arb Current committee: ", current[0]); + console.log("Arb Next committee: ", current[1]); +}); + -contract.getEpochNumber(optChainID, 9372848).then((epoch) => { +contract.getEpochNumber(optChainID, 9377423).then((epoch) => { console.log("Opt epoch: ", epoch); });