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
b2242f40
Commit
b2242f40
authored
Dec 20, 2022
by
Mark Tyneway
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-chain-ops: more fixes
parent
de9a9a16
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
566 additions
and
319 deletions
+566
-319
main.go
op-chain-ops/cmd/withdrawals/main.go
+556
-316
legacy_withdrawal.go
op-chain-ops/crossdomain/legacy_withdrawal.go
+4
-3
message.go
op-chain-ops/crossdomain/message.go
+6
-0
No files found.
op-chain-ops/cmd/withdrawals/main.go
View file @
b2242f40
...
@@ -17,6 +17,7 @@ import (
...
@@ -17,6 +17,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis/migration"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis/migration"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
...
@@ -31,6 +32,8 @@ import (
...
@@ -31,6 +32,8 @@ import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/rpc"
)
)
var
abiTrue
=
common
.
Hash
{
31
:
0x01
}
// callFrame represents the response returned from geth's
// callFrame represents the response returned from geth's
// `debug_traceTransaction` callTracer
// `debug_traceTransaction` callTracer
type
callFrame
struct
{
type
callFrame
struct
{
...
@@ -46,6 +49,20 @@ type callFrame struct {
...
@@ -46,6 +49,20 @@ type callFrame struct {
Calls
[]
callFrame
`json:"calls,omitempty"`
Calls
[]
callFrame
`json:"calls,omitempty"`
}
}
func
(
c
*
callFrame
)
BigValue
()
*
big
.
Int
{
v
:=
strings
.
TrimPrefix
(
c
.
Value
,
"0x"
)
b
,
_
:=
new
(
big
.
Int
)
.
SetString
(
v
,
16
)
return
b
}
type
suspiciousWithdrawal
struct
{
Withdrawal
*
crossdomain
.
Withdrawal
`json:"withdrawal"`
Legacy
*
crossdomain
.
LegacyWithdrawal
`json:"legacy"`
Trace
callFrame
`json:"trace"`
Index
int
`json:"index"`
Reason
string
`json:"reason"`
}
// findWithdrawalCall will find the call frame for the call that
// findWithdrawalCall will find the call frame for the call that
// represents the user's intent.
// represents the user's intent.
func
findWithdrawalCall
(
trace
*
callFrame
,
wd
*
crossdomain
.
LegacyWithdrawal
,
l1xdm
common
.
Address
)
*
callFrame
{
func
findWithdrawalCall
(
trace
*
callFrame
,
wd
*
crossdomain
.
LegacyWithdrawal
,
l1xdm
common
.
Address
)
*
callFrame
{
...
@@ -69,8 +86,7 @@ func createOutput(
...
@@ -69,8 +86,7 @@ func createOutput(
withdrawal
*
crossdomain
.
Withdrawal
,
withdrawal
*
crossdomain
.
Withdrawal
,
oracle
*
bindings
.
L2OutputOracle
,
oracle
*
bindings
.
L2OutputOracle
,
blockNumber
*
big
.
Int
,
blockNumber
*
big
.
Int
,
l2Client
bind
.
ContractBackend
,
clients
*
clients
,
l2GethClient
*
gethclient
.
Client
,
)
(
*
big
.
Int
,
bindings
.
TypesOutputRootProof
,
[][]
byte
,
error
)
{
)
(
*
big
.
Int
,
bindings
.
TypesOutputRootProof
,
[][]
byte
,
error
)
{
// compute the storage slot that the withdrawal is stored in
// compute the storage slot that the withdrawal is stored in
slot
,
err
:=
withdrawal
.
StorageSlot
()
slot
,
err
:=
withdrawal
.
StorageSlot
()
...
@@ -78,7 +94,7 @@ func createOutput(
...
@@ -78,7 +94,7 @@ func createOutput(
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
}
// find the output index that the withdrawal was commited to in
// find the output index that the withdrawal was commit
t
ed to in
l2OutputIndex
,
err
:=
oracle
.
GetL2OutputIndexAfter
(
&
bind
.
CallOpts
{},
blockNumber
)
l2OutputIndex
,
err
:=
oracle
.
GetL2OutputIndexAfter
(
&
bind
.
CallOpts
{},
blockNumber
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
...
@@ -98,13 +114,14 @@ func createOutput(
...
@@ -98,13 +114,14 @@ func createOutput(
)
)
// get the block header committed to in the output
// get the block header committed to in the output
header
,
err
:=
l
2Client
.
HeaderByNumber
(
context
.
Background
(),
l2Output
.
L2BlockNumber
)
header
,
err
:=
clients
.
L
2Client
.
HeaderByNumber
(
context
.
Background
(),
l2Output
.
L2BlockNumber
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
}
// get the storage proof for the withdrawal's storage slot
// get the storage proof for the withdrawal's storage slot
proof
,
err
:=
l2GethClient
.
GetProof
(
context
.
Background
(),
predeploys
.
L2ToL1MessagePasserAddr
,
[]
string
{
slot
.
String
()},
blockNumber
)
proof
,
err
:=
clients
.
L2GethClient
.
GetProof
(
context
.
Background
(),
predeploys
.
L2ToL1MessagePasserAddr
,
[]
string
{
slot
.
String
()},
blockNumber
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
}
...
@@ -124,7 +141,6 @@ func createOutput(
...
@@ -124,7 +141,6 @@ func createOutput(
LatestBlockhash
:
header
.
Hash
(),
LatestBlockhash
:
header
.
Hash
(),
}
}
// compute a storage root hash locally
localOutputRootHash
:=
crypto
.
Keccak256Hash
(
localOutputRootHash
:=
crypto
.
Keccak256Hash
(
outputRootProof
.
Version
[
:
],
outputRootProof
.
Version
[
:
],
outputRootProof
.
StateRoot
[
:
],
outputRootProof
.
StateRoot
[
:
],
...
@@ -134,7 +150,7 @@ func createOutput(
...
@@ -134,7 +150,7 @@ func createOutput(
// ensure that the locally computed hash matches
// ensure that the locally computed hash matches
if
l2Output
.
OutputRoot
!=
localOutputRootHash
{
if
l2Output
.
OutputRoot
!=
localOutputRootHash
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"mismatch in output root hashes
"
,
"got"
,
localOutputRootHash
,
"expect"
,
l2Output
.
OutputRoot
)
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
fmt
.
Errorf
(
"mismatch in output root hashes
, got 0x%x expected 0x%x"
,
localOutputRootHash
,
l2Output
.
OutputRoot
)
}
}
log
.
Info
(
log
.
Info
(
"output root proof"
,
"output root proof"
,
...
@@ -142,6 +158,7 @@ func createOutput(
...
@@ -142,6 +158,7 @@ func createOutput(
"state-root"
,
common
.
Hash
(
outputRootProof
.
StateRoot
),
"state-root"
,
common
.
Hash
(
outputRootProof
.
StateRoot
),
"storage-root"
,
common
.
Hash
(
outputRootProof
.
MessagePasserStorageRoot
),
"storage-root"
,
common
.
Hash
(
outputRootProof
.
MessagePasserStorageRoot
),
"block-hash"
,
common
.
Hash
(
outputRootProof
.
LatestBlockhash
),
"block-hash"
,
common
.
Hash
(
outputRootProof
.
LatestBlockhash
),
"trie-node-count"
,
len
(
trieNodes
),
)
)
return
l2OutputIndex
,
outputRootProof
,
trieNodes
,
nil
return
l2OutputIndex
,
outputRootProof
,
trieNodes
,
nil
...
@@ -184,10 +201,6 @@ func main() {
...
@@ -184,10 +201,6 @@ func main() {
Name
:
"evm-messages"
,
Name
:
"evm-messages"
,
Usage
:
"Path to evm-messages.json"
,
Usage
:
"Path to evm-messages.json"
,
},
},
&
cli
.
Uint64Flag
{
Name
:
"bedrock-transition-block-number"
,
Usage
:
"The blocknumber of the bedrock transition block"
,
},
&
cli
.
StringFlag
{
&
cli
.
StringFlag
{
Name
:
"private-key"
,
Name
:
"private-key"
,
Usage
:
"Key to sign transactions with"
,
Usage
:
"Key to sign transactions with"
,
...
@@ -199,324 +212,371 @@ func main() {
...
@@ -199,324 +212,371 @@ func main() {
},
},
},
},
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
// set up the rpc clients
clients
,
err
:=
newClients
(
ctx
)
l1RpcURL
:=
ctx
.
String
(
"l1-rpc-url"
)
l1Client
,
err
:=
ethclient
.
Dial
(
l1RpcURL
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
l1ChainID
,
err
:=
l1Client
.
ChainID
(
context
.
Background
())
// initialize the contract bindings
contracts
,
err
:=
newContracts
(
ctx
,
clients
.
L1Client
,
clients
.
L2Client
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
l1xdmAddr
:=
common
.
HexToAddress
(
ctx
.
String
(
"l1-crossdomain-messenger-address"
))
log
.
Info
(
"Set up L1 RPC Client"
,
"chain-id"
,
l1ChainID
)
l1ChainID
,
err
:=
clients
.
L1Client
.
ChainID
(
context
.
Background
())
l2RpcURL
:=
ctx
.
String
(
"l2-rpc-url"
)
l2Client
,
err
:=
ethclient
.
Dial
(
l2RpcURL
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
l2ChainID
,
err
:=
l2Client
.
ChainID
(
context
.
Background
())
// create the set of withdrawals
wds
,
err
:=
newWithdrawals
(
ctx
,
l1ChainID
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
log
.
Info
(
"Set up L2 RPC Client"
,
"chain-id"
,
l2ChainID
)
l1RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l1RpcURL
)
period
,
err
:=
contracts
.
OptimismPortal
.
FINALIZATIONPERIODSECONDS
(
&
bind
.
CallOpts
{}
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
l2RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l2RpcURL
)
bedrockStartingBlockNumber
,
err
:=
contracts
.
L2OutputOracle
.
StartingBlockNumber
(
&
bind
.
CallOpts
{}
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
// this script requires geth's rpcs
gclient
:=
gethclient
.
New
(
l2RpcClient
)
// get the evm and ovm messages witness files used as part of
bedrockStartingBlock
,
err
:=
clients
.
L2Client
.
BlockByNumber
(
context
.
Background
(),
bedrockStartingBlockNumber
)
// migration
ovmMsgs
:=
ctx
.
String
(
"ovm-messages"
)
evmMsgs
:=
ctx
.
String
(
"evm-messages"
)
log
.
Debug
(
"Migration data"
,
"ovm-path"
,
ovmMsgs
,
"evm-messages"
,
evmMsgs
)
log
.
Info
(
"Withdrawal config"
,
"finalization-period"
,
period
,
"bedrock-starting-block-number"
,
bedrockStartingBlockNumber
,
"bedrock-starting-block-hash"
,
bedrockStartingBlock
.
Hash
()
.
Hex
())
ovmMessages
,
err
:=
migration
.
NewSentMessage
(
ovmMsgs
)
if
!
bytes
.
Equal
(
bedrockStartingBlock
.
Extra
(),
genesis
.
BedrockTransitionBlockExtraData
)
{
return
errors
.
New
(
"genesis block mismatch"
)
}
outfile
:=
ctx
.
String
(
"bad-withdrawals-out"
)
f
,
err
:=
os
.
OpenFile
(
outfile
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_APPEND
,
0
o755
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
evmMessages
,
err
:=
migration
.
NewSentMessage
(
evmMsgs
)
// create a transactor
opts
,
err
:=
newTransactor
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
optimismPortalAddress
:=
ctx
.
String
(
"optimism-portal-address"
)
// Need this to compare in event parsing
if
len
(
optimismPortalAddress
)
==
0
{
l1StandardBridgeAddress
:=
common
.
HexToAddress
(
ctx
.
String
(
"l1-standard-bridge-address"
))
return
errors
.
New
(
"OptimismPortal address not configured"
)
}
optimismPortalAddr
:=
common
.
HexToAddress
(
optimismPortalAddress
)
migrationData
:=
migration
.
MigrationData
{
// iterate over all of the withdrawals and submit them
OvmMessages
:
ovmMessages
,
for
i
,
wd
:=
range
wds
{
EvmMessages
:
evmMessages
,
log
.
Info
(
"Processing withdrawal"
,
"index"
,
i
)
}
// create the set of withdrawals
// migrate the withdrawal
wds
,
err
:=
migrationData
.
ToWithdrawals
(
)
withdrawal
,
err
:=
crossdomain
.
MigrateWithdrawal
(
wd
,
&
l1xdmAddr
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
len
(
wds
)
==
0
{
return
errors
.
New
(
"no withdrawals"
)
// Pass to Portal
hash
,
err
:=
withdrawal
.
Hash
()
if
err
!=
nil
{
return
err
}
}
log
.
Info
(
"Converted migration data to withdrawals successfully"
,
"count"
,
len
(
wds
))
l1xdmAddress
:=
ctx
.
String
(
"l1-crossdomain-messenger-address"
)
lcdm
:=
wd
.
CrossDomainMessage
()
if
l1xdmAddress
==
""
{
legacyXdmHash
,
err
:=
lcdm
.
Hash
()
return
errors
.
New
(
"Must pass in --l1-crossdomain-messenger-address"
)
if
err
!=
nil
{
return
err
}
}
l1xdmAddr
:=
common
.
HexToAddress
(
l1xdmAddress
)
l1CrossDomainMessenger
,
err
:=
bindings
.
NewL1CrossDomainMessenger
(
l1xdmAddr
,
l1Client
)
// check to see if the withdrawal has already been successfully
// relayed or received
isSuccess
,
err
:=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
legacyXdmHash
)
if
err
!=
nil
{
return
err
}
isFailed
,
err
:=
contracts
.
L1CrossDomainMessenger
.
FailedMessages
(
&
bind
.
CallOpts
{},
legacyXdmHash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
portal
,
err
:=
bindings
.
NewOptimismPortal
(
optimismPortalAddr
,
l1Client
)
// This is just wrong
xdmHash
:=
crypto
.
Keccak256Hash
(
withdrawal
.
Data
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
l2OracleAddr
,
err
:=
portal
.
L2ORACLE
(
&
bind
.
CallOpts
{}
)
isSuccessNew
,
err
:=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
oracle
,
err
:=
bindings
.
NewL2OutputOracle
(
l2OracleAddr
,
l1Client
)
isFailedNew
,
err
:=
contracts
.
L1CrossDomainMessenger
.
FailedMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
return
err
}
}
log
.
Info
(
log
.
Info
(
"cross domain messenger status"
,
"hash"
,
legacyXdmHash
.
Hex
(),
"success"
,
isSuccess
,
"failed"
,
isFailed
,
"is-success-new"
,
isSuccessNew
,
"is-failed-new"
,
isFailedNew
)
"Addresses"
,
"l1-crossdomain-messenger"
,
l1xdmAddr
,
"optimism-portal"
,
optimismPortalAddr
,
"output-oracle"
,
l2OracleAddr
,
)
period
,
err
:=
portal
.
FINALIZATIONPERIODSECONDS
(
&
bind
.
CallOpts
{})
// compute the storage slot
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
// successful messages can be skipped, received messages failed
transitionBlockNumber
:=
new
(
big
.
Int
)
.
SetUint64
(
ctx
.
Uint64
(
"bedrock-transition-block-number"
))
// their execution and should be replayed
log
.
Info
(
"Withdrawal config"
,
"finalization-period"
,
period
,
"bedrock-transition-block-number"
,
transitionBlockNumber
)
if
isSuccessNew
{
log
.
Info
(
"Message already relayed"
,
"index"
,
i
,
"hash"
,
hash
,
"slot"
,
slot
)
if
ctx
.
String
(
"private-key"
)
==
""
{
continue
return
errors
.
New
(
"No private key to transact with"
)
}
}
privateKey
,
err
:=
crypto
.
HexToECDSA
(
strings
.
TrimPrefix
(
ctx
.
String
(
"private-key"
),
"0x"
))
// check the storage value of the slot to ensure that it is in
// the L2 storage. Without this check, the proof will fail
storageValue
,
err
:=
clients
.
L2Client
.
StorageAt
(
context
.
Background
(),
predeploys
.
L2ToL1MessagePasserAddr
,
slot
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
log
.
Debug
(
"L2ToL1MessagePasser status"
,
"value"
,
common
.
Bytes2Hex
(
storageValue
))
type
badWithdrawal
struct
{
// the value should be set to a boolean in storage
Withdrawal
*
crossdomain
.
Withdrawal
`json:"withdrawal"`
if
!
bytes
.
Equal
(
storageValue
,
abiTrue
.
Bytes
())
{
Legacy
*
crossdomain
.
LegacyWithdrawal
`json:"legacy"`
return
fmt
.
Errorf
(
"storage slot %x not found in state"
,
slot
)
Trace
callFrame
`json:"trace"`
Index
int
`json"index"`
}
}
badWithdrawals
:=
make
([]
badWithdrawal
,
0
)
legacySlot
,
err
:=
wd
.
StorageSlot
()
// iterate over all of the withdrawals and submit them
for
i
,
wd
:=
range
wds
{
log
.
Info
(
"Processing withdrawal"
,
"index"
,
i
)
// migrate the withdrawal
withdrawal
,
err
:=
crossdomain
.
MigrateWithdrawal
(
wd
,
&
l1xdmAddr
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
// compute the withdrawal hash
legacyStorageValue
,
err
:=
clients
.
L2Client
.
StorageAt
(
context
.
Background
(),
predeploys
.
LegacyMessagePasserAddr
,
legacySlot
,
nil
)
hash
,
err
:=
withdrawal
.
Hash
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
// check to see if the withdrawal has already been successfully
log
.
Debug
(
"LegacyMessagePasser status"
,
"value"
,
common
.
Bytes2Hex
(
legacyStorageValue
))
// relayed or received
isSuccess
,
err
:=
l1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
hash
)
// check to see if its already been proven
proven
,
err
:=
contracts
.
OptimismPortal
.
ProvenWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
isReceived
,
err
:=
l1CrossDomainMessenger
.
ReceivedMessages
(
&
bind
.
CallOpts
{},
hash
)
// if it has not been proven, then prove it
if
proven
.
Timestamp
.
Cmp
(
common
.
Big0
)
==
0
{
log
.
Info
(
"Proving withdrawal to OptimismPortal"
)
if
err
:=
proveWithdrawalTransaction
(
contracts
,
clients
,
opts
,
withdrawal
,
bedrockStartingBlockNumber
,
period
);
err
!=
nil
{
return
err
}
}
else
{
log
.
Info
(
"Withdrawal already proven to OptimismPortal"
)
}
// check to see if the withdrawal has been finalized already
isFinalized
,
err
:=
contracts
.
OptimismPortal
.
FinalizedWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
// compute the storage slot
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
!
isFinalized
{
// Get the ETH balance of the withdrawal target *before* the finalization
targetBalBefore
,
err
:=
clients
.
L1Client
.
BalanceAt
(
context
.
Background
(),
*
wd
.
Target
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
log
.
Debug
(
"Balance before finalization"
,
"balance"
,
targetBalBefore
,
"account"
,
*
wd
.
Target
)
log
.
Info
(
"cross domain messenger status"
,
"hash"
,
hash
.
Hex
(),
"success"
,
isSuccess
,
"received"
,
isReceived
,
"slot"
,
slot
.
Hex
())
log
.
Info
(
"Finalizing withdrawal"
)
receipt
,
err
:=
finalizeWithdrawalTransaction
(
contracts
,
clients
,
opts
,
wd
,
withdrawal
)
log
.
Info
(
"withdrawal finalized"
,
"tx-hash"
,
receipt
.
TxHash
,
"withdrawal-hash"
,
hash
)
// successful messages can be skipped, received messages failed
finalizationTrace
,
err
:=
callTrace
(
clients
,
receipt
)
// their execution and should be replayed
if
err
!=
nil
{
if
isSuccess
{
return
nil
log
.
Info
(
"Message already relayed"
,
"index"
,
i
,
"hash"
,
hash
,
"slot"
,
slot
)
continue
}
}
// create the values required for submitting a proof
isSuccessNewPost
,
err
:=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
l2OutputIndex
,
outputRootProof
,
trieNodes
,
err
:=
createOutput
(
withdrawal
,
oracle
,
transitionBlockNumber
,
l2Client
,
gclient
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
privateKey
,
l1ChainID
)
// This would indicate that there is a replayability problem
if
err
!=
nil
{
if
isSuccess
&&
isSuccessNewPost
{
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"should revert"
);
err
!=
nil
{
return
err
return
err
}
}
panic
(
"THIS SHOULD REVERT"
)
}
// check to see if its already been proven
callFrame
:=
findWithdrawalCall
(
&
finalizationTrace
,
wd
,
l1xdmAddr
)
proven
,
err
:=
portal
.
ProvenWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
if
callFrame
==
nil
{
if
err
!=
nil
{
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"cannot find callframe"
);
err
!=
nil
{
return
err
return
err
}
}
continue
}
wdTx
:=
withdrawal
.
WithdrawalTransaction
()
traceJson
,
err
:=
json
.
MarshalIndent
(
callFrame
,
""
,
" "
)
// check to see if its been proven
// if it has not been proven, then prove it
if
proven
.
Timestamp
.
Cmp
(
common
.
Big0
)
==
0
{
log
.
Info
(
"Proving withdrawal to OptimismPortal"
)
tx
,
err
:=
portal
.
ProveWithdrawalTransaction
(
opts
,
wdTx
,
l2OutputIndex
,
outputRootProof
,
trieNodes
,
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
log
.
Debug
(
fmt
.
Sprintf
(
"%v"
,
string
(
traceJson
)))
receipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
l1Client
,
tx
)
abi
,
err
:=
bindings
.
L1StandardBridgeMetaData
.
GetAbi
(
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
receipt
.
Status
!=
types
.
ReceiptStatusSuccessful
{
return
errors
.
New
(
"withdrawal proof unsuccessful"
)
}
log
.
Info
(
"withdrawal proved"
,
"tx-hash"
,
tx
.
Hash
(),
"withdrawal-hash"
,
hash
)
calldata
:=
hexutil
.
MustDecode
(
callFrame
.
Input
)
block
,
err
:=
l1Client
.
BlockByHash
(
context
.
Background
(),
receipt
.
BlockHash
)
// this must be the L1 standard bridge
method
,
err
:=
abi
.
MethodById
(
calldata
)
// Handle L1StandardBridge specific logic
if
err
==
nil
{
args
,
err
:=
method
.
Inputs
.
Unpack
(
calldata
[
4
:
])
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
initialTime
:=
block
.
Time
()
for
{
log
.
Info
(
"decoded calldata"
,
"name"
,
method
.
Name
)
log
.
Info
(
"waiting for finalization"
)
if
block
.
Time
()
>=
initialTime
+
period
.
Uint64
()
{
switch
method
.
Name
{
log
.
Info
(
"can be finalized"
)
case
"finalizeERC20Withdrawal"
:
break
if
err
:=
handleFinalizeERC20Withdrawal
(
args
,
receipt
,
l1StandardBridgeAddress
);
err
!=
nil
{
return
err
}
}
time
.
Sleep
(
1
*
time
.
Second
)
case
"finalizeETHWithdrawal"
:
block
,
err
=
l1Client
.
BlockByNumber
(
context
.
Background
(),
nil
)
if
err
:=
handleFinalizeETHWithdrawal
(
args
);
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
default
:
log
.
Info
(
"Unhandled method"
,
"name"
,
method
.
Name
)
}
}
}
else
{
log
.
Info
(
"Withdrawal already proven to OptimismPortal"
)
}
}
// check to see if the withdrawal has been finalized alread
y
// Ensure that the target's balance was increasedData correctl
y
isFinalized
,
err
:=
portal
.
FinalizedWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
wdValue
,
err
:=
wd
.
Value
(
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
method
!=
nil
{
log
.
Info
(
"withdrawal action"
,
"function"
,
method
.
Name
,
"value"
,
wdValue
)
}
else
{
log
.
Info
(
"unknown method"
,
"to"
,
wd
.
Target
,
"data"
,
hexutil
.
Encode
(
wd
.
Data
))
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"unknown method"
);
err
!=
nil
{
return
err
}
}
if
!
isFinalized
{
// check that the user's intents are actually executed
log
.
Info
(
"Finalizing withdrawal"
)
if
common
.
HexToAddress
(
callFrame
.
To
)
!=
*
wd
.
Target
{
log
.
Info
(
"target mismatch"
,
"index"
,
i
)
// Get the ETH balance of the withdrawal target *before* the finalization
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"target mismatch"
);
err
!=
nil
{
targetBalBefore
,
err
:=
l1Client
.
BalanceAt
(
context
.
Background
(),
common
.
BytesToAddress
(
wd
.
Target
.
Bytes
()),
nil
)
if
err
!=
nil
{
return
err
return
err
}
}
panic
(
"target mismatch"
)
//continue
}
if
!
bytes
.
Equal
(
hexutil
.
MustDecode
(
callFrame
.
Input
),
wd
.
Data
)
{
log
.
Info
(
"calldata mismatch"
,
"index"
,
i
)
log
.
Debug
(
fmt
.
Sprintf
(
"Target balance before finalization: %v"
,
targetBalBefore
))
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"calldata mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"calldata mismatch"
)
//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
}
// Finalize withdrawal
// Get the ETH balance of the withdrawal target *after* the finalization
tx
,
err
:=
portal
.
FinalizeWithdrawalTransaction
(
targetBalAfter
,
err
:=
clients
.
L1Client
.
BalanceAt
(
context
.
Background
(),
*
wd
.
Target
,
nil
)
opts
,
wdTx
,
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
receipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
l1Client
,
tx
)
diff
:=
new
(
big
.
Int
)
.
Sub
(
targetBalAfter
,
targetBalBefore
)
log
.
Debug
(
"balances"
,
"before"
,
targetBalBefore
,
"after"
,
targetBalAfter
,
"diff"
,
diff
)
isSuccessNewPost
,
err
=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
receipt
.
Status
!=
types
.
ReceiptStatusSuccessful
{
return
errors
.
New
(
"withdrawal finalize unsuccessful"
)
if
diff
.
Cmp
(
wdValue
)
!=
0
&&
isSuccessNewPost
&&
isSuccess
{
log
.
Info
(
"native eth balance diff mismatch"
,
"index"
,
i
,
"diff"
,
diff
,
"val"
,
wdValue
)
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"balance mismatch"
);
err
!=
nil
{
return
err
}
panic
(
"balance mismatch"
)
//continue
}
}
else
{
log
.
Info
(
"Already finalized"
)
}
}
return
nil
},
}
}
log
.
Info
(
"withdrawal finalized"
,
"tx-hash"
,
tx
.
Hash
(),
"withdrawal-hash"
,
hash
)
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
log
.
Crit
(
"error in migration"
,
"err"
,
err
)
}
}
// fetch the call trace
func
callTrace
(
c
*
clients
,
receipt
*
types
.
Receipt
)
(
callFrame
,
error
)
{
var
finalizationTrace
callFrame
var
finalizationTrace
callFrame
tracer
:=
"callTracer"
tracer
:=
"callTracer"
traceConfig
:=
tracers
.
TraceConfig
{
traceConfig
:=
tracers
.
TraceConfig
{
Tracer
:
&
tracer
,
Tracer
:
&
tracer
,
}
}
err
=
l
1RpcClient
.
Call
(
&
finalizationTrace
,
"debug_traceTransaction"
,
receipt
.
TxHash
,
traceConfig
)
err
:=
c
.
L
1RpcClient
.
Call
(
&
finalizationTrace
,
"debug_traceTransaction"
,
receipt
.
TxHash
,
traceConfig
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
finalizationTrace
,
err
}
}
return
finalizationTrace
,
err
}
callFrame
:=
findWithdrawalCall
(
&
finalizationTrace
,
wd
,
l1xdmAddr
)
func
handleFinalizeETHWithdrawal
(
args
[]
any
)
error
{
if
callFrame
==
nil
{
from
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
return
errors
.
New
(
"cannot find callframe"
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: from"
)
}
}
to
,
ok
:=
args
[
1
]
.
(
common
.
Address
)
traceJson
,
err
:=
json
.
MarshalIndent
(
callFrame
,
""
,
" "
)
if
!
ok
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid type: to"
)
return
err
}
}
log
.
Info
(
fmt
.
Sprintf
(
"%v"
,
string
(
traceJson
)))
amount
,
ok
:=
args
[
2
]
.
(
*
big
.
Int
)
if
!
ok
{
erc20Abi
,
err
:=
bindings
.
ERC20MetaData
.
GetAbi
()
return
fmt
.
Errorf
(
"invalid type: amount"
)
if
err
!=
nil
{
return
err
}
}
transferEvent
:=
erc20Abi
.
Events
[
"Transfer"
]
extraData
,
ok
:=
args
[
3
]
.
([]
byte
)
if
!
ok
{
abi
,
err
:=
bindings
.
L1StandardBridgeMetaData
.
GetAbi
()
return
fmt
.
Errorf
(
"invalid type: extraData"
)
if
err
!=
nil
{
return
err
}
}
calldata
:=
hexutil
.
MustDecode
(
callFrame
.
Input
)
log
.
Info
(
"decoded calldata"
,
"from"
,
from
,
"to"
,
to
,
"amount"
,
amount
,
"extraData"
,
extraData
,
)
// this must be the L1 standard bridge
return
nil
method
,
err
:=
abi
.
MethodById
(
calldata
)
}
// Handle L1StandardBridge specific logic
if
err
==
nil
{
func
handleFinalizeERC20Withdrawal
(
args
[]
any
,
receipt
*
types
.
Receipt
,
l1StandardBridgeAddress
common
.
Address
)
error
{
args
,
err
:=
method
.
Inputs
.
Unpack
(
calldata
[
4
:
]
)
erc20Abi
,
err
:=
bindings
.
ERC20MetaData
.
GetAbi
(
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
transferEvent
:=
erc20Abi
.
Events
[
"Transfer"
]
log
.
Info
(
"decoded calldata"
,
"name"
,
method
.
Name
)
switch
method
.
Name
{
case
"finalizeERC20Withdrawal"
:
// Handle logic for ERC20 withdrawals
// Handle logic for ERC20 withdrawals
l1Token
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
l1Token
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
if
!
ok
{
...
@@ -557,146 +617,326 @@ func main() {
...
@@ -557,146 +617,326 @@ func main() {
for
_
,
l
:=
range
receipt
.
Logs
{
for
_
,
l
:=
range
receipt
.
Logs
{
topic
:=
l
.
Topics
[
0
]
topic
:=
l
.
Topics
[
0
]
if
topic
==
transferEvent
.
ID
{
if
topic
==
transferEvent
.
ID
{
if
l
.
Address
==
l1Token
{
a
,
_
:=
transferEvent
.
Inputs
.
Unpack
(
l
.
Data
)
a
,
_
:=
transferEvent
.
Inputs
.
Unpack
(
l
.
Data
)
// TODO: add a check here for balance diff
if
len
(
l
.
Topics
)
<
3
{
log
.
Info
(
"EVENT FOUND"
,
"args"
,
a
)
return
fmt
.
Errorf
(
""
)
}
}
log
.
Info
(
"receipt topic"
,
"hex"
,
topic
.
Hex
())
fmt
.
Printf
(
"%#v
\n
"
,
l
.
Topics
)
_from
:=
common
.
BytesToAddress
(
l
.
Topics
[
1
]
.
Bytes
())
_to
:=
common
.
BytesToAddress
(
l
.
Topics
[
2
]
.
Bytes
())
// from the L1StandardBridge
if
_from
!=
l1StandardBridgeAddress
{
return
fmt
.
Errorf
(
"from mismatch: %x - %x"
,
_from
,
l1StandardBridgeAddress
)
}
}
case
"finalizeETHWithdrawal"
:
if
to
!=
_to
{
// handle logic for ETH withdrawals
return
fmt
.
Errorf
(
"to mismatch: %x - %x"
,
to
,
_to
)
from
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: from"
)
}
}
to
,
ok
:=
args
[
1
]
.
(
common
.
Address
)
_amount
,
ok
:=
a
[
0
]
.
(
*
big
.
Int
)
if
!
ok
{
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: to"
)
return
fmt
.
Errorf
(
"invalid abi in transfer event"
)
}
if
amount
.
Cmp
(
_amount
)
!=
0
{
return
fmt
.
Errorf
(
"amount mismatch: %d - %d"
,
amount
,
_amount
)
}
}
}
amount
,
ok
:=
args
[
2
]
.
(
*
big
.
Int
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: amount"
)
}
}
extraData
,
ok
:=
args
[
3
]
.
([]
byte
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: extraData"
)
}
}
log
.
Info
(
return
nil
"decoded calldata"
,
}
"from"
,
from
,
"to"
,
to
,
func
proveWithdrawalTransaction
(
c
*
contracts
,
cl
*
clients
,
opts
*
bind
.
TransactOpts
,
withdrawal
*
crossdomain
.
Withdrawal
,
bn
,
finalizationPeriod
*
big
.
Int
)
error
{
"amount"
,
amount
,
l2OutputIndex
,
outputRootProof
,
trieNodes
,
err
:=
createOutput
(
withdrawal
,
c
.
L2OutputOracle
,
bn
,
cl
)
"extraData"
,
extraData
,
if
err
!=
nil
{
return
err
}
hash
,
err
:=
withdrawal
.
Hash
()
if
err
!=
nil
{
return
err
}
wdTx
:=
withdrawal
.
WithdrawalTransaction
()
tx
,
err
:=
c
.
OptimismPortal
.
ProveWithdrawalTransaction
(
opts
,
wdTx
,
l2OutputIndex
,
outputRootProof
,
trieNodes
,
)
)
default
:
if
err
!=
nil
{
log
.
Info
(
"Unhandled method"
,
"name"
,
method
.
Name
)
return
err
}
}
receipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
cl
.
L1Client
,
tx
)
if
err
!=
nil
{
return
err
}
if
receipt
.
Status
!=
types
.
ReceiptStatusSuccessful
{
return
errors
.
New
(
"withdrawal proof unsuccessful"
)
}
}
// Ensure that the target's balance was increasedData correctly
log
.
Info
(
"withdrawal proved"
,
"tx-hash"
,
tx
.
Hash
(),
"withdrawal-hash"
,
hash
)
wdValue
,
err
:=
wd
.
Value
()
block
,
err
:=
cl
.
L1Client
.
BlockByHash
(
context
.
Background
(),
receipt
.
BlockHash
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
method
!=
nil
{
initialTime
:=
block
.
Time
()
log
.
Info
(
"withdrawal action"
,
"function"
,
method
.
Name
,
"value"
,
wdValue
)
for
{
}
else
{
log
.
Info
(
"waiting for finalization"
)
log
.
Info
(
"unknown method"
,
"to"
,
wd
.
Target
,
"data"
,
hexutil
.
Encode
(
wd
.
Data
))
if
block
.
Time
()
>=
initialTime
+
finalizationPeriod
.
Uint64
()
{
log
.
Info
(
"can be finalized"
)
break
}
time
.
Sleep
(
1
*
time
.
Second
)
block
,
err
=
cl
.
L1Client
.
BlockByNumber
(
context
.
Background
(),
nil
)
if
err
!=
nil
{
return
err
}
}
return
nil
}
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
func
finalizeWithdrawalTransaction
(
Withdrawal
:
withdrawal
,
c
*
contracts
,
Legacy
:
wd
,
cl
*
clients
,
Trace
:
finalizationTrace
,
opts
*
bind
.
TransactOpts
,
Index
:
i
,
wd
*
crossdomain
.
LegacyWithdrawal
,
})
withdrawal
*
crossdomain
.
Withdrawal
,
)
(
*
types
.
Receipt
,
error
)
{
if
wd
.
Target
==
nil
{
return
nil
,
errors
.
New
(
"withdrawal target is nil, should never happen"
)
}
wdTx
:=
withdrawal
.
WithdrawalTransaction
()
// Finalize withdrawal
tx
,
err
:=
c
.
OptimismPortal
.
FinalizeWithdrawalTransaction
(
opts
,
wdTx
,
)
if
err
!=
nil
{
return
nil
,
err
}
}
// check that the user's intents are actually executed
if
common
.
HexToAddress
(
callFrame
.
To
)
!=
*
wd
.
Target
{
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
log
.
Info
(
"target mismatch"
,
"index"
,
i
)
receipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
cl
.
L1Client
,
tx
)
if
err
!=
nil
{
return
nil
,
err
}
if
receipt
.
Status
!=
types
.
ReceiptStatusSuccessful
{
return
nil
,
errors
.
New
(
"withdrawal finalize unsuccessful"
)
}
return
receipt
,
nil
}
continue
// contracts represents a set of bound contracts
type
contracts
struct
{
OptimismPortal
*
bindings
.
OptimismPortal
L1CrossDomainMessenger
*
bindings
.
L1CrossDomainMessenger
L2OutputOracle
*
bindings
.
L2OutputOracle
}
// newContracts will create a contracts struct with the contract bindings
// preconfigured
func
newContracts
(
ctx
*
cli
.
Context
,
l1Backend
,
l2Backend
bind
.
ContractBackend
)
(
*
contracts
,
error
)
{
optimismPortalAddress
:=
ctx
.
String
(
"optimism-portal-address"
)
if
len
(
optimismPortalAddress
)
==
0
{
return
nil
,
errors
.
New
(
"OptimismPortal address not configured"
)
}
}
if
!
bytes
.
Equal
(
hexutil
.
MustDecode
(
callFrame
.
Input
),
wd
.
Data
)
{
optimismPortalAddr
:=
common
.
HexToAddress
(
optimismPortalAddress
)
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
log
.
Info
(
"calldata mismatch"
,
"index"
,
i
)
portal
,
err
:=
bindings
.
NewOptimismPortal
(
optimismPortalAddr
,
l1Backend
)
if
err
!=
nil
{
return
nil
,
err
}
continue
l1xdmAddress
:=
ctx
.
String
(
"l1-crossdomain-messenger-address"
)
if
l1xdmAddress
==
""
{
return
nil
,
errors
.
New
(
"L1CrossDomainMessenger address not configured"
)
}
}
if
callFrame
.
Value
!=
"0x"
+
wdValue
.
Text
(
16
)
{
l1xdmAddr
:=
common
.
HexToAddress
(
l1xdmAddress
)
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
log
.
Info
(
"value mismatch"
,
"index"
,
i
)
l1CrossDomainMessenger
,
err
:=
bindings
.
NewL1CrossDomainMessenger
(
l1xdmAddr
,
l1Backend
)
if
err
!=
nil
{
return
nil
,
err
}
continue
l2OracleAddr
,
err
:=
portal
.
L2ORACLE
(
&
bind
.
CallOpts
{})
if
err
!=
nil
{
return
nil
,
err
}
oracle
,
err
:=
bindings
.
NewL2OutputOracle
(
l2OracleAddr
,
l1Backend
)
if
err
!=
nil
{
return
nil
,
err
}
}
// Get the ETH balance of the withdrawal target *after* the finalization
log
.
Info
(
targetBalAfter
,
err
:=
l1Client
.
BalanceAt
(
context
.
Background
(),
*
wd
.
Target
,
nil
)
"Addresses"
,
"l1-crossdomain-messenger"
,
l1xdmAddr
,
"optimism-portal"
,
optimismPortalAddr
,
"l2-output-oracle"
,
l2OracleAddr
,
)
return
&
contracts
{
OptimismPortal
:
portal
,
L1CrossDomainMessenger
:
l1CrossDomainMessenger
,
L2OutputOracle
:
oracle
,
},
nil
}
// clients represents a set of initialized RPC clients
type
clients
struct
{
L1Client
*
ethclient
.
Client
L2Client
*
ethclient
.
Client
L1RpcClient
*
rpc
.
Client
L2RpcClient
*
rpc
.
Client
L1GethClient
*
gethclient
.
Client
L2GethClient
*
gethclient
.
Client
}
// newClients will create new RPC clients
func
newClients
(
ctx
*
cli
.
Context
)
(
*
clients
,
error
)
{
l1RpcURL
:=
ctx
.
String
(
"l1-rpc-url"
)
l1Client
,
err
:=
ethclient
.
Dial
(
l1RpcURL
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
l1ChainID
,
err
:=
l1Client
.
ChainID
(
context
.
Background
())
if
err
!=
nil
{
return
nil
,
err
}
}
diff
:=
new
(
big
.
Int
)
.
Sub
(
targetBalAfter
,
targetBalBefore
)
l2RpcURL
:=
ctx
.
String
(
"l2-rpc-url"
)
log
.
Debug
(
"balances"
,
"before"
,
targetBalBefore
,
"after"
,
targetBalAfter
,
"diff"
,
diff
)
l2Client
,
err
:=
ethclient
.
Dial
(
l2RpcURL
)
if
err
!=
nil
{
return
nil
,
err
}
l2ChainID
,
err
:=
l2Client
.
ChainID
(
context
.
Background
())
if
err
!=
nil
{
return
nil
,
err
}
if
diff
.
Cmp
(
wdValue
)
!=
0
{
l1RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l1RpcURL
)
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
if
err
!=
nil
{
Withdrawal
:
withdrawal
,
return
nil
,
err
Legacy
:
wd
,
}
Trace
:
finalizationTrace
,
Index
:
i
,
l2RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l2RpcURL
)
})
if
err
!=
nil
{
return
nil
,
err
}
log
.
Info
(
"native eth balance diff mismatch"
,
"index"
,
i
)
l1GethClient
:=
gethclient
.
New
(
l1RpcClient
)
l2GethClient
:=
gethclient
.
New
(
l2RpcClient
)
continue
log
.
Info
(
"Set up RPC clients"
,
"l1-chain-id"
,
l1ChainID
,
"l2-chain-id"
,
l2ChainID
,
)
return
&
clients
{
L1Client
:
l1Client
,
L2Client
:
l2Client
,
L1RpcClient
:
l1RpcClient
,
L2RpcClient
:
l2RpcClient
,
L1GethClient
:
l1GethClient
,
L2GethClient
:
l2GethClient
,
},
nil
}
// newWithdrawals will create a set of legacy withdrawals
func
newWithdrawals
(
ctx
*
cli
.
Context
,
l1ChainID
*
big
.
Int
)
([]
*
crossdomain
.
LegacyWithdrawal
,
error
)
{
ovmMsgs
:=
ctx
.
String
(
"ovm-messages"
)
evmMsgs
:=
ctx
.
String
(
"evm-messages"
)
log
.
Debug
(
"Migration data"
,
"ovm-path"
,
ovmMsgs
,
"evm-messages"
,
evmMsgs
)
ovmMessages
,
err
:=
migration
.
NewSentMessage
(
ovmMsgs
)
if
err
!=
nil
{
return
nil
,
err
}
}
// use empty ovmMessages if its not mainnet
if
l1ChainID
.
Cmp
(
common
.
Big1
)
!=
0
{
log
.
Info
(
"not using ovm messages because its not mainnet"
)
ovmMessages
=
[]
*
migration
.
SentMessage
{}
}
}
evmMessages
,
err
:=
migration
.
NewSentMessage
(
evmMsgs
)
if
err
!=
nil
{
return
nil
,
err
}
}
// Write the stuff to disk
migrationData
:=
migration
.
MigrationData
{
if
err
:=
writeJSON
(
ctx
.
String
(
"bad-withdrawals-out"
),
badWithdrawals
);
err
!=
nil
{
OvmMessages
:
ovmMessages
,
return
err
EvmMessages
:
evmMessages
,
}
}
return
nil
wds
,
err
:=
migrationData
.
ToWithdrawals
()
},
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
wds
)
==
0
{
return
nil
,
errors
.
New
(
"no withdrawals"
)
}
}
log
.
Info
(
"Converted migration data to withdrawals successfully"
,
"count"
,
len
(
wds
))
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
return
wds
,
nil
log
.
Crit
(
"error in migration"
,
"err"
,
err
)
}
// newTransactor creates a new transact context given a cli context
func
newTransactor
(
ctx
*
cli
.
Context
)
(
*
bind
.
TransactOpts
,
error
)
{
if
ctx
.
String
(
"private-key"
)
==
""
{
return
nil
,
errors
.
New
(
"No private key to transact with"
)
}
privateKey
,
err
:=
crypto
.
HexToECDSA
(
strings
.
TrimPrefix
(
ctx
.
String
(
"private-key"
),
"0x"
))
if
err
!=
nil
{
return
nil
,
err
}
l1RpcURL
:=
ctx
.
String
(
"l1-rpc-url"
)
l1Client
,
err
:=
ethclient
.
Dial
(
l1RpcURL
)
if
err
!=
nil
{
return
nil
,
err
}
l1ChainID
,
err
:=
l1Client
.
ChainID
(
context
.
Background
())
if
err
!=
nil
{
return
nil
,
err
}
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
privateKey
,
l1ChainID
)
if
err
!=
nil
{
return
nil
,
err
}
}
return
opts
,
nil
}
}
func
writeJSON
(
outfile
string
,
input
interface
{})
error
{
func
writeSuspicious
(
f
,
err
:=
os
.
OpenFile
(
outfile
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
0
o755
)
f
*
os
.
File
,
withdrawal
*
crossdomain
.
Withdrawal
,
wd
*
crossdomain
.
LegacyWithdrawal
,
finalizationTrace
callFrame
,
i
int
,
reason
string
,
)
error
{
bad
:=
suspiciousWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
Reason
:
reason
,
}
data
,
err
:=
json
.
Marshal
(
bad
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
defer
f
.
Close
()
_
,
err
=
f
.
WriteString
(
string
(
data
)
+
"
\n
"
)
return
err
enc
:=
json
.
NewEncoder
(
f
)
enc
.
SetIndent
(
""
,
" "
)
return
enc
.
Encode
(
input
)
}
}
op-chain-ops/crossdomain/legacy_withdrawal.go
View file @
b2242f40
...
@@ -10,6 +10,7 @@ import (
...
@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
)
)
...
@@ -17,7 +18,7 @@ import (
...
@@ -17,7 +18,7 @@ import (
type
LegacyWithdrawal
struct
{
type
LegacyWithdrawal
struct
{
Target
*
common
.
Address
`json:"target"`
Target
*
common
.
Address
`json:"target"`
Sender
*
common
.
Address
`json:"sender"`
Sender
*
common
.
Address
`json:"sender"`
Data
[]
byte
`json:"data"`
Data
hexutil
.
Bytes
`json:"data"`
Nonce
*
big
.
Int
`json:"nonce"`
Nonce
*
big
.
Int
`json:"nonce"`
}
}
...
@@ -38,7 +39,7 @@ func NewLegacyWithdrawal(target, sender *common.Address, data []byte, nonce *big
...
@@ -38,7 +39,7 @@ func NewLegacyWithdrawal(target, sender *common.Address, data []byte, nonce *big
// through the standard optimism cross domain messaging system by hashing in
// through the standard optimism cross domain messaging system by hashing in
// the L2CrossDomainMessenger address.
// the L2CrossDomainMessenger address.
func
(
w
*
LegacyWithdrawal
)
Encode
()
([]
byte
,
error
)
{
func
(
w
*
LegacyWithdrawal
)
Encode
()
([]
byte
,
error
)
{
enc
,
err
:=
EncodeCrossDomainMessageV0
(
w
.
Target
,
w
.
Sender
,
w
.
Data
,
w
.
Nonce
)
enc
,
err
:=
EncodeCrossDomainMessageV0
(
w
.
Target
,
w
.
Sender
,
[]
byte
(
w
.
Data
)
,
w
.
Nonce
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot encode LegacyWithdrawal: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"cannot encode LegacyWithdrawal: %w"
,
err
)
}
}
...
@@ -98,7 +99,7 @@ func (w *LegacyWithdrawal) Decode(data []byte) error {
...
@@ -98,7 +99,7 @@ func (w *LegacyWithdrawal) Decode(data []byte) error {
w
.
Target
=
&
target
w
.
Target
=
&
target
w
.
Sender
=
&
sender
w
.
Sender
=
&
sender
w
.
Data
=
msgData
w
.
Data
=
hexutil
.
Bytes
(
msgData
)
w
.
Nonce
=
nonce
w
.
Nonce
=
nonce
return
nil
return
nil
}
}
...
...
op-chain-ops/crossdomain/message.go
View file @
b2242f40
...
@@ -72,6 +72,12 @@ func (c *CrossDomainMessage) Hash() (common.Hash, error) {
...
@@ -72,6 +72,12 @@ func (c *CrossDomainMessage) Hash() (common.Hash, error) {
}
}
}
}
// HashV1 forces using the V1 hash even if its a legacy hash. This is used
// for the migration process.
func
(
c
*
CrossDomainMessage
)
HashV1
()
(
common
.
Hash
,
error
)
{
return
HashCrossDomainMessageV1
(
c
.
Nonce
,
c
.
Sender
,
c
.
Target
,
c
.
Value
,
c
.
GasLimit
,
c
.
Data
)
}
// ToWithdrawal will turn a CrossDomainMessage into a Withdrawal.
// ToWithdrawal will turn a CrossDomainMessage into a Withdrawal.
// This only works for version 0 CrossDomainMessages as not all of
// This only works for version 0 CrossDomainMessages as not all of
// the data is present for version 1 CrossDomainMessages to be turned
// the data is present for version 1 CrossDomainMessages to be turned
...
...
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