[slash] UpdateConsensusInformation has Staked based Quorum Decider, provide abstraction for inavailability count

pull/1827/head
Edgar Aroutiounian 5 years ago
parent ac579e81d8
commit 91285cb0c3
  1. 19
      consensus/consensus_service.go
  2. 9
      consensus/quorum/one-node-one-vote.go
  3. 13
      consensus/quorum/one-node-staked-vote.go
  4. 70
      consensus/quorum/quorum.go
  5. 14
      internal/chain/engine.go
  6. 48
      internal/chain/reward.go
  7. 11
      staking/slash/slasher.go

@ -457,10 +457,13 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode {
hasError := false hasError := false
header := consensus.ChainReader.CurrentHeader() header := consensus.ChainReader.CurrentHeader()
epoch := header.Epoch() epoch := header.Epoch()
curPubKeys := committee.WithStakingEnabled.ComputePublicKeys( if consensus.Decider.Policy() != quorum.SuperMajorityStake &&
epoch, consensus.ChainReader, consensus.ChainReader.Config().IsStaking(epoch) {
)[int(header.ShardID())] consensus.Decider = quorum.NewDecider(quorum.SuperMajorityStake)
}
_, curPubKeys := committee.WithStakingEnabled.ComputePublicKeys(
epoch, consensus.ChainReader, int(header.ShardID()),
)
consensus.numPrevPubKeys = len(curPubKeys) consensus.numPrevPubKeys = len(curPubKeys)
consensus.getLogger().Info().Msg("[UpdateConsensusInformation] Updating.....") consensus.getLogger().Info().Msg("[UpdateConsensusInformation] Updating.....")
if shard.Schedule.IsLastBlock(header.Number().Uint64()) { if shard.Schedule.IsLastBlock(header.Number().Uint64()) {
@ -468,9 +471,11 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode {
consensus.SetEpochNum(epoch.Uint64() + 1) consensus.SetEpochNum(epoch.Uint64() + 1)
consensus.getLogger().Info().Uint64("headerNum", header.Number().Uint64()). consensus.getLogger().Info().Uint64("headerNum", header.Number().Uint64()).
Msg("[UpdateConsensusInformation] Epoch updated for next epoch") Msg("[UpdateConsensusInformation] Epoch updated for next epoch")
pubKeys = committee.WithStakingEnabled.ComputePublicKeys( _, pubKeys = committee.WithStakingEnabled.ComputePublicKeys(
new(big.Int).Add(epoch, common.Big1), consensus.ChainReader, new(big.Int).Add(epoch, common.Big1),
)[int(header.ShardID())] consensus.ChainReader,
int(header.ShardID()),
)
} else { } else {
consensus.SetEpochNum(epoch.Uint64()) consensus.SetEpochNum(epoch.Uint64())
pubKeys = curPubKeys pubKeys = curPubKeys

@ -6,12 +6,13 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
// "github.com/harmony-one/harmony/staking/effective" // "github.com/harmony-one/harmony/staking/effective"
) )
type uniformVoteWeight struct { type uniformVoteWeight struct {
SignatureReader
DependencyInjectionWriter DependencyInjectionWriter
SignatureReader
} }
// Policy .. // Policy ..
@ -71,3 +72,9 @@ func (v *uniformVoteWeight) Award(
return payout return payout
} }
func (v *uniformVoteWeight) ShouldSlash(k shard.BlsPublicKey) bool {
// No-op, no semantic meaning in one-slot-one-vote
// fmt.Println("Called here for key:", k.Hex())
return false
}

@ -7,6 +7,7 @@ import (
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/numeric" "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"
) )
var ( var (
@ -21,6 +22,8 @@ type stakedVoter struct {
type stakedVoteWeight struct { type stakedVoteWeight struct {
SignatureReader SignatureReader
DependencyInjectionWriter DependencyInjectionWriter
DependencyInjectionReader
slash.ThresholdDecider
// EPOS based staking // EPOS based staking
validatorStakes map[[shard.PublicKeySizeInBytes]byte]stakedVoter validatorStakes map[[shard.PublicKeySizeInBytes]byte]stakedVoter
totalEffectiveStakedAmount *big.Int totalEffectiveStakedAmount *big.Int
@ -73,3 +76,13 @@ func (v *stakedVoteWeight) ToggleActive(*bls.PublicKey) bool {
// TODO Implement // TODO Implement
return true return true
} }
func (v *stakedVoteWeight) ShouldSlash(key shard.BlsPublicKey) bool {
s, _ := v.ShardIDProvider()()
switch s {
case shard.BeaconChainShardID:
return v.SlashThresholdMet(key)
default:
return false
}
}

@ -6,6 +6,7 @@ import (
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
"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/effective" // "github.com/harmony-one/harmony/staking/effective"
) )
@ -75,10 +76,16 @@ type DependencyInjectionWriter interface {
SetShardIDProvider(func() (uint32, error)) SetShardIDProvider(func() (uint32, error))
} }
// DependencyInjectionReader ..
type DependencyInjectionReader interface {
ShardIDProvider() func() (uint32, error)
}
// Decider .. // Decider ..
type Decider interface { type Decider interface {
SignatureReader SignatureReader
DependencyInjectionWriter DependencyInjectionWriter
slash.Slasher
ToggleActive(*bls.PublicKey) bool ToggleActive(*bls.PublicKey) bool
// UpdateVotingPower(keeper effective.StakeKeeper) // UpdateVotingPower(keeper effective.StakeKeeper)
Policy() Policy Policy() Policy
@ -96,17 +103,14 @@ type cIdentities struct {
commit map[string]*bls.Sign commit map[string]*bls.Sign
// viewIDSigs: every validator // viewIDSigs: every validator
// sign on |viewID|blockHash| in view changing message // sign on |viewID|blockHash| in view changing message
viewID map[string]*bls.Sign viewID map[string]*bls.Sign
seenCounter map[[shard.PublicKeySizeInBytes]byte]int
} }
type depInject struct { type depInject struct {
shardIDProvider func() (uint32, error) shardIDProvider func() (uint32, error)
} }
func (d *depInject) SetShardIDProvider(p func() (uint32, error)) {
d.shardIDProvider = p
}
func (s *cIdentities) IndexOf(pubKey *bls.PublicKey) int { func (s *cIdentities) IndexOf(pubKey *bls.PublicKey) int {
idx := -1 idx := -1
for k, v := range s.publicKeys { for k, v := range s.publicKeys {
@ -132,9 +136,23 @@ func (s *cIdentities) Participants() []*bls.PublicKey {
} }
func (s *cIdentities) UpdateParticipants(pubKeys []*bls.PublicKey) { func (s *cIdentities) UpdateParticipants(pubKeys []*bls.PublicKey) {
// TODO - might need to put this in separate method
s.seenCounter = make(map[[shard.PublicKeySizeInBytes]byte]int, len(pubKeys))
for i := range pubKeys {
k := shard.BlsPublicKey{}
k.FromLibBLSPublicKey(pubKeys[i])
s.seenCounter[k] = 0
}
s.publicKeys = append(pubKeys[:0:0], pubKeys...) s.publicKeys = append(pubKeys[:0:0], pubKeys...)
} }
func (s *cIdentities) SlashThresholdMet(key shard.BlsPublicKey) bool {
s.seenCounter[key]++
fmt.Println("Slash Map", s.seenCounter)
return s.seenCounter[key] == slash.UnavailabilityInConsecutiveBlockSigning
}
func (s *cIdentities) DumpParticipants() []string { func (s *cIdentities) DumpParticipants() []string {
keys := make([]string, len(s.publicKeys)) keys := make([]string, len(s.publicKeys))
for i := 0; i < len(s.publicKeys); i++ { for i := 0; i < len(s.publicKeys); i++ {
@ -174,8 +192,8 @@ func (s *cIdentities) AddSignature(p Phase, PubKey *bls.PublicKey, sig *bls.Sign
} }
func (s *cIdentities) Reset(ps []Phase) { func (s *cIdentities) Reset(ps []Phase) {
for _, p := range ps { for i := range ps {
switch m := map[string]*bls.Sign{}; p { switch m := map[string]*bls.Sign{}; ps[i] {
case Prepare: case Prepare:
s.prepare = m s.prepare = m
case Commit: case Commit:
@ -223,43 +241,45 @@ func (s *cIdentities) ReadAllSignatures(p Phase) []*bls.Sign {
return sigs return sigs
} }
func newMapBackedSignatureReader() cIdentities { func newMapBackedSignatureReader() *cIdentities {
return cIdentities{ return &cIdentities{
[]*bls.PublicKey{}, map[string]*bls.Sign{}, []*bls.PublicKey{}, map[string]*bls.Sign{},
map[string]*bls.Sign{}, map[string]*bls.Sign{}, map[string]*bls.Sign{}, map[string]*bls.Sign{},
map[[shard.PublicKeySizeInBytes]byte]int{},
} }
} }
func (c *composite) ShouldSlash(shard.BlsPublicKey) bool { type composite struct {
s, _ := c.shardIDProvider() DependencyInjectionWriter
switch s { SignatureReader
case shard.BeaconChainShardID:
return true
default:
return false
}
} }
type composite struct { func (d *depInject) SetShardIDProvider(p func() (uint32, error)) {
cIdentities d.shardIDProvider = p
depInject }
func (d *depInject) ShardIDProvider() func() (uint32, error) {
return d.shardIDProvider
} }
// NewDecider .. // NewDecider ..
func NewDecider(p Policy) Decider { func NewDecider(p Policy) Decider {
signatureStore := newMapBackedSignatureReader() signatureStore := newMapBackedSignatureReader()
dependencies := depInject{} deps := &depInject{}
c := &composite{signatureStore, dependencies} c := &composite{deps, signatureStore}
switch p { switch p {
case SuperMajorityVote: case SuperMajorityVote:
return &uniformVoteWeight{&c.cIdentities, &c.depInject} return &uniformVoteWeight{c.DependencyInjectionWriter, c}
case SuperMajorityStake: case SuperMajorityStake:
fmt.Println("HRS")
return &stakedVoteWeight{ return &stakedVoteWeight{
&c.cIdentities, &c.depInject, c.SignatureReader,
c.DependencyInjectionWriter,
c.DependencyInjectionWriter.(DependencyInjectionReader),
c.SignatureReader.(slash.ThresholdDecider),
map[[shard.PublicKeySizeInBytes]byte]stakedVoter{}, map[[shard.PublicKeySizeInBytes]byte]stakedVoter{},
big.NewInt(0), big.NewInt(0),
} }
default: default:
// Should not be possible // Should not be possible
return nil return nil

@ -177,12 +177,16 @@ func (e *engineImpl) VerifySeal(chain engine.ChainReader, header *block.Header)
// Finalize implements Engine, accumulating the block rewards, // Finalize implements Engine, accumulating the block rewards,
// setting the final state and assembling the block. // setting the final state and assembling the block.
func (e *engineImpl) Finalize( func (e *engineImpl) Finalize(
chain engine.ChainReader, header *block.Header, state *state.DB, txs []*types.Transaction, chain engine.ChainReader, header *block.Header,
state *state.DB, txs []*types.Transaction,
receipts []*types.Receipt, outcxs []*types.CXReceipt, receipts []*types.Receipt, outcxs []*types.CXReceipt,
incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction) (*types.Block, error) { incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction,
) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
if err := AccumulateRewards(chain, state, header, e.Rewarder(), e.Slasher()); err != nil { if err := AccumulateRewards(
chain, state, header, e.Rewarder(), e.Slasher(),
); err != nil {
return nil, ctxerror.New("cannot pay block reward").WithCause(err) return nil, ctxerror.New("cannot pay block reward").WithCause(err)
} }
@ -221,7 +225,9 @@ func (e *engineImpl) Finalize(
} }
// QuorumForBlock returns the quorum for the given block header. // QuorumForBlock returns the quorum for the given block header.
func QuorumForBlock(chain engine.ChainReader, h *block.Header, reCalculate bool) (quorum int, err error) { func QuorumForBlock(
chain engine.ChainReader, h *block.Header, reCalculate bool,
) (quorum int, err error) {
var ss shard.State var ss shard.State
if reCalculate { if reCalculate {
ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), *chain.Config(), nil) ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), *chain.Config(), nil)

@ -2,6 +2,7 @@ package chain
import ( import (
"math/big" "math/big"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
@ -14,6 +15,7 @@ import (
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/shard"
"github.com/harmony-one/harmony/staking/slash" "github.com/harmony-one/harmony/staking/slash"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -82,29 +84,54 @@ func AccumulateRewards(
} }
accounts := []common.Address{} accounts := []common.Address{}
missing := shard.NodeIDList{}
for idx, member := range parentCommittee.NodeList { for idx, member := range parentCommittee.NodeList {
if signed, err := mask.IndexEnabled(idx); err != nil { switch signed, err := mask.IndexEnabled(idx); true {
case err != nil:
return ctxerror.New("cannot check for committer bit", return ctxerror.New("cannot check for committer bit",
"committerIndex", idx, "committerIndex", idx,
).WithCause(err) ).WithCause(err)
} else if signed { case signed:
accounts = append(accounts, member.EcdsaAddress) accounts = append(accounts, member.EcdsaAddress)
default:
missing = append(missing, member)
} }
} }
type t struct { // do it quickly
w := sync.WaitGroup{}
for i := range missing {
w.Add(1)
go func(member int) {
defer w.Add(-1)
// Slash if missing block was long enough
if slasher.ShouldSlash(missing[member].BlsPublicKey) {
// TODO Logic
}
}(i)
}
w.Wait()
payable := []struct {
string
common.Address common.Address
*big.Int *big.Int
} }{}
signers := []string{}
payable := []t{}
totalAmount := rewarder.Award( totalAmount := rewarder.Award(
BlockReward, accounts, func(receipient common.Address, amount *big.Int) { BlockReward, accounts, func(receipient common.Address, amount *big.Int) {
signers = append(signers, common2.MustAddressToBech32(receipient)) payable = append(payable, struct {
payable = append(payable, t{receipient, amount}) string
}) common.Address
*big.Int
}{
common2.MustAddressToBech32(receipient), receipient, amount,
},
)
},
)
if totalAmount.Cmp(BlockReward) != 0 { if totalAmount.Cmp(BlockReward) != 0 {
utils.Logger().Error(). utils.Logger().Error().
@ -114,7 +141,10 @@ func AccumulateRewards(
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)
} }

@ -2,7 +2,18 @@ package slash
import "github.com/harmony-one/harmony/shard" import "github.com/harmony-one/harmony/shard"
const (
// UnavailabilityInConsecutiveBlockSigning is how many blocks in a row
// before "slashing by unavailability" occurs
UnavailabilityInConsecutiveBlockSigning = 1380
)
// Slasher .. // Slasher ..
type Slasher interface { type Slasher interface {
ShouldSlash(shard.BlsPublicKey) bool ShouldSlash(shard.BlsPublicKey) bool
} }
// ThresholdDecider ..
type ThresholdDecider interface {
SlashThresholdMet(shard.BlsPublicKey) bool
}

Loading…
Cancel
Save