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
3202f0ae
Unverified
Commit
3202f0ae
authored
Feb 03, 2023
by
mergify[bot]
Committed by
GitHub
Feb 03, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into seb/fix-spec-withdrawals
parents
cc64c499
42421ce9
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
317 additions
and
10 deletions
+317
-10
quick-crabs-argue.md
.changeset/quick-crabs-argue.md
+5
-0
wet-files-knock.md
.changeset/wet-files-knock.md
+5
-0
attributes_queue.go
op-node/rollup/derive/attributes_queue.go
+1
-1
engine_queue.go
op-node/rollup/derive/engine_queue.go
+1
-1
.env.example
packages/chain-mon/.env.example
+11
-0
package.json
packages/chain-mon/package.json
+5
-1
index.ts
packages/chain-mon/src/index.ts
+1
-0
service.ts
packages/chain-mon/src/wd-mon/service.ts
+232
-0
.eslintrc.js
packages/sdk/.eslintrc.js
+43
-0
l2-provider.ts
packages/sdk/src/l2-provider.ts
+1
-2
assert.ts
packages/sdk/src/utils/assert.ts
+5
-0
coercion.ts
packages/sdk/src/utils/coercion.ts
+1
-2
derivation.md
specs/derivation.md
+3
-0
yarn.lock
yarn.lock
+3
-3
No files found.
.changeset/quick-crabs-argue.md
0 → 100644
View file @
3202f0ae
---
'
@eth-optimism/sdk'
:
patch
---
Remove assert node builtin from sdk
.changeset/wet-files-knock.md
0 → 100644
View file @
3202f0ae
---
'
@eth-optimism/chain-mon'
:
patch
---
Added withdrawal monitoring to identify proven withdrawals not included in the L2ToL1MessagePasser's sentMessages mapping
op-node/rollup/derive/attributes_queue.go
View file @
3202f0ae
...
@@ -20,7 +20,7 @@ import (
...
@@ -20,7 +20,7 @@ import (
// It also buffers batches that have been output because multiple batches can
// It also buffers batches that have been output because multiple batches can
// be created at once.
// be created at once.
//
//
// This stage can be reset by clearing it
'
s batch buffer.
// This stage can be reset by clearing its batch buffer.
// This stage does not need to retain any references to L1 blocks.
// This stage does not need to retain any references to L1 blocks.
type
AttributesBuilder
interface
{
type
AttributesBuilder
interface
{
...
...
op-node/rollup/derive/engine_queue.go
View file @
3202f0ae
...
@@ -517,7 +517,7 @@ func (eq *EngineQueue) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPa
...
@@ -517,7 +517,7 @@ func (eq *EngineQueue) ConfirmPayload(ctx context.Context) (out *eth.ExecutionPa
return
nil
,
BlockInsertPrestateErr
,
fmt
.
Errorf
(
"cannot complete payload building: not currently building a payload"
)
return
nil
,
BlockInsertPrestateErr
,
fmt
.
Errorf
(
"cannot complete payload building: not currently building a payload"
)
}
}
if
eq
.
buildingOnto
.
Hash
!=
eq
.
unsafeHead
.
Hash
{
// E.g. when safe-attributes consolidation fails, it will drop the existing work.
if
eq
.
buildingOnto
.
Hash
!=
eq
.
unsafeHead
.
Hash
{
// E.g. when safe-attributes consolidation fails, it will drop the existing work.
eq
.
log
.
Warn
(
"engine is building block that reorgs previous usafe head"
,
"onto"
,
eq
.
buildingOnto
,
"unsafe"
,
eq
.
unsafeHead
)
eq
.
log
.
Warn
(
"engine is building block that reorgs previous u
n
safe head"
,
"onto"
,
eq
.
buildingOnto
,
"unsafe"
,
eq
.
unsafeHead
)
}
}
fc
:=
eth
.
ForkchoiceState
{
fc
:=
eth
.
ForkchoiceState
{
HeadBlockHash
:
common
.
Hash
{},
// gets overridden
HeadBlockHash
:
common
.
Hash
{},
// gets overridden
...
...
packages/chain-mon/.env.example
View file @
3202f0ae
...
@@ -7,3 +7,14 @@ DRIPPIE_MON__RPC=
...
@@ -7,3 +7,14 @@ DRIPPIE_MON__RPC=
# Address of the Drippie contract
# Address of the Drippie contract
DRIPPIE_MON__DRIPPIE_ADDRESS=
DRIPPIE_MON__DRIPPIE_ADDRESS=
###############################################################################
# ↓ wd-mon ↓ #
###############################################################################
# RPCs pointing to a base chain and ptimism chain
TWO_STEP_MONITOR__L1_RPC_PROVIDER=
TWO_STEP_MONITOR__L2_RPC_PROVIDER=
# The block number to start monitoring from
TWO_STEP_MONITOR__START_BLOCK_NUMBER=
packages/chain-mon/package.json
View file @
3202f0ae
...
@@ -10,6 +10,7 @@
...
@@ -10,6 +10,7 @@
],
],
"scripts"
:
{
"scripts"
:
{
"start:drippie-mon"
:
"ts-node ./src/drippie-mon/service.ts"
,
"start:drippie-mon"
:
"ts-node ./src/drippie-mon/service.ts"
,
"start:wd-mon"
:
"ts-node ./src/wd-mon/service.ts"
,
"test:coverage"
:
"echo 'No tests defined.'"
,
"test:coverage"
:
"echo 'No tests defined.'"
,
"build"
:
"tsc -p ./tsconfig.json"
,
"build"
:
"tsc -p ./tsconfig.json"
,
"clean"
:
"rimraf dist/ ./tsconfig.tsbuildinfo"
,
"clean"
:
"rimraf dist/ ./tsconfig.tsbuildinfo"
,
...
@@ -35,7 +36,10 @@
...
@@ -35,7 +36,10 @@
"@eth-optimism/contracts-periphery"
:
"1.0.7"
,
"@eth-optimism/contracts-periphery"
:
"1.0.7"
,
"@eth-optimism/core-utils"
:
"0.12.0"
,
"@eth-optimism/core-utils"
:
"0.12.0"
,
"@eth-optimism/sdk"
:
"1.10.1"
,
"@eth-optimism/sdk"
:
"1.10.1"
,
"ethers"
:
"^5.7.0"
"ethers"
:
"^5.7.0"
,
"@types/dateformat"
:
"^5.0.0"
,
"chai-as-promised"
:
"^7.1.1"
,
"dateformat"
:
"^4.5.1"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
...
...
packages/chain-mon/src/index.ts
View file @
3202f0ae
export
*
from
'
./drippie-mon/service
'
export
*
from
'
./drippie-mon/service
'
export
*
from
'
./wd-mon/service
'
packages/chain-mon/src/wd-mon/service.ts
0 → 100644
View file @
3202f0ae
import
{
BaseServiceV2
,
StandardOptions
,
ExpressRouter
,
Gauge
,
validators
,
waitForProvider
,
}
from
'
@eth-optimism/common-ts
'
import
{
CrossChainMessenger
}
from
'
@eth-optimism/sdk
'
import
{
getChainId
,
sleep
}
from
'
@eth-optimism/core-utils
'
import
{
Provider
}
from
'
@ethersproject/abstract-provider
'
import
{
Event
}
from
'
ethers
'
import
dateformat
from
'
dateformat
'
import
{
version
}
from
'
../../package.json
'
type
Options
=
{
l1RpcProvider
:
Provider
l2RpcProvider
:
Provider
startBlockNumber
:
number
sleepTimeMs
:
number
}
type
Metrics
=
{
withdrawalsValidated
:
Gauge
isDetectingForgeries
:
Gauge
nodeConnectionFailures
:
Gauge
}
type
State
=
{
messenger
:
CrossChainMessenger
highestUncheckedBlockNumber
:
number
finalizationWindow
:
number
forgeryDetected
:
boolean
}
export
class
WithdrawalMonitor
extends
BaseServiceV2
<
Options
,
Metrics
,
State
>
{
constructor
(
options
?:
Partial
<
Options
&
StandardOptions
>
)
{
super
({
version
,
name
:
'
two-step-monitor
'
,
loop
:
true
,
options
:
{
loopIntervalMs
:
1000
,
...
options
,
},
optionsSpec
:
{
l1RpcProvider
:
{
validator
:
validators
.
provider
,
desc
:
'
Provider for interacting with L1
'
,
},
l2RpcProvider
:
{
validator
:
validators
.
provider
,
desc
:
'
Provider for interacting with L2
'
,
},
startBlockNumber
:
{
validator
:
validators
.
num
,
default
:
-
1
,
desc
:
'
L1 block number to start checking from
'
,
public
:
true
,
},
sleepTimeMs
:
{
validator
:
validators
.
num
,
default
:
15000
,
desc
:
'
Time in ms to sleep when waiting for a node
'
,
public
:
true
,
},
},
metricsSpec
:
{
withdrawalsValidated
:
{
type
:
Gauge
,
desc
:
'
Latest L1 Block (checked and known)
'
,
labels
:
[
'
type
'
],
},
isDetectingForgeries
:
{
type
:
Gauge
,
desc
:
'
0 if state is ok. 1 or more if forged withdrawals are detected.
'
,
},
nodeConnectionFailures
:
{
type
:
Gauge
,
desc
:
'
Number of times node connection has failed
'
,
labels
:
[
'
layer
'
,
'
section
'
],
},
},
})
}
async
init
():
Promise
<
void
>
{
// Connect to L1.
await
waitForProvider
(
this
.
options
.
l1RpcProvider
,
{
logger
:
this
.
logger
,
name
:
'
L1
'
,
})
// Connect to L2.
await
waitForProvider
(
this
.
options
.
l2RpcProvider
,
{
logger
:
this
.
logger
,
name
:
'
L2
'
,
})
this
.
state
.
messenger
=
new
CrossChainMessenger
({
l1SignerOrProvider
:
this
.
options
.
l1RpcProvider
,
l2SignerOrProvider
:
this
.
options
.
l2RpcProvider
,
l1ChainId
:
await
getChainId
(
this
.
options
.
l1RpcProvider
),
l2ChainId
:
await
getChainId
(
this
.
options
.
l2RpcProvider
),
})
// Not detected by default.
this
.
state
.
forgeryDetected
=
false
// For now we'll just start take it from the env or the tip of the chain
if
(
this
.
options
.
startBlockNumber
===
-
1
)
{
this
.
state
.
highestUncheckedBlockNumber
=
await
this
.
options
.
l1RpcProvider
.
getBlockNumber
()
}
else
{
this
.
state
.
highestUncheckedBlockNumber
=
this
.
options
.
startBlockNumber
}
this
.
logger
.
info
(
`starting L1 block height`
,
{
startBlockNumber
:
this
.
state
.
highestUncheckedBlockNumber
,
})
}
// K8s healthcheck
async
routes
(
router
:
ExpressRouter
):
Promise
<
void
>
{
router
.
get
(
'
/healthz
'
,
async
(
req
,
res
)
=>
{
return
res
.
status
(
200
).
json
({
ok
:
!
this
.
state
.
forgeryDetected
,
})
})
}
async
main
():
Promise
<
void
>
{
// Get current block number
let
latestL1BlockNumber
:
number
try
{
latestL1BlockNumber
=
await
this
.
options
.
l1RpcProvider
.
getBlockNumber
()
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l1
'
,
section
:
'
getBlockNumber
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
chainId
:
this
.
state
.
messenger
.
l1ChainId
,
section
:
'
getBlockNumber
'
,
})
await
sleep
(
this
.
options
.
sleepTimeMs
)
return
}
// See if we have a new unchecked block
if
(
latestL1BlockNumber
<=
this
.
state
.
highestUncheckedBlockNumber
)
{
// The RPC provider is behind us, wait a bit
await
sleep
(
this
.
options
.
sleepTimeMs
)
return
}
this
.
logger
.
info
(
`checking recent blocks`
,
{
fromBlockNumber
:
this
.
state
.
highestUncheckedBlockNumber
,
toBlockNumber
:
latestL1BlockNumber
,
})
// Perform the check
let
proofEvents
:
Event
[]
try
{
// The query includes events in the blockNumbers given as the last two arguments
proofEvents
=
await
this
.
state
.
messenger
.
contracts
.
l1
.
OptimismPortal
.
queryFilter
(
this
.
state
.
messenger
.
contracts
.
l1
.
OptimismPortal
.
filters
.
WithdrawalProven
(),
this
.
state
.
highestUncheckedBlockNumber
,
latestL1BlockNumber
)
}
catch
(
err
)
{
this
.
logger
.
error
(
`got error when connecting to node`
,
{
error
:
err
,
node
:
'
l1
'
,
section
:
'
querying for WithdrawalProven events
'
,
})
this
.
metrics
.
nodeConnectionFailures
.
inc
({
layer
:
'
l1
'
,
section
:
'
querying for WithdrawalProven events
'
,
})
// connection error, wait then restart
await
sleep
(
this
.
options
.
sleepTimeMs
)
return
}
for
(
const
proofEvent
of
proofEvents
)
{
const
exists
=
await
this
.
state
.
messenger
.
contracts
.
l2
.
BedrockMessagePasser
.
sentMessages
(
proofEvent
.
args
.
withdrawalHash
)
const
provenAt
=
`
${
(
dateformat
(
new
Date
(
(
await
this
.
options
.
l1RpcProvider
.
getBlock
(
proofEvent
.
blockHash
))
.
timestamp
*
1000
)
),
'
mmmm dS, yyyy, h:MM:ss TT
'
,
true
)
}
UTC`
if
(
exists
)
{
this
.
metrics
.
withdrawalsValidated
.
inc
()
this
.
logger
.
info
(
`valid withdrawal`
,
{
withdrawalHash
:
proofEvent
.
args
.
withdrawalHash
,
provenAt
,
})
}
else
{
this
.
logger
.
error
(
`withdrawalHash not seen on L2`
,
{
withdrawalHash
:
proofEvent
.
args
.
withdrawalHash
,
provenAt
,
})
this
.
state
.
forgeryDetected
=
true
this
.
metrics
.
isDetectingForgeries
.
set
(
1
)
return
}
}
this
.
state
.
highestUncheckedBlockNumber
=
latestL1BlockNumber
+
1
// If we got through the above without throwing an error, we should be fine to reset.
this
.
state
.
forgeryDetected
=
false
this
.
metrics
.
isDetectingForgeries
.
set
(
0
)
}
}
if
(
require
.
main
===
module
)
{
const
service
=
new
WithdrawalMonitor
()
service
.
run
()
}
packages/sdk/.eslintrc.js
View file @
3202f0ae
module
.
exports
=
{
module
.
exports
=
{
extends
:
'
../../.eslintrc.js
'
,
extends
:
'
../../.eslintrc.js
'
,
overrides
:
[
{
files
:
[
'
src/**/*.ts
'
],
rules
:
{
'
no-restricted-imports
'
:
[
'
error
'
,
'
assert
'
,
'
buffer
'
,
'
child_process
'
,
'
cluster
'
,
'
crypto
'
,
'
dgram
'
,
'
dns
'
,
'
domain
'
,
'
events
'
,
'
freelist
'
,
'
fs
'
,
'
http
'
,
'
https
'
,
'
module
'
,
'
net
'
,
'
os
'
,
'
path
'
,
'
punycode
'
,
'
querystring
'
,
'
readline
'
,
'
repl
'
,
'
smalloc
'
,
'
stream
'
,
'
string_decoder
'
,
'
sys
'
,
'
timers
'
,
'
tls
'
,
'
tracing
'
,
'
tty
'
,
'
url
'
,
'
util
'
,
'
vm
'
,
'
zlib
'
,
],
},
},
],
}
}
packages/sdk/src/l2-provider.ts
View file @
3202f0ae
import
assert
from
'
assert
'
import
{
Provider
,
TransactionRequest
}
from
'
@ethersproject/abstract-provider
'
import
{
Provider
,
TransactionRequest
}
from
'
@ethersproject/abstract-provider
'
import
{
serialize
}
from
'
@ethersproject/transactions
'
import
{
serialize
}
from
'
@ethersproject/transactions
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
import
{
predeploys
,
getContractInterface
}
from
'
@eth-optimism/contracts
'
import
{
predeploys
,
getContractInterface
}
from
'
@eth-optimism/contracts
'
import
cloneDeep
from
'
lodash/cloneDeep
'
import
cloneDeep
from
'
lodash/cloneDeep
'
import
{
assert
}
from
'
./utils/assert
'
import
{
L2Provider
,
ProviderLike
,
NumberLike
}
from
'
./interfaces
'
import
{
L2Provider
,
ProviderLike
,
NumberLike
}
from
'
./interfaces
'
import
{
toProvider
,
toNumber
,
toBigNumber
}
from
'
./utils
'
import
{
toProvider
,
toNumber
,
toBigNumber
}
from
'
./utils
'
...
...
packages/sdk/src/utils/assert.ts
0 → 100644
View file @
3202f0ae
export
const
assert
=
(
condition
:
boolean
,
message
:
string
):
void
=>
{
if
(
!
condition
)
{
throw
new
Error
(
message
)
}
}
packages/sdk/src/utils/coercion.ts
View file @
3202f0ae
import
assert
from
'
assert
'
import
{
import
{
Provider
,
Provider
,
TransactionReceipt
,
TransactionReceipt
,
...
@@ -8,6 +6,7 @@ import {
...
@@ -8,6 +6,7 @@ import {
import
{
Signer
}
from
'
@ethersproject/abstract-signer
'
import
{
Signer
}
from
'
@ethersproject/abstract-signer
'
import
{
ethers
,
BigNumber
}
from
'
ethers
'
import
{
ethers
,
BigNumber
}
from
'
ethers
'
import
{
assert
}
from
'
./assert
'
import
{
import
{
SignerOrProviderLike
,
SignerOrProviderLike
,
ProviderLike
,
ProviderLike
,
...
...
specs/derivation.md
View file @
3202f0ae
...
@@ -710,6 +710,8 @@ enact the change, as linear rewinds of the tip of the chain may not be supported
...
@@ -710,6 +710,8 @@ enact the change, as linear rewinds of the tip of the chain may not be supported
#### L1-sync: payload attributes processing
#### L1-sync: payload attributes processing
[
exec-engine-comm
]:
exec-engine.md#engine-api
If the safe and unsafe L2 heads are identical (whether because of failed consolidation or not), we send the L2 payload
If the safe and unsafe L2 heads are identical (whether because of failed consolidation or not), we send the L2 payload
attributes to the execution engine to be constructed into a proper L2 block.
attributes to the execution engine to be constructed into a proper L2 block.
This L2 block will then become both the new L2 safe and unsafe head.
This L2 block will then become both the new L2 safe and unsafe head.
...
@@ -728,6 +730,7 @@ The payload attributes are then processed with a sequence of:
...
@@ -728,6 +730,7 @@ The payload attributes are then processed with a sequence of:
-
`engine_forkchoiceUpdatedV1`
with current forkchoice state of the stage, and the attributes to start block building.
-
`engine_forkchoiceUpdatedV1`
with current forkchoice state of the stage, and the attributes to start block building.
-
Non-deterministic sources, like the tx-pool, must be disabled to reconstruct the expected block.
-
Non-deterministic sources, like the tx-pool, must be disabled to reconstruct the expected block.
-
`engine_getPayload`
to retrieve the payload, by the payload-ID in the result of the previous step.
-
`engine_getPayload`
to retrieve the payload, by the payload-ID in the result of the previous step.
-
`engine_newPayload`
to import the new payload into the execution engine.
-
`engine_forkchoiceUpdatedV1`
to make the new payload canonical,
-
`engine_forkchoiceUpdatedV1`
to make the new payload canonical,
now with a change of both
`safe`
and
`unsafe`
fields to refer to the payload, and no payload attributes.
now with a change of both
`safe`
and
`unsafe`
fields to refer to the payload, and no payload attributes.
...
...
yarn.lock
View file @
3202f0ae
...
@@ -10580,9 +10580,9 @@ http-basic@^8.1.1:
...
@@ -10580,9 +10580,9 @@ http-basic@^8.1.1:
parse-cache-control "^1.0.1"
parse-cache-control "^1.0.1"
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
version "4.1.
0
"
version "4.1.
1
"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.
0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390
"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.
1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a
"
integrity sha512-
carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8c
Q==
integrity sha512-
er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYX
Q==
http-errors@1.7.2:
http-errors@1.7.2:
version "1.7.2"
version "1.7.2"
...
...
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