Possibility to allow/block specific addresses in erc-to-native mode (#442)
This commit is contained in:
parent
48dd53622c
commit
dc377aeb9b
@ -37,6 +37,9 @@ ORACLE_MAX_PROCESSING_TIME | The workers processes will be killed if this amount
|
|||||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x"
|
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x"
|
||||||
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
|
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
|
||||||
ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one
|
ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one
|
||||||
|
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST | Filename with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests will be automatically processed by the CollectedSignatures watcher. | string
|
||||||
|
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST | Filename with a list of addresses, separated by newlines. If set, determines the blocked set of accounts whose requests will not be automatically processed by the CollectedSignatures watcher. Has a lower priority than the `ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST` | string
|
||||||
|
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
|
||||||
|
|
||||||
|
|
||||||
## UI configuration
|
## UI configuration
|
||||||
|
@ -19,6 +19,6 @@
|
|||||||
"eslint-plugin-jest": "^23.18.0"
|
"eslint-plugin-jest": "^23.18.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.9"
|
"node": ">= 10.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
e2e-commons/access-lists/allowance_list.txt
Normal file
0
e2e-commons/access-lists/allowance_list.txt
Normal file
1
e2e-commons/access-lists/block_list.txt
Normal file
1
e2e-commons/access-lists/block_list.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04
|
@ -23,3 +23,4 @@ ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
|
|||||||
ORACLE_ALLOW_HTTP_FOR_RPC=yes
|
ORACLE_ALLOW_HTTP_FOR_RPC=yes
|
||||||
ORACLE_HOME_START_BLOCK=1
|
ORACLE_HOME_START_BLOCK=1
|
||||||
ORACLE_FOREIGN_START_BLOCK=1
|
ORACLE_FOREIGN_START_BLOCK=1
|
||||||
|
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
|
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
|
||||||
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
|
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
|
||||||
},
|
},
|
||||||
|
"blockedUser": {
|
||||||
|
"address": "0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04",
|
||||||
|
"privateKey": "0x65df4ea787916f6ed9660f0b0fe36858a65735ad0dcd34527497f4ce32e53883"
|
||||||
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
|
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
|
||||||
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||||
|
@ -50,6 +50,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
command: "true"
|
command: "true"
|
||||||
|
volumes:
|
||||||
|
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
|
||||||
|
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
|
||||||
networks:
|
networks:
|
||||||
- ultimate
|
- ultimate
|
||||||
oracle-amb:
|
oracle-amb:
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"axios": "0.19.0"
|
"axios": "0.19.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.9"
|
"node": ">= 10.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"web3": "1.0.0-beta.34"
|
"web3": "1.0.0-beta.34"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.9"
|
"node": ">= 10.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.2.0"
|
"chai": "^4.2.0"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"websocket": "^1.0.28"
|
"websocket": "^1.0.28"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.9"
|
"node": ">= 10.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@ const promiseRetry = require('promise-retry')
|
|||||||
const {
|
const {
|
||||||
user,
|
user,
|
||||||
secondUser,
|
secondUser,
|
||||||
|
blockedUser,
|
||||||
validator,
|
validator,
|
||||||
ercToNativeBridge,
|
ercToNativeBridge,
|
||||||
homeRPC,
|
homeRPC,
|
||||||
foreignRPC
|
foreignRPC
|
||||||
} = require('../../e2e-commons/constants.json')
|
} = require('../../e2e-commons/constants.json')
|
||||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||||
const { uniformRetry } = require('../../e2e-commons/utils')
|
const { uniformRetry, sleep } = require('../../e2e-commons/utils')
|
||||||
const { setRequiredSignatures } = require('./utils')
|
const { setRequiredSignatures } = require('./utils')
|
||||||
|
|
||||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||||
@ -22,6 +23,7 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToNativeBridge.foreign
|
|||||||
const { toBN } = foreignWeb3.utils
|
const { toBN } = foreignWeb3.utils
|
||||||
|
|
||||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||||
|
homeWeb3.eth.accounts.wallet.add(blockedUser.privateKey)
|
||||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||||
@ -142,6 +144,47 @@ describe('erc to native', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
it('should not process transaction from blocked users', async () => {
|
||||||
|
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
|
||||||
|
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||||
|
|
||||||
|
// check that account has tokens in home chain
|
||||||
|
const balance1 = await homeWeb3.eth.getBalance(user.address)
|
||||||
|
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
|
||||||
|
assert(!toBN(balance1).isZero(), 'Account should have tokens')
|
||||||
|
assert(!toBN(balance2).isZero(), 'Account should have tokens')
|
||||||
|
|
||||||
|
// send transaction to home bridge
|
||||||
|
await homeWeb3.eth.sendTransaction({
|
||||||
|
from: user.address,
|
||||||
|
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
|
gasPrice: '1',
|
||||||
|
gas: '1000000',
|
||||||
|
value: homeWeb3.utils.toWei('0.01')
|
||||||
|
})
|
||||||
|
|
||||||
|
// send transaction to home bridge
|
||||||
|
await homeWeb3.eth.sendTransaction({
|
||||||
|
from: blockedUser.address,
|
||||||
|
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
|
gasPrice: '1',
|
||||||
|
gas: '1000000',
|
||||||
|
value: homeWeb3.utils.toWei('0.01')
|
||||||
|
})
|
||||||
|
|
||||||
|
// check that balance increases
|
||||||
|
await uniformRetry(async retry => {
|
||||||
|
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||||
|
if (toBN(balance).lte(toBN(originalBalance1))) {
|
||||||
|
retry()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await sleep(3000)
|
||||||
|
|
||||||
|
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||||
|
assert(toBN(balance).eq(toBN(originalBalance2)), 'Bridge should not process collected signatures from blocked user')
|
||||||
|
})
|
||||||
it('should not invest dai when chai token is disabled', async () => {
|
it('should not invest dai when chai token is disabled', async () => {
|
||||||
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||||
|
|
||||||
|
@ -30,6 +30,10 @@ ORACLE_FOREIGN_START_BLOCK=
|
|||||||
ORACLE_LOG_LEVEL=debug
|
ORACLE_LOG_LEVEL=debug
|
||||||
ORACLE_MAX_PROCESSING_TIME=20000
|
ORACLE_MAX_PROCESSING_TIME=20000
|
||||||
|
|
||||||
|
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST=access-lists/allowance_list.txt
|
||||||
|
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=access-lists/block_list.txt
|
||||||
|
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER=false
|
||||||
|
|
||||||
#Uncomment these lines only if you are going to send transaction by testing scripts
|
#Uncomment these lines only if you are going to send transaction by testing scripts
|
||||||
#USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370
|
#USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370
|
||||||
#USER_ADDRESS_PRIVATE_KEY=
|
#USER_ADDRESS_PRIVATE_KEY=
|
||||||
|
@ -25,6 +25,9 @@ services:
|
|||||||
extends:
|
extends:
|
||||||
file: docker-compose.yml
|
file: docker-compose.yml
|
||||||
service: bridge_collected
|
service: bridge_collected
|
||||||
|
volumes:
|
||||||
|
- '~/bridge_data/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
|
||||||
|
- '~/bridge_data/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
|
||||||
networks:
|
networks:
|
||||||
- net_db_bridge_request
|
- net_db_bridge_request
|
||||||
- net_rabbit_bridge_request
|
- net_rabbit_bridge_request
|
||||||
|
@ -48,6 +48,6 @@
|
|||||||
"sinon": "^6.1.0"
|
"sinon": "^6.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.9"
|
"node": ">= 10.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,18 @@ const { HttpListProviderError } = require('http-list-provider')
|
|||||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
const { web3Home, web3Foreign } = require('../../services/web3')
|
||||||
const { signatureToVRS, packSignatures } = require('../../utils/message')
|
const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
|
||||||
|
const { readAccessListFile } = require('../../utils/utils')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||||
|
|
||||||
|
const {
|
||||||
|
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST,
|
||||||
|
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST,
|
||||||
|
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER
|
||||||
|
} = process.env
|
||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
let validatorContract = null
|
||||||
@ -46,6 +53,49 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
||||||
const message = await homeBridge.methods.message(messageHash).call()
|
const message = await homeBridge.methods.message(messageHash).call()
|
||||||
|
|
||||||
|
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
||||||
|
const parsedMessage = parseMessage(message)
|
||||||
|
const recipient = parsedMessage.recipient.toLowerCase()
|
||||||
|
const originalTxHash = parsedMessage.txHash
|
||||||
|
|
||||||
|
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
|
||||||
|
const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger)
|
||||||
|
if (allowanceList.indexOf(recipient) === -1) {
|
||||||
|
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||||
|
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||||
|
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||||
|
if (allowanceList.indexOf(sender) === -1) {
|
||||||
|
logger.info(
|
||||||
|
{ sender, recipient },
|
||||||
|
'Validator skips a transaction. Neither sender nor recipient addresses are in the allowance list.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info(
|
||||||
|
{ recipient },
|
||||||
|
'Validator skips a transaction. Recipient address is not in the allowance list.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
||||||
|
const blockList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, logger)
|
||||||
|
if (blockList.indexOf(recipient) > -1) {
|
||||||
|
logger.info({ recipient }, 'Validator skips a transaction. Recipient address is in the block list.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||||
|
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||||
|
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||||
|
if (blockList.indexOf(sender) > -1) {
|
||||||
|
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get')
|
logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get')
|
||||||
|
|
||||||
const requiredSignatures = []
|
const requiredSignatures = []
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const fs = require('fs')
|
||||||
const BigNumber = require('bignumber.js')
|
const BigNumber = require('bignumber.js')
|
||||||
const promiseRetry = require('promise-retry')
|
const promiseRetry = require('promise-retry')
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
@ -105,6 +106,27 @@ function nonceError(e) {
|
|||||||
const invert = p => new Promise((res, rej) => p.then(rej, res))
|
const invert = p => new Promise((res, rej) => p.then(rej, res))
|
||||||
const promiseAny = ps => invert(Promise.all(ps.map(invert)))
|
const promiseAny = ps => invert(Promise.all(ps.map(invert)))
|
||||||
|
|
||||||
|
const readAccessLists = {}
|
||||||
|
async function readAccessListFile(fileName, logger) {
|
||||||
|
if (!readAccessLists[fileName]) {
|
||||||
|
logger.debug({ fileName }, 'Access list file read requested')
|
||||||
|
try {
|
||||||
|
const data = await fs.promises.readFile(fileName)
|
||||||
|
readAccessLists[fileName] = data
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.map(addr => addr.trim().toLowerCase())
|
||||||
|
.filter(addr => addr.length === 42)
|
||||||
|
logger.info({ fileName }, `Access list was read successfully, ${data.length} addresses found`)
|
||||||
|
logger.debug({ addresses: readAccessLists[fileName] }, `Read addresses from the file`)
|
||||||
|
} catch (e) {
|
||||||
|
readAccessLists[fileName] = []
|
||||||
|
logger.error({ fileName, error: e }, `Failed to read access list from the file`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return readAccessLists[fileName]
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
syncForEach,
|
syncForEach,
|
||||||
checkHTTPS,
|
checkHTTPS,
|
||||||
@ -115,5 +137,6 @@ module.exports = {
|
|||||||
privateKeyToAddress,
|
privateKeyToAddress,
|
||||||
nonceError,
|
nonceError,
|
||||||
getRetrySequence,
|
getRetrySequence,
|
||||||
promiseAny
|
promiseAny,
|
||||||
|
readAccessListFile
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
"Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
|
"Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
|
||||||
"bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
|
"bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
|
||||||
"7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" },
|
"7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" },
|
||||||
"B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" }
|
"B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" },
|
||||||
|
"c9e38bfdB9c635F0796ad83CC8705dc379F41c04": { "balance": "500000000000000000000" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,6 @@
|
|||||||
"lint": "eslint . --ignore-path ../.eslintignore"
|
"lint": "eslint . --ignore-path ../.eslintignore"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8.9"
|
"node": ">= 10.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user