|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|