From 4b6f1b725f7c01d479206fa0d8160aee03f6a3d2 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 10 Aug 2023 20:28:24 -0400 Subject: [PATCH 01/10] add initial tests --- script/input/11151/internalLlamaInstance.json | 91 +++++++++++++++++++ script/input/31337/internalLlamaInstance.json | 91 +++++++++++++++++++ .../MultipleInstance.integrations.t.sol | 58 ++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 script/input/11151/internalLlamaInstance.json create mode 100644 script/input/31337/internalLlamaInstance.json create mode 100644 test/integrations/MultipleInstance.integrations.t.sol diff --git a/script/input/11151/internalLlamaInstance.json b/script/input/11151/internalLlamaInstance.json new file mode 100644 index 000000000..1107be068 --- /dev/null +++ b/script/input/11151/internalLlamaInstance.json @@ -0,0 +1,91 @@ +{ + "comment": "These addresses are what you get when you run the DeployLlamaFactory script test. The strategy logic is the LlamaRelativeQuantityQuorum contract.", + "factory": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", + "instanceName": "Llama", + "instanceColor": "#6A45EC", + "instanceLogo": "", + "strategyLogic": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", + "accountLogic": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", + "initialStrategies": [ + { + "approvalPeriod": 172800, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 691200, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 5000, + "minDisapprovalPct": 10100, + "queuingPeriod": 0 + }, + { + "approvalPeriod": 0, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 86400, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 0, + "minDisapprovalPct": 1000, + "queuingPeriod": 691200 + }, + { + "approvalPeriod": 0, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 100, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 0, + "minDisapprovalPct": 0, + "queuingPeriod": 0 + } + ], + "initialAccounts": [ + { + "name": "Treasury" + } + ], + "initialRoleDescriptions": ["Core Team"], + "initialRoleHolders": [ + { + "comment": "This assigns role #1 llamaAlice.", + "expiration": 18446744073709551615, + "policyholder": "0x412e8b18F7263B1cAFF489f3ccC873424E438101", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaBob.", + "expiration": 18446744073709551615, + "policyholder": "0xDF01c61Fbf37055Fb4ff895393b27857f412236F", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaCharlie.", + "expiration": 18446744073709551615, + "policyholder": "0xc484b0CB07a9585d17a047F9AE068c34F5A5686f", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaDale.", + "expiration": 18446744073709551615, + "policyholder": "0x134b822179CcC24b89Fe82fDd8F914a2b141f935", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaErica.", + "expiration": 18446744073709551615, + "policyholder": "0x4664246b589528C7d931b90988419F581943B20C", + "quantity": 1, + "role": 1 + } + ], + "initialRolePermissions": [] +} diff --git a/script/input/31337/internalLlamaInstance.json b/script/input/31337/internalLlamaInstance.json new file mode 100644 index 000000000..1107be068 --- /dev/null +++ b/script/input/31337/internalLlamaInstance.json @@ -0,0 +1,91 @@ +{ + "comment": "These addresses are what you get when you run the DeployLlamaFactory script test. The strategy logic is the LlamaRelativeQuantityQuorum contract.", + "factory": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", + "instanceName": "Llama", + "instanceColor": "#6A45EC", + "instanceLogo": "", + "strategyLogic": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", + "accountLogic": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", + "initialStrategies": [ + { + "approvalPeriod": 172800, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 691200, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 5000, + "minDisapprovalPct": 10100, + "queuingPeriod": 0 + }, + { + "approvalPeriod": 0, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 86400, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 0, + "minDisapprovalPct": 1000, + "queuingPeriod": 691200 + }, + { + "approvalPeriod": 0, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 100, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 0, + "minDisapprovalPct": 0, + "queuingPeriod": 0 + } + ], + "initialAccounts": [ + { + "name": "Treasury" + } + ], + "initialRoleDescriptions": ["Core Team"], + "initialRoleHolders": [ + { + "comment": "This assigns role #1 llamaAlice.", + "expiration": 18446744073709551615, + "policyholder": "0x412e8b18F7263B1cAFF489f3ccC873424E438101", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaBob.", + "expiration": 18446744073709551615, + "policyholder": "0xDF01c61Fbf37055Fb4ff895393b27857f412236F", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaCharlie.", + "expiration": 18446744073709551615, + "policyholder": "0xc484b0CB07a9585d17a047F9AE068c34F5A5686f", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaDale.", + "expiration": 18446744073709551615, + "policyholder": "0x134b822179CcC24b89Fe82fDd8F914a2b141f935", + "quantity": 1, + "role": 1 + }, + { + "comment": "This assigns role #1 llamaErica.", + "expiration": 18446744073709551615, + "policyholder": "0x4664246b589528C7d931b90988419F581943B20C", + "quantity": 1, + "role": 1 + } + ], + "initialRolePermissions": [] +} diff --git a/test/integrations/MultipleInstance.integrations.t.sol b/test/integrations/MultipleInstance.integrations.t.sol new file mode 100644 index 000000000..7b8b58e8a --- /dev/null +++ b/test/integrations/MultipleInstance.integrations.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {Test, console2} from "forge-std/Test.sol"; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/Script.sol"; + +import {MockProtocol} from "test/mock/MockProtocol.sol"; +import {MockScript} from "test/mock/MockScript.sol"; + +import {DeployLlamaFactory} from "script/DeployLlamaFactory.s.sol"; +import {DeployLlamaInstance} from "script/DeployLlamaInstance.s.sol"; +import {DeployUtils} from "script/DeployUtils.sol"; + +import {ILlamaAccount} from "src/interfaces/ILlamaAccount.sol"; +import {ILlamaPolicyMetadata} from "src/interfaces/ILlamaPolicyMetadata.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {ActionInfo, PermissionData, RoleHolderData} from "src/lib/Structs.sol"; +import {RoleDescription} from "src/lib/UDVTs.sol"; +import {LlamaCore} from "src/LlamaCore.sol"; +import {LlamaExecutor} from "src/LlamaExecutor.sol"; +import {LlamaPolicy} from "src/LlamaPolicy.sol"; + +contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, Test { + address LLAMA_INSTANCE_DEPLOYER = 0x3d9fEa8AeD0249990133132Bb4BC8d07C6a8259a; + address llamaAlice; + uint256 llamaAlicePrivateKey; + address llamaBob; + uint256 llamaBobPrivateKey; + address llamaCharlie; + uint256 llamaCharliePrivateKey; + address llamaDale; + uint256 llamaDalePrivateKey; + address llamaErica; + uint256 llamaEricaPrivateKey; + + function setUp() public virtual { + // Setting up user addresses and private keys. + (llamaAlice, llamaAlicePrivateKey) = makeAddrAndKey("llamaAlice"); + (llamaBob, llamaBobPrivateKey) = makeAddrAndKey("llamaBob"); + (llamaCharlie, llamaCharliePrivateKey) = makeAddrAndKey("llamaCharlie"); + (llamaDale, llamaDalePrivateKey) = makeAddrAndKey("llamaDale"); + (llamaErica, llamaEricaPrivateKey) = makeAddrAndKey("llamaErica"); + + // Deploy the factory + DeployLlamaFactory.run(); + + // Deploy llama's Llama instance + DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "internalLlamaInstance.json"); + } +} + +contract InitialTest is MultipleInstanceTestSetup { + function test_Initial() external { + assertTrue(true); + } +} From edd24702ea603f5a51c53872e34576c40c4e0190 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 10 Aug 2023 20:38:20 -0400 Subject: [PATCH 02/10] add executor deploy --- script/DeployLlamaFactory.s.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script/DeployLlamaFactory.s.sol b/script/DeployLlamaFactory.s.sol index e9a7c81af..4c9e1869e 100644 --- a/script/DeployLlamaFactory.s.sol +++ b/script/DeployLlamaFactory.s.sol @@ -5,6 +5,7 @@ import {Script, stdJson} from "forge-std/Script.sol"; import {LlamaAccount} from "src/accounts/LlamaAccount.sol"; import {LlamaCore} from "src/LlamaCore.sol"; +import {LlamaExecutor} from "src/LlamaExecutor.sol"; import {LlamaFactory} from "src/LlamaFactory.sol"; import {LlamaLens} from "src/LlamaLens.sol"; import {LlamaPolicy} from "src/LlamaPolicy.sol"; @@ -29,6 +30,7 @@ contract DeployLlamaFactory is Script { LlamaAccount accountLogic; LlamaPolicy policyLogic; LlamaPolicyMetadata policyMetadataLogic; + LlamaExecutor llamaExecutor; // Factory and lens contracts. LlamaFactory factory; @@ -90,5 +92,11 @@ contract DeployLlamaFactory is Script { DeployUtils.print( string.concat(" LlamaRelativeUniqueHolderQuorumLogic:", vm.toString(address(relativeUniqueHolderQuorumLogic))) ); + + // By deploying and verifying an unused executor in this script, we ensure that instances will have their executor + // automatically verified. + vm.broadcast(); + llamaExecutor = new LlamaExecutor(); + DeployUtils.print(string.concat(" LlamaExecutor:", vm.toString(address(llamaExecutor)))); } } From 918042c797f24aaa79968f4075312b6359dde39c Mon Sep 17 00:00:00 2001 From: Austin Green Date: Fri, 11 Aug 2023 19:20:44 -0400 Subject: [PATCH 03/10] pseudocode --- script/input/31337/internalLlamaInstance.json | 2 +- script/input/31337/mockProtocol.json | 113 ++++++++++++++++++ .../MultipleInstance.integrations.t.sol | 61 +++++++++- 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 script/input/31337/mockProtocol.json diff --git a/script/input/31337/internalLlamaInstance.json b/script/input/31337/internalLlamaInstance.json index 1107be068..a8ba86e66 100644 --- a/script/input/31337/internalLlamaInstance.json +++ b/script/input/31337/internalLlamaInstance.json @@ -40,7 +40,7 @@ "forceDisapprovalRoles": [], "isFixedLengthApprovalPeriod": false, "minApprovalPct": 0, - "minDisapprovalPct": 0, + "minDisapprovalPct": 10100, "queuingPeriod": 0 } ], diff --git a/script/input/31337/mockProtocol.json b/script/input/31337/mockProtocol.json new file mode 100644 index 000000000..785d0afcf --- /dev/null +++ b/script/input/31337/mockProtocol.json @@ -0,0 +1,113 @@ +{ + "comment": "These addresses and IDs are what you get when you run the DeployLlama script test. The initialRoleDescriptions are expected to match those in the DeployLlama script.", + "factory": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", + "instanceName": "Mock Protocol Llama", + "instanceColor": "#FF0420", + "instanceLogo": "", + "strategyLogic": "0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496", + "accountLogic": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", + "initialStrategies": [ + { + "approvalPeriod": 172800, + "approvalRole": 1, + "disapprovalRole": 3, + "expirationPeriod": 691200, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": true, + "minApprovalPct": 4000, + "minDisapprovalPct": 5100, + "queuingPeriod": 345600 + }, + { + "approvalPeriod": 172800, + "approvalRole": 2, + "disapprovalRole": 3, + "expirationPeriod": 691200, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": true, + "minApprovalPct": 4000, + "minDisapprovalPct": 5100, + "queuingPeriod": 345600 + }, + { + "approvalPeriod": 172800, + "approvalRole": 2, + "disapprovalRole": 3, + "expirationPeriod": 86400, + "forceApprovalRoles": [1], + "forceDisapprovalRoles": [1], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 8000, + "minDisapprovalPct": 10001, + "queuingPeriod": 0 + }, + { + "approvalPeriod": 0, + "approvalRole": 1, + "disapprovalRole": 1, + "expirationPeriod": 86400, + "forceApprovalRoles": [], + "forceDisapprovalRoles": [], + "isFixedLengthApprovalPeriod": false, + "minApprovalPct": 0, + "minDisapprovalPct": 1000, + "queuingPeriod": 691200 + } + ], + "initialAccounts": [ + { + "name": "MP Treasury" + }, + { + "name": "MP Grants" + } + ], + "initialRoleDescriptions": [ + "ActionCreator", + "Approver", + "Disapprover", + "ForceApprover", + "ForceDisapprover", + "TestRole1", + "TestRole2", + "MadeUpRole" + ], + "initialRoleHolders": [ + { + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('actionCreatorAaron')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0x1f48298c8E770Efd59209fD5D893afAaA6BFf7Bf", + "expiration": 18446744073709551615, + "quantity": 1, + "role": 1 + }, + { + "comment": "This will assign role 2 to the llama's Llama instance's executor. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xC264c377642b946A57160d09FE5e35db06cbb526", + "expiration": 18446744073709551615, + "quantity": 1, + "role": 2 + } + ], + "initialRolePermissions": [ + { + "comment": "This is just used for the DeployLlamaInstance test file. It gives the ActionCreator permission to call `transferERC20()` on the second account with the third strategy.", + "permissionData": { + "selector": "0x51288356", + "strategy": "0x9c7e3be11cB2f4D9A7fA0643D7b76569AF838782", + "target": "0x6aDaEfec2bC0ee7003e48320d4a346a6Be882950" + }, + "role": 1 + }, + { + "comment": "This gives llama's executor permission to create actions for `setScriptAuthorization()` on mock protocol's core with the fourth strategy.", + "permissionData": { + "selector": "0x0a853184", + "strategy": "0x25B47aEb31b20254E2D8c1814E2648Aa1A68CCC3", + "target": "0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4" + }, + "role": 2 + } + ] +} diff --git a/test/integrations/MultipleInstance.integrations.t.sol b/test/integrations/MultipleInstance.integrations.t.sol index 7b8b58e8a..be046a4c9 100644 --- a/test/integrations/MultipleInstance.integrations.t.sol +++ b/test/integrations/MultipleInstance.integrations.t.sol @@ -35,6 +35,19 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T address llamaErica; uint256 llamaEricaPrivateKey; + LlamaCore llamaInstanceCore; + LlamaPolicy llamaInstancePolicy; + LlamaExecutor llamaInstanceExecutor; + + LlamaCore mockCore; + LlamaPolicy mockPolicy; + LlamaExecutor mockExecutor; + + function mineBlock() internal { + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + } + function setUp() public virtual { // Setting up user addresses and private keys. (llamaAlice, llamaAlicePrivateKey) = makeAddrAndKey("llamaAlice"); @@ -48,11 +61,55 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T // Deploy llama's Llama instance DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "internalLlamaInstance.json"); + llamaInstanceCore = core; + llamaInstancePolicy = llamaInstanceCore.policy(); + llamaInstanceExecutor = llamaInstanceCore.executor(); + + // Deploy mock protocol's Llama instance + DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "mockProtocol.json"); + mockCore = core; + mockPolicy = mockCore.policy(); + mockExecutor = mockCore.executor(); + + mineBlock(); + + ); + + // 0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4 - MP LlamaCore + // 0xa359FE9c585FbD6DAAEE4efF9c3bF6bd45D498bC - instant execution strategy + // 0x0a853184 - selector + // Llama role 1 can setScriptAuthorization for mock protocol with instant execution and then mock protocol can + // execute optimistically (these permissions can be set at deploy) + // Llama role 1 can call the script for mock protocol with instant execution and then mock protocol can execute + // optimistically (these permissions need to be updated after the script deploy) } } contract InitialTest is MultipleInstanceTestSetup { - function test_Initial() external { - assertTrue(true); + function test_InitiaXl() external { + /* + 1. Llama instance adds permission to role 1: + { + "comment": "This gives role #1 permission to call `createAction` on the mock protocol's core with the third strategy.", + "permissionData": { + "selector": "0xb3c678b0", + "strategy": "0xa359FE9c585FbD6DAAEE4efF9c3bF6bd45D498bC", + "target": "0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4" + }, + "role": 1 + } + 2. Deploy script contract + 3. llamaAlice calls createAction on llama instance to create action instantly on mock protocol + 4. We test that the action creation, queueAction, executeAction can happen in the same block + 5. Mock protocol gives llama instance a permission to create actions for (target: upgradeScript, strategy: optimistic, target: authorizeScriptAndGivePermissionToCoreTeamToCall) + 6. Optimistically passes so the mock protocol instance authorize the LlamaV1Upgrade script and role 1 has permission to propose calling this newly created target's execute finction with the voting strategy + 7. Core team member creates action to call upgrade with voting, LlamaV1Upgrade script, execute function + */ + + bytes memory scriptCalldata = abi.encodeWithSignature("setScriptAuthorization(address,bool)", address(0), true); + console2.log(address(llamaInstanceExecutor)); + vm.prank(address(llamaInstanceExecutor)); + mockCore.createAction( + 2, ILlamaStrategy(0x25B47aEb31b20254E2D8c1814E2648Aa1A68CCC3), address(mockCore), 0, scriptCalldata, "Hello" } } From 70fa9157ad47b189ed03897689a91278e02648e5 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Sun, 13 Aug 2023 16:32:48 -0400 Subject: [PATCH 04/10] rm executor deploy --- script/DeployLlamaFactory.s.sol | 8 ------ .../llamaInstanceConfig.json} | 28 +++++-------------- 2 files changed, 7 insertions(+), 29 deletions(-) rename script/input/{11151/internalLlamaInstance.json => 11155111/llamaInstanceConfig.json} (86%) diff --git a/script/DeployLlamaFactory.s.sol b/script/DeployLlamaFactory.s.sol index 4c9e1869e..e9a7c81af 100644 --- a/script/DeployLlamaFactory.s.sol +++ b/script/DeployLlamaFactory.s.sol @@ -5,7 +5,6 @@ import {Script, stdJson} from "forge-std/Script.sol"; import {LlamaAccount} from "src/accounts/LlamaAccount.sol"; import {LlamaCore} from "src/LlamaCore.sol"; -import {LlamaExecutor} from "src/LlamaExecutor.sol"; import {LlamaFactory} from "src/LlamaFactory.sol"; import {LlamaLens} from "src/LlamaLens.sol"; import {LlamaPolicy} from "src/LlamaPolicy.sol"; @@ -30,7 +29,6 @@ contract DeployLlamaFactory is Script { LlamaAccount accountLogic; LlamaPolicy policyLogic; LlamaPolicyMetadata policyMetadataLogic; - LlamaExecutor llamaExecutor; // Factory and lens contracts. LlamaFactory factory; @@ -92,11 +90,5 @@ contract DeployLlamaFactory is Script { DeployUtils.print( string.concat(" LlamaRelativeUniqueHolderQuorumLogic:", vm.toString(address(relativeUniqueHolderQuorumLogic))) ); - - // By deploying and verifying an unused executor in this script, we ensure that instances will have their executor - // automatically verified. - vm.broadcast(); - llamaExecutor = new LlamaExecutor(); - DeployUtils.print(string.concat(" LlamaExecutor:", vm.toString(address(llamaExecutor)))); } } diff --git a/script/input/11151/internalLlamaInstance.json b/script/input/11155111/llamaInstanceConfig.json similarity index 86% rename from script/input/11151/internalLlamaInstance.json rename to script/input/11155111/llamaInstanceConfig.json index 1107be068..250239fc5 100644 --- a/script/input/11151/internalLlamaInstance.json +++ b/script/input/11155111/llamaInstanceConfig.json @@ -1,11 +1,11 @@ { - "comment": "These addresses are what you get when you run the DeployLlamaFactory script test. The strategy logic is the LlamaRelativeQuantityQuorum contract.", - "factory": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", + "comment": "The factory address is from https://gist.github.com/mds1/da90e8a4db922fd684dac88bb4c08f92#attempt-3. The strategy logic is the `LlamaRelativeQuantityQuorum` contract.", + "factory": "0xAFF71a204beD7342d21827a607496f5f1806777F", "instanceName": "Llama", "instanceColor": "#6A45EC", "instanceLogo": "", - "strategyLogic": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", - "accountLogic": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", + "strategyLogic": "0xdF0a44747120C1BE2B0b4bDC4B9759218dFA6379", + "accountLogic": "0x2b0C5DDD817cE1F3dACC0CA7613E2DF038d924C4", "initialStrategies": [ { "approvalPeriod": 172800, @@ -54,35 +54,21 @@ { "comment": "This assigns role #1 llamaAlice.", "expiration": 18446744073709551615, - "policyholder": "0x412e8b18F7263B1cAFF489f3ccC873424E438101", + "policyholder": "0xCF468ee5E8eCfaCE78851da762E850Ed0Fc7E7A0", "quantity": 1, "role": 1 }, { "comment": "This assigns role #1 llamaBob.", "expiration": 18446744073709551615, - "policyholder": "0xDF01c61Fbf37055Fb4ff895393b27857f412236F", + "policyholder": "0x9E2962bb94b767Dea9e52b5D73b71394d373CC60", "quantity": 1, "role": 1 }, { "comment": "This assigns role #1 llamaCharlie.", "expiration": 18446744073709551615, - "policyholder": "0xc484b0CB07a9585d17a047F9AE068c34F5A5686f", - "quantity": 1, - "role": 1 - }, - { - "comment": "This assigns role #1 llamaDale.", - "expiration": 18446744073709551615, - "policyholder": "0x134b822179CcC24b89Fe82fDd8F914a2b141f935", - "quantity": 1, - "role": 1 - }, - { - "comment": "This assigns role #1 llamaErica.", - "expiration": 18446744073709551615, - "policyholder": "0x4664246b589528C7d931b90988419F581943B20C", + "policyholder": "0x85Ff2f6d6D05AA7F94A0c69F2cEecEd6F21C5E6B", "quantity": 1, "role": 1 } From 2779392dcfddfc33b4dfe6b9a2ea0e303168bdb3 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Sun, 13 Aug 2023 18:11:43 -0400 Subject: [PATCH 05/10] instant execution test --- ...Instance.json => llamaInstanceConfig.json} | 0 ...l.json => mockProtocolInstanceConfig.json} | 0 test/LlamaCore.t.sol | 46 ++++++++++++ .../MultipleInstance.integrations.t.sol | 72 +++++++++++++++---- 4 files changed, 103 insertions(+), 15 deletions(-) rename script/input/31337/{internalLlamaInstance.json => llamaInstanceConfig.json} (100%) rename script/input/31337/{mockProtocol.json => mockProtocolInstanceConfig.json} (100%) diff --git a/script/input/31337/internalLlamaInstance.json b/script/input/31337/llamaInstanceConfig.json similarity index 100% rename from script/input/31337/internalLlamaInstance.json rename to script/input/31337/llamaInstanceConfig.json diff --git a/script/input/31337/mockProtocol.json b/script/input/31337/mockProtocolInstanceConfig.json similarity index 100% rename from script/input/31337/mockProtocol.json rename to script/input/31337/mockProtocolInstanceConfig.json diff --git a/test/LlamaCore.t.sol b/test/LlamaCore.t.sol index 212bf3e80..e4ab9e14b 100644 --- a/test/LlamaCore.t.sol +++ b/test/LlamaCore.t.sol @@ -2633,6 +2633,52 @@ contract CreateStrategies is LlamaCoreTest { vm.expectRevert(); mpCore.createStrategies(ILlamaStrategy(address(0)), DeployUtils.encodeStrategyConfigs(newStrategies)); } + + function test_ActionsCanBeCreatedQueuedAndExecutedInOneBlock() public { + LlamaRelativeStrategyBase.Config[] memory newStrategies = new LlamaRelativeStrategyBase.Config[](1); + + newStrategies[0] = LlamaRelativeStrategyBase.Config({ + approvalPeriod: 0, + queuingPeriod: 0, + expirationPeriod: 2 days, + isFixedLengthApprovalPeriod: false, + minApprovalPct: 0, + minDisapprovalPct: 10_001, + approvalRole: uint8(Roles.Approver), + disapprovalRole: uint8(Roles.Disapprover), + forceApprovalRoles: new uint8[](0), + forceDisapprovalRoles: new uint8[](0) + }); + + ILlamaStrategy strategyAddress = lens.computeLlamaStrategyAddress( + address(relativeQuantityQuorumLogic), DeployUtils.encodeStrategy(newStrategies[0]), address(mpCore) + ); + PermissionData memory newPermissionData = PermissionData(address(mockProtocol), PAUSE_SELECTOR, strategyAddress); + bytes memory data = abi.encodeCall(MockProtocol.pause, (true)); + + vm.startPrank(address(mpExecutor)); + mpCore.setStrategyLogicAuthorization(relativeQuantityQuorumLogic, true); + mpCore.createStrategies(relativeQuantityQuorumLogic, DeployUtils.encodeStrategyConfigs(newStrategies)); + mpPolicy.setRolePermission(uint8(Roles.ActionCreator), newPermissionData, true); + vm.stopPrank(); + + mineBlock(); + + uint256 preExecutionTimestamp = block.timestamp; + + vm.prank(actionCreatorAaron); + uint256 actionId = + mpCore.createAction(uint8(Roles.ActionCreator), strategyAddress, address(mockProtocol), 0, data, ""); + + ActionInfo memory actionInfo = ActionInfo( + actionId, actionCreatorAaron, uint8(Roles.ActionCreator), strategyAddress, address(mockProtocol), 0, data + ); + mpCore.queueAction(actionInfo); + mpCore.executeAction(actionInfo); + + uint256 postExecutionTimestamp = block.timestamp; + assertEq(postExecutionTimestamp, preExecutionTimestamp); + } } contract SetStrategyAuthorization is LlamaCoreTest { diff --git a/test/integrations/MultipleInstance.integrations.t.sol b/test/integrations/MultipleInstance.integrations.t.sol index be046a4c9..be52ce5a7 100644 --- a/test/integrations/MultipleInstance.integrations.t.sol +++ b/test/integrations/MultipleInstance.integrations.t.sol @@ -60,37 +60,76 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T DeployLlamaFactory.run(); // Deploy llama's Llama instance - DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "internalLlamaInstance.json"); + DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "llamaInstanceConfig.json"); llamaInstanceCore = core; llamaInstancePolicy = llamaInstanceCore.policy(); llamaInstanceExecutor = llamaInstanceCore.executor(); // Deploy mock protocol's Llama instance - DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "mockProtocol.json"); + DeployLlamaInstance.run(LLAMA_INSTANCE_DEPLOYER, "mockProtocolInstanceConfig.json"); mockCore = core; mockPolicy = mockCore.policy(); mockExecutor = mockCore.executor(); mineBlock(); - - ); - - // 0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4 - MP LlamaCore - // 0xa359FE9c585FbD6DAAEE4efF9c3bF6bd45D498bC - instant execution strategy - // 0x0a853184 - selector - // Llama role 1 can setScriptAuthorization for mock protocol with instant execution and then mock protocol can - // execute optimistically (these permissions can be set at deploy) - // Llama role 1 can call the script for mock protocol with instant execution and then mock protocol can execute - // optimistically (these permissions need to be updated after the script deploy) } } contract InitialTest is MultipleInstanceTestSetup { function test_InitiaXl() external { + // SCRIPT OPTIMIZATION + + /* + 1. Deploy llama and mock protocol instances + 2. Mock instance needs to have some authorized GovernanceScript and reserve a role for llama with permission to + create actions for the functions on this + script with an optimistic and high approval strategy. The permissioning happens in the config but the script must be + authorized through an action. + 3. The script will have a function that allows a call to setScriptAuthorization and to setRolePermission to the + governance maintenance role with one + of the two strategies, the script being authorized, any target. + 4. Llama will deploy an upgrade contract and use a multisig like strategy to propose calling the governance script + with this as a parameter. + 5. Now mock instance governance decides if they want to authorize this upgrade script, give llama the role + permission to propose calling it. The upgrade script + has the logic to unauthorize itself and remove all permissions after being called. + 6. The action executes. + 7. Llama proposes calling the script. The governance script forced llama to only have permission to make this + proposal with high buy in + 8. Mock instance executes the action, the script is called, the script is unauthed and all permissions are removed. */ + + // BASE CASE + + /* + 1. Deploy llama and mock protocol instances ✅ + 2. Mock protocol instance config has a role reserved for llama with proposal permissions for setScriptAuthorization. + Proposal permission for calling the script + needs to be set after it's deployed. The execute function of the script will also remove the permission from the + role and unauth the script. The role + includes two permissions: one to setScriptAuthorization with a long optimistic timelock and the other with high buy + in shorter timelock. Mock also gives + llama permission to propose calling setRolePermission so they can propose to give themselves the proposal perission + to call the script. + 3. Llama adds a permission to call createAction on the mock instance shortly after instance launches. This action + still needs to go through their governance + and can be canceled so a simple multisig like permission will suffice. + 4. Llama deploys a script + 5. The test starts with llama using either strategy to create, queue, execute calling setScriptAuth through mock's + createAction. + 6. This goes through mock's governance with minimal input from them. + 7. Once the script is authorized, llama then goes through their action process to call mock's createAction to give + themselves permission to call the script. + 8. Once that's complete llama goes through their action process to call mock's createAction to execute the script + 9. This requires high buy-in and when it executes it removes the permission to call it from the llama role and + unauthorizes the script + */ + + // ARCHIVE /* 1. Llama instance adds permission to role 1: { - "comment": "This gives role #1 permission to call `createAction` on the mock protocol's core with the third strategy.", + "comment": "This gives role #1 permission to call `createAction` on the mock protocol's core with the third + strategy.", "permissionData": { "selector": "0xb3c678b0", "strategy": "0xa359FE9c585FbD6DAAEE4efF9c3bF6bd45D498bC", @@ -101,8 +140,10 @@ contract InitialTest is MultipleInstanceTestSetup { 2. Deploy script contract 3. llamaAlice calls createAction on llama instance to create action instantly on mock protocol 4. We test that the action creation, queueAction, executeAction can happen in the same block - 5. Mock protocol gives llama instance a permission to create actions for (target: upgradeScript, strategy: optimistic, target: authorizeScriptAndGivePermissionToCoreTeamToCall) - 6. Optimistically passes so the mock protocol instance authorize the LlamaV1Upgrade script and role 1 has permission to propose calling this newly created target's execute finction with the voting strategy + 5. Mock protocol gives llama instance a permission to create actions for (target: upgradeScript, strategy: + optimistic, target: authorizeScriptAndGivePermissionToCoreTeamToCall) + 6. Optimistically passes so the mock protocol instance authorize the LlamaV1Upgrade script and role 1 has permission + to propose calling this newly created target's execute finction with the voting strategy 7. Core team member creates action to call upgrade with voting, LlamaV1Upgrade script, execute function */ @@ -111,5 +152,6 @@ contract InitialTest is MultipleInstanceTestSetup { vm.prank(address(llamaInstanceExecutor)); mockCore.createAction( 2, ILlamaStrategy(0x25B47aEb31b20254E2D8c1814E2648Aa1A68CCC3), address(mockCore), 0, scriptCalldata, "Hello" + ); } } From 4799b60874eb38f8e7a9e8df95e021afc1dc3341 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Sun, 13 Aug 2023 22:26:13 -0400 Subject: [PATCH 06/10] instant execution test part 2 --- test/LlamaCore.t.sol | 73 +++++++++++++++++++++++++- test/mock/MockAtomicActionExecutor.sol | 34 ++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 test/mock/MockAtomicActionExecutor.sol diff --git a/test/LlamaCore.t.sol b/test/LlamaCore.t.sol index e4ab9e14b..86ceefe34 100644 --- a/test/LlamaCore.t.sol +++ b/test/LlamaCore.t.sol @@ -5,6 +5,7 @@ import {Test, console2, StdStorage, stdStorage} from "forge-std/Test.sol"; import {MockAccountLogicContract} from "test/mock/MockAccountLogicContract.sol"; import {MockActionGuard} from "test/mock/MockActionGuard.sol"; +import {MockAtomicActionExecutor} from "test/mock/MockAtomicActionExecutor.sol"; import {MockPoorlyImplementedAbsolutePeerReview} from "test/mock/MockPoorlyImplementedStrategy.sol"; import {MockProtocol} from "test/mock/MockProtocol.sol"; import {LlamaCoreSigUtils} from "test/utils/LlamaCoreSigUtils.sol"; @@ -859,6 +860,25 @@ contract CreateActionBySig is LlamaCoreTest { (v, r, s) = vm.sign(privateKey, digest); } + function createOffchainSignatureForInstantExecution(uint256 privateKey, ILlamaStrategy strategy) + internal + view + returns (uint8 v, bytes32 r, bytes32 s) + { + LlamaCoreSigUtils.CreateAction memory createAction = LlamaCoreSigUtils.CreateAction({ + role: uint8(Roles.ActionCreator), + strategy: address(strategy), + target: address(mockProtocol), + value: 0, + data: abi.encodeCall(MockProtocol.pause, (true)), + description: "", + policyholder: actionCreatorAaron, + nonce: 0 + }); + bytes32 digest = getCreateActionTypedDataHash(createAction); + (v, r, s) = vm.sign(privateKey, digest); + } + function createActionBySig(uint8 v, bytes32 r, bytes32 s) internal returns (uint256 actionId) { actionId = mpCore.createActionBySig( actionCreatorAaron, @@ -985,6 +1005,57 @@ contract CreateActionBySig is LlamaCoreTest { vm.expectRevert(LlamaCore.InvalidSignature.selector); createActionBySig(v, r, s); } + + function test_ActionCanBeCreatedQueuedAndExecutedInOneBlock() public { + // Create the instant execution strategy and assign the permission to `Roles.ActionCreator` + LlamaRelativeStrategyBase.Config[] memory newStrategies = new LlamaRelativeStrategyBase.Config[](1); + newStrategies[0] = LlamaRelativeStrategyBase.Config({ + approvalPeriod: 0, + queuingPeriod: 0, + expirationPeriod: 2 days, + isFixedLengthApprovalPeriod: false, + minApprovalPct: 0, + minDisapprovalPct: 10_001, + approvalRole: uint8(Roles.Approver), + disapprovalRole: uint8(Roles.Disapprover), + forceApprovalRoles: new uint8[](0), + forceDisapprovalRoles: new uint8[](0) + }); + ILlamaStrategy instantExecutionStrategy = lens.computeLlamaStrategyAddress( + address(relativeQuantityQuorumLogic), DeployUtils.encodeStrategy(newStrategies[0]), address(mpCore) + ); + PermissionData memory newPermissionData = + PermissionData(address(mockProtocol), PAUSE_SELECTOR, instantExecutionStrategy); + bytes memory data = abi.encodeCall(MockProtocol.pause, (true)); + + vm.startPrank(address(mpExecutor)); + mpCore.setStrategyLogicAuthorization(relativeQuantityQuorumLogic, true); + mpCore.createStrategies(relativeQuantityQuorumLogic, DeployUtils.encodeStrategyConfigs(newStrategies)); + mpPolicy.setRolePermission(uint8(Roles.ActionCreator), newPermissionData, true); + vm.stopPrank(); + + (uint8 v, bytes32 r, bytes32 s) = + createOffchainSignatureForInstantExecution(actionCreatorAaronPrivateKey, instantExecutionStrategy); + + MockAtomicActionExecutor mockAtomicActionExecutor = new MockAtomicActionExecutor(mpCore); + + mineBlock(); + + vm.expectEmit(); + emit ActionExecuted(0, address(mockAtomicActionExecutor), instantExecutionStrategy, actionCreatorAaron, bytes("")); + mockAtomicActionExecutor.createQueueAndExecute( + actionCreatorAaron, + uint8(Roles.ActionCreator), + instantExecutionStrategy, + address(mockProtocol), + 0, + data, + "", + v, + r, + s + ); + } } contract CancelAction is LlamaCoreTest { @@ -2634,7 +2705,7 @@ contract CreateStrategies is LlamaCoreTest { mpCore.createStrategies(ILlamaStrategy(address(0)), DeployUtils.encodeStrategyConfigs(newStrategies)); } - function test_ActionsCanBeCreatedQueuedAndExecutedInOneBlock() public { + function test_ActionCanBeCreatedQueuedAndExecutedInOneBlock() public { LlamaRelativeStrategyBase.Config[] memory newStrategies = new LlamaRelativeStrategyBase.Config[](1); newStrategies[0] = LlamaRelativeStrategyBase.Config({ diff --git a/test/mock/MockAtomicActionExecutor.sol b/test/mock/MockAtomicActionExecutor.sol new file mode 100644 index 000000000..ddd8f11a8 --- /dev/null +++ b/test/mock/MockAtomicActionExecutor.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {ActionInfo} from "src/lib/Structs.sol"; +import {LlamaCore} from "src/LlamaCore.sol"; + +/// @dev A mock contract that can create, queue, and execute actions in a single function. +contract MockAtomicActionExecutor { + LlamaCore immutable core; + + constructor(LlamaCore _core) { + core = _core; + } + + function createQueueAndExecute( + address policyholder, + uint8 role, + ILlamaStrategy strategy, + address target, + uint256 value, + bytes calldata data, + string memory description, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 actionId) { + actionId = core.createActionBySig(policyholder, role, strategy, target, value, data, description, v, r, s); + + ActionInfo memory actionInfo = ActionInfo(actionId, policyholder, role, strategy, target, value, data); + core.queueAction(actionInfo); + core.executeAction(actionInfo); + } +} From a92fa614e8efd96768ce79cc65948dd2b25f953e Mon Sep 17 00:00:00 2001 From: Austin Green Date: Tue, 15 Aug 2023 15:35:25 -0400 Subject: [PATCH 07/10] testing --- .../31337/mockProtocolInstanceConfig.json | 100 ++++++--------- .../MultipleInstance.integrations.t.sol | 115 ++++++++++++++++-- test/mock/MockInstanceUpdateScript.sol | 41 +++++++ test/mock/MockInstanceUpdateVersion1.sol | 29 +++++ 4 files changed, 213 insertions(+), 72 deletions(-) create mode 100644 test/mock/MockInstanceUpdateScript.sol create mode 100644 test/mock/MockInstanceUpdateVersion1.sol diff --git a/script/input/31337/mockProtocolInstanceConfig.json b/script/input/31337/mockProtocolInstanceConfig.json index 785d0afcf..25a9033aa 100644 --- a/script/input/31337/mockProtocolInstanceConfig.json +++ b/script/input/31337/mockProtocolInstanceConfig.json @@ -1,46 +1,22 @@ { "comment": "These addresses and IDs are what you get when you run the DeployLlama script test. The initialRoleDescriptions are expected to match those in the DeployLlama script.", "factory": "0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70809", - "instanceName": "Mock Protocol Llama", - "instanceColor": "#FF0420", - "instanceLogo": "", - "strategyLogic": "0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496", + "instanceName": "Mock Protocol", + "instanceColor": "#000000", + "instanceLogo": "", + "strategyLogic": "0x416C42991d05b31E9A6dC209e91AD22b79D87Ae6", "accountLogic": "0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76", "initialStrategies": [ { "approvalPeriod": 172800, "approvalRole": 1, - "disapprovalRole": 3, - "expirationPeriod": 691200, - "forceApprovalRoles": [], - "forceDisapprovalRoles": [], - "isFixedLengthApprovalPeriod": true, - "minApprovalPct": 4000, - "minDisapprovalPct": 5100, - "queuingPeriod": 345600 - }, - { - "approvalPeriod": 172800, - "approvalRole": 2, - "disapprovalRole": 3, + "disapprovalRole": 1, "expirationPeriod": 691200, "forceApprovalRoles": [], "forceDisapprovalRoles": [], - "isFixedLengthApprovalPeriod": true, - "minApprovalPct": 4000, - "minDisapprovalPct": 5100, - "queuingPeriod": 345600 - }, - { - "approvalPeriod": 172800, - "approvalRole": 2, - "disapprovalRole": 3, - "expirationPeriod": 86400, - "forceApprovalRoles": [1], - "forceDisapprovalRoles": [1], "isFixedLengthApprovalPeriod": false, - "minApprovalPct": 8000, - "minDisapprovalPct": 10001, + "minApprovalPct": 7500, + "minDisapprovalPct": 10100, "queuingPeriod": 0 }, { @@ -64,50 +40,50 @@ "name": "MP Grants" } ], - "initialRoleDescriptions": [ - "ActionCreator", - "Approver", - "Disapprover", - "ForceApprover", - "ForceDisapprover", - "TestRole1", - "TestRole2", - "MadeUpRole" - ], + "initialRoleDescriptions": ["Core Team", "Governance Maintainer"], "initialRoleHolders": [ { - "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('actionCreatorAaron')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", - "policyholder": "0x1f48298c8E770Efd59209fD5D893afAaA6BFf7Bf", + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('mockAlice')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xc1F59Bce24bc332e11f4CE8264bEfFA921b6F7B6", "expiration": 18446744073709551615, "quantity": 1, "role": 1 }, { - "comment": "This will assign role 2 to the llama's Llama instance's executor. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", - "policyholder": "0xC264c377642b946A57160d09FE5e35db06cbb526", + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('mockBob')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xB3E5c14EfA79bc86BA6d5e24387Af93e3987b927", "expiration": 18446744073709551615, "quantity": 1, - "role": 2 - } - ], - "initialRolePermissions": [ + "role": 1 + }, { - "comment": "This is just used for the DeployLlamaInstance test file. It gives the ActionCreator permission to call `transferERC20()` on the second account with the third strategy.", - "permissionData": { - "selector": "0x51288356", - "strategy": "0x9c7e3be11cB2f4D9A7fA0643D7b76569AF838782", - "target": "0x6aDaEfec2bC0ee7003e48320d4a346a6Be882950" - }, + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('mockCharlie')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xD3883689Cf8d4332eBc24c796E1032a8C28EA8c9", + "expiration": 18446744073709551615, + "quantity": 1, "role": 1 }, { - "comment": "This gives llama's executor permission to create actions for `setScriptAuthorization()` on mock protocol's core with the fourth strategy.", - "permissionData": { - "selector": "0x0a853184", - "strategy": "0x25B47aEb31b20254E2D8c1814E2648Aa1A68CCC3", - "target": "0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4" - }, + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('mockDale')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0x500DBc13A7f5FD80A264b3eF2a37bfb968AB309d", + "expiration": 18446744073709551615, + "quantity": 1, + "role": 1 + }, + { + "comment": "This will assign role 1 to the address derived from `makeAddrAndKey('mockErica')`. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xfe677EBFFd09a566E0E2245E57DbdE1469B2BB72", + "expiration": 18446744073709551615, + "quantity": 1, + "role": 1 + }, + { + "comment": "This will assign role 2 to llama's Llama instance's executor. The role assignment is set to never expire (type(uint64).max) because this is the default. The quantity is likewise the default.", + "policyholder": "0xC264c377642b946A57160d09FE5e35db06cbb526", + "expiration": 18446744073709551615, + "quantity": 1, "role": 2 } - ] + ], + "initialRolePermissions": [] } diff --git a/test/integrations/MultipleInstance.integrations.t.sol b/test/integrations/MultipleInstance.integrations.t.sol index be52ce5a7..7a3fe72a9 100644 --- a/test/integrations/MultipleInstance.integrations.t.sol +++ b/test/integrations/MultipleInstance.integrations.t.sol @@ -6,6 +6,8 @@ import {Test, console2} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import {stdJson} from "forge-std/Script.sol"; +import {MockInstanceUpdateScript} from "test/mock/MockInstanceUpdateScript.sol"; +import {MockInstanceUpdateVersion1} from "test/mock/MockInstanceUpdateVersion1.sol"; import {MockProtocol} from "test/mock/MockProtocol.sol"; import {MockScript} from "test/mock/MockScript.sol"; @@ -23,7 +25,10 @@ import {LlamaExecutor} from "src/LlamaExecutor.sol"; import {LlamaPolicy} from "src/LlamaPolicy.sol"; contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, Test { + event ApprovalCast(uint256 id, address indexed policyholder, uint8 indexed role, uint256 quantity, string reason); + address LLAMA_INSTANCE_DEPLOYER = 0x3d9fEa8AeD0249990133132Bb4BC8d07C6a8259a; + address llamaAlice; uint256 llamaAlicePrivateKey; address llamaBob; @@ -35,6 +40,17 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T address llamaErica; uint256 llamaEricaPrivateKey; + address mockAlice; + uint256 mockAlicePrivateKey; + address mockBob; + uint256 mockBobPrivateKey; + address mockCharlie; + uint256 mockCharliePrivateKey; + address mockDale; + uint256 mockDalePrivateKey; + address mockErica; + uint256 mockEricaPrivateKey; + LlamaCore llamaInstanceCore; LlamaPolicy llamaInstancePolicy; LlamaExecutor llamaInstanceExecutor; @@ -43,19 +59,33 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T LlamaPolicy mockPolicy; LlamaExecutor mockExecutor; + ILlamaStrategy MOCK_VOTING_STRATEGY = ILlamaStrategy(0x225D6692B4DD673C6ad57B4800846341d027BC66); + ILlamaStrategy MOCK_OPTIMISTIC_STRATEGY = ILlamaStrategy(0xF7E4BB5159c3fdc50e1Ef6b80BD69988DD6f438d); + ILlamaStrategy LLAMA_VOTING_STRATEGY = ILlamaStrategy(0x881E25C4470136B1B2D64a4942b5346e41477fB6); + + MockInstanceUpdateScript mockInstanceUpdateScript; + MockInstanceUpdateVersion1 mockInstanceUpdateVersion1; + function mineBlock() internal { vm.roll(block.number + 1); vm.warp(block.timestamp + 1); } function setUp() public virtual { - // Setting up user addresses and private keys. + // Setting up user addresses and private keys for Llama. (llamaAlice, llamaAlicePrivateKey) = makeAddrAndKey("llamaAlice"); (llamaBob, llamaBobPrivateKey) = makeAddrAndKey("llamaBob"); (llamaCharlie, llamaCharliePrivateKey) = makeAddrAndKey("llamaCharlie"); (llamaDale, llamaDalePrivateKey) = makeAddrAndKey("llamaDale"); (llamaErica, llamaEricaPrivateKey) = makeAddrAndKey("llamaErica"); + // Setting up user addresses and private keys for Mock. + (mockAlice, mockAlicePrivateKey) = makeAddrAndKey("mockAlice"); + (mockBob, mockBobPrivateKey) = makeAddrAndKey("mockBob"); + (mockCharlie, mockCharliePrivateKey) = makeAddrAndKey("mockCharlie"); + (mockDale, mockDalePrivateKey) = makeAddrAndKey("mockDale"); + (mockErica, mockEricaPrivateKey) = makeAddrAndKey("mockErica"); + // Deploy the factory DeployLlamaFactory.run(); @@ -72,11 +102,82 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T mockExecutor = mockCore.executor(); mineBlock(); + + mockInstanceUpdateScript = new MockInstanceUpdateScript(); + + // In practice this can either happen as an initial action post deployment or we can normalize a post deployment + // configuration flow. + // This would work by deploying with an instant execution strategy and role holder which is an address under our + // control. This address would use its root authority to setup the instance and then remove itself from the system. + // The user could confirm that none of these root permissions are still active before transferring ownership. + vm.startPrank(address(mockExecutor)); + mockCore.setScriptAuthorization(address(mockInstanceUpdateScript), true); + mockPolicy.setRolePermission( + uint8(2), + PermissionData( + address(mockInstanceUpdateScript), + MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, + MOCK_VOTING_STRATEGY + ), + true + ); + mockPolicy.setRolePermission( + uint8(2), + PermissionData( + address(mockInstanceUpdateScript), + MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, + MOCK_OPTIMISTIC_STRATEGY + ), + true + ); + vm.stopPrank(); + + // Deploy the version 1 update script + mockInstanceUpdateVersion1 = new MockInstanceUpdateVersion1(); + + // Now that llama has permission to create actions for `mockInstanceUpdateScript`, it needs a permission in its own + // instance for calling createAction. + vm.prank(address(llamaInstanceExecutor)); + llamaInstancePolicy.setRolePermission( + uint8(1), PermissionData(address(mockCore), LlamaCore.createAction.selector, LLAMA_VOTING_STRATEGY), true + ); + } + + function _llamaApproveAction(address _policyholder, ActionInfo memory actionInfo) public { + vm.expectEmit(); + emit ApprovalCast(actionInfo.id, _policyholder, uint8(1), 1, ""); + vm.prank(_policyholder); + llamaInstanceCore.castApproval(uint8(1), actionInfo, ""); } } -contract InitialTest is MultipleInstanceTestSetup { - function test_InitiaXl() external { +contract MultipleInstanceTest is MultipleInstanceTestSetup { + function test_createsActionToAuthorizeScript() external { + PermissionData memory permissionData = PermissionData( + address(mockInstanceUpdateVersion1), MockInstanceUpdateVersion1.updateInstance.selector, MOCK_VOTING_STRATEGY + ); + bytes memory actionData = abi.encodeCall(MockInstanceUpdateScript.authorizeScriptAndSetPermission, (permissionData)); + bytes memory data = abi.encodeCall( + LlamaCore.createAction, (uint8(2), MOCK_VOTING_STRATEGY, address(mockInstanceUpdateScript), 0, actionData, "") + ); + vm.prank(llamaAlice); + uint256 actionId = llamaInstanceCore.createAction(uint8(1), LLAMA_VOTING_STRATEGY, address(mockCore), 0, data, ""); + ActionInfo memory actionInfo = + ActionInfo(actionId, llamaAlice, uint8(1), LLAMA_VOTING_STRATEGY, address(mockCore), 0, data); + + mineBlock(); + + _llamaApproveAction(llamaBob, actionInfo); + _llamaApproveAction(llamaCharlie, actionInfo); + _llamaApproveAction(llamaDale, actionInfo); + + // Executing llama's action creates an action for the mock instance + vm.expectEmit(); + emit ActionCanceled(actionInfo.id, actionCreatorAaron); + llamaInstanceCore.executeAction(actionInfo); + + assertEq(); + // SCRIPT OPTIMIZATION /* @@ -88,6 +189,7 @@ contract InitialTest is MultipleInstanceTestSetup { 3. The script will have a function that allows a call to setScriptAuthorization and to setRolePermission to the governance maintenance role with one of the two strategies, the script being authorized, any target. + 4. Llama will deploy an upgrade contract and use a multisig like strategy to propose calling the governance script with this as a parameter. 5. Now mock instance governance decides if they want to authorize this upgrade script, give llama the role @@ -146,12 +248,5 @@ contract InitialTest is MultipleInstanceTestSetup { to propose calling this newly created target's execute finction with the voting strategy 7. Core team member creates action to call upgrade with voting, LlamaV1Upgrade script, execute function */ - - bytes memory scriptCalldata = abi.encodeWithSignature("setScriptAuthorization(address,bool)", address(0), true); - console2.log(address(llamaInstanceExecutor)); - vm.prank(address(llamaInstanceExecutor)); - mockCore.createAction( - 2, ILlamaStrategy(0x25B47aEb31b20254E2D8c1814E2648Aa1A68CCC3), address(mockCore), 0, scriptCalldata, "Hello" - ); } } diff --git a/test/mock/MockInstanceUpdateScript.sol b/test/mock/MockInstanceUpdateScript.sol new file mode 100644 index 000000000..1be740c15 --- /dev/null +++ b/test/mock/MockInstanceUpdateScript.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {LlamaBaseScript} from "src/llama-scripts/LlamaBaseScript.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {LlamaCore} from "src/LlamaCore.sol"; +import {LlamaExecutor} from "src/LlamaExecutor.sol"; +import {LlamaPolicy} from "src/LlamaPolicy.sol"; +import {PermissionData} from "src/lib/Structs.sol"; + +/// @dev This is a mock script because it hasn't been audited yet. +contract MockInstanceUpdateScript is LlamaBaseScript { + // ======================== + // ======== Errors ======== + // ======================== + + error InvalidStrategy(); + + function authorizeScriptAndSetPermission(PermissionData memory permissionData) external onlyDelegateCall { + uint8 GOVERNANCE_MAINTAINER_ROLE = 2; + ILlamaStrategy VOTING_STRATEGY = ILlamaStrategy(0x225D6692B4DD673C6ad57B4800846341d027BC66); + ILlamaStrategy OPTIMISTIC_STRATEGY = ILlamaStrategy(0xF7E4BB5159c3fdc50e1Ef6b80BD69988DD6f438d); + if (permissionData.strategy != OPTIMISTIC_STRATEGY && permissionData.strategy != VOTING_STRATEGY) { + revert InvalidStrategy(); + } + + (LlamaCore core, LlamaPolicy policy) = _context(); + core.setScriptAuthorization(permissionData.target, true); + policy.setRolePermission(GOVERNANCE_MAINTAINER_ROLE, permissionData, true); + } + + // ================================ + // ======== Internal Logic ======== + // ================================ + + /// @dev Get the core and policy contracts. + function _context() internal view returns (LlamaCore core, LlamaPolicy policy) { + core = LlamaCore(LlamaExecutor(address(this)).LLAMA_CORE()); + policy = LlamaPolicy(core.policy()); + } +} diff --git a/test/mock/MockInstanceUpdateVersion1.sol b/test/mock/MockInstanceUpdateVersion1.sol new file mode 100644 index 000000000..15a0793bf --- /dev/null +++ b/test/mock/MockInstanceUpdateVersion1.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {LlamaBaseScript} from "src/llama-scripts/LlamaBaseScript.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {LlamaCore} from "src/LlamaCore.sol"; +import {LlamaExecutor} from "src/LlamaExecutor.sol"; +import {LlamaPolicy} from "src/LlamaPolicy.sol"; + +/// @dev Upgrade the llama instance calling this script to version 1. +contract MockInstanceUpdateVersion1 is LlamaBaseScript { + function updateInstance() external onlyDelegateCall { + (LlamaCore core,) = _context(); + // Authorize `LlamaAbsolutePeerReview` + core.setStrategyLogicAuthorization(ILlamaStrategy(0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3), true); + // Authorize `LlamaRelativeUniqueHolderQuorum` + core.setStrategyLogicAuthorization(ILlamaStrategy(0xd21060559c9beb54fC07aFd6151aDf6cFCDDCAeB), true); + } + + // ================================ + // ======== Internal Logic ======== + // ================================ + + /// @dev Get the core and policy contracts. + function _context() internal view returns (LlamaCore core, LlamaPolicy policy) { + core = LlamaCore(LlamaExecutor(address(this)).LLAMA_CORE()); + policy = LlamaPolicy(core.policy()); + } +} From 19ff94b543310122b9e4b505b0c7c14c4ff48c29 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Tue, 15 Aug 2023 17:14:10 -0400 Subject: [PATCH 08/10] testing --- .../MultipleInstance.integrations.t.sol | 252 +++++++++++------- test/mock/MockInstanceUpdateVersion1.sol | 9 +- 2 files changed, 166 insertions(+), 95 deletions(-) diff --git a/test/integrations/MultipleInstance.integrations.t.sol b/test/integrations/MultipleInstance.integrations.t.sol index 7a3fe72a9..147a3a2d4 100644 --- a/test/integrations/MultipleInstance.integrations.t.sol +++ b/test/integrations/MultipleInstance.integrations.t.sol @@ -4,31 +4,37 @@ pragma solidity ^0.8.19; import {Test, console2} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; -import {stdJson} from "forge-std/Script.sol"; import {MockInstanceUpdateScript} from "test/mock/MockInstanceUpdateScript.sol"; import {MockInstanceUpdateVersion1} from "test/mock/MockInstanceUpdateVersion1.sol"; -import {MockProtocol} from "test/mock/MockProtocol.sol"; -import {MockScript} from "test/mock/MockScript.sol"; import {DeployLlamaFactory} from "script/DeployLlamaFactory.s.sol"; import {DeployLlamaInstance} from "script/DeployLlamaInstance.s.sol"; -import {DeployUtils} from "script/DeployUtils.sol"; -import {ILlamaAccount} from "src/interfaces/ILlamaAccount.sol"; -import {ILlamaPolicyMetadata} from "src/interfaces/ILlamaPolicyMetadata.sol"; import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; -import {ActionInfo, PermissionData, RoleHolderData} from "src/lib/Structs.sol"; -import {RoleDescription} from "src/lib/UDVTs.sol"; import {LlamaCore} from "src/LlamaCore.sol"; import {LlamaExecutor} from "src/LlamaExecutor.sol"; import {LlamaPolicy} from "src/LlamaPolicy.sol"; +import {ActionInfo, PermissionData} from "src/lib/Structs.sol"; contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, Test { event ApprovalCast(uint256 id, address indexed policyholder, uint8 indexed role, uint256 quantity, string reason); + event ActionCreated( + uint256 id, + address indexed creator, + uint8 role, + ILlamaStrategy indexed strategy, + address indexed target, + uint256 value, + bytes data, + string description + ); address LLAMA_INSTANCE_DEPLOYER = 0x3d9fEa8AeD0249990133132Bb4BC8d07C6a8259a; + uint8 CORE_TEAM_ROLE = uint8(1); + uint8 GOVERNANCE_MAINTAINER_ROLE = uint8(2); + address llamaAlice; uint256 llamaAlicePrivateKey; address llamaBob; @@ -113,7 +119,7 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T vm.startPrank(address(mockExecutor)); mockCore.setScriptAuthorization(address(mockInstanceUpdateScript), true); mockPolicy.setRolePermission( - uint8(2), + GOVERNANCE_MAINTAINER_ROLE, PermissionData( address(mockInstanceUpdateScript), MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, @@ -122,7 +128,7 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T true ); mockPolicy.setRolePermission( - uint8(2), + GOVERNANCE_MAINTAINER_ROLE, PermissionData( address(mockInstanceUpdateScript), MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, @@ -143,22 +149,24 @@ contract MultipleInstanceTestSetup is DeployLlamaFactory, DeployLlamaInstance, T ); } - function _llamaApproveAction(address _policyholder, ActionInfo memory actionInfo) public { + function _approveAction(LlamaCore _core, address _policyholder, ActionInfo memory actionInfo) public { vm.expectEmit(); emit ApprovalCast(actionInfo.id, _policyholder, uint8(1), 1, ""); vm.prank(_policyholder); - llamaInstanceCore.castApproval(uint8(1), actionInfo, ""); + _core.castApproval(uint8(1), actionInfo, ""); } -} -contract MultipleInstanceTest is MultipleInstanceTestSetup { - function test_createsActionToAuthorizeScript() external { + function createActionToAuthorizeScriptAndSetPermission(ILlamaStrategy strategyForMockInstance) + public + returns (ActionInfo memory) + { PermissionData memory permissionData = PermissionData( - address(mockInstanceUpdateVersion1), MockInstanceUpdateVersion1.updateInstance.selector, MOCK_VOTING_STRATEGY + address(mockInstanceUpdateVersion1), MockInstanceUpdateVersion1.updateInstance.selector, strategyForMockInstance ); bytes memory actionData = abi.encodeCall(MockInstanceUpdateScript.authorizeScriptAndSetPermission, (permissionData)); bytes memory data = abi.encodeCall( - LlamaCore.createAction, (uint8(2), MOCK_VOTING_STRATEGY, address(mockInstanceUpdateScript), 0, actionData, "") + LlamaCore.createAction, + (GOVERNANCE_MAINTAINER_ROLE, strategyForMockInstance, address(mockInstanceUpdateScript), 0, actionData, "") ); vm.prank(llamaAlice); uint256 actionId = llamaInstanceCore.createAction(uint8(1), LLAMA_VOTING_STRATEGY, address(mockCore), 0, data, ""); @@ -167,86 +175,144 @@ contract MultipleInstanceTest is MultipleInstanceTestSetup { mineBlock(); - _llamaApproveAction(llamaBob, actionInfo); - _llamaApproveAction(llamaCharlie, actionInfo); - _llamaApproveAction(llamaDale, actionInfo); + _approveAction(llamaInstanceCore, llamaBob, actionInfo); + _approveAction(llamaInstanceCore, llamaCharlie, actionInfo); + _approveAction(llamaInstanceCore, llamaDale, actionInfo); // Executing llama's action creates an action for the mock instance vm.expectEmit(); - emit ActionCanceled(actionInfo.id, actionCreatorAaron); + emit ActionCreated( + 0, + address(llamaInstanceExecutor), + GOVERNANCE_MAINTAINER_ROLE, + strategyForMockInstance, + address(mockInstanceUpdateScript), + 0, + actionData, + "" + ); + llamaInstanceCore.executeAction(actionInfo); + + return ActionInfo( + 0, + address(llamaInstanceExecutor), + GOVERNANCE_MAINTAINER_ROLE, + strategyForMockInstance, + address(mockInstanceUpdateScript), + 0, + actionData + ); + } +} + +contract MultipleInstanceTest is MultipleInstanceTestSetup { + function test_instanceCanDelegateUpdateRoleToOtherInstance() external { + // Action is created for mock instance to call `MockInstanceUpdateScript` + ActionInfo memory actionInfo = createActionToAuthorizeScriptAndSetPermission(MOCK_VOTING_STRATEGY); + + mineBlock(); + + _approveAction(mockCore, mockBob, actionInfo); + _approveAction(mockCore, mockCharlie, actionInfo); + _approveAction(mockCore, mockDale, actionInfo); + _approveAction(mockCore, mockErica, actionInfo); + + // Script is authorized and llama has permission to create an action for it. + mockCore.executeAction(actionInfo); + + PermissionData memory permissionData = PermissionData( + address(mockInstanceUpdateVersion1), MockInstanceUpdateVersion1.updateInstance.selector, MOCK_VOTING_STRATEGY + ); + bytes memory actionData = abi.encodeCall(MockInstanceUpdateVersion1.updateInstance, (permissionData)); + bytes memory data = abi.encodeCall( + LlamaCore.createAction, + (GOVERNANCE_MAINTAINER_ROLE, MOCK_VOTING_STRATEGY, address(mockInstanceUpdateVersion1), 0, actionData, "") + ); + vm.prank(llamaAlice); + uint256 actionId = llamaInstanceCore.createAction(uint8(1), LLAMA_VOTING_STRATEGY, address(mockCore), 0, data, ""); + actionInfo = ActionInfo(actionId, llamaAlice, uint8(1), LLAMA_VOTING_STRATEGY, address(mockCore), 0, data); + + mineBlock(); + + _approveAction(llamaInstanceCore, llamaBob, actionInfo); + _approveAction(llamaInstanceCore, llamaCharlie, actionInfo); + _approveAction(llamaInstanceCore, llamaDale, actionInfo); + + // Executing llama's action creates an action for the mock instance to call the update script. + vm.expectEmit(); + emit ActionCreated( + actionId, + address(llamaInstanceExecutor), + GOVERNANCE_MAINTAINER_ROLE, + MOCK_VOTING_STRATEGY, + address(mockInstanceUpdateVersion1), + 0, + actionData, + "" + ); llamaInstanceCore.executeAction(actionInfo); - assertEq(); - - // SCRIPT OPTIMIZATION - - /* - 1. Deploy llama and mock protocol instances - 2. Mock instance needs to have some authorized GovernanceScript and reserve a role for llama with permission to - create actions for the functions on this - script with an optimistic and high approval strategy. The permissioning happens in the config but the script must be - authorized through an action. - 3. The script will have a function that allows a call to setScriptAuthorization and to setRolePermission to the - governance maintenance role with one - of the two strategies, the script being authorized, any target. - - 4. Llama will deploy an upgrade contract and use a multisig like strategy to propose calling the governance script - with this as a parameter. - 5. Now mock instance governance decides if they want to authorize this upgrade script, give llama the role - permission to propose calling it. The upgrade script - has the logic to unauthorize itself and remove all permissions after being called. - 6. The action executes. - 7. Llama proposes calling the script. The governance script forced llama to only have permission to make this - proposal with high buy in - 8. Mock instance executes the action, the script is called, the script is unauthed and all permissions are removed. */ - - // BASE CASE - - /* - 1. Deploy llama and mock protocol instances ✅ - 2. Mock protocol instance config has a role reserved for llama with proposal permissions for setScriptAuthorization. - Proposal permission for calling the script - needs to be set after it's deployed. The execute function of the script will also remove the permission from the - role and unauth the script. The role - includes two permissions: one to setScriptAuthorization with a long optimistic timelock and the other with high buy - in shorter timelock. Mock also gives - llama permission to propose calling setRolePermission so they can propose to give themselves the proposal perission - to call the script. - 3. Llama adds a permission to call createAction on the mock instance shortly after instance launches. This action - still needs to go through their governance - and can be canceled so a simple multisig like permission will suffice. - 4. Llama deploys a script - 5. The test starts with llama using either strategy to create, queue, execute calling setScriptAuth through mock's - createAction. - 6. This goes through mock's governance with minimal input from them. - 7. Once the script is authorized, llama then goes through their action process to call mock's createAction to give - themselves permission to call the script. - 8. Once that's complete llama goes through their action process to call mock's createAction to execute the script - 9. This requires high buy-in and when it executes it removes the permission to call it from the llama role and - unauthorizes the script - */ - - // ARCHIVE - /* - 1. Llama instance adds permission to role 1: - { - "comment": "This gives role #1 permission to call `createAction` on the mock protocol's core with the third - strategy.", - "permissionData": { - "selector": "0xb3c678b0", - "strategy": "0xa359FE9c585FbD6DAAEE4efF9c3bF6bd45D498bC", - "target": "0x0845B312d2D91bD864FAb7C8B732783E81e6CAd4" - }, - "role": 1 - } - 2. Deploy script contract - 3. llamaAlice calls createAction on llama instance to create action instantly on mock protocol - 4. We test that the action creation, queueAction, executeAction can happen in the same block - 5. Mock protocol gives llama instance a permission to create actions for (target: upgradeScript, strategy: - optimistic, target: authorizeScriptAndGivePermissionToCoreTeamToCall) - 6. Optimistically passes so the mock protocol instance authorize the LlamaV1Upgrade script and role 1 has permission - to propose calling this newly created target's execute finction with the voting strategy - 7. Core team member creates action to call upgrade with voting, LlamaV1Upgrade script, execute function - */ + actionInfo = ActionInfo( + actionId, + address(llamaInstanceExecutor), + GOVERNANCE_MAINTAINER_ROLE, + MOCK_VOTING_STRATEGY, + address(mockInstanceUpdateVersion1), + 0, + actionData + ); + + mineBlock(); + + _approveAction(mockCore, mockBob, actionInfo); + _approveAction(mockCore, mockCharlie, actionInfo); + _approveAction(mockCore, mockDale, actionInfo); + _approveAction(mockCore, mockErica, actionInfo); + + // Script is executed, unauthorized, and the permission is removed. + mockCore.executeAction(actionInfo); + bytes32 votingPermission = keccak256( + abi.encode( + PermissionData( + address(mockInstanceUpdateScript), + MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, + MOCK_VOTING_STRATEGY + ) + ) + ); + + bytes32 optimisticPermission = keccak256( + abi.encode( + PermissionData( + address(mockInstanceUpdateScript), + MockInstanceUpdateScript.authorizeScriptAndSetPermission.selector, + MOCK_OPTIMISTIC_STRATEGY + ) + ) + ); + + bytes32 upgradeScriptPermission = keccak256( + abi.encode( + PermissionData( + address(mockInstanceUpdateVersion1), MockInstanceUpdateVersion1.updateInstance.selector, MOCK_VOTING_STRATEGY + ) + ) + ); + + assertTrue(mockPolicy.hasPermissionId(address(llamaInstanceExecutor), GOVERNANCE_MAINTAINER_ROLE, votingPermission)); + assertTrue( + mockPolicy.hasPermissionId(address(llamaInstanceExecutor), GOVERNANCE_MAINTAINER_ROLE, optimisticPermission) + ); + assertTrue(mockCore.authorizedScripts(address(mockInstanceUpdateScript))); + + // Assert that upgrade script was executed + assertTrue(mockCore.authorizedStrategyLogics(ILlamaStrategy(0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3))); + assertTrue(mockCore.authorizedStrategyLogics(ILlamaStrategy(0xd21060559c9beb54fC07aFd6151aDf6cFCDDCAeB))); + + // Assert that upgrade script unauthorized itself and removed the permission + assertFalse(mockCore.authorizedScripts(address(mockInstanceUpdateVersion1))); + assertFalse( + mockPolicy.hasPermissionId(address(llamaInstanceExecutor), GOVERNANCE_MAINTAINER_ROLE, upgradeScriptPermission) + ); } } diff --git a/test/mock/MockInstanceUpdateVersion1.sol b/test/mock/MockInstanceUpdateVersion1.sol index 15a0793bf..e1453bfb8 100644 --- a/test/mock/MockInstanceUpdateVersion1.sol +++ b/test/mock/MockInstanceUpdateVersion1.sol @@ -6,15 +6,20 @@ import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; import {LlamaCore} from "src/LlamaCore.sol"; import {LlamaExecutor} from "src/LlamaExecutor.sol"; import {LlamaPolicy} from "src/LlamaPolicy.sol"; +import {PermissionData} from "src/lib/Structs.sol"; /// @dev Upgrade the llama instance calling this script to version 1. contract MockInstanceUpdateVersion1 is LlamaBaseScript { - function updateInstance() external onlyDelegateCall { - (LlamaCore core,) = _context(); + function updateInstance(PermissionData memory permissionData) external onlyDelegateCall { + (LlamaCore core, LlamaPolicy policy) = _context(); // Authorize `LlamaAbsolutePeerReview` core.setStrategyLogicAuthorization(ILlamaStrategy(0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3), true); // Authorize `LlamaRelativeUniqueHolderQuorum` core.setStrategyLogicAuthorization(ILlamaStrategy(0xd21060559c9beb54fC07aFd6151aDf6cFCDDCAeB), true); + + // Unauthorize script after completion and remove permission from governance maintainer role. + core.setScriptAuthorization(SELF, false); + policy.setRolePermission(uint8(2), permissionData, false); } // ================================ From 4bf4998391b13f9d5de658291469e3187cdeeb43 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 17 Aug 2023 09:50:57 -0400 Subject: [PATCH 09/10] Update test/mock/MockAtomicActionExecutor.sol Co-authored-by: dd0sxx --- test/mock/MockAtomicActionExecutor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mock/MockAtomicActionExecutor.sol b/test/mock/MockAtomicActionExecutor.sol index ddd8f11a8..d090e7685 100644 --- a/test/mock/MockAtomicActionExecutor.sol +++ b/test/mock/MockAtomicActionExecutor.sol @@ -7,7 +7,7 @@ import {LlamaCore} from "src/LlamaCore.sol"; /// @dev A mock contract that can create, queue, and execute actions in a single function. contract MockAtomicActionExecutor { - LlamaCore immutable core; + LlamaCore immutable CORE; constructor(LlamaCore _core) { core = _core; From 7b731c1715856d5172b798c97632a68b1b2374f5 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 17 Aug 2023 09:59:25 -0400 Subject: [PATCH 10/10] fix: broken test --- test/mock/MockAtomicActionExecutor.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/mock/MockAtomicActionExecutor.sol b/test/mock/MockAtomicActionExecutor.sol index d090e7685..9a78bf3a1 100644 --- a/test/mock/MockAtomicActionExecutor.sol +++ b/test/mock/MockAtomicActionExecutor.sol @@ -10,7 +10,7 @@ contract MockAtomicActionExecutor { LlamaCore immutable CORE; constructor(LlamaCore _core) { - core = _core; + CORE = _core; } function createQueueAndExecute( @@ -25,10 +25,10 @@ contract MockAtomicActionExecutor { bytes32 r, bytes32 s ) external returns (uint256 actionId) { - actionId = core.createActionBySig(policyholder, role, strategy, target, value, data, description, v, r, s); + actionId = CORE.createActionBySig(policyholder, role, strategy, target, value, data, description, v, r, s); ActionInfo memory actionInfo = ActionInfo(actionId, policyholder, role, strategy, target, value, data); - core.queueAction(actionInfo); - core.executeAction(actionInfo); + CORE.queueAction(actionInfo); + CORE.executeAction(actionInfo); } }