From a98c5b8865ada33885212824d076711590ff5947 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 17:27:46 +0100 Subject: [PATCH 01/54] Add bounty test to test against checkInvarient --- contracts/Bounty.sol | 4 ++++ test/Bounty.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/Bounty.js diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 3f8bf8d4a..d6945df65 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -23,6 +23,10 @@ contract Bounty is PullPayment { return target; } + function checkInvarient() returns(bool){ + return true; + } + function claim(SimpleToken target) { address researcher = researchers[target]; if (researcher == 0) throw; diff --git a/test/Bounty.js b/test/Bounty.js new file mode 100644 index 000000000..4cb3e0325 --- /dev/null +++ b/test/Bounty.js @@ -0,0 +1,14 @@ +contract('Bounty', function(accounts) { + it.only("create target", function(done){ + var bounty = Bounty.deployed(); + + bounty.createTarget(). + then(function() { + return bounty.checkInvarient.call() + }). + then(function(result) { + assert.isTrue(result); + }). + then(done); + }) +}); From 93454369577fcf513d4482b5612a297be6c4db39 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:01:17 +0100 Subject: [PATCH 02/54] Swap SimpleToken with Target --- contracts/Bounty.sol | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index d6945df65..6296b911b 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -1,15 +1,21 @@ pragma solidity ^0.4.0; import './PullPayment.sol'; -import './token/SimpleToken.sol'; /* * Bounty * This bounty will pay out if you can cause a SimpleToken's balance - * to be lower than its totalSupply, which would mean that it doesn't + * to be lower than its totalSupply, which would mean that it doesn't * have sufficient ether for everyone to withdraw. */ -contract Bounty is PullPayment { +contract Target { + function checkInvarient() returns(bool){ + return true; + } +} + +contract Bounty is PullPayment { + Target target; bool public claimed; mapping(address => address) public researchers; @@ -17,22 +23,22 @@ contract Bounty is PullPayment { if (claimed) throw; } - function createTarget() returns(SimpleToken) { - SimpleToken target = new SimpleToken(); + function createTarget() returns(Target) { + target = new Target(); researchers[target] = msg.sender; return target; } function checkInvarient() returns(bool){ - return true; + return target.checkInvarient(); } - function claim(SimpleToken target) { + function claim(Target target) { address researcher = researchers[target]; if (researcher == 0) throw; - // Check SimpleToken contract invariants + // Check Target contract invariants // Customize this to the specifics of your contract - if (target.totalSupply() == target.balance) { + if (!target.checkInvarient()) { throw; } asyncSend(researcher, this.balance); From 986509934b678188e1abec640dc30c69d00a2a3c Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:16:42 +0100 Subject: [PATCH 03/54] Swap target contract at test by using abstract interface --- contracts/Bounty.sol | 9 +++------ contracts/test-helpers/InsecureTargetMock.sol | 7 +++++++ contracts/test-helpers/SecureTargetMock.sol | 7 +++++++ migrations/2_deploy_contracts.js | 2 ++ test/Bounty.js | 19 ++++++++++++++++--- 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 contracts/test-helpers/InsecureTargetMock.sol create mode 100644 contracts/test-helpers/SecureTargetMock.sol diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 6296b911b..71fb82838 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -9,9 +9,7 @@ import './PullPayment.sol'; */ contract Target { - function checkInvarient() returns(bool){ - return true; - } + function checkInvarient() returns(bool); } contract Bounty is PullPayment { @@ -23,8 +21,8 @@ contract Bounty is PullPayment { if (claimed) throw; } - function createTarget() returns(Target) { - target = new Target(); + function createTarget(address targetAddress) returns(Target) { + target = Target(targetAddress); researchers[target] = msg.sender; return target; } @@ -37,7 +35,6 @@ contract Bounty is PullPayment { address researcher = researchers[target]; if (researcher == 0) throw; // Check Target contract invariants - // Customize this to the specifics of your contract if (!target.checkInvarient()) { throw; } diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol new file mode 100644 index 000000000..101d371e9 --- /dev/null +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.0; + +contract InsecureTargetMock { + function checkInvarient() returns(bool){ + return false; + } +} diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol new file mode 100644 index 000000000..67e599018 --- /dev/null +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.0; + +contract SecureTargetMock { + function checkInvarient() returns(bool){ + return true; + } +} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index b29a532cc..692a3013d 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -5,4 +5,6 @@ module.exports = function(deployer) { deployer.deploy(Bounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); + deployer.deploy(SecureTargetMock); + deployer.deploy(InsecureTargetMock); }; diff --git a/test/Bounty.js b/test/Bounty.js index 4cb3e0325..e214b9683 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,8 +1,8 @@ contract('Bounty', function(accounts) { - it.only("create target", function(done){ + it.only("can call checkInvarient for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); - - bounty.createTarget(). + var target = SecureTargetMock.deployed(); + bounty.createTarget(target.address). then(function() { return bounty.checkInvarient.call() }). @@ -11,4 +11,17 @@ contract('Bounty', function(accounts) { }). then(done); }) + + it("can call checkInvarient for InsecureTargetMock", function(done){ + var bounty = Bounty.deployed(); + var target = InsecureTargetMock.deployed(); + bounty.createTarget(target.address). + then(function() { + return bounty.checkInvarient.call() + }). + then(function(result) { + assert.isFalse(result); + }). + then(done); + }) }); From f19a69beb4926a77a8491d20adecbe159a32eb77 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:18:02 +0100 Subject: [PATCH 04/54] Remove only --- test/Bounty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bounty.js b/test/Bounty.js index e214b9683..bb731b8ec 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,5 +1,5 @@ contract('Bounty', function(accounts) { - it.only("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvarient for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = SecureTargetMock.deployed(); bounty.createTarget(target.address). From bc3f2a66cc125cba21428cb0be6ec3f6b3236e57 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:26:25 +0100 Subject: [PATCH 05/54] Deploy mock contracts only for test --- migrations/2_deploy_contracts.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 692a3013d..09f969053 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -5,6 +5,8 @@ module.exports = function(deployer) { deployer.deploy(Bounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); - deployer.deploy(SecureTargetMock); - deployer.deploy(InsecureTargetMock); + if(deployer.network == 'test'){ + deployer.deploy(SecureTargetMock); + deployer.deploy(InsecureTargetMock); + }; }; From 334073a64fc976c38134a04a9b92f88518c8647f Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:29:07 +0100 Subject: [PATCH 06/54] Fix typo --- contracts/Bounty.sol | 8 ++++---- contracts/test-helpers/InsecureTargetMock.sol | 2 +- contracts/test-helpers/SecureTargetMock.sol | 2 +- test/Bounty.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 71fb82838..2bee51e98 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -9,7 +9,7 @@ import './PullPayment.sol'; */ contract Target { - function checkInvarient() returns(bool); + function checkInvariant() returns(bool); } contract Bounty is PullPayment { @@ -27,15 +27,15 @@ contract Bounty is PullPayment { return target; } - function checkInvarient() returns(bool){ - return target.checkInvarient(); + function checkInvariant() returns(bool){ + return target.checkInvariant(); } function claim(Target target) { address researcher = researchers[target]; if (researcher == 0) throw; // Check Target contract invariants - if (!target.checkInvarient()) { + if (!target.checkInvariant()) { throw; } asyncSend(researcher, this.balance); diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 101d371e9..2918361a8 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.0; contract InsecureTargetMock { - function checkInvarient() returns(bool){ + function checkInvariant() returns(bool){ return false; } } diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index 67e599018..bd3a7dae2 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.0; contract SecureTargetMock { - function checkInvarient() returns(bool){ + function checkInvariant() returns(bool){ return true; } } diff --git a/test/Bounty.js b/test/Bounty.js index bb731b8ec..68b567a8d 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,10 +1,10 @@ contract('Bounty', function(accounts) { - it("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvariant for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = SecureTargetMock.deployed(); bounty.createTarget(target.address). then(function() { - return bounty.checkInvarient.call() + return bounty.checkInvariant.call() }). then(function(result) { assert.isTrue(result); @@ -12,12 +12,12 @@ contract('Bounty', function(accounts) { then(done); }) - it("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvariant for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = InsecureTargetMock.deployed(); bounty.createTarget(target.address). then(function() { - return bounty.checkInvarient.call() + return bounty.checkInvariant.call() }). then(function(result) { assert.isFalse(result); From 47b2c6d668a3255f5b2d274ab00a45c06b5f581b Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 23:29:13 +0100 Subject: [PATCH 07/54] WIP Target contract creation via factory pattern --- contracts/Bounty.sol | 8 ++++++-- contracts/test-helpers/InsecureTargetMock.sol | 6 ++++++ contracts/test-helpers/SecureTargetMock.sol | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 2bee51e98..c3bde8cf4 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -8,6 +8,10 @@ import './PullPayment.sol'; * have sufficient ether for everyone to withdraw. */ +contract Factory { + function deployContract() returns (address); +} + contract Target { function checkInvariant() returns(bool); } @@ -21,8 +25,8 @@ contract Bounty is PullPayment { if (claimed) throw; } - function createTarget(address targetAddress) returns(Target) { - target = Target(targetAddress); + function createTarget(address factoryAddress) returns(Target) { + target = Target(Factory(factoryAddress).deployContract()); researchers[target] = msg.sender; return target; } diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 2918361a8..8b85b6eb9 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -5,3 +5,9 @@ contract InsecureTargetMock { return false; } } + +contract Deployer { + function deployContract() returns (address) { + return new InsecureTargetMock(); + } +} diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index bd3a7dae2..382c4b53d 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -5,3 +5,9 @@ contract SecureTargetMock { return true; } } + +contract Deployer { + function deployContract() returns (address) { + return new SecureTargetMock(); + } +} From f0ed649db3cc64d61125f4b7b84af5a1e7a3dada Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 25 Oct 2016 10:15:37 -0700 Subject: [PATCH 08/54] Add release method to Stoppable. Create StoppableMock --- contracts/Stoppable.sol | 7 ++++++- contracts/test-helpers/StoppableMock.sol | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 contracts/test-helpers/StoppableMock.sol diff --git a/contracts/Stoppable.sol b/contracts/Stoppable.sol index d649ce019..99ad5ad3b 100644 --- a/contracts/Stoppable.sol +++ b/contracts/Stoppable.sol @@ -2,7 +2,7 @@ pragma solidity ^0.4.0; /* * Stoppable * Abstract contract that allows children to implement an - * emergency stop mechanism. + * emergency stop mechanism. */ contract Stoppable { address public curator; @@ -21,4 +21,9 @@ contract Stoppable { stopped = true; } + function release() external onlyInEmergency { + if (msg.sender != curator) throw; + stopped = false; + } + } diff --git a/contracts/test-helpers/StoppableMock.sol b/contracts/test-helpers/StoppableMock.sol new file mode 100644 index 000000000..9cbd8a562 --- /dev/null +++ b/contracts/test-helpers/StoppableMock.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.4.0; +import '../Stoppable.sol'; + +// mock class using Stoppable +contract StoppableMock is Stoppable { + bool public drasticMeasureTaken; + uint public count = 0; + + function normalProcess() external stopInEmergency { + count++; + } + + function drasticMeasure() external onlyInEmergency { + drasticMeasureTaken = true; + } + +} From dfc133d9afe395a10fbe741feb76811419bd0d79 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 25 Oct 2016 20:43:35 -0700 Subject: [PATCH 09/54] Create tests for Stoppable. --- contracts/test-helpers/StoppableMock.sol | 9 +- test/Stoppable.js | 108 +++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 test/Stoppable.js diff --git a/contracts/test-helpers/StoppableMock.sol b/contracts/test-helpers/StoppableMock.sol index 9cbd8a562..dcb9834f5 100644 --- a/contracts/test-helpers/StoppableMock.sol +++ b/contracts/test-helpers/StoppableMock.sol @@ -2,9 +2,14 @@ pragma solidity ^0.4.0; import '../Stoppable.sol'; // mock class using Stoppable -contract StoppableMock is Stoppable { +contract StoppableMock is Stoppable(msg.sender) { bool public drasticMeasureTaken; - uint public count = 0; + uint public count; + + function StoppableMock() Stoppable(msg.sender){ + drasticMeasureTaken = false; + count = 0; + } function normalProcess() external stopInEmergency { count++; diff --git a/test/Stoppable.js b/test/Stoppable.js new file mode 100644 index 000000000..f4ecc0e34 --- /dev/null +++ b/test/Stoppable.js @@ -0,0 +1,108 @@ +contract('Stoppable', function(accounts) { + + it("can perform normal process in non-emergency", function(done) { + var stoppable; + return StoppableMock.new(accounts[0]) + .then(function(_stoppable) { + stoppable = _stoppable; + return stoppable.count(); + }) + .then(function(count) { + assert.equal(count, 0); + }) + .then(function () { + return stoppable.normalProcess(); + }) + .then(function() { + return stoppable.count(); + }) + .then(function(count) { + assert.equal(count, 1); + }) + .then(done); + }); + + it("can not perform normal process in emergency", function(done) { + var stoppable; + return StoppableMock.new(accounts[0]) + .then(function(_stoppable) { + stoppable = _stoppable; + return stoppable.emergencyStop(); + }) + .then(function () { + return stoppable.count(); + }) + .then(function(count) { + assert.equal(count, 0); + }) + .then(function () { + return stoppable.normalProcess(); + }) + .then(function() { + return stoppable.count(); + }) + .then(function(count) { + assert.equal(count, 0); + }) + .then(done); + }); + + + it("can not take drastic measure in non-emergency", function(done) { + var stoppable; + return StoppableMock.new(accounts[0]) + .then(function(_stoppable) { + stoppable = _stoppable; + return stoppable.drasticMeasure(); + }) + .then(function() { + return stoppable.drasticMeasureTaken(); + }) + .then(function(taken) { + assert.isFalse(taken); + }) + .then(done); + }); + + it("can take a drastic measure in an emergency", function(done) { + var stoppable; + return StoppableMock.new(accounts[0]) + .then(function(_stoppable) { + stoppable = _stoppable; + return stoppable.emergencyStop(); + }) + .then(function() { + return stoppable.drasticMeasure(); + }) + .then(function() { + return stoppable.drasticMeasureTaken(); + }) + .then(function(taken) { + assert.isTrue(taken); + }) + .then(done); + }); + + it("should resume allowing normal process after emergency is over", function(done) { + var stoppable; + return StoppableMock.new(accounts[0]) + .then(function(_stoppable) { + stoppable = _stoppable; + return stoppable.emergencyStop(); + }) + .then(function () { + return stoppable.release(); + }) + .then(function() { + return stoppable.normalProcess(); + }) + .then(function() { + return stoppable.count(); + }) + .then(function(count) { + assert.equal(count, 1); + }) + .then(done); + }); + +}); From b5469310a1baaf6f7e0ed372d2f0527afd9e8c33 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 17:27:46 +0100 Subject: [PATCH 10/54] Add bounty test to test against checkInvarient --- contracts/bounties/SimpleTokenBounty.sol | 4 ++++ test/Bounty.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/Bounty.js diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 4b54ded6d..6f61f7496 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -23,6 +23,10 @@ contract SimpleTokenBounty is PullPayment { return target; } + function checkInvarient() returns(bool){ + return true; + } + function claim(SimpleToken target) { address researcher = researchers[target]; if (researcher == 0) throw; diff --git a/test/Bounty.js b/test/Bounty.js new file mode 100644 index 000000000..4cb3e0325 --- /dev/null +++ b/test/Bounty.js @@ -0,0 +1,14 @@ +contract('Bounty', function(accounts) { + it.only("create target", function(done){ + var bounty = Bounty.deployed(); + + bounty.createTarget(). + then(function() { + return bounty.checkInvarient.call() + }). + then(function(result) { + assert.isTrue(result); + }). + then(done); + }) +}); From 8df9925d1ad0c0249dcf7255e4f31df4125c7c8d Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:01:17 +0100 Subject: [PATCH 11/54] Swap SimpleToken with Target --- contracts/bounties/SimpleTokenBounty.sol | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 6f61f7496..7372289fe 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -1,15 +1,21 @@ pragma solidity ^0.4.0; import '../PullPayment.sol'; -import '../token/SimpleToken.sol'; /* * Bounty * This bounty will pay out if you can cause a SimpleToken's balance - * to be lower than its totalSupply, which would mean that it doesn't + * to be lower than its totalSupply, which would mean that it doesn't * have sufficient ether for everyone to withdraw. */ -contract SimpleTokenBounty is PullPayment { +contract Target { + function checkInvarient() returns(bool){ + return true; + } +} + +contract Bounty is PullPayment { + Target target; bool public claimed; mapping(address => address) public researchers; @@ -17,22 +23,22 @@ contract SimpleTokenBounty is PullPayment { if (claimed) throw; } - function createTarget() returns(SimpleToken) { - SimpleToken target = new SimpleToken(); + function createTarget() returns(Target) { + target = new Target(); researchers[target] = msg.sender; return target; } function checkInvarient() returns(bool){ - return true; + return target.checkInvarient(); } - function claim(SimpleToken target) { + function claim(Target target) { address researcher = researchers[target]; if (researcher == 0) throw; - // Check SimpleToken contract invariants + // Check Target contract invariants // Customize this to the specifics of your contract - if (target.totalSupply() == target.balance) { + if (!target.checkInvarient()) { throw; } asyncSend(researcher, this.balance); From e318d6d246143f9e775908fe2fd8773e8e728bd6 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:16:42 +0100 Subject: [PATCH 12/54] Swap target contract at test by using abstract interface --- contracts/bounties/SimpleTokenBounty.sol | 9 +++------ contracts/test-helpers/InsecureTargetMock.sol | 7 +++++++ contracts/test-helpers/SecureTargetMock.sol | 7 +++++++ migrations/2_deploy_contracts.js | 2 ++ test/Bounty.js | 19 ++++++++++++++++--- 5 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 contracts/test-helpers/InsecureTargetMock.sol create mode 100644 contracts/test-helpers/SecureTargetMock.sol diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 7372289fe..9aeb6a7d9 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -9,9 +9,7 @@ import '../PullPayment.sol'; */ contract Target { - function checkInvarient() returns(bool){ - return true; - } + function checkInvarient() returns(bool); } contract Bounty is PullPayment { @@ -23,8 +21,8 @@ contract Bounty is PullPayment { if (claimed) throw; } - function createTarget() returns(Target) { - target = new Target(); + function createTarget(address targetAddress) returns(Target) { + target = Target(targetAddress); researchers[target] = msg.sender; return target; } @@ -37,7 +35,6 @@ contract Bounty is PullPayment { address researcher = researchers[target]; if (researcher == 0) throw; // Check Target contract invariants - // Customize this to the specifics of your contract if (!target.checkInvarient()) { throw; } diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol new file mode 100644 index 000000000..101d371e9 --- /dev/null +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.0; + +contract InsecureTargetMock { + function checkInvarient() returns(bool){ + return false; + } +} diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol new file mode 100644 index 000000000..67e599018 --- /dev/null +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.0; + +contract SecureTargetMock { + function checkInvarient() returns(bool){ + return true; + } +} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 8b400d5d1..51e052891 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -6,4 +6,6 @@ module.exports = function(deployer) { deployer.deploy(CrowdsaleTokenBounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); + deployer.deploy(SecureTargetMock); + deployer.deploy(InsecureTargetMock); }; diff --git a/test/Bounty.js b/test/Bounty.js index 4cb3e0325..e214b9683 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,8 +1,8 @@ contract('Bounty', function(accounts) { - it.only("create target", function(done){ + it.only("can call checkInvarient for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); - - bounty.createTarget(). + var target = SecureTargetMock.deployed(); + bounty.createTarget(target.address). then(function() { return bounty.checkInvarient.call() }). @@ -11,4 +11,17 @@ contract('Bounty', function(accounts) { }). then(done); }) + + it("can call checkInvarient for InsecureTargetMock", function(done){ + var bounty = Bounty.deployed(); + var target = InsecureTargetMock.deployed(); + bounty.createTarget(target.address). + then(function() { + return bounty.checkInvarient.call() + }). + then(function(result) { + assert.isFalse(result); + }). + then(done); + }) }); From 83662fa548e9ab854dcd8ff4a3e77367d355ab36 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:18:02 +0100 Subject: [PATCH 13/54] Remove only --- test/Bounty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bounty.js b/test/Bounty.js index e214b9683..bb731b8ec 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,5 +1,5 @@ contract('Bounty', function(accounts) { - it.only("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvarient for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = SecureTargetMock.deployed(); bounty.createTarget(target.address). From 8e7c7d54b1e5fe73722fc9dd01f40fc9d66afc25 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:26:25 +0100 Subject: [PATCH 14/54] Deploy mock contracts only for test --- migrations/2_deploy_contracts.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 51e052891..504e92353 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -6,6 +6,8 @@ module.exports = function(deployer) { deployer.deploy(CrowdsaleTokenBounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); - deployer.deploy(SecureTargetMock); - deployer.deploy(InsecureTargetMock); + if(deployer.network == 'test'){ + deployer.deploy(SecureTargetMock); + deployer.deploy(InsecureTargetMock); + }; }; From 48badda96f933aea76d163be8c6b86969b0254c7 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 18:29:07 +0100 Subject: [PATCH 15/54] Fix typo --- contracts/bounties/SimpleTokenBounty.sol | 8 ++++---- contracts/test-helpers/InsecureTargetMock.sol | 2 +- contracts/test-helpers/SecureTargetMock.sol | 2 +- test/Bounty.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 9aeb6a7d9..42b65eb36 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -9,7 +9,7 @@ import '../PullPayment.sol'; */ contract Target { - function checkInvarient() returns(bool); + function checkInvariant() returns(bool); } contract Bounty is PullPayment { @@ -27,15 +27,15 @@ contract Bounty is PullPayment { return target; } - function checkInvarient() returns(bool){ - return target.checkInvarient(); + function checkInvariant() returns(bool){ + return target.checkInvariant(); } function claim(Target target) { address researcher = researchers[target]; if (researcher == 0) throw; // Check Target contract invariants - if (!target.checkInvarient()) { + if (!target.checkInvariant()) { throw; } asyncSend(researcher, this.balance); diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 101d371e9..2918361a8 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.0; contract InsecureTargetMock { - function checkInvarient() returns(bool){ + function checkInvariant() returns(bool){ return false; } } diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index 67e599018..bd3a7dae2 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.0; contract SecureTargetMock { - function checkInvarient() returns(bool){ + function checkInvariant() returns(bool){ return true; } } diff --git a/test/Bounty.js b/test/Bounty.js index bb731b8ec..68b567a8d 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,10 +1,10 @@ contract('Bounty', function(accounts) { - it("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvariant for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = SecureTargetMock.deployed(); bounty.createTarget(target.address). then(function() { - return bounty.checkInvarient.call() + return bounty.checkInvariant.call() }). then(function(result) { assert.isTrue(result); @@ -12,12 +12,12 @@ contract('Bounty', function(accounts) { then(done); }) - it("can call checkInvarient for InsecureTargetMock", function(done){ + it("can call checkInvariant for InsecureTargetMock", function(done){ var bounty = Bounty.deployed(); var target = InsecureTargetMock.deployed(); bounty.createTarget(target.address). then(function() { - return bounty.checkInvarient.call() + return bounty.checkInvariant.call() }). then(function(result) { assert.isFalse(result); From f2ec8790e80e899fa139235d8dfadd99adada80e Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 23:29:13 +0100 Subject: [PATCH 16/54] WIP Target contract creation via factory pattern --- contracts/bounties/SimpleTokenBounty.sol | 8 ++++++-- contracts/test-helpers/InsecureTargetMock.sol | 6 ++++++ contracts/test-helpers/SecureTargetMock.sol | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 42b65eb36..6721186a4 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -8,6 +8,10 @@ import '../PullPayment.sol'; * have sufficient ether for everyone to withdraw. */ +contract Factory { + function deployContract() returns (address); +} + contract Target { function checkInvariant() returns(bool); } @@ -21,8 +25,8 @@ contract Bounty is PullPayment { if (claimed) throw; } - function createTarget(address targetAddress) returns(Target) { - target = Target(targetAddress); + function createTarget(address factoryAddress) returns(Target) { + target = Target(Factory(factoryAddress).deployContract()); researchers[target] = msg.sender; return target; } diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 2918361a8..8b85b6eb9 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -5,3 +5,9 @@ contract InsecureTargetMock { return false; } } + +contract Deployer { + function deployContract() returns (address) { + return new InsecureTargetMock(); + } +} diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index bd3a7dae2..382c4b53d 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -5,3 +5,9 @@ contract SecureTargetMock { return true; } } + +contract Deployer { + function deployContract() returns (address) { + return new SecureTargetMock(); + } +} From 721d8fbbf79ee578bc82c30e804e24e039680ecc Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Wed, 26 Oct 2016 09:12:17 +0100 Subject: [PATCH 17/54] Pass factory address at contract creation phase --- contracts/bounties/SimpleTokenBounty.sol | 9 ++++++-- test/Bounty.js | 28 +++++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 6721186a4..8fbb2f426 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -16,16 +16,21 @@ contract Target { function checkInvariant() returns(bool); } -contract Bounty is PullPayment { +contract SimpleTokenBounty is PullPayment { Target target; bool public claimed; + address public factoryAddress; mapping(address => address) public researchers; function() { if (claimed) throw; } - function createTarget(address factoryAddress) returns(Target) { + function SimpleTokenBounty(address _factoryAddress){ + factoryAddress = _factoryAddress; + } + + function createTarget() returns(Target) { target = Target(Factory(factoryAddress).deployContract()); researchers[target] = msg.sender; return target; diff --git a/test/Bounty.js b/test/Bounty.js index 68b567a8d..e66dbf7a9 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,11 +1,27 @@ contract('Bounty', function(accounts) { - it("can call checkInvariant for InsecureTargetMock", function(done){ - var bounty = Bounty.deployed(); + it("can create bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); - bounty.createTarget(target.address). - then(function() { - return bounty.checkInvariant.call() + SimpleTokenBounty.new(target.address). + then(function(bounty){ + return bounty.factoryAddress.call() }). + then(function(address){ + assert.equal(address, target.address) + }). + then(done); + }) + + it.only("can call checkInvariant for SecureTargetMock", function(done){ + var bounty; + var target = SecureTargetMock.deployed(); + SimpleTokenBounty.new(target.address). + then(function(_bounty) { + bounty = _bounty; + return bounty.createTarget.sendTransaction({gas:200000}); + }). + // then(function() { + // return bounty.checkInvariant.call() + // }). then(function(result) { assert.isTrue(result); }). @@ -13,7 +29,7 @@ contract('Bounty', function(accounts) { }) it("can call checkInvariant for InsecureTargetMock", function(done){ - var bounty = Bounty.deployed(); + var bounty = SimpleTokenBounty.deployed(); var target = InsecureTargetMock.deployed(); bounty.createTarget(target.address). then(function() { From 14b8496247cf8e96625fc1eaf439ad376db494d1 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Wed, 26 Oct 2016 19:34:08 +0100 Subject: [PATCH 18/54] Pass factory address to bounty --- contracts/test-helpers/InsecureTargetMock.sol | 2 +- contracts/test-helpers/SecureTargetMock.sol | 2 +- migrations/2_deploy_contracts.js | 4 ++-- test/Bounty.js | 24 +++++++++++-------- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 8b85b6eb9..0db1ee180 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -6,7 +6,7 @@ contract InsecureTargetMock { } } -contract Deployer { +contract InsecureTargetFactory { function deployContract() returns (address) { return new InsecureTargetMock(); } diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index 382c4b53d..4c7da0093 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -6,7 +6,7 @@ contract SecureTargetMock { } } -contract Deployer { +contract SecureTargetFactory { function deployContract() returns (address) { return new SecureTargetMock(); } diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 504e92353..67672b289 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -7,7 +7,7 @@ module.exports = function(deployer) { deployer.deploy(Ownable); deployer.deploy(LimitFunds); if(deployer.network == 'test'){ - deployer.deploy(SecureTargetMock); - deployer.deploy(InsecureTargetMock); + deployer.deploy(SecureTargetFactory); + deployer.deploy(InsecureTargetFactory); }; }; diff --git a/test/Bounty.js b/test/Bounty.js index e66dbf7a9..19b43db4e 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -11,17 +11,17 @@ contract('Bounty', function(accounts) { then(done); }) - it.only("can call checkInvariant for SecureTargetMock", function(done){ + it("can call checkInvariant for SecureTargetMock", function(done){ var bounty; - var target = SecureTargetMock.deployed(); - SimpleTokenBounty.new(target.address). + var targetFactory = SecureTargetFactory.deployed(); + SimpleTokenBounty.new(targetFactory.address). then(function(_bounty) { bounty = _bounty; - return bounty.createTarget.sendTransaction({gas:200000}); + return bounty.createTarget(); + }). + then(function() { + return bounty.checkInvariant.call() }). - // then(function() { - // return bounty.checkInvariant.call() - // }). then(function(result) { assert.isTrue(result); }). @@ -29,9 +29,13 @@ contract('Bounty', function(accounts) { }) it("can call checkInvariant for InsecureTargetMock", function(done){ - var bounty = SimpleTokenBounty.deployed(); - var target = InsecureTargetMock.deployed(); - bounty.createTarget(target.address). + var bounty; + var targetFactory = InsecureTargetFactory.deployed(); + SimpleTokenBounty.new(targetFactory.address). + then(function(_bounty) { + bounty = _bounty; + return bounty.createTarget(); + }). then(function() { return bounty.checkInvariant.call() }). From df0c2defc89d7817cd1ef5e43ad3cfcf621855cb Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Wed, 26 Oct 2016 19:48:12 +0100 Subject: [PATCH 19/54] Restructure test --- test/Bounty.js | 69 ++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/test/Bounty.js b/test/Bounty.js index 19b43db4e..cd73a18c4 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,6 +1,7 @@ contract('Bounty', function(accounts) { it("can create bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); + SimpleTokenBounty.new(target.address). then(function(bounty){ return bounty.factoryAddress.call() @@ -11,37 +12,45 @@ contract('Bounty', function(accounts) { then(done); }) - it("can call checkInvariant for SecureTargetMock", function(done){ - var bounty; - var targetFactory = SecureTargetFactory.deployed(); - SimpleTokenBounty.new(targetFactory.address). - then(function(_bounty) { - bounty = _bounty; - return bounty.createTarget(); - }). - then(function() { - return bounty.checkInvariant.call() - }). - then(function(result) { - assert.isTrue(result); - }). - then(done); + describe("SecureTargetMock", function(){ + before(function(){ + targetFactory = SecureTargetFactory.deployed(); + }) + + it("checkInvariant returns true", function(done){ + SimpleTokenBounty.new(targetFactory.address). + then(function(_bounty) { + bounty = _bounty; + return bounty.createTarget(); + }). + then(function() { + return bounty.checkInvariant.call() + }). + then(function(result) { + assert.isTrue(result); + }). + then(done); + }) }) - it("can call checkInvariant for InsecureTargetMock", function(done){ - var bounty; - var targetFactory = InsecureTargetFactory.deployed(); - SimpleTokenBounty.new(targetFactory.address). - then(function(_bounty) { - bounty = _bounty; - return bounty.createTarget(); - }). - then(function() { - return bounty.checkInvariant.call() - }). - then(function(result) { - assert.isFalse(result); - }). - then(done); + describe("InsecureTargetMock", function(){ + before(function(){ + targetFactory = InsecureTargetFactory.deployed(); + }) + + it("checkInvariant returns false", function(done){ + SimpleTokenBounty.new(targetFactory.address). + then(function(_bounty) { + bounty = _bounty; + return bounty.createTarget(); + }). + then(function() { + return bounty.checkInvariant.call() + }). + then(function(result) { + assert.isFalse(result); + }). + then(done); + }) }) }); From e8262f7c4b34cb79715a50fcbfab92c0a0541c2f Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Wed, 26 Oct 2016 22:38:20 +0100 Subject: [PATCH 20/54] Make default function payable --- contracts/bounties/SimpleTokenBounty.sol | 2 +- test/Bounty.js | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 8fbb2f426..c7ff55c46 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -22,7 +22,7 @@ contract SimpleTokenBounty is PullPayment { address public factoryAddress; mapping(address => address) public researchers; - function() { + function() payable { if (claimed) throw; } diff --git a/test/Bounty.js b/test/Bounty.js index cd73a18c4..863657014 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,7 +1,11 @@ contract('Bounty', function(accounts) { + before(function(){ + owner = accounts[0]; + researcher = accounts[1]; + }) + it("can create bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); - SimpleTokenBounty.new(target.address). then(function(bounty){ return bounty.factoryAddress.call() @@ -12,6 +16,22 @@ contract('Bounty', function(accounts) { then(done); }) + it("sets reward", function(done){ + var target = SecureTargetMock.deployed(); + var reward = web3.toWei(1, "ether"); + var bounty; + SimpleTokenBounty.new(target.address). + then(function(bounty){ + web3.eth.sendTransaction({ + from:owner, + to:bounty.address, + value: reward + }) + assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) + }). + then(done); + }) + describe("SecureTargetMock", function(){ before(function(){ targetFactory = SecureTargetFactory.deployed(); From e35ba313a7cb54cc7e21606820bf266764966275 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 18:39:31 +0100 Subject: [PATCH 21/54] Add specs to test success and fail case of claiming --- contracts/bounties/SimpleTokenBounty.sol | 5 +- test/Bounty.js | 98 +++++++++++++++++++----- 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index c7ff55c46..5f6a9c615 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -22,6 +22,8 @@ contract SimpleTokenBounty is PullPayment { address public factoryAddress; mapping(address => address) public researchers; + event TargetCreated(address createdAddress); + function() payable { if (claimed) throw; } @@ -33,6 +35,7 @@ contract SimpleTokenBounty is PullPayment { function createTarget() returns(Target) { target = Target(Factory(factoryAddress).deployContract()); researchers[target] = msg.sender; + TargetCreated(target); return target; } @@ -44,7 +47,7 @@ contract SimpleTokenBounty is PullPayment { address researcher = researchers[target]; if (researcher == 0) throw; // Check Target contract invariants - if (!target.checkInvariant()) { + if (target.checkInvariant()) { throw; } asyncSend(researcher, this.balance); diff --git a/test/Bounty.js b/test/Bounty.js index 863657014..bad2ee90b 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -1,11 +1,15 @@ -contract('Bounty', function(accounts) { - before(function(){ - owner = accounts[0]; - researcher = accounts[1]; +var sendReward = function(sender, receiver, value){ + web3.eth.sendTransaction({ + from:sender, + to:receiver, + value: value }) +} +contract('Bounty', function(accounts) { it("can create bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); + SimpleTokenBounty.new(target.address). then(function(bounty){ return bounty.factoryAddress.call() @@ -18,26 +22,21 @@ contract('Bounty', function(accounts) { it("sets reward", function(done){ var target = SecureTargetMock.deployed(); + var owner = accounts[0]; var reward = web3.toWei(1, "ether"); - var bounty; + SimpleTokenBounty.new(target.address). then(function(bounty){ - web3.eth.sendTransaction({ - from:owner, - to:bounty.address, - value: reward - }) + sendReward(owner, bounty.address, reward); assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) }). then(done); }) describe("SecureTargetMock", function(){ - before(function(){ - targetFactory = SecureTargetFactory.deployed(); - }) - it("checkInvariant returns true", function(done){ + var targetFactory = SecureTargetFactory.deployed(); + var bounty; SimpleTokenBounty.new(targetFactory.address). then(function(_bounty) { bounty = _bounty; @@ -51,14 +50,46 @@ contract('Bounty', function(accounts) { }). then(done); }) + + it("cannot calim reward", function(done){ + var targetFactory = SecureTargetFactory.deployed(); + var owner = accounts[0]; + var researcher = accounts[1]; + var reward = web3.toWei(1, "ether"); + + SimpleTokenBounty.new(targetFactory.address). + then(function(bounty) { + var event = bounty.TargetCreated({}); + event.watch(function(err, result) { + event.stopWatching(); + if (err) { throw err } + var targetAddress = result.args.createdAddress; + sendReward(owner, bounty.address, reward); + assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) + bounty.claim(targetAddress, {from:researcher}). + then(function(){ throw("should not come here")}). + catch(function() { + return bounty.claimed.call(); + }). + then(function(result) { + assert.isFalse(result); + bounty.withdrawPayments({from:researcher}). + then(function(){ throw("should not come here")}). + catch(function() { + assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) + done(); + }) + }) + }) + bounty.createTarget({from:researcher}); + }) + }) }) describe("InsecureTargetMock", function(){ - before(function(){ - targetFactory = InsecureTargetFactory.deployed(); - }) - it("checkInvariant returns false", function(done){ + var targetFactory = InsecureTargetFactory.deployed(); + var bounty; SimpleTokenBounty.new(targetFactory.address). then(function(_bounty) { bounty = _bounty; @@ -72,5 +103,36 @@ contract('Bounty', function(accounts) { }). then(done); }) + + it("calims reward", function(done){ + var targetFactory = InsecureTargetFactory.deployed(); + var owner = accounts[0]; + var researcher = accounts[1]; + var reward = web3.toWei(1, "ether"); + + SimpleTokenBounty.new(targetFactory.address). + then(function(bounty) { + var event = bounty.TargetCreated({}); + event.watch(function(err, result) { + event.stopWatching(); + if (err) { throw err } + var targetAddress = result.args.createdAddress; + sendReward(owner, bounty.address, reward); + assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) + bounty.claim(targetAddress, {from:researcher}). + then(function() { + return bounty.claimed.call(); + }). + then(function(result) { + assert.isTrue(result); + return bounty.withdrawPayments({from:researcher}) + }). + then(function() { + assert.equal(0, web3.eth.getBalance(bounty.address).toNumber()) + }).then(done); + }) + bounty.createTarget({from:researcher}); + }) + }) }) }); From 37f4ff8d3e03dfd16ef05d0b47da6bf4871859b3 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 22:08:36 +0100 Subject: [PATCH 22/54] Add killable to bounty --- contracts/bounties/SimpleTokenBounty.sol | 4 ++-- test/Bounty.js | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/bounties/SimpleTokenBounty.sol index 5f6a9c615..2ea9854fd 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/bounties/SimpleTokenBounty.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.0; import '../PullPayment.sol'; - +import '../Killable.sol'; /* * Bounty * This bounty will pay out if you can cause a SimpleToken's balance @@ -16,7 +16,7 @@ contract Target { function checkInvariant() returns(bool); } -contract SimpleTokenBounty is PullPayment { +contract SimpleTokenBounty is PullPayment, Killable { Target target; bool public claimed; address public factoryAddress; diff --git a/test/Bounty.js b/test/Bounty.js index bad2ee90b..b67b678ed 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -7,7 +7,7 @@ var sendReward = function(sender, receiver, value){ } contract('Bounty', function(accounts) { - it("can create bounty contract with factory address", function(done){ + it("creates bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); SimpleTokenBounty.new(target.address). @@ -33,6 +33,25 @@ contract('Bounty', function(accounts) { then(done); }) + it("ends", function(done){ + var target = SecureTargetMock.deployed(); + var owner = accounts[0]; + var reward = web3.toWei(1, "ether"); + var bounty; + SimpleTokenBounty.new(target.address). + then(function(_bounty){ + bounty = _bounty; + sendReward(owner, bounty.address, reward); + assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) + return bounty.kill() + }). + then(function(){ + assert.equal(0, web3.eth.getBalance(bounty.address).toNumber()) + }). + then(done); + }) + + describe("SecureTargetMock", function(){ it("checkInvariant returns true", function(done){ var targetFactory = SecureTargetFactory.deployed(); From a04481d2d8ad1984286920ef1c5627ef49d4d6ad Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 22:10:53 +0100 Subject: [PATCH 23/54] Remove extra whitespace --- test/Bounty.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Bounty.js b/test/Bounty.js index b67b678ed..c4b5598e2 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -51,7 +51,6 @@ contract('Bounty', function(accounts) { then(done); }) - describe("SecureTargetMock", function(){ it("checkInvariant returns true", function(done){ var targetFactory = SecureTargetFactory.deployed(); From caca41f855fed2ba9c05b2202cae83fb6ab5fdb0 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 22:29:52 +0100 Subject: [PATCH 24/54] Add README --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d8ac3bd4..8767c3e14 100644 --- a/README.md +++ b/README.md @@ -24,20 +24,89 @@ After that, you'll get all the library's contracts in the `contracts/zeppelin` f ```js import "./zeppelin/Rejector.sol"; -contract MetaCoin is Rejector { +contract MetaCoin is Rejector { ... } ``` > NOTE: The current distribution channel is npm, which is not ideal. [We're looking into providing a better tool for code distribution](https://github.com/OpenZeppelin/zeppelin-solidity/issues/13), and ideas are welcome. +## Add your own bounty contract + +So far you inherit Zeppelin contracts into your own contract through inheritance. +A bounty contract, however, is a special contract that is deployed on its own. +Each researcher creates a separate copy of your contract, and can claims bounty by breaking invariants logic on the copy of your contract without hacking your original contract. + +To use the bounty contract, please follow the below instruction. + +### Implement invariant logic into your smart contract + +At contracts/YourContract.sol + +``` +contract YourContract { + function checkInvariant() returns(bool){ + // Implement your logic to make sure that none of the state is broken. + } +} + +contract YourContractFactory { + function deployContract() returns (address) { + // This contract allows researchers to create a copy of your contract + return new YourContract(); + } +} +``` + +### Add the bounty contracts as well as your contracts into migrations + +At `migrations/2_deploy_contracts.js` + +``` +module.exports = function(deployer) { + deployer.deploy(YourContract); + deployer.deploy(YourContractFactory); + deployer.deploy(Bounty); +}; +``` + +### Add a reward to the bounty contract + +After deploying the contract, send rewards money into the bounty contract. + +From `truffle console` + +``` +address = 'your account address' +reward = 'reward to pay to a researcher' + +web3.eth.sendTransaction({ + from:address, + to:bounty.address, + value: web3.toWei(reward, "ether") +} + +``` + +### Researchers hack the contract and claim their reward. + +For each researcher who wants to hack the contract and claims the reward, refer to our [test](./test/Bounty.js) for the detail. + +### Ends the contract + +If you manage to protect your contract from security researchers and wants to end the bounty, kill the contract so that all the rewards go back to the owner of the bounty contract. + +``` +bounty.kill() +``` + #### Truffle Beta Support We also support Truffle Beta npm integration. If you're using Truffle Beta, the contracts in `node_modules` will be enough, so feel free to delete the copies at your `contracts` folder. If you're using Truffle Beta, you can use Zeppelin contracts like so: ```js import "zeppelin-solidity/contracts/Rejector.sol"; -contract MetaCoin is Rejector { +contract MetaCoin is Rejector { ... } ``` From 99f288bfb5cb1403ad2dd597ee6d511bcc420475 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 22:35:08 +0100 Subject: [PATCH 25/54] Consolidate TokenBounties into Bounty --- .../SimpleTokenBounty.sol => Bounty.sol} | 14 +++---- contracts/bounties/CrowdsaleTokenBounty.sol | 38 ------------------- migrations/2_deploy_contracts.js | 2 +- test/Bounty.js | 14 +++---- 4 files changed, 15 insertions(+), 53 deletions(-) rename contracts/{bounties/SimpleTokenBounty.sol => Bounty.sol} (73%) delete mode 100644 contracts/bounties/CrowdsaleTokenBounty.sol diff --git a/contracts/bounties/SimpleTokenBounty.sol b/contracts/Bounty.sol similarity index 73% rename from contracts/bounties/SimpleTokenBounty.sol rename to contracts/Bounty.sol index 2ea9854fd..19a2effdf 100644 --- a/contracts/bounties/SimpleTokenBounty.sol +++ b/contracts/Bounty.sol @@ -1,11 +1,11 @@ pragma solidity ^0.4.0; -import '../PullPayment.sol'; -import '../Killable.sol'; +import './PullPayment.sol'; +import './Killable.sol'; + /* * Bounty - * This bounty will pay out if you can cause a SimpleToken's balance - * to be lower than its totalSupply, which would mean that it doesn't - * have sufficient ether for everyone to withdraw. + * This bounty will pay out to a researcher if he/she breaks invariant logic of + * the contract you bet reward against. */ contract Factory { @@ -16,7 +16,7 @@ contract Target { function checkInvariant() returns(bool); } -contract SimpleTokenBounty is PullPayment, Killable { +contract Bounty is PullPayment, Killable { Target target; bool public claimed; address public factoryAddress; @@ -28,7 +28,7 @@ contract SimpleTokenBounty is PullPayment, Killable { if (claimed) throw; } - function SimpleTokenBounty(address _factoryAddress){ + function Bounty(address _factoryAddress){ factoryAddress = _factoryAddress; } diff --git a/contracts/bounties/CrowdsaleTokenBounty.sol b/contracts/bounties/CrowdsaleTokenBounty.sol deleted file mode 100644 index 814720745..000000000 --- a/contracts/bounties/CrowdsaleTokenBounty.sol +++ /dev/null @@ -1,38 +0,0 @@ -pragma solidity ^0.4.0; -import '../PullPayment.sol'; -import '../token/CrowdsaleToken.sol'; - -/* - * Bounty - * This bounty will pay out if you can cause a CrowdsaleToken's balance - * to be lower than its totalSupply, which would mean that it doesn't - * have sufficient ether for everyone to withdraw. - */ -contract CrowdsaleTokenBounty is PullPayment { - - bool public claimed; - mapping(address => address) public researchers; - - function() { - if (claimed) throw; - } - - function createTarget() returns(CrowdsaleToken) { - CrowdsaleToken target = new CrowdsaleToken(); - researchers[target] = msg.sender; - return target; - } - - function claim(CrowdsaleToken target) { - address researcher = researchers[target]; - if (researcher == 0) throw; - // Check CrowdsaleToken contract invariants - // Customize this to the specifics of your contract - if (target.totalSupply() == target.balance) { - throw; - } - asyncSend(researcher, this.balance); - claimed = true; - } - -} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 67672b289..c43e3ebb8 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -2,7 +2,7 @@ module.exports = function(deployer) { deployer.deploy(PullPaymentBid); deployer.deploy(BadArrayUse); deployer.deploy(ProofOfExistence); - deployer.deploy(SimpleTokenBounty); + deployer.deploy(Bounty); deployer.deploy(CrowdsaleTokenBounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); diff --git a/test/Bounty.js b/test/Bounty.js index c4b5598e2..3fc00e5d0 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -10,7 +10,7 @@ contract('Bounty', function(accounts) { it("creates bounty contract with factory address", function(done){ var target = SecureTargetMock.deployed(); - SimpleTokenBounty.new(target.address). + Bounty.new(target.address). then(function(bounty){ return bounty.factoryAddress.call() }). @@ -25,7 +25,7 @@ contract('Bounty', function(accounts) { var owner = accounts[0]; var reward = web3.toWei(1, "ether"); - SimpleTokenBounty.new(target.address). + Bounty.new(target.address). then(function(bounty){ sendReward(owner, bounty.address, reward); assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) @@ -38,7 +38,7 @@ contract('Bounty', function(accounts) { var owner = accounts[0]; var reward = web3.toWei(1, "ether"); var bounty; - SimpleTokenBounty.new(target.address). + Bounty.new(target.address). then(function(_bounty){ bounty = _bounty; sendReward(owner, bounty.address, reward); @@ -55,7 +55,7 @@ contract('Bounty', function(accounts) { it("checkInvariant returns true", function(done){ var targetFactory = SecureTargetFactory.deployed(); var bounty; - SimpleTokenBounty.new(targetFactory.address). + Bounty.new(targetFactory.address). then(function(_bounty) { bounty = _bounty; return bounty.createTarget(); @@ -75,7 +75,7 @@ contract('Bounty', function(accounts) { var researcher = accounts[1]; var reward = web3.toWei(1, "ether"); - SimpleTokenBounty.new(targetFactory.address). + Bounty.new(targetFactory.address). then(function(bounty) { var event = bounty.TargetCreated({}); event.watch(function(err, result) { @@ -108,7 +108,7 @@ contract('Bounty', function(accounts) { it("checkInvariant returns false", function(done){ var targetFactory = InsecureTargetFactory.deployed(); var bounty; - SimpleTokenBounty.new(targetFactory.address). + Bounty.new(targetFactory.address). then(function(_bounty) { bounty = _bounty; return bounty.createTarget(); @@ -128,7 +128,7 @@ contract('Bounty', function(accounts) { var researcher = accounts[1]; var reward = web3.toWei(1, "ether"); - SimpleTokenBounty.new(targetFactory.address). + Bounty.new(targetFactory.address). then(function(bounty) { var event = bounty.TargetCreated({}); event.watch(function(err, result) { From 9509298622683326e954fe5ae8a572cffc23ff9a Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Thu, 27 Oct 2016 22:57:45 +0100 Subject: [PATCH 26/54] Change spec descriptions --- test/Bounty.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Bounty.js b/test/Bounty.js index 3fc00e5d0..52b5178a6 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -33,7 +33,7 @@ contract('Bounty', function(accounts) { then(done); }) - it("ends", function(done){ + it("empties itself when killed", function(done){ var target = SecureTargetMock.deployed(); var owner = accounts[0]; var reward = web3.toWei(1, "ether"); @@ -51,7 +51,7 @@ contract('Bounty', function(accounts) { then(done); }) - describe("SecureTargetMock", function(){ + describe("Against secure contract", function(){ it("checkInvariant returns true", function(done){ var targetFactory = SecureTargetFactory.deployed(); var bounty; @@ -69,7 +69,7 @@ contract('Bounty', function(accounts) { then(done); }) - it("cannot calim reward", function(done){ + it("cannot claim reward", function(done){ var targetFactory = SecureTargetFactory.deployed(); var owner = accounts[0]; var researcher = accounts[1]; @@ -104,7 +104,7 @@ contract('Bounty', function(accounts) { }) }) - describe("InsecureTargetMock", function(){ + describe("Against broken contract", function(){ it("checkInvariant returns false", function(done){ var targetFactory = InsecureTargetFactory.deployed(); var bounty; @@ -122,7 +122,7 @@ contract('Bounty', function(accounts) { then(done); }) - it("calims reward", function(done){ + it("claims reward", function(done){ var targetFactory = InsecureTargetFactory.deployed(); var owner = accounts[0]; var researcher = accounts[1]; From 71edad126b3c8a400000f8e8e4aff339eb88a474 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Thu, 27 Oct 2016 22:17:21 -0700 Subject: [PATCH 27/54] Create tests for StandardToken --- contracts/test-helpers/StandardTokenMock.sol | 11 +++ test/StandardToken.js | 81 ++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 contracts/test-helpers/StandardTokenMock.sol create mode 100644 test/StandardToken.js diff --git a/contracts/test-helpers/StandardTokenMock.sol b/contracts/test-helpers/StandardTokenMock.sol new file mode 100644 index 000000000..38be987bd --- /dev/null +++ b/contracts/test-helpers/StandardTokenMock.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.4.0; +import '../StandardToken.sol'; + +// mock class using StandardToken +contract StandardTokenMock is StandardToken { + + function StandardTokenMock(address initialAccount, uint initialBalance) { + balances[initialAccount] = initialBalance; + } + +} diff --git a/test/StandardToken.js b/test/StandardToken.js new file mode 100644 index 000000000..40382c175 --- /dev/null +++ b/test/StandardToken.js @@ -0,0 +1,81 @@ +contract('StandardToken', function(accounts) { + + it("should return the correct allowance amount after approval", function(done) { + var token; + return StandardTokenMock.new() + .then(function(_token) { + token = _token; + return token.approve(accounts[1], 100); + }) + .then(function() { + return token.allowance(accounts[0], accounts[1]); + }) + .then(function(allowance) { + assert.equal(allowance, 100); + }) + .then(done); + }); + + it("should return correct balances after transfer", function(done) { + var token; + return StandardTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.transfer(accounts[1], 100); + }) + .then(function() { + return token.balanceOf(accounts[0]); + }) + .then(function(balance) { + assert.equal(balance, 0); + }) + .then(function() { + return token.balanceOf(accounts[1]); + }) + .then(function(balance) { + assert.equal(balance, 100); + }) + .then(done); + }); + + it("should throw an error when trying to transfer more than balance", function(done) { + var token; + return StandardTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.transfer(accounts[1], 101); + }) + .catch(function(error) { + if (error.message.search('invalid JUMP') == -1) throw error + }) + .then(done); + }); + + it("should return correct balances after transfering from another account", function(done) { + var token; + return StandardTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.approve(accounts[1], 100); + }) + .then(function() { + return token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); + }) + .then(function() { + return token.balanceOf(accounts[0]); + }) + .then(function(balance) { + assert.equal(balance, 0); + return token.balanceOf(accounts[2]); + }) + .then(function(balance) { + assert.equal(balance, 100) + return token.balanceOf(accounts[1]); + }) + .then(function(balance) { + assert.equal(balance, 0); + }) + .then(done); + }); + +}); From b6a5830047fc61f2f04df7209c439f7e57741150 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Fri, 28 Oct 2016 11:22:41 +0100 Subject: [PATCH 28/54] Does not allow to create bounty contract without address --- contracts/Bounty.sol | 7 ++++++- migrations/2_deploy_contracts.js | 1 - test/Bounty.js | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 19a2effdf..f5faff6e3 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -28,7 +28,12 @@ contract Bounty is PullPayment, Killable { if (claimed) throw; } - function Bounty(address _factoryAddress){ + modifier withAddress(address _address) { + if(_address == 0) throw; + _; + } + + function Bounty(address _factoryAddress) withAddress(_factoryAddress){ factoryAddress = _factoryAddress; } diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index c43e3ebb8..889364cf6 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -2,7 +2,6 @@ module.exports = function(deployer) { deployer.deploy(PullPaymentBid); deployer.deploy(BadArrayUse); deployer.deploy(ProofOfExistence); - deployer.deploy(Bounty); deployer.deploy(CrowdsaleTokenBounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); diff --git a/test/Bounty.js b/test/Bounty.js index 52b5178a6..8f4b81933 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -33,6 +33,18 @@ contract('Bounty', function(accounts) { then(done); }) + it("cannot create bounty without address", function(done){ + var target = SecureTargetMock.deployed(); + Bounty.new(). + then(function(bounty){ + throw {name : "NoThrowError", message : "should not come here"}; + }). + catch(function(error){ + assert.notEqual(error.name, "NoThrowError"); + }). + then(done); + }) + it("empties itself when killed", function(done){ var target = SecureTargetMock.deployed(); var owner = accounts[0]; From 3764b43689c5455d17d6d2a5610ec04db4387bb9 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Fri, 28 Oct 2016 11:46:41 +0100 Subject: [PATCH 29/54] Update doc on migration step --- README.md | 5 +++-- migrations/2_deploy_contracts.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8767c3e14..618df8d40 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,9 @@ At `migrations/2_deploy_contracts.js` ``` module.exports = function(deployer) { deployer.deploy(YourContract); - deployer.deploy(YourContractFactory); - deployer.deploy(Bounty); + deployer.deploy(YourContractFactory).then(function() { + return deployer.deploy(Bounty, YourContractFactory.address); + }); }; ``` diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 889364cf6..ed1fb1122 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -2,9 +2,9 @@ module.exports = function(deployer) { deployer.deploy(PullPaymentBid); deployer.deploy(BadArrayUse); deployer.deploy(ProofOfExistence); - deployer.deploy(CrowdsaleTokenBounty); deployer.deploy(Ownable); deployer.deploy(LimitFunds); + if(deployer.network == 'test'){ deployer.deploy(SecureTargetFactory); deployer.deploy(InsecureTargetFactory); From 949f25b244228e87aa9c121b22bcd026d316ff1f Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Fri, 28 Oct 2016 21:55:16 +0100 Subject: [PATCH 30/54] Deploy mock contracts on test --- migrations/2_deploy_contracts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index ed1fb1122..869c60623 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -4,9 +4,10 @@ module.exports = function(deployer) { deployer.deploy(ProofOfExistence); deployer.deploy(Ownable); deployer.deploy(LimitFunds); - if(deployer.network == 'test'){ + deployer.deploy(SecureTargetMock); deployer.deploy(SecureTargetFactory); + deployer.deploy(InsecureTargetMock); deployer.deploy(InsecureTargetFactory); }; }; From e2861e75918c50aa754348b2f4a62c69f860961c Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Tue, 18 Oct 2016 14:49:03 -0300 Subject: [PATCH 31/54] fix spacing --- contracts/token/CrowdsaleToken.sol | 42 +++++++++++++++--------------- contracts/token/SimpleToken.sol | 18 ++++++------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/contracts/token/CrowdsaleToken.sol b/contracts/token/CrowdsaleToken.sol index 7fd363cb0..6d748f421 100644 --- a/contracts/token/CrowdsaleToken.sol +++ b/contracts/token/CrowdsaleToken.sol @@ -7,28 +7,28 @@ import "../StandardToken.sol"; */ contract CrowdsaleToken is StandardToken { - string public name = "CrowdsaleToken"; - string public symbol = "CRW"; - uint public decimals = 18; + string public name = "CrowdsaleToken"; + string public symbol = "CRW"; + uint public decimals = 18; - // 1 ether = 500 example tokens - uint PRICE = 500; + // 1 ether = 500 example tokens + uint PRICE = 500; - function () payable { - createTokens(msg.sender); - } - - function createTokens(address recipient) payable { - if (msg.value == 0) throw; - - uint tokens = safeMul(msg.value, getPrice()); + function () payable { + createTokens(msg.sender); + } + + function createTokens(address recipient) payable { + if (msg.value == 0) throw; - totalSupply = safeAdd(totalSupply, tokens); - balances[recipient] = safeAdd(balances[recipient], tokens); - } - - // replace this with any other price function - function getPrice() constant returns (uint result){ - return PRICE; - } + uint tokens = safeMul(msg.value, getPrice()); + + totalSupply = safeAdd(totalSupply, tokens); + balances[recipient] = safeAdd(balances[recipient], tokens); + } + + // replace this with any other price function + function getPrice() constant returns (uint result){ + return PRICE; + } } diff --git a/contracts/token/SimpleToken.sol b/contracts/token/SimpleToken.sol index ccb34769f..917a226cb 100644 --- a/contracts/token/SimpleToken.sol +++ b/contracts/token/SimpleToken.sol @@ -9,14 +9,14 @@ import "../StandardToken.sol"; */ contract SimpleToken is StandardToken { - string public name = "SimpleToken"; - string public symbol = "SIM"; - uint public decimals = 18; - uint public INITIAL_SUPPLY = 10000; - - function SimpleToken() { - totalSupply = INITIAL_SUPPLY; - balances[msg.sender] = INITIAL_SUPPLY; - } + string public name = "SimpleToken"; + string public symbol = "SIM"; + uint public decimals = 18; + uint public INITIAL_SUPPLY = 10000; + + function SimpleToken() { + totalSupply = INITIAL_SUPPLY; + balances[msg.sender] = INITIAL_SUPPLY; + } } From 5683417d611b4b8648e487d7543d9919dd41f340 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 31 Oct 2016 16:29:29 -0300 Subject: [PATCH 32/54] v0.0.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9c0b7a11..cb1549efb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zeppelin-solidity", - "version": "0.0.10", + "version": "0.0.11", "description": "Secure Smart Contract library for Solidity", "main": "truffle.js", "devDependencies": {}, From aa3cd8c03afd7643a9bc246b680e66b8e8a3b640 Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Tue, 1 Nov 2016 10:21:00 -0700 Subject: [PATCH 33/54] Add test for StandardToken --- test/StandardToken.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/StandardToken.js b/test/StandardToken.js index 40382c175..6b83b32b8 100644 --- a/test/StandardToken.js +++ b/test/StandardToken.js @@ -78,4 +78,20 @@ contract('StandardToken', function(accounts) { .then(done); }); + it("should throw an error when trying to transfer more than allowed", function(done) { + var token; + return StandardTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.approve(accounts[1], 99); + }) + .then(function() { + return token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); + }) + .catch(function(error) { + if (error.message.search('invalid JUMP') == -1) throw error + }) + .then(done); + }); + }); From 0f3a8051d69bb0fee10287c0760d9fe7cc65fca2 Mon Sep 17 00:00:00 2001 From: AugustoL Date: Wed, 2 Nov 2016 14:20:52 -0300 Subject: [PATCH 34/54] Added Claimable contract as an extension of Ownable with the Claimable tests --- contracts/Claimable.sol | 26 ++++++++++++++ migrations/2_deploy_contracts.js | 1 + test/Claimable.js | 60 ++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 contracts/Claimable.sol create mode 100644 test/Claimable.js diff --git a/contracts/Claimable.sol b/contracts/Claimable.sol new file mode 100644 index 000000000..40e38d49c --- /dev/null +++ b/contracts/Claimable.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.0; +import './Ownable.sol'; + +/* + * Claimable + * Extension for the Ownable contract, where the ownership needs to be claimed + */ + +contract Claimable is Ownable { + address public pendingOwner; + + modifier onlyPendingOwner() { + if (msg.sender == pendingOwner) + _; + } + + function transfer(address newOwner) onlyOwner { + pendingOwner = newOwner; + } + + function claimOwnership() onlyPendingOwner { + owner = pendingOwner; + pendingOwner = 0x0; + } + +} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 869c60623..dd29abdac 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -3,6 +3,7 @@ module.exports = function(deployer) { deployer.deploy(BadArrayUse); deployer.deploy(ProofOfExistence); deployer.deploy(Ownable); + deployer.deploy(Claimable); deployer.deploy(LimitFunds); if(deployer.network == 'test'){ deployer.deploy(SecureTargetMock); diff --git a/test/Claimable.js b/test/Claimable.js new file mode 100644 index 000000000..9d62663d6 --- /dev/null +++ b/test/Claimable.js @@ -0,0 +1,60 @@ +contract('Claimable', function(accounts) { + + it("should have an owner", function(done) { + var claimable = Claimable.deployed(); + return claimable.owner() + .then(function(owner) { + assert.isTrue(owner != 0); + }) + .then(done) + }); + + it("changes pendingOwner after transfer", function(done) { + var claimable = Claimable.deployed(); + return claimable.transfer(accounts[1]) + .then(function() { + return claimable.pendingOwner(); + }) + .then(function(pendingOwner) { + assert.isTrue(pendingOwner === accounts[1]); + }) + .then(done) + }); + + it("should prevent to claimOwnership from no pendingOwner", function(done) { + var claimable = Claimable.deployed(); + return claimable.claimOwnership({from: accounts[2]}) + .then(function() { + return claimable.owner(); + }) + .then(function(owner) { + assert.isTrue(owner != accounts[2]); + }) + .then(done) + }); + + it("changes allow pending owner to claim ownership", function(done) { + var claimable = Claimable.deployed(); + return claimable.claimOwnership({from: accounts[1]}) + .then(function() { + return claimable.owner(); + }) + .then(function(owner) { + assert.isTrue(owner === accounts[1]); + }) + .then(done) + }); + + it("should prevent non-owners from transfering" ,function(done) { + var claimable = Claimable.deployed(); + return claimable.transfer(accounts[2], {from: accounts[2]}) + .then(function() { + return claimable.pendingOwner(); + }) + .then(function(pendingOwner) { + assert.isFalse(pendingOwner === accounts[2]); + }) + .then(done) + }); + +}); From 4599fbf2482688eddf3d5c8621c076d2d7e748ae Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Tue, 1 Nov 2016 23:34:57 -0300 Subject: [PATCH 35/54] Make Stoppable a subclass of Ownable. Fixes #47 --- contracts/Ownable.sol | 1 + contracts/Stoppable.sol | 16 +++++----------- contracts/examples/StoppableBid.sol | 6 +----- contracts/test-helpers/StoppableMock.sol | 4 ++-- test/Stoppable.js | 10 +++++----- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol index ab8e9583d..8ec50c945 100644 --- a/contracts/Ownable.sol +++ b/contracts/Ownable.sol @@ -1,4 +1,5 @@ pragma solidity ^0.4.0; + /* * Ownable * Base contract with an owner diff --git a/contracts/Stoppable.sol b/contracts/Stoppable.sol index 99ad5ad3b..d008e7b3f 100644 --- a/contracts/Stoppable.sol +++ b/contracts/Stoppable.sol @@ -1,28 +1,22 @@ pragma solidity ^0.4.0; + +import "./Ownable.sol"; /* * Stoppable * Abstract contract that allows children to implement an * emergency stop mechanism. */ -contract Stoppable { - address public curator; +contract Stoppable is Ownable { bool public stopped; modifier stopInEmergency { if (!stopped) _; } modifier onlyInEmergency { if (stopped) _; } - function Stoppable(address _curator) { - if (_curator == 0) throw; - curator = _curator; - } - - function emergencyStop() external { - if (msg.sender != curator) throw; + function emergencyStop() external onlyOwner { stopped = true; } - function release() external onlyInEmergency { - if (msg.sender != curator) throw; + function release() external onlyOwner onlyInEmergency { stopped = false; } diff --git a/contracts/examples/StoppableBid.sol b/contracts/examples/StoppableBid.sol index 364c186f8..08f278e5e 100644 --- a/contracts/examples/StoppableBid.sol +++ b/contracts/examples/StoppableBid.sol @@ -7,10 +7,6 @@ contract StoppableBid is Stoppable, PullPayment { address public highestBidder; uint public highestBid; - function StoppableBid(address _curator) - Stoppable(_curator) - PullPayment() {} - function bid() external stopInEmergency { if (msg.value <= highestBid) throw; @@ -22,7 +18,7 @@ contract StoppableBid is Stoppable, PullPayment { } function withdraw() onlyInEmergency { - suicide(curator); + suicide(owner); } } diff --git a/contracts/test-helpers/StoppableMock.sol b/contracts/test-helpers/StoppableMock.sol index dcb9834f5..e7b1e74a1 100644 --- a/contracts/test-helpers/StoppableMock.sol +++ b/contracts/test-helpers/StoppableMock.sol @@ -2,11 +2,11 @@ pragma solidity ^0.4.0; import '../Stoppable.sol'; // mock class using Stoppable -contract StoppableMock is Stoppable(msg.sender) { +contract StoppableMock is Stoppable { bool public drasticMeasureTaken; uint public count; - function StoppableMock() Stoppable(msg.sender){ + function StoppableMock() { drasticMeasureTaken = false; count = 0; } diff --git a/test/Stoppable.js b/test/Stoppable.js index f4ecc0e34..3fa4f3ad2 100644 --- a/test/Stoppable.js +++ b/test/Stoppable.js @@ -2,7 +2,7 @@ contract('Stoppable', function(accounts) { it("can perform normal process in non-emergency", function(done) { var stoppable; - return StoppableMock.new(accounts[0]) + return StoppableMock.new() .then(function(_stoppable) { stoppable = _stoppable; return stoppable.count(); @@ -24,7 +24,7 @@ contract('Stoppable', function(accounts) { it("can not perform normal process in emergency", function(done) { var stoppable; - return StoppableMock.new(accounts[0]) + return StoppableMock.new() .then(function(_stoppable) { stoppable = _stoppable; return stoppable.emergencyStop(); @@ -50,7 +50,7 @@ contract('Stoppable', function(accounts) { it("can not take drastic measure in non-emergency", function(done) { var stoppable; - return StoppableMock.new(accounts[0]) + return StoppableMock.new() .then(function(_stoppable) { stoppable = _stoppable; return stoppable.drasticMeasure(); @@ -66,7 +66,7 @@ contract('Stoppable', function(accounts) { it("can take a drastic measure in an emergency", function(done) { var stoppable; - return StoppableMock.new(accounts[0]) + return StoppableMock.new() .then(function(_stoppable) { stoppable = _stoppable; return stoppable.emergencyStop(); @@ -85,7 +85,7 @@ contract('Stoppable', function(accounts) { it("should resume allowing normal process after emergency is over", function(done) { var stoppable; - return StoppableMock.new(accounts[0]) + return StoppableMock.new() .then(function(_stoppable) { stoppable = _stoppable; return stoppable.emergencyStop(); From defcf56664920104f96a4e75aedd07ba8ea42055 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 3 Nov 2016 22:58:28 -0300 Subject: [PATCH 36/54] Replace deprecated suicide with selfdestruct --- contracts/Killable.sol | 2 +- contracts/examples/StoppableBid.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/Killable.sol b/contracts/Killable.sol index 28e5d9e98..c5f0a3641 100644 --- a/contracts/Killable.sol +++ b/contracts/Killable.sol @@ -7,6 +7,6 @@ import "./Ownable.sol"; */ contract Killable is Ownable { function kill() { - if (msg.sender == owner) suicide(owner); + if (msg.sender == owner) selfdestruct(owner); } } diff --git a/contracts/examples/StoppableBid.sol b/contracts/examples/StoppableBid.sol index 08f278e5e..fb61905a0 100644 --- a/contracts/examples/StoppableBid.sol +++ b/contracts/examples/StoppableBid.sol @@ -18,7 +18,7 @@ contract StoppableBid is Stoppable, PullPayment { } function withdraw() onlyInEmergency { - suicide(owner); + selfdestruct(owner); } } From 83f41046991262a1411c4b41995f0ce3ccfe1c06 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 3 Nov 2016 20:38:20 -0300 Subject: [PATCH 37/54] migrate to 0.4.4 --- contracts/Bounty.sol | 2 +- contracts/ERC20.sol | 2 +- contracts/Killable.sol | 2 +- contracts/LimitFunds.sol | 2 +- contracts/Migrations.sol | 2 +- contracts/Ownable.sol | 2 +- contracts/PullPayment.sol | 2 +- contracts/Rejector.sol | 9 --------- contracts/SafeMath.sol | 2 +- contracts/StandardToken.sol | 2 +- contracts/Stoppable.sol | 2 +- contracts/examples/BadArrayUse.sol | 2 +- contracts/examples/BadFailEarly.sol | 2 +- contracts/examples/BadPushPayments.sol | 2 +- contracts/examples/GoodArrayUse.sol | 2 +- contracts/examples/GoodFailEarly.sol | 2 +- contracts/examples/GoodPullPayments.sol | 2 +- contracts/examples/ProofOfExistence.sol | 6 ++---- contracts/examples/PullPaymentBid.sol | 2 +- contracts/examples/StoppableBid.sol | 2 +- contracts/test-helpers/InsecureTargetMock.sol | 2 +- contracts/test-helpers/PullPaymentMock.sol | 2 +- contracts/test-helpers/SecureTargetMock.sol | 2 +- contracts/test-helpers/StandardTokenMock.sol | 2 +- contracts/test-helpers/StoppableMock.sol | 2 +- contracts/token/CrowdsaleToken.sol | 2 +- contracts/token/SimpleToken.sol | 2 +- test/TestOwnable.sol | 2 +- 28 files changed, 28 insertions(+), 39 deletions(-) delete mode 100644 contracts/Rejector.sol diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index f5faff6e3..12e25181c 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import './PullPayment.sol'; import './Killable.sol'; diff --git a/contracts/ERC20.sol b/contracts/ERC20.sol index 42b65a617..9d32c69fd 100644 --- a/contracts/ERC20.sol +++ b/contracts/ERC20.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; // see https://github.com/ethereum/EIPs/issues/20 diff --git a/contracts/Killable.sol b/contracts/Killable.sol index c5f0a3641..485576d76 100644 --- a/contracts/Killable.sol +++ b/contracts/Killable.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import "./Ownable.sol"; /* diff --git a/contracts/LimitFunds.sol b/contracts/LimitFunds.sol index 5f3200c33..303444abc 100644 --- a/contracts/LimitFunds.sol +++ b/contracts/LimitFunds.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract LimitFunds { uint LIMIT = 5000; diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol index 608bba8f9..5e1a61e11 100644 --- a/contracts/Migrations.sol +++ b/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract Migrations { address public owner; uint public last_completed_migration; diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol index 8ec50c945..6da95fb74 100644 --- a/contracts/Ownable.sol +++ b/contracts/Ownable.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; /* * Ownable diff --git a/contracts/PullPayment.sol b/contracts/PullPayment.sol index b8d094455..c05f99caf 100644 --- a/contracts/PullPayment.sol +++ b/contracts/PullPayment.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; /* * PullPayment * Base contract supporting async send for pull payments. diff --git a/contracts/Rejector.sol b/contracts/Rejector.sol deleted file mode 100644 index 05525d5e9..000000000 --- a/contracts/Rejector.sol +++ /dev/null @@ -1,9 +0,0 @@ -pragma solidity ^0.4.0; -/* - * Rejector - * Base contract for rejecting direct deposits. - * Fallback function throws immediately. - */ -contract Rejector { - function() { throw; } -} diff --git a/contracts/SafeMath.sol b/contracts/SafeMath.sol index 2d4bebf77..16e8dae76 100644 --- a/contracts/SafeMath.sol +++ b/contracts/SafeMath.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; /** * Math operations with safety checks diff --git a/contracts/StandardToken.sol b/contracts/StandardToken.sol index 69b8e186f..9fe80f7d8 100644 --- a/contracts/StandardToken.sol +++ b/contracts/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import './ERC20.sol'; import './SafeMath.sol'; diff --git a/contracts/Stoppable.sol b/contracts/Stoppable.sol index d008e7b3f..ad48f6bc5 100644 --- a/contracts/Stoppable.sol +++ b/contracts/Stoppable.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import "./Ownable.sol"; /* diff --git a/contracts/examples/BadArrayUse.sol b/contracts/examples/BadArrayUse.sol index d76160dfd..d5c1c7b7b 100644 --- a/contracts/examples/BadArrayUse.sol +++ b/contracts/examples/BadArrayUse.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../PullPayment.sol'; // UNSAFE CODE, DO NOT USE! diff --git a/contracts/examples/BadFailEarly.sol b/contracts/examples/BadFailEarly.sol index 801ed4754..0ad95a92e 100644 --- a/contracts/examples/BadFailEarly.sol +++ b/contracts/examples/BadFailEarly.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; // UNSAFE CODE, DO NOT USE! contract BadFailEarly { diff --git a/contracts/examples/BadPushPayments.sol b/contracts/examples/BadPushPayments.sol index c8d6b9d19..510b39473 100644 --- a/contracts/examples/BadPushPayments.sol +++ b/contracts/examples/BadPushPayments.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; // UNSAFE CODE, DO NOT USE! contract BadPushPayments { diff --git a/contracts/examples/GoodArrayUse.sol b/contracts/examples/GoodArrayUse.sol index 150eb6ba0..ce450293e 100644 --- a/contracts/examples/GoodArrayUse.sol +++ b/contracts/examples/GoodArrayUse.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../PullPayment.sol'; contract GoodArrayUse is PullPayment { diff --git a/contracts/examples/GoodFailEarly.sol b/contracts/examples/GoodFailEarly.sol index 1dc1b8d32..7a3ec9e60 100644 --- a/contracts/examples/GoodFailEarly.sol +++ b/contracts/examples/GoodFailEarly.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract GoodFailEarly { diff --git a/contracts/examples/GoodPullPayments.sol b/contracts/examples/GoodPullPayments.sol index 6a03bbc02..9236d0a4f 100644 --- a/contracts/examples/GoodPullPayments.sol +++ b/contracts/examples/GoodPullPayments.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract GoodPullPayments { address highestBidder; uint highestBid; diff --git a/contracts/examples/ProofOfExistence.sol b/contracts/examples/ProofOfExistence.sol index 95f2412cb..a4ec0d45f 100644 --- a/contracts/examples/ProofOfExistence.sol +++ b/contracts/examples/ProofOfExistence.sol @@ -1,12 +1,10 @@ -pragma solidity ^0.4.0; - -import "../Rejector.sol"; +pragma solidity ^0.4.4; /* * Proof of Existence example contract * see https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05 */ -contract ProofOfExistence is Rejector { +contract ProofOfExistence { mapping (bytes32 => bool) public proofs; diff --git a/contracts/examples/PullPaymentBid.sol b/contracts/examples/PullPaymentBid.sol index 5cf4fe578..01c91c991 100644 --- a/contracts/examples/PullPaymentBid.sol +++ b/contracts/examples/PullPaymentBid.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../PullPayment.sol'; diff --git a/contracts/examples/StoppableBid.sol b/contracts/examples/StoppableBid.sol index fb61905a0..0edbd8b3e 100644 --- a/contracts/examples/StoppableBid.sol +++ b/contracts/examples/StoppableBid.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../PullPayment.sol'; import '../Stoppable.sol'; diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetMock.sol index 0db1ee180..f0d6e2883 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract InsecureTargetMock { function checkInvariant() returns(bool){ diff --git a/contracts/test-helpers/PullPaymentMock.sol b/contracts/test-helpers/PullPaymentMock.sol index 51cb3e107..0c8c3d943 100644 --- a/contracts/test-helpers/PullPaymentMock.sol +++ b/contracts/test-helpers/PullPaymentMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../PullPayment.sol'; // mock class using PullPayment diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetMock.sol index 4c7da0093..e571ae394 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; contract SecureTargetMock { function checkInvariant() returns(bool){ diff --git a/contracts/test-helpers/StandardTokenMock.sol b/contracts/test-helpers/StandardTokenMock.sol index 38be987bd..794d78090 100644 --- a/contracts/test-helpers/StandardTokenMock.sol +++ b/contracts/test-helpers/StandardTokenMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../StandardToken.sol'; // mock class using StandardToken diff --git a/contracts/test-helpers/StoppableMock.sol b/contracts/test-helpers/StoppableMock.sol index e7b1e74a1..6d3464bdf 100644 --- a/contracts/test-helpers/StoppableMock.sol +++ b/contracts/test-helpers/StoppableMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import '../Stoppable.sol'; // mock class using Stoppable diff --git a/contracts/token/CrowdsaleToken.sol b/contracts/token/CrowdsaleToken.sol index 6d748f421..1172efe5d 100644 --- a/contracts/token/CrowdsaleToken.sol +++ b/contracts/token/CrowdsaleToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import "../StandardToken.sol"; diff --git a/contracts/token/SimpleToken.sol b/contracts/token/SimpleToken.sol index 917a226cb..3b115a6fd 100644 --- a/contracts/token/SimpleToken.sol +++ b/contracts/token/SimpleToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import "../StandardToken.sol"; diff --git a/test/TestOwnable.sol b/test/TestOwnable.sol index 1a5433598..cb3cbcf21 100644 --- a/test/TestOwnable.sol +++ b/test/TestOwnable.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity ^0.4.4; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Ownable.sol"; From 278f060c568f69dcc2b1ba7d45ee447456301d69 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Tue, 1 Nov 2016 23:51:45 -0300 Subject: [PATCH 38/54] Replace composition for inheritance in Bounty --- README.md | 30 ++++++------- contracts/Bounty.sol | 18 ++------ ...argetMock.sol => InsecureTargetBounty.sol} | 6 ++- ...eTargetMock.sol => SecureTargetBounty.sol} | 6 ++- migrations/2_deploy_contracts.js | 6 +-- test/Bounty.js | 45 ++++--------------- 6 files changed, 36 insertions(+), 75 deletions(-) rename contracts/test-helpers/{InsecureTargetMock.sol => InsecureTargetBounty.sol} (57%) rename contracts/test-helpers/{SecureTargetMock.sol => SecureTargetBounty.sol} (56%) diff --git a/README.md b/README.md index 618df8d40..827ce016e 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,18 @@ contract MetaCoin is Rejector { ## Add your own bounty contract -So far you inherit Zeppelin contracts into your own contract through inheritance. -A bounty contract, however, is a special contract that is deployed on its own. -Each researcher creates a separate copy of your contract, and can claims bounty by breaking invariants logic on the copy of your contract without hacking your original contract. +To create a bounty for your contract, inherit from the base Bounty contract and provide an implementation for `deployContract()` returning the new contract address. -To use the bounty contract, please follow the below instruction. +``` +import "./zeppelin/Bounty.sol"; +import "./YourContract.sol"; + +contract YourBounty is Bounty { + function deployContract() internal returns(address) { + return new YourContract() + } +} +``` ### Implement invariant logic into your smart contract @@ -45,29 +52,20 @@ At contracts/YourContract.sol ``` contract YourContract { - function checkInvariant() returns(bool){ + function checkInvariant() returns(bool) { // Implement your logic to make sure that none of the state is broken. } } - -contract YourContractFactory { - function deployContract() returns (address) { - // This contract allows researchers to create a copy of your contract - return new YourContract(); - } -} ``` -### Add the bounty contracts as well as your contracts into migrations +### Deploy your bounty contract as usual At `migrations/2_deploy_contracts.js` ``` module.exports = function(deployer) { deployer.deploy(YourContract); - deployer.deploy(YourContractFactory).then(function() { - return deployer.deploy(Bounty, YourContractFactory.address); - }); + deployer.deploy(YourBounty); }; ``` diff --git a/contracts/Bounty.sol b/contracts/Bounty.sol index 12e25181c..0f2ab6c2b 100644 --- a/contracts/Bounty.sol +++ b/contracts/Bounty.sol @@ -8,10 +8,6 @@ import './Killable.sol'; * the contract you bet reward against. */ -contract Factory { - function deployContract() returns (address); -} - contract Target { function checkInvariant() returns(bool); } @@ -19,7 +15,6 @@ contract Target { contract Bounty is PullPayment, Killable { Target target; bool public claimed; - address public factoryAddress; mapping(address => address) public researchers; event TargetCreated(address createdAddress); @@ -28,22 +23,15 @@ contract Bounty is PullPayment, Killable { if (claimed) throw; } - modifier withAddress(address _address) { - if(_address == 0) throw; - _; - } - - function Bounty(address _factoryAddress) withAddress(_factoryAddress){ - factoryAddress = _factoryAddress; - } - function createTarget() returns(Target) { - target = Target(Factory(factoryAddress).deployContract()); + target = Target(deployContract()); researchers[target] = msg.sender; TargetCreated(target); return target; } + function deployContract() internal returns(address); + function checkInvariant() returns(bool){ return target.checkInvariant(); } diff --git a/contracts/test-helpers/InsecureTargetMock.sol b/contracts/test-helpers/InsecureTargetBounty.sol similarity index 57% rename from contracts/test-helpers/InsecureTargetMock.sol rename to contracts/test-helpers/InsecureTargetBounty.sol index f0d6e2883..5087cb6a6 100644 --- a/contracts/test-helpers/InsecureTargetMock.sol +++ b/contracts/test-helpers/InsecureTargetBounty.sol @@ -1,13 +1,15 @@ pragma solidity ^0.4.4; +import "../Bounty.sol"; + contract InsecureTargetMock { function checkInvariant() returns(bool){ return false; } } -contract InsecureTargetFactory { - function deployContract() returns (address) { +contract InsecureTargetBounty is Bounty { + function deployContract() internal returns (address) { return new InsecureTargetMock(); } } diff --git a/contracts/test-helpers/SecureTargetMock.sol b/contracts/test-helpers/SecureTargetBounty.sol similarity index 56% rename from contracts/test-helpers/SecureTargetMock.sol rename to contracts/test-helpers/SecureTargetBounty.sol index e571ae394..f83daf407 100644 --- a/contracts/test-helpers/SecureTargetMock.sol +++ b/contracts/test-helpers/SecureTargetBounty.sol @@ -1,13 +1,15 @@ pragma solidity ^0.4.4; +import "../Bounty.sol"; + contract SecureTargetMock { function checkInvariant() returns(bool){ return true; } } -contract SecureTargetFactory { - function deployContract() returns (address) { +contract SecureTargetBounty is Bounty { + function deployContract() internal returns (address) { return new SecureTargetMock(); } } diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 869c60623..521eaee16 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -5,9 +5,7 @@ module.exports = function(deployer) { deployer.deploy(Ownable); deployer.deploy(LimitFunds); if(deployer.network == 'test'){ - deployer.deploy(SecureTargetMock); - deployer.deploy(SecureTargetFactory); - deployer.deploy(InsecureTargetMock); - deployer.deploy(InsecureTargetFactory); + deployer.deploy(SecureTargetBounty); + deployer.deploy(InsecureTargetBounty); }; }; diff --git a/test/Bounty.js b/test/Bounty.js index 8f4b81933..17eb80a66 100644 --- a/test/Bounty.js +++ b/test/Bounty.js @@ -7,25 +7,12 @@ var sendReward = function(sender, receiver, value){ } contract('Bounty', function(accounts) { - it("creates bounty contract with factory address", function(done){ - var target = SecureTargetMock.deployed(); - - Bounty.new(target.address). - then(function(bounty){ - return bounty.factoryAddress.call() - }). - then(function(address){ - assert.equal(address, target.address) - }). - then(done); - }) it("sets reward", function(done){ - var target = SecureTargetMock.deployed(); var owner = accounts[0]; var reward = web3.toWei(1, "ether"); - Bounty.new(target.address). + SecureTargetBounty.new(). then(function(bounty){ sendReward(owner, bounty.address, reward); assert.equal(reward, web3.eth.getBalance(bounty.address).toNumber()) @@ -33,24 +20,12 @@ contract('Bounty', function(accounts) { then(done); }) - it("cannot create bounty without address", function(done){ - var target = SecureTargetMock.deployed(); - Bounty.new(). - then(function(bounty){ - throw {name : "NoThrowError", message : "should not come here"}; - }). - catch(function(error){ - assert.notEqual(error.name, "NoThrowError"); - }). - then(done); - }) - it("empties itself when killed", function(done){ - var target = SecureTargetMock.deployed(); var owner = accounts[0]; var reward = web3.toWei(1, "ether"); var bounty; - Bounty.new(target.address). + + SecureTargetBounty.new(). then(function(_bounty){ bounty = _bounty; sendReward(owner, bounty.address, reward); @@ -65,9 +40,9 @@ contract('Bounty', function(accounts) { describe("Against secure contract", function(){ it("checkInvariant returns true", function(done){ - var targetFactory = SecureTargetFactory.deployed(); var bounty; - Bounty.new(targetFactory.address). + + SecureTargetBounty.new(). then(function(_bounty) { bounty = _bounty; return bounty.createTarget(); @@ -82,12 +57,11 @@ contract('Bounty', function(accounts) { }) it("cannot claim reward", function(done){ - var targetFactory = SecureTargetFactory.deployed(); var owner = accounts[0]; var researcher = accounts[1]; var reward = web3.toWei(1, "ether"); - Bounty.new(targetFactory.address). + SecureTargetBounty.new(). then(function(bounty) { var event = bounty.TargetCreated({}); event.watch(function(err, result) { @@ -118,9 +92,9 @@ contract('Bounty', function(accounts) { describe("Against broken contract", function(){ it("checkInvariant returns false", function(done){ - var targetFactory = InsecureTargetFactory.deployed(); var bounty; - Bounty.new(targetFactory.address). + + InsecureTargetBounty.new(). then(function(_bounty) { bounty = _bounty; return bounty.createTarget(); @@ -135,12 +109,11 @@ contract('Bounty', function(accounts) { }) it("claims reward", function(done){ - var targetFactory = InsecureTargetFactory.deployed(); var owner = accounts[0]; var researcher = accounts[1]; var reward = web3.toWei(1, "ether"); - Bounty.new(targetFactory.address). + InsecureTargetBounty.new(). then(function(bounty) { var event = bounty.TargetCreated({}); event.watch(function(err, result) { From 5f6b7f93018d330db18d1003ba5723e35fc009b0 Mon Sep 17 00:00:00 2001 From: Steve Ellis Date: Sun, 13 Nov 2016 15:55:06 -0500 Subject: [PATCH 39/54] add check prevening ownables from getting stuck --- contracts/Ownable.sol | 2 +- test/Ownable.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol index 6da95fb74..2998fa403 100644 --- a/contracts/Ownable.sol +++ b/contracts/Ownable.sol @@ -17,7 +17,7 @@ contract Ownable { } function transfer(address newOwner) onlyOwner { - owner = newOwner; + if (newOwner != address(0)) owner = newOwner; } } diff --git a/test/Ownable.js b/test/Ownable.js index 83ae4b93e..8d9c51083 100644 --- a/test/Ownable.js +++ b/test/Ownable.js @@ -34,4 +34,20 @@ contract('Ownable', function(accounts) { .then(done) }); + it("should guard ownership against stuck state" ,function(done) { + var ownable = Ownable.deployed(); + + return ownable.owner() + .then(function (originalOwner) { + return ownable.transfer(null, {from: originalOwner}) + .then(function() { + return ownable.owner(); + }) + .then(function(newOwner) { + assert.equal(originalOwner, newOwner); + }) + .then(done); + }); + }); + }); From 1547000d1c63257659c09b79019dd5114c26d08b Mon Sep 17 00:00:00 2001 From: Steve Ellis Date: Sun, 13 Nov 2016 16:49:05 -0500 Subject: [PATCH 40/54] move Ownable tests away from migration fixtures --- test/Ownable.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/Ownable.js b/test/Ownable.js index 83ae4b93e..8663da9ee 100644 --- a/test/Ownable.js +++ b/test/Ownable.js @@ -1,6 +1,13 @@ contract('Ownable', function(accounts) { + var ownable; + + beforeEach(function() { + return Ownable.new().then(function(deployed) { + ownable = deployed; + }); + }); + it("should have an owner", function(done) { - var ownable = Ownable.deployed(); return ownable.owner() .then(function(owner) { assert.isTrue(owner != 0); @@ -9,7 +16,6 @@ contract('Ownable', function(accounts) { }); it("changes owner after transfer", function(done) { - var ownable = Ownable.deployed(); var other = accounts[1]; return ownable.transfer(other) .then(function() { @@ -22,7 +28,6 @@ contract('Ownable', function(accounts) { }); it("should prevent non-owners from transfering" ,function(done) { - var ownable = Ownable.deployed(); var other = accounts[2]; return ownable.transfer(other, {from: accounts[2]}) .then(function() { From a156064b33eb4d0b815710a93fe878aa69e2faa6 Mon Sep 17 00:00:00 2001 From: Steve Ellis Date: Sun, 13 Nov 2016 17:00:15 -0500 Subject: [PATCH 41/54] make claimable use fresh instances for each test --- test/Claimable.js | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/test/Claimable.js b/test/Claimable.js index 9d62663d6..464b7406e 100644 --- a/test/Claimable.js +++ b/test/Claimable.js @@ -1,7 +1,13 @@ contract('Claimable', function(accounts) { + var claimable; + + beforeEach(function() { + return Claimable.new().then(function(deployed) { + claimable = deployed; + }); + }); it("should have an owner", function(done) { - var claimable = Claimable.deployed(); return claimable.owner() .then(function(owner) { assert.isTrue(owner != 0); @@ -10,19 +16,18 @@ contract('Claimable', function(accounts) { }); it("changes pendingOwner after transfer", function(done) { - var claimable = Claimable.deployed(); - return claimable.transfer(accounts[1]) + var newOwner = accounts[1]; + return claimable.transfer(newOwner) .then(function() { return claimable.pendingOwner(); }) .then(function(pendingOwner) { - assert.isTrue(pendingOwner === accounts[1]); + assert.isTrue(pendingOwner === newOwner); }) .then(done) }); it("should prevent to claimOwnership from no pendingOwner", function(done) { - var claimable = Claimable.deployed(); return claimable.claimOwnership({from: accounts[2]}) .then(function() { return claimable.owner(); @@ -33,20 +38,7 @@ contract('Claimable', function(accounts) { .then(done) }); - it("changes allow pending owner to claim ownership", function(done) { - var claimable = Claimable.deployed(); - return claimable.claimOwnership({from: accounts[1]}) - .then(function() { - return claimable.owner(); - }) - .then(function(owner) { - assert.isTrue(owner === accounts[1]); - }) - .then(done) - }); - it("should prevent non-owners from transfering" ,function(done) { - var claimable = Claimable.deployed(); return claimable.transfer(accounts[2], {from: accounts[2]}) .then(function() { return claimable.pendingOwner(); @@ -57,4 +49,23 @@ contract('Claimable', function(accounts) { .then(done) }); + describe("after initiating a transfer", function () { + var newOwner; + + beforeEach(function () { + newOwner = accounts[1]; + return claimable.transfer(newOwner); + }); + + it("changes allow pending owner to claim ownership", function(done) { + return claimable.claimOwnership({from: newOwner}) + .then(function() { + return claimable.owner(); + }) + .then(function(owner) { + assert.isTrue(owner === newOwner); + }) + .then(done) + }); + }); }); From 0e0d6c0a1ca1dab0cffa0204672685c7b420d202 Mon Sep 17 00:00:00 2001 From: Demian Brener Date: Mon, 14 Nov 2016 17:54:21 -0300 Subject: [PATCH 42/54] Collaborating organizations list --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 827ce016e..143556c12 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,18 @@ Interested in contributing to Zeppelin? - Issue tracker: https://github.com/OpenZeppelin/zeppelin-solidity/issues - Contribution guidelines: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/CONTRIBUTING.md -## Projects using Zeppelin -- [Blockparty](https://github.com/makoto/blockparty) +## Collaborating organizations and audits by Zeppelin +- [Golem](https://golem.network/) +- [Mediachain](https://golem.network/) +- [Truffle](http://truffleframework.com/) +- [Firstblood](http://firstblood.io/) +- [Rootstock](http://www.rsk.co/) +- [Consensys](https://consensys.net/) +- [DigixGlobal](https://www.dgx.io/) +- [DemocracyEarth](http://democracy.earth/) +- [Ether.camp](http://www.ether.camp/) + +among others... ## Contracts TODO From 23448fa53cd851ba3cb804d9509b3bd5e01dd53e Mon Sep 17 00:00:00 2001 From: Demian Brener Date: Mon, 14 Nov 2016 17:56:15 -0300 Subject: [PATCH 43/54] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 143556c12..ccdfb3ede 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Interested in contributing to Zeppelin? - [Rootstock](http://www.rsk.co/) - [Consensys](https://consensys.net/) - [DigixGlobal](https://www.dgx.io/) +- [Coinfund](https://coinfund.io/) - [DemocracyEarth](http://democracy.earth/) - [Ether.camp](http://www.ether.camp/) From 2b62f349eec390f56a195088177d7597e428bb70 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Mon, 14 Nov 2016 18:18:38 -0300 Subject: [PATCH 44/54] Fill totalSupply in StandardTokenMock --- contracts/test-helpers/StandardTokenMock.sol | 1 + test/StandardToken.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/contracts/test-helpers/StandardTokenMock.sol b/contracts/test-helpers/StandardTokenMock.sol index 794d78090..55a9fc38e 100644 --- a/contracts/test-helpers/StandardTokenMock.sol +++ b/contracts/test-helpers/StandardTokenMock.sol @@ -6,6 +6,7 @@ contract StandardTokenMock is StandardToken { function StandardTokenMock(address initialAccount, uint initialBalance) { balances[initialAccount] = initialBalance; + totalSupply = initialBalance; } } diff --git a/test/StandardToken.js b/test/StandardToken.js index 6b83b32b8..ec2f158cd 100644 --- a/test/StandardToken.js +++ b/test/StandardToken.js @@ -1,5 +1,16 @@ contract('StandardToken', function(accounts) { + it("should return the correct totalSupply after construction", function(done) { + return StandardTokenMock.new(accounts[0], 100) + .then(function(token) { + return token.totalSupply(); + }) + .then(function(totalSupply) { + assert.equal(totalSupply, 100); + }) + .then(done); + }) + it("should return the correct allowance amount after approval", function(done) { var token; return StandardTokenMock.new() From 4cd3de4cbf35ca28d82ee05e474cf9ca450de663 Mon Sep 17 00:00:00 2001 From: Demian Brener Date: Mon, 14 Nov 2016 19:24:28 -0300 Subject: [PATCH 45/54] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ccdfb3ede..675a0828a 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Interested in contributing to Zeppelin? - [DigixGlobal](https://www.dgx.io/) - [Coinfund](https://coinfund.io/) - [DemocracyEarth](http://democracy.earth/) +- [Singatura](https://signatura.co/es/) - [Ether.camp](http://www.ether.camp/) among others... From 7c7ca1cf5fb656ae715f8b499be57318d1afc595 Mon Sep 17 00:00:00 2001 From: Demian Brener Date: Mon, 14 Nov 2016 19:28:51 -0300 Subject: [PATCH 46/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 675a0828a..1650ee9f3 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Interested in contributing to Zeppelin? - [DigixGlobal](https://www.dgx.io/) - [Coinfund](https://coinfund.io/) - [DemocracyEarth](http://democracy.earth/) -- [Singatura](https://signatura.co/es/) +- [Signatura](https://signatura.co/) - [Ether.camp](http://www.ether.camp/) among others... From 69463d9120835bc1214be6f9d6d09b2bfe42f0dd Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 16 Nov 2016 12:51:41 -0300 Subject: [PATCH 47/54] fix truffle build --- truffle.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/truffle.js b/truffle.js index 6e3970801..dc2d3eaf9 100644 --- a/truffle.js +++ b/truffle.js @@ -1,14 +1,4 @@ module.exports = { - build: { - "index.html": "index.html", - "app.js": [ - "javascripts/app.js" - ], - "app.css": [ - "stylesheets/app.css" - ], - "images/": "images/" - }, rpc: { host: "localhost", port: 8545 From 10e8d6a09f9aa145cf89384612ea0ed56851825b Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 16 Nov 2016 12:46:27 -0300 Subject: [PATCH 48/54] move erc20 and standardtoken to token folder --- contracts/test-helpers/StandardTokenMock.sol | 2 +- contracts/token/CrowdsaleToken.sol | 2 +- contracts/{ => token}/ERC20.sol | 0 contracts/token/SimpleToken.sol | 2 +- contracts/{ => token}/StandardToken.sol | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename contracts/{ => token}/ERC20.sol (100%) rename contracts/{ => token}/StandardToken.sol (98%) diff --git a/contracts/test-helpers/StandardTokenMock.sol b/contracts/test-helpers/StandardTokenMock.sol index 55a9fc38e..5b222be9a 100644 --- a/contracts/test-helpers/StandardTokenMock.sol +++ b/contracts/test-helpers/StandardTokenMock.sol @@ -1,5 +1,5 @@ pragma solidity ^0.4.4; -import '../StandardToken.sol'; +import '../token/StandardToken.sol'; // mock class using StandardToken contract StandardTokenMock is StandardToken { diff --git a/contracts/token/CrowdsaleToken.sol b/contracts/token/CrowdsaleToken.sol index 1172efe5d..ba3100cbc 100644 --- a/contracts/token/CrowdsaleToken.sol +++ b/contracts/token/CrowdsaleToken.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.4; -import "../StandardToken.sol"; +import "./StandardToken.sol"; /* * Simple ERC20 Token example, with crowdsale token creation diff --git a/contracts/ERC20.sol b/contracts/token/ERC20.sol similarity index 100% rename from contracts/ERC20.sol rename to contracts/token/ERC20.sol diff --git a/contracts/token/SimpleToken.sol b/contracts/token/SimpleToken.sol index 3b115a6fd..fc7efd0d6 100644 --- a/contracts/token/SimpleToken.sol +++ b/contracts/token/SimpleToken.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.4; -import "../StandardToken.sol"; +import "./StandardToken.sol"; /* * Very simple ERC20 Token example, where all tokens are pre-assigned diff --git a/contracts/StandardToken.sol b/contracts/token/StandardToken.sol similarity index 98% rename from contracts/StandardToken.sol rename to contracts/token/StandardToken.sol index 9fe80f7d8..f2a2138bd 100644 --- a/contracts/StandardToken.sol +++ b/contracts/token/StandardToken.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.4; import './ERC20.sol'; -import './SafeMath.sol'; +import '../SafeMath.sol'; /** * ERC20 token From 8acc783309de2456ce0ec4c8a23bbd76b2abbdeb Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 16 Nov 2016 13:13:40 -0300 Subject: [PATCH 49/54] rename lite->basic --- contracts/token/BasicToken.sol | 28 ++++++++++++++++++++++++++++ contracts/token/ERC20Basic.sol | 9 +++++++++ 2 files changed, 37 insertions(+) create mode 100644 contracts/token/BasicToken.sol create mode 100644 contracts/token/ERC20Basic.sol diff --git a/contracts/token/BasicToken.sol b/contracts/token/BasicToken.sol new file mode 100644 index 000000000..3f54f5192 --- /dev/null +++ b/contracts/token/BasicToken.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.4.4; + +import './ERC20Basic.sol'; +import '../SafeMath.sol'; + +/** + * Basic token + * Basic version of StandardToken, with no allowances + */ +contract BasicToken is ERC20Lite, SafeMath { + + mapping(address => uint) balances; + + function transfer(address _to, uint _value) returns (bool success) { + if (balances[msg.sender] < _value) { + throw; + } + balances[msg.sender] = safeSub(balances[msg.sender], _value); + balances[_to] = safeAdd(balances[_to], _value); + Transfer(msg.sender, _to, _value); + return true; + } + + function balanceOf(address _owner) constant returns (uint balance) { + return balances[_owner]; + } + +} diff --git a/contracts/token/ERC20Basic.sol b/contracts/token/ERC20Basic.sol new file mode 100644 index 000000000..8128991e4 --- /dev/null +++ b/contracts/token/ERC20Basic.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.4; + + +contract ERC20Basic { + uint public totalSupply; + function balanceOf(address who) constant returns (uint); + function transfer(address to, uint value) returns (bool ok); + event Transfer(address indexed from, address indexed to, uint value); +} From 3e9bfbc77a118e5bac5bedcdddca45128b015321 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 16 Nov 2016 13:18:42 -0300 Subject: [PATCH 50/54] add simple tests for BasicToken --- contracts/test-helpers/BasicTokenMock.sol | 12 ++++++ contracts/token/BasicToken.sol | 2 +- test/BasicToken.js | 49 +++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 contracts/test-helpers/BasicTokenMock.sol create mode 100644 test/BasicToken.js diff --git a/contracts/test-helpers/BasicTokenMock.sol b/contracts/test-helpers/BasicTokenMock.sol new file mode 100644 index 000000000..9f19e47ea --- /dev/null +++ b/contracts/test-helpers/BasicTokenMock.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.4; +import '../token/BasicToken.sol'; + +// mock class using BasicToken +contract BasicTokenMock is BasicToken { + + function BasicTokenMock(address initialAccount, uint initialBalance) { + balances[initialAccount] = initialBalance; + totalSupply = initialBalance; + } + +} diff --git a/contracts/token/BasicToken.sol b/contracts/token/BasicToken.sol index 3f54f5192..6e6a747c2 100644 --- a/contracts/token/BasicToken.sol +++ b/contracts/token/BasicToken.sol @@ -7,7 +7,7 @@ import '../SafeMath.sol'; * Basic token * Basic version of StandardToken, with no allowances */ -contract BasicToken is ERC20Lite, SafeMath { +contract BasicToken is ERC20Basic, SafeMath { mapping(address => uint) balances; diff --git a/test/BasicToken.js b/test/BasicToken.js new file mode 100644 index 000000000..12b411c41 --- /dev/null +++ b/test/BasicToken.js @@ -0,0 +1,49 @@ +contract('BasicToken', function(accounts) { + + it("should return the correct totalSupply after construction", function(done) { + return BasicTokenMock.new(accounts[0], 100) + .then(function(token) { + return token.totalSupply(); + }) + .then(function(totalSupply) { + assert.equal(totalSupply, 100); + }) + .then(done); + }) + + it("should return correct balances after transfer", function(done) { + var token; + return BasicTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.transfer(accounts[1], 100); + }) + .then(function() { + return token.balanceOf(accounts[0]); + }) + .then(function(balance) { + assert.equal(balance, 0); + }) + .then(function() { + return token.balanceOf(accounts[1]); + }) + .then(function(balance) { + assert.equal(balance, 100); + }) + .then(done); + }); + + it("should throw an error when trying to transfer more than balance", function(done) { + var token; + return BasicTokenMock.new(accounts[0], 100) + .then(function(_token) { + token = _token; + return token.transfer(accounts[1], 101); + }) + .catch(function(error) { + if (error.message.search('invalid JUMP') == -1) throw error + }) + .then(done); + }); + +}); From b4c00cf6ec94a3bf0788534769dd96dcd562bd5b Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 16 Nov 2016 20:30:20 -0300 Subject: [PATCH 51/54] remove references to rejector on README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1650ee9f3..4ef3b8096 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ npm i zeppelin-solidity After that, you'll get all the library's contracts in the `contracts/zeppelin` folder. You can use the contracts in the library like so: ```js -import "./zeppelin/Rejector.sol"; +import "./zeppelin/Ownable.sol"; -contract MetaCoin is Rejector { +contract MyContract is Ownable { ... } ``` @@ -103,9 +103,9 @@ bounty.kill() We also support Truffle Beta npm integration. If you're using Truffle Beta, the contracts in `node_modules` will be enough, so feel free to delete the copies at your `contracts` folder. If you're using Truffle Beta, you can use Zeppelin contracts like so: ```js -import "zeppelin-solidity/contracts/Rejector.sol"; +import "zeppelin-solidity/contracts/Ownable.sol"; -contract MetaCoin is Rejector { +contract MyContract is Ownable { ... } ``` From 44138018dc5ea15dae4a998272ff42bd6d982972 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Nov 2016 18:34:53 -0300 Subject: [PATCH 52/54] limit funds improvements --- contracts/LimitBalance.sol | 18 ++++++++++ contracts/LimitFunds.sol | 12 ------- contracts/test-helpers/LimitBalanceMock.sol | 10 ++++++ test/LimitBalance.js | 38 +++++++++++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 contracts/LimitBalance.sol delete mode 100644 contracts/LimitFunds.sol create mode 100644 contracts/test-helpers/LimitBalanceMock.sol create mode 100644 test/LimitBalance.js diff --git a/contracts/LimitBalance.sol b/contracts/LimitBalance.sol new file mode 100644 index 000000000..f877512e0 --- /dev/null +++ b/contracts/LimitBalance.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.4; +contract LimitBalance { + + uint public limit; + + function LimitBalance(uint _limit) { + limit = _limit; + } + + modifier limitedPayable() { + if (this.balance + msg.value > limit) { + throw; + } + _; + + } + +} diff --git a/contracts/LimitFunds.sol b/contracts/LimitFunds.sol deleted file mode 100644 index 303444abc..000000000 --- a/contracts/LimitFunds.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.4.4; -contract LimitFunds { - - uint LIMIT = 5000; - - function() { throw; } - - function deposit() { - if (this.balance > LIMIT) throw; - } - -} diff --git a/contracts/test-helpers/LimitBalanceMock.sol b/contracts/test-helpers/LimitBalanceMock.sol new file mode 100644 index 000000000..c0f05ba8b --- /dev/null +++ b/contracts/test-helpers/LimitBalanceMock.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.4; +import '../LimitBalance.sol'; + +// mock class using LimitBalance +contract LimitBalanceMock is LimitBalance(1000) { + + function limitedDeposit() payable limitedPayable { + } + +} diff --git a/test/LimitBalance.js b/test/LimitBalance.js new file mode 100644 index 000000000..2afcd2da2 --- /dev/null +++ b/test/LimitBalance.js @@ -0,0 +1,38 @@ +contract('LimitBalance', function(accounts) { + var lb; + + beforeEach(function() { + return LimitBalanceMock.new().then(function(deployed) { + lb = deployed; + }); + }); + + var LIMIT = 1000; + + it("should expose limit", function(done) { + return lb.limit() + .then(function(limit) { + assert.equal(limit, LIMIT); + }) + .then(done) + }); + + it("should allow sending below limit", function(done) { + var amount = 1; + return lb.limitedDeposit({value: amount}) + .then(function() { + assert.equal(web3.eth.getBalance(lb.address), amount); + }) + .then(done) + }); + + it("shouldnt allow sending above limit", function(done) { + var amount = 1100; + return lb.limitedDeposit({value: amount}) + .catch(function(error) { + if (error.message.search('invalid JUMP') == -1) throw error + }) + .then(done) + }); + +}); From 4cd8aa7900127bd3498249e9b9ed49979ca60769 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 18 Nov 2016 12:33:52 -0300 Subject: [PATCH 53/54] add more limit tests to LimitBalance --- contracts/LimitBalance.sol | 2 +- test/LimitBalance.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/contracts/LimitBalance.sol b/contracts/LimitBalance.sol index f877512e0..d8c29baec 100644 --- a/contracts/LimitBalance.sol +++ b/contracts/LimitBalance.sol @@ -8,7 +8,7 @@ contract LimitBalance { } modifier limitedPayable() { - if (this.balance + msg.value > limit) { + if (this.balance > limit) { throw; } _; diff --git a/test/LimitBalance.js b/test/LimitBalance.js index 2afcd2da2..ce75821a0 100644 --- a/test/LimitBalance.js +++ b/test/LimitBalance.js @@ -35,4 +35,30 @@ contract('LimitBalance', function(accounts) { .then(done) }); + it("should allow multiple sends below limit", function(done) { + var amount = 500; + return lb.limitedDeposit({value: amount}) + .then(function() { + assert.equal(web3.eth.getBalance(lb.address), amount); + return lb.limitedDeposit({value: amount}) + }) + .then(function() { + assert.equal(web3.eth.getBalance(lb.address), amount*2); + }) + .then(done) + }); + + it("shouldnt allow multiple sends above limit", function(done) { + var amount = 500; + return lb.limitedDeposit({value: amount}) + .then(function() { + assert.equal(web3.eth.getBalance(lb.address), amount); + return lb.limitedDeposit({value: amount+1}) + }) + .catch(function(error) { + if (error.message.search('invalid JUMP') == -1) throw error; + }) + .then(done) + }); + }); From 7bc35f6f7889802b746cbd1fc8b1fe81272d39cc Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 18 Nov 2016 14:13:28 -0300 Subject: [PATCH 54/54] polish BasicToken --- contracts/token/BasicToken.sol | 3 +-- contracts/token/ERC20Basic.sol | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/token/BasicToken.sol b/contracts/token/BasicToken.sol index 6e6a747c2..704de675d 100644 --- a/contracts/token/BasicToken.sol +++ b/contracts/token/BasicToken.sol @@ -11,14 +11,13 @@ contract BasicToken is ERC20Basic, SafeMath { mapping(address => uint) balances; - function transfer(address _to, uint _value) returns (bool success) { + function transfer(address _to, uint _value) { if (balances[msg.sender] < _value) { throw; } balances[msg.sender] = safeSub(balances[msg.sender], _value); balances[_to] = safeAdd(balances[_to], _value); Transfer(msg.sender, _to, _value); - return true; } function balanceOf(address _owner) constant returns (uint balance) { diff --git a/contracts/token/ERC20Basic.sol b/contracts/token/ERC20Basic.sol index 8128991e4..4f7ca36d1 100644 --- a/contracts/token/ERC20Basic.sol +++ b/contracts/token/ERC20Basic.sol @@ -4,6 +4,6 @@ pragma solidity ^0.4.4; contract ERC20Basic { uint public totalSupply; function balanceOf(address who) constant returns (uint); - function transfer(address to, uint value) returns (bool ok); + function transfer(address to, uint value); event Transfer(address indexed from, address indexed to, uint value); }