From 867fdb1f8d60d953c3db817aa396f6e245aa60a3 Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Sat, 16 Nov 2019 09:53:53 -0800 Subject: [PATCH] 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 --- consensus/consensus_service.go | 13 +- internal/configs/sharding/localnet.go | 1 + node/node.go | 6 +- numeric/decimal.go | 50 +--- shard/committee/assignment.go | 189 ++++++++------- shard/shard_state.go | 74 +++--- staking/effective/calculate.go | 105 +++++++++ staking/effective/calculate_test.go | 47 ++++ staking/effective/epos.json | 324 ++++++++++++++++++++++++++ staking/types/validator.go | 16 +- 10 files changed, 634 insertions(+), 191 deletions(-) create mode 100644 staking/effective/calculate.go create mode 100644 staking/effective/calculate_test.go create mode 100644 staking/effective/epos.json diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 2f5bba52a..278e4aea8 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.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 diff --git a/internal/configs/sharding/localnet.go b/internal/configs/sharding/localnet.go index a2394bcf2..0bbf18182 100644 --- a/internal/configs/sharding/localnet.go +++ b/internal/configs/sharding/localnet.go @@ -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) diff --git a/node/node.go b/node/node.go index 20f008ff1..f65962b5b 100644 --- a/node/node.go +++ b/node/node.go @@ -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). diff --git a/numeric/decimal.go b/numeric/decimal.go index 05afa710c..d09e3591c 100644 --- a/numeric/decimal.go +++ b/numeric/decimal.go @@ -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() -} diff --git a/shard/committee/assignment.go b/shard/committee/assignment.go index 7d4b47e92..7abcbaea3 100644 --- a/shard/committee/assignment.go +++ b/shard/committee/assignment.go @@ -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, - &shard.StakedMember{big.NewInt(0)}, - }, - ) + pubKey.FromLibBLSPublicKey(pub) + superComm[spot].NodeList = append(superComm[spot].NodeList, shard.NodeID{ + common2.ParseAddr(hAccounts[i].Address), + pubKey, + 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) } diff --git a/shard/shard_state.go b/shard/shard_state.go index c868c702e..b8bccccf0 100644 --- a/shard/shard_state.go +++ b/shard/shard_state.go @@ -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() diff --git a/staking/effective/calculate.go b/staking/effective/calculate.go new file mode 100644 index 000000000..96dca579e --- /dev/null +++ b/staking/effective/calculate.go @@ -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 +} diff --git a/staking/effective/calculate_test.go b/staking/effective/calculate_test.go new file mode 100644 index 000000000..565337101 --- /dev/null +++ b/staking/effective/calculate_test.go @@ -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) { + // +} diff --git a/staking/effective/epos.json b/staking/effective/epos.json new file mode 100644 index 000000000..e8bf6a906 --- /dev/null +++ b/staking/effective/epos.json @@ -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" + ] +} diff --git a/staking/types/validator.go b/staking/types/validator.go index f367cbd09..9d7d93458 100644 --- a/staking/types/validator.go +++ b/staking/types/validator.go @@ -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, + ) }