@ -2,19 +2,24 @@ package chain
import (
import (
"math/big"
"math/big"
"sort"
"sync"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/consensus/reward"
"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/state"
"github.com/harmony-one/harmony/core/types"
bls2 "github.com/harmony-one/harmony/crypto/bls"
bls2 "github.com/harmony-one/harmony/crypto/bls"
common2 "github.com/harmony-one/harmony/internal/common"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/slash"
"github.com/harmony-one/harmony/staking/slash"
"github.com/pkg/errors"
"github.com/pkg/errors"
@ -22,97 +27,254 @@ import (
var (
var (
// BlockReward is the block reward, to be split evenly among block signers.
// 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" )
errPayoutNotEqualBlockReward = errors . New ( "total payout not equal to blockreward" )
)
)
// AccumulateRewards credits the coinbase of the given block with the mining
func blockSigners (
// reward. The total reward consists of the static block reward and rewards for
header * block . Header , parentCommittee * shard . Committee ,
// included uncles. The coinbase of each uncle block is also rewarded.
) ( shard . SlotList , shard . SlotList , error ) {
func AccumulateRewards (
committerKeys := [ ] * bls . PublicKey { }
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
for _ , member := range parentCommittee . Slots {
for _ , member := range parentCommittee . Slots {
committerKey := new ( bls . PublicKey )
committerKey := new ( bls . PublicKey )
err := member . BlsPublicKey . ToLibBLSPublicKey ( committerKey )
err := member . BlsPublicKey . ToLibBLSPublicKey ( committerKey )
if err != nil {
if err != nil {
return ctxerror . New ( "cannot convert BLS public key" ,
return nil , nil , ctxerror . New (
"blsPublicKey" , member . BlsPublicKey ) . WithCause ( err )
"cannot convert BLS public key" ,
"blsPublicKey" ,
member . BlsPublicKey ,
) . WithCause ( err )
}
}
committerKeys = append ( committerKeys , committerKey )
committerKeys = append ( committerKeys , committerKey )
}
}
mask , err := bls2 . NewMask ( committerKeys , nil )
mask , err := bls2 . NewMask ( committerKeys , nil )
if err != 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 {
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 { }
payable , missing := shard . SlotList { } , shard . SlotList { }
missing := shard . SlotList { }
for idx , member := range parentCommittee . Slots {
for idx , member := range parentCommittee . Slots {
switch signed , err := mask . IndexEnabled ( idx ) ; true {
switch signed , err := mask . IndexEnabled ( idx ) ; true {
case err != nil :
case err != nil :
return ctxerror . New ( "cannot check for committer bit" ,
return nil , nil , ctxerror . New ( "cannot check for committer bit" ,
"committerIndex" , idx ,
"committerIndex" , idx ,
) . WithCause ( err )
) . WithCause ( err )
case signed :
case signed :
accounts = append ( accounts , member . EcdsaAddress )
payable = append ( payable , member )
default :
default :
missing = append ( missing , member )
missing = append ( missing , member )
}
}
}
}
return payable , missing , nil
}
// do it quickly
func ballotResult (
w := sync . WaitGroup { }
bc engine . ChainReader , header * block . Header , shardID uint32 ,
for i := range missing {
) ( shard . SlotList , shard . SlotList , shard . SlotList , error ) {
w . Add ( 1 )
// TODO ek – retrieving by parent number (blockNum - 1) doesn't work,
go func ( member int ) {
// while it is okay with hash. Sounds like DB inconsistency.
defer w . Done ( )
// Figure out why.
// Slash if missing block was long enough
parentHeader := bc . GetHeaderByHash ( header . ParentHash ( ) )
if slasher . ShouldSlash ( missing [ member ] . BlsPublicKey ) {
if parentHeader == nil {
// TODO Logic
return nil , nil , nil , ctxerror . New (
}
"cannot find parent block header in DB" ,
} ( i )
"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 {
payable := [ ] struct {
string
string
@ -120,15 +282,26 @@ func AccumulateRewards(
* big . Int
* 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 (
totalAmount := rewarder . Award (
BlockReward , accounts , func ( receipient common . Address , amount * big . Int ) {
BlockReward , signer s, func ( receipient common . Address , amount * big . Int ) {
payable = append ( payable , struct {
payable = append ( payable , struct {
string
string
common . Address
common . Address
* big . Int
* big . Int
} {
} { common2 . MustAddressToBech32 ( receipient ) , receipient , amount } ,
common2 . MustAddressToBech32 ( receipient ) , receipient , amount ,
} ,
)
)
} ,
} ,
)
)
@ -138,20 +311,19 @@ func AccumulateRewards(
Int64 ( "block-reward" , BlockReward . Int64 ( ) ) .
Int64 ( "block-reward" , BlockReward . Int64 ( ) ) .
Int64 ( "total-amount-paid-out" , totalAmount . Int64 ( ) ) .
Int64 ( "total-amount-paid-out" , totalAmount . Int64 ( ) ) .
Msg ( "Total paid out was not equal to block-reward" )
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 {
for i := range payable {
signers [ i ] = payable [ i ] . string
state . AddBalance ( payable [ i ] . Address , payable [ i ] . Int )
state . AddBalance ( payable [ i ] . Address , payable [ i ] . Int )
}
}
header . Logger ( utils . Logger ( ) ) . Debug ( ) .
header . Logger ( utils . Logger ( ) ) . Debug ( ) .
Int ( "NumAccounts" , len ( accounts ) ) .
Int ( "NumAccounts" , len ( payable ) ) .
Str ( "TotalAmount" , totalAmount . String ( ) ) .
Str ( "TotalAmount" , totalAmount . String ( ) ) .
Strs ( "Signers" , signers ) .
Msg ( "[Block Reward] Successfully paid out block reward" )
Msg ( "[Block Reward] Successfully paid out block reward" )
return nil
return nil
}
}