Commit 5226c257 authored by jiangpeng's avatar jiangpeng

add dapp vote

parent 2983a997
This diff is collapsed.
......@@ -30,6 +30,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/unfreeze" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/vote" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/wasm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/x2ethereum" //auto gen
)
all:
bash build.sh $(OUT) $(FLAG)
#!/bin/bash
# 官方ci集成脚本
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${1}/$strapp"
#FLAG=$2
mkdir -p "${OUT_DIR}"
cp ./build/* "${OUT_DIR}"
/*Package commands implement dapp client commands*/
package commands
import (
jsonrpc "github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
/*
* 实现合约对应客户端
*/
// Cmd vote client command
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "vote",
Short: "vote command",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
//create tx
createGroupCMD(),
updateMemberCMD(),
createVoteCMD(),
commitVoteCMD(),
//query rpc
groupInfoCMD(),
voteInfoCMD(),
memberInfoCMD(),
listGroupCMD(),
listVoteCMD(),
listMemberCMD(),
)
return cmd
}
func markRequired(cmd *cobra.Command, params ...string) {
for _, param := range params {
_ = cmd.MarkFlagRequired(param)
}
}
func sendCreateTxRPC(cmd *cobra.Command, actionName string, req types.Message) {
title, _ := cmd.Flags().GetString("title")
cfg := types.GetCliSysParam(title)
rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
payLoad := types.MustPBToJSON(req)
pm := &rpctypes.CreateTxIn{
Execer: cfg.ExecName(vty.VoteX),
ActionName: actionName,
Payload: payLoad,
}
var res string
ctx := jsonrpc.NewRPCCtx(rpcAddr, "Chain33.CreateTransaction", pm, &res)
ctx.RunWithoutMarshal()
}
func sendQueryRPC(cmd *cobra.Command, funcName string, req, reply types.Message) {
title, _ := cmd.Flags().GetString("title")
cfg := types.GetCliSysParam(title)
rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
payLoad := types.MustPBToJSON(req)
query := &rpctypes.Query4Jrpc{
Execer: cfg.ExecName(vty.VoteX),
FuncName: funcName,
Payload: payLoad,
}
ctx := jsonrpc.NewRPCCtx(rpcAddr, "Chain33.Query", query, reply)
ctx.Run()
}
package commands
import (
"fmt"
"os"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
func createGroupCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "createGroup",
Short: "create tx(create vote group)",
Run: createGroup,
Example: "createGroup -n=group1 -a=admin1 -m=member1 -m=member2",
}
createGroupFlags(cmd)
return cmd
}
func createGroupFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "group name")
cmd.Flags().StringArrayP("admins", "a", nil, "group admin address array")
cmd.Flags().StringArrayP("members", "m", nil, "group member address array")
cmd.Flags().UintSliceP("weights", "w", nil, "member vote weight array")
markRequired(cmd, "name")
}
func createGroup(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
admins, _ := cmd.Flags().GetStringArray("admins")
memberAddrs, _ := cmd.Flags().GetStringArray("members")
weights, _ := cmd.Flags().GetUintSlice("weights")
if name == "" {
fmt.Fprintf(os.Stderr, "ErrNilGroupName")
}
if len(weights) == 0 {
weights = make([]uint, len(memberAddrs))
}
if len(weights) != len(memberAddrs) {
fmt.Fprintf(os.Stderr, "member address array length should equal with vote weight array length")
}
members := make([]*vty.GroupMember, 0)
for i, addr := range memberAddrs {
members = append(members, &vty.GroupMember{Addr: addr, VoteWeight: uint32(weights[i])})
}
params := &vty.CreateGroup{
Name: name,
Admins: admins,
Members: members,
}
sendCreateTxRPC(cmd, vty.NameCreateGroupAction, params)
}
func updateMemberCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "updateMember",
Short: "create tx(update group members)",
Run: updateMember,
Example: "updateMember -g=id -a=addMember1 -a=addMember2 -r=removeMember1 ...",
}
updateMemberFlags(cmd)
return cmd
}
func updateMemberFlags(cmd *cobra.Command) {
cmd.Flags().StringP("groupID", "g", "", "group id")
cmd.Flags().StringArrayP("addMembers", "a", nil, "group member address array for adding")
cmd.Flags().UintSliceP("weights", "w", nil, "member vote weight array for adding")
cmd.Flags().StringArrayP("removeMembers", "r", nil, "group member address array for removing")
markRequired(cmd, "groupID")
}
func updateMember(cmd *cobra.Command, args []string) {
groupID, _ := cmd.Flags().GetString("groupID")
addAddrs, _ := cmd.Flags().GetStringArray("addMembers")
weights, _ := cmd.Flags().GetUintSlice("weights")
removeAddrs, _ := cmd.Flags().GetStringArray("removeMembers")
if groupID == "" {
fmt.Fprintf(os.Stderr, "ErrNilGroupID")
}
if len(weights) == 0 {
weights = make([]uint, len(addAddrs))
}
if len(weights) != len(addAddrs) {
fmt.Fprintf(os.Stderr, "member address array length should equal with vote weight array length")
}
addMembers := make([]*vty.GroupMember, 0)
for i, addr := range addAddrs {
addMembers = append(addMembers, &vty.GroupMember{Addr: addr, VoteWeight: uint32(weights[i])})
}
params := &vty.UpdateMember{
GroupID: groupID,
RemoveMemberAddrs: removeAddrs,
AddMembers: addMembers,
}
sendCreateTxRPC(cmd, vty.NameUpdateMemberAction, params)
}
func createVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "createVote",
Short: "create tx(create vote)",
Run: createVote,
}
createVoteFlags(cmd)
return cmd
}
func createVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "vote name")
cmd.Flags().StringArrayP("groupIDs", "g", nil, "related group id array")
cmd.Flags().StringArrayP("options", "o", nil, "vote option array")
cmd.Flags().Int64P("beginTime", "b", 0, "vote begin unix timestamp, default set now time")
cmd.Flags().Int64P("endTime", "e", 0, "vote end unix timestamp, default set beginTime + 300 seconds")
markRequired(cmd, "name", "groupIDs", "options")
}
func createVote(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
groupIDs, _ := cmd.Flags().GetStringArray("groupIDs")
options, _ := cmd.Flags().GetStringArray("options")
beginTime, _ := cmd.Flags().GetInt64("beginTime")
endTime, _ := cmd.Flags().GetInt64("endTime")
if name == "" {
fmt.Fprintf(os.Stderr, "ErrNilVoteName")
}
if len(groupIDs) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilGroupIDs")
}
if len(options) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilOptions")
}
if beginTime == 0 {
beginTime = types.Now().Unix()
}
if endTime == 0 {
endTime = beginTime + 300
}
params := &vty.CreateVote{
Name: name,
VoteGroups: groupIDs,
VoteOptions: options,
BeginTimestamp: beginTime,
EndTimestamp: endTime,
}
sendCreateTxRPC(cmd, vty.NameCreateVoteAction, params)
}
func commitVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "commitVote",
Short: "create tx(commit vote)",
Run: commitVote,
}
commitVoteFlags(cmd)
return cmd
}
func commitVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("voteID", "v", "", "vote id")
cmd.Flags().StringP("groupID", "g", "", "belonging group id")
cmd.Flags().Uint32P("optionIndex", "o", 0, "voting option index in option array")
markRequired(cmd, "voteID", "groupID", "optionIndex")
}
func commitVote(cmd *cobra.Command, args []string) {
voteID, _ := cmd.Flags().GetString("voteID")
groupID, _ := cmd.Flags().GetString("groupID")
optionIndex, _ := cmd.Flags().GetUint32("optionIndex")
if voteID == "" {
fmt.Fprintf(os.Stderr, "ErrNilVoteID")
}
if len(groupID) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilGroupID")
}
params := &vty.CommitVote{
VoteID: voteID,
GroupID: groupID,
OptionIndex: optionIndex,
}
sendCreateTxRPC(cmd, vty.NameCommitVoteAction, params)
}
package commands
import (
"fmt"
"os"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
func groupInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "groupInfo",
Short: "show group info",
Run: groupInfo,
}
groupInfoFlags(cmd)
return cmd
}
func groupInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringP("groupID", "g", "", "group id")
markRequired(cmd, "groupID")
}
func groupInfo(cmd *cobra.Command, args []string) {
groupID, _ := cmd.Flags().GetString("groupID")
if len(groupID) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilGroupID")
}
params := &types.ReqString{
Data: groupID,
}
info := &vty.GroupVoteInfo{}
sendQueryRPC(cmd, "GetGroup", params, info)
}
func voteInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "voteInfo",
Short: "show vote info",
Run: voteInfo,
}
voteInfoFlags(cmd)
return cmd
}
func voteInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringP("voteID", "v", "", "vote id")
markRequired(cmd, "voteID")
}
func voteInfo(cmd *cobra.Command, args []string) {
voteID, _ := cmd.Flags().GetString("voteID")
if len(voteID) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilVoteID")
}
params := &types.ReqString{
Data: voteID,
}
info := &vty.VoteInfo{}
sendQueryRPC(cmd, "GetVote", params, info)
}
func memberInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "memberInfo",
Short: "show member info",
Run: memberInfo,
}
memberInfoFlags(cmd)
return cmd
}
func memberInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringP("addr", "a", "", "member address")
markRequired(cmd, "addr")
}
func memberInfo(cmd *cobra.Command, args []string) {
addr, _ := cmd.Flags().GetString("addr")
if len(addr) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilAddress")
}
params := &types.ReqString{
Data: addr,
}
info := &vty.MemberInfo{}
sendQueryRPC(cmd, "GetMember", params, info)
}
func listGroupCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listGroup",
Short: "show group list",
Run: listGroup,
}
listCmdFlags(cmd)
return cmd
}
func listGroup(cmd *cobra.Command, args []string) {
runListCMD(cmd, args, "ListGroup", &vty.GroupVoteInfos{})
}
func listVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listVote",
Short: "show vote list",
Run: listVote,
}
listCmdFlags(cmd)
return cmd
}
func listVote(cmd *cobra.Command, args []string) {
runListCMD(cmd, args, "ListVote", &vty.VoteInfos{})
}
func listMemberCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listMember",
Short: "show member list",
Run: listMember,
}
listCmdFlags(cmd)
return cmd
}
func listMember(cmd *cobra.Command, args []string) {
runListCMD(cmd, args, "ListMember", &vty.MemberInfos{})
}
func listCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("startItem", "s", "", "list start item id, default nil value")
cmd.Flags().Uint32P("count", "c", 10, "list count, default 10")
cmd.Flags().Uint32P("direction", "d", 0, "list direction, default 1 (Ascending order)")
}
func runListCMD(cmd *cobra.Command, args []string, funcName string, reply types.Message) {
startID, _ := cmd.Flags().GetString("startItem")
count, _ := cmd.Flags().GetUint32("count")
direction, _ := cmd.Flags().GetUint32("direction")
params := &vty.ReqListItem{
StartItemID: startID,
Count: int32(count),
Direction: int32(direction),
}
sendQueryRPC(cmd, funcName, params, reply)
}
package executor
import (
"encoding/hex"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
type action struct {
db dbm.KV
txHash []byte
fromAddr string
blockTime int64
height int64
index int
}
func newAction(v *vote, tx *types.Transaction, index int) *action {
return &action{db: v.GetStateDB(),
txHash: tx.Hash(),
fromAddr: tx.From(),
blockTime: v.GetBlockTime(),
height: v.GetHeight(),
index: index}
}
func (a *action) getGroupInfo(groupID string) (*vty.GroupInfo, error) {
info := &vty.GroupInfo{}
err := readStateDB(a.db, formatStateIDKey(groupID), info)
if err == types.ErrNotFound {
err = errGroupNotExist
}
return info, err
}
func (a *action) getVoteInfo(voteID string) (*vty.VoteInfo, error) {
info := &vty.VoteInfo{}
err := readStateDB(a.db, formatStateIDKey(voteID), info)
if err == types.ErrNotFound {
err = errVoteNotExist
}
return info, err
}
func (a *action) createGroup(create *vty.CreateGroup) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
xhash := hex.EncodeToString(a.txHash)
group := &vty.GroupInfo{}
group.Name = create.Name
group.ID = formatGroupID(xhash)
//添加创建者作为默认管理员
group.Admins = append(group.Admins, create.Admins...)
if !checkSliceItemExist(a.fromAddr, create.Admins) {
group.Admins = append(group.Admins, a.fromAddr)
}
group.Members = create.Members
// set default vote weight
for _, member := range group.Members {
if member.VoteWeight < 1 {
member.VoteWeight = 1
}
}
group.MemberNum = uint32(len(group.Members))
group.Creator = a.fromAddr
groupValue := types.Encode(group)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(group.ID), Value: groupValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCreateGroupLog, Log: groupValue})
return receipt, nil
}
func (a *action) updateMember(update *vty.UpdateMember) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
group, err := a.getGroupInfo(update.GroupID)
if err != nil {
elog.Error("vote exec updateMember", "txHash", a.txHash, "err", err)
return nil, errStateDBGet
}
addrMap := make(map[string]int)
for index, member := range group.Members {
addrMap[member.Addr] = index
}
// remove members
for _, addr := range update.GetRemoveMemberAddrs() {
if index, ok := addrMap[addr]; ok {
group.Members = append(group.Members[:index], group.Members[index+1:]...)
}
}
// add members
for _, member := range update.GetAddMembers() {
group.Members = append(group.Members, member)
}
groupValue := types.Encode(group)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(group.ID), Value: groupValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyUpdateMemberLog, Log: groupValue})
return receipt, nil
}
func (a *action) createVote(create *vty.CreateVote) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
vote := &vty.VoteInfo{}
vote.ID = formatVoteID(hex.EncodeToString(a.txHash))
vote.BeginTimestamp = create.BeginTimestamp
vote.EndTimestamp = create.EndTimestamp
vote.Name = create.Name
vote.VoteGroups = create.VoteGroups
vote.VoteOptions = make([]*vty.VoteOption, 0)
for _, option := range create.VoteOptions {
vote.VoteOptions = append(vote.VoteOptions, &vty.VoteOption{Option: option})
}
vote.Creator = a.fromAddr
voteValue := types.Encode(vote)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(vote.ID), Value: voteValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCreateVoteLog, Log: voteValue})
return receipt, nil
}
func (a *action) commitVote(commit *vty.CommitVote) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
vote, err := a.getVoteInfo(commit.VoteID)
group, err1 := a.getGroupInfo(commit.GroupID)
if err != nil || err1 != nil {
elog.Error("vote exec commitVote", "txHash", a.txHash, "err", err, "err1", err1)
return nil, errStateDBGet
}
for _, member := range group.Members {
if member.Addr == a.fromAddr {
vote.VoteOptions[commit.OptionIndex].Score += member.VoteWeight
}
}
vote.VotedMembers = append(vote.VotedMembers, a.fromAddr)
voteValue := types.Encode(vote)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(vote.ID), Value: voteValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCommitVoteLog, Log: voteValue})
return receipt, nil
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
// CheckTx 实现自定义检验交易接口,供框架调用
func (v *vote) CheckTx(tx *types.Transaction, index int) error {
// implement code
txHash := hex.EncodeToString(tx.Hash())
var action vty.VoteAction
err := types.Decode(tx.Payload, &action)
if err != nil {
elog.Error("vote CheckTx", "txHash", txHash, "Decode payload error", err)
return types.ErrActionNotSupport
}
if action.Ty == vty.TyCreateGroupAction {
err = v.checkCreateGroup(action.GetCreateGroup())
} else if action.Ty == vty.TyUpdateMemberAction {
err = v.checkUpdateMember(action.GetUpdateMember())
} else if action.Ty == vty.TyCreateVoteAction {
err = v.checkCreateVote(action.GetCreateVote())
} else if action.Ty == vty.TyCommitVoteAction {
err = v.checkCommitVote(action.GetCommitVote(), tx, index)
} else {
err = types.ErrActionNotSupport
}
if err != nil {
elog.Error("vote CheckTx", "txHash", txHash, "actionName", tx.ActionName(), "err", err, "actionData", action)
}
return err
}
func checkMemberValidity(members []*vty.GroupMember) error {
filter := make(map[string]struct{}, len(members))
for _, member := range members {
if member.GetAddr() == "" {
return errNilMember
}
if _, ok := filter[member.Addr]; ok {
return errDuplicateMember
}
filter[member.Addr] = struct{}{}
}
return nil
}
func (v *vote) checkCreateGroup(create *vty.CreateGroup) error {
if create.GetName() == "" {
return errEmptyName
}
//检测组成员是否有重复
if err := checkMemberValidity(create.GetMembers()); err != nil {
return err
}
//检测管理员是否有重复
if checkSliceItemDuplicate(create.GetAdmins()) {
return errDuplicateAdmin
}
return nil
}
func (v *vote) checkUpdateMember(update *vty.UpdateMember) error {
if len(update.GetGroupID()) != IDLen {
return errInvalidGroupID
}
for _, member := range update.AddMembers {
if len(member.Addr) != addrLen {
return types.ErrInvalidAddress
}
}
for _, addr := range update.RemoveMemberAddrs {
if len(addr) != addrLen {
return types.ErrInvalidAddress
}
}
return nil
}
func (v *vote) checkCreateVote(create *vty.CreateVote) error {
if create.GetName() == "" {
return errEmptyName
}
if create.GetEndTimestamp() <= v.GetBlockTime() || create.EndTimestamp <= create.BeginTimestamp {
return errInvalidVoteTime
}
if len(create.VoteOptions) < 2 {
return errInvalidVoteOption
}
if len(create.VoteGroups) == 0 {
return errEmptyVoteGroup
}
if checkSliceItemDuplicate(create.VoteGroups) {
return errDuplicateGroup
}
return nil
}
func (v *vote) checkCommitVote(commit *vty.CommitVote, tx *types.Transaction, index int) error {
voteID := commit.GetVoteID()
groupID := commit.GetGroupID()
if len(voteID) != IDLen {
return errInvalidVoteID
}
if len(groupID) != IDLen {
return errInvalidGroupID
}
action := newAction(v, tx, index)
voteInfo, err := action.getVoteInfo(voteID)
if err != nil {
return err
}
if voteInfo.EndTimestamp <= action.blockTime {
return errVoteAlreadyFinished
}
if commit.OptionIndex >= uint32(len(voteInfo.VoteOptions)) {
return errInvalidOptionIndex
}
//check group validity
if !checkSliceItemExist(commit.GroupID, voteInfo.VoteGroups) {
return errInvalidGroupID
}
// check if already vote
if checkSliceItemExist(action.fromAddr, voteInfo.VotedMembers) {
return errAlreadyVoted
}
groupInfo, err := action.getGroupInfo(commit.GroupID)
if err != nil {
return err
}
// check if exist in group members
if !checkMemberExist(action.fromAddr, groupInfo.Members) {
return errInvalidGroupMember
}
return nil
}
package executor
import "errors"
var (
errEmptyName = errors.New("ErrEmptyName")
errInvalidMemberWeights = errors.New("errInvalidMemberWeights")
errNilMember = errors.New("errNilMember")
errDuplicateMember = errors.New("errDuplicateMember")
errDuplicateGroup = errors.New("errDuplicateGroup")
errDuplicateAdmin = errors.New("errDuplicateAdmin")
errInvalidVoteTime = errors.New("errInvalidVoteTime")
errInvalidVoteOption = errors.New("errInvalidVoteOption")
errEmptyVoteGroup = errors.New("errEmptyVoteGroup")
errVoteNotExist = errors.New("errVoteNotExist")
errGroupNotExist = errors.New("errGroupNotExist")
errStateDBGet = errors.New("errStateDBGet")
errInvalidVoteID = errors.New("errInvalidVoteID")
errInvalidGroupID = errors.New("errInvalidGroupID")
errInvalidOptionIndex = errors.New("errInvalidOptionIndex")
errAlreadyVoted = errors.New("errAlreadyVoted")
errInvalidGroupMember = errors.New("errInvalidGroupMember")
errVoteAlreadyFinished = errors.New("errVoteAlreadyFinished")
)
package executor
import (
"github.com/33cn/chain33/types"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 实现交易的链上执行接口
* 关键数据上链(statedb)并生成交易回执(log)
*/
func (v *vote) Exec_CreateGroup(payload *votetypes.CreateGroup, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.createGroup(payload)
}
func (v *vote) Exec_UpdateMember(payload *votetypes.UpdateMember, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.updateMember(payload)
}
func (v *vote) Exec_CreateVote(payload *votetypes.CreateVote, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.createVote(payload)
}
func (v *vote) Exec_CommitVote(payload *votetypes.CommitVote, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.commitVote(payload)
}
package executor
import (
"github.com/33cn/chain33/types"
)
/*
* 实现区块回退时本地执行的数据清除
*/
// ExecDelLocal localdb kv数据自动回滚接口
func (v *vote) ExecDelLocal(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
kvs, err := v.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 实现交易相关数据本地执行,数据不上链
* 非关键数据,本地存储(localDB), 用于辅助查询,效率高
*/
func (v *vote) ExecLocal_CreateGroup(payload *vty.CreateGroup, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
groupInfo := decodeGroupInfo(receiptData.Logs[0].Log)
table := newGroupTable(v.GetLocalDB())
err := table.Add(&vty.GroupVoteInfo{GroupInfo: groupInfo})
if err != nil {
elog.Error("execLocal createGroup", "txHash", hex.EncodeToString(tx.Hash()), "table add", err)
return nil, err
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal createGroup", "txHash", hex.EncodeToString(tx.Hash()), "table save", err)
return nil, err
}
dbSet.KV = kvs
kvs, err = v.addGroupMember(groupInfo.GetID(), groupInfo.Members)
if err != nil {
elog.Error("execLocal createGroup", "txHash", hex.EncodeToString(tx.Hash()), "addMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_UpdateMember(update *vty.UpdateMember, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
groupInfo := decodeGroupInfo(receiptData.Logs[0].Log)
table := newGroupTable(v.GetLocalDB())
err := table.Replace(&vty.GroupVoteInfo{GroupInfo: groupInfo})
if err != nil {
elog.Error("execLocal updateMember", "txHash", hex.EncodeToString(tx.Hash()), "groupID", groupInfo.ID, "table replace", err)
return nil, err
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal updateMember", "txHash", hex.EncodeToString(tx.Hash()), "groupID", groupInfo.ID, "table save", err)
return nil, err
}
dbSet.KV = kvs
kvs, err = v.addGroupMember(groupInfo.GetID(), update.AddMembers)
if err != nil {
elog.Error("execLocal updateMember", "txHash", hex.EncodeToString(tx.Hash()), "addMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
kvs, err = v.removeGroupMember(groupInfo.GetID(), update.RemoveMemberAddrs)
if err != nil {
elog.Error("execLocal updateMember", "txHash", hex.EncodeToString(tx.Hash()), "removeMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_CreateVote(payload *vty.CreateVote, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
voteInfo := decodeVoteInfo(receiptData.Logs[0].Log)
table := newVoteTable(v.GetLocalDB())
err := table.Add(voteInfo)
if err != nil {
elog.Error("execLocal createVote", "txHash", hex.EncodeToString(tx.Hash()), "voteTable add", err)
return nil, err
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal createVote", "txHash", hex.EncodeToString(tx.Hash()), "voteTable save", err)
return nil, err
}
dbSet.KV = kvs
// 在关联的投票组表中记录voteID信息
table = newGroupTable(v.GetLocalDB())
for _, groupID := range voteInfo.VoteGroups {
row, err := table.GetData([]byte(groupID))
if err != nil {
continue
}
if info, ok := row.Data.(*vty.GroupVoteInfo); ok {
info.VoteIDs = append(info.VoteIDs, voteInfo.ID)
err = table.Replace(info)
if err != nil {
elog.Error("execLocal createVote", "txHash", hex.EncodeToString(tx.Hash()),
"groupID", groupID, "groupTable replace", err)
return nil, err
}
}
}
kvs, err = table.Save()
if err != nil {
elog.Error("execLocal createVote", "txHash", hex.EncodeToString(tx.Hash()), "groupTable save", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_CommitVote(payload *vty.CommitVote, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code, add customize kv to dbSet...
voteInfo := decodeVoteInfo(receiptData.Logs[0].Log)
table := newVoteTable(v.GetLocalDB())
err := table.Replace(voteInfo)
if err != nil {
elog.Error("execLocal commitVote", "txHash", hex.EncodeToString(tx.Hash()), "voteTable add", err)
return nil, err
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal commitVote", "txHash", hex.EncodeToString(tx.Hash()), "voteTable save", err)
return nil, err
}
dbSet.KV = kvs
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
//当区块回滚时,框架支持自动回滚localdb kv,需要对exec-local返回的kv进行封装
func (v *vote) addAutoRollBack(tx *types.Transaction, kv []*types.KeyValue) *types.LocalDBSet {
dbSet := &types.LocalDBSet{}
dbSet.KV = v.AddRollbackKV(tx, tx.Execer, kv)
return dbSet
}
func (v *vote) addGroupMember(groupID string, members []*vty.GroupMember) ([]*types.KeyValue, error) {
table := newMemberTable(v.GetLocalDB())
for _, member := range members {
addrKey := []byte(member.Addr)
row, err := table.GetData(addrKey)
if err == nil {
info, ok := row.Data.(*vty.MemberInfo)
if ok && !checkSliceItemExist(groupID, info.GroupIDs) {
info.GroupIDs = append(info.GroupIDs, groupID)
err = table.Replace(info)
}
} else if err == types.ErrNotFound {
err = table.Add(&vty.MemberInfo{Addr: member.Addr, GroupIDs: []string{groupID}})
}
// 这个错可能由GetData,Replace,Add返回
if err != nil {
elog.Error("execLocal addMember", "member table Add/Replace", err)
return nil, err
}
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal addMember", "member table save", err)
return nil, err
}
return kvs, nil
}
func (v *vote) removeGroupMember(groupID string, addrs []string) ([]*types.KeyValue, error) {
table := newMemberTable(v.GetLocalDB())
for _, addr := range addrs {
addrKey := []byte(addr)
row, err := table.GetData(addrKey)
if err == types.ErrNotFound {
return nil, nil
} else if err != nil {
elog.Error("execLocal removeMember", "member table getData", err)
return nil, err
}
info, ok := row.Data.(*vty.MemberInfo)
if !ok {
return nil, types.ErrTypeAsset
}
for index, id := range info.GroupIDs {
if id == groupID {
info.GroupIDs = append(info.GroupIDs[:index], info.GroupIDs[index+1:]...)
err = table.Replace(info)
if err != nil {
elog.Error("execLocal removeMember", "member table replace", err)
return nil, err
}
break
}
}
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal addMember", "member table save", err)
return nil, err
}
return kvs, nil
}
package executor
/*
* 用户合约存取kv数据时,key值前缀需要满足一定规范
* 即key = keyPrefix + userKey
* 需要字段前缀查询时,使用’-‘作为分割符号
*/
var (
//keyPrefixStateDB state db key必须前缀
keyPrefixStateDB = "mavl-vote-"
//keyPrefixLocalDB local db的key必须前缀
keyPrefixLocalDB = "LODB-vote-"
)
// groupID or voteID
func formatStateIDKey(id string) []byte {
return []byte(keyPrefixStateDB + id)
}
package executor
import (
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
// Query_GroupInfo query group info
func (v *vote) Query_GetGroup(in *types.ReqString) (types.Message, error) {
if len(in.GetData()) != IDLen {
return nil, errInvalidGroupID
}
groupID := in.Data
table := newGroupTable(v.GetLocalDB())
row, err := table.GetData([]byte(groupID))
if err != nil {
elog.Error("query getGroup", "id", groupID, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.GroupVoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
func (v *vote) Query_GetVote(in *types.ReqString) (types.Message, error) {
if len(in.GetData()) != IDLen {
return nil, errInvalidVoteID
}
voteID := in.Data
table := newVoteTable(v.GetLocalDB())
row, err := table.GetData([]byte(voteID))
if err != nil {
elog.Error("query getVote", "id", voteID, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.VoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
func (v *vote) Query_GetMember(in *types.ReqString) (types.Message, error) {
if len(in.GetData()) != addrLen {
return nil, types.ErrInvalidAddress
}
addr := in.Data
table := newMemberTable(v.GetLocalDB())
row, err := table.GetData([]byte(addr))
if err != nil {
elog.Error("query getMember", "addr", addr, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.MemberInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
func (v *vote) Query_ListGroup(in *vty.ReqListItem) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
table := newGroupTable(v.GetLocalDB())
var primaryKey []byte
primaryKey = append(primaryKey, []byte(in.StartItemID)...)
rows, err := table.ListIndex(groupTablePrimary, nil, primaryKey, in.Count, in.Direction)
if err != nil {
elog.Error("query listGroup", "err", err, "param", in)
return nil, err
}
list := &vty.GroupVoteInfos{GroupList: make([]*vty.GroupVoteInfo, 0)}
for _, row := range rows {
info, ok := row.Data.(*vty.GroupVoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list.GroupList = append(list.GroupList, info)
}
return list, nil
}
func (v *vote) Query_ListVote(in *vty.ReqListItem) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
table := newVoteTable(v.GetLocalDB())
var primaryKey []byte
primaryKey = append(primaryKey, []byte(in.StartItemID)...)
rows, err := table.ListIndex(voteTablePrimary, nil, primaryKey, in.Count, in.Direction)
if err != nil {
elog.Error("query listVote", "err", err, "param", in)
return nil, err
}
list := &vty.VoteInfos{VoteList: make([]*vty.VoteInfo, 0)}
for _, row := range rows {
info, ok := row.Data.(*vty.VoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list.VoteList = append(list.VoteList, info)
}
return list, nil
}
func (v *vote) Query_ListMember(in *vty.ReqListItem) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
table := newMemberTable(v.GetLocalDB())
var primaryKey []byte
primaryKey = append(primaryKey, []byte(in.StartItemID)...)
rows, err := table.ListIndex(memberTablePrimary, nil, primaryKey, in.Count, in.Direction)
if err != nil {
elog.Error("query listMember", "err", err, "param", in)
return nil, err
}
list := &vty.MemberInfos{MemberList: make([]*vty.MemberInfo, 0)}
for _, row := range rows {
info, ok := row.Data.(*vty.MemberInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list.MemberList = append(list.MemberList, info)
}
return list, nil
}
package executor
import (
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
var (
groupTablePrimary = "groupid"
voteTablePrimary = "voteid"
memberTablePrimary = "addr"
)
var groupTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "group",
Primary: groupTablePrimary,
}
var voteTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "vote",
Primary: voteTablePrimary,
}
var memberTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "member",
Primary: memberTablePrimary,
}
//新建表
func newTable(kvDB db.KV, rowMeta table.RowMeta, opt *table.Option) *table.Table {
table, err := table.NewTable(rowMeta, kvDB, opt)
if err != nil {
panic(err)
}
return table
}
func newGroupTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &groupTableRow{}, groupTableOpt)
}
func newVoteTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &voteTableRow{}, voteTableOpt)
}
func newMemberTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &memberTableRow{}, memberTableOpt)
}
//groupTableRow table meta 结构
type groupTableRow struct {
*vty.GroupVoteInfo
}
//CreateRow 新建数据行
func (r *groupTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.GroupVoteInfo{}}
}
//SetPayload 设置数据
func (r *groupTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.GroupVoteInfo); ok {
r.GroupVoteInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *groupTableRow) Get(key string) ([]byte, error) {
if key == groupTablePrimary {
return []byte(r.GroupVoteInfo.GetGroupInfo().GetID()), nil
}
return nil, types.ErrNotFound
}
//voteTableRow table meta 结构
type voteTableRow struct {
*vty.VoteInfo
}
//CreateRow 新建数据行
func (r *voteTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.VoteInfo{}}
}
//SetPayload 设置数据
func (r *voteTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.VoteInfo); ok {
r.VoteInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *voteTableRow) Get(key string) ([]byte, error) {
if key == voteTablePrimary {
return []byte(r.VoteInfo.GetID()), nil
}
return nil, types.ErrNotFound
}
type memberTableRow struct {
*vty.MemberInfo
}
//CreateRow 新建数据行
func (r *memberTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.MemberInfo{}}
}
//SetPayload 设置数据
func (r *memberTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.MemberInfo); ok {
r.MemberInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *memberTableRow) Get(key string) ([]byte, error) {
if key == memberTablePrimary {
return []byte(r.MemberInfo.GetAddr()), nil
}
return nil, types.ErrNotFound
}
package executor
import (
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
const (
// IDLen length of groupID or voteID
IDLen = 65
addrLen = 34
)
func formatGroupID(txHash string) string {
return "g" + txHash
}
func formatVoteID(txHash string) string {
return "v" + txHash
}
func checkMemberExist(addr string, members []*vty.GroupMember) bool {
for _, item := range members {
if addr == item.Addr {
return true
}
}
return false
}
func checkSliceItemExist(target string, items []string) bool {
for _, item := range items {
if target == item {
return true
}
}
return false
}
func checkSliceItemDuplicate(items []string) bool {
filter := make(map[string]struct{}, len(items))
for _, item := range items {
if _, ok := filter[item]; ok {
return true
}
filter[item] = struct{}{}
}
return false
}
func readStateDB(stateDB db.KV, key []byte, result types.Message) error {
val, err := stateDB.Get(key)
if err != nil {
return err
}
return types.Decode(val, result)
}
func mustDecodeProto(data []byte, msg types.Message) {
if err := types.Decode(data, msg); err != nil {
panic(err.Error())
}
}
func decodeGroupInfo(data []byte) *vty.GroupInfo {
info := &vty.GroupInfo{}
mustDecodeProto(data, info)
return info
}
func decodeVoteInfo(data []byte) *vty.VoteInfo {
info := &vty.VoteInfo{}
mustDecodeProto(data, info)
return info
}
package executor
import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 执行器相关定义
* 重载基类相关接口
*/
var (
//日志
elog = log.New("module", "vote.executor")
)
var driverName = votetypes.VoteX
// Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) {
drivers.Register(cfg, GetName(), newVote, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// InitExecType Init Exec Type
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&vote{}))
}
type vote struct {
drivers.DriverBase
}
func newVote() drivers.Driver {
t := &vote{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
// GetName get driver name
func GetName() string {
return newVote().GetName()
}
func (v *vote) GetDriverName() string {
return driverName
}
package types
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/vote/commands"
"github.com/33cn/plugin/plugin/dapp/vote/executor"
"github.com/33cn/plugin/plugin/dapp/vote/rpc"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 初始化dapp相关的组件
*/
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: votetypes.VoteX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.Cmd,
RPC: rpc.Init,
})
}
all:
bash ./create_protobuf.sh
#!/bin/bash
# proto生成命令,将pb.go文件生成到types/目录下, chain33_path支持引用chain33框架的proto文件
chain33_path=$(go list -f '{{.Dir}}' "github.com/33cn/chain33")
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="${chain33_path}/types/proto/"
syntax = "proto3";
package types;
// vote 合约交易行为总类型
message VoteAction {
int32 ty = 1;
oneof value {
CreateGroup createGroup = 2;
UpdateMember updateMember = 3;
CreateVote createVote = 4;
CommitVote commitVote = 5;
}
}
message GroupMember {
string addr = 1;
uint32 voteWeight = 2;
}
//创建投票组
message CreateGroup {
string name = 1;
repeated string admins = 2;
repeated GroupMember members = 3;
}
//更新投票组
message UpdateMember{
string groupID = 1;
repeated GroupMember addMembers = 2;
repeated string removeMemberAddrs = 3;
}
//
message GroupInfo {
string ID = 1;
string name = 2;
uint32 memberNum = 3;
string creator = 4;
repeated string admins = 5;
repeated GroupMember members = 6;
}
message VoteOption {
string option = 1;
uint32 score = 2;
}
//
message CreateVote{
string name = 1;
repeated string voteGroups = 2;
repeated string voteOptions = 3;
int64 beginTimestamp = 4;
int64 endTimestamp = 5;
}
//
message CommitVote{
string voteID = 1;
string groupID = 2;
uint32 optionIndex = 3;
}
//
message VoteInfo{
string ID = 1;
string name = 2;
string creator = 3;
repeated string voteGroups = 4;
repeated VoteOption voteOptions = 5;
int64 beginTimestamp = 6;
int64 endTimestamp = 7;
repeated string votedMembers = 8;
}
message VoteInfos {
repeated VoteInfo voteList = 1;
}
message GroupVoteInfo{
GroupInfo groupInfo = 1;
repeated string voteIDs = 2;
}
message GroupVoteInfos{
repeated GroupVoteInfo groupList = 1;
}
message MemberInfo {
string addr = 1;
repeated string groupIDs = 2;
}
message MemberInfos{
repeated MemberInfo memberList = 1;
}
message ReqListItem{
string startItemID = 1;
int32 count = 2;
int32 direction = 3;
}
package rpc
/*
* 实现json rpc和grpc service接口
* json rpc用Jrpc结构作为接收实例
* grpc使用channelClient结构作为接收实例
*/
package rpc
import (
rpctypes "github.com/33cn/chain33/rpc/types"
)
/*
* rpc相关结构定义和初始化
*/
// 实现grpc的service接口
type channelClient struct {
rpctypes.ChannelClient
}
// Jrpc 实现json rpc调用实例
type Jrpc struct {
cli *channelClient
}
// Grpc grpc
type Grpc struct {
*channelClient
}
// Init init rpc
func Init(name string, s rpctypes.RPCServer) {
cli := &channelClient{}
grpc := &Grpc{channelClient: cli}
cli.Init(name, s, &Jrpc{cli: cli}, grpc)
}
package types
import (
"reflect"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
/*
* 交易相关类型定义
* 交易action通常有对应的log结构,用于交易回执日志记录
* 每一种action和log需要用id数值和name名称加以区分
*/
// action类型id和name,这些常量可以自定义修改
const (
TyUnknowAction = iota + 100
TyCreateGroupAction
TyUpdateMemberAction
TyCreateVoteAction
TyCommitVoteAction
NameCreateGroupAction = "CreateGroup"
NameUpdateMemberAction = "UpdateMember"
NameCreateVoteAction = "CreateVote"
NameCommitVoteAction = "CommitVote"
)
// log类型id值
const (
TyUnknownLog = iota + 100
TyCreateGroupLog
TyUpdateMemberLog
TyCreateVoteLog
TyCommitVoteLog
NameCreateGroupLog = "CreateGroupLog"
NameUpdateMemberLog = "UpdateMemberLog"
NameCreateVoteLog = "CreateVoteLog"
NameCommitVoteLog = "CommitVoteLog"
)
var (
//VoteX 执行器名称定义
VoteX = "vote"
//定义actionMap
actionMap = map[string]int32{
NameCreateGroupAction: TyCreateGroupAction,
NameUpdateMemberAction: TyUpdateMemberAction,
NameCreateVoteAction: TyCreateVoteAction,
NameCommitVoteAction: TyCommitVoteAction,
}
//定义log的id和具体log类型及名称,填入具体自定义log类型
logMap = map[int64]*types.LogInfo{
TyCreateGroupLog: {Ty: reflect.TypeOf(GroupInfo{}), Name: NameCreateGroupLog},
TyUpdateMemberLog: {Ty: reflect.TypeOf(GroupInfo{}), Name: NameUpdateMemberLog},
TyCreateVoteLog: {Ty: reflect.TypeOf(VoteInfo{}), Name: NameCreateVoteLog},
TyCommitVoteLog: {Ty: reflect.TypeOf(VoteInfo{}), Name: NameCommitVoteLog},
}
tlog = log.New("module", "vote.types")
)
// init defines a register function
func init() {
types.AllowUserExec = append(types.AllowUserExec, []byte(VoteX))
//注册合约启用高度
types.RegFork(VoteX, InitFork)
types.RegExec(VoteX, InitExecutor)
}
// InitFork defines register fork
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(VoteX, "Enable", 0)
}
// InitExecutor defines register executor
func InitExecutor(cfg *types.Chain33Config) {
types.RegistorExecutor(VoteX, NewType(cfg))
}
type voteType struct {
types.ExecTypeBase
}
func NewType(cfg *types.Chain33Config) *voteType {
c := &voteType{}
c.SetChild(c)
c.SetConfig(cfg)
return c
}
// GetPayload 获取合约action结构
func (v *voteType) GetPayload() types.Message {
return &VoteAction{}
}
// GeTypeMap 获取合约action的id和name信息
func (v *voteType) GetTypeMap() map[string]int32 {
return actionMap
}
// GetLogMap 获取合约log相关信息
func (v *voteType) GetLogMap() map[int64]*types.LogInfo {
return logMap
}
This diff is collapsed.
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