From 193976ca34a6746d69e82b5668090fb65956061f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:37:21 -0600 Subject: [PATCH 01/11] update --- .../test-contracts/test-voting/VotingTest.sol | 6 ++++- .../test/votingTest/singleVotingTest.ts | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 darc-protocol/test/votingTest/singleVotingTest.ts diff --git a/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol b/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol index bd5ec8b..9d1f1ce 100644 --- a/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol +++ b/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol @@ -9,8 +9,12 @@ import '../../protocol/Runtime/VotingMachine/VotingMachine.sol'; * @notice null */ -contract VotingTestContract is VotingMachine{ +contract VotingTestContract is VotingMachine { function initializeVotingTest() public { this.initialize(); } + + function getValue() public view returns (uint256) { + return 1; + } } \ No newline at end of file diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts new file mode 100644 index 0000000..d561663 --- /dev/null +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -0,0 +1,27 @@ +import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const programOperatorAddress = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; + +describe.only("single voting test", function () { + it ("should pass single voting test", async function () { + const VotingTestFactory = await ethers.getContractFactory("VotingTestContract"); + const votingTest = await VotingTestFactory.deploy(); + await votingTest.deployed(); + await votingTest.initialize(); + + console.log("votingTest.address: ", votingTest.address); + console.log(await votingTest.getValue()); + }); +}) \ No newline at end of file From 7404ec70e19b77f9f847a7250c2aaa7e0416ec1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Thu, 11 Jan 2024 01:04:40 -0600 Subject: [PATCH 02/11] update --- darc-js/src/darcBinary/DARC-latest.json | 6 +- darc-js/src/darcBinary/DARC-test.json | 6 +- darc-js/src/types/basicTypes.ts | 4 +- darc-protocol/contracts/protocol/Plugin.sol | 24 ++- .../ProgramValidator/ProgramValidator.sol | 28 +++ .../contracts/protocol/Runtime/Runtime.sol | 16 +- .../test-contracts/test-voting/VotingTest.sol | 20 -- .../test-voting/VotingTest_SingleTest.sol | 173 ++++++++++++++++++ .../test/votingTest/singleVotingTest.ts | 13 +- 9 files changed, 242 insertions(+), 48 deletions(-) delete mode 100644 darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol create mode 100644 darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol diff --git a/darc-js/src/darcBinary/DARC-latest.json b/darc-js/src/darcBinary/DARC-latest.json index bce807d..514d1b1 100644 --- a/darc-js/src/darcBinary/DARC-latest.json +++ b/darc-js/src/darcBinary/DARC-latest.json @@ -105,7 +105,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { @@ -866,7 +866,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { @@ -1140,7 +1140,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { diff --git a/darc-js/src/darcBinary/DARC-test.json b/darc-js/src/darcBinary/DARC-test.json index f29e6b6..49759d7 100644 --- a/darc-js/src/darcBinary/DARC-test.json +++ b/darc-js/src/darcBinary/DARC-test.json @@ -115,7 +115,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { @@ -911,7 +911,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { @@ -1200,7 +1200,7 @@ }, { "internalType": "bool", - "name": "isEnabled", + "name": "bIsEnable", "type": "bool" }, { diff --git a/darc-js/src/types/basicTypes.ts b/darc-js/src/types/basicTypes.ts index f3372c3..7cb3b00 100644 --- a/darc-js/src/types/basicTypes.ts +++ b/darc-js/src/types/basicTypes.ts @@ -20,7 +20,7 @@ export type VotingRuleStruct = { approvalThresholdPercentage: PromiseOrValue; votingDurationInSeconds: PromiseOrValue; executionPendingDurationInSeconds: PromiseOrValue; - isEnabled: PromiseOrValue; + bIsEnable: PromiseOrValue; notes: PromiseOrValue; bIsAbsoluteMajority: PromiseOrValue; }; @@ -38,7 +38,7 @@ export type VotingRuleStructOutput = [ approvalThresholdPercentage: BigNumber; votingDurationInSeconds: BigNumber; executionPendingDurationInSeconds: BigNumber; - isEnabled: boolean; + bIsEnable: boolean; notes: string; bIsAbsoluteMajority: boolean; }; diff --git a/darc-protocol/contracts/protocol/Plugin.sol b/darc-protocol/contracts/protocol/Plugin.sol index 3f1e3eb..8ebb0e3 100644 --- a/darc-protocol/contracts/protocol/Plugin.sol +++ b/darc-protocol/contracts/protocol/Plugin.sol @@ -44,6 +44,28 @@ enum EnumLogicalOperatorType {UNDEFINED, AND, OR, NOT } * 3. plugin Z, return type: NO, level 3 * * Then the return type of the operation is YES_AND_SKIP_SANDBOX. + * + * + * + * ---------------------------------------------- + * + * For before operation plugins, here is the rule: + * 1. If the return type is YES_AND_SKIP_SANDBOX, then all the plugin level L%3 == 1, e.g. [1, 4, 7, 10, ...] + * 2. If the return type is SANDBOX_NEEDED, then all the plugin level L%3 == 2, e.g. [2, 5, 8, 11, ...] + * 3. If the return type is NO, then all the plugin level L%3 == 0, e.g. [3, 6, 9, 12, ...] + * + * + * ---------------------------------------------- + * + * For after operation plugins, here is the rule: + * 1. If the return type is YES, then all the plugin level L%3 == 1, e.g. [1, 4, 7, 10, ...] + * 2. If the return type is VOTING_NEEDED, then all the plugin level L%3 == 2, e.g. [2, 5, 8, 11, ...] + * 3. If the return type is NO, then all the plugin level L%3 == 0, e.g. [3, 6, 9, 12, ...] + * + * ---------------------------------------------- + * + * If the return type is UNDEFINED, then just throw the error and revert the transaction + * */ enum EnumReturnType { @@ -167,7 +189,7 @@ struct VotingRule { /** * the voting policy is enabled or not */ - bool isEnabled; + bool bIsEnable; /** * the note of the voting policy diff --git a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol index 65ef86e..3d0a7ad 100644 --- a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol +++ b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol @@ -37,5 +37,33 @@ library ProgramValidator{ return true; } + function validateVoteProgram(Program memory currentProgram) internal pure returns (bool) { + //1. check if the program is empty + if (currentProgram.operations.length != 1) { return false; } + return validate_ID_32_VOTE(currentProgram.operations[0]); + } + + function validateExecutePendingProgram(Program memory currentProgram) internal pure returns (bool) { + //1. check if the program is empty + if (currentProgram.operations.length != 1) { return false; } + return validate_ID_33_EXECUTE_PROGRAM(currentProgram.operations[0]); + } + + function validate_ID_32_VOTE(Operation memory op) internal pure returns (bool) { + if (op.opcode != EnumOpcode.VOTE) { return false; } + if (op.param.UINT256_2DARRAY.length != 0) { return false; } + if (op.param.ADDRESS_2DARRAY.length != 0) { return false; } + if (op.param.STRING_ARRAY.length != 0) { return false; } + return true; + } + + function validate_ID_33_EXECUTE_PROGRAM(Operation memory op) internal pure returns (bool) { + if (op.opcode != EnumOpcode.EXECUTE_PROGRAM) { return false; } + if (op.param.UINT256_2DARRAY.length != 0) { return false; } + if (op.param.ADDRESS_2DARRAY.length != 0) { return false; } + if (op.param.STRING_ARRAY.length != 0) { return false; } + return true; + } + } \ No newline at end of file diff --git a/darc-protocol/contracts/protocol/Runtime/Runtime.sol b/darc-protocol/contracts/protocol/Runtime/Runtime.sol index 516736e..3940717 100644 --- a/darc-protocol/contracts/protocol/Runtime/Runtime.sol +++ b/darc-protocol/contracts/protocol/Runtime/Runtime.sol @@ -133,12 +133,8 @@ contract Runtime is Executable, PaymentCheck{ * 2. The operation is a vote operation * 3. The vote operation contains the same number boolean values as the number of the voting policy */ - function validateVoteProgram(Program memory program) internal view returns (bool) { - //1. check if the program is empty - if (program.operations.length == 0) { return false; } - - //2. check if the program is valid - return true;//ProgramValidator.validate(); + function validateVoteProgram(Program memory program) internal pure returns (bool) { + return ProgramValidator.validateVoteProgram(program); } /** @@ -147,12 +143,8 @@ contract Runtime is Executable, PaymentCheck{ * 2. The operation is a execute pending operation: ExecutePending * 3. The execute pending operation contains the same number boolean values as the number of the voting policy */ - function validateExecutePendingProgram(Program memory program) internal view returns (bool) { - //1. check if the program is empty - if (program.operations.length == 0) { return false; } - - //2. check if the program is valid - return true; //ProgramValidator.validate(currentProgram); + function validateExecutePendingProgram(Program memory program) internal pure returns (bool) { + return ProgramValidator.validateExecutePendingProgram(program); } /** diff --git a/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol b/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol deleted file mode 100644 index 9d1f1ce..0000000 --- a/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; -import '../../protocol/Runtime/VotingMachine/VotingMachine.sol'; - - -/** - * @title The unit test contract of the voting machine - * @author DARC Team - * @notice null - */ - -contract VotingTestContract is VotingMachine { - function initializeVotingTest() public { - this.initialize(); - } - - function getValue() public view returns (uint256) { - return 1; - } -} \ No newline at end of file diff --git a/darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol b/darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol new file mode 100644 index 0000000..a153709 --- /dev/null +++ b/darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; +import '../../protocol/Runtime/VotingMachine/VotingMachine.sol'; +import '../../protocol/Runtime/Runtime.sol'; + + +/** + * @title The single voting test contract of the voting machine + * @author DARC Team + * @notice null + */ + +contract VotingTest_SingleTest is Runtime { + function initializeVotingTest() public { + this.initialize(); + } + + function runTest() public { + + // create a token class first + currentMachineState.tokenList[0].bIsInitialized = true; + currentMachineState.tokenList[0].tokenInfo = "token_0"; + currentMachineState.tokenList[0].votingWeight = 1; + currentMachineState.tokenList[0].dividendWeight = 1; + currentMachineState.tokenList[0].totalSupply = 1000; + + // mint 600 tokens to "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; + currentMachineState.tokenList[0].tokenBalance[0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266] = 600; + + // mint 200 tokens to '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' + currentMachineState.tokenList[0].tokenBalance[0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC] = 200; + + // mint 200 tokens to '0x90F79bf6EB2c4f870365E785982E1f101E93b906' + currentMachineState.tokenList[0].tokenBalance[0x90F79bf6EB2c4f870365E785982E1f101E93b906] = 200; + + // add addresses to owner list + currentMachineState.tokenList[0].ownerList.push(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + currentMachineState.tokenList[0].ownerList.push(0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC); + currentMachineState.tokenList[0].ownerList.push(0x90F79bf6EB2c4f870365E785982E1f101E93b906); + + + // add a plugin to the before-op and after-op + // before-op: any operation need sandbox + // after-op: any operation need voting + + + // Plugin storage beforeOpPlugin = Plugin({ + // returnType: EnumReturnType.SANDBOX_NEEDED, + // level: 5, + // conditionNodes: new ConditionNode[](1), + // votingRuleIndex: 0, + // notes: "test", + // bIsEnabled: true, + // bIsInitialized: true, + // bIsBeforeOperation: true + // }); + + // beforeOpPlugin.conditionNodes[0] = ConditionNode({ + // id:0, + // nodeType: EnumConditionNodeType.BOOLEAN_TRUE, + // logicalOperator: EnumLogicalOperatorType.UNDEFINED, + // conditionExpression: 0, + // childList: new uint256[](0), + // param: NodeParam({ + // STRING_ARRAY: new string[](0), + // UINT256_2DARRAY: new uint256[][](0), + // ADDRESS_2DARRAY: new address[][](0), + // BYTES: new bytes(0) + // }) + // }); + + currentMachineState.beforeOpPlugins.push(Plugin({ + returnType: EnumReturnType.SANDBOX_NEEDED, + level: 5, + conditionNodes: new ConditionNode[](1), + votingRuleIndex: 0, + notes: "test", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true + })); + + currentMachineState.beforeOpPlugins[1].conditionNodes[0] = ConditionNode({ + id:0, + nodeType: EnumConditionNodeType.BOOLEAN_TRUE, + logicalOperator: EnumLogicalOperatorType.UNDEFINED, + conditionExpression: 0, + childList: new uint256[](0), + param: NodeParam({ + STRING_ARRAY: new string[](0), + UINT256_2DARRAY: new uint256[][](0), + ADDRESS_2DARRAY: new address[][](0), + BYTES: new bytes(0) + }) + }); + + // add a voting rule + currentMachineState.votingRuleList.push(VotingRule({ + votingTokenClassList: new uint256[](1), + approvalThresholdPercentage: 50, // 50 percent to approve + votingDurationInSeconds: 1000, // 1000 seconds to vote + executionPendingDurationInSeconds: 1000, // 1000 seconds to execute + bIsEnable: true, + notes: "voting rule 0", + bIsAbsoluteMajority: false + })); + + currentMachineState.votingRuleList[0].votingTokenClassList[0] = 0; // class 0 token can vote + + + + currentMachineState.afterOpPlugins.push(Plugin({ + returnType: EnumReturnType.VOTING_NEEDED, + level: 5, + conditionNodes: new ConditionNode[](1), + votingRuleIndex: 0, + notes: "test", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false + })); + + currentMachineState.afterOpPlugins[1].conditionNodes[0] = ConditionNode({ + id:0, + nodeType: EnumConditionNodeType.BOOLEAN_TRUE, + logicalOperator: EnumLogicalOperatorType.UNDEFINED, + conditionExpression: 0, + childList: new uint256[](0), + param: NodeParam({ + STRING_ARRAY: new string[](0), + UINT256_2DARRAY: new uint256[][](0), + ADDRESS_2DARRAY: new address[][](0), + BYTES: new bytes(0) + }) + }); + + + // try to run a program with 1 operation + Program memory program = Program({ + programOperatorAddress: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, + operations: new Operation[](1), + notes: "test" + }); + + program.operations[0] = Operation({ + operatorAddress: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, + opcode: EnumOpcode.BATCH_MINT_TOKENS, + param: Param({ + STRING_ARRAY: new string[](0), + BOOL_ARRAY: new bool[](0), + VOTING_RULE_ARRAY: new VotingRule[](0), + PLUGIN_ARRAY: new Plugin[](0), + PARAMETER_ARRAY: new MachineParameter[](0), + UINT256_2DARRAY: new uint256[][](0), + ADDRESS_2DARRAY: new address[][](0), + BYTES: new bytes(0) + }) + }); + + // make sure that the checkBeforeOperationPlugins returns SANDBOX_NEEDED + EnumReturnType returnType = checkBeforeOperationPlugins(program); + require(returnType == EnumReturnType.SANDBOX_NEEDED, "VotingTest SingleTest: The return type should be SANDBOX_NEEDED"); + + // make sure that the checkAfterOperationPlugins returns VOTING_NEEDED + (EnumReturnType afterReturnType, uint256[] memory afterRuleIdxList) = checkAfterOperationPlugins(program); + + require(afterReturnType == EnumReturnType.VOTING_NEEDED, "VotingTest SingleTest: The return type should be VOTING_NEEDED"); + + require(afterRuleIdxList.length == 1, "VotingTest SingleTest: The length of the rule index list should be 1"); + + require(afterRuleIdxList[0] == 0, "VotingTest SingleTest: The rule index should be 0"); + } +} \ No newline at end of file diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index d561663..554d96a 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -16,12 +16,11 @@ const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; describe.only("single voting test", function () { it ("should pass single voting test", async function () { - const VotingTestFactory = await ethers.getContractFactory("VotingTestContract"); - const votingTest = await VotingTestFactory.deploy(); - await votingTest.deployed(); - await votingTest.initialize(); - - console.log("votingTest.address: ", votingTest.address); - console.log(await votingTest.getValue()); + const VotingTestSingleTestFactory = await ethers.getContractFactory("VotingTest_SingleTest"); + const votingTestSingleTest = await VotingTestSingleTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initializeVotingTest(); + await votingTestSingleTest.runTest(); + console.log("votingTest.address: ", votingTestSingleTest.address); }); }) \ No newline at end of file From 81e18608996bf258e38a62fa546954160b38ffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Fri, 12 Jan 2024 04:54:10 -0600 Subject: [PATCH 03/11] update --- darc-js/src/darcBinary/DARC-latest.json | 6 +- darc-js/src/darcBinary/DARC-test.json | 6 +- darc-js/src/types/basicTypes.ts | 4 +- .../protocol/Dashboard/Dashboard.sol | 7 + .../contracts/protocol/MachineState.sol | 2 +- .../protocol/MachineStateManager.sol | 8 +- darc-protocol/contracts/protocol/Plugin.sol | 2 +- .../Runtime/Executable/Executable.sol | 2 +- .../ProgramValidator/ProgramValidator.sol | 15 ++ .../Runtime/VotingMachine/VotingMachine.sol | 39 +++-- .../PluginTest.sol => TestBaseContract.sol} | 51 +++++- ...Test_SingleTest.sol => VotingTest.sol.old} | 37 ++++- .../test-voting/VotingTestBase.sol | 50 ++++++ darc-protocol/hardhat.config.ts | 2 +- .../test/pluginTest/operator_AND_test.ts | 2 +- .../test/pluginTest/operator_OR_test.ts | 2 +- .../test/votingTest/singleVotingTest.ts | 155 +++++++++++++++++- 17 files changed, 351 insertions(+), 39 deletions(-) rename darc-protocol/contracts/test-contracts/{test-plugin/PluginTest.sol => TestBaseContract.sol} (59%) rename darc-protocol/contracts/test-contracts/test-voting/{VotingTest_SingleTest.sol => VotingTest.sol.old} (80%) create mode 100644 darc-protocol/contracts/test-contracts/test-voting/VotingTestBase.sol diff --git a/darc-js/src/darcBinary/DARC-latest.json b/darc-js/src/darcBinary/DARC-latest.json index 514d1b1..ad0f351 100644 --- a/darc-js/src/darcBinary/DARC-latest.json +++ b/darc-js/src/darcBinary/DARC-latest.json @@ -105,7 +105,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { @@ -866,7 +866,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { @@ -1140,7 +1140,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { diff --git a/darc-js/src/darcBinary/DARC-test.json b/darc-js/src/darcBinary/DARC-test.json index 49759d7..c49091b 100644 --- a/darc-js/src/darcBinary/DARC-test.json +++ b/darc-js/src/darcBinary/DARC-test.json @@ -115,7 +115,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { @@ -911,7 +911,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { @@ -1200,7 +1200,7 @@ }, { "internalType": "bool", - "name": "bIsEnable", + "name": "bIsEnabled", "type": "bool" }, { diff --git a/darc-js/src/types/basicTypes.ts b/darc-js/src/types/basicTypes.ts index 7cb3b00..6747afb 100644 --- a/darc-js/src/types/basicTypes.ts +++ b/darc-js/src/types/basicTypes.ts @@ -20,7 +20,7 @@ export type VotingRuleStruct = { approvalThresholdPercentage: PromiseOrValue; votingDurationInSeconds: PromiseOrValue; executionPendingDurationInSeconds: PromiseOrValue; - bIsEnable: PromiseOrValue; + bIsEnabled: PromiseOrValue; notes: PromiseOrValue; bIsAbsoluteMajority: PromiseOrValue; }; @@ -38,7 +38,7 @@ export type VotingRuleStructOutput = [ approvalThresholdPercentage: BigNumber; votingDurationInSeconds: BigNumber; executionPendingDurationInSeconds: BigNumber; - bIsEnable: boolean; + bIsEnabled: boolean; notes: string; bIsAbsoluteMajority: boolean; }; diff --git a/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol b/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol index 8c06918..a07bff2 100644 --- a/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol +++ b/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol @@ -131,4 +131,11 @@ contract Dashboard is MachineStateManager { function getCurrentDividendPerUnit() public view returns (uint256) { return currentDividendPerUnit(false); } + + /** + * Get all logs of DARC + */ + function getDARClogs() public view returns (string[] memory) { + return DARClogs; + } } diff --git a/darc-protocol/contracts/protocol/MachineState.sol b/darc-protocol/contracts/protocol/MachineState.sol index cf312ba..606b51c 100644 --- a/darc-protocol/contracts/protocol/MachineState.sol +++ b/darc-protocol/contracts/protocol/MachineState.sol @@ -102,7 +102,7 @@ struct MachineState { * The balance sheet and information of each token class * TODO: make the length of the tokenList dynamic (not fixed to 10000) */ - Token[10000] tokenList; + Token[100] tokenList; /** * The member list and internal role index number of each token owner, diff --git a/darc-protocol/contracts/protocol/MachineStateManager.sol b/darc-protocol/contracts/protocol/MachineStateManager.sol index e538b74..bee4cd2 100644 --- a/darc-protocol/contracts/protocol/MachineStateManager.sol +++ b/darc-protocol/contracts/protocol/MachineStateManager.sol @@ -30,8 +30,8 @@ contract MachineStateManager { /** * ======== DARC Machine State ======== */ - MachineState currentMachineState; - MachineState sandboxMachineState; + MachineState public currentMachineState; + MachineState public sandboxMachineState; /** @@ -66,6 +66,10 @@ contract MachineStateManager { */ uint256 dividendBufferSize; + /** + * @notice The logs of the DARC machine state + */ + string[] DARClogs; diff --git a/darc-protocol/contracts/protocol/Plugin.sol b/darc-protocol/contracts/protocol/Plugin.sol index 8ebb0e3..75d7c43 100644 --- a/darc-protocol/contracts/protocol/Plugin.sol +++ b/darc-protocol/contracts/protocol/Plugin.sol @@ -189,7 +189,7 @@ struct VotingRule { /** * the voting policy is enabled or not */ - bool bIsEnable; + bool bIsEnabled; /** * the note of the voting policy diff --git a/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol b/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol index 20e0140..9edaa12 100644 --- a/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol +++ b/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol @@ -55,7 +55,7 @@ contract Executable is MachineStateManager, PluginSystem, VotingMachine, Instruc // 2.4 If the program is voting needed, initialize the voting machine else if (afterReturnType == EnumReturnType.VOTING_NEEDED){ - this.initializeVoting(afterRuleIdxList, currentProgram); + initializeVoting(afterRuleIdxList, currentProgram); } // 2.5 If the program is approved, just execute and end diff --git a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol index 3d0a7ad..67f8355 100644 --- a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol +++ b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol @@ -37,18 +37,33 @@ library ProgramValidator{ return true; } + /** + * Returns true if the program is a valid vote program + * 1. There is only one operation in the program + * 2. The operation is a vote operation + * @param currentProgram The program to be validated + */ function validateVoteProgram(Program memory currentProgram) internal pure returns (bool) { //1. check if the program is empty if (currentProgram.operations.length != 1) { return false; } return validate_ID_32_VOTE(currentProgram.operations[0]); } + /** + * Returns true if the program is a valid execute pending program + * 1. There is only one operation in the program + * 2. The operation is a execute pending operation: ExecutePendingProgram + * @param currentProgram The program to be validated + */ function validateExecutePendingProgram(Program memory currentProgram) internal pure returns (bool) { //1. check if the program is empty if (currentProgram.operations.length != 1) { return false; } return validate_ID_33_EXECUTE_PROGRAM(currentProgram.operations[0]); } + + //----------------- validate each operation ----------------- + function validate_ID_32_VOTE(Operation memory op) internal pure returns (bool) { if (op.opcode != EnumOpcode.VOTE) { return false; } if (op.param.UINT256_2DARRAY.length != 0) { return false; } diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index fe68320..3ca2607 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -82,7 +82,7 @@ contract VotingMachine is MachineStateManager { * @notice If address has voted for the voting item with index i, * and if so, voted[address][i] = true */ - mapping(address => mapping(uint256 => bool)) voted; + mapping(address => mapping(uint256 => bool)) public voted; /** @@ -105,7 +105,7 @@ contract VotingMachine is MachineStateManager { /** * @notice start the voting period */ - function initializeVoting(uint256[] memory votingRuleIndices, Program memory currentProgram) external { + function initializeVoting(uint256[] memory votingRuleIndices, Program memory currentProgram) internal { // make sure the voting period is not in progress require(!isVotingProcesss(), "voting is already in progress"); @@ -132,19 +132,36 @@ contract VotingMachine is MachineStateManager { votingItems[latestVotingItemIndex].powerNo = new uint256[](votingRuleIndices.length); votingItems[latestVotingItemIndex].powerYes = new uint256[](votingRuleIndices.length); votingItems[latestVotingItemIndex].totalPower = new uint256[](votingRuleIndices.length); - - (bIsValid, votingItems[latestVotingItemIndex].votingEndTime) = SafeMathUpgradeable.tryAdd( + return; // checkpoint 2 + uint256 resultVotingDeadline = 0; + (bIsValid, resultVotingDeadline) = SafeMathUpgradeable.tryAdd( block.timestamp, minVotingDuration); require(bIsValid, "voting end time overflow"); - (bIsValid, votingItems[latestVotingItemIndex].executingEndTime) = SafeMathUpgradeable.tryAdd( + + // update the voting end time in the voting items map + votingItems[latestVotingItemIndex].votingEndTime = resultVotingDeadline; + + // update the voting end time in the machine state manager + votingDeadline = resultVotingDeadline; + + uint256 resultExecutionPendingDeadline = 0; + (bIsValid, resultExecutionPendingDeadline) = SafeMathUpgradeable.tryAdd( votingItems[latestVotingItemIndex].votingEndTime, minExecutingDuration ); require(bIsValid, "executing end time overflow"); + + // update the executing end time in the voting items map + votingItems[latestVotingItemIndex].executingEndTime = resultExecutionPendingDeadline; + + // update the executing end time in the machine state manager + executingPendingDeadline = resultExecutionPendingDeadline; + + // update the voting status in the voting items map votingItems[latestVotingItemIndex].votingStatus = VotingStatus.Ongoing; votingItems[latestVotingItemIndex].votingRuleIndices = new uint256[](votingRuleIndices.length); - + // return; // checkpoint 1 failed for (uint256 i = 0; i < votingRuleIndices.length; i++) { votingItems[latestVotingItemIndex].votingRuleIndices[i] = votingRuleIndices[i]; } @@ -171,7 +188,7 @@ contract VotingMachine is MachineStateManager { * @param voter the address of the voter * @param votes the list of votes, true for yes, false for no */ - function vote(address voter, bool[] memory votes) external { + function vote(address voter, bool[] memory votes) internal { require(isVotingProcesss(), "voting is not in progress"); require(block.timestamp < currentVotingEndTime, "voting period has ended"); require(!voted[voter][latestVotingItemIndex], "voter has already voted"); @@ -243,7 +260,7 @@ contract VotingMachine is MachineStateManager { */ function endVoting() private { require(block.timestamp >= currentVotingEndTime, "voting period has not ended"); - VotingStatus[] memory result = this.checkVotingResults(); + VotingStatus[] memory result = checkVotingResults(); for (uint256 i = 0; i < result.length; i++) { // if any voting rule failed, the whole voting process failed, @@ -323,10 +340,10 @@ contract VotingMachine is MachineStateManager { /** * @notice Check the voting state of all the voting rules */ - function checkVotingResults() external view returns (VotingStatus[] memory) { + function checkVotingResults() internal view returns (VotingStatus[] memory) { VotingStatus[] memory votingStatus = new VotingStatus[](votingItems[latestVotingItemIndex].votingRuleIndices.length); for (uint256 i = 0; i < votingItems[latestVotingItemIndex].votingRuleIndices.length; i++) { - votingStatus[i] = this.checkVotingResult(i); + votingStatus[i] = checkVotingResult(i); } return votingStatus; } @@ -340,7 +357,7 @@ contract VotingMachine is MachineStateManager { * No "OnGoing" state because the voting machine will check this function to change the state * @param idx the index of the voting rule */ - function checkVotingResult(uint256 idx) external view returns (VotingStatus) { + function checkVotingResult(uint256 idx) internal view returns (VotingStatus) { bool bIsValid = false; uint256 threshold = currentMachineState.votingRuleList[idx].approvalThresholdPercentage; uint256 currentYes = votingItems[latestVotingItemIndex].powerYes[idx]; diff --git a/darc-protocol/contracts/test-contracts/test-plugin/PluginTest.sol b/darc-protocol/contracts/test-contracts/TestBaseContract.sol similarity index 59% rename from darc-protocol/contracts/test-contracts/test-plugin/PluginTest.sol rename to darc-protocol/contracts/test-contracts/TestBaseContract.sol index a02ea4a..d9c3161 100644 --- a/darc-protocol/contracts/test-contracts/test-plugin/PluginTest.sol +++ b/darc-protocol/contracts/test-contracts/TestBaseContract.sol @@ -1,15 +1,58 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; -import "../../protocol/Runtime/Executable/Executable.sol"; -import "../../protocol/Opcodes.sol"; +import "../protocol/Runtime/Executable/Executable.sol"; +import "../protocol/Opcodes.sol"; +import "../protocol/Dashboard/Dashboard.sol"; /** * @title The test contract of PluginJudgement * @author DARC Team * @notice Only used for testing */ -contract PluginTest is Executable { +contract TestBaseContract is Executable, Dashboard { - function test() public { + function runProgramDirectly(Program memory currentProgram, bool bIsSandbox) public { + executeProgram_Executable(currentProgram, bIsSandbox); + } + + function testExecute(Program memory currentProgram) public { + DARClogs.push("before execute"); + execute(currentProgram); + DARClogs.push("after execute"); + } + + function testCloneStateToSandbox() public { + cloneStateToSandbox(); + } + + function addVotingRule(VotingRule memory votingRule, bool bIsSandbox) public { + if (bIsSandbox) { + sandboxMachineState.votingRuleList.push(votingRule); + } else { + currentMachineState.votingRuleList.push(votingRule); + } + } + + function helper_createToken0AndMint() public { + // create a token class first + currentMachineState.tokenList[0].bIsInitialized = true; + currentMachineState.tokenList[0].tokenInfo = "token_0"; + currentMachineState.tokenList[0].votingWeight = 1; + currentMachineState.tokenList[0].dividendWeight = 1; + currentMachineState.tokenList[0].totalSupply = 1000; + + // mint 600 tokens to "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; + currentMachineState.tokenList[0].tokenBalance[0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266] = 600; + + // mint 200 tokens to '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' + currentMachineState.tokenList[0].tokenBalance[0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC] = 200; + + // mint 200 tokens to '0x90F79bf6EB2c4f870365E785982E1f101E93b906' + currentMachineState.tokenList[0].tokenBalance[0x90F79bf6EB2c4f870365E785982E1f101E93b906] = 200; + + // add addresses to owner list + currentMachineState.tokenList[0].ownerList.push(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + currentMachineState.tokenList[0].ownerList.push(0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC); + currentMachineState.tokenList[0].ownerList.push(0x90F79bf6EB2c4f870365E785982E1f101E93b906); } /** diff --git a/darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol b/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol.old similarity index 80% rename from darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol rename to darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol.old index a153709..77121f1 100644 --- a/darc-protocol/contracts/test-contracts/test-voting/VotingTest_SingleTest.sol +++ b/darc-protocol/contracts/test-contracts/test-voting/VotingTest.sol.old @@ -10,11 +10,21 @@ import '../../protocol/Runtime/Runtime.sol'; * @notice null */ -contract VotingTest_SingleTest is Runtime { +contract VotingTest is Runtime { function initializeVotingTest() public { this.initialize(); } + function votingTest_RunProgramDirectly(Program memory program, bool bIsSandbox) public { + executeProgram_Executable(program,bIsSandbox); + } + + function votingTest_Execute(Program memory program) public { + execute(program); + } + + function votingTest_ + function runTest() public { // create a token class first @@ -100,7 +110,7 @@ contract VotingTest_SingleTest is Runtime { approvalThresholdPercentage: 50, // 50 percent to approve votingDurationInSeconds: 1000, // 1000 seconds to vote executionPendingDurationInSeconds: 1000, // 1000 seconds to execute - bIsEnable: true, + bIsEnabled: true, notes: "voting rule 0", bIsAbsoluteMajority: false })); @@ -169,5 +179,28 @@ contract VotingTest_SingleTest is Runtime { require(afterRuleIdxList.length == 1, "VotingTest SingleTest: The length of the rule index list should be 1"); require(afterRuleIdxList[0] == 0, "VotingTest SingleTest: The rule index should be 0"); + + + // run the program in entrance + execute(program); + + // make sure that voting item is initialized + VotingItem memory votingItem0 = votingItems[1]; + + // make sure that the voting item is initialized + require(votingItem0.votingStatus == VotingStatus.Ongoing, "VotingTest SingleTest: The voting item should be ongoing"); + + require(votingItem0.program.programOperatorAddress == 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, "VotingTest SingleTest: The program operator address should be 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); + + require(votingItem0.program.operations.length == 1, "VotingTest SingleTest: The length of the operations should be 1"); + + require(votingItem0.program.operations[0].opcode == EnumOpcode.BATCH_MINT_TOKENS, "VotingTest SingleTest: The opcode of the operation should be BATCH_MINT_TOKENS"); + + // make sure that the voting rule indices only contains 0 + require(votingItem0.votingRuleIndices.length == 1, "VotingTest SingleTest: The length of the voting rule indices should be 1"); + + require(votingItem0.votingRuleIndices[0] == 0, "VotingTest SingleTest: The voting rule index should be 0"); + return; + require(votingItem0.totalPower[0] == 1000); } } \ No newline at end of file diff --git a/darc-protocol/contracts/test-contracts/test-voting/VotingTestBase.sol b/darc-protocol/contracts/test-contracts/test-voting/VotingTestBase.sol new file mode 100644 index 0000000..4826430 --- /dev/null +++ b/darc-protocol/contracts/test-contracts/test-voting/VotingTestBase.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.19; +import '../../protocol/Runtime/VotingMachine/VotingMachine.sol'; +import '../../protocol/Runtime/Runtime.sol'; + + +/** + * @title The single voting test contract of the voting machine + * @author DARC Team + * @notice null + */ + +contract VotingTestBase is VotingMachine { + function initializeVotingTest() public { + initialize(); + + // add a voting rule + currentMachineState.votingRuleList.push(VotingRule({ + votingTokenClassList: new uint256[](1), + approvalThresholdPercentage: 50, + votingDurationInSeconds: 100000, + executionPendingDurationInSeconds: 100000, + bIsEnabled: true, + notes: "test voting rule", + bIsAbsoluteMajority: true + })); + + currentMachineState.votingRuleList[0].votingTokenClassList[0] = 0; + + // add token class 0 + currentMachineState.tokenList[0].bIsInitialized = true; + currentMachineState.tokenList[0].tokenBalance[0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266] = 600; + currentMachineState.tokenList[0].tokenBalance[0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC] = 400; + currentMachineState.tokenList[0].totalSupply = 1000; + currentMachineState.tokenList[0].votingWeight = 1; + currentMachineState.tokenList[0].dividendWeight = 1; + currentMachineState.tokenList[0].ownerList.push(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + currentMachineState.tokenList[0].ownerList.push(0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC); + + currentMachineState.tokenList[0].tokenInfo = "token_0"; + uint256[] memory tokenClassIndexList = new uint256[](1); + tokenClassIndexList[0] = 0; + initializeVoting(tokenClassIndexList, Program({ + programOperatorAddress: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, + operations: new Operation[](0), + notes: "test voting" + } + )); + } +} \ No newline at end of file diff --git a/darc-protocol/hardhat.config.ts b/darc-protocol/hardhat.config.ts index 2213265..3032847 100644 --- a/darc-protocol/hardhat.config.ts +++ b/darc-protocol/hardhat.config.ts @@ -8,7 +8,7 @@ const config: HardhatUserConfig = { viaIR: true, optimizer: { enabled: true, - runs: 1000, + runs: 10, details: { yul: true, } diff --git a/darc-protocol/test/pluginTest/operator_AND_test.ts b/darc-protocol/test/pluginTest/operator_AND_test.ts index c0eab91..3145623 100644 --- a/darc-protocol/test/pluginTest/operator_AND_test.ts +++ b/darc-protocol/test/pluginTest/operator_AND_test.ts @@ -18,7 +18,7 @@ describe("operator AND test", function () { it ("should pass operator OR test", async function () { - const PluginTestFactory = await ethers.getContractFactory("PluginTest"); + const PluginTestFactory = await ethers.getContractFactory("TestBaseContract"); const pluginTest = await PluginTestFactory.deploy(); await pluginTest.deployed(); await pluginTest.initialize(); diff --git a/darc-protocol/test/pluginTest/operator_OR_test.ts b/darc-protocol/test/pluginTest/operator_OR_test.ts index 4d17974..659bd2c 100644 --- a/darc-protocol/test/pluginTest/operator_OR_test.ts +++ b/darc-protocol/test/pluginTest/operator_OR_test.ts @@ -18,7 +18,7 @@ describe("operator OR test", function () { it ("should pass operator OR test", async function () { - const PluginTestFactory = await ethers.getContractFactory("PluginTest"); + const PluginTestFactory = await ethers.getContractFactory("TestBaseContract"); const pluginTest = await PluginTestFactory.deploy(); await pluginTest.deployed(); await pluginTest.initialize(); diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index 554d96a..a1bf54d 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -6,7 +6,7 @@ import { BigNumber } from "ethers"; import { ConditionNodeStruct } from "../../typechain-types/contracts/protocol/DARC" -const programOperatorAddress = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; +const target0 = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; @@ -16,11 +16,154 @@ const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; describe.only("single voting test", function () { it ("should pass single voting test", async function () { - const VotingTestSingleTestFactory = await ethers.getContractFactory("VotingTest_SingleTest"); - const votingTestSingleTest = await VotingTestSingleTestFactory.deploy(); + + // const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); + + // const votingTestBase = await VotingTestBaseFactory.deploy(); + // await votingTestBase.deployed(); + + // await votingTestBase.initializeVotingTest(); + + // //get + // console.log(await votingTestBase.finiteState()); + + // console.log(await votingTestBase.votingDeadline()); + // console.log("Voting Item 0"); + // console.log(await votingTestBase.votingItems(0)); + // console.log("Voting Item 1"); + // console.log(await votingTestBase.votingItems(1)); + + + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); await votingTestSingleTest.deployed(); - await votingTestSingleTest.initializeVotingTest(); - await votingTestSingleTest.runTest(); + await votingTestSingleTest.initialize(); + + // create tokens, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // add a voting rule that ask 50% to approve in relative majority + await votingTestSingleTest.addVotingRule({ + votingTokenClassList: [BigNumber.from(0)], + approvalThresholdPercentage: BigNumber.from(50), + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in relative majority", + bIsAbsoluteMajority: false, + }, true); + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //return; + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0), BigNumber.from(0), BigNumber.from(0), BigInt(0), BigInt(0)], + [BigNumber.from(100), BigNumber.from(200), BigNumber.from(300), BigInt(16), BigInt(5)], // amount = 100 + ], + ADDRESS_2DARRAY: [ + [target0, target1, target1, target1, target0], + ], + BYTES: [] + } + }], + }; + + const result1 = await votingTestSingleTest.checkProgram_afterOp(program); + const result2 = await votingTestSingleTest.checkProgram_beforeOp(program); + console.log("result1: ", result1); + console.log("result2: ", result2); + + // check after-op result + const result = await votingTestSingleTest.checkProgram_afterOp(program); + await votingTestSingleTest.testCloneStateToSandbox(); + + // get balance of target0 at level 0 + const balance0 = await votingTestSingleTest.getTokenOwnerBalance(0,target0); + + console.log("balance0: ", balance0.toString()); + + const result3 = await votingTestSingleTest.checkProgram_afterOp(program); + console.log("result3: ", result3); + + // now run in sandbox + await votingTestSingleTest.runProgramDirectly(program, true); + + const result4 = await votingTestSingleTest.checkProgram_afterOp(program); + console.log("result4: ", result4); + + + //await votingTestSingleTest.testExecute(program); + try{ + await votingTestSingleTest.testExecute(program); + } + catch(e){ + console.log("error: ", e); + } + const result5 = await votingTestSingleTest.checkProgram_afterOp(program); + console.log("result5: ", result5); + + // console.log("votingTest.address: ", votingTestSingleTest.address); + console.log("Logs: "); + console.log(await votingTestSingleTest.getDARClogs()); }); -}) \ No newline at end of file +}) From 0a0d74592e3671f77bdee15f64ee097bedee1413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sat, 13 Jan 2024 16:25:00 -0600 Subject: [PATCH 04/11] update --- .../protocol/Dashboard/Dashboard.sol | 15 ++++- .../protocol/MachineStateManager.sol | 2 + .../Runtime/Executable/Executable.sol | 2 +- .../Instructions/UtilityInstructions.sol | 2 +- .../contracts/protocol/Runtime/Runtime.sol | 13 ++-- .../Runtime/VotingMachine/VotingMachine.sol | 24 ++++--- .../test-contracts/TestBaseContract.sol | 9 ++- darc-protocol/hardhat.config.ts | 6 +- .../test/votingTest/singleVotingTest.ts | 62 +++++++++++++------ 9 files changed, 93 insertions(+), 42 deletions(-) diff --git a/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol b/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol index a07bff2..f98da34 100644 --- a/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol +++ b/darc-protocol/contracts/protocol/Dashboard/Dashboard.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; -import "../Runtime/Runtime.sol"; +import "../Runtime/Executable/Executable.sol"; /** * @title DARC Dashboard @@ -10,7 +10,7 @@ import "../Runtime/Runtime.sol"; * which is used to read the machine state, including machine state, voting state, * token information, plugin information, etc. */ -contract Dashboard is MachineStateManager { +contract Dashboard is Executable { /** * @notice The getter function of the machine state plugins @@ -138,4 +138,15 @@ contract Dashboard is MachineStateManager { function getDARClogs() public view returns (string[] memory) { return DARClogs; } + + /** + * Get all voting items + */ + function getVotingItemsByIndex(uint256 idx) public view returns (VotingItem memory) { + return votingItems[idx]; + } + + function getLatestVotingItemIndex() public view returns (uint256) { + return latestVotingItemIndex; + } } diff --git a/darc-protocol/contracts/protocol/MachineStateManager.sol b/darc-protocol/contracts/protocol/MachineStateManager.sol index bee4cd2..e4cb9ac 100644 --- a/darc-protocol/contracts/protocol/MachineStateManager.sol +++ b/darc-protocol/contracts/protocol/MachineStateManager.sol @@ -20,6 +20,8 @@ enum FiniteState { EXECUTING_PENDING } + + /** * @notice The core and base contract of DARC, the manager of the DARC machine state * Also this contains some basic function of the DARC machine state diff --git a/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol b/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol index 9edaa12..d632ebf 100644 --- a/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol +++ b/darc-protocol/contracts/protocol/Runtime/Executable/Executable.sol @@ -47,7 +47,7 @@ contract Executable is MachineStateManager, PluginSystem, VotingMachine, Instruc executeProgram_Executable(currentProgram, true); // 2.2 check if the program can pass the after operation plugins in current machine state (EnumReturnType afterReturnType, uint256[] memory afterRuleIdxList) = checkAfterOperationPlugins(currentProgram); - + //return; // checkpoint 2 all right // 2.3 If the program is invalid, revert the transaction if (afterReturnType == EnumReturnType.NO) { revert("The program is denied by the plugin system"); diff --git a/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol b/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol index 10d0374..434fada 100644 --- a/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol +++ b/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol @@ -127,7 +127,7 @@ contract UtilityInstructions is MachineStateManager { } function op_EXECUTE_PROGRAM(Operation memory operation, bool bIsSandbox) internal { - + } function op_END_EMERGENCY(Operation memory operation, bool bIsSandbox) internal { diff --git a/darc-protocol/contracts/protocol/Runtime/Runtime.sol b/darc-protocol/contracts/protocol/Runtime/Runtime.sol index 3940717..f414216 100644 --- a/darc-protocol/contracts/protocol/Runtime/Runtime.sol +++ b/darc-protocol/contracts/protocol/Runtime/Runtime.sol @@ -162,15 +162,18 @@ contract Runtime is Executable, PaymentCheck{ //1. check if the program is valid require(validateVoteProgram(program), "Invalid vote program"); - //2. execute the program - execute(program); + + //2. execute the program (vote) directly, do not use execute() function + // execute(program); // do not use execute() function + vote(program.operations[0].operatorAddress, program.operations[0].param.BOOL_ARRAY); } function executePendingProgram(Program memory program) internal { //1. check if the program is valid - require(validateProgram(program), "Invalid program"); + require(validateExecutePendingProgram(program), "Invalid program"); + + //2. execute the program(executing pending) directly, do not use execute() function + executeProgram_Executable(votingItems[latestVotingItemIndex].program, false); - //2. execute the program - execute(program); } } \ No newline at end of file diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index 3ca2607..c582723 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -14,7 +14,7 @@ enum VotingStatus { struct VotingItem { /** - * @notice The program that is being voted on + * @notice The program that is being voted on */ Program program; @@ -123,16 +123,24 @@ contract VotingMachine is MachineStateManager { function initializeVotingItem(uint256[] memory votingRuleIndices, Program memory currentProgram) private { // calculate the shortest voting duration uint256 minVotingDuration = minVotingDurationInSeconds(votingRuleIndices); + uint256 minExecutingDuration = minExecutePendingProgramDurationInSeconds(votingRuleIndices); bool bIsValid = false; - + // initialize the voting item - votingItems[latestVotingItemIndex].program = currentProgram; - votingItems[latestVotingItemIndex].powerNo = new uint256[](votingRuleIndices.length); - votingItems[latestVotingItemIndex].powerYes = new uint256[](votingRuleIndices.length); - votingItems[latestVotingItemIndex].totalPower = new uint256[](votingRuleIndices.length); - return; // checkpoint 2 + votingItems[latestVotingItemIndex] = VotingItem({ + program: currentProgram, + votingRuleIndices: new uint256[](votingRuleIndices.length), + powerYes: new uint256[](votingRuleIndices.length), + powerNo: new uint256[](votingRuleIndices.length), + totalPower: new uint256[](votingRuleIndices.length), + votingEndTime: 0, + executingEndTime: 0, + votingStatus: VotingStatus.Ongoing, + bIsProgramExecuted: false + }); + uint256 resultVotingDeadline = 0; (bIsValid, resultVotingDeadline) = SafeMathUpgradeable.tryAdd( block.timestamp, @@ -160,8 +168,6 @@ contract VotingMachine is MachineStateManager { // update the voting status in the voting items map votingItems[latestVotingItemIndex].votingStatus = VotingStatus.Ongoing; - votingItems[latestVotingItemIndex].votingRuleIndices = new uint256[](votingRuleIndices.length); - // return; // checkpoint 1 failed for (uint256 i = 0; i < votingRuleIndices.length; i++) { votingItems[latestVotingItemIndex].votingRuleIndices[i] = votingRuleIndices[i]; } diff --git a/darc-protocol/contracts/test-contracts/TestBaseContract.sol b/darc-protocol/contracts/test-contracts/TestBaseContract.sol index d9c3161..149c8ff 100644 --- a/darc-protocol/contracts/test-contracts/TestBaseContract.sol +++ b/darc-protocol/contracts/test-contracts/TestBaseContract.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.19; import "../protocol/Runtime/Executable/Executable.sol"; +import "../protocol/Runtime/Runtime.sol"; import "../protocol/Opcodes.sol"; import "../protocol/Dashboard/Dashboard.sol"; /** @@ -8,16 +9,18 @@ import "../protocol/Dashboard/Dashboard.sol"; * @author DARC Team * @notice Only used for testing */ -contract TestBaseContract is Executable, Dashboard { +contract TestBaseContract is Runtime, Dashboard { function runProgramDirectly(Program memory currentProgram, bool bIsSandbox) public { executeProgram_Executable(currentProgram, bIsSandbox); } function testExecute(Program memory currentProgram) public { - DARClogs.push("before execute"); execute(currentProgram); - DARClogs.push("after execute"); + } + + function testRuntimeEntrance(Program memory currentProgram) public { + runtimeEntrance(currentProgram); } function testCloneStateToSandbox() public { diff --git a/darc-protocol/hardhat.config.ts b/darc-protocol/hardhat.config.ts index 3032847..5170cc4 100644 --- a/darc-protocol/hardhat.config.ts +++ b/darc-protocol/hardhat.config.ts @@ -9,9 +9,9 @@ const config: HardhatUserConfig = { optimizer: { enabled: true, runs: 10, - details: { - yul: true, - } + // details: { + // yul: true, + // } }, }, }, diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index a1bf54d..d7573da 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -3,7 +3,7 @@ import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; import { expect } from "chai"; import { ethers } from "hardhat"; import { BigNumber } from "ethers"; -import { ConditionNodeStruct } from "../../typechain-types/contracts/protocol/DARC" +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" const target0 = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; @@ -17,13 +17,14 @@ const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; describe.only("single voting test", function () { it ("should pass single voting test", async function () { - // const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); + const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); - // const votingTestBase = await VotingTestBaseFactory.deploy(); - // await votingTestBase.deployed(); - - // await votingTestBase.initializeVotingTest(); + const votingTestBase = await VotingTestBaseFactory.deploy(); + await votingTestBase.deployed(); + await votingTestBase.initializeVotingTest(); + //return; + //return; // //get // console.log(await votingTestBase.finiteState()); @@ -75,7 +76,7 @@ describe.only("single voting test", function () { bIsEnabled: true, notes: "50% to approve in relative majority", bIsAbsoluteMajority: false, - }, true); + }, false); //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 @@ -143,7 +144,7 @@ describe.only("single voting test", function () { const result3 = await votingTestSingleTest.checkProgram_afterOp(program); console.log("result3: ", result3); - + // now run in sandbox await votingTestSingleTest.runProgramDirectly(program, true); @@ -152,18 +153,43 @@ describe.only("single voting test", function () { //await votingTestSingleTest.testExecute(program); - try{ - await votingTestSingleTest.testExecute(program); - } - catch(e){ - console.log("error: ", e); - } + + // run a program, this will trigger a voting + await votingTestSingleTest.testExecute(program); + const result5 = await votingTestSingleTest.checkProgram_afterOp(program); console.log("result5: ", result5); - // - console.log("votingTest.address: ", votingTestSingleTest.address); - console.log("Logs: "); - console.log(await votingTestSingleTest.getDARClogs()); + // try to vote now + const program_vote: ProgramStruct = { + programOperatorAddress: target0, + notes: "vote", + operations: [{ + operatorAddress: target0, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [true], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + // read the voting result + const votingItemIndex = 1; + const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + console.log(votingItem); + + // vote + await votingTestSingleTest.testRuntimeEntrance(program_vote); + + // read the voting result again + const votingItem2 = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + console.log(votingItem2); }); }) From e218e15e76aa39e1ba6adcee72ac5d87e4d34100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sun, 14 Jan 2024 04:10:01 -0600 Subject: [PATCH 05/11] update --- .../contracts/protocol/Runtime/Runtime.sol | 25 +++++-- .../Runtime/VotingMachine/VotingMachine.sol | 71 +++++++++++++++++-- .../test/votingTest/singleVotingTest.ts | 16 +++-- 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/darc-protocol/contracts/protocol/Runtime/Runtime.sol b/darc-protocol/contracts/protocol/Runtime/Runtime.sol index f414216..9fc6319 100644 --- a/darc-protocol/contracts/protocol/Runtime/Runtime.sol +++ b/darc-protocol/contracts/protocol/Runtime/Runtime.sol @@ -94,13 +94,26 @@ contract Runtime is Executable, PaymentCheck{ } // If the current state is voting but has reached the voting deadline, - // terminate the voting and change to the execution pending state.abi + // try to end the voting and determine the next state else if (finiteState == FiniteState.VOTING && block.timestamp >= votingDeadline && block.timestamp < executingPendingDeadline) { - finiteState = FiniteState.EXECUTING_PENDING; - executeProgram(program); - return "The program is executed."; + + // check if the voting is passed or rejected + tryEndVotingAfterVotingDeadline(); + + // if the current state is IDLE, just continue to execute the program + if (finiteState == FiniteState.IDLE) { + require(validateProgram(program), "The voting is rejected and now DARC is in idle state. The input program is not a valid program."); + executeProgram(program); + return "The program is executed."; + } + + else if (finiteState == FiniteState.EXECUTING_PENDING) { + require(validateExecutePendingProgram(program), "The voting is passed and now DARC is in executing pending state. The input program is not a valid EXECUTE_PROGRAM to execute the pending program."); + executePendingProgram(program); + return "The pending program is executed after voting and approval."; + } } // If the current state is execution pending or voting but has reached the execution pending deadline, @@ -108,7 +121,7 @@ contract Runtime is Executable, PaymentCheck{ else if ( (finiteState == FiniteState.EXECUTING_PENDING || finiteState == FiniteState.VOTING) && block.timestamp >= executingPendingDeadline) { finiteState = FiniteState.IDLE; - require(validateExecutePendingProgram(program), "[Error 003]The program is not a valid execute pending program."); + require(validateProgram(program), "[Error 003]The program is not a valid program."); executeProgram(program); return "The program is executed."; } @@ -119,7 +132,7 @@ contract Runtime is Executable, PaymentCheck{ /** * @notice Check if current program is a valid program */ - function validateProgram(Program memory program) internal view returns (bool) { + function validateProgram(Program memory program) internal pure returns (bool) { //1. check if the program is empty if (program.operations.length == 0) { return false; } diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index c582723..d1a1a42 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -220,7 +220,8 @@ contract VotingMachine is MachineStateManager { } } - + // after the vote, check if the voting period has ended + tryEndVotingBeforeVotingDeadline(); } /** @@ -260,12 +261,70 @@ contract VotingMachine is MachineStateManager { } /** - * @notice Try to end the voting period if the voting period has ended - * If vote is not passed after the voting deadline, change the finite state to IDLE; - * Otherwise, change the finite state to PENDING, and wait for the executing the pending program + * @notice Try to end the voting period if the voting period has not ended. + * This function is called before the voting deadline expires and every time a vote is casted. + * 1. If any abosolute majority vote is rejected, terminate the voting process + * before the voting deadline and change the finite state to IDLE; + * 2. If all voting rules are absolute majority and passed, change the finite state to EXECUTING_PENDING; */ - function endVoting() private { - require(block.timestamp >= currentVotingEndTime, "voting period has not ended"); + function tryEndVotingBeforeVotingDeadline() internal { + // 1. go through all voting rules, check if any voting rule is absolute majority and rejected + for (uint256 i = 0; i < votingItems[latestVotingItemIndex].votingRuleIndices.length; i++) { + if (currentMachineState.votingRuleList[votingItems[latestVotingItemIndex].votingRuleIndices[i]].bIsAbsoluteMajority) { + // get the approval threshold percentage + uint256 threshold = currentMachineState.votingRuleList[votingItems[latestVotingItemIndex].votingRuleIndices[i]].approvalThresholdPercentage; + + // calculate the total voting power + uint256 totalVotingPower = votingItems[latestVotingItemIndex].totalPower[i]; + + // get the current no votes + uint256 currentNo = votingItems[latestVotingItemIndex].powerNo[i]; + + // determine if the voting rule is rejected + bool bIsRejected = false; + if (currentNo * 100 > totalVotingPower * threshold) { + bIsRejected = true; + } + + // if the voting rule is rejected, terminate the voting process and change the finite state to IDLE + if (bIsRejected) { + votingItems[latestVotingItemIndex].votingStatus = VotingStatus.Ended_AND_Failed; + finiteState = FiniteState.IDLE; + votingDeadline = 0; + executingPendingDeadline = 0; + return; + } + } + } + + // 2. go through all voting rules, check if all voting rules are absolute majority and passed + bool[] memory bIsPassed = new bool[](votingItems[latestVotingItemIndex].votingRuleIndices.length); + for (uint256 i = 0; i < votingItems[latestVotingItemIndex].votingRuleIndices.length; i++) { + + // if any of the voting is not absolute majority, just finish the function and return + if (!currentMachineState.votingRuleList[votingItems[latestVotingItemIndex].votingRuleIndices[i]].bIsAbsoluteMajority) { + return; + } + + // now the i-th vote must be absolute majority. + // if any of the voting is not passed, just finish the function and return + if (checkVotingResult(i) != VotingStatus.Ended_AND_Passed) { + return; + } + } + + // 3. OK, now all the voting rules are absolute majority and passed, change the finite state to EXECUTING_PENDING + finiteState = FiniteState.EXECUTING_PENDING; + } + + /** + * @notice Try to check the final voting results and end the voting period if the voting period has ended + * This function is called after the voting deadline expires. + * 1. If any vote is not passed after the voting deadline, change the finite state to IDLE; + * 2. If all votes are passed after the voting deadline, change the finite state to EXECUTING_PENDING; + */ + function tryEndVotingAfterVotingDeadline() internal { + VotingStatus[] memory result = checkVotingResults(); for (uint256 i = 0; i < result.length; i++) { diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index d7573da..fe01124 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -17,12 +17,12 @@ const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; describe.only("single voting test", function () { it ("should pass single voting test", async function () { - const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); + // const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); - const votingTestBase = await VotingTestBaseFactory.deploy(); - await votingTestBase.deployed(); + // const votingTestBase = await VotingTestBaseFactory.deploy(); + // await votingTestBase.deployed(); - await votingTestBase.initializeVotingTest(); + // await votingTestBase.initializeVotingTest(); //return; //return; // //get @@ -155,7 +155,7 @@ describe.only("single voting test", function () { //await votingTestSingleTest.testExecute(program); // run a program, this will trigger a voting - await votingTestSingleTest.testExecute(program); + await votingTestSingleTest.testRuntimeEntrance(program); const result5 = await votingTestSingleTest.checkProgram_afterOp(program); console.log("result5: ", result5); @@ -185,9 +185,13 @@ describe.only("single voting test", function () { const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); console.log(votingItem); + // read the voting state: + const votingState = await votingTestSingleTest.finiteState(); + console.log(votingState); + console.log(await votingTestSingleTest.votingDeadline()); // vote await votingTestSingleTest.testRuntimeEntrance(program_vote); - + return; // read the voting result again const votingItem2 = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); console.log(votingItem2); From fa018288fe747f83e8956357683c06440600c3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sun, 14 Jan 2024 05:03:22 -0600 Subject: [PATCH 06/11] update --- .../Runtime/VotingMachine/VotingMachine.sol | 4 +- .../test/votingTest/singleVotingTest.ts | 68 ++++++++++++------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index d1a1a42..b522a69 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -355,7 +355,7 @@ contract VotingMachine is MachineStateManager { * @param votingRuleIndices the voting rule indices */ function minVotingDurationInSeconds(uint256[] memory votingRuleIndices) private view returns (uint256){ - uint256 minDuration = 0; + uint256 minDuration = 2**256 - 1; // the largest uint256 for (uint256 i = 0; i < votingRuleIndices.length; i++) { if ( currentMachineState.votingRuleList[votingRuleIndices[i]].votingDurationInSeconds < minDuration) { minDuration = currentMachineState.votingRuleList[votingRuleIndices[i]].votingDurationInSeconds; @@ -369,7 +369,7 @@ contract VotingMachine is MachineStateManager { * @param votingRuleIndices the voting rule indices */ function minExecutePendingProgramDurationInSeconds(uint256[] memory votingRuleIndices) private view returns (uint256){ - uint256 minDuration = 0; + uint256 minDuration = 2**256 - 1; // the largest uint256 for (uint256 i = 0; i < votingRuleIndices.length; i++) { if (currentMachineState.votingRuleList[votingRuleIndices[i]].executionPendingDurationInSeconds < minDuration) { minDuration = currentMachineState.votingRuleList[votingRuleIndices[i]].executionPendingDurationInSeconds; diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index fe01124..dc40bdb 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -6,7 +6,7 @@ import { BigNumber } from "ethers"; import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" -const target0 = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"; +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; @@ -71,8 +71,8 @@ describe.only("single voting test", function () { await votingTestSingleTest.addVotingRule({ votingTokenClassList: [BigNumber.from(0)], approvalThresholdPercentage: BigNumber.from(50), - votingDurationInSeconds: BigNumber.from(1000), - executionPendingDurationInSeconds: BigNumber.from(1000), + votingDurationInSeconds: BigNumber.from(100000), + executionPendingDurationInSeconds: BigNumber.from(100000), bIsEnabled: true, notes: "50% to approve in relative majority", bIsAbsoluteMajority: false, @@ -128,28 +128,28 @@ describe.only("single voting test", function () { }], }; - const result1 = await votingTestSingleTest.checkProgram_afterOp(program); - const result2 = await votingTestSingleTest.checkProgram_beforeOp(program); - console.log("result1: ", result1); - console.log("result2: ", result2); - - // check after-op result - const result = await votingTestSingleTest.checkProgram_afterOp(program); - await votingTestSingleTest.testCloneStateToSandbox(); + // const result1 = await votingTestSingleTest.checkProgram_afterOp(program); + // const result2 = await votingTestSingleTest.checkProgram_beforeOp(program); + // console.log("result1: ", result1); + // console.log("result2: ", result2); + + // // check after-op result + // const result = await votingTestSingleTest.checkProgram_afterOp(program); + // await votingTestSingleTest.testCloneStateToSandbox(); - // get balance of target0 at level 0 - const balance0 = await votingTestSingleTest.getTokenOwnerBalance(0,target0); + // // get balance of target0 at level 0 + // const balance0 = await votingTestSingleTest.getTokenOwnerBalance(0,target0); - console.log("balance0: ", balance0.toString()); + // console.log("balance0: ", balance0.toString()); - const result3 = await votingTestSingleTest.checkProgram_afterOp(program); - console.log("result3: ", result3); + // const result3 = await votingTestSingleTest.checkProgram_afterOp(program); + // console.log("result3: ", result3); - // now run in sandbox - await votingTestSingleTest.runProgramDirectly(program, true); + // // now run in sandbox + // await votingTestSingleTest.runProgramDirectly(program, true); - const result4 = await votingTestSingleTest.checkProgram_afterOp(program); - console.log("result4: ", result4); + // const result4 = await votingTestSingleTest.checkProgram_afterOp(program); + // console.log("result4: ", result4); //await votingTestSingleTest.testExecute(program); @@ -157,8 +157,8 @@ describe.only("single voting test", function () { // run a program, this will trigger a voting await votingTestSingleTest.testRuntimeEntrance(program); - const result5 = await votingTestSingleTest.checkProgram_afterOp(program); - console.log("result5: ", result5); + // const result5 = await votingTestSingleTest.checkProgram_afterOp(program); + // console.log("result5: ", result5); // try to vote now const program_vote: ProgramStruct = { @@ -179,18 +179,34 @@ describe.only("single voting test", function () { } }], } - + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); // read the voting result - const votingItemIndex = 1; + const votingItemIndex = 2; const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); console.log(votingItem); // read the voting state: const votingState = await votingTestSingleTest.finiteState(); - console.log(votingState); + console.log("The voting state is ", votingState); console.log(await votingTestSingleTest.votingDeadline()); // vote - await votingTestSingleTest.testRuntimeEntrance(program_vote); + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + await votingTestSingleTest.testRuntimeEntrance(program_vote).then(async (tx) => { + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + // console log the voting state again + console.log("After vote, the voting state is ", await votingTestSingleTest.finiteState()); + console.log("After vote, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + console.log(await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)); + }); + + // console log the voting state again + console.log("After vote, the voting state is ", await votingTestSingleTest.finiteState()); + console.log("After vote, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + console.log("Voting Item 1") + console.log(await votingTestSingleTest.getVotingItemsByIndex(1)); + console.log("Voting Item 2") + console.log(await votingTestSingleTest.getVotingItemsByIndex(2)); + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); return; // read the voting result again const votingItem2 = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); From 627c4e508d16923994a7607a7ce7e812f89d853a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sun, 14 Jan 2024 06:04:17 -0600 Subject: [PATCH 07/11] update --- darc-docs/docs/DARC Protocol/OpCodes.md | 2 +- darc-protocol/contracts/protocol/Opcodes.sol | 2 +- .../Runtime/Executable/InstructionMachine.sol | 4 +- .../Instructions/UtilityInstructions.sol | 2 +- .../ProgramValidator/ProgramValidator.sol | 6 +- .../contracts/protocol/Runtime/Runtime.sol | 2 +- .../Runtime/VotingMachine/VotingMachine.sol | 9 +- .../protocol/Utilities/OpcodeMap.sol | 4 +- .../test/votingTest/singleVotingTest.ts | 113 ++++++++---------- darc-specs/opcode.md | 2 +- 10 files changed, 61 insertions(+), 85 deletions(-) diff --git a/darc-docs/docs/DARC Protocol/OpCodes.md b/darc-docs/docs/DARC Protocol/OpCodes.md index 572d316..afa6ba9 100644 --- a/darc-docs/docs/DARC Protocol/OpCodes.md +++ b/darc-docs/docs/DARC Protocol/OpCodes.md @@ -53,7 +53,7 @@ By defining these operations through opcodes, the DARC protocol ensures consiste | BATCH_BURN_TOKENS_AND_REFUND | 30 | `tokenClassArray`: uint256[]
| Batch Burn Tokens and Refund | | ADD_STORAGE_IPFS_HASH | 31 | `address`: string[] | Add Storage IPFS Hash | | VOTE | 32 | `voteArray`: bool[] | Vote for a Voting Pending Program | -| EXECUTE_PROGRAM | 33 | N/A | Execute a Program that has been Voted and Approved | +| EXECUTE_PENDING_PROGRAM | 33 | N/A | Execute a Program that has been Voted and Approved | diff --git a/darc-protocol/contracts/protocol/Opcodes.sol b/darc-protocol/contracts/protocol/Opcodes.sol index 0c8c03f..99a9cc1 100644 --- a/darc-protocol/contracts/protocol/Opcodes.sol +++ b/darc-protocol/contracts/protocol/Opcodes.sol @@ -300,7 +300,7 @@ enum EnumOpcode { * @notice Execute a program that has been voted and approved * ID:33 */ - EXECUTE_PROGRAM, + EXECUTE_PENDING_PROGRAM, /** * @notice Emergency mode termination. Emergency agents cannot do anything after this operation diff --git a/darc-protocol/contracts/protocol/Runtime/Executable/InstructionMachine.sol b/darc-protocol/contracts/protocol/Runtime/Executable/InstructionMachine.sol index 9acc3cd..5d4641f 100644 --- a/darc-protocol/contracts/protocol/Runtime/Executable/InstructionMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/Executable/InstructionMachine.sol @@ -205,8 +205,8 @@ OfferDividendsInstructions } // opcode id == 33 - else if (operation.opcode == EnumOpcode.EXECUTE_PROGRAM) { - op_EXECUTE_PROGRAM(operation, bIsSandbox); + else if (operation.opcode == EnumOpcode.EXECUTE_PENDING_PROGRAM) { + op_EXECUTE_PENDING_PROGRAM(operation, bIsSandbox); } // opcode id == 34 diff --git a/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol b/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol index 434fada..190d5ca 100644 --- a/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol +++ b/darc-protocol/contracts/protocol/Runtime/Executable/Instructions/UtilityInstructions.sol @@ -126,7 +126,7 @@ contract UtilityInstructions is MachineStateManager { } - function op_EXECUTE_PROGRAM(Operation memory operation, bool bIsSandbox) internal { + function op_EXECUTE_PENDING_PROGRAM(Operation memory operation, bool bIsSandbox) internal { } diff --git a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol index 67f8355..b81944f 100644 --- a/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol +++ b/darc-protocol/contracts/protocol/Runtime/ProgramValidator/ProgramValidator.sol @@ -58,7 +58,7 @@ library ProgramValidator{ function validateExecutePendingProgram(Program memory currentProgram) internal pure returns (bool) { //1. check if the program is empty if (currentProgram.operations.length != 1) { return false; } - return validate_ID_33_EXECUTE_PROGRAM(currentProgram.operations[0]); + return validate_ID_33_EXECUTE_PENDING_PROGRAM(currentProgram.operations[0]); } @@ -72,8 +72,8 @@ library ProgramValidator{ return true; } - function validate_ID_33_EXECUTE_PROGRAM(Operation memory op) internal pure returns (bool) { - if (op.opcode != EnumOpcode.EXECUTE_PROGRAM) { return false; } + function validate_ID_33_EXECUTE_PENDING_PROGRAM(Operation memory op) internal pure returns (bool) { + if (op.opcode != EnumOpcode.EXECUTE_PENDING_PROGRAM) { return false; } if (op.param.UINT256_2DARRAY.length != 0) { return false; } if (op.param.ADDRESS_2DARRAY.length != 0) { return false; } if (op.param.STRING_ARRAY.length != 0) { return false; } diff --git a/darc-protocol/contracts/protocol/Runtime/Runtime.sol b/darc-protocol/contracts/protocol/Runtime/Runtime.sol index 9fc6319..356b12a 100644 --- a/darc-protocol/contracts/protocol/Runtime/Runtime.sol +++ b/darc-protocol/contracts/protocol/Runtime/Runtime.sol @@ -110,7 +110,7 @@ contract Runtime is Executable, PaymentCheck{ } else if (finiteState == FiniteState.EXECUTING_PENDING) { - require(validateExecutePendingProgram(program), "The voting is passed and now DARC is in executing pending state. The input program is not a valid EXECUTE_PROGRAM to execute the pending program."); + require(validateExecutePendingProgram(program), "The voting is passed and now DARC is in executing pending state. The input program is not a valid EXECUTE_PENDING_PROGRAM to execute the pending program."); executePendingProgram(program); return "The pending program is executed after voting and approval."; } diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index b522a69..91b2b5a 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -84,12 +84,6 @@ contract VotingMachine is MachineStateManager { */ mapping(address => mapping(uint256 => bool)) public voted; - - /** - * @notice the end time of the voting period - */ - uint256 public currentVotingEndTime; - /** * @notice the latest index of the voting item */ @@ -196,7 +190,6 @@ contract VotingMachine is MachineStateManager { */ function vote(address voter, bool[] memory votes) internal { require(isVotingProcesss(), "voting is not in progress"); - require(block.timestamp < currentVotingEndTime, "voting period has ended"); require(!voted[voter][latestVotingItemIndex], "voter has already voted"); require(votes.length == votingItems[latestVotingItemIndex].votingRuleIndices.length, "the number of votes does not match the number of policies"); @@ -298,7 +291,6 @@ contract VotingMachine is MachineStateManager { } // 2. go through all voting rules, check if all voting rules are absolute majority and passed - bool[] memory bIsPassed = new bool[](votingItems[latestVotingItemIndex].votingRuleIndices.length); for (uint256 i = 0; i < votingItems[latestVotingItemIndex].votingRuleIndices.length; i++) { // if any of the voting is not absolute majority, just finish the function and return @@ -315,6 +307,7 @@ contract VotingMachine is MachineStateManager { // 3. OK, now all the voting rules are absolute majority and passed, change the finite state to EXECUTING_PENDING finiteState = FiniteState.EXECUTING_PENDING; + votingItems[latestVotingItemIndex].votingStatus = VotingStatus.Ended_AND_Passed; } /** diff --git a/darc-protocol/contracts/protocol/Utilities/OpcodeMap.sol b/darc-protocol/contracts/protocol/Utilities/OpcodeMap.sol index ff34c68..003f258 100644 --- a/darc-protocol/contracts/protocol/Utilities/OpcodeMap.sol +++ b/darc-protocol/contracts/protocol/Utilities/OpcodeMap.sol @@ -47,7 +47,7 @@ library OpcodeMap{ if (opcode == EnumOpcode.BATCH_BURN_TOKENS_AND_REFUND) return 30; if (opcode == EnumOpcode.ADD_STORAGE_IPFS_HASH) return 31; if (opcode == EnumOpcode.VOTE) return 32; - if (opcode == EnumOpcode.EXECUTE_PROGRAM) return 33; + if (opcode == EnumOpcode.EXECUTE_PENDING_PROGRAM) return 33; if (opcode == EnumOpcode.END_EMERGENCY) return 34; if (opcode == EnumOpcode.UPGRADE_TO_ADDRESS) return 35; if (opcode == EnumOpcode.CONFIRM_UPGRAED_FROM_ADDRESS) return 36; @@ -93,7 +93,7 @@ library OpcodeMap{ if (opcode == EnumOpcode.BATCH_BURN_TOKENS_AND_REFUND) return "BATCH_BURN_TOKENS_AND_REFUND"; if (opcode == EnumOpcode.ADD_STORAGE_IPFS_HASH) return "ADD_STORAGE_IPFS_HASH"; if (opcode == EnumOpcode.VOTE) return "VOTE"; - if (opcode == EnumOpcode.EXECUTE_PROGRAM) return "EXECUTE_PROGRAM"; + if (opcode == EnumOpcode.EXECUTE_PENDING_PROGRAM) return "EXECUTE_PENDING_PROGRAM"; return "UNDEFINED"; } diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index dc40bdb..f29c7fb 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -16,25 +16,6 @@ const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; describe.only("single voting test", function () { it ("should pass single voting test", async function () { - - // const VotingTestBaseFactory = await ethers.getContractFactory("VotingTestBase"); - - // const votingTestBase = await VotingTestBaseFactory.deploy(); - // await votingTestBase.deployed(); - - // await votingTestBase.initializeVotingTest(); - //return; - //return; - // //get - // console.log(await votingTestBase.finiteState()); - - // console.log(await votingTestBase.votingDeadline()); - // console.log("Voting Item 0"); - // console.log(await votingTestBase.votingItems(0)); - // console.log("Voting Item 1"); - // console.log(await votingTestBase.votingItems(1)); - - const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); const votingTestSingleTest = await VotingTestFactory.deploy(); await votingTestSingleTest.deployed(); @@ -71,11 +52,11 @@ describe.only("single voting test", function () { await votingTestSingleTest.addVotingRule({ votingTokenClassList: [BigNumber.from(0)], approvalThresholdPercentage: BigNumber.from(50), - votingDurationInSeconds: BigNumber.from(100000), - executionPendingDurationInSeconds: BigNumber.from(100000), + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), bIsEnabled: true, notes: "50% to approve in relative majority", - bIsAbsoluteMajority: false, + bIsAbsoluteMajority: true, }, false); @@ -128,38 +109,8 @@ describe.only("single voting test", function () { }], }; - // const result1 = await votingTestSingleTest.checkProgram_afterOp(program); - // const result2 = await votingTestSingleTest.checkProgram_beforeOp(program); - // console.log("result1: ", result1); - // console.log("result2: ", result2); - - // // check after-op result - // const result = await votingTestSingleTest.checkProgram_afterOp(program); - // await votingTestSingleTest.testCloneStateToSandbox(); - - // // get balance of target0 at level 0 - // const balance0 = await votingTestSingleTest.getTokenOwnerBalance(0,target0); - - // console.log("balance0: ", balance0.toString()); - - // const result3 = await votingTestSingleTest.checkProgram_afterOp(program); - // console.log("result3: ", result3); - - // // now run in sandbox - // await votingTestSingleTest.runProgramDirectly(program, true); - - // const result4 = await votingTestSingleTest.checkProgram_afterOp(program); - // console.log("result4: ", result4); - - - //await votingTestSingleTest.testExecute(program); - - // run a program, this will trigger a voting await votingTestSingleTest.testRuntimeEntrance(program); - // const result5 = await votingTestSingleTest.checkProgram_afterOp(program); - // console.log("result5: ", result5); - // try to vote now const program_vote: ProgramStruct = { programOperatorAddress: target0, @@ -179,9 +130,10 @@ describe.only("single voting test", function () { } }], } + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); // read the voting result - const votingItemIndex = 2; + const votingItemIndex = 1; const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); console.log(votingItem); @@ -197,19 +149,50 @@ describe.only("single voting test", function () { console.log("After vote, the voting state is ", await votingTestSingleTest.finiteState()); console.log("After vote, the voting deadline is ", await votingTestSingleTest.votingDeadline()); console.log(await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)); + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + console.log("current time stamp is ", (await time.latest()).toString()); + + //return; + + const program_execute_pending_program: ProgramStruct = { + programOperatorAddress: target0, + notes: "execute_pending_program", + operations: [{ + operatorAddress: target0, + opcode: 33, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + // run the execute pending program + await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { + console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); + console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + console.log(await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)); + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + console.log("current time stamp is ", (await time.latest()).toString()); + + //print all token holders and tokens, making sure that the pending program of minting tokens is executed after voting + const owners = await votingTestSingleTest.getTokenOwners(0); + // for (let i = 0; i < owners.length; i++) { + // console.log("owner ", i, " is ", owners[i]); + // console.log("balance of owner ", i, " is ", await votingTestSingleTest.getTokenOwnerBalance(0, owners[i])); + // } + + expect((await votingTestSingleTest.getTokenOwnerBalance(0, target0)).toString()).to.equal("705"); + expect((await votingTestSingleTest.getTokenOwnerBalance(0, target1)).toString()).to.equal("716"); + expect((await votingTestSingleTest.getTokenOwnerBalance(0, target2)).toString()).to.equal("200"); + }); }); - // console log the voting state again - console.log("After vote, the voting state is ", await votingTestSingleTest.finiteState()); - console.log("After vote, the voting deadline is ", await votingTestSingleTest.votingDeadline()); - console.log("Voting Item 1") - console.log(await votingTestSingleTest.getVotingItemsByIndex(1)); - console.log("Voting Item 2") - console.log(await votingTestSingleTest.getVotingItemsByIndex(2)); - console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); - return; - // read the voting result again - const votingItem2 = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); - console.log(votingItem2); }); }) diff --git a/darc-specs/opcode.md b/darc-specs/opcode.md index cedc3b3..12a8a63 100644 --- a/darc-specs/opcode.md +++ b/darc-specs/opcode.md @@ -35,7 +35,7 @@ | 30 | BATCH_BURN_TOKENS_AND_REFUND | UINT256_2D[0] tokenClassArray, UINT256_2D[1] amountArray, UINT256_2D[2] priceArray | Batch Burn tokens and Refund Operation | | 31 | ADD_STORAGE_IPFS_HASH | STRING_ARRAY[0][0] IFPSHash | Add storage IPFS hash to the storage list permanently Operation | | 32 | VOTE | bool[] voteArray | Vote for a voting pending program Operation | -| 33 | EXECUTE_PROGRAM | | Execute a program that has been voted and approved Operation | +| 33 | EXECUTE_PENDING_PROGRAM | | Execute a program that has been voted and approved Operation | | 34 | END_EMERGENCY | | Emergency mode termination Operation | | 35 | UPGRADE_TO_ADDRESS | ADDRESS_2DARRAY[0][0] targetAddress | Upgrade the contract to a new contract address Operation | | 36 | CONFIRM_UPGRAED_FROM_ADDRESS | ADDRESS_2DARRAY[0][0] fromAddress | Accepting current DARCs to be upgraded from the old contract address Operation | From 5678e681de566b98a8f0f39baac1c9260d0264d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:33:11 -0600 Subject: [PATCH 08/11] update --- .../Plugin/Condition_PluginAndVoting.sol | 70 +++++++++---------- .../Plugin/Condition_Withdrawable.sol | 18 ++--- .../contracts/protocol/Runtime/Runtime.sol | 8 ++- .../test/votingTest/singleVotingTest.ts | 54 +++++++++----- 4 files changed, 89 insertions(+), 61 deletions(-) diff --git a/darc-protocol/contracts/protocol/Plugin/Condition_PluginAndVoting.sol b/darc-protocol/contracts/protocol/Plugin/Condition_PluginAndVoting.sol index 821aae6..3e97e1a 100644 --- a/darc-protocol/contracts/protocol/Plugin/Condition_PluginAndVoting.sol +++ b/darc-protocol/contracts/protocol/Plugin/Condition_PluginAndVoting.sol @@ -21,24 +21,24 @@ contract Condition_PluginAndVoting is MachineStateManager { * @param param The parameter list of the condition expression * @param id The id of the condition expression */ - function pluginAndVotingOpExpressionCheck(bool bIsBeforeOperation, Operation memory op, NodeParam memory param, uint256 id) internal view returns (bool) + function pluginAndVotingOpExpressionCheck(bool bIsBeforeOperation, Operation memory op, NodeParam memory param, uint256 id) internal pure returns (bool) { - if (id == 301) return ID_301_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 302) return ID_302_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 303) return ID_303_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 304) return ID_304_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 305) return ID_305_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 306) return ID_306_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 307) return ID_307_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 308) return ID_308_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(bIsBeforeOperation, op, param); - if (id == 309) return ID_309_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 310) return ID_310_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 311) return ID_311_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 312) return ID_312_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 313) return ID_313_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 314) return ID_314_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 315) return ID_315_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); - if (id == 316) return ID_316_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bIsBeforeOperation, op, param); + if (id == 301) return ID_301_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 302) return ID_302_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 303) return ID_303_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 304) return ID_304_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 305) return ID_305_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 306) return ID_306_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 307) return ID_307_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 308) return ID_308_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(op, param); + if (id == 309) return ID_309_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 310) return ID_310_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 311) return ID_311_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 312) return ID_312_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 313) return ID_313_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 314) return ID_314_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 315) return ID_315_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(op, param); + if (id == 316) return ID_316_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(op, param); if (id == 317) return ID_317_ARE_ALL_PLUGINS_BEFORE_OPERATION(op); if (id == 318) return ID_318_ARE_ALL_PLUGINS_AFTER_OPERATION(op); if (id == 319) return ID_319_IS_ANY_PLUGIN_BEFORE_OPERATION(op); @@ -63,7 +63,7 @@ contract Condition_PluginAndVoting is MachineStateManager { // ---------------------- Plugin Condition ---------------------- - function ID_301_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_301_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_301: The UINT_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_ENABLE_PLUGINS) return false; @@ -78,7 +78,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_302_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_302_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_302: The UINT_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_ENABLE_PLUGINS) return false; @@ -93,7 +93,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_303_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_303_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_303: The UINT_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_ENABLE_PLUGINS) return false; @@ -110,7 +110,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_304_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_304_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_304: The UINT_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_ENABLE_PLUGINS) return false; @@ -127,7 +127,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_305_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_305_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_305: The UINT_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_DISABLE_PLUGINS) return false; @@ -142,7 +142,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_306_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_306_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_306: The UINT_256_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_DISABLE_PLUGINS) return false; @@ -157,7 +157,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_307_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_307_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_307: The UINT_256_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_DISABLE_PLUGINS) return false; @@ -174,7 +174,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_308_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_308_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_LIST(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_308: The UINT_256_2DARRAY length is not 1"); if (op.opcode != EnumOpcode.BATCH_DISABLE_PLUGINS) return false; @@ -191,7 +191,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_309_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_309_ENABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_309: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_309: The UINT_256_2DARRAY[0] length is not 2"); @@ -205,7 +205,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_310_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_310_ENABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_310: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_310: The UINT_256_2DARRAY[0] length is not 2"); @@ -219,7 +219,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_311_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_311_ENABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_311: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_311: The UINT_256_2DARRAY[0] length is not 2"); @@ -234,7 +234,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_312_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_312_ENABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_312: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_312: The UINT_256_2DARRAY[0] length is not 2"); @@ -249,7 +249,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_313_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_313_DISABLE_ANY_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_313: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_313: The UINT_256_2DARRAY[0] length is not 2"); @@ -263,7 +263,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_314_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_314_DISABLE_ANY_AFTER_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_314: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_314: The UINT_256_2DARRAY[0] length is not 2"); @@ -277,7 +277,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return false; } - function ID_315_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_315_DISABLE_EACH_BEFORE_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_315: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_315: The UINT_256_2DARRAY[0] length is not 2"); @@ -292,7 +292,7 @@ contract Condition_PluginAndVoting is MachineStateManager { return true; } - function ID_316_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(bool bIsBeforeOperation, Operation memory op, NodeParam memory param) internal view returns (bool) + function ID_316_DISABLE_EACH_AFTER_OP_PLUGIN_INDEX_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool) { require(param.UINT256_2DARRAY.length == 1, "CE ID_316: The UINT_256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_316: The UINT_256_2DARRAY[0] length is not 2"); @@ -491,7 +491,7 @@ contract Condition_PluginAndVoting is MachineStateManager { * The function to get the plugin index list of the before operation plugins * @param op The operation to be checked */ - function getBeforeOpPluginIndexList(Operation memory op) internal view returns (uint256[] memory) { + function getBeforeOpPluginIndexList(Operation memory op) internal pure returns (uint256[] memory) { if (op.opcode == EnumOpcode.BATCH_ENABLE_PLUGINS || op.opcode == EnumOpcode.BATCH_DISABLE_PLUGINS) { uint256[] memory pluginIndexList = new uint256[](op.param.UINT256_2DARRAY[0].length); uint256 pt = 0; @@ -514,7 +514,7 @@ contract Condition_PluginAndVoting is MachineStateManager { * The function to get the plugin index list of the after operation plugins * @param op The operation to be checked */ - function getAfterOpPluginIndexList(Operation memory op) internal view returns (uint256[] memory) { + function getAfterOpPluginIndexList(Operation memory op) internal pure returns (uint256[] memory) { if (op.opcode == EnumOpcode.BATCH_ENABLE_PLUGINS || op.opcode == EnumOpcode.BATCH_DISABLE_PLUGINS) { uint256[] memory pluginIndexList = new uint256[](op.param.UINT256_2DARRAY[0].length); uint256 pt = 0; diff --git a/darc-protocol/contracts/protocol/Plugin/Condition_Withdrawable.sol b/darc-protocol/contracts/protocol/Plugin/Condition_Withdrawable.sol index fa782eb..7dfe214 100644 --- a/darc-protocol/contracts/protocol/Plugin/Condition_Withdrawable.sol +++ b/darc-protocol/contracts/protocol/Plugin/Condition_Withdrawable.sol @@ -21,7 +21,7 @@ contract Condition_Withdrawable is MachineStateManager { * @param param The parameter list of the condition expression * @param id The id of the condition expression */ - function withdrawableExpressionCheck(bool bIsBeforeOperation, Operation memory op, NodeParam memory param, uint256 id) internal view returns (bool) { + function withdrawableExpressionCheck(bool bIsBeforeOperation, Operation memory op, NodeParam memory param, uint256 id) internal pure returns (bool) { if (id == 431) return ID_431_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_GREATER_THAN(op, param); if (id == 432) return ID_432_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_LESS_THAN(op, param); if (id == 433) return ID_433_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_IN_RANGE(op, param); @@ -33,7 +33,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_431_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_GREATER_THAN(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_431_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_GREATER_THAN(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_431: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_431: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_ADD_WITHDRAWABLE_BALANCES) return false; @@ -43,7 +43,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_432_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_LESS_THAN(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_432_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_LESS_THAN(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_432: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_432: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_ADD_WITHDRAWABLE_BALANCES) return false; @@ -53,7 +53,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_433_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_IN_RANGE(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_433_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_433: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_433: The UINT256_2DARRAY[0] length is not 2"); if (op.opcode != EnumOpcode.BATCH_ADD_WITHDRAWABLE_BALANCES) return false; @@ -63,7 +63,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_434_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_EQUALS(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_434_ADD_WITHDRAWABLE_BALANCE_ANY_AMOUNT_EQUALS(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_434: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_434: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_ADD_WITHDRAWABLE_BALANCES) return false; @@ -73,7 +73,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_435_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_GREATER_THAN(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_435_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_GREATER_THAN(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_435: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_435: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_REDUCE_WITHDRAWABLE_BALANCES) return false; @@ -83,7 +83,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_436_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_LESS_THAN(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_436_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_LESS_THAN(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_436: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_436: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_REDUCE_WITHDRAWABLE_BALANCES) return false; @@ -93,7 +93,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_437_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_IN_RANGE(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_437_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_IN_RANGE(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_437: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 2, "CE ID_437: The UINT256_2DARRAY[0] length is not 2"); if (op.opcode != EnumOpcode.BATCH_REDUCE_WITHDRAWABLE_BALANCES) return false; @@ -103,7 +103,7 @@ contract Condition_Withdrawable is MachineStateManager { return false; } - function ID_438_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_EQUALS(Operation memory op, NodeParam memory param) internal view returns (bool){ + function ID_438_REDUCE_WITHDRAWABLE_BALANCE_ANY_AMOUNT_EQUALS(Operation memory op, NodeParam memory param) internal pure returns (bool){ require(param.UINT256_2DARRAY.length == 1, "CE ID_438: The UINT256_2DARRAY length is not 1"); require(param.UINT256_2DARRAY[0].length == 1, "CE ID_438: The UINT256_2DARRAY[0] length is not 1"); if (op.opcode != EnumOpcode.BATCH_REDUCE_WITHDRAWABLE_BALANCES) return false; diff --git a/darc-protocol/contracts/protocol/Runtime/Runtime.sol b/darc-protocol/contracts/protocol/Runtime/Runtime.sol index 356b12a..05ede9c 100644 --- a/darc-protocol/contracts/protocol/Runtime/Runtime.sol +++ b/darc-protocol/contracts/protocol/Runtime/Runtime.sol @@ -184,9 +184,15 @@ contract Runtime is Executable, PaymentCheck{ function executePendingProgram(Program memory program) internal { //1. check if the program is valid require(validateExecutePendingProgram(program), "Invalid program"); + require(votingItems[latestVotingItemIndex].bIsProgramExecuted == false, "The pending program has been executed, and should not be executed again."); + + // change the state back to idle + finiteState = FiniteState.IDLE; + + // change the latest voting item bIsProgramExecuted to true + votingItems[latestVotingItemIndex].bIsProgramExecuted = true; //2. execute the program(executing pending) directly, do not use execute() function executeProgram_Executable(votingItems[latestVotingItemIndex].program, false); - } } \ No newline at end of file diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index f29c7fb..d7d2e76 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -111,6 +111,7 @@ describe.only("single voting test", function () { await votingTestSingleTest.testRuntimeEntrance(program); + // try to vote now const program_vote: ProgramStruct = { programOperatorAddress: target0, @@ -130,27 +131,34 @@ describe.only("single voting test", function () { } }], } - - console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); // read the voting result const votingItemIndex = 1; const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); - console.log(votingItem); + expect(votingItem.votingStatus.toString()).to.equal("2"); // should be VotingStatus.ON_GOING // read the voting state: const votingState = await votingTestSingleTest.finiteState(); - console.log("The voting state is ", votingState); - console.log(await votingTestSingleTest.votingDeadline()); + expect(votingState.toString()).to.equal("2"); // should be FiniteState.VOTING + // vote - console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + await votingTestSingleTest.testRuntimeEntrance(program_vote).then(async (tx) => { console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); // console log the voting state again - console.log("After vote, the voting state is ", await votingTestSingleTest.finiteState()); - console.log("After vote, the voting deadline is ", await votingTestSingleTest.votingDeadline()); - console.log(await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)); - console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); - console.log("current time stamp is ", (await time.latest()).toString()); + + // the voting state should be 3, which is EXECUTING_PENDING + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); + + const currentVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + + // make sure that the powerYES is 600 + expect(currentVotingItem.powerYes[0].toString()).to.equal("600"); + + // make sure that the total power is 1000 + expect(currentVotingItem.totalPower.toString()).to.equal("1000"); + + // the latest voting index should be 1 + expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).equal("1"); //return; @@ -175,12 +183,26 @@ describe.only("single voting test", function () { // run the execute pending program await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { - console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); - console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); - console.log(await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)); - console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); - console.log("current time stamp is ", (await time.latest()).toString()); + expect((await votingTestSingleTest.finiteState()).toString()).to.equal('1'); // back to idle state + expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).to.equal("1"); // the latest voting index should be 1 + + // check the latest voting items + const latestVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + expect(latestVotingItem.votingStatus.toString()).to.equal("0"); // should be VotingStatus.Ended_AND_Passed + + // check the bIsProgramExecuted + expect(latestVotingItem.bIsProgramExecuted).to.equal(true); + + // check the total voting power is 1000 + expect(latestVotingItem.totalPower.toString()).to.equal("1000"); + + // check the powerYes is 600 + expect(latestVotingItem.powerYes[0].toString()).to.equal("600"); + + // console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); + // console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + // expect(((await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)).toString())).to.equal("1") //print all token holders and tokens, making sure that the pending program of minting tokens is executed after voting const owners = await votingTestSingleTest.getTokenOwners(0); // for (let i = 0; i < owners.length; i++) { From 0fb244f8ff28a34ba4645168bd1e6376784b3af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:33:27 -0600 Subject: [PATCH 09/11] update --- .../Runtime/VotingMachine/VotingMachine.sol | 4 +- .../test/votingTest/singleVoteNegativeTest.ts | 168 ++++++++++++++++++ .../test/votingTest/singleVotingTest.ts | 4 +- 3 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 darc-protocol/test/votingTest/singleVoteNegativeTest.ts diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index 91b2b5a..c0b81e6 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -206,8 +206,8 @@ contract VotingMachine is MachineStateManager { powerOf(voter, i)); require(bIsValid, "voting for powerYes overflow"); } else { - (bIsValid, votingItems[latestVotingItemIndex].powerYes[i]) = SafeMathUpgradeable.tryAdd( - votingItems[latestVotingItemIndex].powerYes[i], + (bIsValid, votingItems[latestVotingItemIndex].powerNo[i]) = SafeMathUpgradeable.tryAdd( + votingItems[latestVotingItemIndex].powerNo[i], powerOf(voter, i)); require(bIsValid, "voting for powerNo overflow"); } diff --git a/darc-protocol/test/votingTest/singleVoteNegativeTest.ts b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts new file mode 100644 index 0000000..63c8f62 --- /dev/null +++ b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts @@ -0,0 +1,168 @@ +import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; + +describe.only("single voting netative test", function () { + it ("should pass single voting netative test: start an absolute majority vote, vote once, reject the vote, then change back to idle state", async function () { + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initialize(); + + // create tokens, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // add a voting rule that ask 50% to approve in relative majority + await votingTestSingleTest.addVotingRule({ + votingTokenClassList: [BigNumber.from(0)], + approvalThresholdPercentage: BigNumber.from(50), + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in relative majority", + bIsAbsoluteMajority: true, + }, false); + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //return; + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0), BigNumber.from(0), BigNumber.from(0), BigInt(0), BigInt(0)], + [BigNumber.from(100), BigNumber.from(200), BigNumber.from(300), BigInt(16), BigInt(5)], // amount = 100 + ], + ADDRESS_2DARRAY: [ + [target0, target1, target1, target1, target0], + ], + BYTES: [] + } + }], + }; + + await votingTestSingleTest.testRuntimeEntrance(program); + + + // try to vote now + const program_vote: ProgramStruct = { + programOperatorAddress: target0, + notes: "vote", + operations: [{ + operatorAddress: target0, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [false], // vote for no + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + // read the voting result + const votingItemIndex = 1; + const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + expect(votingItem.votingStatus.toString()).to.equal("2"); // should be VotingStatus.ON_GOING + + // read the voting state: + const votingState = await votingTestSingleTest.finiteState(); + expect(votingState.toString()).to.equal("2"); // should be FiniteState.VOTING + + // vote + + await votingTestSingleTest.testRuntimeEntrance(program_vote).then(async (tx) => { + console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + // console log the voting state again + + // the voting state should be 1, which is idle + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("1"); + + const currentVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + + // make sure that the powerYES is 600 + expect(currentVotingItem.powerNo[0].toString()).to.equal("600"); + + // make sure that the total power is 1000 + expect(currentVotingItem.totalPower.toString()).to.equal("1000"); + + // the latest voting index should be 1 + expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).equal("1"); + + return; + + }); + + }); +}) diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index d7d2e76..2f09f41 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -14,8 +14,8 @@ const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; -describe.only("single voting test", function () { - it ("should pass single voting test", async function () { +describe("single voting test", function () { + it ("should pass single voting test: start an absolute majority vote, vote once, pass the vote, run pending program, then change back to idle state", async function () { const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); const votingTestSingleTest = await VotingTestFactory.deploy(); await votingTestSingleTest.deployed(); From 8d2ebe43966870679a5cdbbe72a85c95785616b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Mon, 15 Jan 2024 02:19:57 -0600 Subject: [PATCH 10/11] update --- .../Runtime/VotingMachine/VotingMachine.sol | 16 +- .../test-contracts/TestBaseContract.sol | 4 + .../test/votingTest/multiAddressVotingTest.ts | 324 +++++++++++++++++ .../votingTest/multiClassTokenVotingTest.ts | 343 ++++++++++++++++++ .../test/votingTest/singleVoteNegativeTest.ts | 2 +- 5 files changed, 682 insertions(+), 7 deletions(-) create mode 100644 darc-protocol/test/votingTest/multiAddressVotingTest.ts create mode 100644 darc-protocol/test/votingTest/multiClassTokenVotingTest.ts diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index c0b81e6..a143d13 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -200,15 +200,16 @@ contract VotingMachine is MachineStateManager { // update the voted status bool bIsValid = false; for (uint256 i = 0; i < votes.length; i++) { + uint256 votingRuleIndex = votingItems[latestVotingItemIndex].votingRuleIndices[i]; if (votes[i]) { (bIsValid, votingItems[latestVotingItemIndex].powerYes[i]) = SafeMathUpgradeable.tryAdd( votingItems[latestVotingItemIndex].powerYes[i], - powerOf(voter, i)); + powerOf(voter, votingRuleIndex)); require(bIsValid, "voting for powerYes overflow"); } else { (bIsValid, votingItems[latestVotingItemIndex].powerNo[i]) = SafeMathUpgradeable.tryAdd( votingItems[latestVotingItemIndex].powerNo[i], - powerOf(voter, i)); + powerOf(voter, votingRuleIndex)); require(bIsValid, "voting for powerNo overflow"); } } @@ -226,7 +227,7 @@ contract VotingMachine is MachineStateManager { * @param voter The address of the voter * @param currentVotingRuleIdx The index of the current voting item */ - function powerOf(address voter, uint256 currentVotingRuleIdx) private view returns (uint256){ + function powerOf(address voter, uint256 currentVotingRuleIdx) internal view returns (uint256){ uint256 totalPower = 0; bool bIsValid = false; uint256 power = 0; @@ -236,11 +237,14 @@ contract VotingMachine is MachineStateManager { // iterate through all token class index, sum up the power of the voter for (uint256 tokenClassIdx = 0; tokenClassIdx < currentVotingRule.votingTokenClassList.length; tokenClassIdx++) { + + // current token class + uint256 currentTokenClass = currentVotingRule.votingTokenClassList[tokenClassIdx]; // get the number of token - uint256 numberOfTokens = currentMachineState.tokenList[tokenClassIdx].tokenBalance[voter]; + uint256 numberOfTokens = currentMachineState.tokenList[currentTokenClass].tokenBalance[voter]; // get the voting weight - uint256 weight = currentMachineState.tokenList[tokenClassIdx].votingWeight; + uint256 weight = currentMachineState.tokenList[currentTokenClass].votingWeight; // get the power of voter for this token class = number of tokens * voting weight (bIsValid, power) = SafeMathUpgradeable.tryMul(numberOfTokens, weight); @@ -250,7 +254,7 @@ contract VotingMachine is MachineStateManager { (bIsValid, totalPower) = SafeMathUpgradeable.tryAdd(totalPower, power); require(bIsValid, "total power overflow"); } - return power; + return totalPower; } /** diff --git a/darc-protocol/contracts/test-contracts/TestBaseContract.sol b/darc-protocol/contracts/test-contracts/TestBaseContract.sol index 149c8ff..429c540 100644 --- a/darc-protocol/contracts/test-contracts/TestBaseContract.sol +++ b/darc-protocol/contracts/test-contracts/TestBaseContract.sol @@ -35,6 +35,10 @@ contract TestBaseContract is Runtime, Dashboard { } } + function getVoterPowerOfVotingRule(uint256 votingRuleIndex, address voterAddress) public view returns (uint256) { + return powerOf(voterAddress, votingRuleIndex); + } + function helper_createToken0AndMint() public { // create a token class first currentMachineState.tokenList[0].bIsInitialized = true; diff --git a/darc-protocol/test/votingTest/multiAddressVotingTest.ts b/darc-protocol/test/votingTest/multiAddressVotingTest.ts new file mode 100644 index 0000000..164b627 --- /dev/null +++ b/darc-protocol/test/votingTest/multiAddressVotingTest.ts @@ -0,0 +1,324 @@ +import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; + +async function letAddressVote(operatorAddress:string,darcAddress:string) { + const currentSigner = ethers.provider.getSigner(operatorAddress); + const currentDARC = (await ethers.getContractFactory("TestBaseContract")).attach(darcAddress).connect(currentSigner); + + const votingProgram: ProgramStruct = { + programOperatorAddress: operatorAddress, + notes: "vote", + operations: [{ + operatorAddress: operatorAddress, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [true], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + await currentDARC.testRuntimeEntrance(votingProgram); +} + +describe("multi address voting test", function () { + it ("should pass multi address voting test: start an absolute majority vote, vote once, pass the vote, run pending program, then change back to idle state", async function () { + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initialize(); + + // create tokens, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // create token class 1, voting weight 1, dividend weight 1 + await votingTestSingleTest.runProgramDirectly({ + programOperatorAddress: target0, + notes: "create token class", + operations: [{ + operatorAddress: target0, + opcode: 2, // create token class + param: { + + + STRING_ARRAY: ["Class2"], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [ BigNumber.from(1)], + [BigNumber.from(1)], + [BigNumber.from(1)], + ], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }, + { + operatorAddress: target0, + opcode: 1, // mint token + param: { + + + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(1), BigNumber.from(1), BigNumber.from(1), BigInt(1)], + [BigNumber.from(100), BigNumber.from(100), BigNumber.from(100), BigInt(100)], // amount = 100 + ], + ADDRESS_2DARRAY: [ + [target0,target1, target2, target3], + ], + BYTES: [] + } + }], + }, false); + + // add a voting rule that ask 51% to approve in relative majority + await votingTestSingleTest.addVotingRule({ + votingTokenClassList: [BigNumber.from(1)], + approvalThresholdPercentage: BigNumber.from(51), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in relative majority", + bIsAbsoluteMajority: true, + }, false); + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //return; + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0)], + [BigNumber.from(100000)], // amount = 100000 + ], + ADDRESS_2DARRAY: [ + [target0], + ], + BYTES: [] + } + }], + }; + + await votingTestSingleTest.testRuntimeEntrance(program); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + + // make sure that total voting power is 400 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).totalPower[0].toString()).to.equal("400"); // should be 400 + + // target 0 vote, 100/400 voted + await letAddressVote(target0, votingTestSingleTest.address); + + //console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) + + + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("100"); // should be 100 + + // target 1 vote, 200/400 voted + await letAddressVote(target1, votingTestSingleTest.address); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("200"); // should be 300 + + // target 2 vote, 300/400 voted + await letAddressVote(target2, votingTestSingleTest.address); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); // should be FiniteState.EXECUTING_PENDING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("300"); // should be 300 + + // finally execute the pending program + + // // try to vote now + // const program_vote: ProgramStruct = { + // programOperatorAddress: target0, + // notes: "vote", + // operations: [{ + // operatorAddress: target0, + // opcode: 32, // vote + // param: { + // STRING_ARRAY: [], + // BOOL_ARRAY: [true], + // VOTING_RULE_ARRAY: [], + // PARAMETER_ARRAY: [], + // PLUGIN_ARRAY: [], + // UINT256_2DARRAY: [], + // ADDRESS_2DARRAY: [], + // BYTES: [] + // } + // }], + // } + // // read the voting result + // const votingItemIndex = 1; + // const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + // expect(votingItem.votingStatus.toString()).to.equal("2"); // should be VotingStatus.ON_GOING + + // // read the voting state: + // const votingState = await votingTestSingleTest.finiteState(); + // expect(votingState.toString()).to.equal("2"); // should be FiniteState.VOTING + + // // vote + // await votingTestSingleTest.testRuntimeEntrance(program_vote).then(async (tx) => { + // console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + // // console log the voting state again + + // // the voting state should be 3, which is EXECUTING_PENDING + // expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); + + // const currentVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + + // // make sure that the powerYES is 600 + // expect(currentVotingItem.powerYes[0].toString()).to.equal("600"); + + // // make sure that the total power is 1000 + // expect(currentVotingItem.totalPower.toString()).to.equal("1000"); + + // // the latest voting index should be 1 + // expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).equal("1"); + + // //return; + + // const program_execute_pending_program: ProgramStruct = { + // programOperatorAddress: target0, + // notes: "execute_pending_program", + // operations: [{ + // operatorAddress: target0, + // opcode: 33, // vote + // param: { + // STRING_ARRAY: [], + // BOOL_ARRAY: [], + // VOTING_RULE_ARRAY: [], + // PARAMETER_ARRAY: [], + // PLUGIN_ARRAY: [], + // UINT256_2DARRAY: [], + // ADDRESS_2DARRAY: [], + // BYTES: [] + // } + // }], + // } + + // // run the execute pending program + // await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { + // expect((await votingTestSingleTest.finiteState()).toString()).to.equal('1'); // back to idle state + // expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).to.equal("1"); // the latest voting index should be 1 + + // // check the latest voting items + // const latestVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + // expect(latestVotingItem.votingStatus.toString()).to.equal("0"); // should be VotingStatus.Ended_AND_Passed + + // // check the bIsProgramExecuted + // expect(latestVotingItem.bIsProgramExecuted).to.equal(true); + + // // check the total voting power is 1000 + // expect(latestVotingItem.totalPower.toString()).to.equal("1000"); + + // // check the powerYes is 600 + // expect(latestVotingItem.powerYes[0].toString()).to.equal("600"); + + // // console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); + // // console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + + // // expect(((await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)).toString())).to.equal("1") + // //print all token holders and tokens, making sure that the pending program of minting tokens is executed after voting + // const owners = await votingTestSingleTest.getTokenOwners(0); + // // for (let i = 0; i < owners.length; i++) { + // // console.log("owner ", i, " is ", owners[i]); + // // console.log("balance of owner ", i, " is ", await votingTestSingleTest.getTokenOwnerBalance(0, owners[i])); + // // } + + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target0)).toString()).to.equal("705"); + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target1)).toString()).to.equal("716"); + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target2)).toString()).to.equal("200"); + // }); + // }); + + }); +}) diff --git a/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts b/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts new file mode 100644 index 0000000..c43bcac --- /dev/null +++ b/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts @@ -0,0 +1,343 @@ +import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; + +async function letAddressVote(operatorAddress:string,darcAddress:string) { + const currentSigner = ethers.provider.getSigner(operatorAddress); + const currentDARC = (await ethers.getContractFactory("TestBaseContract")).attach(darcAddress).connect(currentSigner); + + const votingProgram: ProgramStruct = { + programOperatorAddress: operatorAddress, + notes: "vote", + operations: [{ + operatorAddress: operatorAddress, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [true], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + await currentDARC.testRuntimeEntrance(votingProgram); +} + +describe.only("multi class token voting test", function () { + it ("should pass multi class token voting test", async function () { + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initialize(); + + // create token class 0, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // create token class 1, voting weight 1, dividend weight 1 + await votingTestSingleTest.runProgramDirectly({ + programOperatorAddress: target0, + notes: "create token class", + operations: [{ + operatorAddress: target0, + opcode: 2, // create token class + param: { + STRING_ARRAY: ["C1", "C2", "C3"], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [ BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + ], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }, + { + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(1), BigNumber.from(1), BigNumber.from(1), BigInt(2),BigInt(2),BigInt(2), BigInt(3)], + [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3), BigInt(1), BigInt(1), BigInt(1), BigInt(1)], // amount = 100 + ], + ADDRESS_2DARRAY: [ + [target0,target1, target2, target0, target1, target2, target3], + ], + BYTES: [] + } + }], + }, false); + + // weight of target0 = 1*1 + 5*1 = 6 + // weight of target1 = 2*1 + 5*1 = 7 + // weight of target2 = 3*1 + 5*1 = 8 + // weight of target3 = 10*1 = 10 + // total weight is 6+7+8+10 = 31 + + // add a voting rule that ask 51% to approve in relative majority + await votingTestSingleTest.addVotingRule({ + + // class 1, 2, 3 can vote + votingTokenClassList: [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + approvalThresholdPercentage: BigNumber.from(51), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in relative majority", + bIsAbsoluteMajority: true, + }, false); + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0)], + [BigNumber.from(100000)], // amount = 100000 + ], + ADDRESS_2DARRAY: [ + [target0], + ], + BYTES: [] + } + }], + }; + + await votingTestSingleTest.testRuntimeEntrance(program); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + + // make sure that total voting power is 400 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).totalPower[0].toString()).to.equal("31"); // should be 31 + + // target 0 vote, 6/31 voted + await letAddressVote(target0, votingTestSingleTest.address); + + const indexLatestVotingItem = await votingTestSingleTest.latestVotingItemIndex(); + + // get token number of target0 at level 1 and 2 + console.log(await votingTestSingleTest.getTokenOwnerBalance(1, target0)); + console.log(await votingTestSingleTest.getTokenOwnerBalance(2, target0)); + + console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) + + const powerOfTarget0 = await votingTestSingleTest.getVoterPowerOfVotingRule(0, target0); + console.log("power of target0 is ", powerOfTarget0.toString()); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(indexLatestVotingItem)).powerYes[0].toString()).to.equal("6"); // should be 6 + + await letAddressVote(target1, votingTestSingleTest.address); + + return; + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("6"); // should be 100 + + // target 1 vote, 13/31 willvoted + await letAddressVote(target1, votingTestSingleTest.address); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("31"); // should be 300 + + // target 2 vote, 300/400 voted + await letAddressVote(target2, votingTestSingleTest.address); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); // should be FiniteState.EXECUTING_PENDING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("300"); // should be 300 + + // finally execute the pending program + + // // try to vote now + // const program_vote: ProgramStruct = { + // programOperatorAddress: target0, + // notes: "vote", + // operations: [{ + // operatorAddress: target0, + // opcode: 32, // vote + // param: { + // STRING_ARRAY: [], + // BOOL_ARRAY: [true], + // VOTING_RULE_ARRAY: [], + // PARAMETER_ARRAY: [], + // PLUGIN_ARRAY: [], + // UINT256_2DARRAY: [], + // ADDRESS_2DARRAY: [], + // BYTES: [] + // } + // }], + // } + // // read the voting result + // const votingItemIndex = 1; + // const votingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + // expect(votingItem.votingStatus.toString()).to.equal("2"); // should be VotingStatus.ON_GOING + + // // read the voting state: + // const votingState = await votingTestSingleTest.finiteState(); + // expect(votingState.toString()).to.equal("2"); // should be FiniteState.VOTING + + // // vote + // await votingTestSingleTest.testRuntimeEntrance(program_vote).then(async (tx) => { + // console.log("The latest voting index is ", (await votingTestSingleTest.latestVotingItemIndex()).toString()); + // // console log the voting state again + + // // the voting state should be 3, which is EXECUTING_PENDING + // expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); + + // const currentVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + + // // make sure that the powerYES is 600 + // expect(currentVotingItem.powerYes[0].toString()).to.equal("600"); + + // // make sure that the total power is 1000 + // expect(currentVotingItem.totalPower.toString()).to.equal("1000"); + + // // the latest voting index should be 1 + // expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).equal("1"); + + // //return; + + // const program_execute_pending_program: ProgramStruct = { + // programOperatorAddress: target0, + // notes: "execute_pending_program", + // operations: [{ + // operatorAddress: target0, + // opcode: 33, // vote + // param: { + // STRING_ARRAY: [], + // BOOL_ARRAY: [], + // VOTING_RULE_ARRAY: [], + // PARAMETER_ARRAY: [], + // PLUGIN_ARRAY: [], + // UINT256_2DARRAY: [], + // ADDRESS_2DARRAY: [], + // BYTES: [] + // } + // }], + // } + + // // run the execute pending program + // await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { + // expect((await votingTestSingleTest.finiteState()).toString()).to.equal('1'); // back to idle state + // expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).to.equal("1"); // the latest voting index should be 1 + + // // check the latest voting items + // const latestVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); + // expect(latestVotingItem.votingStatus.toString()).to.equal("0"); // should be VotingStatus.Ended_AND_Passed + + // // check the bIsProgramExecuted + // expect(latestVotingItem.bIsProgramExecuted).to.equal(true); + + // // check the total voting power is 1000 + // expect(latestVotingItem.totalPower.toString()).to.equal("1000"); + + // // check the powerYes is 600 + // expect(latestVotingItem.powerYes[0].toString()).to.equal("600"); + + // // console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); + // // console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); + + // // expect(((await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)).toString())).to.equal("1") + // //print all token holders and tokens, making sure that the pending program of minting tokens is executed after voting + // const owners = await votingTestSingleTest.getTokenOwners(0); + // // for (let i = 0; i < owners.length; i++) { + // // console.log("owner ", i, " is ", owners[i]); + // // console.log("balance of owner ", i, " is ", await votingTestSingleTest.getTokenOwnerBalance(0, owners[i])); + // // } + + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target0)).toString()).to.equal("705"); + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target1)).toString()).to.equal("716"); + // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target2)).toString()).to.equal("200"); + // }); + // }); + + }); +}) diff --git a/darc-protocol/test/votingTest/singleVoteNegativeTest.ts b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts index 63c8f62..8f5f878 100644 --- a/darc-protocol/test/votingTest/singleVoteNegativeTest.ts +++ b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts @@ -14,7 +14,7 @@ const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; const target3 = '0x870526b7973b56163a6997bb7c886f5e4ea53638'; -describe.only("single voting netative test", function () { +describe("single voting netative test", function () { it ("should pass single voting netative test: start an absolute majority vote, vote once, reject the vote, then change back to idle state", async function () { const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); const votingTestSingleTest = await VotingTestFactory.deploy(); From 5cfe9e588a92b1cc2f663fb32da3f3379b9722fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=8B=E5=85=9A=20Lidang?= <6800565+lidangzzz@users.noreply.github.com> Date: Mon, 15 Jan 2024 18:57:12 -0600 Subject: [PATCH 11/11] update --- .../Runtime/VotingMachine/VotingMachine.sol | 19 +- .../test-contracts/TestBaseContract.sol | 31 ++ .../test/votingTest/multiAddressVotingTest.ts | 4 +- .../votingTest/multiClassTokenVotingTest.ts | 104 ++---- .../votingTest/multiItemVotingNegativeTest.ts | 330 +++++++++++++++++ .../test/votingTest/multiItemVotingTest.ts | 342 ++++++++++++++++++ .../test/votingTest/singleVoteNegativeTest.ts | 4 +- .../test/votingTest/singleVotingTest.ts | 4 +- 8 files changed, 760 insertions(+), 78 deletions(-) create mode 100644 darc-protocol/test/votingTest/multiItemVotingNegativeTest.ts create mode 100644 darc-protocol/test/votingTest/multiItemVotingTest.ts diff --git a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol index a143d13..cd9dc43 100644 --- a/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol +++ b/darc-protocol/contracts/protocol/Runtime/VotingMachine/VotingMachine.sol @@ -92,7 +92,7 @@ contract VotingMachine is MachineStateManager { /** * @notice determine if the current state is in the voting period */ - function isVotingProcesss() private view returns (bool) { + function isVotingProcesss() internal view returns (bool) { return finiteState == FiniteState.VOTING; } @@ -354,6 +354,11 @@ contract VotingMachine is MachineStateManager { function minVotingDurationInSeconds(uint256[] memory votingRuleIndices) private view returns (uint256){ uint256 minDuration = 2**256 - 1; // the largest uint256 for (uint256 i = 0; i < votingRuleIndices.length; i++) { + + // the voting rule index must be valid, less than the length of the voting rule list + // if the voting rule index is invalid, throw exception with index and voting rule list length + require(votingRuleIndices[i] < currentMachineState.votingRuleList.length, + "voting rule index out of range"); if ( currentMachineState.votingRuleList[votingRuleIndices[i]].votingDurationInSeconds < minDuration) { minDuration = currentMachineState.votingRuleList[votingRuleIndices[i]].votingDurationInSeconds; } @@ -368,6 +373,11 @@ contract VotingMachine is MachineStateManager { function minExecutePendingProgramDurationInSeconds(uint256[] memory votingRuleIndices) private view returns (uint256){ uint256 minDuration = 2**256 - 1; // the largest uint256 for (uint256 i = 0; i < votingRuleIndices.length; i++) { + + // the voting rule index must be valid, less than the length of the voting rule list + // if the voting rule index is invalid, throw exception with index and voting rule list length + require(votingRuleIndices[i] < currentMachineState.votingRuleList.length, + "voting rule index out of range"); if (currentMachineState.votingRuleList[votingRuleIndices[i]].executionPendingDurationInSeconds < minDuration) { minDuration = currentMachineState.votingRuleList[votingRuleIndices[i]].executionPendingDurationInSeconds; } @@ -417,11 +427,12 @@ contract VotingMachine is MachineStateManager { * return Ended_AND_Passed if the voting is ended and passed * return Ended_AND_Failed if the voting is ended and failed * No "OnGoing" state because the voting machine will check this function to change the state - * @param idx the index of the voting rule + * @param idx the index of voting item(which can be the voting rule index's index, or also the index of powerYes, powerNo, totalPower's index) */ function checkVotingResult(uint256 idx) internal view returns (VotingStatus) { bool bIsValid = false; - uint256 threshold = currentMachineState.votingRuleList[idx].approvalThresholdPercentage; + uint256 votingRuleIndex = votingItems[latestVotingItemIndex].votingRuleIndices[idx]; + uint256 threshold = currentMachineState.votingRuleList[votingRuleIndex].approvalThresholdPercentage; uint256 currentYes = votingItems[latestVotingItemIndex].powerYes[idx]; uint256 currentNo = votingItems[latestVotingItemIndex].powerNo[idx]; uint256 totalVotingPower = votingItems[latestVotingItemIndex].totalPower[idx]; @@ -431,7 +442,7 @@ contract VotingMachine is MachineStateManager { // then currentYes / totalVotingPower > threshold % will pass the voting uint256 leftValue = 0; uint256 rightValue = 0; - if (currentMachineState.votingRuleList[idx].bIsAbsoluteMajority){ + if (currentMachineState.votingRuleList[votingRuleIndex].bIsAbsoluteMajority){ // currentYes * 100 > totalVotingPower * threshold (bIsValid, leftValue) = SafeMathUpgradeable.tryMul(currentYes, 100); require(bIsValid, "currentYes overflow"); diff --git a/darc-protocol/contracts/test-contracts/TestBaseContract.sol b/darc-protocol/contracts/test-contracts/TestBaseContract.sol index 429c540..1ad0922 100644 --- a/darc-protocol/contracts/test-contracts/TestBaseContract.sol +++ b/darc-protocol/contracts/test-contracts/TestBaseContract.sol @@ -27,6 +27,37 @@ contract TestBaseContract is Runtime, Dashboard { cloneStateToSandbox(); } + function voteTest(address voter, bool[] memory votes) public{ + require(isVotingProcesss(), "voting is not in progress"); + require(!voted[voter][latestVotingItemIndex], "voter has already voted"); + require(votes.length == votingItems[latestVotingItemIndex].votingRuleIndices.length, + "the number of votes does not match the number of policies"); + + // set the boolean voted to true + voted[voter][latestVotingItemIndex] = true; + + // update the voted status + bool bIsValid = false; + for (uint256 i = 0; i < votes.length; i++) { + uint256 votingRuleIndex = votingItems[latestVotingItemIndex].votingRuleIndices[i]; + if (votes[i]) { + (bIsValid, votingItems[latestVotingItemIndex].powerYes[i]) = SafeMathUpgradeable.tryAdd( + votingItems[latestVotingItemIndex].powerYes[i], + powerOf(voter, votingRuleIndex)); + require(bIsValid, "voting for powerYes overflow"); + } else { + (bIsValid, votingItems[latestVotingItemIndex].powerNo[i]) = SafeMathUpgradeable.tryAdd( + votingItems[latestVotingItemIndex].powerNo[i], + powerOf(voter, votingRuleIndex)); + require(bIsValid, "voting for powerNo overflow"); + } + } + + // after the vote, check if the voting period has ended + tryEndVotingBeforeVotingDeadline(); + + } + function addVotingRule(VotingRule memory votingRule, bool bIsSandbox) public { if (bIsSandbox) { sandboxMachineState.votingRuleList.push(votingRule); diff --git a/darc-protocol/test/votingTest/multiAddressVotingTest.ts b/darc-protocol/test/votingTest/multiAddressVotingTest.ts index 164b627..e9b9078 100644 --- a/darc-protocol/test/votingTest/multiAddressVotingTest.ts +++ b/darc-protocol/test/votingTest/multiAddressVotingTest.ts @@ -121,14 +121,14 @@ describe("multi address voting test", function () { }], }, false); - // add a voting rule that ask 51% to approve in relative majority + // add a voting rule that ask 51% to approve in absolute majority await votingTestSingleTest.addVotingRule({ votingTokenClassList: [BigNumber.from(1)], approvalThresholdPercentage: BigNumber.from(51), // 51% to approve (absolute majority) votingDurationInSeconds: BigNumber.from(1000), executionPendingDurationInSeconds: BigNumber.from(1000), bIsEnabled: true, - notes: "50% to approve in relative majority", + notes: "50% to approve in absolute majority", bIsAbsoluteMajority: true, }, false); diff --git a/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts b/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts index c43bcac..20bb81b 100644 --- a/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts +++ b/darc-protocol/test/votingTest/multiClassTokenVotingTest.ts @@ -40,7 +40,7 @@ async function letAddressVote(operatorAddress:string,darcAddress:string) { await currentDARC.testRuntimeEntrance(votingProgram); } -describe.only("multi class token voting test", function () { +describe("multi class token voting test", function () { it ("should pass multi class token voting test", async function () { const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); const votingTestSingleTest = await VotingTestFactory.deploy(); @@ -123,7 +123,7 @@ describe.only("multi class token voting test", function () { // weight of target3 = 10*1 = 10 // total weight is 6+7+8+10 = 31 - // add a voting rule that ask 51% to approve in relative majority + // add a voting rule that ask 51% to approve in absolute majority await votingTestSingleTest.addVotingRule({ // class 1, 2, 3 can vote @@ -132,7 +132,7 @@ describe.only("multi class token voting test", function () { votingDurationInSeconds: BigNumber.from(1000), executionPendingDurationInSeconds: BigNumber.from(1000), bIsEnabled: true, - notes: "50% to approve in relative majority", + notes: "50% to approve in absolute majority", bIsAbsoluteMajority: true, }, false); @@ -199,14 +199,14 @@ describe.only("multi class token voting test", function () { const indexLatestVotingItem = await votingTestSingleTest.latestVotingItemIndex(); - // get token number of target0 at level 1 and 2 - console.log(await votingTestSingleTest.getTokenOwnerBalance(1, target0)); - console.log(await votingTestSingleTest.getTokenOwnerBalance(2, target0)); + // // get token number of target0 at level 1 and 2 + // console.log(await votingTestSingleTest.getTokenOwnerBalance(1, target0)); + // console.log(await votingTestSingleTest.getTokenOwnerBalance(2, target0)); - console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) + // console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) const powerOfTarget0 = await votingTestSingleTest.getVoterPowerOfVotingRule(0, target0); - console.log("power of target0 is ", powerOfTarget0.toString()); + // console.log("power of target0 is ", powerOfTarget0.toString()); // make sure that current state is voting expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING @@ -214,25 +214,20 @@ describe.only("multi class token voting test", function () { await letAddressVote(target1, votingTestSingleTest.address); - return; + // make sure that current state is voting expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING - expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("6"); // should be 100 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("13"); // should be 100 - // target 1 vote, 13/31 willvoted - await letAddressVote(target1, votingTestSingleTest.address); - // make sure that current state is voting - expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING - expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("31"); // should be 300 - // target 2 vote, 300/400 voted + // target 2 vote, 21/31 voted await letAddressVote(target2, votingTestSingleTest.address); // make sure that current state is voting expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); // should be FiniteState.EXECUTING_PENDING - expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("300"); // should be 300 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("21"); // should be 300 // finally execute the pending program @@ -285,59 +280,32 @@ describe.only("multi class token voting test", function () { // //return; - // const program_execute_pending_program: ProgramStruct = { - // programOperatorAddress: target0, - // notes: "execute_pending_program", - // operations: [{ - // operatorAddress: target0, - // opcode: 33, // vote - // param: { - // STRING_ARRAY: [], - // BOOL_ARRAY: [], - // VOTING_RULE_ARRAY: [], - // PARAMETER_ARRAY: [], - // PLUGIN_ARRAY: [], - // UINT256_2DARRAY: [], - // ADDRESS_2DARRAY: [], - // BYTES: [] - // } - // }], - // } + const program_execute_pending_program: ProgramStruct = { + programOperatorAddress: target0, + notes: "execute_pending_program", + operations: [{ + operatorAddress: target0, + opcode: 33, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } - // // run the execute pending program - // await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { - // expect((await votingTestSingleTest.finiteState()).toString()).to.equal('1'); // back to idle state - // expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).to.equal("1"); // the latest voting index should be 1 + // run the execute pending program + await votingTestSingleTest.testRuntimeEntrance(program_execute_pending_program).then(async (tx) => { + expect((await votingTestSingleTest.finiteState()).toString()).to.equal('1'); // back to idle state + expect((await votingTestSingleTest.latestVotingItemIndex()).toString()).to.equal("1"); // the latest voting index should be 1 + expect((await votingTestSingleTest.getTokenOwnerBalance(0, target0)).toString()).equal("100600"); + }); - // // check the latest voting items - // const latestVotingItem = await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex); - // expect(latestVotingItem.votingStatus.toString()).to.equal("0"); // should be VotingStatus.Ended_AND_Passed - - // // check the bIsProgramExecuted - // expect(latestVotingItem.bIsProgramExecuted).to.equal(true); - - // // check the total voting power is 1000 - // expect(latestVotingItem.totalPower.toString()).to.equal("1000"); - - // // check the powerYes is 600 - // expect(latestVotingItem.powerYes[0].toString()).to.equal("600"); - - // // console.log("After execute pending program, the voting state is ", await votingTestSingleTest.finiteState()); - // // console.log("After execute pending program, the voting deadline is ", await votingTestSingleTest.votingDeadline()); - - // // expect(((await votingTestSingleTest.getVotingItemsByIndex(votingItemIndex)).toString())).to.equal("1") - // //print all token holders and tokens, making sure that the pending program of minting tokens is executed after voting - // const owners = await votingTestSingleTest.getTokenOwners(0); - // // for (let i = 0; i < owners.length; i++) { - // // console.log("owner ", i, " is ", owners[i]); - // // console.log("balance of owner ", i, " is ", await votingTestSingleTest.getTokenOwnerBalance(0, owners[i])); - // // } - - // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target0)).toString()).to.equal("705"); - // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target1)).toString()).to.equal("716"); - // expect((await votingTestSingleTest.getTokenOwnerBalance(0, target2)).toString()).to.equal("200"); - // }); - // }); }); }) diff --git a/darc-protocol/test/votingTest/multiItemVotingNegativeTest.ts b/darc-protocol/test/votingTest/multiItemVotingNegativeTest.ts new file mode 100644 index 0000000..28caa66 --- /dev/null +++ b/darc-protocol/test/votingTest/multiItemVotingNegativeTest.ts @@ -0,0 +1,330 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'; + +async function letAddressVote(operatorAddress:string,darcAddress:string) { + const currentSigner = ethers.provider.getSigner(operatorAddress); + const currentDARC = (await ethers.getContractFactory("TestBaseContract")).attach(darcAddress).connect(currentSigner); + + const votingProgram: ProgramStruct = { + programOperatorAddress: operatorAddress, + notes: "vote", + operations: [{ + operatorAddress: operatorAddress, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [false, false, false, false], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + await currentDARC.testRuntimeEntrance(votingProgram); +} + +describe("multi item voting test", function () { + it ("should pass multi item voting test", async function () { + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initialize(); + + // create token class 0, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // create token class 1, voting weight 1, dividend weight 1 + await votingTestSingleTest.runProgramDirectly({ + programOperatorAddress: target0, + notes: "create token class", + operations: [{ + operatorAddress: target0, + opcode: 2, // create token class + param: { + STRING_ARRAY: ["C1", "C2", "C3"], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [ BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + ], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }, + { + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(1), BigNumber.from(1), BigNumber.from(1), BigInt(2),BigInt(2),BigInt(2), BigInt(3)], + [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3), BigInt(1), BigInt(2), BigInt(3), BigInt(4)], + ], + ADDRESS_2DARRAY: [ + [target0,target1, target2, target0, target1, target2, target3], + ], + BYTES: [] + } + }], + }, false); + + // weight of target0 = 1*1 + 5*1 = 6 + // weight of target1 = 2*1 + 5*2 = 12 + // weight of target2 = 3*1 + 5*3 = 18 + // weight of target3 = 10*4 = 40 + // total weight is 6+12+18+40 = 76 + + // add a voting rule index 0 that ask 51% to approve in absolute majority + await votingTestSingleTest.addVotingRule({ + + // class 1, 2, 3 can vote + votingTokenClassList: [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + approvalThresholdPercentage: BigNumber.from(51), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false); + + + // add a voting rule index 1, 20% to approve in absolute majority + await votingTestSingleTest.addVotingRule( + { + + // class 1 can vote + votingTokenClassList: [BigNumber.from(1)], + approvalThresholdPercentage: BigNumber.from(20), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "51% to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false + ) + + // add a voting rule index 2, 99% to approve in absolute majority + await votingTestSingleTest.addVotingRule( + { + + // class 1 can vote + votingTokenClassList: [BigNumber.from(2)], + approvalThresholdPercentage: BigNumber.from(99), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "99% token 1 to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false + ) + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(1), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(2), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(1), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0)], + [BigNumber.from(100000)], // amount = 100000 + ], + ADDRESS_2DARRAY: [ + [target0], + ], + BYTES: [] + } + }], + }; + + await votingTestSingleTest.testRuntimeEntrance(program); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + + // make sure that total voting power is 400 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).totalPower[0].toString()).to.equal("76"); // should be 76 + + // target 0 vote, 6/76 voted + await letAddressVote(target0, votingTestSingleTest.address); + + + + const indexLatestVotingItem = await votingTestSingleTest.latestVotingItemIndex(); + + // // get token number of target0 at level 1 and 2 + // console.log(await votingTestSingleTest.getTokenOwnerBalance(1, target0)); + // console.log(await votingTestSingleTest.getTokenOwnerBalance(2, target0)); + + // console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) + + const powerOfTarget0 = await votingTestSingleTest.getVoterPowerOfVotingRule(0, target0); + // console.log("power of target0 is ", powerOfTarget0.toString()); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(indexLatestVotingItem)).powerNo[0].toString()).to.equal("6"); // should be 6 + + + // target 1 vote, 18/76 voted + await letAddressVote(target1, votingTestSingleTest.address); + + + + // make sure that current state is back to idle + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("1"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerNo[0].toString()).to.equal("18"); + + + }); +}) diff --git a/darc-protocol/test/votingTest/multiItemVotingTest.ts b/darc-protocol/test/votingTest/multiItemVotingTest.ts new file mode 100644 index 0000000..bd9365a --- /dev/null +++ b/darc-protocol/test/votingTest/multiItemVotingTest.ts @@ -0,0 +1,342 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; +import { ConditionNodeStruct, ProgramStruct } from "../../typechain-types/contracts/protocol/DARC" + + +const target0 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + +const target1 = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; + +const target2 = '0x90F79bf6EB2c4f870365E785982E1f101E93b906'; + +const target3 = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'; + +async function letAddressVote(operatorAddress:string,darcAddress:string) { + const currentSigner = ethers.provider.getSigner(operatorAddress); + const currentDARC = (await ethers.getContractFactory("TestBaseContract")).attach(darcAddress).connect(currentSigner); + + const votingProgram: ProgramStruct = { + programOperatorAddress: operatorAddress, + notes: "vote", + operations: [{ + operatorAddress: operatorAddress, + opcode: 32, // vote + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [true, true, true, true], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }], + } + + await currentDARC.testRuntimeEntrance(votingProgram); +} + +describe("multi item voting test", function () { + it ("should pass multi item voting test", async function () { + const VotingTestFactory = await ethers.getContractFactory("TestBaseContract"); + const votingTestSingleTest = await VotingTestFactory.deploy(); + await votingTestSingleTest.deployed(); + await votingTestSingleTest.initialize(); + + // create token class 0, mint tokens to target0, target1, target2 + await votingTestSingleTest.helper_createToken0AndMint(); + + // add a before-op plugin to ask all operations as sandbox_needed + await votingTestSingleTest.addBeforeOpPlugin({ + returnType: BigNumber.from(1), // sandbox needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all sandbox_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: true, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + // create token class 1, voting weight 1, dividend weight 1 + await votingTestSingleTest.runProgramDirectly({ + programOperatorAddress: target0, + notes: "create token class", + operations: [{ + operatorAddress: target0, + opcode: 2, // create token class + param: { + STRING_ARRAY: ["C1", "C2", "C3"], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [ BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + [BigNumber.from(1), BigNumber.from(5), BigNumber.from(10)], + ], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }, + { + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(1), BigNumber.from(1), BigNumber.from(1), BigInt(2),BigInt(2),BigInt(2), BigInt(3)], + [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3), BigInt(1), BigInt(2), BigInt(3), BigInt(4)], + ], + ADDRESS_2DARRAY: [ + [target0,target1, target2, target0, target1, target2, target3], + ], + BYTES: [] + } + }], + }, false); + + // weight of target0 = 1*1 + 5*1 = 6 + // weight of target1 = 2*1 + 5*2 = 12 + // weight of target2 = 3*1 + 5*3 = 18 + // weight of target3 = 10*4 = 40 + // total weight is 6+12+18+40 = 76 + + // add a voting rule index 0 that ask 51% to approve in absolute majority + await votingTestSingleTest.addVotingRule({ + + // class 1, 2, 3 can vote + votingTokenClassList: [BigNumber.from(1), BigNumber.from(2), BigNumber.from(3)], + approvalThresholdPercentage: BigNumber.from(51), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "50% to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false); + + + // add a voting rule index 1, 20% to approve in absolute majority + await votingTestSingleTest.addVotingRule( + { + + // class 1 can vote + votingTokenClassList: [BigNumber.from(1)], + approvalThresholdPercentage: BigNumber.from(20), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "51% to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false + ) + + // add a voting rule index 2, 99% to approve in absolute majority + await votingTestSingleTest.addVotingRule( + { + + // class 1 can vote + votingTokenClassList: [BigNumber.from(2)], + approvalThresholdPercentage: BigNumber.from(99), // 51% to approve (absolute majority) + votingDurationInSeconds: BigNumber.from(1000), + executionPendingDurationInSeconds: BigNumber.from(1000), + bIsEnabled: true, + notes: "99% token 1 to approve in absolute majority", + bIsAbsoluteMajority: true, + }, false + ) + + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(0), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(1), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(2), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + //add an after-op plugin that ask all operation to be voting_neede by vote rule 0 + await votingTestSingleTest.addAfterOpPlugin({ + returnType: BigNumber.from(3), // voting needed + level: BigNumber.from(3), + votingRuleIndex: BigNumber.from(1), + notes: "all voting_needed", + bIsEnabled: true, + bIsInitialized: true, + bIsBeforeOperation: false, + conditionNodes:[{ + id: BigNumber.from(0), + nodeType: BigNumber.from(3), // bool true + logicalOperator: BigNumber.from(0), // no operator + conditionExpression: BigNumber.from(0), // always true + childList: [], + param: { + STRING_ARRAY: [], + UINT256_2DARRAY: [], + ADDRESS_2DARRAY: [], + BYTES: [] + } + }] + }); + + + const program = { + programOperatorAddress: target0, + notes: "mint tokens", + operations: [{ + operatorAddress: target0, + opcode: 1, // mint token + param: { + STRING_ARRAY: [], + BOOL_ARRAY: [], + VOTING_RULE_ARRAY: [], + PARAMETER_ARRAY: [], + PLUGIN_ARRAY: [], + UINT256_2DARRAY: [ + [BigNumber.from(0)], + [BigNumber.from(100000)], // amount = 100000 + ], + ADDRESS_2DARRAY: [ + [target0], + ], + BYTES: [] + } + }], + }; + + await votingTestSingleTest.testRuntimeEntrance(program); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + + // make sure that total voting power is 400 + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).totalPower[0].toString()).to.equal("76"); // should be 76 + + // target 0 vote, 6/76 voted + await letAddressVote(target0, votingTestSingleTest.address); + + + + const indexLatestVotingItem = await votingTestSingleTest.latestVotingItemIndex(); + + // // get token number of target0 at level 1 and 2 + // console.log(await votingTestSingleTest.getTokenOwnerBalance(1, target0)); + // console.log(await votingTestSingleTest.getTokenOwnerBalance(2, target0)); + + // console.log(await votingTestSingleTest.getVotingItemsByIndex(1)) + + const powerOfTarget0 = await votingTestSingleTest.getVoterPowerOfVotingRule(0, target0); + // console.log("power of target0 is ", powerOfTarget0.toString()); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(indexLatestVotingItem)).powerYes[0].toString()).to.equal("6"); // should be 6 + + + // target 1 vote, 18/76 voted + await letAddressVote(target1, votingTestSingleTest.address); + + + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("18"); + + + // target 2 vote, 36/76 voted + await letAddressVote(target2, votingTestSingleTest.address); + + // make sure that current state is voting + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("2"); // should be FiniteState.VOTING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("36"); + + // target 3 vote, 76/76 voted + await letAddressVote(target3, votingTestSingleTest.address); + + expect((await votingTestSingleTest.finiteState()).toString()).to.equal("3"); // should be FiniteState.EXECUTION_PENDING + expect((await votingTestSingleTest.getVotingItemsByIndex(1)).powerYes[0].toString()).to.equal("76"); + }); +}) diff --git a/darc-protocol/test/votingTest/singleVoteNegativeTest.ts b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts index 8f5f878..e84494d 100644 --- a/darc-protocol/test/votingTest/singleVoteNegativeTest.ts +++ b/darc-protocol/test/votingTest/singleVoteNegativeTest.ts @@ -48,14 +48,14 @@ describe("single voting netative test", function () { }] }); - // add a voting rule that ask 50% to approve in relative majority + // add a voting rule that ask 50% to approve in absolute majority await votingTestSingleTest.addVotingRule({ votingTokenClassList: [BigNumber.from(0)], approvalThresholdPercentage: BigNumber.from(50), votingDurationInSeconds: BigNumber.from(1000), executionPendingDurationInSeconds: BigNumber.from(1000), bIsEnabled: true, - notes: "50% to approve in relative majority", + notes: "50% to approve in absolute majority", bIsAbsoluteMajority: true, }, false); diff --git a/darc-protocol/test/votingTest/singleVotingTest.ts b/darc-protocol/test/votingTest/singleVotingTest.ts index 2f09f41..16a3e17 100644 --- a/darc-protocol/test/votingTest/singleVotingTest.ts +++ b/darc-protocol/test/votingTest/singleVotingTest.ts @@ -48,14 +48,14 @@ describe("single voting test", function () { }] }); - // add a voting rule that ask 50% to approve in relative majority + // add a voting rule that ask 50% to approve in absolute majority await votingTestSingleTest.addVotingRule({ votingTokenClassList: [BigNumber.from(0)], approvalThresholdPercentage: BigNumber.from(50), votingDurationInSeconds: BigNumber.from(1000), executionPendingDurationInSeconds: BigNumber.from(1000), bIsEnabled: true, - notes: "50% to approve in relative majority", + notes: "50% to approve in absolute majority", bIsAbsoluteMajority: true, }, false);