github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/committee_chain_test.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package light 18 19 import ( 20 "crypto/rand" 21 "testing" 22 "time" 23 24 "github.com/ethereum/go-ethereum/beacon/params" 25 "github.com/ethereum/go-ethereum/beacon/types" 26 "github.com/ethereum/go-ethereum/common/mclock" 27 "github.com/ethereum/go-ethereum/ethdb/memorydb" 28 ) 29 30 var ( 31 testGenesis = newTestGenesis() 32 testGenesis2 = newTestGenesis() 33 34 tfBase = newTestForks(testGenesis, types.Forks{ 35 &types.Fork{Epoch: 0, Version: []byte{0}}, 36 }) 37 tfAlternative = newTestForks(testGenesis, types.Forks{ 38 &types.Fork{Epoch: 0, Version: []byte{0}}, 39 &types.Fork{Epoch: 0x700, Version: []byte{1}}, 40 }) 41 tfAnotherGenesis = newTestForks(testGenesis2, types.Forks{ 42 &types.Fork{Epoch: 0, Version: []byte{0}}, 43 }) 44 45 tcBase = newTestCommitteeChain(nil, tfBase, true, 0, 10, 400, false) 46 tcBaseWithInvalidUpdates = newTestCommitteeChain(tcBase, tfBase, false, 5, 10, 200, false) // signer count too low 47 tcBaseWithBetterUpdates = newTestCommitteeChain(tcBase, tfBase, false, 5, 10, 440, false) 48 tcReorgWithWorseUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 400, false) 49 tcReorgWithWorseUpdates2 = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 380, false) 50 tcReorgWithBetterUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 420, false) 51 tcReorgWithFinalizedUpdates = newTestCommitteeChain(tcBase, tfBase, true, 5, 10, 400, true) 52 tcFork = newTestCommitteeChain(tcBase, tfAlternative, true, 7, 10, 400, false) 53 tcAnotherGenesis = newTestCommitteeChain(nil, tfAnotherGenesis, true, 0, 10, 400, false) 54 ) 55 56 func TestCommitteeChainFixedCommitteeRoots(t *testing.T) { 57 for _, reload := range []bool{false, true} { 58 c := newCommitteeChainTest(t, tfBase, 300, true) 59 c.setClockPeriod(7) 60 c.addFixedCommitteeRoot(tcBase, 4, nil) 61 c.addFixedCommitteeRoot(tcBase, 5, nil) 62 c.addFixedCommitteeRoot(tcBase, 6, nil) 63 c.addFixedCommitteeRoot(tcBase, 8, ErrInvalidPeriod) // range has to be continuous 64 c.addFixedCommitteeRoot(tcBase, 3, nil) 65 c.addFixedCommitteeRoot(tcBase, 2, nil) 66 if reload { 67 c.reloadChain() 68 } 69 c.addCommittee(tcBase, 4, nil) 70 c.addCommittee(tcBase, 6, ErrInvalidPeriod) // range has to be continuous 71 c.addCommittee(tcBase, 5, nil) 72 c.addCommittee(tcBase, 6, nil) 73 c.addCommittee(tcAnotherGenesis, 3, ErrWrongCommitteeRoot) 74 c.addCommittee(tcBase, 3, nil) 75 if reload { 76 c.reloadChain() 77 } 78 c.verifyRange(tcBase, 3, 6) 79 } 80 } 81 82 func TestCommitteeChainCheckpointSync(t *testing.T) { 83 for _, enforceTime := range []bool{false, true} { 84 for _, reload := range []bool{false, true} { 85 c := newCommitteeChainTest(t, tfBase, 300, enforceTime) 86 if enforceTime { 87 c.setClockPeriod(6) 88 } 89 c.insertUpdate(tcBase, 3, true, ErrInvalidPeriod) 90 c.addFixedCommitteeRoot(tcBase, 3, nil) 91 c.addFixedCommitteeRoot(tcBase, 4, nil) 92 c.insertUpdate(tcBase, 4, true, ErrInvalidPeriod) // still no committee 93 c.addCommittee(tcBase, 3, nil) 94 c.addCommittee(tcBase, 4, nil) 95 if reload { 96 c.reloadChain() 97 } 98 c.verifyRange(tcBase, 3, 4) 99 c.insertUpdate(tcBase, 3, false, nil) // update can be added without committee here 100 c.insertUpdate(tcBase, 4, false, ErrNeedCommittee) // but not here as committee 5 is not there yet 101 c.insertUpdate(tcBase, 4, true, nil) 102 c.verifyRange(tcBase, 3, 5) 103 c.insertUpdate(tcBaseWithInvalidUpdates, 5, true, ErrInvalidUpdate) // signer count too low 104 c.insertUpdate(tcBase, 5, true, nil) 105 if reload { 106 c.reloadChain() 107 } 108 if enforceTime { 109 c.insertUpdate(tcBase, 6, true, ErrInvalidUpdate) // future update rejected 110 c.setClockPeriod(7) 111 } 112 c.insertUpdate(tcBase, 6, true, nil) // when the time comes it's accepted 113 if reload { 114 c.reloadChain() 115 } 116 if enforceTime { 117 c.verifyRange(tcBase, 3, 6) // committee 7 is there but still in the future 118 c.setClockPeriod(8) 119 } 120 c.verifyRange(tcBase, 3, 7) // now period 7 can also be verified 121 // try reverse syncing an update 122 c.insertUpdate(tcBase, 2, false, ErrInvalidPeriod) // fixed committee is needed first 123 c.addFixedCommitteeRoot(tcBase, 2, nil) 124 c.addCommittee(tcBase, 2, nil) 125 c.insertUpdate(tcBase, 2, false, nil) 126 c.verifyRange(tcBase, 2, 7) 127 } 128 } 129 } 130 131 func TestCommitteeChainReorg(t *testing.T) { 132 for _, reload := range []bool{false, true} { 133 for _, addBetterUpdates := range []bool{false, true} { 134 c := newCommitteeChainTest(t, tfBase, 300, true) 135 c.setClockPeriod(11) 136 c.addFixedCommitteeRoot(tcBase, 3, nil) 137 c.addFixedCommitteeRoot(tcBase, 4, nil) 138 c.addCommittee(tcBase, 3, nil) 139 for period := uint64(3); period < 10; period++ { 140 c.insertUpdate(tcBase, period, true, nil) 141 } 142 if reload { 143 c.reloadChain() 144 } 145 c.verifyRange(tcBase, 3, 10) 146 c.insertUpdate(tcReorgWithWorseUpdates, 5, true, ErrCannotReorg) 147 c.insertUpdate(tcReorgWithWorseUpdates2, 5, true, ErrCannotReorg) 148 if addBetterUpdates { 149 // add better updates for the base chain and expect first reorg to fail 150 // (only add updates as committees should be the same) 151 for period := uint64(5); period < 10; period++ { 152 c.insertUpdate(tcBaseWithBetterUpdates, period, false, nil) 153 } 154 if reload { 155 c.reloadChain() 156 } 157 c.verifyRange(tcBase, 3, 10) // still on the same chain 158 c.insertUpdate(tcReorgWithBetterUpdates, 5, true, ErrCannotReorg) 159 } else { 160 // reorg with better updates 161 c.insertUpdate(tcReorgWithBetterUpdates, 5, false, ErrNeedCommittee) 162 c.verifyRange(tcBase, 3, 10) // no success yet, still on the base chain 163 c.verifyRange(tcReorgWithBetterUpdates, 3, 5) 164 c.insertUpdate(tcReorgWithBetterUpdates, 5, true, nil) 165 // successful reorg, base chain should only match before the reorg period 166 if reload { 167 c.reloadChain() 168 } 169 c.verifyRange(tcBase, 3, 5) 170 c.verifyRange(tcReorgWithBetterUpdates, 3, 6) 171 for period := uint64(6); period < 10; period++ { 172 c.insertUpdate(tcReorgWithBetterUpdates, period, true, nil) 173 } 174 c.verifyRange(tcReorgWithBetterUpdates, 3, 10) 175 } 176 // reorg with finalized updates; should succeed even if base chain updates 177 // have been improved because a finalized update beats everything else 178 c.insertUpdate(tcReorgWithFinalizedUpdates, 5, false, ErrNeedCommittee) 179 c.insertUpdate(tcReorgWithFinalizedUpdates, 5, true, nil) 180 if reload { 181 c.reloadChain() 182 } 183 c.verifyRange(tcReorgWithFinalizedUpdates, 3, 6) 184 for period := uint64(6); period < 10; period++ { 185 c.insertUpdate(tcReorgWithFinalizedUpdates, period, true, nil) 186 } 187 c.verifyRange(tcReorgWithFinalizedUpdates, 3, 10) 188 } 189 } 190 } 191 192 func TestCommitteeChainFork(t *testing.T) { 193 c := newCommitteeChainTest(t, tfAlternative, 300, true) 194 c.setClockPeriod(11) 195 // trying to sync a chain on an alternative fork with the base chain data 196 c.addFixedCommitteeRoot(tcBase, 0, nil) 197 c.addFixedCommitteeRoot(tcBase, 1, nil) 198 c.addCommittee(tcBase, 0, nil) 199 // shared section should sync without errors 200 for period := uint64(0); period < 7; period++ { 201 c.insertUpdate(tcBase, period, true, nil) 202 } 203 c.insertUpdate(tcBase, 7, true, ErrInvalidUpdate) // wrong fork 204 // committee root #7 is still the same but signatures are already signed with 205 // a different fork id so period 7 should only verify on the alternative fork 206 c.verifyRange(tcBase, 0, 6) 207 c.verifyRange(tcFork, 0, 7) 208 for period := uint64(7); period < 10; period++ { 209 c.insertUpdate(tcFork, period, true, nil) 210 } 211 c.verifyRange(tcFork, 0, 10) 212 // reload the chain while switching to the base fork 213 c.config = tfBase 214 c.reloadChain() 215 // updates 7..9 should be rolled back now 216 c.verifyRange(tcFork, 0, 6) // again, period 7 only verifies on the right fork 217 c.verifyRange(tcBase, 0, 7) 218 c.insertUpdate(tcFork, 7, true, ErrInvalidUpdate) // wrong fork 219 for period := uint64(7); period < 10; period++ { 220 c.insertUpdate(tcBase, period, true, nil) 221 } 222 c.verifyRange(tcBase, 0, 10) 223 } 224 225 type committeeChainTest struct { 226 t *testing.T 227 db *memorydb.Database 228 clock *mclock.Simulated 229 config types.ChainConfig 230 signerThreshold int 231 enforceTime bool 232 chain *CommitteeChain 233 } 234 235 func newCommitteeChainTest(t *testing.T, config types.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest { 236 c := &committeeChainTest{ 237 t: t, 238 db: memorydb.New(), 239 clock: &mclock.Simulated{}, 240 config: config, 241 signerThreshold: signerThreshold, 242 enforceTime: enforceTime, 243 } 244 c.chain = NewTestCommitteeChain(c.db, &config, signerThreshold, enforceTime, c.clock) 245 return c 246 } 247 248 func (c *committeeChainTest) reloadChain() { 249 c.chain = NewTestCommitteeChain(c.db, &c.config, c.signerThreshold, c.enforceTime, c.clock) 250 } 251 252 func (c *committeeChainTest) setClockPeriod(period float64) { 253 target := mclock.AbsTime(period * float64(time.Second*12*params.SyncPeriodLength)) 254 wait := time.Duration(target - c.clock.Now()) 255 if wait < 0 { 256 c.t.Fatalf("Invalid setClockPeriod") 257 } 258 c.clock.Run(wait) 259 } 260 261 func (c *committeeChainTest) addFixedCommitteeRoot(tc *testCommitteeChain, period uint64, expErr error) { 262 if err := c.chain.addFixedCommitteeRoot(period, tc.periods[period].committee.Root()); err != expErr { 263 c.t.Errorf("Incorrect error output from addFixedCommitteeRoot at period %d (expected %v, got %v)", period, expErr, err) 264 } 265 } 266 267 func (c *committeeChainTest) addCommittee(tc *testCommitteeChain, period uint64, expErr error) { 268 if err := c.chain.addCommittee(period, tc.periods[period].committee); err != expErr { 269 c.t.Errorf("Incorrect error output from addCommittee at period %d (expected %v, got %v)", period, expErr, err) 270 } 271 } 272 273 func (c *committeeChainTest) insertUpdate(tc *testCommitteeChain, period uint64, addCommittee bool, expErr error) { 274 var committee *types.SerializedSyncCommittee 275 if addCommittee { 276 committee = tc.periods[period+1].committee 277 } 278 if err := c.chain.InsertUpdate(tc.periods[period].update, committee); err != expErr { 279 c.t.Errorf("Incorrect error output from InsertUpdate at period %d (expected %v, got %v)", period, expErr, err) 280 } 281 } 282 283 func (c *committeeChainTest) verifySignedHeader(tc *testCommitteeChain, period float64, expOk bool) { 284 slot := uint64(period * float64(params.SyncPeriodLength)) 285 signedHead := GenerateTestSignedHeader(types.Header{Slot: slot}, &tc.config, tc.periods[types.SyncPeriod(slot)].committee, slot+1, 400) 286 if ok, _, _ := c.chain.VerifySignedHeader(signedHead); ok != expOk { 287 c.t.Errorf("Incorrect output from VerifySignedHeader at period %f (expected %v, got %v)", period, expOk, ok) 288 } 289 } 290 291 func (c *committeeChainTest) verifyRange(tc *testCommitteeChain, begin, end uint64) { 292 if begin > 0 { 293 c.verifySignedHeader(tc, float64(begin)-0.5, false) 294 } 295 for period := begin; period <= end; period++ { 296 c.verifySignedHeader(tc, float64(period)+0.5, true) 297 } 298 c.verifySignedHeader(tc, float64(end)+1.5, false) 299 } 300 301 func newTestGenesis() types.ChainConfig { 302 var config types.ChainConfig 303 rand.Read(config.GenesisValidatorsRoot[:]) 304 return config 305 } 306 307 func newTestForks(config types.ChainConfig, forks types.Forks) types.ChainConfig { 308 for _, fork := range forks { 309 config.AddFork(fork.Name, fork.Epoch, fork.Version) 310 } 311 return config 312 } 313 314 func newTestCommitteeChain(parent *testCommitteeChain, config types.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain { 315 tc := &testCommitteeChain{ 316 config: config, 317 } 318 if parent != nil { 319 tc.periods = make([]testPeriod, len(parent.periods)) 320 copy(tc.periods, parent.periods) 321 } 322 if newCommittees { 323 if begin == 0 { 324 tc.fillCommittees(begin, end+1) 325 } else { 326 tc.fillCommittees(begin+1, end+1) 327 } 328 } 329 tc.fillUpdates(begin, end, signerCount, finalizedHeader) 330 return tc 331 } 332 333 type testPeriod struct { 334 committee *types.SerializedSyncCommittee 335 update *types.LightClientUpdate 336 } 337 338 type testCommitteeChain struct { 339 periods []testPeriod 340 config types.ChainConfig 341 } 342 343 func (tc *testCommitteeChain) fillCommittees(begin, end int) { 344 if len(tc.periods) <= end { 345 tc.periods = append(tc.periods, make([]testPeriod, end+1-len(tc.periods))...) 346 } 347 for i := begin; i <= end; i++ { 348 tc.periods[i].committee = GenerateTestCommittee() 349 } 350 } 351 352 func (tc *testCommitteeChain) fillUpdates(begin, end int, signerCount int, finalizedHeader bool) { 353 for i := begin; i <= end; i++ { 354 tc.periods[i].update = GenerateTestUpdate(&tc.config, uint64(i), tc.periods[i].committee, tc.periods[i+1].committee, signerCount, finalizedHeader) 355 } 356 }