mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
Save LC Finality and Optimistic Updates in LC Store When Processing Block (#15124)
* add lcStore to Node * changelog entry * add atomic getters and setters for the store * add lcstore to the blockchain package * save lc finality update to store * save lc optimistic update to store * changelog entry * change store fields visibility to private * refactor method names and add tests * add lcstore to the blockchain package * save lc finality update to store * save lc optimistic update to store * changelog entry * refactor method names * setup tests * remove get from getters * add lcstore to the blockchain package * save lc finality update to store * save lc optimistic update to store * changelog entry * refactor method names * setup tests * rename methods * temp * temp * add tests * fixing tests * stash * refactor setUpAltair * remove debug code * refactor bellatrix setup * refactor capella setup * refactor rest - core tests remain * refactor tests to use new functional options utils * use the options * add noFinalizedCheckpoint and finalizedCheckpointInPrevFork options * add tests * changelog entry * refactor tests * deps * fix tests * Update testing/util/lightclient.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update testing/util/lightclient.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update testing/util/lightclient.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * address comments * address comments * address comments * go mod tidy * fix annoying conflicts * go mod tidy * fix conflicts * cleanup tests * add SetupTestConfigCleanup * commit to restart CI checks * address comments * address comments * address comments --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -3244,3 +3244,338 @@ func TestSaveLightClientBootstrap(t *testing.T) {
|
||||
|
||||
reset()
|
||||
}
|
||||
|
||||
func setupLightClientTestRequirements(ctx context.Context, t *testing.T, s *Service, v int, options ...util.LightClientOption) (*util.TestLightClient, *postBlockProcessConfig) {
|
||||
var l *util.TestLightClient
|
||||
switch v {
|
||||
case version.Altair:
|
||||
l = util.NewTestLightClient(t, version.Altair, options...)
|
||||
case version.Bellatrix:
|
||||
l = util.NewTestLightClient(t, version.Bellatrix, options...)
|
||||
case version.Capella:
|
||||
l = util.NewTestLightClient(t, version.Capella, options...)
|
||||
case version.Deneb:
|
||||
l = util.NewTestLightClient(t, version.Deneb, options...)
|
||||
case version.Electra:
|
||||
l = util.NewTestLightClient(t, version.Electra, options...)
|
||||
default:
|
||||
t.Errorf("Unsupported fork version %s", version.String(v))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
err := s.cfg.BeaconDB.SaveBlock(ctx, l.AttestedBlock)
|
||||
require.NoError(t, err)
|
||||
attestedBlockRoot, err := l.AttestedBlock.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = s.cfg.BeaconDB.SaveState(ctx, l.AttestedState, attestedBlockRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
currentBlockRoot, err := l.Block.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
roblock, err := consensusblocks.NewROBlockWithRoot(l.Block, currentBlockRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.cfg.BeaconDB.SaveBlock(ctx, roblock)
|
||||
require.NoError(t, err)
|
||||
err = s.cfg.BeaconDB.SaveState(ctx, l.State, currentBlockRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.cfg.BeaconDB.SaveBlock(ctx, l.FinalizedBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &postBlockProcessConfig{
|
||||
ctx: ctx,
|
||||
roblock: roblock,
|
||||
postState: l.State,
|
||||
isValidPayload: true,
|
||||
}
|
||||
|
||||
return l, cfg
|
||||
}
|
||||
|
||||
func TestProcessLightClientOptimisticUpdate(t *testing.T) {
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableLightClient = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
beaconCfg := params.BeaconConfig()
|
||||
beaconCfg.AltairForkEpoch = 1
|
||||
beaconCfg.BellatrixForkEpoch = 2
|
||||
beaconCfg.CapellaForkEpoch = 3
|
||||
beaconCfg.DenebForkEpoch = 4
|
||||
beaconCfg.ElectraForkEpoch = 5
|
||||
params.OverrideBeaconConfig(beaconCfg)
|
||||
|
||||
s, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
oldOptions []util.LightClientOption
|
||||
newOptions []util.LightClientOption
|
||||
expectReplace bool
|
||||
}{
|
||||
{
|
||||
name: "No old update",
|
||||
oldOptions: nil,
|
||||
newOptions: []util.LightClientOption{},
|
||||
expectReplace: true,
|
||||
},
|
||||
{
|
||||
name: "Same age",
|
||||
oldOptions: []util.LightClientOption{},
|
||||
newOptions: []util.LightClientOption{util.WithSupermajority()}, // supermajority does not matter here and is only added to result in two different updates
|
||||
expectReplace: false,
|
||||
},
|
||||
{
|
||||
name: "Old update is better - age",
|
||||
oldOptions: []util.LightClientOption{util.WithIncreasedAttestedSlot(1)},
|
||||
newOptions: []util.LightClientOption{},
|
||||
expectReplace: false,
|
||||
},
|
||||
{
|
||||
name: "New update is better - age",
|
||||
oldOptions: []util.LightClientOption{},
|
||||
newOptions: []util.LightClientOption{util.WithIncreasedAttestedSlot(1)},
|
||||
expectReplace: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for testVersion := 1; testVersion < 6; testVersion++ { // test all forks
|
||||
var forkEpoch uint64
|
||||
var expectedVersion int
|
||||
|
||||
switch testVersion {
|
||||
case 1:
|
||||
forkEpoch = uint64(params.BeaconConfig().AltairForkEpoch)
|
||||
expectedVersion = version.Altair
|
||||
case 2:
|
||||
forkEpoch = uint64(params.BeaconConfig().BellatrixForkEpoch)
|
||||
expectedVersion = version.Altair
|
||||
case 3:
|
||||
forkEpoch = uint64(params.BeaconConfig().CapellaForkEpoch)
|
||||
expectedVersion = version.Capella
|
||||
case 4:
|
||||
forkEpoch = uint64(params.BeaconConfig().DenebForkEpoch)
|
||||
expectedVersion = version.Deneb
|
||||
case 5:
|
||||
forkEpoch = uint64(params.BeaconConfig().ElectraForkEpoch)
|
||||
expectedVersion = version.Deneb
|
||||
default:
|
||||
t.Errorf("Unsupported fork version %s", version.String(testVersion))
|
||||
}
|
||||
|
||||
t.Run(version.String(testVersion)+"_"+tc.name, func(t *testing.T) {
|
||||
s.genesisTime = time.Unix(time.Now().Unix()-(int64(forkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
|
||||
s.lcStore = &lightClient.Store{}
|
||||
|
||||
var oldActualUpdate interfaces.LightClientOptimisticUpdate
|
||||
var err error
|
||||
if tc.oldOptions != nil {
|
||||
// config for old update
|
||||
lOld, cfgOld := setupLightClientTestRequirements(ctx, t, s, testVersion, tc.oldOptions...)
|
||||
require.NoError(t, s.processLightClientOptimisticUpdate(cfgOld.ctx, cfgOld.roblock, cfgOld.postState))
|
||||
|
||||
oldActualUpdate, err = lightClient.NewLightClientOptimisticUpdateFromBeaconState(
|
||||
lOld.Ctx,
|
||||
lOld.State.Slot(),
|
||||
lOld.State,
|
||||
lOld.Block,
|
||||
lOld.AttestedState,
|
||||
lOld.AttestedBlock,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// check that the old update is saved
|
||||
oldUpdate := s.lcStore.LastOptimisticUpdate()
|
||||
require.NotNil(t, oldUpdate)
|
||||
|
||||
require.DeepEqual(t, oldUpdate, oldActualUpdate, "old update should be saved")
|
||||
}
|
||||
|
||||
// config for new update
|
||||
lNew, cfgNew := setupLightClientTestRequirements(ctx, t, s, testVersion, tc.newOptions...)
|
||||
require.NoError(t, s.processLightClientOptimisticUpdate(cfgNew.ctx, cfgNew.roblock, cfgNew.postState))
|
||||
|
||||
newActualUpdate, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(
|
||||
lNew.Ctx,
|
||||
lNew.State.Slot(),
|
||||
lNew.State,
|
||||
lNew.Block,
|
||||
lNew.AttestedState,
|
||||
lNew.AttestedBlock,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepNotEqual(t, newActualUpdate, oldActualUpdate, "new update should not be equal to old update")
|
||||
|
||||
// check that the new update is saved or skipped
|
||||
newUpdate := s.lcStore.LastOptimisticUpdate()
|
||||
require.NotNil(t, newUpdate)
|
||||
|
||||
if tc.expectReplace {
|
||||
require.DeepEqual(t, newActualUpdate, newUpdate)
|
||||
require.Equal(t, expectedVersion, newUpdate.Version())
|
||||
} else {
|
||||
require.DeepEqual(t, oldActualUpdate, newUpdate)
|
||||
require.Equal(t, expectedVersion, newUpdate.Version())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessLightClientFinalityUpdate(t *testing.T) {
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableLightClient = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
|
||||
params.SetupTestConfigCleanup(t)
|
||||
beaconCfg := params.BeaconConfig()
|
||||
beaconCfg.AltairForkEpoch = 1
|
||||
beaconCfg.BellatrixForkEpoch = 2
|
||||
beaconCfg.CapellaForkEpoch = 3
|
||||
beaconCfg.DenebForkEpoch = 4
|
||||
beaconCfg.ElectraForkEpoch = 5
|
||||
params.OverrideBeaconConfig(beaconCfg)
|
||||
|
||||
s, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
oldOptions []util.LightClientOption
|
||||
newOptions []util.LightClientOption
|
||||
expectReplace bool
|
||||
}{
|
||||
{
|
||||
name: "No old update",
|
||||
oldOptions: nil,
|
||||
newOptions: []util.LightClientOption{},
|
||||
expectReplace: true,
|
||||
},
|
||||
{
|
||||
name: "Old update is better - age - no supermajority",
|
||||
oldOptions: []util.LightClientOption{util.WithIncreasedFinalizedSlot(1)},
|
||||
newOptions: []util.LightClientOption{},
|
||||
expectReplace: false,
|
||||
},
|
||||
{
|
||||
name: "Old update is better - age - both supermajority",
|
||||
oldOptions: []util.LightClientOption{util.WithIncreasedFinalizedSlot(1), util.WithSupermajority()},
|
||||
newOptions: []util.LightClientOption{util.WithSupermajority()},
|
||||
expectReplace: false,
|
||||
},
|
||||
{
|
||||
name: "Old update is better - supermajority",
|
||||
oldOptions: []util.LightClientOption{util.WithSupermajority()},
|
||||
newOptions: []util.LightClientOption{},
|
||||
expectReplace: false,
|
||||
},
|
||||
{
|
||||
name: "New update is better - age - both supermajority",
|
||||
oldOptions: []util.LightClientOption{util.WithSupermajority()},
|
||||
newOptions: []util.LightClientOption{util.WithIncreasedFinalizedSlot(1), util.WithSupermajority()},
|
||||
expectReplace: true,
|
||||
},
|
||||
{
|
||||
name: "New update is better - age - no supermajority",
|
||||
oldOptions: []util.LightClientOption{},
|
||||
newOptions: []util.LightClientOption{util.WithIncreasedFinalizedSlot(1)},
|
||||
expectReplace: true,
|
||||
},
|
||||
{
|
||||
name: "New update is better - supermajority",
|
||||
oldOptions: []util.LightClientOption{},
|
||||
newOptions: []util.LightClientOption{util.WithSupermajority()},
|
||||
expectReplace: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for testVersion := 1; testVersion < 6; testVersion++ { // test all forks
|
||||
var forkEpoch uint64
|
||||
var expectedVersion int
|
||||
|
||||
switch testVersion {
|
||||
case 1:
|
||||
forkEpoch = uint64(params.BeaconConfig().AltairForkEpoch)
|
||||
expectedVersion = version.Altair
|
||||
case 2:
|
||||
forkEpoch = uint64(params.BeaconConfig().BellatrixForkEpoch)
|
||||
expectedVersion = version.Altair
|
||||
case 3:
|
||||
forkEpoch = uint64(params.BeaconConfig().CapellaForkEpoch)
|
||||
expectedVersion = version.Capella
|
||||
case 4:
|
||||
forkEpoch = uint64(params.BeaconConfig().DenebForkEpoch)
|
||||
expectedVersion = version.Deneb
|
||||
case 5:
|
||||
forkEpoch = uint64(params.BeaconConfig().ElectraForkEpoch)
|
||||
expectedVersion = version.Electra
|
||||
default:
|
||||
t.Errorf("Unsupported fork version %s", version.String(testVersion))
|
||||
}
|
||||
|
||||
t.Run(version.String(testVersion)+"_"+tc.name, func(t *testing.T) {
|
||||
s.genesisTime = time.Unix(time.Now().Unix()-(int64(forkEpoch)*int64(params.BeaconConfig().SlotsPerEpoch)*int64(params.BeaconConfig().SecondsPerSlot)), 0)
|
||||
s.lcStore = &lightClient.Store{}
|
||||
|
||||
var actualOldUpdate, actualNewUpdate interfaces.LightClientFinalityUpdate
|
||||
var err error
|
||||
|
||||
if tc.oldOptions != nil {
|
||||
// config for old update
|
||||
lOld, cfgOld := setupLightClientTestRequirements(ctx, t, s, testVersion, tc.oldOptions...)
|
||||
require.NoError(t, s.processLightClientFinalityUpdate(cfgOld.ctx, cfgOld.roblock, cfgOld.postState))
|
||||
|
||||
// check that the old update is saved
|
||||
actualOldUpdate, err = lightClient.NewLightClientFinalityUpdateFromBeaconState(
|
||||
ctx,
|
||||
cfgOld.postState.Slot(),
|
||||
cfgOld.postState,
|
||||
cfgOld.roblock,
|
||||
lOld.AttestedState,
|
||||
lOld.AttestedBlock,
|
||||
lOld.FinalizedBlock,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
oldUpdate := s.lcStore.LastFinalityUpdate()
|
||||
require.DeepEqual(t, actualOldUpdate, oldUpdate)
|
||||
}
|
||||
|
||||
// config for new update
|
||||
lNew, cfgNew := setupLightClientTestRequirements(ctx, t, s, testVersion, tc.newOptions...)
|
||||
require.NoError(t, s.processLightClientFinalityUpdate(cfgNew.ctx, cfgNew.roblock, cfgNew.postState))
|
||||
|
||||
// check that the actual old update and the actual new update are different
|
||||
actualNewUpdate, err = lightClient.NewLightClientFinalityUpdateFromBeaconState(
|
||||
ctx,
|
||||
cfgNew.postState.Slot(),
|
||||
cfgNew.postState,
|
||||
cfgNew.roblock,
|
||||
lNew.AttestedState,
|
||||
lNew.AttestedBlock,
|
||||
lNew.FinalizedBlock,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, actualOldUpdate, actualNewUpdate)
|
||||
|
||||
// check that the new update is saved or skipped
|
||||
newUpdate := s.lcStore.LastFinalityUpdate()
|
||||
|
||||
if tc.expectReplace {
|
||||
require.DeepEqual(t, actualNewUpdate, newUpdate)
|
||||
require.Equal(t, expectedVersion, newUpdate.Version())
|
||||
} else {
|
||||
require.DeepEqual(t, actualOldUpdate, newUpdate)
|
||||
require.Equal(t, expectedVersion, newUpdate.Version())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user