├── .gitattributes ├── .gitignore ├── .travis.yml ├── DAO.sol ├── DAOTokenCreationProxyTransferer.sol ├── DTHPool.sol ├── LICENSE ├── Offer.sol ├── PFOffer.sol ├── README.md ├── README_DTHPool.md ├── RewardOffer.sol ├── Token.sol ├── TokenCreation.sol ├── USNRewardPayOut.sol ├── deploy ├── README.md ├── deploy.js ├── deployOffer.js ├── interface │ ├── README.md │ ├── full.json │ ├── intermediate.json │ └── minimal.json └── prepare.py ├── libs └── oraclize.sol ├── paper ├── Biblio.bib ├── Paper.tex └── Paper.zh-CN.tex ├── simpleWithdraw.sol ├── simpleWithdrawTrustee.sol ├── tests ├── README.md ├── __init__.py ├── args.py ├── jsutils.py ├── scenarios │ ├── __init__.py │ ├── colmattack │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── run.py │ │ └── template.js │ ├── curator_halveminquorum_fueling │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── deploy │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── deploy_dao_creator_template.js │ │ ├── deploy_dao_template.js │ │ ├── deploy_dthpool_template.js │ │ ├── deploy_offer_template.js │ │ ├── deploy_pfoffer_template.js │ │ ├── deploy_usn_template.js │ │ ├── run.py │ │ └── template.js │ ├── deposit │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── run.py │ │ └── template.js │ ├── depositnofunds │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── dthpool │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── extrabalance │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── firecontractor │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── fuel │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── fuel_fail │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── fuel_fail2 │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── fuel_fail_extrabalance │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── fuel_predictive │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── multisplitrewards │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── newcontract │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── newcontractfail │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── pfoffer │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── pfoffer_checkvotestatus_fail │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── pfoffer_payment │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── proposal │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── run.py │ │ └── template.js │ ├── rewards │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── run.py │ │ └── template.js │ ├── sampleoffer_changeclient │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── sampleoffer_dailypayment │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── sampleoffer_gettersvt │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── sampleoffer_setrewardvars │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── singlesplit │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── spendall │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js │ ├── split │ │ ├── __init__.py │ │ ├── arguments.json │ │ ├── run.py │ │ └── template.js │ └── stealextrabalance │ │ ├── __init__.py │ │ ├── run.py │ │ └── template.js ├── sha3.py ├── templates │ └── accounts.template.js ├── test.py └── utils.py ├── withdraw.sol └── withdrawBlack.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Tests related chain files 2 | tests/data/chaindata/ 3 | tests/data/dapp/ 4 | tests/data/nodekey 5 | tests/data/history 6 | tests/data/keystore 7 | tests/genesis_block.json 8 | 9 | # Tests save files and output 10 | tests/data/saved 11 | tests/out.log.geth 12 | 13 | # Ignore created javascript files 14 | tests/deploy.js 15 | tests/deploy_dao_creator.js 16 | tests/deploy_dao.js 17 | tests/deploy_offer.js 18 | tests/deploy_pfoffer.js 19 | tests/deploy_usn.js 20 | tests/deploy_dthpool.js 21 | tests/deposit.js 22 | tests/depositnofunds.js 23 | tests/fund.js 24 | tests/fund_fail.js 25 | tests/fund_fail2.js 26 | tests/fuel.js 27 | tests/fuel_fail.js 28 | tests/fuel_fail2.js 29 | tests/fuel_fail_extrabalance.js 30 | tests/proposal.js 31 | tests/accounts.js 32 | tests/rewards.js 33 | tests/split.js 34 | tests/singlesplit.js 35 | tests/colmattack.js 36 | tests/newcontract.js 37 | tests/newcontractfail.js 38 | tests/multisplitrewards.js 39 | tests/spendall.js 40 | tests/extrabalance.js 41 | tests/stealextrabalance.js 42 | tests/firecontractor.js 43 | tests/dthpool.js 44 | tests/fuel_predictive.js 45 | tests/sampleoffer_dailypayment.js 46 | tests/sampleoffer_daopayownrewards.js 47 | tests/sampleoffer_setrewardvars.js 48 | tests/sampleoffer_changeclient.js 49 | tests/sampleoffer_gettersvt.js 50 | tests/curator_halveminquorum_fueling.js 51 | tests/pfoffer.js 52 | tests/pfoffer_payment.js 53 | tests/pfoffer_checkvotestatus_fail.js 54 | 55 | # Ignore deployment related file 56 | deploy/code.js 57 | deploy/prepare.js 58 | 59 | # python metadata in tests 60 | *.pyc 61 | 62 | # git ignore output of the paper generation 63 | paper/*.aux 64 | paper/*.log 65 | paper/*.out 66 | paper/*.pdf 67 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | notifications: 5 | email: false 6 | 7 | before_install: 8 | # for g++4.8 and C++11 9 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 10 | # for solc 11 | - sudo add-apt-repository -y ppa:ethereum/ethereum 12 | - sudo add-apt-repository -y ppa:ethereum/ethereum-dev 13 | 14 | # solc setup 15 | - sudo apt-get update -y -qq 16 | - sudo apt-get install -yqq solc 17 | 18 | script: cd tests && ./test.py --compile-test 19 | -------------------------------------------------------------------------------- /DAOTokenCreationProxyTransferer.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | 19 | /* 20 | * By default, token creation can be executed on behalf of another address using 21 | * the TokenCreation.sol createTokenProxy() function This contract is used as a 22 | * fall back in case an exchange doesn't implement the "add data to a 23 | * transaction" feature in a timely manner, preventing it from calling 24 | * createTokenProxy(). Calling this contract automatically triggers a call to 25 | * createTokenProxy() using the correct parameters for a given participant in 26 | * the token creation. A unique instance of such a contract would have to be 27 | * deployed per participant, usually using a middleware layer on a webserver, 28 | * for example. 29 | */ 30 | 31 | import "./TokenCreation.sol"; 32 | 33 | contract DAOTokenCreationProxyTransferer { 34 | address public owner; 35 | address public dao; 36 | 37 | //constructor 38 | function DAOTokenCreationProxyTransferer(address _owner, address _dao) { 39 | owner = _owner; 40 | dao = _dao; 41 | 42 | // just in case somebody already added values to this address, 43 | // we will forward it right now. 44 | sendValues(); 45 | } 46 | 47 | // default-function called when values are sent. 48 | function () { 49 | sendValues(); 50 | } 51 | 52 | function sendValues() { 53 | if (this.balance == 0) 54 | return; 55 | 56 | TokenCreationInterface fueling = TokenCreationInterface(dao); 57 | if (now > fueling.closingTime() || 58 | !fueling.createTokenProxy.value(this.balance)(owner)) { 59 | 60 | owner.send(this.balance); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Decentralized Autonomous Organization (DAO) Framework 2 | 3 | [![Build Status](https://travis-ci.org/slockit/DAO.png)](https://travis-ci.org/slockit/DAO) 4 | 5 | ## What is it? 6 | 7 | Note: this is currently not maintained - do not use it as is. 8 | 9 | A Standard Decentralized Autonomous Organization (DAO) framework written in Solidity to run on the Ethereum blockchain. 10 | 11 | Feel free to reuse this framework to create your own Decentralized Autonomous Organization. 12 | 13 | **Reference:** *"Decentralized autonomous organization to automate governance" -* [White Paper](https://download.slock.it/public/DAO/WhitePaper.pdf) - [Primer](https://blog.slock.it/a-primer-to-the-decentralized-autonomous-organization-dao-69fb125bd3cd) 14 | 15 | 16 | 17 | ## Disclaimer 18 | 19 | The future remains a work in progress. Our vision exists in a world where laws vary widely. It is important to remember that anyone who uses the generic DAO framework including the DAO refered to as 'The DAO' or any other DAO will do so at their own risk. One can only speculate about the legal status of DAOs worldwide. Whatever one’s personal beliefs may be, people must draw their own conclusions, relying on legal advice where appropriate. The authors are not a law firm and are not in the business of offering legal advice. 20 | 21 | **If you create a DAO it will be your DAO, and you will be responsible for its operation.** 22 | 23 | 24 | 25 | 26 | 27 | 28 | ## Overview 29 | 30 | Our Standard DAO Framework allows people to create Decentralized Autonomous Organizations (DAOs) governed by the code in this repository written immutably to the blockchain. 31 | 32 | We are making the Standard DAO Framework we developed free and open source, so it can be reused by anyone wishing to put together a transparent organization where governance and decision making systems are immutably programmed in the Ethereum blockchain. This code been reviewed by hundreds of pairs of eyes from our community and by one of the most respected auditing companies in the world, Deja Vu. 33 | 34 | This DAO model is open source under the LGPL, so it can be reused by anyone wishing to put together a transparent organization where governance and decision making system are immutably programmed in the Blockchain. 35 | 36 | Note: Although the word "contract" is used in the DAO’s framework code, the term is a programming convention and is not being used as a legal term of art. The term is a programming convention, not a representation that the code is in and of itself a legally binding and enforceable contract. If you have questions about legal enforceability, consult with legal counsel. 37 | 38 | 39 | 40 | 41 | 42 | 43 | ## Solidity files 44 | 45 | ### DAO.sol: 46 | Standard smart contract for any generated Decentralized Autonomous Organization (DAO) to automate organizational governance and decision-making. 47 | 48 | ### Token.sol: 49 | Defines the functions to check token balances, send tokens, send tokens on behalf of a 3rd party and its corresponding approval process. 50 | 51 | ### TokenCreation.sol: 52 | Token Creation contract, used by the DAO generated by the framework to sell its tokens and initialize its ether. 53 | 54 | ### SampleOffer.sol 55 | Sample Proposal from a Contractor to the DAO generated by the framework. Feel free to use as a template for your own proposal. 56 | 57 | ### ManagedAccount.sol 58 | Basic account, used by the DAO contract generated by the framework to separately manage both the rewards and the extraBalance accounts. 59 | 60 | ### DAOTokenCreationProxyTransferer.sol 61 | This contract is used as a fall back in case an exchange doesn't implement the "add data to a transaction" feature in a timely manner, preventing it from calling createTokenProxy(). 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ## Licensing 70 | The DAO framework is free software: you can redistribute it and/or modify it under the terms of the GNU lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 71 | 72 | The DAO framework is distributed in the hope that it will be useful, 73 | but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lesser General Public License for more details. 74 | 75 | A copy of the GNU lesser General Public License is included 76 | along with the DAO framework. See LICENSE. 77 | 78 | 79 | 80 | 81 | 82 | 83 | ## Additional Disclaimers 84 | 85 | NEITHER THE SOFTWARE NOR ITS CREATORS PROVIDE LEGAL ADVICE AND THIS CODE WAS NOT CREATED TO PROVIDE LEGAL ADVICE OR AS A SUBSTITUTE FOR LEGAL ADVICE. BY USING THIS CODE YOU ALSO AGREE: 86 | 87 | a. The creators of the Software and its contributors are not your lawyers. 88 | 89 | b. The Software is not a lawyer. 90 | 91 | c. Your use of the Software does not, in and of itself, create a legally binding contract in any jurisdiction and does not establish a lawyer-client relationship. Your communication with a non-lawyer will not be subject to the attorney-client privilege and (depending on your jurisdiction) may not be entitled to protection as confidential communication. 92 | 93 | d. The dissemination, distribution, or usage of this software shall not constitute the provision of legal advice within your jurisdiction. Unless you are legally authorized and licensed to do so, you will not use the Software to provide or assist in the provision of legal advice. 94 | 95 | e. You acknowledge and understand that each jurisdiction has its own particular rules regarding the practice of law. IF YOU USE THIS SOFTWARE TO PROVIDE LEGAL ADVICE YOU MAY BE SUBJECT TO CIVIL AND CRIMINAL LIABILITY. PRACTICING LAW WITHOUT A LICENSE IS A VIOLATION OF CRIMINAL LAW IN SOME JURISDICTIONS. CONSULT A LAWYER LICENSED IN YOUR JURISDICTION IF YOU HAVE ANY QUESTIONS ABOUT WHAT DOES OR DOES NOT CONSTITUTE THE PRACTICE OF LAW. 96 | 97 | f. The providers of this software neither warrant nor guarantee this software shall meet the requirements of any particular legal system to form a legally binding contract, nor it it their intention to directly or indirectly facilitate or encourage the unauthorized practice of law. 98 | 99 | g. You agree that in order for you to form a legally binding contract that you shall seek legal advice from an appropriately qualified and experienced lawyer within your jurisdiction. 100 | 101 | h. Issuance of DAO tokens may constitute the sale of securities in certain jurisdictions. Seek appropriate legal advice before deploying DAO code. 102 | 103 | 104 | -------------------------------------------------------------------------------- /README_DTHPool.md: -------------------------------------------------------------------------------- 1 | # DTHPool contract 2 | 3 | ![DTHPool](http://i.imgur.com/2vUP8tE.png) 4 | 5 | The DTHPool contract allows a DTH to lend their DAO Tokens to a delegate in order to vote in his name without losing the control of his DAO tokens. 6 | 7 | ## Normal procedures 8 | 9 | ### Delegate the votes. 10 | 11 | To delegate the votes the DTH needs to call these two functions in two separated transactions: 12 | 13 | 1. DAO.approve(DTHPoolAdress, [amount of Ð that you want to delegate]) 14 | 2. DTHPool.delegateDAOTokens([amount Ð that you want to delegate]) 15 | 16 | Once the delegation is finished, you will exchange normal DAO tokens ( Ð ) by the same 17 | number of delegate dao tokens ( dÐ ) . 18 | 19 | It is convenient that in mist you watch both contracts and both tokens. 20 | 21 | ### Undelegate the votes 22 | 23 | To undelegate the vote the DTH just call: 24 | 25 | DTHPool.undelegateDAOTokens([amount Ð that you want to undelegate]) 26 | 27 | The DTH will be able to undelegate and so restore his original DAO tokens at any time except in the last moments before the voting period of each proposal. 28 | 29 | ### Voting mecanism 30 | 31 | When a new regular proposal is made to the DAO, the delegate can set his voting intention. 32 | 33 | setVoteIntention( 34 | uint _proposalID, 35 | bool _willVote, 36 | bool _supportsProposal, 37 | string _motivation 38 | ) 39 | 40 | * **_proposalID**: the proposal that wants to set the position for. 41 | * **_willVote**: true if this contract will vote to this proposa. 42 | * **_supportsProposal**: true if it will vote to accept the proposal and false if it will not accept. 43 | * **_motivation** free text where the delegate can explain why he set this position. 44 | 45 | The delegate can not vote in split proposals. 46 | 47 | The vote will not effectivelly send the vote to the DAO until the last moment before the 48 | debatingPeriod closes. 49 | 50 | In the testing DTHPool this parameter is set to 2 minutes. 51 | 52 | The contract will make the vote automatically, no action is needed by the user. 53 | 54 | ### Query the delegate positions. 55 | 56 | Users can query the delegate position of any proposal by calling: 57 | 58 | DTHPool.proposalStatuses(proposalId) 59 | 60 | 61 | ## Test and practice 62 | 63 | In order to test and practice with DTHPool contract, I just deployed a test DAO contract and DTHPool contract in the test net. 64 | 65 | * DAO address: 0x0b1aef3ea19bd816e689b7a2a373459170bd8e6e 66 | * DTHPool address: 0x840b7ac7deb5b7441921568a3baa3572e638fecd 67 | 68 | Feel free to contact me ( @jbaylina ) or any member in the #art_of_the_dao channel in thedao.slack.com 69 | 70 | You can get an free invitation here: http://slack.slock.it:3000/ 71 | 72 | There you can get testDAOs, testEther, support and love ;) 73 | 74 | ## Q&A 75 | 76 | Q: What happens if somebody sends the DAO tokens directly to the contract? 77 | 78 | A: If the delegate is a nice guy, he can return them back to the clumsy DTH. The delegate may call fixTokens() to return those tokens back to the sender. 79 | 80 | --- 81 | Q: I want to be a delegate. How can I deploy my own contract? 82 | 83 | A: You just deploy the DTHPool with the following parameters in the constructor: 84 | 85 | * _daoAddress : The Address of the DAO token smart contract -- 0xbb9bc244d798123fde783fcc1c72d3bb8c189413 is TheDAO's 86 | * _delegate : Your own address (If you are the delegate) 87 | * _maxTimeBlocked : Time in seconds before the debate closing time when the vote will be triggered automatically. 88 | * _delegateName : Your name (optional string) 89 | * _delegateUrl : A URL for more information about yourself (optional string) 90 | * _tokenSymbol : Symbol for your delegate tokens. Example jbÐ 91 | 92 | **IMPORTANT:** The automatic timer call in _maxTimeBlocked is done via Oraclize service, so it is important that during the construct or in another transaction after the construction you send some ether to this contract. 93 | 94 | --- 95 | Q: Can a delegate change his vote intention? 96 | 97 | A: NO. They are free to change and communicate any details up until the moment before setting their intention; but, once set it cannot be changed. 98 | 99 | --- 100 | Q: How can a user vote different that his delegate? 101 | 102 | A: He must first undelegate his tokens by calling undelegateDAOTokens then cast his vote directly to the DAO. After the voting period ends for that proposal, he is able to safely delegate back his tokens. 103 | 104 | --- 105 | Q: Can the delegate tokens be transfered? 106 | 107 | A: Yes, like any other token. 108 | 109 | -------------------------------------------------------------------------------- /RewardOffer.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | 19 | /* 20 | Sample Proposal from a Contractor to the DAO including a reward towards the 21 | DAO. 22 | 23 | Feel free to use as a template for your own proposal. 24 | */ 25 | 26 | import "./Offer.sol"; 27 | 28 | contract RewardOffer is Offer { 29 | 30 | uint rewardDivisor; 31 | uint deploymentReward; 32 | 33 | function RewardOffer( 34 | address _contractor, 35 | address _client, 36 | bytes32 _hashOfTheProposalDocument, 37 | uint _totalCost, 38 | uint _initialWithdrawal, 39 | uint128 _minDailyWithdrawalLimit, 40 | uint _payoutFreezePeriod 41 | ) Offer( 42 | _contractor, 43 | _client, 44 | _hashOfTheProposalDocument, 45 | _totalCost, 46 | _initialWithdrawal, 47 | _minDailyWithdrawalLimit, 48 | _payoutFreezePeriod) { 49 | } 50 | 51 | function setRewardDivisor(uint _rewardDivisor) onlyClient noEther { 52 | rewardDivisor = _rewardDivisor; 53 | } 54 | 55 | function setDeploymentReward(uint _deploymentReward) onlyClient noEther { 56 | deploymentReward = _deploymentReward; 57 | } 58 | 59 | // non-value-transfer getters 60 | function getRewardDivisor() noEther constant returns (uint) { 61 | return rewardDivisor; 62 | } 63 | 64 | function getDeploymentReward() noEther constant returns (uint) { 65 | return deploymentReward; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Token.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | 19 | /* 20 | Basic, standardized Token contract with no "premine". Defines the functions to 21 | check token balances, send tokens, send tokens on behalf of a 3rd party and the 22 | corresponding approval process. Tokens need to be created by a derived 23 | contract (e.g. TokenCreation.sol). 24 | 25 | Thank you ConsenSys, this contract originated from: 26 | https://github.com/ConsenSys/Tokens/blob/master/Token_Contracts/contracts/Standard_Token.sol 27 | Which is itself based on the Ethereum standardized contract APIs: 28 | https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs 29 | */ 30 | 31 | /// @title Standard Token Contract. 32 | 33 | pragma solidity ^0.4.6; 34 | 35 | contract Plutocracy { 36 | function getOrModifyBlocked(address _account) returns (bool); 37 | } 38 | 39 | contract TokenInterface { 40 | mapping (address => uint256) balances; 41 | mapping (address => mapping (address => uint256)) allowed; 42 | 43 | string public name; 44 | string public symbol; 45 | uint public decimals; 46 | 47 | /// Total amount of tokens 48 | uint256 public totalSupply; 49 | 50 | /// Governance contract 51 | Plutocracy plutocracy; 52 | 53 | /// @param _owner The address from which the balance will be retrieved 54 | /// @return The balance 55 | function balanceOf(address _owner) constant returns (uint256 balance); 56 | 57 | /// @notice Send `_amount` tokens to `_to` from `msg.sender` 58 | /// @param _to The address of the recipient 59 | /// @param _amount The amount of tokens to be transferred 60 | /// @return Whether the transfer was successful or not 61 | function transfer(address _to, uint256 _amount) returns (bool success); 62 | 63 | /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it 64 | /// is approved by `_from` 65 | /// @param _from The address of the origin of the transfer 66 | /// @param _to The address of the recipient 67 | /// @param _amount The amount of tokens to be transferred 68 | /// @return Whether the transfer was successful or not 69 | function transferFrom(address _from, address _to, uint256 _amount) returns (bool success); 70 | 71 | /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on 72 | /// its behalf 73 | /// @param _spender The address of the account able to transfer the tokens 74 | /// @param _amount The amount of tokens to be approved for transfer 75 | /// @return Whether the approval was successful or not 76 | function approve(address _spender, uint256 _amount) returns (bool success); 77 | 78 | /// @param _owner The address of the account owning tokens 79 | /// @param _spender The address of the account able to transfer the tokens 80 | /// @return Amount of remaining tokens of _owner that _spender is allowed 81 | /// to spend 82 | function allowance( 83 | address _owner, 84 | address _spender 85 | ) constant returns (uint256 remaining); 86 | 87 | event Transfer(address indexed _from, address indexed _to, uint256 _amount); 88 | event Approval( 89 | address indexed _owner, 90 | address indexed _spender, 91 | uint256 _amount 92 | ); 93 | } 94 | 95 | 96 | contract Token is TokenInterface { 97 | 98 | function Token (Plutocracy _plutocracy) { 99 | plutocracy = _plutocracy; 100 | } 101 | 102 | function balanceOf(address _owner) constant returns (uint256 balance) { 103 | return balances[_owner]; 104 | } 105 | 106 | function transfer(address _to, uint256 _amount) returns (bool success) { 107 | if (balances[msg.sender] >= _amount 108 | && _amount > 0 109 | && plutocracy.getOrModifyBlocked(_to)) { 110 | 111 | balances[msg.sender] -= _amount; 112 | balances[_to] += _amount; 113 | Transfer(msg.sender, _to, _amount); 114 | return true; 115 | } else { 116 | return false; 117 | } 118 | } 119 | 120 | function transferFrom( 121 | address _from, 122 | address _to, 123 | uint256 _amount 124 | ) returns (bool success) { 125 | 126 | if (balances[_from] >= _amount 127 | && allowed[_from][msg.sender] >= _amount 128 | && _amount > 0 129 | && plutocracy.getOrModifyBlocked(_to) 130 | && plutocracy.getOrModifyBlocked(_from)) { 131 | 132 | balances[_to] += _amount; 133 | balances[_from] -= _amount; 134 | allowed[_from][msg.sender] -= _amount; 135 | Transfer(_from, _to, _amount); 136 | return true; 137 | } else { 138 | return false; 139 | } 140 | } 141 | 142 | function approve(address _spender, uint256 _amount) returns (bool success) { 143 | allowed[msg.sender][_spender] = _amount; 144 | Approval(msg.sender, _spender, _amount); 145 | return true; 146 | } 147 | 148 | function allowance(address _owner, address _spender) constant returns (uint256 remaining) { 149 | return allowed[_owner][_spender]; 150 | } 151 | } 152 | 153 | -------------------------------------------------------------------------------- /TokenCreation.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | 19 | /* 20 | * Token Creation contract, used by the DAO to create its tokens and initialize 21 | * its ether. Feel free to modify the divisor method to implement different 22 | * Token Creation parameters 23 | */ 24 | 25 | import "./Token.sol"; 26 | 27 | pragma solidity ^0.4.4; 28 | 29 | contract TokenCreationInterface { 30 | /// @dev Constructor setting the minimum fueling goal and the 31 | /// end of the Token Creation 32 | /// (the address can also create Tokens on behalf of other accounts) 33 | // This is the constructor: it can not be overloaded so it is commented out 34 | // function TokenCreation( 35 | // string _tokenName, 36 | // string _tokenSymbol, 37 | // uint _decimalPlaces 38 | // ); 39 | 40 | /// @notice Create Token with `_tokenHolder` as the initial owner of the Token 41 | /// @param _tokenHolder The address of the Tokens's recipient 42 | /// @return Whether the token creation was successful 43 | function createTokenProxy(address _tokenHolder) payable returns (bool success); 44 | event CreatedToken(address indexed to, uint amount); 45 | } 46 | 47 | 48 | contract TokenCreation is TokenCreationInterface, Token { 49 | function TokenCreation( 50 | string _tokenName, 51 | string _tokenSymbol, 52 | uint _decimalPlaces, 53 | Plutocracy _plutocracy) Token(_plutocracy) { 54 | name = _tokenName; 55 | symbol = _tokenSymbol; 56 | decimals = _decimalPlaces; 57 | } 58 | 59 | function createTokenProxy(address _tokenHolder) payable returns (bool success) { 60 | if (msg.value > 0 && this.balance + msg.value > 100000 ether) { 61 | balances[_tokenHolder] += msg.value; 62 | totalSupply += msg.value; 63 | CreatedToken(_tokenHolder, msg.value); 64 | return true; 65 | } 66 | throw; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /USNRewardPayOut.sol: -------------------------------------------------------------------------------- 1 | import "./RewardOffer.sol"; 2 | 3 | 4 | contract USNRewardPayOut { 5 | 6 | RewardOffer public usnContract; 7 | 8 | function USNRewardPayOut(RewardOffer _usnContract) { 9 | usnContract = _usnContract; 10 | } 11 | 12 | // interface for USN 13 | function payOneTimeReward() returns(bool) { 14 | if (msg.value < usnContract.getDeploymentReward()) 15 | throw; 16 | 17 | if (usnContract.getOriginalClient().DAOrewardAccount().call.value(msg.value)()) { 18 | return true; 19 | } else { 20 | throw; 21 | } 22 | } 23 | 24 | // pay reward 25 | function payReward() returns(bool) { 26 | if (usnContract.getOriginalClient().DAOrewardAccount().call.value(msg.value)()) { 27 | return true; 28 | } else { 29 | throw; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deploy/README.md: -------------------------------------------------------------------------------- 1 | # Scripts for DAO deployment using geth 2 | 3 | ## Introduction 4 | 5 | `prepare.py` compiles `DAO.sol` and populates some helper variables inside `prepare.js` 6 | 7 | 1. `loadScript("prepare.js")` loads these variables into geth. 8 | 2. `loadScript("deploy.js")` deploys them. 9 | 10 | ## Example usage 11 | 12 | ``` 13 | usage: prepare.py [-h] [--solc SOLC] 14 | [--creation-duration-mins CREATION_DURATION_MINS] 15 | [--contracts-dir CONTRACTS_DIR] [--no-limits] 16 | [--curator CURATOR] 17 | [--default-proposal-deposit DEFAULT_PROPOSAL_DEPOSIT] 18 | [--split-execution-period SPLIT_EXECUTION_PERIOD] 19 | 20 | DAO deployment script 21 | 22 | optional arguments: 23 | -h, --help show this help message and exit 24 | --solc SOLC Full path to the solc binary to use 25 | --creation-duration-mins CREATION_DURATION_MINS 26 | Deployed DAO creation duration in minutes 27 | --contracts-dir CONTRACTS_DIR 28 | The directory where the contracts are located 29 | --no-limits If given then a version of DAO.sol without limits is 30 | compiled 31 | --curator CURATOR Account to set as the curator 32 | --default-proposal-deposit DEFAULT_PROPOSAL_DEPOSIT 33 | The proposal deposit (in ether) for every proposal of 34 | the DAO 35 | --split-execution-period SPLIT_EXECUTION_PERIOD 36 | Number of seconds after the voting deadline for which 37 | a split proposal is executable 38 | ``` 39 | You can for example call the script with a specifically compiled solc and set the creation to end in 15 mins by doing: 40 | ``` 41 | ./prepare.py --solc ~/ew/solidity/build/solc/solc --creation-duration-mins 15 42 | ``` 43 | -------------------------------------------------------------------------------- /deploy/deploy.js: -------------------------------------------------------------------------------- 1 | // first run prepare.js to import the compiled source code and some other helper variables 2 | // before you do that run prepare.py to compile the latest version of the software in DAO 3 | // and populate the helper variables 4 | 5 | personal.unlockAccount(eth.accounts[0]); 6 | var daoContract = web3.eth.contract(dao_abi); 7 | var min_tokens_to_create = 1; 8 | var closing_time = Math.floor(Date.now() / 1000) + seconds_from_now; 9 | 10 | var creatorContract = web3.eth.contract(creator_abi); 11 | console.log("Creating DAOCreator Contract"); 12 | var _daoCreatorContract = creatorContract.new( 13 | { 14 | from: web3.eth.accounts[0], 15 | data: creator_bin, 16 | gas: 4000000 17 | }, function (e, contract){ 18 | if (e) { 19 | console.log(e+ " at DAOCreator creation!"); 20 | } else if (typeof contract.address != 'undefined') { 21 | console.log("Creating the actual DAO"); 22 | var dao = daoContract.new( 23 | curator, 24 | contract.address, 25 | web3.toWei(default_proposal_deposit, "ether"), 26 | web3.toWei(min_tokens_to_create, "ether"), 27 | closing_time, 28 | 0, 29 | { 30 | from: web3.eth.accounts[0], 31 | data: dao_bin, 32 | gas: 4000000 33 | }, function (e, our_contract) { 34 | // funny thing, without this geth hangs 35 | console.log("At DAO creation callback"); 36 | if (typeof our_contract.address != 'undefined') { 37 | console.log("our new DAO address is: " + our_contract.address); 38 | } 39 | }); 40 | 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /deploy/deployOffer.js: -------------------------------------------------------------------------------- 1 | // first run prepare.js to import the compiled source code and some other helper variables 2 | // before you do that run prepare.py to compile the latest version of the software in DAO 3 | // and populate the helper variables 4 | 5 | personal.unlockAccount(eth.accounts[0]); 6 | var offerContract = web3.eth.contract(offer_abi); 7 | 8 | var _offerrContract = offerContract.new( 9 | contractor, 10 | offer_client_dao_address, 11 | "0x0", 12 | offer_total_costs, 13 | offer_onetime_costs, 14 | offer_min_daily_withdraw, 15 | { 16 | from: web3.eth.accounts[0], 17 | data: offer_bin, 18 | gas: 4000000 19 | }, function (e, contract){ 20 | if (e) { 21 | console.log(e+ " at DAOCreator creation!"); 22 | } else if (typeof contract.address != 'undefined') { 23 | console.log("The Deployed Offer address is:" + contract.address); 24 | 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /deploy/interface/README.md: -------------------------------------------------------------------------------- 1 | ## JSON Interface 2 | 3 | This directory contains 2 versions of the interface with which to interact with the DAO. One is the full DAO generate by the Solidity compiler and the other one is a minimal one containing only the minimum amount of functions that a simple user would want in order to interact with the DAO. 4 | 5 | To link to them from an html webpage all one has to do is wrap the 6 | file's URL like below: 7 | ``` 8 | 9 | ``` 10 | -------------------------------------------------------------------------------- /deploy/interface/intermediate.json: -------------------------------------------------------------------------------- 1 | [{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"approve","inputs":[{"type":"address","name":"_spender"},{"type":"uint256","name":"_amount"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"minTokensToCreate","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"address","name":"_newDAO"}],"name":"getNewDAOAddress","inputs":[{"type":"uint256","name":"_proposalID"}],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"transferFrom","inputs":[{"type":"address","name":"_from"},{"type":"address","name":"_to"},{"type":"uint256","name":"_value"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"_actualBalance"}],"name":"actualBalance","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"closingTime","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"transferWithoutReward","inputs":[{"type":"address","name":"_to"},{"type":"uint256","name":"_value"}],"constant":false},{"type":"function","outputs":[],"name":"refund","inputs":[],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"balance"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner"}],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":"_numberOfProposals"}],"name":"numberOfProposals","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"transfer","inputs":[{"type":"address","name":"_to"},{"type":"uint256","name":"_value"}],"constant":false},{"type":"function","outputs":[{"type":"bool","name":""}],"name":"isFueled","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"createTokenProxy","inputs":[{"type":"address","name":"_tokenHolder"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"_voteID"}],"name":"vote","inputs":[{"type":"uint256","name":"_proposalID"},{"type":"bool","name":"_supportsProposal"}],"constant":false},{"type":"function","outputs":[{"type":"bool","name":"_success"}],"name":"getMyReward","inputs":[],"constant":false},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"transferFromWithoutReward","inputs":[{"type":"address","name":"_from"},{"type":"address","name":"_to"},{"type":"uint256","name":"_value"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"remaining"}],"name":"allowance","inputs":[{"type":"address","name":"_owner"},{"type":"address","name":"_spender"}],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"blocked","inputs":[{"type":"address","name":""}],"constant":true},{"type":"function","outputs":[{"type":"address","name":""}],"name":"curator","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"_codeChecksOut"}],"name":"checkProposalCode","inputs":[{"type":"uint256","name":"_proposalID"},{"type":"address","name":"_recipient"},{"type":"uint256","name":"_amount"},{"type":"bytes","name":"_transactionData"}],"constant":true},{"type":"constructor","inputs":[{"type":"address","name":"_curator"},{"type":"address","name":"_daoCreator"},{"type":"uint256","name":"_proposalDeposit"},{"type":"uint256","name":"_minTokensToCreate"},{"type":"uint256","name":"_closingTime"},{"type":"address","name":"_privateCreation"}]},{"type":"event","name":"FuelingToDate","inputs":[{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalAdded","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"address","name":"recipient","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"bool","name":"newCurator","indexed":false},{"type":"string","name":"description","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalTallied","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"bool","name":"result","indexed":false},{"type":"uint256","name":"quorum","indexed":false}],"anonymous":false},{"type":"event","name":"NewCurator","inputs":[{"type":"address","name":"_newCurator","indexed":true}],"anonymous":false},{"type":"event","name":"AllowedRecipientChanged","inputs":[{"type":"address","name":"_recipient","indexed":true},{"type":"bool","name":"_allowed","indexed":false}],"anonymous":false}] 2 | -------------------------------------------------------------------------------- /deploy/interface/minimal.json: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"open","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"proposalHash","type":"bytes32"},{"name":"proposalDeposit","type":"uint256"},{"name":"newCurator","type":"bool"},{"name":"yea","type":"uint256"},{"name":"nay","type":"uint256"},{"name":"creator","type":"address"}],"type":"function"},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":""}],"name":"closingTime","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"address","name":""}],"name":"curator","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":"balance"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner"}],"constant":true},{"type":"function","outputs":[{"type":"uint256","name":"_numberOfProposals"}],"name":"numberOfProposals","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"address","name":""}],"name":"extraBalance","inputs":[],"constant":true},{"type":"function","outputs":[{"type":"bool","name":"success"}],"name":"createTokenProxy","inputs":[{"type":"address","name":"_tokenHolder"}],"constant":false},{"type":"function","outputs":[{"type":"uint256","name":"_voteID"}],"name":"vote","inputs":[{"type":"uint256","name":"_proposalID"},{"type":"bool","name":"_supportsProposal"}],"constant":false},{"type":"event","name":"FuelingToDate","inputs":[{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalAdded","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"address","name":"recipient","indexed":false},{"type":"uint256","name":"amount","indexed":false},{"type":"bool","name":"newCurator","indexed":false},{"type":"string","name":"description","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalTallied","inputs":[{"type":"uint256","name":"proposalID","indexed":true},{"type":"bool","name":"result","indexed":false},{"type":"uint256","name":"quorum","indexed":false}],"anonymous":false}] 2 | -------------------------------------------------------------------------------- /deploy/prepare.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | import json 4 | import subprocess 5 | import argparse 6 | import os 7 | import inspect 8 | 9 | contracts_dir = "../" 10 | currentdir = os.path.dirname( 11 | os.path.abspath(inspect.getfile(inspect.currentframe())) 12 | ) 13 | os.sys.path.insert(0, os.path.dirname(currentdir)) 14 | from tests.utils import determine_binary, edit_dao_source, rm_file, to_wei 15 | 16 | 17 | class TestDeployContext(): 18 | def __init__(self, args): 19 | self.args = args 20 | self.args.solc = determine_binary(args.solc, 'solc', True) 21 | 22 | def compile_contract(self, contract_name): 23 | if self.args.no_limits and contract_name == "DAO.sol": 24 | contract_path = edit_dao_source( 25 | self.args.contracts_dir, 26 | False, # keep limits 27 | self.args.min_proposal_debate, 28 | self.args.min_split_debate, 29 | True, # halve min quorum test, remove year limit 30 | self.args.split_execution_period, 31 | True, # Normal pricing 32 | True, # Don't edit creationGracePeriod 33 | ) 34 | else: 35 | contract_path = os.path.join( 36 | self.args.contracts_dir, 37 | contract_name 38 | ) 39 | print(" Compiling {}...".format(contract_path)) 40 | data = subprocess.check_output([ 41 | self.args.solc, 42 | contract_path, 43 | "--optimize", 44 | "--combined-json", 45 | "abi,bin" 46 | ]) 47 | return json.loads(data) 48 | 49 | def cleanup(self): 50 | try: 51 | rm_file(os.path.join(self.args.contracts_dir, "DAOcopy.sol")) 52 | rm_file( 53 | os.path.join(self.args.contracts_dir, "TokenCreationCopy.sol") 54 | ) 55 | except: 56 | pass 57 | 58 | 59 | if __name__ == "__main__": 60 | p = argparse.ArgumentParser(description='DAO deployment script') 61 | p.add_argument( 62 | '--solc', 63 | help='Full path to the solc binary to use' 64 | ) 65 | p.add_argument( 66 | '--creation-duration-mins', 67 | type=int, 68 | default=60, 69 | help='Deployed DAO creation duration in minutes' 70 | ) 71 | p.add_argument( 72 | '--contracts-dir', 73 | default="..", 74 | help='The directory where the contracts are located' 75 | ) 76 | p.add_argument( 77 | '--no-limits', 78 | action='store_true', 79 | help='If given then a version of DAO.sol without limits is compiled' 80 | ) 81 | p.add_argument( 82 | '--curator', 83 | default="0x08144824954c65b12f68b75072488e634ac4e67a", # Griff testnet 84 | help='Account to set as the curator' 85 | ) 86 | p.add_argument( 87 | '--default-proposal-deposit', 88 | type=int, 89 | default=1, 90 | help='The proposal deposit (in ether) for every proposal of the DAO' 91 | ) 92 | p.add_argument( 93 | '--split-execution-period', 94 | type=int, 95 | default=20, 96 | help=( 97 | 'Number of seconds after the voting deadline for which a split ' 98 | 'proposal is executable' 99 | ) 100 | ) 101 | p.add_argument( 102 | '--min-proposal-debate', 103 | type=int, 104 | default=3600, 105 | help=( 106 | 'Minumum number of seconds that a generic proposal can have' 107 | ) 108 | ) 109 | p.add_argument( 110 | '--min-split-debate', 111 | type=int, 112 | default=3600, 113 | help=( 114 | 'Minumum number of seconds that a split proposal can have' 115 | ) 116 | ) 117 | p.add_argument( 118 | '--offer-contractor', 119 | default="0x08144824954c65b12f68b75072488e634ac4e67a", # Griff testnet 120 | help='Account to set as the SampleOffer contractor' 121 | ) 122 | p.add_argument( 123 | '--offer-total-costs', 124 | type=int, 125 | default=50, 126 | help='Total costs of the SampleOffer in ether' 127 | ) 128 | p.add_argument( 129 | '--offer-onetime-costs', 130 | type=int, 131 | default=10, 132 | help='Onetime costs of the SampleOffer in ether' 133 | ) 134 | p.add_argument( 135 | '--offer-min-daily-withdraw', 136 | type=int, 137 | default=1, 138 | help='Minimum daily withrdawal limit' 139 | ) 140 | p.add_argument( 141 | '--offer-client-dao-address', 142 | default="0x159fe90ac850c895e4fd144e705923cfa042d974", # A testnet DAO 143 | help='The address of the DAO to set as the client of the SampleOffer' 144 | ) 145 | args = p.parse_args() 146 | ctx = TestDeployContext(args) 147 | comp = ctx.compile_contract("DAO.sol") 148 | comp2 = ctx.compile_contract("SampleOffer.sol") 149 | 150 | with open("prepare.js", "w") as f: 151 | f.write("dao_abi = {};\n".format(comp['contracts']['DAO']['abi'])) 152 | f.write("dao_bin = '{}';\n".format(comp['contracts']['DAO']['bin'])) 153 | f.write("creator_abi = {};\n".format( 154 | comp['contracts']['DAO_Creator']['abi']) 155 | ) 156 | f.write("creator_bin = '{}';\n".format( 157 | comp['contracts']['DAO_Creator']['bin']) 158 | ) 159 | f.write("offer_abi = {};\n".format( 160 | comp2['contracts']['SampleOffer']['abi']) 161 | ) 162 | f.write("offer_bin = '{}';\n".format( 163 | comp2['contracts']['SampleOffer']['bin']) 164 | ) 165 | f.write("seconds_from_now = {};\n".format( 166 | args.creation_duration_mins * 60) 167 | ) 168 | f.write("curator = \"{}\";\n".format(args.curator)) 169 | f.write("default_proposal_deposit = {};\n".format( 170 | args.default_proposal_deposit) 171 | ) 172 | f.write("contractor = \"{}\";\n".format(args.offer_contractor)) 173 | f.write("offer_total_costs = {};\n".format( 174 | to_wei(args.offer_total_costs) 175 | )) 176 | f.write("offer_onetime_costs = {};\n".format( 177 | to_wei(args.offer_onetime_costs) 178 | )) 179 | f.write("offer_min_daily_withdraw = {};\n".format( 180 | to_wei(args.offer_min_daily_withdraw) 181 | )) 182 | f.write("offer_client_dao_address = '{}';\n".format( 183 | args.offer_client_dao_address 184 | )) 185 | 186 | ctx.cleanup() 187 | -------------------------------------------------------------------------------- /paper/Biblio.bib: -------------------------------------------------------------------------------- 1 | @article{CrowdfundingFailTC, 2 | url = {{http://techcrunch.com/2015/11/19/when-crowdfunding-fails-the-backers-are-left-with-no-way-out/}}, 3 | author = {John Biggs}, 4 | title = {{When Crowdfunding Fails The Backers Are Left With No Way Out}}, 5 | year = {{2015}}, 6 | } 7 | 8 | @article{buterin2013ethereum, 9 | url = {{https://github.com/ethereum/wiki/wiki/White-Paper}}, 10 | author = {Vitalik Buterin}, 11 | title = {{Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform}}, 12 | year = {{2013}}, 13 | } 14 | 15 | @article{Vitalik2015subjectivity, 16 | url = {{https://blog.ethereum.org/2015/02/14/subjectivity-exploitability-tradeoff/}}, 17 | author = {Buterin, Vitalik}, 18 | title = {{The Subjectivity / Exploitability Tradeoff}}, 19 | year = {{2015}}, 20 | } 21 | 22 | @article{GriffDiscussion, 23 | author = {Griff Green}, 24 | title = {{private discussion}}, 25 | year = {{2016}}, 26 | } 27 | 28 | @article{9MostDisgracefulCrowdFundings, 29 | url = {{http://gizmodo.com/the-9-most-disgraceful-crowdfunding-failures-of-2015-1747957776}}, 30 | author = {Kate Knibbs}, 31 | title = {{The 9 Most Disgraceful Crowdfunding Failures of 2015}}, 32 | year = {{2015}}, 33 | } 34 | 35 | @article{2015CFReport, 36 | url = {{http://reports.crowdsourcing.org/index.php?route=product/product&path=0_20&product_id=54}}, 37 | author = {Massolution}, 38 | title = {{2015CF - Crowdfunding Industry Report}}, 39 | year = {{2015}}, 40 | } 41 | 42 | @InProceedings{miller1997future, 43 | BookTitle = {{paper delivered at the Extro 3 Conference (August 9)}}, 44 | author = {Miller, Mark}, 45 | title = {{The Future of Law}}, 46 | year = {{1997}}, 47 | } 48 | 49 | @article{ReitwiessnerWoodSolidity, 50 | url = {{http://solidity.readthedocs.org/}}, 51 | author = {Reitwiessner, Christian and Wood, Gavin}, 52 | title = {{Solidity}}, 53 | year = {{2015}}, 54 | } 55 | 56 | @Article{szabo1997formalizing, 57 | author = {Szabo, Nick}, 58 | title = {{Formalizing and securing relationships on public networks}}, 59 | journal = {{First Monday}}, 60 | volume = {{2}}, 61 | number = {{9}}, 62 | year = {{1997}}, 63 | } 64 | 65 | @article{Wood2014ethereum, 66 | url = {{http://gavwood.com/paper.pdf}}, 67 | author = {Gavin Wood}, 68 | title = {{Ethereum: A Secure Decentralised Generalised Transaction Ledger}}, 69 | year = {{2014}}, 70 | } 71 | -------------------------------------------------------------------------------- /simpleWithdraw.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | // TODO: all constants need to be double checked 19 | import "github.com/slockit/DAO/DAO.sol"; 20 | 21 | contract Withdraw { 22 | DAO constant public mainDAO = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413); 23 | uint constant public totalSupply = 11538165987024671407837618; 24 | uint constant public totalWeiSupply = 11898333978710775162018627; 25 | 26 | function withdraw(address donateExtraBalanceTo){ 27 | uint balance = mainDAO.balanceOf(msg.sender); 28 | 29 | // The msg.sender must call approve(this, balance) beforehand so that 30 | // transferFrom() will work and not throw. We need transferFrom() 31 | // instead of transfer() due to the msg.sender in the latter ending 32 | // up to be the contract 33 | if (!mainDAO.transferFrom(msg.sender, this, balance) 34 | || !msg.sender.send(balance) 35 | || !donateExtraBalanceTo.send(balance * totalWeiSupply / totalSupply - balance)) { 36 | 37 | throw; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /simpleWithdrawTrustee.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | contract DAO { 19 | function balanceOf(address addr) returns (uint); 20 | function transferFrom(address from, address to, uint balance) returns (bool); 21 | uint public totalSupply; 22 | } 23 | 24 | contract WithdrawDAO { 25 | DAO constant public mainDAO = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413); 26 | address public trustee = 0xDa4a4626d3E16e094De3225A751aAb7128e96526; // curator multisig 27 | 28 | function withdraw(){ 29 | uint balance = mainDAO.balanceOf(msg.sender); 30 | 31 | if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance)) 32 | throw; 33 | } 34 | 35 | function trusteeWithdraw() { 36 | trustee.send((this.balance + mainDAO.balanceOf(this)) - mainDAO.totalSupply()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests for the DAO contracts 2 | 3 | ## Using the test framework 4 | 5 | For the full array of arguments available run `test.py --help` 6 | 7 | ``` 8 | usage: test.py [-h] [--solc SOLC] [--geth GETH] [--keep-limits] 9 | [--clean-chain] [--verbose] [--closing-time CLOSING_TIME] 10 | [--scenario {none,deploy,fund}] 11 | 12 | DAO contracts test framework 13 | 14 | optional arguments: 15 | -h, --help show this help message and exit 16 | --solc SOLC Full path to the solc binary to use 17 | --geth GETH Full path to the geth binary to use 18 | --keep-limits If given then the debate limits of the original 19 | contracts will not be removed 20 | --clean-chain If given then the blockchain is deleted before any 21 | test scenario is executed 22 | --verbose If given then all test checks are printed in the 23 | console 24 | --closing-time CLOSING_TIME 25 | Number of minutes from now when the newly created DAO 26 | creation ends 27 | --scenario {none,deploy,fund} 28 | Test scenario to play out 29 | ``` 30 | 31 | An example scenario you can run is the deploy scenario. Below you can see a sample test command showcasing many of the arguments: 32 | 33 | ``` 34 | ./test.py --solc ~/ew/solidity/build/solc/solc --clean-chain --closing-time 60 --min-value 50 --scenario deploy --geth $GOPATH/src/github.com/ethereum/go-ethereum/build/bin/geth --verbose 35 | ``` 36 | 37 | ## Scenarios 38 | 39 | You can get a list of the available scenario by calling `test.py --describe-scenarios`. At 40 | the time of writting this readme the following scenarios are available: 41 | 42 | - *singlesplit* 43 | An 'angry' user decides to get out of the DAO and take his money with 44 | him. He creates a proposal to split into an one-member DAO with 45 | himself as the Curator. Then he makes a proposal to this new DAO to 46 | transfer all of the money to himself. Assert that the money he gets 47 | back in the end is equal to the money he put in the original DAO. 48 | 49 | - *rewards* 50 | A kind soul donates to the DAO so the DAO has rewards for 51 | distribution. Create a proposal to send the rewards to the 52 | RewardsAccount, vote and execute it. Subsequently claim rewards and 53 | assert that they are proportional to the tokens held by the account 54 | claiming the reward. 55 | 56 | - *newcontract* 57 | A test of the DAO contract upgrade. We create a new contract with a 58 | completely different code and vote to transfer everything to the new 59 | contract. 60 | 61 | - *deploy* 62 | Deploying of the DAO, DAOcreator and SampleOffer contracts in the 63 | blockchain and noting down of their addresses. 64 | 65 | - *multisplitrewards* 66 | Split out of an already split DAO thus generating a grancdchild DAO. 67 | Subsequently test that rewards can be appropriately claimed for all of 68 | these DAOs by their participants as expected. 69 | 70 | - *proposal* 71 | Create a proposal to send an amount of ether to the SampleOffer 72 | contract. Vote on that proposal, wait for the debating period and then 73 | execute it. Assert that the proposal deposit is returned to its 74 | creator, and that the amount is sent to the SampleOffer and the 75 | promise is valid. 76 | 77 | - *extrabalance* 78 | The DAO spends all its money and has to resort to retrieving money 79 | from the extra balance account. This test checks that this is 80 | succesful. 81 | 82 | - *newcontractfail* 83 | A test of the DAO contract upgrade where the proposal's quorum ends up 84 | being insufficient (<53.3%) and the proposal gets rejected. 85 | 86 | - *split* 87 | Testing an equal split, with a new Curator. Half of the token holders 88 | vote for a split to a new DAO and half vote to stay with the old one. 89 | Assert that the split happens, a new DAO is created and that the 90 | tokens are burned from the old DAO and moved to the new DAO 91 | succesfully. Also assert that the reward tokens are succesfully 92 | transferred. 93 | 94 | - *fuel_fail2* 95 | During the fueling period of the DAO, create DAO tokens from all accounts 96 | with both normal creation and with createTokenProxy(). When the goal is 97 | not reached make sure that the refunds when having used 98 | createTokenProxy() are distributed back to the users correctly. 99 | 100 | - *fuel_fail* 101 | During the fueling period of the DAO, send insufficient ether and 102 | assert that the DAO is not fueled. Then assert that each user can get 103 | a full refund. 104 | 105 | - *fuel* 106 | During the fueling period of the DAO, send enough ether from all 107 | accounts to create tokens and then assert that the user's balance is 108 | indeed correct and that the minimum fueling goal has been reached. 109 | 110 | - *deposit* 111 | Make a proposal to change the default proposal deposit, vote for it 112 | and then assure that the DAO's proposal deposit did indeed change. 113 | 114 | - *colmattack* 115 | Before commit 842ce13aedca6365d1f6f4b62c215d4e9b265ffa an attacker 116 | could create a proposal with a huge deposit. Then he could create a 117 | split DAO proposal to get his share of ether plus his share of the 118 | deposit he gave. Then if the original proposal meets the quorum the 119 | attacker will also get his deposit back. 120 | 121 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/__init__.py -------------------------------------------------------------------------------- /tests/args.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | import argparse 3 | import sys 4 | import os 5 | import json 6 | from utils import available_scenarios, calculate_bytecode 7 | from itertools import izip 8 | 9 | 10 | def pairwise(iterable): 11 | "s -> (s0, s1), (s2, s3), (s4, s5), ..." 12 | a = iter(iterable) 13 | return izip(a, a) 14 | 15 | 16 | def make_bool(val): 17 | if isinstance(val, basestring): 18 | if val.lower() in ["false", "0", "no"]: 19 | return False 20 | elif val.lower() in ["true", "1", "yes"]: 21 | return True 22 | else: 23 | print( 24 | "ERROR: Given string '{}' can not be converted to bool".format( 25 | val 26 | )) 27 | sys.exit(1) 28 | else: 29 | return bool(val) 30 | 31 | 32 | def read_scenario_options(args): 33 | """ Iterate scenarios and load all argument options """ 34 | dir = "scenarios" 35 | for name in os.listdir(dir): 36 | argfile = os.path.join(dir, name, "arguments.json") 37 | if os.path.isfile(argfile): 38 | with open(argfile, 'r') as f: 39 | data = json.loads(f.read()) 40 | 41 | for arg in data: 42 | arg_type = None # interpret as simple string 43 | if 'type' in arg: 44 | if arg['type'] == "int": 45 | arg_type = int 46 | elif arg['type'] == "float": 47 | arg_type = float 48 | elif arg['type'] == "bool": 49 | arg_type = make_bool 50 | else: 51 | print( 52 | "ERROR: Unrecognized type '{}' given for argument" 53 | "'{}'".format(arg['type'], arg['name']) 54 | ) 55 | sys.exit(1) 56 | 57 | if 'type' in arg: 58 | args.add_argument( 59 | "--{}-{}".format(name, arg['name']).replace("_", "-"), 60 | help=arg['description'], 61 | default=arg['default'], 62 | type=arg_type 63 | ) 64 | else: 65 | args.add_argument( 66 | "--{}-{}".format(name, arg['name']).replace("_", "-"), 67 | help=arg['description'], 68 | default=arg['default'] 69 | ) 70 | 71 | 72 | def test_args(): 73 | """ Parse the test arguments and create and return the arguments object""" 74 | p = argparse.ArgumentParser(description='DAO contracts test framework') 75 | read_scenario_options(p) 76 | 77 | p.add_argument( 78 | '--solc', 79 | help='Full path to the solc binary to use' 80 | ) 81 | p.add_argument( 82 | '--geth', 83 | help='Full path to the geth binary to use' 84 | ) 85 | p.add_argument( 86 | '--keep-limits', 87 | action='store_true', 88 | help=( 89 | 'If given then the debate limits of the original ' 90 | 'contracts will not be removed' 91 | ) 92 | ) 93 | p.add_argument( 94 | '--clean-chain', 95 | action='store_true', 96 | help=( 97 | 'If given then the blockchain is deleted before any ' 98 | 'test scenario is executed' 99 | ) 100 | ) 101 | p.add_argument( 102 | '--verbose', 103 | action='store_true', 104 | help='If given then all test checks are printed in the console' 105 | ) 106 | p.add_argument( 107 | '--proposal-fail', 108 | action='store_true', 109 | help='If given, then in the proposal scenario the voting will fail' 110 | ) 111 | p.add_argument( 112 | '--compile-test', 113 | action='store_true', 114 | help='If given, then tests will only try to compile the contracts' 115 | ) 116 | p.add_argument( 117 | '--users-num', 118 | type=int, 119 | help='The number of user accounts to create for the scenarios.' 120 | 'Should be at least 3', 121 | default=5 122 | ) 123 | p.add_argument( 124 | '--scenario', 125 | choices=['none'] + available_scenarios(), 126 | default='none', 127 | help='Available test scenario to play out' 128 | ) 129 | p.add_argument( 130 | '--describe-scenarios', 131 | action='store_true', 132 | help='Print the description of all scenarios and then quit' 133 | ) 134 | p.add_argument( 135 | '--abi', 136 | type=str, 137 | default="", 138 | help=( 139 | "If given then don't run any tests but print the abi of the given " 140 | "function with the arguments provided. Example call:" 141 | "test.py --abi \"transfer address foo uint256 5\"" 142 | ) 143 | ) 144 | p.add_argument( 145 | '--dao-version', 146 | type=str, 147 | default="v1.0", 148 | choices=["v1.0", "master"], 149 | help="The version of the DAO code to run the tests against." 150 | ) 151 | args = p.parse_args() 152 | 153 | # Argument verification 154 | if args.users_num < 3: 155 | print("ERROR: Tests need 3 or more users") 156 | sys.exit(1) 157 | 158 | # if it's an abi test call then just show bytecode and exit 159 | if args.abi != "": 160 | arglist = args.abi.split(" ") 161 | function_args = [] 162 | for type_name, value in pairwise(arglist[1:]): 163 | function_args.append((type_name, value)) 164 | bytecode = calculate_bytecode(arglist[0], *function_args) 165 | print("Requested bytecode is:\n{}\n.Exiting ...".format(bytecode)) 166 | sys.exit(0) 167 | 168 | if args.compile_test: 169 | # if we are asking for compile_test then it should always be against 170 | # the latest version of the contracts 171 | args.dao_version = "master" 172 | 173 | return args 174 | -------------------------------------------------------------------------------- /tests/jsutils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | 4 | def js_common_intro(accounts_num): 5 | """Common functions, variables to add to all js scripts""" 6 | s = "console.log('unlocking accounts');\n" 7 | for i in range(0, accounts_num): 8 | s += "personal.unlockAccount(eth.accounts[{}], '123');\n".format(i) 9 | s += """// set the basic accounts, coinbase should be random so mining rewards don't pollute results 10 | var curator = eth.accounts[0]; 11 | var proposalCreator = eth.accounts[1]; 12 | var contractor = eth.accounts[2]; 13 | var etherBase = '0x9999999999999999999999999999999999999999'; 14 | web3.miner.setEtherbase(etherBase); 15 | 16 | var testMap = {}; 17 | 18 | function checkWork() { 19 | miner.start(1); 20 | admin.sleepBlocks(3); 21 | miner.stop(); 22 | } 23 | 24 | function time_now() { 25 | return Math.floor(Date.now() / 1000); 26 | } 27 | 28 | function bigDiff(astr, bstr) { 29 | return new BigNumber(astr).minus(new BigNumber(bstr)); 30 | } 31 | 32 | function bigDiffRound(astr, bstr) { 33 | return Math.round(bigDiff(astr, bstr)); 34 | } 35 | 36 | function addToTest(name, value) { 37 | testMap[name] = value; 38 | console.log("'" + name + "' = " + value); 39 | } 40 | 41 | function testResults() { 42 | console.log("Test Results: " + JSON.stringify(testMap)); 43 | } 44 | 45 | function testFail(str) { 46 | console.log("TEST FAIL: " + str); 47 | throw ' '; 48 | } 49 | 50 | function attempt_proposal( 51 | argdao, 52 | recipient, 53 | proposal_creator, 54 | ether_amount, 55 | desc, 56 | bytecode, 57 | debating_period, 58 | ether_deposit, 59 | is_split_proposal 60 | ) { 61 | 62 | dao_closing_time = argdao.closingTime(); 63 | 64 | if (!argdao.isFueled()) { 65 | testFail( 66 | "Failed to create a proposal to: '" + desc + "' because the DAO " 67 | + "is not fueled." 68 | ); 69 | } 70 | if (dao_closing_time.gt(time_now())) { 71 | testFail( 72 | "Failed to create a proposal to: '" + desc + "' because the DAO's " 73 | + "creation time has not yet closed.\\ndao_closing_time: " 74 | + dao_closing_time + "\\nnow(): " + time_now() 75 | ); 76 | } 77 | proposals_num_before = argdao.numberOfProposals(); 78 | console.log("Creating a new proposal to: '" + desc + "'"); 79 | argdao.newProposal.sendTransaction( 80 | recipient, 81 | web3.toWei(ether_amount, "ether"), 82 | desc, 83 | bytecode, 84 | debating_period, 85 | is_split_proposal, 86 | { 87 | from: proposal_creator, 88 | value: web3.toWei(ether_deposit, "ether"), 89 | gas: 1000000 90 | }); 91 | checkWork(); 92 | proposals_num_now = argdao.numberOfProposals(); 93 | 94 | if (!proposals_num_now.equals(proposals_num_before.add(1))) { 95 | testFail("Failed to create a proposal to: " + desc + "'"); 96 | } else { 97 | console.log("Proposal succesfully created"); 98 | } 99 | return proposals_num_now; 100 | } 101 | 102 | function attempt_split(argdao, prop_id, user, new_curator, split_exec_period) { 103 | console.log("Account '" + user + "' is calling splitDAO()"); 104 | var vote_deadline = argdao.proposals(prop_id)[3]; 105 | if (vote_deadline.gt(time_now())) { 106 | testFail("Can't split the DAO while the proposal is still debated."); 107 | } 108 | var prop_deadline = vote_deadline.add(split_exec_period); 109 | console.log("prop_deadline: " + prop_deadline); 110 | console.log("now(): " + time_now()); 111 | if (prop_deadline.lessThan(time_now() + 5)) { 112 | testFail("Can no longer vote to split the DAO. 'now > p.votingDeadline + splitExecutionPeriod'"); 113 | } 114 | argdao.splitDAO.sendTransaction( 115 | prop_id, 116 | new_curator, 117 | {from:user, gas: 4700000}); 118 | checkWork(); 119 | console.log("Account '" + user + "' called splitDAO() succesfully"); 120 | } 121 | 122 | function attempt_execute_proposal( 123 | argdao, 124 | prop_id, 125 | bytecode, 126 | prop_creator, 127 | expect_closed, 128 | expect_pass) { 129 | desc = argdao.proposals(prop_id)[2]; 130 | vote_deadline = argdao.proposals(prop_id)[3]; 131 | console.log("Attempting to execute proposal for: '" +desc +"'."); 132 | 133 | if (vote_deadline.gt(time_now())) { 134 | testFail("Can't execute a proposal while it is is still debated."); 135 | } 136 | 137 | argdao.executeProposal.sendTransaction( 138 | prop_id, 139 | bytecode, 140 | {from: prop_creator, gas:4700000} 141 | ); 142 | checkWork(); 143 | var should_quit = false; 144 | if (argdao.proposals(prop_id)[4] == expect_closed) { 145 | should_quit = true; 146 | console.log( 147 | "Expected the proposal to be " + (expect_closed ? "closed" : "open") + 148 | " but it's not" 149 | ); 150 | } 151 | if (argdao.proposals(prop_id)[5] != expect_pass) { 152 | should_quit = true; 153 | console.log( 154 | "Expected the proposal for: '" +desc +" to " + 155 | (expect_pass ? "pass" : "fail") + "." 156 | ); 157 | } 158 | if (should_quit) { 159 | testFail("Failed to execute proposal for: '" +desc +"'."); 160 | } 161 | console.log("Executed proposal: '" + desc + "'."); 162 | } 163 | """ 164 | return s 165 | -------------------------------------------------------------------------------- /tests/scenarios/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/colmattack/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/colmattack/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "attack_debate_secs", 3 | "default": 15, 4 | "description": "Number of seconds the proposal with the large deposit should be debated", 5 | "type": "int" 6 | }, { 7 | "name": "split_debate_secs", 8 | "default": 20, 9 | "description": "Number of seconds the attacker's split proposal should be debated", 10 | "type": "int" 11 | }, { 12 | "name": "attack_deposit", 13 | "default": 600, 14 | "description": "Amount of ether given as deposit in the attack.", 15 | "type": "int" 16 | }] 17 | -------------------------------------------------------------------------------- /tests/scenarios/colmattack/run.py: -------------------------------------------------------------------------------- 1 | # Big thanks to @colm from our slack chat for thinking of this attack !!! 2 | scenario_description = ( 3 | "Before commit 842ce13aedca6365d1f6f4b62c215d4e9b265ffa an attacker could " 4 | "create a proposal with a huge deposit. Then he could create a split DAO " 5 | "proposal to get his share of ether plus his share of the deposit he gave." 6 | " Then if the original proposal meets the quorum the attacker will also " 7 | "get his deposit back." 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('fuel') 13 | 14 | ctx.create_js_file(substitutions={ 15 | "dao_abi": ctx.dao_abi, 16 | "dao_address": ctx.dao_address, 17 | "offer_address": ctx.offer_address, 18 | "attack_debating_period": ctx.args.colmattack_attack_debate_secs, 19 | "split_debating_period": ctx.args.colmattack_split_debate_secs, 20 | "attack_deposit": ctx.args.colmattack_attack_deposit, 21 | }) 22 | 23 | ctx.execute(expected={ 24 | "attacker_eth_balance_diff": 0, 25 | "attacker_dao_balance_diff": 0, 26 | "split_dao_total_supply": ctx.token_amounts[2] 27 | }) 28 | -------------------------------------------------------------------------------- /tests/scenarios/colmattack/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | var attacker = eth.accounts[2]; 4 | addToTest('attacker_balance_before', web3.fromWei(eth.getBalance(attacker))); 5 | addToTest('attacker_dao_balance_before', web3.fromWei(dao.balanceOf(attacker))); 6 | 7 | // add curator to the whitelist 8 | dao.changeAllowedRecipients.sendTransaction(curator, true, {from:curator, gas:200000}); 9 | 10 | var attack_proposal_id = attempt_proposal( 11 | dao, // DAO in question 12 | curator, // recipient 13 | attacker, // proposal creator 14 | 0, // proposal amount in ether 15 | 'The colm attack proposal with a big deposit', // description 16 | '', //bytecode 17 | $attack_debating_period, // debating period 18 | $attack_deposit, // proposal deposit in ether 19 | false // whether it's a split proposal or not 20 | ); 21 | 22 | var split_proposal_id = attempt_proposal( 23 | dao, // DAO in question 24 | attacker, // recipient 25 | attacker, // proposal creator 26 | 0, // proposal amount in ether 27 | 'attacker wants to split out', // description 28 | '', //bytecode 29 | $split_debating_period, // debating period 30 | 0, // proposal deposit in ether 31 | true // whether it's a split proposal or not 32 | ); 33 | 34 | console.log("Vote on proposals"); 35 | // everyone votes on the attack proposal 36 | for (i = 0; i < eth.accounts.length; i++) { 37 | dao.vote.sendTransaction( 38 | attack_proposal_id, 39 | true, 40 | { 41 | from: eth.accounts[i], 42 | gas: 1000000 43 | } 44 | ); 45 | } 46 | // our attacker also votes on his split 47 | dao.vote.sendTransaction(split_proposal_id, true, {from: attacker, gas: 1000000}); 48 | checkWork(); 49 | 50 | 51 | setTimeout(function() { 52 | miner.stop(); 53 | console.log("Attack debate period over."); 54 | 55 | setTimeout(function() { 56 | miner.stop(); 57 | 58 | console.log("Split debate period over. Executing the split proposal..."); 59 | // now the attacker splits 60 | dao.splitDAO.sendTransaction( 61 | split_proposal_id, 62 | attacker, 63 | {from:attacker, gas: 4700000} 64 | ); 65 | checkWork(); 66 | 67 | console.log("Right after the split, execute the attack proposal to get the deposit back"); 68 | attempt_execute_proposal( 69 | dao, // target DAO 70 | attack_prop_id, // proposal ID 71 | '', // transaction bytecode 72 | attacker, // proposal creator 73 | true, // should the proposal be closed after this call? 74 | true // should the proposal pass? 75 | ); 76 | 77 | addToTest('split_proposal_id', split_proposal_id); 78 | addToTest('split_proposal_passed', dao.proposals(split_proposal_id)[5]); 79 | addToTest('split_dao', dao.splitProposalNewAddress(split_proposal_id, 0)); 80 | var splitdao = web3.eth.contract($dao_abi).at(testMap['split_dao']); 81 | addToTest('split_dao_total_supply', web3.fromWei(eth.getBalance(splitdao.address))); 82 | addToTest('attacker_balance_after', web3.fromWei(eth.getBalance(attacker))); 83 | 84 | // now comes the check. His balance should be the same but so should be the amount 85 | // balance of the split DAO and the balance he owned in the previous DAO. With the 86 | // colm attack that would not be the case as he would also get part of his proposal 87 | // deposit into the new DAO and thus make profit. 88 | addToTest( 89 | 'attacker_eth_balance_diff', 90 | bigDiffRound(testMap['attacker_balance_after'], testMap['attacker_balance_before']) 91 | ); 92 | addToTest( 93 | 'attacker_dao_balance_diff', 94 | bigDiffRound(testMap['split_dao_total_supply'], testMap['attacker_dao_balance_before']) 95 | ); 96 | testResults(); 97 | }, ($split_debating_period - $attack_debating_period) * 1000); 98 | 99 | miner.start(1); 100 | console.log("Waiting until the split proposal debate is over"); 101 | }, $attack_debating_period * 1000); 102 | miner.start(1); 103 | console.log("Waiting for the split debate period."); 104 | -------------------------------------------------------------------------------- /tests/scenarios/curator_halveminquorum_fueling/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/curator_halveminquorum_fueling/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils import constrained_sum_sample_pos, arr_str 3 | 4 | 5 | scenario_description = ( 6 | "During the fueling period of the DAO, the curator should not be able " 7 | "to call halveMinQuorum(). This is a test to make sure this can't happen " 8 | "and to assert that the fix introduced by PR: " 9 | "https://github.com/slockit/DAO/pull/152 works as expected" 10 | ) 11 | 12 | 13 | def run(ctx): 14 | ctx.assert_scenario_ran('deploy') 15 | 16 | ctx.total_supply = ( 17 | ctx.args.deploy_min_tokens_to_create + random.randint(1, 100) 18 | ) 19 | ctx.token_amounts = constrained_sum_sample_pos( 20 | len(ctx.accounts), ctx.total_supply 21 | ) 22 | ctx.create_js_file(substitutions={ 23 | "dao_abi": ctx.dao_abi, 24 | "dao_address": ctx.dao_address, 25 | "amounts": arr_str(ctx.token_amounts) 26 | } 27 | ) 28 | 29 | ctx.execute(expected={ 30 | "min_quorum_same": True 31 | }) 32 | -------------------------------------------------------------------------------- /tests/scenarios/curator_halveminquorum_fueling/template.js: -------------------------------------------------------------------------------- 1 | var amounts = $amounts; 2 | 3 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 4 | console.log("Creating DAO tokens"); 5 | for (i = 0; i < eth.accounts.length; i++) { 6 | web3.eth.sendTransaction({ 7 | from:eth.accounts[i], 8 | to: dao.address, 9 | gas:200000, 10 | value:web3.toWei(amounts[i], "ether") 11 | }); 12 | } 13 | checkWork(); 14 | 15 | 16 | addToTest('min_quorum_before', dao.extMinQuorum(0)); 17 | dao.halveMinQuorum.sendTransaction({from:curator, gas:120000}); 18 | checkWork(); 19 | addToTest('min_quorum_after', dao.extMinQuorum(0)); 20 | addToTest( 21 | 'min_quorum_same', 22 | testMap['min_quorum_after'].eq(testMap['min_quorum_before']) 23 | ); 24 | testResults(); 25 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/deploy/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/deploy/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "creation_seconds", 3 | "default": 50, 4 | "description":"Number of seconds from now when the newly created DAO creation ends", 5 | "type":"int" 6 | }, { 7 | "name": "min_tokens_to_create", 8 | "default": 40, 9 | "description": "Minimum value in tokens for the DAO to create in order to be considered fueled", 10 | "type": "int" 11 | },{ 12 | "name": "onetime_costs", 13 | "default": 5, 14 | "description": "The one time costs (in ether) in the offer to the DAO", 15 | "type":"int" 16 | }, { 17 | "name": "total_costs", 18 | "default": 20, 19 | "description": "The total costs (in ether) in the offer to the DAO", 20 | "type":"int" 21 | }, { 22 | "name": "offer_payment_period", 23 | "default": 20, 24 | "description": "Edits the Offer contract to consider a period of X seconds as a day", 25 | "type":"int" 26 | }, { 27 | "name": "pfoffer_payout_freeze_period", 28 | "default": 20, 29 | "description": "Edits the PFOffer contract to consider a period of X seconds as the payout freeze period", 30 | "type":"int" 31 | }, { 32 | "name": "pfoffer_vote_status_deadline", 33 | "default": 1, 34 | "description": "Sets the VoteStatusDeadline of the PFOffer contract", 35 | "type":"int" 36 | },{ 37 | "name": "proposal_deposit", 38 | "default": 20, 39 | "description": "The default proposal deposit for every proposal of the DAO", 40 | "type":"int" 41 | }] 42 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_dao_creator_template.js: -------------------------------------------------------------------------------- 1 | console.log("Creating DAOCreator Contract"); 2 | var creatorContract = web3.eth.contract($creator_abi); 3 | var _daoCreatorContract = creatorContract.new( 4 | { 5 | from: web3.eth.accounts[0], 6 | data: '$creator_bin', 7 | gas: 4700000 8 | }, function (e, contract){ 9 | if (e) { 10 | console.log(e+" at DAOCreator creation!"); 11 | } else if (typeof contract.address != 'undefined') { 12 | addToTest('dao_creator_address', contract.address); 13 | testResults(); 14 | } 15 | }); 16 | checkWork(); 17 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_dao_template.js: -------------------------------------------------------------------------------- 1 | var _curator = web3.eth.accounts[0]; 2 | console.log("Creating DAO Contract"); 3 | var daoContract = web3.eth.contract($dao_abi); 4 | if ($using_old_dao) { 5 | daoContract.new( 6 | _curator, 7 | "$dao_creator_address", 8 | $default_proposal_deposit, 9 | web3.toWei($min_tokens_to_create, "ether"), 10 | $closing_time, 11 | 0, 12 | { 13 | from: web3.eth.accounts[0], 14 | data: '$dao_bin', 15 | gas: 4700000 16 | }, function (e, contract){ 17 | if (e) { 18 | console.log(e+" at DAO creation!"); 19 | } else if (typeof contract.address != 'undefined') { 20 | addToTest('dao_address', contract.address); 21 | testResults(); 22 | } 23 | }); 24 | } else { 25 | daoContract.new( 26 | _curator, 27 | "$dao_creator_address", 28 | $default_proposal_deposit, 29 | web3.toWei($min_tokens_to_create, "ether"), 30 | $closing_time, 31 | 0, 32 | "Test DAO token", // DAO Token name 33 | "A", // DAO Token symbol 34 | 16, 35 | { 36 | from: web3.eth.accounts[0], 37 | data: '$dao_bin', 38 | gas: 4700000 39 | }, function (e, contract){ 40 | if (e) { 41 | console.log(e+" at DAO creation!"); 42 | } else if (typeof contract.address != 'undefined') { 43 | addToTest('dao_address', contract.address); 44 | testResults(); 45 | } 46 | }); 47 | } 48 | checkWork(); 49 | 50 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_dthpool_template.js: -------------------------------------------------------------------------------- 1 | console.log("Creating DTHPool Contract"); 2 | var dthContract = web3.eth.contract($dthpool_abi); 3 | var _dthContract = dthContract.new( 4 | "$dao_address", // client DAO address 5 | eth.accounts[0], // delegate 6 | 30, // maxTimeBlocked 7 | "John Doe", //delegateName 8 | "delegate.daohub.org", //delegateURL 9 | "T", //tokenSymbol 10 | { 11 | from: contractor, 12 | data: '$dthpool_bin', 13 | gas: 3000000 14 | }, function (e, contract){ 15 | if (e) { 16 | console.log(e+" at DTHPool creation!"); 17 | } else if (typeof contract.address != 'undefined') { 18 | addToTest('dthpool_address', contract.address); 19 | testResults(); 20 | } 21 | }); 22 | checkWork(); 23 | 24 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_offer_template.js: -------------------------------------------------------------------------------- 1 | console.log("Creating Offer Contract"); 2 | var offerContract = web3.eth.contract($offer_abi); 3 | var _offerContract = offerContract.new( 4 | contractor, 5 | "$dao_address", // client DAO address 6 | '0x0', // This is a hash of the paper contract. Does not matter for testing 7 | web3.toWei($offer_total, "ether"), //total costs 8 | web3.toWei($offer_onetime, "ether"), //one time costs 9 | web3.toWei(1, "ether"), //min daily costs 10 | { 11 | from: contractor, 12 | data: '$offer_bin', 13 | gas: 3000000 14 | }, function (e, contract){ 15 | if (e) { 16 | console.log(e+" at Offer creation!"); 17 | } else if (typeof contract.address != 'undefined') { 18 | addToTest('offer_address', contract.address); 19 | testResults(); 20 | } 21 | }); 22 | checkWork(); 23 | 24 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_pfoffer_template.js: -------------------------------------------------------------------------------- 1 | console.log("Creating PFOffer Contract"); 2 | var offerContract = web3.eth.contract($pfoffer_abi); 3 | var _offerContract = offerContract.new( 4 | contractor, 5 | "$dao_address", // client DAO address 6 | '0x0', // This is a hash of the paper contract. Does not matter for testing 7 | web3.toWei($offer_total, "ether"), //total costs 8 | web3.toWei($offer_onetime, "ether"), //one time costs 9 | web3.toWei(1, "ether"), //min daily costs 10 | { 11 | from: contractor, 12 | data: '$pfoffer_bin', 13 | gas: 3000000 14 | }, function (e, contract){ 15 | if (e) { 16 | console.log(e+" at PFOffer creation!"); 17 | } else if (typeof contract.address != 'undefined') { 18 | addToTest('pfoffer_address', contract.address); 19 | testResults(); 20 | } 21 | }); 22 | checkWork(); 23 | 24 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/deploy_usn_template.js: -------------------------------------------------------------------------------- 1 | console.log("Creating USNRewardPayout Contract"); 2 | var usnContract = web3.eth.contract($usn_abi); 3 | var _usnContract = usnContract.new( 4 | contractor, 5 | "$offer_address", // offer address to work with 6 | { 7 | from: contractor, 8 | data: '$usn_bin', 9 | gas: 3000000 10 | }, function (e, contract){ 11 | if (e) { 12 | console.log(e+" at USNRewardPayout creation!"); 13 | } else if (typeof contract.address != 'undefined') { 14 | addToTest('usn_address', contract.address); 15 | testResults(); 16 | } 17 | }); 18 | checkWork(); 19 | 20 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | from utils import extract_test_dict, seconds_in_future, bool_to_str 4 | 5 | 6 | scenario_description = ( 7 | "Deploying of the DAO, DAOcreator and SampleOffer contracts in the " 8 | "blockchain and noting down of their addresses" 9 | ) 10 | 11 | 12 | def calculate_closing_time(obj, script_name, substitutions): 13 | obj.closing_time = seconds_in_future(obj.args.deploy_creation_seconds) 14 | substitutions['closing_time'] = obj.closing_time 15 | return substitutions 16 | 17 | 18 | def deploy_contract(ctx, substitutions, name, result, json_dict, cb=None): 19 | ctx.create_js_file( 20 | substitutions=substitutions, 21 | specific_name=name, 22 | cb_before_creation=cb 23 | ) 24 | output = ctx.run_script('{}.js'.format(name)) 25 | results = extract_test_dict(name, output) 26 | 27 | try: 28 | setattr(ctx, result, results[result]) 29 | except: 30 | print( 31 | "ERROR: Could not find '{}' in the deploy scenario" 32 | ". The output was:\n{}".format(result, output) 33 | ) 34 | sys.exit(1) 35 | 36 | print("{} is: {}".format(result, getattr(ctx, result))) 37 | json_dict[result] = getattr(ctx, result) 38 | 39 | 40 | def run(ctx): 41 | json_dict = {} 42 | deploy_contract( 43 | ctx, 44 | substitutions={ 45 | "creator_abi": ctx.creator_abi, 46 | "creator_bin": ctx.creator_bin 47 | }, 48 | name='deploy_dao_creator', 49 | result='dao_creator_address', 50 | json_dict=json_dict 51 | ) 52 | deploy_contract( 53 | ctx, 54 | substitutions={ 55 | "dao_abi": ctx.dao_abi, 56 | "dao_bin": ctx.dao_bin, 57 | "dao_creator_address": ctx.dao_creator_address, 58 | "min_tokens_to_create": ctx.args.deploy_min_tokens_to_create, 59 | "default_proposal_deposit": ctx.args.deploy_proposal_deposit, 60 | "using_old_dao": bool_to_str(ctx.args.dao_version != "master") 61 | }, 62 | name='deploy_dao', 63 | result='dao_address', 64 | json_dict=json_dict, 65 | cb=calculate_closing_time 66 | ) 67 | deploy_contract( 68 | ctx, 69 | substitutions={ 70 | "dao_address": ctx.dao_address, 71 | "offer_abi": ctx.offer_abi, 72 | "offer_bin": ctx.offer_bin, 73 | "offer_onetime": ctx.args.deploy_onetime_costs, 74 | "offer_total": ctx.args.deploy_total_costs, 75 | }, 76 | name='deploy_offer', 77 | result='offer_address', 78 | json_dict=json_dict 79 | ) 80 | 81 | if ctx.scenario_uses_pfoffer(): 82 | deploy_contract( 83 | ctx, 84 | substitutions={ 85 | "dao_address": ctx.dao_address, 86 | "pfoffer_abi": ctx.pfoffer_abi, 87 | "pfoffer_bin": ctx.pfoffer_bin, 88 | "offer_onetime": ctx.args.deploy_onetime_costs, 89 | "offer_total": ctx.args.deploy_total_costs, 90 | }, 91 | name='deploy_pfoffer', 92 | result='pfoffer_address', 93 | json_dict=json_dict 94 | ) 95 | 96 | deploy_contract( 97 | ctx, 98 | substitutions={ 99 | "offer_address": ctx.offer_address, 100 | "usn_abi": ctx.usn_abi, 101 | "usn_bin": ctx.usn_bin 102 | }, 103 | name='deploy_usn', 104 | result='usn_address', 105 | json_dict=json_dict 106 | ) 107 | 108 | if ctx.scenario_uses_dthpool(): 109 | deploy_contract( 110 | ctx, 111 | substitutions={ 112 | "dao_address": ctx.dao_address, 113 | "dthpool_abi": ctx.dthpool_abi, 114 | "dthpool_bin": ctx.dthpool_bin 115 | }, 116 | name='deploy_dthpool', 117 | result='dthpool_address', 118 | json_dict=json_dict 119 | ) 120 | 121 | with open(ctx.save_file, "w") as f: 122 | f.write(json.dumps(json_dict)) 123 | 124 | # after deployment recalculate for the subsequent tests what the min 125 | # amount of tokens is in the case of extrabalance tests 126 | if ctx.scenario_uses_extrabalance(): 127 | ctx.args.deploy_min_tokens_to_create = ( 128 | int(ctx.args.deploy_min_tokens_to_create * 1.5) 129 | ) 130 | -------------------------------------------------------------------------------- /tests/scenarios/deploy/template.js: -------------------------------------------------------------------------------- 1 | var _curator = web3.eth.accounts[0]; 2 | var daoContract = web3.eth.contract($dao_abi); 3 | console.log("Creating DAOCreator Contract"); 4 | var creatorContract = web3.eth.contract($creator_abi); 5 | var _daoCreatorContract = creatorContract.new( 6 | { 7 | from: web3.eth.accounts[0], 8 | data: '$creator_bin', 9 | gas: 4700000 10 | }, function (e, contract){ 11 | if (e) { 12 | console.log(e+" at DAOCreator creation!"); 13 | } else if (typeof contract.address != 'undefined') { 14 | addToTest('dao_creator_address', contract.address); 15 | checkWork(); 16 | var dao = daoContract.new( 17 | _curator, 18 | contract.address, 19 | $default_proposal_deposit, 20 | web3.toWei($min_tokens_to_create, "ether"), 21 | $closing_time, 22 | 0, 23 | { 24 | from: web3.eth.accounts[0], 25 | data: '$dao_bin', 26 | gas: 4700000 27 | }, function (e, contract) { 28 | // funny thing, without this geth hangs 29 | console.log("At DAO creation callback"); 30 | if (typeof contract.address != 'undefined') { 31 | addToTest('dao_address', contract.address); 32 | 33 | // now deploy the PFOffer 34 | var pfofferContract = web3.eth.contract($pfoffer_abi); 35 | var pfoffer = pfofferContract.new( 36 | contractor, 37 | contract.address, // client DAO address 38 | '0x0', // This is a hash of the paper contract. Does not matter for testing 39 | web3.toWei($offer_total, "ether"), //total costs 40 | web3.toWei($offer_onetime, "ether"), //one time costs 41 | web3.toWei(1, "ether"), //min daily costs 42 | { 43 | from: contractor, 44 | data: '$pfoffer_bin', 45 | gas: 3000000 46 | }, function (e, pfoffer_contract) { 47 | if (e) { 48 | console.log(e + " at PFOffer Contract creation!"); 49 | } else if (typeof pfoffer_contract.address != 'undefined') { 50 | addToTest('pfoffer_address', pfoffer_contract.address); 51 | } 52 | }); 53 | checkWork(); 54 | 55 | // now deploy the Sample Offer 56 | var offerContract = web3.eth.contract($offer_abi); 57 | var offer = offerContract.new( 58 | contractor, 59 | contract.address, // client DAO address 60 | '0x0', // This is a hash of the paper contract. Does not matter for testing 61 | web3.toWei($offer_total, "ether"), //total costs 62 | web3.toWei($offer_onetime, "ether"), //one time costs 63 | web3.toWei(1, "ether"), //min daily costs 64 | { 65 | from: contractor, 66 | data: '$offer_bin', 67 | gas: 3000000 68 | }, function (e, offer_contract) { 69 | if (e) { 70 | console.log(e + " at Offer Contract creation!"); 71 | } else if (typeof offer_contract.address != 'undefined') { 72 | addToTest('offer_address', offer_contract.address); 73 | 74 | // finally now deploy the USNRewardPAyout contract 75 | var usnContract = web3.eth.contract($usn_abi); 76 | var usn = usnContract.new( 77 | offer_contract.address, 78 | { 79 | from: contractor, 80 | data: '$usn_bin', 81 | gas: 3000000 82 | }, function (e, usn_contract) { 83 | if (e) { 84 | console.log(e + " at USNRewardpayout Contract creation!"); 85 | } else if (typeof usn_contract.address != 'undefined') { 86 | addToTest('usn_address', usn_contract.address); 87 | testResults(); 88 | } 89 | } 90 | ); 91 | checkWork(); 92 | } 93 | }); 94 | checkWork(); 95 | } 96 | }); 97 | checkWork(); 98 | } 99 | }); 100 | checkWork(); 101 | 102 | -------------------------------------------------------------------------------- /tests/scenarios/deposit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/deposit/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/deposit/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "debate_seconds", 3 | "default": 15, 4 | "description": "Number of seconds that the deposit change proposal should be open for voting", 5 | "type": "int" 6 | }, { 7 | "name": "new_value", 8 | "default": 2, 9 | "description": "The proposed new deposit value.", 10 | "type": "int" 11 | }] 12 | -------------------------------------------------------------------------------- /tests/scenarios/deposit/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Make a proposal to change the default proposal deposit, vote for it and " 5 | "then assure that the DAO's proposal deposit did indeed change" 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('fuel') 11 | 12 | bytecode = calculate_bytecode( 13 | 'changeProposalDeposit', 14 | ('uint256', ctx.args.deposit_new_value) 15 | ) 16 | ctx.create_js_file(substitutions={ 17 | "dao_abi": ctx.dao_abi, 18 | "dao_address": ctx.dao_address, 19 | "proposal_deposit": ctx.args.proposal_deposit, 20 | "transaction_bytecode": bytecode, 21 | "debating_period": ctx.args.deposit_debate_seconds 22 | } 23 | ) 24 | print( 25 | "Notice: Debate period is {} seconds so the test will wait " 26 | "as much".format(ctx.args.proposal_debate_seconds) 27 | ) 28 | 29 | ctx.execute(expected={ 30 | "deposit_after_vote": ctx.args.deposit_new_value 31 | }) 32 | -------------------------------------------------------------------------------- /tests/scenarios/deposit/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var prop_id = attempt_proposal( 3 | dao, // DAO in question 4 | dao.address, // recipient 5 | proposalCreator, // proposal creator 6 | 0, // proposal amount in ether 7 | 'Changing proposal deposit', // description 8 | '$transaction_bytecode', //bytecode 9 | $debating_period, // debating period 10 | $proposal_deposit, // proposal deposit in ether 11 | false // whether it's a split proposal or not 12 | ); 13 | 14 | // in this scenario all users vote for the change 15 | for (i = 0; i < eth.accounts.length; i++) { 16 | dao.vote.sendTransaction( 17 | prop_id, 18 | true, 19 | { 20 | from: eth.accounts[i], 21 | gas: 1000000 22 | } 23 | ); 24 | } 25 | checkWork(); 26 | setTimeout(function() { 27 | miner.stop(); 28 | attempt_execute_proposal( 29 | dao, // target DAO 30 | prop_id, // proposal ID 31 | '$transaction_bytecode', // transaction bytecode 32 | curator, // proposal creator 33 | true, // should the proposal be closed after this call? 34 | true // should the proposal pass? 35 | ); 36 | 37 | addToTest('deposit_after_vote', parseInt(dao.proposalDeposit())); 38 | testResults(); 39 | }, $debating_period * 1000); 40 | miner.start(1); 41 | -------------------------------------------------------------------------------- /tests/scenarios/depositnofunds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/depositnofunds/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/depositnofunds/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "After the DAO has spent all of its money, make a proposal to change " 5 | "the default proposal deposit, vote for it and then assure that the DAO's " 6 | "proposal deposit did indeed change. Effectively test that things work " 7 | "fine after commit: 92beee5d27f66793689448903872302d0f4a6287" 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('spendall') 13 | 14 | bytecode = calculate_bytecode( 15 | 'changeProposalDeposit', 16 | ('uint256', ctx.args.deposit_new_value) 17 | ) 18 | ctx.create_js_file(substitutions={ 19 | "dao_abi": ctx.dao_abi, 20 | "dao_address": ctx.dao_address, 21 | "proposal_deposit": ctx.args.proposal_deposit, 22 | "transaction_bytecode": bytecode, 23 | "debating_period": ctx.args.deposit_debate_seconds 24 | } 25 | ) 26 | print( 27 | "Notice: Debate period is {} seconds so the test will wait " 28 | "as much".format(ctx.args.proposal_debate_seconds) 29 | ) 30 | 31 | ctx.execute(expected={ 32 | "deposit_after_vote": ctx.args.deposit_new_value 33 | }) 34 | -------------------------------------------------------------------------------- /tests/scenarios/depositnofunds/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var prop_id = attempt_proposal( 3 | dao, // DAO in question 4 | dao.address, // recipient 5 | proposalCreator, // proposal creator 6 | 0, // proposal amount in ether 7 | 'Changing proposal deposit', // description 8 | '$transaction_bytecode', //bytecode 9 | $debating_period, // debating period 10 | $proposal_deposit, // proposal deposit in ether 11 | false // whether it's a split proposal or not 12 | ); 13 | 14 | // in this scenario all users vote for the change 15 | for (i = 0; i < eth.accounts.length; i++) { 16 | dao.vote.sendTransaction( 17 | prop_id, 18 | true, 19 | { 20 | from: eth.accounts[i], 21 | gas: 1000000 22 | } 23 | ); 24 | } 25 | checkWork(); 26 | setTimeout(function() { 27 | miner.stop(); 28 | attempt_execute_proposal( 29 | dao, // target DAO 30 | prop_id, // proposal ID 31 | '$transaction_bytecode', // transaction bytecode 32 | curator, // proposal creator 33 | true, // should the proposal be closed after this call? 34 | true // should the proposal pass? 35 | ); 36 | 37 | addToTest('deposit_after_vote', parseInt(dao.proposalDeposit())); 38 | testResults(); 39 | }, $debating_period * 1000); 40 | miner.start(1); 41 | -------------------------------------------------------------------------------- /tests/scenarios/dthpool/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/dthpool/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/dthpool/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | scenario_description = ( 4 | "This scenario alternates various createProposal, vote, transfer," 5 | " setDelegate in various tokenHolders/delegates." 6 | " It checks that all the votes are counted in the correct way after each" 7 | " action." 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('fuel_predictive') 13 | 14 | minamount = 2 # is determined by the total costs + one time costs 15 | amount = random.randint(minamount, sum(ctx.token_amounts)) 16 | ctx.create_js_file(substitutions={ 17 | "dao_abi": ctx.dao_abi, 18 | "dao_address": ctx.dao_address, 19 | "offer_abi": ctx.offer_abi, 20 | "offer_address": ctx.offer_address, 21 | "offer_amount": amount, 22 | "offer_desc": 'Test Proposal', 23 | "dthpool_abi": ctx.dthpool_abi, 24 | "dthpool_address": ctx.dthpool_address, 25 | "proposal_deposit": ctx.args.proposal_deposit, 26 | "transaction_bytecode": '0x2ca15122' # solc --hashes SampleOffer.sol 27 | }) 28 | 29 | ctx.execute(expected={ 30 | "notDelegated": 8, 31 | "delegated": 2, 32 | "voteSet1": True, 33 | "willVote1": True, 34 | "supportsProposal1": True, 35 | "executed1": False, 36 | "y6": 2, 37 | "n6": 0 38 | }) 39 | -------------------------------------------------------------------------------- /tests/scenarios/dthpool/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var dthpool = web3.eth.contract($dthpool_abi).at('$dthpool_address'); 3 | 4 | checkWork(); 5 | 6 | var filter = web3.eth.filter('latest'); 7 | 8 | var pendingWaits = []; 9 | filter.watch(function (error, blockHash) { 10 | var block = eth.getBlock(blockHash); 11 | bn = block.number; 12 | 13 | var i=0; 14 | while (i cur.time )) || 17 | ((cur.time) && (!cur.block) && ((new Date()).getTime() > cur.time ))) 18 | { 19 | var last = pendingWaits.pop(); 20 | if (last !== cur) { 21 | pendingWaits[i] = last; 22 | } 23 | cur.cb(); 24 | } else { 25 | i+=1; 26 | } 27 | } 28 | }); 29 | 30 | function waitBlock(cb) { 31 | var pendingWait = { 32 | block: eth.blockNumber + 6, 33 | time: (new Date()).getTime() + 5*1000, 34 | cb: cb 35 | }; 36 | pendingWaits.push(pendingWait); 37 | } 38 | 39 | function waitTime(t, cb) { 40 | var pendingWait = { 41 | time: (new Date()).getTime() + t*1000, 42 | cb: cb 43 | }; 44 | pendingWaits.push(pendingWait); 45 | } 46 | 47 | 48 | var data_newDefaultDelegate; 49 | 50 | function run(actions, cb) { 51 | 52 | var endAction = function(err, res) { 53 | if (idx >=0) { 54 | if (err) { 55 | console.log("Error in step: " + idx + "err:" + err); 56 | return cb(err); 57 | } 58 | if (actions[idx].action==="V") { 59 | addToTest('y' + actions[idx].step, parseInt(web3.fromWei(dao.proposals(actions[idx].proposal)[9]))); 60 | addToTest('n' + actions[idx].step, parseInt(web3.fromWei(dao.proposals(actions[idx].proposal)[10]))); 61 | } 62 | if (typeof actions[idx].test === "function") { 63 | actions[idx].test(actions[idx]); 64 | } 65 | } 66 | idx += 1; 67 | if (idx == actions.length) { 68 | return cb(); 69 | } 70 | exec_pos(idx); 71 | }; 72 | 73 | 74 | var exec_pos = function(idx) { 75 | if (actions[idx].action === "Aprobe") { 76 | console.log("Aprobe Step: " + actions[idx].step); 77 | dao.approve.sendTransaction( dthpool.address , web3.toWei(actions[idx].amount), {from: eth.accounts[actions[idx].account], gas: 1000000}, function(err) { 78 | if (err) return endAction(err); 79 | waitBlock(endAction); 80 | }); 81 | } else if (actions[idx].action === "Delegate") { 82 | console.log("Delegating step: " + actions[idx].step); 83 | dthpool.delegateDAOTokens.sendTransaction( web3.toWei(actions[idx].amount) , {from: eth.accounts[actions[idx].account], gas: 1000000 }, function(err) { 84 | if (err) return endAction(err); 85 | waitBlock(endAction); 86 | }); 87 | } else if (actions[idx].action === "CreateProposal") { 88 | console.log("Creating Proposal: " + actions[idx].step); 89 | dao.newProposal.sendTransaction(eth.accounts[0], web3.toWei(20,'ether'), "proposal1", 0, 60, false, {from: eth.accounts[0], gas: 1000000, value: web3.toWei(25,'ether')}, function(err) { 90 | if (err) return endAction(err); 91 | waitBlock(endAction); 92 | }); 93 | } else if (actions[idx].action === "SetVoteIntention") { 94 | console.log("Set vote intention step: " + actions[idx].step); 95 | dthpool.setVoteIntention.sendTransaction( actions[idx].proposal , actions[idx].willVote , actions[idx].supportsProposal, "test motivation", {from: eth.accounts[actions[idx].account], gas: 1000000 }, function(err) { 96 | if (err) return endAction(err); 97 | waitBlock(endAction); 98 | }); 99 | } else if (actions[idx].action === "executeAllVotes") { 100 | console.log("Set executeAllVotes step: " + actions[idx].step); 101 | dthpool.executeAllVotes.sendTransaction({from: eth.accounts[actions[idx].account], gas: 1000000 }, function(err) { 102 | if (err) return endAction(err); 103 | waitBlock(endAction); 104 | }); 105 | } else if (actions[idx].action === "Wait") { 106 | console.log("Waiting step: " + actions[idx].step); 107 | waitTime(actions[idx].time, endAction); 108 | } 109 | }; 110 | 111 | var idx =-1; 112 | endAction(); 113 | } 114 | 115 | 116 | var steps = [ 117 | { step: 1, action:"Aprobe", account: 0, amount: 2}, 118 | { step: 2, action:"Delegate", account: 0 , amount: 2, test: function() { 119 | addToTest("notDelegated",web3.fromWei(dao.balanceOf(eth.accounts[0]))); 120 | addToTest("delegated",web3.fromWei(dthpool.balanceOf(eth.accounts[0]))); 121 | }}, 122 | { step: 3, action:"CreateProposal", account: 1 , proposal: 1, supports: false}, 123 | { step: 4, action:"SetVoteIntention", account: 0 , proposal: 1, willVote: true, supportsProposal: true, test: function() { 124 | addToTest( "voteSet1", dthpool.proposalStatuses(1)[0]); 125 | addToTest( "willVote1", dthpool.proposalStatuses(1)[1]); 126 | addToTest( "supportsProposal1", dthpool.proposalStatuses(1)[2]); 127 | addToTest( "executed1", dthpool.proposalStatuses(1)[3]); 128 | }}, 129 | { step: 5, action:"Wait", time: 30}, 130 | { step: 6, action:"executeAllVotes", account: 1 ,proposal: 1, test: function() { 131 | addToTest('y6' , parseInt(web3.fromWei(dao.proposals(1)[9]))); 132 | addToTest('n6' , parseInt(web3.fromWei(dao.proposals(1)[10]))); 133 | }} 134 | 135 | ]; 136 | 137 | miner.start(2); 138 | 139 | run(steps, function() { 140 | filter.stopWatching(); 141 | testResults(); 142 | miner.stop(); 143 | }); 144 | 145 | console.log("Executing steps"); 146 | 147 | -------------------------------------------------------------------------------- /tests/scenarios/extrabalance/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/extrabalance/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode, to_wei 2 | 3 | scenario_description = ( 4 | "The DAO spent all its money and has to resort to retrieving money from " 5 | "the extra balance account. This test checks that this is succesful." 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('spendall') 11 | extra_balance_ether_to_get = 5 12 | bytecode = calculate_bytecode( 13 | 'payOut', 14 | ('address', ctx.dao_address), 15 | ('uint256', to_wei(extra_balance_ether_to_get)) 16 | ) 17 | ctx.create_js_file(substitutions={ 18 | "dao_abi": ctx.dao_abi, 19 | "dao_address": ctx.dao_address, 20 | "proposal_deposit": ctx.args.proposal_deposit, 21 | "debating_period": ctx.args.proposal_debate_seconds, 22 | "transaction_bytecode": bytecode 23 | }) 24 | 25 | ctx.execute(expected={ 26 | "dao_balance_diff_after_claim": extra_balance_ether_to_get 27 | }) 28 | -------------------------------------------------------------------------------- /tests/scenarios/extrabalance/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | addToTest('dao_total_balance_before', web3.fromWei(dao.actualBalance())); 4 | 5 | var extraBalance = dao.extraBalance(); 6 | var claim_prop_id = attempt_proposal( 7 | dao, // DAO in question 8 | extraBalance, // recipient 9 | proposalCreator, // proposal creator 10 | 0, // proposal amount in ether 11 | 'Ask the extraBalance account to pay out to the DAO', // description 12 | '$transaction_bytecode', // transaction bytecode 13 | $debating_period, // debating period 14 | $proposal_deposit, // proposal deposit in ether 15 | false // whether it's a split proposal or not 16 | ); 17 | console.log("Voting on the extra balance payout proposal"); 18 | for (i = 0; i < eth.accounts.length; i++) { 19 | dao.vote.sendTransaction( 20 | claim_prop_id, 21 | true, 22 | { 23 | from: eth.accounts[i], 24 | gas: 1000000 25 | } 26 | ); 27 | } 28 | checkWork(); 29 | 30 | setTimeout(function() { 31 | miner.stop(); 32 | console.log("After extra balance payout debating period. NOW is: " + Math.floor(Date.now() / 1000)); 33 | attempt_execute_proposal( 34 | dao, // target DAO 35 | claim_prop_id, // proposal ID 36 | '$transaction_bytecode', // transaction bytecode 37 | proposalCreator, // proposal creator 38 | true, // should the proposal be closed after this call? 39 | true // should the proposal pass? 40 | ); 41 | 42 | addToTest('dao_total_balance_after_claim', web3.fromWei(dao.actualBalance())); 43 | addToTest('dao_balance_diff_after_claim', 44 | testMap['dao_total_balance_after_claim'].sub( 45 | testMap['dao_total_balance_before'] 46 | ).round()); 47 | 48 | testResults(); 49 | }, $debating_period * 1000); 50 | console.log("Wait for end of debating period for claiming extraBalance payout"); 51 | miner.start(1); 52 | 53 | -------------------------------------------------------------------------------- /tests/scenarios/firecontractor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/firecontractor/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/firecontractor/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Test that the contractor can be fired once the contract has been signed" 5 | " and that all the remaining money is returned to the DAO." 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('proposal') 11 | bytecode = calculate_bytecode('returnRemainingEther') 12 | ctx.create_js_file(substitutions={ 13 | "dao_abi": ctx.dao_abi, 14 | "dao_address": ctx.dao_address, 15 | "offer_abi": ctx.offer_abi, 16 | "offer_address": ctx.offer_address, 17 | "proposal_deposit": ctx.args.proposal_deposit, 18 | "transaction_bytecode": bytecode, 19 | "debating_period": ctx.args.proposal_debate_seconds, 20 | }) 21 | print( 22 | "Notice: Debate period is {} seconds so the test will wait " 23 | "as much".format(ctx.args.proposal_debate_seconds) 24 | ) 25 | 26 | ctx.execute(expected={ 27 | "got_back_all_money": True, 28 | "bad_proposal_failed": True, 29 | "offer_contract_valid": False 30 | }) 31 | -------------------------------------------------------------------------------- /tests/scenarios/firecontractor/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 3 | 4 | var prop_id = attempt_proposal( 5 | dao, // DAO in question 6 | '$offer_address', // recipient 7 | proposalCreator, // proposal creator 8 | 0, // proposal amount in ether 9 | 'Give us back our money!', // description 10 | '$transaction_bytecode', //bytecode 11 | $debating_period, // debating period 12 | $proposal_deposit, // proposal deposit in ether 13 | false // whether it's a split proposal or not 14 | ); 15 | 16 | var bad_prop_id = attempt_proposal( 17 | dao, // DAO in question 18 | '$offer_address', // recipient 19 | proposalCreator, // proposal creator 20 | 10, // proposal amount in ether 21 | 'Give us back our money - bad!', // description 22 | '$transaction_bytecode', //bytecode 23 | $debating_period, // debating period 24 | $proposal_deposit, // proposal deposit in ether 25 | false // whether it's a split proposal or not 26 | ); 27 | 28 | 29 | console.log("Voting for the proposal to fire the contractor"); 30 | for (i = 0; i < eth.accounts.length; i++) { 31 | dao.vote.sendTransaction( 32 | prop_id, 33 | true, //omg it's unanimous! 34 | { 35 | from: eth.accounts[i], 36 | gas: 1000000 37 | } 38 | ); 39 | dao.vote.sendTransaction( 40 | bad_prop_id, 41 | true, //omg it's unanimous! 42 | { 43 | from: eth.accounts[i], 44 | gas: 1000000 45 | } 46 | ); 47 | } 48 | checkWork(); 49 | var offer_balance_before = eth.getBalance(offer.address); 50 | var dao_rewardaccount_before = eth.getBalance(dao.DAOrewardAccount()); 51 | 52 | setTimeout(function() { 53 | miner.stop(); 54 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 55 | // first of all attempt to execute the proposal that sneaks in a value 56 | attempt_execute_proposal( 57 | dao, // target DAO 58 | bad_prop_id, // proposal ID 59 | '$transaction_bytecode', // transaction bytecode 60 | proposalCreator, // proposal creator 61 | false, // should the proposal be closed after this call? 62 | false // should the proposal pass? 63 | ); 64 | var offer_balance_after = eth.getBalance(offer.address); 65 | addToTest('bad_proposal_failed', offer_balance_after.eq(offer_balance_before)); 66 | // addToTest('bad_proposal_failed', true); 67 | addToTest('after_bad_proposal_offer_has', web3.fromWei(eth.getBalance(offer.address))); 68 | addToTest('after_bad_contract_valid', offer.getIsContractValid()); 69 | 70 | attempt_execute_proposal( 71 | dao, // target DAO 72 | prop_id, // proposal ID 73 | '$transaction_bytecode', // transaction bytecode 74 | proposalCreator, // proposal creator 75 | true, // should the proposal be closed after this call? 76 | true // should the proposal pass? 77 | ); 78 | 79 | offer_balance_after = eth.getBalance(offer.address); 80 | var dao_rewardaccount_after = eth.getBalance(dao.DAOrewardAccount()); 81 | 82 | var offer_diff = offer_balance_after.sub(offer_balance_before); 83 | var rewards_diff = dao_rewardaccount_before.sub(dao_rewardaccount_after); 84 | 85 | addToTest('got_back_all_money', rewards_diff.eq(offer_diff)); 86 | addToTest('offer_contract_valid', offer.getIsContractValid()); 87 | 88 | testResults(); 89 | }, $debating_period * 1000); 90 | console.log("Wait for end of debating period"); 91 | miner.start(1); 92 | -------------------------------------------------------------------------------- /tests/scenarios/fuel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/fuel/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/fuel/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils import constrained_sum_sample_pos, arr_str 3 | 4 | 5 | scenario_description = ( 6 | "During the fueling period of the DAO, send enough ether from all " 7 | "accounts to create tokens and then assert that the user's balance is " 8 | "indeed correct and that the minimum fueling goal has been reached" 9 | ) 10 | 11 | 12 | def run(ctx): 13 | ctx.assert_scenario_ran('deploy') 14 | 15 | creation_secs = ctx.remaining_time() 16 | ctx.total_supply = ( 17 | ctx.args.deploy_min_tokens_to_create + random.randint(1, 100) 18 | ) 19 | ctx.token_amounts = constrained_sum_sample_pos( 20 | len(ctx.accounts), ctx.total_supply 21 | ) 22 | ctx.create_js_file(substitutions={ 23 | "dao_abi": ctx.dao_abi, 24 | "dao_address": ctx.dao_address, 25 | "wait_ms": (creation_secs-3)*1000, 26 | "amounts": arr_str(ctx.token_amounts) 27 | } 28 | ) 29 | print( 30 | "Notice: Fueling period is {} seconds so the test will wait " 31 | "as much".format(creation_secs) 32 | ) 33 | 34 | adjusted_amounts = ( 35 | [x/1.5 for x in ctx.token_amounts] 36 | if ctx.scenario_uses_extrabalance() else ctx.token_amounts 37 | ) 38 | adjusted_supply = ( 39 | ctx.total_supply / 1.5 40 | if ctx.scenario_uses_extrabalance() else ctx.total_supply 41 | ) 42 | 43 | ctx.execute(expected={ 44 | "dao_fueled": True, 45 | "total_supply": adjusted_supply, 46 | "balances": adjusted_amounts, 47 | "user0_after": adjusted_amounts[0] 48 | }) 49 | -------------------------------------------------------------------------------- /tests/scenarios/fuel/template.js: -------------------------------------------------------------------------------- 1 | var amounts = $amounts; 2 | 3 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 4 | console.log("Creating DAO tokens"); 5 | for (i = 0; i < eth.accounts.length; i++) { 6 | web3.eth.sendTransaction({ 7 | from:eth.accounts[i], 8 | to: dao.address, 9 | gas:200000, 10 | value:web3.toWei(amounts[i], "ether") 11 | }); 12 | } 13 | 14 | checkWork(); 15 | 16 | setTimeout(function() { 17 | miner.stop(); 18 | addToTest('dao_fueled', dao.isFueled()); 19 | addToTest('total_supply', parseFloat(web3.fromWei(dao.totalSupply()))); 20 | var balances = []; 21 | for (i = 0; i < eth.accounts.length; i++) { 22 | balances.push(parseFloat(web3.fromWei(dao.balanceOf(eth.accounts[i])))); 23 | } 24 | addToTest('balances', balances); 25 | 26 | // now also try to create some extra tokens after the creation ended 27 | // note we use createTokenProxy() directly because with the edited code 28 | // for the test the fallback function becomes a DAO donation code right 29 | // after the end of the creation period 30 | dao.createTokenProxy.sendTransaction(eth.accounts[0],{ 31 | from:eth.accounts[0], 32 | to: dao.address, 33 | gas:200000, 34 | value:web3.toWei(20, "ether") 35 | }); 36 | // and confirm balance is still the same 37 | checkWork(); 38 | addToTest('user0_after', parseFloat(web3.fromWei(dao.balanceOf(eth.accounts[0])))); 39 | testResults(); 40 | }, $wait_ms); 41 | console.log("Wait for end of creation"); 42 | miner.start(1); 43 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/fuel_fail/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils import constrained_sum_sample_pos, arr_str 3 | 4 | 5 | scenario_description = ( 6 | "During the fueling period of the DAO, send insufficient ether " 7 | "and assert that the DAO is not fueled. Then assert that each user can " 8 | "get a full refund" 9 | ) 10 | 11 | 12 | def run(ctx): 13 | ctx.assert_scenario_ran('deploy') 14 | 15 | accounts_num = len(ctx.accounts) 16 | creation_secs = ctx.remaining_time() 17 | ctx.total_supply = random.randint( 18 | 5, ctx.args.deploy_min_tokens_to_create - 4 19 | ) 20 | ctx.token_amounts = constrained_sum_sample_pos( 21 | accounts_num, ctx.total_supply 22 | ) 23 | ctx.create_js_file(substitutions={ 24 | "dao_abi": ctx.dao_abi, 25 | "dao_address": ctx.dao_address, 26 | "wait_ms": (creation_secs-3)*1000, 27 | "amounts": arr_str(ctx.token_amounts) 28 | } 29 | ) 30 | print( 31 | "Notice: Fueling period is {} seconds so the test will wait " 32 | "as much".format(creation_secs) 33 | ) 34 | ctx.execute(expected={ 35 | "dao_fueled": False, 36 | "total_supply": ctx.total_supply, 37 | "refund": ctx.token_amounts 38 | }) 39 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail/template.js: -------------------------------------------------------------------------------- 1 | var amounts = $amounts; 2 | 3 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 4 | console.log("Creating DAO tokens"); 5 | for (i = 0; i < eth.accounts.length; i++) { 6 | web3.eth.sendTransaction({ 7 | from:eth.accounts[i], 8 | to: dao.address, 9 | gas:200000, 10 | value:web3.toWei(amounts[i], "ether") 11 | }); 12 | } 13 | 14 | checkWork(); 15 | 16 | setTimeout(function() { 17 | miner.stop(); 18 | addToTest('dao_min_tokens_to_create', dao.minTokensToCreate()); 19 | addToTest('dao_fueled', dao.isFueled()); 20 | addToTest('total_supply', parseFloat(web3.fromWei(dao.totalSupply()))); 21 | 22 | // since fueling failed let's get a refund 23 | var eth_balance_before_refund = []; 24 | for (i = 0; i < eth.accounts.length; i++) { 25 | eth_balance_before_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 26 | } 27 | addToTest('eth_balance_before_refund', eth_balance_before_refund); 28 | 29 | for (i = 0; i < eth.accounts.length; i++) { 30 | dao.refund.sendTransaction({ 31 | from:eth.accounts[i], 32 | gas:200000 33 | }); 34 | } 35 | checkWork(); 36 | // try to ask for a refund again and see if we get more (we shouldn't) 37 | for (i = 0; i < eth.accounts.length; i++) { 38 | dao.refund.sendTransaction({ 39 | from:eth.accounts[i], 40 | gas:200000 41 | }); 42 | } 43 | checkWork(); 44 | var eth_balance_after_refund = []; 45 | for (i = 0; i < eth.accounts.length; i++) { 46 | eth_balance_after_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 47 | } 48 | addToTest('eth_balance_after_refund', eth_balance_after_refund); 49 | 50 | var refund = []; 51 | for (i = 0; i < eth.accounts.length; i++) { 52 | refund.push((bigDiffRound( 53 | testMap['eth_balance_after_refund'][i], 54 | testMap['eth_balance_before_refund'][i] 55 | ))); 56 | } 57 | addToTest('refund', refund); 58 | 59 | testResults(); 60 | }, $wait_ms); 61 | console.log("Wait for end of creation"); 62 | miner.start(1); 63 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/fuel_fail2/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail2/run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from utils import constrained_sum_sample_pos, arr_str 3 | 4 | 5 | scenario_description = ( 6 | "During the fueling period of the DAO, create DAO tokens from all accounts " 7 | "with both normal creation and with createTokenProxy(). When the goal " 8 | "is not reached make sure that the refunds when having used " 9 | "createTokenProxy() are distributed back to the users correctly" 10 | ) 11 | 12 | 13 | def run(ctx): 14 | ctx.assert_scenario_ran('deploy') 15 | 16 | accounts_num = len(ctx.accounts) 17 | if accounts_num * 2 >= ctx.args.deploy_min_tokens_to_create - 4: 18 | print("Please increase the minimum fueling goal for the scenario.") 19 | sys.exit(1) 20 | 21 | creation_secs = ctx.remaining_time() 22 | total_supply = ctx.args.deploy_min_tokens_to_create - 4 23 | proxy_amounts = constrained_sum_sample_pos( 24 | accounts_num, total_supply / 2 25 | ) 26 | normal_amounts = constrained_sum_sample_pos( 27 | accounts_num, total_supply / 2 28 | ) 29 | ctx.token_amounts = [ 30 | sum(x) for x in zip(proxy_amounts[::-1], normal_amounts) 31 | ] 32 | ctx.total_supply = sum(ctx.token_amounts) 33 | ctx.create_js_file( 34 | substitutions={ 35 | "dao_abi": ctx.dao_abi, 36 | "dao_address": ctx.dao_address, 37 | "wait_ms": (creation_secs-3)*1000, 38 | "proxy_amounts": arr_str(proxy_amounts), 39 | "normal_amounts": arr_str(normal_amounts) 40 | } 41 | ) 42 | print( 43 | "Notice: Fueling period is {} seconds so the test will wait " 44 | "as much".format(creation_secs) 45 | ) 46 | ctx.execute(expected={ 47 | "dao_fueled": False, 48 | "total_supply": ctx.total_supply, 49 | "refund": ctx.token_amounts 50 | }) 51 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail2/template.js: -------------------------------------------------------------------------------- 1 | var proxy_amounts = $proxy_amounts; 2 | var normal_amounts = $normal_amounts; 3 | 4 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 5 | console.log("Creating DAO tokens"); 6 | for (i = 0; i < eth.accounts.length; i++) { 7 | dao.createTokenProxy.sendTransaction( 8 | eth.accounts[eth.accounts.length - 1 - i], 9 | { 10 | from:eth.accounts[i], 11 | gas:200000, 12 | value:web3.toWei(proxy_amounts[i], "ether") 13 | }); 14 | } 15 | for (i = 0; i < eth.accounts.length; i++) { 16 | web3.eth.sendTransaction({ 17 | from:eth.accounts[i], 18 | to: dao.address, 19 | gas:200000, 20 | value:web3.toWei(normal_amounts[i], "ether") 21 | }); 22 | } 23 | 24 | checkWork(); 25 | 26 | setTimeout(function() { 27 | miner.stop(); 28 | addToTest('dao_min_tokens_to_create', dao.minTokensToCreate()); 29 | addToTest('dao_fueled', dao.isFueled()); 30 | addToTest('total_supply', parseInt(web3.fromWei(dao.totalSupply()))); 31 | 32 | // since fueling failed let's get a refund 33 | var eth_balance_before_refund = []; 34 | for (i = 0; i < eth.accounts.length; i++) { 35 | eth_balance_before_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 36 | } 37 | addToTest('eth_balance_before_refund', eth_balance_before_refund); 38 | 39 | for (i = 0; i < eth.accounts.length; i++) { 40 | dao.refund.sendTransaction({ 41 | from:eth.accounts[i], 42 | gas:200000 43 | }); 44 | } 45 | checkWork(); 46 | // try to ask for a refund again and see if we get more (we shouldn't) 47 | for (i = 0; i < eth.accounts.length; i++) { 48 | dao.refund.sendTransaction({ 49 | from:eth.accounts[i], 50 | gas:200000 51 | }); 52 | } 53 | checkWork(); 54 | var eth_balance_after_refund = []; 55 | for (i = 0; i < eth.accounts.length; i++) { 56 | eth_balance_after_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 57 | } 58 | addToTest('eth_balance_after_refund', eth_balance_after_refund); 59 | 60 | var refund = []; 61 | for (i = 0; i < eth.accounts.length; i++) { 62 | refund.push((bigDiffRound( 63 | testMap['eth_balance_after_refund'][i], 64 | testMap['eth_balance_before_refund'][i] 65 | ))); 66 | } 67 | addToTest('refund', refund); 68 | 69 | testResults(); 70 | }, $wait_ms); 71 | console.log("Wait for end of creation"); 72 | miner.start(1); 73 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail_extrabalance/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail_extrabalance/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils import constrained_sum_sample_pos, arr_str 3 | 4 | 5 | scenario_description = ( 6 | "During the fueling period of the DAO, using the extra balance, send" 7 | "insufficient ether and assert that the DAO is not fueled. Then assert " 8 | "that each user can get a full refund including the portion that was sent " 9 | "to the extra balance account. Essentially test that commit " 10 | "9ebf64b44bf5948a2df48a7072168106fcb1f69b does its job correctly." 11 | ) 12 | 13 | 14 | def run(ctx): 15 | ctx.assert_scenario_ran('deploy') 16 | 17 | accounts_num = len(ctx.accounts) 18 | creation_secs = ctx.remaining_time() 19 | ctx.total_supply = random.randint( 20 | 5, ctx.args.deploy_min_tokens_to_create - 4 21 | ) 22 | ctx.token_amounts = constrained_sum_sample_pos( 23 | accounts_num, ctx.total_supply 24 | ) 25 | ctx.create_js_file(substitutions={ 26 | "dao_abi": ctx.dao_abi, 27 | "dao_address": ctx.dao_address, 28 | "wait_ms": (creation_secs)*1000, 29 | "amounts": arr_str(ctx.token_amounts) 30 | } 31 | ) 32 | print( 33 | "Notice: Fueling period is {} seconds so the test will wait " 34 | "as much".format(creation_secs) 35 | ) 36 | adjusted_supply = ctx.total_supply / 1.5 37 | 38 | ctx.execute(expected={ 39 | "dao_fueled": False, 40 | "total_supply": adjusted_supply, 41 | "refund": ctx.token_amounts 42 | }) 43 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_fail_extrabalance/template.js: -------------------------------------------------------------------------------- 1 | var amounts = $amounts; 2 | 3 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 4 | console.log("Creating DAO tokens"); 5 | for (i = 0; i < eth.accounts.length; i++) { 6 | web3.eth.sendTransaction({ 7 | from:eth.accounts[i], 8 | to: dao.address, 9 | gas:200000, 10 | value:web3.toWei(amounts[i], "ether") 11 | }); 12 | } 13 | 14 | checkWork(); 15 | 16 | setTimeout(function() { 17 | miner.stop(); 18 | addToTest('dao_min_tokens_to_create', dao.minTokensToCreate()); 19 | addToTest('dao_fueled', dao.isFueled()); 20 | addToTest('total_supply', parseFloat(web3.fromWei(dao.totalSupply()))); 21 | 22 | // since fueling failed let's get a refund 23 | var eth_balance_before_refund = []; 24 | for (i = 0; i < eth.accounts.length; i++) { 25 | eth_balance_before_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 26 | } 27 | addToTest('eth_balance_before_refund', eth_balance_before_refund); 28 | 29 | for (i = 0; i < eth.accounts.length; i++) { 30 | dao.refund.sendTransaction({ 31 | from:eth.accounts[i], 32 | gas:200000 33 | }); 34 | } 35 | checkWork(); 36 | // try to ask for a refund again and see if we get more (we shouldn't) 37 | for (i = 0; i < eth.accounts.length; i++) { 38 | dao.refund.sendTransaction({ 39 | from:eth.accounts[i], 40 | gas:200000 41 | }); 42 | } 43 | checkWork(); 44 | var eth_balance_after_refund = []; 45 | for (i = 0; i < eth.accounts.length; i++) { 46 | eth_balance_after_refund.push(web3.fromWei(eth.getBalance(eth.accounts[i]))); 47 | } 48 | addToTest('eth_balance_after_refund', eth_balance_after_refund); 49 | 50 | var refund = []; 51 | for (i = 0; i < eth.accounts.length; i++) { 52 | refund.push((bigDiffRound( 53 | testMap['eth_balance_after_refund'][i], 54 | testMap['eth_balance_before_refund'][i] 55 | ))); 56 | } 57 | addToTest('refund', refund); 58 | 59 | testResults(); 60 | }, $wait_ms); 61 | console.log("Wait for end of creation"); 62 | miner.start(1); 63 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_predictive/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/fuel_predictive/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/fuel_predictive/run.py: -------------------------------------------------------------------------------- 1 | from utils import constrained_sum_sample_pos, arr_str 2 | 3 | 4 | scenario_description = ( 5 | "This sccenario fuels the DAO with 5 tokenHollders with 10,20,30,40,50 tokens each" 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('deploy') 11 | 12 | creation_secs = ctx.remaining_time() 13 | ctx.total_supply = 150 14 | ctx.token_amounts = [10 , 20 , 30, 40, 50] 15 | ctx.create_js_file(substitutions={ 16 | "dao_abi": ctx.dao_abi, 17 | "dao_address": ctx.dao_address, 18 | "wait_ms": (creation_secs-3)*1000, 19 | "amounts": arr_str(ctx.token_amounts) 20 | } 21 | ) 22 | print( 23 | "Notice: Fueling period is {} seconds so the test will wait " 24 | "as much".format(creation_secs) 25 | ) 26 | 27 | ctx.execute(expected={ 28 | "dao_fueled": True, 29 | "total_supply": 150, 30 | "balances": [10,20,30,40,50] 31 | }) 32 | -------------------------------------------------------------------------------- /tests/scenarios/fuel_predictive/template.js: -------------------------------------------------------------------------------- 1 | var amounts = $amounts; 2 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 3 | console.log("Creating DAO tokens"); 4 | 5 | if (eth.accounts.length<5) { 6 | console.log("For this test, at least 5 accounts must be created."); 7 | } 8 | for (i = 0; i < amounts.length; i++) { 9 | web3.eth.sendTransaction({ 10 | from:eth.accounts[i], 11 | to: dao.address, 12 | gas:1000000, 13 | value:web3.toWei(amounts[i], "ether") 14 | } /* , function(err, res) { 15 | if (err) { 16 | console.log(err); 17 | } 18 | console.log("succes: " + res); 19 | } */); 20 | } 21 | 22 | checkWork(); 23 | 24 | setTimeout(function() { 25 | miner.stop(); 26 | addToTest('dao_fueled', dao.isFueled()); 27 | addToTest('total_supply', parseFloat(web3.fromWei(dao.totalSupply()))); 28 | var balances = []; 29 | for (i = 0; i < amounts.length; i++) { 30 | balances.push(parseFloat(web3.fromWei(dao.balanceOf(eth.accounts[i])))); 31 | } 32 | addToTest('balances', balances); 33 | 34 | testResults(); 35 | }, $wait_ms); 36 | console.log("Wait for end of creation"); 37 | miner.start(1); 38 | -------------------------------------------------------------------------------- /tests/scenarios/multisplitrewards/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/multisplitrewards/run.py: -------------------------------------------------------------------------------- 1 | from utils import arr_str 2 | import time 3 | 4 | scenario_description = ( 5 | """Split out of an already split DAO thus generating a grancdchild DAO. 6 | Subsequently test that rewards can be appropriately claimed for all of 7 | these DAOs by their participants as expected.""" 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('split') 13 | # right after the split scenario ran, wait sufficient time for the 14 | # child_dao closingTime() to be reached. 15 | time_now = round(time.time()) 16 | if time_now < ctx.child_dao_closing_time: 17 | wait_for_secs = ctx.child_dao_closing_time - time_now 18 | print( 19 | "The child DAO's closing time is not yet reached. Test will wait " 20 | "for {} seconds.".format(wait_for_secs) 21 | ) 22 | time.sleep(wait_for_secs) 23 | ctx.create_js_file(substitutions={ 24 | "dao_abi": ctx.dao_abi, 25 | "dao_address": ctx.dao_address, 26 | "split_execution_period": ctx.args.split_execution_period, 27 | "child_dao_curator": ctx.child_dao_curator, 28 | "grandchild_dao_curator": ctx.grandchild_dao_curator, 29 | "child_dao_address": ctx.child_dao_address, 30 | "child_dao_members": arr_str(ctx.child_dao_members), 31 | "proposal_deposit": ctx.args.proposal_deposit, 32 | "debating_period": ctx.args.proposal_debate_seconds 33 | } 34 | ) 35 | print( 36 | "Notice: Debate period is {} seconds so the test will wait " 37 | "as much".format(ctx.args.proposal_debate_seconds) 38 | ) 39 | 40 | ctx.execute(expected={ 41 | "grandchild_curator_dao_balance": ctx.grandchild_dao_curator_before, 42 | "split_proposal_passed": True 43 | }) 44 | -------------------------------------------------------------------------------- /tests/scenarios/multisplitrewards/template.js: -------------------------------------------------------------------------------- 1 | var dao_abi = $dao_abi; 2 | var dao = eth.contract(dao_abi).at('$dao_address'); 3 | var child_dao = eth.contract(dao_abi).at('$child_dao_address'); 4 | var child_curator = '$child_dao_curator'; 5 | var child_dao_members = $child_dao_members; 6 | var grandchild_curator = '$grandchild_dao_curator'; 7 | var split_execution_period = $split_execution_period; 8 | addToTest('grandchild_curator_childdao_balance', web3.fromWei(child_dao.balanceOf(grandchild_curator)).ceil()); 9 | if (child_curator == grandchild_curator) { 10 | testFail("Child curator can't also be grandchild curator"); 11 | } 12 | 13 | var child_prop_id = attempt_proposal( 14 | child_dao, // DAO in question 15 | grandchild_curator, // recipient 16 | grandchild_curator, // proposal creator 17 | 0, // proposal amount in ether 18 | 'Proposal to split the child DAO to the grand child DAO', // description 19 | '', //bytecode 20 | $debating_period, // debating period 21 | 0, // proposal deposit in ether 22 | true // whether it's a split proposal or not 23 | ); 24 | 25 | console.log("Vote to split to grandchild DAO."); 26 | child_dao.vote.sendTransaction(child_prop_id, true, {from: grandchild_curator, gas: 1000000}); 27 | checkWork(); 28 | console.log("After vote to split call"); 29 | setTimeout(function() { 30 | miner.stop(); 31 | console.log("Executing grandchild DAO split proposal..."); 32 | // now each user who voted for the split should call splitDAO to execute the proposal 33 | attempt_split( 34 | child_dao, 35 | child_prop_id, 36 | grandchild_curator, 37 | grandchild_curator, 38 | split_execution_period 39 | ); 40 | checkWork(); 41 | console.log("After split execution"); 42 | addToTest('split_proposal_passed', child_dao.proposals(child_prop_id)[5]); 43 | addToTest('proposal_newdao', child_dao.splitProposalNewAddress(child_prop_id, 0)); 44 | 45 | 46 | var grandchild_dao = web3.eth.contract(dao_abi).at(testMap['proposal_newdao']); 47 | addToTest('grandchild_dao_total_supply', parseInt(web3.fromWei(grandchild_dao.totalSupply()))); 48 | addToTest('grandchild_dao_proposal_deposit', parseInt(web3.fromWei(grandchild_dao.proposalDeposit()))); 49 | addToTest('grandchild_curator_dao_balance', web3.fromWei(grandchild_dao.balanceOf(grandchild_curator)).ceil()); 50 | testResults(); 51 | }, $debating_period * 1000); 52 | console.log("Wait for end of debating period"); 53 | -------------------------------------------------------------------------------- /tests/scenarios/newcontract/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/newcontract/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/newcontract/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | """A test of the DAO contract upgrade. We create a new contract with a 5 | completely different code and vote to transfer everything to the new 6 | contract. 7 | """ 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('rewards') 13 | 14 | # let's just use an existing account 15 | newAddress = ctx.accounts[4] 16 | bytecode = calculate_bytecode('newContract', ('address', ctx.accounts[4])) 17 | ctx.create_js_file(substitutions={ 18 | "dao_abi": ctx.dao_abi, 19 | "dao_address": ctx.dao_address, 20 | "new_contract_address": newAddress, 21 | "proposal_deposit": ctx.args.proposal_deposit, 22 | "transaction_bytecode": bytecode, 23 | "debating_period": ctx.args.proposal_debate_seconds 24 | }) 25 | print( 26 | "Notice: Debate period is {} seconds so the test will wait " 27 | "as much".format(ctx.args.proposal_debate_seconds) 28 | ) 29 | 30 | ctx.execute(expected={ 31 | "dao_balance_after": 0, 32 | "money_transferred": True, 33 | "reward_tokens_transferred": True 34 | }) 35 | -------------------------------------------------------------------------------- /tests/scenarios/newcontract/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | // No need to have an actual contract for the purpose of the test 4 | var newContract = '$new_contract_address'; 5 | addToTest('new_contract_balance_before', web3.fromWei(eth.getBalance(newContract))); 6 | addToTest('dao_balance_before', web3.fromWei(eth.getBalance(dao.address))); 7 | addToTest('dao_reward_token_before', web3.fromWei(dao.rewardToken(dao.address))); 8 | addToTest('new_contract_reward_token_before', web3.fromWei(dao.rewardToken(newContract))); 9 | console.log("Add new contract as allowed recipient"); 10 | dao.changeAllowedRecipients.sendTransaction(newContract, true, {from: curator, gas: 1000000}); 11 | checkWork(); 12 | 13 | var prop_id = attempt_proposal( 14 | dao, // DAO in question 15 | dao.address, // recipient 16 | proposalCreator, // proposal creator 17 | 0, // proposal amount in ether 18 | 'Move all funds to a new contract', // description 19 | '$transaction_bytecode', //bytecode 20 | $debating_period, // debating period 21 | $proposal_deposit, // proposal deposit in ether 22 | false // whether it's a split proposal or not 23 | ); 24 | 25 | console.log("Vote on the proposal to update"); 26 | for (i = 0; i < eth.accounts.length; i++) { 27 | dao.vote.sendTransaction( 28 | prop_id, 29 | true, 30 | { 31 | from: eth.accounts[i], 32 | gas: 4000000 33 | } 34 | ); 35 | } 36 | checkWork(); 37 | 38 | setTimeout(function() { 39 | miner.stop(); 40 | attempt_execute_proposal( 41 | dao, // target DAO 42 | prop_id, // proposal ID 43 | '$transaction_bytecode', // transaction bytecode 44 | curator, // proposal creator 45 | true, // should the proposal be closed after this call? 46 | true // should the proposal pass? 47 | ); 48 | 49 | addToTest('new_contract_balance_after', web3.fromWei(eth.getBalance(newContract))); 50 | addToTest('dao_balance_after', web3.fromWei(eth.getBalance(dao.address))); 51 | addToTest('dao_reward_token_after', web3.fromWei(dao.rewardToken(dao.address))); 52 | addToTest('new_contract_reward_token_after', web3.fromWei(dao.rewardToken(newContract))); 53 | 54 | addToTest('new_contract_balance', bigDiff( 55 | testMap['new_contract_balance_after'], 56 | testMap['new_contract_balance_before'] 57 | )); 58 | addToTest( 59 | 'money_transferred', 60 | testMap['new_contract_balance'].ceil().equals(testMap['dao_balance_before'].ceil()) 61 | ); 62 | addToTest( 63 | 'reward_tokens_transferred', 64 | testMap['new_contract_reward_token_after'].ceil().equals(testMap['dao_reward_token_before'].ceil()) 65 | ); 66 | 67 | 68 | testResults(); 69 | }, $debating_period * 1000); 70 | console.log("Wait for end of debating period"); 71 | miner.start(1); 72 | -------------------------------------------------------------------------------- /tests/scenarios/newcontractfail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/newcontractfail/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/newcontractfail/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode, create_votes_array_for_quorum, arr_str 2 | 3 | scenario_description = ( 4 | """A test of the DAO contract upgrade where the proposal's quorum ends up 5 | being insufficient (<53.3%) and the proposal gets rejected. 6 | """ 7 | ) 8 | 9 | 10 | def run(ctx): 11 | ctx.assert_scenario_ran('fuel') 12 | 13 | votes = create_votes_array_for_quorum(ctx.token_amounts, 0.4, True, False) 14 | # let's just use an existing account 15 | newAddress = ctx.accounts[4] 16 | bytecode = calculate_bytecode('newContract', ("address", newAddress)) 17 | ctx.create_js_file(substitutions={ 18 | "dao_abi": ctx.dao_abi, 19 | "dao_address": ctx.dao_address, 20 | "new_contract_address": newAddress, 21 | "proposal_deposit": ctx.args.proposal_deposit, 22 | "votes": arr_str(votes), 23 | "transaction_bytecode": bytecode, 24 | "debating_period": ctx.args.proposal_debate_seconds 25 | }) 26 | print( 27 | "Notice: Debate period is {} seconds so the test will wait " 28 | "as much".format(ctx.args.proposal_debate_seconds) 29 | ) 30 | 31 | ctx.execute(expected={ 32 | "new_contract_balance": 0, 33 | "dao_balance_diff": 0 34 | }) 35 | -------------------------------------------------------------------------------- /tests/scenarios/newcontractfail/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | // No need to have an actual contract for the purpose of the test 4 | var newContract = '$new_contract_address'; 5 | addToTest('new_contract_balance_before', web3.fromWei(eth.getBalance(newContract))); 6 | addToTest('dao_balance_before', web3.fromWei(eth.getBalance(dao.address))); 7 | console.log("Add new contract as allowed recipient"); 8 | dao.changeAllowedRecipients.sendTransaction(newContract, true, {from: curator, gas: 1000000}); 9 | checkWork(); 10 | 11 | var prop_id = attempt_proposal( 12 | dao, // DAO in question 13 | dao.address, // recipient 14 | proposalCreator, // proposal creator 15 | 0, // proposal amount in ether 16 | 'Move all funds to a new contract', // description 17 | '$transaction_bytecode', //bytecode 18 | $debating_period, // debating period 19 | $proposal_deposit, // proposal deposit in ether 20 | false // whether it's a split proposal or not 21 | ); 22 | 23 | console.log("Vote on the proposal to update"); 24 | var votes = $votes; 25 | for (i = 0; i < votes.length; i++) { 26 | dao.vote.sendTransaction( 27 | prop_id, 28 | votes[i], 29 | { 30 | from: eth.accounts[i], 31 | gas: 4000000 32 | } 33 | ); 34 | } 35 | checkWork(); 36 | 37 | setTimeout(function() { 38 | miner.stop(); 39 | attempt_execute_proposal( 40 | dao, // target DAO 41 | prop_id, // proposal ID 42 | '$transaction_bytecode', // transaction bytecode 43 | curator, // proposal creator 44 | false, // should the proposal be closed after this call? 45 | false // should the proposal pass? 46 | ); 47 | 48 | addToTest('new_contract_balance_after', web3.fromWei(eth.getBalance(newContract))); 49 | addToTest('new_contract_balance', bigDiff( 50 | testMap['new_contract_balance_after'], 51 | testMap['new_contract_balance_before'] 52 | )); 53 | addToTest('dao_balance_after', web3.fromWei(eth.getBalance(dao.address))); 54 | addToTest('dao_balance_diff', bigDiff( 55 | testMap['dao_balance_after'], 56 | testMap['dao_balance_before'] 57 | )); 58 | 59 | testResults(); 60 | }, $debating_period * 1000); 61 | console.log("Wait for end of debating period"); 62 | miner.start(1); 63 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/pfoffer/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/pfoffer/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Make a proposal to sign the PFOFfer and make sure that no money is " 5 | "transferred during the signing of the proposal. Also assert that " 6 | "calling getDailyPayment immediately after signing and within the " 7 | "payoutFreezePeriod fails." 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('fuel') 13 | bytecode = calculate_bytecode('sign') 14 | ctx.create_js_file(substitutions={ 15 | "dao_abi": ctx.dao_abi, 16 | "dao_address": ctx.dao_address, 17 | "pfoffer_abi": ctx.pfoffer_abi, 18 | "pfoffer_address": ctx.pfoffer_address, 19 | "offer_amount": ctx.args.deploy_total_costs, 20 | "proposal_deposit": ctx.args.proposal_deposit, 21 | "transaction_bytecode": bytecode, 22 | "debating_period": ctx.args.proposal_debate_seconds, 23 | "vote_status_deadline": ctx.args.deploy_pfoffer_vote_status_deadline 24 | }) 25 | print( 26 | "Notice: Debate period is {} seconds so the test will wait " 27 | "as much".format(ctx.args.proposal_debate_seconds) 28 | ) 29 | 30 | ctx.execute(expected={ 31 | "only_contractor_can_watch_proposal": True, 32 | "proposal_succesfully_watched": True, 33 | "approved_before_deadline": True, 34 | "no_money_at_sign": True, 35 | "contract_valid": True, 36 | "onetime_payment_failed": True 37 | }) 38 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var pfoffer = web3.eth.contract($pfoffer_abi).at('$pfoffer_address'); 3 | 4 | console.log("Add pfoffer contract as allowed recipient"); 5 | dao.changeAllowedRecipients.sendTransaction('$pfoffer_address', true, {from: curator, gas: 1000000}); 6 | checkWork(); 7 | 8 | var prop_id = attempt_proposal( 9 | dao, // DAO in question 10 | '$pfoffer_address', // recipient 11 | proposalCreator, // proposal creator 12 | $offer_amount, // proposal amount in ether 13 | 'PFOffer Description', // description 14 | '$transaction_bytecode', //bytecode 15 | $debating_period, // debating period 16 | $proposal_deposit, // proposal deposit in ether 17 | false // whether it's a split proposal or not 18 | ); 19 | 20 | // the contractor should now make the offer contract watch the proposal votes 21 | pfoffer.watchProposal.sendTransaction(prop_id, {from:curator, gas: 400000}); 22 | checkWork(); 23 | addToTest('only_contractor_can_watch_proposal', pfoffer.getProposalID() == 0); 24 | pfoffer.watchProposal.sendTransaction(prop_id, {from:contractor, gas: 400000}); 25 | checkWork(); 26 | addToTest('proposal_succesfully_watched', pfoffer.getProposalID().eq(prop_id)); 27 | 28 | 29 | console.log("Vote on the proposals"); 30 | for (i = 0; i < eth.accounts.length; i++) { 31 | dao.vote.sendTransaction( 32 | prop_id, 33 | true, 34 | { 35 | from: eth.accounts[i], 36 | gas: 4000000 37 | } 38 | ); 39 | } 40 | checkWork(); 41 | 42 | //perform the vote status check 43 | pfoffer.checkVoteStatus.sendTransaction({from: curator, gas: 400000}); 44 | checkWork(); 45 | addToTest('approved_before_deadline', pfoffer.getWasApprovedBeforeDeadline()); 46 | 47 | 48 | setTimeout(function() { 49 | miner.stop(); 50 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 51 | var contractor_before = eth.getBalance(contractor); 52 | // execute the sign() 53 | attempt_execute_proposal( 54 | dao, // target DAO 55 | prop_id, // proposal ID 56 | '$transaction_bytecode', // transaction bytecode 57 | proposalCreator, // proposal creator 58 | true, // should the proposal be closed after this call? 59 | true // should the proposal pass? 60 | ); 61 | var contractor_after = eth.getBalance(contractor); 62 | addToTest('no_money_at_sign', contractor_after.eq(contractor_before)); 63 | addToTest('contract_valid', pfoffer.getIsContractValid()); 64 | // now attempt to execute getOneTimePayment and expect it to fail 65 | pfoffer.performInitialWithdrawal.sendTransaction({from: contractor, gas: 300000}); 66 | checkWork(); 67 | var contractor_after_onetime = eth.getBalance(contractor); 68 | addToTest('onetime_payment_failed', contractor_after_onetime.sub(contractor_after).lt(web3.toWei(1))); 69 | 70 | testResults(); 71 | }, $debating_period * 1000); 72 | console.log("Wait for end of debating period"); 73 | miner.start(1); 74 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_checkvotestatus_fail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/pfoffer_checkvotestatus_fail/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_checkvotestatus_fail/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Work on a PFOFfer contract with a very big vote status deadline which " 5 | "will guarantee failure of the checkVoteStatus() call due to the " 6 | "aforementioned deadline. Check that the failure indeed happens." 7 | ) 8 | 9 | 10 | def run(ctx): 11 | ctx.assert_scenario_ran('fuel') 12 | bytecode = calculate_bytecode('sign') 13 | ctx.create_js_file(substitutions={ 14 | "dao_abi": ctx.dao_abi, 15 | "dao_address": ctx.dao_address, 16 | "pfoffer_abi": ctx.pfoffer_abi, 17 | "pfoffer_address": ctx.pfoffer_address, 18 | "offer_amount": ctx.args.deploy_total_costs, 19 | "proposal_deposit": ctx.args.proposal_deposit, 20 | "transaction_bytecode": bytecode, 21 | "debating_period": ctx.args.proposal_debate_seconds, 22 | "vote_status_deadline": ctx.args.deploy_pfoffer_vote_status_deadline 23 | }) 24 | 25 | ctx.execute(expected={ 26 | "proposal_succesfully_watched": True, 27 | "approved_before_deadline": False, 28 | }) 29 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_checkvotestatus_fail/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var pfoffer = web3.eth.contract($pfoffer_abi).at('$pfoffer_address'); 3 | 4 | console.log("Add pfoffer contract as allowed recipient"); 5 | dao.changeAllowedRecipients.sendTransaction('$pfoffer_address', true, {from: curator, gas: 1000000}); 6 | checkWork(); 7 | 8 | var prop_id = attempt_proposal( 9 | dao, // DAO in question 10 | '$pfoffer_address', // recipient 11 | proposalCreator, // proposal creator 12 | $offer_amount, // proposal amount in ether 13 | 'PFOffer Description', // description 14 | '$transaction_bytecode', //bytecode 15 | $debating_period, // debating period 16 | $proposal_deposit, // proposal deposit in ether 17 | false // whether it's a split proposal or not 18 | ); 19 | 20 | // the contractor should now make the offer contract watch the proposal votes 21 | pfoffer.watchProposal.sendTransaction(prop_id, {from:contractor, gas: 400000}); 22 | checkWork(); 23 | addToTest('proposal_succesfully_watched', pfoffer.getProposalID().eq(prop_id)); 24 | 25 | 26 | console.log("Vote on the proposals"); 27 | for (i = 0; i < eth.accounts.length; i++) { 28 | dao.vote.sendTransaction( 29 | prop_id, 30 | true, 31 | { 32 | from: eth.accounts[i], 33 | gas: 4000000 34 | } 35 | ); 36 | } 37 | checkWork(); 38 | 39 | //perform the vote status check 40 | pfoffer.checkVoteStatus.sendTransaction({from: curator, gas: 400000}); 41 | checkWork(); 42 | addToTest('approved_before_deadline', pfoffer.getWasApprovedBeforeDeadline()); 43 | testResults(); 44 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_payment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/pfoffer_payment/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_payment/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Get the onetime payment from the PFOffer contract after the payoutFreeze " 5 | "period has passed." 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('pfoffer') 11 | ctx.create_js_file(substitutions={ 12 | "dao_abi": ctx.dao_abi, 13 | "dao_address": ctx.dao_address, 14 | "pfoffer_abi": ctx.pfoffer_abi, 15 | "pfoffer_address": ctx.pfoffer_address, 16 | "offer_amount": ctx.args.deploy_total_costs, 17 | "expected_onetime": ctx.args.deploy_onetime_costs, 18 | "proposal_deposit": ctx.args.proposal_deposit, 19 | "debating_period": ctx.args.proposal_debate_seconds, 20 | "payout_freeze_period": ctx.args.deploy_pfoffer_payout_freeze_period 21 | }) 22 | 23 | ctx.execute(expected={ 24 | "one_time_paid": True, 25 | "one_time_costs_amount_as_expected": True 26 | }) 27 | -------------------------------------------------------------------------------- /tests/scenarios/pfoffer_payment/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var pfoffer = web3.eth.contract($pfoffer_abi).at('$pfoffer_address'); 3 | 4 | // assert the time is right 5 | var now = new BigNumber(time_now()); 6 | var sign_time = pfoffer.getDateOfSignature(); 7 | var wait_time = now.sub(sign_time.add($payout_freeze_period)); 8 | if (wait_time < 0) { 9 | wait_time = 0; 10 | } 11 | setTimeout(function() { 12 | var contractor_before = eth.getBalance(contractor); 13 | pfoffer.performInitialWithdrawal.sendTransaction({from: curator, gas: 300000}); 14 | checkWork(); 15 | var contractor_after = eth.getBalance(contractor); 16 | addToTest('only_contractor_can_call', !pfoffer.getInitialWithdrawalDone()); 17 | pfoffer.performInitialWithdrawal.sendTransaction({from: contractor, gas: 500000}); 18 | checkWork(); 19 | var contractor_after2 = eth.getBalance(contractor); 20 | addToTest('one_time_paid', pfoffer.getInitialWithdrawalDone()); 21 | addToTest( 22 | 'one_time_costs_amount_as_expected', 23 | contractor_after2.sub(contractor_after).sub(web3.toWei($expected_onetime)).abs().lt(new BigNumber(100000000000000000)) 24 | ); 25 | testResults(); 26 | }, wait_time * 1000); 27 | console.log("Waiting for " + wait_time + " seconds until the payoutfreezeperiod is over"); 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/scenarios/proposal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/proposal/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/proposal/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "debate_seconds", 3 | "default": 20, 4 | "description": "Number of seconds that a proposal should be open for voting", 5 | "type": "int" 6 | }, { 7 | "name": "deposit", 8 | "default": 25, 9 | "description": "The proposal deposit. Has to be more than 20 ether", 10 | "type": "int" 11 | }, { 12 | "name": "halveminquorum", 13 | "default": false, 14 | "description": "If true then halveMinQuorum() is called before voting on the proposal.", 15 | "type": "bool" 16 | }] 17 | -------------------------------------------------------------------------------- /tests/scenarios/proposal/run.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils import arr_str, create_votes_array, bool_to_str 3 | 4 | scenario_description = ( 5 | "Create a proposal to send an amount of ether to the SampleOffer contract." 6 | " Vote on that proposal, wait for the debating period and then execute it." 7 | " Assert that the proposal deposit is returned to its creator, and that " 8 | "the amount is sent to the SampleOffer and the promise is valid" 9 | ) 10 | 11 | 12 | def count_token_votes(amounts, votes): 13 | """Returns how many tokens votes yay and how many voted nay""" 14 | yay = 0 15 | nay = 0 16 | for idx, amount in enumerate(amounts): 17 | if votes[idx]: 18 | yay += amount 19 | else: 20 | nay += amount 21 | return yay, nay 22 | 23 | 24 | def run(ctx): 25 | ctx.assert_scenario_ran('fuel') 26 | 27 | votes = create_votes_array( 28 | ctx.token_amounts, 29 | not ctx.args.proposal_fail, 30 | False 31 | ) 32 | yay, nay = count_token_votes(ctx.token_amounts, votes) 33 | ctx.create_js_file(substitutions={ 34 | "dao_abi": ctx.dao_abi, 35 | "dao_address": ctx.dao_address, 36 | "offer_abi": ctx.offer_abi, 37 | "offer_address": ctx.offer_address, 38 | "offer_amount": ctx.args.deploy_total_costs, 39 | "offer_desc": 'Test Proposal', 40 | "proposal_deposit": ctx.args.proposal_deposit, 41 | "transaction_bytecode": '0x2ca15122', # solc --hashes SampleOffer.sol 42 | "debating_period": ctx.args.proposal_debate_seconds, 43 | "votes": arr_str(votes), 44 | "should_halve_minquorum": bool_to_str(ctx.args.proposal_halveminquorum) 45 | }) 46 | print( 47 | "Notice: Debate period is {} seconds so the test will wait " 48 | "as much".format(ctx.args.proposal_debate_seconds) 49 | ) 50 | 51 | ctx.execute(expected={ 52 | "dao_proposals_number": "1", 53 | "proposal_yay": yay, 54 | "proposal_nay": nay, 55 | "calculated_deposit": ctx.args.proposal_deposit, 56 | "onetime_costs": ctx.args.deploy_onetime_costs, 57 | "deposit_returned": True, 58 | "offer_promise_valid": True 59 | }) 60 | -------------------------------------------------------------------------------- /tests/scenarios/proposal/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 3 | 4 | console.log("Add offer contract as allowed recipient"); 5 | dao.changeAllowedRecipients.sendTransaction('$offer_address', true, {from: curator, gas: 1000000}); 6 | checkWork(); 7 | 8 | if ($should_halve_minquorum) { 9 | console.log("Calling dao.halveMinQuorum()"); 10 | dao.halveMinQuorum.sendTransaction({from: eth.accounts[0], gas: 1000000}); 11 | } 12 | 13 | addToTest('creator_balance_before', eth.getBalance(proposalCreator)); 14 | var prop_id = attempt_proposal( 15 | dao, // DAO in question 16 | '$offer_address', // recipient 17 | proposalCreator, // proposal creator 18 | $offer_amount, // proposal amount in ether 19 | '$offer_desc', // description 20 | '$transaction_bytecode', //bytecode 21 | $debating_period, // debating period 22 | $proposal_deposit, // proposal deposit in ether 23 | false // whether it's a split proposal or not 24 | ); 25 | 26 | addToTest('creator_balance_after_proposal', eth.getBalance(proposalCreator)); 27 | addToTest( 28 | 'calculated_deposit', 29 | web3.fromWei(testMap['creator_balance_before'].sub(testMap['creator_balance_after_proposal'])) 30 | ); 31 | addToTest('dao_proposals_number', dao.numberOfProposals()); 32 | 33 | var votes = $votes; 34 | console.log("Deadline is: " + dao.proposals(prop_id)[3] + " Voting ... "); 35 | for (i = 0; i < votes.length; i++) { 36 | console.log("User " + i +" is voting ["+ votes[i] +"]. His token balance is: " + web3.fromWei(dao.balanceOf(eth.accounts[i])) + " ether and NOW is: " + Math.floor(Date.now() / 1000)); 37 | dao.vote.sendTransaction( 38 | prop_id, 39 | votes[i], 40 | { 41 | from: eth.accounts[i], 42 | gas: 1000000 43 | } 44 | ); 45 | } 46 | checkWork(); 47 | addToTest('proposal_yay', parseInt(web3.fromWei(dao.proposals(prop_id)[9]))); 48 | addToTest('proposal_nay', parseInt(web3.fromWei(dao.proposals(prop_id)[10]))); 49 | addToTest('contractor_balance_before', eth.getBalance(contractor)); 50 | 51 | setTimeout(function() { 52 | miner.stop(); 53 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 54 | attempt_execute_proposal( 55 | dao, // target DAO 56 | prop_id, // proposal ID 57 | '$transaction_bytecode', // transaction bytecode 58 | proposalCreator, // proposal creator 59 | true, // should the proposal be closed after this call? 60 | true // should the proposal pass? 61 | ); 62 | 63 | addToTest('creator_balance_after_execution', eth.getBalance(proposalCreator)); 64 | addToTest('contractor_balance_after', eth.getBalance(contractor)); 65 | 66 | addToTest( 67 | 'onetime_costs', 68 | web3.fromWei(testMap['contractor_balance_after'].sub(testMap['contractor_balance_before'])) 69 | ); 70 | addToTest( 71 | 'deposit_returned', 72 | testMap['creator_balance_after_execution'].sub(testMap['creator_balance_before']).lt(new BigNumber(100000000000000000)) 73 | ); 74 | addToTest('offer_promise_valid', offer.getIsContractValid()); 75 | 76 | testResults(); 77 | }, $debating_period * 1000); 78 | console.log("Wait for end of debating period"); 79 | miner.start(1); 80 | -------------------------------------------------------------------------------- /tests/scenarios/rewards/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/rewards/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/rewards/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "total_amount", 3 | "default": 200, 4 | "description": "Amount of ether a kind soul will donate to the DAO in the rewards scenario.", 5 | "type": "int" 6 | }] 7 | -------------------------------------------------------------------------------- /tests/scenarios/rewards/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | " A kind soul donates to the DAO so the DAO has rewards for distribution. " 5 | "Create a proposal to send the rewards to the RewardsAccount, vote and " 6 | "execute it. Subsequently claim rewards and assert that they are " 7 | "proportional to the tokens held by the account claiming the reward." 8 | ) 9 | 10 | 11 | def calculate_reward(tokens, total_tokens, total_rewards): 12 | result = (tokens * float(total_rewards)) / float(total_tokens) 13 | return result 14 | 15 | 16 | def run(ctx): 17 | ctx.assert_scenario_ran('proposal') 18 | 19 | bytecode = calculate_bytecode('retrieveDAOReward', ("bool", True)) 20 | ctx.create_js_file(substitutions={ 21 | "dao_abi": ctx.dao_abi, 22 | "dao_address": ctx.dao_address, 23 | "total_rewards": ctx.args.rewards_total_amount, 24 | "proposal_deposit": ctx.args.proposal_deposit, 25 | "transaction_bytecode": bytecode, 26 | "debating_period": ctx.args.proposal_debate_seconds 27 | } 28 | ) 29 | print( 30 | "Notice: Debate period is {} seconds so the test will wait " 31 | "as much".format(ctx.args.proposal_debate_seconds) 32 | ) 33 | 34 | results = ctx.execute(expected={ 35 | "curator_reward_portion": calculate_reward( 36 | ctx.token_amounts[0], 37 | ctx.total_supply, 38 | ctx.args.rewards_total_amount) 39 | }) 40 | ctx.dao_balance_after_rewards = results['DAO_balance'] 41 | ctx.dao_rewardToken_after_rewards = results['DAO_rewardToken'] 42 | -------------------------------------------------------------------------------- /tests/scenarios/rewards/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | // some kind soul makes a donation to the DAO, so rewards get populated 4 | console.log("Donating to DAO..."); 5 | eth.sendTransaction({ 6 | from:eth.accounts[1], 7 | to: dao.DAOrewardAccount(), 8 | gas: 210000, 9 | value: web3.toWei($total_rewards, "ether") 10 | }); 11 | checkWork(); 12 | 13 | var prop_id = attempt_proposal( 14 | dao, // DAO in question 15 | dao.address, // recipient 16 | proposalCreator, // proposal creator 17 | 0, // proposal amount in ether 18 | 'Ask the DAO to retrieveDAOReward()', // description 19 | '$transaction_bytecode', //bytecode 20 | $debating_period, // debating period 21 | $proposal_deposit + 1, // proposal deposit in ether 22 | false // whether it's a split proposal or not 23 | ); 24 | 25 | console.log("Voting for proposal '" + prop_id + "' ..."); 26 | // in this scenario let's just say everyone votes 100% in favour 27 | for (i = 0; i < eth.accounts.length; i++) { 28 | dao.vote.sendTransaction( 29 | prop_id, 30 | true, 31 | { 32 | from: eth.accounts[i], 33 | gas: 1000000 34 | } 35 | ); 36 | } 37 | checkWork(); 38 | 39 | setTimeout(function() { 40 | miner.stop(); 41 | 42 | // now execute the proposal 43 | attempt_execute_proposal( 44 | dao, // target DAO 45 | prop_id, // proposal ID 46 | '$transaction_bytecode', // transaction bytecode 47 | curator, // proposal creator 48 | true, // should the proposal be closed after this call? 49 | true // should the proposal pass? 50 | ); 51 | 52 | addToTest('curator_balance_before_claim', eth.getBalance(curator)); 53 | console.log("Claiming the reward..."); 54 | dao.getMyReward.sendTransaction({from: curator, gas: 1000000}); 55 | checkWork(); 56 | addToTest('curator_balance_after_claim', eth.getBalance(curator)); 57 | addToTest( 58 | 'curator_reward_portion', 59 | parseFloat(web3.fromWei(bigDiff( 60 | testMap['curator_balance_after_claim'], testMap['curator_balance_before_claim'] 61 | ))) 62 | ); 63 | addToTest('DAO_balance', parseFloat(web3.fromWei(eth.getBalance('$dao_address')))); 64 | addToTest('DAO_rewardToken', parseFloat(web3.fromWei(dao.rewardToken('$dao_address')))); 65 | testResults(); 66 | }, $debating_period * 1000); 67 | console.log("Wait for end of debating period"); 68 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_changeclient/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_changeclient/run.py: -------------------------------------------------------------------------------- 1 | from utils import arr_str, calculate_bytecode 2 | import time 3 | 4 | scenario_description = ( 5 | """Test that the offer's client can be properly changed to a split DAO.""" 6 | ) 7 | 8 | 9 | def run(ctx): 10 | ctx.assert_scenario_ran('split') 11 | # right after the split scenario ran, wait sufficient time for the 12 | # child_dao closingTime() to be reached. 13 | time_now = round(time.time()) 14 | if time_now < ctx.child_dao_closing_time: 15 | wait_for_secs = ctx.child_dao_closing_time - time_now 16 | print( 17 | "The child DAO's closing time is not yet reached. Test will wait " 18 | "for {} seconds.".format(wait_for_secs) 19 | ) 20 | time.sleep(wait_for_secs) 21 | transaction_bytecode = calculate_bytecode( 22 | 'updateClientAddress', ('address', ctx.child_dao_address) 23 | ) 24 | ctx.create_js_file(substitutions={ 25 | "dao_abi": ctx.dao_abi, 26 | "dao_address": ctx.dao_address, 27 | "split_execution_period": ctx.args.split_execution_period, 28 | "child_dao_curator": ctx.child_dao_curator, 29 | "child_dao_address": ctx.child_dao_address, 30 | "offer_abi": ctx.offer_abi, 31 | "offer_address": ctx.offer_address, 32 | "child_dao_members": arr_str(ctx.child_dao_members), 33 | "proposal_deposit": ctx.args.proposal_deposit, 34 | "debating_period": ctx.args.proposal_debate_seconds, 35 | "transaction_bytecode": transaction_bytecode 36 | } 37 | ) 38 | print( 39 | "Notice: Debate period is {} seconds so the test will wait " 40 | "as much".format(ctx.args.proposal_debate_seconds) 41 | ) 42 | 43 | ctx.execute(expected={ 44 | "offer_client": ctx.child_dao_address, 45 | "offer_original_client": ctx.dao_address 46 | }) 47 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_changeclient/template.js: -------------------------------------------------------------------------------- 1 | var dao_abi = $dao_abi; 2 | var dao = eth.contract(dao_abi).at('$dao_address'); 3 | var child_dao = eth.contract(dao_abi).at('$child_dao_address'); 4 | var child_curator = '$child_dao_curator'; 5 | var child_dao_members = $child_dao_members; 6 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 7 | 8 | // The DAO will now propose for the child DAO to be the new client 9 | var prop_id = attempt_proposal( 10 | dao, // DAO in question 11 | '$offer_address', // recipient 12 | curator, // proposal creator 13 | 0, // proposal amount in ether 14 | 'Change the client of this Sampleoffer', // description 15 | '$transaction_bytecode', //bytecode 16 | $debating_period, // debating period 17 | $proposal_deposit, // proposal deposit in ether 18 | false // whether it's a split proposal or not 19 | ); 20 | 21 | console.log("Voting for the proposal to change client"); 22 | for (i = 0; i < eth.accounts.length; i++) { 23 | dao.vote.sendTransaction( 24 | prop_id, 25 | true, //omg it's unanimous! 26 | { 27 | from: eth.accounts[i], 28 | gas: 1000000 29 | } 30 | ); 31 | } 32 | checkWork(); 33 | 34 | setTimeout(function() { 35 | miner.stop(); 36 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 37 | attempt_execute_proposal( 38 | dao, // target DAO 39 | prop_id, // proposal ID 40 | '$transaction_bytecode', // transaction bytecode 41 | curator, // proposal creator 42 | true, // should the proposal be closed after this call? 43 | true // should the proposal pass? 44 | ); 45 | 46 | addToTest('offer_original_client', offer.getOriginalClient()); 47 | addToTest('offer_client', offer.getClient()); 48 | 49 | console.log("Add offer contract as allowed recipient for the Child DAO"); 50 | child_dao.changeAllowedRecipients.sendTransaction('$offer_address', true, {from: child_curator, gas: 1000000}); 51 | testResults(); 52 | 53 | }, $debating_period * 1000); 54 | miner.start(1); 55 | console.log("Wait for end of debating period"); 56 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_dailypayment/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_dailypayment/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode, to_wei 2 | 3 | scenario_description = ( 4 | "Test a normal usage of the SampleOffer contract where there is a payment " 5 | "coming in from a deployed USN node and it goes to the DAO Reward account." 6 | "Also test that the contractor can properly withdraw the daily payment, " 7 | "calculating how much Wei he entitlted to depend in the 'day-periods' " 8 | "that have passed since signing the contract." 9 | ) 10 | 11 | 12 | def run(ctx): 13 | ctx.assert_scenario_ran('proposal') 14 | daily_limit_in_ether = 5 15 | pay_reward_amount = 10 16 | bytecode = calculate_bytecode('setDailyWithdrawLimit', ('uint128', to_wei(daily_limit_in_ether))) 17 | ctx.create_js_file(substitutions={ 18 | "dao_abi": ctx.dao_abi, 19 | "dao_address": ctx.dao_address, 20 | "offer_abi": ctx.offer_abi, 21 | "offer_address": ctx.offer_address, 22 | "usn_abi": ctx.usn_abi, 23 | "usn_address": ctx.usn_address, 24 | "proposal_deposit": ctx.args.proposal_deposit, 25 | "pay_reward_amount": pay_reward_amount, 26 | "transaction_bytecode": bytecode, 27 | "debating_period": ctx.args.proposal_debate_seconds, 28 | "offer_payment_period": ctx.args.deploy_offer_payment_period 29 | }) 30 | print( 31 | "Notice: Debate period is {} seconds so the test will wait " 32 | "as much".format(ctx.args.proposal_debate_seconds) 33 | ) 34 | 35 | ctx.execute(expected={ 36 | "offer_daily_withdraw_limit": daily_limit_in_ether, 37 | "contractor_paid_expected": True, 38 | "dao_rewardaccount_diff": pay_reward_amount, 39 | "sample_offer_no_donations": True, 40 | }) 41 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_dailypayment/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 3 | var usn = web3.eth.contract($usn_abi).at('$usn_address'); 4 | 5 | addToTest('dao_rewardaccount_before', eth.getBalance(dao.DAOrewardAccount())); 6 | // 'emulate' a USN node payment 7 | usn.payReward.sendTransaction({from:eth.accounts[3], value: web3.toWei($pay_reward_amount), gas: 100000}); 8 | checkWork(); 9 | addToTest('dao_rewardaccount_after', eth.getBalance(dao.DAOrewardAccount())); 10 | addToTest( 11 | 'dao_rewardaccount_diff', 12 | web3.fromWei(bigDiff(testMap['dao_rewardaccount_after'], testMap['dao_rewardaccount_before'])) 13 | ); 14 | 15 | 16 | var offer_balance_before = eth.getBalance(offer.address); 17 | // also let's sneak in a check that SampleOffer does not accept random donations 18 | web3.eth.sendTransaction({from:eth.accounts[3], to:offer.address, value:web3.toWei(10), gas:24000}); 19 | var offer_balance_after = eth.getBalance(offer.address); 20 | addToTest('sample_offer_no_donations', offer_balance_after.eq(offer_balance_before)); 21 | 22 | 23 | // The DAO will now set the daily withdrawal limit 24 | var prop_id = attempt_proposal( 25 | dao, // DAO in question 26 | '$offer_address', // recipient 27 | proposalCreator, // proposal creator 28 | 0, // proposal amount in ether 29 | 'Set the daily withdrawal limit of SampleOffer', // description 30 | '$transaction_bytecode', //bytecode 31 | $debating_period, // debating period 32 | $proposal_deposit, // proposal deposit in ether 33 | false // whether it's a split proposal or not 34 | ); 35 | 36 | 37 | console.log("Voting for the proposal to set the Daily withdraw limit"); 38 | for (i = 0; i < eth.accounts.length; i++) { 39 | dao.vote.sendTransaction( 40 | prop_id, 41 | true, //omg it's unanimous! 42 | { 43 | from: eth.accounts[i], 44 | gas: 1000000 45 | } 46 | ); 47 | } 48 | checkWork(); 49 | 50 | setTimeout(function() { 51 | miner.stop(); 52 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 53 | attempt_execute_proposal( 54 | dao, // target DAO 55 | prop_id, // proposal ID 56 | '$transaction_bytecode', // transaction bytecode 57 | proposalCreator, // proposal creator 58 | true, // should the proposal be closed after this call? 59 | true // should the proposal pass? 60 | ); 61 | 62 | addToTest('offer_daily_withdraw_limit', web3.fromWei(offer.getDailyWithdrawalLimit())); 63 | 64 | addToTest('contractor_before', eth.getBalance(contractor)); 65 | // now the contractor can attempt to withdraw some money and we should check that this 66 | // occurs and is equal to the daily limit 67 | console.log("-->OfferBalance: " + web3.fromWei(eth.getBalance(offer.address))); 68 | offer.withdraw.sendTransaction({from: contractor, gas: 100000}); 69 | var withdrawTime = new BigNumber(Math.floor(Date.now() / 1000)); 70 | checkWork(); 71 | var expectedWei = ((withdrawTime.sub(offer.getDateOfSignature())).mul(offer.getDailyWithdrawalLimit())) 72 | .div(new BigNumber($offer_payment_period)); 73 | console.log("-->ExpectedWei: " + expectedWei); 74 | console.log("-->OfferBalanceAfter: " + web3.fromWei(eth.getBalance(offer.address))); 75 | addToTest('contractor_after', eth.getBalance(contractor)); 76 | addToTest('contractor_diff', 77 | bigDiff(testMap['contractor_after'], testMap['contractor_before']) 78 | ); 79 | // check that the contractor gets the Wei expected, within a 0.1 Ether difference 80 | var contractor_profit = testMap['contractor_diff'].sub(expectedWei).abs(); 81 | console.log("-->contractor_profit: " + contractor_profit); 82 | addToTest('contractor_paid_expected', contractor_profit.lt(new BigNumber(100000000000000000))); 83 | testResults(); 84 | }, $debating_period * 1000); 85 | console.log("Wait for end of debating period"); 86 | miner.start(1); 87 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_gettersvt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/sampleoffer_gettersvt/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_gettersvt/run.py: -------------------------------------------------------------------------------- 1 | from utils import arr_str, calculate_bytecode 2 | import time 3 | 4 | scenario_description = ( 5 | """Test that the offer's contract can't receive any money through its 6 | getter functions.""" 7 | ) 8 | 9 | 10 | def run(ctx): 11 | ctx.assert_scenario_ran('proposal') 12 | ctx.create_js_file(substitutions={ 13 | "offer_abi": ctx.offer_abi, 14 | "offer_address": ctx.offer_address, 15 | }) 16 | 17 | ctx.execute(expected={ 18 | "sample_offer_no_donations": True, 19 | }) 20 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_gettersvt/template.js: -------------------------------------------------------------------------------- 1 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 2 | var offer_balance_before = eth.getBalance(offer.address); 3 | 4 | offer.getTotalCost.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 5 | checkWork(); 6 | offer.getInitialWithdrawal.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 7 | checkWork(); 8 | offer.getDailyWithdrawalLimit.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 9 | checkWork(); 10 | offer.getMinDailyWithdrawalLimit.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 11 | checkWork(); 12 | offer.getPayoutFreezePeriod.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 13 | checkWork(); 14 | offer.getContractor.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 15 | checkWork(); 16 | offer.getHashOfTheProposalDocument.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 17 | checkWork(); 18 | offer.getLastWithdrawal.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 19 | checkWork(); 20 | offer.getDateOfSignature.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 21 | checkWork(); 22 | offer.getClient.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 23 | checkWork(); 24 | offer.getOriginalClient.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 25 | checkWork(); 26 | offer.getIsContractValid.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 27 | checkWork(); 28 | offer.getRewardDivisor.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 29 | checkWork(); 30 | offer.getDeploymentReward.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 31 | checkWork(); 32 | offer.getInitialWithdrawalDone.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 33 | checkWork(); 34 | offer.getVotingDeadline.sendTransaction({from:eth.accounts[0], value: web3.toWei(10), gas: 200000}); 35 | checkWork(); 36 | 37 | var offer_balance_after = eth.getBalance(offer.address); 38 | addToTest('sample_offer_no_donations', offer_balance_after.eq(offer_balance_before)); 39 | testResults(); 40 | 41 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_setrewardvars/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_setrewardvars/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode 2 | 3 | scenario_description = ( 4 | "Test that the DAO can properly set rewardDivisor and " 5 | "deploymentReward variables. After that call payOneTimeRewards()" 6 | "both with a value less than deploymentReward and a value greater " 7 | "than it and check that the results are as expected." 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('proposal') 13 | reward_divisor = 50000 14 | deployment_reward = 85200 15 | test_deployment_payment = deployment_reward + 10000 16 | set_div_bytecode = calculate_bytecode( 17 | 'setRewardDivisor', ('uint256', reward_divisor) 18 | ) 19 | set_deploy_bytecode = calculate_bytecode( 20 | 'setDeploymentReward', ('uint256', deployment_reward) 21 | ) 22 | ctx.create_js_file(substitutions={ 23 | "dao_abi": ctx.dao_abi, 24 | "dao_address": ctx.dao_address, 25 | "offer_abi": ctx.offer_abi, 26 | "offer_address": ctx.offer_address, 27 | "usn_abi": ctx.usn_abi, 28 | "usn_address": ctx.usn_address, 29 | "proposal_deposit": ctx.args.proposal_deposit, 30 | "set_div_bytecode": set_div_bytecode, 31 | "set_deploy_bytecode": set_deploy_bytecode, 32 | "debating_period": ctx.args.proposal_debate_seconds, 33 | "deployment_reward": deployment_reward, 34 | "test_deployment_payment": test_deployment_payment 35 | }) 36 | print( 37 | "Notice: Debate period is {} seconds so the test will wait " 38 | "as much".format(ctx.args.proposal_debate_seconds) 39 | ) 40 | 41 | ctx.execute(expected={ 42 | "offer_reward_divisor": reward_divisor, 43 | "offer_deployment_reward": deployment_reward, 44 | "pay_less_fails": True, 45 | "reward_payment": test_deployment_payment 46 | }) 47 | -------------------------------------------------------------------------------- /tests/scenarios/sampleoffer_setrewardvars/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var offer = web3.eth.contract($offer_abi).at('$offer_address'); 3 | var usn = web3.eth.contract($usn_abi).at('$usn_address'); 4 | 5 | var dao_rewardaccount_before = eth.getBalance(dao.DAOrewardAccount()); 6 | var dao_balance_before = eth.getBalance(dao.address); 7 | 8 | // make proposals to set both the rewardDivisor and the deploymentReward 9 | var div_prop_id = attempt_proposal( 10 | dao, // DAO in question 11 | '$offer_address', // recipient 12 | proposalCreator, // proposal creator 13 | 0, // proposal amount in ether 14 | 'Set the reward divisor variable', // description 15 | '$set_div_bytecode', //bytecode 16 | $debating_period, // debating period 17 | $proposal_deposit, // proposal deposit in ether 18 | false // whether it's a split proposal or not 19 | ); 20 | var deploy_prop_id = attempt_proposal( 21 | dao, // DAO in question 22 | '$offer_address', // recipient 23 | proposalCreator, // proposal creator 24 | 0, // proposal amount in ether 25 | 'Set the deployment reward proposal', // description 26 | '$set_deploy_bytecode', //bytecode 27 | $debating_period, // debating period 28 | $proposal_deposit, // proposal deposit in ether 29 | false // whether it's a split proposal or not 30 | ); 31 | 32 | 33 | console.log("Voting for both proposals"); 34 | for (i = 0; i < eth.accounts.length; i++) { 35 | dao.vote.sendTransaction( 36 | div_prop_id, 37 | true, //omg it's unanimous! 38 | { 39 | from: eth.accounts[i], 40 | gas: 1000000 41 | } 42 | ); 43 | dao.vote.sendTransaction( 44 | deploy_prop_id, 45 | true, //omg it's unanimous! 46 | { 47 | from: eth.accounts[i], 48 | gas: 1000000 49 | } 50 | ); 51 | } 52 | checkWork(); 53 | 54 | setTimeout(function() { 55 | miner.stop(); 56 | console.log("After debating period. NOW is: " + Math.floor(Date.now() / 1000)); 57 | attempt_execute_proposal( 58 | dao, // target DAO 59 | div_prop_id, // proposal ID 60 | '$set_div_bytecode', // transaction bytecode 61 | proposalCreator, // proposal creator 62 | true, // should the proposal be closed after this call? 63 | true // should the proposal pass? 64 | ); 65 | attempt_execute_proposal( 66 | dao, // target DAO 67 | deploy_prop_id, // proposal ID 68 | '$set_deploy_bytecode', // transaction bytecode 69 | proposalCreator, // proposal creator 70 | true, // should the proposal be closed after this call? 71 | true // should the proposal pass? 72 | ); 73 | 74 | // test that the variables are set appropriately 75 | addToTest('offer_reward_divisor', offer.getRewardDivisor()); 76 | addToTest('offer_deployment_reward', offer.getDeploymentReward()); 77 | 78 | var actor = eth.accounts[0]; 79 | // emulate a USN node with onetimerward payment smaller than the set deployment reward. 80 | var dao_rewardaccount_before = eth.getBalance(dao.DAOrewardAccount()); 81 | usn.payOneTimeReward.sendTransaction({from:actor, value: $deployment_reward - 10000, gas: 200000}); 82 | checkWork(); 83 | var dao_rewardaccount_after = eth.getBalance(dao.DAOrewardAccount()); 84 | addToTest('pay_less_fails', dao_rewardaccount_after.eq(dao_rewardaccount_before)); 85 | 86 | // emulate a USN node with onetimerward payment bigger than the set deployment reward. 87 | dao_rewardaccount_before = eth.getBalance(dao.DAOrewardAccount()); 88 | usn.payOneTimeReward.sendTransaction({from:actor, value: $test_deployment_payment, gas: 200000}); 89 | checkWork(); 90 | dao_rewardaccount_after = eth.getBalance(dao.DAOrewardAccount()); 91 | addToTest('reward_payment', dao_rewardaccount_after.sub(dao_rewardaccount_before)); 92 | 93 | testResults(); 94 | }, $debating_period * 1000); 95 | console.log("Wait for end of debating period"); 96 | miner.start(1); 97 | -------------------------------------------------------------------------------- /tests/scenarios/singlesplit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/singlesplit/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/singlesplit/run.py: -------------------------------------------------------------------------------- 1 | scenario_description = ( 2 | " An 'angry' user decides to get out of the DAO and take his money with " 3 | "him. He creates a proposal to split into an one-member DAO with himself " 4 | "as the Curator. Then he makes a proposal to this new DAO to " 5 | "transfer all of the money to himself. Assert that the money he gets back " 6 | "in the end is equal to the money he put in the original DAO." 7 | ) 8 | 9 | 10 | def run(ctx): 11 | ctx.assert_scenario_ran('fuel') 12 | 13 | ctx.create_js_file(substitutions={ 14 | "dao_abi": ctx.dao_abi, 15 | "dao_address": ctx.dao_address, 16 | "proposal_deposit": ctx.args.proposal_deposit, 17 | "debating_period": ctx.args.proposal_debate_seconds, 18 | "split_execution_period": ctx.args.split_execution_period 19 | }) 20 | print( 21 | "Notice: Debate period is {} seconds so the test will wait " 22 | "as much".format(ctx.args.proposal_debate_seconds) 23 | ) 24 | 25 | ctx.execute(expected={ 26 | "newdao_proposals_num": 1, 27 | "angry_user_profit": ctx.token_amounts[1] + ctx.args.proposal_deposit 28 | }) 29 | -------------------------------------------------------------------------------- /tests/scenarios/singlesplit/template.js: -------------------------------------------------------------------------------- 1 | var dao_abi = $dao_abi; 2 | var dao = web3.eth.contract(dao_abi).at('$dao_address'); 3 | var split_execution_period = $split_execution_period; 4 | var new_curator = eth.accounts[1]; 5 | 6 | var prop_id = attempt_proposal( 7 | dao, // DAO in question 8 | new_curator, // recipient 9 | new_curator, // proposal creator 10 | 0, // proposal amount in ether 11 | 'Our disgruntled user wants to split out', // description 12 | '', //bytecode 13 | $debating_period, // debating period 14 | 0, // proposal deposit in ether 15 | true // whether it's a split proposal or not 16 | ); 17 | 18 | console.log("Voting for split proposal '" + prop_id + "' ..."); 19 | for (i = 0; i < eth.accounts.length; i++) { 20 | dao.vote.sendTransaction( 21 | prop_id, 22 | i == 1 ? true : false, 23 | { 24 | from: eth.accounts[i], 25 | gas: 1000000 26 | } 27 | ); 28 | } 29 | checkWork(); 30 | addToTest('proposal_yay', parseInt(web3.fromWei(dao.proposals(prop_id)[9]))); 31 | addToTest('proposal_nay', parseInt(web3.fromWei(dao.proposals(prop_id)[10]))); 32 | 33 | setTimeout(function() { 34 | miner.stop(); 35 | // now our disgruntled user is the only one to execute the splitDAO function 36 | attempt_split(dao, prop_id, new_curator, new_curator, split_execution_period); 37 | 38 | console.log("After split execution"); 39 | addToTest('proposal_passed', dao.proposals(prop_id)[5]); 40 | addToTest('proposal_newdao', dao.splitProposalNewAddress(prop_id, 0)); 41 | 42 | var newdao = web3.eth.contract(dao_abi).at(testMap['proposal_newdao']); 43 | // check token balance of each user in both DAOs 44 | oldDAOBalance = []; 45 | newDAOBalance = []; 46 | for (i = 0; i < eth.accounts.length; i++) { 47 | oldDAOBalance.push(parseInt(web3.fromWei(dao.balanceOf(eth.accounts[i])))); 48 | newDAOBalance.push(parseInt(web3.fromWei(newdao.balanceOf(eth.accounts[i])))); 49 | } 50 | addToTest('oldDAOBalance', oldDAOBalance); 51 | addToTest('newDAOBalance', newDAOBalance); 52 | addToTest('newDAOTotalSupply', parseInt(web3.fromWei(newdao.totalSupply()))); 53 | 54 | setTimeout(function() { 55 | console.log("MINQUORUM REQUIRED: " + newdao.extMinQuorum(newdao.totalSupply())); 56 | // now our disgruntled user has his own DAO and is the SP of that DAO so ... 57 | var new_prop_id = attempt_proposal( 58 | newdao, // DAO in question 59 | new_curator, // recipient 60 | new_curator, // proposal creator 61 | testMap['newDAOTotalSupply'], // proposal amount in ether 62 | 'Send all money to myself!! Screw you guys ... I am going home!', // description 63 | '', //bytecode 64 | $debating_period, // debating period 65 | $proposal_deposit, // proposal deposit in ether 66 | false // whether it's a split proposal or not 67 | ); 68 | 69 | console.log("Angry user votes in his own DAO..."); 70 | newdao.vote.sendTransaction( 71 | new_prop_id, 72 | true, 73 | { 74 | from: new_curator, 75 | gas: 1000000 76 | }); 77 | checkWork(); 78 | addToTest('newdao_proposals_num', newdao.numberOfProposals()); 79 | addToTest('angry_user_before', web3.fromWei(eth.getBalance(new_curator))); 80 | setTimeout(function() { 81 | // now execute the proposal 82 | attempt_execute_proposal( 83 | newdao, // target DAO 84 | new_prop_id, // proposal ID 85 | '', // transaction bytecode 86 | new_curator, // proposal creator 87 | true, // should the proposal be closed after this call? 88 | true // should the proposal pass? 89 | ); 90 | 91 | addToTest('angry_user_after', web3.fromWei(eth.getBalance(new_curator))); 92 | addToTest( 93 | 'angry_user_profit', 94 | bigDiffRound(testMap['angry_user_after'], testMap['angry_user_before']) 95 | ); 96 | testResults(); 97 | }, $debating_period * 1000); 98 | console.log("Wait for end of second debating period"); 99 | }, split_execution_period * 1000); 100 | console.log("Wait for new DAO fueling period to end"); 101 | }, $debating_period * 1000); 102 | console.log("Wait for end of first debating period"); 103 | -------------------------------------------------------------------------------- /tests/scenarios/spendall/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/spendall/run.py: -------------------------------------------------------------------------------- 1 | scenario_description = ( 2 | "The DAO spends all its money. This scenario is but a pivot used " 3 | " by all the other scenarios that want to deal with extra balance." 4 | ) 5 | 6 | 7 | def run(ctx): 8 | ctx.assert_scenario_ran('fuel') 9 | ctx.create_js_file(substitutions={ 10 | "dao_abi": ctx.dao_abi, 11 | "dao_address": ctx.dao_address, 12 | "proposal_deposit": ctx.args.proposal_deposit, 13 | "debating_period": ctx.args.proposal_debate_seconds, 14 | }) 15 | 16 | ctx.execute(expected={ 17 | "dao_total_balance_after_spend": 0 18 | }) 19 | -------------------------------------------------------------------------------- /tests/scenarios/spendall/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | 3 | addToTest('dao_total_balance_before_spend', web3.fromWei(dao.actualBalance())); 4 | 5 | var prop_id = attempt_proposal( 6 | dao, // DAO in question 7 | curator, // recipient 8 | proposalCreator, // proposal creator 9 | web3.fromWei(dao.totalSupply()), // proposal amount in ether 10 | 'Spend almost all of the DAOs money', // description 11 | '', //bytecode 12 | $debating_period, // debating period 13 | $proposal_deposit, // proposal deposit in ether 14 | false // whether it's a split proposal or not 15 | ); 16 | 17 | 18 | console.log("Deadline is: " + dao.proposals(prop_id)[3] + " Voting ... "); 19 | for (i = 0; i < eth.accounts.length; i++) { 20 | dao.vote.sendTransaction( 21 | prop_id, 22 | true, 23 | { 24 | from: eth.accounts[i], 25 | gas: 1000000 26 | } 27 | ); 28 | } 29 | checkWork(); 30 | 31 | setTimeout(function() { 32 | miner.stop(); 33 | console.log("After spending debating period. NOW is: " + Math.floor(Date.now() / 1000)); 34 | attempt_execute_proposal( 35 | dao, // target DAO 36 | prop_id, // proposal ID 37 | '', // transaction bytecode 38 | proposalCreator, // proposal creator 39 | true, // should the proposal be closed after this call? 40 | true // should the proposal pass? 41 | ); 42 | 43 | addToTest('dao_total_balance_after_spend', web3.fromWei(dao.actualBalance())); 44 | testResults(); 45 | }, ($debating_period) * 1000); 46 | console.log("Wait for end of debating period for spending all of the DAO's money"); 47 | miner.start(1); 48 | -------------------------------------------------------------------------------- /tests/scenarios/split/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockchainsllc/DAO/e50d3bc008cfe0bbe4285de9dda54d3a541cb0b4/tests/scenarios/split/__init__.py -------------------------------------------------------------------------------- /tests/scenarios/split/arguments.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "debate_seconds", 3 | "default": 15, 4 | "description":"Number of seconds to debate the split proposal", 5 | "type": "int" 6 | }, { 7 | "name": "execution_period", 8 | "default": 50, 9 | "description":"Number of seconds after the voting deadline for which a split proposal is executable", 10 | "type": "int" 11 | }] 12 | -------------------------------------------------------------------------------- /tests/scenarios/split/run.py: -------------------------------------------------------------------------------- 1 | from utils import arr_str, create_votes_array 2 | 3 | 4 | scenario_description = ( 5 | " Testing an equal split, with a new Curator. Half of the token holders " 6 | "vote for a split to a new DAO and half vote to stay with the old one. " 7 | "Assert that the split happens, a new DAO is created and that the tokens " 8 | "are burned from the old DAO and moved to the new DAO succesfully. Also " 9 | "assert that the reward tokens are succesfully transferred" 10 | ) 11 | 12 | 13 | def tokens_after_split(votes, original_balance, dao_balance, reward_tokens): 14 | """ 15 | Create expected token and reward token results after the split scenario 16 | Parameters 17 | ---------- 18 | votes : array of booleans 19 | The votes array of what each user voted 20 | 21 | original_balance : array of ints 22 | The original amount of tokens each user had before the split 23 | 24 | dao_balance : int 25 | The balance of ether left in the DAO before the scenario started 26 | 27 | reward_tokens : float 28 | Amount of reward tokens generated in the DAO before the scenario. 29 | 30 | Returns 31 | ---------- 32 | old_dao_balance : array of ints 33 | The balance of tokens left in the old dao. 34 | 35 | new_dao_balance : array of ints 36 | The balance of tokens left in the new dao. 37 | 38 | old_reward_tokens : float 39 | The amount of reward tokens left in the old dao. 40 | 41 | new_reward_tokens : float 42 | The amount of reward tokens left in the new dao. 43 | """ 44 | 45 | old_dao_balance = [] 46 | new_dao_balance = [] 47 | totalSupply = sum(original_balance) 48 | old_reward_tokens = reward_tokens 49 | new_reward_tokens = 0 50 | 51 | for vote, orig in zip(votes, original_balance): 52 | if vote: 53 | new_dao_balance.append(orig * dao_balance / float(totalSupply)) 54 | old_dao_balance.append(0) 55 | rewardToMove = float(orig) * reward_tokens / float(totalSupply) 56 | old_reward_tokens -= float(rewardToMove) 57 | new_reward_tokens += float(rewardToMove) 58 | else: 59 | old_dao_balance.append(orig) 60 | new_dao_balance.append(0) 61 | return ( 62 | old_dao_balance, 63 | new_dao_balance, 64 | old_reward_tokens, 65 | new_reward_tokens 66 | ) 67 | 68 | 69 | def prepare_test_split(ctx, split_gas): 70 | ctx.assert_scenario_ran('rewards') 71 | 72 | votes = create_votes_array( 73 | ctx.token_amounts, 74 | not ctx.args.proposal_fail, 75 | True 76 | ) 77 | # remember the account information of the first True votes. They will be 78 | # the child dao curator in this scenario and grandchild curator in the next 79 | iterator = ( 80 | (ctx.accounts[i], ctx.token_amounts[i]) 81 | for i, vote in enumerate(votes) if vote is True 82 | ) 83 | (ctx.child_dao_curator, _) = next(iterator) 84 | (ctx.grandchild_dao_curator, ctx.grandchild_dao_curator_before) = next(iterator) 85 | ctx.create_js_file(substitutions={ 86 | "dao_abi": ctx.dao_abi, 87 | "dao_address": ctx.dao_address, 88 | "debating_period": ctx.args.split_debate_seconds, 89 | "split_execution_period": ctx.args.split_execution_period, 90 | "split_gas": split_gas, 91 | "votes": arr_str(votes), 92 | "child_dao_curator": ctx.child_dao_curator 93 | } 94 | ) 95 | print( 96 | "Notice: Debate period is {} seconds so the test will wait " 97 | "as much".format(ctx.args.split_debate_seconds) 98 | ) 99 | return votes 100 | 101 | 102 | def run(ctx): 103 | # Use the split_gas variable to test that splitting with insufficient gas, 104 | # will fail reliably and will not leave an empty contract in the state, 105 | # burning away user tokens in the process. 106 | # This should happen with the latest homestead changes: 107 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki#specification 108 | split_gas = 4700000 109 | 110 | votes = prepare_test_split(ctx, split_gas) 111 | oldBalance, newBalance, oldDAORewards, newDAORewards = tokens_after_split( 112 | votes, 113 | ctx.token_amounts, 114 | ctx.dao_balance_after_rewards, 115 | ctx.dao_rewardToken_after_rewards 116 | ) 117 | 118 | results = ctx.execute(expected={ 119 | "newDAOProposalDeposit": 0, 120 | "oldDAOBalance": oldBalance, 121 | "newDAOBalance": newBalance, 122 | "oldDaoRewardTokens": oldDAORewards, 123 | "newDaoRewardTokens": newDAORewards 124 | }) 125 | 126 | # remember some variables so they can be used in later tests 127 | ctx.child_dao_closing_time = results['new_dao_closing_time'] 128 | ctx.child_dao_address = results['proposal_newdao'] 129 | ctx.child_dao_members = [ 130 | ctx.accounts[idx] for idx, x in enumerate(votes) if x is True 131 | ] 132 | ctx.child_dao_balance_at_creation = results['new_dao_balance'] 133 | ctx.child_dao_total_supply_at_creation = results['new_dao_total_supply'] 134 | -------------------------------------------------------------------------------- /tests/scenarios/split/template.js: -------------------------------------------------------------------------------- 1 | var dao_abi = $dao_abi; 2 | var dao = web3.eth.contract(dao_abi).at('$dao_address'); 3 | var split_execution_period = $split_execution_period; 4 | var child_dao_curator = '$child_dao_curator'; 5 | 6 | var prop_id = attempt_proposal( 7 | dao, // DAO in question 8 | child_dao_curator, // recipient 9 | proposalCreator, // proposal creator 10 | 0, // proposal amount in ether 11 | 'Voting to split and change Curator', // description 12 | '', //bytecode 13 | $debating_period, // debating period 14 | 0, // proposal deposit in ether 15 | true // whether it's a split proposal or not 16 | ); 17 | 18 | var votes = $votes; 19 | console.log("Voting for split proposal '" + prop_id + "' ..."); 20 | for (i = 0; i < votes.length; i++) { 21 | console.log("User [" + i + "] is voting '" + votes[i] + "'for split proposal"); 22 | dao.vote.sendTransaction( 23 | prop_id, 24 | votes[i], 25 | { 26 | from: eth.accounts[i], 27 | gas: 1000000 28 | } 29 | ); 30 | } 31 | checkWork(); 32 | addToTest('proposal_yay', parseInt(web3.fromWei(dao.proposals(prop_id)[9]))); 33 | addToTest('proposal_nay', parseInt(web3.fromWei(dao.proposals(prop_id)[10]))); 34 | 35 | setTimeout(function() { 36 | miner.stop(); 37 | console.log("Executing the split proposal..."); 38 | // now each user who voted for the split should call splitDAO to execute the proposal 39 | for (i = 0; i < votes.length; i++) { 40 | if (votes[i]) { 41 | console.log("User [" + i + "] is calling splitDAO()"); 42 | attempt_split(dao, prop_id, eth.accounts[i], child_dao_curator, split_execution_period); 43 | } 44 | } 45 | console.log("After split execution"); 46 | addToTest('proposal_passed', dao.proposals(prop_id)[5]); 47 | addToTest('proposal_newdao', dao.splitProposalNewAddress(prop_id, 0)); 48 | 49 | var newdao = web3.eth.contract(dao_abi).at(testMap['proposal_newdao']); 50 | // check token balance of each user in both DAOs 51 | oldDAOBalance = []; 52 | newDAOBalance = []; 53 | for (i = 0; i < eth.accounts.length; i++) { 54 | oldDAOBalance.push(parseFloat(web3.fromWei(dao.balanceOf(eth.accounts[i])))); 55 | newDAOBalance.push(parseFloat(web3.fromWei(newdao.balanceOf(eth.accounts[i])))); 56 | } 57 | addToTest('oldDAOBalance', oldDAOBalance); 58 | addToTest('newDAOBalance', newDAOBalance); 59 | addToTest('oldDaoRewardTokens', parseFloat(web3.fromWei(dao.rewardToken('$dao_address')))); 60 | addToTest('newDaoRewardTokens', parseFloat(web3.fromWei(dao.rewardToken(testMap['proposal_newdao'])))); 61 | 62 | addToTest('new_dao_balance', web3.fromWei(eth.getBalance(newdao.address)).ceil()); 63 | addToTest('new_dao_total_supply', web3.fromWei(newdao.totalSupply()).ceil()); 64 | addToTest('newDAOProposalDeposit', parseInt(web3.fromWei(newdao.proposalDeposit()))); 65 | addToTest('new_dao_closing_time', parseInt(newdao.closingTime())); 66 | 67 | testResults(); 68 | }, $debating_period * 1000); 69 | console.log("Wait for end of debating period"); 70 | -------------------------------------------------------------------------------- /tests/scenarios/stealextrabalance/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/scenarios/stealextrabalance/run.py: -------------------------------------------------------------------------------- 1 | from utils import calculate_bytecode, to_wei 2 | 3 | scenario_description = ( 4 | "The DAO spent all its money and has to resort to retrieving money from " 5 | "the extra balance account. In the meantime a bad guy tries to pass a " 6 | "proposal to send all of the extraBalance money to himself. Assert that " 7 | "this is impossible." 8 | ) 9 | 10 | 11 | def run(ctx): 12 | ctx.assert_scenario_ran('spendall') 13 | attacker_address = ctx.accounts[3] 14 | bytecode = calculate_bytecode( 15 | 'payOut', 16 | ('address', attacker_address), 17 | ('uint256', to_wei(5)) 18 | ) 19 | ctx.create_js_file(substitutions={ 20 | "dao_abi": ctx.dao_abi, 21 | "dao_address": ctx.dao_address, 22 | "attacker_address": attacker_address, 23 | "proposal_deposit": ctx.args.proposal_deposit, 24 | "debating_period": ctx.args.proposal_debate_seconds, 25 | "transaction_bytecode": bytecode 26 | }) 27 | 28 | ctx.execute(expected={ 29 | "extra_balance_diff_after_attack": 0 30 | }) 31 | -------------------------------------------------------------------------------- /tests/scenarios/stealextrabalance/template.js: -------------------------------------------------------------------------------- 1 | var dao = web3.eth.contract($dao_abi).at('$dao_address'); 2 | var attacker = '$attacker_address'; 3 | var extraBalance = dao.extraBalance(); 4 | 5 | addToTest('extra_balance_before', web3.fromWei(eth.getBalance(extraBalance))); 6 | 7 | var claim_prop_id = attempt_proposal( 8 | dao, // DAO in question 9 | extraBalance, // recipient 10 | attacker, // proposal creator 11 | 0, // proposal amount in ether 12 | 'Ask the extraBalance account to pay out to the DAO', // description 13 | '$transaction_bytecode', // transaction bytecode 14 | $debating_period, // debating period 15 | $proposal_deposit, // proposal deposit in ether 16 | false // whether it's a split proposal or not 17 | ); 18 | console.log("Voting on the extra balance attack payout proposal"); 19 | for (i = 0; i < eth.accounts.length; i++) { 20 | dao.vote.sendTransaction( 21 | claim_prop_id, 22 | true, 23 | { 24 | from: eth.accounts[i], 25 | gas: 1000000 26 | } 27 | ); 28 | } 29 | checkWork(); 30 | 31 | setTimeout(function() { 32 | miner.stop(); 33 | console.log("After extra balance payout attack debating period"); 34 | attempt_execute_proposal( 35 | dao, // target DAO 36 | claim_prop_id, // proposal ID 37 | '$transaction_bytecode', // transaction bytecode 38 | attacker, // proposal creator 39 | false, // should the proposal be closed after this call? 40 | false // should the proposal pass? 41 | ); 42 | 43 | addToTest('extra_balance_after', web3.fromWei(eth.getBalance(extraBalance))); 44 | addToTest('extra_balance_diff_after_attack', 45 | testMap['extra_balance_after'].sub( 46 | testMap['extra_balance_before'] 47 | ).round()); 48 | 49 | testResults(); 50 | }, $debating_period * 1000); 51 | console.log("Wait for end of debating period for claiming extraBalance payout attack"); 52 | miner.start(1); 53 | 54 | -------------------------------------------------------------------------------- /tests/templates/accounts.template.js: -------------------------------------------------------------------------------- 1 | for (i = 0; i < $accounts_number; i++) { 2 | personal.newAccount("123"); 3 | } 4 | console.log(JSON.stringify(eth.accounts)); 5 | -------------------------------------------------------------------------------- /withdraw.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | // TODO: all constants need to be double checked 19 | import "github.com/slockit/DAO/DAO.sol"; 20 | 21 | contract Withdraw { 22 | DAO constant public mother = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413); 23 | mapping (address => bool) public whiteList; 24 | uint constant public totalSupply = 11712722930974665882186911; 25 | uint constant public totalWeiSupply = 12072858342395652843028271; 26 | uint constant public fixChildDAOsListTime = 1468057560; // 09.07.2016 - 11:46:00 CEST 27 | 28 | function Withdraw(){ 29 | // whitelist all childDAO except of attacker DAO (commented out) 30 | whiteList[0xd4fe7bc31cedb7bfb8a345f31e668033056b2728] = true; 31 | whiteList[0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f] = true; 32 | whiteList[0x1975bd06d486162d5dc297798dfc41edd5d160a7] = true; 33 | whiteList[0x319f70bab6845585f412ec7724b744fec6095c85] = true; 34 | whiteList[0x5c8536898fbb74fc7445814902fd08422eac56d0] = true; 35 | whiteList[0x779543a0491a837ca36ce8c635d6154e3c4911a6] = true; 36 | whiteList[0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5] = true; 37 | whiteList[0x200450f06520bdd6c527622a273333384d870efb] = true; 38 | whiteList[0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb] = true; 39 | whiteList[0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091] = true; 40 | 41 | whiteList[0x51e0ddd9998364a2eb38588679f0d2c42653e4a6] = true; 42 | whiteList[0xf0b1aa0eb660754448a7937c022e30aa692fe0c5] = true; 43 | whiteList[0x9f27daea7aca0aa0446220b98d028715e3bc803d] = true; 44 | whiteList[0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b] = true; 45 | whiteList[0x6f6704e5a10332af6672e50b3d9754dc460dfa4d] = true; 46 | whiteList[0x492ea3bb0f3315521c31f273e565b868fc090f17] = true; 47 | whiteList[0x9ea779f907f0b315b364b0cfc39a0fde5b02a416] = true; 48 | whiteList[0xcc34673c6c40e791051898567a1222daf90be287] = true; 49 | whiteList[0xe308bd1ac5fda103967359b2712dd89deffb7973] = true; 50 | whiteList[0xac1ecab32727358dba8962a0f3b261731aad9723] = true; 51 | 52 | whiteList[0x440c59b325d2997a134c2c7c60a8c61611212bad] = true; 53 | whiteList[0x9c15b54878ba618f494b38f0ae7443db6af648ba] = true; 54 | whiteList[0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241] = true; 55 | whiteList[0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b] = true; 56 | whiteList[0x6131c42fa982e56929107413a9d526fd99405560] = true; 57 | whiteList[0x542a9515200d14b68e934e9830d91645a980dd7a] = true; 58 | whiteList[0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4] = true; 59 | whiteList[0x3ba4d81db016dc2890c81f3acec2454bff5aada5] = true; 60 | whiteList[0xe4ae1efdfc53b73893af49113d8694a057b9c0d1] = true; 61 | whiteList[0x0737a6b837f97f46ebade41b9bc3e1c509c85c53] = true; 62 | 63 | whiteList[0x52c5317c848ba20c7504cb2c8052abd1fde29d03] = true; 64 | whiteList[0x5d2b2e6fcbe3b11d26b525e085ff818dae332479] = true; 65 | whiteList[0x057b56736d32b86616a10f619859c6cd6f59092a] = true; 66 | // whiteList[0x304a554a310c7e546dfe434669c62820b7d83490] = true; 67 | whiteList[0x4deb0033bb26bc534b197e61d19e0733e5679784] = true; 68 | whiteList[0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b] = true; 69 | whiteList[0x9da397b9e80755301a3b32173283a91c0ef6c87e] = true; 70 | whiteList[0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9] = true; 71 | whiteList[0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76] = true; 72 | whiteList[0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7] = true; 73 | 74 | whiteList[0xd164b088bd9108b60d0ca3751da4bceb207b0782] = true; 75 | whiteList[0x1cba23d343a983e9b5cfd19496b9a9701ada385f] = true; 76 | whiteList[0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339] = true; 77 | whiteList[0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d] = true; 78 | whiteList[0xbc07118b9ac290e4622f5e77a0853539789effbe] = true; 79 | whiteList[0xacd87e28b0c9d1254e868b81cba4cc20d9a32225] = true; 80 | whiteList[0x5524c55fb03cf21f549444ccbecb664d0acad706] = true; 81 | // whiteList[0xfe24cdd8648121a43a7c86d289be4dd2951ed49f] = true; 82 | whiteList[0x253488078a4edf4d6f42f113d1e62836a942cf1a] = true; 83 | // whiteList[0xb136707642a4ea12fb4bae820f03d2562ebff487] = true; 84 | 85 | whiteList[0xf14c14075d6c4ed84b86798af0956deef67365b5] = true; 86 | // whiteList[0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c] = true; 87 | whiteList[0x6d87578288b6cb5549d5076a207456a1f6a63dc0] = true; 88 | whiteList[0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6] = true; 89 | // whiteList[0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a] = true; 90 | // whiteList[0x84ef4b2357079cd7a7c69fd7a37cd0609a679106] = true; 91 | // whiteList[0xf4c64518ea10f995918a454158c6b61407ea345c] = true; 92 | } 93 | 94 | function withdrawFromChildDAO(DAO _child) { 95 | // to be replaced by a time which allows the direct withdraw to be finished before the childDAO withdraw starts 96 | if (now < fixChildDAOsListTime + 4 weeks) throw; 97 | if (!whiteList[_child] 98 | || _child.lastTimeMinQuorumMet() > fixChildDAOsListTime 99 | || _child.privateCreation() != address(mother)) 100 | throw; 101 | 102 | withdraw(_child); 103 | } 104 | 105 | function withdraw(){ 106 | withdraw(mother); 107 | } 108 | 109 | function withdraw(DAO _dao) internal { 110 | uint balance = _dao.balanceOf(msg.sender); 111 | 112 | // The msg.sender must call approve(this, balance) beforehand so that 113 | // transferFrom() will work and not throw. We need transferFrom() 114 | // instead of transfer() due to the msg.sender in the latter ending 115 | // up to be the contract 116 | if (!_dao.transferFrom(msg.sender, this, balance) 117 | || !msg.sender.send(balance * totalWeiSupply / totalSupply)) { 118 | 119 | throw; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /withdrawBlack.sol: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the DAO. 3 | 4 | The DAO is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU lesser General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | The DAO is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU lesser General Public License 15 | along with the DAO. If not, see . 16 | */ 17 | 18 | // TODO: all constants need to be double checked 19 | import "github.com/slockit/DAO/DAO.sol"; 20 | 21 | contract Withdraw { 22 | DAO constant public mother = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413); 23 | mapping (address => bool) public blackList; 24 | uint constant public totalSupply = 11712722930974665882186911; 25 | uint constant public totalWeiSupply = 12072858342395652843028271; 26 | uint constant public fixChildDAOsListTime = 1468057560; // 09.07.2016 - 11:46:00 CEST 27 | 28 | function Withdraw(){ 29 | // These are the child DAOs where the recursive call exploit was used, 30 | // their token balances are invalid. 31 | blackList[0xb136707642a4ea12fb4bae820f03d2562ebff487] = true; 32 | blackList[0x304a554a310c7e546dfe434669c62820b7d83490] = true; 33 | blackList[0x84ef4b2357079cd7a7c69fd7a37cd0609a679106] = true; 34 | blackList[0xf4c64518ea10f995918a454158c6b61407ea345c] = true; 35 | blackList[0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a] = true; 36 | blackList[0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c] = true; 37 | blackList[0xfe24cdd8648121a43a7c86d289be4dd2951ed49f] = true; 38 | } 39 | 40 | /// This function can be used to redeem child dao tokens. 41 | /// It can only be called 4 weeks after the blacklist was fixed. 42 | /// The reason is that if this more complicated mechanism has a flaw, 43 | /// people will hopefully already have withdrawn most of the ether 44 | /// through the simpler mechanism below. 45 | function withdrawFromChildDAO(uint _childProposalID) { 46 | if (now < fixChildDAOsListTime + 4 weeks) throw; 47 | DAO child = DAO(mother.getNewDAOAddress(_childProposalID)); 48 | // If the child is blacklisted or too new, this does not work. 49 | if (address(child) == 0 || blackList[child] || child.lastTimeMinQuorumMet() > fixChildDAOsListTime) 50 | throw; 51 | 52 | withdraw(child); 53 | } 54 | 55 | /// Withdraw your share of the Ether. 56 | /// Prior to calling this function, you have to approve allow the withdraw 57 | /// contract to transfer your DAO tokens to it. 58 | function withdraw() { 59 | withdraw(mother); 60 | } 61 | 62 | function withdraw(DAO dao) internal { 63 | uint balance = dao.balanceOf(msg.sender); 64 | 65 | // The msg.sender must call approve(this, balance) beforehand so that 66 | // transferFrom() will work and not throw. We need transferFrom() 67 | // instead of transfer() due to the msg.sender in the latter ending 68 | // up to be the contract 69 | if (!dao.transferFrom(msg.sender, this, balance) 70 | || !msg.sender.send(balance * totalWeiSupply / totalSupply)) { 71 | 72 | throw; 73 | } 74 | } 75 | } 76 | --------------------------------------------------------------------------------