TokenBridge is an interoperability solution between Ethereum networks for native to ERC20 and ERC20 to ERC20 cross chain transfers
Branch: master
Clone or download
akolotov Merge pull request #128 from poanetwork/develop
Merge the develop branch to the master branch
Latest commit 7ccf71d Dec 29, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
abis Add ERC20-to-Native contract abis Sep 25, 2018
audit/peppersec Added a file with the security audit for erc20-to-native bridge Dec 12, 2018
config Add default max processing time Oct 24, 2018
docs Add stress testing statistics results Oct 29, 2018
e2e Update submodule to tag 2.1.0 Nov 7, 2018
scripts Use exit code constant for general errors Oct 24, 2018
src Increase extra gas percentage Oct 31, 2018
submodules Update submodule to tag 2.1.0 Nov 7, 2018
test Merge branch 'develop' into support-erc20-native-#81 Oct 16, 2018
.dockerignore Aded cpu and mem limits, fixed dockerignore Nov 13, 2018
.editorconfig Add .editorconfig May 22, 2018
.env.example Merge pull request #119 from ArseniiPetrovich/developing Dec 13, 2018
.eslintignore Add coverage and submodules to .eslintignore Jul 31, 2018
.eslintrc Improve logs for nonce fetching Oct 22, 2018
.gitignore Pipe logs to disk and add script to compute basic statistics Aug 10, 2018
.gitmodules Move contracts submodule up Jul 30, 2018
.nvmrc Add eslint and prettier May 22, 2018
.nycrc Add script for getting test coverage Jul 17, 2018
.prettierrc Add eslint and prettier May 22, 2018
.travis.yml Put all env variables in the same item Aug 6, 2018
CODE_OF_CONDUCT.md README changes, added CONTRIBUTING and CODE_OF_CONDUCT Oct 1, 2018
CONTRIBUTING.md README changes, added CONTRIBUTING and CODE_OF_CONDUCT Oct 1, 2018
Dockerfile Update docs Dec 16, 2018
ERC-to-ERC.png Changes based on review comments Oct 8, 2018
LICENSE Change license to GPL3 Sep 5, 2018
Native-to-ERC.png Changes based on review comments Oct 8, 2018
README.md Add info section about launching commands for installations based on … Dec 27, 2018
docker-compose.yml Remove obsolete bridge service Dec 19, 2018
package-lock.json Add test for parseMessage and refactor Sep 11, 2018
package.json Add test:watch npm script Sep 12, 2018
reset-lastBlock.sh Add reset last block script Jul 27, 2018

README.md

POA Bridge - NodeJS Oracle

Build Status Gitter

Bridge Overview

The POA Bridge allows users to transfer assets between two chains in the Ethereum ecosystem. It is composed of several elements which are located in different POA Network repositories:

Bridge Elements

  1. An oracle written in NodeJS, contained in this repository.
  2. Solidity smart contracts. Used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
  3. Bridge UI Application. A DApp interface to transfer tokens and coins between chains.
  4. Bridge Monitor. A tool for checking balances and unprocessed events in bridged networks.
  5. Bridge Deployment Playbooks. Manages configuration instructions for remote deployments.

The bridge oracle is deployed on specified validator nodes (only nodes whose private keys correspond to addresses specified in the smart contracts) in the network. The oracle connects to two chains via a Remote Procedure Call (RPC). It is responsible for:

  • listening to events related to bridge contracts
  • sending transactions to authorize asset transfers

Following is an overview of the NodeJS bridge oracle and instructions for getting started with the POA Bridge.

Interoperability

Interoperability is the ability to share resources between networks. The POA Bridge is an interoperability protocol where users can transfer value (ERC20 compatible tokens and network coins) between chains in the Ethereum ecosystem. This creates opportunities to use different chains for different purposes. For example, smart contracts can allocate resource intensive operations to a sidechain where transactions are fast and inexpensive.

Network Processes

Network Definitions

Bridging occurs between two networks.

  • Home - or Native - is a network with fast and inexpensive operations. All bridge operations to collect validator confirmations are performed on this side of the bridge.

  • Foreign can be any chain, but generally refers to the Ethereum mainnet.

Operational Modes

