Skip to content

Commit

Permalink
add swap registry to migration script (#129)
Browse files Browse the repository at this point in the history
* tokenAndHandler

* some more params

* added registry conract

* fix default exit stake

* fix token tests

* add period submission event in operator

* parentBlockInterval param

* allow to govern minters of token

* sym

* remove a bunch of unneeded storage from bridge

* Update test/minGov.js

Co-Authored-By: johannbarbie <johannbarbie@users.noreply.github.com>
  • Loading branch information
johannbarbie authored Jan 29, 2019
1 parent 9eb350d commit 8f13f90
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 60 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ Contract may be customized via ENV variables:
| ENV variable | Description | Default value |
| ------- | ----------- | ------------- |
| PROPOSAL_TIME | Governance proposal cool-off time (in seconds) | `1209600` (14 days) |
|EXIT_STAKE| Exit stake (in Wei) |0|
|EXIT_DURATION| Exit duration (in seconds) |`604800` (7 days)|
| EXIT_STAKE | Exit stake (in Wei) |100000000000000000|
| EXIT_DURATION | Exit duration (in seconds) |`604800` (7 days)|
| DEPLOYED_TOKEN | Token to be configured in vault | |
| EPOCH_LENGTH | Number of initial slots for validators | 2 |
| PARENT_BLOCK_INTERVAL | Number of Ethereum blocks between Plasma periods | 2 |
| TAX_RATE | Initial tax rate (0 - 1000 equals 0% - 100%) | 50 |
| POA_REWARD | Reward rate before supply of 7 million reached | 778000000000000000000 |


E.g. `PROPOSAL_TIME=600 EXIT_DURATION=180 yarn deploy` deploys plasma contract with 3 minutes exit duration governed by MinGov with 10 minutes proposal time.

Expand Down
19 changes: 5 additions & 14 deletions contracts/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@ contract Bridge is Adminable {
event NewOperator(address operator);

struct Period {
bytes32 parent; // the id of the parent node
uint32 height; // the height of last block in period
uint32 parentIndex; // the position of this node in the Parent's children list
uint32 timestamp;
bytes32[] children; // unordered list of children below this node
}

bytes32 constant GENESIS = 0x4920616d207665727920616e6772792c20627574206974207761732066756e21;
Expand All @@ -43,14 +40,11 @@ contract Bridge is Adminable {
function initialize(uint256 _parentBlockInterval) public initializer {
// init genesis preiod
Period memory genesisPeriod = Period({
parent: GENESIS,
height: 1,
timestamp: uint32(block.timestamp),
parentIndex: 0,
children: new bytes32[](0)
timestamp: uint32(block.timestamp)
});
tipHash = GENESIS;
periods[tipHash] = genesisPeriod;
periods[GENESIS] = genesisPeriod;
genesisBlockNumber = block.number;
parentBlockInterval = _parentBlockInterval;
operator = msg.sender;
Expand All @@ -74,8 +68,8 @@ contract Bridge is Adminable {
bytes32 _root)
public onlyOperator returns (uint256 newHeight) {

require(periods[_prevHash].parent > 0, "Parent node should exist");
require(periods[_root].height == 0, "Trying to submit the same root twice");
require(periods[_prevHash].timestamp > 0, "Parent node should exist");
require(periods[_root].timestamp == 0, "Trying to submit the same root twice");

// calculate height
newHeight = periods[_prevHash].height + 1;
Expand All @@ -92,11 +86,8 @@ contract Bridge is Adminable {
}
// store the period
Period memory newPeriod = Period({
parent: _prevHash,
height: uint32(newHeight),
timestamp: uint32(block.timestamp),
parentIndex: uint32(periods[_prevHash].children.push(_root) - 1),
children: new bytes32[](0)
timestamp: uint32(block.timestamp)
});
periods[_root] = newPeriod;
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/DepositHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contract DepositHandler is Vault {

bytes32 tipHash = bridge.tipHash();
uint256 timestamp;
(,,, timestamp) = bridge.periods(tipHash);
(, timestamp) = bridge.periods(tipHash);

depositCount++;
deposits[depositCount] = Deposit({
Expand Down
30 changes: 14 additions & 16 deletions contracts/ExitHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ contract ExitHandler is DepositHandler {
*/
function startVerification(bytes32[] memory _proof, uint8 _outputIndex) public payable {
require(msg.value >= exitStake, "Not enough ether sent to pay for verification stake");
bytes32 parent;
(parent,,,) = bridge.periods(_proof[0]);
require(parent > 0, "The referenced period was not submitted to bridge");
uint32 timestamp;
(,timestamp) = bridge.periods(_proof[0]);
require(timestamp > 0, "The referenced period was not submitted to bridge");

// check exiting tx inclusion in the root chain block
bytes32 utxoId;
Expand Down Expand Up @@ -131,11 +131,11 @@ contract ExitHandler is DepositHandler {
bytes32[] memory _doublespendProof, bytes32[] memory _consolidateProof,
uint8 _inputIndex, uint8 _consolidateInputIndex) public {
// output owner of consolidate proof different then owner of some input
bytes32 parent;
(parent,,,) = bridge.periods(_doublespendProof[0]);
require(parent > 0, "The period referenced in doublespendProof was not submitted to bridge");
(parent,,,) = bridge.periods(_consolidateProof[0]);
require(parent > 0, "The period referenced in consolidateProof was not submitted to bridge");
uint32 timestamp;
(,timestamp) = bridge.periods(_doublespendProof[0]);
require(timestamp > 0, "The period referenced in doublespendProof was not submitted to bridge");
(,timestamp) = bridge.periods(_consolidateProof[0]);
require(timestamp > 0, "The period referenced in consolidateProof was not submitted to bridge");

// check consolidate tx inclusion in the root chain block
bytes32 txHash;
Expand Down Expand Up @@ -186,14 +186,13 @@ contract ExitHandler is DepositHandler {
uint8 _outputIndex, uint8 _inputIndex
) public payable {
require(msg.value >= exitStake, "Not enough ether sent to pay for exit stake");
bytes32 parent;
uint32 timestamp;
(parent,,, timestamp) = bridge.periods(_proof[0]);
require(parent > 0, "The referenced period was not submitted to bridge");
(, timestamp) = bridge.periods(_proof[0]);
require(timestamp > 0, "The referenced period was not submitted to bridge");

if (_youngestInputProof.length > 0) {
(parent,,, timestamp) = bridge.periods(_youngestInputProof[0]);
require(parent > 0, "The referenced period was not submitted to bridge");
(, timestamp) = bridge.periods(_youngestInputProof[0]);
require(timestamp > 0, "The referenced period was not submitted to bridge");
}

// check exiting tx inclusion in the root chain block
Expand Down Expand Up @@ -417,10 +416,9 @@ contract ExitHandler is DepositHandler {
// check younger input is actually an input of exiting tx
require(txHash == exitingTx.ins[_inputIndex].outpoint.hash, "Given output is not referenced in exiting tx");

bytes32 parent;
uint32 youngerInputTimestamp;
(parent,,,youngerInputTimestamp) = bridge.periods(_youngerInputProof[0]);
require(parent > 0, "The referenced period was not submitted to bridge");
(,youngerInputTimestamp) = bridge.periods(_youngerInputProof[0]);
require(youngerInputTimestamp > 0, "The referenced period was not submitted to bridge");

require(exits[utxoId].priorityTimestamp < youngerInputTimestamp, "Challenged input should be older");

Expand Down
9 changes: 4 additions & 5 deletions contracts/FastExitHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import "./TxLib.sol";
contract FastExitHandler is ExitHandler {

struct Data {
bytes32 parent;
uint32 timestamp;
bytes32 txHash;
uint64 txPos;
Expand All @@ -29,11 +28,11 @@ contract FastExitHandler is ExitHandler {
require(msg.value >= exitStake, "Not enough ether sent to pay for exit stake");
Data memory data;

(data.parent,,,) = bridge.periods(_proof[0]);
require(data.parent > 0, "The referenced period was not submitted to bridge");
(,data.timestamp) = bridge.periods(_proof[0]);
require(data.timestamp > 0, "The referenced period was not submitted to bridge");

(data.parent,,, data.timestamp) = bridge.periods(_youngestInputProof[0]);
require(data.parent > 0, "The referenced period was not submitted to bridge");
(, data.timestamp) = bridge.periods(_youngestInputProof[0]);
require(data.timestamp > 0, "The referenced period was not submitted to bridge");

// check exiting tx inclusion in the root chain block
bytes memory txData;
Expand Down
8 changes: 5 additions & 3 deletions contracts/MinGov.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ contract MinGov is Ownable {
if (prop.created + proposalTime <= now) {
if (!prop.canceled) {
bool rv;
if ( getSig(prop.msgData) == 0x8f283970 || // changeAdmin(address)
getSig(prop.msgData) == 0x3659cfe6 // upgradeTo(address)
) {
bytes4 sig = getSig(prop.msgData);
// 0x8f283970 = changeAdmin(address)
// 0x3659cfe6 = upgradeTo(address)
// 0x983b2d56 = addMinter(address)
if (sig == 0x8f283970||sig == 0x3659cfe6||sig == 0x983b2d56) {
// this changes proxy parameters
(rv, ) = prop.subject.call(prop.msgData);
} else {
Expand Down
8 changes: 8 additions & 0 deletions contracts/PoaOperator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ contract PoaOperator is Adminable {
}
}

event Submission(
bytes32 indexed blocksRoot,
uint256 indexed slotId,
address owner,
bytes32 periodRoot
);

function submitPeriod(uint256 _slotId, bytes32 _prevHash, bytes32 _blocksRoot) public {
require(_slotId < epochLength, "Incorrect slotId");
Slot storage slot = slots[_slotId];
Expand Down Expand Up @@ -195,5 +202,6 @@ contract PoaOperator is Adminable {
lastEpochBlockHeight = newHeight;
emit Epoch(lastCompleteEpoch);
}
emit Submission(_blocksRoot, _slotId, slot.owner, hashRoot);
}
}
2 changes: 1 addition & 1 deletion contracts/SwapRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ contract SwapRegistry is Adminable {
mstore(0x20, right)
right := keccak256(0, 0x40)
}
(,height ,,) = bridge.periods(right);
(height ,) = bridge.periods(right);
require(height > maxHeight, "unorderly claim");
maxHeight = height;
claimCount += 1;
Expand Down
9 changes: 7 additions & 2 deletions migrations/2_deploy_token.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ const log = require('./utils/log');
const NativeToken = artifacts.require('NativeToken');

module.exports = (deployer) => {

const deployedToken = process.env.DEPLOYED_TOKEN;

deployer.then(async () => {
const nativeToken = await deployer.deploy(NativeToken, "LeapToken", "LEAP", 18);
log('Deployed LEAP Token at', nativeToken.address);
if (!deployedToken) {
const nativeToken = await deployer.deploy(NativeToken, "LeapToken", "LEAP", 18);
log('Deployed LEAP Token at', nativeToken.address);
}
});
};
48 changes: 39 additions & 9 deletions migrations/3_deploy_plasma.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,46 @@ const Bridge = artifacts.require('Bridge');
const Vault = artifacts.require('Vault');
const NativeToken = artifacts.require('NativeToken');
const PoaOperator = artifacts.require('PoaOperator');
const ExitHandler = artifacts.require('ExitHandler');
const ExitHandler = artifacts.require('FastExitHandler');
const SwapRegistry = artifacts.require('SwapRegistry');
const PriorityQueue = artifacts.require('PriorityQueue');
const AdminableProxy = artifacts.require('AdminableProxy');
const BridgeProxy = artifacts.require('BridgeProxy');
const OperatorProxy = artifacts.require('OperatorProxy');
const ExitHandlerProxy = artifacts.require('ExitHandlerProxy');


const DEFAULT_EXIT_DURATION = duration.days(14);
const DEFAULT_EXIT_STAKE = 0;
const DEFAULT_EPOCH_LENGTH = 32;
const DEFAULT_PARENT_BLOCK_INTERVAL = 0;
const DEFAULT_EXIT_DURATION = duration.days(7);
const DEFAULT_EXIT_STAKE = '0x016345785D8A0000'; // 10^17
const DEFAULT_EPOCH_LENGTH = 4;
const DEFAULT_TAX_RATE = 50; // 5%
const DEFAULT_PARENT_BLOCK_INTERVAL = 2;

module.exports = (deployer, network, accounts) => {
const admin = accounts[1];

const exitDuration = process.env.EXIT_DURATION || DEFAULT_EXIT_DURATION;
const exitStake = process.env.EXIT_STAKE || DEFAULT_EXIT_STAKE;
const epochLength = process.env.EPOCH_LENGTH || DEFAULT_EPOCH_LENGTH;
const parentBlockInterval = process.env.PARENT_BLOCK_INTERVAL || DEFAULT_PARENT_BLOCK_INTERVAL;
const deployedToken = process.env.DEPLOYED_TOKEN;
const taxRate = process.env.TAX_RATE || DEFAULT_TAX_RATE;
const poaReward = process.env.POA_REWARD || 0;

let data;

deployer.then(async () => {
const nativeToken = await NativeToken.deployed();
log(' ♻️ Reusing existing Native Token:', nativeToken.address);
let nativeToken;
if(deployedToken) {
nativeToken = await NativeToken.at(deployedToken);
log(' ♻️ Found token in environment:', nativeToken.address);
} else {
nativeToken = await NativeToken.deployed();
log(' ♻️ Reusing existing Native Token:', nativeToken.address);
}

const bridgeCont = await deployer.deploy(Bridge);
data = bridgeCont.contract.methods.initialize(DEFAULT_PARENT_BLOCK_INTERVAL).encodeABI();
data = bridgeCont.contract.methods.initialize(parentBlockInterval).encodeABI();
const bridgeProxy = await deployer.deploy(BridgeProxy, bridgeCont.address, data, { from: admin });

const pqLib = await deployer.deploy(PriorityQueue);
Expand All @@ -45,9 +59,25 @@ module.exports = (deployer, network, accounts) => {
const exitHandlerProxy = await deployer.deploy(ExitHandlerProxy, exitHandlerCont.address, data, { from: admin });

const operatorCont = await deployer.deploy(PoaOperator);
data = await operatorCont.contract.methods.initialize(bridgeProxy.address, exitHandlerProxy.address, DEFAULT_EPOCH_LENGTH).encodeABI();
data = await operatorCont.contract.methods.initialize(bridgeProxy.address, exitHandlerProxy.address, epochLength).encodeABI();
const operatorProxy = await deployer.deploy(OperatorProxy, operatorCont.address, data, { from: admin });

const registryCont = await deployer.deploy(SwapRegistry);
data = await registryCont.contract.methods.initialize(bridgeProxy.address, exitHandlerProxy.address, poaReward).encodeABI();
const registryProxy = await deployer.deploy(AdminableProxy, registryCont.address, data, { from: admin });

const swapRegistry = await SwapRegistry.at(registryProxy.address);
if (taxRate) {
data = await swapRegistry.contract.methods.setTaxRate(taxRate).encodeABI();
await bridgeProxy.applyProposal(data, {from: admin});
}

const isMinter = await nativeToken.isMinter(accounts[0]);
// if we got the right, then add registry as minter
if (isMinter) {
await nativeToken.addMinter(swapRegistry.address);
}

const bridge = await Bridge.at(bridgeProxy.address);
data = await bridge.contract.methods.setOperator(operatorProxy.address).encodeABI();
await bridgeProxy.applyProposal(data, {from: admin});
Expand Down
34 changes: 33 additions & 1 deletion migrations/4_deploy_governance.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const { durationToString, duration } = require('../test/helpers/duration');
const log = require('./utils/log');

const MinGov = artifacts.require('MinGov');
const NativeToken = artifacts.require('NativeToken');
const AdminableProxy = artifacts.require('AdminableProxy');
const BridgeProxy = artifacts.require('BridgeProxy');
const OperatorProxy = artifacts.require('OperatorProxy');
const ExitHandlerProxy = artifacts.require('ExitHandlerProxy');
Expand All @@ -17,9 +19,17 @@ const DEFAULT_PROPOSAL_TIME = duration.days(14);

module.exports = (deployer, network, accounts) => {
const admin = accounts[1];
const proposalTime = process.env.PROPOSAL_TIME || DEFAULT_PROPOSAL_TIME;
const ownerAddr = process.env.GOV_OWNER;
const deployedToken = process.env.DEPLOYED_TOKEN;

deployer.then(async () => {
const proposalTime = process.env.PROPOSAL_TIME || DEFAULT_PROPOSAL_TIME;
let nativeToken;
if(deployedToken) {
nativeToken = await NativeToken.at(deployedToken);
} else {
nativeToken = await NativeToken.deployed();
}

log(' 🕐 Deploying Governance with proposal time:', durationToString(proposalTime));
const governance = await deployer.deploy(MinGov, proposalTime);
Expand All @@ -35,5 +45,27 @@ module.exports = (deployer, network, accounts) => {
const exitHandlerProxy = await ExitHandlerProxy.deployed();
log(' 🔄 Transferring ownership for ExitHandler:', exitHandlerProxy.address);
await exitHandlerProxy.changeAdmin(governance.address, { from: admin });

const registryProxy = await AdminableProxy.deployed();
log(' 🔄 Transferring ownership for SwapRegistry:', registryProxy.address);
await registryProxy.changeAdmin(governance.address, { from: admin });

const isMinter = await nativeToken.isMinter(accounts[0]);
if (isMinter) {
log(' 🔄 Transferring minting right for token:', nativeToken.address);
await nativeToken.addMinter(governance.address);
await nativeToken.renounceMinter();
}

if (ownerAddr) {
log(' 🔄 Transferring ownership for Governance:', ownerAddr);
await governance.transferOwnership(ownerAddr);
}

const isRegistryMinter = await nativeToken.isMinter(registryProxy.address);
if (!isRegistryMinter) {
log(' ⚠ Minting rights could not be set on token:', nativeToken.address);
log(` ⚠ Add SwapRegistry (${registryProxy.address}) as minter manually.`);
}
});
};
Loading

0 comments on commit 8f13f90

Please sign in to comment.