Implementation of EPoS (#1826)

* [staking] Stumble out test case for validator information

* [shard] Provide JSON dump of shard state

* [EPoS] Checkpoint w/RJ

* [EPoS] Implement EPoS at slot level

* [shard] Remove two-value loop

* [epos] Remove test code generation - use commit for testing later

* [epos] Remove debug code, address lint

* [epos] Remove staking epoch comment

* [epos] Use max(320, len(stakers)) for median calculation

* [epos] Remove search for empty spot for staked validator, only use top 320 for median

* [epos] Address PR comments

* [epos] Raise pull count as function parameter

* [epos] Add initial testing for calculate
pull/1833/head
Edgar Aroutiounian 5 years ago committed by GitHub
parent b9479fb835
commit 867fdb1f8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      consensus/consensus_service.go
  2. 1
      internal/configs/sharding/localnet.go
  3. 6
      node/node.go
  4. 50
      numeric/decimal.go
  5. 187
      shard/committee/assignment.go
  6. 74
      shard/shard_state.go
  7. 105
      staking/effective/calculate.go
  8. 47
      staking/effective/calculate_test.go
  9. 324
      staking/effective/epos.json
  10. 16
      staking/types/validator.go

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

@ -110,6 +110,7 @@ var (
localnetReshardingEpoch = []*big.Int{
big.NewInt(0), big.NewInt(localnetV1Epoch), big.NewInt(localnetV2Epoch),
}
// Number of shards, how many slots on each , how many slots owned by Harmony
localnetV0 = MustNewInstance(2, 7, 5, genesis.LocalHarmonyAccounts, genesis.LocalFnAccounts, localnetReshardingEpoch)
localnetV1 = MustNewInstance(2, 8, 5, genesis.LocalHarmonyAccountsV1, genesis.LocalFnAccountsV1, localnetReshardingEpoch)
localnetV2 = MustNewInstance(2, 9, 6, genesis.LocalHarmonyAccountsV2, genesis.LocalFnAccountsV2, localnetReshardingEpoch)

@ -492,9 +492,9 @@ func (node *Node) InitConsensusWithValidators() (err error) {
Uint32("shardID", shardID).
Uint64("epoch", epoch.Uint64()).
Msg("[InitConsensusWithValidators] Try To Get PublicKeys")
_, pubKeys := committee.WithStakingEnabled.ComputePublicKeys(
epoch, node.Consensus.ChainReader, int(shardID),
)
pubKeys := committee.WithStakingEnabled.ComputePublicKeys(
epoch, node.Consensus.ChainReader,
)[int(shardID)]
if len(pubKeys) == 0 {
utils.Logger().Error().
Uint32("shardID", shardID).

@ -1,5 +1,7 @@
package numeric
// Incorporated from cosmos-sdk
import (
"encoding/json"
"errors"
@ -7,7 +9,6 @@ import (
"math/big"
"strconv"
"strings"
"testing"
)
// Dec represent a decimal. NOTE: never use new(Dec) or else we will panic unmarshalling into the
@ -540,50 +541,10 @@ func (d Dec) Ceil() Dec {
//___________________________________________________________________________________
// reuse nil values
var (
nilAmino string
nilJSON []byte
)
func init() {
empty := new(big.Int)
bz, err := empty.MarshalText()
if err != nil {
panic("bad nil amino init")
}
nilAmino = string(bz)
nilJSON, err = json.Marshal(string(bz))
if err != nil {
panic("bad nil json init")
}
}
// MarshalAmino wraps d.MarshalText()
func (d Dec) MarshalAmino() (string, error) {
if d.Int == nil {
return nilAmino, nil
}
bz, err := d.Int.MarshalText()
return string(bz), err
}
// UnmarshalAmino requires a valid JSON string - strings quotes and calls UnmarshalText
func (d *Dec) UnmarshalAmino(text string) (err error) {
tempInt := new(big.Int)
err = tempInt.UnmarshalText([]byte(text))
if err != nil {
return err
}
d.Int = tempInt
return nil
}
// MarshalJSON marshals the decimal
func (d Dec) MarshalJSON() ([]byte, error) {
if d.Int == nil {
return nilJSON, nil
return []byte{}, nil
}
return json.Marshal(d.String())
@ -644,8 +605,3 @@ func MaxDec(d1, d2 Dec) Dec {
}
return d1
}
// DecEq intended to be used with require/assert: require.True(DecEq(...))
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}

@ -2,7 +2,6 @@ package committee
import (
"math/big"
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
@ -11,40 +10,31 @@ import (
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/effective"
staking "github.com/harmony-one/harmony/staking/types"
)
// StateID means reading off whole network when using calls that accept
// a shardID parameter
const StateID = -1
// ValidatorList ..
type ValidatorList interface {
// ValidatorListProvider ..
type ValidatorListProvider interface {
Compute(
epoch *big.Int, config params.ChainConfig, reader StakingCandidatesReader,
epoch *big.Int, config params.ChainConfig, reader DataProvider,
) (shard.State, error)
ReadFromDB(epoch *big.Int, reader ChainReader) (shard.State, error)
ReadFromDB(epoch *big.Int, reader DataProvider) (shard.State, error)
}
// PublicKeys per epoch
type PublicKeys interface {
// If call shardID with StateID then only superCommittee is non-nil,
// otherwise get back the shardSpecific slice as well.
ComputePublicKeys(
epoch *big.Int, reader ChainReader, shardID int,
) (superCommittee, shardSpecific []*bls.PublicKey)
// PublicKeysProvider per epoch
type PublicKeysProvider interface {
ComputePublicKeys(epoch *big.Int, reader DataProvider) [][]*bls.PublicKey
ReadPublicKeysFromDB(
hash common.Hash, reader ChainReader,
hash common.Hash, reader DataProvider,
) ([]*bls.PublicKey, error)
}
// Reader ..
// Reader is committee.Reader and it is the API that committee membership assignment needs
type Reader interface {
PublicKeys
ValidatorList
PublicKeysProvider
ValidatorListProvider
}
// StakingCandidatesReader ..
@ -66,6 +56,12 @@ type ChainReader interface {
Config() *params.ChainConfig
}
// DataProvider ..
type DataProvider interface {
StakingCandidatesReader
ChainReader
}
type partialStakingEnabled struct{}
var (
@ -116,62 +112,98 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
return shardState
}
func with400Stakers(
s shardingconfig.Instance, stakerReader StakingCandidatesReader,
func eposStakedCommittee(
s shardingconfig.Instance, stakerReader DataProvider, stakedSlotsCount int,
) (shard.State, error) {
// TODO Nervous about this because overtime the list will become quite large
candidates := stakerReader.ValidatorCandidates()
stakers := make([]*staking.Validator, len(candidates))
essentials := map[common.Address]effective.SlotOrder{}
// TODO benchmark difference if went with data structure that sorts on insert
for i := range candidates {
// TODO Should be using .ValidatorStakingWithDelegation, not implemented yet
validator, err := stakerReader.ValidatorInformation(candidates[i])
if err != nil {
return nil, err
}
stakers[i] = validator
essentials[validator.Address] = effective.SlotOrder{
validator.Stake,
validator.SlotPubKeys,
}
}
sort.SliceStable(
stakers,
func(i, j int) bool { return stakers[i].Stake.Cmp(stakers[j].Stake) >= 0 },
)
const sCount = 401
top := stakers[:sCount]
shardCount := int(s.NumShards())
superComm := make(shard.State, shardCount)
fillCount := make([]int, shardCount)
// TODO Finish this logic, not correct, need to operate EPoS on slot level,
// not validator level
hAccounts := s.HmyAccounts()
for i := 0; i < shardCount; i++ {
superComm[i] = shard.Committee{}
superComm[i].NodeList = make(shard.NodeIDList, s.NumNodesPerShard())
superComm[i] = shard.Committee{uint32(i), shard.NodeIDList{}}
}
scratchPad := &bls.PublicKey{}
for i := range top {
spot := int(top[i].Address.Big().Int64()) % shardCount
fillCount[spot]++
// scratchPad.DeserializeHexStr()
for i := range hAccounts {
spot := i % shardCount
pub := &bls.PublicKey{}
pub.DeserializeHexStr(hAccounts[i].BlsPublicKey)
pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(scratchPad)
superComm[spot].NodeList = append(
superComm[spot].NodeList,
shard.NodeID{
top[i].Address,
pubKey.FromLibBLSPublicKey(pub)
superComm[spot].NodeList = append(superComm[spot].NodeList, shard.NodeID{
common2.ParseAddr(hAccounts[i].Address),
pubKey,
&shard.StakedMember{big.NewInt(0)},
},
)
nil,
})
}
staked := effective.Apply(essentials, stakedSlotsCount)
shardBig := big.NewInt(int64(shardCount))
if l := len(staked); l < stakedSlotsCount {
// WARN unlikely to happen in production but will happen as we are developing
stakedSlotsCount = l
}
for i := 0; i < stakedSlotsCount; i++ {
bucket := int(new(big.Int).Mod(staked[i].BlsPublicKey.Big(), shardBig).Int64())
slot := staked[i]
superComm[bucket].NodeList = append(superComm[bucket].NodeList, shard.NodeID{
slot.Address,
staked[i].BlsPublicKey,
&slot.Dec,
})
}
utils.Logger().Info().Ints("distribution of Stakers in Shards", fillCount)
return superComm, nil
}
// ComputePublicKeys produces publicKeys of entire supercommittee per epoch
func (def partialStakingEnabled) ComputePublicKeys(
epoch *big.Int, d DataProvider,
) [][]*bls.PublicKey {
config := d.Config()
instance := shard.Schedule.InstanceForEpoch(epoch)
superComm := shard.State{}
if config.IsStaking(epoch) {
superComm, _ = eposStakedCommittee(instance, d, 320)
} else {
superComm = preStakingEnabledCommittee(instance)
}
allIdentities := make([][]*bls.PublicKey, len(superComm))
for i := range superComm {
allIdentities[i] = make([]*bls.PublicKey, len(superComm[i].NodeList))
for j := range superComm[i].NodeList {
identity := &bls.PublicKey{}
superComm[i].NodeList[j].BlsPublicKey.ToLibBLSPublicKey(identity)
allIdentities[i][j] = identity
}
}
return allIdentities
}
func (def partialStakingEnabled) ReadPublicKeysFromDB(
h common.Hash, reader ChainReader,
h common.Hash, reader DataProvider,
) ([]*bls.PublicKey, error) {
header := reader.GetHeaderByHash(h)
shardID := header.ShardID()
@ -202,59 +234,22 @@ func (def partialStakingEnabled) ReadPublicKeysFromDB(
return nil, nil
}
// ComputePublicKeys produces publicKeys of entire supercommittee per epoch, optionally providing a
// shard specific subcommittee
func (def partialStakingEnabled) ComputePublicKeys(
epoch *big.Int, reader ChainReader, shardID int,
) ([]*bls.PublicKey, []*bls.PublicKey) {
config := reader.Config()
instance := shard.Schedule.InstanceForEpoch(epoch)
if !config.IsStaking(epoch) {
superComm := preStakingEnabledCommittee(instance)
spot := 0
allIdentities := make([]*bls.PublicKey, int(instance.NumShards())*instance.NumNodesPerShard())
for i := range superComm {
for j := range superComm[i].NodeList {
identity := &bls.PublicKey{}
superComm[i].NodeList[j].BlsPublicKey.ToLibBLSPublicKey(identity)
allIdentities[spot] = identity
spot++
}
}
if shardID == StateID {
return allIdentities, nil
}
subCommittee := superComm.FindCommitteeByID(uint32(shardID))
subCommitteeIdentities := make([]*bls.PublicKey, len(subCommittee.NodeList))
spot = 0
for i := range subCommittee.NodeList {
identity := &bls.PublicKey{}
subCommittee.NodeList[i].BlsPublicKey.ToLibBLSPublicKey(identity)
subCommitteeIdentities[spot] = identity
spot++
}
return allIdentities, subCommitteeIdentities
}
// TODO Implement for the staked case
return nil, nil
}
func (def partialStakingEnabled) ReadFromDB(
epoch *big.Int, reader ChainReader,
epoch *big.Int, reader DataProvider,
) (newSuperComm shard.State, err error) {
return reader.ReadShardState(epoch)
}
// ReadFromComputation is single entry point for reading the State of the network
func (def partialStakingEnabled) Compute(
epoch *big.Int, config params.ChainConfig, stakerReader StakingCandidatesReader,
epoch *big.Int, config params.ChainConfig, stakerReader DataProvider,
) (newSuperComm shard.State, err error) {
instance := shard.Schedule.InstanceForEpoch(epoch)
if !config.IsStaking(epoch) {
return preStakingEnabledCommittee(instance), nil
}
return with400Stakers(instance, stakerReader)
stakedSlots :=
(instance.NumNodesPerShard() - instance.NumHarmonyOperatedNodesPerShard()) *
int(instance.NumShards())
return eposStakedCommittee(instance, stakerReader, stakedSlots)
}

@ -11,6 +11,7 @@ import (
"github.com/harmony-one/bls/ffi/go/bls"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/numeric"
"golang.org/x/crypto/sha3"
)
@ -27,12 +28,6 @@ type EpochShardState struct {
ShardState State
}
// StakedMember is a committee member with stake
type StakedMember struct {
// nil means not active, 0 means our node, >= 0 means staked node
WithDelegationApplied *big.Int `json:"with-delegation-applied,omitempty"`
}
// State is the collection of all committees
type State []Committee
@ -41,9 +36,10 @@ type BlsPublicKey [PublicKeySizeInBytes]byte
// NodeID represents node id (BLS address)
type NodeID struct {
EcdsaAddress common.Address `json:"ecdsa_address"`
BlsPublicKey BlsPublicKey `json:"bls_pubkey"`
Validator *StakedMember `json:"staked-validator,omitempty" rlp:"nil"`
EcdsaAddress common.Address `json:"ecdsa-address"`
BlsPublicKey BlsPublicKey `json:"bls-pubkey"`
// nil means not active, 0 means our node, >= 0 means staked node
StakeWithDelegationApplied *numeric.Dec `json:"staked-validator" rlp:"nil"`
}
// NodeIDList is a list of NodeIDList.
@ -51,44 +47,44 @@ type NodeIDList []NodeID
// Committee contains the active nodes in one shard
type Committee struct {
ShardID uint32 `json:"shard_id"`
NodeList NodeIDList `json:"node_list"`
ShardID uint32 `json:"shard-id"`
NodeList NodeIDList `json:"subcommittee"`
}
// JSON produces a non-pretty printed JSON string of the SuperCommittee
func (ss State) JSON() string {
type V struct {
ECDSAAddress common.Address `json:"ecdsa_address"`
BLSPublicKey string `json:"bls-public-key"`
type t struct {
NodeID
EcdsaAddress string `json:"one-address"`
}
type T struct {
ShardID uint32 `json:"shard_id"`
Total int `json:"count"`
NodeList []V `json:"entries"`
type v struct {
Committee
Count int `json:"member-count"`
NodeList []t `json:"subcommittee"`
}
t := []T{}
dump := make([]v, len(ss))
for i := range ss {
sub := ss[i]
subList := []V{}
for j := range sub.NodeList {
subList = append(subList, V{
sub.NodeList[j].EcdsaAddress,
sub.NodeList[j].BlsPublicKey.Hex(),
})
c := len(ss[i].NodeList)
dump[i].ShardID = ss[i].ShardID
dump[i].NodeList = make([]t, c)
dump[i].Count = c
for j := range ss[i].NodeList {
n := ss[i].NodeList[j]
dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey
dump[i].NodeList[j].StakeWithDelegationApplied = n.StakeWithDelegationApplied
dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress)
}
t = append(t, T{sub.ShardID, len(sub.NodeList), subList})
}
buf, _ := json.Marshal(t)
buf, _ := json.Marshal(dump)
return string(buf)
}
// FindCommitteeByID returns the committee configuration for the given shard,
// or nil if the given shard is not found.
func (ss State) FindCommitteeByID(shardID uint32) *Committee {
for _, committee := range ss {
if committee.ShardID == shardID {
return &committee
for committee := range ss {
if ss[committee].ShardID == shardID {
return &ss[committee]
}
}
return nil
@ -123,6 +119,11 @@ func CompareShardState(s1, s2 State) int {
return 0
}
// Big ..
func (pk BlsPublicKey) Big() *big.Int {
return new(big.Int).SetBytes(pk[:])
}
// IsEmpty returns whether the bls public key is empty 0 bytes
func (pk BlsPublicKey) IsEmpty() bool {
return bytes.Compare(pk[:], emptyBlsPubKey[:]) == 0
@ -133,6 +134,15 @@ func (pk BlsPublicKey) Hex() string {
return hex.EncodeToString(pk[:])
}
// MarshalJSON ..
func (pk BlsPublicKey) MarshalJSON() ([]byte, error) {
buf := bytes.Buffer{}
buf.WriteString(`"`)
buf.WriteString(pk.Hex())
buf.WriteString(`"`)
return buf.Bytes(), nil
}
// FromLibBLSPublicKey replaces the key contents with the given key,
func (pk *BlsPublicKey) FromLibBLSPublicKey(key *bls.PublicKey) error {
bytes := key.Serialize()

@ -0,0 +1,105 @@
package effective
import (
"encoding/json"
"math/big"
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
// medium.com/harmony-one/introducing-harmonys-effective-proof-of-stake-epos-2d39b4b8d58
var (
two = numeric.NewDecFromBigInt(big.NewInt(2))
c, _ = numeric.NewDecFromStr("0.15")
onePlusC = numeric.OneDec().Add(c)
oneMinusC = numeric.OneDec().Sub(c)
)
func effectiveStake(median, actual numeric.Dec) numeric.Dec {
left := numeric.MinDec(onePlusC.Mul(median), actual)
right := oneMinusC.Mul(median)
return numeric.MaxDec(left, right)
}
// SlotPurchase ..
type SlotPurchase struct {
common.Address `json:"slot-owner"`
shard.BlsPublicKey `json:"bls-public-key"`
numeric.Dec `json:"eposed-stake"`
}
// SlotOrder ..
type SlotOrder struct {
Stake *big.Int
SpreadAmong []shard.BlsPublicKey
}
// Slots ..
type Slots []SlotPurchase
// JSON is a plain JSON dump
func (s Slots) JSON() string {
type t struct {
Slots []SlotPurchase `json:"slots"`
}
b, _ := json.Marshal(t{s})
return string(b)
}
func median(stakes []SlotPurchase) numeric.Dec {
sort.SliceStable(
stakes,
func(i, j int) bool { return stakes[i].Dec.LTE(stakes[j].Dec) },
)
const isEven = 0
switch l := len(stakes); l % 2 {
case isEven:
return stakes[(l/2)-1].Dec.Add(stakes[(l/2)+1].Dec).Quo(two)
default:
return stakes[l/2].Dec
}
}
// Apply ..
func Apply(shortHand map[common.Address]SlotOrder, pull int) Slots {
eposedSlots := Slots{}
if len(shortHand) == 0 {
return eposedSlots
}
// Expand
for staker := range shortHand {
slotsCount := len(shortHand[staker].SpreadAmong)
spread := numeric.NewDecFromBigInt(shortHand[staker].Stake).
QuoInt64(int64(slotsCount))
for i := 0; i < slotsCount; i++ {
eposedSlots = append(eposedSlots, SlotPurchase{
staker,
shortHand[staker].SpreadAmong[i],
spread,
})
}
}
if len(eposedSlots) < len(shortHand) {
// WARN Should never happen
}
sort.SliceStable(
eposedSlots,
func(i, j int) bool { return eposedSlots[i].Dec.GT(eposedSlots[j].Dec) },
)
if l := len(eposedSlots); l < pull {
pull = l
}
picks := eposedSlots[:pull]
median := median(picks)
for i := range picks {
picks[i].Dec = effectiveStake(median, picks[i].Dec)
}
return picks
}

@ -0,0 +1,47 @@
package effective
import (
"encoding/json"
"fmt"
"io/ioutil"
"testing"
)
const eposTestingFile = "epos.json"
var (
testingSlots slotsData
)
type slotsData struct {
EPOSedSlot []string `json:"slots"`
}
func init() {
input, err := ioutil.ReadFile(eposTestingFile)
if err != nil {
panic(
fmt.Sprintf("cannot open genesisblock config file %v, err %v\n",
eposTestingFile,
err,
))
}
t := slotsData{}
oops := json.Unmarshal(input, &t)
if oops != nil {
fmt.Println(oops.Error())
panic("Could not unmarshal slots data into memory")
}
}
func TestMedian(t *testing.T) {
//
}
func TestEffectiveStake(t *testing.T) {
//
}
func TestApply(t *testing.T) {
//
}

@ -0,0 +1,324 @@
{
"slots": [
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.690000000000000000",
"23.666666666666666666",
"23.666666666666666666",
"23.666666666666666666",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"23.500000000000000000",
"22.500000000000000000",
"22.500000000000000000",
"22.500000000000000000",
"22.500000000000000000",
"22.250000000000000000",
"22.250000000000000000",
"22.250000000000000000",
"22.250000000000000000",
"22.000000000000000000",
"22.000000000000000000",
"22.000000000000000000",
"22.000000000000000000",
"22.000000000000000000",
"21.000000000000000000",
"21.000000000000000000",
"21.000000000000000000",
"21.000000000000000000",
"20.666666666666666666",
"20.666666666666666666",
"20.666666666666666666",
"20.666666666666666666",
"20.666666666666666666",
"20.666666666666666666",
"20.600000000000000000",
"20.600000000000000000",
"20.600000000000000000",
"20.600000000000000000",
"20.600000000000000000",
"20.000000000000000000",
"20.000000000000000000",
"20.000000000000000000",
"20.000000000000000000",
"20.000000000000000000",
"19.600000000000000000",
"19.600000000000000000",
"19.600000000000000000",
"19.600000000000000000",
"19.600000000000000000",
"19.500000000000000000",
"19.500000000000000000",
"19.333333333333333333",
"19.333333333333333333",
"19.333333333333333333",
"19.000000000000000000",
"19.000000000000000000",
"19.000000000000000000",
"19.000000000000000000",
"18.750000000000000000",
"18.750000000000000000",
"18.750000000000000000",
"18.750000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.500000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"18.000000000000000000",
"17.714285714285714285",
"17.714285714285714285",
"17.714285714285714285",
"17.714285714285714285",
"17.714285714285714285",
"17.714285714285714285",
"17.714285714285714285",
"17.600000000000000000",
"17.600000000000000000",
"17.600000000000000000",
"17.600000000000000000",
"17.600000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000",
"17.510000000000000000"
]
}

@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
@ -67,8 +67,8 @@ type Validator struct {
func printSlotPubKeys(pubKeys []shard.BlsPublicKey) string {
str := "["
for i, key := range pubKeys {
str += fmt.Sprintf("%d: %s,", i, key.Hex())
for i := range pubKeys {
str += fmt.Sprintf("%d: %s,", i, pubKeys[i].Hex())
}
str += "]"
return str
@ -198,8 +198,10 @@ func CreateValidatorFromNewMsg(val *CreateValidator, blockNum *big.Int) (*Valida
commission := Commission{val.CommissionRates, blockNum}
pubKeys := []shard.BlsPublicKey{}
pubKeys = append(pubKeys, val.SlotPubKeys...)
// TODO: a new validator should have a minimum of 1 token as self delegation, and that should be added as a delegation entry here.
v := Validator{val.ValidatorAddress, pubKeys,
v := Validator{
val.ValidatorAddress, pubKeys,
val.Amount, new(big.Int), val.MinSelfDelegation, val.MaxTotalDelegation, false,
commission, desc, blockNum}
return &v, nil
@ -268,7 +270,9 @@ func (v *Validator) String() string {
Unbonding Height: %v
Minimum SelfDelegation: %v
Description: %v
Commission: %v`, v.Address.Hex(), printSlotPubKeys(v.SlotPubKeys),
Commission: %v`,
common2.MustAddressToBech32(v.Address), printSlotPubKeys(v.SlotPubKeys),
v.Stake, v.UnbondingHeight,
v.MinSelfDelegation, v.Description, v.Commission)
v.MinSelfDelegation, v.Description, v.Commission,
)
}

Loading…
Cancel
Save