The POA bridge currently provides two operational modes, with a 3rd mode in development.

  • Native-to-ERC20 Coins on a Home network can be converted to ERC20-compatible tokens on a Foreign network. Coins are locked on the Home side and the corresponding amount of ERC20 tokens are minted on the Foreign side. When the operation is reversed, tokens are burnt on the Foreign side and unlocked in the Home network.
  • ERC20-to-ERC20 ERC20-compatible tokens on the Foreign network are locked and minted as ERC20-compatible tokens (ERC677 tokens) on the Home network. When transferred from Home to Foreign, they are burnt on the Home side and unlocked in the Foreign network. This can be considered a form of atomic swap when a user swaps the token "X" in network "A" to the token "Y" in network "B".
  • ERC20-to-Native: Currently in development. Pre-existing tokens in the Foreign network are locked and coins are minted in the Home network. The Home network consensus engine in this case should support invocation of Parity's Block Reward contract (https://wiki.parity.io/Block-Reward-Contract.html) to mint coins per the bridge contract request.

Architecture

Native-to-ERC20

Native-to-ERC

ERC20-to-ERC20

ERC-to-ERC

Watcher

A watcher listens for a certain event and creates proper jobs in the queue. These jobs contain the transaction data (without the nonce) and the transaction hash for the related event. The watcher runs on a given frequency, keeping track of the last processed block.

If the watcher observes that the transaction data cannot be prepared, which generally means that the corresponding method of the bridge contract cannot be invoked, it inspects the contract state to identify the potential reason for failure and records this in the logs.

There are three Watchers:

  • Signature Request Watcher: Listens to UserRequestForSignature events on the Home network.
  • Collected Signatures Watcher: Listens to CollectedSignatures events on the Home network.
  • Affirmation Request Watcher: Depends on the bridge mode.
    • Native-to-ERC20: Listens to UserRequestForAffirmation raised by the bridge contract.
    • ERC20-to-ERC20 and ERC20-to-Native: Listens to Transfer events raised by the token contract.

Sender

A sender subscribes to the queue and keeps track of the nonce. It takes jobs from the queue, extracts transaction data, adds the proper nonce, and sends it to the network.

There are two Senders:

  • Home Sender: Sends transaction to the Home network.
  • Foreign Sender: Sends transaction to the Foreign network.

RabbitMQ

RabbitMQ is used to send jobs from watchers to senders.

Redis DB

Redis is used to store the number of blocks that were already inspected by watchers, and the NOnce (Number of Operation) which was used by the sender last time to send a transaction.

For more information on the Redis/RabbitMQ requirements, see #90

How to Use

Installation and Deployment

Deploy the Bridge Contracts

  1. Deploy the bridge contracts

  2. Open bridgeDeploymentResults.json generated by the bridge contract deployment process.

    Native-to-ERC20 mode example:

    {
        "homeBridge": {
            "address": "0xc60daff55ec5b5ce5c3d2105a77e287ff638c35e",
            "deployedBlockNumber": 123321
        },
        "foreignBridge": {
            "address": "0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be",
            "deployedBlockNumber": 456654,
            "erc677": {
                "address": "0x41a29780309dc2582f080f6af89953be3435679a"
            }
        }
    }

    ERC20-to-ERC20 mode example:

    {
        "homeBridge": {
            "address": "0x765a0d90e5a5773deacbd94b2dc941cbb163bdab",
            "deployedBlockNumber": 789987,
            "erc677": {
                "address": "0x269f57f5ae5421d084686f9e353f5b7ee6af54c2"
            }
        },
        "foreignBridge": {
            "address": "0x7ae703ea88b0545eef1f0bf8f91d5276e39be2f7",
            "deployedBlockNumber": 567765
        }
    }

Configuration

  1. Create a .env file: cp .env.example .env

  2. Fill in the required information using the output data from bridgeDeploymentResults.json. Check the tables with the set of parameters below to see their explanation.

Run the Processes

There are two options to run the nodejs oracle:

  1. Docker containers. This requires Docker and Docker Compose installed. If you are on Linux, it's also recommended that you create a docker group and add your user to it, so that you can use the CLI without sudo.
  2. NodeJs Package Manager (NPM).

Docker

  • While running the bridge containers for the first time use VALIDATOR_ADDRESS=<validator address> VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> docker-compose up -d --build
  • For further launches use VALIDATOR_ADDRESS=<validator address> VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> docker-compose up --detach
  • If you want to use any command from this document, prefix it with docker-compose exec bridge_affirmation, if not already prefixed, to execute command inside on of the running docker containers. Make sure bridge service is started before using the commands.

NPM

  • redis-server starts Redis. redis-cli ping will return a pong if Redis is running.
  • rabbitmq-server starts RabbitMQ. Use rabbitmqctl status to check if RabbitMQ is running.
  • npm run watcher:signature-request
  • npm run watcher:collected-signatures
  • npm run watcher:affirmation-request
  • npm run sender:home
  • npm run sender:foreign

