From 837d35489e10db61e1824791f6f4bcd3c6b564fd Mon Sep 17 00:00:00 2001 From: Borja Aranda Date: Thu, 19 Dec 2024 11:34:52 +0100 Subject: [PATCH] add finished scripts --- contracts/foundry.toml | 5 ++ contracts/script/DeployGroupMessages.s.sol | 55 +++++++++++++----- contracts/script/DeployIdentityUpdates.s.sol | 55 +++++++++++++----- .../31337/group_messages_deployment.json | 10 ++++ .../31337/identity_updates_deployment.json | 10 ++++ .../upgrades/UpgradeGroupMessages.s.sol | 55 ++++++++++++------ .../upgrades/UpgradeIdentityUpdates.s.sol | 57 +++++++++++++------ contracts/script/utils/Environment.sol | 9 +++ contracts/script/utils/Utils.sol | 36 ++++++++++++ contracts/src/GroupMessages.sol | 11 +++- contracts/src/IdentityUpdates.sol | 9 ++- contracts/test/GroupMessage.t.sol | 4 +- contracts/test/IdentityUpdates.t.sol | 4 ++ 13 files changed, 254 insertions(+), 66 deletions(-) create mode 100644 contracts/script/output/31337/group_messages_deployment.json create mode 100644 contracts/script/output/31337/identity_updates_deployment.json create mode 100644 contracts/script/utils/Environment.sol create mode 100644 contracts/script/utils/Utils.sol diff --git a/contracts/foundry.toml b/contracts/foundry.toml index acf06f0e..773ba6cd 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -12,6 +12,11 @@ remappings = [ "@openzeppelin-contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/", "forge-std/=dependencies/forge-std-1.9.4/", ] +fs_permissions = [ + { access = "read-write", path = "script/output/31337"}, + { access = "read-write", path = "script/output/11155111"}, + { access = "read-write", path = "script/output/241320161"} +] [soldeer] recursive_deps = true diff --git a/contracts/script/DeployGroupMessages.s.sol b/contracts/script/DeployGroupMessages.s.sol index 8b49702e..8e273bdd 100644 --- a/contracts/script/DeployGroupMessages.s.sol +++ b/contracts/script/DeployGroupMessages.s.sol @@ -3,32 +3,59 @@ pragma solidity 0.8.28; import "forge-std/src/Script.sol"; import "forge-std/src/Vm.sol"; +import "./utils/Utils.sol"; +import "./utils/Environment.sol"; import "src/GroupMessages.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -contract DeployGroupMessages is Script { +contract DeployGroupMessages is Script, Utils, Environment { + GroupMessages groupMessagesImpl; + ERC1967Proxy proxy; + + address admin; + address deployer; + function run() external { + admin = vm.envAddress("XMTP_GROUP_MESSAGES_ADMIN_ADDRESS"); + require(admin != address(0), "XMTP_GROUP_MESSAGES_ADMIN_ADDRESS not set"); + require(admin.code.length == 0, "admin address is a contract, not an EOA"); + uint256 privateKey = vm.envUint("PRIVATE_KEY"); - address deployer = vm.addr(privateKey); + deployer = vm.addr(privateKey); vm.startBroadcast(privateKey); - // Step 1: Deploy the implementation contract. - GroupMessages groupMessagesImpl = new GroupMessages(); + // Deploy the implementation contract. + groupMessagesImpl = new GroupMessages(); + require(address(groupMessagesImpl) != address(0), "Implementation deployment failed"); - // Step 2: Deploy the proxy contract. - ERC1967Proxy proxy = + // Deploy the proxy contract. + proxy = new ERC1967Proxy( address(groupMessagesImpl), - abi.encodeWithSelector(GroupMessages.initialize.selector, deployer) - ); - - console.log( - '{"deployer":"%s","proxy":"%s","implementation":"%s"}', - deployer, - address(proxy), - address(groupMessagesImpl) + abi.encodeWithSelector(GroupMessages.initialize.selector, admin) ); vm.stopBroadcast(); + + _serializeDeploymentData(); + } + + function _serializeDeploymentData() internal { + string memory parent_object = "parent object"; + string memory addresses = "addresses"; + + string memory addressesOutput; + + addressesOutput = vm.serializeAddress(addresses, "groupMessagesDeployer", deployer); + addressesOutput = vm.serializeAddress(addresses, "groupMessagesProxyAdmin", admin); + addressesOutput = vm.serializeAddress(addresses, "groupMessagesProxy", address(proxy)); + addressesOutput = vm.serializeAddress(addresses, "groupMessagesImpl", address(groupMessagesImpl)); + + string memory finalJson; + finalJson = vm.serializeString(parent_object, addresses, addressesOutput); + finalJson = vm.serializeUint(parent_object, "deploymentBlock", block.number); + finalJson = vm.serializeUint(parent_object, "latestUpgradeBlock", block.number); + + writeOutput(finalJson, XMTP_GROUP_MESSAGES_OUTPUT_JSON); } } diff --git a/contracts/script/DeployIdentityUpdates.s.sol b/contracts/script/DeployIdentityUpdates.s.sol index cf969b33..99182568 100644 --- a/contracts/script/DeployIdentityUpdates.s.sol +++ b/contracts/script/DeployIdentityUpdates.s.sol @@ -3,32 +3,59 @@ pragma solidity 0.8.28; import "forge-std/src/Script.sol"; import "forge-std/src/Vm.sol"; +import "./utils/Utils.sol"; +import "./utils/Environment.sol"; import "src/IdentityUpdates.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -contract DeployIdentityUpdates is Script { +contract DeployIdentityUpdates is Script, Utils, Environment { + IdentityUpdates idUpdatesImpl; + ERC1967Proxy proxy; + + address admin; + address deployer; + function run() external { + admin = vm.envAddress("XMTP_IDENTITY_UPDATES_ADMIN_ADDRESS"); + require(admin != address(0), "XMTP_IDENTITY_UPDATES_ADMIN_ADDRESS not set"); + require(admin.code.length == 0, "admin address is a contract, not an EOA"); + uint256 privateKey = vm.envUint("PRIVATE_KEY"); - address deployer = vm.addr(privateKey); + deployer = vm.addr(privateKey); vm.startBroadcast(privateKey); - // Step 1: Deploy the implementation contract - IdentityUpdates idUpdatesImpl = new IdentityUpdates(); + // Deploy the implementation contract. + idUpdatesImpl = new IdentityUpdates(); + require(address(idUpdatesImpl) != address(0), "Implementation deployment failed"); - // Step 2: Deploy the proxy contract - ERC1967Proxy proxy = + // Deploy the proxy contract. + proxy = new ERC1967Proxy( address(idUpdatesImpl), - abi.encodeWithSelector(IdentityUpdates.initialize.selector, deployer) - ); - - console.log( - '{"deployer":"%s","proxy":"%s","implementation":"%s"}', - deployer, - address(proxy), - address(idUpdatesImpl) + abi.encodeWithSelector(IdentityUpdates.initialize.selector, admin) ); vm.stopBroadcast(); + + _serializeDeploymentData(); + } + + function _serializeDeploymentData() internal { + string memory parent_object = "parent object"; + string memory addresses = "addresses"; + + string memory addressesOutput; + + addressesOutput = vm.serializeAddress(addresses, "identityUpdatesDeployer", deployer); + addressesOutput = vm.serializeAddress(addresses, "identityUpdatesProxyAdmin", admin); + addressesOutput = vm.serializeAddress(addresses, "identityUpdatesProxy", address(proxy)); + addressesOutput = vm.serializeAddress(addresses, "identityUpdatesImpl", address(idUpdatesImpl)); + + string memory finalJson; + finalJson = vm.serializeString(parent_object, addresses, addressesOutput); + finalJson = vm.serializeUint(parent_object, "deploymentBlock", block.number); + finalJson = vm.serializeUint(parent_object, "latestUpgradeBlock", block.number); + + writeOutput(finalJson, XMTP_IDENTITY_UPDATES_OUTPUT_JSON); } } diff --git a/contracts/script/output/31337/group_messages_deployment.json b/contracts/script/output/31337/group_messages_deployment.json new file mode 100644 index 00000000..25f1f10b --- /dev/null +++ b/contracts/script/output/31337/group_messages_deployment.json @@ -0,0 +1,10 @@ +{ + "addresses": { + "groupMessagesDeployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "groupMessagesImpl": "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A", + "groupMessagesProxy": "0xD5ac451B0c50B9476107823Af206eD814a2e2580", + "groupMessagesProxyAdmin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "deploymentBlock": 104, + "latestUpgradeBlock": 110 +} \ No newline at end of file diff --git a/contracts/script/output/31337/identity_updates_deployment.json b/contracts/script/output/31337/identity_updates_deployment.json new file mode 100644 index 00000000..5e6af9b5 --- /dev/null +++ b/contracts/script/output/31337/identity_updates_deployment.json @@ -0,0 +1,10 @@ +{ + "addresses": { + "identityUpdatesDeployer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "identityUpdatesImpl": "0x18E317A7D70d8fBf8e6E893616b52390EbBdb629", + "identityUpdatesProxy": "0x5067457698Fd6Fa1C6964e416b3f42713513B3dD", + "identityUpdatesProxyAdmin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + }, + "deploymentBlock": 100, + "latestUpgradeBlock": 108 +} \ No newline at end of file diff --git a/contracts/script/upgrades/UpgradeGroupMessages.s.sol b/contracts/script/upgrades/UpgradeGroupMessages.s.sol index 38b04c0d..e1c8fbd2 100644 --- a/contracts/script/upgrades/UpgradeGroupMessages.s.sol +++ b/contracts/script/upgrades/UpgradeGroupMessages.s.sol @@ -3,34 +3,57 @@ pragma solidity 0.8.28; import "forge-std/src/Script.sol"; import "forge-std/src/Vm.sol"; +import "../utils/Utils.sol"; +import "../utils/Environment.sol"; import "src/GroupMessages.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -contract UpgradeGroupMessages is Script { - function run() external { - address proxyAddress = vm.envAddress("XMTP_GROUP_MESSAGES_PROXY_ADDRESS"); - require(proxyAddress != address(0), "XMTP_GROUP_MESSAGES_PROXY_ADDRESS not set"); +contract UpgradeGroupMessages is Script, Utils, Environment { + GroupMessages newImplementation; + GroupMessages proxy; + + address upgrader; + function run() external { uint256 privateKey = vm.envUint("PRIVATE_KEY"); - address upgrader = vm.addr(privateKey); + upgrader = vm.addr(privateKey); + vm.startBroadcast(privateKey); - // Step 1: Deploy the new implementation contract. - GroupMessages newImplementation = new GroupMessages(); + _initializeProxy(); - // Step 2: Initialize the proxy. - GroupMessages proxy = GroupMessages(proxyAddress); + // Deploy the new implementation contract. + newImplementation = new GroupMessages(); + require(address(newImplementation) != address(0), "Implementation deployment failed"); - // Step 3: Upgrade the proxy pointer to the new implementation. + // Upgrade the proxy pointer to the new implementation. proxy.upgradeToAndCall(address(newImplementation), ""); - console.log( - '{"upgrader":"%s","proxy":"%s","newImplementation":"%s"}', - upgrader, - address(proxy), - address(newImplementation) + vm.stopBroadcast(); + + _serializeUpgradeData(); + } + + function _initializeProxy() internal { + string memory fileContent = readOutput(XMTP_GROUP_MESSAGES_OUTPUT_JSON); + proxy = GroupMessages(stdJson.readAddress(fileContent, ".addresses.groupMessagesProxy")); + require(address(proxy) != address(0), "proxy address not set"); + require( + proxy.hasRole(proxy.DEFAULT_ADMIN_ROLE(), upgrader), + "Upgrader must have admin role" ); + } - vm.stopBroadcast(); + function _serializeUpgradeData() internal { + vm.writeJson( + vm.toString(address(newImplementation)), + getOutputPath(XMTP_GROUP_MESSAGES_OUTPUT_JSON), + ".addresses.groupMessagesImpl" + ); + vm.writeJson( + vm.toString(block.number), + getOutputPath(XMTP_GROUP_MESSAGES_OUTPUT_JSON), + ".latestUpgradeBlock" + ); } } diff --git a/contracts/script/upgrades/UpgradeIdentityUpdates.s.sol b/contracts/script/upgrades/UpgradeIdentityUpdates.s.sol index 4c9d2dd4..22ebbe7f 100644 --- a/contracts/script/upgrades/UpgradeIdentityUpdates.s.sol +++ b/contracts/script/upgrades/UpgradeIdentityUpdates.s.sol @@ -3,34 +3,57 @@ pragma solidity 0.8.28; import "forge-std/src/Script.sol"; import "forge-std/src/Vm.sol"; +import "../utils/Utils.sol"; +import "../utils/Environment.sol"; import "src/IdentityUpdates.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -contract UpgradeGroupMessages is Script { - function run() external { - address proxyAddress = vm.envAddress("XMTP_IDENTITY_UPDATES_PROXY_ADDRESS"); - require(proxyAddress != address(0), "XMTP_IDENTITY_UPDATES_PROXY_ADDRESS not set"); +contract UpgradeIdentityUpdates is Script, Utils, Environment { + IdentityUpdates newImplementation; + IdentityUpdates proxy; + + address upgrader; + function run() external { uint256 privateKey = vm.envUint("PRIVATE_KEY"); - address upgrader = vm.addr(privateKey); + upgrader = vm.addr(privateKey); + vm.startBroadcast(privateKey); - - // Step 1: Deploy the new implementation contract. - IdentityUpdates newImplementation = new IdentityUpdates(); - // Step 2: Initialize the proxy. - IdentityUpdates proxy = IdentityUpdates(proxyAddress); + _initializeProxy(); - // Step 3: Upgrade the proxy pointer to the new implementation. + // Deploy the new implementation contract. + newImplementation = new IdentityUpdates(); + require(address(newImplementation) != address(0), "Implementation deployment failed"); + + // Upgrade the proxy pointer to the new implementation. proxy.upgradeToAndCall(address(newImplementation), ""); - console.log( - '{"upgrader":"%s","proxy":"%s","newImplementation":"%s"}', - upgrader, - address(proxy), - address(newImplementation) + vm.stopBroadcast(); + + _serializeUpgradeData(); + } + + function _initializeProxy() internal { + string memory fileContent = readOutput(XMTP_IDENTITY_UPDATES_OUTPUT_JSON); + proxy = IdentityUpdates(stdJson.readAddress(fileContent, ".addresses.identityUpdatesProxy")); + require(address(proxy) != address(0), "proxy address not set"); + require( + proxy.hasRole(proxy.DEFAULT_ADMIN_ROLE(), upgrader), + "Upgrader must have admin role" ); + } - vm.stopBroadcast(); + function _serializeUpgradeData() internal { + vm.writeJson( + vm.toString(address(newImplementation)), + getOutputPath(XMTP_IDENTITY_UPDATES_OUTPUT_JSON), + ".addresses.identityUpdatesImpl" + ); + vm.writeJson( + vm.toString(block.number), + getOutputPath(XMTP_IDENTITY_UPDATES_OUTPUT_JSON), + ".latestUpgradeBlock" + ); } } diff --git a/contracts/script/utils/Environment.sol b/contracts/script/utils/Environment.sol new file mode 100644 index 00000000..14ab89d4 --- /dev/null +++ b/contracts/script/utils/Environment.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import "forge-std/src/Script.sol"; + +contract Environment is Script { + string public constant XMTP_GROUP_MESSAGES_OUTPUT_JSON = "group_messages_deployment"; + string public constant XMTP_IDENTITY_UPDATES_OUTPUT_JSON = "identity_updates_deployment"; +} diff --git a/contracts/script/utils/Utils.sol b/contracts/script/utils/Utils.sol new file mode 100644 index 00000000..f9fb8fb0 --- /dev/null +++ b/contracts/script/utils/Utils.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import "forge-std/src/Script.sol"; +import "forge-std/src/StdJson.sol"; + +contract Utils is Script { + function readInput(string memory inputFileName) internal view returns (string memory) { + string memory file = getInputPath(inputFileName); + return vm.readFile(file); + } + + function getInputPath(string memory inputFileName) internal view returns (string memory) { + string memory inputDir = string.concat(vm.projectRoot(), "/script/input/"); + string memory chainDir = string.concat(vm.toString(block.chainid), "/"); + string memory file = string.concat(inputFileName, ".json"); + return string.concat(inputDir, chainDir, file); + } + + function readOutput(string memory outputFileName) internal view returns (string memory) { + string memory file = getOutputPath(outputFileName); + return vm.readFile(file); + } + + function writeOutput(string memory outputJson, string memory outputFileName) internal { + string memory outputFilePath = getOutputPath(outputFileName); + vm.writeJson(outputJson, outputFilePath); + } + + function getOutputPath(string memory outputFileName) internal view returns (string memory) { + string memory outputDir = string.concat(vm.projectRoot(), "/script/output/"); + string memory chainDir = string.concat(vm.toString(block.chainid), "/"); + string memory outputFilePath = string.concat(outputDir, chainDir, outputFileName, ".json"); + return outputFilePath; + } +} \ No newline at end of file diff --git a/contracts/src/GroupMessages.sol b/contracts/src/GroupMessages.sol index 20482eeb..0f2a3177 100644 --- a/contracts/src/GroupMessages.sol +++ b/contracts/src/GroupMessages.sol @@ -11,6 +11,11 @@ contract GroupMessages is Initializable, AccessControlUpgradeable, UUPSUpgradeab event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId); event UpgradeAuthorized(address deployer, address newImplementation); + error InvalidIdentityUpdateSize(uint256 actualSize, uint256 minSize, uint256 maxSize); + + uint256 private constant MIN_PAYLOAD_SIZE = 78; + uint256 private constant MAX_PAYLOAD_SIZE = 4_194_304; + error InvalidMessage(); uint64 private sequenceId; @@ -42,8 +47,10 @@ contract GroupMessages is Initializable, AccessControlUpgradeable, UUPSUpgradeab /// @param groupId The group ID. /// @param message The message in bytes. function addMessage(bytes32 groupId, bytes calldata message) public whenNotPaused { - /// @dev 78 bytes contains the minimum length of a valid message. - require(message.length >= 78, InvalidMessage()); + require( + message.length >= MIN_PAYLOAD_SIZE && message.length <= MAX_PAYLOAD_SIZE, + InvalidMessage() + ); /// @dev Incrementing the sequence ID is safe here due to the extremely large limit of uint64. unchecked { diff --git a/contracts/src/IdentityUpdates.sol b/contracts/src/IdentityUpdates.sol index b2bc84ec..0c6686a7 100644 --- a/contracts/src/IdentityUpdates.sol +++ b/contracts/src/IdentityUpdates.sol @@ -13,6 +13,9 @@ contract IdentityUpdates is Initializable, AccessControlUpgradeable, UUPSUpgrade error InvalidIdentityUpdate(); + uint256 private constant MIN_PAYLOAD_SIZE = 104; + uint256 private constant MAX_PAYLOAD_SIZE = 4_194_304; + uint64 private sequenceId; /// @dev Reserved storage gap for future upgrades @@ -42,8 +45,10 @@ contract IdentityUpdates is Initializable, AccessControlUpgradeable, UUPSUpgrade /// @param inboxId The inbox ID. /// @param update The identity update in bytes. function addIdentityUpdate(bytes32 inboxId, bytes calldata update) public whenNotPaused { - /// @dev 104 bytes contains the minimum length of a valid IdentityUpdate. - require(update.length >= 104, InvalidIdentityUpdate()); + require( + update.length >= MIN_PAYLOAD_SIZE && update.length <= MAX_PAYLOAD_SIZE, + InvalidIdentityUpdate() + ); /// @dev Incrementing the sequence ID is safe here due to the extremely large limit of uint64. unchecked { diff --git a/contracts/test/GroupMessage.t.sol b/contracts/test/GroupMessage.t.sol index 543fd8cf..07000306 100644 --- a/contracts/test/GroupMessage.t.sol +++ b/contracts/test/GroupMessage.t.sol @@ -47,7 +47,7 @@ contract GroupMessagesTest is Test { bytes memory message = new bytes(77); for (uint256 i = 0; i < message.length; i++) { message[i] = bytes1(uint8(i % 256)); - } + } vm.expectRevert(GroupMessages.InvalidMessage.selector); groupMessages.addMessage(GROUP_ID, message); @@ -60,12 +60,14 @@ contract GroupMessagesTest is Test { function testPauseUnpause() public { groupMessages.pause(); + assertTrue(groupMessages.paused()); vm.prank(unauthorized); vm.expectRevert(); groupMessages.unpause(); groupMessages.unpause(); + assertFalse(groupMessages.paused()); vm.prank(unauthorized); vm.expectRevert(); diff --git a/contracts/test/IdentityUpdates.t.sol b/contracts/test/IdentityUpdates.t.sol index f42aa9f8..6a2bf3a5 100644 --- a/contracts/test/IdentityUpdates.t.sol +++ b/contracts/test/IdentityUpdates.t.sol @@ -37,6 +37,8 @@ contract IdentityUpdatesTest is Test { message[i] = bytes1(uint8(i % 256)); // Set each byte to its index modulo 256 } + vm.expectEmit(address(identityUpdates)); + emit IdentityUpdates.IdentityUpdateCreated(INBOX_ID, message, 1); identityUpdates.addIdentityUpdate(INBOX_ID, message); } @@ -57,12 +59,14 @@ contract IdentityUpdatesTest is Test { function testPauseUnpause() public { identityUpdates.pause(); + assertTrue(identityUpdates.paused()); vm.prank(unauthorized); vm.expectRevert(); identityUpdates.unpause(); identityUpdates.unpause(); + assertFalse(identityUpdates.paused()); vm.prank(unauthorized); vm.expectRevert();