diff --git a/.env.example b/.env.example index 7a5b006..b9f17c2 100644 --- a/.env.example +++ b/.env.example @@ -13,12 +13,12 @@ VALIDATOR_ADDRESS_PRIVATE_KEY= HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/ HOME_GAS_PRICE_SPEED_TYPE=standard -HOME_GAS_PRICE_FALLBACK=1 +HOME_GAS_PRICE_FALLBACK=1000000000 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=1 +FOREIGN_GAS_PRICE_FALLBACK=1000000000 FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000 QUEUE_URL=amqp://127.0.0.1 @@ -28,9 +28,13 @@ REDIS_LOCK_TTL=1000 HOME_START_BLOCK= FOREIGN_START_BLOCK= +LOG_LEVEL=debug +MAX_PROCESSING_TIME=20000 + #testing accs USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370 USER_ADDRESS_PRIVATE_KEY= HOME_MIN_AMOUNT_PER_TX=0.001 FOREIGN_MIN_AMOUNT_PER_TX=0.001 -BRIDGEABLE_TOKEN_ADDRESS= +HOME_TEST_TX_GAS_PRICE=1000000000 +FOREIGN_TEST_TX_GAS_PRICE=1000000000 diff --git a/.eslintrc b/.eslintrc index 3614120..5d33751 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,6 +10,7 @@ "func-names": "off", "no-await-in-loop": "off", "no-console": "off", + "no-else-return": "off", "no-param-reassign": "off", "no-plusplus": "off", "no-restricted-syntax": "off", diff --git a/README.md b/README.md index c032889..715e827 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ where the _watcher_ could be one of: | Variable | Description | Values | |-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------| -| `BRIDGE_MODE` | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | `NATIVE_TO_ERC` / `ERC_TO_ERC` | +| `BRIDGE_MODE` | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | `NATIVE_TO_ERC` / `ERC_TO_ERC` / `ERC_TO_NATIVE` | | `HOME_RPC_URL` | The HTTPS URL(s) used to communicate to the RPC nodes in the Home network. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s) | | `HOME_BRIDGE_ADDRESS` | The address of the bridge contract address in the Home network. It is used to listen to events from and send validators' transactions to the Home network. | hexidecimal beginning with "0x" | | `HOME_POLLING_INTERVAL` | The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. | integer | @@ -199,11 +199,11 @@ where the _watcher_ could be one of: | `FOREIGN_POLLING_INTERVAL` | The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. | integer | | `HOME_GAS_PRICE_ORACLE_URL` | The URL used to get a JSON response from the gas price prediction oracle for the Home network. The gas price provided by the oracle is used to send the validator's transactions to the RPC node. Since it is assumed that the Home network has a predefined gas price (e.g. the gas price in the Core of POA.Network is `1 GWei`), the gas price oracle parameter can be omitted for such networks. | URL | | `HOME_GAS_PRICE_SPEED_TYPE` | Assuming the gas price oracle responds with the following JSON structure: `{"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}`, this parameter specifies the desirable transaction speed. The speed type can be omitted when `HOME_GAS_PRICE_ORACLE_URL` is not used. | `instant` / `fast` / `standard` / `slow` | -| `HOME_GAS_PRICE_FALLBACK` | The gas price (in GWei) that is used if both the oracle and the fall back gas price specified in the Home Bridge contract are not available. | integer | +| `HOME_GAS_PRICE_FALLBACK` | The gas price (in Wei) that is used if both the oracle and the fall back gas price specified in the Home Bridge contract are not available. | integer | | `HOME_GAS_PRICE_UPDATE_INTERVAL` | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. | integer | | `FOREIGN_GAS_PRICE_ORACLE_URL` | The URL used to get a JSON response from the gas price prediction oracle for the Foreign network. The provided gas price is used to send the validator's transactions to the RPC node. If the Foreign network is Ethereum Foundation mainnet, the oracle URL can be: https://gasprice.poa.network. Otherwise this parameter can be omitted. | URL | | `FOREIGN_GAS_PRICE_SPEED_TYPE` | Assuming the gas price oracle responds with the following JSON structure: `{"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}`, this parameter specifies the desirable transaction speed. The speed type can be omitted when `FOREIGN_GAS_PRICE_ORACLE_URL`is not used. | `instant` / `fast` / `standard` / `slow` | -| `FOREIGN_GAS_PRICE_FALLBACK` | The gas price (in GWei) used if both the oracle and fall back gas price specified in the Foreign Bridge contract are not available. | integer | +| `FOREIGN_GAS_PRICE_FALLBACK` | The gas price (in Wei) used if both the oracle and fall back gas price specified in the Foreign Bridge contract are not available. | integer | | `FOREIGN_GAS_PRICE_UPDATE_INTERVAL` | The interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer | | `VALIDATOR_ADDRESS_PRIVATE_KEY` | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x" | | `HOME_START_BLOCK` | The block number in the Home network used to start watching for events when the bridge instance is run for the first time. Usually this is the same block where the Home Bridge contract is deployed. If a new validator instance is being deployed for an existing set of validators, the block number could be the latest block in the chain. | integer | @@ -212,6 +212,8 @@ where the _watcher_ could be one of: | `REDIS_URL` | Redis DB URL used by watchers and senders to communicate to the database. Typically set to: `redis://127.0.0.1:6379`. | local URL | | `REDIS_LOCK_TTL` | Threshold in milliseconds for locking a resource in the Redis DB. Until the threshold is exceeded, the resource is unlocked. Usually it is `1000`. | integer | | `ALLOW_HTTP` | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no` | +| `LOG_LEVEL` | Set the level of details in the logs. | `trace` / `debug` / `info` / `warn` / `error` / `fatal` | +| `MAX_PROCESSING_TIME` | The workers processes will be killed if this amount of time (in milliseconds) is ellapsed before they finish processing. It is recommended to set this value to 4 times the value of the longest polling time (set with the `HOME_POLLING_INTERVAL` and `FOREIGN_POLLING_INTERVAL` variables). To disable this, set the time to 0. | integer | ### Useful Commands for Development @@ -250,15 +252,22 @@ See the [E2E README](/e2e) for instructions. When running the processes, the following commands can be used to test functionality. -- To send deposits to a home contract run `node scripts/sendUserTxToHome.js ` (or `docker-compose run bridge node scripts/sendUserTxToHome.js `), where `` is how many tx will be sent out to deposit. +- To send deposits to a home contract run `node scripts/native_to_erc20/sendHome.js ` (or `docker-compose run bridge node scripts/native_to_erc20/sendHome.js `), where `` is how many tx will be sent out to deposit. -- To send withdrawals to a foreign contract run `node scripts/sendUserTxToForeign.js ` (or `docker-compose run bridge node scripts/sendUserTxToForeign.js `), where `` is how many tx will be sent out to withdraw. +- To send withdrawals to a foreign contract run `node scripts/native_to_erc20/sendForeign.js ` (or `docker-compose run bridge node scripts/native_to_erc20/sendForeign.js `), where `` is how many tx will be sent out to withdraw. ### ERC20-to-ERC20 Mode Testing -- To deposit from a Foreign to a Home contract run `node scripts/sendUserTxToErcForeign.js `. +- To deposit from a Foreign to a Home contract run `node scripts/erc20_to_erc20/sendForeign.js `. + +- To make withdrawal to Home from a Foreign contract run `node scripts/erc20_to_erc20/sendHome.js `. + +### ERC20-to-Native Mode Testing + +- To deposit from a Foreign to a Home contract run `node scripts/erc20_to_native/sendForeign.js `. + +- To make withdrawal to Home from a Foreign contract run `node scripts/erc20_to_native/sendHome.js `. -- To make withdrawal to Home from a Foreign contract run `node scripts/sendUserTxToErcHome.js `. ### Configuration parameters for testing @@ -270,11 +279,10 @@ When running the processes, the following commands can be used to test functiona | `USER_ADDRESS_PRIVATE_KEY` | A private key belonging to the account. | | `HOME_BRIDGE_ADDRESS` | Address of the bridge in the Home network to send transactions. | | `HOME_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Home network. This should be greater than or equal to the value specified in the `poa-bridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. | +| `HOME_TEST_TX_GAS_PRICE` | The gas price (in Wei) that is used to send transactions in the Home network . | | `FOREIGN_BRIDGE_ADDRESS` | Address of the bridge in the Foreign network to send transactions. | | `FOREIGN_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Foreign network. This should be greater than or equal to the value specified in the `poa-bridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. | -| `ERC20_TOKEN_ADDRESS` | An address of the token deployed on the Foreign side for `ERC20-to-ERC20` mode. Omit this parameter with other bridge modes. | -| `BRIDGEABLE_TOKEN_ADDRESS` | An address of the token deployed on the Foreign side for `Native-to-ERC20` mode or on the Home side for `ERC20-to-ERC20` (specified as erc677 in the `poa-bridge-contracts/deploy/bridgeDeploymentResults.json` file). | - +| `FOREIGN_TEST_TX_GAS_PRICE` | The gas price (in Wei) that is used to send transactions in the Foreign network . | ## Contributing diff --git a/abis/ForeignBridgeErcToNative.abi.json b/abis/ForeignBridgeErcToNative.abi.json new file mode 100644 index 0000000..45fc322 --- /dev/null +++ b/abis/ForeignBridgeErcToNative.abi.json @@ -0,0 +1,452 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "_txHash", + "type": "bytes32" + } + ], + "name": "relayedMessages", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "vs", + "type": "uint8[]" + }, + { + "name": "rs", + "type": "bytes32[]" + }, + { + "name": "ss", + "type": "bytes32[]" + }, + { + "name": "message", + "type": "bytes" + } + ], + "name": "executeSignatures", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_day", + "type": "uint256" + } + ], + "name": "totalSpentPerDay", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCurrentDay", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "requiredBlockConfirmations", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "dailyLimit", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "requiredSignatures", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "deployedAtBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBridgeInterfacesVersion", + "outputs": [ + { + "name": "major", + "type": "uint64" + }, + { + "name": "minor", + "type": "uint64" + }, + { + "name": "patch", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minPerTx", + "type": "uint256" + } + ], + "name": "setMinPerTx", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockConfirmations", + "type": "uint256" + } + ], + "name": "setRequiredBlockConfirmations", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_dailyLimit", + "type": "uint256" + } + ], + "name": "setDailyLimit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_gasPrice", + "type": "uint256" + } + ], + "name": "setGasPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_maxPerTx", + "type": "uint256" + } + ], + "name": "setMaxPerTx", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minPerTx", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withinLimit", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxPerTx", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "gasPrice", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "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": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "gasPrice", + "type": "uint256" + } + ], + "name": "GasPriceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "requiredBlockConfirmations", + "type": "uint256" + } + ], + "name": "RequiredBlockConfirmationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "newLimit", + "type": "uint256" + } + ], + "name": "DailyLimitChanged", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "_validatorContract", + "type": "address" + }, + { + "name": "_erc20token", + "type": "address" + }, + { + "name": "_requiredBlockConfirmations", + "type": "uint256" + }, + { + "name": "_gasPrice", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBridgeMode", + "outputs": [ + { + "name": "_data", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_token", + "type": "address" + }, + { + "name": "_to", + "type": "address" + } + ], + "name": "claimTokens", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "erc20token", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/abis/HomeBridgeErcToNative.abi.json b/abis/HomeBridgeErcToNative.abi.json new file mode 100644 index 0000000..53eefd4 --- /dev/null +++ b/abis/HomeBridgeErcToNative.abi.json @@ -0,0 +1,716 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "_message", + "type": "bytes32" + } + ], + "name": "numMessagesSigned", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + }, + { + "name": "_index", + "type": "uint256" + } + ], + "name": "signature", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_day", + "type": "uint256" + } + ], + "name": "totalSpentPerDay", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCurrentDay", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "requiredBlockConfirmations", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "requiredMessageLength", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_hash", + "type": "bytes32" + } + ], + "name": "message", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "signature", + "type": "bytes" + }, + { + "name": "message", + "type": "bytes" + } + ], + "name": "submitSignature", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "dailyLimit", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_token", + "type": "address" + }, + { + "name": "_to", + "type": "address" + } + ], + "name": "claimTokens", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_withdrawal", + "type": "bytes32" + } + ], + "name": "numAffirmationsSigned", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_withdrawal", + "type": "bytes32" + } + ], + "name": "affirmationsSigned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "requiredSignatures", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_message", + "type": "bytes32" + } + ], + "name": "messagesSigned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "transactionHash", + "type": "bytes32" + } + ], + "name": "executeAffirmation", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "deployedAtBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBridgeInterfacesVersion", + "outputs": [ + { + "name": "major", + "type": "uint64" + }, + { + "name": "minor", + "type": "uint64" + }, + { + "name": "patch", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minPerTx", + "type": "uint256" + } + ], + "name": "setMinPerTx", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockConfirmations", + "type": "uint256" + } + ], + "name": "setRequiredBlockConfirmations", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_dailyLimit", + "type": "uint256" + } + ], + "name": "setDailyLimit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_gasPrice", + "type": "uint256" + } + ], + "name": "setGasPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_maxPerTx", + "type": "uint256" + } + ], + "name": "setMaxPerTx", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minPerTx", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withinLimit", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxPerTx", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "gasPrice", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_number", + "type": "uint256" + } + ], + "name": "isAlreadyProcessed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "UserRequestForSignature", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "transactionHash", + "type": "bytes32" + } + ], + "name": "AffirmationCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "name": "messageHash", + "type": "bytes32" + } + ], + "name": "SignedForUserRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "signer", + "type": "address" + }, + { + "indexed": false, + "name": "transactionHash", + "type": "bytes32" + } + ], + "name": "SignedForAffirmation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "authorityResponsibleForRelay", + "type": "address" + }, + { + "indexed": false, + "name": "messageHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "NumberOfCollectedSignatures", + "type": "uint256" + } + ], + "name": "CollectedSignatures", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "gasPrice", + "type": "uint256" + } + ], + "name": "GasPriceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "requiredBlockConfirmations", + "type": "uint256" + } + ], + "name": "RequiredBlockConfirmationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "newLimit", + "type": "uint256" + } + ], + "name": "DailyLimitChanged", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "_validatorContract", + "type": "address" + }, + { + "name": "_dailyLimit", + "type": "uint256" + }, + { + "name": "_maxPerTx", + "type": "uint256" + }, + { + "name": "_minPerTx", + "type": "uint256" + }, + { + "name": "_homeGasPrice", + "type": "uint256" + }, + { + "name": "_requiredBlockConfirmations", + "type": "uint256" + }, + { + "name": "_blockReward", + "type": "address" + } + ], + "name": "initialize", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBridgeMode", + "outputs": [ + { + "name": "_data", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "blockRewardContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalBurntCoins", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockReward", + "type": "address" + } + ], + "name": "setBlockRewardContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/config/affirmation-request-watcher.config.js b/config/affirmation-request-watcher.config.js index b1961aa..24e770d 100644 --- a/config/affirmation-request-watcher.config.js +++ b/config/affirmation-request-watcher.config.js @@ -2,25 +2,26 @@ require('dotenv').config() const baseConfig = require('./base.config') const erc20Abi = require('../abis/ERC20.abi') -const id = baseConfig.isErcToErc ? 'erc-affirmation-request' : 'affirmation-request' +const id = `${baseConfig.id}-affirmation-request` -module.exports = baseConfig.isErcToErc - ? { - ...baseConfig.bridgeConfig, - ...baseConfig.foreignConfig, - event: 'Transfer', - eventContractAddress: process.env.ERC20_TOKEN_ADDRESS, - eventAbi: erc20Abi, - eventFilter: { to: process.env.FOREIGN_BRIDGE_ADDRESS }, - queue: 'home', - name: `watcher-${id}`, - id - } - : { - ...baseConfig.bridgeConfig, - ...baseConfig.foreignConfig, - event: 'UserRequestForAffirmation', - queue: 'home', - name: `watcher-${id}`, - id - } +module.exports = + baseConfig.id === 'erc-erc' || baseConfig.id === 'erc-native' + ? { + ...baseConfig.bridgeConfig, + ...baseConfig.foreignConfig, + event: 'Transfer', + eventContractAddress: process.env.ERC20_TOKEN_ADDRESS, + eventAbi: erc20Abi, + eventFilter: { to: process.env.FOREIGN_BRIDGE_ADDRESS }, + queue: 'home', + name: `watcher-${id}`, + id + } + : { + ...baseConfig.bridgeConfig, + ...baseConfig.foreignConfig, + event: 'UserRequestForAffirmation', + queue: 'home', + name: `watcher-${id}`, + id + } diff --git a/config/base.config.js b/config/base.config.js index 171113f..ed3799d 100644 --- a/config/base.config.js +++ b/config/base.config.js @@ -1,24 +1,68 @@ require('dotenv').config() +const { toBN } = require('web3').utils const { web3Home, web3Foreign } = require('../src/services/web3') +const { privateKeyToAddress } = require('../src/utils/utils') -const homeNativeAbi = require('../abis/HomeBridgeNativeToErc.abi') -const foreignNativeAbi = require('../abis/ForeignBridgeNativeToErc.abi') +const homeNativeErcAbi = require('../abis/HomeBridgeNativeToErc.abi') +const foreignNativeErcAbi = require('../abis/ForeignBridgeNativeToErc.abi') -const homeErcAbi = require('../abis/HomeBridgeErcToErc.abi') -const foreignErcAbi = require('../abis/ForeignBridgeErcToErc.abi') +const homeErcErcAbi = require('../abis/HomeBridgeErcToErc.abi') +const foreignErcErcAbi = require('../abis/ForeignBridgeErcToErc.abi') -const isErcToErc = process.env.BRIDGE_MODE && process.env.BRIDGE_MODE === 'ERC_TO_ERC' +const homeErcNativeAbi = require('../abis/HomeBridgeErcToNative.abi') +const foreignErcNativeAbi = require('../abis/ForeignBridgeErcToNative.abi') -const homeAbi = isErcToErc ? homeErcAbi : homeNativeAbi -const foreignAbi = isErcToErc ? foreignErcAbi : foreignNativeAbi +const { VALIDATOR_ADDRESS, VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env + +let homeAbi +let foreignAbi +let id + +switch (process.env.BRIDGE_MODE) { + case 'NATIVE_TO_ERC': + homeAbi = homeNativeErcAbi + foreignAbi = foreignNativeErcAbi + id = 'native-erc' + break + case 'ERC_TO_ERC': + homeAbi = homeErcErcAbi + foreignAbi = foreignErcErcAbi + id = 'erc-erc' + break + case 'ERC_TO_NATIVE': + homeAbi = homeErcNativeAbi + foreignAbi = foreignErcNativeAbi + id = 'erc-native' + break + default: + if (process.env.NODE_ENV !== 'test') { + throw new Error(`Bridge Mode: ${process.env.BRIDGE_MODE} not supported.`) + } else { + homeAbi = homeErcNativeAbi + foreignAbi = foreignErcNativeAbi + id = 'erc-native' + } +} + +let maxProcessingTime = null +if (String(process.env.MAX_PROCESSING_TIME) === '0') { + maxProcessingTime = 0 +} else if (!process.env.MAX_PROCESSING_TIME) { + maxProcessingTime = + 4 * Math.max(process.env.HOME_POLLING_INTERVAL, process.env.FOREIGN_POLLING_INTERVAL) +} else { + maxProcessingTime = Number(process.env.MAX_PROCESSING_TIME) +} const bridgeConfig = { homeBridgeAddress: process.env.HOME_BRIDGE_ADDRESS, homeBridgeAbi: homeAbi, foreignBridgeAddress: process.env.FOREIGN_BRIDGE_ADDRESS, foreignBridgeAbi: foreignAbi, - eventFilter: {} + eventFilter: {}, + validatorAddress: VALIDATOR_ADDRESS || privateKeyToAddress(VALIDATOR_ADDRESS_PRIVATE_KEY), + maxProcessingTime } const homeConfig = { @@ -27,7 +71,7 @@ const homeConfig = { bridgeContractAddress: process.env.HOME_BRIDGE_ADDRESS, bridgeAbi: homeAbi, pollingInterval: process.env.HOME_POLLING_INTERVAL, - startBlock: process.env.HOME_START_BLOCK, + startBlock: toBN(process.env.HOME_START_BLOCK || 0), web3: web3Home } @@ -37,7 +81,7 @@ const foreignConfig = { bridgeContractAddress: process.env.FOREIGN_BRIDGE_ADDRESS, bridgeAbi: foreignAbi, pollingInterval: process.env.FOREIGN_POLLING_INTERVAL, - startBlock: process.env.FOREIGN_START_BLOCK, + startBlock: toBN(process.env.FOREIGN_START_BLOCK || 0), web3: web3Foreign } @@ -45,5 +89,5 @@ module.exports = { bridgeConfig, homeConfig, foreignConfig, - isErcToErc + id } diff --git a/config/collected-signatures-watcher.config.js b/config/collected-signatures-watcher.config.js index dcca2a7..cdffa34 100644 --- a/config/collected-signatures-watcher.config.js +++ b/config/collected-signatures-watcher.config.js @@ -1,6 +1,6 @@ const baseConfig = require('./base.config') -const id = baseConfig.isErcToErc ? 'erc-collected-signatures' : 'collected-signatures' +const id = `${baseConfig.id}-collected-signatures` module.exports = { ...baseConfig.bridgeConfig, diff --git a/config/foreign-sender.config.js b/config/foreign-sender.config.js index 472c873..43fab9a 100644 --- a/config/foreign-sender.config.js +++ b/config/foreign-sender.config.js @@ -1,8 +1,10 @@ require('dotenv').config() +const baseConfig = require('./base.config') const { web3Foreign } = require('../src/services/web3') module.exports = { + ...baseConfig.bridgeConfig, queue: 'foreign', id: 'foreign', name: 'sender-foreign', diff --git a/config/home-sender.config.js b/config/home-sender.config.js index 6aa65aa..b9a147e 100644 --- a/config/home-sender.config.js +++ b/config/home-sender.config.js @@ -1,8 +1,10 @@ require('dotenv').config() +const baseConfig = require('./base.config') const { web3Home } = require('../src/services/web3') module.exports = { + ...baseConfig.bridgeConfig, queue: 'home', id: 'home', name: 'sender-home', diff --git a/config/signature-request-watcher.config.js b/config/signature-request-watcher.config.js index d80e371..a487c43 100644 --- a/config/signature-request-watcher.config.js +++ b/config/signature-request-watcher.config.js @@ -1,6 +1,6 @@ const baseConfig = require('./base.config') -const id = baseConfig.isErcToErc ? 'erc-signature-request' : 'signature-request' +const id = `${baseConfig.id}-signature-request` module.exports = { ...baseConfig.bridgeConfig, diff --git a/docs/stress-testing.md b/docs/stress-testing.md index 99a8b80..4bc89b8 100644 --- a/docs/stress-testing.md +++ b/docs/stress-testing.md @@ -84,3 +84,25 @@ This will generate a block with 1000 transactions that the bridge will process. After doing all of this, run `node scripts/compute-stats.js`. This will print some statistics about the processed transactions. + +### Statistics results + +**Signature Requests** + +Tests |1|2|3|4|5|AVG +---|---|---|---|---|---|--- +count|1000|1000|1000|1000|1000|1000 +mean|12112|17714|15377|16468|15745|15483 +median|11968|17925|15518|16994|15931|15667 +min|5584|6423|5803|6121|6090|6004 +max|18685|27554|23844|25310|24198|23918 + +**Collected Signatures** + +Tests |1|2|3|4|5|AVG +---|---|---|---|---|---|--- +count|1000|1000|1000|1000|1000|1000 +mean|8564|5399|6653|9684|9605|7981 +median|8771|5971|7295|9854|10056|8389 +min|7301|2783|3003|8331|7165|5716 +max|9156|7453|9492|10889|10687|9535 diff --git a/e2e/Dockerfile b/e2e/Dockerfile index c9f487c..ad1a5b0 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -12,7 +12,7 @@ RUN mkdir submodules && \ mv poa-bridge-contracts submodules && \ cd submodules/poa-bridge-contracts && \ git fetch && \ - git checkout 5d9f5946cda6043d7584b6da49693f7d3391b78d + git checkout 2.1.0 RUN npm install --unsafe-perm diff --git a/e2e/deploy.js b/e2e/deploy.js index b41e806..90ff828 100644 --- a/e2e/deploy.js +++ b/e2e/deploy.js @@ -14,3 +14,10 @@ shell.cd(deployContractsDir) shell.rm('.env') shell.cp(path.join(envsDir, 'erc-contracts-deploy.env'), path.join(deployContractsDir, '.env')) shell.exec('node deploy.js') +shell.rm('.env') +shell.cp( + path.join(envsDir, 'erc-native-contracts-deploy.env'), + path.join(deployContractsDir, '.env') +) +shell.exec('node src/utils/deployBlockReward.js') +shell.exec('node deploy.js') diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index e447ea4..62ee91f 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -18,6 +18,7 @@ services: build: .. environment: - NODE_ENV=production + - BRIDGE_MODE=NATIVE_TO_ERC - QUEUE_URL=amqp://rabbit - REDIS_URL=redis://redis - HOME_RPC_URL=http://parity1:8545 @@ -28,8 +29,14 @@ services: - VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b - VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 - REDIS_LOCK_TTL=1000 - - GAS_PRICE_SPEED_TYPE=standard - - GAS_PRICE_FALLBACK=1 + - HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/ + - HOME_GAS_PRICE_SPEED_TYPE=standard + - HOME_GAS_PRICE_FALLBACK=1000000000 + - 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 - HOME_POLLING_INTERVAL=500 - FOREIGN_POLLING_INTERVAL=500 - ALLOW_HTTP=yes @@ -44,23 +51,51 @@ services: - HOME_RPC_URL=http://parity1:8545 - FOREIGN_RPC_URL=http://parity2:8545 - HOME_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E - - FOREIGN_BRIDGE_ADDRESS=0xD0B9745831dDA9cbb47D0dEa904972cDcecc52e8 - - ERC20_TOKEN_ADDRESS=0x7777D2BF48993088dC1ceD832863b80427Ff5Ec9 + - FOREIGN_BRIDGE_ADDRESS=0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127 + - ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f - BRIDGEABLE_TOKEN_ADDRESS=0x792455a6bCb62Ed4C4362D323E0590654CA4765c - VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b - VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 - REDIS_LOCK_TTL=1000 - - GAS_PRICE_SPEED_TYPE=standard - - GAS_PRICE_FALLBACK=1 + - HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/ + - HOME_GAS_PRICE_SPEED_TYPE=standard + - HOME_GAS_PRICE_FALLBACK=1000000000 + - 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 + - HOME_POLLING_INTERVAL=500 + - FOREIGN_POLLING_INTERVAL=500 + - ALLOW_HTTP=yes + command: "true" + bridge-erc-native: + build: .. + environment: + - NODE_ENV=production + - BRIDGE_MODE=ERC_TO_NATIVE + - QUEUE_URL=amqp://rabbit + - REDIS_URL=redis://redis + - HOME_RPC_URL=http://parity1:8545 + - FOREIGN_RPC_URL=http://parity2:8545 + - HOME_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda + - FOREIGN_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda + - ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f + - BRIDGEABLE_TOKEN_ADDRESS=0x792455a6bCb62Ed4C4362D323E0590654CA4765c + - VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b + - VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 + - REDIS_LOCK_TTL=1000 + - HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/ + - HOME_GAS_PRICE_SPEED_TYPE=standard + - HOME_GAS_PRICE_FALLBACK=1000000000 + - 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 - HOME_POLLING_INTERVAL=500 - FOREIGN_POLLING_INTERVAL=500 - ALLOW_HTTP=yes - - NUMBER_OF_WITHDRAWALS_TO_SEND=3 - - NUMBER_OF_DEPOSITS_TO_SEND=3 - - USER_ADDRESS=0xbb140FbA6242a1c3887A7823F7750a73101383e3 - - USER_ADDRESS_PRIVATE_KEY=63e48a8ba0b99e0377c6b483af4a072cbca5ffbcfdac77be72e69f4960125800 - - HOME_MIN_AMOUNT_PER_TX=0.01 - - FOREIGN_MIN_AMOUNT_PER_TX=0.01 command: "true" e2e: build: . diff --git a/e2e/envs/contracts-deploy.env b/e2e/envs/contracts-deploy.env index b5c0370..0a07499 100644 --- a/e2e/envs/contracts-deploy.env +++ b/e2e/envs/contracts-deploy.env @@ -2,7 +2,8 @@ BRIDGE_MODE=NATIVE_TO_ERC DEPLOYMENT_ACCOUNT_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 DEPLOYMENT_GAS_LIMIT=4000000 -DEPLOYMENT_GAS_PRICE=10 +HOME_DEPLOYMENT_GAS_PRICE=10000000000 +FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000 GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50 BRIDGEABLE_TOKEN_NAME="Your New Bridged Token" @@ -17,7 +18,7 @@ HOME_DAILY_LIMIT=30000000000000000000000000 HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000 HOME_MIN_AMOUNT_PER_TX=10000000000000000 HOME_REQUIRED_BLOCK_CONFIRMATIONS=1 -HOME_GAS_PRICE=10 +HOME_GAS_PRICE=1000000000 FOREIGN_RPC_URL=http://parity2:8545 FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b @@ -27,7 +28,7 @@ FOREIGN_DAILY_LIMIT=15000000000000000000000000 FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000 FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000 FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1 -FOREIGN_GAS_PRICE=10 +FOREIGN_GAS_PRICE=10000000000 REQUIRED_NUMBER_OF_VALIDATORS=1 VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" diff --git a/e2e/envs/erc-contracts-deploy.env b/e2e/envs/erc-contracts-deploy.env index 5aa00e2..869261e 100644 --- a/e2e/envs/erc-contracts-deploy.env +++ b/e2e/envs/erc-contracts-deploy.env @@ -2,7 +2,8 @@ BRIDGE_MODE=ERC_TO_ERC DEPLOYMENT_ACCOUNT_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 DEPLOYMENT_GAS_LIMIT=4000000 -DEPLOYMENT_GAS_PRICE=10 +HOME_DEPLOYMENT_GAS_PRICE=10000000000 +FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000 GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50 BRIDGEABLE_TOKEN_NAME="Your New Bridged Token" @@ -17,7 +18,7 @@ HOME_DAILY_LIMIT=30000000000000000000000000 HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000 HOME_MIN_AMOUNT_PER_TX=10000000000000000 HOME_REQUIRED_BLOCK_CONFIRMATIONS=1 -HOME_GAS_PRICE=10 +HOME_GAS_PRICE=1000000000 FOREIGN_RPC_URL=http://parity2:8545 FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b @@ -27,8 +28,8 @@ FOREIGN_DAILY_LIMIT=15000000000000000000000000 FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000 FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000 FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1 -FOREIGN_GAS_PRICE=10 -ERC20_TOKEN_ADDRESS=0x7777D2BF48993088dC1ceD832863b80427Ff5Ec9 +FOREIGN_GAS_PRICE=10000000000 +ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f REQUIRED_NUMBER_OF_VALIDATORS=1 VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" diff --git a/e2e/envs/erc-native-contracts-deploy.env b/e2e/envs/erc-native-contracts-deploy.env new file mode 100644 index 0000000..fd86652 --- /dev/null +++ b/e2e/envs/erc-native-contracts-deploy.env @@ -0,0 +1,36 @@ +BRIDGE_MODE=ERC_TO_NATIVE +DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 +DEPLOYMENT_GAS_LIMIT=4000000 +HOME_DEPLOYMENT_GAS_PRICE=10000000000 +FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000 +GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50 + +BRIDGEABLE_TOKEN_NAME="Your New Bridged Token" +BRIDGEABLE_TOKEN_SYMBOL="TEST" +BRIDGEABLE_TOKEN_DECIMALS="18" + +HOME_RPC_URL=http://parity1:8545 +HOME_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +HOME_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +HOME_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +HOME_DAILY_LIMIT=30000000000000000000000000 +HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000 +HOME_MIN_AMOUNT_PER_TX=10000000000000000 +HOME_REQUIRED_BLOCK_CONFIRMATIONS=1 +HOME_GAS_PRICE=1000000000 + +FOREIGN_RPC_URL=http://parity2:8545 +FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +FOREIGN_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b +FOREIGN_DAILY_LIMIT=15000000000000000000000000 +FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000 +FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000 +FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1 +FOREIGN_GAS_PRICE=10000000000 + +BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977 +ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f + +REQUIRED_NUMBER_OF_VALIDATORS=1 +VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" diff --git a/e2e/parity/Dockerfile b/e2e/parity/Dockerfile index 81113df..9f1f7ad 100644 --- a/e2e/parity/Dockerfile +++ b/e2e/parity/Dockerfile @@ -1,4 +1,4 @@ -FROM parity/parity:stable +FROM parity/parity:v1.11.11 WORKDIR /stuff diff --git a/e2e/run-tests.sh b/e2e/run-tests.sh index 094bce3..7c243ff 100755 --- a/e2e/run-tests.sh +++ b/e2e/run-tests.sh @@ -6,6 +6,9 @@ docker-compose run -d bridge npm run watcher:affirmation-request docker-compose run -d bridge-erc npm run watcher:signature-request docker-compose run -d bridge-erc npm run watcher:collected-signatures docker-compose run -d bridge-erc npm run watcher:affirmation-request +docker-compose run -d bridge-erc-native npm run watcher:signature-request +docker-compose run -d bridge-erc-native npm run watcher:collected-signatures +docker-compose run -d bridge-erc-native npm run watcher:affirmation-request docker-compose run -d bridge npm run sender:home docker-compose run -d bridge npm run sender:foreign docker-compose run e2e npm start diff --git a/e2e/test/ercToErc.js b/e2e/test/ercToErc.js index 4dfc5da..30459cb 100644 --- a/e2e/test/ercToErc.js +++ b/e2e/test/ercToErc.js @@ -11,7 +11,7 @@ const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity1:8545') const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity2:8545')) const HOME_BRIDGE_ADDRESS = '0x1feB40aD9420b186F019A717c37f5546165d411E' -const FOREIGN_BRIDGE_ADDRESS = '0xD0B9745831dDA9cbb47D0dEa904972cDcecc52e8' +const FOREIGN_BRIDGE_ADDRESS = '0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127' const { toBN } = foreignWeb3.utils @@ -21,7 +21,7 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey) const tokenAbi = require(path.join(abisDir, 'ERC677BridgeToken.json')).abi const erc20Token = new foreignWeb3.eth.Contract( tokenAbi, - '0x7777D2BF48993088dC1ceD832863b80427Ff5Ec9' + '0x3C665A31199694Bf723fD08844AD290207B5797f' ) const erc677Token = new homeWeb3.eth.Contract( tokenAbi, diff --git a/e2e/test/ercToNative.js b/e2e/test/ercToNative.js new file mode 100644 index 0000000..1c739b9 --- /dev/null +++ b/e2e/test/ercToNative.js @@ -0,0 +1,102 @@ +const path = require('path') +const Web3 = require('web3') +const assert = require('assert') +const promiseRetry = require('promise-retry') +const { user } = require('../constants.json') +const { generateNewBlock } = require('../utils/utils') + +const abisDir = path.join(__dirname, '..', 'submodules/poa-bridge-contracts/build/contracts') + +const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity1:8545')) +const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity2:8545')) + +const HOME_BRIDGE_ADDRESS = '0x488Af810997eD1730cB3a3918cD83b3216E6eAda' +const FOREIGN_BRIDGE_ADDRESS = '0x488Af810997eD1730cB3a3918cD83b3216E6eAda' + +const { toBN } = foreignWeb3.utils + +homeWeb3.eth.accounts.wallet.add(user.privateKey) +foreignWeb3.eth.accounts.wallet.add(user.privateKey) + +const tokenAbi = require(path.join(abisDir, 'ERC677BridgeToken.json')).abi +const erc20Token = new foreignWeb3.eth.Contract( + tokenAbi, + '0x3C665A31199694Bf723fD08844AD290207B5797f' +) + +describe('erc to native', () => { + it('should convert tokens in foreign to coins in home', async () => { + const balance = await erc20Token.methods.balanceOf(user.address).call() + const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + assert(!toBN(balance).isZero(), 'Account should have tokens') + + // send tokens to foreign bridge + await erc20Token.methods + .transfer(FOREIGN_BRIDGE_ADDRESS, homeWeb3.utils.toWei('0.01')) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async retry => { + const balance = await homeWeb3.eth.getBalance(user.address) + if (toBN(balance).lte(toBN(originalBalanceOnHome))) { + retry() + } + }) + }) + it('should convert coins in home to tokens in foreign', async () => { + const originalBalance = await erc20Token.methods.balanceOf(user.address).call() + + // check that account has tokens in home chain + const balance = await homeWeb3.eth.getBalance(user.address) + assert(!toBN(balance).isZero(), 'Account should have tokens') + + // send transaction to home bridge + const depositTx = await homeWeb3.eth.sendTransaction({ + from: user.address, + to: HOME_BRIDGE_ADDRESS, + gasPrice: '1', + gas: '1000000', + value: homeWeb3.utils.toWei('0.01') + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(homeWeb3, user.address) + + // The bridge should create a new transaction with a CollectedSignatures + // event so we generate another trivial transaction + await promiseRetry( + async retry => { + const lastBlockNumber = await homeWeb3.eth.getBlockNumber() + if (lastBlockNumber >= depositTx.blockNumber + 2) { + await generateNewBlock(homeWeb3, user.address) + } else { + retry() + } + }, + { + forever: true, + factor: 1, + minTimeout: 500 + } + ) + + // check that balance increases + await promiseRetry(async retry => { + const balance = await erc20Token.methods.balanceOf(user.address).call() + if (toBN(balance).lte(toBN(originalBalance))) { + retry() + } + }) + }) +}) diff --git a/scripts/sendUserTxToErcForeign.js b/scripts/erc20_to_erc20/sendForeign.js similarity index 72% rename from scripts/sendUserTxToErcForeign.js rename to scripts/erc20_to_erc20/sendForeign.js index b093b7c..ecf7efd 100644 --- a/scripts/sendUserTxToErcForeign.js +++ b/scripts/erc20_to_erc20/sendForeign.js @@ -1,55 +1,34 @@ const path = require('path') require('dotenv').config({ - path: path.join(__dirname, '../.env') + path: path.join(__dirname, '../../.env') }) const Web3 = require('web3') const Web3Utils = require('web3-utils') -const rpcUrlsManager = require('../src/services/getRpcUrlsManager') -const { sendTx, sendRawTx } = require('../src/tx/sendTx') +const rpcUrlsManager = require('../../src/services/getRpcUrlsManager') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') const { USER_ADDRESS, USER_ADDRESS_PRIVATE_KEY, FOREIGN_BRIDGE_ADDRESS, FOREIGN_MIN_AMOUNT_PER_TX, - ERC20_TOKEN_ADDRESS + FOREIGN_TEST_TX_GAS_PRICE } = process.env const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || process.env.NUMBER_OF_DEPOSITS_TO_SEND || 1 -const ERC20_ABI = [ - { - constant: false, - inputs: [ - { - name: '_to', - type: 'address' - }, - { - name: '_value', - type: 'uint256' - } - ], - name: 'transfer', - outputs: [ - { - name: '', - type: 'bool' - } - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function' - } -] +const ERC20_ABI = require('../../abis/ERC20.abi') +const BRIDGE_ABI = require('../../abis/ForeignBridgeErcToErc.abi') const foreignRpcUrl = rpcUrlsManager.foreignUrls[0] const foreignProvider = new Web3.providers.HttpProvider(foreignRpcUrl) const web3Foreign = new Web3(foreignProvider) -const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, ERC20_TOKEN_ADDRESS) - async function main() { + const bridge = new web3Foreign.eth.Contract(BRIDGE_ABI, FOREIGN_BRIDGE_ADDRESS) + const ERC20_TOKEN_ADDRESS = await bridge.methods.erc20token().call() + const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, ERC20_TOKEN_ADDRESS) + try { const foreignChaindId = await sendRawTx({ chain: 'foreign', @@ -75,7 +54,7 @@ async function main() { privateKey: USER_ADDRESS_PRIVATE_KEY, data, nonce, - gasPrice: '1', + gasPrice: FOREIGN_TEST_TX_GAS_PRICE, amount: '0', gasLimit, to: ERC20_TOKEN_ADDRESS, diff --git a/scripts/sendUserTxToErcHome.js b/scripts/erc20_to_erc20/sendHome.js similarity index 76% rename from scripts/sendUserTxToErcHome.js rename to scripts/erc20_to_erc20/sendHome.js index 1461d99..0023481 100644 --- a/scripts/sendUserTxToErcHome.js +++ b/scripts/erc20_to_erc20/sendHome.js @@ -1,18 +1,19 @@ const path = require('path') require('dotenv').config({ - path: path.join(__dirname, '../.env') + path: path.join(__dirname, '../../.env') }) const Web3 = require('web3') const Web3Utils = require('web3-utils') -const rpcUrlsManager = require('../src/services/getRpcUrlsManager') -const { sendTx, sendRawTx } = require('../src/tx/sendTx') +const rpcUrlsManager = require('../../src/services/getRpcUrlsManager') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') +const { isValidAmount } = require('../utils/utils') const { USER_ADDRESS, USER_ADDRESS_PRIVATE_KEY, HOME_BRIDGE_ADDRESS, HOME_MIN_AMOUNT_PER_TX, - BRIDGEABLE_TOKEN_ADDRESS + HOME_TEST_TX_GAS_PRICE } = process.env const NUMBER_OF_WITHDRAWALS_TO_SEND = @@ -48,14 +49,20 @@ const BRIDGEABLE_TOKEN_ABI = [ } ] +const BRIDGE_ABI = require('../../abis/HomeBridgeErcToErc.abi') + const homeRpcUrl = rpcUrlsManager.homeUrls[0] const homeProvider = new Web3.providers.HttpProvider(homeRpcUrl) const web3Home = new Web3(homeProvider) -const erc677 = new web3Home.eth.Contract(BRIDGEABLE_TOKEN_ABI, BRIDGEABLE_TOKEN_ADDRESS) - async function main() { + const bridge = new web3Home.eth.Contract(BRIDGE_ABI, HOME_BRIDGE_ADDRESS) + const BRIDGEABLE_TOKEN_ADDRESS = await bridge.methods.erc677token().call() + const erc677 = new web3Home.eth.Contract(BRIDGEABLE_TOKEN_ABI, BRIDGEABLE_TOKEN_ADDRESS) + try { + await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge) + const homeChainId = await sendRawTx({ chain: 'home', params: [], @@ -80,7 +87,7 @@ async function main() { privateKey: USER_ADDRESS_PRIVATE_KEY, data, nonce, - gasPrice: '1', + gasPrice: HOME_TEST_TX_GAS_PRICE, amount: '0', gasLimit, to: BRIDGEABLE_TOKEN_ADDRESS, diff --git a/scripts/erc20_to_native/sendForeign.js b/scripts/erc20_to_native/sendForeign.js new file mode 100644 index 0000000..a1130a5 --- /dev/null +++ b/scripts/erc20_to_native/sendForeign.js @@ -0,0 +1,74 @@ +const path = require('path') +require('dotenv').config({ + path: path.join(__dirname, '../../.env') +}) +const Web3 = require('web3') +const Web3Utils = require('web3-utils') +const rpcUrlsManager = require('../../src/services/getRpcUrlsManager') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') + +const { + USER_ADDRESS, + USER_ADDRESS_PRIVATE_KEY, + FOREIGN_BRIDGE_ADDRESS, + FOREIGN_MIN_AMOUNT_PER_TX, + FOREIGN_TEST_TX_GAS_PRICE +} = process.env + +const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || process.env.NUMBER_OF_DEPOSITS_TO_SEND || 1 + +const ERC20_ABI = require('../../abis/ERC20.abi') +const BRIDGE_ABI = require('../../abis/ForeignBridgeErcToNative.abi') + +const foreignRpcUrl = rpcUrlsManager.foreignUrls[0] +const foreignProvider = new Web3.providers.HttpProvider(foreignRpcUrl) +const web3Foreign = new Web3(foreignProvider) + +async function main() { + const bridge = new web3Foreign.eth.Contract(BRIDGE_ABI, FOREIGN_BRIDGE_ADDRESS) + const ERC20_TOKEN_ADDRESS = await bridge.methods.erc20token().call() + const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, ERC20_TOKEN_ADDRESS) + + try { + const foreignChaindId = await sendRawTx({ + chain: 'foreign', + params: [], + method: 'net_version' + }) + let nonce = await sendRawTx({ + chain: 'foreign', + method: 'eth_getTransactionCount', + params: [USER_ADDRESS, 'latest'] + }) + nonce = Web3Utils.hexToNumber(nonce) + let actualSent = 0 + for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) { + const gasLimit = await poa20.methods + .transfer(FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX)) + .estimateGas({ from: USER_ADDRESS }) + const data = await poa20.methods + .transfer(FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX)) + .encodeABI({ from: USER_ADDRESS }) + const txHash = await sendTx({ + chain: 'foreign', + privateKey: USER_ADDRESS_PRIVATE_KEY, + data, + nonce, + gasPrice: FOREIGN_TEST_TX_GAS_PRICE, + amount: '0', + gasLimit, + to: ERC20_TOKEN_ADDRESS, + web3: web3Foreign, + chainId: foreignChaindId + }) + if (txHash !== undefined) { + nonce++ + actualSent++ + console.log(actualSent, ' # ', txHash) + } + } + } catch (e) { + console.log(e) + } +} +main() diff --git a/scripts/sendUserTxToHome.js b/scripts/erc20_to_native/sendHome.js similarity index 67% rename from scripts/sendUserTxToHome.js rename to scripts/erc20_to_native/sendHome.js index cfe5df2..137c356 100644 --- a/scripts/sendUserTxToHome.js +++ b/scripts/erc20_to_native/sendHome.js @@ -1,22 +1,29 @@ const path = require('path') require('dotenv').config({ - path: path.join(__dirname, '../.env') + path: path.join(__dirname, '../../.env') }) const Web3Utils = require('web3-utils') -const { web3Home } = require('../src/services/web3') -const { sendTx, sendRawTx } = require('../src/tx/sendTx') +const { web3Home } = require('../../src/services/web3') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') +const { isValidAmount } = require('../utils/utils') +const BRIDGE_ABI = require('../../abis/HomeBridgeErcToNative.abi') const { USER_ADDRESS, USER_ADDRESS_PRIVATE_KEY, HOME_BRIDGE_ADDRESS, - HOME_MIN_AMOUNT_PER_TX + HOME_MIN_AMOUNT_PER_TX, + HOME_TEST_TX_GAS_PRICE } = process.env const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || 1 async function main() { + const bridge = new web3Home.eth.Contract(BRIDGE_ABI, HOME_BRIDGE_ADDRESS) + try { + await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge) + const homeChaindId = await sendRawTx({ chain: 'home', params: [], @@ -35,9 +42,9 @@ async function main() { privateKey: USER_ADDRESS_PRIVATE_KEY, data: '0x', nonce, - gasPrice: '1', + gasPrice: HOME_TEST_TX_GAS_PRICE, amount: HOME_MIN_AMOUNT_PER_TX, - gasLimit: 50000, + gasLimit: 100000, to: HOME_BRIDGE_ADDRESS, web3: web3Home, chainId: homeChaindId diff --git a/scripts/getValidatorStartBlocks.js b/scripts/getValidatorStartBlocks.js index c8bf491..39d8a1f 100644 --- a/scripts/getValidatorStartBlocks.js +++ b/scripts/getValidatorStartBlocks.js @@ -3,18 +3,14 @@ require('dotenv').config({ path: path.join(__dirname, '../.env') }) const Web3 = require('web3') -const HomeNativeABI = require('../abis/HomeBridgeNativeToErc.abi') -const ForeignNativeABI = require('../abis/ForeignBridgeNativeToErc.abi') -const HomeErcABI = require('../abis/HomeBridgeErcToErc.abi') -const ForeignErcABI = require('../abis/ForeignBridgeErcToErc.abi') const bridgeValidatorsABI = require('../abis/BridgeValidators.abi') const rpcUrlsManager = require('../src/services/getRpcUrlsManager') -const isErcToErc = process.env.BRIDGE_MODE && process.env.BRIDGE_MODE === 'ERC_TO_ERC' +const { bridgeConfig } = require('../config/base.config') -const homeABI = isErcToErc ? HomeErcABI : HomeNativeABI -const foreignABI = isErcToErc ? ForeignNativeABI : ForeignErcABI +const homeABI = bridgeConfig.homeBridgeAbi +const foreignABI = bridgeConfig.foreignBridgeAbi async function getStartBlock(rpcUrl, bridgeAddress, bridgeAbi) { try { diff --git a/scripts/sendUserTxToForeign.js b/scripts/native_to_erc20/sendForeign.js similarity index 72% rename from scripts/sendUserTxToForeign.js rename to scripts/native_to_erc20/sendForeign.js index 2266d90..db1184d 100644 --- a/scripts/sendUserTxToForeign.js +++ b/scripts/native_to_erc20/sendForeign.js @@ -1,36 +1,37 @@ const path = require('path') require('dotenv').config({ - path: path.join(__dirname, '../.env') + path: path.join(__dirname, '../../.env') }) const Web3Utils = require('web3-utils') -const { web3Foreign } = require('../src/services/web3') -const { sendTx, sendRawTx } = require('../src/tx/sendTx') +const { web3Foreign } = require('../../src/services/web3') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') +const { isValidAmount } = require('../utils/utils') const { USER_ADDRESS, USER_ADDRESS_PRIVATE_KEY, FOREIGN_BRIDGE_ADDRESS, FOREIGN_MIN_AMOUNT_PER_TX, - ERC20_TOKEN_ADDRESS + FOREIGN_TEST_TX_GAS_PRICE } = process.env const NUMBER_OF_WITHDRAWALS_TO_SEND = process.argv[2] || process.env.NUMBER_OF_WITHDRAWALS_TO_SEND || 1 -const ERC20_ABI = [ +const ERC677_ABI = [ { constant: false, inputs: [ { - name: '_to', + name: '', type: 'address' }, { - name: '_value', + name: '', type: 'uint256' }, { - name: '_data', + name: '', type: 'bytes' } ], @@ -46,11 +47,16 @@ const ERC20_ABI = [ type: 'function' } ] - -const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, ERC20_TOKEN_ADDRESS) +const BRIDGE_ABI = require('../../abis/ForeignBridgeNativeToErc.abi') async function main() { + const bridge = new web3Foreign.eth.Contract(BRIDGE_ABI, FOREIGN_BRIDGE_ADDRESS) + const ERC20_TOKEN_ADDRESS = await bridge.methods.erc677token().call() + const poa20 = new web3Foreign.eth.Contract(ERC677_ABI, ERC20_TOKEN_ADDRESS) + try { + await isValidAmount(FOREIGN_MIN_AMOUNT_PER_TX, bridge) + const foreignChaindId = await sendRawTx({ chain: 'foreign', params: [], @@ -75,7 +81,7 @@ async function main() { privateKey: USER_ADDRESS_PRIVATE_KEY, data, nonce, - gasPrice: '1', + gasPrice: FOREIGN_TEST_TX_GAS_PRICE, amount: '0', gasLimit, to: ERC20_TOKEN_ADDRESS, diff --git a/scripts/native_to_erc20/sendHome.js b/scripts/native_to_erc20/sendHome.js new file mode 100644 index 0000000..de35608 --- /dev/null +++ b/scripts/native_to_erc20/sendHome.js @@ -0,0 +1,62 @@ +const path = require('path') +require('dotenv').config({ + path: path.join(__dirname, '../../.env') +}) +const Web3Utils = require('web3-utils') +const { web3Home } = require('../../src/services/web3') +const { sendTx, sendRawTx } = require('../../src/tx/sendTx') +const { isValidAmount } = require('../utils/utils') +const BRIDGE_ABI = require('../../abis/HomeBridgeNativeToErc.abi') + +const { + USER_ADDRESS, + USER_ADDRESS_PRIVATE_KEY, + HOME_BRIDGE_ADDRESS, + HOME_MIN_AMOUNT_PER_TX, + HOME_TEST_TX_GAS_PRICE +} = process.env + +const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || 1 + +async function main() { + const bridge = new web3Home.eth.Contract(BRIDGE_ABI, HOME_BRIDGE_ADDRESS) + + try { + await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge) + + const homeChaindId = await sendRawTx({ + chain: 'home', + params: [], + method: 'net_version' + }) + let nonce = await sendRawTx({ + chain: 'home', + method: 'eth_getTransactionCount', + params: [USER_ADDRESS, 'latest'] + }) + nonce = Web3Utils.hexToNumber(nonce) + let actualSent = 0 + for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) { + const txHash = await sendTx({ + chain: 'home', + privateKey: USER_ADDRESS_PRIVATE_KEY, + data: '0x', + nonce, + gasPrice: HOME_TEST_TX_GAS_PRICE, + amount: HOME_MIN_AMOUNT_PER_TX, + gasLimit: 100000, + to: HOME_BRIDGE_ADDRESS, + web3: web3Home, + chainId: homeChaindId + }) + if (txHash !== undefined) { + nonce++ + actualSent++ + console.log(actualSent, ' # ', txHash) + } + } + } catch (e) { + console.log(e) + } +} +main() diff --git a/scripts/privateKeyToAddress.js b/scripts/privateKeyToAddress.js new file mode 100644 index 0000000..3ba23ff --- /dev/null +++ b/scripts/privateKeyToAddress.js @@ -0,0 +1,17 @@ +const path = require('path') +require('dotenv').config({ + path: path.join(__dirname, '..', '.env') +}) +const { privateKeyToAddress } = require('../src/utils/utils') +const { EXIT_CODES } = require('../src/utils/constants') + +const privateKey = process.env.VALIDATOR_ADDRESS_PRIVATE_KEY + +if (!privateKey) { + console.error('Environment variable VALIDATOR_ADDRESS_PRIVATE_KEY is not set') + process.exit(EXIT_CODES.GENERAL_ERROR) +} + +const address = privateKeyToAddress(privateKey) + +console.log(address) diff --git a/scripts/resetLastBlock.js b/scripts/resetLastBlock.js index 4ed6be5..57feed4 100644 --- a/scripts/resetLastBlock.js +++ b/scripts/resetLastBlock.js @@ -3,6 +3,8 @@ const path = require('path') require('dotenv').config({ path: path.join(__dirname, '../.env') }) +const { id } = require('../config/base.config') +const { EXIT_CODES } = require('../src/utils/constants') const redis = new Redis(process.env.REDIS_URL) @@ -21,13 +23,11 @@ if (process.argv.length < 4) { function logError(message) { console.log(message) - process.exit(1) + process.exit(EXIT_CODES.GENERAL_ERROR) } function getRedisKey(name) { - const isErcToErc = process.env.BRIDGE_MODE && process.env.BRIDGE_MODE === 'ERC_TO_ERC' - const prefix = isErcToErc ? 'erc-' : '' - return `${prefix}${name}:lastProcessedBlock` + return `${id}-${name}:lastProcessedBlock` } async function main() { diff --git a/scripts/utils/utils.js b/scripts/utils/utils.js new file mode 100644 index 0000000..458746d --- /dev/null +++ b/scripts/utils/utils.js @@ -0,0 +1,18 @@ +const Web3Utils = require('web3-utils') + +async function getMinPerTxLimit(bridge) { + const minPerTx = await bridge.methods.minPerTx().call() + return Web3Utils.fromWei(minPerTx) +} + +async function isValidAmount(amount, bridge) { + const minLimit = await getMinPerTxLimit(bridge) + if (amount < minLimit) { + throw new Error(`The amount per Tx ${amount} should be at least ${minLimit}`) + } +} + +module.exports = { + getMinPerTxLimit, + isValidAmount +} diff --git a/src/events/processAffirmationRequests/estimateGas.js b/src/events/processAffirmationRequests/estimateGas.js index 1749ad5..5146e59 100644 --- a/src/events/processAffirmationRequests/estimateGas.js +++ b/src/events/processAffirmationRequests/estimateGas.js @@ -4,6 +4,9 @@ const { AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') +const logger = require('../../services/logger').child({ + module: 'processAffirmationRequests:estimateGas' +}) async function estimateGas({ web3, @@ -31,6 +34,7 @@ async function estimateGas({ const senderHash = web3.utils.soliditySha3(address, messageHash) // Check if minimum number of validations was already reached + logger.debug('Check if minimum number of validations was already reached') const numAffirmationsSigned = await homeBridge.methods.numAffirmationsSigned(messageHash).call() const alreadyProcessed = await homeBridge.methods .isAlreadyProcessed(numAffirmationsSigned) @@ -41,6 +45,7 @@ async function estimateGas({ } // Check if the message was already signed by this validator + logger.debug('Check if the message was already signed') const alreadySigned = await homeBridge.methods.affirmationsSigned(senderHash).call() if (alreadySigned) { @@ -48,6 +53,7 @@ async function estimateGas({ } // Check if address is validator + logger.debug('Check if address is a validator') const isValidator = await validatorContract.methods.isValidator(address).call() if (!isValidator) { diff --git a/src/events/processAffirmationRequests/index.js b/src/events/processAffirmationRequests/index.js index 6092cc8..8af2771 100644 --- a/src/events/processAffirmationRequests/index.js +++ b/src/events/processAffirmationRequests/index.js @@ -1,9 +1,9 @@ require('dotenv').config() -const logger = require('../../services/logger') +const rootLogger = require('../../services/logger') const { web3Home } = require('../../services/web3') const promiseLimit = require('promise-limit') const bridgeValidatorsABI = require('../../../abis/BridgeValidators.abi') -const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') +const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const estimateGas = require('./estimateGas') const { AlreadyProcessedError, @@ -12,8 +12,6 @@ const { } = require('../../utils/errors') const { HttpListProviderError } = require('http-list-provider') -const { VALIDATOR_ADDRESS } = process.env - const limit = promiseLimit(MAX_CONCURRENT_EVENTS) let validatorContract = null @@ -25,21 +23,30 @@ function processAffirmationRequestsBuilder(config) { const txToSend = [] if (validatorContract === null) { + rootLogger.debug('Getting validator contract address') const validatorContractAddress = await homeBridge.methods.validatorContract().call() + rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained') + validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress) } + rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`) const callbacks = affirmationRequests.map(affirmationRequest => limit(async () => { const { recipient, value } = affirmationRequest.returnValues + const logger = rootLogger.child({ + eventTransactionHash: affirmationRequest.transactionHash + }) + logger.info( - { eventTransactionHash: affirmationRequest.transactionHash, sender: recipient, value }, + { sender: recipient, value }, `Processing affirmationRequest ${affirmationRequest.transactionHash}` ) let gasEstimate try { + logger.debug('Estimate gas') gasEstimate = await estimateGas({ web3: web3Home, homeBridge, @@ -47,25 +54,22 @@ function processAffirmationRequestsBuilder(config) { recipient, value, txHash: affirmationRequest.transactionHash, - address: VALIDATOR_ADDRESS + address: config.validatorAddress }) + logger.debug({ gasEstimate }, 'Gas estimated') } catch (e) { if (e instanceof HttpListProviderError) { throw new Error( 'RPC Connection Error: submitSignature Gas Estimate cannot be obtained.' ) } else if (e instanceof InvalidValidatorError) { - logger.fatal({ address: VALIDATOR_ADDRESS }, 'Invalid validator') - process.exit(10) + logger.fatal({ address: config.validatorAddress }, 'Invalid validator') + process.exit(EXIT_CODES.INCOMPATIBILITY) } else if (e instanceof AlreadySignedError) { - logger.info( - { eventTransactionHash: affirmationRequest.transactionHash }, - `Already signed affirmationRequest ${affirmationRequest.transactionHash}` - ) + logger.info(`Already signed affirmationRequest ${affirmationRequest.transactionHash}`) return } else if (e instanceof AlreadyProcessedError) { logger.info( - { eventTransactionHash: affirmationRequest.transactionHash }, `affirmationRequest ${ affirmationRequest.transactionHash } was already processed by other validators` @@ -79,7 +83,7 @@ function processAffirmationRequestsBuilder(config) { const data = await homeBridge.methods .executeAffirmation(recipient, value, affirmationRequest.transactionHash) - .encodeABI({ from: VALIDATOR_ADDRESS }) + .encodeABI({ from: config.validatorAddress }) txToSend.push({ data, diff --git a/src/events/processCollectedSignatures/estimateGas.js b/src/events/processCollectedSignatures/estimateGas.js index cd49783..7788a99 100644 --- a/src/events/processCollectedSignatures/estimateGas.js +++ b/src/events/processCollectedSignatures/estimateGas.js @@ -6,6 +6,9 @@ const { InvalidValidatorError } = require('../../utils/errors') const { parseMessage } = require('../../utils/message') +const logger = require('../../services/logger').child({ + module: 'processCollectedSignatures:estimateGas' +}) const web3 = new Web3() const { toBN } = Web3.utils @@ -30,6 +33,7 @@ async function estimateGas({ } // check if the message was already processed + logger.debug('Check if the message was already processed') const { txHash } = parseMessage(message) const alreadyProcessed = await foreignBridge.methods.relayedMessages(txHash).call() if (alreadyProcessed) { @@ -37,6 +41,7 @@ async function estimateGas({ } // check if the number of signatures is enough + logger.debug('Check if number of signatures is enough') const requiredSignatures = await validatorContract.methods.requiredSignatures().call() if (toBN(requiredSignatures).gt(toBN(numberOfCollectedSignatures))) { throw new IncompatibleContractError('The number of collected signatures does not match') @@ -45,6 +50,7 @@ async function estimateGas({ // check if all the signatures were made by validators for (let i = 0; i < v.length; i++) { const address = web3.eth.accounts.recover(message, web3.utils.toHex(v[i]), r[i], s[i]) + logger.debug({ address }, 'Check that signature is from a validator') const isValidator = await validatorContract.methods.isValidator(address).call() if (!isValidator) { diff --git a/src/events/processCollectedSignatures/index.js b/src/events/processCollectedSignatures/index.js index 9d3e318..6f08e92 100644 --- a/src/events/processCollectedSignatures/index.js +++ b/src/events/processCollectedSignatures/index.js @@ -2,7 +2,7 @@ require('dotenv').config() const promiseLimit = require('promise-limit') const { HttpListProviderError } = require('http-list-provider') const bridgeValidatorsABI = require('../../../abis/BridgeValidators.abi') -const logger = require('../../services/logger') +const rootLogger = require('../../services/logger') const { web3Home, web3Foreign } = require('../../services/web3') const { signatureToVRS } = require('../../utils/message') const estimateGas = require('./estimateGas') @@ -13,8 +13,6 @@ const { } = require('../../utils/errors') const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') -const { VALIDATOR_ADDRESS } = process.env - const limit = promiseLimit(MAX_CONCURRENT_EVENTS) let validatorContract = null @@ -31,13 +29,17 @@ function processCollectedSignaturesBuilder(config) { const txToSend = [] if (validatorContract === null) { + rootLogger.debug('Getting validator contract address') const validatorContractAddress = await foreignBridge.methods.validatorContract().call() + rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained') + validatorContract = new web3Foreign.eth.Contract( bridgeValidatorsABI, validatorContractAddress ) } + rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`) const callbacks = signatures.map(colSignature => limit(async () => { const { @@ -46,11 +48,14 @@ function processCollectedSignaturesBuilder(config) { NumberOfCollectedSignatures } = colSignature.returnValues - if (authorityResponsibleForRelay === web3Home.utils.toChecksumAddress(VALIDATOR_ADDRESS)) { - logger.info( - { eventTransactionHash: colSignature.transactionHash }, - `Processing CollectedSignatures ${colSignature.transactionHash}` - ) + const logger = rootLogger.child({ + eventTransactionHash: colSignature.transactionHash + }) + + if ( + authorityResponsibleForRelay === web3Home.utils.toChecksumAddress(config.validatorAddress) + ) { + logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`) const message = await homeBridge.methods.message(messageHash).call() const requiredSignatures = [] @@ -58,7 +63,9 @@ function processCollectedSignaturesBuilder(config) { requiredSignatures.fill(0) const [v, r, s] = [[], [], []] + logger.debug('Getting message signatures') const signaturePromises = requiredSignatures.map(async (el, index) => { + logger.debug({ index }, 'Getting message signature') const signature = await homeBridge.methods.signature(messageHash, index).call() const recover = signatureToVRS(signature) v.push(recover.v) @@ -70,6 +77,7 @@ function processCollectedSignaturesBuilder(config) { let gasEstimate try { + logger.debug('Estimate gas') gasEstimate = await estimateGas({ foreignBridge, validatorContract, @@ -79,25 +87,20 @@ function processCollectedSignaturesBuilder(config) { message, numberOfCollectedSignatures: NumberOfCollectedSignatures }) + logger.debug({ gasEstimate }, 'Gas estimated') } catch (e) { if (e instanceof HttpListProviderError) { throw new Error( 'RPC Connection Error: submitSignature Gas Estimate cannot be obtained.' ) } else if (e instanceof AlreadyProcessedError) { - logger.info( - { eventTransactionHash: colSignature.transactionHash }, - `Already processed CollectedSignatures ${colSignature.transactionHash}` - ) + logger.info(`Already processed CollectedSignatures ${colSignature.transactionHash}`) return } else if ( e instanceof IncompatibleContractError || e instanceof InvalidValidatorError ) { - logger.error( - { eventTransactionHash: colSignature.transactionHash }, - `The message couldn't be processed; skipping: ${e.message}` - ) + logger.error(`The message couldn't be processed; skipping: ${e.message}`) return } else { logger.error(e, 'Unknown error while processing transaction') @@ -113,7 +116,6 @@ function processCollectedSignaturesBuilder(config) { }) } else { logger.info( - { eventTransactionHash: colSignature.transactionHash }, `Validator not responsible for relaying CollectedSignatures ${ colSignature.transactionHash }` diff --git a/src/events/processSignatureRequests/estimateGas.js b/src/events/processSignatureRequests/estimateGas.js index 666f43f..a73c01d 100644 --- a/src/events/processSignatureRequests/estimateGas.js +++ b/src/events/processSignatureRequests/estimateGas.js @@ -4,6 +4,9 @@ const { AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') +const logger = require('../../services/logger').child({ + module: 'processSignatureRequests:estimateGas' +}) async function estimateGas({ web3, homeBridge, validatorContract, signature, message, address }) { try { @@ -17,6 +20,7 @@ async function estimateGas({ web3, homeBridge, validatorContract, signature, mes } // Check if minimum number of validations was already reached + logger.debug('Check if minimum number of validations was reached') const messageHash = web3.utils.soliditySha3(message) const numMessagesSigned = await homeBridge.methods.numMessagesSigned(messageHash).call() const alreadyProcessed = await homeBridge.methods.isAlreadyProcessed(numMessagesSigned).call() @@ -26,6 +30,7 @@ async function estimateGas({ web3, homeBridge, validatorContract, signature, mes } // Check if transaction was already signed by this validator + logger.debug('Check if transaction was already signed') const validatorMessageHash = web3.utils.soliditySha3(address, web3.utils.soliditySha3(message)) const alreadySigned = await homeBridge.methods.messagesSigned(validatorMessageHash).call() @@ -34,12 +39,14 @@ async function estimateGas({ web3, homeBridge, validatorContract, signature, mes } // Check if address is validator + logger.debug('Check if address is validator') const isValidator = await validatorContract.methods.isValidator(address).call() if (!isValidator) { throw new InvalidValidatorError(`${address} is not a validator`) } + logger.error('Unrecognized error') throw new Error('Unknown error while processing message') } } diff --git a/src/events/processSignatureRequests/index.js b/src/events/processSignatureRequests/index.js index 93354ca..feb2140 100644 --- a/src/events/processSignatureRequests/index.js +++ b/src/events/processSignatureRequests/index.js @@ -2,7 +2,7 @@ require('dotenv').config() const promiseLimit = require('promise-limit') const { HttpListProviderError } = require('http-list-provider') const bridgeValidatorsABI = require('../../../abis/BridgeValidators.abi') -const logger = require('../../services/logger') +const rootLogger = require('../../services/logger') const { web3Home } = require('../../services/web3') const { createMessage } = require('../../utils/message') const estimateGas = require('./estimateGas') @@ -11,9 +11,9 @@ const { AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') -const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') +const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') -const { VALIDATOR_ADDRESS, VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env +const { VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env const limit = promiseLimit(MAX_CONCURRENT_EVENTS) @@ -31,16 +31,24 @@ function processSignatureRequestsBuilder(config) { } if (validatorContract === null) { + rootLogger.debug('Getting validator contract address') const validatorContractAddress = await homeBridge.methods.validatorContract().call() + rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained') + validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress) } + rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`) const callbacks = signatureRequests.map(signatureRequest => limit(async () => { const { recipient, value } = signatureRequest.returnValues + const logger = rootLogger.child({ + eventTransactionHash: signatureRequest.transactionHash + }) + logger.info( - { eventTransactionHash: signatureRequest.transactionHash, sender: recipient, value }, + { sender: recipient, value }, `Processing signatureRequest ${signatureRequest.transactionHash}` ) @@ -56,31 +64,29 @@ function processSignatureRequestsBuilder(config) { let gasEstimate try { + logger.debug('Estimate gas') gasEstimate = await estimateGas({ web3: web3Home, homeBridge, validatorContract, signature: signature.signature, message, - address: VALIDATOR_ADDRESS + address: config.validatorAddress }) + logger.debug({ gasEstimate }, 'Gas estimated') } catch (e) { if (e instanceof HttpListProviderError) { throw new Error( 'RPC Connection Error: submitSignature Gas Estimate cannot be obtained.' ) } else if (e instanceof InvalidValidatorError) { - logger.fatal({ address: VALIDATOR_ADDRESS }, 'Invalid validator') - process.exit(10) + logger.fatal({ address: config.validatorAddress }, 'Invalid validator') + process.exit(EXIT_CODES.INCOMPATIBILITY) } else if (e instanceof AlreadySignedError) { - logger.info( - { eventTransactionHash: signatureRequest.transactionHash }, - `Already signed signatureRequest ${signatureRequest.transactionHash}` - ) + logger.info(`Already signed signatureRequest ${signatureRequest.transactionHash}`) return } else if (e instanceof AlreadyProcessedError) { logger.info( - { eventTransactionHash: signatureRequest.transactionHash }, `signatureRequest ${ signatureRequest.transactionHash } was already processed by other validators` @@ -94,7 +100,7 @@ function processSignatureRequestsBuilder(config) { const data = await homeBridge.methods .submitSignature(signature.signature, message) - .encodeABI({ from: VALIDATOR_ADDRESS }) + .encodeABI({ from: config.validatorAddress }) txToSend.push({ data, diff --git a/src/events/processTransfers/index.js b/src/events/processTransfers/index.js index 9d935b2..bf1f9ca 100644 --- a/src/events/processTransfers/index.js +++ b/src/events/processTransfers/index.js @@ -2,18 +2,16 @@ require('dotenv').config() const promiseLimit = require('promise-limit') const { HttpListProviderError } = require('http-list-provider') const bridgeValidatorsABI = require('../../../abis/BridgeValidators.abi') -const logger = require('../../services/logger') +const rootLogger = require('../../services/logger') const { web3Home } = require('../../services/web3') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') -const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') +const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const estimateGas = require('../processAffirmationRequests/estimateGas') -const { VALIDATOR_ADDRESS } = process.env - const limit = promiseLimit(MAX_CONCURRENT_EVENTS) let validatorContract = null @@ -25,16 +23,27 @@ function processTransfersBuilder(config) { const txToSend = [] if (validatorContract === null) { + rootLogger.debug('Getting validator contract address') const validatorContractAddress = await homeBridge.methods.validatorContract().call() + rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained') + validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress) } + rootLogger.debug(`Processing ${transfers.length} Transfer events`) const callbacks = transfers.map(transfer => limit(async () => { const { from, value } = transfer.returnValues + const logger = rootLogger.child({ + eventTransactionHash: transfer.transactionHash + }) + + logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`) + let gasEstimate try { + logger.debug('Estimate gas') gasEstimate = await estimateGas({ web3: web3Home, homeBridge, @@ -42,25 +51,22 @@ function processTransfersBuilder(config) { recipient: from, value, txHash: transfer.transactionHash, - address: VALIDATOR_ADDRESS + address: config.validatorAddress }) + logger.debug({ gasEstimate }, 'Gas estimated') } catch (e) { if (e instanceof HttpListProviderError) { throw new Error( 'RPC Connection Error: submitSignature Gas Estimate cannot be obtained.' ) } else if (e instanceof InvalidValidatorError) { - logger.fatal({ address: VALIDATOR_ADDRESS }, 'Invalid validator') - process.exit(10) + logger.fatal({ address: config.validatorAddress }, 'Invalid validator') + process.exit(EXIT_CODES.INCOMPATIBILITY) } else if (e instanceof AlreadySignedError) { - logger.info( - { eventTransactionHash: transfer.transactionHash }, - `Already signed transfer ${transfer.transactionHash}` - ) + logger.info(`Already signed transfer ${transfer.transactionHash}`) return } else if (e instanceof AlreadyProcessedError) { logger.info( - { eventTransactionHash: transfer.transactionHash }, `transfer ${transfer.transactionHash} was already processed by other validators` ) return @@ -72,7 +78,7 @@ function processTransfersBuilder(config) { const data = await homeBridge.methods .executeAffirmation(from, value, transfer.transactionHash) - .encodeABI({ from: VALIDATOR_ADDRESS }) + .encodeABI({ from: config.validatorAddress }) txToSend.push({ data, diff --git a/src/sender.js b/src/sender.js index 55d15ed..7cc4a66 100644 --- a/src/sender.js +++ b/src/sender.js @@ -7,14 +7,23 @@ const logger = require('./services/logger') const rpcUrlsManager = require('./services/getRpcUrlsManager') const { sendTx } = require('./tx/sendTx') const { getNonce, getChainId } = require('./tx/web3') -const { addExtraGas, checkHTTPS, syncForEach, waitForFunds } = require('./utils/utils') -const { EXTRA_GAS_PERCENTAGE } = require('./utils/constants') +const { + addExtraGas, + checkHTTPS, + privateKeyToAddress, + syncForEach, + waitForFunds, + watchdog +} = require('./utils/utils') +const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants') -const { VALIDATOR_ADDRESS, VALIDATOR_ADDRESS_PRIVATE_KEY, REDIS_LOCK_TTL } = process.env +const { VALIDATOR_ADDRESS_PRIVATE_KEY, REDIS_LOCK_TTL } = process.env + +const VALIDATOR_ADDRESS = privateKeyToAddress(VALIDATOR_ADDRESS_PRIVATE_KEY) if (process.argv.length < 3) { logger.error('Please check the number of arguments, config file was not provided') - process.exit(1) + process.exit(EXIT_CODES.GENERAL_ERROR) } const config = require(path.join('../config/', process.argv[2])) @@ -26,7 +35,7 @@ let chainId = 0 async function initialize() { try { - const checkHttps = checkHTTPS(process.env.ALLOW_HTTP) + const checkHttps = checkHTTPS(process.env.ALLOW_HTTP, logger) rpcUrlsManager.homeUrls.forEach(checkHttps('home')) rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign')) @@ -36,11 +45,20 @@ async function initialize() { chainId = await getChainId(web3Instance) connectSenderToQueue({ queueName: config.queue, - cb: main + cb: options => { + if (config.maxProcessingTime) { + return watchdog(() => main(options), config.maxProcessingTime, () => { + logger.fatal('Max processing time reached') + process.exit(EXIT_CODES.MAX_TIME_REACHED) + }) + } + + return main(options) + } }) } catch (e) { logger.error(e.message) - process.exit(1) + process.exit(EXIT_CODES.GENERAL_ERROR) } } @@ -52,12 +70,20 @@ function resume(newBalance) { } async function readNonce(forceUpdate) { + logger.debug('Reading nonce') if (forceUpdate) { + logger.debug('Forcing update of nonce') return getNonce(web3Instance, VALIDATOR_ADDRESS) } - const result = await redis.get(nonceKey) - return result ? Number(result) : getNonce(web3Instance, VALIDATOR_ADDRESS) + const nonce = await redis.get(nonceKey) + if (nonce) { + logger.debug({ nonce }, 'Nonce found in the DB') + return Number(nonce) + } else { + logger.debug("Nonce wasn't found in the DB") + return getNonce(web3Instance, VALIDATOR_ADDRESS) + } } function updateNonce(nonce) { @@ -73,9 +99,11 @@ async function main({ msg, ackMsg, nackMsg, sendToQueue, channel }) { const txArray = JSON.parse(msg.content) logger.info(`Msg received with ${txArray.length} Tx to send`) - const gasPrice = await GasPrice.getPrice() + const gasPrice = GasPrice.getPrice() const ttl = REDIS_LOCK_TTL * txArray.length + + logger.debug('Acquiring lock') const lock = await redlock.lock(nonceLock, ttl) let nonce = await readNonce() @@ -83,10 +111,12 @@ async function main({ msg, ackMsg, nackMsg, sendToQueue, channel }) { let minimumBalance = null const failedTx = [] + logger.debug(`Sending ${txArray.length} transactions`) await syncForEach(txArray, async job => { const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE) try { + logger.info(`Sending transaction with nonce ${nonce}`) const txHash = await sendTx({ chain: config.id, data: job.data, @@ -131,7 +161,10 @@ async function main({ msg, ackMsg, nackMsg, sendToQueue, channel }) { } }) + logger.debug('Updating nonce') await updateNonce(nonce) + + logger.debug('Releasing lock') await lock.unlock() if (failedTx.length) { @@ -139,16 +172,21 @@ async function main({ msg, ackMsg, nackMsg, sendToQueue, channel }) { await sendToQueue(failedTx) } ackMsg(msg) - logger.info(`Finished processing msg`) + logger.debug(`Finished processing msg`) if (insufficientFunds) { + logger.warn( + 'Insufficient funds. Stop sending transactions until the account has the minimum balance' + ) channel.close() - waitForFunds(web3Instance, VALIDATOR_ADDRESS, minimumBalance, resume) + waitForFunds(web3Instance, VALIDATOR_ADDRESS, minimumBalance, resume, logger) } } catch (e) { logger.error(e) nackMsg(msg) } + + logger.debug('Finished') } initialize() diff --git a/src/services/gasPrice.js b/src/services/gasPrice.js index 2c70aa9..4a37352 100644 --- a/src/services/gasPrice.js +++ b/src/services/gasPrice.js @@ -1,16 +1,16 @@ require('dotenv').config() const fetch = require('node-fetch') +const Web3Utils = require('web3-utils') const { web3Home, web3Foreign } = require('../services/web3') -const { isErcToErc } = require('../../config/base.config') -const HomeNativeABI = require('../../abis/HomeBridgeNativeToErc.abi') -const ForeignNativeABI = require('../../abis/ForeignBridgeNativeToErc.abi') -const HomeErcABI = require('../../abis/HomeBridgeErcToErc.abi') -const ForeignErcABI = require('../../abis/ForeignBridgeErcToErc.abi') -const logger = require('../services/logger') +const { bridgeConfig } = require('../../config/base.config') +const logger = require('../services/logger').child({ + module: 'gasPrice' +}) const { setIntervalAndRun } = require('../utils/utils') +const { DEFAULT_UPDATE_INTERVAL } = require('../utils/constants') -const HomeABI = isErcToErc ? HomeErcABI : HomeNativeABI -const ForeignABI = isErcToErc ? ForeignNativeABI : ForeignErcABI +const HomeABI = bridgeConfig.homeBridgeAbi +const ForeignABI = bridgeConfig.foreignBridgeAbi const { FOREIGN_BRIDGE_ADDRESS, @@ -38,18 +38,20 @@ async function fetchGasPriceFromOracle(oracleUrl, speedType) { if (!gasPrice) { throw new Error(`Response from Oracle didn't include gas price for ${speedType} type.`) } - return gasPrice + return Web3Utils.toWei(gasPrice.toString(), 'gwei') } async function fetchGasPrice({ bridgeContract, oracleFn }) { let gasPrice = null try { gasPrice = await oracleFn() + logger.debug({ gasPrice }, 'Gas price updated using the oracle') } catch (e) { logger.error(`Gas Price API is not available. ${e.message}`) try { gasPrice = await bridgeContract.methods.gasPrice().call() + logger.debug({ gasPrice }, 'Gas price updated using the contracts') } catch (e) { logger.error(`There was a problem getting the gas price from the contract. ${e.message}`) } @@ -70,14 +72,14 @@ async function start(chainId) { bridgeContract = homeBridge oracleUrl = HOME_GAS_PRICE_ORACLE_URL speedType = HOME_GAS_PRICE_SPEED_TYPE - updateInterval = HOME_GAS_PRICE_UPDATE_INTERVAL + updateInterval = HOME_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL cachedGasPrice = HOME_GAS_PRICE_FALLBACK } else if (chainId === 'foreign') { bridgeContract = foreignBridge oracleUrl = FOREIGN_GAS_PRICE_ORACLE_URL speedType = FOREIGN_GAS_PRICE_SPEED_TYPE - updateInterval = FOREIGN_GAS_PRICE_UPDATE_INTERVAL + updateInterval = FOREIGN_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL cachedGasPrice = FOREIGN_GAS_PRICE_FALLBACK } else { @@ -93,7 +95,7 @@ async function start(chainId) { }, updateInterval) } -async function getPrice() { +function getPrice() { return cachedGasPrice } diff --git a/src/services/logger.js b/src/services/logger.js index 7a81172..e7122b7 100644 --- a/src/services/logger.js +++ b/src/services/logger.js @@ -7,6 +7,7 @@ const config = const logger = pino({ enabled: process.env.NODE_ENV !== 'test', name: config.name, + level: process.env.LOG_LEVEL || 'debug', base: process.env.NODE_ENV === 'production' ? { diff --git a/src/tx/sendTx.js b/src/tx/sendTx.js index a1a27c3..88ebc97 100644 --- a/src/tx/sendTx.js +++ b/src/tx/sendTx.js @@ -22,7 +22,7 @@ async function sendTx({ to, data, value: Web3Utils.toWei(amount), - gasPrice: Web3Utils.toWei(gasPrice, 'gwei'), + gasPrice, gas: gasLimit }, `0x${privateKey}` diff --git a/src/tx/web3.js b/src/tx/web3.js index d9fb140..f010d93 100644 --- a/src/tx/web3.js +++ b/src/tx/web3.js @@ -1,6 +1,13 @@ +const logger = require('../services/logger').child({ + module: 'web3' +}) + async function getNonce(web3, address) { try { - return await web3.eth.getTransactionCount(address) + logger.debug({ address }, 'Getting transaction count') + const transactionCount = await web3.eth.getTransactionCount(address) + logger.debug({ address, transactionCount }, 'Transaction count obtained') + return transactionCount } catch (e) { throw new Error(`Nonce cannot be obtained`) } @@ -8,7 +15,10 @@ async function getNonce(web3, address) { async function getBlockNumber(web3) { try { - return await web3.eth.getBlockNumber() + logger.debug('Getting block number') + const blockNumber = await web3.eth.getBlockNumber() + logger.debug({ blockNumber }, 'Block number obtained') + return blockNumber } catch (e) { throw new Error(`Block Number cannot be obtained`) } @@ -16,7 +26,10 @@ async function getBlockNumber(web3) { async function getChainId(web3) { try { - return await web3.eth.net.getId() + logger.debug('Getting chain id') + const chainId = await web3.eth.net.getId() + logger.debug({ chainId }, 'Chain id obtained') + return chainId } catch (e) { throw new Error(`Chain Id cannot be obtained`) } @@ -24,7 +37,14 @@ async function getChainId(web3) { async function getRequiredBlockConfirmations(contract) { try { - return await contract.methods.requiredBlockConfirmations().call() + const contractAddress = contract.options.address + logger.debug({ contractAddress }, 'Getting required block confirmations') + const requiredBlockConfirmations = await contract.methods.requiredBlockConfirmations().call() + logger.debug( + { contractAddress, requiredBlockConfirmations }, + 'Required block confirmations obtained' + ) + return requiredBlockConfirmations } catch (e) { throw new Error(`Required block confirmations cannot be obtained`) } @@ -32,7 +52,14 @@ async function getRequiredBlockConfirmations(contract) { async function getEvents({ contract, event, fromBlock, toBlock, filter }) { try { - return await contract.getPastEvents(event, { fromBlock, toBlock, filter }) + const contractAddress = contract.options.address + logger.info( + { contractAddress, event, fromBlock: fromBlock.toString(), toBlock: toBlock.toString() }, + 'Getting past events' + ) + const pastEvents = await contract.getPastEvents(event, { fromBlock, toBlock, filter }) + logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained') + return pastEvents } catch (e) { throw new Error(`${event} events cannot be obtained`) } diff --git a/src/utils/constants.js b/src/utils/constants.js index cfc389b..4579a21 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -1,10 +1,16 @@ module.exports = { - EXTRA_GAS_PERCENTAGE: 0.25, + EXTRA_GAS_PERCENTAGE: 1, MAX_CONCURRENT_EVENTS: 50, RETRY_CONFIG: { retries: 20, factor: 1.4, maxTimeout: 360000, randomize: true + }, + DEFAULT_UPDATE_INTERVAL: 600000, + EXIT_CODES: { + GENERAL_ERROR: 1, + INCOMPATIBILITY: 10, + MAX_TIME_REACHED: 11 } } diff --git a/src/utils/utils.js b/src/utils/utils.js index 6834f75..7bf2085 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,6 +1,6 @@ const BigNumber = require('bignumber.js') const promiseRetry = require('promise-retry') -const logger = require('../services/logger') +const Web3 = require('web3') async function syncForEach(array, callback) { for (let index = 0; index < array.length; index++) { @@ -8,7 +8,7 @@ async function syncForEach(array, callback) { } } -function checkHTTPS(ALLOW_HTTP) { +function checkHTTPS(ALLOW_HTTP, logger) { return function(network) { return function(url) { if (!/^https.*/.test(url)) { @@ -24,13 +24,22 @@ function checkHTTPS(ALLOW_HTTP) { } } -async function waitForFunds(web3, address, minimumBalance, cb) { +async function waitForFunds(web3, address, minimumBalance, cb, logger) { promiseRetry( async retry => { + logger.debug('Getting balance of validator account') const newBalance = web3.utils.toBN(await web3.eth.getBalance(address)) if (newBalance.gte(minimumBalance)) { + logger.debug( + { balance: newBalance, minimumBalance }, + 'Validator has minimum necessary balance' + ) cb(newBalance) } else { + logger.debug( + { balance: newBalance, minimumBalance }, + 'Balance of validator is still less than the minimum' + ) retry() } }, @@ -56,10 +65,43 @@ function setIntervalAndRun(f, interval) { return handler } +/** + * Run function `f` and return its result, unless `timeout` milliseconds pass before `f` ends. If that happens, run + * `kill` instead. + * + * @param {Function} f The function to run. It is assumed that it's an async function. + * @param {Number} timeout Max time in milliseconds to wait for `f` to finish. + * @param {Function} kill Function that will be called if `f` takes more than `timeout` milliseconds. + */ +async function watchdog(f, timeout, kill) { + const timeoutHandler = setTimeout(kill, timeout) + + const result = await f() + clearTimeout(timeoutHandler) + + return result +} + +function add0xPrefix(s) { + if (s.indexOf('0x') === 0) { + return s + } + + return `0x${s}` +} + +function privateKeyToAddress(privateKey) { + return privateKey + ? new Web3().eth.accounts.privateKeyToAccount(add0xPrefix(privateKey)).address + : null +} + module.exports = { syncForEach, checkHTTPS, waitForFunds, addExtraGas, - setIntervalAndRun + setIntervalAndRun, + watchdog, + privateKeyToAddress } diff --git a/src/watcher.js b/src/watcher.js index 7c5a428..dbdfb17 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -1,16 +1,18 @@ require('dotenv').config() const path = require('path') +const { BN, toBN } = require('web3').utils const { connectWatcherToQueue, connection } = require('./services/amqpClient') const { getBlockNumber } = require('./tx/web3') const { redis } = require('./services/redisClient') const logger = require('./services/logger') const rpcUrlsManager = require('./services/getRpcUrlsManager') const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3') -const { checkHTTPS } = require('./utils/utils') +const { checkHTTPS, watchdog } = require('./utils/utils') +const { EXIT_CODES } = require('./utils/constants') if (process.argv.length < 3) { logger.error('Please check the number of arguments, config file was not provided') - process.exit(1) + process.exit(EXIT_CODES.GENERAL_ERROR) } const config = require(path.join('../config/', process.argv[2])) @@ -20,15 +22,18 @@ const processCollectedSignatures = require('./events/processCollectedSignatures' const processAffirmationRequests = require('./events/processAffirmationRequests')(config) const processTransfers = require('./events/processTransfers')(config) +const ZERO = toBN(0) +const ONE = toBN(1) + const web3Instance = config.web3 const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress) const eventContract = new web3Instance.eth.Contract(config.eventAbi, config.eventContractAddress) const lastBlockRedisKey = `${config.id}:lastProcessedBlock` -let lastProcessedBlock = config.startBlock || 0 +let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO) async function initialize() { try { - const checkHttps = checkHTTPS(process.env.ALLOW_HTTP) + const checkHttps = checkHTTPS(process.env.ALLOW_HTTP, logger) rpcUrlsManager.homeUrls.forEach(checkHttps('home')) rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign')) @@ -40,14 +45,21 @@ async function initialize() { }) } catch (e) { logger.error(e) - process.exit(1) + process.exit(EXIT_CODES.GENERAL_ERROR) } } async function runMain({ sendToQueue }) { try { if (connection.isConnected() && redis.status === 'ready') { - await main({ sendToQueue }) + if (config.maxProcessingTime) { + await watchdog(() => main({ sendToQueue }), config.maxProcessingTime, () => { + logger.fatal('Max processing time reached') + process.exit(EXIT_CODES.MAX_TIME_REACHED) + }) + } else { + await main({ sendToQueue }) + } } } catch (e) { logger.error(e) @@ -60,25 +72,32 @@ async function runMain({ sendToQueue }) { async function getLastProcessedBlock() { const result = await redis.get(lastBlockRedisKey) - lastProcessedBlock = result ? Number(result) : lastProcessedBlock + logger.debug( + { fromRedis: result, fromConfig: lastProcessedBlock.toString() }, + 'Last Processed block obtained' + ) + lastProcessedBlock = result ? toBN(result) : lastProcessedBlock } function updateLastProcessedBlock(lastBlockNumber) { lastProcessedBlock = lastBlockNumber - return redis.set(lastBlockRedisKey, lastProcessedBlock) + return redis.set(lastBlockRedisKey, lastProcessedBlock.toString()) } function processEvents(events) { switch (config.id) { - case 'signature-request': - case 'erc-signature-request': + case 'native-erc-signature-request': + case 'erc-erc-signature-request': + case 'erc-native-signature-request': return processSignatureRequests(events) - case 'collected-signatures': - case 'erc-collected-signatures': + case 'native-erc-collected-signatures': + case 'erc-erc-collected-signatures': + case 'erc-native-collected-signatures': return processCollectedSignatures(events) - case 'affirmation-request': + case 'native-erc-affirmation-request': return processAffirmationRequests(events) - case 'erc-affirmation-request': + case 'erc-erc-affirmation-request': + case 'erc-native-affirmation-request': return processTransfers(events) default: return [] @@ -86,28 +105,33 @@ function processEvents(events) { } async function getLastBlockToProcess() { - const lastBlockNumberPromise = getBlockNumber(web3Instance) - const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract) + const lastBlockNumberPromise = getBlockNumber(web3Instance).then(toBN) + const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract).then(toBN) const [lastBlockNumber, requiredBlockConfirmations] = await Promise.all([ lastBlockNumberPromise, requiredBlockConfirmationsPromise ]) - return lastBlockNumber - requiredBlockConfirmations + return lastBlockNumber.sub(requiredBlockConfirmations) } async function main({ sendToQueue }) { try { const lastBlockToProcess = await getLastBlockToProcess() - if (lastBlockToProcess <= lastProcessedBlock) { - logger.info('All blocks already processed') + + if (lastBlockToProcess.lte(lastProcessedBlock)) { + logger.debug('All blocks already processed') return } + + const fromBlock = lastProcessedBlock.add(ONE) + const toBlock = lastBlockToProcess + const events = await getEvents({ contract: eventContract, event: config.event, - fromBlock: lastProcessedBlock + 1, - toBlock: lastBlockToProcess, + fromBlock, + toBlock, filter: config.eventFilter }) logger.info(`Found ${events.length} ${config.event} events`) @@ -121,10 +145,16 @@ async function main({ sendToQueue }) { } } + logger.debug( + { lastProcessedBlock: lastBlockToProcess.toString() }, + 'Updating last processed block' + ) await updateLastProcessedBlock(lastBlockToProcess) } catch (e) { logger.error(e) } + + logger.debug('Finished') } initialize() diff --git a/submodules/poa-bridge-contracts b/submodules/poa-bridge-contracts index 5d9f594..1f61c69 160000 --- a/submodules/poa-bridge-contracts +++ b/submodules/poa-bridge-contracts @@ -1 +1 @@ -Subproject commit 5d9f5946cda6043d7584b6da49693f7d3391b78d +Subproject commit 1f61c695df23b2a8f79d96e138c6bb5d8cd5b592 diff --git a/test/gasPrice.test.js b/test/gasPrice.test.js index 3121cef..a8276c0 100644 --- a/test/gasPrice.test.js +++ b/test/gasPrice.test.js @@ -1,6 +1,8 @@ const sinon = require('sinon') const { expect } = require('chai') +const proxyquire = require('proxyquire').noPreserveCache() const { fetchGasPrice } = require('../src/services/gasPrice') +const { DEFAULT_UPDATE_INTERVAL } = require('../src/utils/constants') describe('gasPrice', () => { describe('fetchGasPrice', () => { @@ -72,4 +74,68 @@ describe('gasPrice', () => { expect(gasPrice).to.equal(null) }) }) + describe('start', () => { + const utils = { setIntervalAndRun: sinon.spy() } + beforeEach(() => { + utils.setIntervalAndRun.resetHistory() + }) + it('should call setIntervalAndRun with HOME_GAS_PRICE_UPDATE_INTERVAL interval value on Home', async () => { + // given + process.env.HOME_GAS_PRICE_UPDATE_INTERVAL = 15000 + const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils }) + + // when + await gasPrice.start('home') + + // then + expect(process.env.HOME_GAS_PRICE_UPDATE_INTERVAL).to.equal('15000') + expect(process.env.HOME_GAS_PRICE_UPDATE_INTERVAL).to.not.equal( + DEFAULT_UPDATE_INTERVAL.toString() + ) + expect(utils.setIntervalAndRun.args[0][1]).to.equal( + process.env.HOME_GAS_PRICE_UPDATE_INTERVAL.toString() + ) + }) + it('should call setIntervalAndRun with FOREIGN_GAS_PRICE_UPDATE_INTERVAL interval value on Foreign', async () => { + // given + process.env.FOREIGN_GAS_PRICE_UPDATE_INTERVAL = 15000 + const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils }) + + // when + await gasPrice.start('foreign') + + // then + expect(process.env.FOREIGN_GAS_PRICE_UPDATE_INTERVAL).to.equal('15000') + expect(process.env.HOME_GAS_PRICE_UPDATE_INTERVAL).to.not.equal( + DEFAULT_UPDATE_INTERVAL.toString() + ) + expect(utils.setIntervalAndRun.args[0][1]).to.equal( + process.env.FOREIGN_GAS_PRICE_UPDATE_INTERVAL.toString() + ) + }) + it('should call setIntervalAndRun with default interval value on Home', async () => { + // given + delete process.env.HOME_GAS_PRICE_UPDATE_INTERVAL + const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils }) + + // when + await gasPrice.start('home') + + // then + expect(process.env.HOME_GAS_PRICE_UPDATE_INTERVAL).to.equal(undefined) + expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL) + }) + it('should call setIntervalAndRun with default interval value on Foreign', async () => { + // given + delete process.env.FOREIGN_GAS_PRICE_UPDATE_INTERVAL + const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils }) + + // when + await gasPrice.start('foreign') + + // then + expect(process.env.FOREIGN_GAS_PRICE_UPDATE_INTERVAL).to.equal(undefined) + expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL) + }) + }) }) diff --git a/test/utils.test.js b/test/utils.test.js index 0c1f5ac..2742690 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -44,22 +44,22 @@ describe('utils', () => { }) it('should do nothing if HTTP is allowed and the URL is https', () => { - utils.checkHTTPS('yes')('home')('https://www.google.com') + utils.checkHTTPS('yes', logger)('home')('https://www.google.com') expect(logger.warn.called).to.equal(false) }) it('should emit a warning if HTTP is allowed and the URL is http', () => { - utils.checkHTTPS('yes')('home')('http://www.google.com') + utils.checkHTTPS('yes', logger)('home')('http://www.google.com') expect(logger.warn.called).to.equal(true) }) it('should do nothing if HTTP is not allowed and the URL is https', () => { - utils.checkHTTPS('no')('home')('https://www.google.com') + utils.checkHTTPS('no', logger)('home')('https://www.google.com') expect(logger.warn.called).to.equal(false) }) it('should throw an error if HTTP is not allowed and the URL is http', () => { - expect(() => utils.checkHTTPS('no')('home')('http://www.google.com')).to.throw() + expect(() => utils.checkHTTPS('no', logger)('home')('http://www.google.com')).to.throw() }) })