Bridge UI

See the Bridge UI installation instructions to configure and use the optional Bridge UI.

Rollback the Last Processed Block in Redis

If the bridge does not handle an event properly (i.e. a transaction stalls due to a low gas price), the Redis DB can be rolled back. You must identify which watcher needs to re-run. For example, if the validator signatures were collected but the transaction with signatures was not sent to the Foreign network, the collected-signatures watcher must look at the block where the corresponding CollectedSignatures event was raised.

Execute this command in the bridge root directory:

bash ./reset-lastBlock.sh <watcher> <block num>

for NPM installation or

docker-compose exec bridge_affirmation bash ./reset-lastBlock.sh <watcher> <block num>

for docker installation respectively, where the watcher could be one of:

  • signature-request
  • collected-signatures
  • affirmation-request

Configuration parameters

Variable Description Values
BRIDGE_MODE The bridge mode. The bridge starts listening to a different set of events based on this parameter. NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE
HOME_RPC_URL The HTTPS URL(s) used to communicate to the RPC nodes in the Home network. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. URL(s)
HOME_BRIDGE_ADDRESS The address of the bridge contract address in the Home network. It is used to listen to events from and send validators' transactions to the Home network. hexidecimal beginning with "0x"
HOME_POLLING_INTERVAL The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. integer
FOREIGN_RPC_URL The HTTPS URL(s) used to communicate to the RPC nodes in the Foreign network. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. URL(s)
FOREIGN_BRIDGE_ADDRESS The address of the bridge contract address in the Foreign network. It is used to listen to events from and send validators' transactions to the Foreign network. hexidecimal beginning with "0x"
ERC20_TOKEN_ADDRESS Used with the ERC_TO_ERC bridge mode, this parameter specifies the ERC20-compatible token contract address. The token contract address is used to identify transactions that transfer tokens to the Foreign Bridge account address. Omit this parameter with other bridge modes. hexidecimal beginning with "0x"
FOREIGN_POLLING_INTERVAL The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. integer
HOME_GAS_PRICE_ORACLE_URL The URL used to get a JSON response from the gas price prediction oracle for the Home network. The gas price provided by the oracle is used to send the validator's transactions to the RPC node. Since it is assumed that the Home network has a predefined gas price (e.g. the gas price in the Core of POA.Network is 1 GWei), the gas price oracle parameter can be omitted for such networks. URL
HOME_GAS_PRICE_SPEED_TYPE Assuming the gas price oracle responds with the following JSON structure: {"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}, this parameter specifies the desirable transaction speed. The speed type can be omitted when HOME_GAS_PRICE_ORACLE_URL is not used. instant / fast / standard / slow
HOME_GAS_PRICE_FALLBACK The gas price (in Wei) that is used if both the oracle and the fall back gas price specified in the Home Bridge contract are not available. integer
HOME_GAS_PRICE_UPDATE_INTERVAL An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. integer
FOREIGN_GAS_PRICE_ORACLE_URL The URL used to get a JSON response from the gas price prediction oracle for the Foreign network. The provided gas price is used to send the validator's transactions to the RPC node. If the Foreign network is Ethereum Foundation mainnet, the oracle URL can be: https://gasprice.poa.network. Otherwise this parameter can be omitted. URL
FOREIGN_GAS_PRICE_SPEED_TYPE Assuming the gas price oracle responds with the following JSON structure: {"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}, this parameter specifies the desirable transaction speed. The speed type can be omitted when FOREIGN_GAS_PRICE_ORACLE_URLis not used. instant / fast / standard / slow
FOREIGN_GAS_PRICE_FALLBACK The gas price (in Wei) used if both the oracle and fall back gas price specified in the Foreign Bridge contract are not available. integer
FOREIGN_GAS_PRICE_UPDATE_INTERVAL The interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. integer
VALIDATOR_ADDRESS_PRIVATE_KEY The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. hexidecimal without "0x"
HOME_START_BLOCK The block number in the Home network used to start watching for events when the bridge instance is run for the first time. Usually this is the same block where the Home Bridge contract is deployed. If a new validator instance is being deployed for an existing set of validators, the block number could be the latest block in the chain. integer
FOREIGN_START_BLOCK The block number in the Foreign network used to start watching for events when the bridge instance runs for the first time. Usually this is the same block where the Foreign Bridge contract was deployed to. If a new validator instance is being deployed for an existing set of validators, the block number could be the latest block in the chain. integer
QUEUE_URL RabbitMQ URL used by watchers and senders to communicate to the message queue. Typically set to: amqp://127.0.0.1. local URL
REDIS_URL Redis DB URL used by watchers and senders to communicate to the database. Typically set to: redis://127.0.0.1:6379. local URL
REDIS_LOCK_TTL Threshold in milliseconds for locking a resource in the Redis DB. Until the threshold is exceeded, the resource is unlocked. Usually it is 1000. integer
ALLOW_HTTP Only use in test environments - must be omitted in production environments.. If this parameter is specified and set to yes, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. yes / no
LOG_LEVEL Set the level of details in the logs. trace / debug / info / warn / error / fatal
MAX_PROCESSING_TIME The workers processes will be killed if this amount of time (in milliseconds) is ellapsed before they finish processing. It is recommended to set this value to 4 times the value of the longest polling time (set with the HOME_POLLING_INTERVAL and FOREIGN_POLLING_INTERVAL variables). To disable this, set the time to 0. integer

Useful Commands for Development

RabbitMQ

Command Description
rabbitmqctl list_queues List all queues
rabbitmqctl purge_queue home Remove all messages from home queue
rabbitmqctl status check if rabbitmq server is currently running
rabbitmq-server start rabbitMQ server

Redis

Use redis-cli

Command Description
KEYS * Returns all keys
SET signature-request:lastProcessedBlock 1234 Set key to hold the string value.
GET signature-request:lastProcessedBlock Get the key value.
DEL signature-request:lastProcessedBlock Removes the specified key.
FLUSHALL Delete all the keys in all existing databases.
redis-cli ping check if redis is running.
redis-server start redis server.

Testing

npm test

E2E tests

See the E2E README for instructions.

Notice: for docker-based installations do not forget to add docker-compose exec bridge_affirmation before the test commands listed below.

Native-to-ERC20 Mode Testing

When running the processes, the following commands can be used to test functionality.

  • To send deposits to a home contract run node scripts/native_to_erc20/sendHome.js <tx num>, where <tx num> is how many tx will be sent out to deposit.

  • To send withdrawals to a foreign contract run node scripts/native_to_erc20/sendForeign.js <tx num>, where <tx num> is how many tx will be sent out to withdraw.

ERC20-to-ERC20 Mode Testing

  • To deposit from a Foreign to a Home contract run node scripts/erc20_to_erc20/sendForeign.js <tx num>.

  • To make withdrawal to Home from a Foreign contract run node scripts/erc20_to_erc20/sendHome.js <tx num>.

ERC20-to-Native Mode Testing

  • To deposit from a Foreign to a Home contract run node scripts/erc20_to_native/sendForeign.js <tx num>.

  • To make withdrawal to Home from a Foreign contract run node scripts/erc20_to_native/sendHome.js <tx num>.

Configuration parameters for testing

Variable Description
HOME_RPC_URL The HTTPS URL(s) used to communicate to the RPC nodes in the Home network.
FOREIGN_RPC_URL The HTTPS URL(s) used to communicate to the RPC nodes in the Foreign network.
USER_ADDRESS An account - the current owner of coins/tokens.
USER_ADDRESS_PRIVATE_KEY A private key belonging to the account.
HOME_BRIDGE_ADDRESS Address of the bridge in the Home network to send transactions.
HOME_MIN_AMOUNT_PER_TX Value (in eth or tokens) to be sent in one transaction for the Home network. This should be greater than or equal to the value specified in the poa-bridge-contracts/deploy/.env file. The default value in that file is 500000000000000000, which is equivalent to 0.5.
HOME_TEST_TX_GAS_PRICE The gas price (in Wei) that is used to send transactions in the Home network .
FOREIGN_BRIDGE_ADDRESS Address of the bridge in the Foreign network to send transactions.
FOREIGN_MIN_AMOUNT_PER_TX Value (in eth or tokens) to be sent in one transaction for the Foreign network. This should be greater than or equal to the value specified in the poa-bridge-contracts/deploy/.env file. The default value in that file is 500000000000000000, which is equivalent to 0.5.
FOREIGN_TEST_TX_GAS_PRICE The gas price (in Wei) that is used to send transactions in the Foreign network .

Contributing

See the CONTRIBUTING document for contribution, testing and pull request protocol.

License

License: LGPL v3.0

This project is licensed under the GNU Lesser General Public License v3.0. See the LICENSE file for details.

References