Merge the develop branch to the master branch, preparation to v1.2.0

This commit is contained in:
Alexander Kolotov 2020-01-06 22:09:12 +03:00 committed by GitHub
commit 727371f251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 445 additions and 5214 deletions

@ -106,5 +106,4 @@ This project is licensed under the GNU Lesser General Public License v3.0. See t
## References ## References
* [Additional Documentation](https://forum.poa.network/c/tokenbridge) * [TokenBridge Documentation](http://www.tokenbridge.net/)
* [POA20 Bridge FAQ](https://forum.poa.network/c/tokenbridge/poa20-bridge)

@ -1 +1 @@
Subproject commit fd86fed8771363f0ea79ffe93d4d0ed425190124 Subproject commit c85aeb737bfd684b20b99520bd632ba4b8866583

@ -5,6 +5,8 @@ const { isV1Bridge } = require('./utils/serverUtils')
const app = express() const app = express()
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100 const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
console.log('MONITOR_TX_NUMBER_THRESHOLD = ' + MONITOR_TX_NUMBER_THRESHOLD) console.log('MONITOR_TX_NUMBER_THRESHOLD = ' + MONITOR_TX_NUMBER_THRESHOLD)
@ -52,18 +54,25 @@ app.get('/validators', async (req, res, next) => {
const results = await readFile('./responses/validators.json') const results = await readFile('./responses/validators.json')
results.homeOk = true results.homeOk = true
results.foreignOk = true results.foreignOk = true
for (const hv in results.home.validators) {
if (results.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) { if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
results.homeOk = false for (const hv in results.home.validators) {
break if (results.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
results.homeOk = false
break
}
} }
} }
for (const hv in results.foreign.validators) {
if (results.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) { if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
results.foreignOk = false for (const hv in results.foreign.validators) {
break if (results.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
results.foreignOk = false
break
}
} }
} }
results.ok = results.homeOk && results.foreignOk results.ok = results.homeOk && results.foreignOk
res.json(results) res.json(results)
} catch (e) { } catch (e) {

@ -10,12 +10,10 @@ const {
COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_RPC_URL,
COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS,
MONITOR_VALIDATOR_HOME_TX_LIMIT,
COMMON_HOME_GAS_PRICE_SUPPLIER_URL, COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
COMMON_HOME_GAS_PRICE_SPEED_TYPE, COMMON_HOME_GAS_PRICE_SPEED_TYPE,
COMMON_HOME_GAS_PRICE_FALLBACK, COMMON_HOME_GAS_PRICE_FALLBACK,
COMMON_HOME_GAS_PRICE_FACTOR, COMMON_HOME_GAS_PRICE_FACTOR,
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT,
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE, COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
COMMON_FOREIGN_GAS_PRICE_FALLBACK, COMMON_FOREIGN_GAS_PRICE_FALLBACK,
@ -23,6 +21,8 @@ const {
} = process.env } = process.env
const MONITOR_HOME_START_BLOCK = Number(process.env.MONITOR_HOME_START_BLOCK) || 0 const MONITOR_HOME_START_BLOCK = Number(process.env.MONITOR_HOME_START_BLOCK) || 0
const MONITOR_FOREIGN_START_BLOCK = Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0 const MONITOR_FOREIGN_START_BLOCK = Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const Web3Utils = Web3.utils const Web3Utils = Web3.utils
@ -65,76 +65,97 @@ async function main(bridgeMode) {
const foreignBridgeValidators = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress) const foreignBridgeValidators = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress)
logger.debug('calling foreignBridgeValidators getValidatorList()') logger.debug('calling foreignBridgeValidators getValidatorList()')
const foreignValidators = await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, { const foreignValidators = (await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
from: MONITOR_FOREIGN_START_BLOCK, from: MONITOR_FOREIGN_START_BLOCK,
to: foreignBlockNumber, to: foreignBlockNumber,
logger logger
}) })).map(web3Foreign.utils.toChecksumAddress)
logger.debug('calling homeBridgeValidators getValidatorList()') logger.debug('calling homeBridgeValidators getValidatorList()')
const homeValidators = await getValidatorList(homeValidatorsAddress, web3Home.eth, { const homeValidators = (await getValidatorList(homeValidatorsAddress, web3Home.eth, {
from: MONITOR_HOME_START_BLOCK, from: MONITOR_HOME_START_BLOCK,
to: homeBlockNumber, to: homeBlockNumber,
logger logger
}) })).map(web3Home.utils.toChecksumAddress)
const homeBalances = {}
logger.debug('calling asyncForEach homeValidators homeBalances')
await asyncForEach(homeValidators, async v => {
homeBalances[v] = Web3Utils.fromWei(await web3Home.eth.getBalance(v))
})
const foreignVBalances = {} const foreignVBalances = {}
const homeVBalances = {} const homeVBalances = {}
logger.debug('calling home getGasPrices') let homeGasPrice
const homeGasPrice = let homeGasPriceGwei
(await gasPriceFromSupplier(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) || let homeTxCost
Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK)
const homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
const homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
logger.debug('calling foreign getGasPrices') if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
const foreignGasPrice = logger.debug('calling home getGasPrices')
(await gasPriceFromSupplier(() => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL), foreignGasPriceSupplierOpts)) || homeGasPrice =
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK) (await gasPriceFromSupplier(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) ||
const foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei') Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK)
const foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT)) homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
}
let foreignGasPrice
let foreignGasPriceGwei
let foreignTxCost
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
logger.debug('calling foreign getGasPrices')
foreignGasPrice =
(await gasPriceFromSupplier(() => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL), foreignGasPriceSupplierOpts)) ||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))
}
let validatorsMatch = true let validatorsMatch = true
logger.debug('calling asyncForEach foreignValidators foreignVBalances') logger.debug('calling asyncForEach foreignValidators foreignVBalances')
await asyncForEach(foreignValidators, async v => { await asyncForEach(foreignValidators, async v => {
const balance = await web3Foreign.eth.getBalance(v) const balance = await web3Foreign.eth.getBalance(v)
const leftTx = Web3Utils.toBN(balance) if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
.div(foreignTxCost) const leftTx = Web3Utils.toBN(balance)
.toString(10) .div(foreignTxCost)
foreignVBalances[v] = { .toString(10)
balance: Web3Utils.fromWei(balance), foreignVBalances[v] = {
leftTx: Number(leftTx), balance: Web3Utils.fromWei(balance),
gasPrice: Number(foreignGasPriceGwei) leftTx: Number(leftTx),
gasPrice: Number(foreignGasPriceGwei)
}
} else {
foreignVBalances[v] = {
balance: Web3Utils.fromWei(balance)
}
} }
if (!homeValidators.includes(v)) { if (!homeValidators.includes(v)) {
validatorsMatch = false validatorsMatch = false
foreignVBalances[v].onlyOnForeign = true foreignVBalances[v].onlyOnForeign = true
} }
}) })
logger.debug('calling asyncForEach homeValidators homeVBalances') logger.debug('calling asyncForEach homeValidators homeVBalances')
await asyncForEach(homeValidators, async v => { await asyncForEach(homeValidators, async v => {
const balance = await web3Home.eth.getBalance(v) const balance = await web3Home.eth.getBalance(v)
const leftTx = homeTxCost.isZero() if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
? 999999 const leftTx = Web3Utils.toBN(balance)
: Web3Utils.toBN(balance) .div(homeTxCost)
.div(homeTxCost) .toString(10)
.toString(10) homeVBalances[v] = {
homeVBalances[v] = { balance: Web3Utils.fromWei(balance),
balance: Web3Utils.fromWei(balance), leftTx: Number(leftTx),
leftTx: Number(leftTx), gasPrice: Number(homeGasPriceGwei)
gasPrice: Number(homeGasPriceGwei) }
} else {
homeVBalances[v] = {
balance: Web3Utils.fromWei(balance)
}
} }
if (!foreignValidators.includes(v)) { if (!foreignValidators.includes(v)) {
validatorsMatch = false validatorsMatch = false
homeVBalances[v].onlyOnHome = true homeVBalances[v].onlyOnHome = true
} }
}) })
logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()') logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()')
const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call() const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call()
logger.debug('calling foreignBridgeValidators.methods.requiredSignatures().call()') logger.debug('calling foreignBridgeValidators.methods.requiredSignatures().call()')

