Skip to content

Commit a7c92d8

Browse files
authored
op-node: Fix gossip validation for Jovian blocks (#17940)
Jovian blocks can have a non-zero blobGasUsed value, as the DA footprint is stored in it.
1 parent aca9c3f commit a7c92d8

File tree

3 files changed

+51
-21
lines changed

3 files changed

+51
-21
lines changed

op-node/p2p/gossip.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/ethereum-optimism/optimism/op-node/rollup"
2424
"github.com/ethereum-optimism/optimism/op-service/eth"
25+
"github.com/ethereum-optimism/optimism/op-service/ptr"
2526
opsigner "github.com/ethereum-optimism/optimism/op-service/signer"
2627
)
2728

@@ -48,8 +49,10 @@ const (
4849
// Message domains, the msg id function uncompresses to keep data monomorphic,
4950
// but invalid compressed data will need a unique different id.
5051

51-
var MessageDomainInvalidSnappy = [4]byte{0, 0, 0, 0}
52-
var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
52+
var (
53+
MessageDomainInvalidSnappy = [4]byte{0, 0, 0, 0}
54+
MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
55+
)
5356

5457
type GossipSetupConfigurables interface {
5558
PeerScoringParams() *ScoringParams
@@ -262,7 +265,6 @@ func (sb *seenBlocks) markSeen(h common.Hash) {
262265
}
263266

264267
func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, blockVersion eth.BlockVersion, gossipConf GossipSetupConfigurables) pubsub.ValidatorEx {
265-
266268
// Seen block hashes per block height
267269
// uint64 -> *seenBlocks
268270
blockHeightLRU, err := lru.New[uint64, *seenBlocks](1000)
@@ -380,15 +382,21 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
380382
}
381383

382384
if blockVersion.HasBlobProperties() {
383-
// [REJECT] if the block is on a topic >= V3 and has a blob gas used value that is not zero
384-
if payload.BlobGasUsed == nil || *payload.BlobGasUsed != 0 {
385-
log.Warn("payload is on v3 topic, but has non-zero blob gas used", "bad_hash", payload.BlockHash.String(), "blob_gas_used", payload.BlobGasUsed)
385+
// [REJECT] if the block is on a topic >= V3 and has a nil blob gas used
386+
if payload.BlobGasUsed == nil {
387+
log.Warn("payload is on v3 topic, but has nil blob gas used", "bad_hash", payload.BlockHash.String())
388+
return pubsub.ValidationReject
389+
// [REJECT] if the block is on a topic >= V3 and has a non-zero blob gas used field pre-Jovian
390+
} else if !cfg.IsDAFootprintBlockLimit(uint64(payload.Timestamp)) && *payload.BlobGasUsed != 0 {
391+
log.Warn("payload is on v3 topic, but has non-zero blob gas used",
392+
"bad_hash", payload.BlockHash.String(), "blob_gas_used", *payload.BlobGasUsed)
386393
return pubsub.ValidationReject
387394
}
388395

389396
// [REJECT] if the block is on a topic >= V3 and has an excess blob gas value that is not zero
390397
if payload.ExcessBlobGas == nil || *payload.ExcessBlobGas != 0 {
391-
log.Warn("payload is on v3 topic, but has non-zero excess blob gas", "bad_hash", payload.BlockHash.String(), "excess_blob_gas", payload.ExcessBlobGas)
398+
log.Warn("payload is on v3 topic, but has non-zero excess blob gas",
399+
"bad_hash", payload.BlockHash.String(), "excess_blob_gas", ptr.Str(payload.ExcessBlobGas))
392400
return pubsub.ValidationReject
393401
}
394402
}
@@ -504,7 +512,7 @@ type publisher struct {
504512
var _ GossipOut = (*publisher)(nil)
505513

506514
func combinePeers(allPeers ...[]peer.ID) []peer.ID {
507-
var seen = make(map[peer.ID]bool)
515+
seen := make(map[peer.ID]bool)
508516
var res []peer.ID
509517
for _, peers := range allPeers {
510518
for _, p := range peers {
@@ -674,7 +682,6 @@ func newBlockTopic(ctx context.Context, topicId string, ps *pubsub.PubSub, log l
674682
validator,
675683
pubsub.WithValidatorTimeout(3*time.Second),
676684
pubsub.WithValidatorConcurrency(4))
677-
678685
if err != nil {
679686
return nil, fmt.Errorf("failed to register gossip topic: %w", err)
680687
}
@@ -707,8 +714,10 @@ func newBlockTopic(ctx context.Context, topicId string, ps *pubsub.PubSub, log l
707714
}, nil
708715
}
709716

710-
type TopicSubscriber func(ctx context.Context, sub *pubsub.Subscription)
711-
type MessageHandler func(ctx context.Context, from peer.ID, msg any) error
717+
type (
718+
TopicSubscriber func(ctx context.Context, sub *pubsub.Subscription)
719+
MessageHandler func(ctx context.Context, from peer.ID, msg any) error
720+
)
712721

713722
func BlocksHandler(onBlock func(ctx context.Context, from peer.ID, msg *eth.ExecutionPayloadEnvelope) error) MessageHandler {
714723
return func(ctx context.Context, from peer.ID, msg any) error {

op-node/p2p/gossip_test.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/ethereum-optimism/optimism/op-node/rollup"
1515
"github.com/ethereum-optimism/optimism/op-service/eth"
16+
"github.com/ethereum-optimism/optimism/op-service/ptr"
1617
opsigner "github.com/ethereum-optimism/optimism/op-service/signer"
1718
"github.com/ethereum-optimism/optimism/op-service/testlog"
1819
"github.com/ethereum-optimism/optimism/op-service/testutils"
@@ -123,19 +124,19 @@ func createSignedP2Payload(payload MarshalSSZ, signer Signer, l2ChainID *big.Int
123124
return snappy.Encode(nil, data), nil
124125
}
125126

126-
func createExecutionPayload(w types.Withdrawals, withdrawalsRoot *common.Hash, excessGas, gasUsed *uint64) *eth.ExecutionPayload {
127+
func createExecutionPayload(w types.Withdrawals, withdrawalsRoot *common.Hash, excessBlobGas, blobGasUsed *uint64) *eth.ExecutionPayload {
127128
return &eth.ExecutionPayload{
128129
Timestamp: hexutil.Uint64(time.Now().Unix()),
129130
Withdrawals: &w,
130131
WithdrawalsRoot: withdrawalsRoot,
131-
ExcessBlobGas: (*eth.Uint64Quantity)(excessGas),
132-
BlobGasUsed: (*eth.Uint64Quantity)(gasUsed),
132+
ExcessBlobGas: (*eth.Uint64Quantity)(excessBlobGas),
133+
BlobGasUsed: (*eth.Uint64Quantity)(blobGasUsed),
133134
}
134135
}
135136

136-
func createEnvelope(h *common.Hash, w types.Withdrawals, withdrawalsRoot *common.Hash, excessGas, gasUsed *uint64) *eth.ExecutionPayloadEnvelope {
137+
func createEnvelope(h *common.Hash, w types.Withdrawals, withdrawalsRoot *common.Hash, excessBlobGas, blobGasUsed *uint64) *eth.ExecutionPayloadEnvelope {
137138
return &eth.ExecutionPayloadEnvelope{
138-
ExecutionPayload: createExecutionPayload(w, withdrawalsRoot, excessGas, gasUsed),
139+
ExecutionPayload: createExecutionPayload(w, withdrawalsRoot, excessBlobGas, blobGasUsed),
139140
ParentBeaconBlockRoot: h,
140141
}
141142
}
@@ -157,7 +158,12 @@ func TestBlockValidator(t *testing.T) {
157158
mockGossipConf := &mockGossipSetupConfigurablesWithThreshold{threshold: 60 * time.Second}
158159
v2Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), cfg, runCfg, eth.BlockV2, mockGossipConf)
159160
v3Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), cfg, runCfg, eth.BlockV3, mockGossipConf)
160-
v4Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), cfg, runCfg, eth.BlockV4, mockGossipConf)
161+
v4Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelDebug), cfg, runCfg, eth.BlockV4, mockGossipConf)
162+
jovianCfg := &rollup.Config{
163+
L2ChainID: big.NewInt(100),
164+
JovianTime: ptr.New(uint64(0)),
165+
}
166+
v4JovianValidator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), jovianCfg, runCfg, eth.BlockV4, mockGossipConf)
161167

