package engine_v2 import ( "fmt" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" ) // get epoch switch of the previous `limit` epoch func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, hash common.Hash, limit int) (*utils.EpochSwitchInfo, error) { epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash) if err != nil { log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err) return nil, err } for i := 0; i < limit; i++ { epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash) if err != nil { log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err) return nil, err } } return epochSwitchInfo, nil } // Given header and its hash, get epoch switch info from the epoch switch block of that epoch, // header is allow to be nil. func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*utils.EpochSwitchInfo, error) { e, ok := x.epochSwitches.Get(hash) if ok { log.Debug("[getEpochSwitchInfo] cache hit", "hash", hash.Hex()) epochSwitchInfo := e.(*utils.EpochSwitchInfo) return epochSwitchInfo, nil } h := header if h == nil { log.Debug("[getEpochSwitchInfo] header missing, get header", "hash", hash.Hex()) h = chain.GetHeaderByHash(hash) if h == nil { log.Warn("[getEpochSwitchInfo] can not find header from db", "hash", hash.Hex()) return nil, fmt.Errorf("[getEpochSwitchInfo] can not find header from db hash %v", hash.Hex()) } } isEpochSwitch, _, err := x.IsEpochSwitch(h) if err != nil { return nil, err } if isEpochSwitch { log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64()) quorumCert, round, masternodes, err := x.getExtraFields(h) if err != nil { return nil, err } epochSwitchInfo := &utils.EpochSwitchInfo{ Masternodes: masternodes, EpochSwitchBlockInfo: &utils.BlockInfo{ Hash: hash, Number: h.Number, Round: round, }, } if quorumCert != nil { epochSwitchInfo.EpochSwitchParentBlockInfo = quorumCert.ProposedBlockInfo } x.epochSwitches.Add(hash, epochSwitchInfo) return epochSwitchInfo, nil } epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, h.ParentHash) if err != nil { log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64()) return nil, err } log.Debug("[getEpochSwitchInfo] get epoch switch info recursively", "hash", hash.Hex(), "number", h.Number.Uint64()) x.epochSwitches.Add(hash, epochSwitchInfo) return epochSwitchInfo, nil } // IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent func (x *XDPoS_v2) isEpochSwitchAtRound(round utils.Round, parentHeader *types.Header) (bool, uint64, error) { epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch if parentHeader.Number.Cmp(x.config.V2.SwitchBlock) == 0 { return true, epochNum, nil } _, round, _, err := x.getExtraFields(parentHeader) if err != nil { log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra)) return false, 0, err } parentRound := round epochStartRound := round - round%utils.Round(x.config.Epoch) return parentRound < epochStartRound, epochNum, nil }