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
7cee3bdb
Commit
7cee3bdb
authored
Jan 24, 2023
by
Mark Tyneway
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-chain-ops: refactor, better comments
parent
1f431310
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
124 additions
and
113 deletions
+124
-113
main.go
op-chain-ops/cmd/withdrawals/main.go
+124
-113
No files found.
op-chain-ops/cmd/withdrawals/main.go
View file @
7cee3bdb
...
...
@@ -32,6 +32,8 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// abiTrue represents the storage representation of the boolean
// value true.
var
abiTrue
=
common
.
Hash
{
31
:
0x01
}
// callFrame represents the response returned from geth's
...
...
@@ -49,12 +51,16 @@ type callFrame struct {
Calls
[]
callFrame
`json:"calls,omitempty"`
}
// BigValue turns a 0x prefixed string into a `big.Int`
func
(
c
*
callFrame
)
BigValue
()
*
big
.
Int
{
v
:=
strings
.
TrimPrefix
(
c
.
Value
,
"0x"
)
b
,
_
:=
new
(
big
.
Int
)
.
SetString
(
v
,
16
)
return
b
}
// suspiciousWithdrawal represents a pending withdrawal that failed for some
// reason after the migration. These are written to disk so that they can
// be manually inspected.
type
suspiciousWithdrawal
struct
{
Withdrawal
*
crossdomain
.
Withdrawal
`json:"withdrawal"`
Legacy
*
crossdomain
.
LegacyWithdrawal
`json:"legacy"`
...
...
@@ -63,107 +69,6 @@ type suspiciousWithdrawal struct {
Reason
string
`json:"reason"`
}
// findWithdrawalCall will find the call frame for the call that
// represents the user's intent.
func
findWithdrawalCall
(
trace
*
callFrame
,
wd
*
crossdomain
.
LegacyWithdrawal
,
l1xdm
common
.
Address
)
*
callFrame
{
isCall
:=
trace
.
Type
==
"CALL"
isTarget
:=
common
.
HexToAddress
(
trace
.
To
)
==
*
wd
.
Target
isFrom
:=
common
.
HexToAddress
(
trace
.
From
)
==
l1xdm
if
isCall
&&
isTarget
&&
isFrom
{
return
trace
}
for
_
,
subcall
:=
range
trace
.
Calls
{
if
call
:=
findWithdrawalCall
(
&
subcall
,
wd
,
l1xdm
);
call
!=
nil
{
return
call
}
}
return
nil
}
// createOutput will create the data required to send a withdrawal
// transaction.
func
createOutput
(
withdrawal
*
crossdomain
.
Withdrawal
,
oracle
*
bindings
.
L2OutputOracle
,
blockNumber
*
big
.
Int
,
clients
*
clients
,
)
(
*
big
.
Int
,
bindings
.
TypesOutputRootProof
,
[][]
byte
,
error
)
{
// compute the storage slot that the withdrawal is stored in
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// find the output index that the withdrawal was committed to in
l2OutputIndex
,
err
:=
oracle
.
GetL2OutputIndexAfter
(
&
bind
.
CallOpts
{},
blockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// fetch the output the commits to the withdrawal using the index
l2Output
,
err
:=
oracle
.
GetL2Output
(
&
bind
.
CallOpts
{},
l2OutputIndex
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
log
.
Debug
(
"L2 output"
,
"index"
,
l2OutputIndex
,
"root"
,
common
.
Bytes2Hex
(
l2Output
.
OutputRoot
[
:
]),
"l2-blocknumber"
,
l2Output
.
L2BlockNumber
,
"timestamp"
,
l2Output
.
Timestamp
,
)
// get the block header committed to in the output
header
,
err
:=
clients
.
L2Client
.
HeaderByNumber
(
context
.
Background
(),
l2Output
.
L2BlockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// get the storage proof for the withdrawal's storage slot
proof
,
err
:=
clients
.
L2GethClient
.
GetProof
(
context
.
Background
(),
predeploys
.
L2ToL1MessagePasserAddr
,
[]
string
{
slot
.
String
()},
blockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
if
count
:=
len
(
proof
.
StorageProof
);
count
!=
1
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"invalid amount of storage proofs: %d"
,
count
)
}
trieNodes
:=
make
([][]
byte
,
len
(
proof
.
StorageProof
[
0
]
.
Proof
))
for
i
,
s
:=
range
proof
.
StorageProof
[
0
]
.
Proof
{
trieNodes
[
i
]
=
common
.
FromHex
(
s
)
}
// create an output root proof
outputRootProof
:=
bindings
.
TypesOutputRootProof
{
Version
:
[
32
]
byte
{},
StateRoot
:
header
.
Root
,
MessagePasserStorageRoot
:
proof
.
StorageHash
,
LatestBlockhash
:
header
.
Hash
(),
}
localOutputRootHash
:=
crypto
.
Keccak256Hash
(
outputRootProof
.
Version
[
:
],
outputRootProof
.
StateRoot
[
:
],
outputRootProof
.
MessagePasserStorageRoot
[
:
],
outputRootProof
.
LatestBlockhash
[
:
],
)
// ensure that the locally computed hash matches
if
l2Output
.
OutputRoot
!=
localOutputRootHash
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"mismatch in output root hashes, got 0x%x expected 0x%x"
,
localOutputRootHash
,
l2Output
.
OutputRoot
)
}
log
.
Info
(
"output root proof"
,
"version"
,
common
.
Hash
(
outputRootProof
.
Version
),
"state-root"
,
common
.
Hash
(
outputRootProof
.
StateRoot
),
"storage-root"
,
common
.
Hash
(
outputRootProof
.
MessagePasserStorageRoot
),
"block-hash"
,
common
.
Hash
(
outputRootProof
.
LatestBlockhash
),
"trie-node-count"
,
len
(
trieNodes
),
)
return
l2OutputIndex
,
outputRootProof
,
trieNodes
,
nil
}
func
main
()
{
log
.
Root
()
.
SetHandler
(
log
.
StreamHandler
(
os
.
Stderr
,
log
.
TerminalFormat
(
isatty
.
IsTerminal
(
os
.
Stderr
.
Fd
()))))
...
...
@@ -408,7 +313,7 @@ func main() {
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"should revert"
);
err
!=
nil
{
return
err
}
panic
(
"
THIS SHOULD REVERT
"
)
panic
(
"
DOUBLE PLAYED DEPOSIT ALLOWED
"
)
}
callFrame
:=
findWithdrawalCall
(
&
finalizationTrace
,
wd
,
l1xdmAddr
)
...
...
@@ -478,8 +383,7 @@ func main() {
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"target mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"target mismatch"
)
//continue
continue
}
if
!
bytes
.
Equal
(
hexutil
.
MustDecode
(
callFrame
.
Input
),
wd
.
Data
)
{
log
.
Info
(
"calldata mismatch"
,
"index"
,
i
)
...
...
@@ -487,16 +391,14 @@ func main() {
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"calldata mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"calldata mismatch"
)
//continue
continue
}
if
callFrame
.
BigValue
()
.
Cmp
(
wdValue
)
!=
0
{
log
.
Info
(
"value mismatch"
,
"index"
,
i
)
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"value mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"value mismatch"
)
//continue
continue
}
// Get the ETH balance of the withdrawal target *after* the finalization
...
...
@@ -518,8 +420,7 @@ func main() {
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"balance mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"balance mismatch"
)
//continue
continue
}
}
else
{
log
.
Info
(
"Already finalized"
)
...
...
@@ -534,6 +435,7 @@ func main() {
}
}
// callTrace will call `debug_traceTransaction` on a remote node
func
callTrace
(
c
*
clients
,
receipt
*
types
.
Receipt
)
(
callFrame
,
error
)
{
var
finalizationTrace
callFrame
tracer
:=
"callTracer"
...
...
@@ -547,6 +449,7 @@ func callTrace(c *clients, receipt *types.Receipt) (callFrame, error) {
return
finalizationTrace
,
err
}
// handleFinalizeETHWithdrawal will ensure that the calldata is correct
func
handleFinalizeETHWithdrawal
(
args
[]
any
)
error
{
from
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
...
...
@@ -576,6 +479,8 @@ func handleFinalizeETHWithdrawal(args []any) error {
return
nil
}
// handleFinalizeERC20Withdrawal will look at the receipt logs and make
// assertions that the values are correct
func
handleFinalizeERC20Withdrawal
(
args
[]
any
,
receipt
*
types
.
Receipt
,
l1StandardBridgeAddress
common
.
Address
)
error
{
erc20Abi
,
err
:=
bindings
.
ERC20MetaData
.
GetAbi
()
if
err
!=
nil
{
...
...
@@ -629,8 +534,6 @@ func handleFinalizeERC20Withdrawal(args []any, receipt *types.Receipt, l1Standar
return
fmt
.
Errorf
(
""
)
}
fmt
.
Printf
(
"%#v
\n
"
,
l
.
Topics
)
_from
:=
common
.
BytesToAddress
(
l
.
Topics
[
1
]
.
Bytes
())
_to
:=
common
.
BytesToAddress
(
l
.
Topics
[
2
]
.
Bytes
())
...
...
@@ -655,6 +558,9 @@ func handleFinalizeERC20Withdrawal(args []any, receipt *types.Receipt, l1Standar
return
nil
}
// proveWithdrawalTransaction will build the data required for proving a
// withdrawal and then send the transaction and make sure that it is included
// and successful and then wait for the finalization period to elapse.
func
proveWithdrawalTransaction
(
c
*
contracts
,
cl
*
clients
,
opts
*
bind
.
TransactOpts
,
withdrawal
*
crossdomain
.
Withdrawal
,
bn
,
finalizationPeriod
*
big
.
Int
)
error
{
l2OutputIndex
,
outputRootProof
,
trieNodes
,
err
:=
createOutput
(
withdrawal
,
c
.
L2OutputOracle
,
bn
,
cl
)
if
err
!=
nil
{
...
...
@@ -868,7 +774,8 @@ func newWithdrawals(ctx *cli.Context, l1ChainID *big.Int) ([]*crossdomain.Legacy
return
nil
,
err
}
// use empty ovmMessages if its not mainnet
// use empty ovmMessages if its not mainnet. The mainnet messages are
// committed to in git.
if
l1ChainID
.
Cmp
(
common
.
Big1
)
!=
0
{
log
.
Info
(
"not using ovm messages because its not mainnet"
)
ovmMessages
=
[]
*
migration
.
SentMessage
{}
...
...
@@ -923,6 +830,110 @@ func newTransactor(ctx *cli.Context) (*bind.TransactOpts, error) {
return
opts
,
nil
}
// findWithdrawalCall will find the call frame for the call that
// represents the user's intent.
func
findWithdrawalCall
(
trace
*
callFrame
,
wd
*
crossdomain
.
LegacyWithdrawal
,
l1xdm
common
.
Address
)
*
callFrame
{
isCall
:=
trace
.
Type
==
"CALL"
isTarget
:=
common
.
HexToAddress
(
trace
.
To
)
==
*
wd
.
Target
isFrom
:=
common
.
HexToAddress
(
trace
.
From
)
==
l1xdm
if
isCall
&&
isTarget
&&
isFrom
{
return
trace
}
for
_
,
subcall
:=
range
trace
.
Calls
{
if
call
:=
findWithdrawalCall
(
&
subcall
,
wd
,
l1xdm
);
call
!=
nil
{
return
call
}
}
return
nil
}
// createOutput will create the data required to send a withdrawal transaction.
func
createOutput
(
withdrawal
*
crossdomain
.
Withdrawal
,
oracle
*
bindings
.
L2OutputOracle
,
blockNumber
*
big
.
Int
,
clients
*
clients
,
)
(
*
big
.
Int
,
bindings
.
TypesOutputRootProof
,
[][]
byte
,
error
)
{
// compute the storage slot that the withdrawal is stored in
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// find the output index that the withdrawal was committed to in
l2OutputIndex
,
err
:=
oracle
.
GetL2OutputIndexAfter
(
&
bind
.
CallOpts
{},
blockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// fetch the output the commits to the withdrawal using the index
l2Output
,
err
:=
oracle
.
GetL2Output
(
&
bind
.
CallOpts
{},
l2OutputIndex
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
log
.
Debug
(
"L2 output"
,
"index"
,
l2OutputIndex
,
"root"
,
common
.
Bytes2Hex
(
l2Output
.
OutputRoot
[
:
]),
"l2-blocknumber"
,
l2Output
.
L2BlockNumber
,
"timestamp"
,
l2Output
.
Timestamp
,
)
// get the block header committed to in the output
header
,
err
:=
clients
.
L2Client
.
HeaderByNumber
(
context
.
Background
(),
l2Output
.
L2BlockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// get the storage proof for the withdrawal's storage slot
proof
,
err
:=
clients
.
L2GethClient
.
GetProof
(
context
.
Background
(),
predeploys
.
L2ToL1MessagePasserAddr
,
[]
string
{
slot
.
String
()},
blockNumber
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
if
count
:=
len
(
proof
.
StorageProof
);
count
!=
1
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"invalid amount of storage proofs: %d"
,
count
)
}
trieNodes
:=
make
([][]
byte
,
len
(
proof
.
StorageProof
[
0
]
.
Proof
))
for
i
,
s
:=
range
proof
.
StorageProof
[
0
]
.
Proof
{
trieNodes
[
i
]
=
common
.
FromHex
(
s
)
}
// create an output root proof
outputRootProof
:=
bindings
.
TypesOutputRootProof
{
Version
:
[
32
]
byte
{},
StateRoot
:
header
.
Root
,
MessagePasserStorageRoot
:
proof
.
StorageHash
,
LatestBlockhash
:
header
.
Hash
(),
}
// TODO(mark): import the function from `op-node` to compute the hash
// instead of doing this. Will update when testing against mainnet.
localOutputRootHash
:=
crypto
.
Keccak256Hash
(
outputRootProof
.
Version
[
:
],
outputRootProof
.
StateRoot
[
:
],
outputRootProof
.
MessagePasserStorageRoot
[
:
],
outputRootProof
.
LatestBlockhash
[
:
],
)
// ensure that the locally computed hash matches
if
l2Output
.
OutputRoot
!=
localOutputRootHash
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"mismatch in output root hashes, got 0x%x expected 0x%x"
,
localOutputRootHash
,
l2Output
.
OutputRoot
)
}
log
.
Info
(
"output root proof"
,
"version"
,
common
.
Hash
(
outputRootProof
.
Version
),
"state-root"
,
common
.
Hash
(
outputRootProof
.
StateRoot
),
"storage-root"
,
common
.
Hash
(
outputRootProof
.
MessagePasserStorageRoot
),
"block-hash"
,
common
.
Hash
(
outputRootProof
.
LatestBlockhash
),
"trie-node-count"
,
len
(
trieNodes
),
)
return
l2OutputIndex
,
outputRootProof
,
trieNodes
,
nil
}
// writeSuspicious will create a suspiciousWithdrawal and then append it to a
// JSONL file. Each line is its own JSON where there is a newline separating them.
func
writeSuspicious
(
f
*
os
.
File
,
withdrawal
*
crossdomain
.
Withdrawal
,
...
...
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