diff --git a/consensus/consortium/v2/consortium.go b/consensus/consortium/v2/consortium.go index cd7682c0d..071d9dfcc 100644 --- a/consensus/consortium/v2/consortium.go +++ b/consensus/consortium/v2/consortium.go @@ -475,7 +475,12 @@ func (c *Consortium) verifyValidatorFieldsInExtraData( extraData.BlockProducersBitSet, ) } - if c.IsPeriodBlock(chain, header, parents) { + isPeriodBlock, err := c.IsPeriodBlock(chain, header, parents) + if err != nil { + log.Error("Failed to check IsPeriodBlock", "blocknum", header.Number, "err", err) + return err + } + if isPeriodBlock { if len(extraData.CheckpointValidators) == 0 { return fmt.Errorf( "%w: checkpoint validator: %v", @@ -679,13 +684,18 @@ func (c *Consortium) snapshot(chain consensus.ChainHeaderReader, number uint64, break } - // this case is only happened in mock mode + // this case is only happened in mock/test mode if number == 0 { validators, err := c.contract.GetBlockProducers(common.Hash{}, common.Big0) if err != nil { return nil, err } snap = newSnapshot(c.chainConfig, c.config, c.signatures, number, hash, validators, nil, c.ethAPI) + if c.db != nil { + if err := snap.store(c.db); err != nil { + return nil, err + } + } break } @@ -931,7 +941,13 @@ func (c *Consortium) getCheckpointValidatorsFromContract( if !isAaron { sort.Sort(validatorsAscending(blockProducers)) } - if !c.IsPeriodBlock(chain, header, nil) { + + isPeriodBlock, err := c.IsPeriodBlock(chain, header, nil) + if err != nil { + log.Error("Failed to check IsPeriodBlock", "blocknum", header.Number, "err", err) + return nil, nil, err + } + if !isPeriodBlock { return nil, blockProducers, nil } validatorCandidates, err := contract.GetValidatorCandidates(parentHash, parentBlockNumber) @@ -1048,7 +1064,12 @@ func (c *Consortium) Prepare(chain consensus.ChainHeaderReader, header *types.He // current epoch, which is used to calculate block producer bit set later on. var latestValidatorCandidates []finality.ValidatorWithBlsPub - if c.IsPeriodBlock(chain, header, nil) { + isPeriodBlock, err := c.IsPeriodBlock(chain, header, nil) + if err != nil { + log.Error("Failed to check IsPeriodBlock", "blocknum", header.Number, "err", err) + return err + } + if isPeriodBlock { extraData.CheckpointValidators = checkpointValidators latestValidatorCandidates = checkpointValidators } else { @@ -1278,8 +1299,13 @@ func (c *Consortium) Finalize(chain consensus.ChainHeaderReader, header *types.H // If isTripp and new period, read all validator candidates and // their amounts, check with stored data in header if c.IsTrippEffective(chain, header) { + isPeriodBlock, err := c.IsPeriodBlock(chain, header, nil) + if err != nil { + log.Error("Failed to check IsPeriodBlock", "blocknum", header.Number, "err", err) + return err + } if c.chainConfig.IsAaron(header.Number) { - if !c.IsPeriodBlock(chain, header, nil) { + if !isPeriodBlock { // Except period block, checkpoint validator list get from contract // is nil at other epoch blocks. From the fact that validator candidate list // does not change over the whole period, it's possible to get the latest @@ -1303,7 +1329,7 @@ func (c *Consortium) Finalize(chain consensus.ChainHeaderReader, header *types.H } } } - if c.IsPeriodBlock(chain, header, nil) { + if isPeriodBlock { if err := verifyValidatorExtraDataWithContract(checkpointValidators, extraData, true, true); err != nil { return err } @@ -1937,29 +1963,64 @@ func (c *Consortium) getLastCheckpointHeader(chain consensus.ChainHeaderReader, return current } +func getParentHeader(chain consensus.ChainHeaderReader, currentHeader *types.Header, parents []*types.Header) *types.Header { + if len(parents) > 0 { + return parents[len(parents)-1] + } else { + return chain.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1) + } +} + // IsPeriodBlock returns indicator whether a block is a period checkpoint block or not, // which is the first checkpoint block (block % EpochV2 == 0) after 00:00 UTC everyday. -func (c *Consortium) IsPeriodBlock(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) bool { +// +// Before Venoki, IsPeriodBlock returns true when header is the first block of an epoch +// whose timestamp is on the different date from first block of previous epoch. +// After Venoki, IsPeriodBlock returns true when header is the first block of an epoch +// (let's call epoch n) whose parent block's timestamp is on the different date from the +// last block of "previous previous" epoch (epoch n - 2) +// +// The caller must ensure to call this function after passing Tripp hardfork only. +func (c *Consortium) IsPeriodBlock(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) (bool, error) { if c.isTest { - return c.testTrippPeriod + return c.testTrippPeriod, nil } number := header.Number.Uint64() - if number%c.config.EpochV2 != 0 || !chain.Config().IsTripp(header.Number) { - return false + if number%c.config.EpochV2 != 0 { + return false, nil } - // Derive parent snapshot. If err, we recursively find the nearest epoch - // block, and determine whether the header period is ahead of that block period. - snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) - if err != nil { - log.Warn("Fail to get snapshot at", "blockNumber", number-1, "blockHash", header.ParentHash, "err", err) - parent := c.getLastCheckpointHeader(chain, header) - if parent == nil { - return false + // When transitioning to Venoki, we actually check the last block of previous epoch + // with the first block of previous epoch. This is unusual but the overall behavior + // shows no difference. Furthermore, we will set the Venoki in the middle of period so + // when transitioning, it is guaranteed to have no change in the period number. As a + // result, we can simplify the code to check Venoki transition here. + if chain.Config().IsVenoki(header.Number) { + snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + log.Error("Failed to get snapshot", "number", number-1, "hash", header.ParentHash) + return false, err + } + parentHeader := getParentHeader(chain, header, parents) + if parentHeader == nil { + log.Error("Failed to get parent header", "number", number-1, "hash", header.ParentHash) + return false, consensus.ErrUnknownAncestor + } + return uint64(parentHeader.Time/dayInSeconds) > snap.CurrentPeriod, nil + } else { + // Derive parent snapshot. If err, we recursively find the nearest epoch + // block, and determine whether the header period is ahead of that block period. + snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + log.Warn("Fail to get snapshot at", "blockNumber", number-1, "blockHash", header.ParentHash, "err", err) + parent := c.getLastCheckpointHeader(chain, header) + if parent == nil { + return false, consensus.ErrUnknownAncestor + } + return uint64(header.Time/dayInSeconds) > uint64(parent.Time/dayInSeconds), nil } - return uint64(header.Time/dayInSeconds) > uint64(parent.Time/dayInSeconds) + return uint64(header.Time/dayInSeconds) > snap.CurrentPeriod, nil } - return uint64(header.Time/dayInSeconds) > snap.CurrentPeriod } // IsTrippEffective returns indicator whether the Tripp consensus rule is effective, diff --git a/consensus/consortium/v2/consortium_test.go b/consensus/consortium/v2/consortium_test.go index 197b6f94a..1f580f917 100644 --- a/consensus/consortium/v2/consortium_test.go +++ b/consensus/consortium/v2/consortium_test.go @@ -18,6 +18,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" gokzg4844 "github.com/crate-crypto/go-kzg-4844" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" consortiumCommon "github.com/ethereum/go-ethereum/consensus/consortium/common" "github.com/ethereum/go-ethereum/consensus/consortium/v2/finality" @@ -30,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/bls/blst" blsCommon "github.com/ethereum/go-ethereum/crypto/bls/common" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/hashicorp/golang-lru/arc/v2" @@ -1109,8 +1111,14 @@ func TestGetCheckpointValidatorFromContract(t *testing.T) { } } +type mockValidator struct { + blsPubKey blsCommon.PublicKey + stakedAmount *big.Int +} + type mockContract struct { - validators map[common.Address]blsCommon.PublicKey + validators map[common.Address]mockValidator + maxValidatorNumber *big.Int } func (contract *mockContract) WrapUpEpoch(opts *consortiumCommon.ApplyTransactOpts) error { @@ -1150,13 +1158,17 @@ func (contract *mockContract) GetBlockProducers(_ common.Hash, _ *big.Int) ([]co } func (contract *mockContract) GetValidatorCandidates(_ common.Hash, _ *big.Int) ([]common.Address, error) { - return nil, nil + var validatorAddresses []common.Address + for address := range contract.validators { + validatorAddresses = append(validatorAddresses, address) + } + return validatorAddresses, nil } func (contract *mockContract) GetBlsPublicKey(_ common.Hash, _ *big.Int, address common.Address) (blsCommon.PublicKey, error) { - if key, ok := contract.validators[address]; ok { - if key != nil { - return key, nil + if val, ok := contract.validators[address]; ok { + if val.blsPubKey != nil { + return val.blsPubKey, nil } else { return nil, errors.New("no BLS public key found") } @@ -1165,12 +1177,16 @@ func (contract *mockContract) GetBlsPublicKey(_ common.Hash, _ *big.Int, address } } -func (contract *mockContract) GetStakedAmount(_ common.Hash, _ *big.Int, _ []common.Address) ([]*big.Int, error) { - return nil, nil +func (contract *mockContract) GetStakedAmount(_ common.Hash, _ *big.Int, candidates []common.Address) ([]*big.Int, error) { + stakedAmount := make([]*big.Int, len(candidates)) + for i, candidate := range candidates { + stakedAmount[i] = contract.validators[candidate].stakedAmount + } + return stakedAmount, nil } func (contract *mockContract) GetMaxValidatorNumber(blockHash common.Hash, blockNumber *big.Int) (*big.Int, error) { - return nil, nil + return contract.maxValidatorNumber, nil } type mockVotePool struct { @@ -1573,9 +1589,11 @@ func TestKnownBlockReorg(t *testing.T) { }).MustCommit(db) mock := &mockContract{ - validators: make(map[common.Address]blsCommon.PublicKey), + validators: make(map[common.Address]mockValidator), + } + mock.validators[validatorAddrs[0]] = mockValidator{ + blsPubKey: blsKeys[0].PublicKey(), } - mock.validators[validatorAddrs[0]] = blsKeys[0].PublicKey() recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) signatures, _ := arc.NewARC[common.Hash, common.Address](inmemorySignatures) @@ -1621,7 +1639,9 @@ func TestKnownBlockReorg(t *testing.T) { } for i := range validatorAddrs { - mock.validators[validatorAddrs[i]] = blsKeys[i].PublicKey() + mock.validators[validatorAddrs[i]] = mockValidator{ + blsPubKey: blsKeys[i].PublicKey(), + } } var checkpointValidators []finality.ValidatorWithBlsPub @@ -1824,8 +1844,10 @@ func TestUpgradeRoninTrustedOrg(t *testing.T) { }).MustCommit(db) mock := &mockContract{ - validators: map[common.Address]blsCommon.PublicKey{ - validatorAddr: blsSecretKey.PublicKey(), + validators: map[common.Address]mockValidator{ + validatorAddr: mockValidator{ + blsPubKey: blsSecretKey.PublicKey(), + }, }, } recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) @@ -2091,8 +2113,10 @@ func TestSystemTransactionOrder(t *testing.T) { }).MustCommit(db) mock := &mockContract{ - validators: map[common.Address]blsCommon.PublicKey{ - validatorAddr: blsSecretKey.PublicKey(), + validators: map[common.Address]mockValidator{ + validatorAddr: mockValidator{ + blsPubKey: blsSecretKey.PublicKey(), + }, }, } recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) @@ -2219,7 +2243,7 @@ func TestIsPeriodBlock(t *testing.T) { recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) signatures, _ := arc.NewARC[common.Hash, common.Address](inmemorySignatures) mock := &mockContract{ - validators: map[common.Address]blsCommon.PublicKey{}, + validators: map[common.Address]mockValidator{}, } c := &Consortium{ chainConfig: &chainConfig, @@ -2231,29 +2255,25 @@ func TestIsPeriodBlock(t *testing.T) { db: db, contract: mock, } - validators := make([]common.Address, NUM_OF_VALIDATORS) - for i := 0; i < NUM_OF_VALIDATORS; i++ { - validators = append(validators, common.BigToAddress(big.NewInt(int64(i)))) - } - var header = &types.Header{} - // header of block 0 - // this must not a period block - header = genesis.Header() - if c.IsPeriodBlock(chain, header, nil) { - t.Errorf("wrong period block") - } - // header of block 200 // this must not a period block header = bs[199].Header() - if c.IsPeriodBlock(chain, header, nil) { + isPeriodBlock, err := c.IsPeriodBlock(chain, header, nil) + if err != nil { + t.Fatal(err) + } + if isPeriodBlock { t.Error("wrong period block") } header = bs[351].Header() - if c.IsPeriodBlock(chain, header, nil) { + isPeriodBlock, err = c.IsPeriodBlock(chain, header, nil) + if err != nil { + t.Fatal(err) + } + if isPeriodBlock { t.Error("wrong period block") } @@ -2274,18 +2294,251 @@ func TestIsPeriodBlock(t *testing.T) { // this must be a period block header = bs[399].Header() // this header must be period header - if !c.IsPeriodBlock(chain, header, nil) { + isPeriodBlock, err = c.IsPeriodBlock(chain, header, nil) + if err != nil { + t.Fatal(err) + } + if !isPeriodBlock { t.Errorf("wrong period block") } // header of block 500 // this must not be a period block header = bs[499].Header() - if c.IsPeriodBlock(chain, header, nil) { + isPeriodBlock, err = c.IsPeriodBlock(chain, header, nil) + if err != nil { + t.Fatal(err) + } + if isPeriodBlock { t.Errorf("wrong period block") } } +func generateChain( + t *testing.T, + genesis *types.Block, + chainConfig *params.ChainConfig, + chain *core.BlockChain, + v2 *Consortium, + db ethdb.Database, + secretKey *ecdsa.PrivateKey, + validatorAddr common.Address, + blsSecretKey blsCommon.SecretKey, + bumpTimeStampLastBlockOfEpoch bool, +) []*types.Block { + extraData := finality.HeaderExtraData{} + encodedExtraData, err := extraData.EncodeV2(chainConfig, common.Big1) + if err != nil { + t.Fatal(err) + } + + parent := genesis + var block []*types.Block + for i := 0; i < 400; i++ { + block, _ = core.GenerateChain( + chainConfig, + parent, + v2, + db, + 1, + func(_ int, bg *core.BlockGen) { + if i == 0 { + bg.OffsetTime(int64(dayInSeconds)) + } + + var blockExtraData = encodedExtraData + if i == 199 { + extraData := finality.HeaderExtraData{ + BlockProducersBitSet: 1, + CheckpointValidators: []finality.ValidatorWithBlsPub{ + { + Address: validatorAddr, + BlsPublicKey: blsSecretKey.PublicKey(), + Weight: consortiumCommon.MaxFinalityVotePercentage, + }, + }, + } + encodedExtraData, err := extraData.EncodeV2(chainConfig, common.Big1) + if err != nil { + t.Fatal(err) + } + blockExtraData = encodedExtraData + } + bg.SetCoinbase(validatorAddr) + bg.SetExtra(blockExtraData[:]) + if bumpTimeStampLastBlockOfEpoch { + if i == 398 { + // Make block 399 to a new date + bg.OffsetTime(int64(dayInSeconds)) + } + } else if i == 399 { + // Make block 400 to a new date + bg.OffsetTime(int64(dayInSeconds)) + } + bg.SetDifficulty(big.NewInt(7)) + }, + true, + ) + + header := block[0].Header() + hash := calculateSealHash(header, big.NewInt(2021)) + sig, err := crypto.Sign(hash[:], secretKey) + if err != nil { + t.Fatalf("Failed to sign block, err %s", err) + } + + copy(header.Extra[len(header.Extra)-consortiumCommon.ExtraSeal:], sig) + block[0] = block[0].WithSeal(header) + parent = block[0] + + if i != 399 { + _, err = chain.InsertChain(block, nil) + if err != nil { + t.Fatalf("Failed to insert chain, err %s", err) + } + } + } + + return block +} + +func createNewChain( + chainConfig *params.ChainConfig, + validatorAddr common.Address, + blsSecretKey blsCommon.SecretKey, +) (*types.Block, *core.BlockChain, ethdb.Database, *Consortium) { + db := rawdb.NewMemoryDatabase() + genesis := (&core.Genesis{ + Config: chainConfig, + }).MustCommit(db) + + mock := &mockContract{ + validators: map[common.Address]mockValidator{ + validatorAddr: mockValidator{ + blsPubKey: blsSecretKey.PublicKey(), + stakedAmount: big.NewInt(10), + }, + }, + maxValidatorNumber: common.Big1, + } + recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) + signatures, _ := arc.NewARC[common.Hash, common.Address](inmemorySignatures) + + v2 := &Consortium{ + chainConfig: chainConfig, + contract: mock, + recents: recents, + signatures: signatures, + fakeDiff: true, + db: db, + config: ¶ms.ConsortiumConfig{ + EpochV2: 200, + }, + } + + chain, _ := core.NewBlockChain(db, nil, chainConfig, v2, vm.Config{}, nil, nil) + return genesis, chain, db, v2 +} + +func TestIsPeriodVenoki(t *testing.T) { + blsSecretKey, err := blst.RandKey() + if err != nil { + t.Fatal(err) + } + secretKey, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + validatorAddr := crypto.PubkeyToAddress(secretKey.PublicKey) + + chainConfig := params.ChainConfig{ + ChainID: big.NewInt(2021), + HomesteadBlock: common.Big0, + EIP150Block: common.Big0, + EIP155Block: common.Big0, + EIP158Block: common.Big0, + ConsortiumV2Block: common.Big0, + MikoBlock: common.Big0, + ShillinBlock: common.Big1, + TrippBlock: common.Big1, + TrippPeriod: common.Big0, + AaronBlock: common.Big1, + Consortium: ¶ms.ConsortiumConfig{ + EpochV2: 200, + }, + TransparentProxyCodeUpgrade: ¶ms.ContractCodeUpgrade{ + Code: hexutil.Bytes{}, + AxieAddress: common.Address{}, + LandAddress: common.Address{}, + }, + } + + genesis, chain, db, v2 := createNewChain(&chainConfig, validatorAddr, blsSecretKey) + block := generateChain(t, genesis, &chainConfig, chain, v2, db, secretKey, validatorAddr, blsSecretKey, false) + + // Case 1 + // Before Venoki, the first block of epoch passes 0:00 must be the + // start of new period + isPeriodBlock, err := v2.IsPeriodBlock(chain, block[0].Header(), nil) + if err != nil { + t.Fatal(err) + } + if !isPeriodBlock { + t.Fatalf("Expect isPeriodBlock before Venoki, got: %v", isPeriodBlock) + } + snapshot, err := v2.snapshot(chain, block[0].NumberU64(), block[0].Hash(), []*types.Header{block[0].Header()}) + if err != nil { + t.Fatal(err) + } + // Snapshot period number must be the period of first block of epoch (block 400) which is 2 + if snapshot.CurrentPeriod != 2 { + t.Fatalf("Mismatch period block before Venoki, exp: %v, got: %v", 2, snapshot.CurrentPeriod) + } + + // Case 2 + // After Venoki, the start of new period must be when the last block of + // previous epoch passes 0:00 + // The last block of previous epoch does not pass 0:00 + chainConfig.VenokiBlock = big.NewInt(200) + genesis, chain, db, v2 = createNewChain(&chainConfig, validatorAddr, blsSecretKey) + block = generateChain(t, genesis, &chainConfig, chain, v2, db, secretKey, validatorAddr, blsSecretKey, false) + isPeriodBlock, err = v2.IsPeriodBlock(chain, block[0].Header(), nil) + if err != nil { + t.Fatal(err) + } + if isPeriodBlock { + t.Fatalf("Expect not isPeriodBlock after Venoki, got: %v", isPeriodBlock) + } + snapshot, err = v2.snapshot(chain, block[0].NumberU64(), block[0].Hash(), []*types.Header{block[0].Header()}) + if err != nil { + t.Fatal(err) + } + // Snapshot period number must be the period of last block of previous epoch (block 399) which is 1 + if snapshot.CurrentPeriod != 1 { + t.Fatalf("Mismatch period block after Venoki, exp: %v, got: %v", 1, snapshot.CurrentPeriod) + } + + // Case 3 + // The last block of previous epoch passes 0:00 + genesis, chain, db, v2 = createNewChain(&chainConfig, validatorAddr, blsSecretKey) + block = generateChain(t, genesis, &chainConfig, chain, v2, db, secretKey, validatorAddr, blsSecretKey, true) + isPeriodBlock, err = v2.IsPeriodBlock(chain, block[0].Header(), nil) + if err != nil { + t.Fatal(err) + } + if !isPeriodBlock { + t.Fatalf("Expect isPeriodBlock after Venoki, got: %v", isPeriodBlock) + } + snapshot, err = v2.snapshot(chain, block[0].NumberU64(), block[0].Hash(), []*types.Header{block[0].Header()}) + if err != nil { + t.Fatal(err) + } + // Snapshot period number must be the period of last block of previous epoch (block 399) which is 2 + if snapshot.CurrentPeriod != 2 { + t.Fatalf("Mismatch period block after Venoki, exp: %v, got: %v", 2, snapshot.CurrentPeriod) + } +} + func TestIsTrippEffective(t *testing.T) { now := uint64(time.Now().Unix()) midnight := uint64(now / dayInSeconds * dayInSeconds) @@ -2315,7 +2568,7 @@ func TestIsTrippEffective(t *testing.T) { recents, _ := arc.NewARC[common.Hash, *Snapshot](inmemorySnapshots) signatures, _ := arc.NewARC[common.Hash, common.Address](inmemorySignatures) mock := &mockContract{ - validators: map[common.Address]blsCommon.PublicKey{}, + validators: map[common.Address]mockValidator{}, } c := &Consortium{ chainConfig: &chainConfig, diff --git a/consensus/consortium/v2/snapshot.go b/consensus/consortium/v2/snapshot.go index 2d1995531..2d051d5c9 100644 --- a/consensus/consortium/v2/snapshot.go +++ b/consensus/consortium/v2/snapshot.go @@ -42,7 +42,12 @@ type Snapshot struct { JustifiedBlockNumber uint64 `json:"justifiedBlockNumber,omitempty"` // The justified block number JustifiedBlockHash common.Hash `json:"justifiedBlockHash,omitempty"` // The justified block hash - CurrentPeriod uint64 `json:"currentPeriod,omitempty"` // Period number where the snapshot was created + + // After Tripp before Venoki, the period number of an epoch calculated based on the timestamp + // of first block in epoch + // After Venoki, the period number of an epoch calculated based on the the timestamp of last block + // in the previous epoch + CurrentPeriod uint64 `json:"currentPeriod,omitempty"` } // validatorsAscending implements the sort interface to allow sorting a list of addresses @@ -259,8 +264,17 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } } - if chainRules.IsTripp && number%s.config.EpochV2 == 0 && header.Time/dayInSeconds > snap.CurrentPeriod { - snap.CurrentPeriod = header.Time / dayInSeconds + // First block of an epoch + if number%s.config.EpochV2 == 0 { + if chainRules.IsVenoki { + parentHeader := FindAncientHeader(header, 1, chain, parents) + if parentHeader == nil { + return nil, consensus.ErrUnknownAncestor + } + snap.CurrentPeriod = parentHeader.Time / dayInSeconds + } else if chainRules.IsTripp { + snap.CurrentPeriod = header.Time / dayInSeconds + } } // Change the validator set base on the size of the validators set if number > 0 && number%s.config.EpochV2 == uint64(len(snap.validators())/2) {