@ -7,12 +7,15 @@ services:
service: rabbit service: rabbit
networks: networks:
- net_rabbit_bridge_transfer - net_rabbit_bridge_transfer
- net_rabbit_bridge_half_duplex_transfer
- net_rabbit_bridge_swap_tokens_worker
redis: redis:
extends: extends:
file: docker-compose.yml file: docker-compose.yml
service: redis service: redis
networks: networks:
- net_db_bridge_transfer - net_db_bridge_transfer
- net_db_bridge_half_duplex_transfer
bridge_request: bridge_request:
extends: extends:
file: docker-compose.yml file: docker-compose.yml

@ -8,6 +8,11 @@ const {
getTokenType getTokenType
} = require('../../commons') } = require('../../commons')
const emptyLogger = {
debug: () => {},
info: () => {}
}
async function initialChecks() { async function initialChecks() {
const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
let result = {} let result = {}
@ -18,7 +23,7 @@ async function initialChecks() {
result.bridgeableTokenAddress = await bridge.methods.erc20token().call() result.bridgeableTokenAddress = await bridge.methods.erc20token().call()
} else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') { } else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') {
const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
result = await getTokensState(bridge) result = await getTokensState(bridge, emptyLogger)
} }
if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') { if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') {

@ -1,14 +1,25 @@
async function getTokensState(bridgeContract) { async function getTokensState(bridgeContract, logger) {
const context = {} const context = {}
context.bridgeableTokenAddress = await bridgeContract.methods.erc20token().call()
try { try {
logger.debug('Getting bridgeable token address')
context.bridgeableTokenAddress = await bridgeContract.methods.erc20token().call()
logger.debug({ address: context.bridgeableTokenAddress }, 'Token address obtained')
} catch (e) {
throw new Error(`Bridgeable token address cannot be obtained`)
}
try {
logger.debug('Getting Half Duplex token address')
const halfDuplexErc20tokenAddress = await bridgeContract.methods.halfDuplexErc20token().call() const halfDuplexErc20tokenAddress = await bridgeContract.methods.halfDuplexErc20token().call()
logger.debug({ address: halfDuplexErc20tokenAddress }, 'Half Duplex token address obtained')
if (halfDuplexErc20tokenAddress !== context.bridgeableTokenAddress) { if (halfDuplexErc20tokenAddress !== context.bridgeableTokenAddress) {
context.halfDuplexTokenAddress = halfDuplexErc20tokenAddress context.halfDuplexTokenAddress = halfDuplexErc20tokenAddress
} else { } else {
logger.info('Migration to support two tokens was not applied')
context.idle = true context.idle = true
} }
} catch (e) { } catch (e) {
logger.info('Old version of contracts is used')
context.idle = true context.idle = true
} }

@ -125,11 +125,13 @@ async function checkConditions() {
let state let state
switch (config.id) { switch (config.id) {
case 'erc-native-transfer': case 'erc-native-transfer':
state = await getTokensState(bridgeContract) logger.debug('Getting token address to listen Transfer events')
state = await getTokensState(bridgeContract, logger)
updateEventContract(state.bridgeableTokenAddress) updateEventContract(state.bridgeableTokenAddress)
break break
case 'erc-native-half-duplex-transfer': case 'erc-native-half-duplex-transfer':
state = await getTokensState(bridgeContract) logger.debug('Getting Half Duplex token address to listen Transfer events')
state = await getTokensState(bridgeContract, logger)
skipEvents = state.idle skipEvents = state.idle
updateEventContract(state.halfDuplexTokenAddress) updateEventContract(state.halfDuplexTokenAddress)
break break

@ -0,0 +1,204 @@
#!/bin/bash
orig_service="/etc/init.d/poabridge"
new_service=${orig_service}
dockercompose_file="docker-compose-erc-native.yml"
ref_dockercompose_file="docker-compose.yml"
source_dockerimage="poanetwork/tokenbridge-oracle:1.2.0-rc0"
old_oracle_services="bridge_affirmation bridge_collected bridge_request bridge_senderforeign bridge_senderhome bridge_transfer"
new_oracle_services="bridge_affirmation bridge_collected bridge_half_duplex_transfer bridge_request bridge_senderforeign bridge_senderhome bridge_swap_tokens_worker bridge_transfer"
echo "Looking for the service file"
if [ ! -f ${orig_service} ]; then
echo "Service file ${orig_service} not found"
exit 1
fi
echo
echo "Indentifying the directory with docker-compose files"
oracle_dir=`cat ${orig_service} | grep 'WORKDIR=' | sed 's/WORKDIR="//' | tr -d '"'`
if [ -z "${oracle_dir}" ]; then
echo "Cannot identify the directory"
exit 1
fi
echo
echo "Updating the service file"
if [ ! -f ${new_service}".bak" ]; then
cp --preserve=all -f ${new_service} ${new_service}".bak"
fi
if [ ! "${orig_service}" == "${new_service}" ]; then
cp --preserve=all ${orig_service} ${new_service}
fi
sed -i 's/-f docker-compose-transfer.yml/-f docker-compose-erc-native.yml/' ${new_service}
sed -i 's/rebuild)/isnotsupported)/' ${new_service}
if [ -z "`grep 'f docker-compose-erc-native.yml' ${new_service}`" ]; then
echo "The service file was not updated properly"
exit 1
fi
echo
echo "Re-enable service file"
systemctl daemon-reload
if [ ! "$?" == "0" ]; then
echo "An error during the service file reload"
exit 1
fi
echo
echo "Generate a new docker-compose file"
cat <<tEOF > ${oracle_dir}/${dockercompose_file}
networks:
net_db_bridge_affirmation: {driver: bridge}
net_db_bridge_collected: {driver: bridge}
net_db_bridge_half_duplex_transfer: {driver: bridge}
net_db_bridge_request: {driver: bridge}
net_db_bridge_senderforeign: {driver: bridge}
net_db_bridge_senderhome: {driver: bridge}
net_db_bridge_transfer: {driver: bridge}
net_rabbit_bridge_affirmation: {driver: bridge}
net_rabbit_bridge_collected: {driver: bridge}
net_rabbit_bridge_half_duplex_transfer: {driver: bridge}
net_rabbit_bridge_request: {driver: bridge}
net_rabbit_bridge_senderforeign: {driver: bridge}
net_rabbit_bridge_senderhome: {driver: bridge}
net_rabbit_bridge_swap_tokens_worker: {driver: bridge}
net_rabbit_bridge_transfer: {driver: bridge}
services:
bridge_affirmation:
extends: {file: docker-compose.yml, service: bridge_affirmation}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_request, net_rabbit_bridge_request]
depends_on:
- redis
- rabbit
bridge_collected:
extends: {file: docker-compose.yml, service: bridge_collected}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_request, net_rabbit_bridge_request]
depends_on:
- redis
- rabbit
bridge_half_duplex_transfer:
build: {context: .., dockerfile: oracle/Dockerfile}
cpus: 0.1
entrypoint: yarn watcher:half-duplex-transfer
env_file: ./.env
environment: [NODE_ENV=production, 'ORACLE_VALIDATOR_ADDRESS=\${ORACLE_VALIDATOR_ADDRESS}']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_half_duplex_transfer, net_rabbit_bridge_half_duplex_transfer]
restart: unless-stopped
depends_on:
- redis
- rabbit
bridge_request:
extends: {file: docker-compose.yml, service: bridge_request}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_request, net_rabbit_bridge_request]
depends_on:
- redis
- rabbit
bridge_senderforeign:
extends: {file: docker-compose.yml, service: bridge_senderforeign}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_request, net_rabbit_bridge_request]
depends_on:
- redis
- rabbit
bridge_senderhome:
extends: {file: docker-compose.yml, service: bridge_senderhome}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_request, net_rabbit_bridge_request]
depends_on:
- redis
- rabbit
bridge_swap_tokens_worker:
build: {context: .., dockerfile: oracle/Dockerfile}
cpus: 0.1
entrypoint: yarn worker:swap-tokens
env_file: ./.env
environment: [NODE_ENV=production, 'ORACLE_VALIDATOR_ADDRESS=\${ORACLE_VALIDATOR_ADDRESS}']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_rabbit_bridge_swap_tokens_worker]
restart: unless-stopped
depends_on:
- rabbit
bridge_transfer:
build: {context: .., dockerfile: oracle/Dockerfile}
cpus: 0.1
entrypoint: yarn watcher:transfer
env_file: ./.env
environment: [NODE_ENV=production, 'ORACLE_VALIDATOR_ADDRESS=\${ORACLE_VALIDATOR_ADDRESS}']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_transfer, net_rabbit_bridge_transfer]
restart: unless-stopped
depends_on:
- redis
- rabbit
rabbit:
extends: {file: docker-compose.yml, service: rabbit}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_rabbit_bridge_transfer, net_rabbit_bridge_swap_tokens_worker, net_rabbit_bridge_half_duplex_transfer]
redis:
extends: {file: docker-compose.yml, service: redis}
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_transfer, net_db_bridge_half_duplex_transfer]
version: '2.4'
tEOF
if [ ! -f ${oracle_dir}/${dockercompose_file} ]; then
echo "New compose file was not generated"
exit 1
fi
chmod --reference=${oracle_dir}/${ref_dockercompose_file} ${oracle_dir}/${dockercompose_file}
chown --reference=${oracle_dir}/${ref_dockercompose_file} ${oracle_dir}/${dockercompose_file}
echo
echo "Delete previous docker images"
for i in ${old_oracle_services}; do
docker rmi "oracle_"${i}
done
echo
echo "Pull the docker image for 1.2.0-rc0"
docker pull ${source_dockerimage}
docker inspect ${source_dockerimage} >/dev/null
if [ ! "$?" == "0" ]; then
echo "An error with pulling of the docker image"
exit 1
fi
echo
echo "Create new set of docker images"
for i in ${new_oracle_services}; do
docker tag ${source_dockerimage} "oracle_"${i}
done

5295
yarn.lock

File diff suppressed because it is too large Load Diff