Permalink
Browse files

bridge RGE draft (home & foreign contracts)

  • Loading branch information...
clbrge committed Aug 15, 2018
1 parent e69e731 commit c89258f1ac35936f2e164f0484f9f7cf8093a420
Showing with 892 additions and 3 deletions.
  1. +155 −0 contracts/BridgeRGEToken.sol
  2. +121 −0 contracts/RougeBridge.sol
  3. +8 −3 migrations/2_deploy_contracts.js
  4. +148 −0 test/BridgeRGEToken.js
  5. +202 −0 test/RougeBridge.js
  6. +258 −0 yarn.lock
@@ -0,0 +1,155 @@
/*

Bridged RGE contract to be used in FOREIGN CHAIN (not Ethereum Mainnet)

See RougeBridge for the contract on Mainnet responsible to lock home RGE for every Bridged RGE

*/

pragma solidity ^0.4.24;

import "./EIP20.sol";

contract BridgeRGEToken is EIP20 {

/* ERC20 */
string public name;
string public symbol;
uint8 public decimals = 6;

/* RGEToken - keeping most of the interface similar to RGE home */
address owner;
string public version = 'v1.0f';
uint256 public totalSupply = 1000000000 * 10**uint(decimals);
uint256 public reserveY1 = 0;
uint256 public reserveY2 = 0;

modifier onlyBy(address _address) {
require(msg.sender == _address);
_;
}

uint public network; /* the foreign RGE network ID */
address public homeAuthority; /* owner of the RougeBridge contract on mainnet */
address public bridge;

constructor(uint _network, address _bridge, address _homeAuthority, string _name, string _symbol)
EIP20 (totalSupply, _name, decimals, _symbol) public {
owner = msg.sender;
network = _network;
homeAuthority = _homeAuthority;
bridge = _bridge;
balances[address(0)] = totalSupply; /* RGE on address(0) means there are not on this foreign chain */
}

bool public bridgeIsOpened = true;

function toggleBridge(bool _flag) onlyBy(owner) public {
bridgeIsOpened = _flag;
}

modifier BridgeOpen() {
require(bridgeIsOpened);
_;
}

mapping (address => mapping (uint => bool)) public claimed;
mapping (address => mapping (uint => bool)) public surrendered;

/* the first hash can be read on the bridge main chain by the user */
/* the second hash is be sure that the RGE tokens are already locked + homeAuthority signature */

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,
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));
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);
claimed[msg.sender][_depositBlock] = true; // distribute the claim
balances[address(0)] -= _value;
balances[msg.sender] += _value;
emit RGEClaim(msg.sender, network, _depositBlock, _value);
emit Transfer(address(0), msg.sender, _value);
return true;
}

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

function surrender(uint256 _value, uint _depositBlock, uint8 vLock, bytes32 rLock, bytes32 sLock) public returns (bool success) {
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));
require(ecrecover(prefixed(_lockHash), vLock, rLock, sLock) == owner);
require(balances[msg.sender] >= _value);
surrendered[msg.sender][_depositBlock] = true; // withdraw tokens from circulation
balances[msg.sender] -= _value;
balances[address(0)] += _value;
emit Transfer(msg.sender, address(0), _value);
emit Surrender(msg.sender, network, _depositBlock, _value);
return true;
}

// XXX to put in lib

function getHexString(bytes32 value) internal pure returns (string) {
bytes memory result = new bytes(64);
string memory characterString = "0123456789abcdef";
bytes memory characters = bytes(characterString);
for (uint8 i = 0; i < 32; i++) {
result[i * 2] = characters[uint256((value[i] & 0xF0) >> 4)];
result[i * 2 + 1] = characters[uint256(value[i] & 0xF)];
}
return string(result);
}

function prefixed(bytes32 _hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n76Bridge fct: ", getHexString(_hash)));
}

// This function should be called after a surrender, to publish information necessary to unlock tokens on home chain

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

function repudiate(bytes32 _hash, address _account, uint _depositBlock, uint8 v, bytes32 r, bytes32 s)
onlyBy(owner) public {
require(msg.sender != _account);
require(surrendered[_account][_depositBlock]);
require(_hash == keccak256(abi.encodePacked('unlocking', _account, network, bridge, _depositBlock)));
require(ecrecover(prefixed(_hash), v, r, s) == msg.sender);
emit Repudiate(_account, network, _depositBlock, v, r, s);
}

/* standard RGE interface */

address public factory;

function setFactory(address _factory) onlyBy(owner) public {
factory = _factory;
}

function newCampaign(uint32 _issuance, uint256 _value) public {
transfer(factory,_value);
require(factory.call(bytes4(keccak256("createCampaign(address,uint32,uint256)")),msg.sender,_issuance,_value));
}

event Burn(address indexed burner, uint256 value);

