Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 0 additions & 116 deletions core/types/interoptypes/interop.go
Original file line number Diff line number Diff line change
@@ -1,132 +1,16 @@
package interoptypes

import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"math"

"github.com/holiman/uint256"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)

var ExecutingMessageEventTopic = crypto.Keccak256Hash([]byte("ExecutingMessage(bytes32,(address,uint256,uint256,uint256,uint256))"))

type Message struct {
Identifier Identifier `json:"identifier"`
PayloadHash common.Hash `json:"payloadHash"`
}

func (m *Message) DecodeEvent(topics []common.Hash, data []byte) error {
if len(topics) != 2 { // event hash, indexed payloadHash
return fmt.Errorf("unexpected number of event topics: %d", len(topics))
}
if topics[0] != ExecutingMessageEventTopic {
return fmt.Errorf("unexpected event topic %q", topics[0])
}
if len(data) != 32*5 {
return fmt.Errorf("unexpected identifier data length: %d", len(data))
}
take := func(length uint) []byte {
taken := data[:length]
data = data[length:]
return taken
}
takeZeroes := func(length uint) error {
for _, v := range take(length) {
if v != 0 {
return errors.New("expected zero")
}
}
return nil
}
if err := takeZeroes(12); err != nil {
return fmt.Errorf("invalid address padding: %w", err)
}
m.Identifier.Origin = common.Address(take(20))
if err := takeZeroes(32 - 8); err != nil {
return fmt.Errorf("invalid block number padding: %w", err)
}
m.Identifier.BlockNumber = binary.BigEndian.Uint64(take(8))
if err := takeZeroes(32 - 4); err != nil {
return fmt.Errorf("invalid log index padding: %w", err)
}
m.Identifier.LogIndex = binary.BigEndian.Uint32(take(4))
if err := takeZeroes(32 - 8); err != nil {
return fmt.Errorf("invalid timestamp padding: %w", err)
}
m.Identifier.Timestamp = binary.BigEndian.Uint64(take(8))
m.Identifier.ChainID.SetBytes32(take(32))
m.PayloadHash = topics[1]
return nil
}

func ExecutingMessagesFromLogs(logs []*types.Log) ([]Message, error) {
var executingMessages []Message
for i, l := range logs {
if l.Address == params.InteropCrossL2InboxAddress {
// ignore events that do not match this
if len(l.Topics) == 0 || l.Topics[0] != ExecutingMessageEventTopic {
continue
}
var msg Message
if err := msg.DecodeEvent(l.Topics, l.Data); err != nil {
return nil, fmt.Errorf("invalid executing message %d, tx-log %d: %w", len(executingMessages), i, err)
}
executingMessages = append(executingMessages, msg)
}
}
return executingMessages, nil
}

type Identifier struct {
Origin common.Address
BlockNumber uint64
LogIndex uint32
Timestamp uint64
ChainID uint256.Int // flat, not a pointer, to make Identifier safe as map key
}

type identifierMarshaling struct {
Origin common.Address `json:"origin"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
LogIndex hexutil.Uint64 `json:"logIndex"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ChainID hexutil.U256 `json:"chainID"`
}

func (id Identifier) MarshalJSON() ([]byte, error) {
var enc identifierMarshaling
enc.Origin = id.Origin
enc.BlockNumber = hexutil.Uint64(id.BlockNumber)
enc.LogIndex = hexutil.Uint64(id.LogIndex)
enc.Timestamp = hexutil.Uint64(id.Timestamp)
enc.ChainID = (hexutil.U256)(id.ChainID)
return json.Marshal(&enc)
}

func (id *Identifier) UnmarshalJSON(input []byte) error {
var dec identifierMarshaling
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
id.Origin = dec.Origin
id.BlockNumber = uint64(dec.BlockNumber)
if dec.LogIndex > math.MaxUint32 {
return fmt.Errorf("log index too large: %d", dec.LogIndex)
}
id.LogIndex = uint32(dec.LogIndex)
id.Timestamp = uint64(dec.Timestamp)
id.ChainID = (uint256.Int)(dec.ChainID)
return nil
}

