Skip to content

Commit

Permalink
Merge pull request #1527 from orbs-network/feature/new-reputation
Browse files Browse the repository at this point in the history
new reputation calculation with a weighted non-repeated draw
  • Loading branch information
noambergIL authored Jan 30, 2020
2 parents cc198f5 + 3d36903 commit 0ee733d
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 139 deletions.
12 changes: 10 additions & 2 deletions services/consensuscontext/call_contract_ordered_committee.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package consensuscontext

import (
"bytes"
"context"
"github.com/orbs-network/orbs-network-go/config"
"github.com/orbs-network/orbs-network-go/crypto/digest"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/orbs-network/orbs-spec/types/go/services"
"github.com/orbs-network/scribe/log"
"github.com/pkg/errors"
"sort"
"time"
)

Expand Down Expand Up @@ -115,9 +117,15 @@ func (s *service) generateGenesisCommittee(ctx context.Context, currentBlockHeig
}

func generateGenesisCommitteeAddresses(nodes map[string]config.ValidatorNode) []byte {
res := make([]byte, 0, len(nodes)*digest.NODE_ADDRESS_SIZE_BYTES)
listOfAddresses := make([][]byte, 0, len(nodes))
for _, value := range nodes {
res = append(res, value.NodeAddress()...)
listOfAddresses = append(listOfAddresses, value.NodeAddress())
}
// committee contract expects same order for input on all nodes so a sort is a must here.
sort.Slice(listOfAddresses, func(i, j int) bool { return bytes.Compare(listOfAddresses[i], listOfAddresses[j]) > 0})
res := make([]byte, 0, len(nodes)*digest.NODE_ADDRESS_SIZE_BYTES)
for _, value := range listOfAddresses {
res = append(res, value...)
}
return res
}
Expand Down
86 changes: 51 additions & 35 deletions services/processor/native/repository/Committee/committee.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
package committee_systemcontract

import (
"bytes"
"encoding/binary"
"github.com/orbs-network/orbs-contract-sdk/go/sdk/v1/env"
"github.com/orbs-network/orbs-network-go/crypto/hash"
"math"
"sort"
)

/**
* This function is meant ot be used via the callsystemcontract func ... it will not give same result when used with RunQuery
*/
func getOrderedCommittee() []byte {
return getOrderedCommitteeForAddresses(_getElectedValidators())
}
Expand All @@ -28,54 +28,70 @@ func _getOrderedCommitteeForAddresses(addresses []byte) [][]byte {
return _getOrderedCommitteeArray(addressArray)
}

func getNextOrderedCommittee() [][]byte {
addresses := _split(_getElectedValidators())
return _orderList(addresses, _generateSeed(env.GetBlockHeight()+1))
}

func _getOrderedCommitteeArray(addresses [][]byte) [][]byte {
return _orderList(addresses, _generateSeed())
return _orderList(addresses, _generateSeed(env.GetBlockHeight()))
}

func _generateSeed() []byte {
func _generateSeed(blockHeight uint64) []byte {
seedBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(seedBytes, env.GetBlockHeight())
binary.LittleEndian.PutUint64(seedBytes, blockHeight)
return seedBytes
}

func _orderList(addrs [][]byte, seed []byte) [][]byte {
addrsToSort := addrsAndScores{addrs, make([]float64, len(addrs))}
for i, addr := range addrs {
addrsToSort.scores[i] = _calculateScoreWithReputation(addr, seed)
}
sort.Sort(addrsToSort)
return addrsToSort.addresses
}
func _orderList(addresses [][]byte, seed []byte) [][]byte {

func _calculateScoreWithReputation(addr []byte, seed []byte) float64 {
rep := getReputation(addr)
return float64(_calculateScore(addr, seed)) / _reputationAsFactor(rep)
}
committeeSize := len(addresses)
orderedCommitteeAddresses := make([][]byte, 0, committeeSize)
random := seed
accumulatedWeights, totalWeight := _calculateAccumulatedWeightArray(addresses)

func _calculateScore(addr []byte, seed []byte) uint32 {
random := hash.CalcSha256(addr, seed)
return binary.LittleEndian.Uint32(random[hash.SHA256_HASH_SIZE_BYTES-4:])
}
for j := 0; j < committeeSize; j++ {
random = _nextRandom(random)
curr := _getRandomWeight(random, totalWeight)

func _reputationAsFactor(reputation uint32) float64 {
return math.Pow(2, float64(reputation))
for i := 0; i < committeeSize; i++ {
if curr > accumulatedWeights[i] {
continue
}
orderedCommitteeAddresses = append(orderedCommitteeAddresses, addresses[i])
currWeight := _absoluteWeight(addresses[i])
totalWeight -= currWeight
accumulatedWeights[i] = 0
for k := i + 1; k < committeeSize; k++ {
if accumulatedWeights[k] != 0 {
accumulatedWeights[k] -= currWeight
}
}
break
}
}
return orderedCommitteeAddresses
}

type addrsAndScores struct {
addresses [][]byte
scores []float64
func _calculateAccumulatedWeightArray(addresses [][]byte) ([]int, int) {
accumulatedWeights := make([]int, len(addresses))
totalWeight := 0
for i, address := range addresses {
weight := _absoluteWeight(address)
totalWeight += weight
accumulatedWeights[i] = totalWeight
}
return accumulatedWeights, totalWeight
}

func (s addrsAndScores) Len() int {
return len(s.addresses)
func _absoluteWeight(address []byte) int {
return 1 << (_getMaxReputation() - getReputation(address))
}

func (s addrsAndScores) Swap(i, j int) {
s.addresses[i], s.addresses[j] = s.addresses[j], s.addresses[i]
s.scores[i], s.scores[j] = s.scores[j], s.scores[i]
func _nextRandom(random []byte) []byte {
return hash.CalcSha256(random)
}

// descending order
func (s addrsAndScores) Less(i, j int) bool {
return s.scores[i] > s.scores[j] || (s.scores[i] == s.scores[j] && bytes.Compare(s.addresses[i], s.addresses[j]) > 0)
func _getRandomWeight(random []byte, maxWeight int) int {
return int(binary.LittleEndian.Uint32(random[hash.SHA256_HASH_SIZE_BYTES-4:]) % uint32(maxWeight)) + 1
}
Loading

0 comments on commit 0ee733d

Please sign in to comment.