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
77194e21
Unverified
Commit
77194e21
authored
Apr 17, 2023
by
OptimismBot
Committed by
GitHub
Apr 17, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5439 from ethereum-optimism/fix/witness-file-parsing
op-chain-ops: prevent invalid decoding errors
parents
a13ff5f8
deec5a91
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
209 additions
and
17 deletions
+209
-17
witness.go
op-chain-ops/crossdomain/witness.go
+39
-17
witness_test.go
op-chain-ops/crossdomain/witness_test.go
+170
-0
No files found.
op-chain-ops/crossdomain/witness.go
View file @
77194e21
...
@@ -41,6 +41,40 @@ func NewSentMessageFromJSON(path string) ([]*SentMessage, error) {
...
@@ -41,6 +41,40 @@ func NewSentMessageFromJSON(path string) ([]*SentMessage, error) {
return
j
,
nil
return
j
,
nil
}
}
// decodeWitnessCalldata abi decodes the calldata encoded in the input witness
// file. It errors if the 4 byte selector is not specifically for `passMessageToL1`.
// It also errors if the abi decoding fails.
func
decodeWitnessCalldata
(
msg
[]
byte
)
([]
byte
,
error
)
{
abi
,
err
:=
bindings
.
LegacyMessagePasserMetaData
.
GetAbi
()
if
err
!=
nil
{
panic
(
"should always be able to get message passer abi"
)
}
if
size
:=
len
(
msg
);
size
<
4
{
return
nil
,
fmt
.
Errorf
(
"message too short: %d"
,
size
)
}
method
,
err
:=
abi
.
MethodById
(
msg
[
:
4
])
if
err
!=
nil
{
return
nil
,
err
}
if
method
.
Sig
!=
"passMessageToL1(bytes)"
{
return
nil
,
fmt
.
Errorf
(
"unknown method: %s"
,
method
.
Name
)
}
out
,
err
:=
method
.
Inputs
.
Unpack
(
msg
[
4
:
])
if
err
!=
nil
{
return
nil
,
err
}
cast
,
ok
:=
out
[
0
]
.
([]
byte
)
if
!
ok
{
panic
(
"should always be able to cast type []byte"
)
}
return
cast
,
nil
}
// ReadWitnessData will read messages and addresses from a raw l2geth state
// ReadWitnessData will read messages and addresses from a raw l2geth state
// dump file.
// dump file.
func
ReadWitnessData
(
path
string
)
([]
*
SentMessage
,
OVMETHAddresses
,
error
)
{
func
ReadWitnessData
(
path
string
)
([]
*
SentMessage
,
OVMETHAddresses
,
error
)
{
...
@@ -72,30 +106,18 @@ func ReadWitnessData(path string) ([]*SentMessage, OVMETHAddresses, error) {
...
@@ -72,30 +106,18 @@ func ReadWitnessData(path string) ([]*SentMessage, OVMETHAddresses, error) {
msg
=
"0x"
+
msg
msg
=
"0x"
+
msg
}
}
abi
,
err
:=
bindings
.
LegacyMessagePasserMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"failed to get abi: %w"
,
err
)
}
msgB
:=
hexutil
.
MustDecode
(
msg
)
msgB
:=
hexutil
.
MustDecode
(
msg
)
method
,
err
:=
abi
.
MethodById
(
msgB
[
:
4
])
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"failed to get method: %w"
,
err
)
}
out
,
err
:=
method
.
Inputs
.
Unpack
(
msgB
[
4
:
])
// Skip any errors
calldata
,
err
:=
decodeWitnessCalldata
(
msgB
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"failed to unpack: %w"
,
err
)
log
.
Warn
(
"cannot decode witness calldata"
,
"err"
,
err
)
}
continue
cast
,
ok
:=
out
[
0
]
.
([]
byte
)
if
!
ok
{
return
nil
,
nil
,
fmt
.
Errorf
(
"failed to cast to bytes"
)
}
}
witnesses
=
append
(
witnesses
,
&
SentMessage
{
witnesses
=
append
(
witnesses
,
&
SentMessage
{
Who
:
common
.
HexToAddress
(
splits
[
1
]),
Who
:
common
.
HexToAddress
(
splits
[
1
]),
Msg
:
ca
st
,
Msg
:
ca
lldata
,
})
})
case
"ETH"
:
case
"ETH"
:
addresses
[
common
.
HexToAddress
(
splits
[
1
])]
=
true
addresses
[
common
.
HexToAddress
(
splits
[
1
])]
=
true
...
...
op-chain-ops/crossdomain/witness_test.go
View file @
77194e21
package
crossdomain
package
crossdomain
import
(
import
(
"context"
"math/big"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -41,3 +51,163 @@ func TestRead(t *testing.T) {
...
@@ -41,3 +51,163 @@ func TestRead(t *testing.T) {
common
.
HexToAddress
(
"0x6340d44c5174588B312F545eEC4a42f8a514eF50"
)
:
true
,
common
.
HexToAddress
(
"0x6340d44c5174588B312F545eEC4a42f8a514eF50"
)
:
true
,
},
addresses
)
},
addresses
)
}
}
// TestDecodeWitnessCallData tests that the witness data is parsed correctly
// from an input bytes slice.
func
TestDecodeWitnessCallData
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
err
bool
msg
[]
byte
want
[]
byte
}{
{
name
:
"too-small"
,
err
:
true
,
msg
:
common
.
FromHex
(
"0x0000"
),
},
{
name
:
"unknown-selector"
,
err
:
true
,
msg
:
common
.
FromHex
(
"0x00000000"
),
},
{
name
:
"wrong-selector"
,
err
:
true
,
// 0x54fd4d50 is the selector for `version()`
msg
:
common
.
FromHex
(
"0x54fd4d50"
),
},
{
name
:
"invalid-calldata-only-selector"
,
err
:
true
,
// 0xcafa81dc is the selector for `passMessageToL1(bytes)`
msg
:
common
.
FromHex
(
"0xcafa81dc"
),
},
{
name
:
"invalid-calldata-invalid-bytes"
,
err
:
true
,
// 0xcafa81dc is the selector for passMessageToL1(bytes)
msg
:
common
.
FromHex
(
"0xcafa81dc0000"
),
},
{
name
:
"valid-calldata"
,
msg
:
common
.
FromHex
(
"0xcafa81dc"
+
"0000000000000000000000000000000000000000000000000000000000000020"
+
"0000000000000000000000000000000000000000000000000000000000000002"
+
"1234000000000000000000000000000000000000000000000000000000000000"
,
),
want
:
common
.
FromHex
(
"0x1234"
),
},
}
for
_
,
tt
:=
range
tests
{
test
:=
tt
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
if
test
.
err
{
_
,
err
:=
decodeWitnessCalldata
(
test
.
msg
)
require
.
Error
(
t
,
err
)
}
else
{
want
,
err
:=
decodeWitnessCalldata
(
test
.
msg
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
test
.
want
,
want
)
}
})
}
}
// TestMessagePasserSafety ensures that the LegacyMessagePasser contract reverts when it is called
// with incorrect calldata. The function signature is correct but the calldata is not abi encoded
// correctly. It is expected the solidity reverts when it cannot abi decode the calldata correctly.
// Only a call to `passMessageToL1` with abi encoded `bytes` will result in the `successfulMessages`
// mapping being updated.
func
TestMessagePasserSafety
(
t
*
testing
.
T
)
{
testKey
,
_
:=
crypto
.
HexToECDSA
(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
)
testAddr
:=
crypto
.
PubkeyToAddress
(
testKey
.
PublicKey
)
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
testKey
,
big
.
NewInt
(
1337
))
require
.
NoError
(
t
,
err
)
backend
:=
backends
.
NewSimulatedBackend
(
core
.
GenesisAlloc
{
testAddr
:
{
Balance
:
big
.
NewInt
(
10000000000000000
)}},
30
_000_000
,
)
defer
backend
.
Close
()
// deploy the LegacyMessagePasser contract
addr
,
tx
,
contract
,
err
:=
bindings
.
DeployLegacyMessagePasser
(
opts
,
backend
)
require
.
NoError
(
t
,
err
)
backend
.
Commit
()
_
,
err
=
bind
.
WaitMined
(
context
.
Background
(),
backend
,
tx
)
require
.
NoError
(
t
,
err
)
// ensure that it deployed
code
,
err
:=
backend
.
CodeAt
(
context
.
Background
(),
addr
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
len
(
code
)
>
0
)
// dummy message
msg
:=
[]
byte
{
0x00
,
0x01
,
0x02
,
0x03
}
// call `passMessageToL1`
msgTx
,
err
:=
contract
.
PassMessageToL1
(
opts
,
msg
)
require
.
NoError
(
t
,
err
)
// ensure that the receipt is successful
backend
.
Commit
()
msgReceipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
backend
,
msgTx
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
msgReceipt
.
Status
,
types
.
ReceiptStatusSuccessful
)
// check for the data in the `successfulMessages` mapping
data
:=
make
([]
byte
,
len
(
msg
)
+
len
(
testAddr
))
copy
(
data
[
:
],
msg
)
copy
(
data
[
len
(
msg
)
:
],
testAddr
.
Bytes
())
digest
:=
crypto
.
Keccak256Hash
(
data
)
contains
,
err
:=
contract
.
SentMessages
(
&
bind
.
CallOpts
{},
digest
)
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
contains
)
// build a transaction with improperly formatted calldata
nonce
,
err
:=
backend
.
NonceAt
(
context
.
Background
(),
testAddr
,
nil
)
require
.
NoError
(
t
,
err
)
// append msg without abi encoding it
selector
:=
crypto
.
Keccak256
([]
byte
(
"passMessageToL1(bytes)"
))[
0
:
4
]
require
.
Equal
(
t
,
selector
,
hexutil
.
MustDecode
(
"0xcafa81dc"
))
calldata
:=
append
(
selector
,
msg
...
)
faultyTransaction
,
err
:=
opts
.
Signer
(
testAddr
,
types
.
NewTx
(
&
types
.
DynamicFeeTx
{
ChainID
:
big
.
NewInt
(
1337
),
Nonce
:
nonce
,
GasTipCap
:
msgTx
.
GasTipCap
(),
GasFeeCap
:
msgTx
.
GasFeeCap
(),
Gas
:
msgTx
.
Gas
()
*
2
,
To
:
msgTx
.
To
(),
Data
:
calldata
,
}))
require
.
NoError
(
t
,
err
)
err
=
backend
.
SendTransaction
(
context
.
Background
(),
faultyTransaction
)
require
.
NoError
(
t
,
err
)
// the transaction should revert
backend
.
Commit
()
badReceipt
,
err
:=
bind
.
WaitMined
(
context
.
Background
(),
backend
,
faultyTransaction
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
badReceipt
.
Status
,
types
.
ReceiptStatusFailed
)
// test the transaction calldata against the abi unpacking
abi
,
err
:=
bindings
.
LegacyMessagePasserMetaData
.
GetAbi
()
require
.
NoError
(
t
,
err
)
method
,
err
:=
abi
.
MethodById
(
selector
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
method
.
Name
,
"passMessageToL1"
)
// the faulty transaction has the correct 4 byte selector but doesn't
// have abi encoded bytes following it
require
.
Equal
(
t
,
faultyTransaction
.
Data
()[
:
4
],
selector
)
_
,
err
=
method
.
Inputs
.
Unpack
(
faultyTransaction
.
Data
()[
4
:
])
require
.
Error
(
t
,
err
)
// the original transaction has the correct 4 byte selector and abi encoded bytes
_
,
err
=
method
.
Inputs
.
Unpack
(
msgTx
.
Data
()[
4
:
])
require
.
NoError
(
t
,
err
)
}
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