Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty signing power workaround. #4663

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions hmy/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func (hmy *Harmony) GetValidatorInformation(
}

computed := availability.ComputeCurrentSigning(
snapshot.Validator, wrapper,
snapshot.Validator, wrapper, bc.Config().IsHIP32(now),
)

lastBlockOfEpoch := shard.Schedule.EpochLastBlock(hmy.BeaconChain.CurrentBlock().Header().Epoch().Uint64())
Expand Down Expand Up @@ -479,7 +479,7 @@ func (hmy *Harmony) GetMedianRawStakeSnapshot() (
// Compute for next epoch
epoch := big.NewInt(0).Add(hmy.CurrentBlock().Epoch(), big.NewInt(1))
instance := shard.Schedule.InstanceForEpoch(epoch)
return committee.NewEPoSRound(epoch, hmy.BlockChain, hmy.BlockChain.Config().IsEPoSBound35(epoch), instance.SlotsLimit(), int(instance.NumShards()))
return committee.NewEPoSRound(epoch, hmy.BlockChain, instance.SlotsLimit(), int(instance.NumShards()))
},
)
if err != nil {
Expand Down Expand Up @@ -634,7 +634,7 @@ func (hmy *Harmony) GetTotalStakingSnapshot() *big.Int {
snapshot, _ := hmy.BlockChain.ReadValidatorSnapshot(candidates[i])
validator, _ := hmy.BlockChain.ReadValidatorInformation(candidates[i])
if !committee.IsEligibleForEPoSAuction(
snapshot, validator,
snapshot, validator, hmy.BlockChain.Config(),
) {
continue
}
Expand Down
10 changes: 10 additions & 0 deletions internal/params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ var (
big.NewInt(0), // MaxRateEpoch
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
}

// TestChainConfig ...
Expand Down Expand Up @@ -414,6 +415,7 @@ var (
big.NewInt(0), // MaxRateEpoch
big.NewInt(0), // MaxRateEpoch
big.NewInt(0),
big.NewInt(0),
}

// TestRules ...
Expand Down Expand Up @@ -589,6 +591,10 @@ type ChainConfig struct {

// TopMaxRateEpoch will make sure the validator max-rate is less to 100% for the cases where the minRate + the validator max-rate-increase > 100%
TopMaxRateEpoch *big.Int `json:"top-max-rate-epoch,omitempty"`

// vote power feature https://github.com/harmony-one/harmony/pull/4683
// if crosslink are not sent for an entire epoch signed and toSign will be 0 and 0. when that happen, next epoch there will no shard 1 validator elected in the committee.
HIP32Epoch *big.Int `json:"hip32-epoch,omitempty"`
}

// String implements the fmt.Stringer interface.
Expand Down Expand Up @@ -844,6 +850,10 @@ func (c *ChainConfig) IsValidatorCodeFix(epoch *big.Int) bool {
return isForked(c.ValidatorCodeFixEpoch, epoch)
}

func (c *ChainConfig) IsHIP32(epoch *big.Int) bool {
return isForked(c.HIP32Epoch, epoch)
}

func (c *ChainConfig) IsHIP30(epoch *big.Int) bool {
return isForked(c.HIP30Epoch, epoch)
}
Expand Down
3 changes: 2 additions & 1 deletion node/node_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ func getCrosslinkHeadersForShards(shardChain core.BlockChain, curBlock *types.Bl
// PostConsensusProcessing is called by consensus participants, after consensus is done, to:
// 1. [leader] send new block to the client
// 2. [leader] send cross shard tx receipts to destination shard
// newBlock is already inserted.
func (node *Node) PostConsensusProcessing(newBlock *types.Block) error {
if node.Consensus.IsLeader() {
if IsRunningBeaconChain(node.Consensus) {
Expand Down Expand Up @@ -394,7 +395,7 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block) error {
return nil
}
computed := availability.ComputeCurrentSigning(
snapshot.Validator, wrapper,
snapshot.Validator, wrapper, node.Beaconchain().Config().IsHIP32(newBlock.Epoch()),
)
lastBlockOfEpoch := shard.Schedule.EpochLastBlock(node.Beaconchain().CurrentBlock().Header().Epoch().Uint64())

Expand Down
13 changes: 7 additions & 6 deletions shard/committee/assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type StakingCandidatesReader interface {
) (*staking.ValidatorWrapper, error)
ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorSnapshot, error)
ValidatorCandidates() []common.Address
Config() *params.ChainConfig
}

// CandidatesForEPoS ..
Expand Down Expand Up @@ -73,7 +74,7 @@ func (p CandidateOrder) MarshalJSON() ([]byte, error) {

// NewEPoSRound runs a fresh computation of EPoS using
// latest data always
func NewEPoSRound(epoch *big.Int, stakedReader StakingCandidatesReader, isExtendedBound bool, slotsLimit, shardCount int) (
func NewEPoSRound(epoch *big.Int, stakedReader StakingCandidatesReader, slotsLimit, shardCount int) (
*CompletedEPoSRound, error,
) {
eligibleCandidate, err := prepareOrders(stakedReader, slotsLimit, shardCount)
Expand All @@ -84,7 +85,7 @@ func NewEPoSRound(epoch *big.Int, stakedReader StakingCandidatesReader, isExtend
epoch,
)
median, winners := effective.Apply(
eligibleCandidate, maxExternalSlots, isExtendedBound,
eligibleCandidate, maxExternalSlots, stakedReader.Config().IsEPoSBound35(epoch),
)
auctionCandidates := make([]*CandidateOrder, len(eligibleCandidate))

Expand Down Expand Up @@ -159,7 +160,7 @@ func prepareOrders(
if err != nil {
return nil, err
}
if !IsEligibleForEPoSAuction(snapshot, validator) {
if !IsEligibleForEPoSAuction(snapshot, validator, stakedReader.Config()) {
continue
}

Expand Down Expand Up @@ -208,7 +209,7 @@ func prepareOrders(
}

// IsEligibleForEPoSAuction ..
func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *staking.ValidatorWrapper) bool {
func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *staking.ValidatorWrapper, config *params.ChainConfig) bool {
// This original condition to check whether a validator is in last committee is not stable
// because cross-links may arrive after the epoch ends and it still got counted into the
// NumBlocksToSign, making this condition to be true when the validator is actually not in committee
Expand All @@ -219,7 +220,7 @@ func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *st
// validator was in last epoch's committee
// validator with below-threshold signing activity won't be considered for next epoch
// and their status will be turned to inactive in FinalizeNewBlock
computed := availability.ComputeCurrentSigning(snapshot.Validator, validator)
computed := availability.ComputeCurrentSigning(snapshot.Validator, validator, config.IsHIP32(snapshot.Epoch))
if computed.IsBelowThreshold {
return false
}
Expand Down Expand Up @@ -349,7 +350,7 @@ func eposStakedCommittee(
}

// TODO(audit): make sure external validator BLS key are also not duplicate to Harmony's keys
completedEPoSRound, err := NewEPoSRound(epoch, stakerReader, stakerReader.Config().IsEPoSBound35(epoch), s.SlotsLimit(), shardCount)
completedEPoSRound, err := NewEPoSRound(epoch, stakerReader, s.SlotsLimit(), shardCount)

if err != nil {
return nil, err
Expand Down
2 changes: 2 additions & 0 deletions staking/availability/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/internal/params"
staking "github.com/harmony-one/harmony/staking/types"
)

Expand All @@ -12,6 +13,7 @@ type Reader interface {
ReadValidatorSnapshot(
addr common.Address,
) (*staking.ValidatorSnapshot, error)
Config() *params.ChainConfig
}

// RoundHeader is the interface of block.Header for calculating the BallotResult.
Expand Down
9 changes: 5 additions & 4 deletions staking/availability/measure.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ func IncrementValidatorSigningCounts(
}

// ComputeCurrentSigning returns (signed, toSign, quotient, error)
func ComputeCurrentSigning(
snapshot, wrapper *staking.ValidatorWrapper,
) *staking.Computed {
func ComputeCurrentSigning(snapshot, wrapper *staking.ValidatorWrapper, isHip32 bool) *staking.Computed {
statsNow, snapSigned, snapToSign :=
wrapper.Counters,
snapshot.Counters.NumBlocksSigned,
Expand All @@ -155,6 +153,9 @@ func ComputeCurrentSigning(
)

if toSign.Cmp(common.Big0) == 0 {
if isHip32 {
computed.IsBelowThreshold = false
}
return computed
}

Expand Down Expand Up @@ -205,7 +206,7 @@ func ComputeAndMutateEPOSStatus(
return err
}

computed := ComputeCurrentSigning(snapshot.Validator, wrapper)
computed := ComputeCurrentSigning(snapshot.Validator, wrapper, bc.Config().IsHIP32(snapshot.Epoch))

utils.Logger().
Info().Msg("check if signing percent is meeting required threshold")
Expand Down
67 changes: 60 additions & 7 deletions staking/availability/measure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/effective"
Expand Down Expand Up @@ -188,7 +189,7 @@ func TestComputeCurrentSigning(t *testing.T) {
snapWrapper := makeTestWrapper(common.Address{}, test.snapSigned, test.snapToSign)
curWrapper := makeTestWrapper(common.Address{}, test.curSigned, test.curToSign)

computed := ComputeCurrentSigning(&snapWrapper, &curWrapper)
computed := ComputeCurrentSigning(&snapWrapper, &curWrapper, false)

if computed.Signed.Cmp(new(big.Int).SetInt64(test.diffSigned)) != 0 {
t.Errorf("test %v: computed signed not expected: %v / %v",
Expand All @@ -204,7 +205,48 @@ func TestComputeCurrentSigning(t *testing.T) {
i, computed.Percentage, expPct)
}
if computed.IsBelowThreshold != test.isBelowThreshold {
t.Errorf("test %v: computed is below threshold not expected: %v / %v",
t.Errorf("test %v: computed is below threshold `%v` expected: `%v`",
i, computed.IsBelowThreshold, test.isBelowThreshold)
}
}
}

func TestComputeCurrentSigningHIP32(t *testing.T) {
tests := []struct {
snapSigned, curSigned, diffSigned int64
snapToSign, curToSign, diffToSign int64
pctNum, pctDiv int64
isBelowThreshold bool
}{
{0, 0, 0, 0, 0, 0, 0, 1, false},
{0, 1, 1, 0, 1, 1, 1, 1, false},
{0, 2, 2, 0, 3, 3, 2, 3, true},
{0, 1, 1, 0, 3, 3, 1, 3, true},
{100, 225, 125, 200, 350, 150, 5, 6, false},
{100, 200, 100, 200, 350, 150, 2, 3, true},
{100, 200, 100, 200, 400, 200, 1, 2, true},
}
for i, test := range tests {
snapWrapper := makeTestWrapper(common.Address{}, test.snapSigned, test.snapToSign)
curWrapper := makeTestWrapper(common.Address{}, test.curSigned, test.curToSign)

computed := ComputeCurrentSigning(&snapWrapper, &curWrapper, true)

if computed.Signed.Cmp(new(big.Int).SetInt64(test.diffSigned)) != 0 {
t.Errorf("test %v: computed signed not expected: %v / %v",
i, computed.Signed, test.diffSigned)
}
if computed.ToSign.Cmp(new(big.Int).SetInt64(test.diffToSign)) != 0 {
t.Errorf("test %v: computed to sign not expected: %v / %v",
i, computed.ToSign, test.diffToSign)
}
expPct := numeric.NewDec(test.pctNum).Quo(numeric.NewDec(test.pctDiv))
if !computed.Percentage.Equal(expPct) {
t.Errorf("test %v: computed percentage not expected: %v / %v",
i, computed.Percentage, expPct)
}
if computed.IsBelowThreshold != test.isBelowThreshold {
t.Errorf("test %v: computed is below threshold `%v`, expected: `%v`",
i, computed.IsBelowThreshold, test.isBelowThreshold)
}
}
Expand Down Expand Up @@ -628,16 +670,22 @@ func (state testStateDB) GetCode(addr common.Address, isValidatorCode bool) []by
}

// testReader is the fake Reader for testing
type testReader map[common.Address]staking.ValidatorWrapper
type testReader struct {
m map[common.Address]staking.ValidatorWrapper
config *params.ChainConfig
}

// newTestReader creates an empty test reader
func newTestReader() testReader {
reader := make(testReader)
return reader
m := make(map[common.Address]staking.ValidatorWrapper)
return testReader{
m: m,
config: &params.ChainConfig{},
}
}

func (reader testReader) ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorSnapshot, error) {
wrapper, ok := reader[addr]
wrapper, ok := reader.m[addr]
if !ok {
return nil, errors.New("not a valid validator address")
}
Expand All @@ -646,8 +694,12 @@ func (reader testReader) ReadValidatorSnapshot(addr common.Address) (*staking.Va
}, nil
}

func (reader testReader) Config() *params.ChainConfig {
return reader.config
}

func (reader testReader) updateValidatorWrapper(addr common.Address, val *staking.ValidatorWrapper) {
reader[addr] = *val
reader.m[addr] = *val
}

func makeTestShardState(numShards, numSlots int) *shard.State {
Expand Down Expand Up @@ -724,6 +776,7 @@ func indexesToBitMap(idxs []int, n int) ([]byte, error) {
return res, nil
}

// makeTestWrapper makes test wrapper
func makeTestWrapper(addr common.Address, numSigned, numToSign int64) staking.ValidatorWrapper {
var val staking.ValidatorWrapper
val.Address = addr
Expand Down