From a98c5b8865ada33885212824d076711590ff5947 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 17:27:46 +0100 Subject: [PATCH 01/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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 b5469310a1baaf6f7e0ed372d2f0527afd9e8c33 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Sat, 22 Oct 2016 17:27:46 +0100 Subject: [PATCH 08/27] 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 09/27] 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 10/27] 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 11/27] 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 12/27] 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 13/27] 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 14/27] 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 15/27] 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 16/27] 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 17/27] 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 18/27] 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 19/27] 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 20/27] 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 21/27] 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 22/27] 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 23/27] 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 24/27] 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 b6a5830047fc61f2f04df7209c439f7e57741150 Mon Sep 17 00:00:00 2001 From: Makoto Inoue Date: Fri, 28 Oct 2016 11:22:41 +0100 Subject: [PATCH 25/27] 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 26/27] 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 27/27] 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); }; };