github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/gossip_test.go (about)

     1  package p2p
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"math/big"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
    13  	"github.com/ethereum-optimism/optimism/op-node/rollup"
    14  	"github.com/golang/snappy"
    15  
    16  	// "github.com/ethereum-optimism/optimism/op-service/eth"
    17  	"github.com/ethereum-optimism/optimism/op-service/eth"
    18  	"github.com/ethereum-optimism/optimism/op-service/testutils"
    19  	"github.com/ethereum/go-ethereum/common"
    20  	"github.com/ethereum/go-ethereum/common/hexutil"
    21  	"github.com/ethereum/go-ethereum/core/types"
    22  	"github.com/ethereum/go-ethereum/crypto"
    23  
    24  	"github.com/ethereum/go-ethereum/log"
    25  	pubsub "github.com/libp2p/go-libp2p-pubsub"
    26  	pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
    27  	"github.com/libp2p/go-libp2p/core/peer"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"github.com/ethereum-optimism/optimism/op-service/testlog"
    31  )
    32  
    33  func TestGuardGossipValidator(t *testing.T) {
    34  	logger := testlog.Logger(t, log.LevelCrit)
    35  	val := guardGossipValidator(logger, func(ctx context.Context, id peer.ID, message *pubsub.Message) pubsub.ValidationResult {
    36  		if id == "mallory" {
    37  			panic("mallory was here")
    38  		}
    39  		if id == "bob" {
    40  			return pubsub.ValidationIgnore
    41  		}
    42  		return pubsub.ValidationAccept
    43  	})
    44  	// Test that panics from mallory are recovered and rejected,
    45  	// and test that we can continue to ignore bob and accept alice.
    46  	require.Equal(t, pubsub.ValidationAccept, val(context.Background(), "alice", nil))
    47  	require.Equal(t, pubsub.ValidationReject, val(context.Background(), "mallory", nil))
    48  	require.Equal(t, pubsub.ValidationIgnore, val(context.Background(), "bob", nil))
    49  	require.Equal(t, pubsub.ValidationReject, val(context.Background(), "mallory", nil))
    50  	require.Equal(t, pubsub.ValidationAccept, val(context.Background(), "alice", nil))
    51  	require.Equal(t, pubsub.ValidationIgnore, val(context.Background(), "bob", nil))
    52  }
    53  
    54  func TestCombinePeers(t *testing.T) {
    55  	res := combinePeers([]peer.ID{"foo", "bar"}, []peer.ID{"bar", "baz"})
    56  	require.Equal(t, []peer.ID{"foo", "bar", "baz"}, res)
    57  }
    58  
    59  func TestVerifyBlockSignature(t *testing.T) {
    60  	logger := testlog.Logger(t, log.LevelCrit)
    61  	cfg := &rollup.Config{
    62  		L2ChainID: big.NewInt(100),
    63  	}
    64  	peerId := peer.ID("foo")
    65  	secrets, err := e2eutils.DefaultMnemonicConfig.Secrets()
    66  	require.NoError(t, err)
    67  	msg := []byte("any msg")
    68  
    69  	t.Run("Valid", func(t *testing.T) {
    70  		runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
    71  		signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
    72  		sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
    73  		require.NoError(t, err)
    74  		result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
    75  		require.Equal(t, pubsub.ValidationAccept, result)
    76  	})
    77  
    78  	t.Run("WrongSigner", func(t *testing.T) {
    79  		runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: common.HexToAddress("0x1234")}
    80  		signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
    81  		sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
    82  		require.NoError(t, err)
    83  		result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
    84  		require.Equal(t, pubsub.ValidationReject, result)
    85  	})
    86  
    87  	t.Run("InvalidSignature", func(t *testing.T) {
    88  		runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
    89  		sig := make([]byte, 65)
    90  		result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig, msg)
    91  		require.Equal(t, pubsub.ValidationReject, result)
    92  	})
    93  
    94  	t.Run("NoSequencer", func(t *testing.T) {
    95  		runCfg := &testutils.MockRuntimeConfig{}
    96  		signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
    97  		sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg)
    98  		require.NoError(t, err)
    99  		result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg)
   100  		require.Equal(t, pubsub.ValidationIgnore, result)
   101  	})
   102  }
   103  
   104  type MarshalSSZ interface {
   105  	MarshalSSZ(w io.Writer) (n int, err error)
   106  }
   107  
   108  func createSignedP2Payload(payload MarshalSSZ, signer Signer, l2ChainID *big.Int) ([]byte, error) {
   109  	var buf bytes.Buffer
   110  	buf.Write(make([]byte, 65))
   111  	if _, err := payload.MarshalSSZ(&buf); err != nil {
   112  		return nil, fmt.Errorf("failed to encoded execution payload to publish: %w", err)
   113  	}
   114  	data := buf.Bytes()
   115  	payloadData := data[65:]
   116  	sig, err := signer.Sign(context.TODO(), SigningDomainBlocksV1, l2ChainID, payloadData)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("failed to sign execution payload with signer: %w", err)
   119  	}
   120  	copy(data[:65], sig[:])
   121  
   122  	// compress the full message
   123  	// This also copies the data, freeing up the original buffer to go back into the pool
   124  	return snappy.Encode(nil, data), nil
   125  }
   126  
   127  func createExecutionPayload(w types.Withdrawals, excessGas, gasUsed *uint64) *eth.ExecutionPayload {
   128  	return &eth.ExecutionPayload{
   129  		Timestamp:     hexutil.Uint64(time.Now().Unix()),
   130  		Withdrawals:   &w,
   131  		ExcessBlobGas: (*eth.Uint64Quantity)(excessGas),
   132  		BlobGasUsed:   (*eth.Uint64Quantity)(gasUsed),
   133  	}
   134  }
   135  
   136  func createEnvelope(h *common.Hash, w types.Withdrawals, excessGas, gasUsed *uint64) *eth.ExecutionPayloadEnvelope {
   137  	return &eth.ExecutionPayloadEnvelope{
   138  		ExecutionPayload:      createExecutionPayload(w, excessGas, gasUsed),
   139  		ParentBeaconBlockRoot: h,
   140  	}
   141  }
   142  
   143  // TestBlockValidator does some very basic tests of the p2p block validation logic
   144  func TestBlockValidator(t *testing.T) {
   145  	// Params Set 1: Create the validation function
   146  	cfg := &rollup.Config{
   147  		L2ChainID: big.NewInt(100),
   148  	}
   149  	secrets, err := e2eutils.DefaultMnemonicConfig.Secrets()
   150  	require.NoError(t, err)
   151  	runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)}
   152  	signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)}
   153  	// Params Set 2: Call the validation function
   154  	peerID := peer.ID("foo")
   155  
   156  	v2Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), cfg, runCfg, eth.BlockV2)
   157  	v3Validator := BuildBlocksValidator(testlog.Logger(t, log.LevelCrit), cfg, runCfg, eth.BlockV3)
   158  
   159  	zero, one := uint64(0), uint64(1)
   160  	beaconHash := common.HexToHash("0x1234")
   161  
   162  	payloadTests := []struct {
   163  		name      string
   164  		validator pubsub.ValidatorEx
   165  		result    pubsub.ValidationResult
   166  		payload   *eth.ExecutionPayload
   167  	}{
   168  		{"V2Valid", v2Validator, pubsub.ValidationAccept, createExecutionPayload(types.Withdrawals{}, nil, nil)},
   169  		{"V2NonZeroWithdrawals", v2Validator, pubsub.ValidationReject, createExecutionPayload(types.Withdrawals{&types.Withdrawal{Index: 1, Validator: 1}}, nil, nil)},
   170  		{"V2NonZeroBlobProperties", v2Validator, pubsub.ValidationReject, createExecutionPayload(types.Withdrawals{}, &zero, &zero)},
   171  		{"V3RejectExecutionPayload", v3Validator, pubsub.ValidationReject, createExecutionPayload(types.Withdrawals{}, &zero, &zero)},
   172  	}
   173  
   174  	for _, tt := range payloadTests {
   175  		test := tt
   176  		t.Run(fmt.Sprintf("ExecutionPayload_%s", test.name), func(t *testing.T) {
   177  			e := &eth.ExecutionPayloadEnvelope{ExecutionPayload: test.payload}
   178  			test.payload.BlockHash, _ = e.CheckBlockHash() // hack to generate the block hash easily.
   179  			data, err := createSignedP2Payload(test.payload, signer, cfg.L2ChainID)
   180  			require.NoError(t, err)
   181  			message := &pubsub.Message{Message: &pubsub_pb.Message{Data: data}}
   182  			res := test.validator(context.TODO(), peerID, message)
   183  			require.Equal(t, res, test.result)
   184  		})
   185  	}
   186  
   187  	envelopeTests := []struct {
   188  		name      string
   189  		validator pubsub.ValidatorEx
   190  		result    pubsub.ValidationResult
   191  		payload   *eth.ExecutionPayloadEnvelope
   192  	}{
   193  		{"V3RejectNonZeroExcessGas", v3Validator, pubsub.ValidationReject, createEnvelope(&beaconHash, types.Withdrawals{}, &one, &zero)},
   194  		{"V3RejectNonZeroBlobGasUsed", v3Validator, pubsub.ValidationReject, createEnvelope(&beaconHash, types.Withdrawals{}, &zero, &one)},
   195  		{"V3RejectNonZeroBlobGasUsed", v3Validator, pubsub.ValidationReject, createEnvelope(&beaconHash, types.Withdrawals{}, &zero, &one)},
   196  		{"V3Valid", v3Validator, pubsub.ValidationAccept, createEnvelope(&beaconHash, types.Withdrawals{}, &zero, &zero)},
   197  	}
   198  
   199  	for _, tt := range envelopeTests {
   200  		test := tt
   201  		t.Run(fmt.Sprintf("ExecutionPayloadEnvelope_%s", test.name), func(t *testing.T) {
   202  			test.payload.ExecutionPayload.BlockHash, _ = test.payload.CheckBlockHash() // hack to generate the block hash easily.
   203  			data, err := createSignedP2Payload(test.payload, signer, cfg.L2ChainID)
   204  			require.NoError(t, err)
   205  			message := &pubsub.Message{Message: &pubsub_pb.Message{Data: data}}
   206  			res := test.validator(context.TODO(), peerID, message)
   207  			require.Equal(t, res, test.result)
   208  		})
   209  	}
   210  }