Compare commits

...

4 Commits

Author SHA1 Message Date
Alexander Kolotov
0451d6e373 Merge the develop branch to the master branch, preparation to v2.6.0 2021-01-11 20:17:53 -06:00
Kirill Fedoseev
409044b8a5 Add env variables for selective validator balance checks (#507) 2021-01-10 20:18:06 -06:00
Kirill Fedoseev
5fc52f42d7 ALM: fix blocking Blockscout/Etherscan requests (#505) 2021-01-07 13:35:23 -06:00
Alexander Kolotov
8a0d9f38b0 Added mediator endpoint in the monitor web service (#503) 2020-12-24 19:15:51 +03:00
10 changed files with 263 additions and 191 deletions

View File

@@ -82,3 +82,5 @@ MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for ot
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST | File with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests should be automatically processed by the CollectedSignatures watcher. | string MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST | File with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests should be automatically processed by the CollectedSignatures watcher. | string
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by newlines. If set, determines the set of accounts whose requests should be marked as unclaimed. Has a lower priority than the `MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST`. | string MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by newlines. If set, determines the set of accounts whose requests should be marked as unclaimed. Has a lower priority than the `MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST`. | string
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false` MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE | If set, defines the list of home validator addresses for which balance should be checked. | `string`
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE | If set, defines the list of foreign validator addresses for which balance should be checked. | `string`

View File

@@ -58,7 +58,7 @@ export const ManualExecutionButton = ({
return return
} }
if (!library || !foreign.bridgeContract) return if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return
const signatures = packSignatures(signatureCollected.map(signatureToVRS)) const signatures = packSignatures(signatureCollected.map(signatureToVRS))
const data = foreign.bridgeContract.methods.executeSignatures(messageData, signatures).encodeABI() const data = foreign.bridgeContract.methods.executeSignatures(messageData, signatures).encodeABI()

View File

@@ -42,7 +42,6 @@ export interface BasicConfirmationParam {
export interface ConfirmationParam extends BasicConfirmationParam { export interface ConfirmationParam extends BasicConfirmationParam {
txHash: string txHash: string
timestamp: number timestamp: number
signature?: string
} }
export interface ExecutionData { export interface ExecutionData {

View File

@@ -110,8 +110,9 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual([signature, signature]) expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -121,14 +122,16 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
expect(res1).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res2).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
@@ -186,7 +189,7 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(1)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
@@ -251,11 +254,12 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(subscriptions.length).toEqual(0) expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual([signature, signature]) expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -265,14 +269,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res3).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
@@ -339,11 +353,12 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(subscriptions.length).toEqual(0) expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual([signature, signature]) expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -353,7 +368,27 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
@@ -361,7 +396,7 @@ describe('getConfirmationsForTx', () => {
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res4).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
@@ -430,7 +465,7 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
@@ -444,14 +479,32 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res3).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res4).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
@@ -517,7 +570,7 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(subscriptions.length).toEqual(0) expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
@@ -531,14 +584,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res3).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
@@ -627,7 +690,7 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1) expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
@@ -641,14 +704,32 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setResult.mock.calls[0][0]()).toEqual( const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res3).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(res4).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
@@ -677,12 +758,13 @@ describe('getConfirmationsForTx', () => {
unsubscribe() unsubscribe()
expect(setResult).toBeCalledTimes(4) expect(setResult).toBeCalledTimes(7)
expect(getValidatorConfirmation).toBeCalledTimes(2) expect(getValidatorConfirmation).toBeCalledTimes(2)
expect(getSuccessExecutionTransaction).toBeCalledTimes(2) expect(getSuccessExecutionTransaction).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(2) expect(setSignatureCollected).toBeCalledTimes(3)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false) expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature]) expect(setSignatureCollected.mock.calls[1][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[2][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(2) expect(getValidatorFailedTransaction).toBeCalledTimes(2)
expect(setFailedConfirmations).toBeCalledTimes(2) expect(setFailedConfirmations).toBeCalledTimes(2)
@@ -694,14 +776,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true) expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setPendingConfirmations.mock.calls[1][0]).toEqual(false) expect(setPendingConfirmations.mock.calls[1][0]).toEqual(false)
expect(setResult.mock.calls[2][0]()).toEqual( const res5 = setResult.mock.calls[4][0](res4)
const res6 = setResult.mock.calls[5][0](res5)
const res7 = setResult.mock.calls[6][0](res6)
expect(res5).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
]) ])
) )
expect(setResult.mock.calls[3][0]).toEqual( expect(res6).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
])
)
expect(res7).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },

View File

@@ -14,7 +14,23 @@ import {
getValidatorPendingTransaction, getValidatorPendingTransaction,
getSuccessExecutionTransaction getSuccessExecutionTransaction
} from './validatorConfirmationHelpers' } from './validatorConfirmationHelpers'
import { ConfirmationParam } from '../hooks/useMessageConfirmations' import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
const mergeConfirmations = (oldConfirmations: BasicConfirmationParam[], newConfirmations: BasicConfirmationParam[]) => {
const confirmations = [...oldConfirmations]
newConfirmations.forEach(validatorData => {
const index = confirmations.findIndex(e => e.validator === validatorData.validator)
const currentStatus = confirmations[index].status
const newStatus = validatorData.status
if (
(validatorData as ConfirmationParam).txHash ||
(newStatus !== currentStatus && newStatus !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED)
) {
confirmations[index] = validatorData
}
})
return confirmations
}
export const getConfirmationsForTx = async ( export const getConfirmationsForTx = async (
messageData: string, messageData: string,
@@ -38,48 +54,45 @@ export const getConfirmationsForTx = async (
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
// If all the information was not collected, then it should retry
let shouldRetry = false
const hashMsg = web3.utils.soliditySha3Raw(messageData) const hashMsg = web3.utils.soliditySha3Raw(messageData)
let validatorConfirmations = await Promise.all( let validatorConfirmations = await Promise.all(
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod)) validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod))
) )
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) const updateConfirmations = (confirmations: BasicConfirmationParam[]) => {
if (confirmations.length === 0) {
setResult((prevConfirmations: ConfirmationParam[]) => { return
if (prevConfirmations && prevConfirmations.length) {
successConfirmations.forEach(validatorData => {
const index = prevConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
return prevConfirmations
} else {
return validatorConfirmations
} }
}) validatorConfirmations = mergeConfirmations(validatorConfirmations, confirmations)
setResult((currentConfirmations: BasicConfirmationParam[]) => {
if (currentConfirmations && currentConfirmations.length) {
return mergeConfirmations(currentConfirmations, confirmations)
}
return confirmations
})
}
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS) const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const hasEnoughSignatures = successConfirmations.length === requiredSignatures
updateConfirmations(validatorConfirmations)
setSignatureCollected(hasEnoughSignatures)
// If signatures not collected, look for pending transactions // If signatures not collected, look for pending transactions
let pendingConfirmationsResult = false if (!hasEnoughSignatures) {
if (successConfirmations.length !== requiredSignatures) {
// Check if confirmation is pending // Check if confirmation is pending
const validatorPendingConfirmationsChecks = await Promise.all( const validatorPendingConfirmationsChecks = await Promise.all(
notSuccessConfirmations.map(getValidatorPendingTransaction(bridgeContract, messageData, getPendingTransactions)) notSuccessConfirmations.map(getValidatorPendingTransaction(bridgeContract, messageData, getPendingTransactions))
) )
const validatorPendingConfirmations = validatorPendingConfirmationsChecks.filter( const validatorPendingConfirmations = validatorPendingConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING c => c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
) )
updateConfirmations(validatorPendingConfirmations)
validatorPendingConfirmations.forEach(validatorData => { setPendingConfirmations(validatorPendingConfirmations.length > 0)
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator) } else {
validatorConfirmations[index] = validatorData setPendingConfirmations(false)
})
if (validatorPendingConfirmations.length > 0) {
pendingConfirmationsResult = true
}
} }
const undefinedConfirmations = validatorConfirmations.filter( const undefinedConfirmations = validatorConfirmations.filter(
@@ -87,7 +100,6 @@ export const getConfirmationsForTx = async (
) )
// Check if confirmation failed // Check if confirmation failed
let failedConfirmationsResult = false
const validatorFailedConfirmationsChecks = await Promise.all( const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map( undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions) getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
@@ -96,76 +108,47 @@ export const getConfirmationsForTx = async (
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter( const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
) )
validatorFailedConfirmations.forEach(validatorData => { setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures)
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator) updateConfirmations(validatorFailedConfirmations)
validatorConfirmations[index] = validatorData
})
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
if (messageConfirmationsFailed) {
failedConfirmationsResult = true
}
const missingConfirmations = validatorConfirmations.filter( const missingConfirmations = validatorConfirmations.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
) )
if (successConfirmations.length !== requiredSignatures && missingConfirmations.length > 0) { if (hasEnoughSignatures) {
shouldRetry = true
}
let signatureCollectedResult: boolean | string[] = false
if (successConfirmations.length === requiredSignatures) {
// If signatures collected, it should set other signatures not found as not required // If signatures collected, it should set other signatures not found as not required
const notRequiredConfirmations = missingConfirmations.map(c => ({ const notRequiredConfirmations = missingConfirmations.map(c => ({
validator: c.validator, validator: c.validator,
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
})) }))
updateConfirmations(notRequiredConfirmations)
notRequiredConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
signatureCollectedResult = true
if (fromHome) { if (fromHome) {
signatureCollectedResult = await Promise.all( // fetch collected signatures for possible manual processing
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call()) setSignatureCollected(
await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
)
) )
} }
} }
// get transactions from success signatures // get transactions from success signatures
const successConfirmationWithData = await Promise.all( const successConfirmationWithData = await Promise.all(
validatorConfirmations successConfirmations.map(
.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, timestamp, getSuccessTransactions)
.map( )
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, timestamp, getSuccessTransactions)
)
) )
const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '') const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '')
updateConfirmations(successConfirmationWithTxFound)
const updatedValidatorConfirmations = [...validatorConfirmations] // retry if not all signatures are collected and some confirmations are still missing
// or some success transactions were not fetched successfully
if (successConfirmationWithTxFound.length > 0) { if (
successConfirmationWithTxFound.forEach(validatorData => { (!hasEnoughSignatures && missingConfirmations.length > 0) ||
const index = updatedValidatorConfirmations.findIndex(e => e.validator === validatorData.validator) successConfirmationWithTxFound.length < successConfirmationWithData.length
updatedValidatorConfirmations[index] = validatorData ) {
})
}
// Set results
setResult(updatedValidatorConfirmations)
setFailedConfirmations(failedConfirmationsResult)
setPendingConfirmations(pendingConfirmationsResult)
setSignatureCollected(signatureCollectedResult)
// Retry if not all transaction were found for validator confirmations
if (successConfirmationWithTxFound.length < successConfirmationWithData.length) {
shouldRetry = true
}
if (shouldRetry) {
const timeoutId = setTimeout( const timeoutId = setTimeout(
() => () =>
getConfirmationsForTx( getConfirmationsForTx(

View File

@@ -25,3 +25,6 @@ MONITOR_CACHE_EVENTS=true
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST= MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST=
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST= MONITOR_HOME_TO_FOREIGN_BLOCK_LIST=
# MONITOR_HOME_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...
# MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...

View File

@@ -12,10 +12,6 @@ const { web3Home } = require('./utils/web3')
const { COMMON_HOME_BRIDGE_ADDRESS, MONITOR_BRIDGE_NAME } = process.env const { COMMON_HOME_BRIDGE_ADDRESS, MONITOR_BRIDGE_NAME } = process.env
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
const { HOME_ERC_TO_ERC_ABI } = require('../commons') const { HOME_ERC_TO_ERC_ABI } = require('../commons')
async function checkWorker() { async function checkWorker() {
@@ -45,27 +41,6 @@ async function checkWorker() {
const vBalances = await validators(bridgeMode) const vBalances = await validators(bridgeMode)
if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances)) if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances))
vBalances.homeOk = true
vBalances.foreignOk = true
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
for (const hv in vBalances.home.validators) {
if (vBalances.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
vBalances.homeOk = false
break
}
}
}
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
for (const hv in vBalances.foreign.validators) {
if (vBalances.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
vBalances.foreignOk = false
break
}
}
}
vBalances.ok = vBalances.homeOk && vBalances.foreignOk vBalances.ok = vBalances.homeOk && vBalances.foreignOk
vBalances.health = true vBalances.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances) writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)

View File

@@ -141,7 +141,8 @@ async function main(mode) {
permanentMediators, permanentMediators,
floatingMediators, floatingMediators,
remotelyControlledMediators, remotelyControlledMediators,
unknown unknown,
lastChecked: Math.floor(Date.now() / 1000)
} }
} }
module.exports = main module.exports = main

View File

@@ -50,6 +50,16 @@ bridgeRouter.get('/alerts', async (req, res, next) => {
} }
}) })
bridgeRouter.get('/mediators', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/mediators.json`)
res.json(results)
} catch (e) {
// this will eventually be handled by your error handling middleware
next(e)
}
})
bridgeRouter.get('/stuckTransfers', async (req, res, next) => { bridgeRouter.get('/stuckTransfers', async (req, res, next) => {
try { try {
const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`) const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`)

View File

@@ -16,10 +16,13 @@ const {
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE, COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
COMMON_FOREIGN_GAS_PRICE_FALLBACK, COMMON_FOREIGN_GAS_PRICE_FALLBACK,
COMMON_FOREIGN_GAS_PRICE_FACTOR COMMON_FOREIGN_GAS_PRICE_FACTOR,
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE,
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE
} = process.env } = process.env
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0 const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0 const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
const homeGasPriceSupplierOpts = { const homeGasPriceSupplierOpts = {
speedType: COMMON_HOME_GAS_PRICE_SPEED_TYPE, speedType: COMMON_HOME_GAS_PRICE_SPEED_TYPE,
@@ -33,12 +36,6 @@ const foreignGasPriceSupplierOpts = {
logger logger
} }
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
async function main(bridgeMode) { async function main(bridgeMode) {
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode) const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS)
@@ -109,53 +106,61 @@ async function main(bridgeMode) {
} }
let validatorsMatch = true let validatorsMatch = true
logger.debug('calling asyncForEach foreignValidators foreignVBalances') const foreignValidatorsWithBalanceCheck =
await asyncForEach(foreignValidators, async v => { typeof MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE === 'string'
const balance = await web3Foreign.eth.getBalance(v) ? MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE.split(' ')
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) { : foreignValidators
const leftTx = Web3Utils.toBN(balance) logger.debug('getting foreignValidators balances')
.div(foreignTxCost) await Promise.all(
.toString(10) foreignValidators.map(async v => {
foreignVBalances[v] = { foreignVBalances[v] = {}
balance: Web3Utils.fromWei(balance), if (foreignValidatorsWithBalanceCheck.includes(v)) {
leftTx: Number(leftTx), const balance = await web3Foreign.eth.getBalance(v)
gasPrice: Number(foreignGasPriceGwei) foreignVBalances[v].balance = Web3Utils.fromWei(balance)
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
foreignVBalances[v].leftTx = Number(
Web3Utils.toBN(balance)
.div(foreignTxCost)
.toString(10)
)
foreignVBalances[v].gasPrice = parseFloat(foreignGasPriceGwei)
}
} }
} else {
foreignVBalances[v] = {
balance: Web3Utils.fromWei(balance)
}
}
if (!homeValidators.includes(v)) { if (!homeValidators.includes(v)) {
validatorsMatch = false validatorsMatch = false
foreignVBalances[v].onlyOnForeign = true foreignVBalances[v].onlyOnForeign = true
}
})
logger.debug('calling asyncForEach homeValidators homeVBalances')
await asyncForEach(homeValidators, async v => {
const balance = await web3Home.eth.getBalance(v)
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
const leftTx = Web3Utils.toBN(balance)
.div(homeTxCost)
.toString(10)
homeVBalances[v] = {
balance: Web3Utils.fromWei(balance),
leftTx: Number(leftTx),
gasPrice: Number(homeGasPriceGwei)
} }
} else { })
homeVBalances[v] = { )
balance: Web3Utils.fromWei(balance)
}
}
if (!foreignValidators.includes(v)) { const homeValidatorsWithBalanceCheck =
validatorsMatch = false typeof MONITOR_HOME_VALIDATORS_BALANCE_ENABLE === 'string'
homeVBalances[v].onlyOnHome = true ? MONITOR_HOME_VALIDATORS_BALANCE_ENABLE.split(' ')
} : homeValidators
}) logger.debug('calling homeValidators balances')
await Promise.all(
homeValidators.map(async v => {
homeVBalances[v] = {}
if (homeValidatorsWithBalanceCheck.includes(v)) {
const balance = await web3Home.eth.getBalance(v)
homeVBalances[v].balance = Web3Utils.fromWei(balance)
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
homeVBalances[v].leftTx = Number(
Web3Utils.toBN(balance)
.div(homeTxCost)
.toString(10)
)
homeVBalances[v].gasPrice = parseFloat(homeGasPriceGwei)
}
}
if (!foreignValidators.includes(v)) {
validatorsMatch = false
homeVBalances[v].onlyOnHome = true
}
})
)
logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()') logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()')
const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call() const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call()
@@ -164,20 +169,22 @@ async function main(bridgeMode) {
logger.debug('Done') logger.debug('Done')
return { return {
home: { home: {
validators: { validators: homeVBalances,
...homeVBalances
},
requiredSignatures: Number(reqSigHome) requiredSignatures: Number(reqSigHome)
}, },
foreign: { foreign: {
validators: { validators: foreignVBalances,
...foreignVBalances
},
requiredSignatures: Number(reqSigForeign) requiredSignatures: Number(reqSigForeign)
}, },
requiredSignaturesMatch: reqSigHome === reqSigForeign, requiredSignaturesMatch: reqSigHome === reqSigForeign,
validatorsMatch, validatorsMatch,
lastChecked: Math.floor(Date.now() / 1000) lastChecked: Math.floor(Date.now() / 1000),
homeOk: Object.values(homeVBalances)
.filter(vb => typeof vb.leftTx === 'number')
.every(vb => vb.leftTx >= MONITOR_TX_NUMBER_THRESHOLD),
foreignOk: Object.values(foreignVBalances)
.filter(vb => typeof vb.leftTx === 'number')
.every(vb => vb.leftTx >= MONITOR_TX_NUMBER_THRESHOLD)
} }
} }