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 ð.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 ð.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 := ð.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 }