Commit 56e73c7f authored by 张振华's avatar 张振华

update

parent 69366e2b
...@@ -105,6 +105,13 @@ blockInterval=3 ...@@ -105,6 +105,13 @@ blockInterval=3
continueBlockNum=12 continueBlockNum=12
isValidator=false isValidator=false
rpcAddr="http://localhost:9801" rpcAddr="http://localhost:9801"
#shuffleType为1表示使用固定出块顺序,为2表示使用vrf信息进行出块顺序洗牌
shuffleType=2
#是否更新topN,如果为true,根据下面几个配置项定期更新topN节点;如果为false,则一直使用初始配置的节点,不关注投票结果
whetherUpdateTopN=true
blockNumToUpdateDelegate=20000
registTopNHeightLimit=100
updateTopNHeightLimit=200
[store] [store]
name="kvdb" name="kvdb"
......
...@@ -119,6 +119,7 @@ type ConsensusState struct { ...@@ -119,6 +119,7 @@ type ConsensusState struct {
vrfInfoMap map[int64] *dty.VrfInfo vrfInfoMap map[int64] *dty.VrfInfo
vrfInfosMap map[int64] []*dty.VrfInfo vrfInfosMap map[int64] []*dty.VrfInfo
cachedTopNCands []*dty.TopNCandidators
} }
// NewConsensusState returns a new ConsensusState. // NewConsensusState returns a new ConsensusState.
...@@ -284,6 +285,8 @@ func (cs *ConsensusState) handleMsg(mi MsgInfo) { ...@@ -284,6 +285,8 @@ func (cs *ConsensusState) handleMsg(mi MsgInfo) {
var err error var err error
msg, peerID, peerIP := mi.Msg, string(mi.PeerID), mi.PeerIP msg, peerID, peerIP := mi.Msg, string(mi.PeerID), mi.PeerIP
dposlog.Info("Recv consensus msg", "msg type", fmt.Sprintf("%T", msg), "peerid", peerID, "peerip", peerIP)
switch msg := msg.(type) { switch msg := msg.(type) {
case *dpostype.DPosVote: case *dpostype.DPosVote:
cs.dposState.recvVote(cs, msg) cs.dposState.recvVote(cs, msg)
...@@ -602,6 +605,99 @@ func (cs *ConsensusState) Init() { ...@@ -602,6 +605,99 @@ func (cs *ConsensusState) Init() {
cs.InitCycleBoundaryInfo(task) cs.InitCycleBoundaryInfo(task)
cs.InitCycleVrfInfo(task) cs.InitCycleVrfInfo(task)
cs.InitCycleVrfInfos(task) cs.InitCycleVrfInfos(task)
info := CalcTopNVersion(cs.client.GetCurrentHeight())
cs.InitTopNCandidators(info.Version)
}
// InitTopNCandidators method
func (cs *ConsensusState) InitTopNCandidators(version int64){
for version > 0 {
info, err := cs.client.QueryTopNCandidators(version)
if err == nil && info != nil && info.Status == dty.TopNCandidatorsVoteMajorOK{
cs.UpdateTopNCandidators(info)
return
}
version--
}
return
}
// UpdateTopNCandidators method
func (cs *ConsensusState) UpdateTopNCandidators(info *dty.TopNCandidators) {
if len(cs.cachedTopNCands) == 0 {
cs.cachedTopNCands = append(cs.cachedTopNCands, info)
return
}
if cs.cachedTopNCands[len(cs.cachedTopNCands) - 1].Version < info.Version {
cs.cachedTopNCands = append(cs.cachedTopNCands, info)
}
}
// GetTopNCandidatorsByVersion method
func (cs *ConsensusState) GetTopNCandidatorsByVersion(version int64)(info *dty.TopNCandidators) {
if len(cs.cachedTopNCands) == 0 || cs.cachedTopNCands[len(cs.cachedTopNCands) - 1].Version < version{
info, err := cs.client.QueryTopNCandidators(version)
if err == nil && info != nil {
if info.Status == dty.TopNCandidatorsVoteMajorOK {
cs.UpdateTopNCandidators(info)
}
return info
}
return nil
}
for i := len(cs.cachedTopNCands) - 1 ; i >= 0 ; i-- {
if cs.cachedTopNCands[i].Version == version {
return cs.cachedTopNCands[i]
} else if cs.cachedTopNCands[i].Version < version {
return nil
}
}
return nil
}
// GetLastestTopNCandidators method
func (cs *ConsensusState) GetLastestTopNCandidators()(info *dty.TopNCandidators) {
length := len(cs.cachedTopNCands)
if length > 0 {
return cs.cachedTopNCands[length -1]
}
return nil
}
// IsTopNRegisted method
func (cs *ConsensusState) IsTopNRegisted(info *dty.TopNCandidators) bool {
if nil == info {
return false
}
for i := 0; i < len(info.CandsVotes); i++ {
if bytes.Equal(info.CandsVotes[i].SignerPubkey, cs.privValidator.GetPubKey().Bytes()) {
return true
}
}
return false
}
// IsInTopN method
func (cs *ConsensusState) IsInTopN(info *dty.TopNCandidators) bool {
if nil == info || info.Status != dty.TopNCandidatorsVoteMajorOK || len(info.FinalCands) == 0 {
return false
}
for i := 0; i < len(info.FinalCands); i++ {
if bytes.Equal(info.FinalCands[i].Pubkey, cs.privValidator.GetPubKey().Bytes()) {
return true
}
}
return false
} }
// InitCycleBoundaryInfo method // InitCycleBoundaryInfo method
...@@ -748,7 +844,7 @@ func (cs *ConsensusState) SendCBTx(info *dty.DposCBInfo) bool { ...@@ -748,7 +844,7 @@ func (cs *ConsensusState) SendCBTx(info *dty.DposCBInfo) bool {
return false return false
} else { } else {
cs.privValidator.SignTx(tx) cs.privValidator.SignTx(tx)
dposlog.Info("Sign RecordCBTx.") dposlog.Info("Sign RecordCBTx ok.")
//将交易发往交易池中,方便后续重启或者新加入的超级节点查询 //将交易发往交易池中,方便后续重启或者新加入的超级节点查询
msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx) msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx)
err = cs.client.GetQueueClient().Send(msg, false) err = cs.client.GetQueueClient().Send(msg, false)
...@@ -772,7 +868,7 @@ func (cs *ConsensusState) SendRegistVrfMTx(info *dty.DposVrfMRegist) bool { ...@@ -772,7 +868,7 @@ func (cs *ConsensusState) SendRegistVrfMTx(info *dty.DposVrfMRegist) bool {
return false return false
} else { } else {
cs.privValidator.SignTx(tx) cs.privValidator.SignTx(tx)
dposlog.Info("Sign RegistVrfMTx.") dposlog.Info("Sign RegistVrfMTx ok.")
//将交易发往交易池中,方便后续重启或者新加入的超级节点查询 //将交易发往交易池中,方便后续重启或者新加入的超级节点查询
msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx) msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx)
err = cs.client.GetQueueClient().Send(msg, false) err = cs.client.GetQueueClient().Send(msg, false)
...@@ -795,7 +891,7 @@ func (cs *ConsensusState) SendRegistVrfRPTx(info *dty.DposVrfRPRegist) bool { ...@@ -795,7 +891,7 @@ func (cs *ConsensusState) SendRegistVrfRPTx(info *dty.DposVrfRPRegist) bool {
return false return false
} else { } else {
cs.privValidator.SignTx(tx) cs.privValidator.SignTx(tx)
dposlog.Info("Sign RegVrfRPTx.") dposlog.Info("Sign RegVrfRPTx ok.")
//将交易发往交易池中,方便后续重启或者新加入的超级节点查询 //将交易发往交易池中,方便后续重启或者新加入的超级节点查询
msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx) msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx)
err = cs.client.GetQueueClient().Send(msg, false) err = cs.client.GetQueueClient().Send(msg, false)
...@@ -803,7 +899,7 @@ func (cs *ConsensusState) SendRegistVrfRPTx(info *dty.DposVrfRPRegist) bool { ...@@ -803,7 +899,7 @@ func (cs *ConsensusState) SendRegistVrfRPTx(info *dty.DposVrfRPRegist) bool {
dposlog.Error("Send RegVrfRPTx to mempool failed.", "err", err) dposlog.Error("Send RegVrfRPTx to mempool failed.", "err", err)
return false return false
} else { } else {
dposlog.Error("Send RegVrfRPTx to mempool ok.", "err", err) dposlog.Info("Send RegVrfRPTx to mempool ok.", "err", err)
} }
} }
...@@ -979,6 +1075,16 @@ func (cs *ConsensusState) GetVrfInfosByCircle(cycle int64) (infos []*dty.VrfInfo ...@@ -979,6 +1075,16 @@ func (cs *ConsensusState) GetVrfInfosByCircle(cycle int64) (infos []*dty.VrfInfo
// ShuffleValidators method // ShuffleValidators method
func (cs *ConsensusState) ShuffleValidators(cycle int64){ func (cs *ConsensusState) ShuffleValidators(cycle int64){
if shuffleType == dposShuffleTypeFixOrderByAddr {
dposlog.Info("ShuffleType FixOrderByAddr,so do nothing", "cycle", cycle)
cs.validatorMgr.VrfValidators = nil
cs.validatorMgr.NoVrfValidators = nil
cs.validatorMgr.ShuffleCycle = cycle
cs.validatorMgr.ShuffleType = ShuffleTypeNoVrf
return
}
if cycle == cs.validatorMgr.ShuffleCycle { if cycle == cs.validatorMgr.ShuffleCycle {
//如果已经洗过牌,则直接返回,不重复洗牌 //如果已经洗过牌,则直接返回,不重复洗牌
dposlog.Info("Shuffle for this cycle is done already.", "cycle", cycle) dposlog.Info("Shuffle for this cycle is done already.", "cycle", cycle)
...@@ -987,16 +1093,11 @@ func (cs *ConsensusState) ShuffleValidators(cycle int64){ ...@@ -987,16 +1093,11 @@ func (cs *ConsensusState) ShuffleValidators(cycle int64){
cbInfo := cs.GetCBInfoByCircle(cycle - 1) cbInfo := cs.GetCBInfoByCircle(cycle - 1)
if cbInfo == nil { if cbInfo == nil {
dposlog.Info("GetCBInfoByCircle for Shuffle failed, don't use vrf to shuffle.", "cycle", cycle) dposlog.Info("GetCBInfoByCircle failed", "cycle", cycle)
} else {
cs.validatorMgr.VrfValidators = nil cs.validatorMgr.LastCycleBoundaryInfo = cbInfo
cs.validatorMgr.NoVrfValidators = nil dposlog.Info("GetCBInfoByCircle ok", "cycle", cycle, "stopHeight", cbInfo.StopHeight, "stopHash", cbInfo.StopHash)
cs.validatorMgr.ShuffleCycle = cycle
cs.validatorMgr.ShuffleType = ShuffleTypeNoVrf
return
} }
cs.validatorMgr.LastCycleBoundaryInfo = cbInfo
dposlog.Info("GetCBInfoByCircle for Shuffle ok", "cycle", cycle, "stopHeight", cbInfo.StopHeight, "stopHash", cbInfo.StopHash)
infos := cs.GetVrfInfosByCircle(cycle - 1) infos := cs.GetVrfInfosByCircle(cycle - 1)
if infos == nil { if infos == nil {
...@@ -1015,7 +1116,7 @@ func (cs *ConsensusState) ShuffleValidators(cycle int64){ ...@@ -1015,7 +1116,7 @@ func (cs *ConsensusState) ShuffleValidators(cycle int64){
for i := 0; i < len(infos); i++ { for i := 0; i < len(infos); i++ {
if isValidVrfInfo(infos[i]) { if isValidVrfInfo(infos[i]) {
var vrfBytes []byte var vrfBytes []byte
vrfBytes = append(vrfBytes, []byte(cbInfo.StopHash)...) //vrfBytes = append(vrfBytes, []byte(cbInfo.StopHash)...)
vrfBytes = append(vrfBytes, infos[i].R...) vrfBytes = append(vrfBytes, infos[i].R...)
item := &ttypes.Validator{ item := &ttypes.Validator{
...@@ -1089,4 +1190,44 @@ func (cs *ConsensusState) VrfEvaluate(input []byte)(hash [32]byte, proof []byte) ...@@ -1089,4 +1190,44 @@ func (cs *ConsensusState) VrfEvaluate(input []byte)(hash [32]byte, proof []byte)
// VrfEvaluate method // VrfEvaluate method
func (cs *ConsensusState) VrfProof(pubkey []byte, input []byte, hash [32]byte, proof []byte) bool{ func (cs *ConsensusState) VrfProof(pubkey []byte, input []byte, hash [32]byte, proof []byte) bool{
return cs.privValidator.VrfProof(pubkey, input, hash, proof) return cs.privValidator.VrfProof(pubkey, input, hash, proof)
}
// SendCBTx method
func (cs *ConsensusState) SendTopNRegistTx(reg *dty.TopNCandidatorRegist) bool {
//info.Pubkey = strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes()))
obj := dty.CanonicalTopNCandidator(reg.Cand)
reg.Cand.Hash = obj.ID()
reg.Cand.SignerPubkey = cs.privValidator.GetPubKey().Bytes()
byteCB, err := json.Marshal(reg.Cand)
if err != nil {
dposlog.Error("marshal TopNCandidator failed", "err", err)
}
sig, err := cs.privValidator.SignMsg(byteCB)
if err != nil {
dposlog.Error("TopNCandidator failed.", "err", err)
return false
} else {
reg.Cand.Signature = sig.Bytes()
tx, err := cs.client.CreateTopNRegistTx(reg)
if err != nil {
dposlog.Error("CreateTopNRegistTx failed.", "err", err)
return false
} else {
cs.privValidator.SignTx(tx)
dposlog.Info("Sign TopNRegistTx ok.")
//将交易发往交易池中,方便后续重启或者新加入的超级节点查询
msg := cs.client.GetQueueClient().NewMessage("mempool", types.EventTx, tx)
err = cs.client.GetQueueClient().Send(msg, false)
if err != nil {
dposlog.Error("Send TopNRegistTx to mempool failed.", "err", err)
return false
} else {
dposlog.Info("Send TopNRegistTx to mempool ok.")
}
}
}
return true
} }
\ No newline at end of file
...@@ -7,11 +7,11 @@ package dpos ...@@ -7,11 +7,11 @@ package dpos
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/util" "github.com/33cn/chain33/util"
"strings" "strings"
"time" "time"
"fmt"
"github.com/33cn/chain33/common/crypto" "github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15" "github.com/33cn/chain33/common/log/log15"
...@@ -27,7 +27,8 @@ import ( ...@@ -27,7 +27,8 @@ import (
) )
const dposVersion = "0.1.0" const dposVersion = "0.1.0"
const dposShuffleTypeFixOrderByAddr = 1
const dposShuffleTypeOrderByVrfInfo = 2
var ( var (
dposlog = log15.New("module", "dpos") dposlog = log15.New("module", "dpos")
genesis string genesis string
...@@ -48,6 +49,11 @@ var ( ...@@ -48,6 +49,11 @@ var (
zeroHash [32]byte zeroHash [32]byte
dposPort string = "36656" dposPort string = "36656"
rpcAddr string = "http://0.0.0.0:8801" rpcAddr string = "http://0.0.0.0:8801"
shuffleType int32 = dposShuffleTypeOrderByVrfInfo //shuffleType为1表示使用固定出块顺序,为2表示使用vrf信息进行出块顺序洗牌
whetherUpdateTopN = false //是否更新topN,如果为true,根据下面几个配置项定期更新topN节点;如果为false,则一直使用初始配置的节点,不关注投票结果
blockNumToUpdateDelegate int64 = 20000
registTopNHeightLimit int64 = 100
updateTopNHeightLimit int64 = 200
) )
func init() { func init() {
...@@ -86,6 +92,11 @@ type subConfig struct { ...@@ -86,6 +92,11 @@ type subConfig struct {
IsValidator bool `json:"isValidator"` IsValidator bool `json:"isValidator"`
Port string `json:"port"` Port string `json:"port"`
RpcAddr string `json:"rpcAddr"` RpcAddr string `json:"rpcAddr"`
ShuffleType int32 `json:"shuffleType"`
WhetherUpdateTopN bool `json:"whetherUpdateTopN"`
BlockNumToUpdateDelegate int64 `json:"blockNumToUpdateDelegate"`
RegistTopNHeightLimit int64 `json:"registTopNHeightLimit"`
UpdateTopNHeightLimit int64 `json:"updateTopNHeightLimit"`
} }
func (client *Client) applyConfig(sub []byte) { func (client *Client) applyConfig(sub []byte) {
...@@ -147,6 +158,26 @@ func (client *Client) applyConfig(sub []byte) { ...@@ -147,6 +158,26 @@ func (client *Client) applyConfig(sub []byte) {
if subcfg.RpcAddr != "" { if subcfg.RpcAddr != "" {
rpcAddr = subcfg.RpcAddr rpcAddr = subcfg.RpcAddr
} }
if subcfg.ShuffleType > 0 {
shuffleType = subcfg.ShuffleType
}
if subcfg.WhetherUpdateTopN {
whetherUpdateTopN = subcfg.WhetherUpdateTopN
}
if subcfg.BlockNumToUpdateDelegate > 0 {
blockNumToUpdateDelegate = subcfg.BlockNumToUpdateDelegate
}
if subcfg.RegistTopNHeightLimit > 0 {
registTopNHeightLimit = subcfg.RegistTopNHeightLimit
}
if subcfg.UpdateTopNHeightLimit > 0 {
updateTopNHeightLimit = subcfg.UpdateTopNHeightLimit
}
} }
// New ... // New ...
...@@ -282,28 +313,38 @@ OuterLoop: ...@@ -282,28 +313,38 @@ OuterLoop:
} }
if block != nil { if block != nil {
//time.Sleep(time.Second * 5) //time.Sleep(time.Second * 5)
cands, err := client.QueryCandidators() //cands, err := client.QueryCandidators()
if err != nil { info := CalcTopNVersion(block.Height)
dposlog.Info("QueryCandidators failed", "err", err) version := info.Version
} else { var topN *dty.TopNCandidators
if len(cands) != int(dposDelegateNum) { for version >= 0 {
dposlog.Info("QueryCandidators success but no enough candidators", "dposDelegateNum", dposDelegateNum, "candidatorNum", len(cands)) topN, err = client.QueryTopNCandidators(version)
if err !=nil || topN == nil {
version --
} else { } else {
validators := make([]*ttypes.Validator, dposDelegateNum) break
nodes := make([]string, dposDelegateNum) }
for i, val := range cands { }
// Make validator
validators[i] = &ttypes.Validator{ if topN == nil {
Address: address.PubKeyToAddress(val.Pubkey).Hash160[:], dposlog.Info("QueryTopNCandidators failed, no candidators")
PubKey: val.Pubkey, } else if len(topN.FinalCands) != int(dposDelegateNum) {
} dposlog.Info("QueryTopNCandidators success but no enough candidators", "dposDelegateNum", dposDelegateNum, "candidatorNum", len(topN.FinalCands))
nodes[i] = val.Ip + ":" + dposPort } else {
validators := make([]*ttypes.Validator, dposDelegateNum)
nodes := make([]string, dposDelegateNum)
for i, val := range topN.FinalCands {
// Make validator
validators[i] = &ttypes.Validator{
Address: address.PubKeyToAddress(val.Pubkey).Hash160[:],
PubKey: val.Pubkey,
} }
valMgr.Validators = ttypes.NewValidatorSet(validators) nodes[i] = val.Ip + ":" + dposPort
dposlog.Info("QueryCandidators success and update validator set", "old validators", printValidators(valMgrTmp.Validators), "new validators", printValidators(valMgr.Validators))
dposlog.Info("QueryCandidators success and update validator node ips", "old validator ips", printNodeIPs(validatorNodes), "new validators ips", printNodeIPs(nodes))
validatorNodes = nodes
} }
valMgr.Validators = ttypes.NewValidatorSet(validators)
dposlog.Info("QueryCandidators success and update validator set", "old validators", printValidators(valMgrTmp.Validators), "new validators", printValidators(valMgr.Validators))
dposlog.Info("QueryCandidators success and update validator node ips", "old validator ips", printNodeIPs(validatorNodes), "new validators ips", printNodeIPs(nodes))
validatorNodes = nodes
} }
} }
...@@ -514,52 +555,6 @@ func (client *Client)QueryCandidators()([]*dty.Candidator, error) { ...@@ -514,52 +555,6 @@ func (client *Client)QueryCandidators()([]*dty.Candidator, error) {
return cands, nil return cands, nil
} }
func (client *Client)MonitorCandidators() {
ticker := time.NewTicker(30 * time.Second)
for {
select {
case <- ticker.C:
dposlog.Info("Monitor Candidators")
block, err := client.RequestLastBlock()
if err != nil {
panic(err)
}
if block != nil {
cands, err := client.QueryCandidators()
if err != nil {
dposlog.Info("Query Candidators failed", "err", err)
} else {
if len(cands) != int(dposDelegateNum) {
dposlog.Info("QueryCandidators success but no enough candidators", "dposDelegateNum", dposDelegateNum, "candidatorNum", len(cands))
} else {
validators := make([]*ttypes.Validator, dposDelegateNum)
for i, val := range cands {
// Make validator
validators[i] = &ttypes.Validator{
Address: address.PubKeyToAddress(val.Pubkey).Hash160[:],
PubKey: val.Pubkey,
}
}
validatorSet := ttypes.NewValidatorSet(validators)
dposlog.Info("QueryCandidators success and update validator set")
if !client.isValidatorSetSame(validatorSet, client.csState.validatorMgr.Validators){
dposlog.Info("ValidatorSet from contract is changed, so stop the node and restart the consensus.")
client.node.Stop()
time.Sleep(time.Second * 3)
go client.StartConsensus()
} else {
dposlog.Info("ValidatorSet from contract is the same,no change.")
}
}
}
}
}
}
}
func (client *Client)isValidatorSetSame(v1, v2 *ttypes.ValidatorSet) bool { func (client *Client)isValidatorSetSame(v1, v2 *ttypes.ValidatorSet) bool {
if v1 == nil || v2 == nil || len(v1.Validators) != len(v2.Validators){ if v1 == nil || v2 == nil || len(v1.Validators) != len(v2.Validators){
return false return false
...@@ -697,3 +692,62 @@ func (client *Client)QueryVrfInfos(pubkeys [][]byte, cycle int64)([]*dty.VrfInfo ...@@ -697,3 +692,62 @@ func (client *Client)QueryVrfInfos(pubkeys [][]byte, cycle int64)([]*dty.VrfInfo
return infos, nil return infos, nil
} }
func (client *Client)CreateTopNRegistTx(reg *dty.TopNCandidatorRegist)(tx*types.Transaction, err error) {
var action dty.DposVoteAction
action.Value = &dty.DposVoteAction_RegistTopN{
RegistTopN: reg,
}
action.Ty = dty.DPosVoteActionRegistTopNCandidator
tx, err = types.CreateFormatTx("dpos", types.Encode(&action))
if err != nil {
return nil, err
}
return tx, nil
}
// QueryCycleBoundaryInfo method
func (client *Client) QueryTopNCandidators(version int64)(*dty.TopNCandidators, error){
req := &dty.TopNCandidatorsQuery{Version: version}
param, err := proto.Marshal(req)
if err != nil {
dposlog.Error("Marshal TopNCandidatorsQuery failed", "version", version, "err", err)
return nil, err
}
msg := client.GetQueueClient().NewMessage("execs", types.EventBlockChainQuery,
&types.ChainExecutor{
Driver: dty.DPosX,
FuncName: dty.FuncNameQueryTopNByVersion,
StateHash: zeroHash[:],
Param:param,
})
err = client.GetQueueClient().Send(msg, true)
if err != nil {
dposlog.Error("send TopNCandidatorsQuery to dpos exec failed", "version", version, "err", err)
return nil, err
}
msg, err = client.GetQueueClient().Wait(msg)
if err != nil {
dposlog.Error("send TopNCandidatorsQuery wait failed", "version", version, "err", err)
return nil, err
}
res := msg.GetData().(types.Message).(*dty.TopNCandidatorsReply)
info := res.TopN
dposlog.Info("TopNCandidatorsQuery get reply", "version", info.Version, "status", info.Status, "final candidators", printCandidators(info.FinalCands))
return info, nil
}
func printCandidators(cands []*dty.Candidator) string {
result := "["
for i := 0; i < len(cands); i++ {
fmt.Sprintf("%spubkey:%s,ip:%s;", result, hex.EncodeToString(cands[i].Pubkey), cands[i].Ip)
}
result += "]"
return result
}
\ No newline at end of file
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"math" "math"
"strings" "strings"
"time" "time"
"os"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto" "github.com/33cn/chain33/common/crypto"
...@@ -53,6 +53,9 @@ var WaitNotifyStateObj = &WaitNofifyState{} ...@@ -53,6 +53,9 @@ var WaitNotifyStateObj = &WaitNofifyState{}
var LastCheckVrfMTime = int64(0) var LastCheckVrfMTime = int64(0)
var LastCheckVrfRPTime = int64(0) var LastCheckVrfRPTime = int64(0)
var LastCheckRegTopNTime = int64(0)
var LastCheckUpdateTopNTime = int64(0)
// Task 为计算当前时间所属周期的数据结构 // Task 为计算当前时间所属周期的数据结构
type Task struct { type Task struct {
NodeID int64 NodeID int64
...@@ -65,6 +68,26 @@ type Task struct { ...@@ -65,6 +68,26 @@ type Task struct {
BlockStop int64 BlockStop int64
} }
type TopNVersionInfo struct {
Version int64
HeightStart int64
HeightStop int64
HeightToStart int64
HeightRegLimit int64
HeightUpdateLimit int64
}
func CalcTopNVersion(height int64) (info TopNVersionInfo) {
info = TopNVersionInfo{}
info.Version = height / blockNumToUpdateDelegate
info.HeightToStart = height % blockNumToUpdateDelegate
info.HeightStart = info.Version * blockNumToUpdateDelegate
info.HeightStop = (info.Version + 1 ) * blockNumToUpdateDelegate - 1
info.HeightRegLimit = info.HeightStart + registTopNHeightLimit
info.HeightUpdateLimit = info.HeightStart + updateTopNHeightLimit
return info
}
// DecideTaskByTime 根据时间戳计算所属的周期,包括cycle周期,负责出块周期,当前出块周期 // DecideTaskByTime 根据时间戳计算所属的周期,包括cycle周期,负责出块周期,当前出块周期
func DecideTaskByTime(now int64) (task Task) { func DecideTaskByTime(now int64) (task Task) {
task.NodeID = now % dposCycle / dposPeriod task.NodeID = now % dposCycle / dposPeriod
...@@ -138,6 +161,10 @@ func generateVote(cs *ConsensusState) *dpostype.Vote { ...@@ -138,6 +161,10 @@ func generateVote(cs *ConsensusState) *dpostype.Vote {
} }
func checkVrf(cs *ConsensusState) { func checkVrf(cs *ConsensusState) {
if shuffleType != dposShuffleTypeOrderByVrfInfo {
return
}
now := time.Now().Unix() now := time.Now().Unix()
task := DecideTaskByTime(now) task := DecideTaskByTime(now)
middleTime := task.CycleStart + (task.CycleStop - task.CycleStart) / 2 middleTime := task.CycleStart + (task.CycleStop - task.CycleStart) / 2
...@@ -191,6 +218,99 @@ func checkVrf(cs *ConsensusState) { ...@@ -191,6 +218,99 @@ func checkVrf(cs *ConsensusState) {
} }
func checkTopNRegist(cs *ConsensusState) {
if whetherUpdateTopN == false {
return
}
now := time.Now().Unix()
if now - LastCheckRegTopNTime < dposBlockInterval * 3 {
//避免短时间频繁检查,5个区块以内不重复检查
return
}
height := cs.client.GetCurrentHeight()
info := CalcTopNVersion(height)
if height <= info.HeightRegLimit {
//在注册TOPN的区块区间内,则检查本节点是否注册成功,如果否则进行注册
topN := cs.GetTopNCandidatorsByVersion(info.Version)
if topN == nil || !cs.IsTopNRegisted(topN) {
cands, err := cs.client.QueryCandidators()
if err != nil || cands == nil {
dposlog.Error("QueryCandidators failed", "now", now, "height", height, "HeightRegLimit", info.HeightRegLimit, "pubkey", strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes())))
LastCheckRegTopNTime = now
return
}
topNCand := &dty.TopNCandidator {
Cands: cands,
Height: height,
SignerPubkey: cs.privValidator.GetPubKey().Bytes(),
}
obj := dty.CanonicalTopNCandidator(topNCand)
topNCand.Hash = obj.ID()
regist := &dty.TopNCandidatorRegist {
Cand: topNCand,
}
cs.SendTopNRegistTx(regist)
LastCheckRegTopNTime = now
} else {
dposlog.Info("TopN is already registed", "now", now, "height", height, "HeightRegLimit", info.HeightRegLimit, "pubkey", strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes())))
LastCheckRegTopNTime = now + (info.HeightStop - height) * dposBlockInterval
}
} else {
LastCheckRegTopNTime = now + (info.HeightStop - height) * dposBlockInterval
}
}
func checkTopNUpdate(cs *ConsensusState) {
if whetherUpdateTopN == false {
return
}
now := time.Now().Unix()
if now - LastCheckUpdateTopNTime < dposBlockInterval * 1 {
//避免短时间频繁检查,1个区块以内不重复检查
return
}
height := cs.client.GetCurrentHeight()
info := CalcTopNVersion(height)
if height >= info.HeightUpdateLimit {
topN := cs.GetLastestTopNCandidators()
if nil == topN {
dposlog.Error("No valid topN, do nothing", "now", now, "height", height, "HeightUpdateLimit", info.HeightUpdateLimit, "pubkey", strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes())))
LastCheckUpdateTopNTime = now + (info.HeightStop - height) * dposBlockInterval
return
}
for i := 0; i < len(topN.FinalCands); i++ {
if isPubkeyExist(topN.FinalCands[i].Pubkey, cs.validatorMgr.Validators.Validators) {
continue
} else {
dposlog.Error("TopN changed, so restart to use latest topN", "now", now, "height", height, "HeightUpdateLimit", info.HeightUpdateLimit, "pubkey", strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes())))
os.Exit(0)
}
}
dposlog.Info("TopN not changed,so do nothing", "now", now, "height", height, "HeightUpdateLimit", info.HeightUpdateLimit, "pubkey", strings.ToUpper(hex.EncodeToString(cs.privValidator.GetPubKey().Bytes())))
LastCheckUpdateTopNTime = now + (info.HeightStop - height) * dposBlockInterval
} else {
LastCheckUpdateTopNTime = now + (info.HeightUpdateLimit - height - 1) * dposBlockInterval
}
}
func isPubkeyExist(pubkey []byte, validators []*dpostype.Validator) bool {
for i := 0; i < len(validators); i++ {
if bytes.Equal(pubkey, validators[i].PubKey) {
return true
}
}
return false
}
func recvCBInfo(cs *ConsensusState, info *dpostype.DPosCBInfo) { func recvCBInfo(cs *ConsensusState, info *dpostype.DPosCBInfo) {
newInfo := &dty.DposCBInfo{ newInfo := &dty.DposCBInfo{
Cycle: info.Cycle, Cycle: info.Cycle,
...@@ -432,9 +552,11 @@ func (voted *VotedState) timeOut(cs *ConsensusState) { ...@@ -432,9 +552,11 @@ func (voted *VotedState) timeOut(cs *ConsensusState) {
//当前时间超过了节点切换时间,需要进行重新投票 //当前时间超过了节点切换时间,需要进行重新投票
dposlog.Info("VotedState timeOut over periodStop.", "periodStop", cs.currentVote.PeriodStop, "cycleStop", cs.currentVote.CycleStop) dposlog.Info("VotedState timeOut over periodStop.", "periodStop", cs.currentVote.PeriodStop, "cycleStop", cs.currentVote.CycleStop)
isCycleSwith := false
//如果到了cycle结尾,需要构造一个交易,把最终的CycleBoundary信息发布出去 //如果到了cycle结尾,需要构造一个交易,把最终的CycleBoundary信息发布出去
if cs.currentVote.PeriodStop == cs.currentVote.CycleStop { if cs.currentVote.PeriodStop == cs.currentVote.CycleStop {
dposlog.Info("Create new tx for cycle change to record cycle boundary info.", "height", block.Height) dposlog.Info("Create new tx for cycle change to record cycle boundary info.", "height", block.Height)
isCycleSwith = true
info := &dty.DposCBInfo{ info := &dty.DposCBInfo{
Cycle: cs.currentVote.Cycle, Cycle: cs.currentVote.Cycle,
...@@ -492,13 +614,25 @@ func (voted *VotedState) timeOut(cs *ConsensusState) { ...@@ -492,13 +614,25 @@ func (voted *VotedState) timeOut(cs *ConsensusState) {
cs.SetNotify(notify2.DPosNotify) cs.SetNotify(notify2.DPosNotify)
cs.dposState.sendNotify(cs, notify.DPosNotify) cs.dposState.sendNotify(cs, notify.DPosNotify)
cs.ClearVotes() cs.ClearVotes()
//检查是否需要更新TopN,如果有更新,则更新TOPN节点后进入新的状态循环。
if isCycleSwith {
checkTopNUpdate(cs)
}
cs.SetState(InitStateObj) cs.SetState(InitStateObj)
dposlog.Info("Change state because of time.", "from", "VotedState", "to", "InitState") dposlog.Info("Change state because of time.", "from", "VotedState", "to", "InitState")
cs.scheduleDPosTimeout(time.Duration(timeoutCheckConnections)*time.Millisecond, InitStateType) cs.scheduleDPosTimeout(time.Duration(timeoutCheckConnections)*time.Millisecond, InitStateType)
return return
} }
//根据时间进行vrf相关处理,如果在(cyclestart,middle)之间,发布M,如果在(middle,cyclestop)之间,发布R、P
checkVrf(cs) checkVrf(cs)
//检查是否应该注册topN,是否已经注册topN
checkTopNRegist(cs)
//当前时间未到节点切换时间,则继续进行出块判断 //当前时间未到节点切换时间,则继续进行出块判断
if block.BlockTime >= task.BlockStop { if block.BlockTime >= task.BlockStop {
//已出块,或者时间落后了。 //已出块,或者时间落后了。
...@@ -534,6 +668,10 @@ func (voted *VotedState) timeOut(cs *ConsensusState) { ...@@ -534,6 +668,10 @@ func (voted *VotedState) timeOut(cs *ConsensusState) {
//根据时间进行vrf相关处理,如果在(cyclestart,middle)之间,发布M,如果在(middle,cyclestop)之间,发布R、P //根据时间进行vrf相关处理,如果在(cyclestart,middle)之间,发布M,如果在(middle,cyclestop)之间,发布R、P
checkVrf(cs) checkVrf(cs)
//检查是否应该注册topN,是否已经注册topN
checkTopNRegist(cs)
//非当前出块节点,如果到了切换出块节点的时间,则进行状态切换,进行投票 //非当前出块节点,如果到了切换出块节点的时间,则进行状态切换,进行投票
if now >= cs.currentVote.PeriodStop { if now >= cs.currentVote.PeriodStop {
//当前时间超过了节点切换时间,需要进行重新投票 //当前时间超过了节点切换时间,需要进行重新投票
...@@ -622,6 +760,13 @@ type WaitNofifyState struct { ...@@ -622,6 +760,13 @@ type WaitNofifyState struct {
func (wait *WaitNofifyState) timeOut(cs *ConsensusState) { func (wait *WaitNofifyState) timeOut(cs *ConsensusState) {
//cs.clearVotes() //cs.clearVotes()
//检查是否需要更新TopN,如果有更新,则更新TOPN节点后进入新的状态循环。
now := time.Now().Unix()
if now >= cs.lastVote.PeriodStop && cs.lastVote.PeriodStop == cs.lastVote.CycleStop {
checkTopNUpdate(cs)
}
cs.SetState(InitStateObj) cs.SetState(InitStateObj)
dposlog.Info("Change state because of time.", "from", "WaitNofifyState", "to", "InitState") dposlog.Info("Change state because of time.", "from", "WaitNofifyState", "to", "InitState")
cs.scheduleDPosTimeout(time.Duration(timeoutCheckConnections)*time.Millisecond, InitStateType) cs.scheduleDPosTimeout(time.Duration(timeoutCheckConnections)*time.Millisecond, InitStateType)
...@@ -693,6 +838,12 @@ func (wait *WaitNofifyState) recvNotify(cs *ConsensusState, notify *dpostype.DPo ...@@ -693,6 +838,12 @@ func (wait *WaitNofifyState) recvNotify(cs *ConsensusState, notify *dpostype.DPo
cs.SaveNotify() cs.SaveNotify()
cs.SetNotify(notify) cs.SetNotify(notify)
//检查是否需要更新TopN,如果有更新,则更新TOPN节点后进入新的状态循环。
now := time.Now().Unix()
if now >= cs.lastVote.PeriodStop && cs.lastVote.PeriodStop == cs.lastVote.CycleStop {
checkTopNUpdate(cs)
}
cs.SetState(InitStateObj) cs.SetState(InitStateObj)
dposlog.Info("Change state because recv notify.", "from", "WaitNofifyState", "to", "InitState") dposlog.Info("Change state because recv notify.", "from", "WaitNofifyState", "to", "InitState")
cs.dposState.timeOut(cs) cs.dposState.timeOut(cs)
......
...@@ -53,7 +53,9 @@ func DPosCmd() *cobra.Command { ...@@ -53,7 +53,9 @@ func DPosCmd() *cobra.Command {
DPosCreateCmd(), DPosCreateCmd(),
DPosVrfVerifyCmd(), DPosVrfVerifyCmd(),
DPosVrfEvaluateCmd(), DPosVrfEvaluateCmd(),
DPosCBRecordCmd(),
DPosCBQueryCmd(), DPosCBQueryCmd(),
DPosTopNQueryCmd(),
) )
return cmd return cmd
...@@ -797,9 +799,9 @@ func DPosCBRecordCmd() *cobra.Command { ...@@ -797,9 +799,9 @@ func DPosCBRecordCmd() *cobra.Command {
func addCBRecordCmdFlags(cmd *cobra.Command) { func addCBRecordCmdFlags(cmd *cobra.Command) {
cmd.Flags().Int64P("cycle", "c", 0, "cycle") cmd.Flags().Int64P("cycle", "c", 0, "cycle")
cmd.MarkFlagRequired("cycle") cmd.MarkFlagRequired("cycle")
cmd.Flags().Int64P("height", "h", 0, "height") cmd.Flags().Int64P("height", "m", 0, "height")
cmd.MarkFlagRequired("height") cmd.MarkFlagRequired("height")
cmd.Flags().StringP("hash", "m", "", "block hash") cmd.Flags().StringP("hash", "s", "", "block hash")
cmd.MarkFlagRequired("hash") cmd.MarkFlagRequired("hash")
cmd.Flags().StringP("privKey", "k", "", "private key") cmd.Flags().StringP("privKey", "k", "", "private key")
cmd.MarkFlagRequired("privKey") cmd.MarkFlagRequired("privKey")
...@@ -867,7 +869,7 @@ func recordCB(cmd *cobra.Command, args []string) { ...@@ -867,7 +869,7 @@ func recordCB(cmd *cobra.Command, args []string) {
ctx.RunWithoutMarshal() ctx.RunWithoutMarshal()
} }
//DPosVrfQueryCmd 构造VRF相关信息查询的命令行 //DPosCBQueryCmd 查询Cycle Boundary info的命令
func DPosCBQueryCmd() *cobra.Command { func DPosCBQueryCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "cbQuery", Use: "cbQuery",
...@@ -934,4 +936,39 @@ func cbQuery(cmd *cobra.Command, args []string) { ...@@ -934,4 +936,39 @@ func cbQuery(cmd *cobra.Command, args []string) {
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res) ctx := jsonrpc.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
ctx.Run() ctx.Run()
} }
}
//DPosVrfQueryCmd 构造VRF相关信息查询的命令行
func DPosTopNQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "topNQuery",
Short: "query topN info",
Run: topNQuery,
}
addTopNQueryFlags(cmd)
return cmd
}
func addTopNQueryFlags(cmd *cobra.Command) {
cmd.Flags().Int64P("version", "v", 0, "version")
cmd.MarkFlagRequired("version")
}
func topNQuery(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
version, _ := cmd.Flags().GetInt64("version")
var params rpctypes.Query4Jrpc
params.Execer = dty.DPosX
req := &dty.TopNCandidatorsQuery{
Version: version,
}
params.FuncName = dty.FuncNameQueryTopNByVersion
params.Payload = types.MustPBToJSON(req)
var res dty.TopNCandidatorsReply
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
ctx.Run()
} }
\ No newline at end of file
...@@ -16,11 +16,14 @@ var logger = log.New("module", "execs.dposvote") ...@@ -16,11 +16,14 @@ var logger = log.New("module", "execs.dposvote")
var driverName = dty.DPosX var driverName = dty.DPosX
var ( var (
dposDelegateNum int64 = 3 //委托节点个数,从配置读取,以后可以根据投票结果来定 dposDelegateNum int64 = 3 //委托节点个数,从配置读取,以后可以根据投票结果来定
dposBlockInterval int64 = 3 //出块间隔,当前按3s dposBlockInterval int64 = 3 //出块间隔,当前按3s
dposContinueBlockNum int64 = 6 //一个委托节点当选后,一次性持续出块数量 dposContinueBlockNum int64 = 6 //一个委托节点当选后,一次性持续出块数量
dposCycle = dposDelegateNum * dposBlockInterval * dposContinueBlockNum dposCycle = dposDelegateNum * dposBlockInterval * dposContinueBlockNum
dposPeriod = dposBlockInterval * dposContinueBlockNum dposPeriod = dposBlockInterval * dposContinueBlockNum
blockNumToUpdateDelegate int64 = 20000
registTopNHeightLimit int64 = 100
updateTopNHeightLimit int64 = 200
) )
type CycleInfo struct { type CycleInfo struct {
...@@ -42,6 +45,10 @@ func calcCycleByTime(now int64) *CycleInfo { ...@@ -42,6 +45,10 @@ func calcCycleByTime(now int64) *CycleInfo {
} }
} }
func calcTopNVersion(height int64) (version, left int64) {
return height / blockNumToUpdateDelegate, height % blockNumToUpdateDelegate
}
func init() { func init() {
ety := types.LoadExecutorType(driverName) ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&DPos{})) ety.InitFuncList(types.ListMethod(&DPos{}))
...@@ -57,11 +64,14 @@ func Init(name string, sub []byte) { ...@@ -57,11 +64,14 @@ func Init(name string, sub []byte) {
drivers.Register(driverName, newDposVote, types.GetDappFork(driverName, "Enable")) drivers.Register(driverName, newDposVote, types.GetDappFork(driverName, "Enable"))
//读取一下配置项,用于和共识模块一致计算cycle //读取一下配置项,用于和共识模块一致计算cycle
dposDelegateNum = types.Conf("config.consensus.sub.dpos").GInt("delegateNum") dposDelegateNum = types.Conf("config.consensus.sub.dpos").GInt("delegateNum")
dposBlockInterval = types.Conf("config.consensus.sub.dpos").GInt("blockInterval") dposBlockInterval = types.Conf("config.consensus.sub.dpos").GInt("blockInterval")
dposContinueBlockNum = types.Conf("config.consensus.sub.dpos").GInt("continueBlockNum") dposContinueBlockNum = types.Conf("config.consensus.sub.dpos").GInt("continueBlockNum")
dposCycle = dposDelegateNum * dposBlockInterval * dposContinueBlockNum blockNumToUpdateDelegate = types.Conf("config.consensus.sub.dpos").GInt("blockNumToUpdateDelegate")
dposPeriod = dposBlockInterval * dposContinueBlockNum registTopNHeightLimit = types.Conf("config.consensus.sub.dpos").GInt("registTopNHeightLimit")
updateTopNHeightLimit = types.Conf("config.consensus.sub.dpos").GInt("updateTopNHeightLimit")
dposCycle = dposDelegateNum * dposBlockInterval * dposContinueBlockNum
dposPeriod = dposBlockInterval * dposContinueBlockNum
} }
//DPos 执行器,用于Dpos候选节点注册、投票,VRF信息注册管理等功能 //DPos 执行器,用于Dpos候选节点注册、投票,VRF信息注册管理等功能
......
...@@ -79,6 +79,14 @@ func Key(id string) (key []byte) { ...@@ -79,6 +79,14 @@ func Key(id string) (key []byte) {
key = append(key, []byte(id)...) key = append(key, []byte(id)...)
return key return key
} }
//Key State数据库中存储记录的Key值格式转换
func TopNKey(id string) (key []byte) {
key = append(key, []byte("mavl-"+dty.DPosX+"-"+"topn"+"-")...)
key = append(key, []byte(id)...)
return key
}
//queryVrfByTime 根据时间信息,查询TopN的受托节点的VRF信息 //queryVrfByTime 根据时间信息,查询TopN的受托节点的VRF信息
func queryVrfByTime(kvdb db.KVDB, req *dty.DposVrfQuery) (types.Message, error) { func queryVrfByTime(kvdb db.KVDB, req *dty.DposVrfQuery) (types.Message, error) {
if req.Ty != dty.QueryVrfByTime{ if req.Ty != dty.QueryVrfByTime{
...@@ -130,7 +138,7 @@ func getVrfInfoFromVrfRP(vrfRP *dty.DposVrfRP) *dty.VrfInfo{ ...@@ -130,7 +138,7 @@ func getVrfInfoFromVrfRP(vrfRP *dty.DposVrfRP) *dty.VrfInfo{
return vrf return vrf
} }
func isRecordExist(vrfM *dty.DposVrfM, vrfs [] *dty.VrfInfo) bool { func isVrfMRecordExist(vrfM *dty.DposVrfM, vrfs [] *dty.VrfInfo) bool {
if nil == vrfM || nil == vrfs || 0 == len(vrfs) { if nil == vrfM || nil == vrfs || 0 == len(vrfs) {
return false return false
} }
...@@ -271,7 +279,7 @@ func queryVrfByCycle(kvdb db.KVDB, req *dty.DposVrfQuery) (types.Message, error) ...@@ -271,7 +279,7 @@ func queryVrfByCycle(kvdb db.KVDB, req *dty.DposVrfQuery) (types.Message, error)
} else { } else {
for i := 0; i < len(rows); i++ { for i := 0; i < len(rows); i++ {
vrfM := rows[i].Data.(*dty.DposVrfM) vrfM := rows[i].Data.(*dty.DposVrfM)
if !isRecordExist(vrfM, vrfs) { if !isVrfMRecordExist(vrfM, vrfs) {
vrf := getVrfInfoFromVrfM(vrfM) vrf := getVrfInfoFromVrfM(vrfM)
vrfs = append(vrfs, vrf) vrfs = append(vrfs, vrf)
} }
...@@ -517,6 +525,34 @@ func (action *Action) newCandicatorInfo(regist *dty.DposCandidatorRegist) *dty.C ...@@ -517,6 +525,34 @@ func (action *Action) newCandicatorInfo(regist *dty.DposCandidatorRegist) *dty.C
return candInfo return candInfo
} }
//readTopNCandicators 根据版本信息查询特定高度区间的TOPN候选节点信息
func (action *Action) readTopNCandicators(version int64) (*dty.TopNCandidators, error) {
strVersion := fmt.Sprintf("%018d", version)
data, err := action.db.Get(TopNKey(strVersion))
if err != nil {
logger.Error("readTopNCandicators have err:", "err", err.Error())
return nil, err
}
var cands dty.TopNCandidators
//decode
err = types.Decode(data, &cands)
if err != nil {
logger.Error("decode TopNCandidators have err:", err.Error())
return nil, err
}
return &cands, nil
}
func (action *Action) saveTopNCandicators(topCands *dty.TopNCandidators) (kvset []*types.KeyValue) {
value := types.Encode(topCands)
strVersion := fmt.Sprintf("%018d", topCands.Version)
err := action.db.Set(TopNKey(strVersion), value)
if err != nil {
logger.Error("saveCandicator have err:", err.Error())
}
kvset = append(kvset, &types.KeyValue{Key: TopNKey(strVersion), Value: value})
return kvset
}
//queryCBInfoByCycle 根据cycle查询stopHeight及stopHash等CBInfo信息,用于VRF计算 //queryCBInfoByCycle 根据cycle查询stopHeight及stopHash等CBInfo信息,用于VRF计算
func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error) { func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error) {
...@@ -525,6 +561,7 @@ func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, erro ...@@ -525,6 +561,7 @@ func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, erro
rows, err := query.ListIndex("cycle", []byte(fmt.Sprintf("%018d", req.Cycle)), nil, 1, 0) rows, err := query.ListIndex("cycle", []byte(fmt.Sprintf("%018d", req.Cycle)), nil, 1, 0)
if err != nil { if err != nil {
logger.Error("queryCBInfoByCycle have err", "cycle", req.Cycle, "err", err.Error())
return nil, err return nil, err
} }
...@@ -536,6 +573,8 @@ func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, erro ...@@ -536,6 +573,8 @@ func queryCBInfoByCycle(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, erro
Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)), Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)),
Signature: hex.EncodeToString(cbInfo.StopHash), Signature: hex.EncodeToString(cbInfo.StopHash),
} }
logger.Info("queryCBInfoByCycle ok", "cycle", req.Cycle, "info", info.String())
return &dty.DposCBReply{CbInfo: info}, nil return &dty.DposCBReply{CbInfo: info}, nil
} }
...@@ -546,6 +585,7 @@ func queryCBInfoByHeight(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, err ...@@ -546,6 +585,7 @@ func queryCBInfoByHeight(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, err
rows, err := query.ListIndex("height", []byte(fmt.Sprintf("%018d", req.StopHeight)), nil, 1, 0) rows, err := query.ListIndex("height", []byte(fmt.Sprintf("%018d", req.StopHeight)), nil, 1, 0)
if err != nil { if err != nil {
logger.Error("queryCBInfoByHeight have err", "height", req.StopHeight, "err", err.Error())
return nil, err return nil, err
} }
...@@ -557,6 +597,8 @@ func queryCBInfoByHeight(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, err ...@@ -557,6 +597,8 @@ func queryCBInfoByHeight(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, err
Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)), Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)),
Signature: hex.EncodeToString(cbInfo.StopHash), Signature: hex.EncodeToString(cbInfo.StopHash),
} }
logger.Info("queryCBInfoByHeight ok", "height", req.StopHeight, "info", info.String())
return &dty.DposCBReply{CbInfo: info}, nil return &dty.DposCBReply{CbInfo: info}, nil
} }
...@@ -567,10 +609,14 @@ func queryCBInfoByHash(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error ...@@ -567,10 +609,14 @@ func queryCBInfoByHash(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error
hash, err := hex.DecodeString(req.StopHash) hash, err := hex.DecodeString(req.StopHash)
if err != nil { if err != nil {
logger.Error("queryCBInfoByHash failed for decoding hash failed", "hash", req.StopHash, "err", err.Error())
return nil, err return nil, err
} }
rows, err := query.ListIndex("hash", hash, nil, 1, 0) rows, err := query.ListIndex("hash", hash, nil, 1, 0)
if err != nil { if err != nil {
logger.Error("queryCBInfoByHash have err", "hash", req.StopHash, "err", err.Error())
return nil, err return nil, err
} }
...@@ -582,9 +628,34 @@ func queryCBInfoByHash(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error ...@@ -582,9 +628,34 @@ func queryCBInfoByHash(kvdb db.KVDB, req *dty.DposCBQuery) (types.Message, error
Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)), Pubkey: strings.ToUpper(hex.EncodeToString(cbInfo.Pubkey)),
Signature: hex.EncodeToString(cbInfo.StopHash), Signature: hex.EncodeToString(cbInfo.StopHash),
} }
logger.Info("queryCBInfoByHash ok", "hash", req.StopHash, "info", info.String())
return &dty.DposCBReply{CbInfo: info}, nil return &dty.DposCBReply{CbInfo: info}, nil
} }
//queryTopNByVersion 根据version查询具体周期使用的TopN超级节点信息
func queryTopNByVersion(db dbm.KV, req *dty.TopNCandidatorsQuery) (types.Message, error) {
strVersion := fmt.Sprintf("%018d", req.Version)
data, err := db.Get(TopNKey(strVersion))
if err != nil || data == nil{
logger.Error("queryTopNByVersion have err:", "err", err.Error())
return nil, err
}
var cands dty.TopNCandidators
//decode
err = types.Decode(data, &cands)
if err != nil {
logger.Error("decode TopNCandidators have err:", err.Error())
return nil, err
}
reply := &dty.TopNCandidatorsReply{
TopN: &cands,
}
return reply, nil
}
//Regist 注册候选节点 //Regist 注册候选节点
func (action *Action) Regist(regist *dty.DposCandidatorRegist) (*types.Receipt, error) { func (action *Action) Regist(regist *dty.DposCandidatorRegist) (*types.Receipt, error) {
var logs []*types.ReceiptLog var logs []*types.ReceiptLog
...@@ -1142,3 +1213,108 @@ func (action *Action) RecordCB(cbInfo *dty.DposCBInfo) (*types.Receipt, error) { ...@@ -1142,3 +1213,108 @@ func (action *Action) RecordCB(cbInfo *dty.DposCBInfo) (*types.Receipt, error) {
return &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}, nil return &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}, nil
} }
//RegistTopN 注册TopN节点
func (action *Action) RegistTopN(regist *dty.TopNCandidatorRegist) (*types.Receipt, error) {
var logs []*types.ReceiptLog
var kv []*types.KeyValue
err := regist.Cand.Verify()
if err != nil {
logger.Error("RegistTopN failed for signature verify failed.", "addr", action.fromaddr, "execaddr", action.execaddr)
return nil, types.ErrInvalidParam
}
currentVersion, left := calcTopNVersion(action.mainHeight)
topNVersion, _ := calcTopNVersion(regist.Cand.Height)
if currentVersion != topNVersion {
logger.Error("RegistTopN failed for wrong version.", "addr", action.fromaddr, "execaddr", action.execaddr,
"regist height", regist.Cand.Height, "regist version", topNVersion, "current height", action.mainHeight, "current version", currentVersion)
return nil, types.ErrInvalidParam
}
if left >= registTopNHeightLimit {
logger.Error("RegistTopN failed for height limit.", "addr", action.fromaddr, "execaddr", action.execaddr,
"current height", action.mainHeight, "registTopNHeightLimit", registTopNHeightLimit, "height in new circle", left)
return nil, types.ErrInvalidParam
}
version := topNVersion - 1
for version >= 0 {
lastTopN, err := action.readTopNCandicators(version)
if err != nil {
logger.Error("read old TopN failed.", "addr", action.fromaddr, "execaddr", action.execaddr, "version", version)
if version == 0 {
//如果从没有注册过,认为是创世阶段,可信环境,只有可信的节点来注册,可以不做过多的判断。
break
} else {
version--
continue
}
}
if lastTopN.Status != dty.TopNCandidatorsVoteMajorOK {
logger.Error("Not legal topN exist.", "addr", action.fromaddr, "execaddr", action.execaddr, "version", version)
if version > 0 {
version--
continue
} else {
break
}
}
isLegalVoter := false
for i := 0; i < len(lastTopN.FinalCands); i++{
if bytes.Equal(regist.Cand.SignerPubkey, lastTopN.FinalCands[i].Pubkey) {
isLegalVoter = true
}
}
if !isLegalVoter {
logger.Error("RegistTopN failed for the voter is not legal topN.", "addr", action.fromaddr, "execaddr", action.execaddr, "voter pubkey", hex.EncodeToString(regist.Cand.SignerPubkey))
return nil, dty.ErrNotLegalTopN
}
break
}
topNCands, err := action.readTopNCandicators(topNVersion)
if topNCands == nil {
topNCands = &dty.TopNCandidators {
Version: topNVersion,
Status: dty.TopNCandidatorsVoteInit,
}
topNCands.CandsVotes = append(topNCands.CandsVotes, regist.Cand)
} else {
for i := 0; i < len(topNCands.CandsVotes); i++ {
if bytes.Equal(topNCands.CandsVotes[i].SignerPubkey, regist.Cand.SignerPubkey) {
logger.Error("RegistTopN failed for vote exist.", "addr", action.fromaddr, "execaddr", action.execaddr, "pubkey", hex.EncodeToString(regist.Cand.SignerPubkey))
return nil, types.ErrInvalidParam
}
}
topNCands.CandsVotes = append(topNCands.CandsVotes, regist.Cand)
}
topNCands.CheckVoteStauts(dposDelegateNum)
logger.Info("RegistTopN add one vote", "addr", action.fromaddr, "execaddr", action.execaddr, "version", topNVersion, "voter pubkey", hex.EncodeToString(regist.Cand.SignerPubkey))
log := &types.ReceiptLog{}
r := &dty.ReceiptTopN{}
log.Ty = dty.TyLogTopNCandidatorRegist
r.Index = action.getIndex()
r.Time = action.blocktime
r.Height = action.mainHeight
r.Version = topNVersion
r.Status = dty.TopNCandidatorStatusRegist
r.Pubkey = regist.Cand.SignerPubkey
r.TopN = regist.Cand
log.Log = types.Encode(r)
logs = append(logs, log)
kv = append(kv, action.saveTopNCandicators(topNCands)...)
return &types.Receipt{Ty: types.ExecOk, KV: kv, Logs: logs}, nil
}
\ No newline at end of file
...@@ -56,3 +56,9 @@ func (d *DPos) Exec_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, ind ...@@ -56,3 +56,9 @@ func (d *DPos) Exec_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, ind
action := NewAction(d, tx, index) action := NewAction(d, tx, index)
return action.RecordCB(payload) return action.RecordCB(payload)
} }
//Exec_RegistTopN DPos执行器注册某一cycle中的TOPN信息
func (d *DPos) Exec_RegistTopN(payload *dty.TopNCandidatorRegist, tx *types.Transaction, index int) (*types.Receipt, error) {
action := NewAction(d, tx, index)
return action.RegistTopN(payload)
}
...@@ -212,6 +212,9 @@ func (d *DPos) execDelLocal(receipt *types.ReceiptData) (*types.LocalDBSet, erro ...@@ -212,6 +212,9 @@ func (d *DPos) execDelLocal(receipt *types.ReceiptData) (*types.LocalDBSet, erro
return nil, err return nil, err
} }
dbSet.KV = append(dbSet.KV, kv...) dbSet.KV = append(dbSet.KV, kv...)
case dty.TyLogTopNCandidatorRegist:
//do nothing now
} }
} }
...@@ -256,4 +259,9 @@ func (d *DPos) ExecDelLocal_VrfRPRegist(payload *dty.DposVrfRPRegist, tx *types. ...@@ -256,4 +259,9 @@ func (d *DPos) ExecDelLocal_VrfRPRegist(payload *dty.DposVrfRPRegist, tx *types.
//ExecDelLocal_RecordCB method //ExecDelLocal_RecordCB method
func (d *DPos) ExecDelLocal_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) { func (d *DPos) ExecDelLocal_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return d.execDelLocal(receiptData) return d.execDelLocal(receiptData)
}
//ExecDelLocal_RegistTopN method
func (d *DPos) ExecDelLocal_RegistTopN(payload *dty.TopNCandidatorRegist, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return d.execDelLocal(receiptData)
} }
\ No newline at end of file
...@@ -209,6 +209,8 @@ func (d *DPos) execLocal(receipt *types.ReceiptData) (*types.LocalDBSet, error) ...@@ -209,6 +209,8 @@ func (d *DPos) execLocal(receipt *types.ReceiptData) (*types.LocalDBSet, error)
return nil, err return nil, err
} }
dbSet.KV = append(dbSet.KV, kvs...) dbSet.KV = append(dbSet.KV, kvs...)
} else if item.Ty == dty.TyLogTopNCandidatorRegist {
//do nothing
} }
} }
...@@ -253,4 +255,9 @@ func (d *DPos) ExecLocal_RegistVrfRP(payload *dty.DposVrfRPRegist, tx *types.Tra ...@@ -253,4 +255,9 @@ func (d *DPos) ExecLocal_RegistVrfRP(payload *dty.DposVrfRPRegist, tx *types.Tra
//ExecLocal_RecordCB method //ExecLocal_RecordCB method
func (d *DPos) ExecLocal_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) { func (d *DPos) ExecLocal_RecordCB(payload *dty.DposCBInfo, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return d.execLocal(receiptData) return d.execLocal(receiptData)
}
//ExecLocal_RegistTopN method
func (d *DPos) ExecLocal_RegistTopN(payload *dty.TopNCandidatorRegist, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return d.execLocal(receiptData)
} }
\ No newline at end of file
...@@ -58,3 +58,8 @@ func (d *DPos) Query_QueryCBInfoByHeight(in *dty.DposCBQuery) (types.Message, er ...@@ -58,3 +58,8 @@ func (d *DPos) Query_QueryCBInfoByHeight(in *dty.DposCBQuery) (types.Message, er
func (d *DPos) Query_QueryCBInfoByHash(in *dty.DposCBQuery) (types.Message, error) { func (d *DPos) Query_QueryCBInfoByHash(in *dty.DposCBQuery) (types.Message, error) {
return queryCBInfoByHash(d.GetLocalDB(), in) return queryCBInfoByHash(d.GetLocalDB(), in)
} }
//Query_QueryTopNByVersion method
func (d *DPos) Query_QueryTopNByVersion(in *dty.TopNCandidatorsQuery) (types.Message, error) {
return queryTopNByVersion(d.GetStateDB(), in)
}
\ No newline at end of file
...@@ -76,8 +76,11 @@ message DposVoteAction { ...@@ -76,8 +76,11 @@ message DposVoteAction {
DposVrfRPRegist registVrfRP = 9; DposVrfRPRegist registVrfRP = 9;
DposVrfQuery vrfQuery = 10; DposVrfQuery vrfQuery = 10;
DposCBInfo recordCB = 11; DposCBInfo recordCB = 11;
DposCBQuery cbQuery = 12;
TopNCandidatorRegist registTopN = 13;
TopNCandidatorsQuery topNQuery = 14;
} }
int32 ty = 12; int32 ty = 15;
} }
message CandidatorQuery{ message CandidatorQuery{
...@@ -261,4 +264,41 @@ message ReceiptCB { ...@@ -261,4 +264,41 @@ message ReceiptCB {
int64 cycleMiddle = 8; int64 cycleMiddle = 8;
int64 cycleStop = 9; int64 cycleStop = 9;
DposCycleBoundaryInfo cbInfo = 10; DposCycleBoundaryInfo cbInfo = 10;
} }
\ No newline at end of file
message TopNCandidator{
repeated Candidator cands = 1;
bytes hash = 2;
int64 height = 3;
bytes signerPubkey = 4;
bytes signature = 5;
}
message TopNCandidators{
repeated TopNCandidator candsVotes = 1;
int64 version = 2;
int64 status = 3;
repeated Candidator finalCands = 4;
}
message TopNCandidatorRegist{
TopNCandidator cand = 1;
}
message TopNCandidatorsQuery{
int64 version = 1;
}
message TopNCandidatorsReply{
TopNCandidators topN = 1;
}
message ReceiptTopN {
int64 Index = 1;
bytes pubkey = 2;
int64 status = 3;
int64 version = 4;
int64 height = 5;
int64 time = 6;
TopNCandidator topN = 10;
}
...@@ -14,6 +14,7 @@ const ( ...@@ -14,6 +14,7 @@ const (
DposVoteActionRegistVrfM DposVoteActionRegistVrfM
DposVoteActionRegistVrfRP DposVoteActionRegistVrfRP
DposVoteActionRecordCB DposVoteActionRecordCB
DPosVoteActionRegistTopNCandidator
CandidatorStatusRegist = iota + 1 CandidatorStatusRegist = iota + 1
CandidatorStatusVoted CandidatorStatusVoted
...@@ -25,6 +26,8 @@ const ( ...@@ -25,6 +26,8 @@ const (
VrfStatusRPRegist VrfStatusRPRegist
CBStatusRecord = iota + 1 CBStatusRecord = iota + 1
TopNCandidatorStatusRegist = iota + 1
) )
//log ty //log ty
...@@ -37,6 +40,7 @@ const ( ...@@ -37,6 +40,7 @@ const (
TyLogVrfMRegist = 1006 TyLogVrfMRegist = 1006
TyLogVrfRPRegist = 1007 TyLogVrfRPRegist = 1007
TyLogCBInfoRecord = 1008 TyLogCBInfoRecord = 1008
TyLogTopNCandidatorRegist = 1009
) )
const ( const (
...@@ -47,6 +51,10 @@ const ( ...@@ -47,6 +51,10 @@ const (
VoteTypeVote int32 = 2 VoteTypeVote int32 = 2
VoteTypeCancelVote int32 = 3 VoteTypeCancelVote int32 = 3
VoteTypeCancelAllVote int32 = 4 VoteTypeCancelAllVote int32 = 4
TopNCandidatorsVoteInit int64 = 0
TopNCandidatorsVoteMajorOK int64 = 1
TopNCandidatorsVoteMajorFail int64 = 2
) )
//包的名字可以通过配置文件来配置 //包的名字可以通过配置文件来配置
//建议用github的组织名称,或者用户名字开头, 再加上自己的插件的名字 //建议用github的组织名称,或者用户名字开头, 再加上自己的插件的名字
...@@ -137,4 +145,7 @@ const ( ...@@ -137,4 +145,7 @@ const (
//QueryCBInfoByHeight 根据stopHeight查询cycle boundary信息 //QueryCBInfoByHeight 根据stopHeight查询cycle boundary信息
QueryLatestCBInfoByHeight = 4 QueryLatestCBInfoByHeight = 4
//FuncNameQueryTopNByVersion func name
FuncNameQueryTopNByVersion = "QueryTopNByVersion"
) )
...@@ -37,6 +37,12 @@ It has these top-level messages: ...@@ -37,6 +37,12 @@ It has these top-level messages:
DposCBQuery DposCBQuery
DposCBReply DposCBReply
ReceiptCB ReceiptCB
TopNCandidator
TopNCandidators
TopNCandidatorRegist
TopNCandidatorsQuery
TopNCandidatorsReply
ReceiptTopN
*/ */
package types package types
...@@ -395,8 +401,11 @@ type DposVoteAction struct { ...@@ -395,8 +401,11 @@ type DposVoteAction struct {
// *DposVoteAction_RegistVrfRP // *DposVoteAction_RegistVrfRP
// *DposVoteAction_VrfQuery // *DposVoteAction_VrfQuery
// *DposVoteAction_RecordCB // *DposVoteAction_RecordCB
// *DposVoteAction_CbQuery
// *DposVoteAction_RegistTopN
// *DposVoteAction_TopNQuery
Value isDposVoteAction_Value `protobuf_oneof:"value"` Value isDposVoteAction_Value `protobuf_oneof:"value"`
Ty int32 `protobuf:"varint,12,opt,name=ty" json:"ty,omitempty"` Ty int32 `protobuf:"varint,15,opt,name=ty" json:"ty,omitempty"`
} }
func (m *DposVoteAction) Reset() { *m = DposVoteAction{} } func (m *DposVoteAction) Reset() { *m = DposVoteAction{} }
...@@ -441,6 +450,15 @@ type DposVoteAction_VrfQuery struct { ...@@ -441,6 +450,15 @@ type DposVoteAction_VrfQuery struct {
type DposVoteAction_RecordCB struct { type DposVoteAction_RecordCB struct {
RecordCB *DposCBInfo `protobuf:"bytes,11,opt,name=recordCB,oneof"` RecordCB *DposCBInfo `protobuf:"bytes,11,opt,name=recordCB,oneof"`
} }
type DposVoteAction_CbQuery struct {
CbQuery *DposCBQuery `protobuf:"bytes,12,opt,name=cbQuery,oneof"`
}
type DposVoteAction_RegistTopN struct {
RegistTopN *TopNCandidatorRegist `protobuf:"bytes,13,opt,name=registTopN,oneof"`
}
type DposVoteAction_TopNQuery struct {
TopNQuery *TopNCandidatorsQuery `protobuf:"bytes,14,opt,name=topNQuery,oneof"`
}
func (*DposVoteAction_Regist) isDposVoteAction_Value() {} func (*DposVoteAction_Regist) isDposVoteAction_Value() {}
func (*DposVoteAction_CancelRegist) isDposVoteAction_Value() {} func (*DposVoteAction_CancelRegist) isDposVoteAction_Value() {}
...@@ -453,6 +471,9 @@ func (*DposVoteAction_RegistVrfM) isDposVoteAction_Value() {} ...@@ -453,6 +471,9 @@ func (*DposVoteAction_RegistVrfM) isDposVoteAction_Value() {}
func (*DposVoteAction_RegistVrfRP) isDposVoteAction_Value() {} func (*DposVoteAction_RegistVrfRP) isDposVoteAction_Value() {}
func (*DposVoteAction_VrfQuery) isDposVoteAction_Value() {} func (*DposVoteAction_VrfQuery) isDposVoteAction_Value() {}
func (*DposVoteAction_RecordCB) isDposVoteAction_Value() {} func (*DposVoteAction_RecordCB) isDposVoteAction_Value() {}
func (*DposVoteAction_CbQuery) isDposVoteAction_Value() {}
func (*DposVoteAction_RegistTopN) isDposVoteAction_Value() {}
func (*DposVoteAction_TopNQuery) isDposVoteAction_Value() {}
func (m *DposVoteAction) GetValue() isDposVoteAction_Value { func (m *DposVoteAction) GetValue() isDposVoteAction_Value {
if m != nil { if m != nil {
...@@ -538,6 +559,27 @@ func (m *DposVoteAction) GetRecordCB() *DposCBInfo { ...@@ -538,6 +559,27 @@ func (m *DposVoteAction) GetRecordCB() *DposCBInfo {
return nil return nil
} }
func (m *DposVoteAction) GetCbQuery() *DposCBQuery {
if x, ok := m.GetValue().(*DposVoteAction_CbQuery); ok {
return x.CbQuery
}
return nil
}
func (m *DposVoteAction) GetRegistTopN() *TopNCandidatorRegist {
if x, ok := m.GetValue().(*DposVoteAction_RegistTopN); ok {
return x.RegistTopN
}
return nil
}
func (m *DposVoteAction) GetTopNQuery() *TopNCandidatorsQuery {
if x, ok := m.GetValue().(*DposVoteAction_TopNQuery); ok {
return x.TopNQuery
}
return nil
}
func (m *DposVoteAction) GetTy() int32 { func (m *DposVoteAction) GetTy() int32 {
if m != nil { if m != nil {
return m.Ty return m.Ty
...@@ -559,6 +601,9 @@ func (*DposVoteAction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer ...@@ -559,6 +601,9 @@ func (*DposVoteAction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer
(*DposVoteAction_RegistVrfRP)(nil), (*DposVoteAction_RegistVrfRP)(nil),
(*DposVoteAction_VrfQuery)(nil), (*DposVoteAction_VrfQuery)(nil),
(*DposVoteAction_RecordCB)(nil), (*DposVoteAction_RecordCB)(nil),
(*DposVoteAction_CbQuery)(nil),
(*DposVoteAction_RegistTopN)(nil),
(*DposVoteAction_TopNQuery)(nil),
} }
} }
...@@ -621,6 +666,21 @@ func _DposVoteAction_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { ...@@ -621,6 +666,21 @@ func _DposVoteAction_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
if err := b.EncodeMessage(x.RecordCB); err != nil { if err := b.EncodeMessage(x.RecordCB); err != nil {
return err return err
} }
case *DposVoteAction_CbQuery:
b.EncodeVarint(12<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.CbQuery); err != nil {
return err
}
case *DposVoteAction_RegistTopN:
b.EncodeVarint(13<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.RegistTopN); err != nil {
return err
}
case *DposVoteAction_TopNQuery:
b.EncodeVarint(14<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.TopNQuery); err != nil {
return err
}
case nil: case nil:
default: default:
return fmt.Errorf("DposVoteAction.Value has unexpected type %T", x) return fmt.Errorf("DposVoteAction.Value has unexpected type %T", x)
...@@ -719,6 +779,30 @@ func _DposVoteAction_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto ...@@ -719,6 +779,30 @@ func _DposVoteAction_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto
err := b.DecodeMessage(msg) err := b.DecodeMessage(msg)
m.Value = &DposVoteAction_RecordCB{msg} m.Value = &DposVoteAction_RecordCB{msg}
return true, err return true, err
case 12: // value.cbQuery
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(DposCBQuery)
err := b.DecodeMessage(msg)
m.Value = &DposVoteAction_CbQuery{msg}
return true, err
case 13: // value.registTopN
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(TopNCandidatorRegist)
err := b.DecodeMessage(msg)
m.Value = &DposVoteAction_RegistTopN{msg}
return true, err
case 14: // value.topNQuery
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(TopNCandidatorsQuery)
err := b.DecodeMessage(msg)
m.Value = &DposVoteAction_TopNQuery{msg}
return true, err
default: default:
return false, nil return false, nil
} }
...@@ -783,6 +867,21 @@ func _DposVoteAction_OneofSizer(msg proto.Message) (n int) { ...@@ -783,6 +867,21 @@ func _DposVoteAction_OneofSizer(msg proto.Message) (n int) {
n += proto.SizeVarint(11<<3 | proto.WireBytes) n += proto.SizeVarint(11<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s)) n += proto.SizeVarint(uint64(s))
n += s n += s
case *DposVoteAction_CbQuery:
s := proto.Size(x.CbQuery)
n += proto.SizeVarint(12<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *DposVoteAction_RegistTopN:
s := proto.Size(x.RegistTopN)
n += proto.SizeVarint(13<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *DposVoteAction_TopNQuery:
s := proto.Size(x.TopNQuery)
n += proto.SizeVarint(14<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case nil: case nil:
default: default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
...@@ -1866,6 +1965,206 @@ func (m *ReceiptCB) GetCbInfo() *DposCycleBoundaryInfo { ...@@ -1866,6 +1965,206 @@ func (m *ReceiptCB) GetCbInfo() *DposCycleBoundaryInfo {
return nil return nil
} }
type TopNCandidator struct {
Cands []*Candidator `protobuf:"bytes,1,rep,name=cands" json:"cands,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height" json:"height,omitempty"`
SignerPubkey []byte `protobuf:"bytes,4,opt,name=signerPubkey,proto3" json:"signerPubkey,omitempty"`
Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"`
}
func (m *TopNCandidator) Reset() { *m = TopNCandidator{} }
func (m *TopNCandidator) String() string { return proto.CompactTextString(m) }
func (*TopNCandidator) ProtoMessage() {}
func (*TopNCandidator) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
func (m *TopNCandidator) GetCands() []*Candidator {
if m != nil {
return m.Cands
}
return nil
}
func (m *TopNCandidator) GetHash() []byte {
if m != nil {
return m.Hash
}
return nil
}
func (m *TopNCandidator) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *TopNCandidator) GetSignerPubkey() []byte {
if m != nil {
return m.SignerPubkey
}
return nil
}
func (m *TopNCandidator) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
type TopNCandidators struct {
CandsVotes []*TopNCandidator `protobuf:"bytes,1,rep,name=candsVotes" json:"candsVotes,omitempty"`
Version int64 `protobuf:"varint,2,opt,name=version" json:"version,omitempty"`
Status int64 `protobuf:"varint,3,opt,name=status" json:"status,omitempty"`
FinalCands []*Candidator `protobuf:"bytes,4,rep,name=finalCands" json:"finalCands,omitempty"`
}
func (m *TopNCandidators) Reset() { *m = TopNCandidators{} }
func (m *TopNCandidators) String() string { return proto.CompactTextString(m) }
func (*TopNCandidators) ProtoMessage() {}
func (*TopNCandidators) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
func (m *TopNCandidators) GetCandsVotes() []*TopNCandidator {
if m != nil {
return m.CandsVotes
}
return nil
}
func (m *TopNCandidators) GetVersion() int64 {
if m != nil {
return m.Version
}
return 0
}
func (m *TopNCandidators) GetStatus() int64 {
if m != nil {
return m.Status
}
return 0
}
func (m *TopNCandidators) GetFinalCands() []*Candidator {
if m != nil {
return m.FinalCands
}
return nil
}
type TopNCandidatorRegist struct {
Cand *TopNCandidator `protobuf:"bytes,1,opt,name=cand" json:"cand,omitempty"`
}
func (m *TopNCandidatorRegist) Reset() { *m = TopNCandidatorRegist{} }
func (m *TopNCandidatorRegist) String() string { return proto.CompactTextString(m) }
func (*TopNCandidatorRegist) ProtoMessage() {}
func (*TopNCandidatorRegist) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
func (m *TopNCandidatorRegist) GetCand() *TopNCandidator {
if m != nil {
return m.Cand
}
return nil
}
type TopNCandidatorsQuery struct {
Version int64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"`
}
func (m *TopNCandidatorsQuery) Reset() { *m = TopNCandidatorsQuery{} }
func (m *TopNCandidatorsQuery) String() string { return proto.CompactTextString(m) }
func (*TopNCandidatorsQuery) ProtoMessage() {}
func (*TopNCandidatorsQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
func (m *TopNCandidatorsQuery) GetVersion() int64 {
if m != nil {
return m.Version
}
return 0
}
type TopNCandidatorsReply struct {
TopN *TopNCandidators `protobuf:"bytes,1,opt,name=topN" json:"topN,omitempty"`
}
func (m *TopNCandidatorsReply) Reset() { *m = TopNCandidatorsReply{} }
func (m *TopNCandidatorsReply) String() string { return proto.CompactTextString(m) }
func (*TopNCandidatorsReply) ProtoMessage() {}
func (*TopNCandidatorsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
func (m *TopNCandidatorsReply) GetTopN() *TopNCandidators {
if m != nil {
return m.TopN
}
return nil
}
type ReceiptTopN struct {
Index int64 `protobuf:"varint,1,opt,name=Index" json:"Index,omitempty"`
Pubkey []byte `protobuf:"bytes,2,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
Status int64 `protobuf:"varint,3,opt,name=status" json:"status,omitempty"`
Version int64 `protobuf:"varint,4,opt,name=version" json:"version,omitempty"`
Height int64 `protobuf:"varint,5,opt,name=height" json:"height,omitempty"`
Time int64 `protobuf:"varint,6,opt,name=time" json:"time,omitempty"`
TopN *TopNCandidator `protobuf:"bytes,10,opt,name=topN" json:"topN,omitempty"`
}
func (m *ReceiptTopN) Reset() { *m = ReceiptTopN{} }
func (m *ReceiptTopN) String() string { return proto.CompactTextString(m) }
func (*ReceiptTopN) ProtoMessage() {}
func (*ReceiptTopN) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} }
func (m *ReceiptTopN) GetIndex() int64 {
if m != nil {
return m.Index
}
return 0
}
func (m *ReceiptTopN) GetPubkey() []byte {
if m != nil {
return m.Pubkey
}
return nil
}
func (m *ReceiptTopN) GetStatus() int64 {
if m != nil {
return m.Status
}
return 0
}
func (m *ReceiptTopN) GetVersion() int64 {
if m != nil {
return m.Version
}
return 0
}
func (m *ReceiptTopN) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *ReceiptTopN) GetTime() int64 {
if m != nil {
return m.Time
}
return 0
}
func (m *ReceiptTopN) GetTopN() *TopNCandidator {
if m != nil {
return m.TopN
}
return nil
}
func init() { func init() {
proto.RegisterType((*CandidatorInfo)(nil), "types.CandidatorInfo") proto.RegisterType((*CandidatorInfo)(nil), "types.CandidatorInfo")
proto.RegisterType((*DposVoter)(nil), "types.DposVoter") proto.RegisterType((*DposVoter)(nil), "types.DposVoter")
...@@ -1896,91 +2195,111 @@ func init() { ...@@ -1896,91 +2195,111 @@ func init() {
proto.RegisterType((*DposCBQuery)(nil), "types.DposCBQuery") proto.RegisterType((*DposCBQuery)(nil), "types.DposCBQuery")
proto.RegisterType((*DposCBReply)(nil), "types.DposCBReply") proto.RegisterType((*DposCBReply)(nil), "types.DposCBReply")
proto.RegisterType((*ReceiptCB)(nil), "types.ReceiptCB") proto.RegisterType((*ReceiptCB)(nil), "types.ReceiptCB")
proto.RegisterType((*TopNCandidator)(nil), "types.TopNCandidator")
proto.RegisterType((*TopNCandidators)(nil), "types.TopNCandidators")
proto.RegisterType((*TopNCandidatorRegist)(nil), "types.TopNCandidatorRegist")
proto.RegisterType((*TopNCandidatorsQuery)(nil), "types.TopNCandidatorsQuery")
proto.RegisterType((*TopNCandidatorsReply)(nil), "types.TopNCandidatorsReply")
proto.RegisterType((*ReceiptTopN)(nil), "types.ReceiptTopN")
} }
func init() { proto.RegisterFile("dposvote.proto", fileDescriptor0) } func init() { proto.RegisterFile("dposvote.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 1285 bytes of a gzipped FileDescriptorProto // 1507 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5f, 0x6f, 0xe3, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5f, 0x6f, 0xdc, 0x44,
0x10, 0xaf, 0xed, 0x38, 0x89, 0xc7, 0xb9, 0x96, 0x5b, 0xd2, 0x93, 0x75, 0x9c, 0x50, 0xb0, 0x0e, 0x10, 0xcf, 0xda, 0xf7, 0xcf, 0xe3, 0xcb, 0x85, 0x2e, 0xd7, 0xca, 0x2a, 0x15, 0x3a, 0xac, 0x22,
0xa9, 0xf0, 0x50, 0x74, 0xa1, 0x88, 0x7f, 0x02, 0xe9, 0x52, 0x24, 0x72, 0x27, 0xee, 0x38, 0xf6, 0x52, 0x1e, 0x02, 0x3d, 0x5a, 0xf1, 0xa7, 0x2a, 0x52, 0x2f, 0x48, 0x5c, 0x2b, 0x5a, 0xca, 0x36,
0xaa, 0x0a, 0x89, 0x27, 0xd7, 0x76, 0x5a, 0x8b, 0x24, 0xb6, 0xd6, 0x4e, 0x75, 0x91, 0x90, 0x40, 0x8a, 0x90, 0x78, 0x72, 0x6c, 0x5f, 0x62, 0x71, 0x39, 0x5b, 0xb6, 0x13, 0xf5, 0x24, 0x24, 0x90,
0x42, 0x7c, 0x03, 0x24, 0xde, 0xe0, 0x81, 0x07, 0xc4, 0x57, 0xe2, 0x13, 0xf0, 0x21, 0x78, 0x40, 0x10, 0xdf, 0x80, 0x67, 0x78, 0xe0, 0xa1, 0xe2, 0x81, 0x4f, 0x01, 0x9f, 0x82, 0x4f, 0xc0, 0x87,
0x3b, 0xbb, 0x5e, 0xaf, 0xdd, 0xa6, 0x25, 0xa5, 0x77, 0xf7, 0xd6, 0x99, 0xdd, 0x9d, 0xfc, 0xe6, 0xe0, 0x01, 0xed, 0xec, 0x7a, 0xbd, 0x76, 0xe2, 0xa4, 0x57, 0xfa, 0xe7, 0xed, 0x66, 0x77, 0x76,
0xf7, 0xdb, 0x9d, 0x19, 0x17, 0x36, 0xa3, 0x2c, 0xcd, 0x4f, 0xd3, 0x22, 0xde, 0xcd, 0x58, 0x5a, 0x3c, 0xf3, 0xfb, 0xcd, 0xce, 0xcc, 0x1e, 0x0c, 0x82, 0x24, 0xce, 0x8e, 0xe3, 0x3c, 0xdc, 0x4a,
0xa4, 0xc4, 0x2e, 0x96, 0x59, 0x9c, 0xfb, 0xff, 0x98, 0xb0, 0xb9, 0x1f, 0xcc, 0xa3, 0x24, 0x0a, 0xd2, 0x38, 0x8f, 0x69, 0x3b, 0x5f, 0x26, 0x61, 0xe6, 0xfe, 0x6b, 0xc0, 0x60, 0xdb, 0x5b, 0x04,
0x8a, 0x94, 0x3d, 0x98, 0x4f, 0x52, 0x72, 0x0b, 0xda, 0xd9, 0xe2, 0xe8, 0xdb, 0x78, 0xe9, 0x19, 0x51, 0xe0, 0xe5, 0x71, 0x7a, 0x77, 0x31, 0x8b, 0xe9, 0x25, 0xe8, 0x24, 0x47, 0x7b, 0xdf, 0x86,
0x03, 0x63, 0xa7, 0x47, 0xa5, 0x45, 0x3c, 0xe8, 0x04, 0x51, 0xc4, 0xe2, 0x3c, 0xf7, 0xcc, 0x81, 0x4b, 0x87, 0x8c, 0xc8, 0x66, 0x9f, 0x49, 0x89, 0x3a, 0xd0, 0xf5, 0x82, 0x20, 0x0d, 0xb3, 0xcc,
0xb1, 0xe3, 0xd0, 0xd2, 0x24, 0x9b, 0x60, 0x26, 0x99, 0x67, 0xa1, 0xd3, 0x4c, 0x32, 0xd2, 0x07, 0x31, 0x46, 0x64, 0xd3, 0x62, 0x85, 0x48, 0x07, 0x60, 0x44, 0x89, 0x63, 0xe2, 0xa2, 0x11, 0x25,
0x9b, 0xff, 0x52, 0xee, 0xb5, 0x06, 0xc6, 0x8e, 0x45, 0x85, 0xc1, 0xe3, 0xe6, 0x45, 0x50, 0x2c, 0x74, 0x08, 0x6d, 0xfe, 0xa5, 0xcc, 0x69, 0x8d, 0xc8, 0xa6, 0xc9, 0x84, 0xc0, 0xed, 0x66, 0xb9,
0x72, 0xcf, 0x46, 0xb7, 0xb4, 0xc8, 0x1d, 0x70, 0x32, 0x16, 0x3f, 0x15, 0x4b, 0x6d, 0x5c, 0xaa, 0x97, 0x1f, 0x65, 0x4e, 0x1b, 0x97, 0xa5, 0x44, 0xaf, 0x80, 0x95, 0xa4, 0xe1, 0x23, 0xb1, 0xd5,
0x1c, 0x7c, 0x35, 0x2f, 0x02, 0x56, 0x1c, 0x24, 0xb3, 0xd8, 0xeb, 0x88, 0x55, 0xe5, 0x20, 0x03, 0xc1, 0xad, 0x72, 0x81, 0xef, 0x66, 0xb9, 0x97, 0xe6, 0x3b, 0xd1, 0x61, 0xe8, 0x74, 0xc5, 0xae,
0x70, 0xd1, 0x18, 0xc7, 0xc9, 0xf1, 0x49, 0xe1, 0x75, 0x71, 0x5d, 0x77, 0xa9, 0x1d, 0x07, 0xcf, 0x5a, 0xa0, 0x23, 0xb0, 0x51, 0x98, 0x86, 0xd1, 0xfe, 0x41, 0xee, 0xf4, 0x70, 0x5f, 0x5f, 0x52,
0xc6, 0x41, 0x7e, 0xe2, 0x39, 0x08, 0x52, 0x77, 0x91, 0xd7, 0x01, 0xd0, 0x7c, 0x30, 0x8f, 0xe2, 0x1a, 0x3b, 0x8f, 0xa7, 0x5e, 0x76, 0xe0, 0x58, 0xe8, 0xa4, 0xbe, 0x44, 0xdf, 0x04, 0x40, 0xf1,
0x67, 0x1e, 0x60, 0x08, 0xcd, 0xc3, 0xb3, 0x49, 0x70, 0xc9, 0x15, 0xd9, 0xa0, 0x41, 0x6e, 0x43, 0xee, 0x22, 0x08, 0x1f, 0x3b, 0x80, 0x26, 0xb4, 0x15, 0x1e, 0x4d, 0x84, 0x5b, 0xb6, 0x88, 0x06,
0x37, 0x63, 0xb1, 0x38, 0xd3, 0xc3, 0x05, 0x65, 0x93, 0x1d, 0x68, 0xf3, 0x94, 0x59, 0xee, 0xdd, 0x05, 0x7a, 0x19, 0x7a, 0x49, 0x1a, 0x8a, 0x33, 0x7d, 0xdc, 0x50, 0x32, 0xdd, 0x84, 0x0e, 0x0f,
0x18, 0x58, 0x3b, 0xee, 0xf0, 0x95, 0x5d, 0x24, 0x7b, 0xf7, 0xb3, 0x2c, 0xcd, 0x0f, 0xf9, 0x02, 0x39, 0xcd, 0x9c, 0xf5, 0x91, 0xb9, 0x69, 0x8f, 0x5f, 0xdb, 0x42, 0xb0, 0xb7, 0x3e, 0x4b, 0xe2,
0x95, 0xeb, 0xfe, 0xf7, 0xe0, 0x28, 0x27, 0x0f, 0x39, 0x61, 0xe9, 0xec, 0x7e, 0x14, 0x31, 0xa4, 0x6c, 0x97, 0x6f, 0x30, 0xb9, 0xef, 0x7e, 0x0f, 0x96, 0x5a, 0xe4, 0x26, 0x67, 0x69, 0x7c, 0x78,
0xde, 0xa1, 0xca, 0xd6, 0x44, 0x31, 0x6b, 0xa2, 0x28, 0xaa, 0x2d, 0x9d, 0x6a, 0x05, 0xb9, 0xa5, 0x27, 0x08, 0x52, 0x84, 0xde, 0x62, 0x4a, 0xd6, 0x48, 0x31, 0x2a, 0xa4, 0x28, 0xa8, 0x4d, 0x1d,
0x43, 0x26, 0xd0, 0x2a, 0x38, 0x8b, 0x82, 0x7e, 0xfc, 0xdb, 0xff, 0x0e, 0xa0, 0x92, 0xff, 0x45, 0x6a, 0xe5, 0x72, 0x4b, 0x77, 0x99, 0x42, 0x2b, 0xe7, 0x28, 0x0a, 0xf8, 0xf1, 0xb7, 0xfb, 0x1d,
0x4b, 0xef, 0x7f, 0x0d, 0x7d, 0x9e, 0x7e, 0x85, 0x80, 0xc6, 0xc7, 0x49, 0x5e, 0x34, 0x70, 0x38, 0x40, 0x49, 0xff, 0xcb, 0xa6, 0xde, 0xfd, 0x1a, 0x86, 0x3c, 0xfc, 0xd2, 0x03, 0x16, 0xee, 0x47,
0xeb, 0xe3, 0xf0, 0x1f, 0xc3, 0xed, 0x7a, 0xe4, 0xfd, 0x60, 0x1e, 0xc6, 0xd3, 0xab, 0xc6, 0xf7, 0x59, 0x5e, 0xf3, 0xc3, 0x5a, 0xdd, 0x0f, 0xf7, 0x01, 0x5c, 0xae, 0x5a, 0xde, 0xf6, 0x16, 0x7e,
0x0f, 0xa0, 0x5b, 0x0a, 0xb5, 0x86, 0x4e, 0xce, 0xc5, 0x3a, 0xf9, 0x9f, 0xc2, 0xa6, 0x44, 0x19, 0x38, 0x7f, 0x56, 0xfb, 0xee, 0x0e, 0xf4, 0x0a, 0xa2, 0x56, 0xe0, 0xc9, 0x3a, 0x9b, 0x27, 0xf7,
0xc6, 0x53, 0x8c, 0xbd, 0x0a, 0x99, 0x52, 0xd4, 0xd2, 0x14, 0xf5, 0x7f, 0xb1, 0x45, 0x00, 0x7e, 0x53, 0x18, 0x48, 0x2f, 0xfd, 0x70, 0x8e, 0xb6, 0x9b, 0x3c, 0x53, 0x8c, 0x9a, 0x1a, 0xa3, 0xee,
0xf4, 0x7e, 0x58, 0x24, 0xe9, 0x9c, 0xbc, 0x07, 0x6d, 0x86, 0x49, 0x62, 0x00, 0x77, 0xf8, 0x9a, 0x9f, 0x1d, 0x61, 0x80, 0x1f, 0xbd, 0xe3, 0xe7, 0x51, 0xbc, 0xa0, 0x37, 0xa1, 0x93, 0x62, 0x90,
0x76, 0xf7, 0x9a, 0x3c, 0x8f, 0x37, 0xa8, 0xdc, 0x4c, 0x3e, 0x87, 0x5e, 0xa8, 0x31, 0x84, 0xe8, 0x68, 0xc0, 0x1e, 0xbf, 0xa1, 0xe5, 0x5e, 0x1d, 0xe7, 0xe9, 0x1a, 0x93, 0xca, 0xf4, 0x73, 0xe8,
0xdd, 0xe1, 0x1b, 0xe7, 0x1e, 0xd6, 0xa9, 0x1c, 0x6f, 0xd0, 0xda, 0x41, 0xf2, 0x21, 0x74, 0x59, 0xfb, 0x1a, 0x42, 0xe8, 0xbd, 0x3d, 0x7e, 0xeb, 0xd4, 0xc3, 0x3a, 0x94, 0xd3, 0x35, 0x56, 0x39,
0x2c, 0x83, 0x58, 0xff, 0x05, 0x81, 0xda, 0x4e, 0xde, 0x84, 0x16, 0xa7, 0x05, 0xaf, 0x8e, 0x3b, 0x48, 0x3f, 0x86, 0x5e, 0x1a, 0x4a, 0x23, 0xe6, 0xd3, 0x78, 0xa0, 0xd4, 0xe9, 0xdb, 0xd0, 0xe2,
0xdc, 0x6a, 0x3c, 0x9a, 0xf1, 0x06, 0xc5, 0x65, 0xf2, 0x3e, 0x40, 0xa8, 0x08, 0xc3, 0x0b, 0xe5, 0xb0, 0x60, 0xea, 0xd8, 0xe3, 0x8d, 0xda, 0xa5, 0x99, 0xae, 0x31, 0xdc, 0xa6, 0x1f, 0x02, 0xf8,
0x0e, 0xb7, 0xeb, 0xbf, 0x21, 0x17, 0xc7, 0x1b, 0x54, 0xdb, 0x4a, 0x46, 0xb0, 0x15, 0xaa, 0xdf, 0x0a, 0x30, 0x4c, 0x28, 0x7b, 0x7c, 0xb1, 0xfa, 0x0d, 0xb9, 0x39, 0x5d, 0x63, 0x9a, 0x2a, 0x9d,
0xff, 0x6a, 0x11, 0xb3, 0x25, 0x96, 0x1b, 0x77, 0x78, 0x4b, 0x9e, 0xde, 0xaf, 0xaf, 0x8e, 0x37, 0xc0, 0x86, 0xaf, 0xbe, 0xff, 0xd5, 0x51, 0x98, 0x2e, 0xb1, 0xdc, 0xd8, 0xe3, 0x4b, 0xf2, 0xf4,
0x68, 0xf3, 0x00, 0xd9, 0x03, 0x87, 0x83, 0x10, 0xa7, 0x3b, 0x78, 0xba, 0xdf, 0x00, 0x5a, 0x9e, 0x76, 0x75, 0x77, 0xba, 0xc6, 0xea, 0x07, 0xe8, 0x0d, 0xb0, 0xb8, 0x13, 0xe2, 0x74, 0x17, 0x4f,
0xad, 0x36, 0x72, 0xc8, 0x82, 0xe7, 0x43, 0x36, 0x79, 0x84, 0x55, 0xaa, 0x0e, 0x99, 0xbb, 0x15, 0x0f, 0x6b, 0x8e, 0x16, 0x67, 0x4b, 0x45, 0xee, 0xb2, 0xc0, 0x79, 0x37, 0x9d, 0xdd, 0xc7, 0x2a,
0x21, 0xda, 0x56, 0xf2, 0x11, 0xb8, 0xca, 0xa2, 0x4f, 0xb0, 0x7a, 0x55, 0x70, 0xe5, 0x49, 0xfa, 0x55, 0x75, 0x99, 0x2f, 0x2b, 0x40, 0x34, 0x55, 0xfa, 0x09, 0xd8, 0x4a, 0x62, 0x0f, 0xb1, 0x7a,
0x44, 0x1d, 0xd5, 0x37, 0x93, 0x7b, 0xd0, 0x3d, 0x65, 0x13, 0x81, 0x14, 0xf0, 0xe0, 0xab, 0xf5, 0x95, 0xee, 0xca, 0x93, 0xec, 0xa1, 0x3a, 0xaa, 0x2b, 0xd3, 0xeb, 0xd0, 0x3b, 0x4e, 0x67, 0xc2,
0x83, 0x25, 0x50, 0xb5, 0x8d, 0xbc, 0xc3, 0xc5, 0x0b, 0x53, 0x16, 0xed, 0x8f, 0xb0, 0xda, 0xb9, 0x53, 0xc0, 0x83, 0xaf, 0x57, 0x0f, 0x16, 0x8e, 0x2a, 0x35, 0xfa, 0x1e, 0x27, 0xcf, 0x8f, 0xd3,
0xc3, 0x9b, 0x3a, 0xb1, 0x23, 0xde, 0x1f, 0x84, 0x64, 0x62, 0x13, 0x7f, 0x76, 0xc5, 0x12, 0xeb, 0x60, 0x7b, 0x82, 0xd5, 0xce, 0x1e, 0x5f, 0xd0, 0x81, 0x9d, 0xf0, 0xfe, 0x20, 0x28, 0x13, 0x4a,
0x9f, 0x4d, 0xcd, 0x62, 0x39, 0xea, 0x80, 0x7d, 0x1a, 0x4c, 0x17, 0xb1, 0xff, 0x25, 0x6c, 0x35, 0x74, 0x0b, 0xba, 0xfe, 0x9e, 0xf8, 0x44, 0x1f, 0xf5, 0x69, 0x45, 0xbf, 0xf8, 0x42, 0xa1, 0x44,
0xd8, 0xe4, 0x8f, 0x4b, 0x5c, 0xe6, 0xdc, 0x33, 0x06, 0x16, 0x7f, 0x5c, 0xd2, 0xc4, 0xc2, 0x94, 0x6f, 0x17, 0x40, 0xec, 0xc4, 0xc9, 0x03, 0x67, 0xbd, 0x92, 0x1f, 0x7c, 0xe9, 0x94, 0xfc, 0xd0,
0x66, 0x8f, 0xf1, 0xd2, 0xd9, 0x14, 0xff, 0x96, 0x91, 0xad, 0x32, 0xb2, 0xff, 0x83, 0x01, 0x9b, 0x0e, 0xd0, 0x5b, 0x60, 0xe5, 0x71, 0xf2, 0x40, 0x7c, 0x70, 0x70, 0xc6, 0xe9, 0x4c, 0x91, 0xa0,
0x0f, 0xf3, 0x74, 0xbe, 0xb2, 0x5a, 0x39, 0xcf, 0xbd, 0x5a, 0x3d, 0xd4, 0x73, 0xa2, 0x71, 0x36, 0xf4, 0x79, 0x89, 0xc8, 0x97, 0xce, 0xc6, 0x88, 0x6c, 0xb6, 0x99, 0x91, 0x2f, 0x27, 0x5d, 0x68,
0xe5, 0xc2, 0xba, 0xd5, 0x0d, 0x11, 0x79, 0x55, 0xca, 0xd6, 0xe1, 0x52, 0x7d, 0xa7, 0xff, 0x09, 0x1f, 0x7b, 0xf3, 0xa3, 0xd0, 0xfd, 0x12, 0x36, 0x6a, 0xcc, 0xf3, 0x42, 0x20, 0x2e, 0x5e, 0xe6,
0xdc, 0xa8, 0xdd, 0x97, 0x8b, 0xd9, 0xe1, 0xf8, 0x65, 0x2e, 0xf8, 0xb7, 0xff, 0xa3, 0x01, 0x37, 0x90, 0x91, 0xc9, 0x0b, 0x81, 0x14, 0xb1, 0x88, 0x72, 0xdf, 0x0d, 0xb4, 0x83, 0xbf, 0xa5, 0x65,
0x78, 0xf8, 0xab, 0x34, 0x0f, 0xe7, 0xda, 0x9a, 0xc7, 0xc7, 0x55, 0x12, 0x82, 0x8e, 0xb7, 0xcb, 0xb3, 0xb0, 0xec, 0xfe, 0x40, 0x60, 0x70, 0x2f, 0x8b, 0x17, 0x8d, 0x95, 0xd5, 0x7a, 0xe1, 0x95,
0x80, 0x82, 0x88, 0xbe, 0x46, 0x44, 0xd5, 0xfb, 0x64, 0xed, 0xfb, 0xcb, 0x84, 0x9b, 0x34, 0x0e, 0xf5, 0x9e, 0x1e, 0x13, 0x0b, 0x93, 0x39, 0x4f, 0x42, 0xbb, 0xcc, 0x66, 0x11, 0x57, 0x99, 0x85,
0xe3, 0x24, 0x2b, 0x90, 0xa4, 0x10, 0x35, 0xed, 0x83, 0x2d, 0x7a, 0xaa, 0x21, 0x7e, 0x5c, 0x34, 0x55, 0x77, 0x99, 0xae, 0xe9, 0xde, 0x86, 0xf5, 0x4a, 0x6e, 0x9f, 0x8d, 0x0e, 0xf7, 0x5f, 0xc6,
0xd4, 0x55, 0xdd, 0x4f, 0x53, 0xda, 0xaa, 0x2b, 0x5d, 0x69, 0xd8, 0x5a, 0x3d, 0x6c, 0xd8, 0xcd, 0x82, 0xbf, 0xdd, 0x1f, 0x09, 0xac, 0x73, 0xf3, 0xcf, 0xd2, 0xe8, 0xac, 0xe7, 0xd6, 0xe8, 0x6e,
0x61, 0xc3, 0x87, 0x9e, 0xd8, 0xb7, 0x7f, 0x12, 0xcc, 0x8f, 0x63, 0x2c, 0x0f, 0x5d, 0x5a, 0xf3, 0x95, 0x41, 0x08, 0x38, 0xde, 0x2d, 0x0c, 0x0a, 0x20, 0x86, 0x1a, 0x10, 0x65, 0x9f, 0x96, 0x75,
0x71, 0xa2, 0x79, 0x02, 0x07, 0xcb, 0x4c, 0xcc, 0x23, 0x36, 0x55, 0x36, 0xb9, 0x2b, 0x2b, 0x98, 0xfa, 0x6f, 0x03, 0x2e, 0xb0, 0xd0, 0x0f, 0xa3, 0x24, 0x47, 0x90, 0x7c, 0xe4, 0x74, 0x08, 0x6d,
0x78, 0xe1, 0x67, 0xdb, 0xbe, 0x28, 0x60, 0xba, 0x54, 0x4e, 0x43, 0xaa, 0x7b, 0xd0, 0xe5, 0xd7, 0xd1, 0xff, 0x89, 0xf8, 0xb8, 0x68, 0xfe, 0x4d, 0x9d, 0x5a, 0x63, 0xda, 0xac, 0x32, 0x5d, 0x72,
0x84, 0x3f, 0x34, 0xf9, 0x68, 0xb7, 0xcf, 0x14, 0x27, 0xbe, 0x48, 0xd5, 0x36, 0xa5, 0x8c, 0xab, 0xd8, 0x6a, 0x1e, 0x8c, 0xda, 0xf5, 0xc1, 0xc8, 0x85, 0xbe, 0xd0, 0xdb, 0x3e, 0xf0, 0x16, 0xfb,
0x29, 0xf3, 0xb7, 0x21, 0xfb, 0x15, 0x2f, 0x22, 0xeb, 0x71, 0xda, 0x07, 0x3b, 0x5c, 0x86, 0xd3, 0x21, 0x96, 0xb2, 0x1e, 0xab, 0xac, 0x71, 0xa0, 0x79, 0x00, 0x3b, 0xcb, 0x44, 0xcc, 0x4e, 0x6d,
0xb8, 0xbc, 0x14, 0x68, 0xf0, 0xdd, 0x27, 0x62, 0xc6, 0x92, 0x7c, 0x0a, 0x8b, 0xf4, 0xc0, 0x98, 0xa6, 0x64, 0x7a, 0x55, 0x56, 0x5b, 0x51, 0x8d, 0x4e, 0x8e, 0x28, 0xa2, 0xd8, 0xea, 0x54, 0x59,
0x21, 0x8f, 0x3d, 0x6a, 0xcc, 0x14, 0x94, 0x76, 0x05, 0x85, 0x8f, 0x57, 0x18, 0xe2, 0x29, 0x9f, 0x35, 0xaa, 0xae, 0x43, 0x8f, 0xa7, 0x09, 0x2f, 0x0a, 0xb2, 0xc0, 0x5c, 0x3c, 0x51, 0x48, 0xf9,
0xa8, 0xe4, 0x04, 0xa7, 0x79, 0xf8, 0x80, 0x86, 0xd6, 0xa3, 0x24, 0x8a, 0xa6, 0x71, 0x39, 0xc2, 0x26, 0x53, 0x6a, 0x8a, 0x19, 0x5b, 0x63, 0xe6, 0x1f, 0x22, 0x7b, 0x2b, 0x2f, 0x78, 0xab, 0x61,
0x69, 0x2e, 0xae, 0x99, 0xdc, 0x9f, 0x66, 0x48, 0x98, 0x45, 0x2b, 0x87, 0xff, 0x93, 0x29, 0x67, 0x3a, 0x84, 0xb6, 0xbf, 0xf4, 0xe7, 0x61, 0x91, 0x14, 0x28, 0x70, 0xed, 0x03, 0x31, 0x0f, 0x4a,
0x28, 0x2c, 0x7a, 0x2f, 0x2e, 0xd7, 0x1e, 0x18, 0x0c, 0x13, 0xed, 0x51, 0x83, 0x71, 0x2b, 0xc3, 0x3c, 0x85, 0x44, 0xfb, 0x40, 0x0e, 0x11, 0xc7, 0x3e, 0x23, 0x87, 0xca, 0x95, 0x4e, 0xe9, 0x0a,
0xe4, 0x7a, 0xd4, 0xc8, 0x14, 0x0f, 0xdd, 0x95, 0x3c, 0x38, 0x97, 0xf1, 0x00, 0x97, 0xf0, 0xe0, 0x1f, 0x05, 0xd1, 0xc4, 0x23, 0x3e, 0xfd, 0xc9, 0x69, 0x53, 0x5b, 0xe1, 0xc3, 0x24, 0x4a, 0xf7,
0x36, 0x79, 0xf8, 0x42, 0x8e, 0x02, 0xaa, 0x95, 0x5c, 0x34, 0x4b, 0x88, 0xac, 0x4d, 0x3d, 0x6b, 0xa3, 0x20, 0x98, 0x87, 0xc5, 0xb8, 0xa9, 0x2d, 0x71, 0xce, 0xa4, 0x7e, 0x9c, 0x20, 0x60, 0x26,
0xcc, 0x4e, 0xbc, 0x22, 0x63, 0xe6, 0x7f, 0x03, 0x5b, 0x8d, 0xf6, 0xb2, 0x7e, 0x38, 0x56, 0x86, 0x2b, 0x17, 0xdc, 0x9f, 0x0c, 0x39, 0xef, 0x61, 0x81, 0x7e, 0x79, 0xb1, 0xf6, 0x81, 0xa4, 0x18,
0x93, 0xf4, 0xb4, 0x84, 0x95, 0xf9, 0xbf, 0x9a, 0x00, 0xf2, 0xe9, 0x1f, 0xb2, 0xc9, 0x9a, 0x9a, 0x68, 0x9f, 0x91, 0x94, 0x4b, 0x09, 0x06, 0xd7, 0x67, 0x24, 0x51, 0x38, 0xf4, 0x1a, 0x71, 0xb0,
0x55, 0x2f, 0xdb, 0xaa, 0xbd, 0x6c, 0x05, 0xa3, 0x75, 0xbe, 0x96, 0xf6, 0x59, 0x2d, 0xdb, 0x35, 0xce, 0xc3, 0x01, 0xce, 0xc1, 0xc1, 0xae, 0xe3, 0xf0, 0x85, 0x1c, 0x5b, 0x54, 0xdb, 0x3b, 0x6b,
0x2d, 0x3b, 0x35, 0x2d, 0xbb, 0x4d, 0x2d, 0x9d, 0x95, 0x5a, 0xc2, 0x65, 0x5a, 0xba, 0x97, 0x68, 0xee, 0x11, 0x51, 0x1b, 0x7a, 0xd4, 0x18, 0x9d, 0xb8, 0x45, 0xe4, 0xd0, 0xfd, 0x06, 0x36, 0x6a,
0xd9, 0x6b, 0x6a, 0xf9, 0xbb, 0x01, 0x9d, 0x43, 0x36, 0xc1, 0xe7, 0x7d, 0xc5, 0x1b, 0xfd, 0xfc, 0xad, 0x70, 0x75, 0x73, 0x69, 0x61, 0x4e, 0xc2, 0xd3, 0x12, 0x52, 0xe2, 0xfe, 0x62, 0x00, 0xc8,
0x59, 0xf0, 0xa7, 0xd0, 0xd3, 0x27, 0x89, 0x0b, 0x5a, 0x98, 0x68, 0xe6, 0xe2, 0x7e, 0x98, 0xc5, 0xab, 0xbf, 0x9b, 0xce, 0x56, 0xe4, 0xac, 0xbc, 0xd9, 0x66, 0xe5, 0x66, 0x2b, 0x37, 0x5a, 0xa7,
0x92, 0x67, 0xcf, 0x23, 0xe4, 0x45, 0x30, 0xcb, 0xa4, 0x8c, 0x95, 0xe3, 0xfc, 0x1c, 0xfc, 0x3f, 0x73, 0xd9, 0x3e, 0xc9, 0x65, 0xa7, 0xc2, 0x65, 0xb7, 0xc2, 0x65, 0xaf, 0xce, 0xa5, 0xd5, 0xc8,
0x0c, 0x70, 0x79, 0x23, 0x59, 0x87, 0x17, 0xe7, 0xff, 0xf2, 0xe2, 0xd4, 0x78, 0x71, 0x6a, 0xbc, 0x25, 0x9c, 0xc7, 0xa5, 0x7d, 0x0e, 0x97, 0xfd, 0x3a, 0x97, 0xbf, 0x11, 0xe8, 0xee, 0xa6, 0x33,
0x38, 0xab, 0x78, 0xd9, 0x53, 0xbc, 0x88, 0xae, 0x78, 0x17, 0xac, 0x53, 0x36, 0x91, 0x3d, 0x91, 0xbc, 0xde, 0xcf, 0x98, 0xd1, 0x2f, 0x1e, 0x05, 0x77, 0x0e, 0x7d, 0x7d, 0xea, 0x39, 0xa3, 0x85,
0x68, 0x3d, 0x51, 0xa6, 0x42, 0xf9, 0xb2, 0xff, 0x9b, 0x01, 0xdb, 0x38, 0x65, 0x71, 0x64, 0xa3, 0x89, 0x66, 0x2e, 0xf2, 0xc3, 0xc8, 0x97, 0x3c, 0x7a, 0x6e, 0x21, 0xcb, 0xbd, 0xc3, 0x44, 0xd2,
0x74, 0x31, 0x8f, 0x02, 0xb6, 0x2c, 0x33, 0x15, 0xd8, 0x0d, 0x1d, 0x3b, 0x7e, 0xb6, 0xa6, 0x99, 0x58, 0x2e, 0x9c, 0x1e, 0x83, 0xfb, 0x84, 0x80, 0xcd, 0x1b, 0xc9, 0x2a, 0xb8, 0x58, 0xff, 0x17,
0xfc, 0xf2, 0x35, 0xcb, 0xcf, 0xd6, 0xd2, 0xc3, 0xbb, 0x0c, 0x5a, 0xfc, 0xab, 0xd7, 0x42, 0x19, 0x17, 0xab, 0x82, 0x8b, 0x55, 0xc1, 0xc5, 0x6a, 0xc2, 0xe5, 0x86, 0xc2, 0x45, 0x74, 0xc5, 0xab,
0x95, 0xad, 0xb1, 0xd4, 0xaa, 0xdd, 0x1e, 0xfe, 0xb1, 0x9d, 0x1c, 0xcf, 0x83, 0x62, 0xc1, 0x62, 0x60, 0x1e, 0xa7, 0x33, 0xd9, 0x13, 0xa9, 0xd6, 0x13, 0x65, 0x28, 0x8c, 0x6f, 0xbb, 0xbf, 0x12,
0x59, 0xe9, 0x2a, 0x87, 0xff, 0xb3, 0x01, 0x50, 0xcd, 0x81, 0xd7, 0x04, 0xcb, 0x59, 0x09, 0xcb, 0xb8, 0x88, 0x13, 0x1e, 0xf7, 0x6c, 0x12, 0x1f, 0x2d, 0x02, 0x2f, 0x5d, 0x16, 0x91, 0x0a, 0xdf,
0x59, 0x0d, 0xcb, 0xd1, 0x61, 0xa5, 0xe0, 0x0a, 0x54, 0xe2, 0x16, 0x5e, 0x3f, 0x2c, 0x71, 0x7b, 0x89, 0xee, 0x3b, 0x3e, 0xb1, 0xe3, 0x44, 0xbe, 0xd2, 0x8d, 0xe2, 0x89, 0x5d, 0xac, 0xf0, 0x2e,
0x5b, 0x6a, 0x14, 0xfd, 0xa0, 0xfc, 0x41, 0x21, 0xef, 0x5b, 0xd0, 0x0e, 0x8f, 0xb0, 0x61, 0x1b, 0x83, 0x12, 0x7f, 0xa1, 0x9b, 0x48, 0xa3, 0x92, 0x35, 0x94, 0x5a, 0x95, 0xec, 0xb9, 0x02, 0x56,
0x2b, 0x46, 0x66, 0x2a, 0x37, 0xf8, 0x7f, 0x9a, 0xe0, 0x94, 0x33, 0xcf, 0xe8, 0xa5, 0xd4, 0xbd, 0x16, 0xed, 0x2f, 0xbc, 0xfc, 0x28, 0x0d, 0x65, 0xa5, 0x2b, 0x17, 0xdc, 0x9f, 0x09, 0x40, 0x39,
0x97, 0xd0, 0xa1, 0xc9, 0x9e, 0x22, 0x48, 0x4c, 0x34, 0x77, 0x74, 0x82, 0x9a, 0xb7, 0xbd, 0xe4, 0xb3, 0x3e, 0x27, 0xb7, 0xac, 0x46, 0xb7, 0xac, 0x66, 0xb7, 0x2c, 0xdd, 0xad, 0x18, 0x6c, 0x6d,
0xea, 0xa8, 0x8d, 0xff, 0xa7, 0x7a, 0xf7, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x53, 0xca, 0x32, 0x7e, 0x01, 0x6e, 0x89, 0xec, 0x6d, 0xa9, 0x51, 0xf4, 0xa3, 0xe2, 0x83, 0x82, 0xde, 0x6b,
0x22, 0xb9, 0x12, 0x00, 0x00, 0xd0, 0xf1, 0xf7, 0xb0, 0x61, 0x93, 0x86, 0xf1, 0x9e, 0x49, 0x05, 0xf7, 0x77, 0x03, 0xac, 0x62,
0xe6, 0x99, 0xbc, 0x92, 0xba, 0xf7, 0x0a, 0x3a, 0x34, 0xbd, 0xa1, 0x00, 0x12, 0x13, 0xcd, 0x15,
0x1d, 0xa0, 0x7a, 0xb6, 0x2b, 0xac, 0x9e, 0x10, 0x18, 0x54, 0x1f, 0x20, 0xf4, 0x1d, 0x68, 0xf3,
0xa9, 0xa7, 0x18, 0x2f, 0x2f, 0x9c, 0x98, 0x8c, 0x98, 0xd8, 0xe7, 0x51, 0x1e, 0x70, 0x26, 0x05,
0x82, 0xf8, 0x5b, 0x43, 0xc4, 0xac, 0x20, 0xc2, 0x67, 0xbe, 0x68, 0x7f, 0x11, 0xa6, 0x0f, 0xf5,
0x1b, 0x51, 0x59, 0x3b, 0xe7, 0x5e, 0xfc, 0x41, 0x60, 0xa3, 0xf6, 0x54, 0xa2, 0x37, 0xf1, 0x41,
0x1d, 0xe0, 0xdc, 0x57, 0x7f, 0x17, 0xd4, 0x1e, 0x65, 0x9a, 0x22, 0x2f, 0xa1, 0xc7, 0x61, 0x9a,
0x45, 0xf1, 0x42, 0xe6, 0x68, 0x21, 0x36, 0xd2, 0x7f, 0x1d, 0x60, 0x16, 0x2d, 0xbc, 0xf9, 0x36,
0x02, 0xd3, 0x6a, 0x02, 0x46, 0x53, 0x72, 0xef, 0xc0, 0xf0, 0xb4, 0x77, 0x21, 0xbd, 0x06, 0x2d,
0xee, 0x8a, 0x4c, 0xe3, 0x06, 0x6f, 0x51, 0xc5, 0x7d, 0xbf, 0x6e, 0x22, 0x53, 0x2d, 0xa0, 0xf0,
0x9f, 0x54, 0xfc, 0x77, 0x27, 0x27, 0x4e, 0x14, 0x4f, 0x06, 0xf1, 0xf6, 0x23, 0x95, 0x67, 0x78,
0x5d, 0x15, 0x75, 0xdc, 0xbf, 0x08, 0xd8, 0xf2, 0xfa, 0xe0, 0xd3, 0xf5, 0xf9, 0x5c, 0x20, 0xcd,
0xe7, 0xd6, 0x09, 0xcc, 0x9f, 0xfa, 0x12, 0x5d, 0x93, 0x71, 0xc0, 0x99, 0xe0, 0x71, 0x95, 0xbd,
0x0e, 0xfe, 0x5b, 0xfc, 0xc1, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xbd, 0x09, 0xbc, 0x3f,
0x16, 0x00, 0x00,
} }
...@@ -24,5 +24,6 @@ var ( ...@@ -24,5 +24,6 @@ var (
ErrSaveTable = errors.New("ErrSaveTable") ErrSaveTable = errors.New("ErrSaveTable")
ErrCBRecordExist = errors.New("ErrCBRecordExist") ErrCBRecordExist = errors.New("ErrCBRecordExist")
ErrCycleNotAllowed = errors.New("ErrCycleNotAllowed") ErrCycleNotAllowed = errors.New("ErrCycleNotAllowed")
ErrVersionTopNNotExist = errors.New("ErrVersionTopNNotExist")
ErrNotLegalTopN = errors.New("ErrNotLegalTopN")
) )
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
ttypes "github.com/33cn/plugin/plugin/consensus/dpos/types" ttypes "github.com/33cn/plugin/plugin/consensus/dpos/types"
"github.com/33cn/chain33/common/crypto"
) )
// CanonicalOnceCBInfo ... // CanonicalOnceCBInfo ...
...@@ -32,12 +33,7 @@ func CanonicalCBInfo(cb *DposCBInfo) CanonicalOnceCBInfo { ...@@ -32,12 +33,7 @@ func CanonicalCBInfo(cb *DposCBInfo) CanonicalOnceCBInfo {
func (cb *DposCBInfo)Verify() error { func (cb *DposCBInfo)Verify() error {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
canonical := CanonicalOnceCBInfo{ canonical := CanonicalCBInfo(cb)
Cycle: cb.Cycle,
StopHeight: cb.StopHeight,
StopHash: cb.StopHash,
Pubkey: cb.Pubkey,
}
byteCB, err := json.Marshal(&canonical) byteCB, err := json.Marshal(&canonical)
if err != nil { if err != nil {
...@@ -73,4 +69,153 @@ func (cb *DposCBInfo)Verify() error { ...@@ -73,4 +69,153 @@ func (cb *DposCBInfo)Verify() error {
} }
return nil return nil
}
type OnceCandidator struct {
Pubkey []byte `json:"pubkey,omitempty"`
Address string `json:"address,omitempty"`
Ip string `json:"ip,omitempty"`
}
// CanonicalOnceTopNCandidator ...
type CanonicalOnceTopNCandidator struct {
Cands []*OnceCandidator `json:"cands,omitempty"`
Hash []byte `json:"hash,omitempty"`
Height int64 `json:"height,omitempty"`
SignerPubkey []byte `json:"signerPubkey,omitempty"`
Signature []byte `json:"signature,omitempty"`
}
func (topN *CanonicalOnceTopNCandidator) onlyCopyCands() CanonicalOnceTopNCandidator{
obj := CanonicalOnceTopNCandidator{}
for i := 0; i < len(topN.Cands); i++ {
cand := &OnceCandidator{
Pubkey: topN.Cands[i].Pubkey,
Address: topN.Cands[i].Address,
Ip: topN.Cands[i].Ip,
}
obj.Cands = append(obj.Cands, cand)
}
return obj
}
func (topN *CanonicalOnceTopNCandidator) ID() []byte{
obj := topN.onlyCopyCands()
encode, err := json.Marshal(&obj)
if err != nil {
return nil
}
return crypto.Ripemd160(encode)
}
// CanonicalCBInfo ...
func CanonicalTopNCandidator(topN *TopNCandidator) CanonicalOnceTopNCandidator {
onceTopNCandidator := CanonicalOnceTopNCandidator{
Height: topN.Height,
SignerPubkey: topN.SignerPubkey,
}
for i := 0; i < len(topN.Cands); i++ {
cand := &OnceCandidator{
Pubkey: topN.Cands[i].Pubkey,
Address: topN.Cands[i].Address,
Ip: topN.Cands[i].Ip,
}
onceTopNCandidator.Cands = append(onceTopNCandidator.Cands, cand)
}
return onceTopNCandidator
}
func (topN *TopNCandidator)copyWithoutSig() *TopNCandidator {
cpy := &TopNCandidator{
Hash: topN.Hash,
Height: topN.Height,
SignerPubkey: topN.SignerPubkey,
}
cpy.Cands = make([]*Candidator, len(topN.Cands))
for i := 0; i < len(topN.Cands); i++ {
cpy.Cands[i] = topN.Cands[i]
}
return cpy
}
// Verify ...
func (topN *TopNCandidator)Verify() error {
buf := new(bytes.Buffer)
cpy := topN.copyWithoutSig()
byteCB, err := json.Marshal(cpy)
if err != nil {
return errors.New(fmt.Sprintf("Error marshal TopNCandidator: %v", err))
}
_, err = buf.Write(byteCB)
if err != nil {
return errors.New(fmt.Sprintf("Error write buffer: %v", err))
}
pubkey, err := ttypes.ConsensusCrypto.PubKeyFromBytes(topN.SignerPubkey)
if err != nil {
return errors.New(fmt.Sprintf("Error PubKeyFromBytes: %v", err))
}
sig, err := ttypes.ConsensusCrypto.SignatureFromBytes(topN.Signature)
if err != nil {
return errors.New(fmt.Sprintf("Error SignatureFromBytes: %v", err))
}
if !pubkey.VerifyBytes(buf.Bytes(), sig) {
return errors.New(fmt.Sprintf("Error VerifyBytes: %v", err))
}
return nil
}
func (cand *Candidator)Copy() *Candidator {
cpy := &Candidator{
Address: cand.Address,
Ip: cand.Ip,
Votes: cand.Votes,
Status: cand.Status,
}
cpy.Pubkey = make([]byte, len(cand.Pubkey))
copy(cpy.Pubkey, cand.Pubkey)
return cpy
}
func (topNs *TopNCandidators)CheckVoteStauts(delegateNum int64) {
if topNs.Status == TopNCandidatorsVoteMajorOK || topNs.Status == TopNCandidatorsVoteMajorFail {
return
}
voteMap := make(map[string] int64)
for i := 0; i < len(topNs.CandsVotes); i++ {
key := hex.EncodeToString(topNs.CandsVotes[i].Hash)
if _, ok := voteMap[key]; ok {
voteMap[key]++
if voteMap[key] >= (delegateNum * 2 / 3) {
topNs.Status = TopNCandidatorsVoteMajorOK
for j := 0; j < len(topNs.CandsVotes[i].Cands); j++ {
topNs.FinalCands = append(topNs.FinalCands, topNs.CandsVotes[i].Cands[j].Copy())
}
return
}
} else {
voteMap[key] = 1
}
}
var maxVotes int64 = 0
var sumVotes int64 = 0
for _, v := range voteMap {
if v > maxVotes {
maxVotes = v
}
sumVotes += v
}
if maxVotes + (delegateNum - sumVotes) < (delegateNum * 2 / 3) {
topNs.Status = TopNCandidatorsVoteMajorFail
}
} }
\ No newline at end of file
...@@ -281,7 +281,7 @@ func (tx *DposCBRow) Get(key string) ([]byte, error) { ...@@ -281,7 +281,7 @@ func (tx *DposCBRow) Get(key string) ([]byte, error) {
} else if key == "height" { } else if key == "height" {
return []byte(fmt.Sprintf("%018d", tx.StopHeight)), nil return []byte(fmt.Sprintf("%018d", tx.StopHeight)), nil
} else if key == "hash" { } else if key == "hash" {
return []byte(fmt.Sprintf("%X", tx.StopHash)), nil return tx.StopHash, nil
} }
return nil, types.ErrNotFound return nil, types.ErrNotFound
......
...@@ -45,6 +45,7 @@ func (t *DPosType) GetTypeMap() map[string]int32 { ...@@ -45,6 +45,7 @@ func (t *DPosType) GetTypeMap() map[string]int32 {
"RegistVrfM": DposVoteActionRegistVrfM, "RegistVrfM": DposVoteActionRegistVrfM,
"RegistVrfRP": DposVoteActionRegistVrfRP, "RegistVrfRP": DposVoteActionRegistVrfRP,
"RecordCB": DposVoteActionRecordCB, "RecordCB": DposVoteActionRecordCB,
"RegistTopN": DPosVoteActionRegistTopNCandidator,
} }
} }
...@@ -59,5 +60,6 @@ func (t *DPosType) GetLogMap() map[int64]*types.LogInfo { ...@@ -59,5 +60,6 @@ func (t *DPosType) GetLogMap() map[int64]*types.LogInfo {
TyLogVrfMRegist: {Ty: reflect.TypeOf(ReceiptVrf{}), Name: "TyLogVrfMRegist"}, TyLogVrfMRegist: {Ty: reflect.TypeOf(ReceiptVrf{}), Name: "TyLogVrfMRegist"},
TyLogVrfRPRegist: {Ty: reflect.TypeOf(ReceiptVrf{}), Name: "TyLogVrfRPRegist"}, TyLogVrfRPRegist: {Ty: reflect.TypeOf(ReceiptVrf{}), Name: "TyLogVrfRPRegist"},
TyLogCBInfoRecord: {Ty: reflect.TypeOf(ReceiptCB{}), Name: "TyLogCBInfoRecord"}, TyLogCBInfoRecord: {Ty: reflect.TypeOf(ReceiptCB{}), Name: "TyLogCBInfoRecord"},
TyLogTopNCandidatorRegist: {Ty: reflect.TypeOf(ReceiptTopN{}), Name: "TyLogTopNCandidatorRegist"},
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment