From 27a28df1e493903b89ecb65c4e02836c71f74ac7 Mon Sep 17 00:00:00 2001 From: noel Date: Tue, 14 May 2024 01:39:14 +0900 Subject: [PATCH] test: MachServiceManager --- contracts/test/AVSDeployer.sol | 6 +- contracts/test/BLSAVSDeployer.sol | 164 ++++++++++++++++++++++++ contracts/test/MachServiceManager.t.sol | 59 ++++++++- 3 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 contracts/test/BLSAVSDeployer.sol diff --git a/contracts/test/AVSDeployer.sol b/contracts/test/AVSDeployer.sol index 98bcff6..944620f 100644 --- a/contracts/test/AVSDeployer.sol +++ b/contracts/test/AVSDeployer.sol @@ -76,7 +76,7 @@ contract AVSDeployer is Test { /// @notice StakeRegistry, Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; - address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); + address public proxyAdminOwner = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38; address public registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); address public unpauser = address(uint160(uint256(keccak256("unpauser")))); @@ -100,12 +100,12 @@ contract AVSDeployer is Test { uint32 defaultMaxOperatorCount = 10; uint16 defaultKickBIPsOfOperatorStake = 15000; uint16 defaultKickBIPsOfTotalStake = 150; - uint8 numQuorums = 192; + uint8 numQuorums = 1; IRegistryCoordinator.OperatorSetParam[] operatorSetParams; uint8 maxQuorumsToRegisterFor = 4; - uint256 maxOperatorsToRegister = 4; + uint256 maxOperatorsToRegister = 10; uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; diff --git a/contracts/test/BLSAVSDeployer.sol b/contracts/test/BLSAVSDeployer.sol new file mode 100644 index 0000000..c256ce0 --- /dev/null +++ b/contracts/test/BLSAVSDeployer.sol @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import {BLSSignatureChecker} from "eigenlayer-middleware/BLSSignatureChecker.sol"; +import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; +import {OperatorStateRetriever} from "eigenlayer-middleware/OperatorStateRetriever.sol"; +import {BitmapUtils} from "eigenlayer-middleware/libraries/BitmapUtils.sol"; +import {AVSDeployer} from "./AVSDeployer.sol"; + +contract BLSAVSDeployer is AVSDeployer { + using BN254 for BN254.G1Point; + + bytes32 msgHash; + uint256 aggSignerPrivKey = 69; + BN254.G2Point aggSignerApkG2; + BN254.G2Point oneHundredQuorumApkG2; + BN254.G1Point sigma; + + function _setUpBLSMockAVSDeployer() public virtual { + _deployMockEigenLayerAndAVS(); + _setAggregatePublicKeysAndSignature(); + } + + function _setUpBLSMockAVSDeployer(uint8 numQuorumsToAdd) public virtual { + _deployMockEigenLayerAndAVS(numQuorumsToAdd); + _setAggregatePublicKeysAndSignature(); + } + + function _setAggregatePublicKeysAndSignature() internal { + // aggSignerPrivKey*g2 + aggSignerApkG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; + aggSignerApkG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; + aggSignerApkG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; + aggSignerApkG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; + + // 100*aggSignerPrivKey*g2 + oneHundredQuorumApkG2.X[1] = 6187649255575786743153792867265230878737103598736372524337965086852090105771; + oneHundredQuorumApkG2.X[0] = 5334877400925935887383922877430837542135722474116902175395820705628447222839; + oneHundredQuorumApkG2.Y[1] = 4668116328019846503695710811760363536142902258271850958815598072072236299223; + oneHundredQuorumApkG2.Y[0] = 21446056442597180561077194011672151329458819211586246807143487001691968661015; + + sigma = BN254.hashToG1(msgHash).scalar_mul(aggSignerPrivKey); + } + + function _generateSignerAndNonSignerPrivateKeys( + uint256 pseudoRandomNumber, + uint256 numSigners, + uint256 numNonSigners + ) internal view returns (uint256[] memory, uint256[] memory) { + uint256[] memory signerPrivateKeys = new uint256[](numSigners); + // generate numSigners numbers that add up to aggSignerPrivKey mod BN254.FR_MODULUS + uint256 sum = 0; + for (uint256 i = 0; i < numSigners - 1; i++) { + signerPrivateKeys[i] = + uint256(keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + sum = addmod(sum, signerPrivateKeys[i], BN254.FR_MODULUS); + } + // signer private keys need to add to aggSignerPrivKey + signerPrivateKeys[numSigners - 1] = + addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); + + uint256[] memory nonSignerPrivateKeys = new uint256[](numNonSigners); + for (uint256 i = 0; i < numNonSigners; i++) { + nonSignerPrivateKeys[i] = + uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + } + + // Sort nonSignerPrivateKeys in order of ascending pubkeyHash + // Uses insertion sort to sort array in place + for (uint256 i = 1; i < nonSignerPrivateKeys.length; i++) { + uint256 privateKey = nonSignerPrivateKeys[i]; + bytes32 pubkeyHash = _toPubkeyHash(privateKey); + uint256 j = i; + + // Move elements of nonSignerPrivateKeys[0..i-1] that are greater than the current key + // to one position ahead of their current position + while (j > 0 && _toPubkeyHash(nonSignerPrivateKeys[j - 1]) > pubkeyHash) { + nonSignerPrivateKeys[j] = nonSignerPrivateKeys[j - 1]; + j--; + } + nonSignerPrivateKeys[j] = privateKey; + } + + return (signerPrivateKeys, nonSignerPrivateKeys); + } + + function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + uint256 pseudoRandomNumber, + uint256 numNonSigners, + uint256 quorumBitmap + ) internal returns (uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { + (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = + _generateSignerAndNonSignerPrivateKeys( + pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners + ); + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + + // randomly combine signer and non-signer private keys + uint256[] memory privateKeys = new uint256[](maxOperatorsToRegister); + // generate addresses and public keys + address[] memory operators = new address[](maxOperatorsToRegister); + BN254.G1Point[] memory pubkeys = new BN254.G1Point[](maxOperatorsToRegister); + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature; + nonSignerStakesAndSignature.quorumApks = new BN254.G1Point[](quorumNumbers.length); + nonSignerStakesAndSignature.nonSignerPubkeys = new BN254.G1Point[](numNonSigners); + bytes32[] memory nonSignerOperatorIds = new bytes32[](numNonSigners); + { + uint256 signerIndex = 0; + uint256 nonSignerIndex = 0; + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { + uint256 randomSeed = uint256(keccak256(abi.encodePacked("privKeyCombination", i))); + if (randomSeed % 2 == 0 && signerIndex < signerPrivateKeys.length) { + privateKeys[i] = signerPrivateKeys[signerIndex]; + signerIndex++; + } else if (nonSignerIndex < nonSignerPrivateKeys.length) { + privateKeys[i] = nonSignerPrivateKeys[nonSignerIndex]; + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = + BN254.generatorG1().scalar_mul(privateKeys[i]); + nonSignerOperatorIds[nonSignerIndex] = + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); + nonSignerIndex++; + } else { + privateKeys[i] = signerPrivateKeys[signerIndex]; + signerIndex++; + } + + operators[i] = _incrementAddress(defaultOperator, i); + pubkeys[i] = BN254.generatorG1().scalar_mul(privateKeys[i]); + + // add the public key to each quorum + for (uint256 j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { + nonSignerStakesAndSignature.quorumApks[j] = + nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); + } + } + } + + // register all operators for the first quorum + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { + cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); + _registerOperatorWithCoordinator(operators[i], quorumBitmap, pubkeys[i], defaultStake); + } + + uint32 referenceBlockNumber = + registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; + cheats.roll(referenceBlockNumber + 100); + + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever + .getCheckSignaturesIndices(registryCoordinator, referenceBlockNumber, quorumNumbers, nonSignerOperatorIds); + + nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; + nonSignerStakesAndSignature.apkG2 = aggSignerApkG2; + nonSignerStakesAndSignature.sigma = sigma; + nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; + nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; + nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; + + return (referenceBlockNumber, nonSignerStakesAndSignature); + } + + function _toPubkeyHash(uint256 privKey) internal view returns (bytes32) { + return BN254.generatorG1().scalar_mul(privKey).hashG1Point(); + } +} diff --git a/contracts/test/MachServiceManager.t.sol b/contracts/test/MachServiceManager.t.sol index 70596bb..b034fc3 100644 --- a/contracts/test/MachServiceManager.t.sol +++ b/contracts/test/MachServiceManager.t.sol @@ -1,17 +1,31 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./AVSDeployer.sol"; +import "forge-std/Test.sol"; + +import "./BLSAVSDeployer.sol"; import "../src/error/Errors.sol"; +import "../src/interfaces/IMachServiceManager.sol"; -contract MachServiceManagerTest is AVSDeployer { +contract MachServiceManagerTest is BLSAVSDeployer { event OperatorAllowed(address operator); event OperatorDisallowed(address operator); event AllowlistEnabled(); event AllowlistDisabled(); + event AlertConfirmerChanged(address previousAddress, address newAddress); + event WhitelisterChanged(address previousAddress, address newAddress); + event QuorumThresholdPercentageChanged(uint8 thresholdPercentages); + event RollupChainIdUpdated(uint256 previousRollupChainId, uint256 newRollupChainId); + event AlertConfirmed(bytes32 indexed alertHeaderHash, bytes32 messageHash); function setUp() public virtual { _deployMockEigenLayerAndAVS(); + + msgHash = keccak256( + abi.encode(IMachServiceManager.ReducedAlertHeader({messageHash: "foo", referenceBlockNumber: 201})) + ); + + _setAggregatePublicKeysAndSignature(); } function test_AddToAllowlist() public { @@ -112,4 +126,45 @@ contract MachServiceManagerTest is AVSDeployer { serviceManager.enableAllowlist(); vm.stopPrank(); } + + function test_confirmAlert() public { + vm.startPrank(proxyAdminOwner); + serviceManager.disableAllowlist(); + vm.stopPrank(); + + uint256 nonRandomNumber = 111; + uint256 numNonSigners = 1; + uint256 quorumBitmap = 1; + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); + + ( + BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, + /* bytes32 signatoryRecordHash */ + ) = serviceManager.checkSignatures(msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature); + + bytes memory quorumThresholdPercentages = new bytes(1); + quorumThresholdPercentages[0] = bytes1(uint8(67)); + + IMachServiceManager.AlertHeader memory alertHeader = IMachServiceManager.AlertHeader({ + messageHash: "foo", + quorumNumbers: quorumNumbers, + quorumThresholdPercentages: quorumThresholdPercentages, + referenceBlockNumber: referenceBlockNumber + }); + + vm.startPrank(proxyAdminOwner); + vm.expectEmit(); + emit AlertConfirmed(msgHash, alertHeader.messageHash); + serviceManager.confirmAlert(alertHeader, nonSignerStakesAndSignature); + + vm.expectRevert(AlreadyAdded.selector); + serviceManager.confirmAlert(alertHeader, nonSignerStakesAndSignature); + + vm.stopPrank(); + } }