├── .gitignore ├── LICENSE ├── README.rst ├── config ├── abi │ ├── devise_rental_proxy.json │ ├── devise_token.json │ └── devise_token_sale.json ├── contract_address.json └── network_to_node.json ├── javascript ├── devise │ ├── base.js │ ├── clients │ │ ├── client.js │ │ ├── contract.js │ │ └── token.js │ ├── index.js │ ├── miners │ │ └── master_node.js │ └── owner │ │ ├── owner.js │ │ └── token_owner.js ├── dist │ ├── development │ │ ├── 1.0.0 │ │ │ └── devise.min.js │ │ ├── 1.6.0 │ │ │ └── devise.min.js │ │ ├── 1.6.1 │ │ │ └── devise.min.js │ │ ├── 1.7.0 │ │ │ └── devise.min.js │ │ └── devise.min.js │ └── production │ │ ├── 1.0.0 │ │ └── devise.min.js │ │ ├── 1.6.0 │ │ └── devise.min.js │ │ ├── 1.6.1 │ │ └── devise.min.js │ │ ├── 1.7.0 │ │ └── devise.min.js │ │ └── devise.min.js ├── package-lock.json ├── package.json ├── tests │ ├── base_client.js │ ├── devise_client_test.js │ ├── helpers │ │ └── assertRevert.js │ ├── index.html │ ├── master_node_test.js │ ├── rental_owner_test.js │ ├── rental_test.js │ ├── token_owner_test.js │ └── token_test.js └── webpack.config.js ├── makefile ├── python ├── Dockerfile ├── MANIFEST.in ├── deps │ ├── cytoolz-0.9.0.1-cp35-cp35m-win32.whl │ ├── cytoolz-0.9.0.1-cp35-cp35m-win_amd64.whl │ ├── cytoolz-0.9.0.1-cp36-cp36m-win32.whl │ ├── cytoolz-0.9.0.1-cp36-cp36m-win_amd64.whl │ ├── cytoolz-0.9.0.1-cp37-cp37m-win32.whl │ ├── cytoolz-0.9.0.1-cp37-cp37m-win_amd64.whl │ ├── hidapi-0.7.99.post21-cp37-cp37m-win32.whl │ ├── hidapi-0.7.99.post21-cp37-cp37m-win_amd64.whl │ ├── ledgerblue-0.1.18-py3-none-any.whl │ ├── lru_dict-1.1.6-cp35-cp35m-win32.whl │ ├── lru_dict-1.1.6-cp35-cp35m-win_amd64.whl │ ├── lru_dict-1.1.6-cp36-cp36m-win32.whl │ ├── lru_dict-1.1.6-cp36-cp36m-win_amd64.whl │ ├── lru_dict-1.1.6-cp37-cp37m-win32.whl │ ├── lru_dict-1.1.6-cp37-cp37m-win_amd64.whl │ ├── pysha3-1.0.2-cp37-cp37m-win32.whl │ └── pysha3-1.0.2-cp37-cp37m-win_amd64.whl ├── devise │ ├── __init__.py │ ├── abi │ │ ├── AuditImpl.json │ │ ├── DeviseRentalImpl.json │ │ ├── DeviseRentalProxy.json │ │ ├── DeviseToken.json │ │ └── DeviseTokenSale.json │ ├── base.py │ ├── clients │ │ ├── __init__.py │ │ ├── api.py │ │ ├── client.py │ │ ├── contract.py │ │ └── token.py │ ├── config.py │ ├── ledger.py │ ├── miners │ │ ├── __init__.py │ │ └── master_node.py │ └── owner │ │ ├── __init__.py │ │ ├── owner.py │ │ └── token_owner.py ├── setup.py └── tests │ ├── __init__.py │ ├── conftest.py │ ├── hash_test.json │ ├── key_file.json │ ├── test_devise_client.py │ ├── test_devise_owner.py │ ├── test_ledger_wallet.py │ ├── test_master_node.py │ ├── test_token.py │ ├── test_token_owner.py │ └── utils.py ├── solidity ├── .solcover.js ├── .soliumignore ├── .soliumrc.json ├── contracts │ ├── AccessControlImpl.sol │ ├── AccessControlImplStorage.sol │ ├── AccessControlProxy.sol │ ├── AccessControlStorage.sol │ ├── AccountingImpl.sol │ ├── AccountingImplStorage.sol │ ├── AccountingProxy.sol │ ├── AccountingStorage.sol │ ├── AuctionImpl.sol │ ├── AuctionProxy.sol │ ├── AuctionStorage.sol │ ├── AuditImpl.sol │ ├── AuditProxy.sol │ ├── AuditStorage.sol │ ├── AuthorizedOnly.sol │ ├── CappedToken.sol │ ├── DateTime.sol │ ├── DeviseMiningImpl.sol │ ├── DeviseMiningProxy.sol │ ├── DeviseMiningStorage.sol │ ├── DeviseRentalImpl.sol │ ├── DeviseRentalProxy.sol │ ├── DeviseRentalStorage.sol │ ├── DeviseToken.sol │ ├── GroveLib.sol │ ├── LeptonStorage.sol │ ├── Migrations.sol │ ├── OwnedUpgradeabilityProxy.sol │ ├── Proxy.sol │ ├── RBACMintableToken.sol │ ├── UpgradeabilityProxy.sol │ └── test │ │ ├── AccessControlImplTimeTravel.sol │ │ ├── DeviseRentalImplTest.sol │ │ ├── DeviseRentalImplTimeTravel.sol │ │ ├── DeviseRentalImplV2.sol │ │ ├── DeviseRentalImplV3.sol │ │ ├── MintableTokenTest.sol │ │ ├── MintableTokenTestBoth.sol │ │ └── TimeTravel.sol ├── migrations │ ├── 1_initial_migration.js │ ├── 2_deploy_contracts.js │ ├── 3_deploy_contracts.js │ ├── 4_deploy_contracts.js │ └── 5_deploy_contracts.js ├── package-lock.json ├── package.json ├── test │ ├── DeviseRental.js │ ├── DeviseRentalUpgradability.js │ ├── LeaseRelatedTest.js │ ├── MasterNodeTest.js │ ├── MintableTokenTest.js │ ├── RentalAccountingTest.js │ ├── RentalEventTest.js │ ├── RentalPauseTests.js │ ├── RentalTestStatic.js │ ├── StateVariableTest.js │ ├── TimeTravelTest.js │ ├── TokenFeatureTest.js │ ├── UpdateLeaseTerms.js │ ├── UpdateLeaseTermsStatic.js │ ├── WalletRelatedTest.js │ ├── helpers │ │ ├── assertRevert.js │ │ └── setupFixtures.js │ ├── leptons.js │ └── test-utils.js ├── truffle-jenkins.js └── truffle.js └── yellow_paper.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # node stuff 107 | node_modules 108 | 109 | # .idea folder 110 | .idea/ 111 | 112 | .DS_Store 113 | .node-xmlhttprequest-*-* 114 | solidity/coverage 115 | solidity/coverage.json 116 | !javascript/dist 117 | -------------------------------------------------------------------------------- /config/abi/devise_token_sale.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/config/abi/devise_token_sale.json -------------------------------------------------------------------------------- /config/contract_address.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "DEVISE_RENTAL": "0x26E47337f0d2CfC39d671158Bd6B04521D6aAD05", 4 | "DEVISE_TOKEN": "0xc2E71fdbFdd9e967D49B99CD288164e40e15992E", 5 | "DEVISE_TOKEN_SALE": "0xdB1548455e29c08377F0999Db2aF55F65758c514" 6 | }, 7 | "4": { 8 | "DEVISE_RENTAL": "0xA3A5387cD8177BA3f5F47696988b1B51A3331CBF", 9 | "DEVISE_TOKEN": "0xF60Ef7D51a4Beb501bFcB380E1abbF49C042Ec53", 10 | "DEVISE_TOKEN_SALE": "0x7e50014E03535a14F844DF56dB4847254754Bb7B" 11 | }, 12 | "7778454": { 13 | "DEVISE_RENTAL": "0xca5c8dC7C604590214c835463B41bC2cbC6deEd5", 14 | "DEVISE_TOKEN": "0xD2AB5fA56D6d571De4d4B6531aD6F9147ddf058D", 15 | "DEVISE_TOKEN_SALE": "0x0987eE274279c6707535FaEE0e2135857f3c3291" 16 | }, 17 | "777666": { 18 | "DEVISE_RENTAL": "0x30Ca3a0917ABC23C3b38A9993d84a14e12cd71Cd", 19 | "DEVISE_TOKEN_SALE": "0xA76068c461716d34499cA221A037Cedb39067e26", 20 | "DEVISE_TOKEN": "0xC1844bbe0537cE51F95F9EC08c55D697fCcf3f17" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /config/network_to_node.json: -------------------------------------------------------------------------------- 1 | { 2 | "MAINNET": "https://mainnet.infura.io/ZQl920lU4Wyl6vyrND55", 3 | "RINKEBY": "https://rinkeby.infura.io/ZQl920lU4Wyl6vyrND55", 4 | "DEV1": "https://dev1.devisechain.io", 5 | "DEV2": "https://dev2.devisechain.io", 6 | "GANACHE": "http://localhost:8545" 7 | } -------------------------------------------------------------------------------- /javascript/devise/base.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Base classes BaseEthereumClient, BaseDeviseClient and related functions 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GPLv3 5 | */ 6 | 7 | // Node.js doesn't have XHR, shim it 8 | if (typeof XMLHttpRequest === 'undefined') { 9 | global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; 10 | } 11 | 12 | let Web3 = require('web3'); 13 | const ETHER_PRECISION = 10 ** 18; 14 | 15 | // The following code is to accommodate using jQuery in Node.js 16 | const GITHUB_USERNAME = 'devisechain'; 17 | const REPO_VERSION = '6c5e4852045522a416bea23cc7a816b1ae79b668'; 18 | const CDN_ROOT = 'https://cdn.jsdelivr.net/gh/' + GITHUB_USERNAME + '/Devise@' + REPO_VERSION + '/config/'; 19 | const CONFIG_URL = 'https://config.devisefoundation.org/config.json'; 20 | 21 | const get_json_sync = function (url) { 22 | let res; 23 | const request = new XMLHttpRequest(); 24 | request.open('GET', url, false); // `false` makes the request synchronous 25 | request.send(null); 26 | 27 | if (request.status === 200) { 28 | return JSON.parse(request.responseText); 29 | } 30 | }; 31 | 32 | const get_contract_abi = function (contractName) { 33 | const url = CDN_ROOT + 'abi/' + contractName + '.json'; 34 | const data = get_json_sync(url); 35 | return data; 36 | }; 37 | 38 | const get_contract_address = function () { 39 | const config = get_json_sync(CONFIG_URL); 40 | return config["CONTRACT_ADDRESSES"]; 41 | }; 42 | 43 | const get_default_node_url = function (network = 'MAINNET') { 44 | const config = get_json_sync(CONFIG_URL); 45 | return config["NETWORK_TO_NODE"][network.toUpperCase()]; 46 | }; 47 | 48 | class BaseEthereumClient { 49 | /** 50 | * Constructor 51 | * @param account default: none, optional address to query the smart contract as 52 | * @param node_url default: auto, optional ethereum node from which to query smart contract information 53 | * @param network default: MainNet, optional network to connect to 54 | */ 55 | constructor(account, node_url, network) { 56 | if (!network) { 57 | network = 'MAINNET' 58 | } 59 | node_url = node_url || get_default_node_url(network); 60 | account = account || '0x0000000000000000000000000000000000000000'; 61 | const provider = new Web3.providers.HttpProvider(node_url); 62 | this.web3 = new Web3(provider); 63 | if (this.web3.eth.net === undefined) 64 | throw "Please use a version of web3.js >= 1.0.0."; 65 | 66 | this.account = account; 67 | this.address = this.account; 68 | } 69 | 70 | async _get_network_id() { 71 | const id = await this.web3.eth.net.getId(); 72 | return id; 73 | } 74 | 75 | async get_eth_balance(address) { 76 | if (address === undefined) 77 | return 0; 78 | const bal = await this.web3.eth.getBalance(address) / ETHER_PRECISION; 79 | return bal; 80 | } 81 | 82 | } 83 | 84 | class BaseDeviseClient extends BaseEthereumClient { 85 | constructor(account, node_url, network) { 86 | super(account, node_url, network); 87 | } 88 | 89 | async init_contracts() { 90 | const token_abi = get_contract_abi('devise_token'); 91 | let rental_abi = get_contract_abi('devise_rental_proxy'); 92 | rental_abi.push({ 93 | "constant": true, 94 | "inputs": [], 95 | "name": "accounting", 96 | "outputs": [ 97 | { 98 | "name": "", 99 | "type": "address" 100 | } 101 | ], 102 | "payable": false, 103 | "stateMutability": "view", 104 | "type": "function" 105 | }); 106 | const contract_address = get_contract_address(); 107 | const network_id = await this._get_network_id(); 108 | this._token_contract = new this.web3.eth.Contract(token_abi, contract_address[network_id].DEVISE_TOKEN); 109 | this._rental_contract = new this.web3.eth.Contract(rental_abi, contract_address[network_id].DEVISE_RENTAL); 110 | } 111 | } 112 | 113 | module.exports = {BaseEthereumClient, BaseDeviseClient, get_json_sync}; 114 | -------------------------------------------------------------------------------- /javascript/devise/clients/client.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * DeviseClient class 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GPLv3 5 | */ 6 | const aggregation = require("aggregation/es6"); 7 | const DeviseToken = require('./token'); 8 | const RentalContract = require('./contract'); 9 | 10 | /* 11 | * DeviseClient 12 | * This is the basic wrapper class around all Devise client facing operations. This wrapper connects to an Ethereum 13 | * node and facilitates smart contract operations such as provision, leaseAll, getBalance, etc. 14 | */ 15 | class DeviseClient extends aggregation(DeviseToken, RentalContract) { 16 | /** 17 | * Constructor 18 | * @param account default: none, optional address to query the smart contract as 19 | * @param nodel_url default: auto, optional ethereum node from which to query smart contract information 20 | * @param network default: MainNet, optional network to connect to 21 | */ 22 | constructor(account, node_url, network) { 23 | super(account, node_url, network); 24 | } 25 | } 26 | 27 | module.exports = DeviseClient; 28 | -------------------------------------------------------------------------------- /javascript/devise/clients/token.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * DeviseToken class 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GPLv3 5 | */ 6 | const BaseClient = require('../base'); 7 | const BaseDeviseClient = BaseClient.BaseDeviseClient; 8 | const TOKEN_PRECISION = 10 ** 6; 9 | 10 | /** 11 | * DeviseToken 12 | * This is the base class for all token contract operations 13 | */ 14 | class DeviseToken extends BaseDeviseClient { 15 | constructor(account, node_url, network) { 16 | super(account, node_url, network); 17 | } 18 | 19 | /** 20 | * The global cap on how many DVZ tokens can be minted 21 | * @returns {Promise} 22 | */ 23 | async cap() { 24 | return await this._token_contract.methods.cap().call() / TOKEN_PRECISION; 25 | } 26 | 27 | /** 28 | * The current number of DVZ Tokens in circulation 29 | * @returns {Promise} 30 | */ 31 | async total_supply() { 32 | return await this._token_contract.methods.totalSupply().call() / TOKEN_PRECISION; 33 | } 34 | 35 | /** 36 | * 37 | * @param owner 38 | * @param spender 39 | * @returns {Promise} 40 | */ 41 | async allowance(owner, spender) { 42 | return await this._token_contract.methods.allowance(owner, spender).call() / TOKEN_PRECISION; 43 | } 44 | 45 | /** 46 | * Query the DVZ token balance the specified address 47 | * @param address 48 | * @returns {Promise} 49 | */ 50 | async balance_of(address) { 51 | return await this._token_contract.methods.balanceOf(address).call() / TOKEN_PRECISION; 52 | } 53 | 54 | /** 55 | * Utility function to query the DVZ token balance of the token sale wallet 56 | * @returns {Promise} 57 | */ 58 | async balance_of_token_wallet() { 59 | const owner = await this._rental_contract.methods.tokenWallet().call(); 60 | return await this.balance_of(owner); 61 | } 62 | 63 | // TODO replace with allowance of rental contract from tokenSaleWallet 64 | // /** 65 | // * Utility function to query the allowance of the token sale contract 66 | // * @returns {Promise} 67 | // */ 68 | // async allowance_of_token_sale_contract() { 69 | // const owner = await this._rental_contract.methods.tokenWallet().call(); 70 | // const spender = await this._token_sale_contract._address; 71 | // return await this.allowance(owner, spender); 72 | // } 73 | 74 | /** 75 | * Utility function to query the Ether balance of the contract owner 76 | * @returns {Promise} 77 | */ 78 | async get_owner_eth_balance() { 79 | const owner = await this._token_contract.methods.owner().call(); 80 | return await this.get_eth_balance(owner); 81 | } 82 | } 83 | 84 | module.exports = DeviseToken; 85 | -------------------------------------------------------------------------------- /javascript/devise/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * devise 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 5 | * 6 | * ES5 Example Usage: 7 | * var client = new devise.DeviseClient(account); 8 | * client.init_contracts().then(() => { 9 | * client.client_summary().then(summary => console.log(summary)); 10 | * }); 11 | 12 | * Async/Await Example Usage: 13 | * const client = new devise.DeviseClient(account); 14 | * await client.init_contracts(); 15 | * const client_summary = await client.client_summary(); 16 | * console.log(client_summary); 17 | */ 18 | 19 | import DeviseClient from './clients/client'; 20 | import DeviseTokenOwner from './owner/token_owner'; 21 | import MasterNode from './miners/master_node'; 22 | import DeviseOwner from './owner/owner'; 23 | 24 | /** 25 | * Polyfill for environments without XHR 26 | */ 27 | if (typeof XMLHttpRequest === 'undefined') { 28 | global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; 29 | } 30 | 31 | export {DeviseClient, DeviseTokenOwner, MasterNode, DeviseOwner}; 32 | -------------------------------------------------------------------------------- /javascript/devise/miners/master_node.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * MasterNode class 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GPLv3 5 | */ 6 | const BaseClient = require('../base'); 7 | const BaseDeviseClient = BaseClient.BaseDeviseClient; 8 | const IU_PRECISION = 10 ** 6; 9 | 10 | /** 11 | * MasterNode 12 | * Smart Contract wrapper class for Master Node operations 13 | */ 14 | class MasterNode extends BaseDeviseClient { 15 | constructor(account, node_url, network) { 16 | super(account, node_url, network); 17 | } 18 | 19 | get_master_nodes() { 20 | return this._rental_contract.methods.getMasterNodes().call(); 21 | } 22 | } 23 | 24 | module.exports = MasterNode; 25 | -------------------------------------------------------------------------------- /javascript/devise/owner/owner.js: -------------------------------------------------------------------------------- 1 | const BaseClient = require('../base'); 2 | const BaseDeviseClient = BaseClient.BaseDeviseClient; 3 | const IU_PRECISION = 10 ** 6; 4 | const TOKEN_PRECISION = 10 ** 6; 5 | const ETHER_PRECISION = 10 ** 18; 6 | 7 | class DeviseOwner extends BaseDeviseClient { 8 | constructor(account, node_url, network) { 9 | super(account, node_url, network); 10 | } 11 | 12 | async get_implementation() { 13 | return await this._rental_contract.methods.implementation().call(); 14 | } 15 | 16 | async get_version() { 17 | return await this._rental_contract.methods.version().call(); 18 | } 19 | 20 | async get_rate_setter() { 21 | return await this._rental_contract.methods.rateSetter().call(); 22 | } 23 | 24 | async get_escrow_history() { 25 | const res = await this._rental_contract.methods.getEscrowHistory().call(); 26 | return res; 27 | } 28 | 29 | async get_escrow_version() { 30 | const hist = await this.get_escrow_history(); 31 | return hist.length; 32 | } 33 | 34 | async get_revenue_history() { 35 | const res = await this._rental_contract.methods.getRevenueHistory().call(); 36 | return res; 37 | } 38 | 39 | async get_revenue_version() { 40 | const hist = await this.get_revenue_history(); 41 | return hist.length; 42 | } 43 | } 44 | 45 | module.exports = DeviseOwner; 46 | -------------------------------------------------------------------------------- /javascript/devise/owner/token_owner.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * DeviseTokenOwner class 3 | * Copyright(c) 2018 Pit.AI Technologies 4 | * LICENSE: GPLv3 5 | */ 6 | const DeviseToken = require('../clients/token'); 7 | 8 | /** 9 | * DeviseTokenOwner 10 | * Smart Contract wrapper class for Token Owner operations 11 | */ 12 | class DeviseTokenOwner extends DeviseToken { 13 | constructor(account, nodel_url, network) { 14 | super(account, nodel_url, network); 15 | } 16 | 17 | async get_minters() { 18 | let minters = []; 19 | const owner = await this._token_contract.methods.owner().call(); 20 | const n = await this._token_contract.methods.getNumberOfMinters().call({from: owner}); 21 | for (let i = 0; i < n; i++) { 22 | const minter = await this._token_contract.methods.getMinter(i).call({from: owner}); 23 | minters.push(minter); 24 | } 25 | return minters; 26 | } 27 | } 28 | 29 | module.exports = DeviseTokenOwner; 30 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devise", 3 | "version": "1.7.0", 4 | "description": "A javascript library for interacting with Devise smart contracts on Ethereum", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "dependencies": { 10 | "aggregation": "^1.2.4", 11 | "web3": "^1.0.0-beta.35" 12 | }, 13 | "devDependencies": { 14 | "babel": "^6.23.0", 15 | "babel-cli": "^6.26.0", 16 | "babel-loader": "^7.1.5", 17 | "babel-plugin-transform-class-properties": "^6.24.1", 18 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 19 | "chai": "^4.1.2", 20 | "mocha": "^5.2.0", 21 | "mocha-junit-reporter": "^1.17.0", 22 | "webpack": "^4.16.3", 23 | "webpack-cli": "^3.1.0", 24 | "xmlhttprequest": "^1.8.0" 25 | }, 26 | "scripts": { 27 | "clean": "rm -r dist/*", 28 | "build_dev": "NODE_ENV=development webpack --mode development --display verbose --display-modules --config webpack.config.js", 29 | "build": "NODE_ENV=production webpack --mode production --config webpack.config.js", 30 | "test": "mocha tests" 31 | }, 32 | "keywords": [ 33 | "Devise" 34 | ], 35 | "author": "Pit.AI Technologies, Inc.", 36 | "license": "ISC" 37 | } -------------------------------------------------------------------------------- /javascript/tests/base_client.js: -------------------------------------------------------------------------------- 1 | let chai = require('chai'); 2 | let assert = chai.assert; 3 | let BaseClient = require('../devise/base'); 4 | const network = 'ganache'; 5 | 6 | describe('BaseClientTests', function () { 7 | this.timeout(10000); 8 | it('CurrentProvider should be set', () => { 9 | let BaseEthereumClient = BaseClient.BaseEthereumClient; 10 | let client = new BaseEthereumClient(undefined, undefined, network); 11 | const provider = client.web3.currentProvider.host; 12 | assert.equal(provider, 'http://localhost:8545'); 13 | }); 14 | it('Should be able to set node_url', () => { 15 | let BaseEthereumClient = BaseClient.BaseEthereumClient; 16 | const node_url = 'https://mainnet.infura.io/ZQl920lU4Wyl6vyrND55'; 17 | let client = new BaseEthereumClient('0x0000000000000000000000000000000000000000', node_url); 18 | const provider = client.web3.currentProvider.host; 19 | assert.equal(provider, node_url) 20 | }); 21 | it('TokenContract should be defined', async () => { 22 | let BaseDeviseClient = BaseClient.BaseDeviseClient; 23 | let client = new BaseDeviseClient(undefined, undefined, network); 24 | await client.init_contracts(); 25 | let contract = client._token_contract; 26 | assert.isDefined(contract); 27 | }); 28 | it('get_eth_balance should return non-zero balance for specific address', async () => { 29 | const BaseEthereumClient = BaseClient.BaseEthereumClient; 30 | const client = new BaseEthereumClient(undefined, undefined, network); 31 | const address = '0xd4a6b94e45b8c0185e33f210f4f96bdae40aa22e'; 32 | const bal = await client.get_eth_balance(address); 33 | assert.isAbove(bal, 0); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /javascript/tests/devise_client_test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; 3 | const DeviseClient = require('../devise/clients/client'); 4 | const network = 'ganache'; 5 | 6 | describe('DeviseClientTest', function () { 7 | this.timeout(10000); 8 | let client; 9 | beforeEach(async () => { 10 | client = new DeviseClient(undefined, undefined, network); 11 | await client.init_contracts(); 12 | }); 13 | it('Should be able to call cap from DevivseToken', async () => { 14 | const cap = await client.cap(); 15 | assert.equal(cap, 10 * 10 ** 9); 16 | }); 17 | it('Should be able to call current_lease_term from RentalContract', async () => { 18 | const term = await client.current_lease_term(); 19 | console.log("Current lease term ", term); 20 | assert.isDefined(term); 21 | }); 22 | it('Should be able to set node_url', () => { 23 | const node_url = 'https://mainnet.infura.io/ZQl920lU4Wyl6vyrND55'; 24 | client = new DeviseClient('0x0000000000000000000000000000000000000000', node_url); 25 | const provider = client.web3.currentProvider.host; 26 | assert.equal(provider, node_url) 27 | }); 28 | it('get_eth_usd_rate should return zero', async () => { 29 | const rate = await client.get_eth_usd_rate(); 30 | assert.equal(rate, 0); 31 | }); 32 | it('get_usd_dvz_rate shoudl return 10', async () => { 33 | const rate = await client.get_usd_dvz_rate(); 34 | assert.equal(rate, 10); 35 | }); 36 | it('get_eth_dvz_rate should return zero', async () => { 37 | const rate = await client.get_eth_dvz_rate(); 38 | assert.equal(rate, 0); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /javascript/tests/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | console.assert(false, 'Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search(': revert') >= 0; 7 | console.assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; -------------------------------------------------------------------------------- /javascript/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Testing Devise 4 | 5 | 6 | 7 | 8 |

Hello Devise!

