From cc6be2fb153114f6b9857460008f9d0afca5c31e Mon Sep 17 00:00:00 2001 From: benk10 Date: Sun, 22 Mar 2020 10:32:12 +0200 Subject: [PATCH] Add ArcUpgradeScheme (#718) * Add ArcUpgradeScheme * Remove .DS_Store * Size limits and fixes * lint * Rename UpgradeScheme -> ControllerUpgradeScheme * Bump version * Use loop for test max contracts upgrade * Fix test * Update execute upgrade scheme * Fix execute * fix tests --- .gitignore | 1 + contracts/libs/Bytes32ToStr.sol | 21 + contracts/schemes/ControllerUpgradeScheme.sol | 166 +++++++ contracts/schemes/UpgradeScheme.sol | 188 ++++---- contracts/utils/DAOFactory.sol | 24 +- .../universalSchemes/UpgradeScheme.md | 12 +- mkdocs.yml | 2 +- package-lock.json | 2 +- package.json | 2 +- test/.DS_Store | Bin 14340 -> 0 bytes test/controllerupgradescheme.js | 239 ++++++++++ test/helpers.js | 9 +- test/upgradescheme.js | 448 +++++++++++------- 13 files changed, 815 insertions(+), 299 deletions(-) create mode 100644 contracts/libs/Bytes32ToStr.sol create mode 100644 contracts/schemes/ControllerUpgradeScheme.sol delete mode 100644 test/.DS_Store create mode 100644 test/controllerupgradescheme.js diff --git a/.gitignore b/.gitignore index 3073c6fe..525b11fc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ node_modules/ .vscode/ site/ yarn* +.DS_Store diff --git a/contracts/libs/Bytes32ToStr.sol b/contracts/libs/Bytes32ToStr.sol new file mode 100644 index 00000000..aff5a9ed --- /dev/null +++ b/contracts/libs/Bytes32ToStr.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.16; + +library Bytes32ToStr { + function toStr(bytes32 x) internal pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + uint j; + for (j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } +} \ No newline at end of file diff --git a/contracts/schemes/ControllerUpgradeScheme.sol b/contracts/schemes/ControllerUpgradeScheme.sol new file mode 100644 index 00000000..caf8ed4c --- /dev/null +++ b/contracts/schemes/ControllerUpgradeScheme.sol @@ -0,0 +1,166 @@ +pragma solidity ^0.5.16; + +import "@daostack/infra-experimental/contracts/votingMachines/IntVoteInterface.sol"; +import "@daostack/infra-experimental/contracts/votingMachines/ProposalExecuteInterface.sol"; +import "../votingMachines/VotingMachineCallbacks.sol"; +import "@openzeppelin/upgrades/contracts/Initializable.sol"; + +/** + * @title A scheme to manage the upgrade of an organization. + * @dev The scheme is used to upgrade the controller of an organization to a new controller. + */ + +contract ControllerUpgradeScheme is Initializable, VotingMachineCallbacks, ProposalExecuteInterface { + + event NewControllerUpgradeProposal( + address indexed _avatar, + bytes32 indexed _proposalId, + address indexed _intVoteInterface, + address _newController, + string _descriptionHash + ); + + event ChangeControllerUpgradeSchemeProposal( + address indexed _avatar, + bytes32 indexed _proposalId, + address indexed _intVoteInterface, + address _newControllerUpgradeScheme, + string _descriptionHash + ); + + event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param); + event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId); + + // Details of an upgrade proposal: + struct UpgradeProposal { + address upgradeContract; // Either the new controller we upgrade to, or the new upgrading scheme. + uint256 proposalType; // 1: Upgrade controller, 2: change upgrade scheme. + } + + mapping(bytes32=>UpgradeProposal) public organizationProposals; + + IntVoteInterface public votingMachine; + bytes32 public voteParams; + Avatar public avatar; + + /** + * @dev initialize + * @param _avatar the avatar this scheme referring to. + * @param _votingMachine the voting machines address to + * @param _voteParams voting machine parameters. + */ + function initialize( + Avatar _avatar, + IntVoteInterface _votingMachine, + bytes32 _voteParams + ) + external + initializer + { + require(_avatar != Avatar(0), "avatar cannot be zero"); + avatar = _avatar; + votingMachine = _votingMachine; + voteParams = _voteParams; + } + + /** + * @dev execution of proposals, can only be called by the voting machine in which the vote is held. + * @param _proposalId the ID of the voting in the voting machine + * @param _param a parameter of the voting result, 1 yes and 2 is no. + */ + function executeProposal(bytes32 _proposalId, int256 _param) external onlyVotingMachine(_proposalId) returns(bool) { + UpgradeProposal memory proposal = organizationProposals[_proposalId]; + require(proposal.proposalType != 0); + delete organizationProposals[_proposalId]; + emit ProposalDeleted(address(avatar), _proposalId); + // Check if vote was successful: + if (_param == 1) { + + // Define controller and get the params: + Controller controller = Controller(avatar.owner()); + // Upgrading controller: + if (proposal.proposalType == 1) { + require(controller.upgradeController(proposal.upgradeContract)); + } + + // Changing upgrade scheme: + if (proposal.proposalType == 2) { + bytes4 permissions = controller.schemesPermissions(address(this)); + require( + controller.registerScheme(proposal.upgradeContract, permissions) + ); + if (proposal.upgradeContract != address(this)) { + require(controller.unregisterSelf()); + } + } + } + emit ProposalExecuted(address(avatar), _proposalId, _param); + return true; + } + + /** + * @dev propose an upgrade of the organization's controller + * @param _newController address of the new controller that is being proposed + * @param _descriptionHash proposal description hash + * @return an id which represents the proposal + */ + function proposeUpgrade(address _newController, string memory _descriptionHash) + public + returns(bytes32) + { + bytes32 proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar)); + UpgradeProposal memory proposal = UpgradeProposal({ + proposalType: 1, + upgradeContract: _newController + }); + organizationProposals[proposalId] = proposal; + emit NewControllerUpgradeProposal( + address(avatar), + proposalId, + address(votingMachine), + _newController, + _descriptionHash + ); + proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({ + blockNumber:block.number, + avatar:avatar + }); + return proposalId; + } + + /** + * @dev propose to replace this scheme by another controller upgrading scheme + * @param _scheme address of the new upgrading scheme + * @param _descriptionHash proposal description hash + * @return an id which represents the proposal + */ + function proposeChangeControllerUpgradingScheme( + address _scheme, + string memory _descriptionHash + ) + public + returns(bytes32) + { + bytes32 proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar)); + require(organizationProposals[proposalId].proposalType == 0); + + UpgradeProposal memory proposal = UpgradeProposal({ + proposalType: 2, + upgradeContract: _scheme + }); + organizationProposals[proposalId] = proposal; + + emit ChangeControllerUpgradeSchemeProposal( + address(avatar), + proposalId, + address(votingMachine), + _scheme, + _descriptionHash + ); + proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({ + blockNumber:block.number, + avatar:avatar + }); + return proposalId; + } +} diff --git a/contracts/schemes/UpgradeScheme.sol b/contracts/schemes/UpgradeScheme.sol index 713df704..930c957f 100644 --- a/contracts/schemes/UpgradeScheme.sol +++ b/contracts/schemes/UpgradeScheme.sol @@ -1,58 +1,63 @@ pragma solidity ^0.5.16; import "@daostack/infra-experimental/contracts/votingMachines/IntVoteInterface.sol"; -import "@daostack/infra-experimental/contracts/votingMachines/ProposalExecuteInterface.sol"; +import "@daostack/infra-experimental/contracts/votingMachines/VotingMachineCallbacksInterface.sol"; import "../votingMachines/VotingMachineCallbacks.sol"; +import "../libs/Bytes32ToStr.sol"; import "@openzeppelin/upgrades/contracts/Initializable.sol"; +import "@openzeppelin/upgrades/contracts/application/Package.sol"; +import "@openzeppelin/upgrades/contracts/application/ImplementationProvider.sol"; + /** - * @title A scheme to manage the upgrade of an organization. - * @dev The scheme is used to upgrade the controller of an organization to a new controller. + * @title UpgradeScheme. + * @dev A scheme for proposing updates */ - -contract UpgradeScheme is Initializable, VotingMachineCallbacks, ProposalExecuteInterface { +contract UpgradeScheme is VotingMachineCallbacks, ProposalExecuteInterface, Initializable { + using Bytes32ToStr for bytes32; event NewUpgradeProposal( address indexed _avatar, bytes32 indexed _proposalId, - address indexed _intVoteInterface, - address _newController, - string _descriptionHash + uint64[3] _packageVersion, + bytes32[] _contractsNames, + address[] _contractsToUpgrade, + string _descriptionHash ); - event ChangeUpgradeSchemeProposal( + event ProposalExecuted( address indexed _avatar, - bytes32 indexed _proposalId, - address indexed _intVoteInterface, - address _newUpgradeScheme, - string _descriptionHash + bytes32 indexed _proposalId ); - event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param); event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId); - // Details of an upgrade proposal: - struct UpgradeProposal { - address upgradeContract; // Either the new controller we upgrade to, or the new upgrading scheme. - uint256 proposalType; // 1: Upgrade controller, 2: change upgrade scheme. + // Details of a voting proposal: + struct Proposal { + uint64[3] packageVersion; + bytes32[] contractsNames; + address[] contractsToUpgrade; + bool exist; } - mapping(bytes32=>UpgradeProposal) public organizationProposals; + mapping(bytes32=>Proposal) public organizationProposals; IntVoteInterface public votingMachine; bytes32 public voteParams; Avatar public avatar; + Package public package; /** * @dev initialize - * @param _avatar the avatar this scheme referring to. + * @param _avatar the avatar to mint reputation from * @param _votingMachine the voting machines address to * @param _voteParams voting machine parameters. */ function initialize( Avatar _avatar, IntVoteInterface _votingMachine, - bytes32 _voteParams + bytes32 _voteParams, + Package _package ) external initializer @@ -61,106 +66,99 @@ contract UpgradeScheme is Initializable, VotingMachineCallbacks, ProposalExecute avatar = _avatar; votingMachine = _votingMachine; voteParams = _voteParams; + package = _package; } /** * @dev execution of proposals, can only be called by the voting machine in which the vote is held. * @param _proposalId the ID of the voting in the voting machine - * @param _param a parameter of the voting result, 1 yes and 2 is no. + * @param _decision a parameter of the voting result, 1 yes and 2 is no. + * @return bool success */ - function executeProposal(bytes32 _proposalId, int256 _param) external onlyVotingMachine(_proposalId) returns(bool) { - UpgradeProposal memory proposal = organizationProposals[_proposalId]; - require(proposal.proposalType != 0); - delete organizationProposals[_proposalId]; - emit ProposalDeleted(address(avatar), _proposalId); - // Check if vote was successful: - if (_param == 1) { - - // Define controller and get the params: - Controller controller = Controller(avatar.owner()); - // Upgrading controller: - if (proposal.proposalType == 1) { - require(controller.upgradeController(proposal.upgradeContract)); - } - - // Changing upgrade scheme: - if (proposal.proposalType == 2) { - bytes4 permissions = controller.schemesPermissions(address(this)); - require( - controller.registerScheme(proposal.upgradeContract, permissions) + function executeProposal(bytes32 _proposalId, int256 _decision) + external + onlyVotingMachine(_proposalId) + returns(bool) { + Proposal storage proposal = organizationProposals[_proposalId]; + require(proposal.exist, "must be a live proposal"); + + if (_decision == 1) { + proposal.exist = false; + address[] memory contractsToUpgrade = proposal.contractsToUpgrade; + for (uint256 i = 0; i < contractsToUpgrade.length; i++) { + bytes32 contractNameBytes = proposal.contractsNames[i]; + string memory contractName = contractNameBytes.toStr(); + address updatedImp = ImplementationProvider( + package.getContract(proposal.packageVersion) + ).getImplementation(contractName); + + Controller controller = Controller(avatar.owner()); + controller.genericCall( + contractsToUpgrade[i], + abi.encodeWithSignature("upgradeTo(address)", updatedImp), + 0 ); - if (proposal.upgradeContract != address(this)) { - require(controller.unregisterSelf()); - } } } - emit ProposalExecuted(address(avatar), _proposalId, _param); + + delete organizationProposals[_proposalId]; + emit ProposalDeleted(address(avatar), _proposalId); + emit ProposalExecuted(address(avatar), _proposalId); return true; } /** - * @dev propose an upgrade of the organization's controller - * @param _newController address of the new controller that is being proposed - * @param _descriptionHash proposal description hash + * @dev propose upgrade contracts Arc version + * The function trigger NewUpgradeProposal event + * @param _packageVersion - the new Arc version to use for the contracts + * @param _contractsNames - names of contracts which needs to be upgraded + * @param _contractsToUpgrade - addresses of contracts which needs to be upgraded + * @param _descriptionHash - proposal description hash * @return an id which represents the proposal */ - function proposeUpgrade(address _newController, string memory _descriptionHash) - public - returns(bytes32) + function proposeUpgrade( + uint64[3] memory _packageVersion, + bytes32[] memory _contractsNames, + address[] memory _contractsToUpgrade, + string memory _descriptionHash) + public + returns(bytes32) { + require(_contractsNames.length <= 60, "can upgrade up to 60 contracts at a time"); + require( + _contractsNames.length == _contractsToUpgrade.length, + "upgrade name and address arrays must have equal lengths" + ); + require(package.hasVersion(_packageVersion), "Specified version doesn't exist in the Package"); + for (uint256 i = 0; i < _contractsToUpgrade.length; i++) { + require( + ImplementationProvider( + package.getContract(_packageVersion) + ).getImplementation(_contractsNames[i].toStr()) != address(0), + "Contract name does not exist in ArcHive package" + ); + } + bytes32 proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar)); - UpgradeProposal memory proposal = UpgradeProposal({ - proposalType: 1, - upgradeContract: _newController + + organizationProposals[proposalId] = Proposal({ + packageVersion: _packageVersion, + contractsNames: _contractsNames, + contractsToUpgrade: _contractsToUpgrade, + exist: true }); - organizationProposals[proposalId] = proposal; - emit NewUpgradeProposal( - address(avatar), - proposalId, - address(votingMachine), - _newController, - _descriptionHash - ); proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({ blockNumber:block.number, avatar:avatar }); - return proposalId; - } - - /** - * @dev propose to replace this scheme by another upgrading scheme - * @param _scheme address of the new upgrading scheme - * @param _descriptionHash proposal description hash - * @return an id which represents the proposal - */ - function proposeChangeUpgradingScheme( - address _scheme, - string memory _descriptionHash - ) - public - returns(bytes32) - { - bytes32 proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar)); - require(organizationProposals[proposalId].proposalType == 0); - - UpgradeProposal memory proposal = UpgradeProposal({ - proposalType: 2, - upgradeContract: _scheme - }); - organizationProposals[proposalId] = proposal; - - emit ChangeUpgradeSchemeProposal( + emit NewUpgradeProposal( address(avatar), proposalId, - address(votingMachine), - _scheme, + _packageVersion, + _contractsNames, + _contractsToUpgrade, _descriptionHash ); - proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({ - blockNumber:block.number, - avatar:avatar - }); return proposalId; } } diff --git a/contracts/utils/DAOFactory.sol b/contracts/utils/DAOFactory.sol index b30122d0..966e05bd 100644 --- a/contracts/utils/DAOFactory.sol +++ b/contracts/utils/DAOFactory.sol @@ -7,12 +7,14 @@ import "@openzeppelin/upgrades/contracts/upgradeability/ProxyAdmin.sol"; import "@openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol"; import "solidity-bytes-utils/contracts/BytesLib.sol"; import "../controller/Controller.sol"; +import "../libs/Bytes32ToStr.sol"; import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol"; contract DAOFactory is Initializable { using BytesLib for bytes; using SafeMath for uint256; + using Bytes32ToStr for bytes32; event NewOrg ( address indexed _avatar, @@ -216,10 +218,10 @@ contract DAOFactory is Initializable { uint256 startIndex = 0; for (uint256 i = 0; i < _schemesNames.length; i++) { address scheme = address(createInstance(locks[_avatar].packageVersion, - bytes32ToStr(_schemesNames[i]), + _schemesNames[i].toStr(), _avatar, _schemesData.slice(startIndex, _schemesInitilizeDataLens[i]))); - emit SchemeInstance(scheme, bytes32ToStr(_schemesNames[i])); + emit SchemeInstance(scheme, _schemesNames[i].toStr()); controller.registerScheme(scheme, _permissions[i]); startIndex = startIndex.add(_schemesInitilizeDataLens[i]); } @@ -231,24 +233,6 @@ contract DAOFactory is Initializable { emit InitialSchemesSet(_avatar); } - function bytes32ToStr(bytes32 x) private pure returns (string memory) { - bytes memory bytesString = new bytes(32); - uint charCount = 0; - uint j; - for (j = 0; j < 32; j++) { - byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); - if (char != 0) { - bytesString[charCount] = char; - charCount++; - } - } - bytes memory bytesStringTrimmed = new bytes(charCount); - for (j = 0; j < charCount; j++) { - bytesStringTrimmed[j] = bytesString[j]; - } - return string(bytesStringTrimmed); - } - /** * @dev Create a new organization * @param _orgName The name of the new organization diff --git a/docs/generated_docs/universalSchemes/UpgradeScheme.md b/docs/generated_docs/universalSchemes/UpgradeScheme.md index d1e003ed..cc39956e 100644 --- a/docs/generated_docs/universalSchemes/UpgradeScheme.md +++ b/docs/generated_docs/universalSchemes/UpgradeScheme.md @@ -1,5 +1,5 @@ -# UpgradeScheme -[see the source](https://github.com/daostack/arc/tree/master/contracts/universalSchemes/UpgradeScheme.sol) +# ControllerUpgradeScheme +[see the source](https://github.com/daostack/arc/tree/master/contracts/universalSchemes/ControllerUpgradeScheme.sol) > A scheme to manage the upgrade of an organization. @@ -15,7 +15,7 @@ ## Events -### ChangeUpgradeSchemeProposal(address,bytes32,address,address,bytes32) +### ChangeControllerUpgradeSchemeProposal(address,bytes32,address,address,bytes32) **Execution cost**: No bound available @@ -26,11 +26,11 @@ Params: 1. **_avatar** *of type `address`* 2. **_proposalId** *of type `bytes32`* 3. **_intVoteInterface** *of type `address`* -4. **_newUpgradeScheme** *of type `address`* +4. **_newControllerUpgradeScheme** *of type `address`* 5. **_params** *of type `bytes32`* --- -### NewUpgradeProposal(address,bytes32,address,address) +### NewControllerUpgradeProposal(address,bytes32,address,address) **Execution cost**: No bound available @@ -202,7 +202,7 @@ Returns: 2. **intVote** *of type `address`* --- -### proposeChangeUpgradingScheme(address,address,bytes32) +### proposeChangeControllerUpgradingScheme(address,address,bytes32) > > propose to replace this scheme by another upgrading scheme diff --git a/mkdocs.yml b/mkdocs.yml index a5f98264..bfc8a879 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,7 +66,7 @@ pages: - SimpleICO: 'generated_docs/universalSchemes/SimpleICO.md' - UniversalScheme: 'generated_docs/universalSchemes/UniversalScheme.md' - UniversalSchemeInterface: 'generated_docs/universalSchemes/UniversalSchemeInterface.md' - - UpgradeScheme: 'generated_docs/universalSchemes/UpgradeScheme.md' + - ControllerUpgradeScheme: 'generated_docs/universalSchemes/ControllerUpgradeScheme.md' - VestingScheme: 'generated_docs/universalSchemes/VestingScheme.md' - VoteInOrganizationScheme: 'generated_docs/universalSchemes/VoteInOrganizationScheme.md' - 'Voting Machines': diff --git a/package-lock.json b/package-lock.json index c8120fc3..b89f4bc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc-experimental", - "version": "0.1.1-rc.5", + "version": "0.1.1-rc.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b2f926f6..2620cc57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc-experimental", - "version": "0.1.1-rc.5", + "version": "0.1.1-rc.6", "description": "A platform for building DAOs", "files": [ "contracts/", diff --git a/test/.DS_Store b/test/.DS_Store deleted file mode 100644 index 99e914355d9772aabe456d96efc925cb2028d6be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14340 zcmeI2&2JJx6u{qVO*Ab^Oxm=KCLBE5YI^X94SF(qXw<8e(uzeOu+)zeH~(#aZ)T^^ z!Y=GW%n)bE?7-~2*?GUb`FM{Jk?44M_Cn-PMDCL`FK==CBT~_P;?n1urmnk`M5H4U z-^(H$X-Z#O(vZPa?dgI$6si0MsVo=BPRcjrD<$7a&*hs8dhzM|;UMlWQrGliAO~`4 z+Psvy4CS1*>b&7VYSN{L;R*x-fj}S-2n7C50=o8c-QTo%xCa7(Kp=2UK<9@FN%Lxw zk8EzH4sN=I*XO#0Hy7XW158;Z$*V~|vbhmO#)y(z(I{~-A}daLMXQrnlYC@zD=v{0 zmneC2lz1qSc{_3yQ(dB}&BHwq2n41H>{Ijv-@A=Ze2RbE#xw5XB@ZOVldt2;cg(jY zbz0Y@{cE56UE-6!l@s}8o~zG(AfITZjn}RIyL#d^+KNdB@{!*fC79=*TUmX(*8EI+ zeYrF(y_OG@X=@ErQs+s7C$V`~%(vxqa&d;jTzLPkA*%gTf*RY1iDw+q)28&`qP>rK zrmS6j!&jC?ba4!G%ffE|7P}ZqmvHN>DZirnIEIDf;kW~_CL=>_t)Ez)j&L38jWx2r zym~lpx#ZwgVr1&9rSZZBW3(?<@bi6iX7}2$$f#v3W!AiO4uLK+_;~#J&SEFA@|*K( zx*m>BE;)E@vHsMFC^z}&+VheCy6`}u>$2a?r-kE_Lkd!R2`TFx=s%M>x9Tt5 zvDsak`eW)r47?vl#k6tc3P?jRT&p0kMZ>qYu*7qffoG@tPl4Ig3$>gV*ZeYFQ?6Ta&@uisAV z%zEhm%;>M9Ue})?EQYK(HcH^)S7RS=Jx9tab#z=;)~1<{Y}{3&t}P<5>cQIRpw7!b z7=L|sm%dx%_O&G~qJ`s_Lkd#63%#Q$KOyKluOhlQYPn@$SDIj_k*+qj<$o+cyVs6K zMlGZH8DpqR%trUrI-B+RzruI>C^6>Kdp*`XwHBIhCT)yoaNo5~@3GT3r3TtQ#iDiO zt6kfO%*}OdJy);gv~tvzXa%zSjGY#2XGMzF@n40a`Z$_}M diff --git a/test/controllerupgradescheme.js b/test/controllerupgradescheme.js new file mode 100644 index 00000000..fcb73390 --- /dev/null +++ b/test/controllerupgradescheme.js @@ -0,0 +1,239 @@ +import * as helpers from './helpers'; +const constants = require('./constants'); +const Controller = artifacts.require("./Controller.sol"); +const AbsoluteVote = artifacts.require('./AbsoluteVote.sol'); +const ControllerUpgradeScheme = artifacts.require('./ControllerUpgradeScheme.sol'); +const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); +const Avatar = artifacts.require("./Avatar.sol"); +const DAOToken = artifacts.require("./DAOToken.sol"); +const Reputation = artifacts.require("./Reputation.sol"); + +export class ControllerUpgradeSchemeParams { + constructor() { + } +} +var registration; +const setupControllerUpgradeSchemeParams = async function( + avatarAddress + ) { + var controllerUpgradeSchemeParams = new ControllerUpgradeSchemeParams(); + + controllerUpgradeSchemeParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + controllerUpgradeSchemeParams.initdata = await new web3.eth.Contract(registration.controllerUpgradeScheme.abi) + .methods + .initialize(avatarAddress, + controllerUpgradeSchemeParams.votingMachine.absoluteVote.address, + controllerUpgradeSchemeParams.votingMachine.params) + .encodeABI(); + return controllerUpgradeSchemeParams; +}; + + +const setupNewController = async function (accounts,permission='0x00000000') { + var token = await DAOToken.new(); + await token.initialize("TEST","TST",0,accounts[0]); + // set up a reputation system + var reputation = await Reputation.new(); + await reputation.initialize(accounts[0]); + var avatar = await Avatar.new('name', token.address, reputation.address); + await avatar.initialize('name', token.address, reputation.address,accounts[0]); + var _controller; + if (permission !== '0'){ + _controller = await Controller.new({from:accounts[1],gas: constants.ARC_GAS_LIMIT}); + await _controller.initialize(avatar.address,accounts[1],{from:accounts[1],gas: constants.ARC_GAS_LIMIT}); + await _controller.registerScheme(accounts[0],permission,{from:accounts[1]}); + await _controller.unregisterSelf({from:accounts[1]}); + } + else { + _controller = await Controller.new({gas: constants.ARC_GAS_LIMIT}); + await _controller.initialize(avatar.address,accounts[0]); + } + return _controller; +}; + + +const setup = async function (accounts) { + var testSetup = new helpers.TestSetup(); + testSetup.standardTokenMock = await ERC20Mock.new(accounts[1],100); + registration = await helpers.registerImplementation(); + testSetup.reputationArray = [20,40,70]; + testSetup.proxyAdmin = accounts[5]; + testSetup.org = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, + accounts, + registration, + [accounts[0], + accounts[1], + accounts[2]], + [1000,0,0], + testSetup.reputationArray); + testSetup.controllerUpgradeSchemeParams= await setupControllerUpgradeSchemeParams( + testSetup.org.avatar.address); + var permissions = "0x0000000a"; + var tx = await registration.daoFactory.setSchemes( + testSetup.org.avatar.address, + [web3.utils.fromAscii("ControllerUpgradeScheme")], + testSetup.controllerUpgradeSchemeParams.initdata, + [helpers.getBytesLength(testSetup.controllerUpgradeSchemeParams.initdata)], + [permissions], + "metaData",{from:testSetup.proxyAdmin}); + testSetup.controllerUpgradeScheme = await ControllerUpgradeScheme.at(tx.logs[1].args._scheme); + return testSetup; +}; +contract('ControllerUpgradeScheme', accounts => { + before(function() { + helpers.etherForEveryone(accounts); + }); + + it("initialize", async() => { + var controllerUpgradeScheme = await ControllerUpgradeScheme.new(); + var absoluteVote = await AbsoluteVote.new(); + await controllerUpgradeScheme.initialize(helpers.SOME_ADDRESS,absoluteVote.address,"0x1234"); + assert.equal(await controllerUpgradeScheme.votingMachine(),absoluteVote.address); + }); + + + it("proposeUpgrade log", async() => { + var testSetup = await setup(accounts); + + var newController = await setupNewController(accounts); + var tx = await testSetup.controllerUpgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "NewControllerUpgradeProposal"); + var votingMachine = await helpers.getValueFromLogs(tx, '_intVoteInterface',1); + assert.equal(votingMachine,testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.address); + }); + + it("proposeChangeControllerUpgradingScheme log", async function() { + var testSetup = await setup(accounts); + + var tx = await testSetup.controllerUpgradeScheme.proposeChangeControllerUpgradingScheme(accounts[0],helpers.NULL_HASH); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "ChangeControllerUpgradeSchemeProposal"); + var votingMachine = await helpers.getValueFromLogs(tx, '_intVoteInterface',1); + assert.equal(votingMachine,testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.address); + }); + + it("execute proposal upgrade controller -yes - proposal data delete", async function() { + var testSetup = await setup(accounts); + + var newController = await setupNewController(accounts); + assert.notEqual(newController.address,await testSetup.org.avatar.owner()); + var tx = await testSetup.controllerUpgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + //check organizationsProposals before execution + var organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],newController.address);//new contract address + assert.equal(organizationProposal[1].toNumber(),1);//proposalType + await testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + assert.equal(newController.address,await testSetup.org.avatar.owner()); + //check organizationsProposals after execution + organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address + assert.equal(organizationProposal[1],0);//proposalType + }); + + it("execute proposal upgrade controller - no decision (same for update scheme) - proposal data delete", async function() { + var testSetup = await setup(accounts); + + var newController = await setupNewController(accounts); + var tx = await testSetup.controllerUpgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + //check organizationsProposals before execution + var organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],newController.address);//new contract address + assert.equal(organizationProposal[1].toNumber(),1);//proposalType + + //Vote with reputation to trigger execution + await testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,0,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + //should not upgrade because the decision is "no" + assert.notEqual(newController.address,await testSetup.org.avatar.owner()); + //check organizationsProposals after execution + organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address + assert.equal(organizationProposal[1],0);//proposalType + }); + + it("execute proposal ChangeUpgradingScheme - yes decision - proposal data delete", async function() { + var testSetup = await setup(accounts); + + + var tx = await testSetup.controllerUpgradeScheme.proposeChangeControllerUpgradingScheme(accounts[0],helpers.NULL_HASH); + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + + //check organizationsProposals before execution + var organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],accounts[0]);//new contract address + assert.equal(organizationProposal[1].toNumber(),2);//proposalType + + //check schemes registration before execution + var controller = await Controller.at(await testSetup.org.avatar.owner()); + assert.equal(await controller.isSchemeRegistered(accounts[0]),false); + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),true); + + await testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + //check organizationsProposals after execution + organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address + assert.equal(organizationProposal[1],0);//proposalType + + //check if scheme upgraded + assert.equal(await controller.isSchemeRegistered(accounts[0]),true); + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),false); + }); + + it("execute proposal ChangeUpgradingScheme - yes decision - check approve increase fee ", async function() { + var testSetup = await setup(accounts); + + + var tx = await testSetup.controllerUpgradeScheme.proposeChangeControllerUpgradingScheme(accounts[0],helpers.NULL_HASH); + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + + //check organizationsProposals before execution + var organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],accounts[0]);//new contract address + assert.equal(organizationProposal[1].toNumber(),2);//proposalType + + //check schemes registration before execution + var controller = await Controller.at(await testSetup.org.avatar.owner()); + assert.equal(await controller.isSchemeRegistered(accounts[0]),false); + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),true); + + await testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + //check organizationsProposals after execution + organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address + assert.equal(organizationProposal[1],0);//proposalType + + //check if scheme upgraded + assert.equal(await controller.isSchemeRegistered(accounts[0]),true); + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),false); + }); + + it("execute proposal ChangeUpgradingScheme - yes decision - check upgrade it self. ", async function() { + var testSetup = await setup(accounts); + + + var tx = await testSetup.controllerUpgradeScheme.proposeChangeControllerUpgradingScheme(testSetup.controllerUpgradeScheme.address,helpers.NULL_HASH); + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + + //check schemes registration before execution + var controller = await Controller.at(await testSetup.org.avatar.owner()); + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),true); + + await testSetup.controllerUpgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + //check organizationsProposals after execution + var organizationProposal = await testSetup.controllerUpgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address + assert.equal(organizationProposal[1],0);//proposalType + + //schemes should still be registered + assert.equal(await controller.isSchemeRegistered(testSetup.controllerUpgradeScheme.address),true); + }); +}); diff --git a/test/helpers.js b/test/helpers.js index bd13f6e4..b2af611c 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -20,8 +20,9 @@ const Competition = artifacts.require("./Competition.sol"); const ContributionRewardExt = artifacts.require("./ContributionRewardExt.sol"); const SchemeRegistrar = artifacts.require("./SchemeRegistrar.sol"); const SchemeFactory = artifacts.require("./SchemeFactory.sol"); -const GenericScheme = artifacts.require("./GenericScheme.sol"); const UpgradeScheme = artifacts.require("./UpgradeScheme.sol"); +const GenericScheme = artifacts.require("./GenericScheme.sol"); +const ControllerUpgradeScheme = artifacts.require("./ControllerUpgradeScheme.sol"); const Auction4Reputation = artifacts.require("./Auction4Reputation.sol"); const LockingEth4Reputation = artifacts.require("./LockingEth4Reputation.sol"); const LockingToken4Reputation = artifacts.require("./LockingToken4Reputation.sol"); @@ -167,8 +168,9 @@ export const registrationAddVersionToPackege = async function (registration,vers registration.contributionRewardExt = await ContributionRewardExt.new(); registration.schemeRegistrar = await SchemeRegistrar.new(); registration.schemeFactory = await SchemeFactory.new(); - registration.genericScheme = await GenericScheme.new(); registration.upgradeScheme = await UpgradeScheme.new(); + registration.genericScheme = await GenericScheme.new(); + registration.controllerUpgradeScheme = await ControllerUpgradeScheme.new(); registration.auction4Reputation = await Auction4Reputation.new(); registration.lockingEth4Reputation = await LockingEth4Reputation.new(); registration.lockingToken4Reputation = await LockingToken4Reputation.new(); @@ -193,8 +195,9 @@ export const registrationAddVersionToPackege = async function (registration,vers await implementationDirectory.setImplementation("ContributionRewardExt",registration.contributionRewardExt.address); await implementationDirectory.setImplementation("SchemeRegistrar",registration.schemeRegistrar.address); await implementationDirectory.setImplementation("SchemeFactory",registration.schemeFactory.address); - await implementationDirectory.setImplementation("GenericScheme",registration.genericScheme.address); await implementationDirectory.setImplementation("UpgradeScheme",registration.upgradeScheme.address); + await implementationDirectory.setImplementation("GenericScheme",registration.genericScheme.address); + await implementationDirectory.setImplementation("ControllerUpgradeScheme",registration.controllerUpgradeScheme.address); await implementationDirectory.setImplementation("Auction4Reputation",registration.auction4Reputation.address); await implementationDirectory.setImplementation("LockingEth4Reputation",registration.lockingEth4Reputation.address); await implementationDirectory.setImplementation("LockingToken4Reputation",registration.lockingToken4Reputation.address); diff --git a/test/upgradescheme.js b/test/upgradescheme.js index d5687385..ea14392e 100644 --- a/test/upgradescheme.js +++ b/test/upgradescheme.js @@ -1,74 +1,73 @@ import * as helpers from './helpers'; -const constants = require('./constants'); -const Controller = artifacts.require("./Controller.sol"); -const AbsoluteVote = artifacts.require('./AbsoluteVote.sol'); const UpgradeScheme = artifacts.require('./UpgradeScheme.sol'); -const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); -const Avatar = artifacts.require("./Avatar.sol"); -const DAOToken = artifacts.require("./DAOToken.sol"); -const Reputation = artifacts.require("./Reputation.sol"); +const ERC20Mock = artifacts.require("./ERC20Mock.sol"); +const AdminUpgradeabilityProxy = artifacts.require("./AdminUpgradeabilityProxy.sol"); +const ImplementationProvider = artifacts.require("./ImplementationProvider.sol"); export class UpgradeSchemeParams { constructor() { } } + var registration; const setupUpgradeSchemeParams = async function( - avatarAddress + accounts, + genesisProtocol, + token, + avatarAddress, ) { - var upgradeSchemeParams = new UpgradeSchemeParams(); + var upgradeSchemeParams = new UpgradeSchemeParams(); - upgradeSchemeParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + if (genesisProtocol === true) { + upgradeSchemeParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); upgradeSchemeParams.initdata = await new web3.eth.Contract(registration.upgradeScheme.abi) .methods .initialize(avatarAddress, - upgradeSchemeParams.votingMachine.absoluteVote.address, - upgradeSchemeParams.votingMachine.params) + upgradeSchemeParams.votingMachine.genesisProtocol.address, + upgradeSchemeParams.votingMachine.params, + registration.packageInstance.address) .encodeABI(); - return upgradeSchemeParams; -}; - - -const setupNewController = async function (accounts,permission='0x00000000') { - var token = await DAOToken.new(); - await token.initialize("TEST","TST",0,accounts[0]); - // set up a reputation system - var reputation = await Reputation.new(); - await reputation.initialize(accounts[0]); - var avatar = await Avatar.new('name', token.address, reputation.address); - await avatar.initialize('name', token.address, reputation.address,accounts[0]); - var _controller; - if (permission !== '0'){ - _controller = await Controller.new({from:accounts[1],gas: constants.ARC_GAS_LIMIT}); - await _controller.initialize(avatar.address,accounts[1],{from:accounts[1],gas: constants.ARC_GAS_LIMIT}); - await _controller.registerScheme(accounts[0],permission,{from:accounts[1]}); - await _controller.unregisterSelf({from:accounts[1]}); - } - else { - _controller = await Controller.new({gas: constants.ARC_GAS_LIMIT}); - await _controller.initialize(avatar.address,accounts[0]); + } else { + upgradeSchemeParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + upgradeSchemeParams.initdata = await new web3.eth.Contract(registration.upgradeScheme.abi) + .methods + .initialize(avatarAddress, + upgradeSchemeParams.votingMachine.absoluteVote.address, + upgradeSchemeParams.votingMachine.params, + registration.packageInstance.address) + .encodeABI(); } - return _controller; + return upgradeSchemeParams; }; - -const setup = async function (accounts) { +const setup = async function (accounts,reputationAccount=0,genesisProtocol = false,tokenAddress=0) { var testSetup = new helpers.TestSetup(); testSetup.standardTokenMock = await ERC20Mock.new(accounts[1],100); registration = await helpers.registerImplementation(); - testSetup.reputationArray = [20,40,70]; + testSetup.reputationArray = [20,10,70]; + var account2; + if (reputationAccount === 0) { + account2 = accounts[2]; + } else { + account2 = reputationAccount; + } testSetup.proxyAdmin = accounts[5]; testSetup.org = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, accounts, registration, [accounts[0], accounts[1], - accounts[2]], + account2], [1000,0,0], testSetup.reputationArray); testSetup.upgradeSchemeParams= await setupUpgradeSchemeParams( - testSetup.org.avatar.address); - var permissions = "0x0000000a"; + accounts, + genesisProtocol, + tokenAddress, + testSetup.org.avatar.address, + ); + + var permissions = "0x0000001f"; var tx = await registration.daoFactory.setSchemes( testSetup.org.avatar.address, [web3.utils.fromAscii("UpgradeScheme")], @@ -76,164 +75,269 @@ const setup = async function (accounts) { [helpers.getBytesLength(testSetup.upgradeSchemeParams.initdata)], [permissions], "metaData",{from:testSetup.proxyAdmin}); + testSetup.registration = registration; testSetup.upgradeScheme = await UpgradeScheme.at(tx.logs[1].args._scheme); return testSetup; }; -contract('UpgradeScheme', accounts => { + +contract('UpgradeScheme', function(accounts) { before(function() { helpers.etherForEveryone(accounts); }); - it("initialize", async() => { - var upgradeScheme = await UpgradeScheme.new(); - var absoluteVote = await AbsoluteVote.new(); - await upgradeScheme.initialize(helpers.SOME_ADDRESS,absoluteVote.address,"0x1234"); - assert.equal(await upgradeScheme.votingMachine(),absoluteVote.address); - }); - - - it("proposeUpgrade log", async() => { + it("proposeUpgrade log", async function() { var testSetup = await setup(accounts); - var newController = await setupNewController(accounts); - var tx = await testSetup.upgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + + var tx = await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "NewUpgradeProposal"); - var votingMachine = await helpers.getValueFromLogs(tx, '_intVoteInterface',1); - assert.equal(votingMachine,testSetup.upgradeSchemeParams.votingMachine.absoluteVote.address); - }); - - it("proposeChangeUpgradingScheme log", async function() { - var testSetup = await setup(accounts); - - var tx = await testSetup.upgradeScheme.proposeChangeUpgradingScheme(accounts[0],helpers.NULL_HASH); - assert.equal(tx.logs.length, 1); - assert.equal(tx.logs[0].event, "ChangeUpgradeSchemeProposal"); - var votingMachine = await helpers.getValueFromLogs(tx, '_intVoteInterface',1); - assert.equal(votingMachine,testSetup.upgradeSchemeParams.votingMachine.absoluteVote.address); - }); - - it("execute proposal upgrade controller -yes - proposal data delete", async function() { - var testSetup = await setup(accounts); - - var newController = await setupNewController(accounts); - assert.notEqual(newController.address,await testSetup.org.avatar.owner()); - var tx = await testSetup.upgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - //check organizationsProposals before execution - var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],newController.address);//new contract address - assert.equal(organizationProposal[1].toNumber(),1);//proposalType - await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal(newController.address,await testSetup.org.avatar.owner()); - //check organizationsProposals after execution - organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address - assert.equal(organizationProposal[1],0);//proposalType - }); - - it("execute proposal upgrade controller - no decision (same for update scheme) - proposal data delete", async function() { - var testSetup = await setup(accounts); - - var newController = await setupNewController(accounts); - var tx = await testSetup.upgradeScheme.proposeUpgrade(newController.address,helpers.NULL_HASH); - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - //check organizationsProposals before execution - var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],newController.address);//new contract address - assert.equal(organizationProposal[1].toNumber(),1);//proposalType - - //Vote with reputation to trigger execution - await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,0,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - //should not upgrade because the decision is "no" - assert.notEqual(newController.address,await testSetup.org.avatar.owner()); - //check organizationsProposals after execution - organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address - assert.equal(organizationProposal[1],0);//proposalType - }); - - it("execute proposal ChangeUpgradingScheme - yes decision - proposal data delete", async function() { - var testSetup = await setup(accounts); - - - var tx = await testSetup.upgradeScheme.proposeChangeUpgradingScheme(accounts[0],helpers.NULL_HASH); - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - - //check organizationsProposals before execution - var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],accounts[0]);//new contract address - assert.equal(organizationProposal[1].toNumber(),2);//proposalType - - //check schemes registration before execution - var controller = await Controller.at(await testSetup.org.avatar.owner()); - assert.equal(await controller.isSchemeRegistered(accounts[0]),false); - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),true); - - await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - - //check organizationsProposals after execution - organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address - assert.equal(organizationProposal[1],0);//proposalType - - //check if scheme upgraded - assert.equal(await controller.isSchemeRegistered(accounts[0]),true); - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),false); }); - it("execute proposal ChangeUpgradingScheme - yes decision - check approve increase fee ", async function() { + it("execute proposeUpgrade -no decision - proposal data delete", async function() { var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); - var tx = await testSetup.upgradeScheme.proposeChangeUpgradingScheme(accounts[0],helpers.NULL_HASH); - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + var tx = await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH); - //check organizationsProposals before execution - var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],accounts[0]);//new contract address - assert.equal(organizationProposal[1].toNumber(),2);//proposalType + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,0,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + //check organizationsProposals after execution + var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal,false); + }); - //check schemes registration before execution - var controller = await Controller.at(await testSetup.org.avatar.owner()); - assert.equal(await controller.isSchemeRegistered(accounts[0]),false); - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),true); + it("execute proposeVote -positive decision - proposal data delete", async function() { + var testSetup = await setup(accounts); - await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + + var tx = await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH + ); - //check organizationsProposals after execution - organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address - assert.equal(organizationProposal[1],0);//proposalType + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal,true); - //check if scheme upgraded - assert.equal(await controller.isSchemeRegistered(accounts[0]),true); - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),false); + await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + //check organizationsProposals after execution + organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal,false); }); - it("execute proposal ChangeUpgradingScheme - yes decision - check upgrade it self. ", async function() { - var testSetup = await setup(accounts); + it("execute proposeVote -positive decision - non existing package reverts", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + try { + await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 2], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH + ); + assert(false, "cannot upgrade to non existing package version"); + } catch(error) { + helpers.assertVMException(error); + } + }); + it("execute proposeVote -positive decision - non existing contract reverts", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + try { + await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avtar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH + ); + assert(false, "cannot upgrade to non existing contract name"); + } catch(error) { + helpers.assertVMException(error); + } + }); - var tx = await testSetup.upgradeScheme.proposeChangeUpgradingScheme(testSetup.upgradeScheme.address,helpers.NULL_HASH); - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + it("execute proposeVote -positive decision - unequal array lengths reverts", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + try { + await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH + ); + assert(false, "contract arrays lengths must match"); + } catch(error) { + helpers.assertVMException(error); + } + }); - //check schemes registration before execution - var controller = await Controller.at(await testSetup.org.avatar.owner()); - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),true); + it("execute proposeVote -positive decision - too many contracts reverts", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + let contractsNames = []; + let contractsToUpgrade = []; + for (let i = 0; i < 21; i++) { + contractsNames.push(web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")); + contractsToUpgrade.push(testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address); + } + try { + await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + contractsNames, + contractsToUpgrade, + helpers.NULL_HASH + ); + assert(false, "can upgrade up to 60 contracts at a time"); + } catch(error) { + helpers.assertVMException(error); + } + }); - await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + it("execute proposeVote -positive decision - verify version upgraded", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + + let avatarProxy = await AdminUpgradeabilityProxy.at(testSetup.org.avatar.address); + let tokenProxy = await AdminUpgradeabilityProxy.at(testSetup.org.token.address); + let reputationProxy = await AdminUpgradeabilityProxy.at(testSetup.org.reputation.address); + + let oldImpAddress = await testSetup.registration.packageInstance.getContract([0,1,0]); + let oldImp = await ImplementationProvider.at(oldImpAddress); + + assert.equal( + await avatarProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("Avatar") + ); + assert.equal( + await tokenProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("DAOToken") + ); + assert.equal( + await reputationProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("Reputation") + ); + + var tx = await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + [web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")], + [testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address], + helpers.NULL_HASH + ); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); - //check organizationsProposals after execution - var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); - assert.equal(organizationProposal[0],0x0000000000000000000000000000000000000000);//new contract address - assert.equal(organizationProposal[1],0);//proposalType + await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal,false); + + let newImpAddress = await testSetup.registration.packageInstance.getContract([0,1,1]); + let newImp = await ImplementationProvider.at(newImpAddress); + + assert.equal( + await avatarProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("Avatar") + ); + assert.equal( + await tokenProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("DAOToken") + ); + assert.equal( + await reputationProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("Reputation") + ); + }); + + it("execute proposeVote -positive decision - verify version upgraded up to 60 contracts", async function() { + var testSetup = await setup(accounts); + await helpers.registrationAddVersionToPackege(registration,[0, 1, 1]); + + let avatarProxy = await AdminUpgradeabilityProxy.at(testSetup.org.avatar.address); + let tokenProxy = await AdminUpgradeabilityProxy.at(testSetup.org.token.address); + let reputationProxy = await AdminUpgradeabilityProxy.at(testSetup.org.reputation.address); + + let oldImpAddress = await testSetup.registration.packageInstance.getContract([0,1,0]); + let oldImp = await ImplementationProvider.at(oldImpAddress); + + assert.equal( + await avatarProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("Avatar") + ); + assert.equal( + await tokenProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("DAOToken") + ); + assert.equal( + await reputationProxy.implementation.call({from: testSetup.org.avatar.address}), + await oldImp.getImplementation("Reputation") + ); + + let contractsNames = []; + let contractsToUpgrade = []; + for (let i = 0; i < 20; i++) { + contractsNames.push(web3.utils.fromAscii("Avatar"),web3.utils.fromAscii("DAOToken"),web3.utils.fromAscii("Reputation")); + contractsToUpgrade.push(testSetup.org.avatar.address, testSetup.org.token.address, testSetup.org.reputation.address); + } + + var tx = await testSetup.upgradeScheme.proposeUpgrade( + [0, 1, 1], + contractsNames, + contractsToUpgrade, + helpers.NULL_HASH + ); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + await testSetup.upgradeSchemeParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var organizationProposal = await testSetup.upgradeScheme.organizationProposals(proposalId); + assert.equal(organizationProposal,false); + + let newImpAddress = await testSetup.registration.packageInstance.getContract([0,1,1]); + let newImp = await ImplementationProvider.at(newImpAddress); + + assert.equal( + await avatarProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("Avatar") + ); + assert.equal( + await tokenProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("DAOToken") + ); + assert.equal( + await reputationProxy.implementation.call({from: testSetup.org.avatar.address}), + await newImp.getImplementation("Reputation") + ); + }); + + + it("cannot init twice", async function() { + var testSetup = await setup(accounts); + + try { + await testSetup.upgradeScheme.initialize( + testSetup.org.avatar.address, + testSetup.upgradeSchemeParams.votingMachine.absoluteVote.address, + testSetup.upgradeSchemeParams.votingMachine.params, + testSetup.registration.packageInstance.address + ); + assert(false, "cannot init twice"); + } catch(error) { + helpers.assertVMException(error); + } + + }); - //schemes should still be registered - assert.equal(await controller.isSchemeRegistered(testSetup.upgradeScheme.address),true); - }); });