Skip to content

Commit

Permalink
Improve peer selection (#960)
Browse files Browse the repository at this point in the history
* improve participant peer selection

* add code comments
  • Loading branch information
Honglei-Cong authored and laizy committed Jun 19, 2019
1 parent a9be329 commit fe819b3
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 67 deletions.
101 changes: 48 additions & 53 deletions consensus/vbft/node_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,23 +205,16 @@ func (self *Server) buildParticipantConfig(blkNum uint32, block *Block, chainCfg
cfg := &BlockParticipantConfig{
BlockNum: blkNum,
Vrf: vrfValue,
ChainConfig: chainCfg, // TODO: copy chain config
ChainConfig: chainCfg,
}

s := 0

Proposers := calcParticipantPeers(cfg, chainCfg, s, s+vconfig.MAX_PROPOSER_COUNT)
if uint32(len(Proposers)) < chainCfg.C+1 {
cfg.Proposers, cfg.Endorsers, cfg.Committers = calcParticipantPeers(cfg, chainCfg)
if uint32(len(cfg.Proposers)) < chainCfg.C+1 {
return nil, fmt.Errorf("cfg Proposers length less than chainCfg.C:%d,%d", uint32(len(cfg.Proposers)), chainCfg.C)
}
cfg.Proposers = Proposers[:chainCfg.C+1]
s += vconfig.MAX_PROPOSER_COUNT
cfg.Endorsers = calcParticipantPeers(cfg, chainCfg, s, s+vconfig.MAX_ENDORSER_COUNT)
if uint32(len(cfg.Endorsers)) < 2*chainCfg.C {
return nil, fmt.Errorf("cfg.Endorsers length less than double chainCfg.C:%d,%d", uint32(len(cfg.Endorsers)), chainCfg.C)
}
s += vconfig.MAX_ENDORSER_COUNT
cfg.Committers = calcParticipantPeers(cfg, chainCfg, s, s+vconfig.MAX_COMMITTER_COUNT)
if uint32(len(cfg.Committers)) < 2*chainCfg.C {
return nil, fmt.Errorf("cfg.Committers length less than double chainCfg.C:%d,%d", uint32(len(cfg.Committers)), chainCfg.C)
}
Expand All @@ -231,62 +224,65 @@ func (self *Server) buildParticipantConfig(blkNum uint32, block *Block, chainCfg
return cfg, nil
}

func calcParticipantPeers(cfg *BlockParticipantConfig, chain *vconfig.ChainConfig, start, end int) []uint32 {
func calcParticipantPeers(cfg *BlockParticipantConfig, chain *vconfig.ChainConfig) ([]uint32, []uint32, []uint32) {

peers := make([]uint32, 0)
peerMap := make(map[uint32]bool)
proposerMap := make(map[uint32]bool)
var cnt uint32

if checkCalcEndorserOrCommitter(end) {
if len(cfg.Proposers) != 0 {
for _, p := range cfg.Proposers {
proposerMap[p] = true
if uint32(len(proposerMap)) >= chain.C {
break
}
}
}
}
for i := start; ; i++ {

// 1. select peers as many as possible
c := int(chain.C)
for i := 0; i < len(chain.PosTable); i++ {
peerId := calcParticipant(cfg.Vrf, chain.PosTable, uint32(i))
if peerId == math.MaxUint32 {
return []uint32{}
}
if checkCalcEndorserOrCommitter(end) {
if _, present := proposerMap[peerId]; present {
continue
}
break
}
if _, present := peerMap[peerId]; !present {
// got new peer
peers = append(peers, peerId)
peerMap[peerId] = true
cnt++
if cnt >= chain.N {
return peers
if len(peerMap) > (c+1)+((2*c+1)*2) || len(peerMap) == int(chain.N) {
break
}
}
if end == vconfig.MAX_PROPOSER_COUNT {
if i >= end && uint32(len(peers)) > chain.C {
return peers
}
}

if len(peerMap) <= c*2 {
log.Errorf("failed to get enough peers %v", peers)
return []uint32{}, []uint32{}, []uint32{}
}

// [p0, p1, p2, .... p_c+1, ... .. p_m, .... pn]
// <-- proposer --><--- endorser --><-- committer -->
nCommitter := 2*c + 1
propsers := peers[0 : c+1]
n1 := (len(peers) - len(propsers)) / 2
endorsers0 := peers[c+1 : c+1+n1]
committers := peers[c+1+n1:]

// copy endorser0 to endorser
endorsers := make([]uint32, 0)
endorsers = append(endorsers, endorsers0...)
if len(endorsers) < nCommitter {
// not enough endorser, get more from committer/proposer
endorsers = append(endorsers, propsers[c])
for i := c - 1; i > (c-1)/2 && len(endorsers) < nCommitter; i-- {
endorsers = append(endorsers, propsers[i])
}
if checkCalcEndorserOrCommitter(end) {
if uint32(len(peers)) > chain.C*2 {
return peers
}
for i := len(committers) - 1; i >= 0 && len(endorsers) < nCommitter; i-- {
endorsers = append(endorsers, committers[i])
}
}
return peers
}

func checkCalcEndorserOrCommitter(end int) bool {
if end == vconfig.MAX_ENDORSER_COUNT+vconfig.MAX_PROPOSER_COUNT ||
end == vconfig.MAX_PROPOSER_COUNT+vconfig.MAX_ENDORSER_COUNT+vconfig.MAX_COMMITTER_COUNT {
return true
if len(committers) < nCommitter {
// not enough committer, get more from endorser/proposer
committers = append(committers, propsers[c])
for i := (c - 1) / 2; i > 0 && len(committers) < nCommitter; i-- {
committers = append(committers, propsers[i])
}
for i := len(endorsers0) - 1; i >= 0 && len(committers) < nCommitter; i-- {
committers = append(committers, endorsers0[i])
}
}
return false

return propsers, endorsers, committers
}

func calcParticipant(vrf vconfig.VRFValue, dposTable []uint32, k uint32) uint32 {
Expand All @@ -297,8 +293,7 @@ func calcParticipant(vrf vconfig.VRFValue, dposTable []uint32, k uint32) uint32
if k >= 512 {
return math.MaxUint32
}
// FIXME:
// take 16bits random variable from vrf, if len(dposTable) is not power of 2,
// Note: take 16bits random variable from vrf, if len(dposTable) is not power of 2,
// this algorithm will break the fairness of vrf. to be fixed
v1 = uint32(vrf[bIdx]) >> bits1
if bIdx+1 < uint32(len(vrf)) {
Expand Down
70 changes: 65 additions & 5 deletions consensus/vbft/node_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package vbft

import (
"crypto/rand"
"crypto/sha512"
"testing"

"github.com/ontio/ontology/common"
Expand All @@ -43,7 +45,6 @@ func constructServer() *Server {
}
blockparticipantconfig := &BlockParticipantConfig{
BlockNum: 1,
L: uint32(2),
Proposers: []uint32{1, 2, 3},
Endorsers: []uint32{1, 2, 3},
Committers: []uint32{1, 2, 3},
Expand All @@ -52,21 +53,21 @@ func constructServer() *Server {
Version: 1,
View: 12,
N: 4,
C: 3,
C: 1,
BlockMsgDelay: 1000,
HashMsgDelay: 1000,
PeerHandshakeTimeout: 10000,
PosTable: []uint32{2, 3, 1, 3, 1, 3, 2, 3, 2, 3, 2, 1, 3},
PosTable: []uint32{2, 3, 1, 3, 1, 3, 2, 3, 2, 3, 2, 1, 3, 0},
}
chainstore := &ChainStore{
chainedBlockNum: 2,
}
server := &Server{
Index: 1,
stateMgr: statemgr,
config: chainconfig,
chainStore: chainstore,
currentParticipantConfig: blockparticipantconfig,
config: chainconfig,
chainStore: chainstore,
}
return server
}
Expand Down Expand Up @@ -150,3 +151,62 @@ func TestGetCommitConsensus(t *testing.T) {
blockproposer, flag := getCommitConsensus(commitMsgs, 2, 7)
t.Logf("TestGetCommitConsensus %d ,%v", blockproposer, flag)
}

func newTestVrfValue() vconfig.VRFValue {
v := make([]byte, 1024)
rand.Read(v[:])
t := sha512.Sum512(v)
f := sha512.Sum512(t[:])
return vconfig.VRFValue(f)
}

func TestCalcParticipantPeers(t *testing.T) {
for i := 4; i < 100; i++ {
testCalcParticipantPeers(t, i, (i-1)/3)
}
for i := 5; i < 100; i++ {
testCalcParticipantPeers(t, i, (i-1)/4)
}
}

func testCalcParticipantPeers(t *testing.T, n, c int) {
server := constructServer()

pos := make([]uint32, 0)
for i := 0; i < n; i++ {
for j := 0; j < 4; j++ {
pos = append(pos, uint32(i))
}
}

chainCfg := server.config
chainCfg.N = uint32(n)
chainCfg.C = uint32(c)
chainCfg.PosTable = pos

cfg := &BlockParticipantConfig{
BlockNum: 100,
Vrf: newTestVrfValue(),
ChainConfig: chainCfg,
}

pp, pe, pc := calcParticipantPeers(cfg, chainCfg)
if len(pp) != c+1 {
t.Fatalf("invalid peers(%d, %d): %v, %v, %v", n, c, pp, pe, pc)
}

// check how many peers are selected
peers := make(map[uint32]bool)
for _, p := range pp {
peers[p] = true
}
for _, p := range pe {
peers[p] = true
}
for _, p := range pc {
peers[p] = true
}
if len(peers) <= 2*c+1 {
t.Fatalf("peers(%d, %d, %d, %d, %d, %d): %v, %v, %v", n, c, len(peers), len(pp), len(pe), len(pc), pp, pe, pc)
}
}
1 change: 0 additions & 1 deletion consensus/vbft/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ type BftAction struct {

type BlockParticipantConfig struct {
BlockNum uint32
L uint32
Vrf vconfig.VRFValue
ChainConfig *vconfig.ChainConfig
Proposers []uint32
Expand Down
9 changes: 4 additions & 5 deletions consensus/vbft/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,16 @@ func HashMsg(msg ConsensusMsg) (common.Uint256, error) {
}

type seedData struct {
BlockNum uint32 `json:"block_num"`
PrevBlockProposer uint32 `json:"prev_block_proposer"` // TODO: change to NodeID
BlockRoot common.Uint256 `json:"block_root"`
VrfValue []byte `json:"vrf_value"`
BlockNum uint32 `json:"block_num"`
PrevBlockProposer uint32 `json:"prev_block_proposer"`
VrfValue []byte `json:"vrf_value"`
}

func getParticipantSelectionSeed(block *Block) vconfig.VRFValue {

data, err := json.Marshal(&seedData{
BlockNum: block.getBlockNum() + 1,
PrevBlockProposer: block.getProposer(),
BlockRoot: block.Block.Header.BlockRoot,
VrfValue: block.getVrfValue(),
})
if err != nil {
Expand Down Expand Up @@ -127,6 +125,7 @@ func verifyVrf(pk keypair.PublicKey, blkNum uint32, prevVrf, newVrf, proof []byt
}
return nil
}

func GetVbftConfigInfo(memdb *overlaydb.MemDB) (*config.VBFTConfig, error) {
//get governance view
goveranceview, err := GetGovernanceView(memdb)
Expand Down
29 changes: 26 additions & 3 deletions consensus/vbft/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
package vbft

import (
"encoding/base64"
"encoding/json"
"fmt"
"testing"

"github.com/ontio/ontology/account"
Expand Down Expand Up @@ -70,15 +73,35 @@ func TestHashMsg(t *testing.T) {
t.Logf("TestHashMsg succ: %v\n", uint256)
}

func TestVrf(t *testing.T) {
func TestVrfParticipantSeed(t *testing.T) {
blk, err := constructBlock()
if err != nil {
t.Errorf("constructBlock failed: %v", err)
}
vrfvalue := getParticipantSelectionSeed(blk)
if len(vrfvalue) == 0 {
t.Errorf("TestVrf failed:")
t.Errorf("TestVrfParticipantSeed failed:")
return
}
t.Log("TestVrf succ")
t.Log("TestVrfParticipantSeed succ")
}

func TestVrf(t *testing.T) {
user := account.NewAccount("")
prevVrf := []byte("test string")
blkNum := uint32(10)
v1, p1, err := computeVrf(user.PrivKey(), blkNum, prevVrf)
if err != nil {
t.Fatalf("compute vrf: %s", err)
}

if err := verifyVrf(user.PubKey(), blkNum, prevVrf, v1, p1); err != nil {
t.Fatalf("verify vrf: %s", err)
}

// test json byte formatting
data, _ := json.Marshal(&vrfData{10, prevVrf})
fmt.Println(string(data))
x := base64.StdEncoding.EncodeToString(prevVrf)
fmt.Println(x)
}

0 comments on commit fe819b3

Please sign in to comment.