github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/driver/origin_selector_test.go (about) 1 package driver 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/ethereum-optimism/optimism/op-node/rollup" 8 "github.com/ethereum-optimism/optimism/op-service/eth" 9 "github.com/ethereum-optimism/optimism/op-service/testlog" 10 "github.com/ethereum-optimism/optimism/op-service/testutils" 11 "github.com/ethereum/go-ethereum/common" 12 "github.com/ethereum/go-ethereum/log" 13 "github.com/stretchr/testify/require" 14 ) 15 16 // TestOriginSelectorAdvances ensures that the origin selector 17 // advances the origin 18 // 19 // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 24. 20 // The next L2 time is 26 which is after the next L1 block time. There 21 // is no conf depth to stop the origin selection so block `b` should 22 // be the next L1 origin 23 func TestOriginSelectorAdvances(t *testing.T) { 24 log := testlog.Logger(t, log.LevelCrit) 25 cfg := &rollup.Config{ 26 MaxSequencerDrift: 500, 27 BlockTime: 2, 28 } 29 l1 := &testutils.MockL1Source{} 30 defer l1.AssertExpectations(t) 31 a := eth.L1BlockRef{ 32 Hash: common.Hash{'a'}, 33 Number: 10, 34 Time: 20, 35 } 36 b := eth.L1BlockRef{ 37 Hash: common.Hash{'b'}, 38 Number: 11, 39 Time: 25, 40 ParentHash: a.Hash, 41 } 42 l2Head := eth.L2BlockRef{ 43 L1Origin: a.ID(), 44 Time: 24, 45 } 46 47 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 48 l1.ExpectL1BlockRefByNumber(b.Number, b, nil) 49 50 s := NewL1OriginSelector(log, cfg, l1) 51 next, err := s.FindL1Origin(context.Background(), l2Head) 52 require.Nil(t, err) 53 require.Equal(t, b, next) 54 } 55 56 // TestOriginSelectorRespectsOriginTiming ensures that the origin selector 57 // does not pick an origin that is ahead of the next L2 block time 58 // 59 // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 22. 60 // The next L2 time is 24 which is before the next L1 block time. There 61 // is no conf depth to stop the LOS from potentially selecting block `b` 62 // but it should select block `a` because the L2 block time must be ahead 63 // of the the timestamp of it's L1 origin. 64 func TestOriginSelectorRespectsOriginTiming(t *testing.T) { 65 log := testlog.Logger(t, log.LevelCrit) 66 cfg := &rollup.Config{ 67 MaxSequencerDrift: 500, 68 BlockTime: 2, 69 } 70 l1 := &testutils.MockL1Source{} 71 defer l1.AssertExpectations(t) 72 a := eth.L1BlockRef{ 73 Hash: common.Hash{'a'}, 74 Number: 10, 75 Time: 20, 76 } 77 b := eth.L1BlockRef{ 78 Hash: common.Hash{'b'}, 79 Number: 11, 80 Time: 25, 81 ParentHash: a.Hash, 82 } 83 l2Head := eth.L2BlockRef{ 84 L1Origin: a.ID(), 85 Time: 22, 86 } 87 88 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 89 l1.ExpectL1BlockRefByNumber(b.Number, b, nil) 90 91 s := NewL1OriginSelector(log, cfg, l1) 92 next, err := s.FindL1Origin(context.Background(), l2Head) 93 require.Nil(t, err) 94 require.Equal(t, a, next) 95 } 96 97 // TestOriginSelectorRespectsConfDepth ensures that the origin selector 98 // will respect the confirmation depth requirement 99 // 100 // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 27. 101 // The next L2 time is 29 which enough to normally select block `b` 102 // as the origin, however block `b` is the L1 Head & the sequencer 103 // needs to wait until that block is confirmed enough before advancing. 104 func TestOriginSelectorRespectsConfDepth(t *testing.T) { 105 log := testlog.Logger(t, log.LevelCrit) 106 cfg := &rollup.Config{ 107 MaxSequencerDrift: 500, 108 BlockTime: 2, 109 } 110 l1 := &testutils.MockL1Source{} 111 defer l1.AssertExpectations(t) 112 a := eth.L1BlockRef{ 113 Hash: common.Hash{'a'}, 114 Number: 10, 115 Time: 20, 116 } 117 b := eth.L1BlockRef{ 118 Hash: common.Hash{'b'}, 119 Number: 11, 120 Time: 25, 121 ParentHash: a.Hash, 122 } 123 l2Head := eth.L2BlockRef{ 124 L1Origin: a.ID(), 125 Time: 27, 126 } 127 128 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 129 confDepthL1 := NewConfDepth(10, func() eth.L1BlockRef { return b }, l1) 130 s := NewL1OriginSelector(log, cfg, confDepthL1) 131 132 next, err := s.FindL1Origin(context.Background(), l2Head) 133 require.Nil(t, err) 134 require.Equal(t, a, next) 135 } 136 137 // TestOriginSelectorStrictConfDepth ensures that the origin selector will maintain the sequencer conf depth, 138 // even while the time delta between the current L1 origin and the next 139 // L2 block is greater than the sequencer drift. 140 // It's more important to maintain safety with an empty block than to maintain liveness with poor conf depth. 141 // 142 // There are 2 L1 blocks at time 20 & 25. The L2 Head is at time 27. 143 // The next L2 time is 29. The sequencer drift is 8 so the L2 head is 144 // valid with origin `a`, but the next L2 block is not valid with origin `b.` 145 // This is because 29 (next L2 time) > 20 (origin) + 8 (seq drift) => invalid block. 146 // We maintain confirmation distance, even though we would shift to the next origin if we could. 147 func TestOriginSelectorStrictConfDepth(t *testing.T) { 148 log := testlog.Logger(t, log.LevelCrit) 149 cfg := &rollup.Config{ 150 MaxSequencerDrift: 8, 151 BlockTime: 2, 152 } 153 l1 := &testutils.MockL1Source{} 154 defer l1.AssertExpectations(t) 155 a := eth.L1BlockRef{ 156 Hash: common.Hash{'a'}, 157 Number: 10, 158 Time: 20, 159 } 160 b := eth.L1BlockRef{ 161 Hash: common.Hash{'b'}, 162 Number: 11, 163 Time: 25, 164 ParentHash: a.Hash, 165 } 166 l2Head := eth.L2BlockRef{ 167 L1Origin: a.ID(), 168 Time: 27, 169 } 170 171 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 172 confDepthL1 := NewConfDepth(10, func() eth.L1BlockRef { return b }, l1) 173 s := NewL1OriginSelector(log, cfg, confDepthL1) 174 175 _, err := s.FindL1Origin(context.Background(), l2Head) 176 require.ErrorContains(t, err, "sequencer time drift") 177 } 178 179 // TestOriginSelectorSeqDriftRespectsNextOriginTime 180 // 181 // There are 2 L1 blocks at time 20 & 100. The L2 Head is at time 27. 182 // The next L2 time is 29. Even though the next L2 time is past the seq 183 // drift, the origin should remain on block `a` because the next origin's 184 // time is greater than the next L2 time. 185 func TestOriginSelectorSeqDriftRespectsNextOriginTime(t *testing.T) { 186 log := testlog.Logger(t, log.LevelCrit) 187 cfg := &rollup.Config{ 188 MaxSequencerDrift: 8, 189 BlockTime: 2, 190 } 191 l1 := &testutils.MockL1Source{} 192 defer l1.AssertExpectations(t) 193 a := eth.L1BlockRef{ 194 Hash: common.Hash{'a'}, 195 Number: 10, 196 Time: 20, 197 } 198 b := eth.L1BlockRef{ 199 Hash: common.Hash{'b'}, 200 Number: 11, 201 Time: 100, 202 ParentHash: a.Hash, 203 } 204 l2Head := eth.L2BlockRef{ 205 L1Origin: a.ID(), 206 Time: 27, 207 } 208 209 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 210 l1.ExpectL1BlockRefByNumber(b.Number, b, nil) 211 212 s := NewL1OriginSelector(log, cfg, l1) 213 next, err := s.FindL1Origin(context.Background(), l2Head) 214 require.Nil(t, err) 215 require.Equal(t, a, next) 216 } 217 218 // TestOriginSelectorHandlesLateL1Blocks tests the forced repeat of the previous origin, 219 // but with a conf depth that first prevents it from learning about the need to repeat. 220 // 221 // There are 2 L1 blocks at time 20 & 100. The L2 Head is at time 27. 222 // The next L2 time is 29. Even though the next L2 time is past the seq 223 // drift, the origin should remain on block `a` because the next origin's 224 // time is greater than the next L2 time. 225 // Due to a conf depth of 2, block `b` is not immediately visible, 226 // and the origin selection should fail until it is visible, by waiting for block `c`. 227 func TestOriginSelectorHandlesLateL1Blocks(t *testing.T) { 228 log := testlog.Logger(t, log.LevelCrit) 229 cfg := &rollup.Config{ 230 MaxSequencerDrift: 8, 231 BlockTime: 2, 232 } 233 l1 := &testutils.MockL1Source{} 234 defer l1.AssertExpectations(t) 235 a := eth.L1BlockRef{ 236 Hash: common.Hash{'a'}, 237 Number: 10, 238 Time: 20, 239 } 240 b := eth.L1BlockRef{ 241 Hash: common.Hash{'b'}, 242 Number: 11, 243 Time: 100, 244 ParentHash: a.Hash, 245 } 246 c := eth.L1BlockRef{ 247 Hash: common.Hash{'c'}, 248 Number: 12, 249 Time: 150, 250 ParentHash: b.Hash, 251 } 252 d := eth.L1BlockRef{ 253 Hash: common.Hash{'d'}, 254 Number: 13, 255 Time: 200, 256 ParentHash: c.Hash, 257 } 258 l2Head := eth.L2BlockRef{ 259 L1Origin: a.ID(), 260 Time: 27, 261 } 262 263 // l2 head does not change, so we start at the same origin again and again until we meet the conf depth 264 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 265 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 266 l1.ExpectL1BlockRefByHash(a.Hash, a, nil) 267 l1.ExpectL1BlockRefByNumber(b.Number, b, nil) 268 269 l1Head := b 270 confDepthL1 := NewConfDepth(2, func() eth.L1BlockRef { return l1Head }, l1) 271 s := NewL1OriginSelector(log, cfg, confDepthL1) 272 273 _, err := s.FindL1Origin(context.Background(), l2Head) 274 require.ErrorContains(t, err, "sequencer time drift") 275 276 l1Head = c 277 _, err = s.FindL1Origin(context.Background(), l2Head) 278 require.ErrorContains(t, err, "sequencer time drift") 279 280 l1Head = d 281 next, err := s.FindL1Origin(context.Background(), l2Head) 282 require.Nil(t, err) 283 require.Equal(t, a, next, "must stay on a because the L1 time may not be higher than the L2 time") 284 }