[staking][reward] Give out block reward for cross links (#1869)

* [staking][reward] Give out block reward for cross links

* [staking][reward] Wrap reward logic based on epoch

* [votepower] Factor out votepower b/c need to compute elsewhere as well

* [reward][votepower] Award beaconchain committee members their percent due

* [reward] Only reward non-harmony slots

* [reward] Factor out only signers by commit bitmap

* [reward][votepower] Fix leftover merge based naming changes, fix nil pointer map

* [reward] Handle cross link payment succicently

* [reward] Remove unnecessary helper

* [reward] Handle legacy case of one-node-one-vote block reward (pre-staking)

* [reward] Incorrect log of reward

* [reward] Adjust back to big.Int for legacy rewarding

* [reward] Add +1 for i nonce

* [reward] Abandon formula for nonce, sizes not reliable, easier to do explicit bucketing

* [reward] Remove Prints, fix mistake on using wrong header

* [quorum] Adjust log print out for IsQuorumAchieved

* [reward] Redundant check in code path that only runs in >= staking-epoch

* [reward] Fix mistake on staked block undoing effect of voting power-sharing
pull/1874/head
Edgar Aroutiounian 5 years ago committed by GitHub
parent 98eee91ecc
commit 1cf258ee06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      consensus/engine/consensus_engine.go
  2. 8
      consensus/quorum/one-node-one-vote.go
  3. 193
      consensus/quorum/one-node-staked-vote.go
  4. 8
      consensus/quorum/quorum.go
  5. 7
      consensus/reward/rewarder.go
  6. 93
      consensus/votepower/roster.go
  7. 20
      internal/chain/engine.go
  8. 314
      internal/chain/reward.go
  9. 8
      node/node.go

@ -94,13 +94,21 @@ type Engine interface {
// SetSlasher assigns the slasher used
SetSlasher(slash.Slasher)
// Beaconchain provides the handle for Beaconchain
Beaconchain() ChainReader
// SetBeaconchain sets the beaconchain handler on engine
SetBeaconchain(ChainReader)
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *block.Header, state *state.DB, txs []*types.Transaction,
Finalize(chain ChainReader, header *block.Header,
state *state.DB, txs []*types.Transaction,
receipts []*types.Receipt, outcxs []*types.CXReceipt,
incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction) (*types.Block, error)
incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction,
) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.

@ -27,7 +27,7 @@ func (v *uniformVoteWeight) IsQuorumAchieved(p Phase) bool {
r := v.SignersCount(p) >= v.TwoThirdsSignersCount()
utils.Logger().Info().Str("phase", p.String()).
Int64("signers-count", v.SignersCount(p)).
Int64("threshold", v.QuorumThreshold().Int64()).
Int64("threshold", v.TwoThirdsSignersCount()).
Int64("participants", v.ParticipantsCount()).
Msg("Quorum details")
return r
@ -60,7 +60,9 @@ func (v *uniformVoteWeight) ToggleActive(*bls.PublicKey) bool {
func (v *uniformVoteWeight) Award(
// Here hook is the callback which gets the amount the earner is due in just reward
// up to the hook to do side-effects like write the statedb
Pie *big.Int, earners []common.Address, hook func(earner common.Address, due *big.Int),
Pie *big.Int,
earners shard.SlotList,
hook func(earner common.Address, due *big.Int),
) *big.Int {
payout := big.NewInt(0)
last := big.NewInt(0)
@ -70,7 +72,7 @@ func (v *uniformVoteWeight) Award(
cur := big.NewInt(0)
cur.Mul(Pie, big.NewInt(int64(i+1))).Div(cur, count)
diff := big.NewInt(0).Sub(cur, last)
hook(common.Address(account), diff)
hook(common.Address(account.EcdsaAddress), diff)
payout = big.NewInt(0).Add(payout, diff)
last = cur
}

@ -2,10 +2,9 @@ package quorum
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/votepower"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
@ -16,8 +15,6 @@ import (
var (
twoThird = numeric.NewDec(2).Quo(numeric.NewDec(3))
ninetyPercent = numeric.MustNewDecFromStr("0.90")
harmonysShare = numeric.MustNewDecFromStr("0.68")
stakersShare = numeric.MustNewDecFromStr("0.32")
totalShare = numeric.MustNewDecFromStr("1.00")
)
@ -31,23 +28,12 @@ type TallyResult struct {
theirPercent numeric.Dec
}
type stakedVoter struct {
isActive, isHarmonyNode bool
earningAccount common.Address
effectivePercent numeric.Dec
rawStake numeric.Dec
}
type stakedVoteWeight struct {
SignatureReader
DependencyInjectionWriter
DependencyInjectionReader
slash.ThresholdDecider
voters map[shard.BlsPublicKey]stakedVoter
ourVotingPowerTotal numeric.Dec
theirVotingPowerTotal numeric.Dec
stakedTotal numeric.Dec
hmySlotCount int64
roster votepower.Roster
}
// Policy ..
@ -58,7 +44,14 @@ func (v *stakedVoteWeight) Policy() Policy {
// IsQuorumAchieved ..
func (v *stakedVoteWeight) IsQuorumAchieved(p Phase) bool {
t := v.QuorumThreshold()
currentTotalPower := v.computeCurrentTotalPower(p)
currentTotalPower, err := v.computeCurrentTotalPower(p)
if err != nil {
utils.Logger().Error().
AnErr("bls error", err).
Msg("Failure in attempt bls-key reading")
return false
}
utils.Logger().Info().
Str("policy", v.Policy().String()).
@ -69,27 +62,24 @@ func (v *stakedVoteWeight) IsQuorumAchieved(p Phase) bool {
return currentTotalPower.GT(t)
}
func (v *stakedVoteWeight) computeCurrentTotalPower(p Phase) numeric.Dec {
func (v *stakedVoteWeight) computeCurrentTotalPower(p Phase) (*numeric.Dec, error) {
w := shard.BlsPublicKey{}
members := v.Participants()
currentTotalPower := numeric.ZeroDec()
for i := range members {
if v.ReadSignature(p, members[i]) != nil {
w.FromLibBLSPublicKey(members[i])
voter, ok := v.voters[w]
if !ok {
utils.Logger().Error().Bytes("BlsPubKey", w[:]).Msg("No voter found")
return numeric.ZeroDec()
err := w.FromLibBLSPublicKey(members[i])
if err != nil {
return nil, err
}
currentTotalPower = currentTotalPower.Add(
voter.effectivePercent,
v.roster.Voters[w].EffectivePercent,
)
}
}
return currentTotalPower
return &currentTotalPower, nil
}
// QuorumThreshold ..
@ -99,43 +89,52 @@ func (v *stakedVoteWeight) QuorumThreshold() numeric.Dec {
// RewardThreshold ..
func (v *stakedVoteWeight) IsRewardThresholdAchieved() bool {
return v.computeCurrentTotalPower(Commit).GTE(ninetyPercent)
reached, err := v.computeCurrentTotalPower(Commit)
if err != nil {
utils.Logger().Error().
AnErr("bls error", err).
Msg("Failure in attempt bls-key reading")
return false
}
return reached.GTE(ninetyPercent)
}
// Award ..
func (v *stakedVoteWeight) Award(
Pie *big.Int, earners []common.Address, hook func(earner common.Address, due *big.Int),
) *big.Int {
payout := big.NewInt(0)
last := big.NewInt(0)
count := big.NewInt(int64(len(earners)))
// proportional := map[common.Address]numeric.Dec{}
for _, voter := range v.voters {
if voter.isHarmonyNode == false {
// proportional[details.earningAccount] = details.effective.QuoTruncate(
// v.stakedTotal,
// )
}
}
// TODO Finish implementing this logic w/Chao
// func (v *stakedVoteWeight) Award(
// Pie numeric.Dec,
// earners []common.Address,
// hook func(earner common.Address, due *big.Int),
// ) numeric.Dec {
// payout := big.NewInt(0)
// last := big.NewInt(0)
// count := big.NewInt(int64(len(earners)))
// // proportional := map[common.Address]numeric.Dec{}
for i := range earners {
cur := big.NewInt(0)
// for _, voter := range v.voters {
// if voter.isHarmonyNode == false {
// // proportional[details.earningAccount] = details.effective.QuoTruncate(
// // v.stakedTotal,
// // )
// }
// }
// // TODO Finish implementing this logic w/Chao
cur.Mul(Pie, big.NewInt(int64(i+1))).Div(cur, count)
// for i := range earners {
// cur := big.NewInt(0)
diff := big.NewInt(0).Sub(cur, last)
// cur.Mul(Pie, big.NewInt(int64(i+1))).Div(cur, count)
// hook(common.Address(account), diff)
// diff := big.NewInt(0).Sub(cur, last)
payout = big.NewInt(0).Add(payout, diff)
// // hook(common.Address(account), diff)
last = cur
}
// payout = big.NewInt(0).Add(payout, diff)
return payout
}
// last = cur
// }
// return payout
// }
var (
errSumOfVotingPowerNotOne = errors.New("sum of total votes do not sum to 100 percent")
@ -148,76 +147,37 @@ func (v *stakedVoteWeight) SetVoters(
staked shard.SlotList,
) (*TallyResult, error) {
s, _ := v.ShardIDProvider()()
v.voters = map[shard.BlsPublicKey]stakedVoter{}
v.Reset([]Phase{Prepare, Commit, ViewChange})
v.hmySlotCount = 0
v.stakedTotal = numeric.ZeroDec()
for i := range staked {
if staked[i].TotalStake == nil {
v.hmySlotCount++
} else {
v.stakedTotal = v.stakedTotal.Add(*staked[i].TotalStake)
}
}
ourCount := numeric.NewDec(v.hmySlotCount)
ourPercentage := numeric.ZeroDec()
theirPercentage := numeric.ZeroDec()
totalStakedPercent := numeric.ZeroDec()
for i := range staked {
member := stakedVoter{
isActive: true,
isHarmonyNode: true,
earningAccount: staked[i].EcdsaAddress,
effectivePercent: numeric.ZeroDec(),
}
// Real Staker
if staked[i].TotalStake != nil {
member.isHarmonyNode = false
member.effectivePercent = staked[i].TotalStake.
Quo(v.stakedTotal).
Mul(stakersShare)
theirPercentage = theirPercentage.Add(member.effectivePercent)
} else { // Our node
member.effectivePercent = harmonysShare.Quo(ourCount)
ourPercentage = ourPercentage.Add(member.effectivePercent)
}
totalStakedPercent = totalStakedPercent.Add(member.effectivePercent)
v.voters[staked[i].BlsPublicKey] = member
}
roster := votepower.Compute(staked)
utils.Logger().Info().
Str("our-percentage", ourPercentage.String()).
Str("their-percentage", theirPercentage.String()).
Str("our-percentage", roster.OurVotingPowerTotalPercentage.String()).
Str("their-percentage", roster.TheirVotingPowerTotalPercentage.String()).
Uint32("on-shard", s).
Str("Raw-Staked", v.stakedTotal.String()).
Str("Raw-Staked", roster.RawStakedTotal.String()).
Msg("Total staked")
//switch {
//case totalStakedPercent.Equal(totalShare) == false:
//case roster.totalStakedPercent.Equal(totalShare) == false:
// return nil, errSumOfVotingPowerNotOne
//case ourPercentage.Add(theirPercentage).Equal(totalShare) == false:
//case roster.ourPercentage.Add(theirPercentage).Equal(totalShare) == false:
// return nil, errSumOfOursAndTheirsNotOne
//}
// Hold onto this calculation
v.ourVotingPowerTotal = ourPercentage
v.theirVotingPowerTotal = theirPercentage
return &TallyResult{ourPercentage, theirPercentage}, nil
v.roster = *roster
return &TallyResult{
roster.OurVotingPowerTotalPercentage, roster.TheirVotingPowerTotalPercentage,
}, nil
}
func (v *stakedVoteWeight) ToggleActive(k *bls.PublicKey) bool {
w := shard.BlsPublicKey{}
w.FromLibBLSPublicKey(k)
g := v.voters[w]
g.isActive = !g.isActive
v.voters[w] = g
return v.voters[w].isActive
g := v.roster.Voters[w]
g.IsActive = !g.IsActive
v.roster.Voters[w] = g
return v.roster.Voters[w].IsActive
}
func (v *stakedVoteWeight) ShouldSlash(key shard.BlsPublicKey) bool {
@ -232,6 +192,7 @@ func (v *stakedVoteWeight) ShouldSlash(key shard.BlsPublicKey) bool {
func (v *stakedVoteWeight) JSON() string {
s, _ := v.ShardIDProvider()()
voterCount := len(v.roster.Voters)
type u struct {
IsHarmony bool `json:"is-harmony-slot"`
@ -250,18 +211,18 @@ func (v *stakedVoteWeight) JSON() string {
TotalStaked string `json:"total-raw-staked"`
}
parts := make([]u, len(v.voters))
parts := make([]u, voterCount)
i := 0
for identity, voter := range v.voters {
for identity, voter := range v.roster.Voters {
member := u{
voter.isHarmonyNode,
voter.IsHarmonyNode,
identity.Hex(),
voter.effectivePercent.String(),
voter.EffectivePercent.String(),
"",
}
if !voter.isHarmonyNode {
member.RawStake = voter.rawStake.String()
if !voter.IsHarmonyNode {
member.RawStake = voter.RawStake.String()
}
parts[i] = member
i++
@ -270,11 +231,11 @@ func (v *stakedVoteWeight) JSON() string {
b1, _ := json.Marshal(t{
v.Policy().String(),
s,
len(v.voters),
voterCount,
parts,
v.ourVotingPowerTotal.String(),
v.theirVotingPowerTotal.String(),
v.stakedTotal.String(),
v.roster.OurVotingPowerTotalPercentage.String(),
v.roster.TheirVotingPowerTotalPercentage.String(),
v.roster.RawStakedTotal.String(),
})
return string(b1)
}

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/votepower"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/slash"
@ -296,16 +297,13 @@ func NewDecider(p Policy) Decider {
c.DependencyInjectionWriter, c.DependencyInjectionReader, c,
}
case SuperMajorityStake:
roster := votepower.NewRoster()
return &stakedVoteWeight{
c.SignatureReader,
c.DependencyInjectionWriter,
c.DependencyInjectionWriter.(DependencyInjectionReader),
c.SignatureReader.(slash.ThresholdDecider),
map[shard.BlsPublicKey]stakedVoter{},
numeric.ZeroDec(),
numeric.ZeroDec(),
numeric.ZeroDec(),
0,
*roster,
}
default:
// Should not be possible

@ -4,13 +4,14 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/shard"
)
// Distributor ..
type Distributor interface {
Award(
Pie *big.Int,
earners []common.Address,
pie *big.Int,
earners shard.SlotList,
hook func(earner common.Address, due *big.Int),
) (payout *big.Int)
) *big.Int
}

@ -0,0 +1,93 @@
package votepower
import (
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
var (
// HarmonysShare ..
HarmonysShare = numeric.MustNewDecFromStr("0.68")
// StakersShare ..
StakersShare = numeric.MustNewDecFromStr("0.32")
)
type stakedVoter struct {
IsActive, IsHarmonyNode bool
EarningAccount common.Address
EffectivePercent numeric.Dec
RawStake numeric.Dec
}
// Roster ..
type Roster struct {
Voters map[shard.BlsPublicKey]stakedVoter
OurVotingPowerTotalPercentage numeric.Dec
TheirVotingPowerTotalPercentage numeric.Dec
RawStakedTotal numeric.Dec
HmySlotCount int64
}
// Compute ..
func Compute(staked shard.SlotList) *Roster {
roster := NewRoster()
for i := range staked {
if staked[i].TotalStake == nil {
roster.HmySlotCount++
} else {
roster.RawStakedTotal = roster.RawStakedTotal.Add(
*staked[i].TotalStake,
)
}
}
ourCount := numeric.NewDec(roster.HmySlotCount)
ourPercentage := numeric.ZeroDec()
theirPercentage := numeric.ZeroDec()
totalStakedPercent := numeric.ZeroDec()
for i := range staked {
member := stakedVoter{
IsActive: true,
IsHarmonyNode: true,
EarningAccount: staked[i].EcdsaAddress,
EffectivePercent: numeric.ZeroDec(),
}
// Real Staker
if staked[i].TotalStake != nil {
member.IsHarmonyNode = false
member.EffectivePercent = staked[i].TotalStake.
Quo(roster.RawStakedTotal).
Mul(StakersShare)
theirPercentage = theirPercentage.Add(member.EffectivePercent)
} else { // Our node
// TODO See the todo on where this called in one-node-staked-vote,
// need to have these two values of our
// percentage and hmy percentage sum to 1
member.EffectivePercent = HarmonysShare.Quo(ourCount)
ourPercentage = ourPercentage.Add(member.EffectivePercent)
}
totalStakedPercent = totalStakedPercent.Add(member.EffectivePercent)
roster.Voters[staked[i].BlsPublicKey] = member
}
roster.OurVotingPowerTotalPercentage = ourPercentage
roster.TheirVotingPowerTotalPercentage = theirPercentage
return roster
}
// NewRoster ..
func NewRoster() *Roster {
return &Roster{
map[shard.BlsPublicKey]stakedVoter{},
numeric.ZeroDec(),
numeric.ZeroDec(),
numeric.ZeroDec(),
0,
}
}

@ -23,12 +23,13 @@ import (
)
type engineImpl struct {
d reward.Distributor
s slash.Slasher
d reward.Distributor
s slash.Slasher
beacon engine.ChainReader
}
// Engine is an algorithm-agnostic consensus engine.
var Engine = &engineImpl{nil, nil}
var Engine = &engineImpl{nil, nil, nil}
// Rewarder handles the distribution of block rewards
func (e *engineImpl) Rewarder() reward.Distributor {
@ -50,6 +51,15 @@ func (e *engineImpl) SetSlasher(s slash.Slasher) {
e.s = s
}
func (e *engineImpl) Beaconchain() engine.ChainReader {
return e.beacon
}
// SetSlasher assigns the slasher used
func (e *engineImpl) SetBeaconchain(beaconchain engine.ChainReader) {
e.beacon = beaconchain
}
// SealHash returns the hash of a block prior to it being sealed.
func (e *engineImpl) SealHash(header *block.Header) (hash common.Hash) {
hasher := sha3.NewLegacyKeccak256()
@ -182,14 +192,16 @@ func (e *engineImpl) Finalize(
receipts []*types.Receipt, outcxs []*types.CXReceipt,
incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction,
) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root
// Header seems complete, assemble into a block and return
if err := AccumulateRewards(
chain, state, header, e.Rewarder(), e.Slasher(),
chain, state, header, e.Rewarder(), e.Slasher(), e.Beaconchain(),
); err != nil {
return nil, ctxerror.New("cannot pay block reward").WithCause(err)
}
// TODO Shouldnt this logic only apply to beaconchain, right?
// Withdraw unlocked tokens to the delegators' accounts
// Only do such at the last block of an epoch
if len(header.ShardState()) > 0 {

@ -2,19 +2,24 @@ package chain
import (
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/consensus/reward"
"github.com/harmony-one/harmony/consensus/votepower"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
bls2 "github.com/harmony-one/harmony/crypto/bls"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/slash"
"github.com/pkg/errors"
@ -22,97 +27,254 @@ import (
var (
// BlockReward is the block reward, to be split evenly among block signers.
BlockReward = new(big.Int).Mul(big.NewInt(24), big.NewInt(denominations.One))
BlockReward = new(big.Int).Mul(big.NewInt(24), big.NewInt(denominations.One))
// BlockRewardStakedCase is the baseline block reward in staked case -
BlockRewardStakedCase = numeric.NewDecFromBigInt(new(big.Int).Mul(
big.NewInt(18), big.NewInt(denominations.One),
))
errPayoutNotEqualBlockReward = errors.New("total payout not equal to blockreward")
)
// AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func AccumulateRewards(
bc engine.ChainReader, state *state.DB,
header *block.Header, rewarder reward.Distributor,
slasher slash.Slasher,
) error {
blockNum := header.Number().Uint64()
if blockNum == 0 {
// Epoch block has no parent to reward.
return nil
}
// TODO ek – retrieving by parent number (blockNum - 1) doesn't work,
// while it is okay with hash. Sounds like DB inconsistency.
// Figure out why.
parentHeader := bc.GetHeaderByHash(header.ParentHash())
if parentHeader == nil {
return ctxerror.New("cannot find parent block header in DB",
"parentHash", header.ParentHash())
}
if parentHeader.Number().Cmp(common.Big0) == 0 {
// Parent is an epoch block,
// which is not signed in the usual manner therefore rewards nothing.
return nil
}
parentShardState, err := bc.ReadShardState(parentHeader.Epoch())
if err != nil {
return ctxerror.New("cannot read shard state",
"epoch", parentHeader.Epoch(),
).WithCause(err)
}
parentCommittee := parentShardState.FindCommitteeByID(parentHeader.ShardID())
if parentCommittee == nil {
return ctxerror.New("cannot find shard in the shard state",
"parentBlockNumber", parentHeader.Number(),
"shardID", parentHeader.ShardID(),
)
}
var committerKeys []*bls.PublicKey
func blockSigners(
header *block.Header, parentCommittee *shard.Committee,
) (shard.SlotList, shard.SlotList, error) {
committerKeys := []*bls.PublicKey{}
for _, member := range parentCommittee.Slots {
committerKey := new(bls.PublicKey)
err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil {
return ctxerror.New("cannot convert BLS public key",
"blsPublicKey", member.BlsPublicKey).WithCause(err)
return nil, nil, ctxerror.New(
"cannot convert BLS public key",
"blsPublicKey",
member.BlsPublicKey,
).WithCause(err)
}
committerKeys = append(committerKeys, committerKey)
}
mask, err := bls2.NewMask(committerKeys, nil)
if err != nil {
return ctxerror.New("cannot create group sig mask").WithCause(err)
return nil, nil, ctxerror.New(
"cannot create group sig mask",
).WithCause(err)
}
if err := mask.SetMask(header.LastCommitBitmap()); err != nil {
return ctxerror.New("cannot set group sig mask bits").WithCause(err)
return nil, nil, ctxerror.New(
"cannot set group sig mask bits",
).WithCause(err)
}
accounts := []common.Address{}
missing := shard.SlotList{}
payable, missing := shard.SlotList{}, shard.SlotList{}
for idx, member := range parentCommittee.Slots {
switch signed, err := mask.IndexEnabled(idx); true {
case err != nil:
return ctxerror.New("cannot check for committer bit",
return nil, nil, ctxerror.New("cannot check for committer bit",
"committerIndex", idx,
).WithCause(err)
case signed:
accounts = append(accounts, member.EcdsaAddress)
payable = append(payable, member)
default:
missing = append(missing, member)
}
}
return payable, missing, nil
}
// do it quickly
w := sync.WaitGroup{}
for i := range missing {
w.Add(1)
go func(member int) {
defer w.Done()
// Slash if missing block was long enough
if slasher.ShouldSlash(missing[member].BlsPublicKey) {
// TODO Logic
}
}(i)
func ballotResult(
bc engine.ChainReader, header *block.Header, shardID uint32,
) (shard.SlotList, shard.SlotList, shard.SlotList, error) {
// TODO ek – retrieving by parent number (blockNum - 1) doesn't work,
// while it is okay with hash. Sounds like DB inconsistency.
// Figure out why.
parentHeader := bc.GetHeaderByHash(header.ParentHash())
if parentHeader == nil {
return nil, nil, nil, ctxerror.New(
"cannot find parent block header in DB",
"parentHash", header.ParentHash(),
)
}
parentShardState, err := bc.ReadShardState(parentHeader.Epoch())
if err != nil {
return nil, nil, nil, ctxerror.New(
"cannot read shard state", "epoch", parentHeader.Epoch(),
).WithCause(err)
}
parentCommittee := parentShardState.FindCommitteeByID(shardID)
if parentCommittee == nil {
return nil, nil, nil, ctxerror.New(
"cannot find shard in the shard state",
"parentBlockNumber", parentHeader.Number(),
"shardID", parentHeader.ShardID(),
)
}
w.Wait()
payable, missing, err := blockSigners(header, parentCommittee)
return parentCommittee.Slots, payable, missing, err
}
func ballotResultBeaconchain(
bc engine.ChainReader, header *block.Header,
) (shard.SlotList, shard.SlotList, shard.SlotList, error) {
return ballotResult(bc, header, shard.BeaconChainShardID)
}
// AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func AccumulateRewards(
bc engine.ChainReader, state *state.DB, header *block.Header,
rewarder reward.Distributor, slasher slash.Slasher,
beaconChain engine.ChainReader,
) error {
blockNum := header.Number().Uint64()
if blockNum == 0 {
// genesis block has no parent to reward.
return nil
}
if bc.Config().IsStaking(header.Epoch()) &&
bc.CurrentHeader().ShardID() != shard.BeaconChainShardID {
return nil
}
if bc.Config().IsStaking(header.Epoch()) &&
bc.CurrentHeader().ShardID() == shard.BeaconChainShardID {
// Take care of my own beacon chain committee, _ is missing, for slashing
members, payable, _, err := ballotResultBeaconchain(beaconChain, header)
if err != nil {
return err
}
votingPower := votepower.Compute(members)
for beaconMember := range payable {
// TODO Give out whatever leftover to the last voter/handle
// what to do about share of those that didn't sign
voter := votingPower.Voters[payable[beaconMember].BlsPublicKey]
if !voter.IsHarmonyNode {
due := BlockRewardStakedCase.Mul(
voter.EffectivePercent.Quo(votepower.StakersShare),
)
state.AddBalance(voter.EarningAccount, due.RoundInt())
}
}
// Handle rewards for shardchain
if cxLinks := header.CrossLinks(); len(cxLinks) != 0 {
crossLinks := types.CrossLinks{}
err := rlp.DecodeBytes(cxLinks, &crossLinks)
if err != nil {
return err
}
w := sync.WaitGroup{}
type slotPayable struct {
effective numeric.Dec
payee common.Address
bucket int
index int
oops error
}
payable := make(chan slotPayable)
slotError := func(err error, receive chan slotPayable) {
s := slotPayable{}
s.oops = err
go func() {
receive <- s
}()
}
for i := range crossLinks {
w.Add(1)
go func(i int) {
defer w.Done()
cxLink := crossLinks[i]
subCommittee := shard.State{}
if err := rlp.DecodeBytes(
cxLink.ChainHeader.ShardState(), &subCommittee,
); err != nil {
slotError(err, payable)
return
}
subComm := subCommittee.FindCommitteeByID(cxLink.ShardID())
// _ are the missing signers, later for slashing
payableSigners, _, err := blockSigners(cxLink.Header(), subComm)
votingPower := votepower.Compute(subComm.Slots)
if err != nil {
slotError(err, payable)
return
}
for member := range payableSigners {
voter := votingPower.Voters[payableSigners[member].BlsPublicKey]
if !voter.IsHarmonyNode {
due := BlockRewardStakedCase.Mul(
voter.EffectivePercent.Quo(votepower.StakersShare),
)
to := voter.EarningAccount
go func(signersDue numeric.Dec, addr common.Address, j int) {
payable <- slotPayable{
effective: signersDue,
payee: addr,
bucket: i,
index: j,
oops: nil,
}
}(due, to, member)
}
}
}(i)
}
w.Wait()
resultsHandle := make([][]slotPayable, len(crossLinks))
for i := range resultsHandle {
resultsHandle[i] = []slotPayable{}
}
for payThem := range payable {
bucket := payThem.bucket
resultsHandle[bucket] = append(resultsHandle[bucket], payThem)
}
// Check if any errors and sort each bucket to enforce order
for bucket := range resultsHandle {
for payThem := range resultsHandle[bucket] {
if err := resultsHandle[bucket][payThem].oops; err != nil {
return err
}
}
sort.SliceStable(resultsHandle[bucket],
func(i, j int) bool {
return resultsHandle[bucket][i].index < resultsHandle[bucket][j].index
},
)
}
// Finally do the pay
for bucket := range resultsHandle {
for payThem := range resultsHandle[bucket] {
state.AddBalance(
resultsHandle[bucket][payThem].payee,
resultsHandle[bucket][payThem].effective.TruncateInt(),
)
}
}
}
return nil
}
payable := []struct {
string
@ -120,15 +282,26 @@ func AccumulateRewards(
*big.Int
}{}
parentHeader := bc.GetHeaderByHash(header.ParentHash())
if parentHeader.Number().Cmp(common.Big0) == 0 {
// Parent is an epoch block,
// which is not signed in the usual manner therefore rewards nothing.
return nil
}
_, signers, _, err := ballotResult(bc, header, header.ShardID())
if err != nil {
return err
}
totalAmount := rewarder.Award(
BlockReward, accounts, func(receipient common.Address, amount *big.Int) {
BlockReward, signers, func(receipient common.Address, amount *big.Int) {
payable = append(payable, struct {
string
common.Address
*big.Int
}{
common2.MustAddressToBech32(receipient), receipient, amount,
},
}{common2.MustAddressToBech32(receipient), receipient, amount},
)
},
)
@ -138,20 +311,19 @@ func AccumulateRewards(
Int64("block-reward", BlockReward.Int64()).
Int64("total-amount-paid-out", totalAmount.Int64()).
Msg("Total paid out was not equal to block-reward")
return errors.Wrapf(errPayoutNotEqualBlockReward, "payout "+totalAmount.String())
return errors.Wrapf(
errPayoutNotEqualBlockReward, "payout "+totalAmount.String(),
)
}
signers := make([]string, len(payable))
for i := range payable {
signers[i] = payable[i].string
state.AddBalance(payable[i].Address, payable[i].Int)
}
header.Logger(utils.Logger()).Debug().
Int("NumAccounts", len(accounts)).
Int("NumAccounts", len(payable)).
Str("TotalAmount", totalAmount.String()).
Strs("Signers", signers).
Msg("[Block Reward] Successfully paid out block reward")
return nil
}

@ -429,7 +429,9 @@ func New(host p2p.Host, consensusObj *consensus.Consensus,
node.Worker = worker.New(node.Blockchain().Config(), blockchain, chain.Engine)
if node.Blockchain().ShardID() != shard.BeaconChainShardID {
node.BeaconWorker = worker.New(node.Beaconchain().Config(), beaconChain, chain.Engine)
node.BeaconWorker = worker.New(
node.Beaconchain().Config(), beaconChain, chain.Engine,
)
}
node.pendingCXReceipts = make(map[string]*types.CXReceiptsProof)
@ -437,8 +439,10 @@ func New(host p2p.Host, consensusObj *consensus.Consensus,
node.Consensus.VerifiedNewBlock = make(chan *types.Block)
chain.Engine.SetRewarder(node.Consensus.Decider.(reward.Distributor))
chain.Engine.SetSlasher(node.Consensus.Decider.(slash.Slasher))
chain.Engine.SetBeaconchain(beaconChain)
// the sequence number is the next block number to be added in consensus protocol, which is always one more than current chain header block
// the sequence number is the next block number to be added in consensus protocol, which is
// always one more than current chain header block
node.Consensus.SetBlockNum(blockchain.CurrentBlock().NumberU64() + 1)
// Add Faucet contract to all shards, so that on testnet, we can demo wallet in explorer

Loading…
Cancel
Save