Skip to content

Commit

Permalink
Fix reorg detection with dynamic contracts (#406)
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh authored Jan 6, 2025
1 parent ece5293 commit 0401ff3
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 156 deletions.
56 changes: 32 additions & 24 deletions codegenerator/cli/npm/envio/src/ReorgDetection.res
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ type blockData = {
blockTimestamp: int,
}

type reorgGuard = {
lastBlockScannedData: blockData,
firstBlockParentNumberAndHash: option<blockNumberAndHash>,
}

module LastBlockScannedHashes: {
type t
/**Instantiat t with existing data*/
Expand Down Expand Up @@ -69,7 +74,7 @@ module LastBlockScannedHashes: {

let getAllBlockNumbers: t => Belt.Array.t<int>

let hasReorgOccurred: (t, ~firstBlockParentNumberAndHash: option<blockNumberAndHash>) => bool
let hasReorgOccurred: (t, ~reorgGuard: reorgGuard) => bool

/**
Return a BlockNumbersAndHashes.t rolled back to where blockData is less
Expand Down Expand Up @@ -402,31 +407,34 @@ module LastBlockScannedHashes: {
/**
Checks whether reorg has occured by comparing the parent hash with the last saved block hash.
*/
let rec hasReorgOccurredInternal = (
lastBlockScannedDataList,
~firstBlockParentNumberAndHash: option<blockNumberAndHash>,
) => {
switch (firstBlockParentNumberAndHash, lastBlockScannedDataList) {
| (Some({blockHash: parentHash, blockNumber: parentBlockNumber}), list{head, ...tail}) =>
if parentBlockNumber == head.blockNumber {
parentHash != head.blockHash
} else {
//if block numbers do not match, this is a dynamic contract case and should recurse
//through the list to look for a matching block or nothing to validate
tail->hasReorgOccurredInternal(~firstBlockParentNumberAndHash)
}
| _ => //If parentHash is None, either it's the genesis block (no reorg)
let rec hasReorgOccurredInternal = (lastBlockScannedDataList, ~reorgGuard: reorgGuard) => {
switch lastBlockScannedDataList {
| list{head, ...tail} =>
switch reorgGuard {
| {lastBlockScannedData} if lastBlockScannedData.blockNumber == head.blockNumber =>
lastBlockScannedData.blockHash != head.blockHash
//If parentHash is None, either it's the genesis block (no reorg)
//Or its already confirmed so no Reorg
//If recentLastBlockData is None, we have not yet saved blockData to compare against
false
| {firstBlockParentNumberAndHash: None} => false
| {
firstBlockParentNumberAndHash: Some({
blockHash: parentHash,
blockNumber: parentBlockNumber,
}),
} =>
if parentBlockNumber == head.blockNumber {
parentHash != head.blockHash
} else {
//if block numbers do not match, this is a dynamic contract case and should recurse
//through the list to look for a matching block or nothing to validate
tail->hasReorgOccurredInternal(~reorgGuard)
}
}
//If recentLastBlockData is None, we have not yet saved blockData to compare against
| _ => false
}
}

let hasReorgOccurred = (
lastBlockScannedHashes: t,
~firstBlockParentNumberAndHash: option<blockNumberAndHash>,
) =>
lastBlockScannedHashes.lastBlockScannedDataList->hasReorgOccurredInternal(
~firstBlockParentNumberAndHash,
)
let hasReorgOccurred = (lastBlockScannedHashes: t, ~reorgGuard: reorgGuard) =>
lastBlockScannedHashes.lastBlockScannedDataList->hasReorgOccurredInternal(~reorgGuard)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,12 @@ type blockRangeFetchStats = {
@as("page fetch time (ms)") pageFetchTime?: int,
}

type reorgGuard = {
lastBlockScannedData: ReorgDetection.blockData,
firstBlockParentNumberAndHash: option<ReorgDetection.blockNumberAndHash>,
}

/**
Thes response returned from a block range fetch
*/
type blockRangeFetchResponse = {
currentBlockHeight: int,
reorgGuard: reorgGuard,
reorgGuard: ReorgDetection.reorgGuard,
parsedQueueItems: array<Internal.eventItem>,
fromBlockQueried: int,
latestFetchedBlockNumber: int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ module Make = (

let lastBlockScannedData = await lastBlockQueriedPromise

let reorgGuard = {
let reorgGuard: ReorgDetection.reorgGuard = {
lastBlockScannedData,
firstBlockParentNumberAndHash: Some({
ReorgDetection.blockHash: lastBlockScannedData.blockHash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ module Make = (

let lastBlockScannedData = await lastBlockQueriedPromise

let reorgGuard = {
let reorgGuard: ReorgDetection.reorgGuard = {
lastBlockScannedData,
firstBlockParentNumberAndHash: pageUnsafe.rollbackGuard->Option.map(v => {
ReorgDetection.blockHash: v.firstParentHash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ module Make = (
let totalTimeElapsed =
startFetchingBatchTimeRef->Hrtime.timeSince->Hrtime.toMillis->Hrtime.intFromMillis

let reorgGuard: reorgGuard = {
let reorgGuard: ReorgDetection.reorgGuard = {
firstBlockParentNumberAndHash: optFirstBlockParent->Option.map(b => {
ReorgDetection.blockNumber: b.number,
blockHash: b.hash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,11 @@ let handleBlockRangeResponse = (state, ~chain, ~response: ChainWorker.blockRange
"stats": stats,
})

let {firstBlockParentNumberAndHash, lastBlockScannedData} = reorgGuard
let {lastBlockScannedData} = reorgGuard

let hasReorgOccurred =
chainFetcher.lastBlockScannedHashes->ReorgDetection.LastBlockScannedHashes.hasReorgOccurred(
~firstBlockParentNumberAndHash,
~reorgGuard,
)

if !hasReorgOccurred || !(state.config->Config.shouldRollbackOnReorg) {
Expand Down
146 changes: 30 additions & 116 deletions scenarios/erc20_multichain_factory/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions scenarios/helpers/src/Indexer.res
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,11 @@ module type S = {
}

module ChainWorker: {
type reorgGuard = {
lastBlockScannedData: ReorgDetection.blockData,
firstBlockParentNumberAndHash: option<ReorgDetection.blockNumberAndHash>,
}
type blockRangeFetchArgs
type blockRangeFetchStats
type blockRangeFetchResponse = {
currentBlockHeight: int,
reorgGuard: reorgGuard,
reorgGuard: ReorgDetection.reorgGuard,
parsedQueueItems: array<Internal.eventItem>,
fromBlockQueried: int,
latestFetchedBlockNumber: int,
Expand Down

0 comments on commit 0401ff3

Please sign in to comment.