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 (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"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/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis/migration"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
...
...
@@ -31,6 +32,8 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
var
abiTrue
=
common
.
Hash
{
31
:
0x01
}
// callFrame represents the response returned from geth's
// `debug_traceTransaction` callTracer
type
callFrame
struct
{
...
...
@@ -46,6 +49,20 @@ type callFrame struct {
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
// represents the user's intent.
func
findWithdrawalCall
(
trace
*
callFrame
,
wd
*
crossdomain
.
LegacyWithdrawal
,
l1xdm
common
.
Address
)
*
callFrame
{
...
...
@@ -69,8 +86,7 @@ func createOutput(
withdrawal
*
crossdomain
.
Withdrawal
,
oracle
*
bindings
.
L2OutputOracle
,
blockNumber
*
big
.
Int
,
l2Client
bind
.
ContractBackend
,
l2GethClient
*
gethclient
.
Client
,
clients
*
clients
,
)
(
*
big
.
Int
,
bindings
.
TypesOutputRootProof
,
[][]
byte
,
error
)
{
// compute the storage slot that the withdrawal is stored in
slot
,
err
:=
withdrawal
.
StorageSlot
()
...
...
@@ -78,7 +94,7 @@ func createOutput(
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
)
if
err
!=
nil
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
...
...
@@ -98,13 +114,14 @@ func createOutput(
)
// 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
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
// 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
{
return
nil
,
bindings
.
TypesOutputRootProof
{},
nil
,
err
}
...
...
@@ -124,7 +141,6 @@ func createOutput(
LatestBlockhash
:
header
.
Hash
(),
}
// compute a storage root hash locally
localOutputRootHash
:=
crypto
.
Keccak256Hash
(
outputRootProof
.
Version
[
:
],
outputRootProof
.
StateRoot
[
:
],
...
...
@@ -134,7 +150,7 @@ func createOutput(
// ensure that the locally computed hash matches
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
(
"output root proof"
,
...
...
@@ -142,6 +158,7 @@ func createOutput(
"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
...
...
@@ -184,10 +201,6 @@ func main() {
Name
:
"evm-messages"
,
Usage
:
"Path to evm-messages.json"
,
},
&
cli
.
Uint64Flag
{
Name
:
"bedrock-transition-block-number"
,
Usage
:
"The blocknumber of the bedrock transition block"
,
},
&
cli
.
StringFlag
{
Name
:
"private-key"
,
Usage
:
"Key to sign transactions with"
,
...
...
@@ -199,324 +212,371 @@ func main() {
},
},
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
// set up the rpc clients
l1RpcURL
:=
ctx
.
String
(
"l1-rpc-url"
)
l1Client
,
err
:=
ethclient
.
Dial
(
l1RpcURL
)
clients
,
err
:=
newClients
(
ctx
)
if
err
!=
nil
{
return
err
}
l1ChainID
,
err
:=
l1Client
.
ChainID
(
context
.
Background
())
// initialize the contract bindings
contracts
,
err
:=
newContracts
(
ctx
,
clients
.
L1Client
,
clients
.
L2Client
)
if
err
!=
nil
{
return
err
}
l1xdmAddr
:=
common
.
HexToAddress
(
ctx
.
String
(
"l1-crossdomain-messenger-address"
))
log
.
Info
(
"Set up L1 RPC Client"
,
"chain-id"
,
l1ChainID
)
l2RpcURL
:=
ctx
.
String
(
"l2-rpc-url"
)
l2Client
,
err
:=
ethclient
.
Dial
(
l2RpcURL
)
l1ChainID
,
err
:=
clients
.
L1Client
.
ChainID
(
context
.
Background
())
if
err
!=
nil
{
return
err
}
l2ChainID
,
err
:=
l2Client
.
ChainID
(
context
.
Background
())
// create the set of withdrawals
wds
,
err
:=
newWithdrawals
(
ctx
,
l1ChainID
)
if
err
!=
nil
{
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
{
return
err
}
l2RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l2RpcURL
)
bedrockStartingBlockNumber
,
err
:=
contracts
.
L2OutputOracle
.
StartingBlockNumber
(
&
bind
.
CallOpts
{}
)
if
err
!=
nil
{
return
err
}
// this script requires geth's rpcs
gclient
:=
gethclient
.
New
(
l2RpcClient
)
// get the evm and ovm messages witness files used as part of
// migration
ovmMsgs
:=
ctx
.
String
(
"ovm-messages"
)
evmMsgs
:=
ctx
.
String
(
"evm-messages"
)
bedrockStartingBlock
,
err
:=
clients
.
L2Client
.
BlockByNumber
(
context
.
Background
(),
bedrockStartingBlockNumber
)
log
.
Debug
(
"Migration data"
,
"ovm-path"
,
ovmMsgs
,
"evm-messages"
,
evmMsgs
)
ovmMessages
,
err
:=
migration
.
NewSentMessage
(
ovmMsgs
)
log
.
Info
(
"Withdrawal config"
,
"finalization-period"
,
period
,
"bedrock-starting-block-number"
,
bedrockStartingBlockNumber
,
"bedrock-starting-block-hash"
,
bedrockStartingBlock
.
Hash
()
.
Hex
())
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
{
return
err
}
evmMessages
,
err
:=
migration
.
NewSentMessage
(
evmMsgs
)
// create a transactor
opts
,
err
:=
newTransactor
(
ctx
)
if
err
!=
nil
{
return
err
}
optimismPortalAddress
:=
ctx
.
String
(
"optimism-portal-address"
)
if
len
(
optimismPortalAddress
)
==
0
{
return
errors
.
New
(
"OptimismPortal address not configured"
)
}
optimismPortalAddr
:=
common
.
HexToAddress
(
optimismPortalAddress
)
// Need this to compare in event parsing
l1StandardBridgeAddress
:=
common
.
HexToAddress
(
ctx
.
String
(
"l1-standard-bridge-address"
))
migrationData
:=
migration
.
MigrationData
{
OvmMessages
:
ovmMessages
,
EvmMessages
:
evmMessages
,
}
// iterate over all of the withdrawals and submit them
for
i
,
wd
:=
range
wds
{
log
.
Info
(
"Processing withdrawal"
,
"index"
,
i
)
// create the set of withdrawals
wds
,
err
:=
migrationData
.
ToWithdrawals
(
)
// migrate the withdrawal
withdrawal
,
err
:=
crossdomain
.
MigrateWithdrawal
(
wd
,
&
l1xdmAddr
)
if
err
!=
nil
{
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"
)
if
l1xdmAddress
==
""
{
return
errors
.
New
(
"Must pass in --l1-crossdomain-messenger-address"
)
lcdm
:=
wd
.
CrossDomainMessage
()
legacyXdmHash
,
err
:=
lcdm
.
Hash
()
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
{
return
err
}
portal
,
err
:=
bindings
.
NewOptimismPortal
(
optimismPortalAddr
,
l1Client
)
// This is just wrong
xdmHash
:=
crypto
.
Keccak256Hash
(
withdrawal
.
Data
)
if
err
!=
nil
{
return
err
}
l2OracleAddr
,
err
:=
portal
.
L2ORACLE
(
&
bind
.
CallOpts
{}
)
isSuccessNew
,
err
:=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
return
err
}
oracle
,
err
:=
bindings
.
NewL2OutputOracle
(
l2OracleAddr
,
l1Client
)
isFailedNew
,
err
:=
contracts
.
L1CrossDomainMessenger
.
FailedMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
return
nil
return
err
}
log
.
Info
(
"Addresses"
,
"l1-crossdomain-messenger"
,
l1xdmAddr
,
"optimism-portal"
,
optimismPortalAddr
,
"output-oracle"
,
l2OracleAddr
,
)
log
.
Info
(
"cross domain messenger status"
,
"hash"
,
legacyXdmHash
.
Hex
(),
"success"
,
isSuccess
,
"failed"
,
isFailed
,
"is-success-new"
,
isSuccessNew
,
"is-failed-new"
,
isFailedNew
)
period
,
err
:=
portal
.
FINALIZATIONPERIODSECONDS
(
&
bind
.
CallOpts
{})
// compute the storage slot
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
err
!=
nil
{
return
err
}
transitionBlockNumber
:=
new
(
big
.
Int
)
.
SetUint64
(
ctx
.
Uint64
(
"bedrock-transition-block-number"
))
log
.
Info
(
"Withdrawal config"
,
"finalization-period"
,
period
,
"bedrock-transition-block-number"
,
transitionBlockNumber
)
if
ctx
.
String
(
"private-key"
)
==
""
{
return
errors
.
New
(
"No private key to transact with"
)
// successful messages can be skipped, received messages failed
// their execution and should be replayed
if
isSuccessNew
{
log
.
Info
(
"Message already relayed"
,
"index"
,
i
,
"hash"
,
hash
,
"slot"
,
slot
)
continue
}
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
{
return
err
}
log
.
Debug
(
"L2ToL1MessagePasser status"
,
"value"
,
common
.
Bytes2Hex
(
storageValue
))
type
badWithdrawal
struct
{
Withdrawal
*
crossdomain
.
Withdrawal
`json:"withdrawal"`
Legacy
*
crossdomain
.
LegacyWithdrawal
`json:"legacy"`
Trace
callFrame
`json:"trace"`
Index
int
`json"index"`
// the value should be set to a boolean in storage
if
!
bytes
.
Equal
(
storageValue
,
abiTrue
.
Bytes
())
{
return
fmt
.
Errorf
(
"storage slot %x not found in state"
,
slot
)
}
badWithdrawals
:=
make
([]
badWithdrawal
,
0
)
// 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
)
legacySlot
,
err
:=
wd
.
StorageSlot
()
if
err
!=
nil
{
return
err
}
// compute the withdrawal hash
hash
,
err
:=
withdrawal
.
Hash
()
legacyStorageValue
,
err
:=
clients
.
L2Client
.
StorageAt
(
context
.
Background
(),
predeploys
.
LegacyMessagePasserAddr
,
legacySlot
,
nil
)
if
err
!=
nil
{
return
err
}
// check to see if the withdrawal has already been successfully
// relayed or received
isSuccess
,
err
:=
l1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
hash
)
log
.
Debug
(
"LegacyMessagePasser status"
,
"value"
,
common
.
Bytes2Hex
(
legacyStorageValue
))
// check to see if its already been proven
proven
,
err
:=
contracts
.
OptimismPortal
.
ProvenWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
if
err
!=
nil
{
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
{
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
{
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
// their execution and should be replayed
if
isSuccess
{
log
.
Info
(
"Message already relayed"
,
"index"
,
i
,
"hash"
,
hash
,
"slot"
,
slot
)
continue
finalizationTrace
,
err
:=
callTrace
(
clients
,
receipt
)
if
err
!=
nil
{
return
nil
}
// create the values required for submitting a proof
l2OutputIndex
,
outputRootProof
,
trieNodes
,
err
:=
createOutput
(
withdrawal
,
oracle
,
transitionBlockNumber
,
l2Client
,
gclient
)
isSuccessNewPost
,
err
:=
contracts
.
L1CrossDomainMessenger
.
SuccessfulMessages
(
&
bind
.
CallOpts
{},
xdmHash
)
if
err
!=
nil
{
return
err
}
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
privateKey
,
l1ChainID
)
if
err
!=
nil
{
// This would indicate that there is a replayability problem
if
isSuccess
&&
isSuccessNewPost
{
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"should revert"
);
err
!=
nil
{
return
err
}
panic
(
"THIS SHOULD REVERT"
)
}
// check to see if its already been proven
proven
,
err
:=
portal
.
ProvenWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
if
err
!=
nil
{
callFrame
:=
findWithdrawalCall
(
&
finalizationTrace
,
wd
,
l1xdmAddr
)
if
callFrame
==
nil
{
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"cannot find callframe"
);
err
!=
nil
{
return
err
}
continue
}
wdTx
:=
withdrawal
.
WithdrawalTransaction
()
// 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
,
)
traceJson
,
err
:=
json
.
MarshalIndent
(
callFrame
,
""
,
" "
)
if
err
!=
nil
{
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
{
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
{
return
err
}
initialTime
:=
block
.
Time
()
for
{
log
.
Info
(
"waiting for finalization"
)
if
block
.
Time
()
>=
initialTime
+
period
.
Uint64
()
{
log
.
Info
(
"can be finalized"
)
break
log
.
Info
(
"decoded calldata"
,
"name"
,
method
.
Name
)
switch
method
.
Name
{
case
"finalizeERC20Withdrawal"
:
if
err
:=
handleFinalizeERC20Withdrawal
(
args
,
receipt
,
l1StandardBridgeAddress
);
err
!=
nil
{
return
err
}
time
.
Sleep
(
1
*
time
.
Second
)
block
,
err
=
l1Client
.
BlockByNumber
(
context
.
Background
(),
nil
)
if
err
!=
nil
{
case
"finalizeETHWithdrawal"
:
if
err
:=
handleFinalizeETHWithdrawal
(
args
);
err
!=
nil
{
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
isFinalized
,
err
:=
portal
.
FinalizedWithdrawals
(
&
bind
.
CallOpts
{},
hash
)
// Ensure that the target's balance was increasedData correctl
y
wdValue
,
err
:=
wd
.
Value
(
)
if
err
!=
nil
{
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
{
log
.
Info
(
"Finalizing withdrawal"
)
// check that the user's intents are actually executed
if
common
.
HexToAddress
(
callFrame
.
To
)
!=
*
wd
.
Target
{
log
.
Info
(
"target mismatch"
,
"index"
,
i
)
// Get the ETH balance of the withdrawal target *before* the finalization
targetBalBefore
,
err
:=
l1Client
.
BalanceAt
(
context
.
Background
(),
common
.
BytesToAddress
(
wd
.
Target
.
Bytes
()),
nil
)
if
err
!=
nil
{
if
err
:=
writeSuspicious
(
f
,
withdrawal
,
wd
,
finalizationTrace
,
i
,
"target mismatch"
);
err
!=
nil
{
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
tx
,
err
:=
portal
.
FinalizeWithdrawalTransaction
(
opts
,
wdTx
,
)
// Get the ETH balance of the withdrawal target *after* the finalization
targetBalAfter
,
err
:=
clients
.
L1Client
.
BalanceAt
(
context
.
Background
(),
*
wd
.
Target
,
nil
)
if
err
!=
nil
{
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
{
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
tracer
:=
"callTracer"
traceConfig
:=
tracers
.
TraceConfig
{
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
{
return
err
return
finalizationTrace
,
err
}
return
finalizationTrace
,
err
}
callFrame
:=
findWithdrawalCall
(
&
finalizationTrace
,
wd
,
l1xdmAddr
)
if
callFrame
==
nil
{
return
errors
.
New
(
"cannot find callframe"
)
func
handleFinalizeETHWithdrawal
(
args
[]
any
)
error
{
from
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: from"
)
}
traceJson
,
err
:=
json
.
MarshalIndent
(
callFrame
,
""
,
" "
)
if
err
!=
nil
{
return
err
to
,
ok
:=
args
[
1
]
.
(
common
.
Address
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: to"
)
}
log
.
Info
(
fmt
.
Sprintf
(
"%v"
,
string
(
traceJson
)))
erc20Abi
,
err
:=
bindings
.
ERC20MetaData
.
GetAbi
()
if
err
!=
nil
{
return
err
amount
,
ok
:=
args
[
2
]
.
(
*
big
.
Int
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: amount"
)
}
transferEvent
:=
erc20Abi
.
Events
[
"Transfer"
]
abi
,
err
:=
bindings
.
L1StandardBridgeMetaData
.
GetAbi
()
if
err
!=
nil
{
return
err
extraData
,
ok
:=
args
[
3
]
.
([]
byte
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: extraData"
)
}
calldata
:=
hexutil
.
MustDecode
(
callFrame
.
Input
)
log
.
Info
(
"decoded calldata"
,
"from"
,
from
,
"to"
,
to
,
"amount"
,
amount
,
"extraData"
,
extraData
,
)
// 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
:
]
)
return
nil
}
func
handleFinalizeERC20Withdrawal
(
args
[]
any
,
receipt
*
types
.
Receipt
,
l1StandardBridgeAddress
common
.
Address
)
error
{
erc20Abi
,
err
:=
bindings
.
ERC20MetaData
.
GetAbi
(
)
if
err
!=
nil
{
return
err
}
transferEvent
:=
erc20Abi
.
Events
[
"Transfer"
]
log
.
Info
(
"decoded calldata"
,
"name"
,
method
.
Name
)
switch
method
.
Name
{
case
"finalizeERC20Withdrawal"
:
// Handle logic for ERC20 withdrawals
l1Token
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
...
...
@@ -557,146 +617,326 @@ func main() {
for
_
,
l
:=
range
receipt
.
Logs
{
topic
:=
l
.
Topics
[
0
]
if
topic
==
transferEvent
.
ID
{
if
l
.
Address
==
l1Token
{
a
,
_
:=
transferEvent
.
Inputs
.
Unpack
(
l
.
Data
)
// TODO: add a check here for balance diff
log
.
Info
(
"EVENT FOUND"
,
"args"
,
a
)
if
len
(
l
.
Topics
)
<
3
{
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"
:
// handle logic for ETH withdrawals
from
,
ok
:=
args
[
0
]
.
(
common
.
Address
)
if
!
ok
{
return
fmt
.
Errorf
(
"invalid type: from"
)
if
to
!=
_to
{
return
fmt
.
Errorf
(
"to mismatch: %x - %x"
,
to
,
_to
)
}
to
,
ok
:=
args
[
1
]
.
(
common
.
Address
)
_amount
,
ok
:=
a
[
0
]
.
(
*
big
.
Int
)
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
(
"decoded calldata"
,
"from"
,
from
,
"to"
,
to
,
"amount"
,
amount
,
"extraData"
,
extraData
,
return
nil
}
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
{
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
:
log
.
Info
(
"Unhandled method"
,
"name"
,
method
.
Name
)
if
err
!=
nil
{
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
wdValue
,
err
:=
wd
.
Value
()
log
.
Info
(
"withdrawal proved"
,
"tx-hash"
,
tx
.
Hash
(),
"withdrawal-hash"
,
hash
)
block
,
err
:=
cl
.
L1Client
.
BlockByHash
(
context
.
Background
(),
receipt
.
BlockHash
)
if
err
!=
nil
{
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
))
initialTime
:=
block
.
Time
()
for
{
log
.
Info
(
"waiting for finalization"
)
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
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
func
finalizeWithdrawalTransaction
(
c
*
contracts
,
cl
*
clients
,
opts
*
bind
.
TransactOpts
,
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
)
{
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
optimismPortalAddr
:=
common
.
HexToAddress
(
optimismPortalAddress
)
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
)
{
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
l1xdmAddr
:=
common
.
HexToAddress
(
l1xdmAddress
)
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
targetBalAfter
,
err
:=
l1Client
.
BalanceAt
(
context
.
Background
(),
*
wd
.
Target
,
nil
)
log
.
Info
(
"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
{
return
err
return
nil
,
err
}
l1ChainID
,
err
:=
l1Client
.
ChainID
(
context
.
Background
())
if
err
!=
nil
{
return
nil
,
err
}
diff
:=
new
(
big
.
Int
)
.
Sub
(
targetBalAfter
,
targetBalBefore
)
log
.
Debug
(
"balances"
,
"before"
,
targetBalBefore
,
"after"
,
targetBalAfter
,
"diff"
,
diff
)
l2RpcURL
:=
ctx
.
String
(
"l2-rpc-url"
)
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
{
badWithdrawals
=
append
(
badWithdrawals
,
badWithdrawal
{
Withdrawal
:
withdrawal
,
Legacy
:
wd
,
Trace
:
finalizationTrace
,
Index
:
i
,
})
l1RpcClient
,
err
:=
rpc
.
DialContext
(
context
.
Background
(),
l1RpcURL
)
if
err
!=
nil
{
return
nil
,
err
}
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
if
err
:=
writeJSON
(
ctx
.
String
(
"bad-withdrawals-out"
),
badWithdrawals
);
err
!=
nil
{
return
err
migrationData
:=
migration
.
MigrationData
{
OvmMessages
:
ovmMessages
,
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
{
log
.
Crit
(
"error in migration"
,
"err"
,
err
)
return
wds
,
nil
}
// 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
{
f
,
err
:=
os
.
OpenFile
(
outfile
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
0
o755
)
func
writeSuspicious
(
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
{
return
err
}
defer
f
.
Close
()
enc
:=
json
.
NewEncoder
(
f
)
enc
.
SetIndent
(
""
,
" "
)
return
enc
.
Encode
(
input
)
_
,
err
=
f
.
WriteString
(
string
(
data
)
+
"
\n
"
)
return
err
}
op-chain-ops/crossdomain/legacy_withdrawal.go
View file @
b2242f40
...
...
@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
...
...
@@ -17,7 +18,7 @@ import (
type
LegacyWithdrawal
struct
{
Target
*
common
.
Address
`json:"target"`
Sender
*
common
.
Address
`json:"sender"`
Data
[]
byte
`json:"data"`
Data
hexutil
.
Bytes
`json:"data"`
Nonce
*
big
.
Int
`json:"nonce"`
}
...
...
@@ -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
// the L2CrossDomainMessenger address.
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
{
return
nil
,
fmt
.
Errorf
(
"cannot encode LegacyWithdrawal: %w"
,
err
)
}
...
...
@@ -98,7 +99,7 @@ func (w *LegacyWithdrawal) Decode(data []byte) error {
w
.
Target
=
&
target
w
.
Sender
=
&
sender
w
.
Data
=
msgData
w
.
Data
=
hexutil
.
Bytes
(
msgData
)
w
.
Nonce
=
nonce
return
nil
}
...
...
op-chain-ops/crossdomain/message.go
View file @
b2242f40
...
...
@@ -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.
// This only works for version 0 CrossDomainMessages as not all of
// 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