Support of closing epoch in oracle scripts
This commit is contained in:
parent
38e96b5552
commit
7f575238e1
|
@ -21,6 +21,8 @@ const bridgeAbi = [
|
||||||
'event NewEpochCancelled(uint indexed epoch)',
|
'event NewEpochCancelled(uint indexed epoch)',
|
||||||
'event NewFundsTransfer(uint indexed oldEpoch, uint indexed newEpoch)',
|
'event NewFundsTransfer(uint indexed oldEpoch, uint indexed newEpoch)',
|
||||||
'event EpochStart(uint indexed epoch, uint x, uint y)',
|
'event EpochStart(uint indexed epoch, uint x, uint y)',
|
||||||
|
'event EpochClose(uint indexed epoch)',
|
||||||
|
'event ForceSign()',
|
||||||
'function getThreshold(uint epoch) view returns (uint)',
|
'function getThreshold(uint epoch) view returns (uint)',
|
||||||
'function getParties(uint epoch) view returns (uint)',
|
'function getParties(uint epoch) view returns (uint)',
|
||||||
'function getRangeSize() view returns (uint)',
|
'function getRangeSize() view returns (uint)',
|
||||||
|
@ -160,7 +162,6 @@ async function sendSign(event, transactionHash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendStartSign() {
|
async function sendStartSign() {
|
||||||
redisTx.incr(`foreignNonce${epoch}`)
|
|
||||||
signQueue.send({
|
signQueue.send({
|
||||||
epoch,
|
epoch,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
|
@ -169,6 +170,7 @@ async function sendStartSign() {
|
||||||
parties: (await bridge.getParties(epoch)).toNumber()
|
parties: (await bridge.getParties(epoch)).toNumber()
|
||||||
})
|
})
|
||||||
foreignNonce[epoch] += 1
|
foreignNonce[epoch] += 1
|
||||||
|
redisTx.incr(`foreignNonce${epoch}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processEpochStart(event) {
|
async function processEpochStart(event) {
|
||||||
|
@ -186,6 +188,19 @@ async function processEpochStart(event) {
|
||||||
foreignNonce[epoch] = 0
|
foreignNonce[epoch] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendEpochClose() {
|
||||||
|
logger.debug(`Consumed epoch ${epoch} close event`)
|
||||||
|
signQueue.send({
|
||||||
|
closeEpoch: epoch,
|
||||||
|
blockNumber,
|
||||||
|
nonce: foreignNonce[epoch],
|
||||||
|
threshold: (await bridge.getThreshold(epoch)).toNumber(),
|
||||||
|
parties: (await bridge.getParties(epoch)).toNumber()
|
||||||
|
})
|
||||||
|
foreignNonce[epoch] += 1
|
||||||
|
redisTx.incr(`foreignNonce${epoch}`)
|
||||||
|
}
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
channel = await connectRabbit(RABBITMQ_URL)
|
channel = await connectRabbit(RABBITMQ_URL)
|
||||||
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
exchangeQueue = await assertQueue(channel, 'exchangeQueue')
|
||||||
|
@ -264,6 +279,8 @@ async function loop() {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
for (let curBlockNumber = blockNumber, i = 0; curBlockNumber <= endBlock; curBlockNumber += 1) {
|
for (let curBlockNumber = blockNumber, i = 0; curBlockNumber <= endBlock; curBlockNumber += 1) {
|
||||||
|
const rangeOffset = (curBlockNumber + 1 - epochStart) % rangeSize
|
||||||
|
const rangeStart = curBlockNumber - (rangeOffset || rangeSize)
|
||||||
let epochTimeUpdated = false
|
let epochTimeUpdated = false
|
||||||
while (i < bridgeEvents.length && bridgeEvents[i].blockNumber === curBlockNumber) {
|
while (i < bridgeEvents.length && bridgeEvents[i].blockNumber === curBlockNumber) {
|
||||||
const event = bridge.interface.parseLog(bridgeEvents[i])
|
const event = bridge.interface.parseLog(bridgeEvents[i])
|
||||||
|
@ -306,6 +323,19 @@ async function loop() {
|
||||||
epoch
|
epoch
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
case 'EpochClose':
|
||||||
|
if (isCurrentValidator) {
|
||||||
|
await sendEpochClose()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'ForceSign':
|
||||||
|
if (isCurrentValidator && lastTransactionBlockNumber > rangeStart) {
|
||||||
|
logger.debug('Consumed force sign event')
|
||||||
|
lastTransactionBlockNumber = 0
|
||||||
|
redisTx.set('lastTransactionBlockNumber', 0)
|
||||||
|
await sendStartSign()
|
||||||
|
}
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
logger.warn('Unknown event %o', event)
|
logger.warn('Unknown event %o', event)
|
||||||
}
|
}
|
||||||
|
@ -320,10 +350,10 @@ async function loop() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((curBlockNumber + 1 - epochStart) % rangeSize === 0) {
|
if (rangeOffset === 0) {
|
||||||
logger.info('Reached end of the current block range')
|
logger.info('Reached end of the current block range')
|
||||||
|
|
||||||
if (lastTransactionBlockNumber > curBlockNumber - rangeSize) {
|
if (isCurrentValidator && lastTransactionBlockNumber > curBlockNumber - rangeSize) {
|
||||||
logger.info('Sending message to start signature generation for the ended range')
|
logger.info('Sending message to start signature generation for the ended range')
|
||||||
await sendStartSign()
|
await sendStartSign()
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,21 @@ const bridgeAbi = [
|
||||||
'function getNextThreshold() view returns (uint)',
|
'function getNextThreshold() view returns (uint)',
|
||||||
'function getValidators() view returns (address[])',
|
'function getValidators() view returns (address[])',
|
||||||
'function getNextValidators() view returns (address[])',
|
'function getNextValidators() view returns (address[])',
|
||||||
|
'function getCloseEpoch() view returns (bool)',
|
||||||
|
'function getNextCloseEpoch() view returns (bool)',
|
||||||
'function status() view returns (uint)',
|
'function status() view returns (uint)',
|
||||||
'function votesCount(bytes32) view returns (uint)',
|
'function votesCount(bytes32) view returns (uint)',
|
||||||
'function getNextPartyId(address a) view returns (uint)',
|
'function getNextPartyId(address a) view returns (uint)',
|
||||||
'function confirmKeygen(uint x, uint y)',
|
'function confirmKeygen(uint x, uint y)',
|
||||||
'function confirmFundsTransfer()',
|
'function confirmFundsTransfer()',
|
||||||
|
'function confirmCloseEpoch()',
|
||||||
'function startVoting()',
|
'function startVoting()',
|
||||||
'function voteStartKeygen()',
|
'function voteStartKeygen()',
|
||||||
'function voteCancelKeygen()',
|
'function voteCancelKeygen()',
|
||||||
'function voteAddValidator(address validator)',
|
'function voteAddValidator(address validator)',
|
||||||
'function voteRemoveValidator(address validator)',
|
'function voteRemoveValidator(address validator)',
|
||||||
'function voteChangeThreshold(uint threshold)',
|
'function voteChangeThreshold(uint threshold)',
|
||||||
|
'function voteChangeCloseEpoch(bool closeEpoch)',
|
||||||
'function transfer(bytes32 hash, address to, uint value)'
|
'function transfer(bytes32 hash, address to, uint value)'
|
||||||
]
|
]
|
||||||
const sharedDbAbi = [
|
const sharedDbAbi = [
|
||||||
|
|
|
@ -189,6 +189,14 @@ async function confirmFundsTransfer(req, res) {
|
||||||
logger.debug('Confirm funds transfer end')
|
logger.debug('Confirm funds transfer end')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function confirmCloseEpoch(req, res) {
|
||||||
|
logger.debug('Confirm close epoch call')
|
||||||
|
const query = bridge.interface.functions.confirmCloseEpoch.encode([])
|
||||||
|
await homeSendQuery(query)
|
||||||
|
res.send()
|
||||||
|
logger.debug('Confirm close epoch end')
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -257,6 +265,14 @@ async function voteChangeThreshold(req, res) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function voteChangeCloseEpoch(req, res) {
|
||||||
|
if (req.params.closeEpoch === 'true' || req.params.closeEpoch === 'false') {
|
||||||
|
logger.info('Voting for changing close epoch')
|
||||||
|
const query = bridge.interface.functions.voteChangeCloseEpoch.encode([req.params.closeEpoch === 'true'])
|
||||||
|
await sendVote(query, req, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function voteRemoveValidator(req, res) {
|
async function voteRemoveValidator(req, res) {
|
||||||
if (ethers.utils.isHexString(req.params.validator, 20)) {
|
if (ethers.utils.isHexString(req.params.validator, 20)) {
|
||||||
logger.info('Voting for removing validator')
|
logger.info('Voting for removing validator')
|
||||||
|
@ -298,14 +314,17 @@ async function info(req, res) {
|
||||||
logger.debug('Info start')
|
logger.debug('Info start')
|
||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
x, y, epoch, rangeSize, nextRangeSize, epochStartBlock, foreignNonce, nextEpoch,
|
x, y, epoch, rangeSize, nextRangeSize, closeEpoch, nextCloseEpoch, epochStartBlock,
|
||||||
threshold, nextThreshold, validators, nextValidators, status, homeBalance
|
foreignNonce, nextEpoch, threshold, nextThreshold, validators, nextValidators, status,
|
||||||
|
homeBalance
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
bridge.getX().then((value) => new BN(value).toString(16)),
|
bridge.getX().then((value) => new BN(value).toString(16)),
|
||||||
bridge.getY().then((value) => new BN(value).toString(16)),
|
bridge.getY().then((value) => new BN(value).toString(16)),
|
||||||
bridge.epoch().then(boundX),
|
bridge.epoch().then(boundX),
|
||||||
bridge.getRangeSize().then(boundX),
|
bridge.getRangeSize().then(boundX),
|
||||||
bridge.getNextRangeSize().then(boundX),
|
bridge.getNextRangeSize().then(boundX),
|
||||||
|
bridge.getCloseEpoch(),
|
||||||
|
bridge.getNextCloseEpoch(),
|
||||||
bridge.getStartBlock().then(boundX),
|
bridge.getStartBlock().then(boundX),
|
||||||
bridge.getNonce().then(boundX),
|
bridge.getNonce().then(boundX),
|
||||||
bridge.nextEpoch().then(boundX),
|
bridge.nextEpoch().then(boundX),
|
||||||
|
@ -338,6 +357,8 @@ async function info(req, res) {
|
||||||
nextEpoch,
|
nextEpoch,
|
||||||
threshold,
|
threshold,
|
||||||
nextThreshold,
|
nextThreshold,
|
||||||
|
closeEpoch,
|
||||||
|
nextCloseEpoch,
|
||||||
homeBridgeAddress: HOME_BRIDGE_ADDRESS,
|
homeBridgeAddress: HOME_BRIDGE_ADDRESS,
|
||||||
foreignBridgeAddress: foreignAddress,
|
foreignBridgeAddress: foreignAddress,
|
||||||
foreignNonce,
|
foreignNonce,
|
||||||
|
@ -371,6 +392,7 @@ app.post('/signupsign', signupSign)
|
||||||
|
|
||||||
app.post('/confirmKeygen', confirmKeygen)
|
app.post('/confirmKeygen', confirmKeygen)
|
||||||
app.post('/confirmFundsTransfer', confirmFundsTransfer)
|
app.post('/confirmFundsTransfer', confirmFundsTransfer)
|
||||||
|
app.post('/confirmCloseEpoch', confirmCloseEpoch)
|
||||||
app.post('/transfer', transfer)
|
app.post('/transfer', transfer)
|
||||||
|
|
||||||
votesProxyApp.get('/vote/startVoting', voteStartVoting)
|
votesProxyApp.get('/vote/startVoting', voteStartVoting)
|
||||||
|
@ -379,6 +401,7 @@ votesProxyApp.get('/vote/cancelKeygen', voteCancelKeygen)
|
||||||
votesProxyApp.get('/vote/addValidator/:validator', voteAddValidator)
|
votesProxyApp.get('/vote/addValidator/:validator', voteAddValidator)
|
||||||
votesProxyApp.get('/vote/removeValidator/:validator', voteRemoveValidator)
|
votesProxyApp.get('/vote/removeValidator/:validator', voteRemoveValidator)
|
||||||
votesProxyApp.get('/vote/changeThreshold/:threshold', voteChangeThreshold)
|
votesProxyApp.get('/vote/changeThreshold/:threshold', voteChangeThreshold)
|
||||||
|
votesProxyApp.get('/vote/changeCloseEpoch/:closeEpoch', voteChangeCloseEpoch)
|
||||||
votesProxyApp.get('/info', info)
|
votesProxyApp.get('/info', info)
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
|
@ -11,10 +11,12 @@ function decodeStatus(status) {
|
||||||
case 0:
|
case 0:
|
||||||
return 'ready'
|
return 'ready'
|
||||||
case 1:
|
case 1:
|
||||||
return 'voting'
|
return 'closing_epoch'
|
||||||
case 2:
|
case 2:
|
||||||
return 'keygen'
|
return 'voting'
|
||||||
case 3:
|
case 3:
|
||||||
|
return 'keygen'
|
||||||
|
case 4:
|
||||||
return 'funds_transfer'
|
return 'funds_transfer'
|
||||||
default:
|
default:
|
||||||
return 'unknown_state'
|
return 'unknown_state'
|
||||||
|
|
|
@ -21,6 +21,7 @@ const SIGN_NONCE_CHECK_INTERVAL = parseInt(process.env.SIGN_NONCE_CHECK_INTERVAL
|
||||||
const SEND_TIMEOUT = parseInt(process.env.SEND_TIMEOUT, 10)
|
const SEND_TIMEOUT = parseInt(process.env.SEND_TIMEOUT, 10)
|
||||||
|
|
||||||
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
const httpClient = axios.create({ baseURL: FOREIGN_URL })
|
||||||
|
const proxyClient = axios.create({ baseURL: PROXY_URL })
|
||||||
|
|
||||||
const SIGN_OK = 0
|
const SIGN_OK = 0
|
||||||
const SIGN_NONCE_INTERRUPT = 1
|
const SIGN_NONCE_INTERRUPT = 1
|
||||||
|
@ -67,8 +68,12 @@ function restart(req, res) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmFundsTransfer() {
|
async function confirmFundsTransfer() {
|
||||||
exec.execSync(`curl -X POST -H "Content-Type: application/json" "${PROXY_URL}/confirmFundsTransfer"`, { stdio: 'pipe' })
|
await proxyClient.post('/confirmFundsTransfer')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmCloseEpoch() {
|
||||||
|
await proxyClient.post('/confirmCloseEpoch')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccountFromFile(file) {
|
function getAccountFromFile(file) {
|
||||||
|
@ -216,10 +221,10 @@ async function main() {
|
||||||
|
|
||||||
logger.info('Consumed sign event: %o', data)
|
logger.info('Consumed sign event: %o', data)
|
||||||
const {
|
const {
|
||||||
nonce, epoch, newEpoch, parties, threshold
|
nonce, epoch, newEpoch, parties, threshold, closeEpoch
|
||||||
} = data
|
} = data
|
||||||
|
|
||||||
const keysFile = `/keys/keys${epoch}.store`
|
const keysFile = `/keys/keys${epoch || closeEpoch}.store`
|
||||||
const { address: from, publicKey } = getAccountFromFile(keysFile)
|
const { address: from, publicKey } = getAccountFromFile(keysFile)
|
||||||
if (from === '') {
|
if (from === '') {
|
||||||
logger.info('No keys found, acking message')
|
logger.info('No keys found, acking message')
|
||||||
|
@ -236,7 +241,34 @@ async function main() {
|
||||||
|
|
||||||
attempt = 1
|
attempt = 1
|
||||||
|
|
||||||
if (!newEpoch) {
|
if (closeEpoch) {
|
||||||
|
while (true) {
|
||||||
|
logger.info(`Building corresponding account flags transaction, nonce ${nonce}`)
|
||||||
|
|
||||||
|
const tx = new Transaction({
|
||||||
|
from,
|
||||||
|
accountNumber: account.account_number,
|
||||||
|
sequence: nonce,
|
||||||
|
flags: 0x01,
|
||||||
|
memo: `Attempt ${attempt}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const hash = sha256(tx.getSignBytes())
|
||||||
|
logger.info(`Starting signature generation for transaction hash ${hash}`)
|
||||||
|
const signResult = await sign(keysFile, hash, tx, publicKey, from)
|
||||||
|
|
||||||
|
if (signResult === SIGN_OK || signResult === SIGN_NONCE_INTERRUPT) {
|
||||||
|
await confirmCloseEpoch()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// signer either failed, or timed out after parties signup
|
||||||
|
attempt = nextAttempt || attempt + 1
|
||||||
|
nextAttempt = null
|
||||||
|
logger.warn(`Sign failed, starting next attempt ${attempt}`)
|
||||||
|
await delay(1000)
|
||||||
|
}
|
||||||
|
} else if (!newEpoch) {
|
||||||
const exchanges = await getExchangeMessages(nonce)
|
const exchanges = await getExchangeMessages(nonce)
|
||||||
const exchangesData = exchanges.map((exchangeMsg) => JSON.parse(exchangeMsg.content))
|
const exchangesData = exchanges.map((exchangeMsg) => JSON.parse(exchangeMsg.content))
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,22 @@ const BNB_ASSET = 'BNB'
|
||||||
class Transaction {
|
class Transaction {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
const {
|
const {
|
||||||
from, accountNumber, sequence, recipients, asset, memo = ''
|
from, accountNumber, sequence, recipients, asset, memo = '', flags
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
|
let msg
|
||||||
|
if (flags) {
|
||||||
|
msg = {
|
||||||
|
from: crypto.decodeAddress(from),
|
||||||
|
flags,
|
||||||
|
msgType: 'NewOrderMsg' // until 'SetAccountFlagsMsg' is not available
|
||||||
|
}
|
||||||
|
|
||||||
|
this.signMsg = {
|
||||||
|
flags,
|
||||||
|
from
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const totalTokens = recipients.reduce(
|
const totalTokens = recipients.reduce(
|
||||||
(sum, { tokens }) => sum.plus(new BN(tokens || 0)), new BN(0)
|
(sum, { tokens }) => sum.plus(new BN(tokens || 0)), new BN(0)
|
||||||
)
|
)
|
||||||
|
@ -61,7 +74,7 @@ class Transaction {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const msg = {
|
msg = {
|
||||||
inputs: inputs.map((x) => ({
|
inputs: inputs.map((x) => ({
|
||||||
...x,
|
...x,
|
||||||
address: crypto.decodeAddress(x.address)
|
address: crypto.decodeAddress(x.address)
|
||||||
|
@ -77,6 +90,7 @@ class Transaction {
|
||||||
inputs,
|
inputs,
|
||||||
outputs
|
outputs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.tx = new TransactionBnc({
|
this.tx = new TransactionBnc({
|
||||||
account_number: accountNumber,
|
account_number: accountNumber,
|
||||||
|
@ -109,6 +123,7 @@ class Transaction {
|
||||||
sequence: this.tx.sequence
|
sequence: this.tx.sequence
|
||||||
}]
|
}]
|
||||||
return this.tx.serialize()
|
return this.tx.serialize()
|
||||||
|
.replace(/ce6dc043/, 'bea6e301') // until 'SetAccountFlagsMsg' is not available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue