Skip to content

Commit

Permalink
Merge the develop branch to the master branch, preparation to v5.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
akolotov committed May 25, 2020
2 parents a7ce444 + f74821a commit 129f4b4
Show file tree
Hide file tree
Showing 90 changed files with 7,837 additions and 900 deletions.
7 changes: 7 additions & 0 deletions .dockerignore
@@ -1,9 +1,16 @@
node_modules
deploy/node_modules
.git
.gitignore
.dockerignore
deploy/.env
deploy/*.config
deploy/*.env
docker-compose.yml
Dockerfile
*.log
flats
contracts.sublime-project
contracts.sublime-workspace
upgrade/.env*
!upgrade/.env.example
6 changes: 5 additions & 1 deletion .gitignore
Expand Up @@ -5,4 +5,8 @@ flats
.idea
coverage
*.sublime-*
.0x-artifacts
.0x-artifacts
upgrade/.env*
deploy/.env
deploy/*.config
deploy/*.env
1 change: 1 addition & 0 deletions .solhint.json
Expand Up @@ -9,6 +9,7 @@
"multiple-sends": "off",
"bracket-align": "off",
"no-complex-fallback": "off",
"no-simple-event-func-name": "off",
"compiler-version": ["error", "0.4.24"]
}
}
18 changes: 17 additions & 1 deletion Dockerfile
Expand Up @@ -18,9 +18,25 @@ COPY ./upgrade/package.json ./upgrade/
COPY ./upgrade/package-lock.json ./upgrade/
RUN cd ./upgrade; npm install; cd ..

COPY . .
COPY ./scripts ./scripts

COPY truffle-config.js truffle-config.js
COPY ./contracts ./contracts
RUN npm run compile

COPY flatten.sh flatten.sh
RUN bash flatten.sh

COPY .eslintignore .eslintignore
COPY .eslintrc .eslintrc
COPY .prettierrc .prettierrc

COPY ./upgrade ./upgrade
COPY deploy.sh deploy.sh
COPY ./deploy ./deploy
COPY .solhint.json .solhint.json
COPY codechecks.yml codechecks.yml
COPY ./test ./test

ENV PATH="/contracts/:${PATH}"
ENV NOFLAT=true
10 changes: 6 additions & 4 deletions README.md
Expand Up @@ -3,7 +3,7 @@
[![Coverage Status](https://coveralls.io/repos/github/poanetwork/tokenbridge-contracts/badge.svg?branch=master)](https://coveralls.io/github/poanetwork/tokenbridge-contracts?branch=master)

# POA Bridge Smart Contracts
These contracts provide the core functionality for the POA bridge. They implement the logic to relay assests between two EVM-based blockchain networks. The contracts collect bridge validator's signatures to approve and facilitate relay operations.
These contracts provide the core functionality for the POA bridge. They implement the logic to relay assests between two EVM-based blockchain networks. The contracts collect bridge validator's signatures to approve and facilitate relay operations.

The POA bridge smart contracts are intended to work with [the bridge process implemented on NodeJS](https://github.com/poanetwork/token-bridge).
Please refer to the bridge process documentation to configure and deploy the bridge.
Expand Down Expand Up @@ -36,6 +36,7 @@ The POA bridge contracts consist of several components:
* The **Foreign Bridge** smart contract. This is deployed in the Ethereum Mainnet.
* Depending on the type of relay operations the following components are also used:
* in `NATIVE-TO-ERC` mode: the ERC20 token (in fact, the ERC677 extension is used) is deployed on the Foreign network;
* in `AMB-NATIVE-TO-ERC` mode: the ERC20 token (in fact, the ERC677 extension is used) is deployed on the Foreign network;
* in `ERC-TO-ERC` mode: the ERC20 token (in fact, the ERC677 extension is used) is deployed on the Home network;
* in `AMB-ERC-TO-ERC` mode: the ERC20 token (in fact, the ERC677 extension is used) is deployed on the Home network;
* in `ERC-TO-NATIVE` mode: The home network nodes must support consensus engine that allows using a smart contract for block reward calculation;
Expand All @@ -59,16 +60,17 @@ Responsibilities and roles of the bridge:
- **User** role:
- sends assets to Bridge contracts:
- in `NATIVE-TO-ERC` mode: send native coins to the Home Bridge to receive ERC20 tokens from the Foreign Bridge, send ERC20 tokens to the Foreign Bridge to unlock native coins from the Home Bridge;
- in `ERC-TO-ERC` mode: transfer ERC20 tokens to the Foreign Bridge to mint ERC20 tokens on the Home Network, transfer ERC20 tokens to the Home Bridge to unlock ERC20 tokens on Foreign networks;
- in `ERC-TO-ERC` mode: transfer ERC20 tokens to the Foreign Bridge to mint ERC20 tokens on the Home Network, transfer ERC20 tokens to the Home Bridge to unlock ERC20 tokens on Foreign networks;
- in `ERC-TO-NATIVE` mode: send ERC20 tokens to the Foreign Bridge to receive native coins from the Home Bridge, send native coins to the Home Bridge to unlock ERC20 tokens from the Foreign Bridge;
- in `ARBITRARY-MESSAGE` mode: Invoke Home/Foreign Bridge to send a message that will be executed on the other Network as an arbitrary contract method invocation;
- in `AMB-ERC-TO-ERC` mode: transfer ERC20 tokens to the Foreign Mediator which will interact with Foreign AMB Bridge to mint ERC20 tokens on the Home Network, transfer ERC20 tokens to the Home Mediator which will interact with Home AMB Bridge to unlock ERC20 tokens on Foreign network.
- in `AMB-NATIVE-TO-ERC` mode: send native coins to the Home Mediator which will interact with Home AMB Bridge to mint ERC20 tokens on the Foreign Network, transfer ERC20 tokens to the Foreign Mediator which will interact with Foreign AMB Bridge to unlock native coins from Home network.

## Usage

There are two ways to deploy contracts:
* install and use NodeJS
* use Docker to deploy
* use Docker to deploy

### Deployment with NodeJS

Expand Down Expand Up @@ -101,7 +103,7 @@ npm run flatten
The flattened contracts can be found in the `flats` directory.

### Deployment in the Docker environment
[Docker](https://www.docker.com/community-edition) and [Docker Compose](https://docs.docker.com/compose/install/) can be used to deploy contracts without NodeJS installed on the system.
[Docker](https://www.docker.com/community-edition) and [Docker Compose](https://docs.docker.com/compose/install/) can be used to deploy contracts without NodeJS installed on the system.
If you are on Linux, we recommend you [create a docker group and add your user to it](https://docs.docker.com/install/linux/linux-postinstall/), so that you can use the CLI without `sudo`.

#### Prepare the docker container
Expand Down
15 changes: 15 additions & 0 deletions REWARD_MANAGEMENT.md
Expand Up @@ -72,3 +72,18 @@ Fees are calculated and distributed on Home network. Validators will receive ERC
Fees are calculated and distributed on Home network. Validators will receive ERC20 tokens.
![ERC-ERC-HomeToForeign](https://user-images.githubusercontent.com/4614574/59939670-0cc32480-942f-11e9-9693-727125555c97.png)

## AMB-NATIVE-TO-ERC
Configuration:
```
HOME_REWARDABLE=ONE_DIRECTION
FOREIGN_REWARDABLE=ONE_DIRECTION
```
### Home to Foreign transfer
Fees are calculated and distributed on Foreign network. The reward accounts will receive ERC20 tokens.
![AMB-NATIVE-TO-ERC677-Home-Foreign](https://user-images.githubusercontent.com/4614574/74660965-dd0f1c80-5175-11ea-8d6c-51b8bd85f844.png)

### Foreign to Home transfer
Fees are calculated and distributed on Home network. The reward accounts will receive native tokens.
![AMB-NATIVE-TO-ERC677-Foreign-Home](https://user-images.githubusercontent.com/4614574/74660986-e6988480-5175-11ea-9216-7f008a6fdaf0.png)


12 changes: 7 additions & 5 deletions contracts/interfaces/IAMB.sol
Expand Up @@ -4,9 +4,11 @@ interface IAMB {
function messageSender() external view returns (address);
function maxGasPerTx() external view returns (uint256);
function transactionHash() external view returns (bytes32);
function messageCallStatus(bytes32 _txHash) external view returns (bool);
function failedMessageDataHash(bytes32 _txHash) external view returns (bytes32);
function failedMessageReceiver(bytes32 _txHash) external view returns (address);
function failedMessageSender(bytes32 _txHash) external view returns (address);
function requireToPassMessage(address _contract, bytes _data, uint256 _gas) external;
function messageId() external view returns (bytes32);
function messageSourceChainId() external view returns (bytes32);
function messageCallStatus(bytes32 _messageId) external view returns (bool);
function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32);
function failedMessageReceiver(bytes32 _messageId) external view returns (address);
function failedMessageSender(bytes32 _messageId) external view returns (address);
function requireToPassMessage(address _contract, bytes _data, uint256 _gas) external returns (bytes32);
}
5 changes: 5 additions & 0 deletions contracts/interfaces/IMediatorFeeManager.sol
@@ -0,0 +1,5 @@
pragma solidity 0.4.24;

interface IMediatorFeeManager {
function calculateFee(uint256) external view returns (uint256);
}
20 changes: 20 additions & 0 deletions contracts/libraries/Address.sol
@@ -0,0 +1,20 @@
pragma solidity 0.4.24;

import "../upgradeable_contracts/Sacrifice.sol";

/**
* @title Address
* @dev Helper methods for Address type.
*/
library Address {
/**
* @dev Try to send native tokens to the address. If it fails, it will force the transfer by creating a selfdestruct contract
* @param _receiver address that will receive the native tokens
* @param _value the amount of native tokens to send
*/
function safeSendValue(address _receiver, uint256 _value) internal {
if (!_receiver.send(_value)) {
(new Sacrifice).value(_value)(_receiver);
}
}
}
126 changes: 81 additions & 45 deletions contracts/libraries/ArbitraryMessage.sol
@@ -1,76 +1,112 @@
pragma solidity 0.4.24;

