Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
2c0ad576
Unverified
Commit
2c0ad576
authored
2 years ago
by
Matthew Slipper
Committed by
GitHub
2 years ago
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4764 from ethereum-optimism/sc/fd-bedrock-support
feat(fd): support Bedrock networks
parents
24ca4622
b004d1ad
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
247 additions
and
95 deletions
+247
-95
nervous-years-explain.md
.changeset/nervous-years-explain.md
+5
-0
.depcheckrc
packages/data-transport-layer/.depcheckrc
+1
-0
package.json
packages/data-transport-layer/package.json
+1
-1
service.ts
packages/data-transport-layer/src/services/main/service.ts
+1
-1
helpers.ts
packages/fault-detector/src/helpers.ts
+35
-23
service.ts
packages/fault-detector/src/service.ts
+186
-64
helpers.spec.ts
packages/fault-detector/test/helpers.spec.ts
+17
-5
yarn.lock
yarn.lock
+1
-1
No files found.
.changeset/nervous-years-explain.md
0 → 100644
View file @
2c0ad576
---
'
@eth-optimism/fault-detector'
:
minor
---
Updates the fault detector to support Bedrock networks.
This diff is collapsed.
Click to expand it.
packages/data-transport-layer/.depcheckrc
View file @
2c0ad576
ignores: [
ignores: [
"@babel/eslint-parser",
"@babel/eslint-parser",
"@types/level",
"@typescript-eslint/parser",
"@typescript-eslint/parser",
"eslint-plugin-import",
"eslint-plugin-import",
"eslint-plugin-unicorn",
"eslint-plugin-unicorn",
...
...
This diff is collapsed.
Click to expand it.
packages/data-transport-layer/package.json
View file @
2c0ad576
...
@@ -52,7 +52,7 @@
...
@@ -52,7 +52,7 @@
"ethers"
:
"^5.7.0"
,
"ethers"
:
"^5.7.0"
,
"express"
:
"^4.17.1"
,
"express"
:
"^4.17.1"
,
"express-prom-bundle"
:
"^6.3.6"
,
"express-prom-bundle"
:
"^6.3.6"
,
"level
"
:
"
^6.0.1"
,
"level
6"
:
"npm:level@
^6.0.1"
,
"levelup"
:
"^4.4.0"
"levelup"
:
"^4.4.0"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
...
...
This diff is collapsed.
Click to expand it.
packages/data-transport-layer/src/services/main/service.ts
View file @
2c0ad576
/* Imports: External */
/* Imports: External */
import
{
BaseService
,
LegacyMetrics
}
from
'
@eth-optimism/common-ts
'
import
{
BaseService
,
LegacyMetrics
}
from
'
@eth-optimism/common-ts
'
import
{
LevelUp
}
from
'
levelup
'
import
{
LevelUp
}
from
'
levelup
'
import
level
from
'
level
'
import
level
from
'
level
6
'
import
{
Counter
}
from
'
prom-client
'
import
{
Counter
}
from
'
prom-client
'
/* Imports: Internal */
/* Imports: Internal */
...
...
This diff is collapsed.
Click to expand it.
packages/fault-detector/src/helpers.ts
View file @
2c0ad576
import
{
Contract
}
from
'
ethers
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
export
interface
OutputOracle
<
TSubmissionEventArgs
>
{
contract
:
Contract
filter
:
any
getTotalElements
:
()
=>
Promise
<
BigNumber
>
getEventIndex
:
(
args
:
TSubmissionEventArgs
)
=>
BigNumber
}
/**
/**
* Partial event interface, meant to reduce the size of the event cache to avoid
* Partial event interface, meant to reduce the size of the event cache to avoid
...
@@ -41,27 +48,32 @@ const getCache = (
...
@@ -41,27 +48,32 @@ const getCache = (
}
}
/**
/**
* Updates the event cache for
the SCC
.
* Updates the event cache for
a contract and event
.
*
*
* @param scc The State Commitment Chain contract.
* @param contract Contract to update cache for.
* @param filter Event filter to use.
*/
*/
export
const
update
StateBatchEventCache
=
async
(
export
const
update
OracleCache
=
async
<
TSubmissionEventArgs
>
(
scc
:
Contract
oracle
:
OutputOracle
<
TSubmissionEventArgs
>
):
Promise
<
void
>
=>
{
):
Promise
<
void
>
=>
{
const
cache
=
getCache
(
scc
.
address
)
const
cache
=
getCache
(
oracle
.
contract
.
address
)
let
currentBlock
=
cache
.
highestBlock
let
currentBlock
=
cache
.
highestBlock
const
endingBlock
=
await
scc
.
provider
.
getBlockNumber
()
const
endingBlock
=
await
oracle
.
contract
.
provider
.
getBlockNumber
()
let
step
=
endingBlock
-
currentBlock
let
step
=
endingBlock
-
currentBlock
let
failures
=
0
let
failures
=
0
while
(
currentBlock
<
endingBlock
)
{
while
(
currentBlock
<
endingBlock
)
{
try
{
try
{
const
events
=
await
scc
.
queryFilter
(
const
events
=
await
oracle
.
contract
.
queryFilter
(
scc
.
filters
.
StateBatchAppended
()
,
oracle
.
filter
,
currentBlock
,
currentBlock
,
currentBlock
+
step
currentBlock
+
step
)
)
// Throw the events into the cache.
for
(
const
event
of
events
)
{
for
(
const
event
of
events
)
{
cache
.
eventCache
[
event
.
args
.
_batchIndex
.
toNumber
()]
=
{
cache
.
eventCache
[
oracle
.
getEventIndex
(
event
.
args
as
TSubmissionEventArgs
).
toNumber
()
]
=
{
blockNumber
:
event
.
blockNumber
,
blockNumber
:
event
.
blockNumber
,
transactionHash
:
event
.
transactionHash
,
transactionHash
:
event
.
transactionHash
,
args
:
event
.
args
,
args
:
event
.
args
,
...
@@ -97,15 +109,15 @@ export const updateStateBatchEventCache = async (
...
@@ -97,15 +109,15 @@ export const updateStateBatchEventCache = async (
/**
/**
* Finds the Event that corresponds to a given state batch by index.
* Finds the Event that corresponds to a given state batch by index.
*
*
* @param
scc StateCommitmentChain contract.
* @param
oracle Output oracle contract
* @param index State batch index to search for.
* @param index State batch index to search for.
* @returns Event corresponding to the batch.
* @returns Event corresponding to the batch.
*/
*/
export
const
findEventForStateBatch
=
async
(
export
const
findEventForStateBatch
=
async
<
TSubmissionEventArgs
>
(
scc
:
Contract
,
oracle
:
OutputOracle
<
TSubmissionEventArgs
>
,
index
:
number
index
:
number
):
Promise
<
PartialEvent
>
=>
{
):
Promise
<
PartialEvent
>
=>
{
const
cache
=
getCache
(
scc
.
address
)
const
cache
=
getCache
(
oracle
.
contract
.
address
)
// Try to find the event in cache first.
// Try to find the event in cache first.
if
(
cache
.
eventCache
[
index
])
{
if
(
cache
.
eventCache
[
index
])
{
...
@@ -113,7 +125,7 @@ export const findEventForStateBatch = async (
...
@@ -113,7 +125,7 @@ export const findEventForStateBatch = async (
}
}
// Update the event cache if we don't have the event.
// Update the event cache if we don't have the event.
await
update
StateBatchEventCache
(
scc
)
await
update
OracleCache
(
oracle
)
// Event better be in cache now!
// Event better be in cache now!
if
(
cache
.
eventCache
[
index
]
===
undefined
)
{
if
(
cache
.
eventCache
[
index
]
===
undefined
)
{
...
@@ -126,23 +138,23 @@ export const findEventForStateBatch = async (
...
@@ -126,23 +138,23 @@ export const findEventForStateBatch = async (
/**
/**
* Finds the first state batch index that has not yet passed the fault proof window.
* Finds the first state batch index that has not yet passed the fault proof window.
*
*
* @param
scc StateCommitmentChain
contract.
* @param
oracle Output oracle
contract.
* @returns Starting state root batch index.
* @returns Starting state root batch index.
*/
*/
export
const
findFirstUnfinalizedStateBatchIndex
=
async
(
export
const
findFirstUnfinalizedStateBatchIndex
=
async
<
TSubmissionEventArgs
>
(
scc
:
Contract
oracle
:
OutputOracle
<
TSubmissionEventArgs
>
,
fpw
:
number
):
Promise
<
number
>
=>
{
):
Promise
<
number
>
=>
{
const
fpw
=
(
await
scc
.
FRAUD_PROOF_WINDOW
()).
toNumber
()
const
latestBlock
=
await
oracle
.
contract
.
provider
.
getBlock
(
'
latest
'
)
const
latestBlock
=
await
scc
.
provider
.
getBlock
(
'
latest
'
)
const
totalBatches
=
(
await
oracle
.
getTotalElements
()).
toNumber
()
const
totalBatches
=
(
await
scc
.
getTotalBatches
()).
toNumber
()
// Perform a binary search to find the next batch that will pass the challenge period.
// Perform a binary search to find the next batch that will pass the challenge period.
let
lo
=
0
let
lo
=
0
let
hi
=
totalBatches
let
hi
=
totalBatches
while
(
lo
!==
hi
)
{
while
(
lo
!==
hi
)
{
const
mid
=
Math
.
floor
((
lo
+
hi
)
/
2
)
const
mid
=
Math
.
floor
((
lo
+
hi
)
/
2
)
const
event
=
await
findEventForStateBatch
(
scc
,
mid
)
const
event
=
await
findEventForStateBatch
(
oracle
,
mid
)
const
block
=
await
scc
.
provider
.
getBlock
(
event
.
blockNumber
)
const
block
=
await
oracle
.
contract
.
provider
.
getBlock
(
event
.
blockNumber
)
if
(
block
.
timestamp
+
fpw
<
latestBlock
.
timestamp
)
{
if
(
block
.
timestamp
+
fpw
<
latestBlock
.
timestamp
)
{
lo
=
mid
+
1
lo
=
mid
+
1
...
...
This diff is collapsed.
Click to expand it.
packages/fault-detector/src/service.ts
View file @
2c0ad576
...
@@ -9,21 +9,23 @@ import {
...
@@ -9,21 +9,23 @@ import {
import
{
getChainId
,
sleep
,
toRpcHexString
}
from
'
@eth-optimism/core-utils
'
import
{
getChainId
,
sleep
,
toRpcHexString
}
from
'
@eth-optimism/core-utils
'
import
{
CrossChainMessenger
}
from
'
@eth-optimism/sdk
'
import
{
CrossChainMessenger
}
from
'
@eth-optimism/sdk
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
import
{
Contract
,
ethers
,
Transaction
}
from
'
ethers
'
import
{
ethers
,
Transaction
}
from
'
ethers
'
import
dateformat
from
'
dateformat
'
import
dateformat
from
'
dateformat
'
import
{
version
}
from
'
../package.json
'
import
{
version
}
from
'
../package.json
'
import
{
import
{
findFirstUnfinalizedStateBatchIndex
,
findFirstUnfinalizedStateBatchIndex
,
findEventForStateBatch
,
findEventForStateBatch
,
updateStateBatchEventCache
,
PartialEvent
,
PartialEvent
,
OutputOracle
,
updateOracleCache
,
}
from
'
./helpers
'
}
from
'
./helpers
'
type
Options
=
{
type
Options
=
{
l1RpcProvider
:
Provider
l1RpcProvider
:
Provider
l2RpcProvider
:
Provider
l2RpcProvider
:
Provider
startBatchIndex
:
number
startBatchIndex
:
number
bedrock
:
boolean
}
}
type
Metrics
=
{
type
Metrics
=
{
...
@@ -34,7 +36,7 @@ type Metrics = {
...
@@ -34,7 +36,7 @@ type Metrics = {
type
State
=
{
type
State
=
{
fpw
:
number
fpw
:
number
scc
:
Contract
oo
:
OutputOracle
<
any
>
messenger
:
CrossChainMessenger
messenger
:
CrossChainMessenger
highestCheckedBatchIndex
:
number
highestCheckedBatchIndex
:
number
diverged
:
boolean
diverged
:
boolean
...
@@ -65,6 +67,12 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -65,6 +67,12 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
desc
:
'
Batch index to start checking from
'
,
desc
:
'
Batch index to start checking from
'
,
public
:
true
,
public
:
true
,
},
},
bedrock
:
{
validator
:
validators
.
bool
,
default
:
false
,
desc
:
'
Whether or not the service is running against a Bedrock chain
'
,
public
:
true
,
},
},
},
metricsSpec
:
{
metricsSpec
:
{
highestBatchIndex
:
{
highestBatchIndex
:
{
...
@@ -103,24 +111,42 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -103,24 +111,42 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
l2SignerOrProvider
:
this
.
options
.
l2RpcProvider
,
l2SignerOrProvider
:
this
.
options
.
l2RpcProvider
,
l1ChainId
:
await
getChainId
(
this
.
options
.
l1RpcProvider
),
l1ChainId
:
await
getChainId
(
this
.
options
.
l1RpcProvider
),
l2ChainId
:
await
getChainId
(
this
.
options
.
l2RpcProvider
),
l2ChainId
:
await
getChainId
(
this
.
options
.
l2RpcProvider
),
bedrock
:
this
.
options
.
bedrock
,
})
})
// Not diverged by default.
// Not diverged by default.
this
.
state
.
diverged
=
false
this
.
state
.
diverged
=
false
// We use this a lot, a bit cleaner to pull out to the top level of the state object.
// We use this a lot, a bit cleaner to pull out to the top level of the state object.
this
.
state
.
scc
=
this
.
state
.
messenger
.
contracts
.
l1
.
StateCommitmentChain
this
.
state
.
fpw
=
await
this
.
state
.
messenger
.
getChallengePeriodSeconds
()
this
.
state
.
fpw
=
(
await
this
.
state
.
scc
.
FRAUD_PROOF_WINDOW
()).
toNumber
()
if
(
this
.
options
.
bedrock
)
{
const
oo
=
this
.
state
.
messenger
.
contracts
.
l1
.
L2OutputOracle
this
.
state
.
oo
=
{
contract
:
oo
,
filter
:
oo
.
filters
.
OutputProposed
(),
getTotalElements
:
async
()
=>
oo
.
latestOutputIndex
(),
getEventIndex
:
(
args
)
=>
args
.
l2OutputIndex
,
}
}
else
{
const
oo
=
this
.
state
.
messenger
.
contracts
.
l1
.
StateCommitmentChain
this
.
state
.
oo
=
{
contract
:
oo
,
filter
:
oo
.
filters
.
StateBatchAppended
(),
getTotalElements
:
async
()
=>
oo
.
getTotalBatches
(),
getEventIndex
:
(
args
)
=>
args
.
_batchIndex
,
}
}
// Populate the event cache.
// Populate the event cache.
this
.
logger
.
info
(
`warming event cache, this might take a while...`
)
this
.
logger
.
info
(
`warming event cache, this might take a while...`
)
await
update
StateBatchEventCache
(
this
.
state
.
scc
)
await
update
OracleCache
(
this
.
state
.
oo
)
// Figure out where to start syncing from.
// Figure out where to start syncing from.
if
(
this
.
options
.
startBatchIndex
===
-
1
)
{
if
(
this
.
options
.
startBatchIndex
===
-
1
)
{
this
.
logger
.
info
(
`finding appropriate starting height`
)
this
.
logger
.
info
(
`finding appropriate starting height`
)
const
firstUnfinalized
=
await
findFirstUnfinalizedStateBatchIndex
(
const
firstUnfinalized
=
await
findFirstUnfinalizedStateBatchIndex
(
this
.
state
.
scc
this
.
state
.
oo
,
this
.
state
.
fpw
)
)
// We may not have an unfinalized batches in the case where no batches have been submitted
// We may not have an unfinalized batches in the case where no batches have been submitted
...
@@ -129,7 +155,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -129,7 +155,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
if
(
firstUnfinalized
===
undefined
)
{
if
(
firstUnfinalized
===
undefined
)
{
this
.
logger
.
info
(
`no unfinalized batches found, starting from latest`
)
this
.
logger
.
info
(
`no unfinalized batches found, starting from latest`
)
this
.
state
.
highestCheckedBatchIndex
=
(
this
.
state
.
highestCheckedBatchIndex
=
(
await
this
.
state
.
scc
.
getTotalBatche
s
()
await
this
.
state
.
oo
.
getTotalElement
s
()
).
toNumber
()
).
toNumber
()
}
else
{
}
else
{
this
.
state
.
highestCheckedBatchIndex
=
firstUnfinalized
this
.
state
.
highestCheckedBatchIndex
=
firstUnfinalized
...
@@ -141,6 +167,14 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -141,6 +167,14 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
this
.
logger
.
info
(
`starting height`
,
{
this
.
logger
.
info
(
`starting height`
,
{
startBatchIndex
:
this
.
state
.
highestCheckedBatchIndex
,
startBatchIndex
:
this
.
state
.
highestCheckedBatchIndex
,
})
})
// Set the initial metrics.
this
.
metrics
.
highestBatchIndex
.
set
(
{
type
:
'
checked
'
,
},
this
.
state
.
highestCheckedBatchIndex
)
}
}
async
routes
(
router
:
ExpressRouter
):
Promise
<
void
>
{
async
routes
(
router
:
ExpressRouter
):
Promise
<
void
>
{
...
@@ -154,7 +188,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -154,7 +188,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
async
main
():
Promise
<
void
>
{
async
main
():
Promise
<
void
>
{
let
latestBatchIndex
:
number
let
latestBatchIndex
:
number
try
{
try
{
latestBatchIndex
=
(
await
this
.
state
.
scc
.
getTotalBatche
s
()).
toNumber
()
latestBatchIndex
=
(
await
this
.
state
.
oo
.
getTotalElement
s
()).
toNumber
()
}
catch
(
err
)
{
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
error
:
err
,
...
@@ -189,7 +223,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -189,7 +223,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
let
event
:
PartialEvent
let
event
:
PartialEvent
try
{
try
{
event
=
await
findEventForStateBatch
(
event
=
await
findEventForStateBatch
(
this
.
state
.
scc
,
this
.
state
.
oo
,
this
.
state
.
highestCheckedBatchIndex
this
.
state
.
highestCheckedBatchIndex
)
)
}
catch
(
err
)
{
}
catch
(
err
)
{
...
@@ -206,34 +240,6 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -206,34 +240,6 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
return
return
}
}
let
batchTransaction
:
Transaction
try
{
batchTransaction
=
await
this
.
options
.
l1RpcProvider
.
getTransaction
(
event
.
transactionHash
)
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l1
'
,
section
:
'
getTransaction
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l1
'
,
section
:
'
getTransaction
'
,
})
await
sleep
(
15000
)
return
}
const
[
stateRoots
]
=
this
.
state
.
scc
.
interface
.
decodeFunctionData
(
'
appendStateBatch
'
,
batchTransaction
.
data
)
const
batchStart
=
event
.
args
.
_prevTotalElements
.
toNumber
()
+
1
const
batchSize
=
event
.
args
.
_batchSize
.
toNumber
()
const
batchEnd
=
batchStart
+
batchSize
let
latestBlock
:
number
let
latestBlock
:
number
try
{
try
{
latestBlock
=
await
this
.
options
.
l2RpcProvider
.
getBlockNumber
()
latestBlock
=
await
this
.
options
.
l2RpcProvider
.
getBlockNumber
()
...
@@ -251,55 +257,80 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -251,55 +257,80 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
return
return
}
}
if
(
latestBlock
<
batchEnd
)
{
if
(
this
.
options
.
bedrock
)
{
this
.
logger
.
info
(
`node is behind, waiting for sync`
,
{
if
(
latestBlock
<
event
.
args
.
l2BlockNumber
.
toNumber
())
{
batchEnd
,
this
.
logger
.
info
(
`node is behind, waiting for sync`
,
{
latestBlock
,
batchEnd
:
event
.
args
.
l2BlockNumber
.
toNumber
(),
})
latestBlock
,
return
})
}
return
}
// `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into
let
targetBlock
:
any
// multiple requests of maximum 1000 blocks in the case that batchSize > 1000.
let
blocks
:
any
[]
=
[]
for
(
let
i
=
0
;
i
<
batchSize
;
i
+=
1000
)
{
let
newBlocks
:
any
[]
try
{
try
{
newBlocks
=
await
(
targetBlock
=
await
(
this
.
options
.
l2RpcProvider
as
ethers
.
providers
.
JsonRpcProvider
this
.
options
.
l2RpcProvider
as
ethers
.
providers
.
JsonRpcProvider
).
send
(
'
eth_getBlockRange
'
,
[
).
send
(
'
eth_getBlockByNumber
'
,
[
toRpcHexString
(
batchStart
+
i
),
toRpcHexString
(
event
.
args
.
l2BlockNumber
.
toNumber
()),
toRpcHexString
(
batchStart
+
i
+
Math
.
min
(
batchSize
-
i
,
1000
)
-
1
),
false
,
false
,
])
])
}
catch
(
err
)
{
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
error
:
err
,
node
:
'
l2
'
,
node
:
'
l2
'
,
section
:
'
getBlock
Range
'
,
section
:
'
getBlock
'
,
})
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l2
'
,
layer
:
'
l2
'
,
section
:
'
getBlock
Range
'
,
section
:
'
getBlock
'
,
})
})
await
sleep
(
15000
)
await
sleep
(
15000
)
return
return
}
}
blocks
=
blocks
.
concat
(
newBlocks
)
let
messagePasserProofResponse
:
any
}
try
{
messagePasserProofResponse
=
await
(
this
.
options
.
l2RpcProvider
as
ethers
.
providers
.
JsonRpcProvider
).
send
(
'
eth_getProof
'
,
[
this
.
state
.
messenger
.
contracts
.
l2
.
BedrockMessagePasser
.
address
,
[],
toRpcHexString
(
event
.
args
.
l2BlockNumber
.
toNumber
()),
])
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l2
'
,
section
:
'
getProof
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l2
'
,
section
:
'
getProof
'
,
})
await
sleep
(
15000
)
return
}
const
outputRoot
=
ethers
.
utils
.
solidityKeccak256
(
[
'
uint256
'
,
'
bytes32
'
,
'
bytes32
'
,
'
bytes32
'
],
[
0
,
targetBlock
.
stateRoot
,
messagePasserProofResponse
.
storageHash
,
targetBlock
.
hash
,
]
)
for
(
const
[
i
,
stateRoot
]
of
stateRoots
.
entries
())
{
if
(
outputRoot
!==
event
.
args
.
outputRoot
)
{
if
(
blocks
[
i
].
stateRoot
!==
stateRoot
)
{
this
.
state
.
diverged
=
true
this
.
state
.
diverged
=
true
this
.
metrics
.
isCurrentlyMismatched
.
set
(
1
)
this
.
metrics
.
isCurrentlyMismatched
.
set
(
1
)
this
.
logger
.
error
(
`state root mismatch`
,
{
this
.
logger
.
error
(
`state root mismatch`
,
{
blockNumber
:
blocks
[
i
]
.
number
,
blockNumber
:
targetBlock
.
number
,
expectedStateRoot
:
blocks
[
i
].
state
Root
,
expectedStateRoot
:
event
.
args
.
output
Root
,
actualStateRoot
:
state
Root
,
actualStateRoot
:
output
Root
,
finalizationTime
:
dateformat
(
finalizationTime
:
dateformat
(
new
Date
(
new
Date
(
(
ethers
.
BigNumber
.
from
(
blocks
[
i
]
.
timestamp
).
toNumber
()
+
(
ethers
.
BigNumber
.
from
(
targetBlock
.
timestamp
).
toNumber
()
+
this
.
state
.
fpw
)
*
this
.
state
.
fpw
)
*
1000
1000
),
),
...
@@ -308,8 +339,99 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
...
@@ -308,8 +339,99 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
})
})
return
return
}
}
}
else
{
let
batchTransaction
:
Transaction
try
{
batchTransaction
=
await
this
.
options
.
l1RpcProvider
.
getTransaction
(
event
.
transactionHash
)
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l1
'
,
section
:
'
getTransaction
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l1
'
,
section
:
'
getTransaction
'
,
})
await
sleep
(
15000
)
return
}
const
[
stateRoots
]
=
this
.
state
.
oo
.
contract
.
interface
.
decodeFunctionData
(
'
appendStateBatch
'
,
batchTransaction
.
data
)
const
batchStart
=
event
.
args
.
_prevTotalElements
.
toNumber
()
+
1
const
batchSize
=
event
.
args
.
_batchSize
.
toNumber
()
const
batchEnd
=
batchStart
+
batchSize
if
(
latestBlock
<
batchEnd
)
{
this
.
logger
.
info
(
`node is behind, waiting for sync`
,
{
batchEnd
,
latestBlock
,
})
return
}
// `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into
// multiple requests of maximum 1000 blocks in the case that batchSize > 1000.
let
blocks
:
any
[]
=
[]
for
(
let
i
=
0
;
i
<
batchSize
;
i
+=
1000
)
{
let
newBlocks
:
any
[]
try
{
newBlocks
=
await
(
this
.
options
.
l2RpcProvider
as
ethers
.
providers
.
JsonRpcProvider
).
send
(
'
eth_getBlockRange
'
,
[
toRpcHexString
(
batchStart
+
i
),
toRpcHexString
(
batchStart
+
i
+
Math
.
min
(
batchSize
-
i
,
1000
)
-
1
),
false
,
])
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l2
'
,
section
:
'
getBlockRange
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l2
'
,
section
:
'
getBlockRange
'
,
})
await
sleep
(
15000
)
return
}
blocks
=
blocks
.
concat
(
newBlocks
)
}
for
(
const
[
i
,
stateRoot
]
of
stateRoots
.
entries
())
{
if
(
blocks
[
i
].
stateRoot
!==
stateRoot
)
{
this
.
state
.
diverged
=
true
this
.
metrics
.
isCurrentlyMismatched
.
set
(
1
)
this
.
logger
.
error
(
`state root mismatch`
,
{
blockNumber
:
blocks
[
i
].
number
,
expectedStateRoot
:
blocks
[
i
].
stateRoot
,
actualStateRoot
:
stateRoot
,
finalizationTime
:
dateformat
(
new
Date
(
(
ethers
.
BigNumber
.
from
(
blocks
[
i
].
timestamp
).
toNumber
()
+
this
.
state
.
fpw
)
*
1000
),
'
mmmm dS, yyyy, h:MM:ss TT
'
),
})
return
}
}
}
}
this
.
logger
.
info
(
`checked batch ok`
,
{
batchIndex
:
this
.
state
.
highestCheckedBatchIndex
,
})
this
.
state
.
highestCheckedBatchIndex
++
this
.
state
.
highestCheckedBatchIndex
++
this
.
metrics
.
highestBatchIndex
.
set
(
this
.
metrics
.
highestBatchIndex
.
set
(
{
{
...
...
This diff is collapsed.
Click to expand it.
packages/fault-detector/test/helpers.spec.ts
View file @
2c0ad576
...
@@ -12,6 +12,7 @@ import { expect } from './setup'
...
@@ -12,6 +12,7 @@ import { expect } from './setup'
import
{
import
{
findEventForStateBatch
,
findEventForStateBatch
,
findFirstUnfinalizedStateBatchIndex
,
findFirstUnfinalizedStateBatchIndex
,
OutputOracle
,
}
from
'
../src
'
}
from
'
../src
'
describe
(
'
helpers
'
,
()
=>
{
describe
(
'
helpers
'
,
()
=>
{
...
@@ -28,6 +29,7 @@ describe('helpers', () => {
...
@@ -28,6 +29,7 @@ describe('helpers', () => {
let
AddressManager
:
Contract
let
AddressManager
:
Contract
let
ChainStorageContainer
:
Contract
let
ChainStorageContainer
:
Contract
let
StateCommitmentChain
:
Contract
let
StateCommitmentChain
:
Contract
let
oracle
:
OutputOracle
<
any
>
beforeEach
(
async
()
=>
{
beforeEach
(
async
()
=>
{
// Set up fakes
// Set up fakes
FakeBondManager
=
await
smock
.
fake
(
getContractInterface
(
'
BondManager
'
))
FakeBondManager
=
await
smock
.
fake
(
getContractInterface
(
'
BondManager
'
))
...
@@ -67,6 +69,13 @@ describe('helpers', () => {
...
@@ -67,6 +69,13 @@ describe('helpers', () => {
// Set up mock returns
// Set up mock returns
FakeCanonicalTransactionChain
.
getTotalElements
.
returns
(
1000000000
)
// just needs to be large
FakeCanonicalTransactionChain
.
getTotalElements
.
returns
(
1000000000
)
// just needs to be large
FakeBondManager
.
isCollateralized
.
returns
(
true
)
FakeBondManager
.
isCollateralized
.
returns
(
true
)
oracle
=
{
contract
:
StateCommitmentChain
,
filter
:
StateCommitmentChain
.
filters
.
StateBatchAppended
(),
getTotalElements
:
async
()
=>
StateCommitmentChain
.
getTotalBatches
(),
getEventIndex
:
(
args
:
any
)
=>
args
.
_batchIndex
,
}
})
})
describe
(
'
findEventForStateBatch
'
,
()
=>
{
describe
(
'
findEventForStateBatch
'
,
()
=>
{
...
@@ -79,7 +88,7 @@ describe('helpers', () => {
...
@@ -79,7 +88,7 @@ describe('helpers', () => {
})
})
it
(
'
should return the event
'
,
async
()
=>
{
it
(
'
should return the event
'
,
async
()
=>
{
const
event
=
await
findEventForStateBatch
(
StateCommitmentChain
,
0
)
const
event
=
await
findEventForStateBatch
(
oracle
,
0
)
expect
(
event
.
args
.
_batchIndex
).
to
.
equal
(
0
)
expect
(
event
.
args
.
_batchIndex
).
to
.
equal
(
0
)
})
})
...
@@ -88,7 +97,7 @@ describe('helpers', () => {
...
@@ -88,7 +97,7 @@ describe('helpers', () => {
describe
(
'
when the event does not exist
'
,
()
=>
{
describe
(
'
when the event does not exist
'
,
()
=>
{
it
(
'
should throw an error
'
,
async
()
=>
{
it
(
'
should throw an error
'
,
async
()
=>
{
await
expect
(
await
expect
(
findEventForStateBatch
(
StateCommitmentChain
,
0
)
findEventForStateBatch
(
oracle
,
0
)
).
to
.
eventually
.
be
.
rejectedWith
(
'
unable to find event for batch
'
)
).
to
.
eventually
.
be
.
rejectedWith
(
'
unable to find event for batch
'
)
})
})
})
})
...
@@ -119,7 +128,8 @@ describe('helpers', () => {
...
@@ -119,7 +128,8 @@ describe('helpers', () => {
it
(
'
should find the first batch older than the FPW
'
,
async
()
=>
{
it
(
'
should find the first batch older than the FPW
'
,
async
()
=>
{
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
StateCommitmentChain
oracle
,
challengeWindowSeconds
)
)
expect
(
first
).
to
.
equal
(
1
)
expect
(
first
).
to
.
equal
(
1
)
...
@@ -144,7 +154,8 @@ describe('helpers', () => {
...
@@ -144,7 +154,8 @@ describe('helpers', () => {
it
(
'
should return zero
'
,
async
()
=>
{
it
(
'
should return zero
'
,
async
()
=>
{
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
StateCommitmentChain
oracle
,
challengeWindowSeconds
)
)
expect
(
first
).
to
.
equal
(
0
)
expect
(
first
).
to
.
equal
(
0
)
...
@@ -177,7 +188,8 @@ describe('helpers', () => {
...
@@ -177,7 +188,8 @@ describe('helpers', () => {
it
(
'
should return undefined
'
,
async
()
=>
{
it
(
'
should return undefined
'
,
async
()
=>
{
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
const
first
=
await
findFirstUnfinalizedStateBatchIndex
(
StateCommitmentChain
oracle
,
challengeWindowSeconds
)
)
expect
(
first
).
to
.
equal
(
undefined
)
expect
(
first
).
to
.
equal
(
undefined
)
...
...
This diff is collapsed.
Click to expand it.
yarn.lock
View file @
2c0ad576
...
@@ -12090,7 +12090,7 @@ level-ws@^2.0.0:
...
@@ -12090,7 +12090,7 @@ level-ws@^2.0.0:
readable-stream "^3.1.0"
readable-stream "^3.1.0"
xtend "^4.0.1"
xtend "^4.0.1"
level@^6.0.1
:
"level6@npm:level@^6.0.1"
:
version "6.0.1"
version "6.0.1"
resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6"
resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6"
integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==
integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment