Update ALM logic to continue displaying failed signatures from validators (#399)

This commit is contained in:
Gerardo Nardelli 2020-07-17 17:59:25 -03:00 committed by GitHub
parent efc433e9e0
commit dc060387bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 141 additions and 41 deletions

@ -313,7 +313,7 @@ export const useMessageConfirmations = ({
// Sets the message status based in the collected information // Sets the message status based in the collected information
useEffect( useEffect(
() => { () => {
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) { if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS && existsConfirmation(confirmations)) {
const newStatus = executionData.executionResult const newStatus = executionData.executionResult
? CONFIRMATIONS_STATUS.SUCCESS ? CONFIRMATIONS_STATUS.SUCCESS
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED : CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED

@ -33,7 +33,7 @@ export class BlockNumberProvider {
} }
stop() { stop() {
this.running = this.running - 1 this.running = this.running > 0 ? this.running - 1 : 0
if (!this.running) { if (!this.running) {
clearTimeout(this.ref) clearTimeout(this.ref)

@ -105,13 +105,13 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorSuccessTransaction).toBeCalledTimes(1) expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
expect(getValidatorFailedTransaction).toBeCalledTimes(0) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(0) expect(setFailedConfirmations).toBeCalledTimes(0)
expect(getValidatorPendingTransaction).toBeCalledTimes(0) expect(getValidatorPendingTransaction).toBeCalledTimes(0)
expect(setPendingConfirmations).toBeCalledTimes(0) expect(setPendingConfirmations).toBeCalledTimes(0)
expect(setResult.mock.calls[0][0]).toEqual( expect(setResult.mock.calls[0][0]()).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 },
@ -243,13 +243,13 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorSuccessTransaction).toBeCalledTimes(1) expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1) expect(setSignatureCollected).toBeCalledTimes(1)
expect(getValidatorFailedTransaction).toBeCalledTimes(0) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(0) expect(setFailedConfirmations).toBeCalledTimes(0)
expect(getValidatorPendingTransaction).toBeCalledTimes(0) expect(getValidatorPendingTransaction).toBeCalledTimes(0)
expect(setPendingConfirmations).toBeCalledTimes(0) expect(setPendingConfirmations).toBeCalledTimes(0)
expect(setResult.mock.calls[0][0]).toEqual( expect(setResult.mock.calls[0][0]()).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 },
@ -264,6 +264,93 @@ describe('getConfirmationsForTx', () => {
]) ])
) )
}) })
test('should set validator confirmations status, validator transactions, keep failed found transaction and not retry', async () => {
const validator4 = '0x9d2dC11C342F4eF3C5491A048D0f0eBCd2D8f7C3'
const validatorList = [validator1, validator2, validator3, validator4]
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
validator,
status:
validator !== validator3 && validator !== validator4
? VALIDATOR_CONFIRMATION_STATUS.SUCCESS
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? '0x123' : '',
timestamp: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? 123 : 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator3
? VALIDATOR_CONFIRMATION_STATUS.FAILED
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator3 ? '0x123' : '',
timestamp: validatorData.validator === validator3 ? 123 : 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
timestamp: 0
}))
const setResult = jest.fn()
const setSignatureCollected = jest.fn()
const setFailedConfirmations = jest.fn()
const setPendingConfirmations = jest.fn()
await getConfirmationsForTx(
messageData,
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
setResult,
requiredSignatures,
setSignatureCollected,
waitingBlocksResolved,
subscriptions,
timestamp,
getFailedTransactions,
setFailedConfirmations,
getPendingTransactions,
setPendingConfirmations,
getSuccessTransactions
)
unsubscribe()
expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(0)
expect(getValidatorPendingTransaction).toBeCalledTimes(0)
expect(setPendingConfirmations).toBeCalledTimes(0)
expect(setResult.mock.calls[0][0]()).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(setResult.mock.calls[1][0]).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
])
)
})
test('should look for failed and pending transactions for not confirmed validators', async () => { test('should look for failed and pending transactions for not confirmed validators', async () => {
// Validator1 success // Validator1 success
// Validator2 failed // Validator2 failed
@ -335,7 +422,7 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorPendingTransaction).toBeCalledTimes(1) expect(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setResult.mock.calls[0][0]).toEqual( expect(setResult.mock.calls[0][0]()).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 },
@ -419,7 +506,7 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorPendingTransaction).toBeCalledTimes(1) expect(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(0) expect(setPendingConfirmations).toBeCalledTimes(0)
expect(setResult.mock.calls[0][0]).toEqual( expect(setResult.mock.calls[0][0]()).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 },

@ -13,6 +13,7 @@ import {
getValidatorPendingTransaction, getValidatorPendingTransaction,
getValidatorSuccessTransaction getValidatorSuccessTransaction
} from './validatorConfirmationHelpers' } from './validatorConfirmationHelpers'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
export const getConfirmationsForTx = async ( export const getConfirmationsForTx = async (
messageData: string, messageData: string,
@ -43,9 +44,21 @@ export const getConfirmationsForTx = async (
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
setResult((prevConfirmations: ConfirmationParam[]) => {
if (prevConfirmations && prevConfirmations.length) {
successConfirmations.forEach(validatorData => {
const index = prevConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
return prevConfirmations
} else {
return validatorConfirmations
}
})
const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS) const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
// If signatures not collected, it needs to retry in the next blocks // If signatures not collected, look for pending transactions
if (successConfirmations.length !== requiredSignatures) { if (successConfirmations.length !== requiredSignatures) {
// Check if confirmation is pending // Check if confirmation is pending
const validatorPendingConfirmationsChecks = await Promise.all( const validatorPendingConfirmationsChecks = await Promise.all(
@ -63,49 +76,49 @@ export const getConfirmationsForTx = async (
if (validatorPendingConfirmations.length > 0) { if (validatorPendingConfirmations.length > 0) {
setPendingConfirmations(true) setPendingConfirmations(true)
} }
}
const undefinedConfirmations = validatorConfirmations.filter( const undefinedConfirmations = validatorConfirmations.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
) )
// Check if confirmation failed
const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
)
)
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
)
validatorFailedConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
if (messageConfirmationsFailed) {
setFailedConfirmations(true)
}
const missingConfirmations = validatorConfirmations.filter( // Check if confirmation failed
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
) )
)
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
)
validatorFailedConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
if (messageConfirmationsFailed) {
setFailedConfirmations(true)
}
if (missingConfirmations.length > 0) { const missingConfirmations = validatorConfirmations.filter(
shouldRetry = true c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
} )
} else {
// If signatures collected, it should set other signatures as not required if (successConfirmations.length !== requiredSignatures && missingConfirmations.length > 0) {
const notRequiredConfirmations = notSuccessConfirmations.map(c => ({ shouldRetry = true
}
if (successConfirmations.length === requiredSignatures) {
// If signatures collected, it should set other signatures not found as not required
const notRequiredConfirmations = missingConfirmations.map(c => ({
validator: c.validator, validator: c.validator,
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
})) }))
validatorConfirmations = [...successConfirmations, ...notRequiredConfirmations] validatorConfirmations = [...validatorConfirmations, ...notRequiredConfirmations]
setSignatureCollected(true) setSignatureCollected(true)
} }
// Set confirmations to update UI and continue requesting the transactions for the signatures
setResult(validatorConfirmations)
// get transactions from success signatures // get transactions from success signatures
const successConfirmationWithData = await Promise.all( const successConfirmationWithData = await Promise.all(
validatorConfirmations validatorConfirmations