parent
32f4db4c4f
commit
09c427f080
@ -1,105 +1,54 @@ |
|||||||
package effective |
package effective |
||||||
|
|
||||||
import ( |
import ( |
||||||
"encoding/json" |
"fmt" |
||||||
"math/big" |
"math/big" |
||||||
"sort" |
"sort" |
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/harmony-one/harmony/numeric" |
"github.com/harmony-one/harmony/numeric" |
||||||
"github.com/harmony-one/harmony/shard" |
|
||||||
) |
) |
||||||
|
|
||||||
// medium.com/harmony-one/introducing-harmonys-effective-proof-of-stake-epos-2d39b4b8d58
|
// medium.com/harmony-one/introducing-harmonys-effective-proof-of-stake-epos-2d39b4b8d58
|
||||||
var ( |
var ( |
||||||
two = numeric.NewDecFromBigInt(big.NewInt(2)) |
|
||||||
c, _ = numeric.NewDecFromStr("0.15") |
c, _ = numeric.NewDecFromStr("0.15") |
||||||
onePlusC = numeric.OneDec().Add(c) |
onePlusC = numeric.OneDec().Add(c) |
||||||
oneMinusC = numeric.OneDec().Sub(c) |
oneMinusC = numeric.OneDec().Sub(c) |
||||||
) |
) |
||||||
|
|
||||||
func effectiveStake(median, actual numeric.Dec) numeric.Dec { |
// Stake computes the effective proof of stake as descibed in whitepaper
|
||||||
left := numeric.MinDec(onePlusC.Mul(median), actual) |
func Stake(median, actual *big.Int) numeric.Dec { |
||||||
right := oneMinusC.Mul(median) |
medianDec := numeric.NewDecFromBigInt(median) |
||||||
|
actualDec := numeric.NewDecFromBigInt(actual) |
||||||
|
left := numeric.MinDec(onePlusC.Mul(medianDec), actualDec) |
||||||
|
right := oneMinusC.Mul(medianDec) |
||||||
return numeric.MaxDec(left, right) |
return numeric.MaxDec(left, right) |
||||||
} |
} |
||||||
|
|
||||||
// SlotPurchase ..
|
// Median find the median stake
|
||||||
type SlotPurchase struct { |
func Median(stakes []*big.Int) *big.Int { |
||||||
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( |
sort.SliceStable( |
||||||
stakes, |
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 |
const isEven = 0 |
||||||
switch l := len(stakes); l % 2 { |
switch l := len(stakes); l % 2 { |
||||||
case isEven: |
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: |
default: |
||||||
return stakes[l/2].Dec |
return stakes[l/2] |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
// Apply ..
|
// Apply ..
|
||||||
func Apply(shortHand map[common.Address]SlotOrder, pull int) Slots { |
func Apply(stakes []*big.Int) []numeric.Dec { |
||||||
eposedSlots := Slots{} |
asNumeric := make([]numeric.Dec, len(stakes)) |
||||||
if len(shortHand) == 0 { |
median := Median(stakes) |
||||||
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 { |
fmt.Println("Median is:", median.Uint64()) |
||||||
pull = l |
|
||||||
} |
|
||||||
picks := eposedSlots[:pull] |
|
||||||
median := median(picks) |
|
||||||
|
|
||||||
for i := range picks { |
for i := range stakes { |
||||||
picks[i].Dec = effectiveStake(median, picks[i].Dec) |
asNumeric[i] = Stake(median, stakes[i]) |
||||||
} |
} |
||||||
|
return asNumeric |
||||||
return picks |
|
||||||
} |
} |
||||||
|
@ -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
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue