Skip to content

Commit

Permalink
Activation flag HIP32.
Browse files Browse the repository at this point in the history
  • Loading branch information
Frozen committed Aug 28, 2024
1 parent 1c00737 commit 428bb20
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 26 deletions.
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 @@ -358,6 +358,7 @@ var (
big.NewInt(0), // MaxRateEpoch
big.NewInt(0), // MaxRateEpoch
big.NewInt(0),
big.NewInt(0),
}

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

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

// MaxRateEpoch will make sure the validator max-rate is at least equal to the minRate + the validator max-rate-increase
MaxRateEpoch *big.Int `json:"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 @@ -833,6 +839,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 node.IsRunningBeaconChain() {
Expand Down Expand Up @@ -386,7 +387,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
13 changes: 6 additions & 7 deletions staking/availability/measure.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package availability
import (
"math/big"

"github.com/harmony-one/harmony/core/state"

"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
Expand Down Expand Up @@ -141,9 +140,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 @@ -158,7 +155,9 @@ func ComputeCurrentSigning(
)

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

Expand Down Expand Up @@ -209,7 +208,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
69 changes: 60 additions & 9 deletions staking/availability/measure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"reflect"
"testing"

"github.com/harmony-one/harmony/crypto/bls"

"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 +188,48 @@ 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",
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)
}
}
}

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",
Expand All @@ -204,7 +245,7 @@ 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)
}
}
Expand Down Expand Up @@ -628,16 +669,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 +693,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

0 comments on commit 428bb20

Please sign in to comment.