Skip to content

Commit

Permalink
Merge branch 'master' into fix/snapshot-pruning
Browse files Browse the repository at this point in the history
  • Loading branch information
Francesco4203 authored Jun 12, 2024
2 parents 3e3358f + 9d6d2d7 commit 62df14f
Show file tree
Hide file tree
Showing 10 changed files with 482 additions and 121 deletions.
2 changes: 1 addition & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ var (
MaxCurVoteAmountPerBlock = &cli.IntFlag{
Name: "votepool.maxcurvoteperblock",
Usage: "The maximum finality vote per current block",
Value: 22,
Value: 64,
Category: flags.FastFinalityCategory,
}

Expand Down
191 changes: 149 additions & 42 deletions consensus/consortium/v2/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ type Consortium struct {
votePool consensus.VotePool

// This is used in unit test only
isTest bool
testTrippEffective bool
testTrippPeriod bool
}
Expand Down Expand Up @@ -270,7 +271,7 @@ func (c *Consortium) VerifyVote(chain consensus.ChainHeaderReader, vote *types.V
// verifyFinalitySignatures verifies the finality signatures in the block header
func (c *Consortium) verifyFinalitySignatures(
chain consensus.ChainHeaderReader,
finalityVotedValidators finality.FinalityVoteBitSet,
finalityVotedValidators finality.BitSet,
finalitySignatures blsCommon.Signature,
header *types.Header,
parents []*types.Header,
Expand Down Expand Up @@ -354,6 +355,44 @@ func (c *Consortium) VerifyHeaderAndParents(chain consensus.ChainHeaderReader, h
return c.verifyCascadingFields(chain, header, parents)
}

func (c *Consortium) verifyValidatorFieldsInExtraData(
chain consensus.ChainHeaderReader,
extraData *finality.HeaderExtraData,
header *types.Header,
) error {
isEpoch := header.Number.Uint64()%c.config.EpochV2 == 0 || c.chainConfig.IsOnConsortiumV2(header.Number)
if !isEpoch && (len(extraData.CheckpointValidators) != 0 || len(extraData.BlockProducers) != 0) || extraData.BlockProducersBitSet != 0 {
return consortiumCommon.ErrExtraValidators
}

if c.IsTrippEffective(chain, header) {
if c.chainConfig.IsAaron(header.Number) {
if isEpoch && (extraData.BlockProducersBitSet == 0 || len(extraData.BlockProducers) != 0) {
return consortiumCommon.ErrExtraValidators
}
} else if isEpoch && (extraData.BlockProducersBitSet != 0 || len(extraData.BlockProducers) == 0) {
return consortiumCommon.ErrExtraValidators
}
if c.IsPeriodBlock(chain, header) {
if len(extraData.CheckpointValidators) == 0 {
return consortiumCommon.ErrExtraValidators
}
} else {
if len(extraData.CheckpointValidators) != 0 {
return consortiumCommon.ErrExtraValidators
}
}
} else {
if isEpoch && len(extraData.CheckpointValidators) == 0 {
return consortiumCommon.ErrExtraValidators
}
if len(extraData.BlockProducers) != 0 || extraData.BlockProducersBitSet != 0 {
return consortiumCommon.ErrExtraValidators
}
}
return nil
}

// verifyCascadingFields verifies all the header fields that are not standalone,
// rather depend on a batch of previous headers. The caller may optionally pass
// in a batch of parents (ascending order) to avoid looking those up from the
Expand All @@ -378,33 +417,14 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he

// Check extra data
isShillin := c.chainConfig.IsShillin(header.Number)
isTrippEffective := c.IsTrippEffective(chain, header)
extraData, err := finality.DecodeExtraV2(header.Extra, c.chainConfig, header.Number)
if err != nil {
return err
}

// Check extra data
isEpoch := number%c.config.EpochV2 == 0 || c.chainConfig.IsOnConsortiumV2(header.Number)

if !isEpoch && (len(extraData.CheckpointValidators) != 0 || len(extraData.BlockProducers) != 0) {
return consortiumCommon.ErrExtraValidators
}

if isTrippEffective {
if isEpoch && len(extraData.BlockProducers) == 0 {
return consortiumCommon.ErrExtraValidators
}
if c.IsPeriodBlock(chain, header) && len(extraData.CheckpointValidators) == 0 {
return consortiumCommon.ErrExtraValidators
}
} else {
if isEpoch && len(extraData.CheckpointValidators) == 0 {
return consortiumCommon.ErrExtraValidators
}
if len(extraData.BlockProducers) != 0 {
return consortiumCommon.ErrExtraValidators
}
err = c.verifyValidatorFieldsInExtraData(chain, extraData, header)
if err != nil {
return err
}

if isShillin && extraData.HasFinalityVote == 1 {
Expand Down Expand Up @@ -723,7 +743,10 @@ func (c *Consortium) getCheckpointValidatorsFromContract(
var checkpointValidators []finality.ValidatorWithBlsPub

if c.IsTrippEffective(chain, header) {
sort.Sort(validatorsAscending(blockProducers))
isAaron := c.chainConfig.IsAaron(header.Number)
if !isAaron {
sort.Sort(validatorsAscending(blockProducers))
}
if !c.IsPeriodBlock(chain, header) {
return nil, blockProducers, nil
}
Expand All @@ -734,6 +757,14 @@ func (c *Consortium) getCheckpointValidatorsFromContract(
if len(validatorCandidates) > MaxValidatorCandidates {
validatorCandidates = validatorCandidates[:MaxValidatorCandidates]
}

// After Aaron, bit set is used, it is necessary to keep
// the validator candidate list in a ascending order, which
// enable block producer list to be consistent after reconstruction.
if isAaron {
sort.Sort(validatorsAscending(validatorCandidates))
}

stakedAmounts, err := c.contract.GetStakedAmount(parentHash, parentBlockNumber, validatorCandidates)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -812,20 +843,39 @@ func (c *Consortium) Prepare(chain consensus.ChainHeaderReader, header *types.He
var extraData finality.HeaderExtraData

if number%c.config.EpochV2 == 0 || c.chainConfig.IsOnConsortiumV2(big.NewInt(int64(number))) {
checkpointValidator, blockProducers, err := c.getCheckpointValidatorsFromContract(chain, header)
checkpointValidators, blockProducers, err := c.getCheckpointValidatorsFromContract(chain, header)
if err != nil {
return err
}
// After Tripp, validator candidates are read only once at
// the start of new period, whereas block producer address is read
// at the start of every epoch.
// After Tripp, validator candidate list is read only once at
// the start of new period and does not change over the whole
// period; whereas block producer list is changed and read at
// the start of every new epoch.
if c.IsTrippEffective(chain, header) {
// latestValidatorCandidates is the latest validator candidate list at the
// current epoch, which is used to calculate block producer bit set later on.
var latestValidatorCandidates []finality.ValidatorWithBlsPub

if c.IsPeriodBlock(chain, header) {
extraData.CheckpointValidators = checkpointValidator
extraData.CheckpointValidators = checkpointValidators
latestValidatorCandidates = checkpointValidators
} else {
// 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
// validator candidate set from the snapshot.
latestValidatorCandidates = snap.ValidatorsWithBlsPub
}
// After Aaron, to reduce memory to store block producer list
// in header, block producer bit set is constructed to store the
// indices of block producer in validator candidate lists.
if c.chainConfig.IsAaron(header.Number) {
extraData.BlockProducersBitSet = encodeValidatorBitSet(latestValidatorCandidates, blockProducers)
} else {
extraData.BlockProducers = blockProducers
}
extraData.BlockProducers = blockProducers
} else {
extraData.CheckpointValidators = checkpointValidator
extraData.CheckpointValidators = checkpointValidators
}
}

Expand Down Expand Up @@ -941,7 +991,7 @@ func (c *Consortium) processSystemTransactions(chain consensus.ChainHeaderReader
}

func (c *Consortium) upgradeRoninTrustedOrg(blockNumber *big.Int, state *state.StateDB) {
// The upgrade only happens in 1 block: Miko hardfork block
// The upgrade only happens once at Miko hardfork block
if c.chainConfig.MikoBlock != nil && c.chainConfig.MikoBlock.Cmp(blockNumber) == 0 {
state.SetState(
c.chainConfig.RoninTrustedOrgUpgrade.ProxyAddress,
Expand All @@ -951,6 +1001,21 @@ func (c *Consortium) upgradeRoninTrustedOrg(blockNumber *big.Int, state *state.S
}
}

func (c *Consortium) upgradeTransparentProxyCode(blockNumber *big.Int, statedb *state.StateDB) {
// The transparent proxy code upgrade only happens once at Aaron hardfork block
if c.chainConfig.AaronBlock != nil && c.chainConfig.AaronBlock.Cmp(blockNumber) == 0 {
code := c.chainConfig.TransparentProxyCodeUpgrade.Code
statedb.SetCode(
c.chainConfig.TransparentProxyCodeUpgrade.AxieAddress,
code,
)
statedb.SetCode(
c.chainConfig.TransparentProxyCodeUpgrade.LandAddress,
code,
)
}
}

// Finalize implements consensus.Engine that calls three methods from smart contracts:
// - WrapUpEpoch at epoch to distribute rewards and sort the validators set
// - Slash the validator who does not sign if it is in-turn
Expand All @@ -977,6 +1042,10 @@ func (c *Consortium) Finalize(chain consensus.ChainHeaderReader, header *types.H
SignTxFn: signTxFn,
EthAPI: c.ethAPI,
}
snap, err := c.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil)
if err != nil {
return err
}

// If the block is an epoch end block, verify the validator list
// The verification can only be done when the state is ready, it can't be done in VerifyHeader.
Expand All @@ -993,13 +1062,27 @@ 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) {
if len(blockProducers) != len(extraData.BlockProducers) {
return errMismatchingValidators
}
for i := range blockProducers {
if blockProducers[i] != extraData.BlockProducers[i] {
if c.chainConfig.IsAaron(header.Number) {
if !c.IsPeriodBlock(chain, header) {
// 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
// validator candidate set from the snapshot.
checkpointValidators = snap.ValidatorsWithBlsPub
}
bitSet := encodeValidatorBitSet(checkpointValidators, blockProducers)
if bitSet != extraData.BlockProducersBitSet {
return errMismatchingValidators
}
} else {
if len(blockProducers) != len(extraData.BlockProducers) {
return errMismatchingValidators
}
for i := range blockProducers {
if blockProducers[i] != extraData.BlockProducers[i] {
return errMismatchingValidators
}
}
}
if c.IsPeriodBlock(chain, header) {
if len(checkpointValidators) != len(extraData.CheckpointValidators) {
Expand Down Expand Up @@ -1041,6 +1124,7 @@ func (c *Consortium) Finalize(chain consensus.ChainHeaderReader, header *types.H
return err
}
c.upgradeRoninTrustedOrg(header.Number, state)
c.upgradeTransparentProxyCode(header.Number, state)
if len(*transactOpts.EVMContext.InternalTransactions) > 0 {
*internalTxs = append(*internalTxs, *transactOpts.EVMContext.InternalTransactions...)
}
Expand Down Expand Up @@ -1087,7 +1171,7 @@ func (c *Consortium) FinalizeAndAssemble(chain consensus.ChainHeaderReader, head
return nil, nil, err
}
c.upgradeRoninTrustedOrg(header.Number, state)

c.upgradeTransparentProxyCode(header.Number, state)
// should not happen. Once happen, stop the node is better than broadcast the block
if header.GasLimit < header.GasUsed {
return nil, nil, errors.New("gas consumption of system txs exceed the gas limit")
Expand Down Expand Up @@ -1357,7 +1441,7 @@ func (c *Consortium) assembleFinalityVote(chain consensus.ChainHeaderReader, hea
if c.chainConfig.IsShillin(header.Number) {
var (
signatures []blsCommon.Signature
finalityVotedValidators finality.FinalityVoteBitSet
finalityVotedValidators finality.BitSet
finalityThreshold int
accumulatedVoteWeight int
)
Expand Down Expand Up @@ -1610,6 +1694,27 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) {
}
}

func encodeValidatorBitSet(validatorCandidates []finality.ValidatorWithBlsPub, blockProducers []common.Address) finality.BitSet {
var bitSet finality.BitSet
for _, validator := range blockProducers {
for idx, candidate := range validatorCandidates {
if validator == candidate.Address {
bitSet.SetBit(idx)
}
}
}
return bitSet
}

func decodeValidatorBitSet(bitSet finality.BitSet, validatorCandidates []finality.ValidatorWithBlsPub) []common.Address {
indices := bitSet.Indices()
blockProducers := make([]common.Address, len(indices))
for i, idx := range indices {
blockProducers[i] = validatorCandidates[idx].Address
}
return blockProducers
}

// getLastCheckpointHeader returns the last checkpoint header, this function is used as a fallback when we cannot
// get the snapshot to query the period number
func (c *Consortium) getLastCheckpointHeader(chain consensus.ChainHeaderReader, currentHeader *types.Header) *types.Header {
Expand All @@ -1634,10 +1739,9 @@ func (c *Consortium) getLastCheckpointHeader(chain consensus.ChainHeaderReader,
// 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) bool {
if c.testTrippPeriod {
return true
if c.isTest {
return c.testTrippPeriod
}

number := header.Number.Uint64()
if number%c.config.EpochV2 != 0 || !chain.Config().IsTripp(header.Number) {
return false
Expand All @@ -1661,6 +1765,9 @@ func (c *Consortium) IsPeriodBlock(chain consensus.ChainHeaderReader, header *ty
// which is the first period that is greater than Tripp period, calculated by formula:
// period := timestamp / dayInSeconds.
func (c *Consortium) IsTrippEffective(chain consensus.ChainHeaderReader, header *types.Header) bool {
if c.isTest {
return c.testTrippEffective
}
if c.chainConfig.IsTripp(header.Number) {
if c.testTrippEffective {
return true
Expand Down
Loading

0 comments on commit 62df14f

Please sign in to comment.