From 44238c0610ada038855e69c220f6ba8aae2f2b99 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 28 Oct 2017 13:26:56 +0530 Subject: [PATCH 1/4] contracts: corrections to nonces and redeem comment --- .gitignore | 2 ++ contracts/Staking.sol | 51 +++++++++++++++++--------------- contracts/StakingData.sol | 4 ++- contracts/UtilityToken.sol | 53 ++++++++++++++++++++-------------- contracts/UtilityTokenData.sol | 4 ++- 5 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b442727c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# OSX +.DS_Store \ No newline at end of file diff --git a/contracts/Staking.sol b/contracts/Staking.sol index 67c6472c..0d03fbfa 100644 --- a/contracts/Staking.sol +++ b/contracts/Staking.sol @@ -13,7 +13,7 @@ pragma solidity ^0.4.17; // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// +// // ---------------------------------------------------------------------------- // Staking // @@ -43,8 +43,7 @@ contract Staking is OpsManaged, StakingData { eip20Token = _eip20Token; } - // TBD: move to a parent contract because this is identical to `hashIntent` in Staking? - function hashIntent( + function hashMintingIntent( bytes32 _uuid, address _account, uint256 _accountNonce, @@ -58,6 +57,17 @@ contract Staking is OpsManaged, StakingData { return keccak256(_uuid, _account, _accountNonce, _amountST, _amountUT, _escrowUnlockHeight); } + function hashUnstakingIntent( + bytes32 _uuid, + address _account, + uint256 _accountNonce, + uint256 _amountUT, + uint256 _escrowUnlockHeight + ) + public pure returns (bytes32) { + return keccak256(_uuid, _account, _accountNonce, _amountUT, _escrowUnlockHeight); + } + // @dev `_chainId` should be blank for MVU // @dev `_stakingAccount` is optional function registerUtilityToken( @@ -68,7 +78,7 @@ contract Staking is OpsManaged, StakingData { bytes32 _chainId, address _stakingAccount ) - public onlyAdmin returns (bool, bytes32) { + public onlyAdmin returns(bytes32) { require(bytes(_name).length > 0); require(bytes(_symbol).length > 0); require(_decimals > 0); @@ -91,11 +101,11 @@ contract Staking is OpsManaged, StakingData { UtilityTokenRegistered(uuid, _symbol, _name, _decimals, _conversionRate, _chainId, _stakingAccount); - return (true, uuid); + return uuid; } - - // 2 step like ownership --> Yes - // If this is 0, it cannot be set to non-0 + + // TODO: 2 step change + // Require that if this is 0, it cannot be set to non-0 function setUtilityTokenStakingAccount(bytes32 _uuid, address _newStakingAccount) public returns (bool) { require(_uuid != ""); require(utilityTokens[_uuid].conversionRate > 0); @@ -104,7 +114,7 @@ contract Staking is OpsManaged, StakingData { // TODO; } - function stake(bytes32 _uuid, uint256 _amountST) external returns (bool, uint256) { + function stake(bytes32 _uuid, uint256 _amountST) external returns (uint256) { require(_uuid != ""); require(utilityTokens[_uuid].conversionRate > 0); require(_amountST > 0); @@ -115,8 +125,9 @@ contract Staking is OpsManaged, StakingData { require(eip20Token.transferFrom(msg.sender, address(this), _amountST)); uint256 amountUT = _amountST.mul(utilityToken.conversionRate); - uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT; - bytes32 mintingIntentHash = hashIntent( + uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + nonces[msg.sender]++; + bytes32 mintingIntentHash = hashMintingIntent( _uuid, msg.sender, nonces[msg.sender], @@ -142,16 +153,13 @@ contract Staking is OpsManaged, StakingData { mintingIntentHash ); - nonces[msg.sender]++; - - return (true, amountUT); + return amountUT; } - // TODO: Come back to this - // when calling hashIntent, make sure the beneficiary is passed in as _staker + // when calling hashMintingIntent, make sure the beneficiary is passed in as _staker // if _uuid == a branded token, require(msg.sender == adminAddress) && require(_beneficiary == utilityTokens[_uuid].stakingAccount) // requires(eip20Token.transferFrom(_beneficiary, this, _amount)) - // Calls hashIntent + // Calls hashMintingIntent // Adds Stake to stakes // Emits event: MintingIntentDeclared function stakeFor(address _beneficiary, bytes32 _uuid, uint256 _amountST) external returns (bool, uint256) { @@ -164,7 +172,6 @@ contract Staking is OpsManaged, StakingData { } // @dev Checks msg.sender for purposes of MVU - // TBD: don't delete Stake after processing? function processStaking(bytes32 _uuid, bytes32 _mintingIntentHash) external returns (bool) { require(_uuid != ""); require(utilityTokens[_uuid].conversionRate > 0); @@ -189,20 +196,19 @@ contract Staking is OpsManaged, StakingData { uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _unstakingIntentHash - ) external returns (bool) { + ) external onlyAdmin returns (bool) { require(_uuid != ""); - require(utilityTokens[_uuid].conversionRate > 0); + require(utilityTokens[_uuid].conversionRate > 0); require(nonces[_unstaker] < _unstakerNonce); require(_amountST > 0); require(_amountUT > 0); require(_escrowUnlockHeight > 0); require(_unstakingIntentHash != ""); - bytes32 unstakingIntentHash = hashIntent( + bytes32 unstakingIntentHash = hashUnstakingIntent( _uuid, _unstaker, nonces[_unstaker], - _amountST, _amountUT, _escrowUnlockHeight ); @@ -220,7 +226,6 @@ contract Staking is OpsManaged, StakingData { return true; } - // TBD: don't delete Unstake after processing? function processUnstaking(bytes32 _uuid, bytes32 _unstakingIntentHash) public returns (bool) { require(_uuid != ""); require(utilityTokens[_uuid].conversionRate > 0); diff --git a/contracts/StakingData.sol b/contracts/StakingData.sol index bbdc4d75..b308e018 100644 --- a/contracts/StakingData.sol +++ b/contracts/StakingData.sol @@ -31,7 +31,9 @@ contract StakingData { EIP20Interface public eip20Token; // ~2 weeks, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT = 80667; + uint256 public constant BLOCKS_TO_WAIT_LONG = 80667; + // ~1hour, assuming ~15s per block + uint256 public constant BLOCKS_TO_WAIT_SHORT = 240; // [uuid == H(utility token name, chainId)] mapping(bytes32 => UtilityToken) public utilityTokens; diff --git a/contracts/UtilityToken.sol b/contracts/UtilityToken.sol index e88ae54a..d08b2c7f 100644 --- a/contracts/UtilityToken.sol +++ b/contracts/UtilityToken.sol @@ -13,7 +13,7 @@ pragma solidity ^0.4.17; // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// +// // ---------------------------------------------------------------------------- // Utility Token // @@ -32,7 +32,7 @@ import "./UtilityTokenData.sol"; contract UtilityToken is EIP20Token, UtilityTokenData { using SafeMath for uint256; - event UnstakingIntentDeclared(bytes32 indexed _uuid, address indexed _unstaker, uint256 _unstakerNonce, uint256 _amountST, uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _unstakingIntentHash /* redundant for abundance of clarity for MVU */); + event UnstakingIntentDeclared(bytes32 indexed _uuid, address indexed _unstaker, uint256 _unstakerNonce, uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _unstakingIntentHash /* redundant for abundance of clarity for MVU */); event Redeemed(bytes32 indexed _uuid, address indexed _account, uint256 _amount, uint256 _balance, uint256 _totalSupply); event MintingIntentConfirmed(bytes32 indexed _uuid, bytes32 _mintingIntentHash); event Minted(bytes32 indexed _uuid, address indexed _account, uint256 _amount, uint256 _balance, uint256 _totalSupply); @@ -48,8 +48,8 @@ contract UtilityToken is EIP20Token, UtilityTokenData { uuid = keccak256(_name, _chainId); } - // TBD: move to a parent contract because this is identical to `hashIntent` in Staking? - function hashIntent( + // TBD: move to a parent contract because this is identical to `hashMintingIntent` in Staking? + function hashMintingIntent( bytes32 _uuid, address _account, uint256 _accountNonce, @@ -61,26 +61,39 @@ contract UtilityToken is EIP20Token, UtilityTokenData { return keccak256(_uuid, _account, _accountNonce, _amountST, _amountUT, _escrowUnlockHeight); } + function hashUnstakingIntent( + bytes32 _uuid, + address _account, + uint256 _accountNonce, + uint256 _amountUT, + uint256 _escrowUnlockHeight + ) + public pure returns (bytes32) { + return keccak256(_uuid, _account, _accountNonce, _amountUT, _escrowUnlockHeight); + } + // @dev There's no need for msg.sender to approve this contract to transfer // because calling `transfer` from `redeem` has the same msg.sender - function redeem(uint256 _amountUT) public returns (bool, uint256) { + // TODO redeem needs user nonce from staking contract as parameter + // and must be strictly greater than stored nonce in UtilityToken + function redeem(uint256 _amountUT) public returns (bool) { require(_amountUT > 0); require(transfer(address(this), _amountUT)); - uint256 amountST = _amountUT.div(conversionRate); - uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT; - bytes32 unstakingIntentHash = hashIntent( + uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + bytes32 unstakingIntentHash = hashUnstakingIntent( uuid, msg.sender, nonces[msg.sender], - amountST, _amountUT, escrowUnlockHeight ); + + nonces[msg.sender]++; + redemptions[unstakingIntentHash] = Redemption({ redeemer: msg.sender, amountUT: _amountUT, - amountST: amountST, escrowUnlockHeight: escrowUnlockHeight }); @@ -89,26 +102,22 @@ contract UtilityToken is EIP20Token, UtilityTokenData { msg.sender, nonces[msg.sender], _amountUT, - amountST, escrowUnlockHeight, unstakingIntentHash ); - nonces[msg.sender]++; - - return (true, amountST); + return (true); } - // TBD: don't delete Redemption after processing? function processRedepmtion(bytes32 _unstakingIntentHash) public returns (bool) { require(_unstakingIntentHash != ""); require(redemptions[_unstakingIntentHash].redeemer == msg.sender); Redemption storage redemption = redemptions[_unstakingIntentHash]; - tokenTotalSupply = tokenTotalSupply.sub(redemption.amountST); - balances[address(this)] = balances[address(this)].sub(redemption.amountST); + tokenTotalSupply = tokenTotalSupply.sub(redemption.amountUT); + balances[address(this)] = balances[address(this)].sub(redemption.amountUT); - Redeemed(uuid, redemption.redeemer, redemption.amountST, balances[redemption.redeemer], tokenTotalSupply); + Redeemed(uuid, redemption.redeemer, redemption.amountUT, balances[redemption.redeemer], tokenTotalSupply); delete redemptions[_unstakingIntentHash]; @@ -131,10 +140,12 @@ contract UtilityToken is EIP20Token, UtilityTokenData { require(_escrowUnlockHeight > 0); require(_mintingIntentHash != ""); - bytes32 mintingIntentHash = hashIntent( + nonces[_minter] = _minterNonce; + + bytes32 mintingIntentHash = hashMintingIntent( _uuid, _minter, - nonces[_minter], + _minterNonce, _amountST, _amountUT, _escrowUnlockHeight @@ -146,14 +157,12 @@ contract UtilityToken is EIP20Token, UtilityTokenData { minter: _minter, amount: _amountUT }); - nonces[_minter]++; MintingIntentConfirmed(_uuid, mintingIntentHash); return true; } - // TBD: don't delete Mint after processing? function processMinting(bytes32 _mintingIntentHash) external returns (bool) { require(_mintingIntentHash != ""); diff --git a/contracts/UtilityTokenData.sol b/contracts/UtilityTokenData.sol index ef08a069..d3f0b6a9 100644 --- a/contracts/UtilityTokenData.sol +++ b/contracts/UtilityTokenData.sol @@ -29,7 +29,9 @@ contract UtilityTokenData { bytes32 public uuid; // ~2 weeks, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT = 80667; + uint256 public constant BLOCKS_TO_WAIT_LONG = 80667; + // ~1hour, assuming ~15s per block + uint256 public constant BLOCKS_TO_WAIT_SHORT = 240; // [staker address] mapping(address => uint) nonces; From 555f610da38ce6334d525abdc6cc3d899e8902db Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 28 Oct 2017 13:39:07 +0530 Subject: [PATCH 2/4] vagrant: local solc --- .gitignore | 6 +++- Vagrantfile | 67 +++++++++++++++++++++++++++++++++++++++++++ dev/local-poa-geth.sh | 24 ++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 Vagrantfile create mode 100644 dev/local-poa-geth.sh diff --git a/.gitignore b/.gitignore index b442727c..66888954 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ # OSX -.DS_Store \ No newline at end of file +.DS_Store + +# Vagrant +.vagrant/ +ubuntu-xenial-16.04-cloudimg-console.log diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 00000000..f579ad06 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,67 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "ubuntu/xenial64" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + config.vm.provision :shell, path: "dev/local-poa-geth.sh" +end diff --git a/dev/local-poa-geth.sh b/dev/local-poa-geth.sh new file mode 100644 index 00000000..5c662d53 --- /dev/null +++ b/dev/local-poa-geth.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# local PoA geth - PoA geth + +add-apt-repository -y ppa:ethereum/ethereum +apt-get update +# apt install -y build-essential +apt-get install -y ethereum solc + +#wget https://nodejs.org/dist/v8.7.0/node-v8.7.0.tar.gz +#tar zxf node-v8.7.0.tar.gz node-v8.7.0 +#cd node-v8.7.0 +#./configure && make && make install + +wget -q https://nodejs.org/dist/v8.7.0/node-v8.7.0-linux-x64.tar.xz +tar -xf node-v8.7.0-linux-x64.tar.xz +mv node-v8.7.0-linux-x64 /usr/local +ln -s /usr/local/node-v8.7.0-linux-x64/bin/node /usr/local/bin/node +ln -s /usr/local/node-v8.7.0-linux-x64/bin/npm /usr/local/bin/npm +ln -s /usr/local/node-v8.7.0-linux-x64/bin/npx /usr/local/bin/npx +rm node-v8.7.0-linux-x64.tar.xz + +npm install -g truffle +export PATH="$PATH:/usr/local/node-v8.7.0-linux-x64/bin" \ No newline at end of file From bf17ce15aef0322ba27b96c842030722641090e3 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 28 Oct 2017 13:52:39 +0530 Subject: [PATCH 3/4] contracts: add combined-json solc compile sh --- .gitignore | 4 ++++ contracts/compile.sh | 15 +++++++++++++++ 2 files changed, 19 insertions(+) create mode 100755 contracts/compile.sh diff --git a/.gitignore b/.gitignore index 66888954..e044f700 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ # Vagrant .vagrant/ ubuntu-xenial-16.04-cloudimg-console.log + +# don't commit contract binaries in protocol +# we do commit the contract binaries in platform +contracts/bin/ \ No newline at end of file diff --git a/contracts/compile.sh b/contracts/compile.sh new file mode 100755 index 00000000..61d87119 --- /dev/null +++ b/contracts/compile.sh @@ -0,0 +1,15 @@ +#! /bin/sh + +mkdir -p "./bin" + +echo "" +echo "Compiling Staking.sol" +echo "" + +solc --combined-json=abi,bin Staking.sol > ./bin/Staking.json + +echo "" +echo "Compiling UtilityToken.sol" +echo "" + +solc --combined-json=abi,bin UtilityToken.sol > ./bin/UtilityToken.json From 863869eb955aca2f8c26fbdfaa36f4e6219591ee Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 28 Oct 2017 14:00:22 +0530 Subject: [PATCH 4/4] contracts: compilation correction --- contracts/UtilityTokenData.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/UtilityTokenData.sol b/contracts/UtilityTokenData.sol index d3f0b6a9..cd94b03a 100644 --- a/contracts/UtilityTokenData.sol +++ b/contracts/UtilityTokenData.sol @@ -50,7 +50,6 @@ contract UtilityTokenData { struct Redemption { address redeemer; uint256 amountUT; - uint256 amountST; uint256 escrowUnlockHeight; } }