github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/async/asyncgossiper_test.go (about) 1 package async 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/ethereum-optimism/optimism/op-service/eth" 10 "github.com/ethereum/go-ethereum/common/hexutil" 11 "github.com/ethereum/go-ethereum/log" 12 "github.com/stretchr/testify/require" 13 ) 14 15 type mockNetwork struct { 16 reqs []*eth.ExecutionPayloadEnvelope 17 } 18 19 func (m *mockNetwork) PublishL2Payload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error { 20 m.reqs = append(m.reqs, payload) 21 return nil 22 } 23 24 type mockMetrics struct{} 25 26 func (m *mockMetrics) RecordPublishingError() {} 27 28 // TestAsyncGossiper tests the AsyncGossiper component 29 // because the component is small and simple, it is tested as a whole 30 // this test starts, runs, clears and stops the AsyncGossiper 31 // because the AsyncGossiper is run in an async component, it is tested with eventually 32 func TestAsyncGossiper(t *testing.T) { 33 m := &mockNetwork{} 34 // Create a new instance of AsyncGossiper 35 p := NewAsyncGossiper(context.Background(), m, log.New(), &mockMetrics{}) 36 37 // Start the AsyncGossiper 38 p.Start() 39 40 // Test that the AsyncGossiper is running within a short duration 41 require.Eventually(t, func() bool { 42 return p.running.Load() 43 }, 10*time.Second, 10*time.Millisecond) 44 45 // send a payload 46 payload := ð.ExecutionPayload{ 47 BlockNumber: hexutil.Uint64(1), 48 } 49 envelope := ð.ExecutionPayloadEnvelope{ 50 ExecutionPayload: payload, 51 } 52 p.Gossip(envelope) 53 require.Eventually(t, func() bool { 54 // Test that the gossiper has content at all 55 return p.Get() == envelope && 56 // Test that the payload has been sent to the (mock) network 57 m.reqs[0] == envelope 58 }, 10*time.Second, 10*time.Millisecond) 59 60 p.Clear() 61 require.Eventually(t, func() bool { 62 // Test that the gossiper has no payload 63 return p.Get() == nil 64 }, 10*time.Second, 10*time.Millisecond) 65 66 // Stop the AsyncGossiper 67 p.Stop() 68 69 // Test that the AsyncGossiper stops within a short duration 70 require.Eventually(t, func() bool { 71 return !p.running.Load() 72 }, 10*time.Second, 10*time.Millisecond) 73 } 74 75 // TestAsyncGossiperLoop confirms that when called repeatedly, the AsyncGossiper holds the latest payload 76 // and sends all payloads to the network 77 func TestAsyncGossiperLoop(t *testing.T) { 78 m := &mockNetwork{} 79 // Create a new instance of AsyncGossiper 80 p := NewAsyncGossiper(context.Background(), m, log.New(), &mockMetrics{}) 81 82 // Start the AsyncGossiper 83 p.Start() 84 85 // Test that the AsyncGossiper is running within a short duration 86 require.Eventually(t, func() bool { 87 return p.running.Load() 88 }, 10*time.Second, 10*time.Millisecond) 89 90 // send multiple payloads 91 for i := 0; i < 10; i++ { 92 payload := ð.ExecutionPayload{ 93 BlockNumber: hexutil.Uint64(i), 94 } 95 envelope := ð.ExecutionPayloadEnvelope{ 96 ExecutionPayload: payload, 97 } 98 p.Gossip(envelope) 99 require.Eventually(t, func() bool { 100 // Test that the gossiper has content at all 101 return p.Get() == envelope && 102 // Test that the payload has been sent to the (mock) network 103 m.reqs[len(m.reqs)-1] == envelope 104 }, 10*time.Second, 10*time.Millisecond) 105 } 106 require.Equal(t, 10, len(m.reqs)) 107 // Stop the AsyncGossiper 108 p.Stop() 109 // Test that the AsyncGossiper stops within a short duration 110 require.Eventually(t, func() bool { 111 return !p.running.Load() 112 }, 10*time.Second, 10*time.Millisecond) 113 } 114 115 // failingNetwork is a mock network that always fails to publish 116 type failingNetwork struct{} 117 118 func (f *failingNetwork) PublishL2Payload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error { 119 return errors.New("failed to publish") 120 } 121 122 // TestAsyncGossiperFailToPublish tests that the AsyncGossiper clears the stored payload if the network fails 123 func TestAsyncGossiperFailToPublish(t *testing.T) { 124 m := &failingNetwork{} 125 // Create a new instance of AsyncGossiper 126 p := NewAsyncGossiper(context.Background(), m, log.New(), &mockMetrics{}) 127 128 // Start the AsyncGossiper 129 p.Start() 130 131 // send a payload 132 payload := ð.ExecutionPayload{ 133 BlockNumber: hexutil.Uint64(1), 134 } 135 envelope := ð.ExecutionPayloadEnvelope{ 136 ExecutionPayload: payload, 137 } 138 p.Gossip(envelope) 139 // Rather than expect the payload to become available, we should never see it, due to the publish failure 140 require.Never(t, func() bool { 141 return p.Get() == envelope 142 }, 10*time.Second, 10*time.Millisecond) 143 // Stop the AsyncGossiper 144 p.Stop() 145 // Test that the AsyncGossiper stops within a short duration 146 require.Eventually(t, func() bool { 147 return !p.running.Load() 148 }, 10*time.Second, 10*time.Millisecond) 149 }