9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /javascript/tests/master_node_test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; 3 | const MasterNode = require('../devise/miners/master_node'); 4 | const network = 'ganache'; 5 | 6 | describe('MasterNodeTest', function () { 7 | this.timeout(10000); 8 | let master; 9 | beforeEach(async () => { 10 | master = new MasterNode(undefined, undefined, network); 11 | await master.init_contracts(); 12 | }); 13 | it('GetMasterNodes should return an array of objects', async () => { 14 | const nodes = await master.get_master_nodes(); 15 | assert.deepEqual(nodes, ['0xd4a6B94E45B8c0185e33F210f4F96bDAe40aa22E']); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /javascript/tests/rental_owner_test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; 3 | const RentalOwner = require('../devise/owner/owner'); 4 | const network = 'ganache'; 5 | const TOKEN_PRECISION = 10 ** 6; 6 | const assertRevert = require('./helpers/assertRevert'); 7 | 8 | describe('RentalOwnerTest', function () { 9 | this.timeout(20000); 10 | let rental_owner; 11 | let accounts; 12 | let pitai; 13 | let web3; 14 | const impl_address = '0x5a1e6BC336D5d19E0ADfaa6A1826CF39A55315bA'; 15 | const gas = 3000000; 16 | beforeEach(async () => { 17 | rental_owner = new RentalOwner(undefined, undefined, network); 18 | await rental_owner.init_contracts(); 19 | accounts = await rental_owner.web3.eth.getAccounts(); 20 | pitai = accounts[0]; 21 | web3 = rental_owner.web3; 22 | }); 23 | it("get_implementation should return an address", async function () { 24 | const impl = await rental_owner.get_implementation(); 25 | assert.equal(impl, impl_address); 26 | }); 27 | it("get_version should return a positive number", async function () { 28 | const ver = await rental_owner.get_version(); 29 | assert.equal(ver, 1); 30 | }); 31 | it('get_escrow_history should return an array', async () => { 32 | const escrow_hist = await rental_owner.get_escrow_history(); 33 | assert.deepEqual(escrow_hist, ['0x93c86A7574a1E5eAF773B807fFF3496728f5B1BC']); 34 | }); 35 | it('get_escrow_version should return a positive number', async () => { 36 | const escrow_ver = await rental_owner.get_escrow_version(); 37 | assert.equal(escrow_ver, 1); 38 | }); 39 | it('get_revenue_history should return an array', async () => { 40 | const rev_hist = await rental_owner.get_revenue_history(); 41 | assert.deepEqual(rev_hist, ['0x5c7Fe1B9bad324c5c8B90f66243B45F65B3f5fcd']); 42 | }); 43 | it('get_revenue_version should return a positive number', async () => { 44 | const rev_ver = await rental_owner.get_revenue_version(); 45 | assert.equal(rev_ver, 1); 46 | }); 47 | it('get_rate_setter should be zero', async () => { 48 | const rate_setter = await rental_owner.get_rate_setter(); 49 | assert.equal(rate_setter, "0x0000000000000000000000000000000000000000"); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /javascript/tests/token_owner_test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; 3 | const TokenOwner = require('../devise/owner/token_owner'); 4 | const network = 'ganache'; 5 | 6 | describe('TokenOwnerTest', function () { 7 | this.timeout(10000); 8 | let owner; 9 | beforeEach(async () => { 10 | owner = new TokenOwner(undefined, undefined, network); 11 | await owner.init_contracts(); 12 | }); 13 | it('GetMinters should return an array of objects', async () => { 14 | const minters = await owner.get_minters(); 15 | console.log(minters, minters.length); 16 | assert.equal(true, true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /javascript/tests/token_test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; 3 | const DeviseToken = require('../devise/clients/token'); 4 | const network = 'ganache'; 5 | 6 | describe('DeviseTokenTests', function () { 7 | this.timeout(10000); 8 | let token; 9 | beforeEach(async () => { 10 | token = new DeviseToken(undefined, undefined, network); 11 | await token.init_contracts(); 12 | }); 13 | it('Cap should be non-zero', async () => { 14 | const cap = await token.cap(); 15 | assert.equal(cap, 10 * 10 ** 9); 16 | }); 17 | it('TotalSupply should be non-zero', async () => { 18 | const total_supply = await token.total_supply(); 19 | assert.isAtLeast(total_supply, 250 * 10 ** 6); 20 | }); 21 | it('Allowance should be zero by default', async () => { 22 | const owner = '0x99429f64cf4d5837620dcc293c1a537d58729b68'; 23 | const spender = '0xbA809B53AD58dd87903E07b21e6cd00683d62252'; 24 | const allowance = await token.allowance(owner, spender); 25 | assert.equal(allowance, 0); 26 | }); 27 | it('Allowance should be non-zero for specific accounts', async () => { 28 | const owner = await token._rental_contract.methods.escrowWallet().call(); 29 | const accounting_address = await token._rental_contract.methods.accounting().call(); 30 | const allowance = await token.allowance(owner, accounting_address); 31 | assert.isAtLeast(allowance, 3000); 32 | }); 33 | it('Balance should be zero for random account', async () => { 34 | const addr = '0x99429f64cf4d5837620dcc293c1a537d58729b68'; 35 | const bal = await token.balance_of(addr); 36 | assert.equal(bal, 0); 37 | }); 38 | // TODO enable this once we can query tokenWallet on rental contract 39 | it.skip('Balance should be non-zero for specific account', async () => { 40 | const addr = await token._rental_contract.methods.escrowWallet().call(); 41 | const bal = await token.balance_of(addr); 42 | assert.isAtLeast(bal, 250 * 10 ** 6); 43 | }); 44 | // TODO this should be enabled when the token wallet is added to rental contract 45 | it.skip('Balance should be non-zero for token wallet account', async () => { 46 | const bal = await token.balance_of_token_wallet(); 47 | assert.isAtLeast(bal, 250 * 10 ** 6); 48 | }); 49 | it('get_owner_eth_balance should not throw exception', async () => { 50 | const bal = await token.get_owner_eth_balance(); 51 | assert.isAtLeast(bal, 0); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /javascript/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | process.env.BABEL_ENV = process.env.NODE_ENV; 4 | 5 | module.exports = { 6 | entry: './devise/index.js', 7 | output: { 8 | filename: 'devise.min.js', 9 | library: 'devise', 10 | libraryTarget: 'umd', 11 | path: path.resolve(__dirname, 'dist', process.env.NODE_ENV, require("./package.json").version) 12 | }, 13 | module: { 14 | rules: [{ 15 | test: /\.(js|jsx)$/, 16 | include: path.resolve(__dirname, 'devise'), 17 | loader: 'babel-loader', 18 | query: { 19 | // This is a feature of `babel-loader` for webpack (not Babel itself). 20 | // It enables caching results in ./node_modules/.cache/babel-loader/ 21 | // directory for faster rebuilds. 22 | cacheDirectory: true 23 | } 24 | }] 25 | }, 26 | externals: [ 27 | { 28 | "web3": { 29 | root: "Web3", 30 | commonjs2: "web3", 31 | commonjs: ["web3"], 32 | amd: "web3" 33 | } 34 | }, 35 | "xmlhttprequest" 36 | ] 37 | }; 38 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | VERSION = $(shell echo "import devise;print(devise.__version__)" | PYTHONPATH=./python python) 2 | 3 | all: python_test solidity_test javascript_test 4 | 5 | python_test: solidity_compile solidity_migrate setup_python 6 | @echo Running python tests 7 | cd python && PYTHONPATH=. pytest 8 | 9 | solidity_coverage: 10 | @echo Running solidity coverage 11 | cd solidity && \ 12 | ./node_modules/.bin/solidity-coverage 13 | cd solidity && \ 14 | ./node_modules/.bin/istanbul report cobertura 15 | 16 | solidity_test: unlock_test_owner unlock_test_accounts 17 | @echo Running solidity tests 18 | cd solidity && truffle test 19 | 20 | 21 | javascript_test_jenkins: setup_javacript 22 | @echo Running javascript tests 23 | cd javascript && MOCHA_FILE=./javascript-test-results.xml ./node_modules/.bin/mocha tests --reporter mocha-junit-reporter 24 | 25 | javascript_test: setup_javacript 26 | @echo Running javascript tests 27 | cd javascript && npm test 28 | 29 | solidity_compile: unlock_test_owner 30 | @echo Compiling Smart Contracts 31 | cd solidity && \ 32 | rm -rf build && \ 33 | truffle compile && \ 34 | cat build/contracts/DeviseRentalImpl.json | jq -r '.abi' > ../config/abi/devise_rental_proxy.json && \ 35 | cat build/contracts/DeviseToken.json | jq -r '.abi' > ../config/abi/devise_token.json && \ 36 | cat build/contracts/DeviseRentalProxy.json | jq -r '.abi' > ../python/Devise/abi/DeviseRentalProxy.json && \ 37 | cat build/contracts/DeviseRentalImpl.json | jq -r '.abi' > ../python/Devise/abi/DeviseRentalImpl.json && \ 38 | cat build/contracts/DeviseToken.json | jq -r '.abi' > ../python/Devise/abi/DeviseToken.json && \ 39 | cat build/contracts/AuditImpl.json | jq -r '.abi' > ../python/Devise/abi/AuditImpl.json 40 | 41 | 42 | solidity_migrate: unlock_test_owner unlock_test_accounts 43 | @echo Deploying Smart Contracts 44 | cd solidity && \ 45 | truffle migrate --reset 46 | 47 | unlock_test_owner: 48 | @echo Unlocking accounts[0] 49 | cd solidity && echo "web3.personal.unlockAccount(web3.eth.accounts[0], '')" | truffle console 50 | 51 | unlock_test_accounts: 52 | @echo Unlocking accounts[0:20] 53 | cd solidity && echo "web3.eth.accounts.forEach((acct, idx) => web3.personal.unlockAccount(acct, ''))" | truffle console 54 | 55 | setup_solidity: 56 | # Install solidity deps 57 | cd solidity && npm install --python=/usr/bin/python2.7 58 | # Install json parser if it's not already installed 59 | command -v jq || brew install jq 60 | 61 | setup_python: 62 | # Install python deps 63 | cd python && \ 64 | pip3 install setuptools -U && \ 65 | pip3 install .[dev] 66 | 67 | setup_javacript: 68 | # Install javascript deps 69 | cd javascript && npm install --python=/usr/bin/python2.7 && \ 70 | NEXT_VERSION=$(VERSION) && \ 71 | node -e "\ 72 | var j = require('./package.json');\ 73 | j.version = \"$$NEXT_VERSION\";\ 74 | var s = JSON.stringify(j, null, 2);\ 75 | require('fs').writeFileSync('./package.json', s);" && \ 76 | npm run build_dev && \ 77 | npm run build 78 | 79 | setup: setup_solidity setup_python 80 | 81 | deploy_pypi: 82 | cp README.rst python/ 83 | cd python && python3 setup.py clean sdist upload 84 | rm python/README.rst 85 | 86 | deploy_pypi_test: 87 | cp README.rst python/ 88 | cd python && python3 setup.py clean sdist upload -r testpypi 89 | rm python/README.rst 90 | 91 | docker_build: 92 | cd python && docker build --no-cache -t devise:latest . 93 | 94 | docker_tag: 95 | cd python && docker tag devise:latest devisechain/python:$(VERSION) 96 | cd python && docker tag devise:latest devisechain/python:latest 97 | 98 | docker_push: 99 | docker push devisechain/python:latest 100 | docker push devisechain/python:$(VERSION) 101 | -------------------------------------------------------------------------------- /python/Dockerfile: -------------------------------------------------------------------------------- 1 | # Python environment containing Pit.AI's devise python library 2 | FROM ubuntu:18.04 3 | 4 | # Setup python3 and required dependencies 5 | ENV DEBIAN_FRONTEND noninteractive 6 | RUN apt-get update && apt-get install -y ca-certificates python3 python3-pip python3-pkgconfig libffi-dev dh-autoreconf libsecp256k1-dev libusb-1.0-0-dev libudev-dev 7 | RUN pip3 install pip==10.0.1 8 | 9 | # Install devise python library 10 | RUN pip3 install devise jupyter pandas 11 | 12 | # Add Tini. Tini operates as a process subreaper for jupyter. This prevents kernel crashes. 13 | ENV TINI_VERSION v0.6.0 14 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini 15 | RUN chmod +x /usr/bin/tini 16 | ENTRYPOINT ["/usr/bin/tini", "--"] 17 | 18 | # Expose Jupyter port 19 | EXPOSE 3477 20 | 21 | # Create a regular user to run jupyter 22 | RUN useradd -ms /bin/bash ubuntu 23 | USER ubuntu 24 | WORKDIR /home/ubuntu 25 | 26 | # Use bash as the shell inside Jupyter terminals 27 | ENV SHELL=/bin/bash 28 | 29 | # Finally, run Jupyter notebook 30 | CMD mkdir -p ~/.devise/notebooks && jupyter notebook --notebook-dir=~/.devise/notebooks --ip=* --port=3477 --NotebookApp.token='' --NotebookApp.password='' 31 | -------------------------------------------------------------------------------- /python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include devise/abi/*.json 2 | include deps/*.tgz 3 | include deps/*.whl 4 | -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp35-cp35m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp35-cp35m-win32.whl -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp35-cp35m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp35-cp35m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp36-cp36m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp36-cp36m-win32.whl -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp36-cp36m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp36-cp36m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp37-cp37m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp37-cp37m-win32.whl -------------------------------------------------------------------------------- /python/deps/cytoolz-0.9.0.1-cp37-cp37m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/cytoolz-0.9.0.1-cp37-cp37m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/hidapi-0.7.99.post21-cp37-cp37m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/hidapi-0.7.99.post21-cp37-cp37m-win32.whl -------------------------------------------------------------------------------- /python/deps/hidapi-0.7.99.post21-cp37-cp37m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/hidapi-0.7.99.post21-cp37-cp37m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/ledgerblue-0.1.18-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/ledgerblue-0.1.18-py3-none-any.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp35-cp35m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp35-cp35m-win32.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp35-cp35m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp35-cp35m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp36-cp36m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp36-cp36m-win32.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp36-cp36m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp36-cp36m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp37-cp37m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp37-cp37m-win32.whl -------------------------------------------------------------------------------- /python/deps/lru_dict-1.1.6-cp37-cp37m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/lru_dict-1.1.6-cp37-cp37m-win_amd64.whl -------------------------------------------------------------------------------- /python/deps/pysha3-1.0.2-cp37-cp37m-win32.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/pysha3-1.0.2-cp37-cp37m-win32.whl -------------------------------------------------------------------------------- /python/deps/pysha3-1.0.2-cp37-cp37m-win_amd64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/deps/pysha3-1.0.2-cp37-cp37m-win_amd64.whl -------------------------------------------------------------------------------- /python/devise/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '1.7.0' 3 | from .clients import DeviseClient, DeviseToken 4 | from .miners import MasterNode 5 | from .owner import DeviseOwner 6 | -------------------------------------------------------------------------------- /python/devise/abi/AuditImpl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [ 5 | { 6 | "name": "addr", 7 | "type": "address" 8 | }, 9 | { 10 | "name": "roleName", 11 | "type": "string" 12 | } 13 | ], 14 | "name": "checkRole", 15 | "outputs": [], 16 | "payable": false, 17 | "stateMutability": "view", 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [ 23 | { 24 | "name": "addr", 25 | "type": "address" 26 | }, 27 | { 28 | "name": "roleName", 29 | "type": "string" 30 | } 31 | ], 32 | "name": "hasRole", 33 | "outputs": [ 34 | { 35 | "name": "", 36 | "type": "bool" 37 | } 38 | ], 39 | "payable": false, 40 | "stateMutability": "view", 41 | "type": "function" 42 | }, 43 | { 44 | "constant": true, 45 | "inputs": [], 46 | "name": "ROLE_AUDIT_UPDATER", 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "string" 51 | } 52 | ], 53 | "payable": false, 54 | "stateMutability": "view", 55 | "type": "function" 56 | }, 57 | { 58 | "constant": true, 59 | "inputs": [], 60 | "name": "auditUpdater", 61 | "outputs": [ 62 | { 63 | "name": "", 64 | "type": "address" 65 | } 66 | ], 67 | "payable": false, 68 | "stateMutability": "view", 69 | "type": "function" 70 | }, 71 | { 72 | "constant": true, 73 | "inputs": [], 74 | "name": "owner", 75 | "outputs": [ 76 | { 77 | "name": "", 78 | "type": "address" 79 | } 80 | ], 81 | "payable": false, 82 | "stateMutability": "view", 83 | "type": "function" 84 | }, 85 | { 86 | "anonymous": false, 87 | "inputs": [ 88 | { 89 | "indexed": true, 90 | "name": "eventType", 91 | "type": "bytes20" 92 | }, 93 | { 94 | "indexed": false, 95 | "name": "eventRawString", 96 | "type": "string" 97 | }, 98 | { 99 | "indexed": false, 100 | "name": "contentHash", 101 | "type": "bytes20" 102 | } 103 | ], 104 | "name": "AuditableEventCreated", 105 | "type": "event" 106 | }, 107 | { 108 | "anonymous": false, 109 | "inputs": [ 110 | { 111 | "indexed": false, 112 | "name": "addr", 113 | "type": "address" 114 | }, 115 | { 116 | "indexed": false, 117 | "name": "roleName", 118 | "type": "string" 119 | } 120 | ], 121 | "name": "RoleAdded", 122 | "type": "event" 123 | }, 124 | { 125 | "anonymous": false, 126 | "inputs": [ 127 | { 128 | "indexed": false, 129 | "name": "addr", 130 | "type": "address" 131 | }, 132 | { 133 | "indexed": false, 134 | "name": "roleName", 135 | "type": "string" 136 | } 137 | ], 138 | "name": "RoleRemoved", 139 | "type": "event" 140 | }, 141 | { 142 | "constant": false, 143 | "inputs": [ 144 | { 145 | "name": "addr", 146 | "type": "address" 147 | } 148 | ], 149 | "name": "addAuditUpdater", 150 | "outputs": [], 151 | "payable": false, 152 | "stateMutability": "nonpayable", 153 | "type": "function" 154 | }, 155 | { 156 | "constant": false, 157 | "inputs": [ 158 | { 159 | "name": "addr", 160 | "type": "address" 161 | } 162 | ], 163 | "name": "removeAuditUpdater", 164 | "outputs": [], 165 | "payable": false, 166 | "stateMutability": "nonpayable", 167 | "type": "function" 168 | }, 169 | { 170 | "constant": false, 171 | "inputs": [ 172 | { 173 | "name": "eventTypeHash", 174 | "type": "bytes20" 175 | }, 176 | { 177 | "name": "eventType", 178 | "type": "string" 179 | }, 180 | { 181 | "name": "contentHash", 182 | "type": "bytes20" 183 | } 184 | ], 185 | "name": "createAuditableEvent", 186 | "outputs": [], 187 | "payable": false, 188 | "stateMutability": "nonpayable", 189 | "type": "function" 190 | } 191 | ] 192 | -------------------------------------------------------------------------------- /python/devise/abi/DeviseTokenSale.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/devise/abi/DeviseTokenSale.json -------------------------------------------------------------------------------- /python/devise/clients/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .api import RentalAPI 3 | from .client import DeviseClient 4 | from .contract import RentalContract 5 | from .token import DeviseToken 6 | -------------------------------------------------------------------------------- /python/devise/clients/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.DeviseClient 4 | ~~~~~~~~~ 5 | This is the basic wrapper class around all Devise client facing operations. This wrapper connects to an Ethereum 6 | node and facilitates smart contract operations such as provision, leaseAll, getBalance, etc. It also allows for the 7 | download of the rented data. 8 | 9 | :copyright: © 2018 Pit.AI 10 | :license: GPLv3, see LICENSE for more details. 11 | """ 12 | from .api import RentalAPI 13 | from .contract import RentalContract 14 | 15 | 16 | class DeviseClient(RentalContract, RentalAPI): 17 | """ 18 | This the wrapper class around all Devise operations. 19 | 20 | Please note: 21 | 1- This is a thin wrapper around web3.py and all operations in this wrapper can be done more verbosely with web3. 22 | 2- Your private key is used to sign transactions and messages locally and is never transmitted. 23 | 24 | Usage: 25 | Option 1, using a hardware wallet (Ledger or Trezor): 26 | # Create a client instance connecting to your wallet (make sure to have it plugged in). 27 | client = DeviseClient(account='0x12134535...', auth_type='ledger') 28 | client.provision(1000000) 29 | balance = client.dvz_balance_escrow 30 | 31 | Option 2, using an encrypted json keystore file: 32 | # Create a client instance using a json keystore file exported from a wallet 33 | # Note: the private key is only used locally and is never transmitted to the node 34 | client = DeviseClient(key_file='/path/to/key-file.json/) 35 | client.provision(1000000) 36 | balance = client.dvz_balance_escrow 37 | 38 | Option 3, using a private key directly: 39 | # Create a client instance providing a clear text private key exported from an Ethereum wallet 40 | # Note 1: the private key is only used locally and is never transmitted to the node 41 | # Note 2: if you don't specify a password, you will be prompted to enter a password to decrypt your key file 42 | # for each transaction. 43 | client = DeviseClient(private_key='35e51d3f2e0c24c6e21a93...') 44 | client.provision(1000000) 45 | balance = client.dvz_balance_escrow 46 | 47 | Option 4, using an account address and a local Official Ethereum Wallet 48 | # Create a client instance providing the address of the account to use. 49 | # DeviseClient can find the corresponding encrypted keystore in the default keystore paths on disk without 50 | # you having to specify a path. 51 | # Note 1: the private key is only used locally and is never transmitted to the node 52 | # Note 2: if you don't specify a password, you will be prompted to enter a password to decrypt your key file 53 | # for each transaction. 54 | client = DeviseClient(account='0x12134535...') 55 | client.provision(1000000) 56 | balance = client.dvz_balance_escrow 57 | """ 58 | pass 59 | -------------------------------------------------------------------------------- /python/devise/clients/token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.owner.DeviseToken 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | This is the base class for all token contract operations 6 | 7 | :copyright: © 2018 Pit.AI 8 | :license: GPLv3, see LICENSE for more details. 9 | """ 10 | from devise.base import costs_gas, BaseDeviseClient 11 | 12 | TOKEN_PRECISION = int(1e6) 13 | 14 | 15 | class DeviseToken(BaseDeviseClient): 16 | """ 17 | This is the base class for all token operations 18 | """ 19 | 20 | @property 21 | def total_supply(self): 22 | """ 23 | Returns the total supply of tokens 24 | """ 25 | return self._token_contract.functions.totalSupply().call() / TOKEN_PRECISION 26 | 27 | @property 28 | def cap(self): 29 | """ 30 | Returns the maximum possible supply for this token 31 | """ 32 | return self._token_contract.functions.cap().call() / TOKEN_PRECISION 33 | 34 | def allowance(self, owner, spender): 35 | """ 36 | Checks the amount of tokens that an owner allowed to a spender 37 | :param owner: the owner of the tokens 38 | :param spender: the authorized spender 39 | :return: The amount spender is allowed to spend 40 | """ 41 | return self._token_contract.functions.allowance(owner, spender).call() / TOKEN_PRECISION 42 | 43 | def balance_of(self, address): 44 | """ 45 | Returns the balance in tokens of the address provided 46 | :param address: the address for which we're querying the token blance 47 | """ 48 | return self._token_contract.functions.balanceOf(address).call() / TOKEN_PRECISION 49 | 50 | @costs_gas 51 | def transfer(self, to_address, amount): 52 | """ 53 | Transfer {amount} of tokens to {to_address} 54 | :param to_address: the intended recipient of the tokens 55 | :param amount: the number of tokens to transfer 56 | """ 57 | assert len(to_address) == 42, "Invalid to_address parameter" 58 | assert 0 < amount <= self.balance_of(self.address), \ 59 | "invalid amount specified, must be > 0 and less than or equal to token balance of current account!" 60 | 61 | micro_dvz_tokens = int(amount * TOKEN_PRECISION) 62 | return self._transact(self._token_contract.functions.transfer(to_address, micro_dvz_tokens), 63 | {"from": self.address}) 64 | 65 | @costs_gas 66 | def transfer_from(self, from_address, to_address, amount): 67 | """ 68 | Transfer amount from from_address to to_address 69 | :param from_address: the account from which to take the tokens 70 | :param to_address: the intended recipient of the tokens 71 | :param amount: the number of tokens to transfer 72 | """ 73 | assert len(from_address) == 42, "Invalid from_address parameter" 74 | assert len(to_address) == 42, "Invalid to_address parameter" 75 | assert 0 < amount <= self.balance_of(from_address), \ 76 | "invalid amount specified, must be > 0 and <= the token balance of the from_address!" 77 | assert amount <= self.allowance(from_address, self.address), \ 78 | "invalid amount specified, must be <= approved spending allowance" 79 | 80 | micro_dvz_tokens = int(amount * TOKEN_PRECISION) 81 | return self._transact(self._token_contract.functions.transferFrom(from_address, to_address, micro_dvz_tokens), 82 | {"from": self.address}) 83 | 84 | @costs_gas 85 | def approve(self, spender_address, amount): 86 | """ 87 | Approve spender_address to transfer up to amount our of the current wallet 88 | :param spender_address: The address authorized to spend the amount of tokens specified 89 | :param amount: the max amount of tokens to be spent by spender_address 90 | """ 91 | assert len(spender_address) == 42, "Invalid spender_address parameter" 92 | micro_dvz_amount = int(amount * TOKEN_PRECISION) 93 | return self._transact(self._token_contract.functions.approve(spender_address, micro_dvz_amount), 94 | {"from": self.address}) 95 | 96 | @costs_gas 97 | def increase_approval(self, spender_address, amount): 98 | """ 99 | Increase the allownace of spender_address by the amount of tokens specified 100 | :param spender_address: The spender address 101 | :param amount: The amount of tokens to increase allowance by 102 | """ 103 | assert len(spender_address) == 42, "Invalid spender_address parameter" 104 | micro_dvz_amount = int(amount * TOKEN_PRECISION) 105 | return self._transact(self._token_contract.functions.increaseApproval(spender_address, micro_dvz_amount), 106 | {"from": self.address}) 107 | 108 | @costs_gas 109 | def decrease_approval(self, spender_address, amount): 110 | """ 111 | Decrease the allownace of spender_address by the amount of tokens specified 112 | :param spender_address: The spender address 113 | :param amount: The amount of tokens to decrease allowance by 114 | """ 115 | assert len(spender_address) == 42, "Invalid spender_address parameter" 116 | micro_dvz_amount = int(amount * TOKEN_PRECISION) 117 | return self._transact(self._token_contract.functions.decreaseApproval(spender_address, micro_dvz_amount), 118 | {"from": self.address}) 119 | -------------------------------------------------------------------------------- /python/devise/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.config 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | This file contains static configuration values for the Devise python codebase 6 | 7 | :copyright: © 2018 Pit.AI 8 | :license: GPLv3, see LICENSE for more details. 9 | """ 10 | 11 | # A static mapping of blockchain ID to deployed contract addresses 12 | 13 | CONTRACT_ADDRESSES = { 14 | # Main Ethereum network 15 | '1': { 16 | "DEVISE_RENTAL": "0xF08C62A99318277DE9Cc5699D6904032Cb598199", 17 | "DEVISE_TOKEN": "0xc2E71fdbFdd9e967D49B99CD288164e40e15992E", 18 | "DEVISE_TOKEN_SALE": "0xdB1548455e29c08377F0999Db2aF55F65758c514" 19 | }, 20 | # Rinkeby test network 21 | '4': { 22 | "DEVISE_RENTAL": "0xA3A5387cD8177BA3f5F47696988b1B51A3331CBF", 23 | "DEVISE_TOKEN": "0xF60Ef7D51a4Beb501bFcB380E1abbF49C042Ec53", 24 | "DEVISE_TOKEN_SALE": "0x7e50014E03535a14F844DF56dB4847254754Bb7B" 25 | }, 26 | # Ganache test tool 27 | '7778454': { 28 | 'DEVISE_RENTAL': '0xca5c8dC7C604590214c835463B41bC2cbC6deEd5', 29 | 'DEVISE_TOKEN': '0xD2AB5fA56D6d571De4d4B6531aD6F9147ddf058D', 30 | 'DEVISE_TOKEN_SALE': '0x0987eE274279c6707535FaEE0e2135857f3c3291', 31 | 'AUDIT': '0xF41B40b8CC749181C41644d7d797c71A1eb4f0E9' 32 | }, 33 | # dev1.devisechain.io 34 | "777666": { 35 | "DEVISE_RENTAL": "0x30Ca3a0917ABC23C3b38A9993d84a14e12cd71Cd", 36 | "DEVISE_TOKEN_SALE": "0xA76068c461716d34499cA221A037Cedb39067e26", 37 | "DEVISE_TOKEN": "0xC1844bbe0537cE51F95F9EC08c55D697fCcf3f17" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /python/devise/ledger.py: -------------------------------------------------------------------------------- 1 | # References: 2 | # https://github.com/LedgerHQ/blue-app-eth 3 | # https://github.com/bargst/pyethoff 4 | import struct 5 | 6 | from ledgerblue.comm import getDongle 7 | 8 | ETHEREUM_PATH_PREFIX = "44'/60'/0'/" 9 | CHUNK_SIZE = 150 10 | 11 | 12 | class LedgerWallet: 13 | """ 14 | """ 15 | 16 | def __init__(self): 17 | self.dongle = getDongle(debug=False) 18 | 19 | def _parse_bip32_path(self, account_index): 20 | """ 21 | Use an index to account to retrieve the internal 22 | path for the key 23 | """ 24 | path = ETHEREUM_PATH_PREFIX + str(account_index) 25 | result = bytes() 26 | elements = path.split('/') 27 | for pathElement in elements: 28 | element = pathElement.split("'") 29 | if len(element) == 1: 30 | result = result + struct.pack(">I", int(element[0])) 31 | else: 32 | result = result + struct.pack(">I", 0x80000000 | int(element[0])) 33 | return result 34 | 35 | def _exchange(self, donglePath, rlp_encoded_tx=None): 36 | result = None 37 | if rlp_encoded_tx is None: 38 | apdu = bytes.fromhex('e0020000') 39 | apdu += bytes([len(donglePath) + 1]) 40 | apdu += bytes([len(donglePath) // 4]) 41 | apdu += donglePath 42 | result = self.dongle.exchange(apdu, timeout=60) 43 | else: 44 | offset = 0 45 | while offset != len(rlp_encoded_tx): 46 | if (len(rlp_encoded_tx) - offset) > CHUNK_SIZE: 47 | chunk = rlp_encoded_tx[offset: offset + CHUNK_SIZE] 48 | else: 49 | chunk = rlp_encoded_tx[offset:] 50 | if offset == 0: 51 | p1 = '00' 52 | else: 53 | p1 = '80' 54 | 55 | apdu = bytes.fromhex('e004' + p1 + '00') 56 | if p1 == '00': 57 | apdu += bytes([len(donglePath) + 1 + len(chunk)]) 58 | apdu += bytes([len(donglePath) // 4]) 59 | apdu += donglePath 60 | else: 61 | apdu += bytes([len(chunk)]) 62 | 63 | apdu += chunk 64 | result = self.dongle.exchange(apdu, timeout=60) 65 | offset += len(chunk) 66 | 67 | return result 68 | 69 | def get_address(self, account_index): 70 | """ 71 | Query the ledger device for a public ethereum address. 72 | account_index is the number in the HD wallet tree 73 | """ 74 | donglePath = self._parse_bip32_path(account_index) 75 | 76 | result = self._exchange(donglePath) 77 | 78 | # Parse result 79 | offset = 1 + result[0] 80 | address = result[offset + 1: offset + 1 + result[offset]] 81 | 82 | return '0x' + address.decode() 83 | 84 | def get_account_index(self, address): 85 | """ 86 | Convert an address to an account index 87 | """ 88 | account_index = 0 89 | status = address != self.get_address(account_index) 90 | while status: 91 | account_index += 1 92 | status = address != self.get_address(account_index) 93 | 94 | return account_index 95 | 96 | def sign(self, rlp_encoded_tx, account_index=None, address=''): 97 | """ 98 | Sign an RLP encoded transaction 99 | """ 100 | if account_index is None: 101 | # Convert an address to an offset 102 | if address == '': 103 | raise Exception('Invalid offset and address provided') 104 | else: 105 | account_index = self.get_account_index(address) 106 | 107 | donglePath = self._parse_bip32_path(account_index) 108 | 109 | result = self._exchange(donglePath, rlp_encoded_tx) 110 | 111 | # Retrieve VRS from sig 112 | v = result[0] 113 | r = int.from_bytes(result[1:1 + 32], 'big') 114 | s = int.from_bytes(result[1 + 32: 1 + 32 + 32], 'big') 115 | 116 | return (v, r, s) 117 | -------------------------------------------------------------------------------- /python/devise/miners/__init__.py: -------------------------------------------------------------------------------- 1 | from .master_node import MasterNode 2 | -------------------------------------------------------------------------------- /python/devise/miners/master_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.miners.MasterNode 4 | ~~~~~~~~~ 5 | This is the wrapper around all the master node smart contract operations. This wrapper connects to an Ethereum 6 | node and facilitates smart contract operations such as adding leptons to the blockchain. 7 | 8 | :copyright: © 2018 Pit.AI 9 | :license: GPLv3, see LICENSE for more details. 10 | """ 11 | 12 | from devise.base import IU_PRECISION, BaseDeviseClient 13 | 14 | 15 | class MasterNode(BaseDeviseClient): 16 | """ 17 | Wrapper class around Master Node smart contract operations. For example: adding leptons to the blockchain 18 | """ 19 | 20 | def add_lepton(self, lepton_hash, previous_lepton_hash, incremental_usefulness, gas_price=None): 21 | """ 22 | Add a new lepton to the block chain (for example when a miner finds a lepton and it passes the proof) 23 | :param lepton_hash the lepton hash to add 24 | :param previous_lepton_hash the lepton hash to of the previous lepton in the chain 25 | :param incremental_usefulness the incremental usefulness of the new lepton 26 | :param gas_price the gas price to use for this transaction 27 | :return: the transaction receipt 28 | """ 29 | # Make sure we are running this as the owner of the clients contract 30 | assert self.account in self.get_master_nodes(), "address %s is not a master node!" % self.account 31 | # Convert the iu to int based on our precision setting 32 | contract_iu = int(incremental_usefulness * IU_PRECISION) 33 | # validate the hashes 34 | assert len(lepton_hash) == 40, "lepton_hash must be a sha1 hash encoded as a 40 character hex string" 35 | assert previous_lepton_hash is None or len( 36 | previous_lepton_hash) == 40, "previous_lepton_hash must be a sha1 hash encoded as a 40 character hex string" 37 | if previous_lepton_hash is None: 38 | previous_lepton_hash = '00' 39 | # Build a transaction dictionary with the optional gas_price and nonce 40 | transaction = {"from": self.account} 41 | if gas_price is not None: 42 | transaction["gasPrice"] = gas_price 43 | # execute transaction 44 | return self._transact(self._rental_contract.functions.addLepton( 45 | bytes.fromhex(lepton_hash), bytes.fromhex(previous_lepton_hash), contract_iu), transaction) 46 | 47 | def get_master_nodes(self): 48 | """returns a list of all authorized master nodes""" 49 | return self._rental_contract.functions.getMasterNodes().call() 50 | -------------------------------------------------------------------------------- /python/devise/owner/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .owner import DeviseOwner 3 | from .token_owner import DeviseTokenOwner 4 | -------------------------------------------------------------------------------- /python/devise/owner/owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.owner.DeviseOwner 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | This is the base class for all smart contract operations from Pit.AI (owner of the smart contract). 6 | 7 | :copyright: © 2018 Pit.AI 8 | :license: GPLv3, see LICENSE for more details. 9 | """ 10 | import hashlib 11 | import json 12 | from json import JSONDecodeError 13 | 14 | import requests 15 | 16 | from devise.base import costs_gas, BaseDeviseClient 17 | from devise.clients.contract import USD_PRECISION 18 | from devise.clients.token import TOKEN_PRECISION 19 | 20 | EVENT_TYPES = { 21 | "LATEST_WEIGHTS_UPDATED": "LatestWeightsUpdated" 22 | } 23 | 24 | 25 | class DeviseOwner(BaseDeviseClient): 26 | """ 27 | This is the base class for all smart contract operations from Pit.AI (owner of the smart contract). 28 | """ 29 | 30 | @property 31 | def implementation(self): 32 | return self._rental_proxy_contract.functions.implementation().call() 33 | 34 | @property 35 | def impl_version(self): 36 | return self._rental_proxy_contract.functions.version().call() 37 | 38 | def get_all_implementations(self): 39 | [impl_history, ver_history] = self._rental_proxy_contract.functions.getAllImplementations().call() 40 | history = [{"ver": _ver, "impl": _impl} for _ver, _impl in zip(ver_history, impl_history)] 41 | return history 42 | 43 | def get_master_nodes(self): 44 | """returns a list of all authorized master nodes""" 45 | return self._rental_contract.functions.getMasterNodes().call() 46 | 47 | def get_rate_setter(self): 48 | return self._rental_contract.functions.rateSetter().call() 49 | 50 | def get_audit_updater(self): 51 | return self._audit_contract.functions.auditUpdater().call() 52 | 53 | def get_escrow_history(self): 54 | return self._rental_contract.functions.getEscrowHistory().call() 55 | 56 | def get_revenue_history(self): 57 | return self._rental_contract.functions.getRevenueHistory().call() 58 | 59 | @costs_gas 60 | def set_historical_data_fee(self, tokens): 61 | """ 62 | Updates the cost in tokens to gain access to the historical weights and returns data archive 63 | :param tokens: the amount of tokens paid to change the account status historical data access status 64 | """ 65 | micro_tokens = tokens * TOKEN_PRECISION 66 | return self._transact(self._rental_contract.functions.setHistoricalDataFee(micro_tokens), 67 | {"from": self.address}) 68 | 69 | @costs_gas 70 | def set_power_user_fee(self, tokens): 71 | """ 72 | Updates the cost in tokens to gain power user privileges 73 | :param tokens: the amount of tokens paid to change the account status to power user 74 | """ 75 | micro_tokens = tokens * TOKEN_PRECISION 76 | return self._transact(self._rental_contract.functions.setPowerUserClubFee(micro_tokens), {"from": self.address}) 77 | 78 | @costs_gas 79 | def update_contract_state(self): 80 | """ 81 | Updates the internal state of the contract 82 | """ 83 | return self._transact(self._rental_contract.functions.updateGlobalState(), {"from": self.address}) 84 | 85 | @costs_gas 86 | def add_master_node(self, address): 87 | """Authorizes an address to perform the master node role""" 88 | return self._transact(self._rental_contract.functions.addMasterNode(address), {"from": self.address}) 89 | 90 | @costs_gas 91 | def remove_master_node(self, address): 92 | """Unauthorizes an address to perform the master node role""" 93 | return self._transact(self._rental_contract.functions.removeMasterNode(address), {"from": self.address}) 94 | 95 | @costs_gas 96 | def add_rate_setter(self, address): 97 | return self._transact(self._rental_contract.functions.addRateSetter(address), {"from": self.address}) 98 | 99 | @costs_gas 100 | def remove_rate_setter(self, address): 101 | return self._transact(self._rental_contract.functions.removeRateSetter(address), {"from": self.address}) 102 | 103 | @costs_gas 104 | def add_audit_updater(self, address): 105 | return self._transact(self._audit_contract.functions.addAuditUpdater(address), {"from": self.address}) 106 | 107 | @costs_gas 108 | def remove_audit_updater(self, address): 109 | return self._transact(self._audit_contract.functions.removeAuditUpdater(address), {"from": self.address}) 110 | 111 | @costs_gas 112 | def set_eth_usd_rate(self): 113 | try: 114 | price = self._get_eth_usd_price() 115 | except JSONDecodeError: 116 | self.logger.info("!!!!!! WARNING: ERROR WHEN GETTING REAL TIME ETHER/USD PRICE. " 117 | "PLEASE TRY AGAIN LATER. !!!!!") 118 | price = None 119 | assert price is not None, "Fail to get real time Ether price. Please try again later!!!" 120 | self.logger.info("Setting the exchange rate at $%s per ether", price) 121 | price = int(float(price) * USD_PRECISION) 122 | assert price > 0 123 | self._transact(self._rental_contract.functions.setRateETHUSD(price), {"from": self.address}) 124 | 125 | def _validate_hash(self, content_hash): 126 | # Validate hash received 127 | try: 128 | assert len(content_hash) == 40, "Hash provided must be a valid sha1 hash" 129 | hash_bytes = bytes.fromhex(content_hash) 130 | except ValueError: 131 | raise AssertionError("Hash provided must be a valid sha1 hash") 132 | 133 | return hash_bytes 134 | 135 | @costs_gas 136 | def latest_weights_updated(self, content_hash): 137 | """ 138 | Triggers a transaction which emits an event of type LatestWeightsUpdated from the rental smart contract 139 | :param content_hash: The sha1 hash of the content of the file as a hex string 140 | :return: True if the transaction is successful and False otherwise 141 | """ 142 | # Validate hash received 143 | hash_bytes = self._validate_hash(content_hash) 144 | 145 | # Build a transaction with double the gas price estimated 146 | transaction = { 147 | "from": self.address, 148 | "gasPrice": self.w3.eth.generateGasPrice() * 2 149 | } 150 | event_type = EVENT_TYPES["LATEST_WEIGHTS_UPDATED"] 151 | event_type_hash = hashlib.sha1(event_type.encode('utf8')).hexdigest() 152 | return self._transact( 153 | self._audit_contract.functions.createAuditableEvent(event_type_hash, event_type, hash_bytes), 154 | transaction) 155 | 156 | def _get_eth_usd_price(self): 157 | return json.loads(requests.get('https://api.gdax.com/products/ETH-USD/ticker').text).get("price", None) 158 | -------------------------------------------------------------------------------- /python/devise/owner/token_owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | devise.owner.DeviseTokenOwner 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | This is the base class for all owner token operations contract operations 6 | 7 | :copyright: © 2018 Pit.AI 8 | :license: GPLv3, see LICENSE for more details. 9 | """ 10 | from devise.base import costs_gas 11 | from devise.clients.token import DeviseToken 12 | 13 | 14 | class DeviseTokenOwner(DeviseToken): 15 | """ 16 | This is the base class for all owner token operations 17 | """ 18 | 19 | @costs_gas 20 | def mint(self, address_to, tokens): 21 | """ 22 | Mints the number of tokens specified to the beneficiary account specified 23 | :param address_to: the recipient of the minted tokens 24 | :param tokens: the amount of tokens minted 25 | """ 26 | assert self.total_supply + tokens <= self.cap, "Minted tokens + total_supply cannot exceed the cap %s DVZ" % self.cap 27 | micro_dvz_amount = int(tokens * 1e6) 28 | return self._transact(self._token_contract.functions.mint(address_to, micro_dvz_amount), {"from": self.address}) 29 | 30 | @costs_gas 31 | def add_minter(self, address): 32 | """ 33 | Adds the minter role to authorize a new minter address 34 | :param address: the minter address to authorize to mint 35 | """ 36 | self._transact(self._token_contract.functions.addMinter(address), {"from": self.address}) 37 | 38 | @costs_gas 39 | def remove_minter(self, address): 40 | """ 41 | Removes the minter role to de-authorize a minter address 42 | :param address: the minter address to remove the minter role from 43 | """ 44 | self._transact(self._token_contract.functions.removeMinter(address), {"from": self.address}) 45 | 46 | def get_minters(self): 47 | """ 48 | Retrieve a list of minters 49 | :return: An array of minter addresses 50 | """ 51 | minters = [] 52 | n = self._token_contract.functions.getNumberOfMinters().call({"from": self.address}) 53 | for i in range(n): 54 | minter = self._token_contract.functions.getMinter(i).call({"from": self.address}) 55 | minters.append(minter) 56 | 57 | return minters 58 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import io 3 | import os 4 | import re 5 | import subprocess 6 | import sys 7 | 8 | from setuptools import setup, find_packages 9 | 10 | sys.path.append('.') 11 | 12 | try: 13 | doc_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'README.rst') 14 | with io.open(doc_path, 'rt', encoding='utf8') as f: 15 | readme = f.read() 16 | except: 17 | print("Warning, no readme!") 18 | readme = '' 19 | 20 | 21 | def install_devise_dependencies(): 22 | print("Installing platform specific wheels...") 23 | try: 24 | # For windows, try to install one of the pre-built binary wheels 25 | subprocess.call([sys.executable, '-m', 'pip', 'install', '--find-links=deps/', 'toolz==0.9.0']) 26 | subprocess.call([sys.executable, '-m', 'pip', 'install', '--no-index', '--find-links=deps/', 'cytoolz']) 27 | subprocess.call([sys.executable, '-m', 'pip', 'install', '--no-index', '--find-links=deps/', 'lru_dict']) 28 | subprocess.call([sys.executable, '-m', 'pip', 'install', '--no-index', '--find-links=deps/', 'pysha3']) 29 | subprocess.call([sys.executable, '-m', 'pip', 'install', '--no-index', '--find-links=deps/', 'hidapi']) 30 | except: 31 | pass 32 | 33 | # Install ledger blue 34 | print("Installing custom dependencies...") 35 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', "--find-links=deps/", "ledgerblue==0.1.18"]) 36 | 37 | 38 | def devise_setup(**kwargs): 39 | """Install prerequisites first then run the rest""" 40 | install_devise_dependencies() 41 | setup(**kwargs) 42 | 43 | 44 | # Get our current package version from the devise module 45 | with io.open('devise/__init__.py', 'rt', encoding='utf8') as f: 46 | version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1) 47 | 48 | devise_setup(name='devise', 49 | maintainer='Devise Foundation', 50 | version=version, 51 | license='GPL-3', 52 | description='Devise: An Ethereum Marketplace for Engineering Better Representations of Financial Markets', 53 | url='https://github.com/devisechain/devise', 54 | long_description=readme, 55 | packages=find_packages(exclude=['tests']), 56 | include_package_data=True, 57 | install_requires=[ 58 | 'web3==4.2.1', 59 | 'rlp==0.6.0', 60 | 'pysha3==1.0.2', 61 | 'eth-abi==1.1.1', 62 | 'eth-utils==1.0.3' 63 | ], 64 | extras_require={ 65 | 'dev': [ 66 | 'pytest', 67 | 'pep8', 68 | 'pylint', 69 | 'pytest-cov' 70 | ] 71 | }, 72 | classifiers=[ 73 | 'Development Status :: 4 - Beta', 74 | 'Environment :: Console', 75 | 'Intended Audience :: Developers', 76 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 77 | 'Operating System :: OS Independent', 78 | 'Programming Language :: Python', 79 | 'Programming Language :: Python :: 3.5', 80 | 'Programming Language :: Python :: 3.6', 81 | 'Topic :: Software Development :: Libraries', 82 | 'Topic :: Office/Business :: Financial :: Investment' 83 | ]) 84 | -------------------------------------------------------------------------------- /python/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/python/tests/__init__.py -------------------------------------------------------------------------------- /python/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # For unit tests, use local ganache test network 4 | os.environ["ETHEREUM_NETWORK"] = "ganache" 5 | 6 | import pytest 7 | from ledgerblue.commException import CommException 8 | from web3 import Web3 9 | 10 | from devise import DeviseClient, MasterNode 11 | from devise.clients.token import DeviseToken 12 | from devise.owner import DeviseOwner 13 | from devise.owner.token_owner import DeviseTokenOwner 14 | from .utils import TEST_KEYS 15 | 16 | 17 | @pytest.fixture() 18 | def client(): 19 | """Devise Client fixture created using an account's private key""" 20 | client = DeviseClient(private_key=TEST_KEYS[5]) 21 | 22 | net_id = client.w3.version.network 23 | if net_id == "1": 24 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 25 | 26 | return client 27 | 28 | 29 | @pytest.fixture() 30 | def client_ledger(): 31 | """Devise Client fixture created to use Ledger nano s hardware wallet""" 32 | try: 33 | client = DeviseClient(account='0x879CEAA289334fE176B95ba341Dc2780ECE4da20', auth_type='ledger') 34 | except CommException: 35 | return None 36 | 37 | net_id = client.w3.version.network 38 | if net_id == "1": 39 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 40 | 41 | return client 42 | 43 | 44 | @pytest.fixture() 45 | def owner_client(): 46 | """Devise Client fixture created using an account's private key""" 47 | client = DeviseOwner(private_key=TEST_KEYS[0]) 48 | 49 | net_id = client.w3.version.network 50 | if net_id == "1": 51 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 52 | 53 | escrow_wallet_account = Web3().eth.accounts[3] 54 | client_wallet = DeviseClient(private_key=TEST_KEYS[3]) 55 | accounting_contract = client_wallet._rental_contract.functions.accounting().call() 56 | client_wallet._transact(client_wallet._token_contract.functions.approve(accounting_contract, int(1e20)), 57 | {"from": escrow_wallet_account}) 58 | return client 59 | 60 | 61 | @pytest.fixture() 62 | def token_owner_client(): 63 | """Devise Client fixture created using an account's private key""" 64 | client = DeviseTokenOwner(private_key=TEST_KEYS[1]) 65 | 66 | net_id = client.w3.version.network 67 | if net_id == "1": 68 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 69 | 70 | return client 71 | 72 | 73 | @pytest.fixture() 74 | def token_wallet_client(): 75 | """Devise Client fixture created using an account's private key""" 76 | client = DeviseToken(private_key=TEST_KEYS[2]) 77 | 78 | net_id = client.w3.version.network 79 | if net_id == "1": 80 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 81 | 82 | return client 83 | 84 | 85 | @pytest.fixture() 86 | def token_client(): 87 | """Devise Client fixture created using an account's private key""" 88 | client = DeviseToken(private_key=TEST_KEYS[0]) 89 | 90 | net_id = client.w3.version.network 91 | if net_id == "1": 92 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 93 | 94 | return client 95 | 96 | 97 | @pytest.fixture() 98 | def master_node(): 99 | """Devise Client fixture created using an account's private key""" 100 | client = MasterNode(private_key=TEST_KEYS[0]) 101 | 102 | net_id = client.w3.version.network 103 | if net_id == "1": 104 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 105 | 106 | return client 107 | 108 | 109 | @pytest.fixture() 110 | def rate_setter(): 111 | """Devise Client fixture created for setting rate""" 112 | client = DeviseOwner(private_key=TEST_KEYS[5]) 113 | 114 | net_id = client.w3.version.network 115 | if net_id == "1": 116 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 117 | 118 | return client 119 | 120 | 121 | @pytest.fixture() 122 | def weights_updater(): 123 | """Devise Client fixture created for setting rate""" 124 | client = DeviseOwner(private_key=TEST_KEYS[5]) 125 | 126 | net_id = client.w3.version.network 127 | if net_id == "1": 128 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 129 | 130 | return client 131 | 132 | 133 | @pytest.fixture() 134 | def client_local_pk(): 135 | """Devise Client fixture created using an account's private key""" 136 | client = DeviseClient(private_key=TEST_KEYS[5]) 137 | 138 | net_id = client.w3.version.network 139 | if net_id == "1": 140 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 141 | 142 | return client 143 | 144 | 145 | @pytest.fixture() 146 | def client_local_keyfile(token_wallet_client): 147 | """Devise Client fixture created using an account's private key""" 148 | key_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'key_file.json') 149 | client = DeviseClient(key_file=key_path) 150 | 151 | # TODO Remove this and replace with real provisioning with ether in the tests 152 | token_wallet_client.transfer(client.address, 10000000) 153 | 154 | net_id = client.w3.version.network 155 | if net_id == "1": 156 | raise RuntimeError("Cowardly refusing to run tests against MainNet!!") 157 | 158 | return client 159 | -------------------------------------------------------------------------------- /python/tests/hash_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "22ff9f93-0c8a-471f-b1c6-dffa4fd80975", 4 | "address": "93c86a7574a1e5eaf773b807fff3496728f5b1bc", 5 | "crypto": { 6 | "cipherparams": { 7 | "iv": "49e78ccc5f6f32ff13c394ba24f56114" 8 | }, 9 | "kdfparams": { 10 | "c": 1000000, 11 | "prf": "hmac-sha256", 12 | "salt": "8cb3b03aafeee562cae9969f012030e1", 13 | "dklen": 32 14 | }, 15 | "ciphertext": "cf1e4d70f766970d77c5f9257bff045848760e41c6b66ba88162918e680a3417", 16 | "cipher": "aes-128-ctr", 17 | "mac": "1df4498b62fff0ee4ab7b5aa379f4fd1c4184c2aa7633a4506dd031bc37c2923", 18 | "kdf": "pbkdf2" 19 | } 20 | } -------------------------------------------------------------------------------- /python/tests/key_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "id": "22ff9f93-0c8a-471f-b1c6-dffa4fd80975", 4 | "address": "93c86a7574a1e5eaf773b807fff3496728f5b1bc", 5 | "crypto": { 6 | "cipherparams": { 7 | "iv": "49e78ccc5f6f32ff13c394ba24f56114" 8 | }, 9 | "kdfparams": { 10 | "c": 1000000, 11 | "prf": "hmac-sha256", 12 | "salt": "8cb3b03aafeee562cae9969f012030e1", 13 | "dklen": 32 14 | }, 15 | "ciphertext": "cf1e4d70f766970d77c5f9257bff045848760e41c6b66ba88162918e680a3417", 16 | "cipher": "aes-128-ctr", 17 | "mac": "1df4498b62fff0ee4ab7b5aa379f4fd1c4184c2aa7633a4506dd031bc37c2923", 18 | "kdf": "pbkdf2" 19 | } 20 | } -------------------------------------------------------------------------------- /python/tests/test_devise_owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DeviseOwner tests 4 | ~~~~~~~~~ 5 | These are the DeviseOwnser tests. These assume you are running ganache or similar tool. Do not use on MainNet! 6 | For example, you can use ganache with the mnemonic "moment reform peace alter nominee you label idle license organ 7 | youth good", and in secure mode with --secure, and these tests should pass. 8 | 9 | To run these tests against your own deployed contracts, make sure you change the contract addresses in 10 | Devise/base.py to match what you deployed with truffle. 11 | 12 | :copyright: © 2018 Pit.AI 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | 16 | import pytest 17 | 18 | from .utils import evm_snapshot, evm_revert 19 | 20 | 21 | class TestDeviseOwner(object): 22 | @pytest.fixture(scope="function", autouse=True) 23 | def setup_method(self, owner_client, client, token_wallet_client): 24 | self.client = client 25 | _ = owner_client 26 | self.snapshot_id = evm_snapshot(client) 27 | # TODO Remove this and replace with real provisioning with ether in the tests 28 | token_wallet_client.transfer(client.address, 10000000) 29 | 30 | def teardown_method(self, method): 31 | self.snapshot_id = evm_revert(self.snapshot_id, self.client) 32 | 33 | def test_set_power_user_fee(self, owner_client, client): 34 | owner_client.set_power_user_fee(5000) 35 | client.provision(client.dvz_balance) 36 | token_bal = client.dvz_balance_escrow 37 | client.apply_for_power_user() 38 | assert client.client_summary == { 39 | 'client': '0xA1C2684B68A98c9636FC22F3B4E4332eF35A2408', 40 | 'beneficiary': '0xA1C2684B68A98c9636FC22F3B4E4332eF35A2408', 41 | 'dvz_balance_escrow': token_bal - 5000, 42 | 'dvz_balance': 0.0, 43 | 'last_term_paid': None, 44 | 'power_user': True, 45 | 'historical_data_access': False, 46 | 'current_term_seats': 0, 47 | 'indicative_next_term_seats': 0 48 | } 49 | 50 | def test_set_historical_data_fee(self, owner_client, client): 51 | owner_client.set_historical_data_fee(5000) 52 | client.provision(client.dvz_balance) 53 | token_bal = client.dvz_balance_escrow 54 | client.request_historical_data_access() 55 | assert client.client_summary == { 56 | 'client': '0xA1C2684B68A98c9636FC22F3B4E4332eF35A2408', 57 | 'beneficiary': '0xA1C2684B68A98c9636FC22F3B4E4332eF35A2408', 58 | 'dvz_balance_escrow': token_bal - 5000, 59 | 'dvz_balance': 0.0, 60 | 'last_term_paid': None, 61 | 'power_user': True, 62 | 'historical_data_access': True, 63 | 'current_term_seats': 0, 64 | 'indicative_next_term_seats': 0 65 | } 66 | 67 | def test_get_escrow_history(self, owner_client): 68 | escrow_hist = owner_client.get_escrow_history() 69 | assert escrow_hist == ['0x93c86A7574a1E5eAF773B807fFF3496728f5B1BC'] 70 | 71 | def test_get_revenue_history(self, owner_client): 72 | rev_hist = owner_client.get_revenue_history() 73 | assert rev_hist == ['0x5c7Fe1B9bad324c5c8B90f66243B45F65B3f5fcd'] 74 | 75 | def test_implementation(self, owner_client): 76 | impl = owner_client.implementation 77 | assert impl == '0x5a1e6BC336D5d19E0ADfaa6A1826CF39A55315bA' 78 | 79 | def test_impl_version(self, owner_client): 80 | ver = owner_client.impl_version 81 | assert ver == 1 82 | 83 | def test_get_all_implementations(self, owner_client): 84 | history = owner_client.get_all_implementations() 85 | assert history == [{"impl": '0x5a1e6BC336D5d19E0ADfaa6A1826CF39A55315bA', "ver": 1}] 86 | 87 | def test_get_rate_setter(self, owner_client): 88 | rate_setter = owner_client.get_rate_setter() 89 | assert rate_setter == '0x0000000000000000000000000000000000000000' 90 | 91 | def test_get_audit_updater(self, owner_client): 92 | weights_updater = owner_client.get_audit_updater() 93 | assert weights_updater == '0x0000000000000000000000000000000000000000' 94 | 95 | def test_add_rate_setter(self, owner_client, client): 96 | rate_setter = client.address 97 | ret = owner_client.add_rate_setter(rate_setter) 98 | assert ret 99 | 100 | def test_remove_rate_setter(self, owner_client, client): 101 | owner_client.add_rate_setter(client.address) 102 | owner_client.remove_rate_setter(client.address) 103 | assert owner_client.get_rate_setter() == '0x0000000000000000000000000000000000000000' 104 | 105 | def test_add_audit_updater(self, owner_client, client): 106 | weights_updater = client.address 107 | ret = owner_client.add_audit_updater(weights_updater) 108 | assert ret 109 | 110 | def test_remove_audit_updater(self, owner_client, client): 111 | owner_client.add_audit_updater(client.address) 112 | owner_client.remove_audit_updater(client.address) 113 | assert owner_client.get_audit_updater() == '0x0000000000000000000000000000000000000000' 114 | 115 | def test_set_eth_usd_rate(self, owner_client, rate_setter, client): 116 | owner_client.add_rate_setter(rate_setter.address) 117 | rate_setter.set_eth_usd_rate() 118 | rate = client.eth_usd_rate 119 | assert rate > 100 120 | 121 | def test_latest_weights_updated(self, owner_client, weights_updater): 122 | owner_client.add_audit_updater(weights_updater.address) 123 | hash = '38a1e8a65521791b9d34cd62fac36ceb5349bb6c' 124 | tx = weights_updater.latest_weights_updated(hash) 125 | assert tx is True 126 | with pytest.raises(AssertionError): 127 | hash = '0x38a1e8a65521791b9d34cd62fac36ceb5349bb6c' 128 | weights_updater.latest_weights_updated(hash) 129 | -------------------------------------------------------------------------------- /python/tests/test_ledger_wallet.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | from ledgerblue.commException import CommException 5 | 6 | from devise.ledger import LedgerWallet 7 | 8 | 9 | class TestLedgerWallet(object): 10 | @pytest.mark.skipif(os.environ.get("JENKINS_BUILD", False), 11 | reason="Jenkins cannot access a ledger hardware wallet!") 12 | def test_account_index(self): 13 | ledger = None 14 | try: 15 | ledger = LedgerWallet() 16 | except CommException: 17 | pytest.skip('Ledger nano dongle not found!') 18 | 19 | index = ledger.get_account_index('0xc5b7e45ba600324868a0c86a567b902dc35f0958ca46fb86dcaf352f12e6d913') 20 | assert index == 0 21 | -------------------------------------------------------------------------------- /python/tests/test_master_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | MasterNode tests 4 | ~~~~~~~~~ 5 | These are the MasterNode tests. These assume you are running ganache or similar tool. Do not use on MainNet! 6 | For example, you can use ganache with the mnemonic "moment reform peace alter nominee you label idle license organ 7 | youth good", and in secure mode with --secure, and these tests should pass. 8 | 9 | To run these tests against your own deployed contracts, make sure you change the contract addresses in 10 | Devise/base.py to match what you deployed with truffle. 11 | 12 | :copyright: © 2018 Pit.AI 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | import hashlib 16 | 17 | import pytest 18 | 19 | from devise import MasterNode 20 | from .utils import evm_snapshot, evm_revert, TEST_KEYS 21 | 22 | 23 | class TestMasterNode(object): 24 | @pytest.fixture(scope="function", autouse=True) 25 | def setup_method(self, owner_client, client): 26 | self.client = client 27 | _ = owner_client 28 | self.snapshot_id = evm_snapshot(client) 29 | 30 | def teardown_method(self, method): 31 | self.snapshot_id = evm_revert(self.snapshot_id, self.client) 32 | 33 | def test_add_lepton(self, master_node, client): 34 | """Tests that we can add leptons as the owner""" 35 | leptons = client.get_all_leptons() 36 | new_lepton = 'hello world %s' % (len(leptons) + 1) 37 | lepton1_hash = hashlib.sha1(new_lepton.encode('utf8')).hexdigest() 38 | master_node.add_lepton(lepton1_hash, None, 0.5123456789123456789) 39 | 40 | new_leptons = client.get_all_leptons() 41 | assert len(new_leptons) == len(leptons) + 1 42 | assert new_leptons[-1] == {"hash": lepton1_hash, "previous_hash": None, "incremental_usefulness": 0.512345} 43 | 44 | new_lepton = 'hello world 2 %s' % (len(leptons) + 1) 45 | lepton2_hash = hashlib.sha1(new_lepton.encode('utf8')).hexdigest() 46 | master_node.add_lepton(lepton2_hash, lepton1_hash, 0.5123456789123456789) 47 | new_leptons = client.get_all_leptons() 48 | assert len(new_leptons) == len(leptons) + 2 49 | assert new_leptons[-1] == {"hash": lepton2_hash, "previous_hash": lepton1_hash, 50 | "incremental_usefulness": 0.512345} 51 | 52 | def test_add_master_node(self, owner_client, client): 53 | master_node = MasterNode(private_key=TEST_KEYS[2]) 54 | new_lepton = 'hello world 1' 55 | lepton_hash = hashlib.sha1(new_lepton.encode('utf8')).hexdigest() 56 | # Not everyone can add leptons 57 | with pytest.raises(Exception): 58 | master_node.add_lepton(lepton_hash, None, 0.5123456789123456789) 59 | num_leptons = len(client.get_all_leptons()) 60 | # make this address a master node 61 | owner_client.add_master_node(master_node.address) 62 | assert master_node.address in owner_client.get_master_nodes() 63 | # Add a lepton as the new master node 64 | master_node.add_lepton(lepton_hash, None, 0.5123456789123456789) 65 | new_leptons = client.get_all_leptons() 66 | assert len(new_leptons) == num_leptons + 1 67 | assert new_leptons[-1] == {"hash": lepton_hash, "previous_hash": None, 68 | "incremental_usefulness": 0.512345} 69 | 70 | # more than one master node can add leptons: 71 | master_node2 = MasterNode(private_key=TEST_KEYS[3]) 72 | assert master_node2.address not in owner_client.get_master_nodes() 73 | owner_client.add_master_node(master_node2.address) 74 | lepton_hash2 = hashlib.sha1("hello world 2".encode('utf8')).hexdigest() 75 | lepton_hash3 = hashlib.sha1("hello world 3".encode('utf8')).hexdigest() 76 | master_node2.add_lepton(lepton_hash2, lepton_hash, 0.5123456789123456789) 77 | master_node.add_lepton(lepton_hash3, lepton_hash2, 0.5123456789123456789) 78 | new_leptons = client.get_all_leptons() 79 | assert len(new_leptons) == 3 80 | assert new_leptons[-1] == {"hash": lepton_hash3, "previous_hash": lepton_hash2, 81 | "incremental_usefulness": 0.512345} 82 | 83 | def test_remove_master_node(self, owner_client): 84 | master_node = MasterNode(private_key=TEST_KEYS[2]) 85 | new_lepton = 'hello world 1' 86 | lepton_hash = hashlib.sha1(new_lepton.encode('utf8')).hexdigest() 87 | 88 | # Add a master node 89 | owner_client.add_master_node(master_node.address) 90 | assert master_node.address in owner_client.get_master_nodes() 91 | # remove the master node 92 | owner_client.remove_master_node(master_node.address) 93 | assert master_node.address not in owner_client.get_master_nodes() 94 | 95 | # Not everyone can add leptons 96 | with pytest.raises(Exception): 97 | master_node.add_lepton(lepton_hash, None, 0.5123456789123456789) 98 | -------------------------------------------------------------------------------- /python/tests/test_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Token tests 4 | ~~~~~~~~~ 5 | These are the token tests. These assume you are running ganache or similar tool. Do not use on MainNet! 6 | For example, you can use ganache with the mnemonic "moment reform peace alter nominee you label idle license organ 7 | youth good", and in secure mode with --secure, and these tests should pass. 8 | 9 | To run these tests against your own deployed contracts, make sure you change the contract addresses in 10 | Devise/base.py to match what you deployed with truffle. 11 | 12 | :copyright: © 2018 Pit.AI 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | 16 | import pytest 17 | from pytest import raises 18 | 19 | from devise.clients.token import DeviseToken 20 | from .utils import evm_snapshot, evm_revert, TEST_KEYS 21 | 22 | 23 | class TestToken(object): 24 | @pytest.fixture(scope="function", autouse=True) 25 | def setup_method(self, token_client): 26 | self.token_client = token_client 27 | self.snapshot_id = evm_snapshot(token_client) 28 | 29 | def teardown_method(self, method): 30 | self.snapshot_id = evm_revert(self.snapshot_id, self.token_client) 31 | 32 | def test_total_supply(self, token_client): 33 | assert token_client.total_supply == 1e9 34 | 35 | def test_balance_of(self, token_client): 36 | assert token_client.balance_of(token_client.w3.eth.accounts[2]) == 1e9 37 | 38 | def test_cap(self, token_client): 39 | assert token_client.cap == 10e9 40 | 41 | def test_approve_allowance(self, token_client): 42 | approved_address = token_client.w3.eth.accounts[1] 43 | token_client.approve(approved_address, 12345) 44 | assert token_client.allowance(token_client.address, approved_address) == 12345 45 | 46 | token_client.approve(approved_address, 0) 47 | assert token_client.allowance(token_client.address, approved_address) == 0 48 | 49 | def test_transfer(self, token_wallet_client, token_client): 50 | # Start with some spending money from the token wallet 51 | assert token_client.balance_of(token_client.address) == 0 52 | token_wallet_client.transfer(token_client.address, 1) 53 | assert token_client.balance_of(token_client.address) == 1 54 | 55 | # Send using the regular account 56 | recipient_address = token_client.w3.eth.accounts[1] 57 | prev_bal = token_client.balance_of(recipient_address) 58 | token_client.transfer(recipient_address, 1) 59 | assert token_client.balance_of(recipient_address) == prev_bal + 1 60 | 61 | # We don't have enough funds for this transfer, should raise an error 62 | with raises(AssertionError): 63 | token_client.transfer(recipient_address, 1) 64 | 65 | def test_transfer_from(self, token_wallet_client, token_client): 66 | # Start with some spending money from the token wallet 67 | assert token_client.balance_of(token_client.address) == 0 68 | token_wallet_client.transfer(token_client.address, 12345) 69 | assert token_client.balance_of(token_client.address) == 12345 70 | 71 | approved_address = token_client.w3.eth.accounts[1] 72 | token_client.approve(approved_address, 12345) 73 | approved_client = DeviseToken(private_key=TEST_KEYS[1]) 74 | approved_client.transfer_from(token_client.address, approved_address, 12345) 75 | assert token_client.balance_of(approved_address) == 12345 76 | assert token_client.allowance(token_client.address, approved_address) == 0 77 | 78 | # Reached allowance, should fail 79 | with raises(Exception): 80 | approved_client.transfer_from(token_client.address, approved_address, 1) 81 | 82 | def test_increase_approval(self, token_client): 83 | approved_address = token_client.w3.eth.accounts[1] 84 | token_client.approve(approved_address, 12345) 85 | assert token_client.allowance(token_client.address, approved_address) == 12345 86 | token_client.increase_approval(approved_address, 1) 87 | assert token_client.allowance(token_client.address, approved_address) == 12346 88 | 89 | def test_decrease_approval(self, token_client): 90 | approved_address = token_client.w3.eth.accounts[1] 91 | token_client.approve(approved_address, 12345) 92 | assert token_client.allowance(token_client.address, approved_address) == 12345 93 | token_client.decrease_approval(approved_address, 1) 94 | assert token_client.allowance(token_client.address, approved_address) == 12344 95 | token_client.decrease_approval(approved_address, 150000) 96 | assert token_client.allowance(token_client.address, approved_address) == 0 97 | 98 | def test_reset_approval(self, token_client, token_wallet_client): 99 | # Start with some spending money from the token wallet 100 | assert token_client.balance_of(token_client.address) == 0 101 | token_wallet_client.transfer(token_client.address, 12345 * 2) 102 | assert token_client.balance_of(token_client.address) == 24690 103 | 104 | # Approve a client to spend up to 12345 105 | approved_address = token_client.w3.eth.accounts[1] 106 | token_client.approve(approved_address, 12345) 107 | assert token_client.allowance(token_client.address, approved_address) == 12345 108 | 109 | # Client spends some money, allowance goes down 110 | client = DeviseToken(private_key=TEST_KEYS[1]) 111 | client.transfer_from(token_client.address, client.address, 12340) 112 | assert token_client.allowance(token_client.address, approved_address) == 5 113 | 114 | # client has insufficient allowance 115 | with raises(AssertionError): 116 | client.transfer_from(token_client.address, client.address, 10) 117 | 118 | # reset client allowance 119 | token_client.approve(approved_address, 12345) 120 | assert token_client.allowance(token_client.address, approved_address) == 12345 121 | client.transfer_from(token_client.address, client.address, 100) 122 | assert token_client.allowance(token_client.address, approved_address) == 12245 123 | -------------------------------------------------------------------------------- /python/tests/test_token_owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Token owner tests 4 | ~~~~~~~~~ 5 | These are the token tests. These assume you are running ganache or similar tool. Do not use on MainNet! 6 | For example, you can use ganache with the mnemonic "moment reform peace alter nominee you label idle license organ 7 | youth good", and in secure mode with --secure, and these tests should pass. 8 | 9 | To run these tests against your own deployed contracts, make sure you change the contract addresses in 10 | Devise/base.py to match what you deployed with truffle. 11 | 12 | :copyright: © 2018 Pit.AI 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | 16 | import pytest 17 | from pytest import raises 18 | 19 | from devise.owner.token_owner import DeviseTokenOwner 20 | from .utils import evm_snapshot, evm_revert, TEST_KEYS 21 | 22 | 23 | class TestTokenOwner(object): 24 | @pytest.fixture(scope="function", autouse=True) 25 | def setup_method(self, owner_client, client): 26 | self.client = client 27 | _ = owner_client 28 | self.snapshot_id = evm_snapshot(client) 29 | 30 | def teardown_method(self, method): 31 | self.snapshot_id = evm_revert(self.snapshot_id, self.client) 32 | 33 | def test_mint(self, token_owner_client): 34 | token_wallet_account = token_owner_client.w3.eth.accounts[0] 35 | # Can't mint more than cap 36 | with raises(AssertionError): 37 | assert token_owner_client.mint(token_wallet_account, token_owner_client.cap + 1) 38 | 39 | # Minting credits the recipient address with the right amount 40 | assert token_owner_client.total_supply == 1e9 41 | token_owner_client.mint(token_wallet_account, 1000) 42 | assert token_owner_client.total_supply == 1e9 + 1000 43 | assert token_owner_client.balance_of(token_owner_client.address) == 0 44 | assert token_owner_client.balance_of(token_wallet_account) == 1000 45 | 46 | def test_add_minter(self, token_owner_client, token_client): 47 | with raises(ValueError): 48 | DeviseTokenOwner(account=token_client.address, password='password').mint(token_client.address, 1000) 49 | 50 | token_owner_client.add_minter(token_client.address) 51 | DeviseTokenOwner(private_key=TEST_KEYS[1]).mint(token_client.address, 1000) 52 | assert token_client.balance_of(token_client.address) == 1000 53 | 54 | def test_remove_minter(self, token_owner_client, token_client): 55 | token_owner_client.add_minter(token_client.address) 56 | DeviseTokenOwner(private_key=TEST_KEYS[1]).mint(token_client.address, 1000) 57 | assert token_client.balance_of(token_client.address) == 1000 58 | 59 | token_owner_client.remove_minter(token_client.address) 60 | with raises(ValueError): 61 | DeviseTokenOwner(account=token_client.address, password='password').mint(token_client.address, 1000) 62 | 63 | def test_get_minters(self, token_owner_client, token_client): 64 | token_owner_client.add_minter(token_client.address) 65 | minters = token_owner_client.get_minters() 66 | assert len(minters) == 2 67 | assert minters[0] == token_owner_client.address 68 | -------------------------------------------------------------------------------- /python/tests/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Test utilities to interact with ganache/testrpc 4 | """ 5 | # Ganache test private keys 6 | TEST_KEYS = [ 7 | '8d377499433184695c672b3b970dc1e2ef50ae5ff50052773d7dffa194388b36', 8 | '52c006688764c10edc04880a7cba1a1a51cfe2baff22469ee8f8d89d5c49a953', 9 | '4d3ed2d4d476ad5a9f4c780daea028dd83f01bfba3e33484150c7341ba448d41', 10 | '2e3a5c9de39b817f5f51a1efd5a9d8272cfc11f9a5c1f7fd657b8a7ae28e3643', 11 | 'e2e9e2b711f219066121699ad7e166e1a62073c59f3f4dcae512f8877408d1c3', 12 | 'c5b7e45ba600324868a0c86a567b902dc35f0958ca46fb86dcaf352f12e6d913' 13 | ] 14 | 15 | 16 | def time_travel(seconds, client): 17 | """Moves time forward on the test blockchain""" 18 | client.w3.manager.request_blocking('evm_increaseTime', [seconds]) 19 | client.w3.manager.request_blocking('evm_mine', []) 20 | 21 | 22 | def evm_snapshot(client): 23 | """Takes a snapshot of the current state of the blockchain""" 24 | return client.w3.manager.request_blocking('evm_snapshot', []) 25 | 26 | 27 | def evm_revert(snapshot_id, client): 28 | """Reverts the test blockchain to a saved snapshot""" 29 | return client.w3.manager.request_blocking('evm_revert', [snapshot_id]) 30 | -------------------------------------------------------------------------------- /solidity/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 6545, 3 | testrpcOptions: '-p 6545 -u 0x54fd80d6ae7584d8e9a19fe1df43f04e5282cc43 -e 1000000 -a 20 ', 4 | testCommand: 'truffle test', 5 | norpc: false, 6 | copyPackages: ['openzeppelin-solidity'] 7 | }; 8 | -------------------------------------------------------------------------------- /solidity/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /solidity/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "double" 10 | ], 11 | "indentation": [ 12 | "error", 13 | 4 14 | ], 15 | "security/no-inline-assembly": [ 16 | "warning" 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /solidity/contracts/AccessControlImplStorage.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./DateTime.sol"; 5 | import "./AccountingImpl.sol"; 6 | import "./AuctionImpl.sol"; 7 | import "./LeptonStorage.sol"; 8 | import "./AccessControlStorage.sol"; 9 | import "./AuctionStorage.sol"; 10 | 11 | 12 | /** 13 | * @title AccessControlImplStorage 14 | * @dev This is the parent storage interface for the AccessControlProxy and AccessControl implementation contracts. 15 | */ 16 | contract AccessControlImplStorage { 17 | // the maximum percentage of total seats that one client can rent 18 | uint internal maxSeatPercentage = 100; 19 | uint internal maxSeatMultiple = 100 / maxSeatPercentage; 20 | uint8 internal usefulnessDecimals = 6; 21 | uint32 internal usefulnessBaseline = uint32(10 ** uint256(usefulnessDecimals)); 22 | // minimum price per bit, 1,000 DVZ, 6 decimals 23 | uint public minimumPricePerBit = 10 ** 3 * 10 ** 6; 24 | // total number of seats that can be rented in any lease term 25 | uint8 public totalSeats = 100; 26 | 27 | // The storage contract containing renters and prices 28 | DateTime public dateUtils; 29 | AccessControlStorage public acStorage; 30 | AuctionStorage public auctionStorage; 31 | LeptonStorage public leptonStorage; 32 | // auction and accounting contracts 33 | Auction public auction; 34 | Accounting public accounting; 35 | } 36 | -------------------------------------------------------------------------------- /solidity/contracts/AccessControlProxy.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./OwnedUpgradeabilityProxy.sol"; 5 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 6 | import "./AccessControlImplStorage.sol"; 7 | 8 | 9 | /** 10 | * @title AccessControlProxy 11 | * @dev entry point for all access control logic. 12 | * This proxy allows us to upgrade the access control logic through an upgradeTo method. 13 | */ 14 | contract AccessControlProxy is OwnedUpgradeabilityProxy, AccessControlImplStorage, Ownable { 15 | constructor(DateTime dateTime, LeptonStorage leptonStorage_, AccessControlStorage acStorage_, AuctionStorage auctionStorage_) public { 16 | dateUtils = dateTime; 17 | acStorage = acStorage_; 18 | auctionStorage = auctionStorage_; 19 | leptonStorage = leptonStorage_; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solidity/contracts/AccessControlStorage.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./AuthorizedOnly.sol"; 5 | 6 | 7 | /** 8 | * @title AccessControlStorage 9 | * @dev Standalone contract containing the access control state, including current renters, seat allocations, lease 10 | * terms, and price history per lease term. 11 | */ 12 | contract AccessControlStorage is AuthorizedOnly { 13 | struct Price { 14 | uint pricePerBitOfIU; 15 | uint totalIncrementalUsefulness; 16 | uint leaseTerm; 17 | } 18 | 19 | // current renter status mapping 20 | mapping(address => uint) internal rentersIndex; 21 | address[] internal renters; 22 | // a mapping from a client to the number of seats currently allocated 23 | mapping(address => uint8) internal currentTermSeats; 24 | // currently available number of seats 25 | uint8 public seatsAvailable = 100; 26 | // the current lease term index (starting from the genesis month/year) 27 | uint internal constant GENESIS_YEAR = 2018; 28 | uint internal constant GENESIS_MONTH = 1; 29 | uint public leaseTerm = 0; 30 | // a mapping of leaseTerms to price structs 31 | mapping(uint => Price) internal prices; 32 | 33 | constructor() public { 34 | owner = msg.sender; 35 | seatsAvailable = 100; 36 | } 37 | 38 | function isRenter(address client) public view returns (bool) { 39 | return rentersIndex[client] != 0; 40 | } 41 | 42 | function getRenter(uint index) public view returns (address) { 43 | return renters[index]; 44 | } 45 | 46 | function getNumberOfRenters() public view returns (uint) { 47 | return renters.length; 48 | } 49 | 50 | function getCurrentTermSeats(address client) public view returns (uint8) { 51 | return currentTermSeats[client]; 52 | } 53 | 54 | function getSeatsAvailable() public view returns (uint8) { 55 | return seatsAvailable; 56 | } 57 | 58 | function setCurrentTermSeats(address client, uint8 seats) public onlyAuthorized { 59 | seatsAvailable += getCurrentTermSeats(client); 60 | if (seats == 0) 61 | removeRenter(client); 62 | else { 63 | seatsAvailable -= seats; 64 | addRenter(client, seats); 65 | } 66 | } 67 | 68 | function getAllRenters() public view returns (address[]) { 69 | return renters; 70 | } 71 | 72 | /// @dev returns the price structure for the given lease term index 73 | function getPriceForTerm(uint leaseTerm_) public view returns (uint price, uint totalIncrementalUsefulness) { 74 | return (prices[leaseTerm_].pricePerBitOfIU, prices[leaseTerm_].totalIncrementalUsefulness); 75 | } 76 | 77 | function getCurrentLeaseTerm() public view returns (uint) { 78 | return leaseTerm; 79 | } 80 | 81 | function setCurrentLeaseTerm(uint leaseTerm_) public onlyAuthorized { 82 | leaseTerm = leaseTerm_; 83 | } 84 | 85 | function getPriceCurrentTerm() public view returns (uint price, uint totalIncrementalUsefulness) { 86 | return getPriceForTerm(leaseTerm); 87 | } 88 | 89 | function getPriceNextTerm() public view returns (uint price, uint totalIncrementalUsefulness) { 90 | return getPriceForTerm(leaseTerm + 1); 91 | } 92 | 93 | function setPriceForTerm(uint leaseTerm_, uint pricePerBit, uint totalIncrementalUsefulness) public onlyAuthorized { 94 | prices[leaseTerm_].pricePerBitOfIU = pricePerBit; 95 | prices[leaseTerm_].totalIncrementalUsefulness = totalIncrementalUsefulness; 96 | } 97 | 98 | /// @dev calculates the index of the current lease term relative to genesis month and year 99 | function calculateLeaseTerm(uint _year, uint _month) public pure returns (uint) { 100 | return (_year - GENESIS_YEAR) * 12 + _month - GENESIS_MONTH; 101 | } 102 | 103 | function addRenter(address client, uint8 seats) internal { 104 | if (rentersIndex[client] == 0) { 105 | renters.push(client); 106 | rentersIndex[client] = renters.length; 107 | } 108 | currentTermSeats[client] = seats; 109 | } 110 | 111 | function removeRenter(address client) internal { 112 | if (rentersIndex[client] != 0) { 113 | uint index = rentersIndex[client] - 1; 114 | if (renters.length > 1) { 115 | // move last renter into this renter's index and update its index 116 | renters[index] = renters[renters.length - 1]; 117 | rentersIndex[renters[index]] = index + 1; 118 | // recover gas 119 | delete renters[renters.length - 1]; 120 | } 121 | 122 | renters.length = renters.length - 1; 123 | rentersIndex[client] = 0; 124 | currentTermSeats[client] = 0; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /solidity/contracts/AccountingImplStorage.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./DeviseToken.sol"; 5 | import "./AccountingStorage.sol"; 6 | 7 | 8 | /** 9 | * @title AccountingImplStorage 10 | * @dev Parent storage class for AccountingProxy and AccountingImpl. 11 | */ 12 | contract AccountingImplStorage { 13 | DeviseToken internal token; 14 | AccountingStorage public accountingStorage; 15 | 16 | address public escrowWallet; 17 | address public revenueWallet; 18 | uint internal powerUserClubFee = 0; 19 | uint internal historicalDataFee = 0; 20 | uint internal constant INIT_POWER_USER_MIN = 0; 21 | uint internal powerUserMinimum = INIT_POWER_USER_MIN; 22 | } 23 | -------------------------------------------------------------------------------- /solidity/contracts/AccountingProxy.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./OwnedUpgradeabilityProxy.sol"; 5 | import "./DeviseToken.sol"; 6 | import "./AccountingStorage.sol"; 7 | import "./AccountingStorage.sol"; 8 | import "./AccountingImplStorage.sol"; 9 | 10 | 11 | /** 12 | * @title AccountingProxy 13 | * @dev entry point for all accounting logic. 14 | * This proxy allows us to upgrade the accounting logic through an upgradeTo method. 15 | */ 16 | contract AccountingProxy is OwnedUpgradeabilityProxy, AccountingImplStorage, Ownable { 17 | /// @dev proxy constructor, takes a token and storage contract address 18 | constructor(DeviseToken _token, AccountingStorage _accountingStorage) public { 19 | setUpgradeabilityOwner(msg.sender); 20 | owner = msg.sender; 21 | token = _token; 22 | accountingStorage = _accountingStorage; 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /solidity/contracts/AccountingStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "./AuthorizedOnly.sol"; 4 | 5 | 6 | /** 7 | * @title AccountingStorage 8 | * @dev Standalone storage contract containing the accounting states, including client lists, and escrow balances. 9 | */ 10 | contract AccountingStorage is AuthorizedOnly { 11 | struct Allowance { 12 | uint balance; 13 | uint leaseTermPaid; 14 | bool isPowerUser; 15 | bool canAccessHistoricalData; 16 | } 17 | 18 | struct Client { 19 | bool isClient; 20 | address beneficiary; 21 | Allowance allowance; 22 | } 23 | 24 | // a mapping of client addresses to client structs 25 | mapping(address => Client) internal clients; 26 | // the list of client addresses 27 | address[] internal clientsArray; 28 | 29 | /// @dev adds a new client to the list of clients and mapping 30 | function addClient(address client) public onlyAuthorized { 31 | clientsArray.push(client); 32 | clients[client].isClient = true; 33 | } 34 | 35 | /// @dev checks if an address is a client 36 | function isClient(address client) public view returns (bool) { 37 | return clients[client].isClient; 38 | } 39 | 40 | /// @dev returns a list of all client addresses 41 | function getClients() public view returns (address[]) { 42 | return clientsArray; 43 | } 44 | 45 | /// @dev gets the client address at the specified index 46 | function getClient(uint index) public view returns (address) { 47 | return clientsArray[index]; 48 | } 49 | 50 | /// @dev gets the number of client addresses int the clients array 51 | function getNumberOfClients() public view returns (uint) { 52 | return clientsArray.length; 53 | } 54 | 55 | /// @dev gets the escrow balance of a client by address 56 | function getAllowance(address client) public view returns (uint allowance) { 57 | return clients[client].allowance.balance; 58 | } 59 | 60 | /// @dev sets the escrow balance of a client by address 61 | function setAllowance(address client, uint balance) public onlyAuthorized { 62 | clients[client].allowance.balance = balance; 63 | } 64 | 65 | /// @dev gets the last lease term paid by a client by address 66 | function getLastLeaseTermPaid(address client) public view returns (uint) { 67 | return clients[client].allowance.leaseTermPaid; 68 | } 69 | 70 | /// @dev sets the last lease term paid by a client by address 71 | function setLastLeaseTermPaid(address client, uint leaseTerm) public onlyAuthorized { 72 | clients[client].allowance.leaseTermPaid = leaseTerm; 73 | } 74 | 75 | /// @dev gets the beneficiary address of a client by address 76 | function getBeneficiary(address client) public view returns (address) { 77 | return clients[client].beneficiary; 78 | } 79 | 80 | /// @dev sets the beneficiary address of a client by address 81 | function setBeneficiary(address client, address beneficiary) public onlyAuthorized { 82 | clients[client].beneficiary = beneficiary; 83 | } 84 | 85 | /// @dev gets the power user status of a client by address 86 | function isPowerUser(address client) public view returns (bool) { 87 | return clients[client].allowance.isPowerUser; 88 | } 89 | 90 | /// @dev sets the power user status of a client by address 91 | function setPowerUser(address client, bool status) public onlyAuthorized { 92 | clients[client].allowance.isPowerUser = status; 93 | } 94 | 95 | /// @dev gets the historical data access status of a client by address 96 | function canAccessHistoricalData(address client) public view returns (bool) { 97 | return clients[client].allowance.canAccessHistoricalData; 98 | } 99 | 100 | /// @dev sets the historical data access status of a client by address 101 | function setCanAccessHistoricalData(address client, bool status) public onlyAuthorized { 102 | clients[client].allowance.canAccessHistoricalData = status; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /solidity/contracts/AuctionImpl.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 6 | import "./AuthorizedOnly.sol"; 7 | 8 | 9 | /** 10 | * @title Auction 11 | * @dev auction logic. 12 | * This contract calculates the auction price and seats given arrays of bids. 13 | */ 14 | contract Auction is Ownable, AuthorizedOnly { 15 | using SafeMath for uint; 16 | 17 | /// returns the auction price corresponding to the current state of the bids tree and current client escrow balances 18 | function calculateAuctionPrice(uint8[] bidderSeats, uint[] bidderLimitPrices, uint8 totalSeats, uint minimumPricePerBit) public view returns (uint, uint8[]) { 19 | // revenue so far 20 | uint rev; 21 | // revenue including current bidder 22 | uint winningPricePerBit = minimumPricePerBit; 23 | uint8[] memory allocatedSeats = new uint8[](bidderSeats.length); 24 | // total seats allocated so far 25 | uint8 seatsRented = 0; 26 | for (uint x = 0; x < bidderSeats.length; x++) { 27 | uint8 seats = bidderSeats[x]; 28 | uint pricePerBit = bidderLimitPrices[x]; 29 | 30 | // this bidder has enough to cover her bid, see if setting the price here maximizes revenue 31 | uint8 seatsAssignable = seatsRented + seats > totalSeats ? totalSeats - seatsRented : seats; 32 | if (seatsAssignable == 0 || pricePerBit * (seatsRented + seatsAssignable) < rev) { 33 | return (winningPricePerBit, allocatedSeats); 34 | } 35 | // allocate seats 36 | allocatedSeats[x] = seatsAssignable; 37 | seatsRented = seatsRented + seatsAssignable; 38 | rev = pricePerBit * seatsRented; 39 | // record new current best price 40 | winningPricePerBit = pricePerBit; 41 | } 42 | return (winningPricePerBit, allocatedSeats); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solidity/contracts/AuctionProxy.sol: -------------------------------------------------------------------------------- 1 | //noinspection WarnInspections 2 | pragma solidity ^0.4.23; 3 | 4 | import "./OwnedUpgradeabilityProxy.sol"; 5 | import "./AuctionStorage.sol"; 6 | import "./DeviseToken.sol"; 7 | 8 | 9 | /** 10 | * @title AuctionProxy 11 | * @dev entry point for auction logic. 12 | * This proxy allows us to upgrade the auction logic through an upgradeTo method. 13 | */ 14 | contract AuctionProxy is OwnedUpgradeabilityProxy, Ownable { 15 | constructor() public { 16 | setUpgradeabilityOwner(msg.sender); 17 | owner = msg.sender; 18 | } 19 | } -------------------------------------------------------------------------------- /solidity/contracts/AuctionStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./GroveLib.sol"; 4 | import "./AuthorizedOnly.sol"; 5 | 6 | 7 | /** 8 | * @title AuctionStorage 9 | * @dev Standalone contract containing the bidding tree which maintains the structure of bids in descending limit price 10 | * order 11 | */ 12 | contract AuctionStorage is AuthorizedOnly { 13 | GroveLib.Index internal index; 14 | 15 | function getIndexRoot() public view returns (address) { 16 | return index.root; 17 | } 18 | 19 | function getIndexMax() public view returns (address) { 20 | return index.maxId; 21 | } 22 | 23 | /// @dev Get the number of bids in the tree 24 | function getNodeCount() public view returns (uint) { 25 | return index.nodeCount; 26 | } 27 | 28 | function getPreviousNode(address curNode) public view returns (address) { 29 | return GroveLib.getPreviousNode(index, curNode); 30 | } 31 | 32 | function query(bytes2 operator, uint pricePerBit) public view returns (address) { 33 | return GroveLib.query(index, operator, pricePerBit); 34 | } 35 | 36 | function getNodeValue(address curNode) public view returns (address client, uint8 seats, uint limitPrice) { 37 | return GroveLib.getNodeValueBid(index, curNode); 38 | } 39 | 40 | function getNodeValueSeats(address curNode) public view returns (uint8) { 41 | address client; 42 | uint8 seats; 43 | uint pricePerBit; 44 | (client, seats, pricePerBit) = GroveLib.getNodeValueBid(index, curNode); 45 | return seats; 46 | } 47 | 48 | function getNodeValueAddress(address curNode) public view returns (address) { 49 | address client; 50 | uint8 seats; 51 | uint pricePerBit; 52 | (client, seats, pricePerBit) = GroveLib.getNodeValueBid(index, curNode); 53 | return client; 54 | } 55 | 56 | function insertBid(address client, uint8 seats, uint pricePerBitOfIU) public onlyAuthorized { 57 | GroveLib.insert(index, client, seats, pricePerBitOfIU); 58 | } 59 | 60 | function removeBid(address client) public onlyAuthorized { 61 | GroveLib.remove(index, client); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /solidity/contracts/AuditImpl.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "./AuditStorage.sol"; 4 | import "openzeppelin-solidity/contracts/ownership/rbac/RBAC.sol"; 5 | 6 | 7 | contract AuditImpl is AuditStorage, RBAC { 8 | string public constant ROLE_AUDIT_UPDATER = "audit-updater"; 9 | address public auditUpdater; 10 | 11 | modifier onlyOwner() { 12 | if (msg.sender != owner) revert(); 13 | _; 14 | } 15 | 16 | modifier onlyAuditUpdater() { 17 | checkRole(msg.sender, ROLE_AUDIT_UPDATER); 18 | _; 19 | } 20 | 21 | /* Events for DApps to listen to */ 22 | event AuditableEventCreated(bytes20 indexed eventType, string eventRawString, bytes20 contentHash); 23 | 24 | /** 25 | * @dev adds a rate setter role to an address 26 | * @param addr address 27 | */ 28 | function addAuditUpdater(address addr) public onlyOwner { 29 | removeAuditUpdater(auditUpdater); 30 | if (!hasRole(addr, ROLE_AUDIT_UPDATER)) { 31 | addRole(addr, ROLE_AUDIT_UPDATER); 32 | auditUpdater = addr; 33 | } 34 | } 35 | 36 | /** 37 | * @dev removes the rate setter role from address 38 | * @param addr address 39 | */ 40 | function removeAuditUpdater(address addr) public onlyOwner { 41 | if (hasRole(addr, ROLE_AUDIT_UPDATER)) { 42 | removeRole(addr, ROLE_AUDIT_UPDATER); 43 | auditUpdater = 0x0; 44 | } 45 | } 46 | 47 | /** 48 | * @dev Emit an event that latest weights have been updated 49 | * @param contentHash The hash for the content of the latest weights file 50 | */ 51 | function createAuditableEvent(bytes20 eventTypeHash, string eventType, bytes20 contentHash) public onlyAuditUpdater { 52 | emit AuditableEventCreated(eventTypeHash, eventType, contentHash); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /solidity/contracts/AuditProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "./AuditStorage.sol"; 4 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import "./Proxy.sol"; 6 | 7 | 8 | contract AuditProxy is Proxy, AuditStorage { 9 | using SafeMath for uint256; 10 | 11 | modifier onlyOwner() { 12 | require(msg.sender == owner); 13 | _; 14 | } 15 | 16 | /// @dev This event will be emitted every time the implementation gets upgraded 17 | /// @param version representing the version number of the upgraded implementation 18 | /// @param implementation representing the address of the upgraded implementation 19 | event Upgraded(uint version, address indexed implementation); 20 | 21 | function AuditProxy() public { 22 | owner = msg.sender; 23 | } 24 | 25 | /// @dev Allows the owner to upgrade the current version of the proxy. 26 | /// @param implementation representing the address of the new implementation to be set. 27 | function upgradeTo(address implementation) public onlyOwner { 28 | require(_implementation != implementation); 29 | if (implVersions[implementation] == 0) { 30 | _highestVersion = _highestVersion.add(1); 31 | implVersions[implementation] = _highestVersion; 32 | } 33 | _implementation = implementation; 34 | implHistory.push(implementation); 35 | uint ver = implVersions[implementation]; 36 | Upgraded(ver, implementation); 37 | } 38 | 39 | /// @dev Gets the address of the current implementation 40 | /// @return address of the current implementation 41 | function implementation() public view returns (address) { 42 | return _implementation; 43 | } 44 | 45 | /// @dev Gets the version of the current implementation 46 | /// @return address of the current implementation 47 | function version() public view returns (uint) { 48 | return implVersions[_implementation]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /solidity/contracts/AuditStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract AuditStorage { 4 | // The highest version number among all implementations 5 | uint internal _highestVersion; 6 | 7 | // Address of the current implementation 8 | address internal _implementation; 9 | 10 | // A one-to-one mapping from implementation address to 11 | // a version number 12 | mapping(address => uint) internal implVersions; 13 | // A history of all implementations. It is possible to 14 | // have duplicate implementations as the same implementation 15 | // has been pointed to at different occasions 16 | address[] internal implHistory; 17 | 18 | address public owner; 19 | } 20 | -------------------------------------------------------------------------------- /solidity/contracts/AuthorizedOnly.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 4 | 5 | 6 | /** 7 | * @title AuthorizedOnly 8 | * @dev This class provides authorization mechanism for contracts in a generic manner. Each subclass of this contract 9 | * can authorize addresses as well as list who is authorized to access features protected by the onlyAuthorized modifier 10 | */ 11 | contract AuthorizedOnly is Ownable { 12 | mapping(address => uint) public authorized; 13 | address[] public authorizedAddresses; 14 | 15 | modifier onlyAuthorized { 16 | if (!isAuthorized(msg.sender)) revert(); 17 | _; 18 | } 19 | 20 | function authorize(address newAddress) public onlyOwner { 21 | if (!isAuthorized(newAddress)) { 22 | authorizedAddresses.push(newAddress); 23 | authorized[newAddress] = authorizedAddresses.length; 24 | } 25 | } 26 | 27 | function unauthorize(address oldAddress) public onlyOwner { 28 | if (isAuthorized(oldAddress)) { 29 | uint index = authorized[oldAddress] - 1; 30 | // remove from array 31 | if (authorizedAddresses.length > 1) { 32 | authorizedAddresses[index] = authorizedAddresses[authorizedAddresses.length - 1]; 33 | // get some gas back 34 | delete (authorizedAddresses[authorizedAddresses.length - 1]); 35 | } 36 | authorizedAddresses.length--; 37 | // remove from mapping 38 | delete authorized[oldAddress]; 39 | assert(!isAuthorized(oldAddress)); 40 | } 41 | } 42 | 43 | function isAuthorized(address checkAddress) public view returns (bool) { 44 | return authorized[checkAddress] > 0; 45 | } 46 | } -------------------------------------------------------------------------------- /solidity/contracts/CappedToken.sol: -------------------------------------------------------------------------------- 1 | // This contract is adapted from CappedToken by OpenZeppelin 2 | pragma solidity ^0.4.19; 3 | 4 | import "openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; 5 | 6 | 7 | /** 8 | * @title Capped token 9 | * @dev Mintable token with a token cap. 10 | */ 11 | contract CappedToken is MintableToken { 12 | 13 | uint256 public cap; 14 | 15 | constructor(uint256 _cap) public { 16 | require(_cap > 0); 17 | cap = _cap; 18 | } 19 | 20 | /** 21 | * @dev Function to mint tokens 22 | * @param _to The address that will receive the minted tokens. 23 | * @param _amount The amount of tokens to mint. 24 | * @return A boolean that indicates if the operation was successful. 25 | */ 26 | function mint( 27 | address _to, 28 | uint256 _amount 29 | ) 30 | hasMintPermission 31 | canMint 32 | public 33 | returns (bool) 34 | { 35 | require(totalSupply_.add(_amount) <= cap); 36 | 37 | return super.mint(_to, _amount); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /solidity/contracts/DateTime.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | contract DateTime { 4 | /* 5 | * Date and Time utilities for ethereum contracts 6 | * 7 | */ 8 | struct _DateTime { 9 | uint16 year; 10 | uint8 month; 11 | uint8 day; 12 | uint8 hour; 13 | uint8 minute; 14 | uint8 second; 15 | uint8 weekday; 16 | } 17 | 18 | uint constant DAY_IN_SECONDS = 86400; 19 | uint constant YEAR_IN_SECONDS = 31536000; 20 | uint constant LEAP_YEAR_IN_SECONDS = 31622400; 21 | 22 | uint constant HOUR_IN_SECONDS = 3600; 23 | uint constant MINUTE_IN_SECONDS = 60; 24 | 25 | uint16 constant ORIGIN_YEAR = 1970; 26 | 27 | function isLeapYear(uint16 year) public pure returns (bool) { 28 | if (year % 4 != 0) { 29 | return false; 30 | } 31 | if (year % 100 != 0) { 32 | return true; 33 | } 34 | if (year % 400 != 0) { 35 | return false; 36 | } 37 | return true; 38 | } 39 | 40 | function leapYearsBefore(uint year) public pure returns (uint) { 41 | uint leapYears = year - 1; 42 | return leapYears / 4 - year / 100 + year / 400; 43 | } 44 | 45 | function getDaysInMonth(uint8 month, uint16 year) public pure returns (uint8) { 46 | if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { 47 | return 31; 48 | } 49 | else if (month == 4 || month == 6 || month == 9 || month == 11) { 50 | return 30; 51 | } 52 | else if (isLeapYear(year)) { 53 | return 29; 54 | } 55 | else { 56 | return 28; 57 | } 58 | } 59 | 60 | function parseTimestamp(uint timestamp) internal pure returns (_DateTime dt) { 61 | uint secondsAccountedFor = 0; 62 | uint buf; 63 | uint8 i; 64 | 65 | // Year 66 | dt.year = getYear(timestamp); 67 | buf = leapYearsBefore(dt.year) - leapYearsBefore(ORIGIN_YEAR); 68 | 69 | secondsAccountedFor += LEAP_YEAR_IN_SECONDS * buf; 70 | secondsAccountedFor += YEAR_IN_SECONDS * (dt.year - ORIGIN_YEAR - buf); 71 | 72 | // Month 73 | uint secondsInMonth; 74 | for (i = 1; i <= 12; i++) { 75 | secondsInMonth = DAY_IN_SECONDS * getDaysInMonth(i, dt.year); 76 | if (secondsInMonth + secondsAccountedFor > timestamp) { 77 | dt.month = i; 78 | break; 79 | } 80 | secondsAccountedFor += secondsInMonth; 81 | } 82 | 83 | // Day 84 | for (i = 1; i <= getDaysInMonth(dt.month, dt.year); i++) { 85 | if (DAY_IN_SECONDS + secondsAccountedFor > timestamp) { 86 | dt.day = i; 87 | break; 88 | } 89 | secondsAccountedFor += DAY_IN_SECONDS; 90 | } 91 | 92 | // Hour 93 | dt.hour = getHour(timestamp); 94 | 95 | // Minute 96 | dt.minute = getMinute(timestamp); 97 | 98 | // Second 99 | dt.second = getSecond(timestamp); 100 | 101 | // Day of week. 102 | dt.weekday = getWeekday(timestamp); 103 | } 104 | 105 | function getYear(uint timestamp) public pure returns (uint16) { 106 | uint secondsAccountedFor = 0; 107 | uint16 year; 108 | uint numLeapYears; 109 | 110 | // Year 111 | year = uint16(ORIGIN_YEAR + timestamp / YEAR_IN_SECONDS); 112 | numLeapYears = leapYearsBefore(year) - leapYearsBefore(ORIGIN_YEAR); 113 | 114 | secondsAccountedFor += LEAP_YEAR_IN_SECONDS * numLeapYears; 115 | secondsAccountedFor += YEAR_IN_SECONDS * (year - ORIGIN_YEAR - numLeapYears); 116 | 117 | while (secondsAccountedFor > timestamp) { 118 | if (isLeapYear(uint16(year - 1))) { 119 | secondsAccountedFor -= LEAP_YEAR_IN_SECONDS; 120 | } 121 | else { 122 | secondsAccountedFor -= YEAR_IN_SECONDS; 123 | } 124 | year -= 1; 125 | } 126 | return year; 127 | } 128 | 129 | function getMonth(uint timestamp) public pure returns (uint8) { 130 | return parseTimestamp(timestamp).month; 131 | } 132 | 133 | function getDay(uint timestamp) public pure returns (uint8) { 134 | return parseTimestamp(timestamp).day; 135 | } 136 | 137 | function getHour(uint timestamp) public pure returns (uint8) { 138 | return uint8((timestamp / 60 / 60) % 24); 139 | } 140 | 141 | function getMinute(uint timestamp) public pure returns (uint8) { 142 | return uint8((timestamp / 60) % 60); 143 | } 144 | 145 | function getSecond(uint timestamp) public pure returns (uint8) { 146 | return uint8(timestamp % 60); 147 | } 148 | 149 | function getWeekday(uint timestamp) public pure returns (uint8) { 150 | return uint8((timestamp / DAY_IN_SECONDS + 4) % 7); 151 | } 152 | 153 | function toTimestamp(uint16 year, uint8 month, uint8 day) public pure returns (uint timestamp) { 154 | return toTimestamp(year, month, day, 0, 0, 0); 155 | } 156 | 157 | function toTimestamp(uint16 year, uint8 month, uint8 day, uint8 hour) public pure returns (uint timestamp) { 158 | return toTimestamp(year, month, day, hour, 0, 0); 159 | } 160 | 161 | function toTimestamp(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute) public pure returns (uint timestamp) { 162 | return toTimestamp(year, month, day, hour, minute, 0); 163 | } 164 | 165 | function toTimestamp(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute, uint8 second) public pure returns (uint timestamp) { 166 | uint16 i; 167 | 168 | // Year 169 | for (i = ORIGIN_YEAR; i < year; i++) { 170 | if (isLeapYear(i)) { 171 | timestamp += LEAP_YEAR_IN_SECONDS; 172 | } 173 | else { 174 | timestamp += YEAR_IN_SECONDS; 175 | } 176 | } 177 | 178 | // Month 179 | uint8[12] memory monthDayCounts; 180 | monthDayCounts[0] = 31; 181 | if (isLeapYear(year)) { 182 | monthDayCounts[1] = 29; 183 | } 184 | else { 185 | monthDayCounts[1] = 28; 186 | } 187 | monthDayCounts[2] = 31; 188 | monthDayCounts[3] = 30; 189 | monthDayCounts[4] = 31; 190 | monthDayCounts[5] = 30; 191 | monthDayCounts[6] = 31; 192 | monthDayCounts[7] = 31; 193 | monthDayCounts[8] = 30; 194 | monthDayCounts[9] = 31; 195 | monthDayCounts[10] = 30; 196 | monthDayCounts[11] = 31; 197 | 198 | for (i = 1; i < month; i++) { 199 | timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1]; 200 | } 201 | 202 | // Day 203 | timestamp += DAY_IN_SECONDS * (day - 1); 204 | 205 | // Hour 206 | timestamp += HOUR_IN_SECONDS * (hour); 207 | 208 | // Minute 209 | timestamp += MINUTE_IN_SECONDS * (minute); 210 | 211 | // Second 212 | timestamp += second; 213 | 214 | return timestamp; 215 | } 216 | } -------------------------------------------------------------------------------- /solidity/contracts/DeviseMiningImpl.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./DeviseMiningStorage.sol"; 4 | import "openzeppelin-solidity/contracts/ownership/rbac/RBAC.sol"; 5 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 6 | import "./DeviseRentalImpl.sol"; 7 | 8 | 9 | contract DeviseMiningImpl is DeviseMiningStorage, AuthorizedOnly, RBAC { 10 | using SafeMath for uint256; 11 | 12 | string public constant ROLE_MASTER_NODE = "master-node"; 13 | address[] public masterNodes; 14 | // mapping(bytes20 => uint256) public leptons; 15 | 16 | modifier onlyOwner() { 17 | if (msg.sender != owner) revert(); 18 | _; 19 | } 20 | 21 | modifier onlyMasterNodes() { 22 | checkRole(msg.sender, ROLE_MASTER_NODE); 23 | _; 24 | } 25 | 26 | event LeptonAdded(bytes20 s, uint iu); 27 | 28 | /// @notice Get the total incremental usefulness of the blockchain 29 | /// @return the total incremental usefulness of the blockchain 30 | function getTotalIncrementalUsefulness() public view returns (uint) { 31 | return permData.totalIncrementalUsefulness(); 32 | } 33 | 34 | /// @notice Add a lepton to the chain, to be called by the contract owners as leptons are mined and selected 35 | /// @param _lepton A sha1 lepton hash 36 | /// @param _prevLepton The previous sha1 lepton hash in the chain 37 | /// @param _incrementalUsefulness The incremental usefulness added by the lepton being added 38 | function addLepton(bytes20 _lepton, bytes20 _prevLepton, uint _incrementalUsefulness) public onlyAuthorized { 39 | permData.addLepton(_lepton, _prevLepton, _incrementalUsefulness); 40 | emit LeptonAdded(_lepton, _incrementalUsefulness); 41 | } 42 | 43 | /** 44 | * @dev adds the master node role to an address 45 | * @param addr address 46 | */ 47 | function addMasterNode(address addr) public onlyAuthorized { 48 | if (!hasRole(addr, ROLE_MASTER_NODE)) { 49 | addRole(addr, ROLE_MASTER_NODE); 50 | masterNodes.push(addr); 51 | } 52 | } 53 | 54 | /// Get all leptons 55 | /// @return bytes20[], uint[] 56 | function getAllLeptons() public view returns (bytes20[], uint[]) { 57 | uint numLeptons = permData.getNumberOfLeptons(); 58 | bytes20[] memory hashes = new bytes20[](numLeptons); 59 | uint[] memory ius = new uint[](numLeptons); 60 | for (uint x = 0; x < numLeptons; x++) { 61 | var (hash, iu) = permData.getLepton(x); 62 | hashes[x] = hash; 63 | ius[x] = iu; 64 | } 65 | return (hashes, ius); 66 | } 67 | 68 | /// @notice Get the current number of leptons 69 | function getNumberOfLeptons() public view returns (uint) { 70 | return permData.getNumberOfLeptons(); 71 | } 72 | 73 | /// @notice Get the lepton and incremental usefulness at the specified index 74 | /// @param index the index for which to return the lepton and incremental usefulness 75 | /// @return (string, string leptonHash, uint incremental_usefulness * 1e9) 76 | function getLepton(uint index) public view returns (bytes20, uint) { 77 | return permData.getLepton(index); 78 | } 79 | 80 | /** 81 | * @dev removes the master node role from address 82 | * @param addr address 83 | */ 84 | function removeMasterNode(address addr) public onlyAuthorized { 85 | if (hasRole(addr, ROLE_MASTER_NODE)) { 86 | removeRole(addr, ROLE_MASTER_NODE); 87 | removeMasterNodeByValue(addr); 88 | } 89 | } 90 | 91 | /** 92 | * @dev returns all current master nodes 93 | */ 94 | function getMasterNodes() public constant returns (address[]) { 95 | return masterNodes; 96 | } 97 | 98 | function isMasterNode(address addr) public view returns (bool) { 99 | return hasRole(addr, ROLE_MASTER_NODE); 100 | } 101 | 102 | /* 103 | * Start of internal functions 104 | */ 105 | /** 106 | * @dev removes a master node from the master nodes array 107 | */ 108 | function removeMasterNodeByValue(address addr) internal { 109 | for (uint i; i < masterNodes.length; i++) { 110 | if (masterNodes[i] == addr) { 111 | if (masterNodes.length > 1) { 112 | // copy last element into this address spot and shrink array 113 | masterNodes[i] = masterNodes[masterNodes.length - 1]; 114 | masterNodes.length--; 115 | } else 116 | masterNodes.length = 0; 117 | 118 | return; 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /solidity/contracts/DeviseMiningProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./DeviseMiningStorage.sol"; 4 | import "./LeptonStorage.sol"; 5 | import "./OwnedUpgradeabilityProxy.sol"; 6 | import "./AuthorizedOnly.sol"; 7 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 8 | 9 | 10 | contract DeviseMiningProxy is OwnedUpgradeabilityProxy, DeviseMiningStorage, AuthorizedOnly { 11 | using SafeMath for uint256; 12 | 13 | modifier onlyOwner() { 14 | require(msg.sender == owner); 15 | _; 16 | } 17 | 18 | /// @dev This event will be emitted every time the implementation gets upgraded 19 | /// @param version representing the version number of the upgraded implementation 20 | /// @param implementation representing the address of the upgraded implementation 21 | event Upgraded(uint version, address indexed implementation); 22 | 23 | function DeviseMiningProxy(LeptonStorage _permData) public { 24 | owner = msg.sender; 25 | permData = _permData; 26 | } 27 | 28 | /// @dev Allows the owner to upgrade the current version of the proxy. 29 | /// @param implementation representing the address of the new implementation to be set. 30 | function upgradeTo(address implementation) public onlyOwner { 31 | require(_implementation != implementation); 32 | if (implVersions[implementation] == 0) { 33 | _highestVersion = _highestVersion.add(1); 34 | implVersions[implementation] = _highestVersion; 35 | } 36 | _implementation = implementation; 37 | implHistory.push(implementation); 38 | uint ver = implVersions[implementation]; 39 | Upgraded(ver, implementation); 40 | } 41 | 42 | /// @dev Gets the address of the current implementation 43 | /// @return address of the current implementation 44 | function implementation() public view returns (address) { 45 | return _implementation; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /solidity/contracts/DeviseMiningStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./LeptonStorage.sol"; 4 | 5 | 6 | contract DeviseMiningStorage { 7 | // The highest version number among all implementations 8 | uint internal _highestVersion; 9 | 10 | // Address of the current implementation 11 | address internal _implementation; 12 | 13 | // A one-to-one mapping from implementation address to 14 | // a version number 15 | mapping(address => uint) internal implVersions; 16 | // A history of all implementations. It is possible to 17 | // have duplicate implementations as the same implementation 18 | // has been pointed to at different occasions 19 | address[] internal implHistory; 20 | 21 | address public owner; 22 | 23 | LeptonStorage internal permData; 24 | } 25 | -------------------------------------------------------------------------------- /solidity/contracts/DeviseRentalStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 4 | import "./DateTime.sol"; 5 | import "./AccessControlStorage.sol"; 6 | import "./DeviseToken.sol"; 7 | 8 | 9 | contract DeviseRentalStorage { 10 | // The highest version number among all implementations 11 | uint internal _highestVersion; 12 | 13 | // Address of the current implementation 14 | address internal _implementation; 15 | 16 | // A one-to-one mapping from implementation address to 17 | // a version number 18 | mapping(address => uint) internal implVersions; 19 | // A history of all implementations. It is possible to 20 | // have duplicate implementations as the same implementation 21 | // has been pointed to at different occasions 22 | address[] internal implHistory; 23 | 24 | address public owner; 25 | bool public paused = false; 26 | address public escrowWallet; 27 | address[] internal escrowHistory; 28 | address public revenueWallet; 29 | address[] internal revenueHistory; 30 | 31 | DeviseToken internal token; 32 | DateTime internal dateUtils; 33 | AccessControlStorage internal permData; 34 | } -------------------------------------------------------------------------------- /solidity/contracts/DeviseToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./RBACMintableToken.sol"; 4 | import "openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol"; 5 | import "./CappedToken.sol"; 6 | 7 | 8 | contract DeviseToken is CappedToken, BurnableToken, RBACMintableToken { 9 | string public name = "DEVISE"; 10 | string public symbol = "DVZ"; 11 | // The pricision is set to micro DVZ 12 | uint8 public decimals = 6; 13 | 14 | function DeviseToken(uint256 _cap) public 15 | CappedToken(_cap) { 16 | addMinter(owner); 17 | } 18 | 19 | /** 20 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 21 | * @param newOwner The address to transfer ownership to. 22 | */ 23 | function transferOwnership(address newOwner) public onlyOwner { 24 | removeMinter(owner); 25 | addMinter(newOwner); 26 | super.transferOwnership(newOwner); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /solidity/contracts/LeptonStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./AuthorizedOnly.sol"; 4 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | 6 | 7 | contract LeptonStorage is AuthorizedOnly { 8 | using SafeMath for uint256; 9 | 10 | struct LeptonPrice { 11 | bytes20 lepton; 12 | uint incrementalUsefulness; 13 | } 14 | 15 | uint public totalIncrementalUsefulness; 16 | mapping(bytes20 => uint256) public leptons; 17 | // an array of lepton prices to loop through 18 | // accessible from derived contract 19 | LeptonPrice[] internal leptonPrices; 20 | uint8 internal usefulnessDecimals = 6; 21 | uint32 internal usefulnessBaseline = uint32(10 ** uint256(usefulnessDecimals)); 22 | 23 | /// @notice Get the current number of leptons 24 | function getNumberOfLeptons() public view returns (uint) { 25 | return leptonPrices.length; 26 | } 27 | 28 | /// @notice Add a lepton to the chain, to be called by the contract owners as leptons are mined and selected 29 | /// @param _lepton A sha1 lepton hash 30 | /// @param _prevLepton The previous sha1 lepton hash in the chain 31 | /// @param _incrementalUsefulness The incremental usefulness added by the lepton being added 32 | function addLepton(bytes20 _lepton, bytes20 _prevLepton, uint _incrementalUsefulness) public onlyAuthorized { 33 | require(_incrementalUsefulness > 0); 34 | uint numLeptons = getNumberOfLeptons(); 35 | if (numLeptons > 0) { 36 | var (prevHash,) = getLepton(numLeptons - 1); 37 | if (prevHash != _prevLepton) 38 | revert("Previous lepton does not match the last lepton in the chain!"); 39 | } 40 | if (leptons[_lepton] != 0) 41 | revert("Duplicate lepton!"); 42 | 43 | _addLepton(_lepton, _incrementalUsefulness); 44 | leptons[_lepton] = getNumberOfLeptons(); 45 | } 46 | 47 | /// @notice Get the lepton and incremental usefulness at the specified index 48 | /// @param index the index for which to return the lepton and incremental usefulness 49 | /// @return (bytes20 leptonHash, uint incremental_usefulness * 1e9) 50 | function getLepton(uint index) public returns (bytes20, uint) { 51 | return (leptonPrices[index].lepton, leptonPrices[index].incrementalUsefulness); 52 | } 53 | 54 | // LeptonWarehouse related interfaces 55 | function _addLepton(bytes20 _lepton, uint _incrementalUsefulness) internal { 56 | addLeptonEx(_lepton, _incrementalUsefulness); 57 | totalIncrementalUsefulness = totalIncrementalUsefulness.add(_incrementalUsefulness); 58 | } 59 | 60 | function addLeptonEx(bytes20 _lepton, uint _incrementalUsefulness) internal { 61 | LeptonPrice memory spNew = LeptonPrice(_lepton, _incrementalUsefulness); 62 | leptonPrices.push(spNew); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /solidity/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solidity/contracts/OwnedUpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "./UpgradeabilityProxy.sol"; 4 | 5 | /** 6 | * @title OwnedUpgradeabilityProxy 7 | * @dev This contract combines an upgradeability proxy with basic authorization control functionality 8 | */ 9 | contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { 10 | 11 | /** 12 | * @dev This event will be emitted every time the implementation gets upgraded 13 | * @param version representing the version number of the upgraded implementation 14 | * @param implementation representing the address of the upgraded implementation 15 | */ 16 | event Upgraded(uint version, address indexed implementation); 17 | 18 | /** 19 | * @dev Event to show ownership has been transferred 20 | * @param previousOwner representing the address of the previous owner 21 | * @param newOwner representing the address of the new owner 22 | */ 23 | event ProxyOwnershipTransferred(address previousOwner, address newOwner); 24 | 25 | // Storage position of the owner of the contract 26 | bytes32 private constant proxyOwnerPosition = keccak256("io.devisechain.proxy.owner"); 27 | 28 | /** 29 | * @dev the constructor sets the original owner of the contract to the sender account. 30 | */ 31 | function OwnedUpgradeabilityProxy() public { 32 | setUpgradeabilityOwner(msg.sender); 33 | } 34 | 35 | /** 36 | * @dev Throws if called by any account other than the owner. 37 | */ 38 | modifier onlyProxyOwner() { 39 | require(msg.sender == proxyOwner()); 40 | _; 41 | } 42 | 43 | /** 44 | * @dev Tells the address of the owner 45 | * @return the address of the owner 46 | */ 47 | function proxyOwner() public view returns (address owner) { 48 | bytes32 position = proxyOwnerPosition; 49 | assembly { 50 | owner := sload(position) 51 | } 52 | } 53 | 54 | /** 55 | * @dev Sets the address of the owner 56 | */ 57 | function setUpgradeabilityOwner(address newProxyOwner) internal { 58 | bytes32 position = proxyOwnerPosition; 59 | assembly { 60 | sstore(position, newProxyOwner) 61 | } 62 | } 63 | 64 | /** 65 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 66 | * @param newOwner The address to transfer ownership to. 67 | */ 68 | function transferProxyOwnership(address newOwner) public onlyProxyOwner { 69 | require(newOwner != address(0)); 70 | emit ProxyOwnershipTransferred(proxyOwner(), newOwner); 71 | setUpgradeabilityOwner(newOwner); 72 | } 73 | 74 | /** 75 | * @dev Allows the proxy owner to upgrade the current version of the proxy. 76 | * @param implementationAddress representing the address of the new implementation to be set. 77 | */ 78 | function upgradeTo(address implementationAddress) public onlyProxyOwner { 79 | _upgradeTo(implementationAddress); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /solidity/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2018 Zeppelin-os Labs 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | pragma solidity ^0.4.19; 25 | 26 | 27 | /** 28 | * @title Proxy 29 | * @dev Gives the possibility to delegate any call to a foreign implementation. 30 | */ 31 | contract Proxy { 32 | 33 | /** 34 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 35 | * This function will return whatever the implementation call returns 36 | */ 37 | function() public payable { 38 | address _impl = implementation(); 39 | require(_impl != address(0)); 40 | 41 | assembly { 42 | // allocate output byte array, 0x40 has a free memory pointer 43 | let ptr := mload(0x40) 44 | // copy calldatasize bytes from calldata at position 0 to mem at position ptr 45 | // calldatasize: size of call data in bytes 46 | calldatacopy(ptr, 0, calldatasize) 47 | // call contract at address _impl with input mem at ptr with size calldatasize, 48 | // and output mem at 0 with size 0 49 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 50 | // returndatasize: size of last return data 51 | let size := returndatasize 52 | // copy size bytes from return data at position 0 to mem at position ptr 53 | returndatacopy(ptr, 0, size) 54 | 55 | switch result 56 | case 0 {revert(ptr, size)} 57 | default {return (ptr, size)} 58 | } 59 | } 60 | 61 | /** 62 | * @dev Tells the address of the implementation where every call will be delegated. 63 | * @return address of the implementation to which it will be delegated 64 | */ 65 | function implementation() public view returns (address); 66 | } 67 | -------------------------------------------------------------------------------- /solidity/contracts/RBACMintableToken.sol: -------------------------------------------------------------------------------- 1 | // This contract is adapted from RBACMintableToken by OpenZeppelin 2 | pragma solidity ^0.4.19; 3 | 4 | import "openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; 5 | import "openzeppelin-solidity/contracts/ownership/rbac/RBAC.sol"; 6 | 7 | 8 | /** 9 | * @title RBACMintableToken 10 | * @author Vittorio Minacori (@vittominacori) 11 | * @dev Mintable Token, with RBAC minter permissions 12 | */ 13 | contract RBACMintableToken is MintableToken, RBAC { 14 | /** 15 | * A constant role name for indicating minters. 16 | */ 17 | string public constant ROLE_MINTER = "minter"; 18 | address[] internal minters; 19 | 20 | /** 21 | * @dev override the Mintable token modifier to add role based logic 22 | */ 23 | modifier hasMintPermission() { 24 | checkRole(msg.sender, ROLE_MINTER); 25 | _; 26 | } 27 | 28 | /** 29 | * @dev add a minter role to an address 30 | * @param minter address 31 | */ 32 | function addMinter(address minter) onlyOwner public { 33 | if (!hasRole(minter, ROLE_MINTER)) 34 | minters.push(minter); 35 | addRole(minter, ROLE_MINTER); 36 | } 37 | 38 | /** 39 | * @dev remove a minter role from an address 40 | * @param minter address 41 | */ 42 | function removeMinter(address minter) onlyOwner public { 43 | removeRole(minter, ROLE_MINTER); 44 | removeMinterByValue(minter); 45 | } 46 | 47 | function getNumberOfMinters() onlyOwner public view returns (uint) { 48 | return minters.length; 49 | } 50 | 51 | function getMinter(uint _index) onlyOwner public view returns (address) { 52 | require(_index < minters.length); 53 | return minters[_index]; 54 | } 55 | 56 | function removeMinterByIndex(uint index) internal { 57 | require(minters.length > 0); 58 | if (minters.length > 1) { 59 | minters[index] = minters[minters.length - 1]; 60 | // recover gas 61 | delete (minters[minters.length - 1]); 62 | } 63 | minters.length--; 64 | } 65 | 66 | function removeMinterByValue(address _client) internal { 67 | for (uint i = 0; i < minters.length; i++) { 68 | if (minters[i] == _client) { 69 | removeMinterByIndex(i); 70 | break; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /solidity/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Proxy.sol"; 4 | 5 | /** 6 | * @title UpgradeabilityProxy 7 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 8 | */ 9 | contract UpgradeabilityProxy is Proxy { 10 | /** 11 | * @dev This event will be emitted every time the implementation gets upgraded 12 | * @param implementation representing the address of the upgraded implementation 13 | */ 14 | event Upgraded(address indexed implementation); 15 | 16 | // Storage position of the address of the current implementation 17 | bytes32 private constant implementationPosition = keccak256("io.devisechain.proxy.implementation"); 18 | 19 | /** 20 | * @dev Constructor function 21 | */ 22 | function UpgradeabilityProxy() public {} 23 | 24 | /** 25 | * @dev Tells the address of the current implementation 26 | * @return address of the current implementation 27 | */ 28 | function implementation() public view returns (address impl) { 29 | bytes32 position = implementationPosition; 30 | assembly { 31 | impl := sload(position) 32 | } 33 | } 34 | 35 | /** 36 | * @dev Sets the address of the current implementation 37 | * @param newImplementation address representing the new implementation to be set 38 | */ 39 | function setImplementation(address newImplementation) internal { 40 | bytes32 position = implementationPosition; 41 | assembly { 42 | sstore(position, newImplementation) 43 | } 44 | } 45 | 46 | /** 47 | * @dev Upgrades the implementation address 48 | * @param newImplementation representing the address of the new implementation to be set 49 | */ 50 | function _upgradeTo(address newImplementation) internal { 51 | address currentImplementation = implementation(); 52 | require(currentImplementation != newImplementation); 53 | setImplementation(newImplementation); 54 | emit Upgraded(newImplementation); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /solidity/contracts/test/AccessControlImplTimeTravel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./TimeTravel.sol"; 4 | import "../AccessControlImpl.sol"; 5 | 6 | 7 | contract AccessControlImplTimeTravel is AccessControl { 8 | TimeTravel internal timeTravel; 9 | 10 | function setTimeTravel(TimeTravel _timeTravel) public onlyOwner { 11 | timeTravel = _timeTravel; 12 | } 13 | 14 | function getCurrentDate() internal returns (uint _year, uint _month, uint _day) { 15 | uint tt = timeTravel.currentTimeStamp(); 16 | uint _timestamp = tt > block.timestamp ? tt : block.timestamp; 17 | uint year = dateUtils.getYear(_timestamp); 18 | uint month = dateUtils.getMonth(_timestamp); 19 | uint day = dateUtils.getDay(_timestamp); 20 | return (year, month, day); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /solidity/contracts/test/DeviseRentalImplTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "../DeviseRentalImpl.sol"; 4 | 5 | 6 | contract DeviseRentalImplTest is DeviseRentalImpl { 7 | modifier onlyTest() { 8 | if (msg.sender != owner) revert(); 9 | _; 10 | } 11 | 12 | function mockCurrentTotalUsefulness() public onlyTest { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /solidity/contracts/test/DeviseRentalImplTimeTravel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "../DeviseRentalImpl.sol"; 4 | import "./TimeTravel.sol"; 5 | 6 | 7 | contract DeviseRentalImplTimeTravel is DeviseRentalImpl { 8 | TimeTravel internal timeTravel; 9 | 10 | function setTimeTravel(TimeTravel _timeTravel) public onlyOwner { 11 | timeTravel = _timeTravel; 12 | } 13 | 14 | function _getCurrentDate() internal returns (uint _year, uint _month, uint _day) { 15 | uint tt = timeTravel.currentTimeStamp(); 16 | uint _timestamp = tt > block.timestamp ? tt : block.timestamp; 17 | uint year = dateUtils.getYear(_timestamp); 18 | uint month = dateUtils.getMonth(_timestamp); 19 | uint day = dateUtils.getDay(_timestamp); 20 | return (year, month, day); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /solidity/contracts/test/DeviseRentalImplV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "../DeviseRentalImpl.sol"; 4 | 5 | 6 | contract DeviseRentalImplV2 is DeviseRentalImpl { 7 | 8 | function provision(uint _amount) public { 9 | require(_amount > 0); 10 | super.provision(_amount - 2); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /solidity/contracts/test/DeviseRentalImplV3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "./DeviseRentalImplV2.sol"; 4 | 5 | 6 | contract DeviseRentalImplV3 is DeviseRentalImplV2 { 7 | using SafeMath for uint; 8 | 9 | uint internal _version; 10 | uint internal totalIncrementalUsefulness; 11 | string internal test; 12 | address masterNode; 13 | 14 | modifier onlyMaster() { 15 | if (masterNode == 0x0 || msg.sender != masterNode) revert(); 16 | _; 17 | } 18 | 19 | function getAllowance_v2() public returns (uint) { 20 | return getAllowance(); 21 | } 22 | 23 | function getTest() public returns (string) { 24 | return test; 25 | } 26 | 27 | function setVersion(uint _ver) public { 28 | _version = _ver; 29 | } 30 | 31 | function setMasterNode(address addr) public onlyOwner { 32 | leptonProxy.addMasterNode(addr); 33 | masterNode = addr; 34 | } 35 | 36 | function addLepton(bytes20 _lepton, bytes20 _prevLepton, uint _incrementalUsefulness) public onlyMaster { 37 | accessControl.updateGlobalState(); 38 | leptonProxy.addLepton(_lepton, _prevLepton, _incrementalUsefulness); 39 | } 40 | 41 | function getVersion() public view returns (uint) { 42 | return _version; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solidity/contracts/test/MintableTokenTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | import "openzeppelin-solidity/contracts/ownership/rbac/RBAC.sol"; 6 | 7 | 8 | /** 9 | * @title Mintable token 10 | * @dev Simple ERC20 Token example, with mintable token creation 11 | * @dev Issue: * https://github.com/OpenZeppelin/openzeppelin-solidity/issues/120 12 | * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol 13 | */ 14 | contract MintableToken is StandardToken, Ownable { 15 | event Mint(address indexed to, uint256 amount); 16 | event MintFinished(); 17 | 18 | bool public mintingFinished = false; 19 | 20 | 21 | modifier canMint() { 22 | require(!mintingFinished); 23 | _; 24 | } 25 | 26 | modifier hasMintPermission() { 27 | require(msg.sender == owner); 28 | _; 29 | } 30 | 31 | /** 32 | * @dev Function to mint tokens 33 | * @param _to The address that will receive the minted tokens. 34 | * @param _amount The amount of tokens to mint. 35 | * @return A boolean that indicates if the operation was successful. 36 | */ 37 | function mint( 38 | address _to, 39 | uint256 _amount 40 | ) 41 | hasMintPermission 42 | canMint 43 | public 44 | returns (bool) 45 | { 46 | totalSupply_ = totalSupply_.add(_amount); 47 | balances[_to] = balances[_to].add(_amount); 48 | emit Mint(_to, _amount); 49 | emit Transfer(address(0), _to, _amount); 50 | return true; 51 | } 52 | 53 | /** 54 | * @dev Function to stop minting new tokens. 55 | * @return True if the operation was successful. 56 | */ 57 | function finishMinting() onlyOwner canMint public returns (bool) { 58 | mintingFinished = true; 59 | emit MintFinished(); 60 | return true; 61 | } 62 | } 63 | 64 | /** 65 | * @title Capped token 66 | * @dev Mintable token with a token cap. 67 | */ 68 | contract CappedToken is MintableToken { 69 | 70 | uint256 public cap; 71 | 72 | constructor(uint256 _cap) public { 73 | require(_cap > 0); 74 | cap = _cap; 75 | } 76 | 77 | /** 78 | * @dev Function to mint tokens 79 | * @param _to The address that will receive the minted tokens. 80 | * @param _amount The amount of tokens to mint. 81 | * @return A boolean that indicates if the operation was successful. 82 | */ 83 | function mint( 84 | address _to, 85 | uint256 _amount 86 | ) 87 | hasMintPermission 88 | canMint 89 | public 90 | returns (bool) 91 | { 92 | require(totalSupply_.add(_amount) <= cap); 93 | 94 | return super.mint(_to, _amount); 95 | } 96 | 97 | } 98 | 99 | 100 | 101 | /** 102 | * @title RBACMintableToken 103 | * @author Vittorio Minacori (@vittominacori) 104 | * @dev Mintable Token, with RBAC minter permissions 105 | */ 106 | contract RBACMintableToken is MintableToken, RBAC { 107 | /** 108 | * A constant role name for indicating minters. 109 | */ 110 | string public constant ROLE_MINTER = "minter"; 111 | 112 | /** 113 | * @dev override the Mintable token modifier to add role based logic 114 | */ 115 | modifier hasMintPermission() { 116 | checkRole(msg.sender, ROLE_MINTER); 117 | _; 118 | } 119 | 120 | /** 121 | * @dev add a minter role to an address 122 | * @param minter address 123 | */ 124 | function addMinter(address minter) onlyOwner public { 125 | addRole(minter, ROLE_MINTER); 126 | } 127 | 128 | /** 129 | * @dev remove a minter role from an address 130 | * @param minter address 131 | */ 132 | function removeMinter(address minter) onlyOwner public { 133 | removeRole(minter, ROLE_MINTER); 134 | } 135 | } 136 | 137 | contract MintableTokenTest is CappedToken, RBACMintableToken { 138 | string public name = "DEVISE"; 139 | string public symbol = "DVZ"; 140 | uint8 public decimals = 18; 141 | 142 | function MintableTokenTest(uint256 _cap) public 143 | CappedToken(_cap) { 144 | addMinter(owner); 145 | } 146 | 147 | /** 148 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 149 | * @param newOwner The address to transfer ownership to. 150 | */ 151 | function transferOwnership(address newOwner) public onlyOwner { 152 | removeMinter(owner); 153 | addMinter(newOwner); 154 | super.transferOwnership(newOwner); 155 | } 156 | } 157 | 158 | 159 | contract MintableTokenTest1 is RBACMintableToken, CappedToken { 160 | string public name = "DEVISE"; 161 | string public symbol = "DVZ"; 162 | uint8 public decimals = 18; 163 | 164 | function MintableTokenTest1(uint256 _cap) public 165 | CappedToken(_cap) { 166 | addMinter(owner); 167 | } 168 | 169 | /** 170 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 171 | * @param newOwner The address to transfer ownership to. 172 | */ 173 | function transferOwnership(address newOwner) public onlyOwner { 174 | removeMinter(owner); 175 | addMinter(newOwner); 176 | super.transferOwnership(newOwner); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /solidity/contracts/test/MintableTokenTestBoth.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | import "openzeppelin-solidity/contracts/ownership/rbac/RBAC.sol"; 6 | 7 | 8 | /** 9 | * @title Mintable token 10 | * @dev Simple ERC20 Token example, with mintable token creation 11 | * @dev Issue: * https://github.com/OpenZeppelin/openzeppelin-solidity/issues/120 12 | * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol 13 | */ 14 | contract MintableToken is StandardToken, Ownable { 15 | event Mint(address indexed to, uint256 amount); 16 | event MintFinished(); 17 | 18 | bool public mintingFinished = false; 19 | 20 | 21 | modifier canMint() { 22 | require(!mintingFinished); 23 | _; 24 | } 25 | 26 | modifier hasMintPermission() { 27 | require(msg.sender == owner); 28 | _; 29 | } 30 | 31 | /** 32 | * @dev Function to mint tokens 33 | * @param _to The address that will receive the minted tokens. 34 | * @param _amount The amount of tokens to mint. 35 | * @return A boolean that indicates if the operation was successful. 36 | */ 37 | function mint( 38 | address _to, 39 | uint256 _amount 40 | ) 41 | hasMintPermission 42 | canMint 43 | public 44 | returns (bool) 45 | { 46 | totalSupply_ = totalSupply_.add(_amount); 47 | balances[_to] = balances[_to].add(_amount); 48 | emit Mint(_to, _amount); 49 | emit Transfer(address(0), _to, _amount); 50 | return true; 51 | } 52 | 53 | /** 54 | * @dev Function to stop minting new tokens. 55 | * @return True if the operation was successful. 56 | */ 57 | function finishMinting() onlyOwner canMint public returns (bool) { 58 | mintingFinished = true; 59 | emit MintFinished(); 60 | return true; 61 | } 62 | } 63 | 64 | /** 65 | * @title Capped token 66 | * @dev Mintable token with a token cap. 67 | */ 68 | contract CappedToken is MintableToken { 69 | 70 | uint256 public cap; 71 | 72 | constructor(uint256 _cap) public { 73 | require(_cap > 0); 74 | cap = _cap; 75 | } 76 | 77 | /** 78 | * @dev Function to mint tokens 79 | * @param _to The address that will receive the minted tokens. 80 | * @param _amount The amount of tokens to mint. 81 | * @return A boolean that indicates if the operation was successful. 82 | */ 83 | function mint( 84 | address _to, 85 | uint256 _amount 86 | ) 87 | onlyOwner 88 | canMint 89 | public 90 | returns (bool) 91 | { 92 | require(totalSupply_.add(_amount) <= cap); 93 | 94 | return super.mint(_to, _amount); 95 | } 96 | 97 | } 98 | 99 | 100 | 101 | /** 102 | * @title RBACMintableToken 103 | * @author Vittorio Minacori (@vittominacori) 104 | * @dev Mintable Token, with RBAC minter permissions 105 | */ 106 | contract RBACMintableToken is MintableToken, RBAC { 107 | /** 108 | * A constant role name for indicating minters. 109 | */ 110 | string public constant ROLE_MINTER = "minter"; 111 | 112 | /** 113 | * @dev override the Mintable token modifier to add role based logic 114 | */ 115 | modifier hasMintPermission() { 116 | checkRole(msg.sender, ROLE_MINTER); 117 | _; 118 | } 119 | 120 | /** 121 | * @dev add a minter role to an address 122 | * @param minter address 123 | */ 124 | function addMinter(address minter) onlyOwner public { 125 | addRole(minter, ROLE_MINTER); 126 | } 127 | 128 | /** 129 | * @dev remove a minter role from an address 130 | * @param minter address 131 | */ 132 | function removeMinter(address minter) onlyOwner public { 133 | removeRole(minter, ROLE_MINTER); 134 | } 135 | 136 | /** 137 | * @dev Function to mint tokens 138 | * @param _to The address that will receive the minted tokens. 139 | * @param _amount The amount of tokens to mint. 140 | * @return A boolean that indicates if the operation was successful. 141 | */ 142 | function mint( 143 | address _to, 144 | uint256 _amount 145 | ) 146 | onlyOwner 147 | canMint 148 | public 149 | returns (bool) 150 | { 151 | return super.mint(_to, _amount); 152 | } 153 | } 154 | 155 | contract MintableTokenTestBoth is CappedToken, RBACMintableToken { 156 | string public name = "DEVISE"; 157 | string public symbol = "DVZ"; 158 | uint8 public decimals = 18; 159 | 160 | function MintableTokenTestBoth(uint256 _cap) public 161 | CappedToken(_cap) { 162 | addMinter(owner); 163 | } 164 | 165 | /** 166 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 167 | * @param newOwner The address to transfer ownership to. 168 | */ 169 | function transferOwnership(address newOwner) public onlyOwner { 170 | removeMinter(owner); 171 | addMinter(newOwner); 172 | super.transferOwnership(newOwner); 173 | } 174 | } 175 | 176 | 177 | contract MintableTokenTestBoth1 is RBACMintableToken, CappedToken { 178 | string public name = "DEVISE"; 179 | string public symbol = "DVZ"; 180 | uint8 public decimals = 18; 181 | 182 | function MintableTokenTestBoth1(uint256 _cap) public 183 | CappedToken(_cap) { 184 | addMinter(owner); 185 | } 186 | 187 | /** 188 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 189 | * @param newOwner The address to transfer ownership to. 190 | */ 191 | function transferOwnership(address newOwner) public onlyOwner { 192 | removeMinter(owner); 193 | addMinter(newOwner); 194 | super.transferOwnership(newOwner); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /solidity/contracts/test/TimeTravel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.19; 2 | 3 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 4 | 5 | 6 | contract TimeTravel { 7 | using SafeMath for uint256; 8 | 9 | uint256 public currentTimeStamp; 10 | address internal owner; 11 | 12 | modifier onlyOwner() { 13 | if (msg.sender != owner) revert(); 14 | _; 15 | } 16 | 17 | function TimeTravel() public { 18 | currentTimeStamp = block.timestamp; 19 | owner = msg.sender; 20 | } 21 | 22 | function timeTravelForward(uint256 sec) public onlyOwner { 23 | currentTimeStamp = currentTimeStamp.add(sec); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solidity/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("./Migrations.sol"); 2 | const TruffleConfig = require('../truffle'); 3 | 4 | module.exports = async function (deployer, network, accounts) { 5 | const config = TruffleConfig.networks[network]; 6 | if (config.network_id === 74824) { 7 | await web3.personal.unlockAccount(config.from, "pitai12345678", 6000); 8 | await web3.personal.unlockAccount(accounts[1], "pitai12345678", 6000); 9 | await web3.personal.unlockAccount(accounts[2], "pitai12345678", 6000); 10 | await web3.personal.unlockAccount(accounts[3], "pitai12345678", 6000); 11 | for (let i = 1; i < accounts.length; i++) { 12 | let bal = (await web3.eth.getBalance(accounts[i])).toNumber(); 13 | if (bal <= 20 * 10 ** 18) { 14 | await web3.eth.sendTransaction({from: config.from, to: accounts[i], value: web3.toWei(20, "ether")}); 15 | } 16 | } 17 | } 18 | deployer.deploy(Migrations); 19 | }; 20 | -------------------------------------------------------------------------------- /solidity/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const DeviseToken = artifacts.require("./DeviseToken"); 2 | const MintableTokenTest = artifacts.require("./test/MintableTokenTest"); 3 | 4 | module.exports = function (deployer, network, accounts) { 5 | const microDVZ = 10 ** 6; 6 | const billionDVZ = 10 ** 9; 7 | const pitai = accounts[0]; 8 | const tokenOwner = accounts[1]; 9 | const tokenWallet = accounts[2]; 10 | // total cap is 10 billion and the decimal is 6 11 | const cap = 10 * billionDVZ * microDVZ; 12 | 13 | deployer.deploy(DeviseToken, cap, {from: pitai}).then(async function () { 14 | const token = await DeviseToken.deployed(); 15 | await token.transferOwnership(tokenOwner, {from: pitai}); 16 | await deployer.deploy(MintableTokenTest, cap, {from: pitai}); 17 | const saleAmount = 1 * billionDVZ * microDVZ; 18 | await token.mint(tokenWallet, saleAmount, {from: tokenOwner}); 19 | }); 20 | }; -------------------------------------------------------------------------------- /solidity/migrations/3_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const DeviseRentalImpl = artifacts.require("./DeviseRentalImpl"); 2 | const AccountingStorage = artifacts.require("./AccountingStorage"); 3 | const AuctionStorage = artifacts.require("./AuctionStorage"); 4 | const AccountingProxy = artifacts.require("./AccountingProxy"); 5 | const AuctionProxy = artifacts.require("./AuctionProxy"); 6 | const AccountingImpl = artifacts.require("./Accounting"); 7 | const AuctionImpl = artifacts.require("./Auction"); 8 | const LeptonStorage = artifacts.require("./LeptonStorage"); 9 | const DeviseRentalProxy = artifacts.require("./DeviseRentalProxy"); 10 | const DeviseMiningProxy = artifacts.require("./DeviseMiningProxy"); 11 | const DeviseMiningImpl = artifacts.require("./DeviseMiningImpl"); 12 | const AuditProxy = artifacts.require("./AuditProxy"); 13 | const AuditImpl = artifacts.require("./AuditImpl"); 14 | const DateTime = artifacts.require("./DateTime"); 15 | const DeviseToken = artifacts.require("./DeviseToken"); 16 | const AccessControlStorage = artifacts.require("./AccessControlStorage"); 17 | const AccessControlProxy = artifacts.require("./AccessControlProxy"); 18 | const AccessControlImpl = artifacts.require("./AccessControl"); 19 | 20 | module.exports = function (deployer, network, accounts) { 21 | const pitai = accounts[0]; 22 | const tokenWallet = accounts[2]; 23 | const escrowWallet = accounts[3]; 24 | const revenueWallet = accounts[4]; 25 | const microDVZ = 10 ** 6; 26 | const billionDVZ = 10 ** 9; 27 | 28 | // DateTime contract 29 | return deployer.deploy(DateTime, {from: pitai}).then(async function (dateTime) { 30 | // Devise token 31 | const token = await DeviseToken.deployed(); 32 | const leptonStorage = await deployer.deploy(LeptonStorage, {from: pitai}); 33 | 34 | // Main entry point Rental proxy and implementation 35 | const rentalProxy = await deployer.deploy(DeviseRentalProxy, token.address, {from: pitai}); 36 | const rentalImpl = await deployer.deploy(DeviseRentalImpl, {from: pitai}); 37 | const rental = DeviseRentalImpl.at(rentalProxy.address); 38 | await rentalProxy.upgradeTo(rentalImpl.address); 39 | 40 | // Deploying audit contract 41 | const auditProxy = await deployer.deploy(AuditProxy, {from: pitai}); 42 | const auditImpl = await deployer.deploy(AuditImpl, {from: pitai}); 43 | await auditProxy.upgradeTo(auditImpl.address); 44 | 45 | // Lepton Storage and Mining contracts 46 | const miningProxy = await deployer.deploy(DeviseMiningProxy, leptonStorage.address, {from: pitai}); 47 | await leptonStorage.authorize(miningProxy.address, {from: pitai}); 48 | const mininigImpl = await deployer.deploy(DeviseMiningImpl, {from: pitai}); 49 | await miningProxy.upgradeTo(mininigImpl.address, {from: pitai}); 50 | const mining = DeviseMiningImpl.at(miningProxy.address); 51 | // setting up rental proxy implementation and initializing it 52 | await rental.setLeptonProxy(miningProxy.address, {from: pitai}); 53 | await mining.authorize(rentalProxy.address, {from: pitai}); 54 | 55 | // Deploying accounting contract 56 | const accountingStorage = await deployer.deploy(AccountingStorage, {from: pitai}); 57 | const accountingProxy = await deployer.deploy(AccountingProxy, token.address, accountingStorage.address, {from: pitai}); 58 | await accountingStorage.authorize(accountingProxy.address, {from: pitai}); 59 | const accountingImpl = await deployer.deploy(AccountingImpl, {from: pitai}); 60 | await accountingProxy.upgradeTo(accountingImpl.address, {from: pitai}); 61 | const accounting = AccountingImpl.at(accountingProxy.address); 62 | // set accounting contract on rental 63 | await rental.setAccountingContract(accountingProxy.address, {from: pitai}); 64 | await accounting.authorize(rentalProxy.address, {from: pitai}); 65 | 66 | // Deploying auction contract 67 | const auctionProxy = await deployer.deploy(AuctionProxy, {from: pitai}); 68 | const auctionImpl = await deployer.deploy(AuctionImpl, {from: pitai}); 69 | await auctionProxy.upgradeTo(auctionImpl.address, {from: pitai}); 70 | const auction = AuctionImpl.at(auctionProxy.address); 71 | 72 | // Deploying accessControl contract 73 | const accessControlStorage = await deployer.deploy(AccessControlStorage, {from: pitai}); 74 | const auctionStorage = await deployer.deploy(AuctionStorage, {from: pitai}); 75 | const accessControlProxy = await deployer.deploy(AccessControlProxy, dateTime.address, leptonStorage.address, accessControlStorage.address, auctionStorage.address, {from: pitai}); 76 | await accessControlStorage.authorize(accessControlProxy.address, {from: pitai}); 77 | await auctionStorage.authorize(accessControlProxy.address, {from: pitai}); 78 | const accessControlImpl = await deployer.deploy(AccessControlImpl, {from: pitai}); 79 | await accessControlProxy.upgradeTo(accessControlImpl.address, {from: pitai}); 80 | const accessControl = AccessControlImpl.at(accessControlProxy.address); 81 | await accessControl.authorize(rentalProxy.address); 82 | await rental.setAccessControlContract(accessControlProxy.address, {from: pitai}); 83 | await accounting.authorize(accessControlProxy.address, {from: pitai}); 84 | await accessControl.setAccountingContract(accountingProxy.address); 85 | await auction.authorize(accessControlProxy.address, {from: pitai}); 86 | await accessControl.setAuctionContract(auctionProxy.address); 87 | 88 | // setting wallets on rental 89 | await rental.setEscrowWallet(escrowWallet, {from: pitai}); 90 | await rental.setRevenueWallet(revenueWallet, {from: pitai}); 91 | await rental.setTokenWallet(tokenWallet, {from: pitai}); 92 | await rental.addMasterNode(pitai, {from: pitai}); 93 | 94 | // for tests, pre-approving accounting contract to sell 1B DVZ from tokenWallet 95 | const saleAmount = 1 * billionDVZ * microDVZ; 96 | await token.approve(accountingProxy.address, saleAmount, {from: tokenWallet}); 97 | }); 98 | }; 99 | 100 | 101 | -------------------------------------------------------------------------------- /solidity/migrations/4_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const MintableTokenTestBoth = artifacts.require("./test/MintableTokenTestBoth"); 2 | const MintableTokenTest1 = artifacts.require("./test/MintableTokenTest1"); 3 | 4 | module.exports = async function (deployer, network, accounts) { 5 | const pitai = accounts[0]; 6 | // total cap is 1 billion and the decimal is 18 7 | const cap = 10 ** 9 * 10 ** 18; 8 | await deployer.deploy(MintableTokenTest1, cap, {from: pitai}); 9 | await deployer.deploy(MintableTokenTestBoth, cap, {from: pitai}); 10 | }; 11 | -------------------------------------------------------------------------------- /solidity/migrations/5_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const MintableTokenTestBoth = artifacts.require("./test/MintableTokenTestBoth"); 2 | const MintableTokenTestBoth1 = artifacts.require("./test/MintableTokenTestBoth1"); 3 | 4 | module.exports = function (deployer, network, accounts) { 5 | const pitai = accounts[0]; 6 | // total cap is 1 billion and the decimal is 18 7 | const cap = 10 ** 9 * 10 ** 18; 8 | 9 | return deployer.deploy(MintableTokenTestBoth, cap, {from: pitai}).then(function () { 10 | return deployer.deploy(MintableTokenTestBoth1, cap, {from: pitai}); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /solidity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devise", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "eslintConfig": { 10 | "root": true, 11 | "env": { 12 | "es6": true, 13 | "node": true 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 2017, 17 | "sourceType": "module", 18 | "ecmaFeatures": { 19 | "jsx": true, 20 | "experimentalObjectRestSpread": true 21 | } 22 | } 23 | }, 24 | "scripts": { 25 | "test": "truffle test" 26 | }, 27 | "author": "", 28 | "license": "ISC", 29 | "dependencies": { 30 | "eslint": "^4.19.1", 31 | "mocha-istanbul": "^0.3.0", 32 | "mocha-junit-reporter": "^1.17.0", 33 | "moment": "^2.22.1", 34 | "openzeppelin-solidity": "git+https://git@github.com/OpenZeppelin/openzeppelin-solidity.git#746673a94f7e43835fcb5cb7b1af8ff1eea4e276", 35 | "solhint": "^1.3.0", 36 | "solidity-coverage": "^0.5.4", 37 | "solium": "^1.1.6", 38 | "truffle": "4.1.11" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /solidity/test/MasterNodeTest.js: -------------------------------------------------------------------------------- 1 | const setupFixturesHelper = require('./helpers/setupFixtures'); 2 | const {timeTravel, evmSnapshot, evmRevert} = require('./test-utils'); 3 | const leptons = require('./leptons'); 4 | const assertRevert = require('./helpers/assertRevert'); 5 | 6 | const pitai = web3.eth.accounts[0]; 7 | const tokenWallet = web3.eth.accounts[1]; 8 | const escrowWallet = web3.eth.accounts[2]; 9 | const revenueWallet = web3.eth.accounts[3]; 10 | const clients = web3.eth.accounts.slice(4); 11 | 12 | let rental; 13 | let lepton; 14 | let testSnapshotId = 0; 15 | 16 | async function setupFixtures() { 17 | ({ 18 | rental, 19 | lepton 20 | } = await setupFixturesHelper(pitai, escrowWallet, tokenWallet, revenueWallet, null, true, false)); 21 | // move forward 1 month 22 | await timeTravel(86400 * 31); 23 | // snapshot the blockchain 24 | testSnapshotId = (await evmSnapshot()).result; 25 | } 26 | 27 | contract("DeviseRentalImpl", () => { 28 | before(setupFixtures); 29 | // reset to our fixtures state after each test 30 | afterEach(async () => { 31 | evmRevert(testSnapshotId); 32 | // workaround ganache/testrpc crash 33 | testSnapshotId = (await evmSnapshot()).result; 34 | }); 35 | 36 | it("Can add master node", async () => { 37 | await rental.addMasterNode(clients[1], {from: pitai}); 38 | const masterNodes = await lepton.getMasterNodes.call(); 39 | assert.deepEqual([clients[1]], masterNodes); 40 | }); 41 | 42 | it("Only owner can add master node", async () => { 43 | await assertRevert(rental.addMasterNode(clients[1], {from: clients[1]})); 44 | await rental.addMasterNode(clients[1], {from: pitai}); 45 | await assertRevert(rental.addMasterNode(clients[1], {from: clients[1]})); 46 | }); 47 | 48 | it("Only owner can remove a master node", async () => { 49 | await rental.addMasterNode(clients[1], {from: pitai}); 50 | await assertRevert(rental.removeMasterNode(clients[1], {from: clients[1]})); 51 | }); 52 | 53 | it("Can remove master node", async () => { 54 | await rental.addMasterNode(clients[1], {from: pitai}); 55 | await rental.addMasterNode(clients[2], {from: pitai}); 56 | await rental.addMasterNode(clients[3], {from: pitai}); 57 | const masterNodes1 = await rental.getMasterNodes.call(); 58 | assert.deepEqual([clients[1], clients[2], clients[3]], masterNodes1); 59 | await rental.removeMasterNode(clients[1], {from: pitai}); 60 | const masterNodes2 = await rental.getMasterNodes.call(); 61 | assert.deepEqual([clients[3], clients[2]], masterNodes2); 62 | await rental.removeMasterNode(clients[2], {from: pitai}); 63 | const masterNodes3 = await rental.getMasterNodes.call(); 64 | assert.deepEqual([clients[3]], masterNodes3); 65 | await rental.removeMasterNode(clients[3], {from: pitai}); 66 | const masterNodes4 = await rental.getMasterNodes.call(); 67 | assert.deepEqual([], masterNodes4); 68 | }); 69 | 70 | it("Only a master node can add Leptons", async () => { 71 | await rental.addMasterNode(clients[1], {from: pitai}); 72 | await assertRevert(rental.addLepton(leptons[0], '', 1000000, {from: clients[2]})); 73 | const tx1 = await rental.addLepton(leptons[0], '', 1000000, {from: clients[1]}); 74 | await assertRevert(rental.addLepton(leptons[1], leptons[0], 1000000, {from: clients[2]})); 75 | console.log("Gas used to add first lepton: " + tx1["receipt"]["gasUsed"]); 76 | console.log("Adding second lepton..."); 77 | const tx2 = await rental.addLepton(leptons[1], leptons[0], 1000000, {from: clients[1]}); 78 | console.log("Gas used to add second lepton: " + tx2["receipt"]["gasUsed"]); 79 | const tx3 = await rental.addLepton(leptons[2], leptons[1], 1000000, {from: clients[1]}); 80 | console.log("Gas used to add third lepton: " + tx3["receipt"]["gasUsed"]); 81 | const firstLepton = await rental.getLepton(0); 82 | const secondLepton = await rental.getLepton(1); 83 | assert.deepEqual([leptons[0], 1000000], [firstLepton[0], firstLepton[1].toNumber()]); 84 | assert.deepEqual([leptons[1], 1000000], [secondLepton[0], secondLepton[1].toNumber()]); 85 | }); 86 | 87 | it("Leptons can only be added if prevLepton matches last lepton in the chain", async () => { 88 | await rental.addMasterNode(clients[1], {from: pitai}); 89 | await rental.addLepton(leptons[0], '', 1000000, {from: clients[1]}); 90 | console.log("Adding third lepton out of order..."); 91 | await assertRevert(rental.addLepton(leptons[2], leptons[1], 1000000, {from: clients[1]})); 92 | }); 93 | 94 | it("No duplicate Lepton can be added", async () => { 95 | await rental.addMasterNode(clients[1], {from: pitai}); 96 | await rental.addLepton(leptons[0], '', 1000000, {from: clients[1]}); 97 | console.log("Adding duplicate lepton..."); 98 | await assertRevert(rental.addLepton(leptons[0], '', 1000000, {from: clients[1]})); 99 | }); 100 | 101 | it("Can get all leptons at once", async () => { 102 | await rental.addMasterNode(clients[1], {from: pitai}); 103 | await rental.addLepton(leptons[0], '', 1000000, {from: clients[1]}); 104 | await rental.addLepton(leptons[1], leptons[0], 900000, {from: clients[1]}); 105 | const [hashes, incrementalUsefulnesses] = await rental.getAllLeptons.call(); 106 | assert.deepEqual(hashes, [leptons[0], leptons[1]]); 107 | assert.deepEqual(incrementalUsefulnesses, [new web3.BigNumber(1000000), new web3.BigNumber(900000)]); 108 | }); 109 | }); -------------------------------------------------------------------------------- /solidity/test/MintableTokenTest.js: -------------------------------------------------------------------------------- 1 | const MintableTokenTest = artifacts.require("./test/MintableTokenTest"); 2 | const MintableTokenTest1 = artifacts.require("./test/MintableTokenTest1"); 3 | const MintableTokenTestBoth = artifacts.require("./test/MintableTokenTestBoth"); 4 | const MintableTokenTestBoth1 = artifacts.require("./test/MintableTokenTestBoth1"); 5 | const assertRevert = require('./helpers/assertRevert'); 6 | 7 | let token; 8 | let token1; 9 | let tokenBoth; 10 | let tokenBoth1; 11 | let pitai = web3.eth.accounts[0]; 12 | 13 | contract("MintableTokenTest", () => { 14 | before(async () => { 15 | token = await MintableTokenTest.deployed(); 16 | token1 = await MintableTokenTest1.deployed(); 17 | }); 18 | 19 | describe("Can mint", () => { 20 | it("Inheritance order: CappedToken, RBACMintableToken", async () => { 21 | await token.mint(pitai, 1000, {from: pitai}); 22 | const bal = (await token.balanceOf(pitai)).toNumber(); 23 | assert.equal(bal, 1000); 24 | }); 25 | 26 | it("Inheritance order: RBACMintableToken, CappedToken", async () => { 27 | await token1.mint(pitai, 1000, {from: pitai}); 28 | const bal = (await token1.balanceOf(pitai)).toNumber(); 29 | assert.equal(bal, 1000); 30 | }); 31 | }); 32 | 33 | describe("Cannot mint more tokens than the cap", () => { 34 | it("Inheritance order: CappedToken, RBACMintableToken", async () => { 35 | const lotsOfTokens = 2 * 10 ** 9 * 10 ** 18; 36 | const cap = (await token.cap.call()).toNumber(); 37 | assert.isAbove(lotsOfTokens, cap); 38 | await assertRevert(token.mint(pitai, lotsOfTokens, {from: pitai})); 39 | }); 40 | 41 | it("Inheritance order: RBACMintableToken, CappedToken", async () => { 42 | const lotsOfTokens = 2 * 10 ** 9 * 10 ** 18; 43 | const cap = (await token1.cap.call()).toNumber(); 44 | assert.isAbove(lotsOfTokens, cap); 45 | await assertRevert(token1.mint(pitai, lotsOfTokens, {from: pitai})); 46 | }); 47 | }); 48 | }); 49 | 50 | contract("MintableTokenTestBoth", () => { 51 | before(async () => { 52 | tokenBoth = await MintableTokenTestBoth.deployed(); 53 | tokenBoth1 = await MintableTokenTestBoth1.deployed(); 54 | }); 55 | 56 | describe("Can mint", () => { 57 | it("Inheritance order: CappedToken, RBACMintableToken", async () => { 58 | await tokenBoth.mint(pitai, 1000, {from: pitai}); 59 | const bal = (await tokenBoth.balanceOf(pitai)).toNumber(); 60 | assert.equal(bal, 1000); 61 | }); 62 | 63 | it("Inheritance order: RBACMintableToken, CappedToken", async () => { 64 | await tokenBoth1.mint(pitai, 1000, {from: pitai}); 65 | const bal = (await tokenBoth1.balanceOf(pitai)).toNumber(); 66 | assert.equal(bal, 1000); 67 | }); 68 | }); 69 | 70 | describe("Cannot mint more tokens than the cap", () => { 71 | it("Inheritance order: CappedToken, RBACMintableToken, it works because super is used", async () => { 72 | const lotsOfTokens = 2 * 10 ** 9 * 10 ** 18; 73 | const cap = (await tokenBoth.cap.call()).toNumber(); 74 | assert.isAbove(lotsOfTokens, cap); 75 | await assertRevert(tokenBoth.mint(pitai, lotsOfTokens, {from: pitai})); 76 | }); 77 | 78 | it("Inheritance order: RBACMintableToken, CappedToken", async () => { 79 | const lotsOfTokens = 2 * 10 ** 9 * 10 ** 18; 80 | const cap = (await tokenBoth1.cap.call()).toNumber(); 81 | assert.isAbove(lotsOfTokens, cap); 82 | await assertRevert(tokenBoth1.mint(pitai, lotsOfTokens, {from: pitai})); 83 | }); 84 | }); 85 | }); 86 | 87 | contract("Test multiple minters", () => { 88 | before(async () => { 89 | token = await MintableTokenTest.deployed(); 90 | token1 = await MintableTokenTest1.deployed(); 91 | }); 92 | 93 | it("Add two minters", async () => { 94 | const minter1 = web3.eth.accounts[5]; 95 | const minter2 = web3.eth.accounts[6]; 96 | let ret = await token1.hasRole.call(minter1, "minter"); 97 | assert.equal(ret, false); 98 | await token1.addMinter(minter1, {from: pitai}); 99 | await token1.addMinter(minter2, {from: pitai}); 100 | ret = await token1.hasRole.call(minter2, "minter"); 101 | assert.equal(ret, true); 102 | ret = await token1.hasRole.call(pitai, "minter"); 103 | assert.equal(ret, true); 104 | let bal = (await token1.totalSupply.call()).toNumber(); 105 | assert.equal(bal, 0); 106 | await token1.mint(pitai, 1000, {from: pitai}); 107 | bal = (await token1.totalSupply.call()).toNumber(); 108 | assert.equal(bal, 1000); 109 | await token1.mint(minter2, 2000, {from: minter2}); 110 | bal = (await token1.totalSupply.call()).toNumber(); 111 | assert.equal(bal, 3000); 112 | await token1.mint(minter1, 3000, {from: minter1}); 113 | bal = (await token1.totalSupply.call()).toNumber(); 114 | assert.equal(bal, 6000); 115 | }); 116 | }); -------------------------------------------------------------------------------- /solidity/test/RentalAccountingTest.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const setupFixturesHelper = require('./helpers/setupFixtures'); 3 | const {transferTokens} = require('./test-utils'); 4 | 5 | const pitai = web3.eth.accounts[0]; 6 | const tokenWallet = web3.eth.accounts[2]; 7 | const escrowWallet = web3.eth.accounts[3]; 8 | const revenueWallet = web3.eth.accounts[4]; 9 | const clients = web3.eth.accounts.slice(5); 10 | const microDVZ = 10 ** 6; 11 | const millionDVZ = 10 ** 6; 12 | 13 | let token; 14 | let rentalProxy; 15 | 16 | async function setupFixtures() { 17 | ({ 18 | rental: rentalProxy, 19 | proxy, 20 | token, 21 | dateTime, 22 | auctionProxy, 23 | accountingProxy 24 | } = await setupFixturesHelper(pitai, escrowWallet, tokenWallet, revenueWallet, null, true, true)); 25 | } 26 | 27 | contract("Test provision", () => { 28 | beforeEach(setupFixtures); 29 | 30 | it("The token balance should decrease after provision", async () => { 31 | const ether_amount = 1000; 32 | const client = clients[0]; 33 | await transferTokens(token, rentalProxy, tokenWallet, client, ether_amount); 34 | const dvz_amount = (await token.balanceOf.call(client)).toNumber(); 35 | await token.approve(accountingProxy.address, dvz_amount, {from: client}); 36 | const amtProvisioned = 1 * millionDVZ * microDVZ; 37 | await rentalProxy.provision(amtProvisioned, {from: client}); 38 | const after = (await token.balanceOf.call(client)).toNumber(); 39 | assert.equal(dvz_amount, after + amtProvisioned); 40 | const allow = (await rentalProxy.getAllowance.call({from: client})).toNumber(); 41 | assert.equal(allow, amtProvisioned); 42 | }); 43 | 44 | 45 | it("All provisioned clients are returned by getter", async () => { 46 | const numClients = (await rentalProxy.getNumberOfClients.call()).toNumber(); 47 | assert.equal(numClients, 0); 48 | const ether_amount = 1000; 49 | // Provision one client 50 | await transferTokens(token, rentalProxy, tokenWallet, clients[0], ether_amount); 51 | const dvz_amount = (await token.balanceOf.call(clients[0])).toNumber(); 52 | await token.approve(accountingProxy.address, dvz_amount, {from: clients[0]}); 53 | const amtProvisioned = 1 * millionDVZ * microDVZ; 54 | await rentalProxy.provision(amtProvisioned, {from: clients[0]}); 55 | const numClients1 = (await rentalProxy.getNumberOfClients.call()).toNumber(); 56 | assert.equal(numClients1, 1); 57 | assert.equal(await rentalProxy.getClient.call(0), clients[0]); 58 | // provision second client 59 | await transferTokens(token, rentalProxy, tokenWallet, clients[1], ether_amount); 60 | await token.approve(accountingProxy.address, dvz_amount, {from: clients[1]}); 61 | await rentalProxy.provision(amtProvisioned, {from: clients[1]}); 62 | const numClients2 = (await rentalProxy.getNumberOfClients.call()).toNumber(); 63 | assert.equal(numClients2, 2); 64 | for (let i = 0; i < numClients2; i++) { 65 | assert.equal(await rentalProxy.getClient.call(i), clients[i]); 66 | } 67 | }); 68 | 69 | }); 70 | })(); -------------------------------------------------------------------------------- /solidity/test/RentalPauseTests.js: -------------------------------------------------------------------------------- 1 | const setupFixturesHelper = require('./helpers/setupFixtures'); 2 | const DeviseRental_v1 = artifacts.require("./test/DeviseRentalImpl"); 3 | const {timeTravel, evmSnapshot, evmRevert} = require('./test-utils'); 4 | const assertRevert = require('./helpers/assertRevert'); 5 | 6 | const pitai = web3.eth.accounts[0]; 7 | const escrowWallet = web3.eth.accounts[1]; 8 | const revenueWallet = web3.eth.accounts[2]; 9 | const tokenWallet = web3.eth.accounts[3]; 10 | const clients = web3.eth.accounts.slice(4); 11 | let token; 12 | let rental; 13 | let proxy; 14 | let testSnapshotId = 0; 15 | let eternalStorage; 16 | let microDVZ = 10 ** 6; 17 | let millionDVZ = 10 ** 6; 18 | 19 | async function setupFixtures() { 20 | ({ 21 | rental, 22 | proxy, 23 | token, 24 | eternalStorage 25 | } = await setupFixturesHelper(pitai, escrowWallet, tokenWallet, revenueWallet, clients, true, true)); 26 | // snapshot the blockchain 27 | testSnapshotId = (await evmSnapshot()).result; 28 | } 29 | 30 | contract("Rental Contract (Pausing tests)", function () { 31 | // before running all tests, setup fixtures 32 | before(setupFixtures); 33 | // reset to our fixtures state after each test 34 | afterEach(async () => { 35 | evmRevert(testSnapshotId); 36 | // workaround ganache/testrpc crash 37 | testSnapshotId = (await evmSnapshot()).result; 38 | }); 39 | 40 | 41 | it("Pausing contract stops non owner functions", async () => { 42 | const client = clients[0]; 43 | // non owner transactions 44 | await rental.provision(12000 * microDVZ, {from: client}); 45 | await rental.applyForPowerUser({from: client}); 46 | await rental.requestHistoricalData({from: client}); 47 | await rental.designateBeneficiary(client, {from: client}); 48 | await rental.leaseAll(1000 * microDVZ, 1, {from: client}); 49 | await rental.withdraw(1, {from: client}); 50 | // Pause contract 51 | await proxy.pause({from: pitai}); 52 | // owner operations still work 53 | await rental.setHistoricalDataFee(0, {from: pitai}); 54 | await rental.setPowerUserClubFee(0, {from: pitai}); 55 | // await rental.setDataContract(eternalStorage.address, {from: pitai}); 56 | await proxy.upgradeTo((await DeviseRental_v1.new()).address); 57 | // client operations are paused 58 | await assertRevert(rental.provision(10000 * microDVZ, {from: client})); 59 | await assertRevert(rental.applyForPowerUser({from: client})); 60 | await assertRevert(rental.requestHistoricalData({from: client})); 61 | await assertRevert(rental.designateBeneficiary(client, {from: client})); 62 | await assertRevert(rental.leaseAll(1000 * microDVZ, 1, {from: client})); 63 | await assertRevert(rental.withdraw(1, {from: client})); 64 | }); 65 | 66 | it("Unpausing contract restores non owner functions", async () => { 67 | const client = clients[0]; 68 | // Pause contract 69 | await proxy.pause({from: pitai}); 70 | // UnPause contract 71 | await proxy.unpause({from: pitai}); 72 | // owner operations still work 73 | await rental.setHistoricalDataFee(0, {from: pitai}); 74 | await rental.setPowerUserClubFee(0, {from: pitai}); 75 | // await rental.setDataContract(eternalStorage.address, {from: pitai}); 76 | await proxy.upgradeTo((await DeviseRental_v1.new()).address, {from: pitai}); 77 | // client operations are ok 78 | await rental.provision(12000 * microDVZ, {from: client}); 79 | await rental.applyForPowerUser({from: client}); 80 | await rental.requestHistoricalData({from: client}); 81 | await rental.designateBeneficiary(client, {from: client}); 82 | await rental.leaseAll(1000 * microDVZ, 1, {from: client}); 83 | await rental.withdraw(1, {from: client}); 84 | }); 85 | 86 | it("Should be able to make calls after pause", async () => { 87 | const client = clients[0]; 88 | await rental.provision(10000 * microDVZ, {from: client}); 89 | // Pause contract 90 | await proxy.pause({from: pitai}); 91 | await rental.getAllowance.call({from: client}); 92 | }); 93 | 94 | it("Should be able to get the correct info after pause, with time travel", async () => { 95 | const client = clients[0]; 96 | await rental.provision(millionDVZ * microDVZ, {from: client}); 97 | const bal = (await rental.getAllowance.call({from: client})).toNumber(); 98 | await rental.leaseAll(1000 * microDVZ, 1, {from: client}); 99 | const bal1 = (await rental.getAllowance.call({from: client})).toNumber(); 100 | assert.isAbove(bal, bal1); 101 | const sum1 = await rental.getClientSummary(client); 102 | const lt1 = sum1[3].toNumber(); 103 | // Pause contract 104 | await proxy.pause({from: pitai}); 105 | timeTravel(86400 * 180); 106 | const bal2 = (await rental.getAllowance.call({from: client})).toNumber(); 107 | assert.isAbove(bal1, bal2); 108 | const sum2 = await rental.getClientSummary(client); 109 | const lt2 = sum2[3].toNumber(); 110 | assert.isAtLeast(lt2, lt1 + 5); 111 | }); 112 | }); -------------------------------------------------------------------------------- /solidity/test/TimeTravelTest.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const setupFixturesHelper = require('./helpers/setupFixtures'); 3 | const TimeTravel = artifacts.require("./test/TimeTravel"); 4 | const leptons = require('./leptons'); 5 | const {transferTokens} = require('./test-utils'); 6 | 7 | const pitai = web3.eth.accounts[0]; 8 | const tokenWallet = web3.eth.accounts[2]; 9 | const escrowWallet = web3.eth.accounts[3]; 10 | const revenueWallet = web3.eth.accounts[4]; 11 | const clients = web3.eth.accounts.slice(5); 12 | const microDVZ = 10 ** 6; 13 | const millionDVZ = 10 ** 6; 14 | const IUDecimals = 10 ** 6; 15 | 16 | let token; 17 | let accessControlProxy; 18 | let accountingProxy; 19 | let rental; 20 | let timeTravelSC; 21 | 22 | async function setupFixtures() { 23 | ({ 24 | rental: rental, 25 | token, 26 | accessControlProxy, 27 | accountingProxy 28 | } = await setupFixturesHelper(pitai, escrowWallet, tokenWallet, revenueWallet, clients, true, false)); 29 | 30 | await rental.addMasterNode(pitai); 31 | 32 | timeTravelSC = await TimeTravel.new({from: pitai}); 33 | const AccessControl = artifacts.require("./test/AccessControlImplTimeTravel"); 34 | await accessControlProxy.upgradeTo((await AccessControl.new()).address, {from: pitai}); 35 | const accessControl = AccessControl.at(accessControlProxy.address); 36 | await accessControl.setTimeTravel(timeTravelSC.address, {from: pitai}); 37 | } 38 | 39 | async function timeTravel(time) { 40 | await timeTravelSC.timeTravelForward(time, {from: pitai}); 41 | } 42 | 43 | contract("Test time travel using the smart contract approach", () => { 44 | beforeEach(setupFixtures); 45 | 46 | it("Should deduct rent when time travel 31 days", async () => { 47 | const client = clients[0]; 48 | const numLeptons = (await rental.getNumberOfLeptons()).toNumber(); 49 | for (let i = numLeptons; i < leptons.length; i++) { 50 | await rental.addLepton(leptons[i], i > 0 ? leptons[i - 1] : '', IUDecimals * (3)); 51 | } 52 | // approve so to recognize revenue 53 | // 10 million tokens 54 | const rev_amount = 10 * millionDVZ * microDVZ; 55 | await token.approve(accountingProxy.address, rev_amount, {from: escrowWallet}); 56 | 57 | // purchase a lot of tokens 58 | const ether_amount = 3000; 59 | await transferTokens(token, rental, tokenWallet, client, ether_amount); 60 | 61 | let dvz_amount = 10 * millionDVZ * microDVZ; 62 | await token.approve(accountingProxy.address, dvz_amount, {from: client}); 63 | await rental.provision(dvz_amount, {from: client}); 64 | 65 | const bal0 = (await rental.getAllowance.call({from: client})).toNumber(); 66 | const prc_per_bit = 5000 * microDVZ; 67 | const seats = 10; 68 | await rental.leaseAll(prc_per_bit, seats, {from: client}); 69 | const bal1 = (await rental.getAllowance.call({from: client})).toNumber(); 70 | assert.isAbove(bal0, bal1); 71 | await timeTravel(86400 * 31); 72 | const bal2 = (await rental.getAllowance.call({from: client})).toNumber(); 73 | assert.isAbove(bal1, bal2); 74 | }); 75 | }); 76 | })(); -------------------------------------------------------------------------------- /solidity/test/UpdateLeaseTermsStatic.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const DeviseRentalBase = artifacts.require("./DeviseRentalProxy"); 3 | const setupFixturesHelper = require('./helpers/setupFixtures'); 4 | const leptons = require('./leptons'); 5 | 6 | const pitai = web3.eth.accounts[0]; 7 | const clients = web3.eth.accounts.slice(5); 8 | let token; 9 | let rental; 10 | let proxy; 11 | let estor; 12 | let microDVZ = 10 ** 6; 13 | 14 | async function setupFixtures() { 15 | const pitai = web3.eth.accounts[0]; 16 | const tokenWallet = web3.eth.accounts[1]; 17 | const escrowWallet = web3.eth.accounts[2]; 18 | const revenueWallet = web3.eth.accounts[3]; 19 | 20 | ({ 21 | proxy, 22 | rental, 23 | token, 24 | eternalStorage: estor 25 | } = await setupFixturesHelper(pitai, escrowWallet, tokenWallet, revenueWallet, clients, true, true)); 26 | } 27 | 28 | contract("UpdateLeaseTermsStatic", function () { 29 | // before running all tests, setup fixtures 30 | before(setupFixtures); 31 | 32 | it("Provides a way to get all bids", async () => { 33 | const provision_amount = 1000000 * microDVZ; 34 | const client1 = clients[0]; 35 | const client2 = clients[1]; 36 | await rental.provision(provision_amount, {from: client1}); 37 | await rental.provision(provision_amount, {from: client2}); 38 | const client_bid1 = 10 * 10 ** 3 * microDVZ; 39 | const client_bid2 = 20 * 10 ** 3 * microDVZ; 40 | await rental.leaseAll(client_bid1, 5, {from: client1}); 41 | await rental.leaseAll(client_bid2, 7, {from: client2}); 42 | const bidders = await rental.getAllBidders.call(); 43 | const secondClient = [bidders[0][0], bidders[1][0], bidders[2][0]]; 44 | const firstClient = [bidders[0][1], bidders[1][1], bidders[2][1]]; 45 | assert.equal(secondClient[0], client2); 46 | assert.equal(secondClient[1].toNumber(), 7); 47 | assert.equal(secondClient[2].toNumber(), client_bid2); 48 | assert.equal(firstClient[0], client1); 49 | assert.equal(firstClient[1].toNumber(), 5); 50 | assert.equal(firstClient[2].toNumber(), client_bid1); 51 | }); 52 | 53 | it("Can add new functions with upgrades", async () => { 54 | const DeviseRental_v2 = artifacts.require("./test/DeviseRentalImplV3"); 55 | await rental.provision(10000, {from: clients[0]}); 56 | const bal_v1 = (await rental.getAllowance.call({from: clients[0]})).toNumber(); 57 | // upgrade to v2 58 | const proxy = DeviseRentalBase.at(rental.address); 59 | await proxy.upgradeTo((await DeviseRental_v2.new({from: pitai})).address, {from: pitai}); 60 | const rental_v2 = DeviseRental_v2.at(proxy.address); 61 | const bal_v2 = (await rental_v2.getAllowance_v2.call({from: clients[0]})).toNumber(); 62 | assert.equal(bal_v1, bal_v2); 63 | }); 64 | 65 | it("Cannot override the type of state variables with upgrades", async () => { 66 | const DeviseRental_v3 = artifacts.require("./test/DeviseRentalImplV3"); 67 | await proxy.upgradeTo((await DeviseRental_v3.new({from: pitai})).address, {from: pitai}); 68 | const rental_v3 = DeviseRental_v3.at(proxy.address); 69 | // can't work without Proxy fallback assembly 70 | await rental_v3.setVersion(3, {from: pitai}); 71 | const testString1 = (await proxy.version.call({from: clients[0]})).toNumber(); 72 | assert.equal(testString1, 3); 73 | }); 74 | 75 | it("Only owner can upgrade contract", async () => { 76 | const DeviseRental_v3 = artifacts.require("./test/DeviseRentalImplV3"); 77 | await proxy.upgradeTo((await DeviseRental_v3.new({from: pitai})).address, {from: pitai}); 78 | try { 79 | await proxy.upgradeTo((await DeviseRental_v3.new({from: pitai})).address, {from: clients[0]}); 80 | expect.fail(null, null, "Only owner should be able to upgrade contract"); 81 | } catch (e) { 82 | } 83 | }); 84 | 85 | it("Can list all leptons in the blockchain", async () => { 86 | const numLeptons = (await rental.getNumberOfLeptons.call()).toNumber(); 87 | assert.equal(numLeptons, 6); 88 | for (let i = 0; i < numLeptons; i++) { 89 | const lepton = await rental.getLepton(i); 90 | assert.equal(lepton[0], leptons[i]); 91 | } 92 | }); 93 | 94 | it.skip("Can get data contract", async function () { 95 | const dataConract = await rental.getDataContract.call(); 96 | assert.equal(dataConract, estor.address); 97 | }); 98 | 99 | it.skip("Can set new data contract", async function () { 100 | estor = await DeviseEternalStorage.new(); 101 | await rental.setDataContract(estor.address); 102 | const dataConract = await rental.getDataContract.call(); 103 | assert.equal(dataConract, estor.address); 104 | }); 105 | 106 | }); 107 | })(); -------------------------------------------------------------------------------- /solidity/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search(': revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; -------------------------------------------------------------------------------- /solidity/test/leptons.js: -------------------------------------------------------------------------------- 1 | module.exports = ["0x38a1e8a65521791b9d34cd62fac36ceb5349bb6c", "0x3af1ff2e4d7c1cc6fb7616c7c8e3380704d94a69", "0x4e96dc9090e882f88703aad4960d9c76d03bf36d", "0x6143c4e4f7371c10adeef4d9ae5dc75a71c00cb6", "0xe3d6eaa94c7d02c16ca9844ecc4196652d1bb81c", "0x0291ca29a97916faa3974a316593e8b40d21a80a", "0x7538585650fa248c8f8ecd954f70f17f5b76b5f0", "0xdb1a2128129129dd77608efff5f7aa6ff3c38824", "0xb7c1751739413bab04f300493cb44fe390698e40", "0x0af0c0f3ea7a513d0f609b05222550f0053f9bb8", "0xaae29b28a63518aeb29b70a0f9cdb2cede366cee", "0x712823d8e5d84ce206cf6513f79ef80e47ac8f3b", "0x4be33ff69264cbbf445f37e38c495b8c77f9f6bc", "0x32ef8aee27f6bda10fc07d47fa80b5379e183db6", "0x5511d718d1032e096720107d1b7dc573acae9200", "0xc146ceb12637d88737c05aecdf4526f2de550e99", "0x4e817c238b69742ff457165f254a395ae39199cc", "0x90b6cb3a61252cc49964da0fe34818d532bdcb1e", "0x59a897c2c1c8c046a37273800e228fb150d9d4f7", "0xc2421aa3825bb51d1dfeefd4200b258caa30d090", "0x8a0fe3b34a8c4d9ee73e9786e2b3282706f82fea", "0x7c32377bc04e5255fb5e6dfe5192757f6e5b1d81", "0x27d636f7194bc7432dddd37bc0ac5423b0ab09ed", "0x94670e07e264707a695795d49a0c295de5d0ae5e", "0x2796a44d085441e0ca94b30496bd0216ce1180e5", "0x8d29b96b44fb632e6e5071098145443ca8495da9", "0x5b2df9a9acb312d2b66a523466a7e9a2e1cd407a", "0x7d2a61dac9de222a3be699ed381962ebb3e3f494"]; 2 | 3 | -------------------------------------------------------------------------------- /solidity/truffle-jenkins.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | networks: { 5 | development: { 6 | host: "localhost", 7 | port: 8545, 8 | network_id: "*", // Match any network id 9 | gas: 6000000, 10 | } 11 | }, 12 | solc: { 13 | optimizer: { 14 | "enabled": true, 15 | "runs": 1 16 | } 17 | }, 18 | mocha: { 19 | reporter: "mocha-junit-reporter" 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /solidity/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | networks: { 5 | development: { 6 | host: "localhost", 7 | port: 8545, 8 | network_id: "*", // Match any network id 9 | gas: 6000000, 10 | } 11 | }, 12 | solc: { 13 | optimizer: { 14 | "enabled": true, 15 | "runs": 1 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /yellow_paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devisechain/Devise/fdeed3ec278439476b4e3c8c9131707d6b7d8004/yellow_paper.pdf --------------------------------------------------------------------------------