Commit 099f5849 authored by mdj33's avatar mdj33 Committed by vipwzw

paracross super node manage

parent 28de2d98
......@@ -108,6 +108,11 @@ function para_transfer() {
echo "txhash=$txhash"
query_tx "${PARA_CLI}" "${txhash}"
echo "=========== # para chain takeover node group ============="
txhash=$(${PARA_CLI} send para node -o takeover -k 0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b)
echo "tx=$txhash"
query_tx "${PARA_CLI}" "${txhash}"
}
function para_transfer2account() {
......
......@@ -12,6 +12,7 @@ import (
"strings"
"github.com/33cn/chain33/rpc/jsonclient"
rpcTypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/system/dapp/commands"
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
......@@ -31,6 +32,12 @@ func ParcCmd() *cobra.Command {
CreateRawTransferCmd(),
CreateRawWithdrawCmd(),
CreateRawTransferToExecCmd(),
CreateRawNodeManageCmd(),
GetParaInfoCmd(),
GetParaListCmd(),
GetNodeGroupCmd(),
GetNodeInfoCmd(),
GetNodeListCmd(),
IsSyncCmd(),
GetHeightCmd(),
GetBlockInfoCmd(),
......@@ -229,6 +236,59 @@ func createWithdraw(cmd *cobra.Command, args []string) {
commands.CreateAssetWithdraw(cmd, args, pt.ParaX)
}
//CreateRawNodeManageCmd create super node mange tx
func CreateRawNodeManageCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "node",
Short: "Create a super node manage cmd",
Run: createNodeTx,
}
addNodeManageFlags(cmd)
return cmd
}
func addNodeManageFlags(cmd *cobra.Command) {
cmd.Flags().StringP("operation", "o", "", "add,delete,vote,takeover operation")
cmd.MarkFlagRequired("operation")
cmd.Flags().StringP("addr", "a", "", "operating addr object")
cmd.Flags().StringP("value", "v", "pass", "vote value: pass,no")
}
func createNodeTx(cmd *cobra.Command, args []string) {
op, _ := cmd.Flags().GetString("operation")
opAddr, _ := cmd.Flags().GetString("addr")
val, _ := cmd.Flags().GetString("value")
if op != "vote" && op != "delete" && op != "add" && op != "takeover" {
fmt.Println(os.Stderr, "operation should be one of add, delete,vote,takeover")
}
if (op == "vote" || op == "add" || op == "delete") && opAddr == "" {
fmt.Println(os.Stderr, "addr parameter should not be null")
return
}
if op == "vote" && (val != "pass" && val != "no") {
fmt.Println(os.Stderr, "vote operation value parameter require pass or no value")
return
}
payload := &pt.ParaNodeAddrConfig{Op: op, Value: val, Addr: opAddr}
//modify := &pt.ParacrossAction{
// Ty: pt.ParacrossActionNodeConfig,
// Value: &pt.ParacrossAction_NodeConfig{NodeConfig: v},
//}
params := &rpcTypes.CreateTxIn{
Execer: types.ExecName(pt.ParaX),
ActionName: "NodeConfig",
Payload: types.MustPBToJSON(payload),
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
}
// IsSyncCmd query parachain is sync
func IsSyncCmd() *cobra.Command {
cmd := &cobra.Command{
......@@ -303,3 +363,149 @@ func blockInfo(cmd *cobra.Command, args []string) {
ctx.Run()
}
// GetParaInfoCmd get para chain status by height
func GetParaInfoCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "para_status",
Short: "Get para chain current status",
Run: paraInfo,
}
addParaBodyCmdFlags(cmd)
return cmd
}
func addParaBodyCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("title", "t", "", "parallel chain's title")
cmd.MarkFlagRequired("title")
cmd.Flags().Int64P("height", "g", 0, "height to para chain")
cmd.MarkFlagRequired("height")
}
func paraInfo(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
title, _ := cmd.Flags().GetString("title")
height, _ := cmd.Flags().GetInt64("height")
params := pt.ReqParacrossTitleHeight{
Title: title,
Height: height,
}
var res pt.ReceiptParacrossDone
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.GetTitleHeight", params, &res)
ctx.Run()
}
// GetParaListCmd get para chain info list
func GetParaListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "para_list",
Short: "Get para chain info list by titles",
Run: paraList,
}
return cmd
}
func paraList(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
var res pt.RespParacrossTitles
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.ListTitles", types.ReqNil{}, &res)
ctx.Run()
}
// GetNodeInfoCmd get node current status
func GetNodeInfoCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "node_status",
Short: "Get node current vote status",
Run: nodeInfo,
}
addNodeBodyCmdFlags(cmd)
return cmd
}
func addNodeBodyCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("title", "t", "", "parallel chain's title")
cmd.MarkFlagRequired("title")
cmd.Flags().StringP("addr", "a", "", "addr apply for super user")
cmd.MarkFlagRequired("addr")
}
func nodeInfo(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
title, _ := cmd.Flags().GetString("title")
addr, _ := cmd.Flags().GetString("addr")
params := pt.ReqParacrossNodeInfo{
Title: title,
Addr: addr,
}
var res pt.ParaNodeAddrStatus
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.GetNodeStatus", params, &res)
ctx.Run()
}
// GetNodeListCmd get node list by status
func GetNodeListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "node_list",
Short: "Get node info list by status",
Run: nodeList,
}
addNodeListCmdFlags(cmd)
return cmd
}
func addNodeListCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("title", "t", "", "parallel chain's title")
cmd.MarkFlagRequired("title")
cmd.Flags().Int32P("status", "s", 0, "status:0:add,1:added,2:quit,3:quited,4:refused")
cmd.MarkFlagRequired("status")
}
func nodeList(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
title, _ := cmd.Flags().GetString("title")
status, _ := cmd.Flags().GetInt32("status")
params := pt.ReqParacrossNodeInfo{
Title: title,
Status: status,
}
var res pt.RespParacrossNodeAddrs
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.ListNodeStatus", params, &res)
ctx.Run()
}
// GetNodeListCmd get node list by status
func GetNodeGroupCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "node_group",
Short: "Get super node group by title",
Run: nodeGroup,
}
addNodeGroupCmdFlags(cmd)
return cmd
}
func addNodeGroupCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("title", "t", "", "parallel chain's title")
cmd.MarkFlagRequired("title")
}
func nodeGroup(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
title, _ := cmd.Flags().GetString("title")
var res types.ReplyConfig
ctx := jsonclient.NewRPCCtx(rpcLaddr, "paracross.GetNodeGroup", types.ReqString{Data: title}, &res)
ctx.Run()
}
......@@ -39,33 +39,36 @@ func newAction(t *Paracross, tx *types.Transaction) *action {
t.GetBlockTime(), t.GetHeight(), dapp.ExecAddress(string(tx.Execer)), t.GetAPI(), tx, t}
}
func getNodes(db dbm.KV, title string) (map[string]struct{}, error) {
key := calcConfigNodesKey(title)
func getNodes(db dbm.KV, key []byte) (map[string]struct{}, []string, error) {
item, err := db.Get(key)
if err != nil {
clog.Info("getNodes", "get db key", string(key), "failed", err)
if isNotFound(err) {
err = pt.ErrTitleNotExist
}
return nil, errors.Wrapf(err, "db get key:%s", string(key))
return nil, nil, errors.Wrapf(err, "db get key:%s", string(key))
}
var config types.ConfigItem
err = types.Decode(item, &config)
if err != nil {
return nil, errors.Wrap(err, "decode config")
return nil, nil, errors.Wrap(err, "decode config")
}
value := config.GetArr()
if value == nil {
// 在配置地址后,发现配置错了, 删除会出现这种情况
return map[string]struct{}{}, nil
return map[string]struct{}{}, nil, nil
}
var nodes []string
uniqNode := make(map[string]struct{})
for _, v := range value.Value {
if _, exist := uniqNode[v]; !exist {
uniqNode[v] = struct{}{}
nodes = append(nodes, v)
}
}
return uniqNode, nil
return uniqNode, nodes, nil
}
func validTitle(title string) bool {
......@@ -220,8 +223,8 @@ func (a *action) Commit(commit *pt.ParacrossCommitAction) (*types.Receipt, error
if !validTitle(commit.Status.Title) {
return nil, pt.ErrInvalidTitle
}
nodes, err := getNodes(a.db, commit.Status.Title)
key := calcParaNodeGroupKey(commit.Status.Title)
nodes, _, err := getNodes(a.db, key)
if err != nil {
return nil, errors.Wrapf(err, "getNodes for title:%s", commit.Status.Title)
}
......
......@@ -65,7 +65,7 @@ func (suite *AssetTransferTestSuite) SetupTest() {
MainBlockHash10 = blockDetail.Block.Hash()
// setup title nodes : len = 1
nodeConfigKey := calcConfigNodesKey(Title)
nodeConfigKey := calcManageConfigNodesKey(Title)
nodeValue := makeNodeInfo(Title, Title, 1)
suite.stateDB.Set(nodeConfigKey, types.Encode(nodeValue))
value, err := suite.stateDB.Get(nodeConfigKey)
......
......@@ -60,7 +60,7 @@ func (suite *AssetWithdrawTestSuite) SetupTest() {
MainBlockHash10 = blockDetail.Block.Hash()
// setup title nodes : len = 1
nodeConfigKey := calcConfigNodesKey(Title)
nodeConfigKey := calcManageConfigNodesKey(Title)
nodeValue := makeNodeInfo(Title, Title, 1)
suite.stateDB.Set(nodeConfigKey, types.Encode(nodeValue))
value, err := suite.stateDB.Get(nodeConfigKey)
......
......@@ -86,3 +86,9 @@ func (e *Paracross) Exec_TransferToExec(payload *types.AssetsTransferToExec, tx
a := newAction(e, tx)
return a.TransferToExec(payload, tx, index)
}
//Exec_NodeConfig exec super node config
func (e *Paracross) Exec_NodeConfig(payload *pt.ParaNodeAddrConfig, tx *types.Transaction, index int) (*types.Receipt, error) {
a := newAction(e, tx)
return a.NodeConfig(payload)
}
......@@ -49,6 +49,35 @@ func (e *Paracross) ExecDelLocal_Commit(payload *pt.ParacrossCommitAction, tx *t
return &set, nil
}
func (e *Paracross) ExecDelLocal_NodeConfig(payload *pt.ParaNodeAddrConfig, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
var set types.LocalDBSet
for _, log := range receiptData.Logs {
if log.Ty == pt.TyLogParaNodeConfig {
var g pt.ReceiptParaNodeConfig
err := types.Decode(log.Log, &g)
if err != nil {
return nil, err
}
if g.Prev != nil {
set.KV = append(set.KV, &types.KeyValue{
Key: calcLocalNodeTitleStatus(g.Current.Title, g.Current.ApplyAddr, g.Prev.Status), Value: types.Encode(g.Prev)})
}
set.KV = append(set.KV, &types.KeyValue{
Key: calcLocalNodeTitleStatus(g.Current.Title, g.Current.ApplyAddr, g.Current.Status), Value: nil})
} else if log.Ty == pt.TyLogParaNodeVoteDone {
var g pt.ReceiptParaNodeVoteDone
err := types.Decode(log.Log, &g)
if err != nil {
return nil, err
}
key := calcLocalNodeTitleDone(g.Title, g.TargetAddr)
set.KV = append(set.KV, &types.KeyValue{Key: key, Value: nil})
}
}
return &set, nil
}
//ExecDelLocal_AssetTransfer asset transfer del local db process
func (e *Paracross) ExecDelLocal_AssetTransfer(payload *types.AssetsTransfer, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
var set types.LocalDBSet
......
......@@ -51,6 +51,36 @@ func (e *Paracross) ExecLocal_Commit(payload *pt.ParacrossCommitAction, tx *type
return &set, nil
}
func (e *Paracross) ExecLocal_NodeConfig(payload *pt.ParaNodeAddrConfig, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
var set types.LocalDBSet
for _, log := range receiptData.Logs {
if log.Ty == pt.TyLogParaNodeConfig {
var g pt.ReceiptParaNodeConfig
err := types.Decode(log.Log, &g)
if err != nil {
return nil, err
}
if g.Prev != nil {
set.KV = append(set.KV, &types.KeyValue{
Key: calcLocalNodeTitleStatus(g.Current.Title, g.Current.ApplyAddr, g.Prev.Status), Value: nil})
}
set.KV = append(set.KV, &types.KeyValue{
Key: calcLocalNodeTitleStatus(g.Current.Title, g.Current.ApplyAddr, g.Current.Status),
Value: types.Encode(g.Current)})
} else if log.Ty == pt.TyLogParaNodeVoteDone {
var g pt.ReceiptParaNodeVoteDone
err := types.Decode(log.Log, &g)
if err != nil {
return nil, err
}
key := calcLocalNodeTitleDone(g.Title, g.TargetAddr)
set.KV = append(set.KV, &types.KeyValue{Key: key, Value: types.Encode(&g)})
}
}
return &set, nil
}
//ExecLocal_AssetTransfer asset transfer local proc
func (e *Paracross) ExecLocal_AssetTransfer(payload *types.AssetsTransfer, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
var set types.LocalDBSet
......
......@@ -14,22 +14,33 @@ var (
title string
titleHeight string
titleHash string
configNodes string
managerConfigNodes string //manager 合约配置的nodes
paraConfigNodes string //平行链自组织配置的nodes,最初是从manager同步过来
paraConfigNodeAddr string //平行链配置节点账户
localTx string
localTitle string
localTitleHeight string
localAssetKey string
localNodeTitle string
localNodeTitleStatus string
localNodeTitleDone string
)
func setPrefix() {
title = "mavl-paracross-title-"
titleHeight = "mavl-paracross-titleHeight-"
titleHash = "mavl-paracross-titleHash-"
configNodes = "paracross-nodes-"
managerConfigNodes = "paracross-nodes-"
paraConfigNodes = "mavl-paracross-nodes-title-"
paraConfigNodeAddr = "mavl-paracross-nodes-titleAddr-"
localTx = "LODB-paracross-titleHeightAddr-"
localTitle = "LODB-paracross-title-"
localTitleHeight = "LODB-paracross-titleHeight-"
localAssetKey = "LODB-paracross-asset-"
localNodeTitleStatus = "LODB-paracross-nodesTitleStatus-"
localNodeTitleDone = "LODB-paracross-nodesTitleDone-"
}
func calcTitleKey(t string) []byte {
......@@ -48,11 +59,19 @@ func calcLocalHeightKey(title string, height int64) []byte {
return []byte(fmt.Sprintf(localTitleHeight+"%s-%d", title, height))
}
func calcConfigNodesKey(title string) []byte {
key := configNodes + title
func calcManageConfigNodesKey(title string) []byte {
key := managerConfigNodes + title
return []byte(types.ManageKey(key))
}
func calcParaNodeGroupKey(title string) []byte {
return []byte(fmt.Sprintf(paraConfigNodes+"%s", title))
}
func calcParaNodeAddrKey(title string, addr string) []byte {
return []byte(fmt.Sprintf(paraConfigNodeAddr+"%s-%s", title, addr))
}
func calcLocalTxKey(title string, height int64, addr string) []byte {
return []byte(fmt.Sprintf(localTx+"%s-%012-%s", title, height, addr))
}
......@@ -61,10 +80,6 @@ func calcLocalTitleKey(title string) []byte {
return []byte(fmt.Sprintf(localTitle+"%s", title))
}
func calcLocalTitleHeightKey(title string, height int64) []byte {
return []byte(fmt.Sprintf(localTitle+"%s-%012d", title, height))
}
func calcLocalTitlePrefix() []byte {
return []byte(localTitle)
}
......@@ -72,3 +87,15 @@ func calcLocalTitlePrefix() []byte {
func calcLocalAssetKey(hash []byte) []byte {
return []byte(fmt.Sprintf(localAssetKey+"%s", hash))
}
func calcLocalNodeTitleStatus(title, addr string, status int32) []byte {
return []byte(fmt.Sprintf(localNodeTitleStatus+"%s-%02d-%s", title, status, addr))
}
func calcLocalNodeStatusPrefix(title string, status int32) []byte {
return []byte(fmt.Sprintf(localNodeTitleStatus+"%s-%02d", title, status))
}
func calcLocalNodeTitleDone(title, addr string) []byte {
return []byte(fmt.Sprintf(localNodeTitleDone+"%s-%s", title, addr))
}
......@@ -322,7 +322,7 @@ func (c *Paracross) allow(tx *types.Transaction, index int) error {
return nil
}
if types.IsDappFork(c.GetHeight(), pt.ParaX, pt.ForkCommitTx) {
if payload.Ty == pt.ParacrossActionCommit {
if payload.Ty == pt.ParacrossActionCommit || payload.Ty == pt.ParacrossActionNodeConfig {
return nil
}
}
......
......@@ -111,7 +111,7 @@ func (suite *CommitTestSuite) SetupSuite() {
MainBlockHash10 = blockDetail.Block.Hash()
// setup title nodes : len = 4
nodeConfigKey := calcConfigNodesKey(Title)
nodeConfigKey := calcManageConfigNodesKey(Title)
nodeValue := makeNodeInfo(Title, Title, 4)
suite.stateDB.Set(nodeConfigKey, types.Encode(nodeValue))
value, err := suite.stateDB.Get(nodeConfigKey)
......@@ -139,7 +139,7 @@ func (suite *CommitTestSuite) SetupSuite() {
}
func (suite *CommitTestSuite) TestSetup() {
nodeConfigKey := calcConfigNodesKey(Title)
nodeConfigKey := calcManageConfigNodesKey(Title)
suite.T().Log(string(nodeConfigKey))
_, err := suite.stateDB.Get(nodeConfigKey)
if err != nil {
......
......@@ -6,6 +6,7 @@ package executor
import (
"encoding/hex"
"fmt"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
......@@ -39,6 +40,45 @@ func (p *Paracross) Query_GetTitleByHash(in *pt.ReqParacrossTitleHash) (types.Me
}
func (p *Paracross) Query_GetNodeGroup(in *types.ReqString) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
key := calcParaNodeGroupKey(in.GetData())
ret, _, err := getNodes(p.GetStateDB(), key)
if err != nil {
return nil, errors.Cause(err)
}
var nodes []string
for k, _ := range ret {
nodes = append(nodes, k)
}
var reply types.ReplyConfig
reply.Key = string(key)
reply.Value = fmt.Sprint(nodes)
return &reply, nil
}
func (p *Paracross) Query_GetNodeAddrInfo(in *pt.ReqParacrossNodeInfo) (types.Message, error) {
if in == nil || in.Title == "" || in.Addr == "" {
return nil, types.ErrInvalidParam
}
key := calcParaNodeAddrKey(in.Title, in.Addr)
stat, err := getNodeAddr(p.GetStateDB(), key)
if err != nil {
return nil, err
}
return stat, nil
}
func (p *Paracross) Query_ListNodeStatusInfo(in *pt.ReqParacrossNodeInfo) (types.Message, error) {
if in == nil || in.Title == "" {
return nil, types.ErrInvalidParam
}
return listLocalNodeStatus(p.GetLocalDB(), in.Title, in.Status)
}
//Query_ListTitles query paracross titles list
func (p *Paracross) Query_ListTitles(in *types.ReqNil) (types.Message, error) {
return p.paracrossListTitles()
......@@ -130,8 +170,29 @@ func listLocalTitles(db dbm.KVDB) (types.Message, error) {
return &resp, nil
}
//按状态遍历
func listLocalNodeStatus(db dbm.KVDB, title string, status int32) (types.Message, error) {
prefix := calcLocalNodeStatusPrefix(title, status)
res, err := db.List(prefix, []byte(""), 0, 1)
if err != nil {
return nil, err
}
var resp pt.RespParacrossNodeAddrs
for _, r := range res {
var st pt.ReceiptParaNodeVoteDone
err = types.Decode(r, &st)
if err != nil {
panic(err)
}
resp.Addrs = append(resp.Addrs, &st)
}
return &resp, nil
}
func loadLocalTitle(db dbm.KV, title string, height int64) (types.Message, error) {
key := calcLocalTitleHeightKey(title, height)
key := calcLocalHeightKey(title, height)
res, err := db.Get(key)
if err != nil {
return nil, err
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/pkg/errors"
)
func getNodeAddr(db dbm.KV, key []byte) (*pt.ParaNodeAddrStatus, error) {
val, err := db.Get(key)
if err != nil {
return nil, err
}
var status pt.ParaNodeAddrStatus
err = types.Decode(val, &status)
return &status, err
}
func saveNodeAddr(db dbm.KV, key []byte, status types.Message) error {
// use as a types.Message
val := types.Encode(status)
return db.Set(key, val)
}
func makeVoteDoneReceipt(config *pt.ParaNodeAddrConfig, totalCount, commitCount, most int, ok bool, status int32) *types.Receipt {
log := &pt.ReceiptParaNodeVoteDone{
Title: config.Title,
TargetAddr: config.Addr,
TotalNodes: int32(totalCount),
TotalVote: int32(commitCount),
MostVote: int32(most),
VoteRst: ok,
DoneStatus: status,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: nil,
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaNodeVoteDone,
Log: types.Encode(log),
},
},
}
}
func makeNodeConfigReceipt(addr string, config *pt.ParaNodeAddrConfig, prev, current *pt.ParaNodeAddrStatus) *types.Receipt {
key := calcParaNodeAddrKey(config.Title, config.Addr)
log := &pt.ReceiptParaNodeConfig{
Addr: addr,
Config: config,
Prev: prev,
Current: current,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaNodeConfig,
Log: types.Encode(log),
},
},
}
}
func makeParaNodeGroupReiceipt(title string, prev, current *types.ConfigItem) *types.Receipt {
key := calcParaNodeGroupKey(title)
log := &types.ReceiptConfig{Prev: prev, Current: current}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: pt.TyLogParaNodeGroupUpdate,
Log: types.Encode(log),
},
},
}
}
func (a *action) nodeAdd(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
key := calcParaNodeGroupKey(config.Title)
nodes, _, err := getNodes(a.db, key)
if err != nil {
return nil, errors.Wrapf(err, "getNodes for title:%s", config.Title)
}
if validNode(a.fromaddr, nodes) {
return nil, errors.Wrapf(pt.ErrParaNodeAddrExisted, "nodeAddr existed:%s", a.fromaddr)
}
key = calcParaNodeAddrKey(config.Title, config.Addr)
stat, err := getNodeAddr(a.db, key)
if err != nil {
if !isNotFound(err) {
return nil, err
}
clog.Info("first time add node addr", "key", string(key))
stat := &pt.ParaNodeAddrStatus{Status: pt.ParacrossNodeAdding,
Title: config.Title,
ApplyAddr: config.Addr,
Votes: &pt.ParaNodeVoteDetail{}}
saveNodeAddr(a.db, key, stat)
return makeNodeConfigReceipt(a.fromaddr, config, nil, stat), nil
}
copyStatus := *stat
if stat.Status != pt.ParacrossNodeQuited {
clog.Error("nodeaccount.nodeAdd key exist", "key", string(key), "status", stat)
return nil, pt.ErrParaNodeAddrExisted
}
stat.Status = pt.ParacrossNodeAdding
stat.Votes = &pt.ParaNodeVoteDetail{}
saveNodeAddr(a.db, key, stat)
return makeNodeConfigReceipt(a.fromaddr, config, &copyStatus, stat), nil
}
func (a *action) nodeDelete(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
key := calcParaNodeGroupKey(config.Title)
nodes, _, err := getNodes(a.db, key)
if err != nil {
return nil, errors.Wrapf(err, "getNodes for title:%s", config.Title)
}
if !validNode(a.fromaddr, nodes) {
return nil, errors.Wrapf(pt.ErrParaNodeAddrNotExisted, "nodeAddr not existed:%s", a.fromaddr)
}
//不允许最后一个账户退出
if len(nodes) == 1 {
return nil, errors.Wrapf(pt.ErrParaNodeGroupLastAddr, "nodeAddr last one:%s", a.fromaddr)
}
key = calcParaNodeAddrKey(config.Title, config.Addr)
stat, err := getNodeAddr(a.db, key)
if err != nil {
return nil, err
}
//refused or quiting
if stat.Status != pt.ParacrossNodeAdded {
clog.Error("nodeaccount.nodeDelete wrong status", "key", string(key), "status", stat)
return nil, errors.Wrapf(pt.ErrParaNodeGroupRefuseByVote, "nodeAddr refused by vote:%s", a.fromaddr)
}
copyStat := *stat
stat.Status = pt.ParacrossNodeQuiting
stat.Votes = &pt.ParaNodeVoteDetail{}
saveNodeAddr(a.db, key, stat)
return makeNodeConfigReceipt(a.fromaddr, config, &copyStat, stat), nil
}
func getMostVote(stat *pt.ParaNodeAddrStatus) (int, bool) {
var ok, nok int
for _, v := range stat.GetVotes().Votes {
if v == pt.ParaNodeVotePass {
ok++
} else {
nok++
}
}
if ok > nok {
return ok, true
}
return nok, false
}
func hasVoted(addrs []string, addr string) (bool, int) {
return hasCommited(addrs, addr)
}
func (a *action) nodeVote(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
key := calcParaNodeGroupKey(config.Title)
nodes, _, err := getNodes(a.db, key)
if err != nil {
return nil, errors.Wrapf(err, "getNodes for title:%s", config.Title)
}
if !validNode(a.fromaddr, nodes) {
return nil, errors.Wrapf(pt.ErrNodeNotForTheTitle, "not validNode:%s", a.fromaddr)
}
if a.fromaddr == config.Addr {
return nil, errors.Wrapf(pt.ErrParaNodeVoteSelf, "not allow to vote self:%s", a.fromaddr)
}
// 如果投票账户是group账户,需计算此账户之外的投票
if validNode(config.Addr, nodes) {
temp := make(map[string]struct{})
for k, _ := range nodes {
if k != config.Addr {
temp[k] = struct{}{}
}
}
nodes = temp
}
key = calcParaNodeAddrKey(config.Title, config.Addr)
stat, err := getNodeAddr(a.db, key)
if err != nil {
return nil, err
}
copyStat := *stat
if stat.Votes == nil {
stat.Votes = &pt.ParaNodeVoteDetail{}
}
found, index := hasVoted(stat.Votes.Addrs, a.fromaddr)
if found {
stat.Votes.Votes[index] = config.Value
} else {
stat.Votes.Addrs = append(stat.Votes.Addrs, a.fromaddr)
stat.Votes.Votes = append(stat.Votes.Votes, config.Value)
}
receipt := makeNodeConfigReceipt(config.Addr, config, &copyStat, stat)
most, ok := getMostVote(stat)
if !isCommitDone(stat, nodes, most) {
saveNodeAddr(a.db, key, stat)
return receipt, nil
}
clog.Info("paracross.nodeVote commit ----pass", "most", most, "pass", ok)
var receiptGroup *types.Receipt
if !ok {
stat.Status = pt.ParacrossNodeRefused
} else {
if stat.Status == pt.ParacrossNodeAdding {
receiptGroup, err = unpdateNodeGroup(a.db, config.Title, config.Addr, true)
if err != nil {
return nil, err
}
stat.Status = pt.ParacrossNodeAdded
} else if stat.Status == pt.ParacrossNodeQuiting {
receiptGroup, err = unpdateNodeGroup(a.db, config.Title, config.Addr, false)
if err != nil {
return nil, err
}
stat.Status = pt.ParacrossNodeQuited
}
}
saveNodeAddr(a.db, key, stat)
receipt = makeNodeConfigReceipt(config.Addr, config, &copyStat, stat)
if receiptGroup != nil {
receipt.KV = append(receipt.KV, receiptGroup.KV...)
receipt.Logs = append(receipt.Logs, receiptGroup.Logs...)
}
receiptDone := makeVoteDoneReceipt(config, len(nodes), len(stat.Votes.Addrs), most, ok, stat.Status)
receipt.KV = append(receipt.KV, receiptDone.KV...)
receipt.Logs = append(receipt.Logs, receiptDone.Logs...)
return receipt, nil
}
func unpdateNodeGroup(db dbm.KV, title, addr string, add bool) (*types.Receipt, error) {
var item types.ConfigItem
key := calcParaNodeGroupKey(title)
value, err := db.Get(key)
if err != nil {
return nil, err
}
if value != nil {
err = types.Decode(value, &item)
if err != nil {
clog.Error("unpdateNodeGroup", "decode db key", key)
return nil, err // types.ErrBadConfigValue
}
}
copyValue := *item.GetArr()
copyItem := item
copyItem.Value = &types.ConfigItem_Arr{Arr: &copyValue}
if add {
item.GetArr().Value = append(item.GetArr().Value, addr)
item.Addr = addr
clog.Info("unpdateNodeGroup", "add key", key, "from", copyItem.GetArr().Value, "to", item.GetArr().Value)
} else {
//必须保留至少1个授权账户
if len(item.GetArr().Value) <= 1 {
return nil, pt.ErrParaNodeGroupLastAddr
}
item.Addr = addr
item.GetArr().Value = make([]string, 0)
for _, value := range copyItem.GetArr().Value {
clog.Info("unpdateNodeGroup", "key delete", key, "current", value)
if value != addr {
item.GetArr().Value = append(item.GetArr().Value, value)
}
}
}
return makeParaNodeGroupReiceipt(title, &copyItem, &item), nil
}
func (a *action) nodeTakeover(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
key := calcManageConfigNodesKey(config.Title)
_, nodes, err := getNodes(a.db, key)
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, pt.ErrParaManageNodesNotSet
}
var item types.ConfigItem
key = calcParaNodeGroupKey(config.Title)
value, err := a.db.Get(key)
if err != nil && !isNotFound(err) {
return nil, err
}
if value != nil {
return nil, pt.ErrParaNodeGroupExisted
}
item.Key = string(key)
emptyValue := &types.ArrayConfig{Value: make([]string, 0)}
arr := types.ConfigItem_Arr{Arr: emptyValue}
item.Value = &arr
copyItem := item
item.GetArr().Value = append(item.GetArr().Value, nodes...)
item.Addr = a.fromaddr
a.db.Set(key, types.Encode(&item))
receipt := makeParaNodeGroupReiceipt(config.Title, &copyItem, &item)
//update add addr
for _, addr := range nodes {
key = calcParaNodeAddrKey(config.Title, addr)
stat := &pt.ParaNodeAddrStatus{Status: pt.ParacrossNodeAdded,
Title: config.Title,
ApplyAddr: addr,
Votes: &pt.ParaNodeVoteDetail{}}
saveNodeAddr(a.db, key, stat)
config.Addr = addr
r := makeNodeConfigReceipt(a.fromaddr, config, nil, stat)
receipt.KV = append(receipt.KV, r.KV...)
receipt.Logs = append(receipt.Logs, r.Logs...)
}
return receipt, nil
}
func (a *action) NodeConfig(config *pt.ParaNodeAddrConfig) (*types.Receipt, error) {
if !validTitle(config.Title) {
return nil, pt.ErrInvalidTitle
}
if config.Op == pt.ParaNodeAdd {
if config.Addr != a.fromaddr {
return nil, types.ErrFromAddr
}
return a.nodeAdd(config)
} else if config.Op == pt.ParaNodeDelete {
if config.Addr != a.fromaddr {
return nil, types.ErrFromAddr
}
return a.nodeDelete(config)
} else if config.Op == pt.ParaNodeVote {
return a.nodeVote(config)
} else if config.Op == pt.ParaNodeTakeover {
return a.nodeTakeover(config)
} else {
return nil, pt.ErrParaUnSupportNodeOper
}
}
# paracross 执行器 授权账户管理
## 执行逻辑
1. 平行链申请开链之前申请几个授权账户,没有授权账户无法做跨链交易,目前没有押金机制
1. 主链超级用户会分别向主链和平行链的manager合约发送账户添加tx,作为平行链的初始授权账户
1. 平行链开链后在做跨链tx之前需要任何一个初始授权账户在平行链上发送takeover tx把初始授权账户接管到平行链,
由平行链自己管理,如果不发送接管tx,平行链跨链无法完成
1. 平行链接管初始授权账户后,初始授权账户审核后续新授权账户的添加和退出申请工作,必须除自己外,超过2/3数同意
才可加入和退出,如果只剩最后一个账户,则不允许退出,如果随意退出有只留下僵尸授权账户风险,平行链失控
1. 当前授权账户有投票删除某一个授权账户的权利,2/3数同意的规则
1. 特殊或异常场景:
1. 新申请节点还没投票就退出,不允许,必须都投完票之后再申请
1. 申请节点退出后重新申请,允许重新加入,但需要重新投票
......@@ -3,6 +3,7 @@ syntax = "proto3";
import "transaction.proto";
import "common.proto";
import "blockchain.proto";
import "executor.proto";
package types;
......@@ -33,6 +34,62 @@ message ParacrossConsensusStatus {
string consensBlockHash = 4;
}
message ParaNodeAddrConfig{
string title = 1;
string op = 2;
string addr = 3;
string value = 4;
}
message ParaNodeVoteDetail{
repeated string addrs = 1;
repeated string votes = 2;
}
message ParaNodeAddrStatus{
int32 status = 1;
string title = 2;
string applyAddr = 3;
ParaNodeVoteDetail votes = 4;
}
message ReceiptParaNodeConfig {
string addr = 1;
ParaNodeAddrConfig config = 2;
ParaNodeAddrStatus prev = 3;
ParaNodeAddrStatus current = 4;
}
message ReceiptParaNodeVoteRecord {
string fromAddr = 1;
string voteAddr = 2;
int32 value = 3;
}
message ReceiptParaNodeVoteDone {
string title = 1;
string targetAddr = 2;
int32 totalNodes = 3;
int32 totalVote = 4;
int32 mostVote = 5;
bool voteRst = 6;
int32 doneStatus = 7;
}
// node query
message ReqParacrossNodeInfo {
string title = 1;
string addr = 2;
int32 status = 3;
}
message RespParacrossNodeAddrs {
repeated ReceiptParaNodeVoteDone addrs = 1;
}
message ParaBlock2MainMap {
int64 height = 1;
string blockHash = 2;
......@@ -78,6 +135,8 @@ message ParacrossAction {
AssetsTransfer transfer = 6;
AssetsWithdraw withdraw = 7;
AssetsTransferToExec transferToExec = 8;
ParaNodeAddrConfig nodeConfig = 9;
}
int32 ty = 2;
}
......@@ -149,6 +208,8 @@ message ParacrossAsset {
bool success = 23;
}
service paracross {
rpc GetTitle(ReqString) returns (ParacrossConsensusStatus) {}
rpc ListTitles(ReqNil) returns (RespParacrossTitles) {}
......
......@@ -167,3 +167,57 @@ func (c *Jrpc) GetBlock2MainInfo(req *types.ReqBlocks, result *interface{}) erro
*result = *ret
return err
}
func (c *channelClient) GetNodeGroup(ctx context.Context, req *types.ReqString) (*types.ReplyConfig, error) {
data, err := c.Query(pt.GetExecName(), "GetNodeGroup", req)
if err != nil {
return nil, err
}
if resp, ok := data.(*types.ReplyConfig); ok {
return resp, nil
}
return nil, types.ErrDecode
}
// ListTitles get paracross consensus titles list
func (c *Jrpc) GetNodeGroup(req *types.ReqString, result *interface{}) error {
data, err := c.cli.GetNodeGroup(context.Background(), req)
*result = data
return err
}
func (c *channelClient) GetNodeStatus(ctx context.Context, req *pt.ReqParacrossNodeInfo) (*pt.ParaNodeAddrStatus, error) {
data, err := c.Query(pt.GetExecName(), "GetNodeAddrInfo", req)
if err != nil {
return nil, err
}
if resp, ok := data.(*pt.ParaNodeAddrStatus); ok {
return resp, nil
}
return nil, types.ErrDecode
}
// ListTitles get paracross consensus titles list
func (c *Jrpc) GetNodeStatus(req *pt.ReqParacrossNodeInfo, result *interface{}) error {
data, err := c.cli.GetNodeStatus(context.Background(), req)
*result = data
return err
}
func (c *channelClient) ListNodeStatus(ctx context.Context, req *pt.ReqParacrossNodeInfo) (*pt.RespParacrossNodeAddrs, error) {
data, err := c.Query(pt.GetExecName(), "ListNodeStatusInfo", req)
if err != nil {
return nil, err
}
if resp, ok := data.(*pt.RespParacrossNodeAddrs); ok {
return resp, nil
}
return nil, types.ErrDecode
}
// ListTitles get paracross consensus titles list
func (c *Jrpc) ListNodeStatus(req *pt.ReqParacrossNodeInfo, result *interface{}) error {
data, err := c.cli.ListNodeStatus(context.Background(), req)
*result = data
return err
}
......@@ -27,4 +27,14 @@ var (
ErrParaWaitingNewSeq = errors.New("ErrParaWaitingNewSeq")
// ErrParaCurHashNotMatch para curr main hash not match with pre, main node may switched
ErrParaCurHashNotMatch = errors.New("ErrParaCurHashNotMatch")
// ErrParaUnSupportNodeOper unsupport node operation
ErrParaUnSupportNodeOper = errors.New("ErrParaUnSupportNodeOper")
ErrParaNodeAddrExisted = errors.New("ErrParaNodeAddrExisted")
ErrParaNodeAddrNotExisted = errors.New("ErrParaNodeAddrNotExisted")
ErrParaManageNodesNotSet = errors.New("ErrParaManageNodesNotSet")
ErrParaNodeGroupNotSet = errors.New("ErrParaManageNodesNotSet")
ErrParaNodeGroupExisted = errors.New("ErrParaNodesExisted")
ErrParaNodeGroupLastAddr = errors.New("ErrParaNodeGroupLastAddr")
ErrParaNodeGroupRefuseByVote = errors.New("ErrParaNodeGroupRefuseByVote")
ErrParaNodeVoteSelf = errors.New("ErrParaNodeVoteSelf")
)
......@@ -35,6 +35,10 @@ const (
TyLogParacrossMiner = 655
// TyLogParaAssetDeposit asset deposit log key
TyLogParaAssetDeposit = 656
// TyLogParaNodeConfig config super node log key
TyLogParaNodeConfig = 657
TyLogParaNodeVoteDone = 658
TyLogParaNodeGroupUpdate = 659
)
type paracrossCommitTx struct {
......@@ -66,6 +70,7 @@ const (
ParacrossActionAssetTransfer = iota + paraCrossTransferActionTypeStart
// ParacrossActionAssetWithdraw paracross asset withdraw key
ParacrossActionAssetWithdraw
ParacrossActionNodeConfig
)
// status
......@@ -76,6 +81,30 @@ const (
ParacrossStatusCommitDone
)
// node config op
const (
ParaNodeAdd = "add"
ParaNodeDelete = "delete"
ParaNodeVote = "vote"
ParaNodeTakeover = "takeover"
ParaNodeVotePass = "pass"
ParaNodeVoteNo = "no"
)
const (
// ParacrossNodeAdding apply for adding group
ParacrossNodeAdding = iota
// ParacrossNodeAdded, pass to add by votes
ParacrossNodeAdded
// ParacrossNodeQuiting, apply for quiting
ParacrossNodeQuiting
// ParacrossNodeQuited, pass to quite by votes
ParacrossNodeQuited
// ParacrossNodeRefused, refused by votes, add or quite
ParacrossNodeRefused
)
var (
// ParacrossActionCommitStr Commit string
ParacrossActionCommitStr = string("Commit")
......@@ -132,6 +161,20 @@ func createRawCommitTx(status *ParacrossNodeStatus, name string, fee int64) (*ty
return tx, nil
}
func createRawNodeConfigTx(config *ParaNodeAddrConfig) (*types.Transaction, error) {
config.Title = types.GetTitle()
action := &ParacrossAction{
Ty: ParacrossActionNodeConfig,
Value: &ParacrossAction_NodeConfig{config},
}
tx := &types.Transaction{
Payload: types.Encode(action),
}
return tx, nil
}
// CreateRawAssetTransferTx create asset transfer tx
func CreateRawAssetTransferTx(param *types.CreateTx) (*types.Transaction, error) {
// 跨链交易需要在主链和平行链上执行, 所以应该可以在主链和平行链上构建
......
......@@ -61,6 +61,9 @@ func (p *ParacrossType) GetLogMap() map[int64]*types.LogInfo {
TyLogParaAssetTransfer: {Ty: reflect.TypeOf(types.ReceiptAccountTransfer{}), Name: "LogParaAssetTransfer"},
TyLogParaAssetDeposit: {Ty: reflect.TypeOf(types.ReceiptAccountTransfer{}), Name: "LogParaAssetDeposit"},
TyLogParacrossMiner: {Ty: reflect.TypeOf(ReceiptParacrossMiner{}), Name: "LogParacrossMiner"},
TyLogParaNodeConfig: {Ty: reflect.TypeOf(ReceiptParaNodeConfig{}), Name: "LogParaNodeConfig"},
TyLogParaNodeGroupUpdate: {Ty: reflect.TypeOf(types.ReceiptConfig{}), Name: "LogParaNodeGroupUpdate"},
TyLogParaNodeVoteDone: {Ty: reflect.TypeOf(ReceiptParaNodeVoteDone{}), Name: "LogParaNodeVoteDone"},
}
}
......@@ -74,6 +77,7 @@ func (p *ParacrossType) GetTypeMap() map[string]int32 {
"Transfer": ParacrossActionTransfer,
"Withdraw": ParacrossActionWithdraw,
"TransferToExec": ParacrossActionTransferToExec,
"NodeConfig": ParacrossActionNodeConfig,
}
}
......@@ -107,6 +111,17 @@ func (p ParacrossType) CreateTx(action string, message json.RawMessage) (*types.
action == "ParacrossTransferToExec" || action == "TransferToExec" {
return p.CreateRawTransferTx(action, message)
} else if action == "NodeConfig" {
if !types.IsPara() {
return nil, types.ErrNotSupport
}
var param ParaNodeAddrConfig
err := json.Unmarshal(message, &param)
if err != nil {
glog.Error("CreateTx.NodeConfig", "Error", err)
return nil, types.ErrInvalidParam
}
return createRawNodeConfigTx(&param)
}
return nil, types.ErrNotSupport
......
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