diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9df98ab --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +SEPOLIA_RPC_URL= +ETHERSCAN_API_KEY= +PRIVATE_KEY= +ACCOUNT_ADDRESS= diff --git a/foundry.toml b/foundry.toml index 6b5a1e5..f348782 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,7 +2,17 @@ src = "src" out = "out" libs = ["lib"] -solc = "^0.8.20" + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +solc = "0.8.22" +# https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +ffi = true +ast = true +build_info = true +extra_output = ["storageLayout"] +[rpc_endpoints] +sepolia = "${SEPOLIA_RPC_URL}" +[etherscan] +sepolia = { key = "${ETHERSCAN_API_KEY}" } diff --git a/script/deployFinalityRelayer.s.sol b/script/deployFinalityRelayer.s.sol index 1e46703..a5c56a0 100644 --- a/script/deployFinalityRelayer.s.sol +++ b/script/deployFinalityRelayer.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.20; import "forge-std/Vm.sol"; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; diff --git a/src/bls/BLSApkRegistry.sol b/src/bls/BLSApkRegistry.sol index b158dde..843ff6c 100644 --- a/src/bls/BLSApkRegistry.sol +++ b/src/bls/BLSApkRegistry.sol @@ -11,8 +11,6 @@ import "../interfaces/IBLSApkRegistry.sol"; import "./BLSApkRegistryStorage.sol"; - - contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, BLSApkRegistryStorage, EIP712 { using BN254 for BN254.G1Point; @@ -28,43 +26,43 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B modifier onlyRelayerManager() { require( - msg.sender == relayerManager, - "BLSApkRegistry.onlyRelayerManager: caller is not the relayer manager address" + msg.sender == relayerManager, "BLSApkRegistry.onlyRelayerManager: caller is not the relayer manager address" ); _; } - constructor() - EIP712("BLSApkRegistry", "v0.0.1") - { + constructor() EIP712("BLSApkRegistry", "v0.0.1") { _disableInitializers(); } - function initialize( - address _initialOwner, - address _finalityRelayerManager, - address _relayerManager - ) external initializer { + function initialize(address _initialOwner, address _finalityRelayerManager, address _relayerManager) + external + initializer + { _transferOwnership(_initialOwner); finalityRelayerManager = _finalityRelayerManager; relayerManager = _relayerManager; _initializeApk(); } - function registerOperator( - address operator - ) public onlyFinalityRelayerManager { - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + /** + * @dev Registers an operator within the system. + * + * This function allows the Finality Relayer Manager to add an operator to the system. + * The operator's public key is retrieved and used to update the associated proof (APK). + * + * @param operator The address of the operator to be registered. + */ + function registerOperator(address operator) public onlyFinalityRelayerManager { + (BN254.G1Point memory pubkey,) = getRegisteredPubkey(operator); _processApkUpdate(pubkey); emit OperatorAdded(operator, operatorToPubkeyHash[operator]); } - function deregisterOperator( - address operator - ) public onlyFinalityRelayerManager { - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + function deregisterOperator(address operator) public onlyFinalityRelayerManager { + (BN254.G1Point memory pubkey,) = getRegisteredPubkey(operator); _processApkUpdate(pubkey.negate()); emit OperatorRemoved(operator, operatorToPubkeyHash[operator]); @@ -82,10 +80,7 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, - "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); + require(pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey"); require( operatorToPubkeyHash[operator] == bytes32(0), "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" @@ -96,23 +91,30 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B "BLSApkRegistry.registerBLSPublicKey: public key already registered" ); - uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.Y, - pubkeyRegistrationMessageHash.X, - pubkeyRegistrationMessageHash.Y - ))) % BN254.FR_MODULUS; - - require(BN254.pairing( - params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), - BN254.negGeneratorG2(), - pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - params.pubkeyG2 - ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + uint256 gamma = uint256( + keccak256( + abi.encodePacked( + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, + pubkeyRegistrationMessageHash.Y + ) + ) + ) % BN254.FR_MODULUS; + + require( + BN254.pairing( + params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), + BN254.negGeneratorG2(), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + params.pubkeyG2 + ), + "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" + ); operatorToPubkey[operator] = params.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; @@ -123,12 +125,14 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B return pubkeyHash; } - function checkSignatures( - bytes32 msgHash, - uint256 referenceBlockNumber, - FinalityNonSignerAndSignature memory params - ) public view returns (StakeTotals memory, bytes32) { - require(referenceBlockNumber < uint32(block.number), "BLSSignatureChecker.checkSignatures: invalid reference block"); + function checkSignatures(bytes32 msgHash, uint256 referenceBlockNumber, FinalityNonSignerAndSignature memory params) + public + view + returns (StakeTotals memory, bytes32) + { + require( + referenceBlockNumber < uint32(block.number), "BLSSignatureChecker.checkSignatures: invalid reference block" + ); BN254.G1Point memory signerApk = BN254.G1Point(0, 0); bytes32[] memory nonSignersPubkeyHashes; if (params.nonSignerPubkeys.length > 0) { @@ -140,25 +144,21 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B } else { signerApk = currentApk; } - (bool pairingSuccessful, bool signatureIsValid) = trySignatureAndApkVerification(msgHash, signerApk, params.apkG2, params.sigma); + (bool pairingSuccessful, bool signatureIsValid) = + trySignatureAndApkVerification(msgHash, signerApk, params.apkG2, params.sigma); require(pairingSuccessful, "BLSSignatureChecker.checkSignatures: pairing precompile call failed"); require(signatureIsValid, "BLSSignatureChecker.checkSignatures: signature is invalid"); bytes32 signatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, nonSignersPubkeyHashes)); - StakeTotals memory stakeTotals = StakeTotals({ - totalBtcStaking: params.totalBtcStake, - totalMantaStaking: params.totalMantaStake - }); + StakeTotals memory stakeTotals = + StakeTotals({totalBtcStaking: params.totalBtcStake, totalMantaStaking: params.totalMantaStake}); return (stakeTotals, signatoryRecordHash); } function addOrRemoveBlsRegisterWhitelist(address register, bool isAdd) external onlyRelayerManager { - require( - register != address(0), - "BLSApkRegistry.addOrRemoverBlsRegisterWhitelist: operator address is zero" - ); + require(register != address(0), "BLSApkRegistry.addOrRemoverBlsRegisterWhitelist: operator address is zero"); blsRegisterWhitelist[register] = isAdd; } @@ -167,8 +167,14 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B BN254.G1Point memory apk, BN254.G2Point memory apkG2, BN254.G1Point memory sigma - ) public view returns(bool pairingSuccessful, bool siganatureIsValid) { - uint256 gamma = uint256(keccak256(abi.encodePacked(msgHash, apk.X, apk.Y, apkG2.X[0], apkG2.X[1], apkG2.Y[0], apkG2.Y[1], sigma.X, sigma.Y))) % BN254.FR_MODULUS; + ) public view returns (bool pairingSuccessful, bool siganatureIsValid) { + uint256 gamma = uint256( + keccak256( + abi.encodePacked( + msgHash, apk.X, apk.Y, apkG2.X[0], apkG2.X[1], apkG2.Y[0], apkG2.Y[1], sigma.X, sigma.Y + ) + ) + ) % BN254.FR_MODULUS; (pairingSuccessful, siganatureIsValid) = BN254.safePairing( sigma.plus(apk.scalar_mul(gamma)), BN254.negGeneratorG2(), @@ -194,11 +200,9 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B lastUpdate.apkHash = newApkHash; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - apkHistory.push(ApkUpdate({ - apkHash: newApkHash, - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); + apkHistory.push( + ApkUpdate({apkHash: newApkHash, updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0}) + ); } } @@ -206,30 +210,24 @@ contract BLSApkRegistry is Initializable, OwnableUpgradeable, IBLSApkRegistry, B BN254.G1Point memory pubkey = operatorToPubkey[operator]; bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + require(pubkeyHash != bytes32(0), "BLSApkRegistry.getRegisteredPubkey: operator is not registered"); return (pubkey, pubkeyHash); } - function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4( - keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) - ) - ); + function getPubkeyRegMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1(_hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)))); } function _initializeApk() internal { require(apkHistory.length == 0, "BLSApkRegistry.initializeApk: apk already exists"); - apkHistory.push(ApkUpdate({ - apkHash: bytes24(0), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); + apkHistory.push( + ApkUpdate({apkHash: bytes24(0), updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0}) + ); } + function getPubkeyHash(address operator) public view returns (bytes32) { + return operatorToPubkeyHash[operator]; + } } diff --git a/src/bls/BLSApkRegistryStorage.sol b/src/bls/BLSApkRegistryStorage.sol index c564de2..a143745 100644 --- a/src/bls/BLSApkRegistryStorage.sol +++ b/src/bls/BLSApkRegistryStorage.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -import "../libraries/BN254.sol"; - -import "../interfaces/IBLSApkRegistry.sol"; -import "../interfaces/IFinalityRelayerManager.sol"; +import {IBLSApkRegistry} from "../interfaces/IBLSApkRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { // Constants diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 3e00458..321e02d 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -29,21 +29,11 @@ interface IBLSApkRegistry { uint256 totalMantaStaking; } - event NewPubkeyRegistration( - address indexed operator, - BN254.G1Point pubkeyG1, - BN254.G2Point pubkeyG2 - ); + event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); - event OperatorAdded( - address operator, - bytes32 operatorId - ); + event OperatorAdded(address operator, bytes32 operatorId); - event OperatorRemoved( - address operator, - bytes32 operatorId - ); + event OperatorRemoved(address operator, bytes32 operatorId); function registerOperator(address operator) external; @@ -55,17 +45,14 @@ interface IBLSApkRegistry { BN254.G1Point memory msgHash ) external returns (bytes32); - function checkSignatures( - bytes32 msgHash, - uint256 referenceBlockNumber, - FinalityNonSignerAndSignature memory params - ) external view returns (StakeTotals memory, bytes32); - + function checkSignatures(bytes32 msgHash, uint256 referenceBlockNumber, FinalityNonSignerAndSignature memory params) + external + view + returns (StakeTotals memory, bytes32); function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); function addOrRemoveBlsRegisterWhitelist(address operator, bool isAdd) external; - function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); - + function getPubkeyRegMessageHash(address operator) external view returns (BN254.G1Point memory); } diff --git a/src/utils/EmptyContract.sol b/src/utils/EmptyContract.sol index 31b4f6e..88bf495 100644 --- a/src/utils/EmptyContract.sol +++ b/src/utils/EmptyContract.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract EmptyContract { diff --git a/test/BLSApkRegistry.t.sol b/test/BLSApkRegistry.t.sol index b014983..b537b4c 100644 --- a/test/BLSApkRegistry.t.sol +++ b/test/BLSApkRegistry.t.sol @@ -2,12 +2,56 @@ pragma solidity ^0.8.20; import {Test, console} from "forge-std/Test.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {BLSApkRegistry} from "../src/bls/BLSApkRegistry.sol"; -import {BN254} from "../src/libraries/BN254.sol"; -import {IBLSApkRegistry} from "../src/interfaces/IBLSApkRegistry.sol"; +import "../src/libraries/BN254.sol"; -// forge test -vvvv contract BLSApkRegistryTest is Test { + using BN254 for BN254.G1Point; + + ERC1967Proxy proxy; + BLSApkRegistry internal blsApkRegistry; + + Account internal owner = makeAccount("owner"); + Account internal finalityRelayerManager = makeAccount("finalityRelayerManager"); + Account internal relayerManager = makeAccount("relayerManager"); + Account internal operator = makeAccount("operator"); + function setUp() public { + BLSApkRegistry implementation = new BLSApkRegistry(); + proxy = new ERC1967Proxy( + address(implementation), + abi.encodeCall(implementation.initialize, (owner.addr, finalityRelayerManager.addr, relayerManager.addr)) + ); + blsApkRegistry = BLSApkRegistry(address(proxy)); + emit log_address(owner.addr); + } + + function testAddBlsRegisterWhitelist() public { + vm.prank(relayerManager.addr); + blsApkRegistry.addOrRemoveBlsRegisterWhitelist(operator.addr, true); + assertEq(blsApkRegistry.blsRegisterWhitelist(operator.addr), true); + } + + function testRemoveBlsRegisterWhitelist() public { + vm.prank(relayerManager.addr); + blsApkRegistry.addOrRemoveBlsRegisterWhitelist(operator.addr, true); + vm.prank(relayerManager.addr); + blsApkRegistry.addOrRemoveBlsRegisterWhitelist(operator.addr, false); + assertEq(blsApkRegistry.blsRegisterWhitelist(operator.addr), false); + } + + function testAddBlsRegisterWhitelistWithZeroAddress() public { + vm.prank(relayerManager.addr); + vm.expectRevert("BLSApkRegistry.addOrRemoverBlsRegisterWhitelist: operator address is zero"); + blsApkRegistry.addOrRemoveBlsRegisterWhitelist(address(0), true); + } + + function testNonRelayerManagerCannotAddBlsRegisterWhitelist() public { + address nonRelayerManager = address(0x2); + vm.prank(nonRelayerManager); + vm.expectRevert(bytes("BLSApkRegistry.onlyRelayerManager: caller is not the relayer manager address")); + blsApkRegistry.addOrRemoveBlsRegisterWhitelist(operator.addr, true); } } diff --git a/test/FinalityRelayerManager.t.sol b/test/FinalityRelayerManager.t.sol index 5ce46f9..7f6a1f0 100644 --- a/test/FinalityRelayerManager.t.sol +++ b/test/FinalityRelayerManager.t.sol @@ -1,8 +1,6 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Test, console} from "forge-std/Test.sol"; -contract FinalityRelayerManagerTest is Test { - -} +contract FinalityRelayerManagerTest is Test {}