Add Alm unit tests (#388)
This commit is contained in:
parent
2edd8f2783
commit
4c44aa5fcd
@ -1,5 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
// Removed basic test from setup. Keeping this so CI passes until we add unit tests.
|
|
||||||
})
|
|
469
alm/src/utils/__tests__/contracts.test.ts
Normal file
469
alm/src/utils/__tests__/contracts.test.ts
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
import 'jest'
|
||||||
|
import { getRequiredBlockConfirmations, getRequiredSignatures, getValidatorList } from '../contract'
|
||||||
|
import { Contract } from 'web3-eth-contract'
|
||||||
|
import { SnapshotProvider } from '../../services/SnapshotProvider'
|
||||||
|
|
||||||
|
describe('getRequiredBlockConfirmations', () => {
|
||||||
|
const methodsBuilder = (value: string) => ({
|
||||||
|
requiredBlockConfirmations: () => {
|
||||||
|
return {
|
||||||
|
call: () => {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should call requiredBlockConfirmations method if no events present', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
methods: methodsBuilder('1')
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredBlockConfirmationEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredBlockConfirmations(contract, 10, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(1)
|
||||||
|
})
|
||||||
|
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||||
|
methods: methodsBuilder('3')
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredBlockConfirmationEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 8,
|
||||||
|
returnValues: {
|
||||||
|
requiredBlockConfirmations: '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredBlockConfirmations(contract, 10, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(1)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('Should call to get events if block number was not included in the snapshot', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
requiredBlockConfirmations: '2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
methods: methodsBuilder('3')
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredBlockConfirmationEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 8,
|
||||||
|
returnValues: {
|
||||||
|
requiredBlockConfirmations: '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredBlockConfirmations(contract, 10, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(2)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(1)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('RequiredBlockConfirmationChanged', {
|
||||||
|
fromBlock: 9,
|
||||||
|
toBlock: 10
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('Should use the most updated event', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
requiredBlockConfirmations: '2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 11,
|
||||||
|
returnValues: {
|
||||||
|
requiredBlockConfirmations: '3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
methods: methodsBuilder('3')
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredBlockConfirmationEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 11
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredBlockConfirmations(contract, 15, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(3)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(1)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('RequiredBlockConfirmationChanged', {
|
||||||
|
fromBlock: 12,
|
||||||
|
toBlock: 15
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getRequiredSignatures', () => {
|
||||||
|
test('Should not call to get events if block number was included in the snapshot', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => [])
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredSignaturesEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 7,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 8,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredSignatures(contract, 10, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(2)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('Should call to get events if block number is higher than the snapshot block number', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => [
|
||||||
|
{
|
||||||
|
blockNumber: 15,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredSignaturesEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 7,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 8,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredSignatures(contract, 20, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(3)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(1)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('RequiredSignaturesChanged', {
|
||||||
|
fromBlock: 11,
|
||||||
|
toBlock: 20
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('Should use the most updated event before the block number', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => [
|
||||||
|
{
|
||||||
|
blockNumber: 15,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
requiredSignaturesEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 5,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockNumber: 6,
|
||||||
|
returnValues: {
|
||||||
|
requiredSignatures: '2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const result = await getRequiredSignatures(contract, 7, snapshotProvider)
|
||||||
|
|
||||||
|
expect(result).toEqual(2)
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getValidatorList', () => {
|
||||||
|
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
|
||||||
|
const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
|
||||||
|
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
|
||||||
|
const methodsBuilder = (value: string[]) => ({
|
||||||
|
validatorList: () => {
|
||||||
|
return {
|
||||||
|
call: () => {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
test('Should return the current validator list if no events found', async () => {
|
||||||
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||||
|
methods: methodsBuilder(currentValidators)
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
validatorAddedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
validatorRemovedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const list = await getValidatorList(contract, 20, snapshotProvider)
|
||||||
|
|
||||||
|
expect(list.length).toEqual(3)
|
||||||
|
expect(list).toEqual(expect.arrayContaining(currentValidators))
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(2)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorAdded', {
|
||||||
|
fromBlock: 20
|
||||||
|
})
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorRemoved', {
|
||||||
|
fromBlock: 20
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('If validator was added later from snapshot it should not include it', async () => {
|
||||||
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||||
|
methods: methodsBuilder(currentValidators)
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
validatorAddedEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
validator: validator3
|
||||||
|
},
|
||||||
|
event: 'ValidatorAdded'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
validatorRemovedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const list = await getValidatorList(contract, 5, snapshotProvider)
|
||||||
|
|
||||||
|
expect(list.length).toEqual(2)
|
||||||
|
expect(list).toEqual(expect.arrayContaining([validator1, validator2]))
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(2)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorAdded', {
|
||||||
|
fromBlock: 11
|
||||||
|
})
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorRemoved', {
|
||||||
|
fromBlock: 11
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('If validator was added later from chain it should not include it', async () => {
|
||||||
|
const currentValidators = [validator1, validator2, validator3]
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(event => {
|
||||||
|
if (event === 'ValidatorAdded') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
validator: validator3
|
||||||
|
},
|
||||||
|
event: 'ValidatorAdded'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: methodsBuilder(currentValidators)
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
validatorAddedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
validatorRemovedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const list = await getValidatorList(contract, 15, snapshotProvider)
|
||||||
|
|
||||||
|
expect(list.length).toEqual(2)
|
||||||
|
expect(list).toEqual(expect.arrayContaining([validator1, validator2]))
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(2)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorAdded', {
|
||||||
|
fromBlock: 15
|
||||||
|
})
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorRemoved', {
|
||||||
|
fromBlock: 15
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('If validator was removed later from snapshot it should include it', async () => {
|
||||||
|
const currentValidators = [validator1, validator2]
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(() => []),
|
||||||
|
methods: methodsBuilder(currentValidators)
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
validatorAddedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
validatorRemovedEvents: () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
validator: validator3
|
||||||
|
},
|
||||||
|
event: 'ValidatorRemoved'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const list = await getValidatorList(contract, 5, snapshotProvider)
|
||||||
|
|
||||||
|
expect(list.length).toEqual(3)
|
||||||
|
expect(list).toEqual(expect.arrayContaining([validator1, validator2, validator3]))
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(2)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorAdded', {
|
||||||
|
fromBlock: 11
|
||||||
|
})
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorRemoved', {
|
||||||
|
fromBlock: 11
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('If validator was removed later from chain it should include it', async () => {
|
||||||
|
const currentValidators = [validator1, validator2]
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: jest.fn().mockImplementation(event => {
|
||||||
|
if (event === 'ValidatorRemoved') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
blockNumber: 9,
|
||||||
|
returnValues: {
|
||||||
|
validator: validator3
|
||||||
|
},
|
||||||
|
event: 'ValidatorRemoved'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: methodsBuilder(currentValidators)
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const snapshotProvider = ({
|
||||||
|
validatorAddedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
validatorRemovedEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
snapshotBlockNumber: () => {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
} as unknown) as SnapshotProvider
|
||||||
|
|
||||||
|
const list = await getValidatorList(contract, 15, snapshotProvider)
|
||||||
|
|
||||||
|
expect(list.length).toEqual(3)
|
||||||
|
expect(list).toEqual(expect.arrayContaining([validator1, validator2, validator3]))
|
||||||
|
expect(contract.getPastEvents).toBeCalledTimes(2)
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorAdded', {
|
||||||
|
fromBlock: 15
|
||||||
|
})
|
||||||
|
expect(contract.getPastEvents).toHaveBeenCalledWith('ValidatorRemoved', {
|
||||||
|
fromBlock: 15
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
155
alm/src/utils/__tests__/explorer.test.ts
Normal file
155
alm/src/utils/__tests__/explorer.test.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import 'jest'
|
||||||
|
import {
|
||||||
|
getFailedTransactions,
|
||||||
|
getSuccessTransactions,
|
||||||
|
filterValidatorSignatureTransaction,
|
||||||
|
getExecutionFailedTransactionForMessage,
|
||||||
|
APITransaction,
|
||||||
|
getValidatorPendingTransactionsForMessage,
|
||||||
|
getExecutionPendingTransactionsForMessage
|
||||||
|
} from '../explorer'
|
||||||
|
import { EXECUTE_AFFIRMATION_HASH, EXECUTE_SIGNATURES_HASH, SUBMIT_SIGNATURE_HASH } from '../../config/constants'
|
||||||
|
|
||||||
|
const messageData = '0x123456'
|
||||||
|
const OTHER_HASH = 'aabbccdd'
|
||||||
|
const bridgeAddress = '0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560'
|
||||||
|
const otherAddress = '0xD4075FB57fCf038bFc702c915Ef9592534bED5c1'
|
||||||
|
|
||||||
|
describe('getFailedTransactions', () => {
|
||||||
|
test('should only return failed transactions', async () => {
|
||||||
|
const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
|
||||||
|
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
const result = await getFailedTransactions('', '', 0, 1, '', fetchAccountTransactions)
|
||||||
|
expect(result.length).toEqual(3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getSuccessTransactions', () => {
|
||||||
|
test('should only return success transactions', async () => {
|
||||||
|
const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
|
||||||
|
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
const result = await getSuccessTransactions('', '', 0, 1, '', fetchAccountTransactions)
|
||||||
|
expect(result.length).toEqual(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('filterValidatorSignatureTransaction', () => {
|
||||||
|
test('should return submit signatures related transaction', () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${SUBMIT_SIGNATURE_HASH}112233` },
|
||||||
|
{ input: `0x${SUBMIT_SIGNATURE_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}112233` }
|
||||||
|
] as APITransaction[]
|
||||||
|
|
||||||
|
const result = filterValidatorSignatureTransaction(transactions, messageData)
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${SUBMIT_SIGNATURE_HASH}123456` })
|
||||||
|
})
|
||||||
|
test('should return execute affirmation related transaction', () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${EXECUTE_AFFIRMATION_HASH}112233` },
|
||||||
|
{ input: `0x${EXECUTE_AFFIRMATION_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}112233` }
|
||||||
|
] as APITransaction[]
|
||||||
|
|
||||||
|
const result = filterValidatorSignatureTransaction(transactions, messageData)
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${EXECUTE_AFFIRMATION_HASH}123456` })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getExecutionFailedTransactionForMessage', () => {
|
||||||
|
test('should return failed transaction related to signatures execution', async () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${EXECUTE_SIGNATURES_HASH}112233` },
|
||||||
|
{ input: `0x${EXECUTE_SIGNATURES_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}123456` },
|
||||||
|
{ input: `0x${OTHER_HASH}112233` }
|
||||||
|
] as APITransaction[]
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
|
||||||
|
const result = await getExecutionFailedTransactionForMessage(
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
to: '',
|
||||||
|
messageData,
|
||||||
|
startTimestamp: 0,
|
||||||
|
endTimestamp: 1
|
||||||
|
},
|
||||||
|
fetchAccountTransactions
|
||||||
|
)
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${EXECUTE_SIGNATURES_HASH}123456` })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getValidatorPendingTransactionsForMessage', () => {
|
||||||
|
test('should return pending transaction for submit signature transaction', async () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${SUBMIT_SIGNATURE_HASH}112233`, to: bridgeAddress },
|
||||||
|
{ input: `0x${SUBMIT_SIGNATURE_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${SUBMIT_SIGNATURE_HASH}123456`, to: otherAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}112233`, to: bridgeAddress }
|
||||||
|
] as APITransaction[]
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
|
||||||
|
const result = await getValidatorPendingTransactionsForMessage(
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
to: bridgeAddress,
|
||||||
|
messageData
|
||||||
|
},
|
||||||
|
fetchAccountTransactions
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${SUBMIT_SIGNATURE_HASH}123456`, to: bridgeAddress })
|
||||||
|
})
|
||||||
|
test('should return pending transaction for execute affirmation transaction', async () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${EXECUTE_AFFIRMATION_HASH}112233`, to: bridgeAddress },
|
||||||
|
{ input: `0x${EXECUTE_AFFIRMATION_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${EXECUTE_AFFIRMATION_HASH}123456`, to: otherAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}112233`, to: bridgeAddress }
|
||||||
|
] as APITransaction[]
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
|
||||||
|
const result = await getValidatorPendingTransactionsForMessage(
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
to: bridgeAddress,
|
||||||
|
messageData
|
||||||
|
},
|
||||||
|
fetchAccountTransactions
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${EXECUTE_AFFIRMATION_HASH}123456`, to: bridgeAddress })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('getExecutionPendingTransactionsForMessage', () => {
|
||||||
|
test('should return pending transaction for signatures execution transaction', async () => {
|
||||||
|
const transactions = [
|
||||||
|
{ input: `0x${EXECUTE_SIGNATURES_HASH}112233`, to: bridgeAddress },
|
||||||
|
{ input: `0x${EXECUTE_SIGNATURES_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${EXECUTE_SIGNATURES_HASH}123456`, to: otherAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}123456`, to: bridgeAddress },
|
||||||
|
{ input: `0x${OTHER_HASH}112233`, to: bridgeAddress }
|
||||||
|
] as APITransaction[]
|
||||||
|
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||||
|
|
||||||
|
const result = await getExecutionPendingTransactionsForMessage(
|
||||||
|
{
|
||||||
|
account: '',
|
||||||
|
to: bridgeAddress,
|
||||||
|
messageData
|
||||||
|
},
|
||||||
|
fetchAccountTransactions
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0]).toEqual({ input: `0x${EXECUTE_SIGNATURES_HASH}123456`, to: bridgeAddress })
|
||||||
|
})
|
||||||
|
})
|
437
alm/src/utils/__tests__/getConfirmationsForTx.test.ts
Normal file
437
alm/src/utils/__tests__/getConfirmationsForTx.test.ts
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
import 'jest'
|
||||||
|
import { getConfirmationsForTx } from '../getConfirmationsForTx'
|
||||||
|
import * as helpers from '../validatorConfirmationHelpers'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
import { Contract } from 'web3-eth-contract'
|
||||||
|
import { APIPendingTransaction, APITransaction } from '../explorer'
|
||||||
|
import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants'
|
||||||
|
import { BasicConfirmationParam } from '../../hooks/useMessageConfirmations'
|
||||||
|
|
||||||
|
jest.mock('../validatorConfirmationHelpers')
|
||||||
|
|
||||||
|
const getValidatorSuccessTransaction = helpers.getValidatorSuccessTransaction as jest.Mock<any>
|
||||||
|
const getValidatorConfirmation = helpers.getValidatorConfirmation as jest.Mock<any>
|
||||||
|
const getValidatorFailedTransaction = helpers.getValidatorFailedTransaction as jest.Mock<any>
|
||||||
|
const getValidatorPendingTransaction = helpers.getValidatorPendingTransaction as jest.Mock<any>
|
||||||
|
|
||||||
|
const messageData = '0x111111111'
|
||||||
|
const web3 = {
|
||||||
|
utils: {
|
||||||
|
soliditySha3Raw: (data: string) => `0xaaaa${data.replace('0x', '')}`
|
||||||
|
}
|
||||||
|
} as Web3
|
||||||
|
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
|
||||||
|
const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
|
||||||
|
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
|
||||||
|
const validatorList = [validator1, validator2, validator3]
|
||||||
|
const bridgeContract = {} as Contract
|
||||||
|
const confirmationContractMethod = () => {}
|
||||||
|
const requiredSignatures = 2
|
||||||
|
const waitingBlocksResolved = true
|
||||||
|
let subscriptions: Array<number> = []
|
||||||
|
const timestamp = 1594045859
|
||||||
|
const getFailedTransactions = (): Promise<APITransaction[]> => Promise.resolve([])
|
||||||
|
const getPendingTransactions = (): Promise<APIPendingTransaction[]> => Promise.resolve([])
|
||||||
|
const getSuccessTransactions = (): Promise<APITransaction[]> => Promise.resolve([])
|
||||||
|
|
||||||
|
const unsubscribe = () => {
|
||||||
|
subscriptions.forEach(s => {
|
||||||
|
clearTimeout(s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Clear all instances and calls to constructor and all methods:
|
||||||
|
getValidatorSuccessTransaction.mockClear()
|
||||||
|
getValidatorConfirmation.mockClear()
|
||||||
|
getValidatorFailedTransaction.mockClear()
|
||||||
|
getValidatorPendingTransaction.mockClear()
|
||||||
|
subscriptions = []
|
||||||
|
})
|
||||||
|
describe('getConfirmationsForTx', () => {
|
||||||
|
test('should set validator confirmations status when signatures collected even if validator transactions not found yet and set remaining validator as not required', async () => {
|
||||||
|
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
|
||||||
|
validator,
|
||||||
|
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
}))
|
||||||
|
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 0
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 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(1)
|
||||||
|
expect(setResult).toBeCalledTimes(2)
|
||||||
|
expect(getValidatorConfirmation).toBeCalledTimes(1)
|
||||||
|
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setSignatureCollected).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
expect(getValidatorFailedTransaction).toBeCalledTimes(0)
|
||||||
|
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.NOT_REQUIRED }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(setResult.mock.calls[1][0]).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
||||||
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
||||||
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
test('should set validator confirmations status when signatures not collected even if validator transactions not found yet', async () => {
|
||||||
|
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
|
||||||
|
validator,
|
||||||
|
status: validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
}))
|
||||||
|
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 0
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 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(setResult).toBeCalledTimes(2)
|
||||||
|
expect(getValidatorConfirmation).toBeCalledTimes(1)
|
||||||
|
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setSignatureCollected).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setFailedConfirmations).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setPendingConfirmations).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('should set validator confirmations status, validator transactions and not retry', async () => {
|
||||||
|
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
|
||||||
|
validator,
|
||||||
|
status: validator !== validator3 ? 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 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator !== validator3 ? 123 : 0
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 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(0)
|
||||||
|
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.NOT_REQUIRED }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
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.NOT_REQUIRED }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
test('should look for failed and pending transactions for not confirmed validators', async () => {
|
||||||
|
// Validator1 success
|
||||||
|
// Validator2 failed
|
||||||
|
// Validator3 Pending
|
||||||
|
|
||||||
|
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
|
||||||
|
validator,
|
||||||
|
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
}))
|
||||||
|
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
|
txHash: validatorData.validator === validator1 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator === validator1 ? 123 : 0
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status:
|
||||||
|
validatorData.validator === validator2
|
||||||
|
? VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
|
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: validatorData.validator === validator2 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator === validator2 ? 123 : 0
|
||||||
|
}))
|
||||||
|
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status:
|
||||||
|
validatorData.validator === validator3
|
||||||
|
? VALIDATOR_CONFIRMATION_STATUS.PENDING
|
||||||
|
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: validatorData.validator === validator3 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator === validator3 ? 123 : 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(setResult).toBeCalledTimes(2)
|
||||||
|
expect(getValidatorConfirmation).toBeCalledTimes(1)
|
||||||
|
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setSignatureCollected).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setFailedConfirmations).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setPendingConfirmations).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
expect(setResult.mock.calls[0][0]).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
||||||
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
|
||||||
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
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.FAILED, txHash: '0x123', timestamp: 123 },
|
||||||
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
test('should set as failed if enough signatures failed', async () => {
|
||||||
|
// Validator1 success
|
||||||
|
// Validator2 failed
|
||||||
|
// Validator3 failed
|
||||||
|
|
||||||
|
getValidatorConfirmation.mockImplementation(() => async (validator: string) => ({
|
||||||
|
validator,
|
||||||
|
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
}))
|
||||||
|
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
|
txHash: validatorData.validator === validator1 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator === validator1 ? 123 : 0
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status:
|
||||||
|
validatorData.validator !== validator1
|
||||||
|
? VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
|
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
txHash: validatorData.validator !== validator1 ? '0x123' : '',
|
||||||
|
timestamp: validatorData.validator !== validator1 ? 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(0)
|
||||||
|
|
||||||
|
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
|
||||||
|
expect(setFailedConfirmations).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
|
||||||
|
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.FAILED, txHash: '0x123', timestamp: 123 },
|
||||||
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
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.FAILED, txHash: '0x123', timestamp: 123 },
|
||||||
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
303
alm/src/utils/__tests__/getFinalizationEvent.test.ts
Normal file
303
alm/src/utils/__tests__/getFinalizationEvent.test.ts
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
import 'jest'
|
||||||
|
import { Contract, EventData } from 'web3-eth-contract'
|
||||||
|
import Web3 from 'web3'
|
||||||
|
import { getFinalizationEvent } from '../getFinalizationEvent'
|
||||||
|
import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants'
|
||||||
|
|
||||||
|
const eventName = 'RelayedMessage'
|
||||||
|
const timestamp = 1594045859
|
||||||
|
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
|
||||||
|
const txHash = '0xdab36c9210e7e45fb82af10ffe4960461e41661dce0c9cd36b2843adaa1df156'
|
||||||
|
|
||||||
|
const web3 = ({
|
||||||
|
eth: {
|
||||||
|
getTransactionReceipt: () => ({
|
||||||
|
from: validator1
|
||||||
|
}),
|
||||||
|
getBlock: () => ({ timestamp })
|
||||||
|
},
|
||||||
|
utils: {
|
||||||
|
toChecksumAddress: (a: string) => a
|
||||||
|
}
|
||||||
|
} as unknown) as Web3
|
||||||
|
const waitingBlocksResolved = true
|
||||||
|
const message = {
|
||||||
|
id: '0x123',
|
||||||
|
data: '0x123456789'
|
||||||
|
}
|
||||||
|
const interval = 10000
|
||||||
|
let subscriptions: Array<number> = []
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
transactionHash: txHash,
|
||||||
|
blockNumber: 5523145,
|
||||||
|
returnValues: {
|
||||||
|
status: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bridgeAddress = '0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560'
|
||||||
|
|
||||||
|
const unsubscribe = () => {
|
||||||
|
subscriptions.forEach(s => {
|
||||||
|
clearTimeout(s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
subscriptions = []
|
||||||
|
})
|
||||||
|
describe('getFinalizationEvent', () => {
|
||||||
|
test('should get finalization event and not try to get failed or pending transactions', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return [event]
|
||||||
|
}
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const collectedSignaturesEvent = null
|
||||||
|
const setResult = jest.fn()
|
||||||
|
const getFailedExecution = jest.fn()
|
||||||
|
const setFailedExecution = jest.fn()
|
||||||
|
const getPendingExecution = jest.fn()
|
||||||
|
const setPendingExecution = jest.fn()
|
||||||
|
|
||||||
|
await getFinalizationEvent(
|
||||||
|
contract,
|
||||||
|
eventName,
|
||||||
|
web3,
|
||||||
|
setResult,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution,
|
||||||
|
getPendingExecution,
|
||||||
|
setPendingExecution
|
||||||
|
)
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
|
||||||
|
expect(subscriptions.length).toEqual(0)
|
||||||
|
expect(setResult).toBeCalledTimes(1)
|
||||||
|
expect(setResult.mock.calls[0][0]).toEqual({
|
||||||
|
validator: validator1,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
|
txHash,
|
||||||
|
timestamp,
|
||||||
|
executionResult: true
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getFailedExecution).toBeCalledTimes(0)
|
||||||
|
expect(setFailedExecution).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getPendingExecution).toBeCalledTimes(0)
|
||||||
|
expect(setPendingExecution).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('should retry to get finalization event and not try to get failed or pending transactions if foreign to home transaction', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const collectedSignaturesEvent = null
|
||||||
|
const setResult = jest.fn()
|
||||||
|
const getFailedExecution = jest.fn()
|
||||||
|
const setFailedExecution = jest.fn()
|
||||||
|
const getPendingExecution = jest.fn()
|
||||||
|
const setPendingExecution = jest.fn()
|
||||||
|
|
||||||
|
await getFinalizationEvent(
|
||||||
|
contract,
|
||||||
|
eventName,
|
||||||
|
web3,
|
||||||
|
setResult,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution,
|
||||||
|
getPendingExecution,
|
||||||
|
setPendingExecution
|
||||||
|
)
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
|
||||||
|
expect(subscriptions.length).toEqual(1)
|
||||||
|
expect(setResult).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getFailedExecution).toBeCalledTimes(0)
|
||||||
|
expect(setFailedExecution).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getPendingExecution).toBeCalledTimes(0)
|
||||||
|
expect(setPendingExecution).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('should retry to get finalization event and try to get failed and pending transactions if home to foreign transaction', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
address: bridgeAddress
|
||||||
|
}
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const collectedSignaturesEvent = ({
|
||||||
|
returnValues: {
|
||||||
|
authorityResponsibleForRelay: validator1
|
||||||
|
}
|
||||||
|
} as unknown) as EventData
|
||||||
|
const setResult = jest.fn()
|
||||||
|
const getFailedExecution = jest.fn().mockResolvedValue([])
|
||||||
|
const setFailedExecution = jest.fn()
|
||||||
|
const getPendingExecution = jest.fn().mockResolvedValue([])
|
||||||
|
const setPendingExecution = jest.fn()
|
||||||
|
|
||||||
|
await getFinalizationEvent(
|
||||||
|
contract,
|
||||||
|
eventName,
|
||||||
|
web3,
|
||||||
|
setResult,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution,
|
||||||
|
getPendingExecution,
|
||||||
|
setPendingExecution
|
||||||
|
)
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
|
||||||
|
expect(subscriptions.length).toEqual(1)
|
||||||
|
expect(setResult).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getFailedExecution).toBeCalledTimes(1)
|
||||||
|
expect(setFailedExecution).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getPendingExecution).toBeCalledTimes(1)
|
||||||
|
expect(setPendingExecution).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
test('should retry to get finalization event and not to try to get failed transaction if pending transactions found if home to foreign transaction', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
address: bridgeAddress
|
||||||
|
}
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const collectedSignaturesEvent = ({
|
||||||
|
returnValues: {
|
||||||
|
authorityResponsibleForRelay: validator1
|
||||||
|
}
|
||||||
|
} as unknown) as EventData
|
||||||
|
const setResult = jest.fn()
|
||||||
|
const getFailedExecution = jest.fn().mockResolvedValue([])
|
||||||
|
const setFailedExecution = jest.fn()
|
||||||
|
const getPendingExecution = jest.fn().mockResolvedValue([{ hash: txHash }])
|
||||||
|
const setPendingExecution = jest.fn()
|
||||||
|
|
||||||
|
await getFinalizationEvent(
|
||||||
|
contract,
|
||||||
|
eventName,
|
||||||
|
web3,
|
||||||
|
setResult,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution,
|
||||||
|
getPendingExecution,
|
||||||
|
setPendingExecution
|
||||||
|
)
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
|
||||||
|
expect(subscriptions.length).toEqual(1)
|
||||||
|
expect(setResult).toBeCalledTimes(1)
|
||||||
|
expect(setResult.mock.calls[0][0]).toEqual({
|
||||||
|
validator: validator1,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.PENDING,
|
||||||
|
txHash,
|
||||||
|
timestamp: expect.any(Number),
|
||||||
|
executionResult: false
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getFailedExecution).toBeCalledTimes(0)
|
||||||
|
expect(setFailedExecution).toBeCalledTimes(0)
|
||||||
|
|
||||||
|
expect(getPendingExecution).toBeCalledTimes(1)
|
||||||
|
expect(setPendingExecution).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
test('should retry to get finalization event even if failed transaction found if home to foreign transaction', async () => {
|
||||||
|
const contract = ({
|
||||||
|
getPastEvents: () => {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
address: bridgeAddress
|
||||||
|
}
|
||||||
|
} as unknown) as Contract
|
||||||
|
|
||||||
|
const collectedSignaturesEvent = ({
|
||||||
|
returnValues: {
|
||||||
|
authorityResponsibleForRelay: validator1
|
||||||
|
}
|
||||||
|
} as unknown) as EventData
|
||||||
|
const setResult = jest.fn()
|
||||||
|
const getFailedExecution = jest.fn().mockResolvedValue([{ timeStamp: timestamp, hash: txHash }])
|
||||||
|
const setFailedExecution = jest.fn()
|
||||||
|
const getPendingExecution = jest.fn().mockResolvedValue([])
|
||||||
|
const setPendingExecution = jest.fn()
|
||||||
|
|
||||||
|
await getFinalizationEvent(
|
||||||
|
contract,
|
||||||
|
eventName,
|
||||||
|
web3,
|
||||||
|
setResult,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution,
|
||||||
|
getPendingExecution,
|
||||||
|
setPendingExecution
|
||||||
|
)
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
|
||||||
|
expect(subscriptions.length).toEqual(1)
|
||||||
|
expect(setResult).toBeCalledTimes(1)
|
||||||
|
expect(setResult.mock.calls[0][0]).toEqual({
|
||||||
|
validator: validator1,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
|
||||||
|
txHash,
|
||||||
|
timestamp: expect.any(Number),
|
||||||
|
executionResult: false
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getFailedExecution).toBeCalledTimes(1)
|
||||||
|
expect(setFailedExecution).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
expect(getPendingExecution).toBeCalledTimes(1)
|
||||||
|
expect(setPendingExecution).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
})
|
@ -217,14 +217,11 @@ export const getValidatorSuccessTransactionsForMessage = async ({
|
|||||||
return filterValidatorSignatureTransaction(transactions, messageData)
|
return filterValidatorSignatureTransaction(transactions, messageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getExecutionFailedTransactionForMessage = async ({
|
export const getExecutionFailedTransactionForMessage = async (
|
||||||
account,
|
{ account, to, messageData, startTimestamp, endTimestamp }: GetFailedTransactionParams,
|
||||||
to,
|
getFailedTransactionsMethod = getFailedTransactions
|
||||||
messageData,
|
): Promise<APITransaction[]> => {
|
||||||
startTimestamp,
|
const failedTransactions = await getFailedTransactionsMethod(
|
||||||
endTimestamp
|
|
||||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
|
||||||
const failedTransactions = await getFailedTransactions(
|
|
||||||
account,
|
account,
|
||||||
to,
|
to,
|
||||||
startTimestamp,
|
startTimestamp,
|
||||||
@ -237,12 +234,11 @@ export const getExecutionFailedTransactionForMessage = async ({
|
|||||||
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))
|
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getValidatorPendingTransactionsForMessage = async ({
|
export const getValidatorPendingTransactionsForMessage = async (
|
||||||
account,
|
{ account, to, messageData }: GetPendingTransactionParams,
|
||||||
to,
|
fetchPendingTransactionsMethod = fetchPendingTransactions
|
||||||
messageData
|
): Promise<APIPendingTransaction[]> => {
|
||||||
}: GetPendingTransactionParams): Promise<APIPendingTransaction[]> => {
|
const pendingTransactions = await fetchPendingTransactionsMethod({
|
||||||
const pendingTransactions = await fetchPendingTransactions({
|
|
||||||
account,
|
account,
|
||||||
api: HOME_EXPLORER_API
|
api: HOME_EXPLORER_API
|
||||||
})
|
})
|
||||||
@ -258,12 +254,11 @@ export const getValidatorPendingTransactionsForMessage = async ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getExecutionPendingTransactionsForMessage = async ({
|
export const getExecutionPendingTransactionsForMessage = async (
|
||||||
account,
|
{ account, to, messageData }: GetPendingTransactionParams,
|
||||||
to,
|
fetchPendingTransactionsMethod = fetchPendingTransactions
|
||||||
messageData
|
): Promise<APIPendingTransaction[]> => {
|
||||||
}: GetPendingTransactionParams): Promise<APIPendingTransaction[]> => {
|
const pendingTransactions = await fetchPendingTransactionsMethod({
|
||||||
const pendingTransactions = await fetchPendingTransactions({
|
|
||||||
account,
|
account,
|
||||||
api: FOREIGN_EXPLORER_API
|
api: FOREIGN_EXPLORER_API
|
||||||
})
|
})
|
||||||
|
@ -1,176 +1,18 @@
|
|||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { Contract } from 'web3-eth-contract'
|
import { Contract } from 'web3-eth-contract'
|
||||||
import validatorsCache from '../services/ValidatorsCache'
|
import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||||
import {
|
|
||||||
CACHE_KEY_FAILED,
|
|
||||||
CACHE_KEY_SUCCESS,
|
|
||||||
HOME_RPC_POLLING_INTERVAL,
|
|
||||||
ONE_DAY_TIMESTAMP,
|
|
||||||
VALIDATOR_CONFIRMATION_STATUS
|
|
||||||
} from '../config/constants'
|
|
||||||
import {
|
import {
|
||||||
GetFailedTransactionParams,
|
GetFailedTransactionParams,
|
||||||
APITransaction,
|
APITransaction,
|
||||||
APIPendingTransaction,
|
APIPendingTransaction,
|
||||||
GetPendingTransactionParams
|
GetPendingTransactionParams
|
||||||
} from './explorer'
|
} from './explorer'
|
||||||
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
|
import {
|
||||||
|
getValidatorConfirmation,
|
||||||
export const getValidatorConfirmation = (
|
getValidatorFailedTransaction,
|
||||||
web3: Web3,
|
getValidatorPendingTransaction,
|
||||||
hashMsg: string,
|
getValidatorSuccessTransaction
|
||||||
bridgeContract: Contract,
|
} from './validatorConfirmationHelpers'
|
||||||
confirmationContractMethod: Function
|
|
||||||
) => async (validator: string): Promise<BasicConfirmationParam> => {
|
|
||||||
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
|
|
||||||
|
|
||||||
const signatureFromCache = validatorsCache.get(hashSenderMsg)
|
|
||||||
if (signatureFromCache) {
|
|
||||||
return {
|
|
||||||
validator,
|
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmed = await confirmationContractMethod(bridgeContract, hashSenderMsg)
|
|
||||||
const status = confirmed ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
|
||||||
|
|
||||||
// If validator confirmed signature, we cache the result to avoid doing future requests for a result that won't change
|
|
||||||
if (confirmed) {
|
|
||||||
validatorsCache.set(hashSenderMsg, confirmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validator,
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getValidatorSuccessTransaction = (
|
|
||||||
bridgeContract: Contract,
|
|
||||||
messageData: string,
|
|
||||||
timestamp: number,
|
|
||||||
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
|
||||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
|
||||||
const { validator } = validatorData
|
|
||||||
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
|
|
||||||
const fromCache = validatorsCache.getData(validatorCacheKey)
|
|
||||||
|
|
||||||
if (fromCache && fromCache.txHash) {
|
|
||||||
return fromCache
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactions = await getSuccessTransactions({
|
|
||||||
account: validatorData.validator,
|
|
||||||
to: bridgeContract.options.address,
|
|
||||||
messageData,
|
|
||||||
startTimestamp: timestamp,
|
|
||||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
|
||||||
})
|
|
||||||
|
|
||||||
let txHashTimestamp = 0
|
|
||||||
let txHash = ''
|
|
||||||
const status = VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
|
||||||
|
|
||||||
if (transactions.length > 0) {
|
|
||||||
const tx = transactions[0]
|
|
||||||
txHashTimestamp = parseInt(tx.timeStamp)
|
|
||||||
txHash = tx.hash
|
|
||||||
|
|
||||||
// cache the result
|
|
||||||
validatorsCache.setData(validatorCacheKey, {
|
|
||||||
validator,
|
|
||||||
status,
|
|
||||||
txHash,
|
|
||||||
timestamp: txHashTimestamp
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validator,
|
|
||||||
status,
|
|
||||||
txHash,
|
|
||||||
timestamp: txHashTimestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getValidatorFailedTransaction = (
|
|
||||||
bridgeContract: Contract,
|
|
||||||
messageData: string,
|
|
||||||
timestamp: number,
|
|
||||||
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
|
||||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
|
||||||
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
|
|
||||||
const failedFromCache = validatorsCache.getData(validatorCacheKey)
|
|
||||||
|
|
||||||
if (failedFromCache && failedFromCache.txHash) {
|
|
||||||
return failedFromCache
|
|
||||||
}
|
|
||||||
|
|
||||||
const failedTransactions = await getFailedTransactions({
|
|
||||||
account: validatorData.validator,
|
|
||||||
to: bridgeContract.options.address,
|
|
||||||
messageData,
|
|
||||||
startTimestamp: timestamp,
|
|
||||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
|
||||||
})
|
|
||||||
const newStatus =
|
|
||||||
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
|
||||||
|
|
||||||
let txHashTimestamp = 0
|
|
||||||
let txHash = ''
|
|
||||||
// If validator signature failed, we cache the result to avoid doing future requests for a result that won't change
|
|
||||||
if (failedTransactions.length > 0) {
|
|
||||||
const failedTx = failedTransactions[0]
|
|
||||||
txHashTimestamp = parseInt(failedTx.timeStamp)
|
|
||||||
txHash = failedTx.hash
|
|
||||||
|
|
||||||
validatorsCache.setData(validatorCacheKey, {
|
|
||||||
validator: validatorData.validator,
|
|
||||||
status: newStatus,
|
|
||||||
txHash,
|
|
||||||
timestamp: txHashTimestamp
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validator: validatorData.validator,
|
|
||||||
status: newStatus,
|
|
||||||
txHash,
|
|
||||||
timestamp: txHashTimestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getValidatorPendingTransaction = (
|
|
||||||
bridgeContract: Contract,
|
|
||||||
messageData: string,
|
|
||||||
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>
|
|
||||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
|
||||||
const failedTransactions = await getPendingTransactions({
|
|
||||||
account: validatorData.validator,
|
|
||||||
to: bridgeContract.options.address,
|
|
||||||
messageData
|
|
||||||
})
|
|
||||||
|
|
||||||
const newStatus =
|
|
||||||
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.PENDING : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
|
||||||
|
|
||||||
let timestamp = 0
|
|
||||||
let txHash = ''
|
|
||||||
|
|
||||||
if (failedTransactions.length > 0) {
|
|
||||||
const failedTx = failedTransactions[0]
|
|
||||||
timestamp = Math.floor(new Date().getTime() / 1000.0)
|
|
||||||
txHash = failedTx.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validator: validatorData.validator,
|
|
||||||
status: newStatus,
|
|
||||||
txHash,
|
|
||||||
timestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getConfirmationsForTx = async (
|
export const getConfirmationsForTx = async (
|
||||||
messageData: string,
|
messageData: string,
|
||||||
|
172
alm/src/utils/validatorConfirmationHelpers.ts
Normal file
172
alm/src/utils/validatorConfirmationHelpers.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import Web3 from 'web3'
|
||||||
|
import { Contract } from 'web3-eth-contract'
|
||||||
|
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||||
|
import validatorsCache from '../services/ValidatorsCache'
|
||||||
|
import {
|
||||||
|
CACHE_KEY_FAILED,
|
||||||
|
CACHE_KEY_SUCCESS,
|
||||||
|
ONE_DAY_TIMESTAMP,
|
||||||
|
VALIDATOR_CONFIRMATION_STATUS
|
||||||
|
} from '../config/constants'
|
||||||
|
import {
|
||||||
|
APIPendingTransaction,
|
||||||
|
APITransaction,
|
||||||
|
GetFailedTransactionParams,
|
||||||
|
GetPendingTransactionParams
|
||||||
|
} from './explorer'
|
||||||
|
|
||||||
|
export const getValidatorConfirmation = (
|
||||||
|
web3: Web3,
|
||||||
|
hashMsg: string,
|
||||||
|
bridgeContract: Contract,
|
||||||
|
confirmationContractMethod: Function
|
||||||
|
) => async (validator: string): Promise<BasicConfirmationParam> => {
|
||||||
|
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
|
||||||
|
|
||||||
|
const signatureFromCache = validatorsCache.get(hashSenderMsg)
|
||||||
|
if (signatureFromCache) {
|
||||||
|
return {
|
||||||
|
validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await confirmationContractMethod(bridgeContract, hashSenderMsg)
|
||||||
|
const status = confirmed ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
|
||||||
|
// If validator confirmed signature, we cache the result to avoid doing future requests for a result that won't change
|
||||||
|
if (confirmed) {
|
||||||
|
validatorsCache.set(hashSenderMsg, confirmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator,
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidatorSuccessTransaction = (
|
||||||
|
bridgeContract: Contract,
|
||||||
|
messageData: string,
|
||||||
|
timestamp: number,
|
||||||
|
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||||
|
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||||
|
const { validator } = validatorData
|
||||||
|
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
|
||||||
|
const fromCache = validatorsCache.getData(validatorCacheKey)
|
||||||
|
|
||||||
|
if (fromCache && fromCache.txHash) {
|
||||||
|
return fromCache
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactions = await getSuccessTransactions({
|
||||||
|
account: validatorData.validator,
|
||||||
|
to: bridgeContract.options.address,
|
||||||
|
messageData,
|
||||||
|
startTimestamp: timestamp,
|
||||||
|
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||||
|
})
|
||||||
|
|
||||||
|
let txHashTimestamp = 0
|
||||||
|
let txHash = ''
|
||||||
|
const status = VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||||
|
|
||||||
|
if (transactions.length > 0) {
|
||||||
|
const tx = transactions[0]
|
||||||
|
txHashTimestamp = parseInt(tx.timeStamp)
|
||||||
|
txHash = tx.hash
|
||||||
|
|
||||||
|
// cache the result
|
||||||
|
validatorsCache.setData(validatorCacheKey, {
|
||||||
|
validator,
|
||||||
|
status,
|
||||||
|
txHash,
|
||||||
|
timestamp: txHashTimestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator,
|
||||||
|
status,
|
||||||
|
txHash,
|
||||||
|
timestamp: txHashTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidatorFailedTransaction = (
|
||||||
|
bridgeContract: Contract,
|
||||||
|
messageData: string,
|
||||||
|
timestamp: number,
|
||||||
|
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||||
|
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||||
|
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
|
||||||
|
const failedFromCache = validatorsCache.getData(validatorCacheKey)
|
||||||
|
|
||||||
|
if (failedFromCache && failedFromCache.txHash) {
|
||||||
|
return failedFromCache
|
||||||
|
}
|
||||||
|
|
||||||
|
const failedTransactions = await getFailedTransactions({
|
||||||
|
account: validatorData.validator,
|
||||||
|
to: bridgeContract.options.address,
|
||||||
|
messageData,
|
||||||
|
startTimestamp: timestamp,
|
||||||
|
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||||
|
})
|
||||||
|
const newStatus =
|
||||||
|
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
|
||||||
|
let txHashTimestamp = 0
|
||||||
|
let txHash = ''
|
||||||
|
// If validator signature failed, we cache the result to avoid doing future requests for a result that won't change
|
||||||
|
if (failedTransactions.length > 0) {
|
||||||
|
const failedTx = failedTransactions[0]
|
||||||
|
txHashTimestamp = parseInt(failedTx.timeStamp)
|
||||||
|
txHash = failedTx.hash
|
||||||
|
|
||||||
|
validatorsCache.setData(validatorCacheKey, {
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: newStatus,
|
||||||
|
txHash,
|
||||||
|
timestamp: txHashTimestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: newStatus,
|
||||||
|
txHash,
|
||||||
|
timestamp: txHashTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidatorPendingTransaction = (
|
||||||
|
bridgeContract: Contract,
|
||||||
|
messageData: string,
|
||||||
|
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>
|
||||||
|
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||||
|
const failedTransactions = await getPendingTransactions({
|
||||||
|
account: validatorData.validator,
|
||||||
|
to: bridgeContract.options.address,
|
||||||
|
messageData
|
||||||
|
})
|
||||||
|
|
||||||
|
const newStatus =
|
||||||
|
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.PENDING : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
|
||||||
|
let timestamp = 0
|
||||||
|
let txHash = ''
|
||||||
|
|
||||||
|
if (failedTransactions.length > 0) {
|
||||||
|
const failedTx = failedTransactions[0]
|
||||||
|
timestamp = Math.floor(new Date().getTime() / 1000.0)
|
||||||
|
txHash = failedTx.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: newStatus,
|
||||||
|
txHash,
|
||||||
|
timestamp
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user