import "../interfaces/IBridgeValidators.sol";
import "./Message.sol";

library ArbitraryMessage {
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 32 bytes :: bytes32 txHash
// offset 52: 20 bytes :: address - sender address
// offset 72: 20 bytes :: address - executor contract
// offset 104: 32 bytes :: uint256 - gasLimit
// offset 136: 1 bytes :: bytes1 - dataType
// (optional) 137: 32 bytes :: uint256 - gasPrice
// (optional) 137: 1 bytes :: bytes1 - gasPriceSpeed

// bytes 1 to 32 are 0 because message length is stored as little endian.
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 zero bytes followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61

function unpackData(bytes _data, bool applyDataOffset)
/**
* @dev Unpacks data fields from AMB message
* layout of message :: bytes:
* offset 0 : 32 bytes :: uint256 - message length
* offset 32 : 32 bytes :: bytes32 - messageId
* offset 64 : 20 bytes :: address - sender address
* offset 84 : 20 bytes :: address - executor contract
* offset 104 : 4 bytes :: uint32 - gasLimit
* offset 108 : 1 bytes :: uint8 - source chain id length (X)
* offset 109 : 1 bytes :: uint8 - destination chain id length (Y)
* offset 110 : 1 bytes :: bytes1 - dataType
* (optional) 111 : 32 bytes :: uint256 - gasPrice
* (optional) 111 : 1 bytes :: bytes1 - gasPriceSpeed
* offset 111/143/112 : X bytes :: bytes - source chain id
* offset 111/143/112 + X : Y bytes :: bytes - destination chain id
* NOTE: when message structure is changed, make sure that MESSAGE_PACKING_VERSION from VersionableAMB is updated as well
* NOTE: assembly code uses calldatacopy, make sure that message is passed as the first argument in the calldata
* @param _data encoded message
*/
function unpackData(bytes _data)
internal
pure
returns (
bytes32 messageId,
address sender,
address executor,
bytes32 txHash,
uint256 gasLimit,
uint32 gasLimit,
bytes1 dataType,
uint256[2] chainIds,
uint256 gasPrice,
bytes memory data
)
{
uint256 dataOffset = 0;
// 32 (message id) + 20 (sender) + 20 (executor) + 4 (gasLimit) + 1 (source chain id length) + 1 (destination chain id length) + 1 (dataType)
uint256 srcdataptr = 32 + 20 + 20 + 4 + 1 + 1 + 1;
uint256 datasize;
// 32 (tx hash) + 20 (sender) + 20 (executor) + 32 (gasLimit) + 1 (dataType)
uint256 srcdataptr = 32 + 20 + 20 + 32 + 1;

assembly {
txHash := mload(add(_data, 32))
sender := mload(add(_data, 52))
executor := mload(add(_data, 72))
gasLimit := mload(add(_data, 104))
dataType := and(mload(add(_data, 136)), 0xFF00000000000000000000000000000000000000000000000000000000000000)
messageId := mload(add(_data, 32)) // 32 bytes
sender := and(mload(add(_data, 52)), 0xffffffffffffffffffffffffffffffffffffffff) // 20 bytes

// executor (20 bytes) + gasLimit (4 bytes) + srcChainIdLength (1 byte) + dstChainIdLength (1 bytes) + dataType (1 byte) + remainder (5 bytes)
let blob := mload(add(_data, 84))

// after bit shift left 12 bytes are zeros automatically
executor := shr(96, blob)
gasLimit := and(shr(64, blob), 0xffffffff)

// load source chain id length
let chainIdLength := byte(24, blob)

dataType := and(shl(208, blob), 0xFF00000000000000000000000000000000000000000000000000000000000000)
switch dataType
case 0x0000000000000000000000000000000000000000000000000000000000000000 {
gasPrice := 0
}
case 0x0100000000000000000000000000000000000000000000000000000000000000 {
gasPrice := mload(add(_data, 137)) // 32
srcdataptr := add(srcdataptr, 0x20)
gasPrice := mload(add(_data, 111)) // 32
srcdataptr := add(srcdataptr, 32)
}
case 0x0200000000000000000000000000000000000000000000000000000000000000 {
gasPrice := 0
srcdataptr := add(srcdataptr, 0x01)
srcdataptr := add(srcdataptr, 1)
}

// at this moment srcdataptr points to sourceChainId

// mask for sourceChainId
// e.g. length X -> (1 << (X * 8)) - 1
let mask := sub(shl(shl(3, chainIdLength), 1), 1)

// increase payload offset by length of source chain id
srcdataptr := add(srcdataptr, chainIdLength)

// write sourceChainId
mstore(chainIds, and(mload(add(_data, srcdataptr)), mask))

// at this moment srcdataptr points to destinationChainId

// load destination chain id length
chainIdLength := byte(25, blob)

// mask for destinationChainId
// e.g. length X -> (1 << (X * 8)) - 1
mask := sub(shl(shl(3, chainIdLength), 1), 1)

// increase payload offset by length of destination chain id
srcdataptr := add(srcdataptr, chainIdLength)

// write destinationChainId
mstore(add(chainIds, 32), and(mload(add(_data, srcdataptr)), mask))

// at this moment srcdataptr points to payload

// datasize = message length - payload offset
datasize := sub(mload(_data), srcdataptr)
}

data = new bytes(datasize);
assembly {
// BYTES_HEADER_SIZE
let dataptr := add(data, 32)
if eq(applyDataOffset, 1) {
dataOffset := 32
}
// 68 = 4 (selector) + 32 (bytes header) + 32 (bytes length)
calldatacopy(dataptr, add(add(68, srcdataptr), dataOffset), datasize)
// 36 = 4 (selector) + 32 (bytes length header)
srcdataptr := add(srcdataptr, 36)

// calldataload(4) - offset of first bytes argument in the calldata
calldatacopy(add(data, 32), add(calldataload(4), srcdataptr), datasize)
}
}
}
12 changes: 8 additions & 4 deletions contracts/libraries/Bytes.sol
Expand Up @@ -6,8 +6,12 @@ pragma solidity 0.4.24;
*/
library Bytes {
/**
* @dev Truncate bytes array if its size is more than 32 bytes
* and pads the array with zeros from the right side if its size is less than 32.
* @dev Converts bytes array to bytes32.
* Truncates bytes array if its size is more than 32 bytes.
* NOTE: This function does not perform any checks on the received parameter.
* Make sure that the _bytes argument has a correct length, not less than 32 bytes.
* A case when _bytes has length less than 32 will lead to the undefined behaviour,
* since assembly will read data from memory that is not related to the _bytes argument.
* @param _bytes to be converted to bytes32 type
* @return bytes32 type of the firsts 32 bytes array in parameter.
*/
Expand All @@ -18,8 +22,8 @@ library Bytes {
}

/**
* @dev Truncate bytes array if its size is more than 20 bytes
* and pads the array with zeros from the right side if its size is less than 20.
* @dev Truncate bytes array if its size is more than 20 bytes.
* NOTE: Similar to the bytesToBytes32 function, make sure that _bytes is not shorter than 20 bytes.
* @param _bytes to be converted to address type
* @return address included in the firsts 20 bytes of the bytes array in parameter.
*/
Expand Down

0 comments on commit 129f4b4

Please sign in to comment.