Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2a6a64637 | ||
|
|
8d4eb86a19 | ||
|
|
cc6afb3736 | ||
|
|
84ecfc30d9 | ||
|
|
5d770e8607 | ||
|
|
db89d1c12e | ||
|
|
4fd4ac3d73 | ||
|
|
cbd9d607ce | ||
|
|
346fa1e732 | ||
|
|
f6fa83d7ea | ||
|
|
1564ccc580 | ||
|
|
7a54e584d5 | ||
|
|
1d79cf82f3 | ||
|
|
9884b4b424 | ||
|
|
d577a71096 | ||
|
|
803f0074e6 | ||
|
|
e288df39dc | ||
|
|
8d0e70359f | ||
|
|
ab8270ee7c | ||
|
|
6b55c54497 | ||
|
|
114f62da7b | ||
|
|
8b010f887d | ||
|
|
d593577ace | ||
|
|
4e2971f880 | ||
|
|
54858ae597 | ||
|
|
443474e802 | ||
|
|
677bc50519 | ||
|
|
9af253b83e | ||
|
|
01186d6aa8 | ||
|
|
dd9add50a0 | ||
|
|
c39e81f97d | ||
|
|
f72ca7d38f | ||
|
|
b4ec04173c | ||
|
|
6aafce052c | ||
|
|
244306f0bf | ||
|
|
2093ff2d7e | ||
|
|
76daf5a436 | ||
|
|
c865198290 | ||
|
|
055a444fae | ||
|
|
2be0e9f363 | ||
|
|
52239a9506 | ||
|
|
f3419cbec4 | ||
|
|
40be5a5f8e | ||
|
|
a2e9dae43d | ||
|
|
414fe9cb65 | ||
|
|
272dc142de | ||
|
|
984b238279 | ||
|
|
93ebcc049e | ||
|
|
b532c8bad0 | ||
|
|
9c8259700b | ||
|
|
c66923827b | ||
|
|
c94b68ab5b | ||
|
|
d3653bc31f | ||
|
|
c9d100491c | ||
|
|
5c92cf50d7 | ||
|
|
5d22ad1ecb | ||
|
|
110b9accc9 | ||
|
|
82a3bdd86b | ||
|
|
9b3c65f2c4 | ||
|
|
8ea6c716ea | ||
|
|
3e09fe890e | ||
|
|
0b183fb861 | ||
|
|
811e8da6b5 | ||
|
|
54f6fb5835 | ||
|
|
208cfafa95 | ||
|
|
2e89d7724a | ||
|
|
b88dd66cac | ||
|
|
054225d348 | ||
|
|
cf3aa56929 | ||
|
|
1da1acbfbe | ||
|
|
3f27b98b8b | ||
|
|
4281adfd95 | ||
|
|
319b493568 | ||
|
|
4e5e21541e | ||
|
|
86fe89d3ec | ||
|
|
47a0a7c0f5 | ||
|
|
dcf83ffb55 | ||
|
|
4c5df11fd3 | ||
|
|
b254df7c25 | ||
|
|
11c1595dd6 | ||
|
|
59564bd600 | ||
|
|
f1d24f0e2c | ||
|
|
68a2b5be90 | ||
|
|
1da7340fb9 | ||
|
|
26ff22ac7e | ||
|
|
1737a1d6a2 | ||
|
|
8629aff80d | ||
|
|
091e80e5e9 | ||
|
|
c5cad3c2d8 | ||
|
|
8597e13952 | ||
|
|
c9ed138326 | ||
|
|
8f47c93369 | ||
|
|
14b5968efa | ||
|
|
ca1cf80615 | ||
|
|
b1721881f5 | ||
|
|
ae6692abcd | ||
|
|
3be812d55f | ||
|
|
a466fe57dc | ||
|
|
9e5f185bce | ||
|
|
81a936af45 | ||
|
|
4cb2df32e3 | ||
|
|
2b3b2d41ce | ||
|
|
2f30a42748 | ||
|
|
a669ea622c | ||
|
|
404b1d4959 | ||
|
|
9285ac534e | ||
|
|
1dba07623d | ||
|
|
98e2dc9c0d | ||
|
|
3a3ebf9747 | ||
|
|
f8d8a2d55b | ||
|
|
d7edbb2281 | ||
|
|
03ab7b44b6 | ||
|
|
c86e956fa0 | ||
|
|
fddeb6869f | ||
|
|
ff20521cce | ||
|
|
c5b1aa1424 | ||
|
|
22937a9b0e | ||
|
|
23d109b84c | ||
|
|
7b5b240735 | ||
|
|
13d49de5c6 | ||
|
|
7e937bdefa | ||
|
|
7607a7fdb1 | ||
|
|
07968c347f | ||
|
|
25ff5888ba | ||
|
|
57a9417afc | ||
|
|
c00139202d | ||
|
|
8480b4d499 | ||
|
|
a11211d813 | ||
|
|
cc10335d99 | ||
|
|
505aa3f261 | ||
|
|
2159fc1a57 | ||
|
|
164f21d9eb | ||
|
|
1013f69a8b | ||
|
|
563fbe5773 | ||
|
|
70dc09935c | ||
|
|
369b90a6d5 | ||
|
|
e5df7ec37d | ||
|
|
0d047d49ef | ||
|
|
822fa918e2 | ||
|
|
baf65b7001 | ||
|
|
94a6a5ad74 | ||
|
|
5ab299089f |
@@ -1,88 +1,289 @@
|
||||
version: 2
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
tokenbridge-orb:
|
||||
commands:
|
||||
install-chrome:
|
||||
steps:
|
||||
- run:
|
||||
name: Update dpkg
|
||||
command: |
|
||||
sudo apt-get clean
|
||||
sudo apt-get update
|
||||
sudo apt-get install dpkg
|
||||
- run:
|
||||
name: Install Chrome
|
||||
command: |
|
||||
wget -O chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo dpkg -i chrome.deb
|
||||
install-node:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Node
|
||||
command: |
|
||||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
nvm install 10.16.3 && nvm alias default 10.16.3
|
||||
|
||||
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
|
||||
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||
install-yarn:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Yarn
|
||||
command: |
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
sudo apt-get update && sudo apt-get -y install yarn
|
||||
yarn-install-cached-on-machine:
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: Restore Machine Yarn Package Cache
|
||||
keys:
|
||||
- yarn-machine-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Install npm dependencies using Yarn
|
||||
command: nvm use default; yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Machine Yarn Package Cache
|
||||
key: yarn-machine-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
wait-for-oracle:
|
||||
parameters:
|
||||
redis-key:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: Install redis tools
|
||||
command: sudo apt-get install -y redis-tools
|
||||
- run:
|
||||
name: Wait for the Oracle to start
|
||||
command: |
|
||||
set +e
|
||||
i=0
|
||||
while [[ $(redis-cli GET << parameters.redis-key >> ) ]]; do
|
||||
((i++))
|
||||
if [ "$i" -gt 30 ]
|
||||
then
|
||||
exit -1
|
||||
fi
|
||||
|
||||
echo "Sleeping..."
|
||||
sleep 3
|
||||
done
|
||||
executors:
|
||||
docker-node:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
machine-with-docker-caching:
|
||||
machine:
|
||||
image: circleci/classic:latest
|
||||
docker_layer_caching: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
initialize:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- restore_cache:
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run: git submodule status > submodule.status
|
||||
- restore_cache:
|
||||
name: Restore contracts submodule with compiled contracts
|
||||
keys:
|
||||
- contracts-{{ checksum "submodule.status" }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- run: touch install_deploy.log; test -d contracts/build/contracts || yarn install:deploy &> install_deploy.log
|
||||
- store_artifacts:
|
||||
path: install_deploy.log
|
||||
- run: test -d contracts/build/contracts || yarn compile:contracts
|
||||
- save_cache:
|
||||
name: Save contracts submodule with compiled contracts
|
||||
key: contracts-{{ checksum "submodule.status" }}
|
||||
paths:
|
||||
- contracts
|
||||
- save_cache:
|
||||
name: Save initialized project for subsequent jobs
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/project
|
||||
initialize-root:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: sudo su - -c 'export CI=true && cd /home/circleci/project && yarn initialize && yarn test'
|
||||
build:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn run build
|
||||
lint:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- run: yarn compile:contracts
|
||||
- restore_cache:
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn run lint
|
||||
ansible-lint:
|
||||
docker:
|
||||
- image: particlekit/ansible-lint
|
||||
steps:
|
||||
- checkout
|
||||
- run: ./deployment/lint.sh
|
||||
test:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
environment:
|
||||
HOME_RPC_URL: http://example.com
|
||||
FOREIGN_RPC_URL: http://example.com
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- run: yarn compile:contracts
|
||||
- restore_cache:
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn run test
|
||||
oracle-e2e:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- setup_remote_docker
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: yarn run oracle-e2e
|
||||
ui-e2e:
|
||||
machine:
|
||||
image: circleci/classic:latest
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: |
|
||||
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
|
||||
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||
- run: nvm install 11.4.0 && nvm alias default 11.4.0
|
||||
- run: curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
- run: echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
- run: sudo apt-get update && sudo apt-get install yarn
|
||||
- run: wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
- run: sudo dpkg -i google-chrome-stable_current_amd64.deb
|
||||
- tokenbridge-orb/install-node
|
||||
- tokenbridge-orb/install-yarn
|
||||
- tokenbridge-orb/install-chrome
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- tokenbridge-orb/yarn-install-cached-on-machine
|
||||
- run: yarn run ui-e2e
|
||||
cover:
|
||||
docker:
|
||||
- image: circleci/node:10.15
|
||||
monitor-e2e:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: ./monitor-e2e/run-tests.sh
|
||||
cover:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn workspace ui run coverage
|
||||
- run: yarn workspace ui run coveralls
|
||||
deployment-oracle:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh oracle
|
||||
no_output_timeout: 40m
|
||||
deployment-ui:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh ui
|
||||
no_output_timeout: 40m
|
||||
deployment-monitor:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh monitor
|
||||
no_output_timeout: 40m
|
||||
deployment-repo:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- tokenbridge-orb/install-node
|
||||
- tokenbridge-orb/install-yarn
|
||||
- tokenbridge-orb/yarn-install-cached-on-machine
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh repo
|
||||
no_output_timeout: 40m
|
||||
|
||||
ultimate:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
parameters:
|
||||
scenario-name:
|
||||
description: "Molecule scenario name used to create the infrastructure"
|
||||
type: string
|
||||
redis-key:
|
||||
description: "Redis key checked for non-emptiness to assert if Oracle is running"
|
||||
type: string
|
||||
ui-e2e-grep:
|
||||
description: "Mocha grep string used to run ui-e2e tests specific to given type of bridge"
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- tokenbridge-orb/install-node
|
||||
- tokenbridge-orb/install-chrome
|
||||
- tokenbridge-orb/install-yarn
|
||||
- tokenbridge-orb/yarn-install-cached-on-machine
|
||||
- run:
|
||||
name: Prepare the infrastructure
|
||||
command: e2e-commons/up.sh deploy << parameters.scenario-name >>
|
||||
no_output_timeout: 50m
|
||||
- tokenbridge-orb/wait-for-oracle:
|
||||
redis-key: << parameters.redis-key >>
|
||||
- run:
|
||||
name: Run the ui-e2e tests
|
||||
command: |
|
||||
nvm use default;
|
||||
node ./e2e-commons/scripts/blocks.js &
|
||||
cd ui-e2e; yarn mocha -g "<< parameters.ui-e2e-grep >>" -b ./test.js
|
||||
workflows:
|
||||
version: 2
|
||||
coverage:
|
||||
tokenbridge:
|
||||
jobs:
|
||||
- cover:
|
||||
- initialize
|
||||
- initialize-root:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- build:
|
||||
requires:
|
||||
- initialize
|
||||
- lint:
|
||||
requires:
|
||||
- initialize
|
||||
- test:
|
||||
requires:
|
||||
- initialize
|
||||
- cover:
|
||||
requires:
|
||||
- initialize
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
tokenbridge:
|
||||
jobs:
|
||||
- build
|
||||
- lint
|
||||
- ansible-lint
|
||||
- test
|
||||
- oracle-e2e
|
||||
- ui-e2e
|
||||
- monitor-e2e
|
||||
- deployment-oracle
|
||||
- deployment-ui
|
||||
- deployment-monitor
|
||||
- deployment-repo
|
||||
- ultimate:
|
||||
name: "ultimate: native to erc"
|
||||
scenario-name: native-to-erc
|
||||
redis-key: native-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "NATIVE TO ERC"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to native"
|
||||
scenario-name: erc-to-native
|
||||
redis-key: erc-native-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO NATIVE"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to erc"
|
||||
scenario-name: erc-to-erc
|
||||
redis-key: erc-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO ERC"
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,6 +43,8 @@ hosts
|
||||
Vagrantfile
|
||||
vagrant-hosts.yml
|
||||
.vagrant
|
||||
deployment/venv
|
||||
__pycache__
|
||||
|
||||
#monitor
|
||||
monitor/responses/*
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "contracts"]
|
||||
path = contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
url = https://github.com/poanetwork/tokenbridge-contracts.git
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
"printWidth": 120,
|
||||
"bracketSpacing": true
|
||||
}
|
||||
|
||||
71
CONFIGURATION.md
Normal file
71
CONFIGURATION.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Configuration
|
||||
|
||||
## Common configuration
|
||||
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
COMMON_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)
|
||||
COMMON_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)
|
||||
COMMON_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"
|
||||
COMMON_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"
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_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
|
||||
COMMON_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 `COMMON_HOME_GAS_PRICE_SUPPLIER_URL` is not used. | `instant` / `fast` / `standard` / `slow`
|
||||
COMMON_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
|
||||
COMMON_HOME_GAS_PRICE_FACTOR | A value that will multiply the gas price of the oracle to convert it to gwei. If the oracle API returns gas prices in gwei then this can be set to `1`. Also, it could be used to intentionally pay more gas than suggested by the oracle to guarantee the transaction verification. E.g. `1.25` or `1.5`. | integer
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_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
|
||||
COMMON_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 `COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL`is not used. | `instant` / `fast` / `standard` / `slow`
|
||||
COMMON_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
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR | A value that will multiply the gas price of the oracle to convert it to gwei. If the oracle API returns gas prices in gwei then this can be set to `1`. Also, it could be used to intentionally pay more gas than suggested by the oracle to guarantee the transaction verification. E.g. `1.25` or `1.5`. | integer
|
||||
|
||||
|
||||
## Oracle configuration
|
||||
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
ORACLE_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
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC | **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`
|
||||
ORACLE_HOME_RPC_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
|
||||
ORACLE_FOREIGN_RPC_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
|
||||
ORACLE_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
|
||||
ORACLE_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
|
||||
ORACLE_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
|
||||
ORACLE_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
|
||||
ORACLE_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
|
||||
ORACLE_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
|
||||
ORACLE_LOG_LEVEL | Set the level of details in the logs. | `trace` / `debug` / `info` / `warn` / `error` / `fatal`
|
||||
ORACLE_MAX_PROCESSING_TIME | The workers processes will be killed if this amount of time (in milliseconds) is elapsed 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
|
||||
ORACLE_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"
|
||||
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
|
||||
|
||||
|
||||
## UI configuration
|
||||
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
UI_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network. | string
|
||||
UI_DESCRIPTION | The meta description for the deployed bridge page. | string
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME | name of the home native coin | string
|
||||
UI_HOME_NETWORK_DISPLAY_NAME | name to be displayed for home network | string
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME | name to be displayed for foreign network | string
|
||||
UI_HOME_WITHOUT_EVENTS | `true` if home network doesn't support events | true/false
|
||||
UI_FOREIGN_WITHOUT_EVENTS | `true` if foreign network doesn't support events | true/false
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE | template link to transaction on home explorer. `%s` will be replaced by transaction hash | URL template
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE | template link to transaction on foreign explorer. `%s` will be replaced by transaction hash | URL template
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE | template link to address on home explorer. `%s` will be replaced by address | URL template
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explorer. `%s` will be replaced by address | URL template
|
||||
UI_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
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer
|
||||
UI_PORT | The port for the UI app. | integer
|
||||
UI_STYLES | The set of styles to render the bridge UI page. Currently only `classic` is implemented | classic
|
||||
|
||||
|
||||
## Monitor configuration
|
||||
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
MONITOR_HOME_START_BLOCK | The app will monitor transactions starting from this block. | integer
|
||||
MONITOR_FOREIGN_START_BLOCK | The app will monitor transactions starting from this block. | integer
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT | Average gas usage of a transaction sent by a validator, it is used to estimate the number of transaction that can be paid by the validator. | integer
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by a validator, it is used to estimate the number of transaction that can be paid by the validator. | integer
|
||||
MONITOR_TX_NUMBER_THRESHOLD | If estimated number of transaction is equal to or below this value, the monitor will report that the validator has less funds than it is required. | integer
|
||||
MONITOR_PORT | The port for the Monitor. | integer
|
||||
@@ -2,17 +2,12 @@ FROM node:10
|
||||
|
||||
WORKDIR /mono
|
||||
COPY package.json .
|
||||
COPY oracle/package.json ./oracle/
|
||||
COPY oracle-e2e/package.json ./oracle-e2e/
|
||||
COPY ui/package.json ./ui/
|
||||
COPY ui/lib/web3-eth/index.js ./ui/lib/web3-eth/index.js
|
||||
COPY ui-e2e/package.json ./ui-e2e/
|
||||
COPY monitor/package.json ./monitor/
|
||||
COPY monitor-e2e/package.json ./monitor-e2e/
|
||||
COPY contracts/package.json ./contracts/
|
||||
COPY ui/lib/web3-eth/index.js ./ui/lib/web3-eth/index.js
|
||||
|
||||
COPY yarn.lock .
|
||||
RUN yarn install
|
||||
RUN yarn install --frozen-lockfile
|
||||
COPY ./contracts ./contracts
|
||||
RUN yarn install:deploy
|
||||
RUN yarn compile:contracts
|
||||
|
||||
60
README.md
60
README.md
@@ -3,15 +3,15 @@
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
|
||||
# Tokenbridge
|
||||
Welcome to the **POA Token Bridge** monorepository!
|
||||
Welcome to the **POA TokenBridge** monorepository!
|
||||
|
||||
Please note that this repository as a **work in progress**.
|
||||
|
||||
## Overview
|
||||
|
||||
The POA Token Bridge allows users to transfer assets between two chains in the Ethereum ecosystem. It is composed of several elements which are contained within this monorepository.
|
||||
The POA TokenBridge allows users to transfer assets between two chains in the Ethereum ecosystem. It is composed of several elements which are contained within this monorepository.
|
||||
|
||||
For a complete picture of the POA Token Bridge functionality, it is useful to explore each subrepository.
|
||||
For a complete picture of the POA TokenBridge functionality, it is useful to explore each subrepository.
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -24,9 +24,23 @@ Sub-repositories maintained within this monorepo are listed below.
|
||||
| [Monitor](monitor/README.md) | Tool for checking balances and unprocessed events in bridged networks. |
|
||||
| [Deployment](deployment/README.md) | Ansible playbooks for deploying cross-chain bridges. |
|
||||
| [Oracle-E2E](oracle-e2e/README.md) | End to end tests for the Oracle |
|
||||
| [Monitor-E2E](monitor-e2e/README.md) | End to end tests for the Monitor |
|
||||
| [UI-E2E](ui-e2e/README.md) | End to end tests for the UI |
|
||||
| [Deployment-E2E](deployment-e2e/README.md) | End to end tests for the Deployment |
|
||||
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
|
||||
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
|
||||
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
|
||||
## Available deployments
|
||||
|
||||
| **Launched by POA** | **Launched by 3rd parties** |
|
||||
| ---------- | ---------- |
|
||||
| [POA20 Bridge](https://bridge.poa.net/) | [Ocean TokenBridge](https://bridge.oceanprotocol.com/) |
|
||||
| [xDai Bridge](https://dai-bridge.poa.network/) | [Thunder bridge](https://ui.stormdapps.com/) |
|
||||
| [WETC Bridge](https://wetc.app/) | [Volta TokenBridge](https://vt.volta.bridge.eth.events/) & [DAI bridge to Volta Chain](https://dai.volta.bridge.eth.events/) |
|
||||
| | [Artis Brige](https://bridge.artis.network/) |
|
||||
| | [Tenda bridge](https://bridge-mainnet.tenda.network) & [xDai-to-Tenda bridge](https://bridge-xdai.tenda.network/) |
|
||||
|
||||
## Network Definitions
|
||||
|
||||
@@ -38,34 +52,23 @@ Additionally there are [Smart Contracts](https://github.com/poanetwork/poa-bridg
|
||||
|
||||
## Operational Modes
|
||||
|
||||
The POA TokenBridge provides three operational modes:
|
||||
The POA TokenBridge provides four operational modes:
|
||||
|
||||
- [x] `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. **More Information: [POA-to-POA20 Bridge](https://medium.com/poa-network/introducing-poa-bridge-and-poa20-55d8b78058ac)**
|
||||
- [x] `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". **More Information: [ERC20-to-ERC20](https://medium.com/poa-network/introducing-the-erc20-to-erc20-tokenbridge-ce266cc1a2d0)**
|
||||
- [x] `ERC20-to-Native`: Pre-existing **tokens** in the Foreign network are locked and **coins** are minted in the `Home` network. In this mode, the Home network consensus engine invokes [Parity's Block Reward contract](https://wiki.parity.io/Block-Reward-Contract.html) to mint coins per the bridge contract request. **More Information: [xDai Chain](https://medium.com/poa-network/poa-network-partners-with-makerdao-on-xdai-chain-the-first-ever-usd-stable-blockchain-65a078c41e6a)**
|
||||
- [x] `Arbitrary-Message`: Transfer arbitrary data between two networks as so the data could be interpreted as an arbitrary contract method invocation.
|
||||
|
||||
## Initializing the monorepository
|
||||
|
||||
Clone the repository with submodules:
|
||||
Clone the repository:
|
||||
```bash
|
||||
git clone --recursive https://github.com/poanetwork/tokenbridge
|
||||
|
||||
# or initialize submodules if already cloned without --recursive option:
|
||||
git submodule update --init
|
||||
git clone https://github.com/poanetwork/tokenbridge
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
Initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
```
|
||||
yarn install && yarn install:deploy
|
||||
```
|
||||
|
||||
_**Note**: The installation should be performed with an unprivileged Linux account or with the following flag: `yarn install --unsafe-perm`. [More information](https://docs.npmjs.com/misc/scripts#user)_
|
||||
|
||||
Compile the Smart Contracts
|
||||
|
||||
```
|
||||
yarn compile:contracts
|
||||
yarn initialize
|
||||
```
|
||||
|
||||
## Linting
|
||||
@@ -76,14 +79,6 @@ Running linter for all JS projects:
|
||||
yarn lint
|
||||
```
|
||||
|
||||
Running linter for all Ansible playbooks:
|
||||
|
||||
- [ansible-lint](https://github.com/ansible/ansible-lint) is required
|
||||
|
||||
```
|
||||
yarn ansible-lint
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Running tests for all projects:
|
||||
@@ -92,10 +87,15 @@ Running tests for all projects:
|
||||
yarn test
|
||||
```
|
||||
|
||||
Additionaly there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [UI](ui-e2e/README.md).
|
||||
Additionally there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [UI](ui-e2e/README.md).
|
||||
|
||||
For details on building, running and developing please refer to respective READMEs in sub-repositories.
|
||||
|
||||
## Building, running and deploying
|
||||
|
||||
Please refer to the instructions in sub-directories.
|
||||
Configuration details are available [here](./CONFIGURATION.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [CONTRIBUTING](CONTRIBUTING.md) document for contribution, testing and pull request protocol.
|
||||
|
||||
Binary file not shown.
13
commons/.eslintrc
Normal file
13
commons/.eslintrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"../.eslintrc"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-expressions": "off",
|
||||
"import/no-extraneous-dependencies": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
2
commons/README.md
Normal file
2
commons/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# POA TokenBridge / Commons
|
||||
Interfaces, constants and utilities shared between the sub-repositories
|
||||
96
commons/abis.js
Normal file
96
commons/abis.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const HOME_NATIVE_TO_ERC_ABI = require('../contracts/build/contracts/HomeBridgeNativeToErc').abi
|
||||
const FOREIGN_NATIVE_TO_ERC_ABI = require('../contracts/build/contracts/ForeignBridgeNativeToErc').abi
|
||||
const HOME_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeBridgeErcToErc').abi
|
||||
const FOREIGN_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignBridgeErc677ToErc677').abi
|
||||
const HOME_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/HomeBridgeErcToNative').abi
|
||||
const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignBridgeErcToNative').abi
|
||||
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
|
||||
const ERC677_ABI = require('../contracts/build/contracts/ERC677').abi
|
||||
const ERC677_BRIDGE_TOKEN_ABI = require('../contracts/build/contracts/ERC677BridgeToken').abi
|
||||
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/IBlockReward').abi
|
||||
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
|
||||
const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/RewardableValidators').abi
|
||||
const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
|
||||
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
|
||||
const BOX_ABI = require('../contracts/build/contracts/Box').abi
|
||||
|
||||
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
|
||||
const { BRIDGE_MODES } = require('./constants')
|
||||
|
||||
const ERC20_BYTES32_ABI = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'name',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'symbol',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
function getBridgeABIs(bridgeMode) {
|
||||
let HOME_ABI = null
|
||||
let FOREIGN_ABI = null
|
||||
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC) {
|
||||
HOME_ABI = HOME_NATIVE_TO_ERC_ABI
|
||||
FOREIGN_ABI = FOREIGN_NATIVE_TO_ERC_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
|
||||
HOME_ABI = HOME_ERC_TO_ERC_ABI
|
||||
FOREIGN_ABI = FOREIGN_ERC_TO_ERC_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
|
||||
HOME_ABI = HOME_ERC_TO_NATIVE_ABI
|
||||
FOREIGN_ABI = FOREIGN_ERC_TO_NATIVE_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1) {
|
||||
HOME_ABI = HOME_V1_ABI
|
||||
FOREIGN_ABI = FOREIGN_V1_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
HOME_ABI = HOME_AMB_ABI
|
||||
FOREIGN_ABI = FOREIGN_AMB_ABI
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
|
||||
}
|
||||
|
||||
return { HOME_ABI, FOREIGN_ABI }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getBridgeABIs,
|
||||
HOME_NATIVE_TO_ERC_ABI,
|
||||
FOREIGN_NATIVE_TO_ERC_ABI,
|
||||
HOME_ERC_TO_ERC_ABI,
|
||||
FOREIGN_ERC_TO_ERC_ABI,
|
||||
HOME_ERC_TO_NATIVE_ABI,
|
||||
FOREIGN_ERC_TO_NATIVE_ABI,
|
||||
ERC20_ABI,
|
||||
ERC677_ABI,
|
||||
ERC677_BRIDGE_TOKEN_ABI,
|
||||
BLOCK_REWARD_ABI,
|
||||
BRIDGE_VALIDATORS_ABI,
|
||||
REWARDABLE_VALIDATORS_ABI,
|
||||
HOME_V1_ABI,
|
||||
FOREIGN_V1_ABI,
|
||||
ERC20_BYTES32_ABI,
|
||||
HOME_AMB_ABI,
|
||||
FOREIGN_AMB_ABI,
|
||||
BOX_ABI
|
||||
}
|
||||
27
commons/constants.js
Normal file
27
commons/constants.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const BRIDGE_MODES = {
|
||||
NATIVE_TO_ERC: 'NATIVE_TO_ERC',
|
||||
ERC_TO_ERC: 'ERC_TO_ERC',
|
||||
ERC_TO_NATIVE: 'ERC_TO_NATIVE',
|
||||
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1',
|
||||
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE'
|
||||
}
|
||||
|
||||
const ERC_TYPES = {
|
||||
ERC20: 'ERC20',
|
||||
ERC677: 'ERC677'
|
||||
}
|
||||
|
||||
const FEE_MANAGER_MODE = {
|
||||
ONE_DIRECTION: 'ONE_DIRECTION',
|
||||
BOTH_DIRECTIONS: 'BOTH_DIRECTIONS',
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||
|
||||
module.exports = {
|
||||
BRIDGE_MODES,
|
||||
ERC_TYPES,
|
||||
FEE_MANAGER_MODE,
|
||||
ZERO_ADDRESS
|
||||
}
|
||||
11
commons/index.js
Normal file
11
commons/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const constants = require('./constants')
|
||||
const abis = require('./abis')
|
||||
const utils = require('./utils')
|
||||
const message = require('./message')
|
||||
|
||||
module.exports = {
|
||||
...constants,
|
||||
...abis,
|
||||
...utils,
|
||||
...message
|
||||
}
|
||||
27
commons/message.js
Normal file
27
commons/message.js
Normal file
@@ -0,0 +1,27 @@
|
||||
function strip0x(input) {
|
||||
return input.replace(/^0x/, '')
|
||||
}
|
||||
|
||||
function addTxHashToData({ encodedData, transactionHash }) {
|
||||
return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2)
|
||||
}
|
||||
|
||||
function parseAMBMessage(message) {
|
||||
message = strip0x(message)
|
||||
|
||||
const txHash = `0x${message.slice(0, 64)}`
|
||||
const sender = `0x${message.slice(64, 104)}`
|
||||
const executor = `0x${message.slice(104, 144)}`
|
||||
|
||||
return {
|
||||
sender,
|
||||
executor,
|
||||
txHash
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addTxHashToData,
|
||||
parseAMBMessage,
|
||||
strip0x
|
||||
}
|
||||
17
commons/package.json
Normal file
17
commons/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "commons",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"test": "NODE_ENV=test mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"web3-utils": "1.0.0-beta.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bn-chai": "^1.0.1",
|
||||
"chai": "^4.2.0"
|
||||
}
|
||||
}
|
||||
12
commons/test/constants.test.js
Normal file
12
commons/test/constants.test.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const { expect } = require('chai')
|
||||
const { BRIDGE_MODES, ERC_TYPES } = require('../constants')
|
||||
|
||||
describe('constants', () => {
|
||||
it('should contain correct number of bridge types', () => {
|
||||
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(5)
|
||||
})
|
||||
|
||||
it('should contain correct number of erc types', () => {
|
||||
expect(Object.keys(ERC_TYPES).length).to.be.equal(2)
|
||||
})
|
||||
})
|
||||
162
commons/test/gas.js
Normal file
162
commons/test/gas.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const { expect } = require('chai')
|
||||
const Web3Utils = require('web3-utils')
|
||||
const { gasPriceWithinLimits, normalizeGasPrice } = require('..')
|
||||
|
||||
const GAS_PRICE_BOUNDARIES = {
|
||||
MIN: 1,
|
||||
MAX: 250
|
||||
}
|
||||
|
||||
describe('gas', () => {
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 30
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 300
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('gasPriceWithinLimits', () => {
|
||||
it('should return gas price if gas price is between boundaries', () => {
|
||||
// given
|
||||
const minGasPrice = 1
|
||||
const middleGasPrice = 10
|
||||
const maxGasPrice = 250
|
||||
|
||||
// when
|
||||
const minGasPriceWithinLimits = gasPriceWithinLimits(minGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
const middleGasPriceWithinLimits = gasPriceWithinLimits(middleGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
const maxGasPriceWithinLimits = gasPriceWithinLimits(maxGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// then
|
||||
expect(minGasPriceWithinLimits).to.equal(minGasPrice)
|
||||
expect(middleGasPriceWithinLimits).to.equal(middleGasPrice)
|
||||
expect(maxGasPriceWithinLimits).to.equal(maxGasPrice)
|
||||
})
|
||||
|
||||
it('should return min limit if gas price is below min boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 0.5
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MIN)
|
||||
})
|
||||
|
||||
it('should return max limit if gas price is above max boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 260
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MAX)
|
||||
})
|
||||
|
||||
it('should return gas price if boundaries not provided', () => {
|
||||
// Given
|
||||
const initialGasPrice = 260
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(initialGasPrice)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
})
|
||||
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
})
|
||||
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
|
||||
it('should respect gas price max limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 4
|
||||
const maxInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MAX.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor, GAS_PRICE_BOUNDARIES).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(maxInWei)
|
||||
})
|
||||
|
||||
it('should respect gas price min limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 1
|
||||
const factor = 0.01
|
||||
const minInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MIN.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor, GAS_PRICE_BOUNDARIES).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(minInWei)
|
||||
})
|
||||
})
|
||||
})
|
||||
64
commons/test/getTokenType.js
Normal file
64
commons/test/getTokenType.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const { expect } = require('chai')
|
||||
const { getTokenType, ERC_TYPES } = require('..')
|
||||
|
||||
describe('getTokenType', () => {
|
||||
it('should return ERC677 if bridgeContract is equal to bridgeAddress', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.resolve(bridgeAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC677)
|
||||
})
|
||||
|
||||
it('should return ERC20 if bridgeContract is not equal to bridgeAddress', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.resolve('0xBFCb120F7B1de491262CA4D9D8Eba70438b6896E')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC20)
|
||||
})
|
||||
|
||||
it('should return ERC20 if bridgeContract is not present', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC20)
|
||||
})
|
||||
})
|
||||
69
commons/test/message.test.js
Normal file
69
commons/test/message.test.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const { BN } = require('web3-utils')
|
||||
const { expect } = require('chai').use(require('bn-chai')(BN))
|
||||
const { parseAMBMessage, strip0x, addTxHashToData } = require('../message')
|
||||
|
||||
describe('strip0x', () => {
|
||||
it('should remove 0x from input', () => {
|
||||
// Given
|
||||
const input = '0x12345'
|
||||
|
||||
// When
|
||||
const result = strip0x(input)
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal('12345')
|
||||
})
|
||||
it('should not modify input if 0x is not present', () => {
|
||||
// Given
|
||||
const input = '12345'
|
||||
|
||||
// When
|
||||
const result = strip0x(input)
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal(input)
|
||||
})
|
||||
})
|
||||
describe('addTxHashToData', () => {
|
||||
it('should add txHash to encoded data at position 2', () => {
|
||||
// Given
|
||||
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
|
||||
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
|
||||
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
|
||||
const msgDataType = '00'
|
||||
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
|
||||
const encodedData = `0x${strip0x(msgSender)}${strip0x(msgExecutor)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
const transactionHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const message = `0x${strip0x(transactionHash)}${strip0x(msgSender)}${strip0x(
|
||||
msgExecutor
|
||||
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
// When
|
||||
const result = addTxHashToData({ encodedData, transactionHash })
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal(message)
|
||||
})
|
||||
})
|
||||
describe('parseAMBMessage', () => {
|
||||
it('should parse data type 00', () => {
|
||||
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
|
||||
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
|
||||
const msgTxHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
|
||||
const msgDataType = '00'
|
||||
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
|
||||
const message = `0x${strip0x(msgTxHash)}${strip0x(msgSender)}${strip0x(
|
||||
msgExecutor
|
||||
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
// when
|
||||
const { sender, executor, txHash } = parseAMBMessage(message)
|
||||
|
||||
// then
|
||||
expect(sender).to.be.equal(msgSender)
|
||||
expect(executor).to.be.equal(msgExecutor)
|
||||
expect(txHash).to.be.equal(msgTxHash)
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import { processValidatorsEvents, parseValidatorEvent } from '../contract'
|
||||
const { expect } = require('chai')
|
||||
const { processValidatorsEvents, parseValidatorEvent } = require('..')
|
||||
|
||||
describe('parseValidatorEvent', () => {
|
||||
it('should parse ValidatorAdded event from v1', () => {
|
||||
@@ -14,8 +15,8 @@ describe('parseValidatorEvent', () => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
// Then
|
||||
expect(event.event).toBe('ValidatorAdded')
|
||||
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
expect(event.event).to.be.equal('ValidatorAdded')
|
||||
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
})
|
||||
it('should parse ValidatorAdded event', () => {
|
||||
// Given
|
||||
@@ -33,8 +34,8 @@ describe('parseValidatorEvent', () => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
// Then
|
||||
expect(event.event).toBe('ValidatorAdded')
|
||||
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
expect(event.event).to.be.equal('ValidatorAdded')
|
||||
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
})
|
||||
it('should parse ValidatorAdded event from rewardableValidators', () => {
|
||||
// Given
|
||||
@@ -52,8 +53,8 @@ describe('parseValidatorEvent', () => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
// Then
|
||||
expect(event.event).toBe('ValidatorAdded')
|
||||
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
expect(event.event).to.be.equal('ValidatorAdded')
|
||||
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
})
|
||||
it('should parse ValidatorRemoved event', () => {
|
||||
// Given
|
||||
@@ -71,8 +72,8 @@ describe('parseValidatorEvent', () => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
// Then
|
||||
expect(event.event).toBe('ValidatorRemoved')
|
||||
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
expect(event.event).to.be.equal('ValidatorRemoved')
|
||||
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
})
|
||||
it('should parse ValidatorRemoved event from v1', () => {
|
||||
// Given
|
||||
@@ -87,8 +88,8 @@ describe('parseValidatorEvent', () => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
// Then
|
||||
expect(event.event).toBe('ValidatorRemoved')
|
||||
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
expect(event.event).to.be.equal('ValidatorRemoved')
|
||||
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
|
||||
})
|
||||
})
|
||||
describe('processValidatorsEvents', () => {
|
||||
@@ -169,9 +170,9 @@ describe('processValidatorsEvents', () => {
|
||||
const validatorList = processValidatorsEvents(events)
|
||||
|
||||
// Then
|
||||
expect(validatorList.length).toBe(3)
|
||||
expect(validatorList[0]).toBe('0xCbd25A2a5708051747a052dBB1b291865Fc0e474')
|
||||
expect(validatorList[1]).toBe('0xBac68A86Cf596E3b124781E0bdbC47bb458bec62')
|
||||
expect(validatorList[2]).toBe('0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955')
|
||||
expect(validatorList.length).to.be.equal(3)
|
||||
expect(validatorList[0]).to.be.equal('0xCbd25A2a5708051747a052dBB1b291865Fc0e474')
|
||||
expect(validatorList[1]).to.be.equal('0xBac68A86Cf596E3b124781E0bdbC47bb458bec62')
|
||||
expect(validatorList[2]).to.be.equal('0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955')
|
||||
})
|
||||
})
|
||||
264
commons/utils.js
Normal file
264
commons/utils.js
Normal file
@@ -0,0 +1,264 @@
|
||||
const { toWei, toBN } = require('web3-utils')
|
||||
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
|
||||
const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
|
||||
|
||||
function decodeBridgeMode(bridgeModeHash) {
|
||||
switch (bridgeModeHash) {
|
||||
case '0x92a8d7fe':
|
||||
return BRIDGE_MODES.NATIVE_TO_ERC
|
||||
case '0xba4690f5':
|
||||
return BRIDGE_MODES.ERC_TO_ERC
|
||||
case '0x18762d46':
|
||||
return BRIDGE_MODES.ERC_TO_NATIVE
|
||||
case '0x2544fbb9':
|
||||
return BRIDGE_MODES.ARBITRARY_MESSAGE
|
||||
default:
|
||||
throw new Error(`Unrecognized bridge mode hash: '${bridgeModeHash}'`)
|
||||
}
|
||||
}
|
||||
|
||||
const decodeFeeManagerMode = managerModeHash => {
|
||||
switch (managerModeHash) {
|
||||
case '0xf2aed8f7':
|
||||
return FEE_MANAGER_MODE.ONE_DIRECTION
|
||||
case '0xd7de965f':
|
||||
return FEE_MANAGER_MODE.BOTH_DIRECTIONS
|
||||
default:
|
||||
throw new Error(`Unrecognized fee manager mode hash: '${managerModeHash}'`)
|
||||
}
|
||||
}
|
||||
|
||||
async function getBridgeMode(contract) {
|
||||
try {
|
||||
const bridgeModeHash = await contract.methods.getBridgeMode().call()
|
||||
return decodeBridgeMode(bridgeModeHash)
|
||||
} catch (e) {
|
||||
return BRIDGE_MODES.NATIVE_TO_ERC_V1
|
||||
}
|
||||
}
|
||||
|
||||
const getTokenType = async (bridgeTokenContract, bridgeAddress) => {
|
||||
try {
|
||||
const resultBridgeAddress = await bridgeTokenContract.methods.bridgeContract().call()
|
||||
if (resultBridgeAddress === bridgeAddress) {
|
||||
return ERC_TYPES.ERC677
|
||||
} else {
|
||||
return ERC_TYPES.ERC20
|
||||
}
|
||||
} catch (e) {
|
||||
return ERC_TYPES.ERC20
|
||||
}
|
||||
}
|
||||
|
||||
const getUnit = bridgeMode => {
|
||||
let unitHome = null
|
||||
let unitForeign = null
|
||||
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC) {
|
||||
unitHome = 'Native coins'
|
||||
unitForeign = 'Tokens'
|
||||
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
|
||||
unitHome = 'Tokens'
|
||||
unitForeign = 'Tokens'
|
||||
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
|
||||
unitHome = 'Native coins'
|
||||
unitForeign = 'Tokens'
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
|
||||
}
|
||||
|
||||
return { unitHome, unitForeign }
|
||||
}
|
||||
|
||||
const parseValidatorEvent = event => {
|
||||
if (
|
||||
event.event === undefined &&
|
||||
event.raw &&
|
||||
event.raw.topics &&
|
||||
(event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987' ||
|
||||
event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')
|
||||
) {
|
||||
const rawAddress = event.raw.topics.length > 1 ? event.raw.topics[1] : event.raw.data
|
||||
const address = '0x' + rawAddress.slice(26)
|
||||
event.event = 'ValidatorAdded'
|
||||
event.returnValues.validator = address
|
||||
} else if (
|
||||
event.event === undefined &&
|
||||
event.raw &&
|
||||
event.raw.topics &&
|
||||
event.raw.topics[0] === '0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1'
|
||||
) {
|
||||
const rawAddress = event.raw.data === '0x' ? event.raw.topics[1] : event.raw.data
|
||||
const address = '0x' + rawAddress.slice(26)
|
||||
event.event = 'ValidatorRemoved'
|
||||
event.returnValues.validator = address
|
||||
}
|
||||
}
|
||||
|
||||
const processValidatorsEvents = events => {
|
||||
const validatorList = new Set()
|
||||
events.forEach(event => {
|
||||
parseValidatorEvent(event)
|
||||
|
||||
if (event.event === 'ValidatorAdded') {
|
||||
validatorList.add(event.returnValues.validator)
|
||||
} else if (event.event === 'ValidatorRemoved') {
|
||||
validatorList.delete(event.returnValues.validator)
|
||||
}
|
||||
})
|
||||
|
||||
return Array.from(validatorList)
|
||||
}
|
||||
|
||||
const tryCall = async (method, fallbackValue) => {
|
||||
try {
|
||||
return await method.call()
|
||||
} catch (e) {
|
||||
return fallbackValue
|
||||
}
|
||||
}
|
||||
|
||||
const getDeployedAtBlock = async contract => tryCall(contract.methods.deployedAtBlock(), 0)
|
||||
|
||||
const getPastEvents = async (
|
||||
contract,
|
||||
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
|
||||
) => {
|
||||
let events
|
||||
try {
|
||||
events = await contract.getPastEvents(event, {
|
||||
...options,
|
||||
fromBlock,
|
||||
toBlock
|
||||
})
|
||||
} catch (e) {
|
||||
if (e.message.includes('query returned more than') && toBlock !== 'latest') {
|
||||
const middle = toBN(fromBlock)
|
||||
.add(toBlock)
|
||||
.divRound(toBN(2))
|
||||
const middlePlusOne = middle.add(toBN(1))
|
||||
|
||||
const firstHalfEvents = await getPastEvents(contract, {
|
||||
...options,
|
||||
event,
|
||||
fromBlock,
|
||||
toBlock: middle
|
||||
})
|
||||
const secondHalfEvents = await getPastEvents(contract, {
|
||||
...options,
|
||||
event,
|
||||
fromBlock: middlePlusOne,
|
||||
toBlock
|
||||
})
|
||||
events = [...firstHalfEvents, ...secondHalfEvents]
|
||||
} else {
|
||||
throw new Error(e)
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
const getValidatorList = async (address, eth, options) => {
|
||||
options.logger && options.logger.debug && options.logger.debug('getting validatorList')
|
||||
|
||||
const validatorsContract = new eth.Contract(REWARDABLE_VALIDATORS_ABI, address) // in monitor, BRIDGE_VALIDATORS_ABI was used
|
||||
const validators = await tryCall(validatorsContract.methods.validatorList(), [])
|
||||
|
||||
if (validators.length) {
|
||||
return validators
|
||||
}
|
||||
|
||||
options.logger && options.logger.debug && options.logger.debug('getting validatorsEvents')
|
||||
|
||||
const deployedAtBlock = await tryCall(validatorsContract.methods.deployedAtBlock(), 0)
|
||||
const fromBlock = options.fromBlock || Number(deployedAtBlock) || 0
|
||||
const toBlock = options.toBlock || 'latest'
|
||||
|
||||
const validatorsEvents = await getPastEvents(new eth.Contract([], address), {
|
||||
event: 'allEvents',
|
||||
fromBlock,
|
||||
toBlock,
|
||||
options: {}
|
||||
})
|
||||
|
||||
return processValidatorsEvents(validatorsEvents)
|
||||
}
|
||||
|
||||
const gasPriceWithinLimits = (gasPrice, limits) => {
|
||||
if (!limits) {
|
||||
return gasPrice
|
||||
}
|
||||
if (gasPrice < limits.MIN) {
|
||||
return limits.MIN
|
||||
} else if (gasPrice > limits.MAX) {
|
||||
return limits.MAX
|
||||
} else {
|
||||
return gasPrice
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
|
||||
let gasPrice = oracleGasPrice * factor
|
||||
gasPrice = gasPriceWithinLimits(gasPrice, limits)
|
||||
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
||||
}
|
||||
|
||||
// fetchFn has to be supplied (instead of just url to oracle),
|
||||
// because this utility function is shared between Browser and Node,
|
||||
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
|
||||
const gasPriceFromSupplier = async (fetchFn, options = {}) => {
|
||||
try {
|
||||
const response = await fetchFn()
|
||||
const json = await response.json()
|
||||
const oracleGasPrice = json[options.speedType]
|
||||
|
||||
if (!oracleGasPrice) {
|
||||
options.logger &&
|
||||
options.logger.error &&
|
||||
options.logger.error(`Response from Oracle didn't include gas price for ${options.speedType} type.`)
|
||||
return null
|
||||
}
|
||||
|
||||
const normalizedGasPrice = normalizeGasPrice(oracleGasPrice, options.factor, options.limits)
|
||||
|
||||
options.logger &&
|
||||
options.logger.debug &&
|
||||
options.logger.debug({ oracleGasPrice, normalizedGasPrice }, 'Gas price updated using the API')
|
||||
|
||||
return normalizedGasPrice
|
||||
} catch (e) {
|
||||
options.logger && options.logger.error && options.logger.error(`Gas Price API is not available. ${e.message}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const gasPriceFromContract = async (bridgeContract, options = {}) => {
|
||||
try {
|
||||
const gasPrice = await bridgeContract.methods.gasPrice().call()
|
||||
options.logger &&
|
||||
options.logger.debug &&
|
||||
options.logger.debug({ gasPrice }, 'Gas price updated using the contracts')
|
||||
return gasPrice
|
||||
} catch (e) {
|
||||
options.logger &&
|
||||
options.logger.error &&
|
||||
options.logger.error(`There was a problem getting the gas price from the contract. ${e.message}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
decodeBridgeMode,
|
||||
decodeFeeManagerMode,
|
||||
getBridgeMode,
|
||||
getTokenType,
|
||||
getUnit,
|
||||
parseValidatorEvent,
|
||||
processValidatorsEvents,
|
||||
getValidatorList,
|
||||
getPastEvents,
|
||||
getDeployedAtBlock,
|
||||
normalizeGasPrice,
|
||||
gasPriceFromSupplier,
|
||||
gasPriceFromContract,
|
||||
gasPriceWithinLimits
|
||||
}
|
||||
163
commons/v1Abis.js
Normal file
163
commons/v1Abis.js
Normal file
@@ -0,0 +1,163 @@
|
||||
const homeV1Abi = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorContract',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'Deposit',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'transactionHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'Withdraw',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'deployedAtBlock',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
const foreignViAbi = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorContract',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'transactionHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'Deposit',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'homeGasPrice',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'Withdraw',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'deployedAtBlock',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'erc677token',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
HOME_V1_ABI: homeV1Abi,
|
||||
FOREIGN_V1_ABI: foreignViAbi
|
||||
}
|
||||
Submodule contracts updated: bbb97a63c9...20d262702d
4
deployment-e2e/Dockerfile
Normal file
4
deployment-e2e/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM python:3.7-stretch
|
||||
RUN curl -fsSL https://get.docker.com | sh
|
||||
RUN pip3 install docker molecule==2.22rc1 molecule[docker] flake8
|
||||
WORKDIR mono/deployment-e2e
|
||||
36
deployment-e2e/README.md
Normal file
36
deployment-e2e/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# POA TokenBridge / Deployment Testing
|
||||
|
||||
The deployment playbooks are tested using [Molecule](https://molecule.readthedocs.io).
|
||||
|
||||
## Push remote branch
|
||||
|
||||
The deployment playbooks are cloning the monorepository on target hosts, using your current local git branch name. If the branch does not exists on remote, you need to push it.
|
||||
|
||||
```
|
||||
git push
|
||||
```
|
||||
|
||||
Alternatively, if there are no changes except the playbooks, you can use the `master` branch:
|
||||
|
||||
```
|
||||
CIRCLE_BRANCH=master ./molecule.sh <scenario_name>
|
||||
```
|
||||
|
||||
In this case `master` branch will be used as a codebase for Monitor, UI, Oracle and Contracts deployed by your local playbook.
|
||||
|
||||
## Run the tests
|
||||
|
||||
```
|
||||
CIRCLE_BRANCH=master ./molecule.sh <scenario_name>
|
||||
```
|
||||
|
||||
Available scenarios:
|
||||
|
||||
Scenario | Description
|
||||
--- | ---
|
||||
oracle | Deploys and checks standalone Oracle on Ubuntu host
|
||||
ui | Deploys and checks standalone UI on Ubuntu host
|
||||
|
||||
## Ultimate E2E tests
|
||||
|
||||
For information on the Ultimate tests, please refer to [Ultimate](../e2e-commons/ULTIMATE.md).
|
||||
12
deployment-e2e/docker-compose.yml
Normal file
12
deployment-e2e/docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
version: '3.0'
|
||||
services:
|
||||
molecule_runner:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deployment-e2e/Dockerfile
|
||||
restart: 'no'
|
||||
privileged: true
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ..:/mono
|
||||
9
deployment-e2e/molecule.sh
Executable file
9
deployment-e2e/molecule.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
cd $(dirname $0)
|
||||
set -e # exit when any command fails
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
docker-compose build && docker-compose run molecule_runner /bin/bash -c "molecule test --scenario-name $1"
|
||||
|
||||
shift # Shift all the parameters down by one
|
||||
done
|
||||
14
deployment-e2e/molecule/monitor/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/monitor/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
55
deployment-e2e/molecule/monitor/molecule.yml
Normal file
55
deployment-e2e/molecule/monitor/molecule.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: monitor-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- monitor
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
monitor-host:
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: monitor
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
54
deployment-e2e/molecule/monitor/tests/test_monitor.py
Normal file
54
deployment-e2e/molecule/monitor/tests/test_monitor.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('monitor')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("monitor_monitor_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("tokenbridge-monitor"),
|
||||
("rsyslog")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", [
|
||||
("/etc/rsyslog.d/33-monitor-docker.conf"),
|
||||
("/etc/rsyslog.d/38-monitor-remote-logging.conf")
|
||||
])
|
||||
def test_logging(host, filename):
|
||||
assert host.file(filename).exists
|
||||
assert host.file(filename).mode == 0o0644
|
||||
|
||||
|
||||
def test_home_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'grep -q -i "home"'
|
||||
)
|
||||
|
||||
|
||||
def test_foreign_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'grep -q -i "foreign"'
|
||||
)
|
||||
|
||||
|
||||
def test_no_error(host):
|
||||
assert host.run_expect(
|
||||
[1],
|
||||
'curl -s http://localhost:3003 | '
|
||||
'grep -i -q "error"'
|
||||
)
|
||||
14
deployment-e2e/molecule/oracle/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/oracle/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
56
deployment-e2e/molecule/oracle/molecule.yml
Normal file
56
deployment-e2e/molecule/oracle/molecule.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: oracle-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-host:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: oracle
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
38
deployment-e2e/molecule/oracle/tests/test_oracle.py
Normal file
38
deployment-e2e/molecule/oracle/tests/test_oracle.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("oracle_rabbit_1"),
|
||||
("oracle_redis_1"),
|
||||
("oracle_bridge_request_1"),
|
||||
("oracle_bridge_collected_1"),
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("poabridge"),
|
||||
("rsyslog")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", [
|
||||
("/etc/rsyslog.d/31-oracle-docker.conf"),
|
||||
("/etc/rsyslog.d/36-oracle-remote-logging.conf")
|
||||
])
|
||||
def test_logging(host, filename):
|
||||
assert host.file(filename).exists
|
||||
assert host.file(filename).mode == 0o0644
|
||||
13
deployment-e2e/molecule/prepare.yml
Normal file
13
deployment-e2e/molecule/prepare.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: prepare
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: install apt packages
|
||||
apt:
|
||||
name: "{{ packages }}"
|
||||
vars:
|
||||
packages:
|
||||
- apt-transport-https
|
||||
- rsyslog
|
||||
- shell: service rsyslog start
|
||||
- shell: groupadd docker && chgrp docker /var/run/docker.sock
|
||||
14
deployment-e2e/molecule/repo/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/repo/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
12
deployment-e2e/molecule/repo/converge.yml
Normal file
12
deployment-e2e/molecule/repo/converge.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Install Repository
|
||||
hosts: all
|
||||
become: true
|
||||
tasks:
|
||||
- import_role:
|
||||
name: ../../../deployment/roles/common
|
||||
tasks_from: repo
|
||||
# Test that running the task again works
|
||||
- import_role:
|
||||
name: ../../../deployment/roles/common
|
||||
tasks_from: repo
|
||||
30
deployment-e2e/molecule/repo/molecule.yml
Normal file
30
deployment-e2e/molecule/repo/molecule.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: repo-host
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ./converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
repo-host:
|
||||
bridge_repo_branch: master
|
||||
verifier:
|
||||
name: testinfra
|
||||
scenario:
|
||||
name: repo
|
||||
test_sequence:
|
||||
- destroy
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
49
deployment-e2e/molecule/repo/tests/test_existing.py
Normal file
49
deployment-e2e/molecule/repo/tests/test_existing.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", [
|
||||
("/home/poadocker"),
|
||||
("/home/poadocker/bridge"),
|
||||
("/home/poadocker/bridge/commons"),
|
||||
("/home/poadocker/bridge/e2e-commons"),
|
||||
("/home/poadocker/bridge/deployment"),
|
||||
("/home/poadocker/bridge/contracts"),
|
||||
("/home/poadocker/bridge/oracle"),
|
||||
("/home/poadocker/bridge/monitor"),
|
||||
("/home/poadocker/bridge/ui"),
|
||||
("/home/poadocker/bridge/parity")
|
||||
])
|
||||
def test_existing_folders(host, path):
|
||||
assert host.file(path).exists
|
||||
assert host.file(path).is_directory
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", [
|
||||
("/home/poadocker/bridge/package.json"),
|
||||
("/home/poadocker/bridge/commons/package.json"),
|
||||
("/home/poadocker/bridge/contracts/package.json"),
|
||||
("/home/poadocker/bridge/oracle/package.json"),
|
||||
("/home/poadocker/bridge/monitor/package.json"),
|
||||
("/home/poadocker/bridge/ui/package.json")
|
||||
])
|
||||
def test_existing_package_json(host, path):
|
||||
assert host.file(path).exists
|
||||
assert host.file(path).is_file
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", [
|
||||
("/home/poadocker/bridge/Dockerfile.e2e"),
|
||||
("/home/poadocker/bridge/contracts/Dockerfile"),
|
||||
("/home/poadocker/bridge/parity/Dockerfile"),
|
||||
("/home/poadocker/bridge/oracle/Dockerfile"),
|
||||
("/home/poadocker/bridge/monitor/Dockerfile"),
|
||||
("/home/poadocker/bridge/ui/Dockerfile")
|
||||
])
|
||||
def test_existing_docker_files(host, path):
|
||||
assert host.file(path).exists
|
||||
assert host.file(path).is_file
|
||||
23
deployment-e2e/molecule/repo/tests/test_non_existing.py
Normal file
23
deployment-e2e/molecule/repo/tests/test_non_existing.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", [
|
||||
("/home/poadocker/bridge/node_modules"),
|
||||
("/home/poadocker/bridge/ui/node_modules"),
|
||||
("/home/poadocker/bridge/oracle/node_modules"),
|
||||
("/home/poadocker/bridge/monitor/node_modules"),
|
||||
("/home/poadocker/bridge/contracts/node_modules"),
|
||||
])
|
||||
def test_non_existing_node_modules(host, path):
|
||||
assert not host.file(path).exists
|
||||
|
||||
@pytest.mark.parametrize("path", [
|
||||
("/home/poadocker/bridge/.git")
|
||||
])
|
||||
def test_non_existing_git(host, path):
|
||||
assert not host.file(path).exists
|
||||
34
deployment-e2e/molecule/tests/test_all.py
Normal file
34
deployment-e2e/molecule/tests/test_all.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
|
||||
|
||||
|
||||
def test_repo(host):
|
||||
assert host.file('/home/poadocker/bridge').exists
|
||||
assert host.file('/home/poadocker/bridge').is_directory
|
||||
assert host.file('/home/poadocker/bridge/package.json').exists
|
||||
|
||||
|
||||
def test_docker_group(host):
|
||||
assert host.group('docker').exists
|
||||
|
||||
|
||||
def test_user(host):
|
||||
assert host.user('poadocker').exists
|
||||
assert 'docker' in host.user('poadocker').groups
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", [
|
||||
("/etc/rsyslog.d/30-docker.conf"),
|
||||
("/etc/rsyslog.d/35-docker-remote-logging.conf")
|
||||
])
|
||||
def test_logging(host, filename):
|
||||
assert host.file(filename).exists
|
||||
assert host.file(filename).mode == 0o0644
|
||||
|
||||
|
||||
def test_docker_config(host):
|
||||
assert host.file('/etc/docker/daemon.json').exists
|
||||
14
deployment-e2e/molecule/ui/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ui/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
55
deployment-e2e/molecule/ui/molecule.yml
Normal file
55
deployment-e2e/molecule/ui/molecule.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: ui-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
ui-host:
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: ui
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
48
deployment-e2e/molecule/ui/tests/test_ui.py
Normal file
48
deployment-e2e/molecule/ui/tests/test_ui.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ui')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("ui_ui_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("tokenbridge-ui"),
|
||||
("rsyslog")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", [
|
||||
("/etc/rsyslog.d/32-ui-docker.conf"),
|
||||
("/etc/rsyslog.d/37-ui-remote-logging.conf")
|
||||
])
|
||||
def test_logging(host, filename):
|
||||
assert host.file(filename).exists
|
||||
assert host.file(filename).mode == 0o0644
|
||||
|
||||
|
||||
def test_index_page_title(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3001 | '
|
||||
'grep "<title>" | '
|
||||
'grep -q "TokenBridge UI app"'
|
||||
)
|
||||
|
||||
|
||||
def test_index_page_error(host):
|
||||
assert host.run_expect(
|
||||
[1],
|
||||
'curl -s http://localhost:3001 | '
|
||||
'grep -i -q "error"'
|
||||
)
|
||||
5
deployment-e2e/molecule/ultimate-commons/converge.yml
Normal file
5
deployment-e2e/molecule/ultimate-commons/converge.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- import_playbook: ../../../deployment/site.yml
|
||||
# The docker-compose files have to be modified, in order to join the docker containers over network with the parity containers
|
||||
- import_playbook: ./oracle-docker-compose.yml
|
||||
- import_playbook: ./ui-docker-compose.yml
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
- name: Overwrite Oracle the docker-compose
|
||||
hosts: oracle
|
||||
become: true
|
||||
tasks:
|
||||
- name: stop the service
|
||||
shell: service poabridge stop
|
||||
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/docker-compose.yml"
|
||||
register: docker_compose_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all Oracle containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/docker-compose.yml"
|
||||
|
||||
- name: Slurp docker compose extended file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml"
|
||||
register: docker_compose_extended_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all Oracle containers to the network in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {item: {'networks': docker_compose_extended_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_extended_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
|
||||
- name: Write new docker-compose extended file
|
||||
copy:
|
||||
content: "{{ docker_compose_extended_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml"
|
||||
|
||||
- name: start the service
|
||||
shell: service poabridge start
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
- name: Overwrite UI the docker-compose
|
||||
hosts: ui
|
||||
become: true
|
||||
tasks:
|
||||
- name: stop the service
|
||||
shell: service tokenbridge-ui stop
|
||||
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/ui/docker-compose.yml"
|
||||
register: docker_compose_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all UI containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': ['ultimate']}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/ui/docker-compose.yml"
|
||||
|
||||
- name: start the service
|
||||
shell: service tokenbridge-ui start
|
||||
14
deployment-e2e/molecule/ultimate-erc-to-erc/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ultimate-erc-to-erc/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
54
deployment-e2e/molecule/ultimate-erc-to-erc/molecule.yml
Normal file
54
deployment-e2e/molecule/ultimate-erc-to-erc/molecule.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-erc-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-erc
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-erc-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-erc-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-erc-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-erc-to-erc
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
14
deployment-e2e/molecule/ultimate-erc-to-native/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ultimate-erc-to-native/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
56
deployment-e2e/molecule/ultimate-erc-to-native/molecule.yml
Normal file
56
deployment-e2e/molecule/ultimate-erc-to-native/molecule.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-erc-to-native-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-native
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-erc-to-native-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-native
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-erc-to-native-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_HOME_START_BLOCK: 1
|
||||
ORACLE_FOREIGN_START_BLOCK: 1
|
||||
ui-erc-to-native-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-erc-to-native
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
14
deployment-e2e/molecule/ultimate-native-to-erc/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ultimate-native-to-erc/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
54
deployment-e2e/molecule/ultimate-native-to-erc/molecule.yml
Normal file
54
deployment-e2e/molecule/ultimate-native-to-erc/molecule.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-native-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- native-to-erc
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-native-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- native-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-native-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-native-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-native-to-erc
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
11
deployment/.yamllint
Normal file
11
deployment/.yamllint
Normal file
@@ -0,0 +1,11 @@
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
braces:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
brackets:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
line-length: disable
|
||||
truthy: disable
|
||||
96
deployment/CONFIGURATION.md
Normal file
96
deployment/CONFIGURATION.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# POA TokenBridge / Deployment Configuration
|
||||
|
||||
Please see the [Configuration](../CONFIGURATION.md) for additional configuration and execution details.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
A functional Ubuntu 16.04 server launched using a trusted hosting provider. For more information, see our tutorials on [setting up a validator node on AWS](https://github.com/poanetwork/wiki/wiki/Validator-Node-on-AWS) or [setting up on non-AWS](https://github.com/poanetwork/wiki/wiki/Validator-Node-Non-AWS).
|
||||
* Record the IP address (required for file setup).
|
||||
* Setup ssh access to your node via public+private keys (using passwords is less secure).
|
||||
* When creating the node, set a meaningful `hostname` that can identify you (e.g. `validator-0x...`).
|
||||
|
||||
## Initialization
|
||||
|
||||
1. Clone this repository and go to the `deployment` folder
|
||||
```
|
||||
git clone --recursive https://github.com/poanetwork/tokenbridge
|
||||
cd tokenbridge/deployment
|
||||
```
|
||||
2. Create the file `hosts.yml` from `hosts.yml.example`
|
||||
```
|
||||
cp hosts.yml.example hosts.yml
|
||||
```
|
||||
|
||||
`hosts.yml` should have the following structure:
|
||||
|
||||
```yaml
|
||||
<bridge_name>:
|
||||
children:
|
||||
oracle:
|
||||
hosts:
|
||||
<host_ip_A>:
|
||||
ansible_user: <user>
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "<private_key>"
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to <ip>:<port> address.
|
||||
<host_ip_B>:
|
||||
# (...)
|
||||
ui:
|
||||
hosts:
|
||||
<host_ip_B>:
|
||||
ansible_user: <user>
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
<host_ip_C>:
|
||||
ansible_user: <user>
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_B>:
|
||||
ansible_user: <user>
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
#monitor_cron_schedule: "*/4 * * * *" # When this parameter is set, it will overwrite default schedule for performing checks
|
||||
```
|
||||
|
||||
The config above would install the Oracle on `<host_ip_A>`, UI on `<host_ip_C>`, and both Oracle, UI and Monitor on `<host_ip_B>`.
|
||||
|
||||
Example config for installing only UI:
|
||||
```yaml
|
||||
<bridge_name>:
|
||||
children:
|
||||
oracle:
|
||||
hosts:
|
||||
ui:
|
||||
hosts:
|
||||
<host_ip>:
|
||||
ansible_user: <user>
|
||||
```
|
||||
|
||||
| Value | Description |
|
||||
|:------------------------------------------------|:----------------------------------------------------------------------------------------------------------|
|
||||
| `<bridge_name>` | The bridge name which tells Ansible which file to use. This is located in `group_vars/<bridge_name>.yml`. |
|
||||
| `<host_ip>` | Remote server IP address. |
|
||||
| ansible_user: `<user>` | User that will ssh into the node. This is typically `ubuntu` or `root`. |
|
||||
| ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: `"<private_key>"` | The private key for the specified validator address. |
|
||||
| syslog_server_port: `"<protocol>://<ip>:<port>"` | Optional port specification for bridge logs. This value will be provided by an administrator if required. |
|
||||
|
||||
`hosts.yml` can contain multiple bridge configurations at once.
|
||||
|
||||
3. Copy the bridge name(s) to the hosts.yml file.
|
||||
1. Go to the group_vars folder.
|
||||
`cd group_vars`
|
||||
2. Note the <bridge_name> and add it to the hosts.yml configuration. For example, if a bridge file is named sokol-kovan.yml, you would change the <bridge_name> value in hosts.yml to sokol-kovan.
|
||||
|
||||
## Administrator Configurations
|
||||
|
||||
1. The `group_vars/<bridge_name>.yml` file contains the public bridge parameters. This file is prepared by administrators for each bridge. The validator only needs to add the required bridge name in the hosts.yml file to tell Ansible which file to use.
|
||||
|
||||
`group_vars/example.yml` shows an example configuration for the POA/Sokol - POA/Sokol bridge. Parameter values should match values from the .env file for the Oracle. See [Configuration parameters](../../oracle/README.md#configuration-parameters) for details.
|
||||
|
||||
2. You can also add the following parameters in the `group_vars` to change the default behavior of the playbooks:
|
||||
|
||||
2.1 `compose_service_user` - specifies the user created by the playbooks. This user runs the TokenBridge Oracle.
|
||||
|
||||
2.4 `bridge_path` sets the path where the TokenBridge Oracle is installed. By default, it points. to the home folder of `compose_service_user`
|
||||
|
||||
2.5 `docker_compose_version` - specifies a version of docker-compose to be installed.
|
||||
|
||||
2.6 `ALLOW_HTTP` (`no` by default) can be set to `yes` to allow bridge insecure connections to the network.
|
||||
128
deployment/EXECUTION.md
Normal file
128
deployment/EXECUTION.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# POA TokenBridge / Deployment Execution
|
||||
|
||||
Please refer to the [Configuration](./CONFIGURATION.md) first.
|
||||
|
||||
## Dependencies
|
||||
|
||||
On your local machine install:
|
||||
* Python 2 (v2.6-v2.7)/Python3 (v3.5+)
|
||||
* [Ansible v2.3+](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
|
||||
* Git
|
||||
|
||||
The playbook will automatically install `Docker`, `docker-compose`, `Python`, `Git` and it dependencies (such as `curl`, `ca-certificates`, `apt-transport-https`, etc.) to the node. Also this playbooks creates an additional non-sudo docker user to run service as.
|
||||
|
||||
## Running the playbook
|
||||
|
||||
```yaml
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
## Useful arguments
|
||||
|
||||
To be used with the ansible-playbook command, for example:
|
||||
|
||||
```yaml
|
||||
ansible-playbook -i hosts.yml site.yml --ask-become-pass
|
||||
```
|
||||
|
||||
* `--ask-pass` - ask for the password used to connect to the bridge VM.
|
||||
|
||||
* `--ask-become-pass` - ask for the `become` password used to execute some commands (such as Docker installation) with root privileges.
|
||||
|
||||
* `-i <file>` - use specified file as a `hosts.yml` file.
|
||||
|
||||
* `-e "<variable>=<value>"` - override default variable.
|
||||
|
||||
* `--private-key=<file_name>` - if private keyfile is required to connect to the ubuntu instance.
|
||||
|
||||
* `--user=<username>` - connect as this username
|
||||
|
||||
## Service commands
|
||||
|
||||
The deployed components have the following services:
|
||||
|
||||
Component | Service Name
|
||||
--- | ---
|
||||
Oracle | poabridge
|
||||
UI | tokenbridge-ui
|
||||
Monitor | tokenbridge-monitor
|
||||
|
||||
Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service.
|
||||
|
||||
Commands format:
|
||||
```bash
|
||||
sudo service <service_name> [start|stop|restart|status|rebuild]
|
||||
```
|
||||
|
||||
## 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 the `reset-lastBlock.sh` script in the bridge root directory. For example, if you've installed your bridge with this deployment script and all the default parameters, use the following set of commands:
|
||||
|
||||
```shell
|
||||
$ sudo su poadocker
|
||||
$ cd ~/bridge
|
||||
$ docker-compose stop bridge_affirmation bridge_request bridge_collected
|
||||
$ docker-compose exec bridge_senderhome bash ./reset-lastBlock.sh <watcher> <block num>
|
||||
$ exit
|
||||
$ sudo service poabridge restart
|
||||
```
|
||||
where the _<watcher>_ could be one of the following:
|
||||
|
||||
- `signature-request`
|
||||
- `collected-signatures`
|
||||
- `affirmation-request`
|
||||
|
||||
## Reset nonce counters
|
||||
In case some tx from your bridge validator account were done outside the bridge, you might need to update nonce counters.
|
||||
|
||||
1. ssh to your bridge node and run:
|
||||
```
|
||||
$ sudo su poadocker
|
||||
$ cd ~/bridge
|
||||
```
|
||||
1. stop running docker containers using the nonce by running:
|
||||
```
|
||||
$ docker-compose stop bridge_senderhome bridge_senderforeign
|
||||
```
|
||||
1. Connect to the redis container:
|
||||
```
|
||||
$ docker-compose exec redis /bin/bash
|
||||
```
|
||||
you should get a shell prompt from inside the docker container, similar to this:
|
||||
```
|
||||
root@redis:/data#
|
||||
```
|
||||
1. connect to redis database by running `redis-cli`, prompt should change once again to
|
||||
```
|
||||
127.0.0.1:6379>
|
||||
```
|
||||
1. list existing keys by running `keys *`, output should look like this:
|
||||
```
|
||||
127.0.0.1:6379> keys *
|
||||
1) "erc-native-affirmation-request:lastProcessedBlock"
|
||||
2) "erc-native-collected-signatures:lastProcessedBlock"
|
||||
3) "erc-native-signature-request:lastProcessedBlock"
|
||||
4) "home:nonce"
|
||||
5) "foreign:nonce"
|
||||
```
|
||||
1. delete keys containing last used nonces on both networks
|
||||
```
|
||||
127.0.0.1:6379> del "home:nonce" "foreign:nonce"
|
||||
```
|
||||
1. exit from redis container by running `exit` twice
|
||||
1. start the rest of bridge containers:
|
||||
```
|
||||
$ docker-compose start bridge_senderhome bridge_senderforeign
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
If the `syslog_server_port` option in the hosts.yml file is not set, all logs will be stored in `/var/log/docker/` folder in the set of folders with the `bridge_` prefix.
|
||||
|
||||
If the `syslog_server_port` is set, logs will be redirected to the specified server and cannot be accessed on the bridge machine.
|
||||
|
||||
```yaml
|
||||
syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to the <ip>:<port> address.
|
||||
```
|
||||
@@ -1,33 +1,22 @@
|
||||
# POA Token Bridge / Deployment
|
||||
# POA TokenBridge / Deployment
|
||||
Ansible playbooks for deploying cross-chain bridges.
|
||||
|
||||
## Overview
|
||||
Please refer to the [POA Token Bridge](../README.md) overview first of all.
|
||||
Please refer to the [POA TokenBridge](../README.md) overview first of all.
|
||||
|
||||
These playbooks are designed to automate the deployment process for cross-chain bridges on bridge validator nodes. This process installs the bridge as a service and sets .env configurations on a remote server. Playbooks for the current Token Bridge Oracle deployment are located in the [Oracle](oracle) folder.
|
||||
|
||||
## Dependencies
|
||||
|
||||
The playbooks automatically install `Docker`, `docker-compose`, `Python`, `Git`and it dependencies (such as `curl`, `ca-certificates`, `apt-transport-https`, etc.). Install [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) to launch playbooks.
|
||||
|
||||
## Linting
|
||||
|
||||
- [ansible-lint](https://github.com/ansible/ansible-lint) is required
|
||||
|
||||
`yarn ansible-lint`
|
||||
These playbooks are designed to automate the deployment process for cross-chain bridges on bridge validator nodes. This process installs the bridge as a service and sets .env configurations on a remote server.
|
||||
|
||||
## Configuration
|
||||
|
||||
Please see the [Oracle](../oracle/README.md) for configuration and execution details.
|
||||
Please refer to [Configuration](./CONFIGURATION.md).
|
||||
|
||||
## Bridge service commands
|
||||
## Execution
|
||||
|
||||
The Bridge service is named `poabridge`. Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service.
|
||||
Please refer to [Execution](./EXECUTION.md).
|
||||
|
||||
Commands format:
|
||||
```bash
|
||||
sudo service poabridge [start|stop|restart|status|rebuild]
|
||||
```
|
||||
## Testing
|
||||
|
||||
Please refer to [Deployment-E2E](../deployment-e2e/README.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
50
deployment/group_vars/dai.yml
Normal file
50
deployment/group_vars/dai.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "xDai"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "xDai chain"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Home Gasprice
|
||||
# COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://localhost:8888/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 0
|
||||
COMMON_HOME_GAS_PRICE_FACTOR: 600000
|
||||
|
||||
## Foreign Gasprice
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring MakerDAO stable tokens between the Ethereum network to xDai chain in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/dai/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/dai/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
6
deployment/group_vars/erc-to-erc.yml
Normal file
6
deployment/group_vars/erc-to-erc.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_ERC"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x1feB40aD9420b186F019A717c37f5546165d411E"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127"
|
||||
UI_PORT: 3001
|
||||
MONITOR_PORT: 3011
|
||||
6
deployment/group_vars/erc-to-native.yml
Normal file
6
deployment/group_vars/erc-to-native.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
|
||||
UI_PORT: 3002
|
||||
MONITOR_PORT: 3012
|
||||
52
deployment/group_vars/example.yml
Normal file
52
deployment/group_vars/example.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
ORACLE_LOG_LEVEL: debug
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
||||
|
||||
## Home Gasprice
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_HOME_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Foreign Gasprice
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
6
deployment/group_vars/native-to-erc.yml
Normal file
6
deployment/group_vars/native-to-erc.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x32198D570fffC7033641F8A9094FFDCaAEF42624"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x2B6871b9B02F73fa24F4864322CdC78604207769"
|
||||
UI_PORT: 3000
|
||||
MONITOR_PORT: 3010
|
||||
49
deployment/group_vars/ultimate.yml
Normal file
49
deployment/group_vars/ultimate.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
## General settings
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC: yes
|
||||
ORACLE_LOG_LEVEL: debug
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
||||
|
||||
## Home Gasprice
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_HOME_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Foreign Gasprice
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
|
||||
#ui
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
#montior
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
51
deployment/group_vars/wetc.yml
Normal file
51
deployment/group_vars/wetc.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "ETC"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "Ethereum Classic"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 7000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 7000
|
||||
|
||||
## Home Gasprice
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice-etc.poa.network/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 15000000000 # in wei
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## Foreign Gasprice
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000 # in wei
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring native tokens from the Ethereum Classic Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/etc/mainnet/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/etc/mainnet/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
20
deployment/hosts.yml.example
Normal file
20
deployment/hosts.yml.example
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
sokol-kovan:
|
||||
children:
|
||||
oracle:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
ui:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
monitor:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
#monitor_cron_schedule: "*/4 * * * *"
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)
|
||||
ansible-lint -v -t bug ./oracle/roles/**
|
||||
@@ -1,138 +0,0 @@
|
||||
### Prerequisites
|
||||
1. A functional Ubuntu 16.04 server launched using a trusted hosting provider. For more information, see our tutorials on [setting up a validator node on AWS](https://github.com/poanetwork/wiki/wiki/Validator-Node-on-AWS) or [setting up on non-AWS](https://github.com/poanetwork/wiki/wiki/Validator-Node-Non-AWS).
|
||||
* Record the IP address (required for file setup).
|
||||
* Setup ssh access to your node via public+private keys (using passwords is less secure).
|
||||
* When creating the node, set a meaningful `hostname` that can identify you (e.g. `validator-0x...`).
|
||||
|
||||
2. On your local machine install:
|
||||
* Python 2 (v2.6-v2.7)/Python3 (v3.5+)
|
||||
* Ansible v2.3+
|
||||
* Git
|
||||
|
||||
### Configuration
|
||||
|
||||
1. Clone this repository and go to the `deployment/oracle` folder
|
||||
```
|
||||
git clone --recursive https://github.com/poanetwork/tokenbridge
|
||||
cd tokenbridge/deployment/oracle
|
||||
```
|
||||
2. Create the file `hosts.yml` from `hosts.yml.example`
|
||||
```
|
||||
cp hosts.yml.example hosts.yml
|
||||
```
|
||||
|
||||
`hosts.yml` should have the following structure:
|
||||
|
||||
```yaml
|
||||
<bridge_name>:
|
||||
hosts:
|
||||
<host_ip>:
|
||||
ansible_user: <user>
|
||||
VALIDATOR_ADDRESS_PRIVATE_KEY: "<private_key>"
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to <ip>:<port> address.
|
||||
```
|
||||
|
||||
| Value | Description |
|
||||
|:------------------------------------------------|:----------------------------------------------------------------------------------------------------------|
|
||||
| `<bridge_name>` | The bridge name which tells Ansible which file to use. This is located in `group_vars/<bridge_name>.yml`. |
|
||||
| `<host_ip>` | Remote server IP address. |
|
||||
| ansible_user: `<user>` | User that will ssh into the node. This is typically `ubuntu` or `root`. |
|
||||
| VALIDATOR_ADDRESS_PRIVATE_KEY: `"<private_key>"` | The private key for the specified validator address. |
|
||||
| syslog_server_port: `"<protocol>://<ip>:<port>"` | Optional port specification for bridge logs. This value will be provided by an administrator if required. |
|
||||
|
||||
|
||||
`hosts.yml` can contain multiple hosts and bridge configurations (groups) at once.
|
||||
|
||||
|
||||
3. Copy the bridge name(s) to the hosts.yml file.
|
||||
1. Go to the group_vars folder.
|
||||
`cd group_vars`
|
||||
2. Note the <bridge_name> and add it to the hosts.yml configuration. For example, if a bridge file is named sokol-kovan.yml, you would change the <bridge_name> value in hosts.yml to sokol-kovan.
|
||||
|
||||
#### Administrator Configurations
|
||||
|
||||
1. The `group_vars/<bridge_name>.yml` file contains the public bridge parameters. This file is prepared by administrators for each bridge. The validator only needs to add the required bridge name in the hosts.yml file to tell Ansible which file to use.
|
||||
|
||||
`group_vars/example.yml` shows an example configuration for the POA/Sokol - POA/Sokol bridge. Parameter values should match values from the .env file for the Oracle. See [Configuration parameters](../../oracle/README.md#configuration-parameters) for details.
|
||||
|
||||
2. You can also add the following parameters in the `group_vars` to change the default behavior of `deployment-bridge` playbooks:
|
||||
|
||||
2.1 `compose_service_user` - specifies users to be created by playbooks. This user will be used to run Token Bridge Oracle.
|
||||
|
||||
2.2 `bridge_repo` contains address of Token Bridge Oracle repository. The default value is https://github.com/poanetwork/tokenbridge.
|
||||
|
||||
2.3 `bridge_repo_branch` points to the specific branch or commit to use with the `bridge_repo`. If `bridge_repo_branch` is not specified, the default (`master`) branch is used.
|
||||
|
||||
2.4 `bridge_path` set the path where Token Bridge Oracle would be installed. By default it point to the home folder of `compose_service_user`
|
||||
|
||||
2.5 `docker_compose_version` - specifies a version of docker-compose to be installed.
|
||||
|
||||
2.6 `ALLOW_HTTP` (`no` by default) can be set to `yes` to allow bridge insecure connections to the network.
|
||||
|
||||
## Execution
|
||||
|
||||
The playbook can be executed once [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) is installed and all configuration variables are set.
|
||||
|
||||
It will automatically install `Docker`, `docker-compose`, `Python`, `Git` and it dependencies (such as `curl`, `ca-certificates`, `apt-transport-https`, etc.) to the node. Also this playbooks creates an additional non-sudo docker user to run service as.
|
||||
|
||||
```yaml
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
**Useful arguments:**
|
||||
|
||||
To be used with the ansible-playbook command, for example:
|
||||
|
||||
```yaml
|
||||
`ansible-playbook -i hosts.yml site.yml --ask-become-pass`
|
||||
```
|
||||
|
||||
* `--ask-pass` - ask for the password used to connect to the bridge VM.
|
||||
|
||||
* `--ask-become-pass` - ask for the `become` password used to execute some commands (such as Docker installation) with root privileges.
|
||||
|
||||
* `-i <file>` - use specified file as a `hosts.yml` file.
|
||||
|
||||
* `-e "<variable>=<value>"` - override default variable.
|
||||
|
||||
* `--private-key=<file_name>` - if private keyfile is required to connect to the ubuntu instance.
|
||||
|
||||
* `--user=<username>` - connect as this username
|
||||
|
||||
## Bridge service commands
|
||||
|
||||
The Bridge service is named `poabridge`. Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service.
|
||||
|
||||
Commands format:
|
||||
```bash
|
||||
sudo service poabridge [start|stop|restart|status|rebuild]
|
||||
```
|
||||
|
||||
## 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 the `reset-lastBlock.sh` script in the bridge root directory. For example, if you've installed your bridge with this deployment script and all the default parameters, use the following set of commands:
|
||||
|
||||
```shell
|
||||
$ sudo su poadocker
|
||||
$ cd ~/bridge
|
||||
$ docker-compose exec bridge_affirmation bash ./reset-lastBlock.sh <watcher> <block num>
|
||||
$ exit
|
||||
$ sudo service poabridge restart
|
||||
```
|
||||
where the _<watcher>_ could be one of the following:
|
||||
|
||||
- `signature-request`
|
||||
- `collected-signatures`
|
||||
- `affirmation-request`
|
||||
|
||||
## Logs
|
||||
|
||||
If the `syslog_server_port` option in the hosts.yml file is not set, all logs will be stored in `/var/log/docker/` folder in the set of folders with the `bridge_` prefix.
|
||||
|
||||
If the `syslog_server_port` is set, logs will be redirected to the specified server and cannot be accessed on the bridge machine.
|
||||
|
||||
```yaml
|
||||
syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to the <ip>:<port> address.
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
## General settings
|
||||
BRIDGE_MODE: "ERC_TO_NATIVE"
|
||||
|
||||
## Home contract
|
||||
HOME_RPC_URL: "https://dai.poa.network"
|
||||
HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
HOME_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
FOREIGN_RPC_URL: "https://mainnet.infura.io"
|
||||
FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
|
||||
ERC20_TOKEN_ADDRESS: "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
|
||||
FOREIGN_POLLING_INTERVAL: 5000
|
||||
|
||||
## Gasprice
|
||||
#HOME_GAS_PRICE_ORACLE_URL: "https://localhost:8888/"
|
||||
HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
HOME_GAS_PRICE_FALLBACK: 0
|
||||
HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
FOREIGN_GAS_PRICE_ORACLE_URL: "https://gasprice.poa.network/"
|
||||
FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
@@ -1,24 +0,0 @@
|
||||
## General settings
|
||||
BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
|
||||
## Home contract
|
||||
HOME_RPC_URL: "https://sokol.poa.network"
|
||||
HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
|
||||
HOME_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
FOREIGN_RPC_URL: "https://sokol.poa.network"
|
||||
FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
|
||||
ERC20_TOKEN_ADDRESS: "0x6ef22442D600E1865AD8A8c254d6befCe7f4e6e4"
|
||||
FOREIGN_POLLING_INTERVAL: 1000
|
||||
|
||||
## Gasprice
|
||||
HOME_GAS_PRICE_ORACLE_URL: "https://gasprice.poa.network/"
|
||||
HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
HOME_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
FOREIGN_GAS_PRICE_ORACLE_URL: "https://gasprice.poa.network/"
|
||||
FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
@@ -1,24 +0,0 @@
|
||||
## General settings
|
||||
BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
|
||||
## Home contract
|
||||
HOME_RPC_URL: "https://ethereumclassic.network"
|
||||
HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
HOME_POLLING_INTERVAL: 7000
|
||||
|
||||
## Foreign contract
|
||||
FOREIGN_RPC_URL: "https://mainnet.infura.io/"
|
||||
FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
|
||||
ERC20_TOKEN_ADDRESS: "0x86aaBCc646f290b9Fc9Bd05CE17C3858d1511Da1"
|
||||
FOREIGN_POLLING_INTERVAL: 7000
|
||||
|
||||
## Gasprice
|
||||
HOME_GAS_PRICE_ORACLE_URL: "https://gasprice-etc.poa.network/"
|
||||
HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
HOME_GAS_PRICE_FALLBACK: 15000000000 # in wei
|
||||
HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
FOREIGN_GAS_PRICE_ORACLE_URL: "https://gasprice.poa.network/"
|
||||
FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
FOREIGN_GAS_PRICE_FALLBACK: 10000000000 # in wei
|
||||
FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
@@ -1,6 +0,0 @@
|
||||
sokol-kovan:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
@@ -1,17 +0,0 @@
|
||||
This role installs required dependencies:
|
||||
|
||||
* apt-transport-https
|
||||
|
||||
* ca-certificates
|
||||
|
||||
* curl
|
||||
|
||||
* software-properties-common
|
||||
|
||||
* docker-ce (+python library)
|
||||
|
||||
* docker-compose (+python library)
|
||||
|
||||
* git
|
||||
|
||||
* python-pip
|
||||
@@ -1,2 +0,0 @@
|
||||
docker_compose_version: 1.22.0
|
||||
compose_service_user: poadocker
|
||||
@@ -1 +0,0 @@
|
||||
This role brings up a docker container using docker-compose.
|
||||
@@ -1 +0,0 @@
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
@@ -1,4 +0,0 @@
|
||||
- name: Launch container
|
||||
shell: docker-compose up -d
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
@@ -1 +0,0 @@
|
||||
This role sets up remote logging for services.
|
||||
@@ -1,2 +0,0 @@
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
syslog_server_port: udp://127.0.0.1:514
|
||||
@@ -1 +0,0 @@
|
||||
This role gets the start blocks for both home and foreign networks.
|
||||
@@ -1 +0,0 @@
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
@@ -1,33 +0,0 @@
|
||||
- name: Get blocks
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: BLOCKS
|
||||
|
||||
- name: Write blocks
|
||||
blockinfile:
|
||||
path: "{{ bridge_path }}/oracle/.env"
|
||||
marker: "## {mark} Calculated by scripts/getValidatorStartBlocks.js"
|
||||
block: |
|
||||
HOME_START_BLOCK={{ (BLOCKS.stdout | from_json).homeStartBlock }}
|
||||
FOREIGN_START_BLOCK={{ (BLOCKS.stdout | from_json).foreignStartBlock }}
|
||||
|
||||
- name: Get validator address
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run -e VALIDATOR_ADDRESS_PRIVATE_KEY="{{ VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: VADDRESS
|
||||
|
||||
- name: Set VALIDATOR_ADDRESS variable
|
||||
set_fact:
|
||||
VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}"
|
||||
|
||||
- name: Install .key config
|
||||
template:
|
||||
src: key.j2
|
||||
dest: "/root/.key"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
@@ -1 +0,0 @@
|
||||
This role sets the .env config (excluding starting blocks).
|
||||
@@ -1,6 +0,0 @@
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
|
||||
ALLOW_HTTP: no
|
||||
QUEUE_URL: amqp://rabbit
|
||||
REDIS_URL: redis://redis
|
||||
REDIS_LOCK_TTL: 1000
|
||||
@@ -1,42 +0,0 @@
|
||||
## General settings
|
||||
BRIDGE_MODE={{ BRIDGE_MODE }}
|
||||
|
||||
## Home contract
|
||||
HOME_RPC_URL={{ HOME_RPC_URL }}
|
||||
HOME_BRIDGE_ADDRESS={{ HOME_BRIDGE_ADDRESS }}
|
||||
HOME_POLLING_INTERVAL={{ HOME_POLLING_INTERVAL }}
|
||||
|
||||
## Foreign contract
|
||||
FOREIGN_RPC_URL={{ FOREIGN_RPC_URL }}
|
||||
FOREIGN_BRIDGE_ADDRESS={{ FOREIGN_BRIDGE_ADDRESS }}
|
||||
ERC20_TOKEN_ADDRESS={{ ERC20_TOKEN_ADDRESS }}
|
||||
FOREIGN_POLLING_INTERVAL={{ FOREIGN_POLLING_INTERVAL }}
|
||||
|
||||
## Gasprice
|
||||
{% if HOME_GAS_PRICE_ORACLE_URL | default('') != '' %}
|
||||
HOME_GAS_PRICE_ORACLE_URL={{ HOME_GAS_PRICE_ORACLE_URL }}
|
||||
{% endif %}
|
||||
{% if HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
HOME_GAS_PRICE_SPEED_TYPE={{ HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
HOME_GAS_PRICE_FALLBACK={{ HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if HOME_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
|
||||
HOME_GAS_PRICE_UPDATE_INTERVAL={{ HOME_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
{% endif %}
|
||||
|
||||
{% if FOREIGN_GAS_PRICE_ORACLE_URL | default('') != '' %}
|
||||
FOREIGN_GAS_PRICE_ORACLE_URL={{ FOREIGN_GAS_PRICE_ORACLE_URL }}
|
||||
{% endif %}
|
||||
{% if FOREIGN_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
FOREIGN_GAS_PRICE_SPEED_TYPE={{ FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
FOREIGN_GAS_PRICE_FALLBACK={{ FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
{% if FOREIGN_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
|
||||
FOREIGN_GAS_PRICE_UPDATE_INTERVAL={{ FOREIGN_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
{% endif %}
|
||||
|
||||
## Transport configuration
|
||||
ALLOW_HTTP={{ "yes" if ALLOW_HTTP else "no" }}
|
||||
QUEUE_URL={{ QUEUE_URL }}
|
||||
REDIS_URL={{ REDIS_URL }}
|
||||
REDIS_LOCK_TTL={{ REDIS_LOCK_TTL }}
|
||||
@@ -1 +0,0 @@
|
||||
This role clones the repo from a specified URL.
|
||||
@@ -1,3 +0,0 @@
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
bridge_repo: https://github.com/poanetwork/tokenbridge.git
|
||||
bridge_repo_branch: master
|
||||
@@ -1,6 +0,0 @@
|
||||
- name: Get bridge repo
|
||||
git:
|
||||
repo: "{{ bridge_repo }}"
|
||||
dest: "{{ bridge_path }}"
|
||||
force: yes
|
||||
version: "{{ bridge_repo_branch }}"
|
||||
@@ -1,5 +0,0 @@
|
||||
This role creates a poabridge service which is designed to manage docker-compose bridge deployment.
|
||||
|
||||
/etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases.
|
||||
|
||||
/etc/init.d/poabridge rebuild - builds a new bridge deployment from scratch.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
# defaults
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
keyfile_path: "/root/.key"
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
- name: "Set poabridge service"
|
||||
template:
|
||||
src: poabridge.j2
|
||||
dest: "/etc/init.d/poabridge"
|
||||
owner: root
|
||||
mode: 755
|
||||
|
||||
- name: "Start/Enable poabridge service"
|
||||
service:
|
||||
name: "poabridge"
|
||||
state: started
|
||||
enabled: yes
|
||||
use: service
|
||||
@@ -1,19 +0,0 @@
|
||||
- name: Install python if necessary
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
become: true
|
||||
tasks:
|
||||
- name: Install python
|
||||
raw: "test -e {{ ansible_python_interpreter | default ('/usr/bin/python') }} || (sudo apt -y update && sudo apt install -y python-minimal)"
|
||||
tags: install_dependencies
|
||||
|
||||
- name: Install bridge
|
||||
hosts: all
|
||||
roles:
|
||||
- { role: dependencies, tags: install_dependencies, become: true }
|
||||
- { role: repo, tags: clone_repo, become: true }
|
||||
- { role: pre_config, tags: pre_config, become: true }
|
||||
- { role: jumpbox, tags: launch_jumpbox, become: true }
|
||||
- { role: post_config, tags: post_config, become: true }
|
||||
- { role: logging, tags: set_logging, become: true}
|
||||
- { role: servinstall, tags: install_service, become: true }
|
||||
4
deployment/requirements.txt
Normal file
4
deployment/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# pre-release because it contains "CI Fixes for ansible 2.8"
|
||||
molecule==2.22rc1
|
||||
docker
|
||||
flake8
|
||||
4
deployment/roles/common/defaults/main.yml
Normal file
4
deployment/roles/common/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
docker_compose_version: 1.23.2
|
||||
compose_service_user: poadocker
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"disable-legacy-registry": true,
|
||||
"live-restore": true,
|
||||
"no-new-privileges": true
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
- name: restart rsyslog
|
||||
service:
|
||||
name: rsyslog
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
- name: Install the gpg key for docker
|
||||
apt_key:
|
||||
url: "https://download.docker.com/linux/ubuntu/gpg"
|
||||
state: present
|
||||
|
||||
|
||||
- name: Install the docker repos
|
||||
apt_repository:
|
||||
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
|
||||
@@ -19,7 +20,8 @@
|
||||
- software-properties-common
|
||||
- docker-ce
|
||||
- git
|
||||
- "{{ (ansible_python_interpreter | default ('python')).split('/')[-1] }}-pip"
|
||||
- python3
|
||||
- python3-pip
|
||||
|
||||
- name: Install Docker Compose
|
||||
get_url:
|
||||
@@ -29,13 +31,9 @@
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0755"
|
||||
|
||||
|
||||
- name: Install python docker library
|
||||
pip:
|
||||
name: "{{ item }}"
|
||||
with_items:
|
||||
- docker
|
||||
- docker-compose
|
||||
shell: pip3 install docker docker-compose setuptools
|
||||
|
||||
- name: Add user to run docker-compose
|
||||
user:
|
||||
@@ -52,7 +50,7 @@
|
||||
- name: Configure auditd
|
||||
blockinfile:
|
||||
path: /etc/audit/audit.rules
|
||||
block: |
|
||||
block: |
|
||||
-w /usr/bin/docker -p wa
|
||||
-w /var/lib/docker -p wa
|
||||
-w /etc/docker -p wa
|
||||
@@ -61,13 +59,13 @@
|
||||
-w /etc/default/docker -p wa
|
||||
-w /etc/docker/daemon.json -p wa
|
||||
-w /usr/bin/docker-containerd -p wa
|
||||
-w /usr/bin/docker-runc -p wa
|
||||
-w /usr/bin/docker-runc -p wa
|
||||
notify: restart auditd
|
||||
|
||||
- name: Configure docker engine
|
||||
copy:
|
||||
src: daemon.json
|
||||
dest: /etc/docker/daemon.json
|
||||
dest: /etc/docker/
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0640
|
||||
30
deployment/roles/common/tasks/logging.yml
Normal file
30
deployment/roles/common/tasks/logging.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
- name: Set the local docker logs configuration file
|
||||
template:
|
||||
src: 30-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/30-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the log configuration file to send docker logs to remote server
|
||||
template:
|
||||
src: 35-docker-remote-logging.conf.j2
|
||||
dest: /etc/rsyslog.d/35-docker-remote-logging.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
when: syslog_server_port is defined
|
||||
|
||||
- name: Set the logrotate config file
|
||||
template:
|
||||
src: docker-logs.j2
|
||||
dest: /etc/logrotate.d/docker-logs
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: restart rsyslog
|
||||
service:
|
||||
name: rsyslog
|
||||
state: restarted
|
||||
4
deployment/roles/common/tasks/main.yml
Normal file
4
deployment/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
- include_tasks: dependencies.yml
|
||||
- include_tasks: repo.yml
|
||||
- include_tasks: logging.yml
|
||||
34
deployment/roles/common/tasks/repo.yml
Normal file
34
deployment/roles/common/tasks/repo.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
- name: Create archives of the monorepository
|
||||
shell: |
|
||||
(git ls-tree -r HEAD --name-only | sed '/^contracts$/d') | xargs tar zcfv monorepo.tar.gz --files-from -
|
||||
cd contracts; (git ls-tree -r HEAD --name-only) | xargs tar zcfv ../contracts.tar.gz --files-from -
|
||||
delegate_to: 127.0.0.1
|
||||
become: false
|
||||
args:
|
||||
chdir: "{{ lookup('env', 'PWD') }}/.."
|
||||
|
||||
- name: Copy the archives
|
||||
copy:
|
||||
src: ../../../../{{ item }}
|
||||
dest: "{{ bridge_path }}/"
|
||||
with_items:
|
||||
- monorepo.tar.gz
|
||||
- contracts.tar.gz
|
||||
|
||||
- name: Untar the archives
|
||||
shell: |
|
||||
tar zxfv monorepo.tar.gz && rm monorepo.tar.gz
|
||||
mkdir -p contracts && tar zxfv contracts.tar.gz -C ./contracts && rm contracts.tar.gz
|
||||
args:
|
||||
chdir: "{{ bridge_path }}"
|
||||
|
||||
- name: Remove local archives
|
||||
shell: rm {{ item }}
|
||||
delegate_to: 127.0.0.1
|
||||
become: false
|
||||
args:
|
||||
chdir: "{{ lookup('env', 'PWD') }}/.."
|
||||
with_items:
|
||||
- monorepo.tar.gz
|
||||
- contracts.tar.gz
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user