Merge pull request #32 from paritytech/truffle

more solidity tests
This commit is contained in:
Marek Kotewicz 2017-09-04 16:11:22 +02:00 committed by GitHub
commit ef5336404a
4 changed files with 364 additions and 44 deletions

View File

@ -9,6 +9,11 @@ trim_trailing_whitespace=true
max_line_length=120
insert_final_newline=true
[*.sol]
indent_style=space
indent_size=4
tab_size=4
[*.js]
indent_style=space
indent_size=2

View File

@ -18,6 +18,51 @@ library Authorities {
}
}
/// Library used only to test Signer library via rpc calls
library SignerTest {
function signer (bytes signature, bytes message) constant returns (address) {
return Signer.signer(signature, message);
}
}
library Utils {
function toString (uint256 v) internal returns (string str) {
// it is used only for small numbers
bytes memory reversed = new bytes(8);
uint i = 0;
while (v != 0) {
uint remainder = v % 10;
v = v / 10;
reversed[i++] = byte(48 + remainder);
}
bytes memory s = new bytes(i);
for (uint j = 0; j < i; j++) {
s[j] = reversed[i - j - 1];
}
str = string(s);
}
}
library Signer {
function signer (bytes signature, bytes message) internal returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hash(message), uint8(v), r, s);
}
function hash (bytes message) internal returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
return sha3(prefix, Utils.toString(message.length), message);
}
}
contract EthereumBridge {
using Authorities for address[];
@ -40,8 +85,7 @@ contract EthereumBridge {
/// Multisig authority validation
modifier allAuthorities (uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
var hash = sha3(prefix, message.length, message);
var hash = Signer.hash(message);
var used = new address[](requiredSignatures);
require(requiredSignatures <= v.length);
@ -57,7 +101,7 @@ contract EthereumBridge {
/// Constructor.
function EthereumBridge (uint n, address[] a) {
require(n != 0);
require(n != 0);
require(n <= a.length);
requiredSignatures = n;
authorities = a;
@ -79,9 +123,9 @@ contract EthereumBridge {
uint value;
bytes32 hash;
assembly {
recipient := mload(message)
value := mload(add(message, 0x32))
hash := mload(add(message, 0x64))
recipient := mload(add(message, 0x20))
value := mload(add(message, 0x40))
hash := mload(add(message, 0x60))
}
// Duplicated withdraw
@ -92,35 +136,6 @@ contract EthereumBridge {
recipient.transfer(value);
Withdraw(recipient, value);
}
/// Used to elect new authorities.
///
// message contains:
// new requiredSignatures (uint)
// new number of authorities (uint)
// new authorities (bytes20)
function reelect (uint8[] v, bytes32[] r, bytes32[] s, bytes message) allAuthorities(v, r, s, message) {
uint newRequiredSignatures;
uint newAuthoritiesNumber;
address addressPtr;
assembly {
newRequiredSignatures := mload(message)
newAuthoritiesNumber := mload(add(message, 0x32))
}
require(newRequiredSignatures <= newAuthoritiesNumber);
authorities.truncate(newAuthoritiesNumber);
for (uint i = 0; i < newAuthoritiesNumber; i++) {
assembly {
let offset := add(0x64, mul(0x32, i))
addressPtr := mload(add(message, offset))
}
authorities[i] = addressPtr;
}
}
}
contract KovanBridge {
@ -166,7 +181,7 @@ contract KovanBridge {
/// Constructor.
function KovanBridge(uint n, address[] a) {
require(n != 0);
require(n != 0);
require(n <= a.length);
requiredSignatures = n;
authorities = a;
@ -184,8 +199,8 @@ contract KovanBridge {
/// deposit value (uint)
/// mainnet transaction hash (bytes32) // to avoid transaction duplication
function deposit (address recipient, uint value, bytes32 transactionHash) onlyAuthority() {
// Protection from misbehaing authority
var hash = sha3(recipient, value, transactionHash);
// Protection from misbehaing authority
var hash = sha3(recipient, value, transactionHash);
// Duplicated deposits
require(!deposits[hash].contains(msg.sender));
@ -220,10 +235,11 @@ contract KovanBridge {
/// withdrawal value (uint)
/// kovan transaction hash (bytes32) // to avoid transaction duplication
function submitSignature (bytes signature, bytes message) onlyAuthority() {
// Valid signature must have 65 bytes
require(signature.length == 65);
// Valid withdraw message must have 84 bytes
require(message.length == 84);
// Validate submited signatures
require(Signer.signer(signature, message) == msg.sender);
// Valid withdraw message must have 84 bytes
require(message.length == 84);
var hash = sha3(message);
// Duplicated signatures

27
truffle/test/signer.js Normal file
View File

@ -0,0 +1,27 @@
var Signer = artifacts.require("SignerTest");
contract("Signer", function() {
it("should validate signature", function() {
var signature = "0xb585c41f3cceb2ff9b5c033f2edbefe93415bde365489c989bad8cef3b18e38148a13e100608a29735d709fe708926d37adcecfffb32b1d598727028a16df5db1b";
var message = "0xdeadbeaf";
var account = "0x006e27b6a72e1f34c626762f3c4761547aff1421";
return Signer.new().then(function(instance) {
return instance.signer.call(signature, message)
}).then(function(result) {
assert.equal(account, result);
})
})
it("should validate signature for long message", function() {
var signature = "0x3c9158597e22fa43fcc6636399c560441808e1d8496de0108e401a2ad71022b15d1191cf3c96e06759601c8e00ce7f03f350c12b19d0a8ba3ab3c07a71063f2b1c";
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var account = "0x006e27b6a72e1f34c626762f3c4761547aff1421";
return Signer.new().then(function(instance) {
return instance.signer.call(signature, message)
}).then(function(result) {
assert.equal(account, result);
})
})
})

View File

@ -85,7 +85,7 @@ contract('KovanBridge', function(accounts) {
})
})
it("should ignore misbehaving authority", function() {
it("should ignore misbehaving authority when confirming deposit", function() {
var meta;
var requiredSignatures = 2;
var authorities = [accounts[0], accounts[1], accounts[2]];
@ -107,11 +107,283 @@ contract('KovanBridge', function(accounts) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Deposit", result.logs[0].event, "Event name should be Deposit");
assert.equal(user_account, result.logs[0].args.recipient, "Event recipient should be transaction sender");
assert.equal(value, result.logs[0].args.value, "Event value should match deposited ether");
assert.equal(value, result.logs[0].args.value, "Event value should match transaction value");
return meta.balances.call(user_account);
}).then(function(result) {
assert.equal(value, result, "Contract balance should change");
})
})
it("should allow user to transfer value internally", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var user_account = accounts[2];
var user_account2 = accounts[3];
var value = web3.toWei(3, "ether");
var value2 = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return meta.deposit(user_account, value, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transfer(user_account2, value2, false, { from: user_account });
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Transfer", result.logs[0].event, "Event name should be Transfer");
assert.equal(user_account, result.logs[0].args.from, "Event from should be transaction sender");
assert.equal(user_account2, result.logs[0].args.to, "Event from should be transaction recipient");
assert.equal(value2, result.logs[0].args.value, "Event value should match transaction value");
return Promise.all([
meta.balances.call(user_account),
meta.balances.call(user_account2)
])
}).then(function(result) {
assert.equal(web3.toWei(2, "ether"), result[0]);
assert.equal(web3.toWei(1, "ether"), result[1]);
})
})
it("should not allow user to transfer value", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var user_account = accounts[2];
var user_account2 = accounts[3];
var value = web3.toWei(3, "ether");
var value2 = web3.toWei(4, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return meta.deposit(user_account, value, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transfer(user_account2, value2, false, { from: user_account });
}).then(function(result) {
assert(false, "Transfer should fail");
}, function(err) {
})
})
it("should allow user to trigger withdraw", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var user_account = accounts[2];
var user_account2 = accounts[3];
var value = web3.toWei(3, "ether");
var value2 = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return meta.deposit(user_account, value, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transfer(user_account2, value2, true, { from: user_account });
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Withdraw", result.logs[0].event, "Event name should be Withdraw");
assert.equal(user_account2, result.logs[0].args.recipient, "Event recipient should be equal to transaction recipient");
assert.equal(value2, result.logs[0].args.value, "Event value should match transaction value");
return Promise.all([
meta.balances.call(user_account),
meta.balances.call(user_account2)
])
}).then(function(result) {
assert.equal(web3.toWei(2, "ether"), result[0]);
assert.equal(web3.toWei(0, "ether"), result[1]);
})
})
function sign(address, data) {
return new Promise(function(resolve, reject) {
web3.eth.sign(address, data, function(err, result) {
if (err !== null) {
return reject(err);
} else {
return resolve(normalizeSignature(result));
//return resolve(result);
}
})
})
}
// geth && testrpc has different output of eth_sign than parity
// https://github.com/ethereumjs/testrpc/issues/243#issuecomment-326750236
function normalizeSignature(signature) {
// strip 0x
signature = signature.substr(2);
// increase v by 27...
return "0x" + signature.substr(0, 128) + (parseInt(signature.substr(128), 16) + 27).toString(16);
}
it("should successfully submit signature and trigger CollectedSignatures event", function() {
var meta;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
signature = result;
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures");
assert.equal(authorities[0], result.logs[0].args.authority, "Event authority should be equal to transaction sender");
return Promise.all([
meta.signature.call(result.logs[0].args.messageHash, 0),
meta.message(result.logs[0].args.messageHash),
])
}).then(function(result) {
assert.equal(signature, result[0]);
assert.equal(message, result[1]);
})
})
it("should successfully submit signature but not trigger CollectedSignatures event", function() {
var meta;
var requiredSignatures = 2;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
assert.equal(0, result.logs.length, "No events should be created");
})
})
it("should be able to collect signatures for multiple events in parallel", function() {
var meta;
var signatures_for_message = [];
var signatures_for_message2 = [];
var requiredSignatures = 2;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var message2 = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return Promise.all([
sign(authorities[0], message),
sign(authorities[1], message),
sign(authorities[0], message2),
sign(authorities[1], message2),
]);
}).then(function(result) {
signatures_for_message.push(result[0]);
signatures_for_message.push(result[1]);
signatures_for_message2.push(result[2]);
signatures_for_message2.push(result[3]);
return meta.submitSignature(signatures_for_message[0], message, { from: authorities[0] });
}).then(function(result) {
assert.equal(0, result.logs.length, "No events should be created");
return meta.submitSignature(signatures_for_message2[1], message2, { from: authorities[1] });
}).then(function(result) {
assert.equal(0, result.logs.length, "No events should be created");
return meta.submitSignature(signatures_for_message2[0], message2, { from: authorities[0] });
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures");
assert.equal(authorities[0], result.logs[0].args.authority, "Event authority should be equal to transaction sender");
return Promise.all([
meta.signature.call(result.logs[0].args.messageHash, 0),
meta.signature.call(result.logs[0].args.messageHash, 1),
meta.message(result.logs[0].args.messageHash),
])
}).then(function(result) {
assert.equal(signatures_for_message2[1], result[0]);
assert.equal(signatures_for_message2[0], result[1]);
assert.equal(message2, result[2]);
return meta.submitSignature(signatures_for_message[1], message, { from: authorities[1] });
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures");
assert.equal(authorities[1], result.logs[0].args.authority, "Event authority should be equal to transaction sender");
return Promise.all([
meta.signature.call(result.logs[0].args.messageHash, 0),
meta.signature.call(result.logs[0].args.messageHash, 1),
meta.message(result.logs[0].args.messageHash),
])
}).then(function(result) {
assert.equal(signatures_for_message[0], result[0]);
assert.equal(signatures_for_message[1], result[1]);
assert.equal(message, result[2]);
})
})
it("should not be possible to submit to short message", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var message = "0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
})
})
it("should not be possible to submit different message then the signed one", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var message2 = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message2, { from: authorities[0] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
})
})
it("should not be possible to submit signature signed by different authority", function() {
var meta;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[1] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
})
})
it("should not be possible to submit signature twice", function() {
var meta;
var requiredSignatures = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return KovanBridge.new(requiredSignatures, authorities).then(function(instance) {
meta = instance;
return sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
})
})
})