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  }