function burn(uint256 _value) public returns (bool success) {
require(_value > 0);
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
totalSupply -= _value;
emit Transfer(msg.sender, address(0), _value);
emit Burn(msg.sender, _value);
return true;
}

}
@@ -0,0 +1,121 @@
/*

Bridge to send RGE in other chains (eg POA, ...)

*/

pragma solidity ^0.4.24;

import "./RGETokenInterface.sol";

contract RougeBridge {

string public version = 'v0.1';

address public owner;

modifier onlyBy(address _account) {
require(msg.sender == _account);
_;
}

RGETokenInterface public rge;

constructor(address _rge) public {
owner = msg.sender;
rge = RGETokenInterface(_rge);
}

mapping (uint => bool) public isOpen; /* is the bridge open for this network */
mapping (uint => address) public foreignAuthority; /* foreignAuthority network owner */

function adminBridge(uint _network, bool _flag, address _foreignAuthority) onlyBy(owner) public {
isOpen[_network] = _flag;
foreignAuthority[_network] = _foreignAuthority;
}

mapping (address => mapping (uint => mapping (uint => uint256))) public escrow;
mapping (address => mapping (uint => mapping (uint => bytes32))) public escrowSeal;

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

/* caller need to approve RGE transfer beforehand */
function deposit(uint256 _value, uint _network) public {
require(isOpen[_network]);
require(msg.sender != owner);
require(_value > 0);
require(escrow[msg.sender][_network][block.number] == 0); // only 1 deposit per block per user maximum (XXX use punitive assert ?)
assert(escrowSeal[msg.sender][_network][block.number] == bytes32(0)); // there can't be seal, but let's be sure
require(rge.transferFrom(msg.sender, this, _value)); // transfer tokens to this home bridge contract
emit BridgeDeposit(msg.sender, _network, block.number, _value);
escrow[msg.sender][_network][block.number] = _value;
}

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

function withdraw(uint _network, uint _depositBlock) public {
require(escrowSeal[msg.sender][_network][_depositBlock] == bytes32(0)); // can't withdraw locked tokens (ie no seal)
uint256 _value = escrow[msg.sender][_network][_depositBlock];
require(_value > 0); // tokens should be in escrow
escrow[msg.sender][_network][_depositBlock] = 0;
require(rge.transfer(msg.sender, _value)); // transfer back tokens to sender
emit BridgeWithdraw(msg.sender, _network, _depositBlock, _value);
}

function getHexString(bytes32 value) internal pure returns (string) {
bytes memory result = new bytes(64);
string memory characterString = "0123456789abcdef";
bytes memory characters = bytes(characterString);
for (uint8 i = 0; i < 32; i++) {
result[i * 2] = characters[uint256((value[i] & 0xF0) >> 4)];
result[i * 2 + 1] = characters[uint256(value[i] & 0xF)];
}
return string(result);
}

function prefixed(bytes32 _hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n76Bridge fct: ", getHexString(_hash)));
}


// Calculating the seal and locking the deposit (foreign authority needs to undersign this)

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

function lockEscrow(bytes32 _hash, address _account, uint _network, uint _depositBlock, uint8 v, bytes32 r, bytes32 s)
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));
escrowSeal[_account][_network][_depositBlock] = sealHash;
emit EscrowLocked(_account, _network, _depositBlock, v, r, s, sealHash);
}

// The seal needs to be signed again by RGE bridge owner to be used as authorization to claim token in foreign chain
// 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);

function createAuth(bytes32 _authHash, 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);
}

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)));
require(ecrecover(prefixed(_hash), v, r, s) == foreignAuthority[_network]);
escrowSeal[_account][_network][_depositBlock] = bytes32(0); // remove the seal
emit EscrowUnlocked(_account, _network, _depositBlock);
}

}
@@ -2,11 +2,16 @@
const TestRGEToken = artifacts.require("./TestRGEToken.sol");
const RougeFactory = artifacts.require("./RougeFactory.sol");

const RougeBridge = artifacts.require("./RougeBridge.sol");
const BridgeRGEToken = artifacts.require("./BridgeRGEToken.sol");

module.exports = async function(deployer) {

await Promise.all([
deployer.deploy(TestRGEToken),
deployer.deploy(RougeFactory)
deployer.deploy(RougeFactory),
deployer.deploy(RougeBridge, '0x345ca3e014aaf5dca488057592ee47305d9b3e10'),
deployer.deploy(BridgeRGEToken, 3, '0x0', '0x0', 'Foreign RGE', 'f_RGE')
]);

instances = await Promise.all([
@@ -19,7 +24,7 @@ module.exports = async function(deployer) {

results = await Promise.all([
rge.setFactory(factory.address),
factory.setParams(rge.address,100000)
factory.setParams(rge.address, 100000)
]);

};
Oops, something went wrong.

0 comments on commit c89258f

Please sign in to comment.