type SafetyLevel string

func (lvl SafetyLevel) String() string {
Expand Down
156 changes: 0 additions & 156 deletions core/types/interoptypes/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,6 @@ import (
"github.com/ethereum/go-ethereum/params"
)

func FuzzMessage_DecodeEvent(f *testing.F) {
f.Fuzz(func(t *testing.T, validEvTopic bool, numTopics uint8, data []byte) {
if len(data) < 32 {
return
}
if len(data) > 100_000 {
return
}
if validEvTopic { // valid even signature topic implies a topic to be there
numTopics += 1
}
if numTopics > 4 { // There can be no more than 4 topics per log event
return
}
if int(numTopics)*32 > len(data) {
return
}
var topics []common.Hash
if validEvTopic {
topics = append(topics, ExecutingMessageEventTopic)
}
for i := 0; i < int(numTopics); i++ {
var topic common.Hash
copy(topic[:], data[:])
data = data[32:]
}
require.NotPanics(t, func() {
var m Message
_ = m.DecodeEvent(topics, data)
})
})
}

func TestSafetyLevel(t *testing.T) {
require.True(t, Invalid.wellFormatted())
require.True(t, Unsafe.wellFormatted())
Expand All @@ -54,129 +21,6 @@ func TestSafetyLevel(t *testing.T) {
require.False(t, SafetyLevel("").wellFormatted())
}

func TestInteropMessageFormatEdgeCases(t *testing.T) {
tests := []struct {
name string
log *types.Log
expectedError string
}{
{
name: "Empty Topics",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{},
Data: make([]byte, 32*5),
},
expectedError: "unexpected number of event topics: 0",
},
{
name: "Wrong Event Topic",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash([]byte("wrong topic")),
common.BytesToHash([]byte("payloadHash")),
},
Data: make([]byte, 32*5),
},
expectedError: "unexpected event topic",
},
{
name: "Missing PayloadHash Topic",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
},
Data: make([]byte, 32*5),
},
expectedError: "unexpected number of event topics: 1",
},
{
name: "Too Many Topics",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
common.BytesToHash([]byte("payloadHash")),
common.BytesToHash([]byte("extra")),
},
Data: make([]byte, 32*5),
},
expectedError: "unexpected number of event topics: 3",
},
{
name: "Data Too Short",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
common.BytesToHash([]byte("payloadHash")),
},
Data: make([]byte, 32*4), // One word too short
},
expectedError: "unexpected identifier data length: 128",
},
{
name: "Data Too Long",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
common.BytesToHash([]byte("payloadHash")),
},
Data: make([]byte, 32*6), // One word too long
},
expectedError: "unexpected identifier data length: 192",
},
{
name: "Invalid Address Padding",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
common.BytesToHash([]byte("payloadHash")),
},
Data: func() []byte {
data := make([]byte, 32*5)
data[0] = 1 // Add non-zero byte in address padding
return data
}(),
},
expectedError: "invalid address padding",
},
{
name: "Invalid Block Number Padding",
log: &types.Log{
Address: params.InteropCrossL2InboxAddress,
Topics: []common.Hash{
common.BytesToHash(ExecutingMessageEventTopic[:]),
common.BytesToHash([]byte("payloadHash")),
},
Data: func() []byte {
data := make([]byte, 32*5)
data[32+23] = 1 // Add non-zero byte in block number padding
return data
}(),
},
expectedError: "invalid block number padding",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var msg Message
err := msg.DecodeEvent(tt.log.Topics, tt.log.Data)
if tt.expectedError != "" {
require.Error(t, err)
require.ErrorContains(t, err, tt.expectedError)
} else {
require.NoError(t, err)
}
})
}
}

func TestTxToInteropAccessList(t *testing.T) {
t.Run("Tx has no access list", func(t *testing.T) {
tx := types.NewTx(&types.DynamicFeeTx{})
Expand Down