162168
zero, one := uint64(0), uint64(1)
163169
beaconHash, withdrawalsRoot := common.HexToHash("0x1234"), common.HexToHash("0x9876")
@@ -174,8 +180,7 @@ func TestBlockValidator(t *testing.T) {
174180
{"V3RejectExecutionPayload", v3Validator, pubsub.ValidationReject, createExecutionPayload(types.Withdrawals{}, nil, &zero, &zero)},
175181
}
176182

177-
for _, tt := range payloadTests {
178-
test := tt
183+
for _, test := range payloadTests {
179184
t.Run(fmt.Sprintf("ExecutionPayload_%s", test.name), func(t *testing.T) {
180185
e := &eth.ExecutionPayloadEnvelope{ExecutionPayload: test.payload}
181186
test.payload.BlockHash, _ = e.CheckBlockHash() // hack to generate the block hash easily.
@@ -198,10 +203,11 @@ func TestBlockValidator(t *testing.T) {
198203
{"V3Valid", v3Validator, pubsub.ValidationAccept, createEnvelope(&beaconHash, types.Withdrawals{}, nil, &zero, &zero)},
199204
{"V4Valid", v4Validator, pubsub.ValidationAccept, createEnvelope(&beaconHash, types.Withdrawals{}, &withdrawalsRoot, &zero, &zero)},
200205
{"V4RejectNoWithdrawalRoot", v4Validator, pubsub.ValidationReject, createEnvelope(&beaconHash, types.Withdrawals{}, nil, &zero, &zero)},
206+
{"V4AcceptNonZeroBlobGasUsedJovian", v4JovianValidator, pubsub.ValidationAccept, createEnvelope(&beaconHash, types.Withdrawals{}, &withdrawalsRoot, &zero, &one)},
207+
// Note: v3+ test cases with nil blobGasUsed cannot easily be included because they already fail at the SSZ marshaling stage.
201208
}
202209

203-
for _, tt := range envelopeTests {
204-
test := tt
210+
for _, test := range envelopeTests {
205211
t.Run(fmt.Sprintf("ExecutionPayloadEnvelope_%s", test.name), func(t *testing.T) {
206212
test.payload.ExecutionPayload.BlockHash, _ = test.payload.CheckBlockHash() // hack to generate the block hash easily.
207213
data, err := createSignedP2Payload(test.payload, signer, cfg.L2ChainID)

op-service/ptr/ptr.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Package ptr provides helper functions for working with pointers.
2+
package ptr
3+
4+
import "fmt"
5+
6+
func New[T any](v T) *T {
7+
return &v
8+
}
9+
10+
func Str[T any](v *T) string {
11+
if v == nil {
12+
return "<nil>"
13+
}
14+
return fmt.Sprintf("%v", *v)
15+
}

0 commit comments

Comments
 (0)