Fixed eslint error/warnings
This commit is contained in:
parent
34d6927c0b
commit
62c1dd8064
|
@ -8,7 +8,7 @@ const {
|
||||||
THRESHOLD, HOME_TOKEN_ADDRESS, MIN_TX_LIMIT, MAX_TX_LIMIT, BLOCKS_RANGE_SIZE
|
THRESHOLD, HOME_TOKEN_ADDRESS, MIN_TX_LIMIT, MAX_TX_LIMIT, BLOCKS_RANGE_SIZE
|
||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
module.exports = deployer => {
|
module.exports = (deployer) => {
|
||||||
deployer.deploy(
|
deployer.deploy(
|
||||||
Bridge,
|
Bridge,
|
||||||
THRESHOLD,
|
THRESHOLD,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const SharedDB = artifacts.require('SharedDB')
|
const SharedDB = artifacts.require('SharedDB')
|
||||||
|
|
||||||
module.exports = deployer => {
|
module.exports = (deployer) => {
|
||||||
deployer.deploy(SharedDB)
|
deployer.deploy(SharedDB)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,72 @@ const { publicKeyToAddress } = require('./crypto')
|
||||||
|
|
||||||
const { FOREIGN_URL, PROXY_URL, FOREIGN_ASSET } = process.env
|
const { FOREIGN_URL, PROXY_URL, FOREIGN_ASSET } = process.env
|
||||||
|
|
||||||
const FOREIGN_START_TIME = parseInt(process.env.FOREIGN_START_TIME)
|
const FOREIGN_START_TIME = parseInt(process.env.FOREIGN_START_TIME, 10)
|
||||||
const FOREIGN_FETCH_INTERVAL = parseInt(process.env.FOREIGN_FETCH_INTERVAL)
|
const FOREIGN_FETCH_INTERVAL = parseInt(process.env.FOREIGN_FETCH_INTERVAL, 10)
|
||||||
const FOREIGN_FETCH_BLOCK_TIME_OFFSET = parseInt(process.env.FOREIGN_FETCH_BLOCK_TIME_OFFSET)
|
const FOREIGN_FETCH_BLOCK_TIME_OFFSET = parseInt(process.env.FOREIGN_FETCH_BLOCK_TIME_OFFSET, 10)
|
||||||
|
|
||||||
const foreignHttpClient = axios.create({ baseURL: FOREIGN_URL })
|
const foreignHttpClient = axios.create({ baseURL: FOREIGN_URL })
|
||||||
const proxyHttpClient = axios.create({ baseURL: PROXY_URL })
|
const proxyHttpClient = axios.create({ baseURL: PROXY_URL })
|
||||||
|
|
||||||
|
function getLastForeignAddress() {
|
||||||
|
const epoch = Math.max(0, ...fs.readdirSync('/keys')
|
||||||
|
.map((x) => parseInt(x.split('.')[0].substr(4), 10)))
|
||||||
|
if (epoch === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const keysFile = `/keys/keys${epoch}.store`
|
||||||
|
const publicKey = JSON.parse(fs.readFileSync(keysFile))[5]
|
||||||
|
return publicKeyToAddress(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTx(hash) {
|
||||||
|
return foreignHttpClient
|
||||||
|
.get(`/api/v1/tx/${hash}`, {
|
||||||
|
params: {
|
||||||
|
format: 'json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => res.data.tx.value)
|
||||||
|
.catch(() => getTx(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBlockTime() {
|
||||||
|
return foreignHttpClient
|
||||||
|
.get('/api/v1/time')
|
||||||
|
.then((res) => Date.parse(res.data.block_time) - FOREIGN_FETCH_BLOCK_TIME_OFFSET)
|
||||||
|
.catch(() => getBlockTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchNewTransactions() {
|
||||||
|
logger.debug('Fetching new transactions')
|
||||||
|
const startTime = parseInt(await redis.get('foreignTime'), 10) + 1
|
||||||
|
const address = getLastForeignAddress()
|
||||||
|
const endTime = Math.min(startTime + 3 * 30 * 24 * 60 * 60 * 1000, await getBlockTime())
|
||||||
|
if (address === null) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
logger.debug('Sending api transactions request')
|
||||||
|
const params = {
|
||||||
|
address,
|
||||||
|
side: 'RECEIVE',
|
||||||
|
txAsset: FOREIGN_ASSET,
|
||||||
|
txType: 'TRANSFER',
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.trace('%o', params)
|
||||||
|
const transactions = (await foreignHttpClient
|
||||||
|
.get('/api/v1/transactions', { params })).data.tx
|
||||||
|
return {
|
||||||
|
transactions,
|
||||||
|
endTime
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return await fetchNewTransactions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
if (await redis.get('foreignTime') === null) {
|
if (await redis.get('foreignTime') === null) {
|
||||||
logger.info('Set default foreign time')
|
logger.info('Set default foreign time')
|
||||||
|
@ -26,21 +85,23 @@ async function initialize () {
|
||||||
async function main() {
|
async function main() {
|
||||||
const { transactions, endTime } = await fetchNewTransactions()
|
const { transactions, endTime } = await fetchNewTransactions()
|
||||||
if (!transactions || transactions.length === 0) {
|
if (!transactions || transactions.length === 0) {
|
||||||
logger.debug(`Found 0 new transactions`)
|
logger.debug('Found 0 new transactions')
|
||||||
await new Promise(r => setTimeout(r, FOREIGN_FETCH_INTERVAL))
|
await new Promise((r) => setTimeout(r, FOREIGN_FETCH_INTERVAL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Found ${transactions.length} new transactions`)
|
logger.info(`Found ${transactions.length} new transactions`)
|
||||||
logger.trace('%o', transactions)
|
logger.trace('%o', transactions)
|
||||||
|
|
||||||
for (const tx of transactions.reverse()) {
|
for (let i = transactions.length - 1; i >= 0; i -= 1) {
|
||||||
|
const tx = transactions[i]
|
||||||
if (tx.memo !== 'funding') {
|
if (tx.memo !== 'funding') {
|
||||||
const publicKeyEncoded = (await getTx(tx.txHash)).signatures[0].pub_key.value
|
const publicKeyEncoded = (await getTx(tx.txHash)).signatures[0].pub_key.value
|
||||||
await proxyHttpClient
|
await proxyHttpClient
|
||||||
.post('/transfer', {
|
.post('/transfer', {
|
||||||
to: computeAddress(Buffer.from(publicKeyEncoded, 'base64')),
|
to: computeAddress(Buffer.from(publicKeyEncoded, 'base64')),
|
||||||
value: new BN(tx.value).multipliedBy(10 ** 18).integerValue(),
|
value: new BN(tx.value).multipliedBy(10 ** 18)
|
||||||
|
.integerValue(),
|
||||||
hash: `0x${tx.txHash}`
|
hash: `0x${tx.txHash}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -48,60 +109,8 @@ async function main () {
|
||||||
await redis.set('foreignTime', endTime)
|
await redis.set('foreignTime', endTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTx (hash) {
|
initialize()
|
||||||
return foreignHttpClient
|
.then(async () => {
|
||||||
.get(`/api/v1/tx/${hash}`, {
|
|
||||||
params: {
|
|
||||||
format: 'json'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => res.data.tx.value)
|
|
||||||
.catch(() => getTx(hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBlockTime () {
|
|
||||||
return foreignHttpClient
|
|
||||||
.get(`/api/v1/time`)
|
|
||||||
.then(res => Date.parse(res.data.block_time) - FOREIGN_FETCH_BLOCK_TIME_OFFSET)
|
|
||||||
.catch(() => getBlockTime())
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchNewTransactions () {
|
|
||||||
logger.debug('Fetching new transactions')
|
|
||||||
const startTime = parseInt(await redis.get('foreignTime')) + 1
|
|
||||||
const address = getLastForeignAddress()
|
|
||||||
const endTime = Math.min(startTime + 3 * 30 * 24 * 60 * 60 * 1000, await getBlockTime())
|
|
||||||
if (address === null)
|
|
||||||
return {}
|
|
||||||
logger.debug('Sending api transactions request')
|
|
||||||
const params = {
|
|
||||||
address,
|
|
||||||
side: 'RECEIVE',
|
|
||||||
txAsset: FOREIGN_ASSET,
|
|
||||||
txType: 'TRANSFER',
|
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
logger.trace('%o', params)
|
|
||||||
const transactions = (await foreignHttpClient
|
|
||||||
.get('/api/v1/transactions', { params })).data.tx
|
|
||||||
return { transactions, endTime }
|
|
||||||
} catch (e) {
|
|
||||||
return await fetchNewTransactions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLastForeignAddress () {
|
|
||||||
const epoch = Math.max(0, ...fs.readdirSync('/keys').map(x => parseInt(x.split('.')[0].substr(4))))
|
|
||||||
if (epoch === 0)
|
|
||||||
return null
|
|
||||||
const keysFile = `/keys/keys${epoch}.store`
|
|
||||||
const publicKey = JSON.parse(fs.readFileSync(keysFile))[5]
|
|
||||||
return publicKeyToAddress(publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize().then(async () => {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
await main()
|
await main()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
"ethers": "4.0.33",
|
"ethers": "4.0.33",
|
||||||
"pino": "5.13.4",
|
"pino": "5.13.4",
|
||||||
"pino-pretty": "3.2.1"
|
"pino-pretty": "3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,8 @@ services:
|
||||||
- HOME_RPC_URL
|
- HOME_RPC_URL
|
||||||
- HOME_BRIDGE_ADDRESS
|
- HOME_BRIDGE_ADDRESS
|
||||||
- HOME_TOKEN_ADDRESS
|
- HOME_TOKEN_ADDRESS
|
||||||
- HOME_CHAIN_ID
|
|
||||||
- SIDE_RPC_URL
|
- SIDE_RPC_URL
|
||||||
- SIDE_SHARED_DB_ADDRESS
|
- SIDE_SHARED_DB_ADDRESS
|
||||||
- SIDE_CHAIN_ID
|
|
||||||
- VALIDATOR_PRIVATE_KEY
|
- VALIDATOR_PRIVATE_KEY
|
||||||
- FOREIGN_URL
|
- FOREIGN_URL
|
||||||
- FOREIGN_ASSET
|
- FOREIGN_ASSET
|
||||||
|
@ -83,7 +81,6 @@ services:
|
||||||
- HOME_RPC_URL
|
- HOME_RPC_URL
|
||||||
- HOME_BRIDGE_ADDRESS
|
- HOME_BRIDGE_ADDRESS
|
||||||
- HOME_TOKEN_ADDRESS
|
- HOME_TOKEN_ADDRESS
|
||||||
- HOME_CHAIN_ID
|
|
||||||
- HOME_START_BLOCK
|
- HOME_START_BLOCK
|
||||||
- BLOCKS_RANGE_SIZE
|
- BLOCKS_RANGE_SIZE
|
||||||
- VALIDATOR_PRIVATE_KEY
|
- VALIDATOR_PRIVATE_KEY
|
||||||
|
|
|
@ -9,10 +9,8 @@ services:
|
||||||
- HOME_RPC_URL
|
- HOME_RPC_URL
|
||||||
- HOME_BRIDGE_ADDRESS
|
- HOME_BRIDGE_ADDRESS
|
||||||
- HOME_TOKEN_ADDRESS
|
- HOME_TOKEN_ADDRESS
|
||||||
- HOME_CHAIN_ID
|
|
||||||
- SIDE_RPC_URL
|
- SIDE_RPC_URL
|
||||||
- SIDE_SHARED_DB_ADDRESS
|
- SIDE_SHARED_DB_ADDRESS
|
||||||
- SIDE_CHAIN_ID
|
|
||||||
- VALIDATOR_PRIVATE_KEY
|
- VALIDATOR_PRIVATE_KEY
|
||||||
- FOREIGN_URL
|
- FOREIGN_URL
|
||||||
- FOREIGN_ASSET
|
- FOREIGN_ASSET
|
||||||
|
@ -93,7 +91,6 @@ services:
|
||||||
- HOME_RPC_URL
|
- HOME_RPC_URL
|
||||||
- HOME_BRIDGE_ADDRESS
|
- HOME_BRIDGE_ADDRESS
|
||||||
- HOME_TOKEN_ADDRESS
|
- HOME_TOKEN_ADDRESS
|
||||||
- HOME_CHAIN_ID
|
|
||||||
- HOME_START_BLOCK
|
- HOME_START_BLOCK
|
||||||
- VALIDATOR_PRIVATE_KEY
|
- VALIDATOR_PRIVATE_KEY
|
||||||
- 'RABBITMQ_URL=amqp://rabbitmq:5672'
|
- 'RABBITMQ_URL=amqp://rabbitmq:5672'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const utils = require('ethers').utils
|
const { utils } = require('ethers')
|
||||||
const BN = require('bignumber.js')
|
const BN = require('bignumber.js')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
|
@ -10,19 +10,21 @@ const { publicKeyToAddress } = require('./crypto')
|
||||||
|
|
||||||
const abiBridge = require('./contracts_data/Bridge.json').abi
|
const abiBridge = require('./contracts_data/Bridge.json').abi
|
||||||
|
|
||||||
const { HOME_RPC_URL, HOME_BRIDGE_ADDRESS, RABBITMQ_URL, HOME_START_BLOCK, VALIDATOR_PRIVATE_KEY } = process.env
|
const {
|
||||||
|
HOME_RPC_URL, HOME_BRIDGE_ADDRESS, RABBITMQ_URL, HOME_START_BLOCK, VALIDATOR_PRIVATE_KEY
|
||||||
|
} = process.env
|
||||||
|
|
||||||
const homeWeb3 = new Web3(HOME_RPC_URL)
|
const homeWeb3 = new Web3(HOME_RPC_URL)
|
||||||
const bridge = new homeWeb3.eth.Contract(abiBridge, HOME_BRIDGE_ADDRESS)
|
const bridge = new homeWeb3.eth.Contract(abiBridge, HOME_BRIDGE_ADDRESS)
|
||||||
const validatorAddress = homeWeb3.eth.accounts.privateKeyToAccount(`0x${VALIDATOR_PRIVATE_KEY}`).address
|
const validatorAddress = homeWeb3.eth.accounts.privateKeyToAccount(`0x${VALIDATOR_PRIVATE_KEY}`).address
|
||||||
|
|
||||||
|
const foreignNonce = []
|
||||||
let channel
|
let channel
|
||||||
let exchangeQueue
|
let exchangeQueue
|
||||||
let signQueue
|
let signQueue
|
||||||
let keygenQueue
|
let keygenQueue
|
||||||
let cancelKeygenQueue
|
let cancelKeygenQueue
|
||||||
let blockNumber
|
let blockNumber
|
||||||
let foreignNonce = []
|
|
||||||
let epoch
|
let epoch
|
||||||
let epochStart
|
let epochStart
|
||||||
let redisTx
|
let redisTx
|
||||||
|
@ -36,10 +38,11 @@ async function resetFutureMessages (queue) {
|
||||||
if (messageCount) {
|
if (messageCount) {
|
||||||
logger.info(`Filtering ${messageCount} reloaded messages from queue ${queue.name}`)
|
logger.info(`Filtering ${messageCount} reloaded messages from queue ${queue.name}`)
|
||||||
const backup = await assertQueue(channel, `${queue.name}.backup`)
|
const backup = await assertQueue(channel, `${queue.name}.backup`)
|
||||||
do {
|
while (true) {
|
||||||
const message = await queue.get()
|
const message = await queue.get()
|
||||||
if (message === false)
|
if (message === false) {
|
||||||
break
|
break
|
||||||
|
}
|
||||||
const data = JSON.parse(message.content)
|
const data = JSON.parse(message.content)
|
||||||
if (data.blockNumber < blockNumber) {
|
if (data.blockNumber < blockNumber) {
|
||||||
logger.debug('Saving message %o', data)
|
logger.debug('Saving message %o', data)
|
||||||
|
@ -48,140 +51,42 @@ async function resetFutureMessages (queue) {
|
||||||
logger.debug('Dropping message %o', data)
|
logger.debug('Dropping message %o', data)
|
||||||
}
|
}
|
||||||
channel.ack(message)
|
channel.ack(message)
|
||||||
} while (true)
|
}
|
||||||
|
|
||||||
logger.debug('Dropped messages came from future')
|
logger.debug('Dropped messages came from future')
|
||||||
|
|
||||||
do {
|
while (true) {
|
||||||
const message = await backup.get()
|
const message = await backup.get()
|
||||||
if (message === false)
|
if (message === false) {
|
||||||
break
|
break
|
||||||
|
}
|
||||||
const data = JSON.parse(message.content)
|
const data = JSON.parse(message.content)
|
||||||
logger.debug('Requeuing message %o', data)
|
logger.debug('Requeuing message %o', data)
|
||||||
queue.send(data)
|
queue.send(data)
|
||||||
channel.ack(message)
|
channel.ack(message)
|
||||||
} while (true)
|
}
|
||||||
|
|
||||||
logger.debug('Redirected messages back to initial queue')
|
logger.debug('Redirected messages back to initial queue')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initialize () {
|
|
||||||
channel = await connectRabbit(RABBITMQ_URL)
|
|
||||||
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
|
||||||
signQueue = await assertQueue(channel, 'signQueue')
|
|
||||||
keygenQueue = await assertQueue(channel, 'keygenQueue')
|
|
||||||
cancelKeygenQueue = await assertQueue(channel, 'cancelKeygenQueue')
|
|
||||||
|
|
||||||
const events = await bridge.getPastEvents('EpochStart', {
|
|
||||||
fromBlock: 1
|
|
||||||
})
|
|
||||||
epoch = events.length ? events[events.length - 1].returnValues.epoch.toNumber() : 0
|
|
||||||
logger.info(`Current epoch ${epoch}`)
|
|
||||||
epochStart = events.length ? events[events.length - 1].blockNumber : 1
|
|
||||||
const saved = (parseInt(await redis.get('homeBlock')) + 1) || parseInt(HOME_START_BLOCK)
|
|
||||||
if (epochStart > saved) {
|
|
||||||
logger.info(`Data in db is outdated, starting from epoch ${epoch}, block #${epochStart}`)
|
|
||||||
blockNumber = epochStart
|
|
||||||
rangeSize = (await bridge.methods.getRangeSize().call()).toNumber()
|
|
||||||
await redis.multi()
|
|
||||||
.set('homeBlock', blockNumber - 1)
|
|
||||||
.set(`foreignNonce${epoch}`, 0)
|
|
||||||
.exec()
|
|
||||||
foreignNonce[epoch] = 0
|
|
||||||
} else {
|
|
||||||
logger.info('Restoring epoch and block number from local db')
|
|
||||||
blockNumber = saved
|
|
||||||
foreignNonce[epoch] = parseInt(await redis.get(`foreignNonce${epoch}`)) || 0
|
|
||||||
}
|
|
||||||
logger.debug('Checking if current validator')
|
|
||||||
isCurrentValidator = (await bridge.methods.getValidators().call()).includes(validatorAddress)
|
|
||||||
if (isCurrentValidator) {
|
|
||||||
logger.info(`${validatorAddress} is a current validator`)
|
|
||||||
} else {
|
|
||||||
logger.info(`${validatorAddress} is not a current validator`)
|
|
||||||
}
|
|
||||||
|
|
||||||
await resetFutureMessages(keygenQueue)
|
|
||||||
await resetFutureMessages(cancelKeygenQueue)
|
|
||||||
await resetFutureMessages(exchangeQueue)
|
|
||||||
await resetFutureMessages(signQueue)
|
|
||||||
logger.debug(`Sending start commands`)
|
|
||||||
await axios.get('http://keygen:8001/start')
|
|
||||||
await axios.get('http://signer:8001/start')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main () {
|
|
||||||
logger.debug(`Watching events in block #${blockNumber}`)
|
|
||||||
if (await homeWeb3.eth.getBlock(blockNumber) === null) {
|
|
||||||
logger.debug('No block')
|
|
||||||
await new Promise(r => setTimeout(r, 1000))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
redisTx = redis.multi()
|
|
||||||
|
|
||||||
const bridgeEvents = await bridge.getPastEvents('allEvents', {
|
|
||||||
fromBlock: blockNumber,
|
|
||||||
toBlock: blockNumber
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const event of bridgeEvents) {
|
|
||||||
switch (event.event) {
|
|
||||||
case 'NewEpoch':
|
|
||||||
await sendKeygen(event)
|
|
||||||
break
|
|
||||||
case 'NewEpochCancelled':
|
|
||||||
sendKeygenCancellation(event)
|
|
||||||
break
|
|
||||||
case 'NewFundsTransfer':
|
|
||||||
isCurrentValidator && await sendSignFundsTransfer(event)
|
|
||||||
break
|
|
||||||
case 'ExchangeRequest':
|
|
||||||
isCurrentValidator && await sendSign(event)
|
|
||||||
break
|
|
||||||
case 'EpochStart':
|
|
||||||
await processEpochStart(event)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((blockNumber + 1 - epochStart) % rangeSize === 0) {
|
|
||||||
logger.info('Reached end of the current block range')
|
|
||||||
|
|
||||||
if (lastTransactionBlockNumber > blockNumber - rangeSize) {
|
|
||||||
logger.info('Sending message to start signature generation for the ended range')
|
|
||||||
await sendStartSign()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockNumber++
|
|
||||||
// Exec redis tx
|
|
||||||
await redisTx.incr('homeBlock').exec()
|
|
||||||
await redis.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize().then(async () => {
|
|
||||||
while (true) {
|
|
||||||
await main()
|
|
||||||
}
|
|
||||||
}, e => logger.warn('Initialization failed %o', e))
|
|
||||||
|
|
||||||
async function sendKeygen(event) {
|
async function sendKeygen(event) {
|
||||||
const newEpoch = event.returnValues.newEpoch.toNumber()
|
const newEpoch = event.returnValues.newEpoch.toNumber()
|
||||||
keygenQueue.send({
|
keygenQueue.send({
|
||||||
epoch: newEpoch,
|
epoch: newEpoch,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
threshold: (await bridge.methods.getThreshold(newEpoch).call()).toNumber(),
|
threshold: (await bridge.methods.getThreshold(newEpoch)
|
||||||
parties: (await bridge.methods.getParties(newEpoch).call()).toNumber()
|
.call()).toNumber(),
|
||||||
|
parties: (await bridge.methods.getParties(newEpoch)
|
||||||
|
.call()).toNumber()
|
||||||
})
|
})
|
||||||
logger.debug('Sent keygen start event')
|
logger.debug('Sent keygen start event')
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendKeygenCancellation(event) {
|
function sendKeygenCancellation(event) {
|
||||||
const epoch = event.returnValues.epoch.toNumber()
|
const eventEpoch = event.returnValues.epoch.toNumber()
|
||||||
cancelKeygenQueue.send({
|
cancelKeygenQueue.send({
|
||||||
epoch,
|
epoch: eventEpoch,
|
||||||
blockNumber
|
blockNumber
|
||||||
})
|
})
|
||||||
logger.debug('Sent keygen cancellation event')
|
logger.debug('Sent keygen cancellation event')
|
||||||
|
@ -195,11 +100,13 @@ async function sendSignFundsTransfer (event) {
|
||||||
blockNumber,
|
blockNumber,
|
||||||
newEpoch,
|
newEpoch,
|
||||||
nonce: foreignNonce[oldEpoch],
|
nonce: foreignNonce[oldEpoch],
|
||||||
threshold: (await bridge.methods.getThreshold(oldEpoch).call()).toNumber(),
|
threshold: (await bridge.methods.getThreshold(oldEpoch)
|
||||||
parties: (await bridge.methods.getParties(oldEpoch).call()).toNumber()
|
.call()).toNumber(),
|
||||||
|
parties: (await bridge.methods.getParties(oldEpoch)
|
||||||
|
.call()).toNumber()
|
||||||
})
|
})
|
||||||
logger.debug('Sent sign funds transfer event')
|
logger.debug('Sent sign funds transfer event')
|
||||||
foreignNonce[oldEpoch]++
|
foreignNonce[oldEpoch] += 1
|
||||||
redisTx.incr(`foreignNonce${oldEpoch}`)
|
redisTx.incr(`foreignNonce${oldEpoch}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +122,11 @@ async function sendSign (event) {
|
||||||
chainId: await homeWeb3.eth.net.getId()
|
chainId: await homeWeb3.eth.net.getId()
|
||||||
})
|
})
|
||||||
const hash = homeWeb3.utils.sha3(msg)
|
const hash = homeWeb3.utils.sha3(msg)
|
||||||
const publicKey = utils.recoverPublicKey(hash, { r: tx.r, s: tx.s, v: tx.v })
|
const publicKey = utils.recoverPublicKey(hash, {
|
||||||
|
r: tx.r,
|
||||||
|
s: tx.s,
|
||||||
|
v: tx.v
|
||||||
|
})
|
||||||
const msgToQueue = {
|
const msgToQueue = {
|
||||||
epoch,
|
epoch,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
|
@ -223,7 +134,8 @@ async function sendSign (event) {
|
||||||
x: publicKey.substr(4, 64),
|
x: publicKey.substr(4, 64),
|
||||||
y: publicKey.substr(68, 64)
|
y: publicKey.substr(68, 64)
|
||||||
}),
|
}),
|
||||||
value: (new BN(event.returnValues.value)).dividedBy(10 ** 18).toFixed(8, 3),
|
value: (new BN(event.returnValues.value)).dividedBy(10 ** 18)
|
||||||
|
.toFixed(8, 3),
|
||||||
nonce: event.returnValues.nonce.toNumber()
|
nonce: event.returnValues.nonce.toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,18 +152,23 @@ async function sendStartSign () {
|
||||||
signQueue.send({
|
signQueue.send({
|
||||||
epoch,
|
epoch,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
nonce: foreignNonce[epoch]++,
|
nonce: foreignNonce[epoch],
|
||||||
threshold: (await bridge.methods.getThreshold(epoch).call()).toNumber(),
|
threshold: (await bridge.methods.getThreshold(epoch)
|
||||||
parties: (await bridge.methods.getParties(epoch).call()).toNumber()
|
.call()).toNumber(),
|
||||||
|
parties: (await bridge.methods.getParties(epoch)
|
||||||
|
.call()).toNumber()
|
||||||
})
|
})
|
||||||
|
foreignNonce[epoch] += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processEpochStart(event) {
|
async function processEpochStart(event) {
|
||||||
epoch = event.returnValues.epoch.toNumber()
|
epoch = event.returnValues.epoch.toNumber()
|
||||||
epochStart = blockNumber
|
epochStart = blockNumber
|
||||||
logger.info(`Epoch ${epoch} started`)
|
logger.info(`Epoch ${epoch} started`)
|
||||||
rangeSize = (await bridge.methods.getRangeSize().call()).toNumber()
|
rangeSize = (await bridge.methods.getRangeSize()
|
||||||
isCurrentValidator = (await bridge.methods.getValidators().call()).includes(validatorAddress)
|
.call()).toNumber()
|
||||||
|
isCurrentValidator = (await bridge.methods.getValidators()
|
||||||
|
.call()).includes(validatorAddress)
|
||||||
if (isCurrentValidator) {
|
if (isCurrentValidator) {
|
||||||
logger.info(`${validatorAddress} is a current validator`)
|
logger.info(`${validatorAddress} is a current validator`)
|
||||||
} else {
|
} else {
|
||||||
|
@ -260,3 +177,115 @@ async function processEpochStart (event) {
|
||||||
logger.info(`Updated range size to ${rangeSize}`)
|
logger.info(`Updated range size to ${rangeSize}`)
|
||||||
foreignNonce[epoch] = 0
|
foreignNonce[epoch] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
channel = await connectRabbit(RABBITMQ_URL)
|
||||||
|
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
||||||
|
signQueue = await assertQueue(channel, 'signQueue')
|
||||||
|
keygenQueue = await assertQueue(channel, 'keygenQueue')
|
||||||
|
cancelKeygenQueue = await assertQueue(channel, 'cancelKeygenQueue')
|
||||||
|
|
||||||
|
const events = await bridge.getPastEvents('EpochStart', {
|
||||||
|
fromBlock: 1
|
||||||
|
})
|
||||||
|
epoch = events.length ? events[events.length - 1].returnValues.epoch.toNumber() : 0
|
||||||
|
logger.info(`Current epoch ${epoch}`)
|
||||||
|
epochStart = events.length ? events[events.length - 1].blockNumber : 1
|
||||||
|
const saved = (parseInt(await redis.get('homeBlock'), 10) + 1) || parseInt(HOME_START_BLOCK, 10)
|
||||||
|
if (epochStart > saved) {
|
||||||
|
logger.info(`Data in db is outdated, starting from epoch ${epoch}, block #${epochStart}`)
|
||||||
|
blockNumber = epochStart
|
||||||
|
rangeSize = (await bridge.methods.getRangeSize()
|
||||||
|
.call()).toNumber()
|
||||||
|
await redis.multi()
|
||||||
|
.set('homeBlock', blockNumber - 1)
|
||||||
|
.set(`foreignNonce${epoch}`, 0)
|
||||||
|
.exec()
|
||||||
|
foreignNonce[epoch] = 0
|
||||||
|
} else {
|
||||||
|
logger.info('Restoring epoch and block number from local db')
|
||||||
|
blockNumber = saved
|
||||||
|
foreignNonce[epoch] = parseInt(await redis.get(`foreignNonce${epoch}`), 10) || 0
|
||||||
|
}
|
||||||
|
logger.debug('Checking if current validator')
|
||||||
|
isCurrentValidator = (await bridge.methods.getValidators()
|
||||||
|
.call()).includes(validatorAddress)
|
||||||
|
if (isCurrentValidator) {
|
||||||
|
logger.info(`${validatorAddress} is a current validator`)
|
||||||
|
} else {
|
||||||
|
logger.info(`${validatorAddress} is not a current validator`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await resetFutureMessages(keygenQueue)
|
||||||
|
await resetFutureMessages(cancelKeygenQueue)
|
||||||
|
await resetFutureMessages(exchangeQueue)
|
||||||
|
await resetFutureMessages(signQueue)
|
||||||
|
logger.debug('Sending start commands')
|
||||||
|
await axios.get('http://keygen:8001/start')
|
||||||
|
await axios.get('http://signer:8001/start')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
logger.debug(`Watching events in block #${blockNumber}`)
|
||||||
|
if (await homeWeb3.eth.getBlock(blockNumber) === null) {
|
||||||
|
logger.debug('No block')
|
||||||
|
await new Promise((r) => setTimeout(r, 1000))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redisTx = redis.multi()
|
||||||
|
|
||||||
|
const bridgeEvents = await bridge.getPastEvents('allEvents', {
|
||||||
|
fromBlock: blockNumber,
|
||||||
|
toBlock: blockNumber
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < bridgeEvents.length; i += 1) {
|
||||||
|
const event = bridgeEvents[i]
|
||||||
|
switch (event.event) {
|
||||||
|
case 'NewEpoch':
|
||||||
|
await sendKeygen(event)
|
||||||
|
break
|
||||||
|
case 'NewEpochCancelled':
|
||||||
|
sendKeygenCancellation(event)
|
||||||
|
break
|
||||||
|
case 'NewFundsTransfer':
|
||||||
|
if (isCurrentValidator) {
|
||||||
|
await sendSignFundsTransfer(event)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'ExchangeRequest':
|
||||||
|
if (isCurrentValidator) {
|
||||||
|
await sendSign(event)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'EpochStart':
|
||||||
|
await processEpochStart(event)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logger.warn('Unknown event %o', event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((blockNumber + 1 - epochStart) % rangeSize === 0) {
|
||||||
|
logger.info('Reached end of the current block range')
|
||||||
|
|
||||||
|
if (lastTransactionBlockNumber > blockNumber - rangeSize) {
|
||||||
|
logger.info('Sending message to start signature generation for the ended range')
|
||||||
|
await sendStartSign()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNumber += 1
|
||||||
|
// Exec redis tx
|
||||||
|
await redisTx.incr('homeBlock')
|
||||||
|
.exec()
|
||||||
|
await redis.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
.then(async () => {
|
||||||
|
while (true) {
|
||||||
|
await main()
|
||||||
|
}
|
||||||
|
}, (e) => logger.warn('Initialization failed %o', e))
|
||||||
|
|
|
@ -11,5 +11,8 @@
|
||||||
"pino": "5.13.4",
|
"pino": "5.13.4",
|
||||||
"pino-pretty": "3.2.1",
|
"pino-pretty": "3.2.1",
|
||||||
"axios": "0.19.0"
|
"axios": "0.19.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,16 @@ function Tokenizer (_buffer) {
|
||||||
const buffer = _buffer
|
const buffer = _buffer
|
||||||
let position = 0
|
let position = 0
|
||||||
return {
|
return {
|
||||||
isEmpty: function () {
|
isEmpty() {
|
||||||
return position === buffer.length
|
return position === buffer.length
|
||||||
},
|
},
|
||||||
parse: function (length = 32, base = 16) {
|
parse(length = 32, base = 16) {
|
||||||
const res = new BN(buffer.slice(position, position + length)).toString(base)
|
const res = new BN(buffer.slice(position, position + length)).toString(base)
|
||||||
position += length
|
position += length
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
byte: function () {
|
byte() {
|
||||||
|
// eslint-disable-next-line no-plusplus
|
||||||
return buffer[position++]
|
return buffer[position++]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +22,7 @@ function Tokenizer (_buffer) {
|
||||||
const keygenDecoders = [
|
const keygenDecoders = [
|
||||||
null,
|
null,
|
||||||
// round 1
|
// round 1
|
||||||
function (tokenizer) {
|
(tokenizer) => {
|
||||||
const res = {
|
const res = {
|
||||||
e: {
|
e: {
|
||||||
n: tokenizer.parse(256, 10)
|
n: tokenizer.parse(256, 10)
|
||||||
|
@ -37,23 +38,21 @@ const keygenDecoders = [
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
// round 2
|
// round 2
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
|
||||||
blind_factor: tokenizer.parse(),
|
blind_factor: tokenizer.parse(),
|
||||||
y_i: {
|
y_i: {
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
y: tokenizer.parse()
|
y: tokenizer.parse()
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
// round 3
|
// round 3
|
||||||
function (tokenizer) {
|
(tokenizer) => {
|
||||||
const res = {
|
const res = {
|
||||||
ciphertext: [],
|
ciphertext: [],
|
||||||
tag: []
|
tag: []
|
||||||
}
|
}
|
||||||
const ciphertextLength = tokenizer.byte() // probably 32
|
const ciphertextLength = tokenizer.byte() // probably 32
|
||||||
for (let i = 0; i < ciphertextLength; i++) {
|
for (let i = 0; i < ciphertextLength; i += 1) {
|
||||||
res.ciphertext.push(tokenizer.byte())
|
res.ciphertext.push(tokenizer.byte())
|
||||||
}
|
}
|
||||||
while (!tokenizer.isEmpty()) {
|
while (!tokenizer.isEmpty()) {
|
||||||
|
@ -62,7 +61,7 @@ const keygenDecoders = [
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
// round 4
|
// round 4
|
||||||
function (tokenizer) {
|
(tokenizer) => {
|
||||||
const res = {
|
const res = {
|
||||||
parameters: {
|
parameters: {
|
||||||
threshold: tokenizer.byte(),
|
threshold: tokenizer.byte(),
|
||||||
|
@ -73,14 +72,55 @@ const keygenDecoders = [
|
||||||
while (!tokenizer.isEmpty()) {
|
while (!tokenizer.isEmpty()) {
|
||||||
res.commitments.push({
|
res.commitments.push({
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
y: tokenizer.parse(),
|
y: tokenizer.parse()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
// round 5
|
// round 5
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
pk: {
|
||||||
|
x: tokenizer.parse(),
|
||||||
|
y: tokenizer.parse()
|
||||||
|
},
|
||||||
|
pk_t_rand_commitment: {
|
||||||
|
x: tokenizer.parse(),
|
||||||
|
y: tokenizer.parse()
|
||||||
|
},
|
||||||
|
challenge_response: tokenizer.parse()
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|
||||||
|
const signDecoders = [
|
||||||
|
// round 0
|
||||||
|
(tokenizer) => tokenizer.byte(),
|
||||||
|
// round 1
|
||||||
|
(tokenizer) => [
|
||||||
|
{
|
||||||
|
com: tokenizer.parse()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
c: tokenizer.parse(512)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// round 2
|
||||||
|
(tokenizer) => {
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < 2; i += 1) {
|
||||||
|
res[i] = {
|
||||||
|
c: tokenizer.parse(512),
|
||||||
|
b_proof: {
|
||||||
|
pk: {
|
||||||
|
x: tokenizer.parse(),
|
||||||
|
y: tokenizer.parse()
|
||||||
|
},
|
||||||
|
pk_t_rand_commitment: {
|
||||||
|
x: tokenizer.parse(),
|
||||||
|
y: tokenizer.parse()
|
||||||
|
},
|
||||||
|
challenge_response: tokenizer.parse()
|
||||||
|
},
|
||||||
|
beta_tag_proof: {
|
||||||
pk: {
|
pk: {
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
y: tokenizer.parse()
|
y: tokenizer.parse()
|
||||||
|
@ -92,79 +132,25 @@ const keygenDecoders = [
|
||||||
challenge_response: tokenizer.parse()
|
challenge_response: tokenizer.parse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
||||||
const signDecoders = [
|
|
||||||
// round 0
|
|
||||||
function (tokenizer) {
|
|
||||||
return tokenizer.byte()
|
|
||||||
},
|
|
||||||
// round 1
|
|
||||||
function (tokenizer) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
com: tokenizer.parse()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
c: tokenizer.parse(512)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// round 2
|
|
||||||
function (tokenizer) {
|
|
||||||
const res = []
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
res[i] = {
|
|
||||||
c: tokenizer.parse(512),
|
|
||||||
b_proof: {
|
|
||||||
pk: {
|
|
||||||
x: tokenizer.parse(),
|
|
||||||
y: tokenizer.parse(),
|
|
||||||
},
|
|
||||||
pk_t_rand_commitment: {
|
|
||||||
x: tokenizer.parse(),
|
|
||||||
y: tokenizer.parse(),
|
|
||||||
},
|
|
||||||
challenge_response: tokenizer.parse(),
|
|
||||||
},
|
|
||||||
beta_tag_proof: {
|
|
||||||
pk: {
|
|
||||||
x: tokenizer.parse(),
|
|
||||||
y: tokenizer.parse(),
|
|
||||||
},
|
|
||||||
pk_t_rand_commitment: {
|
|
||||||
x: tokenizer.parse(),
|
|
||||||
y: tokenizer.parse(),
|
|
||||||
},
|
|
||||||
challenge_response: tokenizer.parse(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
// round 3
|
// round 3
|
||||||
function (tokenizer) {
|
(tokenizer) => tokenizer.parse(),
|
||||||
return tokenizer.parse()
|
|
||||||
},
|
|
||||||
// round 4
|
// round 4
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
|
||||||
blind_factor: tokenizer.parse(),
|
blind_factor: tokenizer.parse(),
|
||||||
g_gamma_i: {
|
g_gamma_i: {
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
y: tokenizer.parse()
|
y: tokenizer.parse()
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
// round 5
|
// round 5
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
|
||||||
com: tokenizer.parse()
|
com: tokenizer.parse()
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
// round 6
|
// round 6
|
||||||
function (tokenizer) {
|
(tokenizer) => [
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
V_i: {
|
V_i: {
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
|
@ -192,17 +178,13 @@ const signDecoders = [
|
||||||
z1: tokenizer.parse(),
|
z1: tokenizer.parse(),
|
||||||
z2: tokenizer.parse()
|
z2: tokenizer.parse()
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
|
||||||
// round 7
|
// round 7
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
|
||||||
com: tokenizer.parse()
|
com: tokenizer.parse()
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
// round 8
|
// round 8
|
||||||
function (tokenizer) {
|
(tokenizer) => ({
|
||||||
return {
|
|
||||||
u_i: {
|
u_i: {
|
||||||
x: tokenizer.parse(),
|
x: tokenizer.parse(),
|
||||||
y: tokenizer.parse()
|
y: tokenizer.parse()
|
||||||
|
@ -212,18 +194,17 @@ const signDecoders = [
|
||||||
y: tokenizer.parse()
|
y: tokenizer.parse()
|
||||||
},
|
},
|
||||||
blind_factor: tokenizer.parse()
|
blind_factor: tokenizer.parse()
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
// round 9
|
// round 9
|
||||||
function (tokenizer) {
|
(tokenizer) => tokenizer.parse()
|
||||||
return tokenizer.parse()
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
module.exports = function (isKeygen, round, value) {
|
function decode(isKeygen, round, value) {
|
||||||
value = Buffer.from(value.substr(2), 'hex')
|
const newValue = Buffer.from(value.substr(2), 'hex')
|
||||||
const tokenizer = Tokenizer(value)
|
const tokenizer = Tokenizer(newValue)
|
||||||
const roundNumber = parseInt(round[round.length - 1])
|
const roundNumber = parseInt(round[round.length - 1], 10)
|
||||||
const decoder = (isKeygen ? keygenDecoders : signDecoders)[roundNumber]
|
const decoder = (isKeygen ? keygenDecoders : signDecoders)[roundNumber]
|
||||||
return JSON.stringify(decoder(tokenizer))
|
return JSON.stringify(decoder(tokenizer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = decode
|
||||||
|
|
|
@ -9,36 +9,37 @@ function makeBuffer (value, length = 32, base = 16) {
|
||||||
const keygenEncoders = [
|
const keygenEncoders = [
|
||||||
null,
|
null,
|
||||||
// round 1
|
// round 1
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.e.n, 256, 10)
|
yield makeBuffer(value.e.n, 256, 10)
|
||||||
yield makeBuffer(value.com)
|
yield makeBuffer(value.com)
|
||||||
for (let x of value.correct_key_proof.sigma_vec) {
|
for (let i = 0; i < value.correct_key_proof.sigma_vec.length; i += 1) {
|
||||||
yield makeBuffer(x, 256, 10)
|
yield makeBuffer(value.correct_key_proof.sigma_vecp[i], 256, 10)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// round 2
|
// round 2
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.blind_factor)
|
yield makeBuffer(value.blind_factor)
|
||||||
yield makeBuffer(value.y_i.x)
|
yield makeBuffer(value.y_i.x)
|
||||||
yield makeBuffer(value.y_i.y)
|
yield makeBuffer(value.y_i.y)
|
||||||
},
|
},
|
||||||
// round 3
|
// round 3
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield Buffer.from([value.ciphertext.length])
|
yield Buffer.from([value.ciphertext.length])
|
||||||
yield Buffer.from(value.ciphertext) // 32 bytes or less
|
yield Buffer.from(value.ciphertext) // 32 bytes or less
|
||||||
yield Buffer.from(value.tag) // 16 bytes or less
|
yield Buffer.from(value.tag) // 16 bytes or less
|
||||||
},
|
},
|
||||||
// round 4
|
// round 4
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield Buffer.from([value.parameters.threshold]) // 1 byte
|
yield Buffer.from([value.parameters.threshold]) // 1 byte
|
||||||
yield Buffer.from([value.parameters.share_count]) // 1 byte
|
yield Buffer.from([value.parameters.share_count]) // 1 byte
|
||||||
for (let x of value.commitments) {
|
for (let i = 0; i < value.commitments.length; i += 1) {
|
||||||
|
const x = value.correct_key_proof.sigma_vec[i]
|
||||||
yield makeBuffer(x.x)
|
yield makeBuffer(x.x)
|
||||||
yield makeBuffer(x.y)
|
yield makeBuffer(x.y)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// round 5
|
// round 5
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.pk.x)
|
yield makeBuffer(value.pk.x)
|
||||||
yield makeBuffer(value.pk.y)
|
yield makeBuffer(value.pk.y)
|
||||||
yield makeBuffer(value.pk_t_rand_commitment.x)
|
yield makeBuffer(value.pk_t_rand_commitment.x)
|
||||||
|
@ -49,17 +50,17 @@ const keygenEncoders = [
|
||||||
|
|
||||||
const signEncoders = [
|
const signEncoders = [
|
||||||
// round 0
|
// round 0
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield Buffer.from([value])
|
yield Buffer.from([value])
|
||||||
},
|
},
|
||||||
// round 1
|
// round 1
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value[0].com)
|
yield makeBuffer(value[0].com)
|
||||||
yield makeBuffer(value[1].c, 512)
|
yield makeBuffer(value[1].c, 512)
|
||||||
},
|
},
|
||||||
// round 2
|
// round 2
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
for (let i = 0; i < 2; i++) {
|
for (let i = 0; i < 2; i += 1) {
|
||||||
yield makeBuffer(value[i].c, 512)
|
yield makeBuffer(value[i].c, 512)
|
||||||
yield makeBuffer(value[i].b_proof.pk.x)
|
yield makeBuffer(value[i].b_proof.pk.x)
|
||||||
yield makeBuffer(value[i].b_proof.pk.y)
|
yield makeBuffer(value[i].b_proof.pk.y)
|
||||||
|
@ -74,21 +75,21 @@ const signEncoders = [
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// round 3
|
// round 3
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value)
|
yield makeBuffer(value)
|
||||||
},
|
},
|
||||||
// round 4
|
// round 4
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.blind_factor)
|
yield makeBuffer(value.blind_factor)
|
||||||
yield makeBuffer(value.g_gamma_i.x)
|
yield makeBuffer(value.g_gamma_i.x)
|
||||||
yield makeBuffer(value.g_gamma_i.y)
|
yield makeBuffer(value.g_gamma_i.y)
|
||||||
},
|
},
|
||||||
// round 5
|
// round 5
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.com)
|
yield makeBuffer(value.com)
|
||||||
},
|
},
|
||||||
// round 6
|
// round 6
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value[0].V_i.x)
|
yield makeBuffer(value[0].V_i.x)
|
||||||
yield makeBuffer(value[0].V_i.y)
|
yield makeBuffer(value[0].V_i.y)
|
||||||
yield makeBuffer(value[0].A_i.x)
|
yield makeBuffer(value[0].A_i.x)
|
||||||
|
@ -104,11 +105,11 @@ const signEncoders = [
|
||||||
yield makeBuffer(value[1].z2)
|
yield makeBuffer(value[1].z2)
|
||||||
},
|
},
|
||||||
// round 7
|
// round 7
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.com)
|
yield makeBuffer(value.com)
|
||||||
},
|
},
|
||||||
// round 8
|
// round 8
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value.u_i.x)
|
yield makeBuffer(value.u_i.x)
|
||||||
yield makeBuffer(value.u_i.y)
|
yield makeBuffer(value.u_i.y)
|
||||||
yield makeBuffer(value.t_i.x)
|
yield makeBuffer(value.t_i.x)
|
||||||
|
@ -116,23 +117,23 @@ const signEncoders = [
|
||||||
yield makeBuffer(value.blind_factor)
|
yield makeBuffer(value.blind_factor)
|
||||||
},
|
},
|
||||||
// round 9
|
// round 9
|
||||||
function * (value) {
|
function* g(value) {
|
||||||
yield makeBuffer(value)
|
yield makeBuffer(value)
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
module.exports = function (isKeygen, round, value) {
|
function encode(isKeygen, round, value) {
|
||||||
const parsedValue = JSON.parse(value)
|
const parsedValue = JSON.parse(value)
|
||||||
const roundNumber = parseInt(round[round.length - 1])
|
const roundNumber = parseInt(round[round.length - 1], 10)
|
||||||
const encoder = (isKeygen ? keygenEncoders : signEncoders)[roundNumber]
|
const encoder = (isKeygen ? keygenEncoders : signEncoders)[roundNumber]
|
||||||
const generator = encoder(parsedValue)
|
const generator = encoder(parsedValue)
|
||||||
const buffers = []
|
const buffers = []
|
||||||
let next
|
let next = generator.next()
|
||||||
while (true) {
|
while (!next.done) {
|
||||||
next = generator.next()
|
|
||||||
if (next.done)
|
|
||||||
break
|
|
||||||
buffers.push(next.value)
|
buffers.push(next.value)
|
||||||
|
next = generator.next()
|
||||||
}
|
}
|
||||||
return Buffer.concat(buffers)
|
return Buffer.concat(buffers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = encode
|
||||||
|
|
|
@ -12,8 +12,8 @@ const logger = require('./logger')
|
||||||
const { publicKeyToAddress } = require('./crypto')
|
const { publicKeyToAddress } = require('./crypto')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
HOME_RPC_URL, HOME_BRIDGE_ADDRESS, SIDE_RPC_URL, SIDE_SHARED_DB_ADDRESS, VALIDATOR_PRIVATE_KEY, HOME_CHAIN_ID,
|
HOME_RPC_URL, HOME_BRIDGE_ADDRESS, SIDE_RPC_URL, SIDE_SHARED_DB_ADDRESS, VALIDATOR_PRIVATE_KEY,
|
||||||
SIDE_CHAIN_ID, HOME_TOKEN_ADDRESS, FOREIGN_URL, FOREIGN_ASSET
|
HOME_TOKEN_ADDRESS, FOREIGN_URL, FOREIGN_ASSET
|
||||||
} = process.env
|
} = process.env
|
||||||
const abiSharedDb = require('./contracts_data/SharedDB.json').abi
|
const abiSharedDb = require('./contracts_data/SharedDB.json').abi
|
||||||
const abiBridge = require('./contracts_data/Bridge.json').abi
|
const abiBridge = require('./contracts_data/Bridge.json').abi
|
||||||
|
@ -39,26 +39,7 @@ const app = express()
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.use(express.urlencoded({ extended: true }))
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
app.post('/get', get)
|
|
||||||
app.post('/set', set)
|
|
||||||
app.post('/signupkeygen', signupKeygen)
|
|
||||||
app.post('/signupsign', signupSign)
|
|
||||||
|
|
||||||
app.post('/confirmKeygen', confirmKeygen)
|
|
||||||
app.post('/confirmFundsTransfer', confirmFundsTransfer)
|
|
||||||
app.post('/transfer', transfer)
|
|
||||||
|
|
||||||
const votesProxyApp = express()
|
const votesProxyApp = express()
|
||||||
votesProxyApp.use(express.json())
|
|
||||||
votesProxyApp.use(express.urlencoded({ extended: true }))
|
|
||||||
|
|
||||||
votesProxyApp.get('/vote/startVoting', voteStartVoting)
|
|
||||||
votesProxyApp.get('/vote/startKeygen', voteStartKeygen)
|
|
||||||
votesProxyApp.get('/vote/cancelKeygen', voteCancelKeygen)
|
|
||||||
votesProxyApp.get('/vote/addValidator/:validator', voteAddValidator)
|
|
||||||
votesProxyApp.get('/vote/removeValidator/:validator', voteRemoveValidator)
|
|
||||||
votesProxyApp.get('/vote/changeThreshold/:threshold', voteChangeThreshold)
|
|
||||||
votesProxyApp.get('/info', info)
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
homeValidatorNonce = await homeWeb3.eth.getTransactionCount(validatorAddress)
|
homeValidatorNonce = await homeWeb3.eth.getTransactionCount(validatorAddress)
|
||||||
|
@ -88,27 +69,69 @@ function Err (data) {
|
||||||
return { Err: data }
|
return { Err: data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sideSendQuery(query) {
|
||||||
|
return lock.acquire('home', async () => {
|
||||||
|
logger.debug('Sending side query')
|
||||||
|
const encodedABI = query.encodeABI()
|
||||||
|
const senderResponse = await sideSender({
|
||||||
|
data: encodedABI,
|
||||||
|
to: SIDE_SHARED_DB_ADDRESS,
|
||||||
|
nonce: sideValidatorNonce
|
||||||
|
})
|
||||||
|
if (senderResponse !== true) {
|
||||||
|
sideValidatorNonce += 1
|
||||||
|
}
|
||||||
|
return senderResponse
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function homeSendQuery(query) {
|
||||||
|
return lock.acquire('home', async () => {
|
||||||
|
logger.debug('Sending home query')
|
||||||
|
const encodedABI = query.encodeABI()
|
||||||
|
const senderResponse = await homeSender({
|
||||||
|
data: encodedABI,
|
||||||
|
to: HOME_BRIDGE_ADDRESS,
|
||||||
|
nonce: homeValidatorNonce
|
||||||
|
})
|
||||||
|
if (senderResponse !== true) {
|
||||||
|
homeValidatorNonce += 1
|
||||||
|
}
|
||||||
|
return senderResponse
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function get(req, res) {
|
async function get(req, res) {
|
||||||
logger.debug('Get call, %o', req.body.key)
|
logger.debug('Get call, %o', req.body.key)
|
||||||
const round = req.body.key.second
|
const round = req.body.key.second
|
||||||
const uuid = req.body.key.third
|
const uuid = req.body.key.third
|
||||||
let from
|
let from
|
||||||
if (uuid.startsWith('k'))
|
if (uuid.startsWith('k')) {
|
||||||
from = (await bridge.methods.getNextValidators().call())[parseInt(req.body.key.first) - 1]
|
from = (await bridge.methods.getNextValidators()
|
||||||
else {
|
.call())[parseInt(req.body.key.first, 10) - 1]
|
||||||
const validators = await bridge.methods.getValidators().call()
|
} else {
|
||||||
from = await sharedDb.methods.getSignupAddress(uuid, validators, parseInt(req.body.key.first)).call()
|
const validators = await bridge.methods.getValidators()
|
||||||
|
.call()
|
||||||
|
from = await sharedDb.methods.getSignupAddress(
|
||||||
|
uuid,
|
||||||
|
validators, parseInt(req.body.key.first, 10)
|
||||||
|
)
|
||||||
|
.call()
|
||||||
}
|
}
|
||||||
const to = Number(req.body.key.fourth) // 0 if empty
|
const to = Number(req.body.key.fourth) // 0 if empty
|
||||||
const key = homeWeb3.utils.sha3(`${round}_${to}`)
|
const key = homeWeb3.utils.sha3(`${round}_${to}`)
|
||||||
|
|
||||||
const data = await sharedDb.methods.getData(from, sideWeb3.utils.sha3(uuid), key).call()
|
const data = await sharedDb.methods.getData(from, sideWeb3.utils.sha3(uuid), key)
|
||||||
|
.call()
|
||||||
|
|
||||||
if (data.length > 2) {
|
if (data.length > 2) {
|
||||||
logger.trace(`Received encoded data: ${data}`)
|
logger.trace(`Received encoded data: ${data}`)
|
||||||
const decoded = decode(uuid[0] === 'k', round, data)
|
const decoded = decode(uuid[0] === 'k', round, data)
|
||||||
logger.trace('Decoded data: %o', decoded)
|
logger.trace('Decoded data: %o', decoded)
|
||||||
res.send(Ok({ key: req.body.key, value: decoded }))
|
res.send(Ok({
|
||||||
|
key: req.body.key,
|
||||||
|
value: decoded
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => res.send(Err(null)), 1000)
|
setTimeout(() => res.send(Err(null)), 1000)
|
||||||
}
|
}
|
||||||
|
@ -136,14 +159,19 @@ async function set (req, res) {
|
||||||
|
|
||||||
async function signupKeygen(req, res) {
|
async function signupKeygen(req, res) {
|
||||||
logger.debug('SignupKeygen call')
|
logger.debug('SignupKeygen call')
|
||||||
const epoch = (await bridge.methods.nextEpoch().call()).toNumber()
|
const epoch = (await bridge.methods.nextEpoch()
|
||||||
const partyId = (await bridge.methods.getNextPartyId(validatorAddress).call()).toNumber()
|
.call()).toNumber()
|
||||||
|
const partyId = (await bridge.methods.getNextPartyId(validatorAddress)
|
||||||
|
.call()).toNumber()
|
||||||
|
|
||||||
if (partyId === 0) {
|
if (partyId === 0) {
|
||||||
res.send(Err({ message: 'Not a validator' }))
|
res.send(Err({ message: 'Not a validator' }))
|
||||||
logger.debug('Not a validator')
|
logger.debug('Not a validator')
|
||||||
} else {
|
} else {
|
||||||
res.send(Ok({ uuid: `k${epoch}`, number: partyId }))
|
res.send(Ok({
|
||||||
|
uuid: `k${epoch}`,
|
||||||
|
number: partyId
|
||||||
|
}))
|
||||||
logger.debug('SignupKeygen end')
|
logger.debug('SignupKeygen end')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,15 +185,23 @@ async function signupSign (req, res) {
|
||||||
|
|
||||||
// Already have signup
|
// Already have signup
|
||||||
if (receipt.status === false) {
|
if (receipt.status === false) {
|
||||||
res.send(Ok({ uuid: hash, number: 0 }))
|
res.send(Ok({
|
||||||
|
uuid: hash,
|
||||||
|
number: 0
|
||||||
|
}))
|
||||||
logger.debug('Already have signup')
|
logger.debug('Already have signup')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const validators = await bridge.methods.getValidators().call()
|
const validators = await bridge.methods.getValidators()
|
||||||
const id = (await sharedDb.methods.getSignupNumber(hash, validators, validatorAddress).call()).toNumber()
|
.call()
|
||||||
|
const id = (await sharedDb.methods.getSignupNumber(hash, validators, validatorAddress)
|
||||||
|
.call()).toNumber()
|
||||||
|
|
||||||
res.send(Ok({ uuid: hash, number: id }))
|
res.send(Ok({
|
||||||
|
uuid: hash,
|
||||||
|
number: id
|
||||||
|
}))
|
||||||
logger.debug('SignupSign end')
|
logger.debug('SignupSign end')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,48 +222,6 @@ async function confirmFundsTransfer (req, res) {
|
||||||
logger.debug('Confirm funds transfer end')
|
logger.debug('Confirm funds transfer end')
|
||||||
}
|
}
|
||||||
|
|
||||||
function sideSendQuery (query) {
|
|
||||||
return lock.acquire('home', async () => {
|
|
||||||
logger.debug('Sending side query')
|
|
||||||
const encodedABI = query.encodeABI()
|
|
||||||
const senderResponse = await sideSender({
|
|
||||||
data: encodedABI,
|
|
||||||
to: SIDE_SHARED_DB_ADDRESS,
|
|
||||||
nonce: sideValidatorNonce
|
|
||||||
})
|
|
||||||
if (senderResponse !== true) {
|
|
||||||
sideValidatorNonce++
|
|
||||||
}
|
|
||||||
return senderResponse
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function homeSendQuery (query) {
|
|
||||||
return lock.acquire('home', async () => {
|
|
||||||
logger.debug('Sending home query')
|
|
||||||
const encodedABI = query.encodeABI()
|
|
||||||
const senderResponse = await homeSender({
|
|
||||||
data: encodedABI,
|
|
||||||
to: HOME_BRIDGE_ADDRESS,
|
|
||||||
nonce: homeValidatorNonce
|
|
||||||
})
|
|
||||||
if (senderResponse !== true) {
|
|
||||||
homeValidatorNonce++
|
|
||||||
}
|
|
||||||
return senderResponse
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseReason (message) {
|
|
||||||
const result = /(?<="reason":").*?(?=")/.exec(message)
|
|
||||||
return result ? result[0] : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseError (message) {
|
|
||||||
const result = /(?<="error":").*?(?=")/.exec(message)
|
|
||||||
return result ? result[0] : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendVote(query, req, res, waitFlag = false) {
|
async function sendVote(query, req, res, waitFlag = false) {
|
||||||
try {
|
try {
|
||||||
const sentQuery = await homeSendQuery(query)
|
const sentQuery = await homeSendQuery(query)
|
||||||
|
@ -308,6 +302,8 @@ function decodeStatus (status) {
|
||||||
return 'keygen'
|
return 'keygen'
|
||||||
case 3:
|
case 3:
|
||||||
return 'funds_transfer'
|
return 'funds_transfer'
|
||||||
|
default:
|
||||||
|
return 'unknown_state'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,32 +315,101 @@ function boundX (x) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toNumber(x) {
|
||||||
|
return x.toNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function transfer(req, res) {
|
||||||
|
logger.info('Transfer start')
|
||||||
|
const { hash, to, value } = req.body
|
||||||
|
if (homeWeb3.utils.isAddress(to)) {
|
||||||
|
logger.info(`Calling transfer to ${to}, ${value} tokens`)
|
||||||
|
const query = bridge.methods.transfer(hash, to, `0x${new BN(value).toString(16)}`)
|
||||||
|
await homeSendQuery(query)
|
||||||
|
}
|
||||||
|
res.send()
|
||||||
|
logger.info('Transfer end')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getForeignBalances(address) {
|
||||||
|
return httpClient
|
||||||
|
.get(`/api/v1/account/${address}`)
|
||||||
|
.then((res) => res.data.balances.reduce((prev, cur) => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
prev[cur.symbol] = cur.free
|
||||||
|
return prev
|
||||||
|
}, {}))
|
||||||
|
.catch(() => ({}))
|
||||||
|
}
|
||||||
|
|
||||||
async function info(req, res) {
|
async function info(req, res) {
|
||||||
logger.debug('Info start')
|
logger.debug('Info start')
|
||||||
try {
|
try {
|
||||||
const [ x, y, epoch, rangeSize, nextRangeSize, epochStartBlock, foreignNonce, nextEpoch, threshold, nextThreshold, validators, nextValidators, status, homeBalance ] = await Promise.all([
|
const [
|
||||||
bridge.methods.getX().call().then(x => new BN(x).toString(16)),
|
x, y, epoch, rangeSize, nextRangeSize, epochStartBlock, foreignNonce, nextEpoch,
|
||||||
bridge.methods.getY().call().then(x => new BN(x).toString(16)),
|
threshold, nextThreshold, validators, nextValidators, status, homeBalance
|
||||||
bridge.methods.epoch().call().then(x => x.toNumber()),
|
] = await Promise.all([
|
||||||
bridge.methods.getRangeSize().call().then(x => x.toNumber()),
|
bridge.methods.getX()
|
||||||
bridge.methods.getNextRangeSize().call().then(x => x.toNumber()),
|
.call()
|
||||||
bridge.methods.getStartBlock().call().then(x => x.toNumber()),
|
.then((value) => new BN(value).toString(16)),
|
||||||
bridge.methods.getNonce().call().then(boundX),
|
bridge.methods.getY()
|
||||||
bridge.methods.nextEpoch().call().then(x => x.toNumber()),
|
.call()
|
||||||
bridge.methods.getThreshold().call().then(x => x.toNumber()),
|
.then((value) => new BN(value).toString(16)),
|
||||||
bridge.methods.getNextThreshold().call().then(x => x.toNumber()),
|
bridge.methods.epoch()
|
||||||
bridge.methods.getValidators().call(),
|
.call()
|
||||||
bridge.methods.getNextValidators().call(),
|
.then(toNumber),
|
||||||
bridge.methods.status().call(),
|
bridge.methods.getRangeSize()
|
||||||
token.methods.balanceOf(HOME_BRIDGE_ADDRESS).call().then(x => parseFloat(new BN(x).dividedBy(10 ** 18).toFixed(8, 3)))
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getNextRangeSize()
|
||||||
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getStartBlock()
|
||||||
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getNonce()
|
||||||
|
.call()
|
||||||
|
.then(boundX),
|
||||||
|
bridge.methods.nextEpoch()
|
||||||
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getThreshold()
|
||||||
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getNextThreshold()
|
||||||
|
.call()
|
||||||
|
.then(toNumber),
|
||||||
|
bridge.methods.getValidators()
|
||||||
|
.call(),
|
||||||
|
bridge.methods.getNextValidators()
|
||||||
|
.call(),
|
||||||
|
bridge.methods.status()
|
||||||
|
.call(),
|
||||||
|
token.methods.balanceOf(HOME_BRIDGE_ADDRESS)
|
||||||
|
.call()
|
||||||
|
.then((value) => parseFloat(new BN(value).dividedBy(10 ** 18)
|
||||||
|
.toFixed(8, 3)))
|
||||||
])
|
])
|
||||||
const [ confirmationsForFundsTransfer, votesForVoting, votesForKeygen, votesForCancelKeygen ] = await Promise.all([
|
const [
|
||||||
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack([ 'uint8', 'uint256' ], [ 1, nextEpoch ]))).call().then(boundX),
|
confirmationsForFundsTransfer, votesForVoting, votesForKeygen, votesForCancelKeygen
|
||||||
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack([ 'uint8', 'uint256' ], [ 2, nextEpoch ]))).call().then(boundX),
|
] = await Promise.all([
|
||||||
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack([ 'uint8', 'uint256' ], [ 7, nextEpoch ]))).call().then(boundX),
|
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack(['uint8', 'uint256'], [1, nextEpoch])))
|
||||||
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack([ 'uint8', 'uint256' ], [ 8, nextEpoch ]))).call().then(boundX)
|
.call()
|
||||||
|
.then(boundX),
|
||||||
|
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack(['uint8', 'uint256'], [2, nextEpoch])))
|
||||||
|
.call()
|
||||||
|
.then(boundX),
|
||||||
|
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack(['uint8', 'uint256'], [7, nextEpoch])))
|
||||||
|
.call()
|
||||||
|
.then(boundX),
|
||||||
|
bridge.methods.votesCount(homeWeb3.utils.sha3(utils.solidityPack(['uint8', 'uint256'], [8, nextEpoch])))
|
||||||
|
.call()
|
||||||
|
.then(boundX)
|
||||||
])
|
])
|
||||||
const foreignAddress = publicKeyToAddress({ x, y })
|
const foreignAddress = publicKeyToAddress({
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
})
|
||||||
const balances = await getForeignBalances(foreignAddress)
|
const balances = await getForeignBalances(foreignAddress)
|
||||||
const msg = {
|
const msg = {
|
||||||
epoch,
|
epoch,
|
||||||
|
@ -361,7 +426,7 @@ async function info (req, res) {
|
||||||
nextValidators,
|
nextValidators,
|
||||||
homeBalance,
|
homeBalance,
|
||||||
foreignBalanceTokens: parseFloat(balances[FOREIGN_ASSET]) || 0,
|
foreignBalanceTokens: parseFloat(balances[FOREIGN_ASSET]) || 0,
|
||||||
foreignBalanceNative: parseFloat(balances['BNB']) || 0,
|
foreignBalanceNative: parseFloat(balances.BNB) || 0,
|
||||||
bridgeStatus: decodeStatus(status),
|
bridgeStatus: decodeStatus(status),
|
||||||
votesForVoting,
|
votesForVoting,
|
||||||
votesForKeygen,
|
votesForKeygen,
|
||||||
|
@ -372,29 +437,27 @@ async function info (req, res) {
|
||||||
res.send(msg)
|
res.send(msg)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('%o', e)
|
logger.debug('%o', e)
|
||||||
res.send({ message: 'Something went wrong, resend request', error: e })
|
res.send({
|
||||||
|
message: 'Something went wrong, resend request',
|
||||||
|
error: e
|
||||||
|
})
|
||||||
}
|
}
|
||||||
logger.debug('Info end')
|
logger.debug('Info end')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function transfer (req, res) {
|
app.post('/get', get)
|
||||||
logger.info('Transfer start')
|
app.post('/set', set)
|
||||||
const { hash, to, value } = req.body
|
app.post('/signupkeygen', signupKeygen)
|
||||||
if (homeWeb3.utils.isAddress(to)) {
|
app.post('/signupsign', signupSign)
|
||||||
logger.info(`Calling transfer to ${to}, ${value} tokens`)
|
|
||||||
const query = bridge.methods.transfer(hash, to, '0x' + (new BN(value).toString(16)))
|
|
||||||
await homeSendQuery(query)
|
|
||||||
}
|
|
||||||
res.send()
|
|
||||||
logger.info('Transfer end')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getForeignBalances (address) {
|
app.post('/confirmKeygen', confirmKeygen)
|
||||||
return httpClient
|
app.post('/confirmFundsTransfer', confirmFundsTransfer)
|
||||||
.get(`/api/v1/account/${address}`)
|
app.post('/transfer', transfer)
|
||||||
.then(res => res.data.balances.reduce((prev, cur) => {
|
|
||||||
prev[cur.symbol] = cur.free
|
votesProxyApp.get('/vote/startVoting', voteStartVoting)
|
||||||
return prev
|
votesProxyApp.get('/vote/startKeygen', voteStartKeygen)
|
||||||
}, {}))
|
votesProxyApp.get('/vote/cancelKeygen', voteCancelKeygen)
|
||||||
.catch(err => ({}))
|
votesProxyApp.get('/vote/addValidator/:validator', voteAddValidator)
|
||||||
}
|
votesProxyApp.get('/vote/removeValidator/:validator', voteRemoveValidator)
|
||||||
|
votesProxyApp.get('/vote/changeThreshold/:threshold', voteChangeThreshold)
|
||||||
|
votesProxyApp.get('/info', info)
|
||||||
|
|
|
@ -12,5 +12,8 @@
|
||||||
"ethers": "4.0.37",
|
"ethers": "4.0.37",
|
||||||
"pino": "5.13.4",
|
"pino": "5.13.4",
|
||||||
"pino-pretty": "3.2.1"
|
"pino-pretty": "3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ function sendRpcRequest (url, method, params) {
|
||||||
params,
|
params,
|
||||||
id: 1
|
id: 1
|
||||||
})
|
})
|
||||||
.then(res => res.data)
|
.then((res) => res.data)
|
||||||
.catch(async e => {
|
.catch(async () => {
|
||||||
logger.warn(`Request to ${url}, method ${method} failed, retrying`)
|
logger.warn(`Request to ${url}, method ${method} failed, retrying`)
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
await new Promise((res) => setTimeout(res, 1000))
|
||||||
return sendRpcRequest(url, method, params)
|
return sendRpcRequest(url, method, params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ async function createSender (url, privateKey) {
|
||||||
const signer = new ethers.utils.SigningKey(privateKey)
|
const signer = new ethers.utils.SigningKey(privateKey)
|
||||||
|
|
||||||
const chainId = await web3.eth.net.getId()
|
const chainId = await web3.eth.net.getId()
|
||||||
return async function (tx) {
|
return async function send(tx) {
|
||||||
tx = {
|
const newTx = {
|
||||||
data: tx.data,
|
data: tx.data,
|
||||||
to: tx.to,
|
to: tx.to,
|
||||||
nonce: tx.nonce,
|
nonce: tx.nonce,
|
||||||
|
@ -38,13 +38,13 @@ async function createSender (url, privateKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.trace(`Preparing and sending transaction %o on ${url}`, tx)
|
logger.trace(`Preparing and sending transaction %o on ${url}`, newTx)
|
||||||
const estimate = await sendRpcRequest(url, 'eth_estimateGas', [{
|
const estimate = await sendRpcRequest(url, 'eth_estimateGas', [{
|
||||||
from: signer.address,
|
from: signer.address,
|
||||||
to: tx.to,
|
to: newTx.to,
|
||||||
data: tx.data,
|
data: newTx.data,
|
||||||
gasPrice: tx.gasPrice,
|
gasPrice: newTx.gasPrice,
|
||||||
value: tx.value,
|
value: newTx.value,
|
||||||
gas: `0x${new BN(MAX_GAS_LIMIT).toString(16)}`
|
gas: `0x${new BN(MAX_GAS_LIMIT).toString(16)}`
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
@ -52,8 +52,9 @@ async function createSender (url, privateKey) {
|
||||||
logger.debug('Gas estimate failed %o, skipping tx, reverting nonce', estimate.error)
|
logger.debug('Gas estimate failed %o, skipping tx, reverting nonce', estimate.error)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const gasLimit = BN.min(new BN(estimate.result, 16).multipliedBy(GAS_LIMIT_FACTOR), MAX_GAS_LIMIT)
|
const gasLimit = BN.min(new BN(estimate.result, 16)
|
||||||
tx.gasLimit = `0x${new BN(gasLimit).toString(16)}`
|
.multipliedBy(GAS_LIMIT_FACTOR), MAX_GAS_LIMIT)
|
||||||
|
newTx.gasLimit = `0x${new BN(gasLimit).toString(16)}`
|
||||||
logger.trace(`Estimated gas to ${gasLimit}`)
|
logger.trace(`Estimated gas to ${gasLimit}`)
|
||||||
|
|
||||||
const hash = web3.utils.sha3(ethers.utils.serializeTransaction(tx))
|
const hash = web3.utils.sha3(ethers.utils.serializeTransaction(tx))
|
||||||
|
@ -68,7 +69,10 @@ async function createSender (url, privateKey) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return { txHash: result, gasLimit: tx.gasLimit }
|
return {
|
||||||
|
txHash: result,
|
||||||
|
gasLimit: tx.gasLimit
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn('Something failed, %o', e)
|
logger.warn('Something failed, %o', e)
|
||||||
return false
|
return false
|
||||||
|
@ -81,11 +85,14 @@ async function waitForReceipt (url, txHash) {
|
||||||
const { result, error } = await sendRpcRequest(url, 'eth_getTransactionReceipt', [txHash])
|
const { result, error } = await sendRpcRequest(url, 'eth_getTransactionReceipt', [txHash])
|
||||||
|
|
||||||
if (result === null || error) {
|
if (result === null || error) {
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
await new Promise((res) => setTimeout(res, 1000))
|
||||||
} else {
|
} else {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { createSender, waitForReceipt }
|
module.exports = {
|
||||||
|
createSender,
|
||||||
|
waitForReceipt
|
||||||
|
}
|
||||||
|
|
|
@ -3,5 +3,8 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ioredis": "4.14.1"
|
"ioredis": "4.14.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ redis.on('error', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
redis.on('connect', async () => {
|
redis.on('connect', async () => {
|
||||||
await redis.set('homeBlock', parseInt(process.argv[2]))
|
await redis.set('homeBlock', parseInt(process.argv[2], 10))
|
||||||
await redis.save()
|
await redis.save()
|
||||||
redis.disconnect()
|
redis.disconnect()
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,30 +2,30 @@ const amqp = require('amqplib')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
|
||||||
function _connectRabbit (url) {
|
|
||||||
return amqp.connect(url).catch(() => {
|
|
||||||
logger.debug('Failed to connect to rabbitmqServer, reconnecting')
|
|
||||||
return new Promise(resolve =>
|
|
||||||
setTimeout(() => resolve(_connectRabbit(url)), 2000)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function connectRabbit(url) {
|
async function connectRabbit(url) {
|
||||||
const connection = await _connectRabbit(url)
|
while (true) {
|
||||||
return await connection.createChannel()
|
try {
|
||||||
|
return (await amqp.connect(url)).createChannel()
|
||||||
|
} catch (e) {
|
||||||
|
logger.debug('Failed to connect to rabbitmqServer, reconnecting')
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assertQueue(channel, name) {
|
async function assertQueue(channel, name) {
|
||||||
const queue = await channel.assertQueue(name)
|
const queue = await channel.assertQueue(name)
|
||||||
return {
|
return {
|
||||||
name: queue.queue,
|
name: queue.queue,
|
||||||
send: msg => channel.sendToQueue(queue.queue, Buffer.from(JSON.stringify(msg)), {
|
send: (msg) => channel.sendToQueue(queue.queue, Buffer.from(JSON.stringify(msg)), {
|
||||||
persistent: true
|
persistent: true
|
||||||
}),
|
}),
|
||||||
get: consumer => channel.get(queue.queue, consumer),
|
get: (consumer) => channel.get(queue.queue, consumer),
|
||||||
consume: consumer => channel.consume(queue.queue, consumer)
|
consume: (consumer) => channel.consume(queue.queue, consumer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { connectRabbit, assertQueue }
|
module.exports = {
|
||||||
|
connectRabbit,
|
||||||
|
assertQueue
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const bech32 = require('bech32')
|
const bech32 = require('bech32')
|
||||||
|
|
||||||
|
function padZeros(s, len) {
|
||||||
|
while (s.length < len) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
s = `0${s}`
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
function sha256(bytes) {
|
||||||
|
return crypto.createHash('sha256')
|
||||||
|
.update(bytes)
|
||||||
|
.digest('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
function ripemd160(bytes) {
|
||||||
|
return crypto.createHash('ripemd160')
|
||||||
|
.update(bytes)
|
||||||
|
.digest('hex')
|
||||||
|
}
|
||||||
|
|
||||||
function publicKeyToAddress({ x, y }) {
|
function publicKeyToAddress({ x, y }) {
|
||||||
const compact = (parseInt(y[y.length - 1], 16) % 2 ? '03' : '02') + padZeros(x, 64)
|
const compact = (parseInt(y[y.length - 1], 16) % 2 ? '03' : '02') + padZeros(x, 64)
|
||||||
const sha256Hash = sha256(Buffer.from(compact, 'hex'))
|
const sha256Hash = sha256(Buffer.from(compact, 'hex'))
|
||||||
|
@ -9,18 +29,8 @@ function publicKeyToAddress ({ x, y }) {
|
||||||
return bech32.encode('tbnb', words)
|
return bech32.encode('tbnb', words)
|
||||||
}
|
}
|
||||||
|
|
||||||
function padZeros (s, len) {
|
module.exports = {
|
||||||
while (s.length < len)
|
publicKeyToAddress,
|
||||||
s = '0' + s
|
padZeros,
|
||||||
return s
|
sha256
|
||||||
}
|
}
|
||||||
|
|
||||||
function sha256 (bytes) {
|
|
||||||
return crypto.createHash('sha256').update(bytes).digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
function ripemd160 (bytes) {
|
|
||||||
return crypto.createHash('ripemd160').update(bytes).digest('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { publicKeyToAddress, padZeros, sha256 }
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const Redis = require('ioredis')
|
const Redis = require('ioredis')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
|
||||||
logger.info('Connecting to redis')
|
logger.info('Connecting to redis')
|
||||||
|
|
||||||
const redis = new Redis({
|
const redis = new Redis({
|
||||||
|
@ -14,7 +15,7 @@ redis.on('connect', () => {
|
||||||
logger.info('Connected to redis')
|
logger.info('Connected to redis')
|
||||||
})
|
})
|
||||||
|
|
||||||
redis.on('error', e => {
|
redis.on('error', (e) => {
|
||||||
logger.warn('Redis error %o', e)
|
logger.warn('Redis error %o', e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,14 @@ const { publicKeyToAddress } = require('./crypto')
|
||||||
const { RABBITMQ_URL, PROXY_URL } = process.env
|
const { RABBITMQ_URL, PROXY_URL } = process.env
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.get('/start', (req, res) => {
|
|
||||||
logger.info('Ready to start')
|
|
||||||
ready = true
|
|
||||||
res.send()
|
|
||||||
})
|
|
||||||
app.listen(8001, () => logger.debug('Listening on 8001'))
|
|
||||||
|
|
||||||
let currentKeygenEpoch = null
|
let currentKeygenEpoch = null
|
||||||
let ready = false
|
let ready = false
|
||||||
|
|
||||||
|
async function confirmKeygen(keysFile) {
|
||||||
|
exec.execSync(`curl -X POST -H "Content-Type: application/json" -d @"${keysFile}" "${PROXY_URL}/confirmKeygen"`, { stdio: 'pipe' })
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
logger.info('Connecting to RabbitMQ server')
|
logger.info('Connecting to RabbitMQ server')
|
||||||
const channel = await connectRabbit(RABBITMQ_URL)
|
const channel = await connectRabbit(RABBITMQ_URL)
|
||||||
|
@ -27,11 +25,11 @@ async function main () {
|
||||||
const cancelKeygenQueue = await assertQueue(channel, 'cancelKeygenQueue')
|
const cancelKeygenQueue = await assertQueue(channel, 'cancelKeygenQueue')
|
||||||
|
|
||||||
while (!ready) {
|
while (!ready) {
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
await new Promise((res) => setTimeout(res, 1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.prefetch(1)
|
channel.prefetch(1)
|
||||||
keygenQueue.consume(msg => {
|
keygenQueue.consume((msg) => {
|
||||||
const { epoch, parties, threshold } = JSON.parse(msg.content)
|
const { epoch, parties, threshold } = JSON.parse(msg.content)
|
||||||
logger.info(`Consumed new epoch event, starting keygen for epoch ${epoch}`)
|
logger.info(`Consumed new epoch event, starting keygen for epoch ${epoch}`)
|
||||||
|
|
||||||
|
@ -41,7 +39,10 @@ async function main () {
|
||||||
currentKeygenEpoch = epoch
|
currentKeygenEpoch = epoch
|
||||||
|
|
||||||
logger.debug('Writing params')
|
logger.debug('Writing params')
|
||||||
fs.writeFileSync('./params', JSON.stringify({ parties: parties.toString(), threshold: threshold.toString() }))
|
fs.writeFileSync('./params', JSON.stringify({
|
||||||
|
parties: parties.toString(),
|
||||||
|
threshold: threshold.toString()
|
||||||
|
}))
|
||||||
const cmd = exec.execFile('./keygen-entrypoint.sh', [PROXY_URL, keysFile], async () => {
|
const cmd = exec.execFile('./keygen-entrypoint.sh', [PROXY_URL, keysFile], async () => {
|
||||||
currentKeygenEpoch = null
|
currentKeygenEpoch = null
|
||||||
if (fs.existsSync(keysFile)) {
|
if (fs.existsSync(keysFile)) {
|
||||||
|
@ -57,11 +58,11 @@ async function main () {
|
||||||
logger.debug('Ack for keygen message')
|
logger.debug('Ack for keygen message')
|
||||||
channel.ack(msg)
|
channel.ack(msg)
|
||||||
})
|
})
|
||||||
cmd.stdout.on('data', data => logger.debug(data.toString()))
|
cmd.stdout.on('data', (data) => logger.debug(data.toString()))
|
||||||
cmd.stderr.on('data', data => logger.debug(data.toString()))
|
cmd.stderr.on('data', (data) => logger.debug(data.toString()))
|
||||||
})
|
})
|
||||||
|
|
||||||
cancelKeygenQueue.consume(async msg => {
|
cancelKeygenQueue.consume(async (msg) => {
|
||||||
const { epoch } = JSON.parse(msg.content)
|
const { epoch } = JSON.parse(msg.content)
|
||||||
logger.info(`Consumed new cancel event for epoch ${epoch} keygen`)
|
logger.info(`Consumed new cancel event for epoch ${epoch} keygen`)
|
||||||
if (currentKeygenEpoch === epoch) {
|
if (currentKeygenEpoch === epoch) {
|
||||||
|
@ -72,8 +73,12 @@ async function main () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
||||||
async function confirmKeygen (keysFile) {
|
app.get('/start', (req, res) => {
|
||||||
exec.execSync(`curl -X POST -H "Content-Type: application/json" -d @"${keysFile}" "${PROXY_URL}/confirmKeygen"`, { stdio: 'pipe' })
|
logger.info('Ready to start')
|
||||||
}
|
ready = true
|
||||||
|
res.send()
|
||||||
|
})
|
||||||
|
app.listen(8001, () => logger.debug('Listening on 8001'))
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
@ -7,5 +7,8 @@
|
||||||
"pino": "5.13.4",
|
"pino": "5.13.4",
|
||||||
"pino-pretty": "3.2.1",
|
"pino-pretty": "3.2.1",
|
||||||
"express": "4.17.1"
|
"express": "4.17.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,8 @@
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"pino": "5.13.4",
|
"pino": "5.13.4",
|
||||||
"pino-pretty": "3.2.1"
|
"pino-pretty": "3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
const exec = require('child_process')
|
const exec = require('child_process')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const BN = require('bignumber.js')
|
const BN = require('bignumber.js')
|
||||||
|
const axios = require('axios')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
const { connectRabbit, assertQueue } = require('./amqp')
|
const { connectRabbit, assertQueue } = require('./amqp')
|
||||||
const { publicKeyToAddress, sha256 } = require('./crypto')
|
const { publicKeyToAddress, sha256 } = require('./crypto')
|
||||||
|
|
||||||
const app = express()
|
|
||||||
app.get('/restart/:attempt', restart)
|
|
||||||
app.get('/start', (req, res) => {
|
|
||||||
logger.info('Ready to start')
|
|
||||||
ready = true
|
|
||||||
res.send()
|
|
||||||
})
|
|
||||||
app.listen(8001, () => logger.debug('Listening on 8001'))
|
|
||||||
|
|
||||||
const { RABBITMQ_URL, FOREIGN_URL, PROXY_URL, FOREIGN_ASSET } = process.env
|
|
||||||
const Transaction = require('./tx')
|
const Transaction = require('./tx')
|
||||||
const axios = require('axios')
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
const {
|
||||||
|
RABBITMQ_URL, FOREIGN_URL, PROXY_URL, FOREIGN_ASSET
|
||||||
|
} = process.env
|
||||||
|
|
||||||
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
||||||
|
|
||||||
|
@ -29,117 +25,10 @@ let ready = false
|
||||||
let exchangeQueue
|
let exchangeQueue
|
||||||
let channel
|
let channel
|
||||||
|
|
||||||
async function main () {
|
|
||||||
logger.info('Connecting to RabbitMQ server')
|
|
||||||
channel = await connectRabbit(RABBITMQ_URL)
|
|
||||||
logger.info('Connecting to signature events queue')
|
|
||||||
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
|
||||||
const signQueue = await assertQueue(channel, 'signQueue')
|
|
||||||
|
|
||||||
while (!ready) {
|
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.prefetch(1)
|
|
||||||
signQueue.consume(async msg => {
|
|
||||||
const data = JSON.parse(msg.content)
|
|
||||||
|
|
||||||
logger.info('Consumed sign event: %o', data)
|
|
||||||
const { nonce, epoch, newEpoch, parties, threshold } = data
|
|
||||||
|
|
||||||
const keysFile = `/keys/keys${epoch}.store`
|
|
||||||
const { address: from, publicKey } = getAccountFromFile(keysFile)
|
|
||||||
if (from === '') {
|
|
||||||
logger.info('No keys found, acking message')
|
|
||||||
channel.ack(msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const account = await getAccount(from)
|
|
||||||
|
|
||||||
logger.debug('Writing params')
|
|
||||||
fs.writeFileSync('./params', JSON.stringify({ parties: parties.toString(), threshold: threshold.toString() }))
|
|
||||||
|
|
||||||
attempt = 1
|
|
||||||
|
|
||||||
if (!newEpoch) {
|
|
||||||
const exchanges = await getExchangeMessages(nonce)
|
|
||||||
const exchangesData = exchanges.map(msg => JSON.parse(msg.content))
|
|
||||||
|
|
||||||
if (exchanges.length > 0 && account.sequence <= nonce) {
|
|
||||||
const recipients = exchangesData.map(({ value, recipient }) => ({ to: recipient, tokens: value }))
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
logger.info(`Building corresponding transfer transaction, nonce ${nonce}`)
|
|
||||||
|
|
||||||
const tx = new Transaction({
|
|
||||||
from,
|
|
||||||
accountNumber: account.account_number,
|
|
||||||
sequence: nonce,
|
|
||||||
recipients,
|
|
||||||
asset: FOREIGN_ASSET,
|
|
||||||
memo: `Attempt ${attempt}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const hash = sha256(tx.getSignBytes())
|
|
||||||
logger.info(`Starting signature generation for transaction hash ${hash}`)
|
|
||||||
const done = await sign(keysFile, hash, tx, publicKey) && await waitForAccountNonce(from, nonce + 1)
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
exchanges.forEach(msg => channel.ack(msg))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
attempt = nextAttempt ? nextAttempt : attempt + 1
|
|
||||||
logger.warn(`Sign failed, starting next attempt ${attempt}`)
|
|
||||||
nextAttempt = null
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (account.sequence <= nonce) {
|
|
||||||
const newKeysFile = `/keys/keys${newEpoch}.store`
|
|
||||||
const { address: to } = getAccountFromFile(newKeysFile)
|
|
||||||
|
|
||||||
while (to !== '') {
|
|
||||||
logger.info(`Building corresponding transaction for transferring all funds, nonce ${nonce}, recipient ${to}`)
|
|
||||||
const tx = new Transaction({
|
|
||||||
from,
|
|
||||||
accountNumber: account.account_number,
|
|
||||||
sequence: nonce,
|
|
||||||
recipients: [ {
|
|
||||||
to,
|
|
||||||
tokens: account.balances.find(x => x.symbol === FOREIGN_ASSET).free,
|
|
||||||
bnbs: new BN(account.balances.find(x => x.symbol === 'BNB').free).minus(new BN(60000).div(10 ** 8)),
|
|
||||||
} ],
|
|
||||||
asset: FOREIGN_ASSET,
|
|
||||||
memo: `Attempt ${attempt}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const hash = sha256(tx.getSignBytes())
|
|
||||||
logger.info(`Starting signature generation for transaction hash ${hash}`)
|
|
||||||
const done = await sign(keysFile, hash, tx, publicKey) && await waitForAccountNonce(from, nonce + 1)
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
await confirmFundsTransfer()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
attempt = nextAttempt ? nextAttempt : attempt + 1
|
|
||||||
logger.warn(`Sign failed, starting next attempt ${attempt}`)
|
|
||||||
nextAttempt = null
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.debug('Tx has been already sent')
|
|
||||||
}
|
|
||||||
logger.info('Acking message')
|
|
||||||
channel.ack(msg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
||||||
async function getExchangeMessages(nonce) {
|
async function getExchangeMessages(nonce) {
|
||||||
logger.debug('Getting exchange messages')
|
logger.debug('Getting exchange messages')
|
||||||
const messages = []
|
const messages = []
|
||||||
do {
|
while (true) {
|
||||||
const msg = await exchangeQueue.get()
|
const msg = await exchangeQueue.get()
|
||||||
if (msg === false) {
|
if (msg === false) {
|
||||||
break
|
break
|
||||||
|
@ -151,38 +40,11 @@ async function getExchangeMessages (nonce) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
messages.push(msg)
|
messages.push(msg)
|
||||||
} while (true)
|
}
|
||||||
logger.debug(`Found ${messages.length} messages`)
|
logger.debug(`Found ${messages.length} messages`)
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign (keysFile, hash, tx, publicKey) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const cmd = exec.execFile('./sign-entrypoint.sh', [ PROXY_URL, keysFile, hash ], async (error) => {
|
|
||||||
if (fs.existsSync('signature')) {
|
|
||||||
logger.info('Finished signature generation')
|
|
||||||
const signature = JSON.parse(fs.readFileSync('signature'))
|
|
||||||
logger.debug('%o', signature)
|
|
||||||
|
|
||||||
logger.info('Building signed transaction')
|
|
||||||
const signedTx = tx.addSignature(publicKey, { r: signature[1], s: signature[3] })
|
|
||||||
|
|
||||||
logger.info('Sending transaction')
|
|
||||||
logger.debug(signedTx)
|
|
||||||
await sendTx(signedTx)
|
|
||||||
resolve(true)
|
|
||||||
} else if (error === null || error.code === 0) {
|
|
||||||
resolve(true)
|
|
||||||
} else {
|
|
||||||
logger.warn('Sign failed')
|
|
||||||
resolve(false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cmd.stdout.on('data', data => logger.debug(data.toString()))
|
|
||||||
cmd.stderr.on('data', data => logger.debug(data.toString()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function restart(req, res) {
|
function restart(req, res) {
|
||||||
logger.info('Cancelling current sign')
|
logger.info('Cancelling current sign')
|
||||||
nextAttempt = req.params.attempt
|
nextAttempt = req.params.attempt
|
||||||
|
@ -204,48 +66,206 @@ function getAccountFromFile (file) {
|
||||||
const publicKey = JSON.parse(fs.readFileSync(file))[5]
|
const publicKey = JSON.parse(fs.readFileSync(file))[5]
|
||||||
return {
|
return {
|
||||||
address: publicKeyToAddress(publicKey),
|
address: publicKeyToAddress(publicKey),
|
||||||
publicKey: publicKey
|
publicKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForAccountNonce (address, nonce) {
|
|
||||||
cancelled = false
|
|
||||||
logger.info(`Waiting for account ${address} to have nonce ${nonce}`)
|
|
||||||
while (!cancelled) {
|
|
||||||
const sequence = (await getAccount(address)).sequence
|
|
||||||
if (sequence >= nonce)
|
|
||||||
break
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
logger.debug('Waiting for needed account nonce')
|
|
||||||
}
|
|
||||||
logger.info('Account nonce is OK')
|
|
||||||
return !cancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAccount(address) {
|
function getAccount(address) {
|
||||||
logger.info(`Getting account ${address} data`)
|
logger.info(`Getting account ${address} data`)
|
||||||
return httpClient
|
return httpClient
|
||||||
.get(`/api/v1/account/${address}`)
|
.get(`/api/v1/account/${address}`)
|
||||||
.then(res => res.data)
|
.then((res) => res.data)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
logger.debug('Retrying')
|
logger.debug('Retrying')
|
||||||
return getAccount(address)
|
return getAccount(address)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForAccountNonce(address, nonce) {
|
||||||
|
cancelled = false
|
||||||
|
logger.info(`Waiting for account ${address} to have nonce ${nonce}`)
|
||||||
|
while (!cancelled) {
|
||||||
|
const { sequence } = await getAccount(address)
|
||||||
|
if (sequence >= nonce) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
logger.debug('Waiting for needed account nonce')
|
||||||
|
}
|
||||||
|
logger.info('Account nonce is OK')
|
||||||
|
return !cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function sendTx(tx) {
|
function sendTx(tx) {
|
||||||
return httpClient
|
return httpClient
|
||||||
.post(`/api/v1/broadcast?sync=true`, tx, {
|
.post('/api/v1/broadcast?sync=true', tx, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain'
|
'Content-Type': 'text/plain'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
if (err.response.data.message.includes('Tx already exists in cache'))
|
if (err.response.data.message.includes('Tx already exists in cache')) {
|
||||||
logger.debug('Tx already exists in cache')
|
logger.debug('Tx already exists in cache')
|
||||||
else {
|
return true
|
||||||
logger.info('Something failed, restarting: %o', err.response)
|
|
||||||
return new Promise(resolve => setTimeout(() => resolve(sendTx(tx)), 1000))
|
|
||||||
}
|
}
|
||||||
|
logger.info('Something failed, restarting: %o', err.response)
|
||||||
|
return new Promise((resolve) => setTimeout(() => resolve(sendTx(tx)), 1000))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sign(keysFile, hash, tx, publicKey) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const cmd = exec.execFile('./sign-entrypoint.sh', [PROXY_URL, keysFile, hash], async (error) => {
|
||||||
|
if (fs.existsSync('signature')) {
|
||||||
|
logger.info('Finished signature generation')
|
||||||
|
const signature = JSON.parse(fs.readFileSync('signature'))
|
||||||
|
logger.debug('%o', signature)
|
||||||
|
|
||||||
|
logger.info('Building signed transaction')
|
||||||
|
const signedTx = tx.addSignature(publicKey, {
|
||||||
|
r: signature[1],
|
||||||
|
s: signature[3]
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('Sending transaction')
|
||||||
|
logger.debug(signedTx)
|
||||||
|
await sendTx(signedTx)
|
||||||
|
resolve(true)
|
||||||
|
} else if (error === null || error.code === 0) {
|
||||||
|
resolve(true)
|
||||||
|
} else {
|
||||||
|
logger.warn('Sign failed')
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cmd.stdout.on('data', (data) => logger.debug(data.toString()))
|
||||||
|
cmd.stderr.on('data', (data) => logger.debug(data.toString()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
logger.info('Connecting to RabbitMQ server')
|
||||||
|
channel = await connectRabbit(RABBITMQ_URL)
|
||||||
|
logger.info('Connecting to signature events queue')
|
||||||
|
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
||||||
|
const signQueue = await assertQueue(channel, 'signQueue')
|
||||||
|
|
||||||
|
while (!ready) {
|
||||||
|
await new Promise((res) => setTimeout(res, 1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.prefetch(1)
|
||||||
|
signQueue.consume(async (msg) => {
|
||||||
|
const data = JSON.parse(msg.content)
|
||||||
|
|
||||||
|
logger.info('Consumed sign event: %o', data)
|
||||||
|
const {
|
||||||
|
nonce, epoch, newEpoch, parties, threshold
|
||||||
|
} = data
|
||||||
|
|
||||||
|
const keysFile = `/keys/keys${epoch}.store`
|
||||||
|
const { address: from, publicKey } = getAccountFromFile(keysFile)
|
||||||
|
if (from === '') {
|
||||||
|
logger.info('No keys found, acking message')
|
||||||
|
channel.ack(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const account = await getAccount(from)
|
||||||
|
|
||||||
|
logger.debug('Writing params')
|
||||||
|
fs.writeFileSync('./params', JSON.stringify({
|
||||||
|
parties: parties.toString(),
|
||||||
|
threshold: threshold.toString()
|
||||||
|
}))
|
||||||
|
|
||||||
|
attempt = 1
|
||||||
|
|
||||||
|
if (!newEpoch) {
|
||||||
|
const exchanges = await getExchangeMessages(nonce)
|
||||||
|
const exchangesData = exchanges.map((exchangeMsg) => JSON.parse(exchangeMsg.content))
|
||||||
|
|
||||||
|
if (exchanges.length > 0 && account.sequence <= nonce) {
|
||||||
|
const recipients = exchangesData.map(({ value, recipient }) => ({
|
||||||
|
to: recipient,
|
||||||
|
tokens: value
|
||||||
|
}))
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
logger.info(`Building corresponding transfer transaction, nonce ${nonce}`)
|
||||||
|
|
||||||
|
const tx = new Transaction({
|
||||||
|
from,
|
||||||
|
accountNumber: account.account_number,
|
||||||
|
sequence: nonce,
|
||||||
|
recipients,
|
||||||
|
asset: FOREIGN_ASSET,
|
||||||
|
memo: `Attempt ${attempt}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const hash = sha256(tx.getSignBytes())
|
||||||
|
logger.info(`Starting signature generation for transaction hash ${hash}`)
|
||||||
|
const done = await sign(keysFile, hash, tx, publicKey)
|
||||||
|
&& await waitForAccountNonce(from, nonce + 1)
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
exchanges.forEach((exchangeMsg) => channel.ack(exchangeMsg))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempt = nextAttempt || attempt + 1
|
||||||
|
logger.warn(`Sign failed, starting next attempt ${attempt}`)
|
||||||
|
nextAttempt = null
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (account.sequence <= nonce) {
|
||||||
|
const newKeysFile = `/keys/keys${newEpoch}.store`
|
||||||
|
const { address: to } = getAccountFromFile(newKeysFile)
|
||||||
|
|
||||||
|
while (to !== '') {
|
||||||
|
logger.info(`Building corresponding transaction for transferring all funds, nonce ${nonce}, recipient ${to}`)
|
||||||
|
const tx = new Transaction({
|
||||||
|
from,
|
||||||
|
accountNumber: account.account_number,
|
||||||
|
sequence: nonce,
|
||||||
|
recipients: [{
|
||||||
|
to,
|
||||||
|
tokens: account.balances.find((token) => token.symbol === FOREIGN_ASSET).free,
|
||||||
|
bnbs: new BN(account.balances.find((token) => token.symbol === 'BNB').free).minus(new BN(60000).div(10 ** 8))
|
||||||
|
}],
|
||||||
|
asset: FOREIGN_ASSET,
|
||||||
|
memo: `Attempt ${attempt}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const hash = sha256(tx.getSignBytes())
|
||||||
|
logger.info(`Starting signature generation for transaction hash ${hash}`)
|
||||||
|
const done = await sign(keysFile, hash, tx, publicKey)
|
||||||
|
&& await waitForAccountNonce(from, nonce + 1)
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
await confirmFundsTransfer()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempt = nextAttempt || attempt + 1
|
||||||
|
logger.warn(`Sign failed, starting next attempt ${attempt}`)
|
||||||
|
nextAttempt = null
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug('Tx has been already sent')
|
||||||
|
}
|
||||||
|
logger.info('Acking message')
|
||||||
|
channel.ack(msg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get('/restart/:attempt', restart)
|
||||||
|
app.get('/start', (req, res) => {
|
||||||
|
logger.info('Ready to start')
|
||||||
|
ready = true
|
||||||
|
res.send()
|
||||||
|
})
|
||||||
|
app.listen(8001, () => logger.debug('Listening on 8001'))
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
@ -11,21 +11,29 @@ const BNB_ASSET = 'BNB'
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
const { from, accountNumber, sequence, recipients, asset, memo = '' } = options
|
const {
|
||||||
|
from, accountNumber, sequence, recipients, asset, memo = ''
|
||||||
|
} = options
|
||||||
|
|
||||||
const totalTokens = recipients.reduce((sum, { tokens }) => sum.plus(new BN(tokens || 0)), new BN(0))
|
const totalTokens = recipients.reduce(
|
||||||
const totalBnbs = recipients.reduce((sum, { bnbs }) => sum.plus(new BN(bnbs || 0)), new BN(0))
|
(sum, { tokens }) => sum.plus(new BN(tokens || 0)), new BN(0)
|
||||||
|
)
|
||||||
|
const totalBnbs = recipients.reduce(
|
||||||
|
(sum, { bnbs }) => sum.plus(new BN(bnbs || 0)), new BN(0)
|
||||||
|
)
|
||||||
const senderCoins = []
|
const senderCoins = []
|
||||||
if (asset && totalTokens.isGreaterThan(0)) {
|
if (asset && totalTokens.isGreaterThan(0)) {
|
||||||
senderCoins.push({
|
senderCoins.push({
|
||||||
denom: asset,
|
denom: asset,
|
||||||
amount: totalTokens.multipliedBy(10 ** 8).toNumber(),
|
amount: totalTokens.multipliedBy(10 ** 8)
|
||||||
|
.toNumber()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (totalBnbs.isGreaterThan(0)) {
|
if (totalBnbs.isGreaterThan(0)) {
|
||||||
senderCoins.push({
|
senderCoins.push({
|
||||||
denom: BNB_ASSET,
|
denom: BNB_ASSET,
|
||||||
amount: totalBnbs.multipliedBy(10 ** 8).toNumber(),
|
amount: totalBnbs.multipliedBy(10 ** 8)
|
||||||
|
.toNumber()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
senderCoins.sort((a, b) => a.denom > b.denom)
|
senderCoins.sort((a, b) => a.denom > b.denom)
|
||||||
|
@ -39,13 +47,15 @@ class Transaction {
|
||||||
if (asset && tokens) {
|
if (asset && tokens) {
|
||||||
receiverCoins.push({
|
receiverCoins.push({
|
||||||
denom: asset,
|
denom: asset,
|
||||||
amount: new BN(tokens).multipliedBy(10 ** 8).toNumber(),
|
amount: new BN(tokens).multipliedBy(10 ** 8)
|
||||||
|
.toNumber()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (bnbs) {
|
if (bnbs) {
|
||||||
receiverCoins.push({
|
receiverCoins.push({
|
||||||
denom: BNB_ASSET,
|
denom: BNB_ASSET,
|
||||||
amount: new BN(bnbs).multipliedBy(10 ** 8).toNumber(),
|
amount: new BN(bnbs).multipliedBy(10 ** 8)
|
||||||
|
.toNumber()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
receiverCoins.sort((a, b) => a.denom > b.denom)
|
receiverCoins.sort((a, b) => a.denom > b.denom)
|
||||||
|
@ -56,8 +66,14 @@ class Transaction {
|
||||||
})
|
})
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
inputs: inputs.map((x) => ({...x, address: crypto.decodeAddress(x.address)})),
|
inputs: inputs.map((x) => ({
|
||||||
outputs: outputs.map((x) => ({...x, address: crypto.decodeAddress(x.address)})),
|
...x,
|
||||||
|
address: crypto.decodeAddress(x.address)
|
||||||
|
})),
|
||||||
|
outputs: outputs.map((x) => ({
|
||||||
|
...x,
|
||||||
|
address: crypto.decodeAddress(x.address)
|
||||||
|
})),
|
||||||
msgType: 'MsgSend'
|
msgType: 'MsgSend'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +88,7 @@ class Transaction {
|
||||||
memo,
|
memo,
|
||||||
msg,
|
msg,
|
||||||
sequence,
|
sequence,
|
||||||
type: msg.msgType,
|
type: msg.msgType
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,14 +102,16 @@ class Transaction {
|
||||||
const s = new BN(signature.s, 16)
|
const s = new BN(signature.s, 16)
|
||||||
if (s.gt(n.div(2))) {
|
if (s.gt(n.div(2))) {
|
||||||
logger.debug('Normalizing s')
|
logger.debug('Normalizing s')
|
||||||
signature.s = n.minus(s).toString(16)
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
signature.s = n.minus(s)
|
||||||
|
.toString(16)
|
||||||
}
|
}
|
||||||
const publicKeyEncoded = Buffer.from('eb5ae98721' + (yLast % 2 ? '03' : '02') + padZeros(publicKey.x, 64), 'hex')
|
const publicKeyEncoded = Buffer.from(`eb5ae98721${yLast % 2 ? '03' : '02'}${padZeros(publicKey.x, 64)}`, 'hex')
|
||||||
this.tx.signatures = [{
|
this.tx.signatures = [{
|
||||||
pub_key: publicKeyEncoded,
|
pub_key: publicKeyEncoded,
|
||||||
signature: Buffer.from(padZeros(signature.r, 64) + padZeros(signature.s, 64), 'hex'),
|
signature: Buffer.from(padZeros(signature.r, 64) + padZeros(signature.s, 64), 'hex'),
|
||||||
account_number: this.tx.account_number,
|
account_number: this.tx.account_number,
|
||||||
sequence: this.tx.sequence,
|
sequence: this.tx.sequence
|
||||||
}]
|
}]
|
||||||
return this.tx.serialize()
|
return this.tx.serialize()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,14 @@ const { FOREIGN_URL, FOREIGN_ASSET } = process.env
|
||||||
const address = process.argv[2]
|
const address = process.argv[2]
|
||||||
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
||||||
|
|
||||||
|
function main() {
|
||||||
httpClient
|
httpClient
|
||||||
.get(`/api/v1/account/${address}`)
|
.get(`/api/v1/account/${address}`)
|
||||||
.then(res => {
|
.then((res) => {
|
||||||
console.log(`BNB: ${parseFloat(res.data.balances.find(x => x.symbol === 'BNB').free)}`)
|
console.log(`BNB: ${parseFloat(res.data.balances.find((token) => token.symbol === 'BNB').free)}`)
|
||||||
console.log(`${FOREIGN_ASSET}: ${parseFloat(res.data.balances.find(x => x.symbol === FOREIGN_ASSET).free)}`)
|
console.log(`${FOREIGN_ASSET}: ${parseFloat(res.data.balances.find((token) => token.symbol === FOREIGN_ASSET).free)}`)
|
||||||
})
|
})
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
@ -43,10 +43,11 @@ async function main () {
|
||||||
receipt = await client.transfer(from, to, tokens, FOREIGN_ASSET, 'exchange')
|
receipt = await client.transfer(from, to, tokens, FOREIGN_ASSET, 'exchange')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receipt.status === 200)
|
if (receipt.status === 200) {
|
||||||
console.log(receipt.result[0].hash)
|
console.log(receipt.result[0].hash)
|
||||||
else
|
} else {
|
||||||
console.log(receipt)
|
console.log(receipt)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -8,11 +8,18 @@ const abiToken = require('./IERC20').abi
|
||||||
const web3 = new Web3(HOME_RPC_URL, null, { transactionConfirmationBlocks: 1 })
|
const web3 = new Web3(HOME_RPC_URL, null, { transactionConfirmationBlocks: 1 })
|
||||||
const token = new web3.eth.Contract(abiToken, HOME_TOKEN_ADDRESS)
|
const token = new web3.eth.Contract(abiToken, HOME_TOKEN_ADDRESS)
|
||||||
|
|
||||||
|
function main() {
|
||||||
const address = process.argv[2]
|
const address = process.argv[2]
|
||||||
|
|
||||||
web3.eth.getBalance(address).then(x => console.log(`${x.toString()} wei`))
|
web3.eth.getBalance(address)
|
||||||
|
.then((balance) => console.log(`${balance.toString()} wei`))
|
||||||
|
|
||||||
token.methods.balanceOf(address).call()
|
token.methods.balanceOf(address)
|
||||||
.then(x => parseFloat(new BN(x).dividedBy(10 ** 18).toFixed(8, 3)))
|
.call()
|
||||||
.then(x => console.log(`${x.toString()} tokens`))
|
.then((balance) => parseFloat(new BN(balance).dividedBy(10 ** 18)
|
||||||
|
.toFixed(8, 3)))
|
||||||
|
.then((balance) => console.log(`${balance.toString()} tokens`))
|
||||||
.catch(() => console.log('0 tokens'))
|
.catch(() => console.log('0 tokens'))
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const BN = require('bignumber.js')
|
const BN = require('bignumber.js')
|
||||||
|
|
||||||
const { HOME_RPC_URL, HOME_BRIDGE_ADDRESS, HOME_PRIVATE_KEY, HOME_TOKEN_ADDRESS } = process.env
|
const {
|
||||||
|
HOME_RPC_URL, HOME_BRIDGE_ADDRESS, HOME_PRIVATE_KEY, HOME_TOKEN_ADDRESS
|
||||||
|
} = process.env
|
||||||
|
|
||||||
const abiToken = require('./IERC20').abi
|
const abiToken = require('./IERC20').abi
|
||||||
const abiBridge = require('./Bridge').abi
|
const abiBridge = require('./Bridge').abi
|
||||||
|
@ -16,19 +18,20 @@ const sender = web3.eth.accounts.privateKeyToAccount(`0x${PRIVATE_KEY}`).address
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const HOME_CHAIN_ID = await web3.eth.net.getId()
|
const HOME_CHAIN_ID = await web3.eth.net.getId()
|
||||||
const blockGasLimit = (await web3.eth.getBlock("latest", false)).gasLimit
|
const blockGasLimit = (await web3.eth.getBlock('latest', false)).gasLimit
|
||||||
|
|
||||||
const to = process.argv[2]
|
const to = process.argv[2]
|
||||||
|
|
||||||
const amount = parseInt(process.argv[3])
|
const amount = parseInt(process.argv[3], 10)
|
||||||
let coins = process.argv[4]
|
let coins = process.argv[4]
|
||||||
|
|
||||||
const txCount = await web3.eth.getTransactionCount(sender)
|
const txCount = await web3.eth.getTransactionCount(sender)
|
||||||
|
|
||||||
if (to === "bridge" && amount !== 0) {
|
if (to === 'bridge' && amount !== 0) {
|
||||||
console.log(`Transfer from ${sender} to ${HOME_BRIDGE_ADDRESS}, ${amount} tokens`)
|
console.log(`Transfer from ${sender} to ${HOME_BRIDGE_ADDRESS}, ${amount} tokens`)
|
||||||
|
|
||||||
const queryApprove = token.methods.approve(HOME_BRIDGE_ADDRESS, '0x'+(new BN(amount).multipliedBy(10 ** 18).toString(16)))
|
const queryApprove = token.methods.approve(HOME_BRIDGE_ADDRESS, `0x${new BN(amount).multipliedBy(10 ** 18)
|
||||||
|
.toString(16)}`)
|
||||||
const txApprove = {
|
const txApprove = {
|
||||||
data: queryApprove.encodeABI(),
|
data: queryApprove.encodeABI(),
|
||||||
from: sender,
|
from: sender,
|
||||||
|
@ -42,9 +45,10 @@ async function main () {
|
||||||
const signedTxApprove = await web3.eth.accounts.signTransaction(txApprove, PRIVATE_KEY)
|
const signedTxApprove = await web3.eth.accounts.signTransaction(txApprove, PRIVATE_KEY)
|
||||||
|
|
||||||
const receiptApprove = await web3.eth.sendSignedTransaction(signedTxApprove.rawTransaction)
|
const receiptApprove = await web3.eth.sendSignedTransaction(signedTxApprove.rawTransaction)
|
||||||
console.log('txHash approve: ' + receiptApprove.transactionHash)
|
console.log(`txHash approve: ${receiptApprove.transactionHash}`)
|
||||||
|
|
||||||
const queryExchange = bridge.methods.exchange('0x'+(new BN(amount).multipliedBy(10 ** 18).toString(16)))
|
const queryExchange = bridge.methods.exchange(`0x${new BN(amount).multipliedBy(10 ** 18)
|
||||||
|
.toString(16)}`)
|
||||||
const txExchange = {
|
const txExchange = {
|
||||||
data: queryExchange.encodeABI(),
|
data: queryExchange.encodeABI(),
|
||||||
from: sender,
|
from: sender,
|
||||||
|
@ -58,11 +62,12 @@ async function main () {
|
||||||
const signedTxExchange = await web3.eth.accounts.signTransaction(txExchange, PRIVATE_KEY)
|
const signedTxExchange = await web3.eth.accounts.signTransaction(txExchange, PRIVATE_KEY)
|
||||||
|
|
||||||
const receiptExchange = await web3.eth.sendSignedTransaction(signedTxExchange.rawTransaction)
|
const receiptExchange = await web3.eth.sendSignedTransaction(signedTxExchange.rawTransaction)
|
||||||
console.log('txHash exchange: ' + receiptExchange.transactionHash)
|
console.log(`txHash exchange: ${receiptExchange.transactionHash}`)
|
||||||
} else if (amount !== 0) {
|
} else if (amount !== 0) {
|
||||||
console.log(`Transfer from ${sender} to ${to}, ${amount} tokens`)
|
console.log(`Transfer from ${sender} to ${to}, ${amount} tokens`)
|
||||||
|
|
||||||
const query = token.methods.transfer(to, '0x'+(new BN(amount).multipliedBy(10 ** 18).toString(16)))
|
const query = token.methods.transfer(to, `0x${new BN(amount).multipliedBy(10 ** 18)
|
||||||
|
.toString(16)}`)
|
||||||
const tx = {
|
const tx = {
|
||||||
data: query.encodeABI(),
|
data: query.encodeABI(),
|
||||||
from: sender,
|
from: sender,
|
||||||
|
@ -75,7 +80,7 @@ async function main () {
|
||||||
}) * 1.5), blockGasLimit)
|
}) * 1.5), blockGasLimit)
|
||||||
const signedTx = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
|
const signedTx = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
|
||||||
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||||
console.log('txHash transfer: ' + receipt.transactionHash)
|
console.log(`txHash transfer: ${receipt.transactionHash}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coins) {
|
if (coins) {
|
||||||
|
@ -85,7 +90,7 @@ async function main () {
|
||||||
const tx = {
|
const tx = {
|
||||||
data: '0x',
|
data: '0x',
|
||||||
from: sender,
|
from: sender,
|
||||||
to: to,
|
to,
|
||||||
nonce: await web3.eth.getTransactionCount(sender),
|
nonce: await web3.eth.getTransactionCount(sender),
|
||||||
chainId: HOME_CHAIN_ID,
|
chainId: HOME_CHAIN_ID,
|
||||||
value: web3.utils.toWei(new BN(coins).toString(), 'ether'),
|
value: web3.utils.toWei(new BN(coins).toString(), 'ether'),
|
||||||
|
@ -94,9 +99,8 @@ async function main () {
|
||||||
const signedTx = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
|
const signedTx = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
|
||||||
|
|
||||||
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||||
console.log('txHash: ' + receipt.transactionHash)
|
console.log(`txHash: ${receipt.transactionHash}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -2,20 +2,6 @@ const { utils } = require('ethers')
|
||||||
const bech32 = require('bech32')
|
const bech32 = require('bech32')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
|
|
||||||
const privateKey = process.argv[2].startsWith('0x') ? process.argv[2] : '0x' + process.argv[2]
|
|
||||||
|
|
||||||
const ethAddress = utils.computeAddress(privateKey)
|
|
||||||
const publicKey = utils.computePublicKey(privateKey, true)
|
|
||||||
|
|
||||||
console.log(`Eth address: ${ethAddress}\nBnc address: ${publicKeyToAddress(publicKey)}`)
|
|
||||||
|
|
||||||
function publicKeyToAddress (publicKey) {
|
|
||||||
const sha256Hash = sha256(Buffer.from(publicKey.substr(2), 'hex'))
|
|
||||||
const hash = ripemd160(Buffer.from(sha256Hash, 'hex'))
|
|
||||||
const words = bech32.toWords(Buffer.from(hash, 'hex'))
|
|
||||||
return bech32.encode('tbnb', words)
|
|
||||||
}
|
|
||||||
|
|
||||||
function sha256(bytes) {
|
function sha256(bytes) {
|
||||||
return crypto.createHash('sha256').update(bytes).digest('hex')
|
return crypto.createHash('sha256').update(bytes).digest('hex')
|
||||||
}
|
}
|
||||||
|
@ -23,3 +9,21 @@ function sha256 (bytes) {
|
||||||
function ripemd160(bytes) {
|
function ripemd160(bytes) {
|
||||||
return crypto.createHash('ripemd160').update(bytes).digest('hex')
|
return crypto.createHash('ripemd160').update(bytes).digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function publicKeyToAddress(publicKey) {
|
||||||
|
const sha256Hash = sha256(Buffer.from(publicKey.substr(2), 'hex'))
|
||||||
|
const hash = ripemd160(Buffer.from(sha256Hash, 'hex'))
|
||||||
|
const words = bech32.toWords(Buffer.from(hash, 'hex'))
|
||||||
|
return bech32.encode('tbnb', words)
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const privateKey = process.argv[2].startsWith('0x') ? process.argv[2] : `0x${process.argv[2]}`
|
||||||
|
|
||||||
|
const ethAddress = utils.computeAddress(privateKey)
|
||||||
|
const publicKey = utils.computePublicKey(privateKey, true)
|
||||||
|
|
||||||
|
console.log(`Eth address: ${ethAddress}\nBnc address: ${publicKeyToAddress(publicKey)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
|
@ -15,20 +15,19 @@ async function main () {
|
||||||
|
|
||||||
console.log(`Transfer from ${sender} to ${to}, ${amount} eth`)
|
console.log(`Transfer from ${sender} to ${to}, ${amount} eth`)
|
||||||
|
|
||||||
const tx_coins = {
|
const txCoins = {
|
||||||
data: '0x',
|
data: '0x',
|
||||||
from: sender,
|
from: sender,
|
||||||
to: to,
|
to,
|
||||||
nonce: await web3.eth.getTransactionCount(sender),
|
nonce: await web3.eth.getTransactionCount(sender),
|
||||||
chainId: SIDE_CHAIN_ID,
|
chainId: SIDE_CHAIN_ID,
|
||||||
value: web3.utils.toWei(new BN(amount).toString(), 'ether'),
|
value: web3.utils.toWei(new BN(amount).toString(), 'ether'),
|
||||||
gas: 21000
|
gas: 21000
|
||||||
}
|
}
|
||||||
const signedTx = await web3.eth.accounts.signTransaction(tx_coins, SIDE_PRIVATE_KEY)
|
const signedTx = await web3.eth.accounts.signTransaction(txCoins, SIDE_PRIVATE_KEY)
|
||||||
|
|
||||||
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||||
console.log('txHash: ' + receipt.transactionHash)
|
console.log(`txHash: ${receipt.transactionHash}`)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -6,5 +6,8 @@
|
||||||
"axios": "0.19.0",
|
"axios": "0.19.0",
|
||||||
"@binance-chain/javascript-sdk": "2.16.1",
|
"@binance-chain/javascript-sdk": "2.16.1",
|
||||||
"bignumber.js": "9.0.0"
|
"bignumber.js": "9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,15 @@ const { getBalance } = require('./utils/bncController')
|
||||||
|
|
||||||
const { controller1, controller3 } = require('./utils/proxyController')
|
const { controller1, controller3 } = require('./utils/proxyController')
|
||||||
|
|
||||||
module.exports = newValidator => {
|
module.exports = (newValidator) => {
|
||||||
describe('add validator', function () {
|
describe('add validator', function () {
|
||||||
let info
|
let info
|
||||||
let initialInfo
|
let initialInfo
|
||||||
let nextValidators
|
let nextValidators
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
initialInfo = info = await controller1.getInfo()
|
initialInfo = await controller1.getInfo()
|
||||||
|
info = initialInfo
|
||||||
nextValidators = [...initialInfo.validators, newValidator]
|
nextValidators = [...initialInfo.validators, newValidator]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ module.exports = newValidator => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller3.voteStartVoting()
|
await controller3.voteStartVoting()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'voting')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'voting')
|
||||||
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
|
@ -46,7 +47,10 @@ module.exports = newValidator => {
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
|
|
||||||
await controller3.voteAddValidator(newValidator)
|
await controller3.voteAddValidator(newValidator)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.nextValidators.length === nextValidators.length)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.nextValidators.length === nextValidators.length
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, nextValidators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, nextValidators, 'Next validators are not set correctly')
|
||||||
|
|
||||||
|
@ -67,7 +71,7 @@ module.exports = newValidator => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller3.voteStartKeygen()
|
await controller3.voteStartKeygen()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'keygen')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'keygen')
|
||||||
|
|
||||||
await controller3.voteStartKeygen()
|
await controller3.voteStartKeygen()
|
||||||
await delay(5000)
|
await delay(5000)
|
||||||
|
@ -81,12 +85,15 @@ module.exports = newValidator => {
|
||||||
|
|
||||||
it('should finish keygen process and start funds transfer', async function () {
|
it('should finish keygen process and start funds transfer', async function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'funds_transfer')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'funds_transfer')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should transfer all funds to new account and start new epoch', async function () {
|
it('should transfer all funds to new account and start new epoch', async function () {
|
||||||
this.timeout(300000)
|
this.timeout(300000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.epoch === initialInfo.epoch + 1)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.epoch === initialInfo.epoch + 1
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, nextValidators, 'Incorrect set of validators in new epoch')
|
assert.deepStrictEqual(info.validators, nextValidators, 'Incorrect set of validators in new epoch')
|
||||||
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
||||||
|
|
|
@ -2,7 +2,7 @@ const { delay } = require('./utils/wait')
|
||||||
|
|
||||||
const { controller1 } = require('./utils/proxyController')
|
const { controller1 } = require('./utils/proxyController')
|
||||||
|
|
||||||
module.exports = usersFunc => {
|
module.exports = (usersFunc) => {
|
||||||
describe('exchange of tokens in bnc => eth direction', function () {
|
describe('exchange of tokens in bnc => eth direction', function () {
|
||||||
let users
|
let users
|
||||||
let info
|
let info
|
||||||
|
@ -11,21 +11,21 @@ module.exports = usersFunc => {
|
||||||
before(async function () {
|
before(async function () {
|
||||||
users = usersFunc()
|
users = usersFunc()
|
||||||
info = await controller1.getInfo()
|
info = await controller1.getInfo()
|
||||||
ethBalances = await Promise.all(users.map(user => user.getEthBalance()))
|
ethBalances = await Promise.all(users.map((user) => user.getEthBalance()))
|
||||||
|
|
||||||
await Promise.all(users.map((user, i) => user.exchangeBnc(info.foreignBridgeAddress, 3 + i)))
|
await Promise.all(users.map((user, i) => user.exchangeBnc(info.foreignBridgeAddress, 3 + i)))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make coorect exchange transactions on eth side', async function () {
|
it('should make correct exchange transactions on eth side', async function () {
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i += 1) {
|
||||||
do {
|
while (true) {
|
||||||
const user = users[i]
|
const user = users[i]
|
||||||
const newEthBalance = await user.getEthBalance()
|
const newEthBalance = await user.getEthBalance()
|
||||||
if (newEthBalance === ethBalances[i] + 3 + i) {
|
if (newEthBalance === ethBalances[i] + 3 + i) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
await delay(500)
|
await delay(500)
|
||||||
} while (true)
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,13 +5,14 @@ const { getBalance } = require('./utils/bncController')
|
||||||
|
|
||||||
const { controller1, controller2, controller3 } = require('./utils/proxyController')
|
const { controller1, controller2, controller3 } = require('./utils/proxyController')
|
||||||
|
|
||||||
module.exports = newThreshold => {
|
module.exports = (newThreshold) => {
|
||||||
describe('change threshold', function () {
|
describe('change threshold', function () {
|
||||||
let info
|
let info
|
||||||
let initialInfo
|
let initialInfo
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
initialInfo = info = await controller1.getInfo()
|
initialInfo = await controller1.getInfo()
|
||||||
|
info = initialInfo
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should start voting process', async function () {
|
it('should start voting process', async function () {
|
||||||
|
@ -21,7 +22,7 @@ module.exports = newThreshold => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller2.voteStartVoting()
|
await controller2.voteStartVoting()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'voting')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'voting')
|
||||||
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
|
@ -46,7 +47,10 @@ module.exports = newThreshold => {
|
||||||
assert.deepStrictEqual(info.nextThreshold, initialInfo.threshold, 'Next threshold is not set correctly')
|
assert.deepStrictEqual(info.nextThreshold, initialInfo.threshold, 'Next threshold is not set correctly')
|
||||||
|
|
||||||
await controller2.voteChangeThreshold(newThreshold)
|
await controller2.voteChangeThreshold(newThreshold)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.nextThreshold === newThreshold)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.nextThreshold === newThreshold
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
assert.deepStrictEqual(info.threshold, initialInfo.threshold, 'Threshold not set correctly')
|
assert.deepStrictEqual(info.threshold, initialInfo.threshold, 'Threshold not set correctly')
|
||||||
|
@ -70,7 +74,7 @@ module.exports = newThreshold => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller2.voteStartKeygen()
|
await controller2.voteStartKeygen()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'keygen')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'keygen')
|
||||||
|
|
||||||
await controller3.voteStartKeygen()
|
await controller3.voteStartKeygen()
|
||||||
await delay(5000)
|
await delay(5000)
|
||||||
|
@ -86,12 +90,15 @@ module.exports = newThreshold => {
|
||||||
|
|
||||||
it('should finish keygen process and start funds transfer', async function () {
|
it('should finish keygen process and start funds transfer', async function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'funds_transfer')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'funds_transfer')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should transfer all funds to new account and start new epoch', async function () {
|
it('should transfer all funds to new account and start new epoch', async function () {
|
||||||
this.timeout(300000)
|
this.timeout(300000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.epoch === initialInfo.epoch + 1)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.epoch === initialInfo.epoch + 1
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Incorrect set of validators in new epoch')
|
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Incorrect set of validators in new epoch')
|
||||||
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const { getSequence } = require('./utils/bncController')
|
const { getSequence } = require('./utils/bncController')
|
||||||
const { waitPromise, delay } = require('./utils/wait')
|
const { waitPromise, delay, seqMap } = require('./utils/wait')
|
||||||
|
|
||||||
const { HOME_BRIDGE_ADDRESS } = process.env
|
const { HOME_BRIDGE_ADDRESS } = process.env
|
||||||
|
|
||||||
const { controller1 } = require('./utils/proxyController')
|
const { controller1 } = require('./utils/proxyController')
|
||||||
|
|
||||||
module.exports = usersFunc => {
|
module.exports = (usersFunc) => {
|
||||||
describe('exchange of tokens in eth => bnc direction', function () {
|
describe('exchange of tokens in eth => bnc direction', function () {
|
||||||
let info
|
let info
|
||||||
let users
|
let users
|
||||||
|
@ -17,8 +17,8 @@ module.exports = usersFunc => {
|
||||||
before(async function () {
|
before(async function () {
|
||||||
users = usersFunc()
|
users = usersFunc()
|
||||||
info = await controller1.getInfo()
|
info = await controller1.getInfo()
|
||||||
ethBalances = await Promise.all(users.map(user => user.getEthBalance()))
|
ethBalances = await Promise.all(users.map((user) => user.getEthBalance()))
|
||||||
bncBalances = await users.seqMap(user => user.getBncBalance())
|
bncBalances = await seqMap(users, (user) => user.getBncBalance())
|
||||||
|
|
||||||
bncBridgeSequence = await getSequence(info.foreignBridgeAddress)
|
bncBridgeSequence = await getSequence(info.foreignBridgeAddress)
|
||||||
await Promise.all(users.map((user, i) => user.approveEth(HOME_BRIDGE_ADDRESS, 5 + i)))
|
await Promise.all(users.map((user, i) => user.approveEth(HOME_BRIDGE_ADDRESS, 5 + i)))
|
||||||
|
@ -26,21 +26,24 @@ module.exports = usersFunc => {
|
||||||
|
|
||||||
it('should accept exchange requests', async function () {
|
it('should accept exchange requests', async function () {
|
||||||
await Promise.all(users.map((user, i) => user.exchangeEth(5 + i)))
|
await Promise.all(users.map((user, i) => user.exchangeEth(5 + i)))
|
||||||
const newEthBalances = await Promise.all(users.map(user => user.getEthBalance()))
|
const newEthBalances = await Promise.all(users.map((user) => user.getEthBalance()))
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i += 1) {
|
||||||
assert.strictEqual(newEthBalances[i], ethBalances[i] - 5 - i, `Balance of ${users[i].ethAddress} did not updated as expected`)
|
assert.strictEqual(newEthBalances[i], ethBalances[i] - 5 - i, `Balance of ${users[i].ethAddress} did not updated as expected`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make exchange transaction on bnc side', async function () {
|
it('should make exchange transaction on bnc side', async function () {
|
||||||
this.timeout(300000)
|
this.timeout(300000)
|
||||||
await waitPromise(() => getSequence(info.foreignBridgeAddress), sequence => sequence === bncBridgeSequence + 1)
|
await waitPromise(
|
||||||
|
() => getSequence(info.foreignBridgeAddress),
|
||||||
|
(sequence) => sequence === bncBridgeSequence + 1
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make correct exchange transaction', async function () {
|
it('should make correct exchange transaction', async function () {
|
||||||
await delay(10000)
|
await delay(10000)
|
||||||
const newBncBalances = await Promise.all(users.map(user => user.getBncBalance()))
|
const newBncBalances = await Promise.all(users.map((user) => user.getBncBalance()))
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i += 1) {
|
||||||
assert.strictEqual(newBncBalances[i], bncBalances[i] + 5 + i, `Balance of ${users[i].bncAddress} did not updated as expected`)
|
assert.strictEqual(newBncBalances[i], bncBalances[i] + 5 + i, `Balance of ${users[i].bncAddress} did not updated as expected`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const createUser = require('./utils/user')
|
const createUser = require('./utils/user')
|
||||||
const { waitPromise } = require('./utils/wait')
|
const { waitPromise, seqMap } = require('./utils/wait')
|
||||||
|
|
||||||
const testEthToBnc = require('./ethToBnc')
|
const testEthToBnc = require('./ethToBnc')
|
||||||
const testBncToEth = require('./bncToEth')
|
const testBncToEth = require('./bncToEth')
|
||||||
|
@ -18,7 +18,7 @@ describe('bridge tests', function () {
|
||||||
let users
|
let users
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
users = await usersConfig.seqMap(user => createUser(user.privateKey))
|
users = await seqMap(usersConfig, (user) => createUser(user.privateKey))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generation of initial epoch keys', function () {
|
describe('generation of initial epoch keys', function () {
|
||||||
|
@ -33,7 +33,7 @@ describe('bridge tests', function () {
|
||||||
|
|
||||||
it('should generate keys', async function () {
|
it('should generate keys', async function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.epoch === 1)
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.epoch === 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -5,15 +5,16 @@ const { getBalance } = require('./utils/bncController')
|
||||||
|
|
||||||
const { controller1, controller2, controller3 } = require('./utils/proxyController')
|
const { controller1, controller2, controller3 } = require('./utils/proxyController')
|
||||||
|
|
||||||
module.exports = oldValidator => {
|
module.exports = (oldValidator) => {
|
||||||
describe('remove validator', function () {
|
describe('remove validator', function () {
|
||||||
let info
|
let info
|
||||||
let initialInfo
|
let initialInfo
|
||||||
let nextValidators
|
let nextValidators
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
initialInfo = info = await controller1.getInfo()
|
initialInfo = await controller1.getInfo()
|
||||||
nextValidators = initialInfo.validators.filter(validator => validator !== oldValidator)
|
info = initialInfo
|
||||||
|
nextValidators = initialInfo.validators.filter((validator) => validator !== oldValidator)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should start voting process', async function () {
|
it('should start voting process', async function () {
|
||||||
|
@ -23,7 +24,7 @@ module.exports = oldValidator => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller2.voteStartVoting()
|
await controller2.voteStartVoting()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'voting')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'voting')
|
||||||
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
assert.deepStrictEqual(info.epoch, initialInfo.epoch, 'Current epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
assert.deepStrictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Next epoch is not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
|
@ -46,7 +47,10 @@ module.exports = oldValidator => {
|
||||||
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, initialInfo.validators, 'Next validators are not set correctly')
|
||||||
|
|
||||||
await controller2.voteRemoveValidator(oldValidator)
|
await controller2.voteRemoveValidator(oldValidator)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.nextValidators.length === nextValidators.length)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.nextValidators.length === nextValidators.length
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
assert.deepStrictEqual(info.validators, initialInfo.validators, 'Validators are not set correctly')
|
||||||
assert.deepStrictEqual(info.nextValidators, nextValidators, 'Next validators are not set correctly')
|
assert.deepStrictEqual(info.nextValidators, nextValidators, 'Next validators are not set correctly')
|
||||||
|
|
||||||
|
@ -67,7 +71,7 @@ module.exports = oldValidator => {
|
||||||
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
assert.strictEqual(info.bridgeStatus, 'voting', 'Should not change state after one vote')
|
||||||
|
|
||||||
await controller2.voteStartKeygen()
|
await controller2.voteStartKeygen()
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'keygen')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'keygen')
|
||||||
|
|
||||||
await controller3.voteStartKeygen()
|
await controller3.voteStartKeygen()
|
||||||
await delay(5000)
|
await delay(5000)
|
||||||
|
@ -81,12 +85,15 @@ module.exports = oldValidator => {
|
||||||
|
|
||||||
it('should finish keygen process and start funds transfer', async function () {
|
it('should finish keygen process and start funds transfer', async function () {
|
||||||
this.timeout(120000)
|
this.timeout(120000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.bridgeStatus === 'funds_transfer')
|
info = await waitPromise(controller1.getInfo, (newInfo) => newInfo.bridgeStatus === 'funds_transfer')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should transfer all funds to new account and start new epoch', async function () {
|
it('should transfer all funds to new account and start new epoch', async function () {
|
||||||
this.timeout(300000)
|
this.timeout(300000)
|
||||||
info = await waitPromise(controller1.getInfo, info => info.epoch === initialInfo.epoch + 1)
|
info = await waitPromise(
|
||||||
|
controller1.getInfo,
|
||||||
|
(newInfo) => newInfo.epoch === initialInfo.epoch + 1
|
||||||
|
)
|
||||||
assert.deepStrictEqual(info.validators, nextValidators, 'Incorrect set of validators in new epoch')
|
assert.deepStrictEqual(info.validators, nextValidators, 'Incorrect set of validators in new epoch')
|
||||||
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
assert.strictEqual(info.nextEpoch, initialInfo.epoch + 1, 'Incorrect next epoch')
|
||||||
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
assert.strictEqual(info.bridgeStatus, 'ready', 'Incorrect bridge state in new epoch')
|
||||||
|
|
|
@ -16,7 +16,7 @@ module.exports = async function main (privateKey) {
|
||||||
await delay(1000)
|
await delay(1000)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transfer: async function (to, tokens, bnbs) {
|
async transfer(to, tokens, bnbs) {
|
||||||
const outputs = [{
|
const outputs = [{
|
||||||
to,
|
to,
|
||||||
coins: []
|
coins: []
|
||||||
|
@ -35,7 +35,7 @@ module.exports = async function main (privateKey) {
|
||||||
}
|
}
|
||||||
await client.multiSend(from, outputs, 'funding')
|
await client.multiSend(from, outputs, 'funding')
|
||||||
},
|
},
|
||||||
exchange: async function (to, value) {
|
async exchange(to, value) {
|
||||||
await client.transfer(from, to, value.toString(), FOREIGN_ASSET, 'exchange')
|
await client.transfer(from, to, value.toString(), FOREIGN_ASSET, 'exchange')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ const bnc = axios.create({
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getBalance: async function (address) {
|
async getBalance(address) {
|
||||||
const response = await retry(5, () => bnc.get(`/api/v1/account/${address}`))
|
const response = await retry(5, () => bnc.get(`/api/v1/account/${address}`))
|
||||||
const tokens = response.data.balances.find(x => x.symbol === FOREIGN_ASSET)
|
const tokens = response.data.balances.find((x) => x.symbol === FOREIGN_ASSET)
|
||||||
return response && tokens ? parseFloat(tokens.free) : 0
|
return response && tokens ? parseFloat(tokens.free) : 0
|
||||||
},
|
},
|
||||||
getSequence: async function (address) {
|
async getSequence(address) {
|
||||||
const response = await retry(5, () => bnc.get(`/api/v1/account/${address}/sequence`))
|
const response = await retry(5, () => bnc.get(`/api/v1/account/${address}/sequence`))
|
||||||
return response ? response.data.sequence : 0
|
return response ? response.data.sequence : 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,22 +9,22 @@ function createController (validatorId) {
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getInfo: async function () {
|
async getInfo() {
|
||||||
return (await proxy.get('/info')).data
|
return (await proxy.get('/info')).data
|
||||||
},
|
},
|
||||||
voteStartVoting: async function () {
|
async voteStartVoting() {
|
||||||
return (await proxy.get('/vote/startVoting')).data
|
return (await proxy.get('/vote/startVoting')).data
|
||||||
},
|
},
|
||||||
voteStartKeygen: async function () {
|
async voteStartKeygen() {
|
||||||
return (await proxy.get('/vote/startKeygen')).data
|
return (await proxy.get('/vote/startKeygen')).data
|
||||||
},
|
},
|
||||||
voteAddValidator: async function (validatorAddress) {
|
async voteAddValidator(validatorAddress) {
|
||||||
return (await proxy.get(`/vote/addValidator/${validatorAddress}`)).data
|
return (await proxy.get(`/vote/addValidator/${validatorAddress}`)).data
|
||||||
},
|
},
|
||||||
voteRemoveValidator: async function (validatorAddress) {
|
async voteRemoveValidator(validatorAddress) {
|
||||||
return (await proxy.get(`/vote/removeValidator/${validatorAddress}`)).data
|
return (await proxy.get(`/vote/removeValidator/${validatorAddress}`)).data
|
||||||
},
|
},
|
||||||
voteChangeThreshold: async function (threshold) {
|
async voteChangeThreshold(threshold) {
|
||||||
return (await proxy.get(`/vote/changeThreshold/${threshold}`)).data
|
return (await proxy.get(`/vote/changeThreshold/${threshold}`)).data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ const txOptions = {
|
||||||
gasLimit: 200000
|
gasLimit: 200000
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = async function (privateKey) {
|
async function createUser(privateKey) {
|
||||||
const wallet = new ethers.Wallet(privateKey, provider)
|
const wallet = new ethers.Wallet(privateKey, provider)
|
||||||
const ethAddress = wallet.address
|
const ethAddress = wallet.address
|
||||||
const bncAddress = getAddressFromPrivateKey(privateKey)
|
const bncAddress = getAddressFromPrivateKey(privateKey)
|
||||||
|
@ -23,32 +23,34 @@ module.exports = async function (privateKey) {
|
||||||
return {
|
return {
|
||||||
ethAddress,
|
ethAddress,
|
||||||
bncAddress,
|
bncAddress,
|
||||||
getEthBalance: async function () {
|
async getEthBalance() {
|
||||||
const balance = await token.balanceOf(ethAddress)
|
const balance = await token.balanceOf(ethAddress)
|
||||||
return parseFloat(new BN(balance).dividedBy(10 ** 18).toFixed(8, 3))
|
return parseFloat(new BN(balance).dividedBy(10 ** 18).toFixed(8, 3))
|
||||||
},
|
},
|
||||||
transferEth: async function (to, value) {
|
async transferEth(to, value) {
|
||||||
const tx = await token.transfer(to, '0x' + (new BN(value).multipliedBy(10 ** 18).toString(16)), txOptions)
|
const tx = await token.transfer(to, `0x${new BN(value).multipliedBy(10 ** 18).toString(16)}`, txOptions)
|
||||||
await tx.wait()
|
await tx.wait()
|
||||||
},
|
},
|
||||||
approveEth: async function (to, value) {
|
async approveEth(to, value) {
|
||||||
const tx = await token.approve(to, '0x' + (new BN(value).multipliedBy(10 ** 18).toString(16)), txOptions)
|
const tx = await token.approve(to, `0x${new BN(value).multipliedBy(10 ** 18).toString(16)}`, txOptions)
|
||||||
await tx.wait()
|
await tx.wait()
|
||||||
},
|
},
|
||||||
exchangeEth: async function (value) {
|
async exchangeEth(value) {
|
||||||
const tx = await bridge.exchange('0x' + (new BN(value).multipliedBy(10 ** 18).toString(16)), txOptions)
|
const tx = await bridge.exchange(`0x${new BN(value).multipliedBy(10 ** 18).toString(16)}`, txOptions)
|
||||||
await tx.wait()
|
await tx.wait()
|
||||||
},
|
},
|
||||||
getBncBalance: async function () {
|
async getBncBalance() {
|
||||||
const balance = await getBalance(bncAddress)
|
const balance = await getBalance(bncAddress)
|
||||||
await delay(1000)
|
await delay(1000)
|
||||||
return balance
|
return balance
|
||||||
},
|
},
|
||||||
transferBnc: async function (bridgeAddress, tokens, bnbs) {
|
async transferBnc(bridgeAddress, tokens, bnbs) {
|
||||||
return await bncClient.transfer(bridgeAddress, tokens, bnbs)
|
return await bncClient.transfer(bridgeAddress, tokens, bnbs)
|
||||||
},
|
},
|
||||||
exchangeBnc: async function (bridgeAddress, value) {
|
async exchangeBnc(bridgeAddress, value) {
|
||||||
return await bncClient.exchange(bridgeAddress, value)
|
return await bncClient.exchange(bridgeAddress, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = createUser
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
async function delay(ms) {
|
async function delay(ms) {
|
||||||
await new Promise(res => setTimeout(res, ms))
|
await new Promise((res) => setTimeout(res, ms))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitPromise(getPromise, checker) {
|
async function waitPromise(getPromise, checker) {
|
||||||
do {
|
while (true) {
|
||||||
const result = await getPromise()
|
const result = await getPromise()
|
||||||
if (checker(result))
|
if (checker(result)) {
|
||||||
return result
|
return result
|
||||||
|
}
|
||||||
await delay(1000)
|
await delay(1000)
|
||||||
} while (true)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function retry(n, getPromise) {
|
async function retry(n, getPromise) {
|
||||||
|
@ -17,16 +18,17 @@ async function retry (n, getPromise) {
|
||||||
return await getPromise()
|
return await getPromise()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await delay(3000)
|
await delay(3000)
|
||||||
n--
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
n -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.prototype.seqMap = async function (transition) {
|
async function seqMap(arr, transition) {
|
||||||
const results = []
|
const results = []
|
||||||
for (let i = 0; i < this.length; i++) {
|
for (let i = 0; i < arr.length; i += 1) {
|
||||||
results[i] = await transition(this[i])
|
results[i] = await transition(arr[i])
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
@ -34,5 +36,6 @@ Array.prototype.seqMap = async function (transition) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
waitPromise,
|
waitPromise,
|
||||||
delay,
|
delay,
|
||||||
retry
|
retry,
|
||||||
|
seqMap
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue