From 0ec09f4131078cd7d1143f9df8653fc36bff8e31 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Mon, 5 Dec 2016 11:32:41 -0800 Subject: [PATCH 01/11] Create tests for Shareable --- contracts/test-helpers/ShareableMock.sol | 19 +++++ test/Shareable.js | 103 +++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 contracts/test-helpers/ShareableMock.sol create mode 100644 test/Shareable.js diff --git a/contracts/test-helpers/ShareableMock.sol b/contracts/test-helpers/ShareableMock.sol new file mode 100644 index 000000000..82530e888 --- /dev/null +++ b/contracts/test-helpers/ShareableMock.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.4.4; +import "../Shareable.sol"; + +contract ShareableMock is Shareable { + + uint public count = 0; + + function ShareableMock(address[] _owners, uint _required) Shareable(_owners, _required) { + + } + + function increaseCount(bytes32 action) onlymanyowners(action) { + count = count + 1; + } + + function isOwnerConst(address _addr) constant returns (bool) { + return isOwner(_addr); + } +} diff --git a/test/Shareable.js b/test/Shareable.js new file mode 100644 index 000000000..883552856 --- /dev/null +++ b/test/Shareable.js @@ -0,0 +1,103 @@ +contract('Shareable', function(accounts) { + + it('should construct with correct owners and number of sigs required', async function() { + let requiredSigs = 2; + let owners = accounts.slice(1,4); + let shareable = await ShareableMock.new(owners, requiredSigs); + + let required = await shareable.required(); + assert.equal(required, requiredSigs); + let owner = await shareable.getOwner(0); + assert.equal(owner, accounts[0]); + + for(let i = 0; i < accounts.length; i++) { + let owner = await shareable.getOwner(i); + let isowner = await shareable.isOwnerConst(accounts[i]); + if(i <= owners.length) { + assert.equal(accounts[i], owner); + assert.isTrue(isowner); + } else { + assert.notEqual(accounts[i], owner); + assert.isFalse(isowner); + } + } + }); + + it('should only perform multisig function with enough sigs', async function() { + let requiredSigs = 3; + let owners = accounts.slice(1,4); + let shareable = await ShareableMock.new(owners, requiredSigs); + let hash = 1234; + + let initCount = await shareable.count(); + initCount = initCount.toString(); + + for(let i = 0; i < requiredSigs; i++) { + await shareable.increaseCount(hash, {from: accounts[i]}); + let count = await shareable.count(); + if(i == requiredSigs - 1) { + assert.equal(Number(initCount)+1, count.toString()); + } else { + assert.equal(initCount, count.toString()); + } + } + }); + + it('should require approval from different owners', async function() { + let requiredSigs = 2; + let owners = accounts.slice(1,4); + let shareable = await ShareableMock.new(owners, requiredSigs); + let hash = 1234; + + let initCount = await shareable.count(); + initCount = initCount.toString(); + + //Count shouldn't increase when the same owner calls repeatedly + for(let i = 0; i < 2; i++) { + await shareable.increaseCount(hash); + let count = await shareable.count(); + assert.equal(initCount, count.toString()); + } + }); + + it('should reset sig count after operation is approved', async function() { + let requiredSigs = 3; + let owners = accounts.slice(1,4); + let shareable = await ShareableMock.new(owners, requiredSigs); + let hash = 1234; + + let initCount = await shareable.count(); + initCount = initCount.toString(); + + for(let i = 0; i < requiredSigs * 3; i++) { + await shareable.increaseCount(hash, {from: accounts[i % 4]}); + let count = await shareable.count(); + if((i%(requiredSigs)) == requiredSigs - 1) { + initCount = Number(initCount)+1; + assert.equal(initCount, count.toString()); + } else { + assert.equal(initCount, count.toString()); + } + } + }); + + it('should not perform multisig function after an owner revokes', async function() { + let requiredSigs = 3; + let owners = accounts.slice(1,4); + let shareable = await ShareableMock.new(owners, requiredSigs); + let hash = 1234; + + let initCount = await shareable.count(); + initCount = initCount.toString(); + + for(let i = 0; i < requiredSigs; i++) { + if(i == 1) { + await shareable.revoke(hash, {from: accounts[i-1]}); + } + await shareable.increaseCount(hash, {from: accounts[i]}); + let count = await shareable.count(); + assert.equal(initCount, count.toString()); + } + }); + +}); From c390e94da6d0198b4ff190b131aa4b212650eef7 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 6 Dec 2016 11:45:02 -0800 Subject: [PATCH 02/11] Create tests for DayLimit --- contracts/test-helpers/DayLimitMock.sol | 15 ++++++ test/DayLimit.js | 72 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 contracts/test-helpers/DayLimitMock.sol create mode 100644 test/DayLimit.js diff --git a/contracts/test-helpers/DayLimitMock.sol b/contracts/test-helpers/DayLimitMock.sol new file mode 100644 index 000000000..6383652df --- /dev/null +++ b/contracts/test-helpers/DayLimitMock.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.4.4; +import "../DayLimit.sol"; + +contract DayLimitMock is DayLimit { + uint public totalSpending; + + function DayLimitMock(uint _value, address[] _owners, uint _required) DayLimit(_value) Shareable(_owners, _required) { + totalSpending = 0; + } + + function attemptSpend(uint _value) external limitedDaily(_value) { + totalSpending += _value; + } + +} diff --git a/test/DayLimit.js b/test/DayLimit.js new file mode 100644 index 000000000..3bb93f580 --- /dev/null +++ b/test/DayLimit.js @@ -0,0 +1,72 @@ +contract('DayLimit', function(accounts) { + + it('should construct with the passed daily limit', async function() { + let initLimit = 10; + let dayLimit = await DayLimitMock.new(initLimit, accounts, 2); + let dailyLimit = await dayLimit.dailyLimit(); + assert.equal(initLimit, dailyLimit); + }); + + it('should be able to spend if daily limit is not reached', async function() { + let limit = 10; + let dayLimit = await DayLimitMock.new(limit, accounts, 1); + + await dayLimit.attemptSpend(8); + let spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.attemptSpend(2); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 10); + }); + + it('should prevent spending if daily limit is reached', async function() { + let limit = 10; + let dayLimit = await DayLimitMock.new(limit, accounts, 1); + + await dayLimit.attemptSpend(8); + let spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.attemptSpend(3); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + }); + + it('should allow spending if daily limit is reached and then set higher', async function() { + let limit = 10; + let dayLimit = await DayLimitMock.new(limit, accounts, 1); + + await dayLimit.attemptSpend(8); + let spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.attemptSpend(3); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.setDailyLimit(15); + await dayLimit.attemptSpend(3); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 11); + }); + + it('should allow spending if daily limit is reached and then amount spent is reset', async function() { + let limit = 10; + let dayLimit = await DayLimitMock.new(limit, accounts, 1); + + await dayLimit.attemptSpend(8); + let spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.attemptSpend(3); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 8); + + await dayLimit.resetSpentToday(15); + await dayLimit.attemptSpend(3); + spentToday = await dayLimit.spentToday(); + assert.equal(spentToday, 3); + }); + +}); From e8459148a8b9a017f0801821762c787487c7e34e Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Mon, 19 Dec 2016 21:52:37 -0800 Subject: [PATCH 03/11] Remove unnecessary toString calls --- test/Shareable.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/Shareable.js b/test/Shareable.js index 883552856..36bb7a89d 100644 --- a/test/Shareable.js +++ b/test/Shareable.js @@ -67,16 +67,16 @@ contract('Shareable', function(accounts) { let hash = 1234; let initCount = await shareable.count(); - initCount = initCount.toString(); + //initCount = initCount.toString(); for(let i = 0; i < requiredSigs * 3; i++) { await shareable.increaseCount(hash, {from: accounts[i % 4]}); let count = await shareable.count(); if((i%(requiredSigs)) == requiredSigs - 1) { initCount = Number(initCount)+1; - assert.equal(initCount, count.toString()); + assert.equal(initCount, count); } else { - assert.equal(initCount, count.toString()); + assert.equal(initCount, count); } } }); @@ -88,7 +88,6 @@ contract('Shareable', function(accounts) { let hash = 1234; let initCount = await shareable.count(); - initCount = initCount.toString(); for(let i = 0; i < requiredSigs; i++) { if(i == 1) { @@ -96,7 +95,7 @@ contract('Shareable', function(accounts) { } await shareable.increaseCount(hash, {from: accounts[i]}); let count = await shareable.count(); - assert.equal(initCount, count.toString()); + assert.equal(initCount, count); } }); From e5eaa919b119a0c114d567090bd13a5841a676cb Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 20 Dec 2016 13:02:24 -0800 Subject: [PATCH 04/11] Make isOwner() const. Add necessary toString() calls in tests. --- contracts/Shareable.sol | 5 ++--- contracts/test-helpers/ShareableMock.sol | 3 --- test/Shareable.js | 7 +++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/contracts/Shareable.sol b/contracts/Shareable.sol index b73f31eee..d416f80be 100644 --- a/contracts/Shareable.sol +++ b/contracts/Shareable.sol @@ -4,7 +4,7 @@ pragma solidity ^0.4.4; /* * Shareable * - * Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol + * Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol * * inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a single, or, crucially, each of a number of, designated owners. * @@ -98,7 +98,7 @@ contract Shareable { return address(owners[ownerIndex + 1]); } - function isOwner(address _addr) returns (bool) { + function isOwner(address _addr) constant returns (bool) { return ownerIndex[uint(_addr)] > 0; } @@ -162,4 +162,3 @@ contract Shareable { } } - diff --git a/contracts/test-helpers/ShareableMock.sol b/contracts/test-helpers/ShareableMock.sol index 82530e888..ebf06546e 100644 --- a/contracts/test-helpers/ShareableMock.sol +++ b/contracts/test-helpers/ShareableMock.sol @@ -13,7 +13,4 @@ contract ShareableMock is Shareable { count = count + 1; } - function isOwnerConst(address _addr) constant returns (bool) { - return isOwner(_addr); - } } diff --git a/test/Shareable.js b/test/Shareable.js index 36bb7a89d..fba268268 100644 --- a/test/Shareable.js +++ b/test/Shareable.js @@ -12,7 +12,7 @@ contract('Shareable', function(accounts) { for(let i = 0; i < accounts.length; i++) { let owner = await shareable.getOwner(i); - let isowner = await shareable.isOwnerConst(accounts[i]); + let isowner = await shareable.isOwner(accounts[i]); if(i <= owners.length) { assert.equal(accounts[i], owner); assert.isTrue(isowner); @@ -67,7 +67,6 @@ contract('Shareable', function(accounts) { let hash = 1234; let initCount = await shareable.count(); - //initCount = initCount.toString(); for(let i = 0; i < requiredSigs * 3; i++) { await shareable.increaseCount(hash, {from: accounts[i % 4]}); @@ -76,7 +75,7 @@ contract('Shareable', function(accounts) { initCount = Number(initCount)+1; assert.equal(initCount, count); } else { - assert.equal(initCount, count); + assert.equal(initCount.toString(), count); } } }); @@ -95,7 +94,7 @@ contract('Shareable', function(accounts) { } await shareable.increaseCount(hash, {from: accounts[i]}); let count = await shareable.count(); - assert.equal(initCount, count); + assert.equal(initCount.toString(), count); } }); From 9c81dd29bbe6848afb056d407dfad3db8ed8ff32 Mon Sep 17 00:00:00 2001 From: Arkadiy Kukarkin Date: Wed, 21 Dec 2016 14:26:32 -0500 Subject: [PATCH 05/11] Fix example contracts --- contracts/examples/BadPushPayments.sol | 2 +- contracts/examples/GoodPullPayments.sol | 2 +- contracts/examples/PullPaymentBid.sol | 2 +- contracts/examples/StoppableBid.sol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/examples/BadPushPayments.sol b/contracts/examples/BadPushPayments.sol index 2d85ffeec..7bc11c665 100644 --- a/contracts/examples/BadPushPayments.sol +++ b/contracts/examples/BadPushPayments.sol @@ -7,7 +7,7 @@ contract BadPushPayments { address highestBidder; uint highestBid; - function bid() { + function bid() payable { if (msg.value < highestBid) throw; if (highestBidder != 0) { diff --git a/contracts/examples/GoodPullPayments.sol b/contracts/examples/GoodPullPayments.sol index 1469eb57f..fc0cd986b 100644 --- a/contracts/examples/GoodPullPayments.sol +++ b/contracts/examples/GoodPullPayments.sol @@ -6,7 +6,7 @@ contract GoodPullPayments { uint highestBid; mapping(address => uint) refunds; - function bid() external { + function bid() external payable { if (msg.value < highestBid) throw; if (highestBidder != 0) { diff --git a/contracts/examples/PullPaymentBid.sol b/contracts/examples/PullPaymentBid.sol index ad4b612b5..8ba51422f 100644 --- a/contracts/examples/PullPaymentBid.sol +++ b/contracts/examples/PullPaymentBid.sol @@ -8,7 +8,7 @@ contract PullPaymentBid is PullPayment { address public highestBidder; uint public highestBid; - function bid() external { + function bid() external payable { if (msg.value <= highestBid) throw; if (highestBidder != 0) { diff --git a/contracts/examples/StoppableBid.sol b/contracts/examples/StoppableBid.sol index 61b9d43aa..53e1aebdc 100644 --- a/contracts/examples/StoppableBid.sol +++ b/contracts/examples/StoppableBid.sol @@ -9,7 +9,7 @@ contract StoppableBid is Stoppable, PullPayment { address public highestBidder; uint public highestBid; - function bid() external stopInEmergency { + function bid() external payable stopInEmergency { if (msg.value <= highestBid) throw; if (highestBidder != 0) { From 36fa2a72cfa8ffe832e62b781e5666267c7d71d9 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Wed, 21 Dec 2016 16:41:06 -0800 Subject: [PATCH 06/11] Decouple Shareable from DayLimit --- contracts/DayLimit.sol | 14 +++++++------- contracts/MultisigWallet.sol | 8 ++++++++ contracts/test-helpers/DayLimitMock.sol | 2 +- test/DayLimit.js | 10 +++++----- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/contracts/DayLimit.sol b/contracts/DayLimit.sol index d4f1cb521..7a301fb58 100644 --- a/contracts/DayLimit.sol +++ b/contracts/DayLimit.sol @@ -11,7 +11,7 @@ import './Shareable.sol'; * on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method * uses is specified in the modifier. */ -contract DayLimit is Shareable { +contract DayLimit { // FIELDS uint public dailyLimit; @@ -38,13 +38,13 @@ contract DayLimit is Shareable { // METHODS - // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. - function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { + // (re)sets the daily limit. doesn't alter the amount already spent today. + function setDailyLimit(uint _newLimit) external { dailyLimit = _newLimit; } - // resets the amount already spent today. needs many of the owners to confirm - function resetSpentToday() onlymanyowners(sha3(msg.data)) external { + // resets the amount already spent today. + function resetSpentToday() external { spentToday = 0; } @@ -53,14 +53,14 @@ contract DayLimit is Shareable { // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and // returns true. otherwise just returns false. - function underLimit(uint _value) internal onlyOwner returns (bool) { + function underLimit(uint _value) internal returns (bool) { // reset the spend limit if we're on a different day to last time. if (today() > lastDay) { spentToday = 0; lastDay = today(); } // check to see if there's enough left - if so, subtract and return true. - // overflow protection // dailyLimit check + // overflow protection // dailyLimit check if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) { spentToday += _value; return true; diff --git a/contracts/MultisigWallet.sol b/contracts/MultisigWallet.sol index 1eb24c091..c5e76b060 100644 --- a/contracts/MultisigWallet.sol +++ b/contracts/MultisigWallet.sol @@ -83,6 +83,14 @@ contract MultisigWallet is Multisig, Shareable, DayLimit { } } + function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { + this.setDailyLimit(_newLimit); + } + + function resetSpentToday() onlymanyowners(sha3(msg.data)) external { + this.resetSpentToday(); + } + // INTERNAL METHODS diff --git a/contracts/test-helpers/DayLimitMock.sol b/contracts/test-helpers/DayLimitMock.sol index 6383652df..74cd71e6b 100644 --- a/contracts/test-helpers/DayLimitMock.sol +++ b/contracts/test-helpers/DayLimitMock.sol @@ -4,7 +4,7 @@ import "../DayLimit.sol"; contract DayLimitMock is DayLimit { uint public totalSpending; - function DayLimitMock(uint _value, address[] _owners, uint _required) DayLimit(_value) Shareable(_owners, _required) { + function DayLimitMock(uint _value) DayLimit(_value) { totalSpending = 0; } diff --git a/test/DayLimit.js b/test/DayLimit.js index 3bb93f580..559001f5d 100644 --- a/test/DayLimit.js +++ b/test/DayLimit.js @@ -2,14 +2,14 @@ contract('DayLimit', function(accounts) { it('should construct with the passed daily limit', async function() { let initLimit = 10; - let dayLimit = await DayLimitMock.new(initLimit, accounts, 2); + let dayLimit = await DayLimitMock.new(initLimit); let dailyLimit = await dayLimit.dailyLimit(); assert.equal(initLimit, dailyLimit); }); it('should be able to spend if daily limit is not reached', async function() { let limit = 10; - let dayLimit = await DayLimitMock.new(limit, accounts, 1); + let dayLimit = await DayLimitMock.new(limit); await dayLimit.attemptSpend(8); let spentToday = await dayLimit.spentToday(); @@ -22,7 +22,7 @@ contract('DayLimit', function(accounts) { it('should prevent spending if daily limit is reached', async function() { let limit = 10; - let dayLimit = await DayLimitMock.new(limit, accounts, 1); + let dayLimit = await DayLimitMock.new(limit); await dayLimit.attemptSpend(8); let spentToday = await dayLimit.spentToday(); @@ -35,7 +35,7 @@ contract('DayLimit', function(accounts) { it('should allow spending if daily limit is reached and then set higher', async function() { let limit = 10; - let dayLimit = await DayLimitMock.new(limit, accounts, 1); + let dayLimit = await DayLimitMock.new(limit); await dayLimit.attemptSpend(8); let spentToday = await dayLimit.spentToday(); @@ -53,7 +53,7 @@ contract('DayLimit', function(accounts) { it('should allow spending if daily limit is reached and then amount spent is reset', async function() { let limit = 10; - let dayLimit = await DayLimitMock.new(limit, accounts, 1); + let dayLimit = await DayLimitMock.new(limit); await dayLimit.attemptSpend(8); let spentToday = await dayLimit.spentToday(); From 8aa06bda3821f98468dd0448ba7d799a107b975a Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Thu, 8 Dec 2016 18:01:35 -0800 Subject: [PATCH 07/11] Create tests for MultisigWallet --- contracts/test-helpers/MultisigWalletMock.sol | 12 ++ test/MultisigWallet.js | 163 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 contracts/test-helpers/MultisigWalletMock.sol create mode 100644 test/MultisigWallet.js diff --git a/contracts/test-helpers/MultisigWalletMock.sol b/contracts/test-helpers/MultisigWalletMock.sol new file mode 100644 index 000000000..75ab67dbb --- /dev/null +++ b/contracts/test-helpers/MultisigWalletMock.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.4; +import "../MultisigWallet.sol"; + +contract MultisigWalletMock is MultisigWallet { + uint public totalSpending; + + function MultisigWalletMock(address[] _owners, uint _required, uint _daylimit) + MultisigWallet(_owners, _required, _daylimit) { } + + function changeOwner(address _from, address _to) external { } + +} diff --git a/test/MultisigWallet.js b/test/MultisigWallet.js new file mode 100644 index 000000000..32c1ec781 --- /dev/null +++ b/test/MultisigWallet.js @@ -0,0 +1,163 @@ +contract('MultisigWallet', function(accounts) { + //from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6 + web3.eth.getTransactionReceiptMined = function (txnHash, interval) { + var transactionReceiptAsync; + interval = interval ? interval : 500; + transactionReceiptAsync = function(txnHash, resolve, reject) { + try { + var receipt = web3.eth.getTransactionReceipt(txnHash); + if (receipt == null) { + setTimeout(function () { + transactionReceiptAsync(txnHash, resolve, reject); + }, interval); + } else { + resolve(receipt); + } + } catch(e) { + reject(e); + } + }; + + if (Array.isArray(txnHash)) { + var promises = []; + txnHash.forEach(function (oneTxHash) { + promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval)); + }); + return Promise.all(promises); + } else { + return new Promise(function (resolve, reject) { + transactionReceiptAsync(txnHash, resolve, reject); + }); + } +}; + + + it('should send balance to passed address upon death', async function() { + //Give account[0] 20 ether + web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { + if(err) + console.log("ERROR:" + err); + }); + + let dailyLimit = 10; + let ownersRequired = 2; + + //Create MultisigWallet contract with 10 ether + let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')}); + + //Get balances of owner and wallet after wallet creation. + let ownerBalance = web3.eth.getBalance(accounts[0]); + let walletBalance = web3.eth.getBalance(wallet.address); + let hash = 1234; + + //Call kill function from two different owner accounts, satisfying owners required + await wallet.kill(accounts[0], {data: hash}); + let txnHash = await wallet.kill(accounts[0], {from: accounts[1], data: hash}); + + let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Get balances of owner and wallet after kill function is complete, compare with previous values + let newOwnerBalance = web3.eth.getBalance(accounts[0]); + let newWalletBalance = web3.eth.getBalance(wallet.address); + + assert.isTrue(newOwnerBalance > ownerBalance); + assert.isTrue(newWalletBalance < walletBalance); + }); + + it('should execute transaction if below daily limit', async function() { + //Give account[0] 20 ether + web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { + if(err) + console.log("ERROR:" + err); + }); + + let dailyLimit = 10; + let ownersRequired = 2; + + //Create MultisigWallet contract with 10 ether + let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')}); + + let accountBalance = web3.eth.getBalance(accounts[2]); + let hash = 1234; + + //Owner account0 commands wallet to send 9 wei to account2 + let txnHash = await wallet.execute(accounts[2], 9, hash); + let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Balance of account2 should have increased + let newAccountBalance = web3.eth.getBalance(accounts[2]); + assert.isTrue(newAccountBalance > accountBalance); + }); + + it('should prevent execution of transaction if above daily limit', async function() { + //Give account[0] 20 ether + web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { + if(err) + console.log("ERROR:" + err); + }); + + let dailyLimit = 10; + let ownersRequired = 2; + + //Create MultisigWallet contract with 10 ether + let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')}); + + let accountBalance = web3.eth.getBalance(accounts[2]); + let hash = 1234; + + //Owner account0 commands wallet to send 9 wei to account2 + let txnHash = await wallet.execute(accounts[2], 9, hash); + let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Balance of account2 should have increased + let newAccountBalance = web3.eth.getBalance(accounts[2]); + assert.isTrue(newAccountBalance > accountBalance); + + accountBalance = newAccountBalance; + hash = 4567; + + //Owner account0 commands wallet to send 2 more wei to account2, going over the daily limit of 10 + txnHash = await wallet.execute(accounts[2], 2, hash); + receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Balance of account2 should not change + newAccountBalance = web3.eth.getBalance(accounts[2]); + assert.equal(newAccountBalance.toString(), accountBalance.toString()); + }); + + it('should execute transaction if above daily limit and enough owners approve', async function() { + //Give account[0] 20 ether + web3.eth.sendTransaction({from: web3.eth.coinbase, to: accounts[0], value: web3.toWei('20','ether')}, function(err, result) { + if(err) + console.log("ERROR:" + err); + }); + + let dailyLimit = 10; + let ownersRequired = 2; + + //Create MultisigWallet contract with 10 ether + let wallet = await MultisigWalletMock.new(accounts, ownersRequired, dailyLimit, {value: web3.toWei('10', 'ether')}); + + let accountBalance = web3.eth.getBalance(accounts[2]); + let hash = 1234; + + //Owner account0 commands wallet to send 11 wei to account2 + let txnHash = await wallet.execute(accounts[2], 11, hash); + let receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Balance of account2 should not change + let newAccountBalance = web3.eth.getBalance(accounts[2]); + assert.equal(newAccountBalance.toString(), accountBalance.toString()); + + accountBalance = newAccountBalance; + + //Owner account1 commands wallet to send 11 wei to account2 + txnHash = await wallet.execute(accounts[2], 2, hash); + receiptMined = await web3.eth.getTransactionReceiptMined(txnHash); + + //Balance of account2 should change + newAccountBalance = web3.eth.getBalance(accounts[2]); + assert.isTrue(newAccountBalance > accountBalance); + }); + +}); From 813c1ff0cd990a63519993d71aa8dfcec77cf131 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 20 Dec 2016 13:12:45 -0800 Subject: [PATCH 08/11] Add payable to MultisigWalletMock() --- contracts/test-helpers/MultisigWalletMock.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test-helpers/MultisigWalletMock.sol b/contracts/test-helpers/MultisigWalletMock.sol index 75ab67dbb..19adc558d 100644 --- a/contracts/test-helpers/MultisigWalletMock.sol +++ b/contracts/test-helpers/MultisigWalletMock.sol @@ -5,7 +5,7 @@ contract MultisigWalletMock is MultisigWallet { uint public totalSpending; function MultisigWalletMock(address[] _owners, uint _required, uint _daylimit) - MultisigWallet(_owners, _required, _daylimit) { } + MultisigWallet(_owners, _required, _daylimit) payable { } function changeOwner(address _from, address _to) external { } From 1529e19b09c2719e52f3757e9ac829e3c2a2ac09 Mon Sep 17 00:00:00 2001 From: Demian Elias Brener Date: Fri, 23 Dec 2016 14:19:20 -0300 Subject: [PATCH 09/11] introductory guide to smart contracts --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dbdb39f8c..40b00e27c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ With Zeppelin, you can build distributed applications, protocols and organizatio - using common contract security patterns (See [Onward with Ethereum Smart Contract Security](https://medium.com/bitcorps-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.y3kvdetbz)) - in the [Solidity language](http://solidity.readthedocs.io/en/develop/). +> NOTE: New to smart contract development? Check our [introductory guide](https://github.com/OpenZeppelin/zeppelin-solidity/issues/121). + ## Getting Started Zeppelin integrates with [Truffle](https://github.com/ConsenSys/truffle), an Ethereum development environment. Please install Truffle and initialize your project with `truffle init`. From c21006e6d30e4c832f1903351d655b3696d5c20c Mon Sep 17 00:00:00 2001 From: Demian Elias Brener Date: Fri, 23 Dec 2016 14:29:17 -0300 Subject: [PATCH 10/11] fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40b00e27c..bf8353e83 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ With Zeppelin, you can build distributed applications, protocols and organizatio - using common contract security patterns (See [Onward with Ethereum Smart Contract Security](https://medium.com/bitcorps-blog/onward-with-ethereum-smart-contract-security-97a827e47702#.y3kvdetbz)) - in the [Solidity language](http://solidity.readthedocs.io/en/develop/). -> NOTE: New to smart contract development? Check our [introductory guide](https://github.com/OpenZeppelin/zeppelin-solidity/issues/121). +> NOTE: New to smart contract development? Check our [introductory guide](https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05#.cox40d2ut). ## Getting Started From a5602e7e7b13cc35c19b5e00af534b384105016e Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Mon, 26 Dec 2016 13:59:21 -0800 Subject: [PATCH 11/11] Make resetSpentToday and setDailyLimit internal functions --- contracts/DayLimit.sol | 4 ++-- contracts/MultisigWallet.sol | 4 ++-- contracts/test-helpers/DayLimitMock.sol | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/contracts/DayLimit.sol b/contracts/DayLimit.sol index 7a301fb58..41f9e4994 100644 --- a/contracts/DayLimit.sol +++ b/contracts/DayLimit.sol @@ -39,12 +39,12 @@ contract DayLimit { // METHODS // (re)sets the daily limit. doesn't alter the amount already spent today. - function setDailyLimit(uint _newLimit) external { + function _setDailyLimit(uint _newLimit) internal { dailyLimit = _newLimit; } // resets the amount already spent today. - function resetSpentToday() external { + function _resetSpentToday() internal { spentToday = 0; } diff --git a/contracts/MultisigWallet.sol b/contracts/MultisigWallet.sol index c5e76b060..867de28ff 100644 --- a/contracts/MultisigWallet.sol +++ b/contracts/MultisigWallet.sol @@ -84,11 +84,11 @@ contract MultisigWallet is Multisig, Shareable, DayLimit { } function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { - this.setDailyLimit(_newLimit); + _setDailyLimit(_newLimit); } function resetSpentToday() onlymanyowners(sha3(msg.data)) external { - this.resetSpentToday(); + _resetSpentToday(); } diff --git a/contracts/test-helpers/DayLimitMock.sol b/contracts/test-helpers/DayLimitMock.sol index 74cd71e6b..0ba5ca23f 100644 --- a/contracts/test-helpers/DayLimitMock.sol +++ b/contracts/test-helpers/DayLimitMock.sol @@ -12,4 +12,12 @@ contract DayLimitMock is DayLimit { totalSpending += _value; } + function setDailyLimit(uint _newLimit) external { + _setDailyLimit(_newLimit); + } + + function resetSpentToday() external { + _resetSpentToday(); + } + }