Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
remove salt in keccak256 hashes + remove authHash (fixes #3, fixes #4)
Browse files Browse the repository at this point in the history
  • Loading branch information
clbrge committed Aug 23, 2018
1 parent c89258f commit df0ea5c
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 67 deletions.
17 changes: 9 additions & 8 deletions contracts/BridgeRGEToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract BridgeRGEToken is EIP20 {

/* RGEToken - keeping most of the interface similar to RGE home */
address owner;
string public version = 'v1.0f';
string public version = 'v1.0-0.2'; /* composition rge version + bridge version */
uint256 public totalSupply = 1000000000 * 10**uint(decimals);
uint256 public reserveY1 = 0;
uint256 public reserveY2 = 0;
Expand All @@ -39,6 +39,8 @@ contract BridgeRGEToken is EIP20 {
network = _network;
homeAuthority = _homeAuthority;
bridge = _bridge;
name = _name;
symbol = _symbol;
balances[address(0)] = totalSupply; /* RGE on address(0) means there are not on this foreign chain */
}

Expand All @@ -61,17 +63,16 @@ contract BridgeRGEToken is EIP20 {

event RGEClaim(address indexed account, uint indexed _network, uint indexed depositBlock, uint256 value);

function claim(bytes32 _sealHash, bytes32 _authHash, uint256 _value, uint _depositBlock, uint _lockBlock,
function claim(bytes32 _sealHash, uint256 _value, uint _depositBlock, uint _lockBlock,
uint8 vLock, bytes32 rLock, bytes32 sLock, uint8 vAuth, bytes32 rAuth, bytes32 sAuth)
BridgeOpen public returns (bool success) {
require(msg.sender != homeAuthority);
require(balances[address(0)] >= _value);
require(!claimed[msg.sender][_depositBlock]); // check if the deposit has not been already claimed
bytes32 _lockHash = keccak256(abi.encodePacked('locking', msg.sender, _value, network, bridge, _depositBlock));
bytes32 _lockHash = keccak256(abi.encodePacked(msg.sender, _value, network, bridge, _depositBlock));
require(ecrecover(prefixed(_lockHash), vLock, rLock, sLock) == owner);
require(_sealHash == keccak256(abi.encodePacked('sealing', _lockHash, vLock, rLock, sLock, _lockBlock)));
require(_authHash == keccak256(abi.encodePacked('authorization', _sealHash)));
require(ecrecover(prefixed(_authHash), vAuth, rAuth, sAuth) == homeAuthority);
require(_sealHash == keccak256(abi.encodePacked(_lockHash, vLock, rLock, sLock, _lockBlock)));
require(ecrecover(prefixed(_sealHash), vAuth, rAuth, sAuth) == homeAuthority);
claimed[msg.sender][_depositBlock] = true; // distribute the claim
balances[address(0)] -= _value;
balances[msg.sender] += _value;
Expand All @@ -86,7 +87,7 @@ contract BridgeRGEToken is EIP20 {
require(msg.sender != homeAuthority);
require(claimed[msg.sender][_depositBlock]);
require(!surrendered[msg.sender][_depositBlock]);
bytes32 _lockHash = keccak256(abi.encodePacked('locking', msg.sender, _value, network, bridge, _depositBlock));
bytes32 _lockHash = keccak256(abi.encodePacked(msg.sender, _value, network, bridge, _depositBlock));
require(ecrecover(prefixed(_lockHash), vLock, rLock, sLock) == owner);
require(balances[msg.sender] >= _value);
surrendered[msg.sender][_depositBlock] = true; // withdraw tokens from circulation
Expand Down Expand Up @@ -122,7 +123,7 @@ contract BridgeRGEToken is EIP20 {
onlyBy(owner) public {
require(msg.sender != _account);
require(surrendered[_account][_depositBlock]);
require(_hash == keccak256(abi.encodePacked('unlocking', _account, network, bridge, _depositBlock)));
require(_hash == keccak256(abi.encodePacked(_account, network, bridge, _depositBlock)));
require(ecrecover(prefixed(_hash), v, r, s) == msg.sender);
emit Repudiate(_account, network, _depositBlock, v, r, s);
}
Expand Down
23 changes: 11 additions & 12 deletions contracts/RougeBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "./RGETokenInterface.sol";

contract RougeBridge {

string public version = 'v0.1';
string public version = 'v0.2';

address public owner;

Expand Down Expand Up @@ -86,9 +86,9 @@ contract RougeBridge {
onlyBy(owner) public {
require(escrow[_account][_network][_depositBlock] > 0); // do not work for nothing
require(_hash == keccak256(abi.encodePacked(
'locking', _account, escrow[_account][_network][_depositBlock], _network, this, _depositBlock)));
require(ecrecover(prefixed(_hash), v, r, s) == foreignAuthority[_network]); // confirms that foreign authority has undersigned the tokens Locking
bytes32 sealHash = keccak256(abi.encodePacked('sealing', _hash, v, r, s, block.number));
_account, escrow[_account][_network][_depositBlock], _network, this, _depositBlock)));
require(ecrecover(prefixed(_hash), v, r, s) == foreignAuthority[_network]); // confirms that foreign authority has undersigned the tokens Locking
bytes32 sealHash = keccak256(abi.encodePacked(_hash, v, r, s, block.number));
escrowSeal[_account][_network][_depositBlock] = sealHash;
emit EscrowLocked(_account, _network, _depositBlock, v, r, s, sealHash);
}
Expand All @@ -97,24 +97,23 @@ contract RougeBridge {
// The user can claim the bridged tokens as soon as the authorization is visible on the main blockchain (via LOGS)
// (it's ok if the claiming tx comes before the authorization tx)

event BridgeAuth(address indexed account, uint indexed _network, uint indexed depositBlock, uint8 v, bytes32 r, bytes32 s, bytes32 authHash);
event BridgeAuth(address indexed account, uint indexed _network, uint indexed depositBlock, uint8 v, bytes32 r, bytes32 s);

function createAuth(bytes32 _authHash, address _account, uint _network, uint _depositBlock, uint8 v, bytes32 r, bytes32 s)
function createAuth(address _account, uint _network, uint _depositBlock, uint8 v, bytes32 r, bytes32 s)
onlyBy(owner) public {
require(_authHash == keccak256(abi.encodePacked('authorization', escrowSeal[_account][_network][_depositBlock])));
require(ecrecover(prefixed(_authHash), v, r, s) == owner);
emit BridgeAuth(_account, _network, _depositBlock, v, r, s, _authHash);
require(ecrecover(prefixed(escrowSeal[_account][_network][_depositBlock]), v, r, s) == owner);
emit BridgeAuth(_account, _network, _depositBlock, v, r, s);
}

event EscrowUnlocked(address indexed account, uint indexed _network, uint indexed depositBlock);

function unlockEscrow(bytes32 _hash, address _account, uint _network, uint _depositBlock, uint8 v, bytes32 r, bytes32 s)
onlyBy(owner) public {
require(escrow[_account][_network][_depositBlock] > 0);
escrowSeal[_account][_network][_depositBlock] != bytes32(0); // seal shoudl exists
require(_hash == keccak256(abi.encodePacked('unlocking', _account, _network, this, _depositBlock)));
escrowSeal[_account][_network][_depositBlock] != bytes32(0); // seal should exists
require(_hash == keccak256(abi.encodePacked(_account, _network, this, _depositBlock)));
require(ecrecover(prefixed(_hash), v, r, s) == foreignAuthority[_network]);
escrowSeal[_account][_network][_depositBlock] = bytes32(0); // remove the seal
escrowSeal[_account][_network][_depositBlock] = bytes32(0); // remove the seal
emit EscrowUnlocked(_account, _network, _depositBlock);
}

Expand Down
40 changes: 16 additions & 24 deletions test/BridgeRGEToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,24 @@ const RGEToken = artifacts.require("./TestRGEToken.sol");
const RougeBridge = artifacts.require("./RougeBridge.sol");
const BridgeRGEToken = artifacts.require("./BridgeRGEToken.sol");

const createLockHash = function(msg, user, deposit, foreign_network, bridge, depositBlock) {
const createLockHash = function(user, deposit, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "address", "uint", "uint", "address", "uint" ],
[ msg, new BN(user, 16), deposit, foreign_network, new BN(bridge, 16), depositBlock ]
[ "address", "uint", "uint", "address", "uint" ],
[ new BN(user, 16), deposit, foreign_network, new BN(bridge, 16), depositBlock ]
).toString('hex')
}

const createSealHash = function(msg, hash, v, r, s, lockBlock) {
const createSealHash = function(hash, v, r, s, lockBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "bytes32", "uint8", "bytes32", "bytes32", "uint" ],
[ msg, hash, v, r, s, lockBlock ]
[ "bytes32", "uint8", "bytes32", "bytes32", "uint" ],
[ hash, v, r, s, lockBlock ]
).toString('hex')
}

const createAuthHash = function(msg, hash) {
const createUnlockHash = function(user, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "bytes32" ], [ msg, hash ]
).toString('hex')
}
const createUnlockHash = function(msg, user, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "address", "uint", "address", "uint" ],
[ msg, new BN(user, 16), foreign_network, new BN(bridge, 16), depositBlock ]
[ "address", "uint", "address", "uint" ],
[ new BN(user, 16), foreign_network, new BN(bridge, 16), depositBlock ]
).toString('hex')
}

Expand All @@ -46,7 +41,6 @@ contract('BridgeRGEToken', function(accounts) {

it("Claim tokens locked in home bridge", async function() {


// user need to use the SAME address on both chain.
const user = accounts[1];

Expand All @@ -69,21 +63,20 @@ contract('BridgeRGEToken', function(accounts) {
const tx = await bridge.deposit(deposit, foreign_network, {from: user, gas: 67431 +20000, gasPrice: web3.toWei(1, "gwei")})
const depositBlock = tx.receipt.blockNumber;

const lockHash = createLockHash('locking', user, deposit, foreign_network, bridge.address, depositBlock)
const lockHash = createLockHash(user, deposit, foreign_network, bridge.address, depositBlock)
const signLock = getAccountSignature(lockHash, foreign_authority)
const lock_tx = await bridge.lockEscrow(lockHash, user, foreign_network, depositBlock, signLock.v, signLock.r, signLock.s);
const lockBlock = lock_tx.receipt.blockNumber;
const expected_seal = createSealHash('sealing', lockHash, signLock.v, signLock.r, signLock.s, lockBlock);
const expected_seal = createSealHash(lockHash, signLock.v, signLock.r, signLock.s, lockBlock);
const seal = await bridge.escrowSeal.call(user, foreign_network, depositBlock);
assert.equal(seal, expected_seal, "we got a correct seal");

// owner is create the Auth Hash (to be used on foreign chain)

const authHash = createAuthHash('authorization', seal);
const signAuth = getAccountSignature(authHash, accounts[0]);
const auth_tx = await bridge.createAuth(authHash, user, foreign_network, depositBlock, signAuth.v, signAuth.r, signAuth.s);
const signAuth = getAccountSignature(seal, accounts[0]);
const auth_tx = await bridge.createAuth(user, foreign_network, depositBlock, signAuth.v, signAuth.r, signAuth.s);

const event_BridgeAuth_sign = web3.sha3('BridgeAuth(address,uint256,uint256,uint8,bytes32,bytes32,bytes32)')
const event_BridgeAuth_sign = web3.sha3('BridgeAuth(address,uint256,uint256,uint8,bytes32,bytes32)')
auth_tx.receipt.logs.forEach( function(e) {
if (e.topics[0] === event_BridgeAuth_sign) {
assert.equal(e.topics[1].slice(26, 66), user.substr(2), "user coherent in log");
Expand All @@ -92,7 +85,6 @@ contract('BridgeRGEToken', function(accounts) {
assert.equal(web3.toDecimal(e.data.slice(0, 66)), signAuth.v, "sign v ok");
assert.equal('0x' + e.data.slice(66, 130), signAuth.r, "sign r ok");
assert.equal('0x' + e.data.slice(130, 194), signAuth.s, "sign s ok");
assert.equal('0x' + e.data.slice(194, 260), authHash, "AuthHash ok");
}
})

Expand All @@ -110,7 +102,7 @@ contract('BridgeRGEToken', function(accounts) {

// all these arguments can be read from main chain ledger : LOG EscrowLocked (+ its block number) & LOG BridgeAuth

const claim_tx = await f_rge.claim(seal, authHash, deposit, depositBlock, lockBlock, signLock.v, signLock.r, signLock.s, signAuth.v, signAuth.r, signAuth.s, {from: user})
const claim_tx = await f_rge.claim(seal, deposit, depositBlock, lockBlock, signLock.v, signLock.r, signLock.s, signAuth.v, signAuth.r, signAuth.s, {from: user})

const user_balance_after = await f_rge.balanceOf.call(user);
assert.equal(user_balance_after.toNumber(), deposit, "user get his rge on foreign chain");
Expand All @@ -121,7 +113,7 @@ contract('BridgeRGEToken', function(accounts) {
assert.equal(user_balance_post.toNumber(), 0, "user lost his f_rge tokens after surrender");


const hash2 = createUnlockHash('unlocking', user, foreign_network, bridge.address, depositBlock)
const hash2 = createUnlockHash(user, foreign_network, bridge.address, depositBlock)
const sign2 = getAccountSignature(hash2, foreign_authority)
const repudiate_tx = await f_rge.repudiate(hash2, user, depositBlock, sign2.v, sign2.r, sign2.s, {from: foreign_authority});

Expand Down
39 changes: 16 additions & 23 deletions test/RougeBridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,24 @@ const ethUtil = require('ethereumjs-util')
const RGEToken = artifacts.require("./TestRGEToken.sol");
const RougeBridge = artifacts.require("./RougeBridge.sol");

const createLockHash = function(msg, user, deposit, foreign_network, bridge, depositBlock) {
const createLockHash = function(user, deposit, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "address", "uint", "uint", "address", "uint" ],
[ msg, new BN(user, 16), deposit, foreign_network, new BN(bridge, 16), depositBlock ]
[ "address", "uint", "uint", "address", "uint" ],
[ new BN(user, 16), deposit, foreign_network, new BN(bridge, 16), depositBlock ]
).toString('hex')
}

const createSealHash = function(msg, hash, v, r, s, lockBlock) {
const createSealHash = function(hash, v, r, s, lockBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "bytes32", "uint8", "bytes32", "bytes32", "uint" ],
[ msg, hash, v, r, s, lockBlock ]
[ "bytes32", "uint8", "bytes32", "bytes32", "uint" ],
[ hash, v, r, s, lockBlock ]
).toString('hex')
}

const createAuthHash = function(msg, hash) {
const createUnlockHash = function(user, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "bytes32" ], [ msg, hash ]
).toString('hex')
}
const createUnlockHash = function(msg, user, foreign_network, bridge, depositBlock) {
return '0x' + abi.soliditySHA3(
[ "string", "address", "uint", "address", "uint" ],
[ msg, new BN(user, 16), foreign_network, new BN(bridge, 16), depositBlock ]
[ "address", "uint", "address", "uint" ],
[ new BN(user, 16), foreign_network, new BN(bridge, 16), depositBlock ]
).toString('hex')
}

Expand Down Expand Up @@ -147,22 +142,21 @@ contract('RougeBridge', function(accounts) {

// foreign chain + owner locking fct

const hash1 = createLockHash('locking', user, deposit, foreign_network, bridge.address, depositBlock)
const hash1 = createLockHash(user, deposit, foreign_network, bridge.address, depositBlock)
const sign1 = getSignature(hash1, foreign_authority_pkey)
const lock_tx = await bridge.lockEscrow(hash1, user, foreign_network, depositBlock, sign1.v, sign1.r, sign1.s);
const lockBlock = lock_tx.receipt.blockNumber;

const expected_seal = createSealHash('sealing', hash1, sign1.v, sign1.r, sign1.s, lockBlock);
const expected_seal = createSealHash(hash1, sign1.v, sign1.r, sign1.s, lockBlock);
const seal = await bridge.escrowSeal.call(user, foreign_network, depositBlock);
assert.equal(seal, expected_seal, "check seal in that locked tokens");

// owner is create the Auth Hash (to be used on foreign chain)
// owner is signing the sealHash for green light (to be used on foreign chain)

const authHash = createAuthHash('authorization', seal);
const signAuth = getAccountSignature(authHash, accounts[0]);
const auth_tx = await bridge.createAuth(authHash, user, foreign_network, depositBlock, signAuth.v, signAuth.r, signAuth.s);
const signAuth = getAccountSignature(seal, accounts[0]);
const auth_tx = await bridge.createAuth(user, foreign_network, depositBlock, signAuth.v, signAuth.r, signAuth.s);

const event_BridgeAuth_sign = web3.sha3('BridgeAuth(address,uint256,uint256,uint8,bytes32,bytes32,bytes32)')
const event_BridgeAuth_sign = web3.sha3('BridgeAuth(address,uint256,uint256,uint8,bytes32,bytes32)')
auth_tx.receipt.logs.forEach( function(e) {
if (e.topics[0] === event_BridgeAuth_sign) {
assert.equal(e.topics[1].slice(26, 66), user.substr(2), "user coherent in log");
Expand All @@ -171,14 +165,13 @@ contract('RougeBridge', function(accounts) {
assert.equal(web3.toDecimal(e.data.slice(0, 66)), signAuth.v, "sign v ok");
assert.equal('0x' + e.data.slice(66, 130), signAuth.r, "sign r ok");
assert.equal('0x' + e.data.slice(130, 194), signAuth.s, "sign s ok");
assert.equal('0x' + e.data.slice(194, 260), authHash, "AuthHash ok");
}
})

// this should fail as expected
// await bridge.withdraw(foreign_network, depositBlock, {from: user})

const hash2 = createUnlockHash('unlocking', user, foreign_network, bridge.address, depositBlock)
const hash2 = createUnlockHash(user, foreign_network, bridge.address, depositBlock)
const sign2 = getSignature(hash2, foreign_authority_pkey)
const unlock_tx = await bridge.unlockEscrow(hash2, user, foreign_network, depositBlock, sign2.v, sign2.r, sign2.s);

Expand Down

0 comments on commit df0ea5c

Please sign in to comment.