-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
203 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,64 @@ | ||
// 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); | ||
|
||
function setUp() public virtual { | ||
_deployMockEigenLayerAndAVS(); | ||
} | ||
|
||
function test_AddToAllowlist() public { | ||
vm.startPrank(proxyAdminOwner); | ||
assertFalse(serviceManager.allowlist(defaultOperator), "mismatch"); | ||
vm.expectEmit(); | ||
emit OperatorAllowed(defaultOperator); | ||
serviceManager.addToAllowlist(defaultOperator); | ||
assertTrue(serviceManager.allowlist(defaultOperator), "mismatch"); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_AddToAllowlist_RevertIfZeroAddress() public { | ||
vm.startPrank(proxyAdminOwner); | ||
vm.expectRevert(ZeroAddress.selector); | ||
serviceManager.addToAllowlist(address(0)); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_AddToAllowlist_RevertIfAlreadyInAllowlist() public { | ||
test_AddToAllowlist(); | ||
vm.startPrank(proxyAdminOwner); | ||
vm.expectRevert(AlreadyInAllowlist.selector); | ||
serviceManager.addToAllowlist(defaultOperator); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_RemoveFromAllowlist() public { | ||
test_AddToAllowlist(); | ||
vm.startPrank(proxyAdminOwner); | ||
assertTrue(serviceManager.allowlist(defaultOperator), "Operator should be in allowlist before removal"); | ||
|
||
vm.expectEmit(); | ||
emit OperatorDisallowed(defaultOperator); | ||
|
||
serviceManager.removeFromAllowlist(defaultOperator); | ||
|
||
assertFalse(serviceManager.allowlist(defaultOperator), "Operator should not be in allowlist after removal"); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_RemoveFromAllowlist_RevertIfNotInAllowlist() public { | ||
address nonListedOperator = address(0xdead); | ||
|
||
function test_confirmAlert() public { | ||
vm.startPrank(proxyAdminOwner); | ||
assertFalse(serviceManager.allowlist(nonListedOperator), "Operator should not be in allowlist"); | ||
|
||
vm.expectRevert(NotInAllowlist.selector); | ||
serviceManager.removeFromAllowlist(nonListedOperator); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_DisableAllowlist() public { | ||
vm.startPrank(proxyAdminOwner); | ||
assertTrue(serviceManager.allowlistEnabled(), "Allowlist should be enabled initially"); | ||
|
||
vm.expectEmit(true, true, true, true); // Check all parameters of the event | ||
emit AllowlistDisabled(); | ||
|
||
serviceManager.disableAllowlist(); | ||
|
||
assertFalse(serviceManager.allowlistEnabled(), "Allowlist should be disabled after calling disableAllowlist"); | ||
vm.stopPrank(); | ||
} | ||
|
||
function test_DisableAllowlist_RevertIfAlreadyDisabled() public { | ||
vm.startPrank(proxyAdminOwner); | ||
|
||
// First, ensure the allowlist is disabled | ||
serviceManager.disableAllowlist(); | ||
|
||
assertFalse(serviceManager.allowlistEnabled(), "Allowlist should already be disabled"); | ||
vm.expectRevert(AlreadyDisabled.selector); // Expect the specific revert for trying to disable an already disabled allowlist | ||
serviceManager.disableAllowlist(); | ||
vm.stopPrank(); | ||
} | ||
uint256 nonRandomNumber = 111; | ||
uint256 numNonSigners = 1; | ||
uint256 quorumBitmap = 1; | ||
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); | ||
|
||
function test_EnableAllowlist() public { | ||
vm.startPrank(proxyAdminOwner); | ||
msgHash = keccak256( | ||
abi.encode(IMachServiceManager.ReducedAlertHeader({messageHash: "foo", referenceBlockNumber: 201})) | ||
); | ||
|
||
// First, ensure the allowlist is disabled | ||
serviceManager.disableAllowlist(); | ||
_setAggregatePublicKeysAndSignature(); | ||
|
||
assertFalse(serviceManager.allowlistEnabled(), "Allowlist should be disabled initially"); | ||
( | ||
uint32 referenceBlockNumber, | ||
BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature | ||
) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); | ||
|
||
vm.expectEmit(true, true, true, true); // Check all parameters of the event | ||
emit AllowlistEnabled(); | ||
( | ||
BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, | ||
/* bytes32 signatoryRecordHash */ | ||
) = serviceManager.checkSignatures(msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature); | ||
|
||
serviceManager.enableAllowlist(); | ||
bytes memory quorumThresholdPercentages = new bytes(1); | ||
quorumThresholdPercentages[0] = bytes1(uint8(67)); | ||
|
||
assertTrue(serviceManager.allowlistEnabled(), "Allowlist should be enabled after calling enableAllowlist"); | ||
vm.stopPrank(); | ||
} | ||
IMachServiceManager.AlertHeader memory header = IMachServiceManager.AlertHeader({ | ||
messageHash: "foo", | ||
quorumNumbers: quorumNumbers, | ||
quorumThresholdPercentages: quorumThresholdPercentages, | ||
referenceBlockNumber: referenceBlockNumber | ||
}); | ||
|
||
function test_EnableAllowlist_RevertIfAlreadyEnabled() public { | ||
vm.startPrank(proxyAdminOwner); | ||
vm.expectRevert(AlreadyEnabled.selector); // Expect the specific revert for trying to enable an already enabled allowlist | ||
serviceManager.enableAllowlist(); | ||
serviceManager.confirmAlert(header, nonSignerStakesAndSignature); | ||
vm.stopPrank(); | ||
} | ||
} |