The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
woop/core/blockchain_leader_rotation.go

128 lines
3.6 KiB

package core
import (
"bytes"
"hash/crc32"
"strconv"
"strings"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
"github.com/pkg/errors"
)
// LeaderRotationMeta contains information about leader rotation
type LeaderRotationMeta struct {
Pub []byte // bls public key of previous block miner
Epoch uint64 // epoch number of previously inserted block
Count uint64 // quantity of continuous blocks inserted by the same leader
Shifts uint64 // number of leader shifts, shift happens when leader changes
}
// ShortString returns string representation of the struct
func (a LeaderRotationMeta) ShortString() string {
s := strings.Builder{}
s.Write(a.Pub[3:])
s.WriteString(" ")
s.WriteString(strconv.FormatUint(a.Epoch, 10))
s.WriteString(" ")
s.WriteString(strconv.FormatUint(a.Count, 10))
s.WriteString(" ")
s.WriteString(strconv.FormatUint(a.Shifts, 10))
return s.String()
}
// Hash returns hash of the struct
func (a LeaderRotationMeta) Hash() []byte {
c := crc32.NewIEEE()
c.Write(a.Pub)
c.Write([]byte(strconv.FormatUint(a.Epoch, 10)))
c.Write([]byte(strconv.FormatUint(a.Count, 10)))
c.Write([]byte(strconv.FormatUint(a.Shifts, 10)))
return c.Sum(nil)
}
// Clone returns a copy of the struct
func (a LeaderRotationMeta) Clone() LeaderRotationMeta {
return LeaderRotationMeta{
Pub: append([]byte{}, a.Pub...),
Epoch: a.Epoch,
Count: a.Count,
Shifts: a.Shifts,
}
}
// buildLeaderRotationMeta builds leader rotation meta if feature is activated.
func (bc *BlockChainImpl) buildLeaderRotationMeta(curHeader *block.Header) error {
if !bc.chainConfig.IsLeaderRotationInternalValidators(curHeader.Epoch()) {
return nil
}
if curHeader.NumberU64() == 0 {
return errors.New("current header is genesis")
}
curPubKey, err := bc.getLeaderPubKeyFromCoinbase(curHeader)
if err != nil {
return err
}
for i := curHeader.NumberU64() - 1; i >= 0; i-- {
header := bc.GetHeaderByNumber(i)
if header == nil {
return errors.New("header is nil")
}
blockPubKey, err := bc.getLeaderPubKeyFromCoinbase(header)
if err != nil {
return err
}
if curPubKey.Bytes != blockPubKey.Bytes || curHeader.Epoch().Uint64() != header.Epoch().Uint64() {
for j := i; j <= curHeader.NumberU64(); j++ {
header := bc.GetHeaderByNumber(j)
if header == nil {
return errors.New("header is nil")
}
err := bc.saveLeaderRotationMeta(header)
if err != nil {
utils.Logger().Error().Err(err).Msg("save leader continuous blocks count error")
return err
}
}
return nil
}
}
return errors.New("no leader rotation meta to save")
}
// saveLeaderRotationMeta saves leader rotation meta if feature is activated.
func (bc *BlockChainImpl) saveLeaderRotationMeta(h *block.Header) error {
blockPubKey, err := bc.getLeaderPubKeyFromCoinbase(h)
if err != nil {
return err
}
bc.leaderRotationMeta = processRotationMeta(h.Epoch().Uint64(), blockPubKey.Bytes, bc.leaderRotationMeta)
return nil
}
func processRotationMeta(epoch uint64, blockPubKey bls.SerializedPublicKey, s LeaderRotationMeta) LeaderRotationMeta {
// increase counter only if the same leader and epoch
if bytes.Equal(s.Pub, blockPubKey[:]) && s.Epoch == epoch {
s.Count++
} else {
s.Count = 1
}
// we should increase shifts if the leader has changed.
if !bytes.Equal(s.Pub, blockPubKey[:]) {
s.Shifts++
}
// but set to zero if new
if s.Epoch != epoch {
s.Shifts = 0
}
s.Epoch = epoch
return LeaderRotationMeta{
Pub: blockPubKey[:],
Epoch: s.Epoch,
Count: s.Count,
Shifts: s.Shifts,
}
}