[EPoS] Checkpoint w/RJ

pull/1827/head
Edgar Aroutiounian 5 years ago
parent 32f4db4c4f
commit 09c427f080
  1. 113
      core/blockchain.go
  2. 2
      internal/configs/sharding/localnet.go
  3. 3
      internal/params/config.go
  4. 10
      numeric/decimal.go
  5. 163
      shard/committee/assignment.go
  6. 91
      staking/effective/calculate.go
  7. 20
      staking/effective/values.go
  8. 5
      staking/types/validator.go

@ -37,13 +37,13 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/block"
consensus_engine "github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
@ -2441,74 +2441,65 @@ func (bc *BlockChain) CurrentValidatorAddresses() []common.Address {
}
const (
a = "one1x4080kv34a4s3s886vs3rvvhm28pk3r0fnfh6n"
b = "one1pjq82a97vfxvmlm60htyc7kafhyjm59f7wgz22"
c = "one1yc06ghr2p8xnl2380kpfayweguuhxdtupkhqzw"
d = "one1wh4p0kuc7unxez2z8f82zfnhsg4ty6dupqyjt2"
e = "one16qsd5ant9v94jrs89mruzx62h7ekcfxmduh2rx"
f = "one1spshr72utf6rwxseaz339j09ed8p6f8ke370zj"
g = "one1v788ag7ukh6wjujvpempss6h9ugvfxmnsyklr5"
h = "one1hx3gy5e864eycmjtpcvsuz59m3lu7y89q0ddhl"
i = "one1mmvhupm7ejytype664mhs2m95vn9pfdjrqfadv"
blsKey1 = "9a010ea58079ddcd31d5d10ac85e9b5a7732972ee5478d7296091b2af96fcd673c49d6dd7a4a9e7acd2e4aab0add0e00"
blsKey2 = "9275355e60f8c78337d538eb15e3f8eda120c28635f996d8a13f544d118db32749bcc0ed02a795770141d979a8b9de14"
// Pick big number for interesting looking one addresses
amount = 400
fixedRandomGen = 98765654323
fixedRandomGenStakeL = 40
fixedRandomGenStakeH = 150
)
// ValidatorCandidates returns the up to date validator candidates for next epoch
func (bc *BlockChain) ValidatorCandidates() []common.Address {
// TODO Turn this into 400 length generated slice
return []common.Address{
common2.ParseAddr(a),
common2.ParseAddr(b),
common2.ParseAddr(c),
common2.ParseAddr(d),
common2.ParseAddr(e),
common2.ParseAddr(f),
common2.ParseAddr(g),
common2.ParseAddr(h),
}
}
var (
// By fixing the source, we have predicable sequence
sequenceL = rand.New(rand.NewSource(42))
sequenceH = rand.New(rand.NewSource(84))
accountGenerator = rand.New(rand.NewSource(1337))
)
var (
sequence = rand.New(rand.NewSource(42))
tempBank map[common.Address]*staking.Validator = map[common.Address]*staking.Validator{}
addrs []common.Address
)
// ValidatorInformation returns the information of validator
func (bc *BlockChain) ValidatorInformation(addr common.Address) (*staking.Validator, error) {
// EDGAR 0x355E77D991Af6B08C0e7d32111b197da8e1B446f
// EDGAR 0x0C807574BE624CcdFf7A7dD64c7ADd4dC92dd0a9
// EDGAR 0x261fa45c6A09cD3Faa277d829e91d9473973357C
// EDGAR 0x75eA17DB98F7266C89423A4EA12677822Ab269Bc
// EDGAR 0xD020dA766b2b0b590E072ec7c11b4AbFb36c24DB
// EDGAR 0x806171f95C5a74371a19e8a312c9e5Cb4E1D24f6
// EDGAR 0x678E7Ea3DCb5f4e9724C0e761843572f10c49B73
// EDGAR 0xB9A2825327D5724C6E4b0e190E0a85dc7FCf10e5
func init() {
addrs = make([]common.Address, amount)
for i := 0; i < amount; i++ {
addr := common.Address{}
addr.SetBytes(
big.NewInt(int64(accountGenerator.Intn(fixedRandomGen))).Bytes(),
)
addrs[i] = addr
someValidator := &staking.Validator{}
someValidator.Address = addr
someValidator.Stake = big.NewInt(int64(sequence.Intn(100)))
switch B := common2.MustAddressToBech32(addr); true {
case B == a:
fmt.Println("Called here:", B, a)
return someValidator, nil
case B == b:
case B == c:
case B == d:
case B == e:
case B == f:
case B == g:
case B == h:
}
fmt.Println("EDGAR", addr.String())
return nil, nil
// state, err := bc.StateAt(bc.CurrentBlock().Root())
// if err != nil || state == nil {
// return nil, err
// }
// wrapper := state.GetStakingInfo(addr)
// if wrapper == nil {
// return nil, fmt.Errorf("ValidatorInformation not found: %v", addr)
// }
// return &wrapper.Validator, nil
low := sequenceL.Intn(fixedRandomGenStakeL)
high := sequenceL.Intn(fixedRandomGenStakeH)
r := sequenceL.Intn(fixedRandomGenStakeH) + 1
modBy := high - low + 1
if modBy <= 0 {
modBy *= -1
modBy++
}
someValidator.Stake = new(big.Int).Abs(big.NewInt(int64(
(r % modBy) + low,
)))
k := shard.BlsPublicKey{}
j := bls.PublicKey{}
j.DeserializeHexStr(blsKey1)
k.FromLibBLSPublicKey(&j)
someValidator.SlotPubKeys = []shard.BlsPublicKey{k}
tempBank[addr] = someValidator
}
}
// ValidatorCandidates returns the up to date validator candidates for next epoch
func (bc *BlockChain) ValidatorCandidates() []common.Address {
return addrs
}
// ValidatorInformation returns the information of validator
func (bc *BlockChain) ValidatorInformation(addr common.Address) (*staking.Validator, error) {
return tempBank[addr], nil
}
// DelegatorsInformation returns up to date information of delegators of a given validator address

@ -17,7 +17,7 @@ const (
localnetV1Epoch = 1
localnetV2Epoch = 2
localnetEpochBlock1 = 10
localnetEpochBlock1 = 5
twoOne = 5
localnetVdfDifficulty = 5000 // This takes about 10s to finish the vdf

@ -36,7 +36,8 @@ var (
ChainID: TestnetChainID,
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(0),
StakingEpoch: big.NewInt(5),
// MinEpoch needed is at least 1, crashes on 0
StakingEpoch: big.NewInt(1),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
}

@ -483,11 +483,6 @@ func (d Dec) RoundInt64() int64 {
return chopped.Int64()
}
// RoundInt round the decimal using bankers rounding
func (d Dec) RoundInt() *big.Int {
return chopPrecisionAndRoundNonMutative(d.Int)
}
//___________________________________________________________________________________
// similar to chopPrecisionAndRound, but always rounds down
@ -509,11 +504,6 @@ func (d Dec) TruncateInt64() int64 {
return chopped.Int64()
}
// TruncateInt truncates the decimals from the number and returns an Int
func (d Dec) TruncateInt() *big.Int {
return chopPrecisionAndTruncateNonMutative(d.Int)
}
// TruncateDec truncates the decimals from the number and returns a Dec
func (d Dec) TruncateDec() Dec {
return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int))

@ -1,6 +1,7 @@
package committee
import (
"encoding/json"
"fmt"
"math/big"
@ -16,6 +17,10 @@ import (
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
// ValidatorListProvider ..
type ValidatorListProvider interface {
Compute(
@ -26,7 +31,12 @@ type ValidatorListProvider interface {
// PublicKeysProvider per epoch
type PublicKeysProvider interface {
ComputePublicKeys(epoch *big.Int, reader DataProvider) [][]*bls.PublicKey
// If call shardID with StateID then only superCommittee is non-nil,
// otherwise get back the shardSpecific slice as well.
ComputePublicKeys(
epoch *big.Int, reader DataProvider, shardID int,
) (superCommittee, shardSpecific []*bls.PublicKey)
ReadPublicKeysFromDB(
hash common.Hash, reader DataProvider,
) ([]*bls.PublicKey, error)
@ -113,13 +123,12 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
return shardState
}
func eposStakedCommittee(
s shardingconfig.Instance, stakerReader DataProvider, stakedSlotsCount int,
func with400Stakers(
s shardingconfig.Instance, stakerReader DataProvider,
) (shard.State, error) {
// TODO Nervous about this because overtime the list will become quite large
candidates := stakerReader.ValidatorCandidates()
essentials := map[common.Address]effective.SlotOrder{}
stakers := make([]*staking.Validator, len(candidates))
// TODO benchmark difference if went with data structure that sorts on insert
for i := range candidates {
// TODO Should be using .ValidatorStakingWithDelegation, not implemented yet
@ -133,74 +142,152 @@ func eposStakedCommittee(
}
}
for i := range stakers {
staker := stakers[i]
stakers[i].Stake = new(big.Int).Div(
staker.Stake, big.NewInt(int64(len(staker.SlotPubKeys))),
)
}
unsortedStakes := make([]int, len(stakers))
eposStakes := make([]*big.Int, len(stakers))
for i, j := range stakers {
unsortedStakes[i] = int(j.Stake.Int64())
eposStakes[i] = j.Stake
}
s3 := effective.Apply(eposStakes)
sort.SliceStable(
stakers,
func(i, j int) bool { return stakers[i].Stake.Cmp(stakers[j].Stake) >= 0 },
)
// for i, j := range stakers {
// sortedStakes[i] = int(j.Stake.Int64())
// }
type t struct {
Stakes []int
}
t2 := t{make([]int, len(eposStakes))}
for i := range s3 {
t2.Stakes[i] = int(s3[i].TruncateInt64())
}
s1, _ := json.Marshal(t{unsortedStakes})
s2, _ := json.Marshal(t2)
fmt.Println("Unsorted")
fmt.Println(string(s1))
fmt.Println("as EPOS")
fmt.Println(string(s2))
// fmt.Println("Sorted stakers %+v\n", stakers)
shardCount := int(s.NumShards())
superComm := make(shard.State, shardCount)
hAccounts := s.HmyAccounts()
fillCount := make([]int, shardCount)
for i := 0; i < shardCount; i++ {
superComm[i] = shard.Committee{uint32(i), shard.NodeIDList{}}
superComm[i] = shard.Committee{
uint32(i), make(shard.NodeIDList, s.NumNodesPerShard()),
}
}
for i := range hAccounts {
spot := i % shardCount
shardBig := big.NewInt(int64(s.NumShards()))
for i := 0; i < len(s.FnAccounts()); i++ {
bucket := int(new(big.Int).Mod(stakers[i].Address.Big(), shardBig).Int64())
org := stakers[i].Stake
epos := big.NewInt(s3[i].TruncateInt64())
fmt.Println("stakes", org, epos)
superComm[bucket].NodeList[fillCount[bucket]] = shard.NodeID{
stakers[i].Address,
stakers[i].SlotPubKeys[0],
epos,
}
fillCount[bucket]++
}
hAccounts := s.HmyAccounts()
offset := 0
for i := range fillCount {
missing := s.NumNodesPerShard() - fillCount[i]
for j := 0; j < missing; j++ {
pub := &bls.PublicKey{}
pub.DeserializeHexStr(hAccounts[i].BlsPublicKey)
pub.DeserializeHexStr(hAccounts[offset].BlsPublicKey)
pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(pub)
superComm[spot].NodeList = append(superComm[spot].NodeList, shard.NodeID{
common2.ParseAddr(hAccounts[i].Address),
superComm[i].NodeList[fillCount[i]+j] = shard.NodeID{
common2.ParseAddr(hAccounts[offset].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
offset++
}
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,
})
}
fmt.Println("Final", superComm.JSON())
fmt.Println("stakers", fillCount)
return superComm, nil
}
// ComputePublicKeys produces publicKeys of entire supercommittee per epoch
// ComputePublicKeys produces publicKeys of entire supercommittee per epoch, optionally providing a
// shard specific subcommittee
func (def partialStakingEnabled) ComputePublicKeys(
epoch *big.Int, d DataProvider,
) [][]*bls.PublicKey {
epoch *big.Int, d DataProvider, shardID int,
) ([]*bls.PublicKey, []*bls.PublicKey) {
config := d.Config()
instance := shard.Schedule.InstanceForEpoch(epoch)
superComm := shard.State{}
if config.IsStaking(epoch) {
superComm, _ = eposStakedCommittee(instance, d, 320)
superComm, _ = with400Stakers(instance, d)
} else {
superComm = preStakingEnabledCommittee(instance)
}
allIdentities := make([][]*bls.PublicKey, len(superComm))
spot := 0
shouldBe := int(instance.NumShards()) * instance.NumNodesPerShard()
total := 0
for i := range superComm {
total += len(superComm[i].NodeList)
}
if shouldBe != total {
fmt.Println("Count mismatch", shouldBe, total)
}
allIdentities := make([]*bls.PublicKey, shouldBe)
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
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
return allIdentities, subCommitteeIdentities
}
func (def partialStakingEnabled) ReadPublicKeysFromDB(

@ -1,105 +1,54 @@
package effective
import (
"encoding/json"
"fmt"
"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)
// Stake computes the effective proof of stake as descibed in whitepaper
func Stake(median, actual *big.Int) numeric.Dec {
medianDec := numeric.NewDecFromBigInt(median)
actualDec := numeric.NewDecFromBigInt(actual)
left := numeric.MinDec(onePlusC.Mul(medianDec), actualDec)
right := oneMinusC.Mul(medianDec)
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 {
// Median find the median stake
func Median(stakes []*big.Int) *big.Int {
sort.SliceStable(
stakes,
func(i, j int) bool { return stakes[i].Dec.LTE(stakes[j].Dec) },
func(i, j int) bool { return stakes[i].Cmp(stakes[j]) <= 0 },
)
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)
middle := new(big.Int).Add(stakes[(l/2)-1], stakes[(l/2)+1])
return new(big.Int).Div(middle, big.NewInt(2))
default:
return stakes[l/2].Dec
return stakes[l/2]
}
}
// 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) },
)
func Apply(stakes []*big.Int) []numeric.Dec {
asNumeric := make([]numeric.Dec, len(stakes))
median := Median(stakes)
if l := len(eposedSlots); l < pull {
pull = l
}
picks := eposedSlots[:pull]
median := median(picks)
fmt.Println("Median is:", median.Uint64())
for i := range picks {
picks[i].Dec = effectiveStake(median, picks[i].Dec)
for i := range stakes {
asNumeric[i] = Stake(median, stakes[i])
}
return picks
return asNumeric
}

@ -0,0 +1,20 @@
package effective
import "github.com/harmony-one/harmony/numeric"
const (
// ValidatorsPerShard ..
ValidatorsPerShard = 400
// AllValidatorsCount ..
AllValidatorsCount = ValidatorsPerShard * 4
)
// StakeKeeper ..
type StakeKeeper interface {
// Activity
Inventory() struct {
BLSPublicKeys [][48]byte `json:"bls_pubkey"`
WithDelegationApplied []numeric.Dec `json:"with-delegation-applied,omitempty"`
// CurrentlyActive []bool
}
}

@ -198,12 +198,11 @@ 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,
val.Amount, new(big.Int), val.MinSelfDelegation, val.MaxTotalDelegation, false,
commission, desc, blockNum}
commission, desc, big.NewInt(0),
}
return &v, nil
}

Loading…
Cancel
Save