From 4490fa8ed23ef55f14192f4165649474423fe427 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 17 Sep 2024 10:11:46 -0400 Subject: [PATCH] feat: Scaffolding for DeployAuthSystem Script (#11908) * feat: Scaffolding for DeployAuthSystem Script * feat: Remove redundant documentation * Update DeployAuthSystem.s.sol --- .../scripts/DeployAuthSystem.s.sol | 43 ++++++++ .../test/DeployAuthSystem.t.sol | 102 ++++++++++++++++-- 2 files changed, 137 insertions(+), 8 deletions(-) diff --git a/packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol b/packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol index 13ac34ea48c8..ac2f51ee4be1 100644 --- a/packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol +++ b/packages/contracts-bedrock/scripts/DeployAuthSystem.s.sol @@ -76,3 +76,46 @@ contract DeployAuthSystemOutput is CommonBase { return _safe; } } + +contract DeployAuthSystem is Script { + function run(string memory _infile, string memory _outfile) public { + (DeployAuthSystemInput dasi, DeployAuthSystemOutput daso) = etchIOContracts(); + + dasi.loadInputFile(_infile); + + run(dasi, daso); + + daso.writeOutputFile(_outfile); + } + + function run(DeployAuthSystemInput _dasi, DeployAuthSystemOutput _daso) public { + deploySafe(_dasi, _daso); + } + + function deploySafe(DeployAuthSystemInput _dasi, DeployAuthSystemOutput _daso) public { + address[] memory owners = _dasi.owners(); + uint256 threshold = _dasi.threshold(); + + // TODO: replace with a real deployment. The safe deployment logic is fairly complex, so for the purposes of + // this scaffolding PR we'll just etch the code. + address safe = makeAddr("safe"); + vm.etch(safe, type(Safe).runtimeCode); + vm.store(safe, bytes32(uint256(3)), bytes32(uint256(owners.length))); + vm.store(safe, bytes32(uint256(4)), bytes32(uint256(threshold))); + + _daso.set(_daso.safe.selector, safe); + } + + function etchIOContracts() public returns (DeployAuthSystemInput dasi_, DeployAuthSystemOutput daso_) { + (dasi_, daso_) = getIOContracts(); + vm.etch(address(dasi_), type(DeployAuthSystemInput).runtimeCode); + vm.etch(address(daso_), type(DeployAuthSystemOutput).runtimeCode); + vm.allowCheatcodes(address(dasi_)); + vm.allowCheatcodes(address(daso_)); + } + + function getIOContracts() public view returns (DeployAuthSystemInput dasi_, DeployAuthSystemOutput daso_) { + dasi_ = DeployAuthSystemInput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployAuthSystemInput")); + daso_ = DeployAuthSystemOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployAuthSystemOutput")); + } +} diff --git a/packages/contracts-bedrock/test/DeployAuthSystem.t.sol b/packages/contracts-bedrock/test/DeployAuthSystem.t.sol index 60eb27ba1c3a..f06e9237cb86 100644 --- a/packages/contracts-bedrock/test/DeployAuthSystem.t.sol +++ b/packages/contracts-bedrock/test/DeployAuthSystem.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { Test } from "forge-std/Test.sol"; +import { Test, stdStorage, StdStorage } from "forge-std/Test.sol"; import { stdToml } from "forge-std/StdToml.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; -import { DeployAuthSystemInput, DeployAuthSystemOutput } from "scripts/DeployAuthSystem.s.sol"; +import { DeployAuthSystemInput, DeployAuthSystem, DeployAuthSystemOutput } from "scripts/DeployAuthSystem.s.sol"; contract DeployAuthSystemInput_Test is Test { DeployAuthSystemInput dasi; @@ -68,13 +68,10 @@ contract DeployAuthSystemOutput_Test is Test { function test_set_succeeds() public { address safeAddr = makeAddr("safe"); - // Ensure the address has code, since it's expected to be a contract vm.etch(safeAddr, hex"01"); - // Set the output data daso.set(daso.safe.selector, safeAddr); - // Compare the test data to the getter method assertEq(safeAddr, address(daso.safe()), "100"); } @@ -95,13 +92,11 @@ contract DeployAuthSystemOutput_Test is Test { function test_writeOutputFile_succeeds() public { string memory root = vm.projectRoot(); - // Use the expected data from the test fixture. string memory expOutPath = string.concat(root, "/test/fixtures/test-deploy-auth-system-out.toml"); string memory expOutToml = vm.readFile(expOutPath); address expSafe = expOutToml.readAddress(".safe"); - // Etch code at each address so the code checks pass when settings values. vm.etch(expSafe, hex"01"); daso.set(daso.safe.selector, expSafe); @@ -110,9 +105,100 @@ contract DeployAuthSystemOutput_Test is Test { daso.writeOutputFile(actOutPath); string memory actOutToml = vm.readFile(actOutPath); - // Clean up before asserting so that we don't leave any files behind. vm.removeFile(actOutPath); assertEq(expOutToml, actOutToml); } } + +contract DeployAuthSystem_Test is Test { + using stdStorage for StdStorage; + + DeployAuthSystem deployAuthSystem; + DeployAuthSystemInput dasi; + DeployAuthSystemOutput daso; + + // Define default input variables for testing. + uint256 defaultThreshold = 5; + uint256 defaultOwnersLength = 7; + address[] defaultOwners; + + function setUp() public { + deployAuthSystem = new DeployAuthSystem(); + (dasi, daso) = deployAuthSystem.etchIOContracts(); + for (uint256 i = 0; i < defaultOwnersLength; i++) { + defaultOwners.push(makeAddr(string.concat("owner", vm.toString(i)))); + } + } + + function hash(bytes32 _seed, uint256 _i) internal pure returns (bytes32) { + return keccak256(abi.encode(_seed, _i)); + } + + function testFuzz_run_memory_succeeds(bytes32 _seed) public { + address[] memory _owners = Solarray.addresses( + address(uint160(uint256(hash(_seed, 0)))), + address(uint160(uint256(hash(_seed, 1)))), + address(uint160(uint256(hash(_seed, 2)))), + address(uint160(uint256(hash(_seed, 3)))), + address(uint160(uint256(hash(_seed, 4)))), + address(uint160(uint256(hash(_seed, 5)))), + address(uint160(uint256(hash(_seed, 6)))) + ); + + uint256 threshold = bound(uint256(_seed), 1, _owners.length - 1); + + dasi.set(dasi.owners.selector, _owners); + dasi.set(dasi.threshold.selector, threshold); + + deployAuthSystem.run(dasi, daso); + + assertNotEq(address(daso.safe()), address(0), "100"); + assertEq(daso.safe().getThreshold(), threshold, "200"); + // TODO: the getOwners() method requires iterating over the owners linked list. + // Since we're not yet performing a proper deployment of the Safe, this call will revert. + // assertEq(daso.safe().getOwners().length, _owners.length, "300"); + + // Architecture assertions. + // TODO: these will become relevant as we add more contracts to the auth system, and need to test their + // relationships. + + daso.checkOutput(); + } + + function test_run_io_succeeds() public { + string memory root = vm.projectRoot(); + string memory inpath = string.concat(root, "/test/fixtures/test-deploy-auth-system-in.toml"); + string memory outpath = string.concat(root, "/.testdata/test-deploy-auth-system-out.toml"); + + deployAuthSystem.run(inpath, outpath); + + string memory actOutToml = vm.readFile(outpath); + string memory expOutToml = vm.readFile(string.concat(root, "/test/fixtures/test-deploy-auth-system-out.toml")); + + vm.removeFile(outpath); + assertEq(expOutToml, actOutToml); + } + + function test_run_NullInput_reverts() public { + dasi.set(dasi.owners.selector, defaultOwners); + dasi.set(dasi.threshold.selector, defaultThreshold); + + // Zero out the owners length slot + uint256 slot = 9; + vm.store(address(dasi), bytes32(uint256(9)), bytes32(0)); + vm.expectRevert("DeployAuthSystemInput: owners not set"); + deployAuthSystem.run(dasi, daso); + vm.store(address(dasi), bytes32(uint256(9)), bytes32(defaultOwnersLength)); + + slot = zeroOutSlotForSelector(dasi.threshold.selector); + vm.expectRevert("DeployAuthSystemInput: threshold not set"); + deployAuthSystem.run(dasi, daso); + vm.store(address(dasi), bytes32(slot), bytes32(defaultThreshold)); + } + + function zeroOutSlotForSelector(bytes4 _selector) internal returns (uint256 slot_) { + slot_ = stdstore.enable_packed_slots().target(address(dasi)).sig(_selector).find(); + vm.store(address(dasi), bytes32(slot_), bytes32(0)); + } +}