173 lines
6.6 KiB
JavaScript
173 lines
6.6 KiB
JavaScript
const logger = require('../logger')('web3Cache')
|
|
const { readCacheFile, writeCacheFile } = require('./file')
|
|
const { web3Home, web3Foreign } = require('./web3')
|
|
const { getPastEvents: commonGetPastEvents } = require('../../commons')
|
|
|
|
const { MONITOR_BRIDGE_NAME, MONITOR_CACHE_EVENTS } = process.env
|
|
|
|
let isDirty = false
|
|
|
|
const homeTxSendersCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/home/txSenders.json`
|
|
const cachedHomeTxSenders = readCacheFile(homeTxSendersCacheFile) || {}
|
|
const foreignTxSendersCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/foreign/txSenders.json`
|
|
const cachedForeignTxSenders = readCacheFile(foreignTxSendersCacheFile) || {}
|
|
const homeIsContractCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/home/isContract.json`
|
|
const cachedHomeIsContract = readCacheFile(homeIsContractCacheFile) || {}
|
|
const foreignIsContractCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/foreign/isContract.json`
|
|
const cachedForeignIsContract = readCacheFile(foreignIsContractCacheFile) || {}
|
|
|
|
async function getHomeTxSender(txHash) {
|
|
if (!cachedHomeTxSenders[txHash]) {
|
|
logger.debug(`Fetching sender for home tx ${txHash}`)
|
|
cachedHomeTxSenders[txHash] = (await web3Home.eth.getTransaction(txHash)).from.toLowerCase()
|
|
isDirty = true
|
|
}
|
|
return cachedHomeTxSenders[txHash]
|
|
}
|
|
|
|
async function getForeignTxSender(txHash) {
|
|
if (!cachedForeignTxSenders[txHash]) {
|
|
logger.debug(`Fetching sender for foreign tx ${txHash}`)
|
|
cachedForeignTxSenders[txHash] = (await web3Foreign.eth.getTransaction(txHash)).from.toLowerCase()
|
|
isDirty = true
|
|
}
|
|
return cachedForeignTxSenders[txHash]
|
|
}
|
|
|
|
async function isHomeContract(address) {
|
|
if (typeof cachedHomeIsContract[address] !== 'boolean') {
|
|
logger.debug(`Fetching home contract code size for tx ${address}`)
|
|
cachedHomeIsContract[address] = (await web3Home.eth.getCode(address)).length > 2
|
|
isDirty = true
|
|
}
|
|
return cachedHomeIsContract[address]
|
|
}
|
|
|
|
async function isForeignContract(address) {
|
|
if (typeof cachedForeignIsContract[address] !== 'boolean') {
|
|
logger.debug(`Fetching foreign contract code size for tx ${address}`)
|
|
cachedForeignIsContract[address] = (await web3Foreign.eth.getCode(address)).length > 2
|
|
isDirty = true
|
|
}
|
|
return cachedForeignIsContract[address]
|
|
}
|
|
|
|
async function getPastEvents(contract, options) {
|
|
if (MONITOR_CACHE_EVENTS !== 'true') {
|
|
return commonGetPastEvents(contract, options)
|
|
}
|
|
|
|
const contractAddr = contract.options.address
|
|
|
|
let eventSignature
|
|
if (options.event.includes('(')) {
|
|
eventSignature = options.event
|
|
options.event = web3Home.utils.sha3(eventSignature)
|
|
const eventABI = contract.options.jsonInterface.find(e => e.type === 'event' && e.signature === options.event)
|
|
if (!eventABI) {
|
|
throw new Error(`Event ${eventSignature} not found`)
|
|
}
|
|
} else {
|
|
const eventABI = contract.options.jsonInterface.find(
|
|
e => e.type === 'event' && (e.name === options.event || e.signature === options.event)
|
|
)
|
|
if (!eventABI) {
|
|
throw new Error(`Event ${options.event} not found`)
|
|
}
|
|
eventSignature = `${eventABI.name}(${eventABI.inputs.map(i => i.type).join(',')})`
|
|
}
|
|
|
|
const cacheFile = `./cache/${MONITOR_BRIDGE_NAME}/${options.chain}/${contractAddr}/${eventSignature}.json`
|
|
|
|
const { fromBlock, toBlock } = options
|
|
const { fromBlock: cachedFromBlock, toBlock: cachedToBlock, events: cachedEvents } = readCacheFile(cacheFile) || {
|
|
fromBlock: 0,
|
|
toBlock: 0,
|
|
events: []
|
|
}
|
|
|
|
let result
|
|
if (cachedFromBlock > toBlock || fromBlock > cachedToBlock) {
|
|
// requested: A...B
|
|
// cached: C...D
|
|
// OR
|
|
// requested: A...B
|
|
// cached: C...D
|
|
logger.debug(`Fetching events for blocks ${fromBlock}...${toBlock}`)
|
|
result = await commonGetPastEvents(contract, options)
|
|
} else if (fromBlock < cachedFromBlock && toBlock <= cachedToBlock) {
|
|
// requested: A...B
|
|
// cached: C...D
|
|
logger.debug(`Cache hit for blocks ${cachedFromBlock}...${toBlock}`)
|
|
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
|
|
result = [
|
|
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
|
...cachedEvents.filter(e => e.blockNumber <= toBlock)
|
|
]
|
|
} else if (fromBlock < cachedFromBlock && cachedToBlock < toBlock) {
|
|
// requested: A.....B
|
|
// cached: C.D
|
|
logger.debug(`Cache hit for blocks ${cachedFromBlock}...${cachedToBlock}`)
|
|
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
|
|
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
|
|
result = [
|
|
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
|
...cachedEvents,
|
|
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
|
]
|
|
} else if (cachedFromBlock <= fromBlock && toBlock <= cachedToBlock) {
|
|
// requested: A.B
|
|
// cached: C.....D
|
|
logger.debug(`Cache hit for blocks ${fromBlock}...${toBlock}`)
|
|
result = cachedEvents.filter(e => fromBlock <= e.blockNumber && e.blockNumber <= toBlock)
|
|
} else if (fromBlock >= cachedFromBlock && toBlock > cachedToBlock) {
|
|
// requested: A...B
|
|
// cached: C...D
|
|
logger.debug(`Cache hit for blocks ${fromBlock}...${cachedToBlock}`)
|
|
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
|
|
result = [
|
|
...cachedEvents.filter(e => e.blockNumber >= fromBlock),
|
|
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
|
]
|
|
} else {
|
|
throw new Error(
|
|
`Something is broken with cache resolution for getPastEvents,
|
|
requested blocks ${fromBlock}...${toBlock},
|
|
cached blocks ${cachedFromBlock}...${cachedToBlock}`
|
|
)
|
|
}
|
|
|
|
// it is not safe to cache events with too low block confirmations
|
|
// so, only events from finalized blocks are included into the cache
|
|
const safeToBlock = options.safeToBlock || toBlock
|
|
const cacheToSave = result.filter(e => e.blockNumber <= safeToBlock)
|
|
logger.debug(
|
|
`Saving events cache for ${MONITOR_BRIDGE_NAME}/${options.chain}/${contractAddr}/${eventSignature} on disk`
|
|
)
|
|
writeCacheFile(cacheFile, {
|
|
fromBlock,
|
|
toBlock: safeToBlock,
|
|
events: cacheToSave
|
|
})
|
|
return result
|
|
}
|
|
|
|
function saveCache() {
|
|
if (isDirty) {
|
|
logger.debug('Saving cache on disk')
|
|
writeCacheFile(homeTxSendersCacheFile, cachedHomeTxSenders)
|
|
writeCacheFile(foreignTxSendersCacheFile, cachedForeignTxSenders)
|
|
writeCacheFile(homeIsContractCacheFile, cachedHomeIsContract)
|
|
writeCacheFile(foreignIsContractCacheFile, cachedForeignIsContract)
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
getHomeTxSender,
|
|
getForeignTxSender,
|
|
isHomeContract,
|
|
isForeignContract,
|
|
getPastEvents,
|
|
saveCache
|
|
}
|