commit
3d7a9318d1
@ -1,8 +1,127 @@ |
||||
package core |
||||
|
||||
type leaderRotationMeta struct { |
||||
pub []byte |
||||
epoch uint64 |
||||
count uint64 |
||||
shifts uint64 |
||||
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, |
||||
} |
||||
} |
||||
|
@ -0,0 +1,57 @@ |
||||
package core |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
var k1 = bls.SerializedPublicKey{1, 2, 3} |
||||
|
||||
func TestRotationMetaProcess(t *testing.T) { |
||||
t.Run("same_leader_increase_count", func(t *testing.T) { |
||||
rs := processRotationMeta(1, bls.SerializedPublicKey{}, LeaderRotationMeta{ |
||||
Pub: bls.SerializedPublicKey{}.Bytes(), |
||||
Epoch: 1, |
||||
Count: 1, |
||||
Shifts: 1, |
||||
}) |
||||
require.Equal(t, LeaderRotationMeta{ |
||||
Pub: bls.SerializedPublicKey{}.Bytes(), |
||||
Epoch: 1, |
||||
Count: 2, |
||||
Shifts: 1, |
||||
}, rs) |
||||
}) |
||||
|
||||
t.Run("new_leader_increase_shifts", func(t *testing.T) { |
||||
rs := processRotationMeta(1, k1, LeaderRotationMeta{ |
||||
Pub: bls.SerializedPublicKey{}.Bytes(), |
||||
Epoch: 1, |
||||
Count: 1, |
||||
Shifts: 1, |
||||
}) |
||||
require.Equal(t, LeaderRotationMeta{ |
||||
Pub: k1.Bytes(), |
||||
Epoch: 1, |
||||
Count: 1, |
||||
Shifts: 2, |
||||
}, rs) |
||||
}) |
||||
|
||||
t.Run("new_epoch_reset_count", func(t *testing.T) { |
||||
rs := processRotationMeta(2, k1, LeaderRotationMeta{ |
||||
Pub: bls.SerializedPublicKey{}.Bytes(), |
||||
Epoch: 1, |
||||
Count: 1, |
||||
Shifts: 1, |
||||
}) |
||||
require.Equal(t, LeaderRotationMeta{ |
||||
Pub: k1.Bytes(), |
||||
Epoch: 2, |
||||
Count: 1, |
||||
Shifts: 0, |
||||
}, rs) |
||||
}) |
||||
} |
Loading…
Reference in new issue