Commit 2c249090 authored by 张振华's avatar 张振华

Merge branch 'master' into guess

parents 335380b0 e26a5b79
......@@ -33,6 +33,8 @@ const (
delAct int64 = 2 //reference blockstore.go, del para block action
paraCrossTxCount = 2 //current only support 2 txs for cross
minBlockNum = 6 //min block number startHeight before lastHeight in mainchain
)
var (
......@@ -40,7 +42,6 @@ var (
grpcSite = "localhost:8802"
genesisBlockTime int64 = 1514533390
startHeight int64 //parachain sync from startHeight in mainchain
searchSeq int64 //start sequence to search startHeight in mainchain
blockSec int64 = 5 //write block interval, second
emptyBlockInterval int64 = 4 //write empty block every interval blocks in mainchain
zeroHash [32]byte
......@@ -75,7 +76,6 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
}
if cfg.StartHeight > 0 {
startHeight = cfg.StartHeight
searchSeq = calcSearchseq(cfg.StartHeight)
}
if cfg.WriteBlockSeconds > 0 {
blockSec = cfg.WriteBlockSeconds
......@@ -139,13 +139,6 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
return para
}
func calcSearchseq(height int64) int64 {
if height < 1000 {
return 0
}
return height - 1000
}
//para 不检查任何的交易
func (client *client) CheckBlock(parent *types.Block, current *types.BlockDetail) error {
err := checkMinerTx(current)
......@@ -179,10 +172,7 @@ func (client *client) InitBlock() {
}
if block == nil {
startSeq := int64(0)
if searchSeq > 0 {
startSeq = client.GetSeqByHeightOnMain(startHeight, searchSeq)
}
startSeq := client.GetStartSeq(startHeight)
// 创世区块
newblock := &types.Block{}
newblock.Height = 0
......@@ -191,37 +181,47 @@ func (client *client) InitBlock() {
tx := client.CreateGenesisTx()
newblock.Txs = tx
newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
client.WriteBlock(zeroHash[:], newblock, startSeq-int64(1))
client.WriteBlock(zeroHash[:], newblock, startSeq-1)
} else {
client.SetCurrentBlock(block)
}
}
func (client *client) GetSeqByHeightOnMain(height int64, originSeq int64) int64 {
lastSeq, err := client.GetLastSeqOnMainChain()
plog.Info("Searching for the sequence", "heightOnMain", height, "searchSeq", searchSeq, "lastSeq", lastSeq)
// GetStartSeq get startSeq in mainchain
func (client *client) GetStartSeq(height int64) int64 {
if height == 0 {
return 0
}
lastHeight, err := client.GetLastHeightOnMainChain()
if err != nil {
panic(err)
}
hint := time.NewTicker(10 * time.Second)
defer hint.Stop()
for originSeq <= lastSeq {
if lastHeight < height {
panic(fmt.Sprintf("lastHeight(%d) less than startHeight(%d) in mainchain", lastHeight, height))
}
hint := time.NewTicker(5 * time.Second)
for lastHeight < height+minBlockNum {
select {
case <-hint.C:
plog.Info("Still Searching......", "searchAtSeq", originSeq, "lastSeq", lastSeq)
plog.Info("Waiting lastHeight increase......", "lastHeight", lastHeight, "startHeight", height)
default:
blockDetail, seqTy, err := client.GetBlockOnMainBySeq(originSeq)
lastHeight, err = client.GetLastHeightOnMainChain()
if err != nil {
panic(err)
}
if blockDetail.Block.Height == height && seqTy == addAct {
plog.Info("the target sequence in mainchain", "heightOnMain", height, "targetSeq", originSeq)
return originSeq
}
originSeq++
time.Sleep(time.Second)
}
}
panic("Main chain has not reached the height currently")
hint.Stop()
plog.Info(fmt.Sprintf("lastHeight more than %d blocks after startHeight", minBlockNum), "lastHeight", lastHeight, "startHeight", height)
seq, err := client.GetSeqByHeightOnMainChain(height)
if err != nil {
panic(err)
}
plog.Info("the start sequence in mainchain", "startHeight", height, "startSeq", seq)
return seq
}
func (client *client) CreateGenesisTx() (ret []*types.Transaction) {
......@@ -357,6 +357,15 @@ func (client *client) getLastBlockInfo() (int64, *types.Block, []byte, int64, er
}
func (client *client) GetLastHeightOnMainChain() (int64, error) {
header, err := client.grpcClient.GetLastHeader(context.Background(), &types.ReqNil{})
if err != nil {
plog.Error("GetLastHeightOnMainChain", "Error", err.Error())
return -1, err
}
return header.Height, nil
}
func (client *client) GetLastSeqOnMainChain() (int64, error) {
seq, err := client.grpcClient.GetLastBlockSequence(context.Background(), &types.ReqNil{})
if err != nil {
......@@ -367,6 +376,24 @@ func (client *client) GetLastSeqOnMainChain() (int64, error) {
return seq.Data, nil
}
func (client *client) GetSeqByHeightOnMainChain(height int64) (int64, error) {
hash, err := client.GetHashByHeightOnMainChain(height)
if err != nil {
return -1, err
}
seq, err := client.GetSeqByHashOnMainChain(hash)
return seq, err
}
func (client *client) GetHashByHeightOnMainChain(height int64) ([]byte, error) {
reply, err := client.grpcClient.GetBlockHash(context.Background(), &types.ReqInt{Height: height})
if err != nil {
plog.Error("GetHashByHeightOnMainChain", "Error", err.Error())
return nil, err
}
return reply.Hash, nil
}
func (client *client) GetSeqByHashOnMainChain(hash []byte) (int64, error) {
seq, err := client.grpcClient.GetSequenceByHash(context.Background(), &types.ReqHash{Hash: hash})
if err != nil {
......@@ -465,7 +492,6 @@ func (client *client) RequestTx(currSeq int64, preMainBlockHash []byte) ([]*type
//lastSeq = currSeq-1, main node not update
if lastSeq+1 == currSeq {
plog.Debug("Waiting new sequence from main chain")
time.Sleep(time.Second * time.Duration(blockSec*2))
return nil, nil, -1, paracross.ErrParaWaitingNewSeq
}
......@@ -596,15 +622,16 @@ func (client *client) CreateBlock() {
txs, blockOnMain, seqTy, err := client.RequestTx(currSeq, lastSeqMainHash)
if err != nil {
incSeqFlag = false
if err == paracross.ErrParaCurHashNotMatch {
newSeq, newSeqMainHash, err := client.switchHashMatchedBlock(currSeq)
if err == nil {
currSeq = newSeq
lastSeqMainHash = newSeqMainHash
continue
}
}
incSeqFlag = false
time.Sleep(time.Second)
time.Sleep(time.Second * time.Duration(blockSec))
continue
}
......
......@@ -92,6 +92,9 @@ func (s *suiteParaCommitMsg) initEnv(cfg *types.Config, sub *types.ConfigSubModu
ret := &types.Reply{IsOk: true, Msg: data}
s.grpcCli.On("QueryChain", mock.Anything, mock.Anything).Return(ret, nil).Maybe()
s.grpcCli.On("SendTransaction", mock.Anything, mock.Anything).Return(reply, nil).Maybe()
s.grpcCli.On("GetLastHeader", mock.Anything, mock.Anything).Return(&types.Header{Height: cfg.Consensus.StartHeight + minBlockNum}, nil).Maybe()
s.grpcCli.On("GetBlockHash", mock.Anything, mock.Anything).Return(&types.ReplyHash{Hash: []byte("1")}, nil).Maybe()
s.grpcCli.On("GetSequenceByHash", mock.Anything, mock.Anything).Return(&types.Int64{Data: cfg.Consensus.StartHeight}, nil).Maybe()
s.para.grpcClient = s.grpcCli
s.para.SetQueueClient(q.Client())
......
......@@ -58,7 +58,7 @@ func (s *suiteParaClient) initEnv(cfg *types.Config, sub *types.ConfigSubModule)
s.store = store.New(cfg.Store, sub.Store)
s.store.SetQueueClient(q.Client())
cfg.Consensus.StartHeight = 0
//cfg.Consensus.StartHeight = 0
cfg.Consensus.EmptyBlockInterval = 1
s.para = New(cfg.Consensus, sub.Consensus["para"]).(*client)
s.grpcCli = &typesmocks.Chain33Client{}
......@@ -103,6 +103,8 @@ func (s *suiteParaClient) initEnv(cfg *types.Config, sub *types.ConfigSubModule)
ret := &types.Reply{IsOk: true, Msg: data}
s.grpcCli.On("QueryChain", mock.Anything, mock.Anything).Return(ret, nil).Maybe()
s.grpcCli.On("SendTransaction", mock.Anything, mock.Anything).Return(reply, nil).Maybe()
s.grpcCli.On("GetLastHeader", mock.Anything, mock.Anything).Return(&types.Header{Height: cfg.Consensus.StartHeight + minBlockNum}, nil).Maybe()
s.grpcCli.On("GetBlockHash", mock.Anything, mock.Anything).Return(&types.ReplyHash{Hash: []byte("1")}, nil).Maybe()
s.para.grpcClient = s.grpcCli
s.para.SetQueueClient(q.Client())
......
......@@ -49,5 +49,4 @@ cli send jsvm call -a "{\"hello\": \"world\"}" -f hello -n test -k 14KEKbYtKKQm
第四步:query test合约hello函数
cli jsvm query -a "{\"hello\": \"world\"}" -f hello -n test
*/
......@@ -27,3 +27,18 @@ func (action *Action) GetMainHeightByTxHash(txHash []byte) int64 {
return -1
}
// GetMainBlockHashByHeight get Hash
func (action *Action) GetMainBlockHashByHeight(height int64) ([]byte, error) {
for i := 0; i < retryNum; i++ {
req := &types.ReqInt{Height: height}
replyHash, err := action.grpcClient.GetBlockHash(context.Background(), req)
if err != nil {
time.Sleep(time.Second)
} else {
return replyHash.Hash, nil
}
}
return nil, types.ErrBlockNotFound
}
......@@ -127,6 +127,7 @@ type Action struct {
conn *grpc.ClientConn
grpcClient types.Chain33Client
index int
lottery *Lottery
}
// NewLotteryAction generate New Action
......@@ -135,10 +136,9 @@ func NewLotteryAction(l *Lottery, tx *types.Transaction, index int) *Action {
fromaddr := tx.From()
msgRecvOp := grpc.WithMaxMsgSize(grpcRecSize)
if types.IsPara() && cfg.ParaRemoteGrpcClient == "" {
panic("ParaRemoteGrpcClient error")
}
conn, err := grpc.Dial(cfg.ParaRemoteGrpcClient, grpc.WithInsecure(), msgRecvOp)
paraRemoteGrpcClient := types.Conf("config.consensus").GStr("ParaRemoteGrpcClient")
conn, err := grpc.Dial(paraRemoteGrpcClient, grpc.WithInsecure(), msgRecvOp)
if err != nil {
panic(err)
......@@ -146,7 +146,7 @@ func NewLotteryAction(l *Lottery, tx *types.Transaction, index int) *Action {
grpcClient := types.NewChain33Client(conn)
return &Action{l.GetCoinsAccount(), l.GetStateDB(), hash, fromaddr, l.GetBlockTime(),
l.GetHeight(), dapp.ExecAddress(string(tx.Execer)), l.GetDifficulty(), l.GetAPI(), conn, grpcClient, index}
l.GetHeight(), dapp.ExecAddress(string(tx.Execer)), l.GetDifficulty(), l.GetAPI(), conn, grpcClient, index, l}
}
// GetLottCommonRecipt generate logs for lottery common action
......@@ -569,7 +569,6 @@ func (action *Action) LotteryClose(draw *pty.LotteryClose) (*types.Receipt, erro
func (action *Action) findLuckyNum(isSolo bool, lott *LotteryDB) int64 {
var num int64
var msg types.Message
var err error
var hash []byte
if isSolo {
//used for internal verification
......@@ -579,7 +578,12 @@ func (action *Action) findLuckyNum(isSolo bool, lott *LotteryDB) int64 {
//在主链上,当前高度查询不到,如果要保证区块个数,高度传入action.height-1
llog.Debug("findLuckyNum on randnum module")
if !types.IsPara() {
req := &types.ReqRandHash{ExecName: "ticket", Height: action.height - 1, BlockNum: blockNum}
blockHash, err := action.api.GetBlockHash(&types.ReqInt{Height: action.height - 1})
if err != nil {
return -1
}
req := &types.ReqRandHash{ExecName: "ticket", Hash: blockHash.Hash, BlockNum: blockNum}
msg, err = action.api.Query("ticket", "RandNumHash", req)
if err != nil {
return -1
......@@ -587,12 +591,20 @@ func (action *Action) findLuckyNum(isSolo bool, lott *LotteryDB) int64 {
reply := msg.(*types.ReplyHash)
hash = reply.Hash
} else {
mainHeight := action.GetMainHeightByTxHash(action.txhash)
if mainHeight < 0 {
llog.Error("findLuckyNum", "mainHeight", mainHeight)
txs := action.lottery.GetTxs()
if len(txs) < action.index+1 {
llog.Error("findLuckyNum", "len(txs)", len(txs), "index", action.index)
return -1
}
req := &types.ReqRandHash{ExecName: "ticket", Height: mainHeight, BlockNum: blockNum}
msg, err := action.api.Query("paracross", "GetMainBlockHash", txs[0])
if err != nil {
return -1
}
queryReply := msg.(*types.ReplyHash)
mainBlockHash := queryReply.Hash
req := &types.ReqRandHash{ExecName: "ticket", Hash: mainBlockHash, BlockNum: blockNum}
reply, err := action.grpcClient.QueryRandNum(context.Background(), req)
if err != nil {
return -1
......
......@@ -12,6 +12,7 @@ import (
"strings"
"github.com/33cn/chain33/rpc/jsonclient"
"github.com/33cn/chain33/system/dapp/commands"
"github.com/33cn/chain33/types"
pt "github.com/33cn/plugin/plugin/dapp/paracross/types"
"github.com/spf13/cobra"
......@@ -162,19 +163,12 @@ func addCreateTransferFlags(cmd *cobra.Command) {
cmd.Flags().StringP("note", "n", "", "transaction note info")
cmd.Flags().StringP("title", "", "", "the title of para chain, like `p.user.guodun.`")
cmd.MarkFlagRequired("title")
cmd.Flags().StringP("symbol", "s", "", "default for bty, symbol for token")
cmd.MarkFlagRequired("symbol")
}
func createTransfer(cmd *cobra.Command, args []string) {
txHex, err := createTransferTx(cmd, false)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(txHex)
commands.CreateAssetTransfer(cmd, args, pt.ParaX)
}
//CreateRawTransferToExecCmd create raw transfer to exec tx
......@@ -189,27 +183,20 @@ func CreateRawTransferToExecCmd() *cobra.Command {
}
func addCreateTransferToExecFlags(cmd *cobra.Command) {
cmd.Flags().StringP("to", "t", "", "receiver exec name")
cmd.MarkFlagRequired("to")
cmd.Flags().Float64P("amount", "a", 0, "transaction amount")
cmd.MarkFlagRequired("amount")
cmd.Flags().StringP("note", "n", "", "transaction note info")
cmd.Flags().StringP("title", "", "", "the title of para chain, like `p.user.guodun.`")
cmd.MarkFlagRequired("title")
cmd.Flags().StringP("symbol", "s", "coins.bty", "default for bty, symbol for token")
cmd.MarkFlagRequired("symbol")
cmd.Flags().StringP("symbol", "s", "", "default for bty, symbol for token")
cmd.Flags().StringP("exec", "e", "", "asset deposit exec")
cmd.MarkFlagRequired("exec")
}
func createTransferToExec(cmd *cobra.Command, args []string) {
txHex, err := createTransferTx(cmd, false)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(txHex)
commands.CreateAssetSendToExec(cmd, args, pt.ParaX)
}
//CreateRawWithdrawCmd create raw withdraw tx
......@@ -229,59 +216,15 @@ func addCreateWithdrawFlags(cmd *cobra.Command) {
cmd.Flags().StringP("note", "n", "", "transaction note info")
cmd.Flags().StringP("title", "", "", "the title of para chain, like `p.user.guodun.`")
cmd.MarkFlagRequired("title")
cmd.Flags().StringP("from", "t", "", "exec name")
cmd.MarkFlagRequired("from")
cmd.Flags().StringP("symbol", "s", "", "default for bty, symbol for token")
}
cmd.MarkFlagRequired("symbol")
func createWithdraw(cmd *cobra.Command, args []string) {
txHex, err := createTransferTx(cmd, true)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(txHex)
cmd.Flags().StringP("exec", "e", "", "asset deposit exec")
cmd.MarkFlagRequired("exec")
}
func createTransferTx(cmd *cobra.Command, isWithdraw bool) (string, error) {
amount, _ := cmd.Flags().GetFloat64("amount")
if amount < 0 {
return "", types.ErrAmount
}
amountInt64 := int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4
toAddr, _ := cmd.Flags().GetString("to")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
title, _ := cmd.Flags().GetString("title")
if !strings.HasPrefix(title, "user.p") {
fmt.Fprintln(os.Stderr, "title is not right, title format like `user.p.guodun.`")
return "", types.ErrInvalidParam
}
execName := title + pt.ParaX
param := types.CreateTx{
To: toAddr,
Amount: amountInt64,
Fee: 0,
Note: []byte(note),
IsWithdraw: isWithdraw,
IsToken: false,
TokenSymbol: symbol,
ExecName: execName,
}
tx, err := pt.CreateRawTransferTx(&param)
if err != nil {
return "", err
}
txHex := types.Encode(tx)
return hex.EncodeToString(txHex), nil
func createWithdraw(cmd *cobra.Command, args []string) {
commands.CreateAssetWithdraw(cmd, args, pt.ParaX)
}
// IsSyncCmd query parachain is sync
......
......@@ -40,6 +40,38 @@ func (p *Paracross) Query_GetAssetTxResult(in *types.ReqHash) (types.Message, er
return p.paracrossGetAssetTxResult(in.Hash)
}
// Query_GetMainBlockHash query get mainblockHash by tx
func (p *Paracross) Query_GetMainBlockHash(in *types.Transaction) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
return p.paracrossGetMainBlockHash(in)
}
func (p *Paracross) paracrossGetMainBlockHash(tx *types.Transaction) (types.Message, error) {
var paraAction pt.ParacrossAction
err := types.Decode(tx.GetPayload(), &paraAction)
if err != nil {
return nil, err
}
if paraAction.GetTy() != pt.ParacrossActionMiner {
return nil, types.ErrCoinBaseTxType
}
if paraAction.GetMiner() == nil {
return nil, pt.ErrParaEmptyMinerTx
}
paraNodeStatus := paraAction.GetMiner().GetStatus()
if paraNodeStatus == nil {
return nil, types.ErrCoinBaseTxType
}
mainHashFromNode := paraNodeStatus.MainBlockHash
return &types.ReplyHash{Hash: mainHashFromNode}, nil
}
func (p *Paracross) paracrossGetHeight(title string) (types.Message, error) {
ret, err := getTitle(p.GetStateDB(), calcTitleKey(title))
if err != nil {
......
......@@ -5,7 +5,8 @@
package types
import (
fmt "fmt"
"encoding/json"
"fmt"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/log/log15"
......@@ -187,34 +188,28 @@ func CreateRawMinerTx(status *ParacrossNodeStatus) (*types.Transaction, error) {
}
// CreateRawTransferTx create paracross asset transfer tx with transfer and withdraw
func CreateRawTransferTx(param *types.CreateTx) (*types.Transaction, error) {
if !types.IsParaExecName(param.GetExecName()) {
tlog.Error("CreateRawTransferTx", "exec", param.GetExecName())
return nil, types.ErrInvalidParam
}
transfer := &ParacrossAction{}
if !param.IsWithdraw {
v := &ParacrossAction_Transfer{Transfer: &types.AssetsTransfer{
Amount: param.Amount, Note: param.GetNote(), To: param.GetTo(), Cointoken: param.TokenSymbol}}
transfer.Value = v
transfer.Ty = ParacrossActionTransfer
} else {
v := &ParacrossAction_Withdraw{Withdraw: &types.AssetsWithdraw{
Amount: param.Amount, Note: param.GetNote(), To: param.GetTo(), Cointoken: param.TokenSymbol}}
transfer.Value = v
transfer.Ty = ParacrossActionWithdraw
}
tx := &types.Transaction{
Execer: []byte(param.GetExecName()),
Payload: types.Encode(transfer),
To: address.ExecAddress(param.GetExecName()),
Fee: param.Fee,
}
var err error
tx, err = types.FormatTx(param.GetExecName(), tx)
func (p ParacrossType) CreateRawTransferTx(action string, param json.RawMessage) (*types.Transaction, error) {
tlog.Error("ParacrossType CreateTx failed", "action", action, "msg", string(param))
tx, err := p.ExecTypeBase.CreateTx(action, param)
if err != nil {
tlog.Error("ParacrossType CreateTx failed", "err", err, "action", action, "msg", string(param))
return nil, err
}
if !types.IsPara() {
var transfer ParacrossAction
err = types.Decode(tx.Payload, &transfer)
if err != nil {
tlog.Error("ParacrossType CreateTx failed", "decode payload err", err, "action", action, "msg", string(param))
return nil, err
}
if action == "Transfer" {
tx.To = transfer.GetTransfer().To
} else if action == "Withdraw" {
tx.To = transfer.GetWithdraw().To
} else if action == "TransferToExec" {
tx.To = transfer.GetTransferToExec().To
}
}
return tx, nil
}
......@@ -98,15 +98,16 @@ func (p ParacrossType) CreateTx(action string, message json.RawMessage) (*types.
}
return CreateRawAssetTransferTx(&param)
} else if action == "ParacrossTransfer" || action == "ParacrossWithdraw" || action == "ParacrossTransferToExec" {
} else if action == "ParacrossTransfer" || action == "Transfer" ||
action == "ParacrossWithdraw" || action == "Withdraw" ||
action == "ParacrossTransferToExec" || action == "TransferToExec" {
var param types.CreateTx
err := json.Unmarshal(message, &param)
if err != nil {
glog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam
}
return CreateRawTransferTx(&param)
return p.CreateRawTransferTx(action, message)
}
return nil, types.ErrNotSupport
......
......@@ -28,6 +28,7 @@ func PokerBullCmd() *cobra.Command {
PokerBullContinueRawTxCmd(),
PokerBullQuitRawTxCmd(),
PokerBullQueryResultRawTxCmd(),
PokerBullPlayRawTxCmd(),
)
return cmd
......@@ -45,7 +46,7 @@ func PokerBullStartRawTxCmd() *cobra.Command {
}
func addPokerbullStartFlags(cmd *cobra.Command) {
cmd.Flags().Uint64P("value", "a", 0, "value")
cmd.Flags().Uint64P("value", "v", 0, "value")
cmd.MarkFlagRequired("value")
cmd.Flags().Uint32P("playerCount", "p", 0, "player count")
......@@ -121,7 +122,7 @@ func pokerbullQuit(cmd *cobra.Command, args []string) {
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(pkt.PokerBullX),
ActionName: pkt.CreatequitTx,
ActionName: pkt.CreateQuitTx,
Payload: []byte(fmt.Sprintf("{\"gameId\":\"%s\"}", gameID)),
}
......@@ -130,6 +131,54 @@ func pokerbullQuit(cmd *cobra.Command, args []string) {
ctx.RunWithoutMarshal()
}
// PokerBullPlayRawTxCmd 生成已匹配玩家游戏命令行
func PokerBullPlayRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "play",
Short: "Play game",
Run: pokerbullPlay,
}
addPokerbullPlayFlags(cmd)
return cmd
}
func addPokerbullPlayFlags(cmd *cobra.Command) {
cmd.Flags().StringP("gameID", "g", "", "game ID")
cmd.MarkFlagRequired("gameID")
cmd.Flags().Uint32P("round", "r", 0, "round")
cmd.MarkFlagRequired("round")
cmd.Flags().Uint64P("value", "v", 0, "value")
cmd.MarkFlagRequired("value")
cmd.Flags().StringArrayP("address", "a", nil, "address")
cmd.MarkFlagRequired("address")
}
func pokerbullPlay(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
gameID, _ := cmd.Flags().GetString("gameID")
round, _ := cmd.Flags().GetUint32("round")
value, _ := cmd.Flags().GetUint64("value")
address, _ := cmd.Flags().GetStringArray("address")
payload := &pkt.PBGamePlay{
GameId: gameID,
Value: int64(value) * types.Coin,
Round: int32(round),
}
payload.Address = make([]string, len(address))
copy(payload.Address, address)
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(pkt.PokerBullX),
ActionName: pkt.CreatePlayTx,
Payload: types.MustPBToJSON(payload),
}
var res string
ctx := jsonrpc.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// PokerBullQueryResultRawTxCmd 查询命令行
func PokerBullQueryResultRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
......
......@@ -26,3 +26,9 @@ func (c *PokerBull) Exec_Quit(payload *pkt.PBGameQuit, tx *types.Transaction, in
action := NewAction(c, tx, index)
return action.GameQuit(payload)
}
// Exec_Play 已匹配玩家直接开始游戏
func (c *PokerBull) Exec_Play(payload *pkt.PBGamePlay, tx *types.Transaction, index int) (*types.Receipt, error) {
action := NewAction(c, tx, index)
return action.GamePlay(payload)
}
......@@ -57,7 +57,7 @@ func (c *PokerBull) execLocal(receipt *types.ReceiptData) (*types.LocalDBSet, er
dbSet := &types.LocalDBSet{}
for i := 0; i < len(receipt.Logs); i++ {
item := receipt.Logs[i]
if item.Ty == pkt.TyLogPBGameStart || item.Ty == pkt.TyLogPBGameContinue || item.Ty == pkt.TyLogPBGameQuit {
if item.Ty == pkt.TyLogPBGameStart || item.Ty == pkt.TyLogPBGameContinue || item.Ty == pkt.TyLogPBGameQuit || item.Ty == pkt.TyLogPBGamePlay {
var Gamelog pkt.ReceiptPBGame
err := types.Decode(item.Log, &Gamelog)
if err != nil {
......@@ -84,3 +84,8 @@ func (c *PokerBull) ExecLocal_Continue(payload *pkt.PBGameContinue, tx *types.Tr
func (c *PokerBull) ExecLocal_Quit(payload *pkt.PBGameQuit, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(receiptData)
}
// ExecLocal_Play 已匹配游戏交易local执行
func (c *PokerBull) ExecLocal_Play(payload *pkt.PBGamePlay, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return c.execLocal(receiptData)
}
......@@ -6,7 +6,7 @@ package types;
message PokerBull {
string gameId = 1; //默认是由创建这局游戏的txHash作为gameId
int32 status = 2; // Start 1 -> Continue 2 -> Quit 3
int64 startTime = 3; //开始时间
string startTime = 3; //开始时间
string startTxHash = 4; //游戏启动交易hash
int64 value = 5; //赌注
PBPoker poker = 6; //扑克牌
......@@ -15,7 +15,7 @@ message PokerBull {
repeated PBResult results = 9; //游戏结果集
int64 index = 10; //索引
int64 prevIndex = 11; //上级索引
int64 quitTime = 12; //游戏结束时间
string quitTime = 12; //游戏结束时间
string quitTxHash = 13; //游戏结束交易hash
string dealerAddr = 14; //下局庄家地址
bool isWaiting = 15; //游戏是否处于等待状态
......@@ -25,19 +25,21 @@ message PokerBull {
//一把牌
message PBHand {
repeated int32 cards = 1; //一把牌,五张
int32 result = 2; //斗牛结果 (没牛:0, 牛1-9:1-9, 牛牛:10)
string address = 3; //玩家地址
bool isWin = 4; //是否赢庄家
int32 leverage = 5; //赌注倍数
repeated int32 cards = 1; //一把牌,五张
int32 result = 2; //斗牛结果 (没牛:0, 牛1-9:1-9, 牛牛:10)
string address = 3; //玩家地址
bool isWin = 4; //是否赢庄家
int32 leverage = 5; //赌注倍数
string roundTime = 6; //本回合开始时间
}
//玩家
message PBPlayer {
repeated PBHand hands = 1; //历史发牌和斗牛结果
string address = 2; //玩家地址
int64 txHash = 3; //发牌随机数因子txhash的整数格式
bool ready = 4; // continue状态下,是否ready
repeated PBHand hands = 1; //历史发牌和斗牛结果
string address = 2; //玩家地址
int64 txHash = 3; //发牌随机数因子txhash的整数格式
bool ready = 4; //continue状态下,是否ready
string matchTime = 5; //玩家匹配时间
}
//本局游戏结果
......@@ -58,10 +60,11 @@ message PBPoker {
//游戏状态
message PBGameAction {
oneof value {
PBGameStart start = 1;
PBGameStart start = 1;
PBGameContinue continue = 2;
PBGameQuit quit = 3;
PBGameQuery query = 4;
PBGameQuit quit = 3;
PBGameQuery query = 4;
PBGamePlay play = 5;
}
int32 ty = 10;
}
......@@ -87,6 +90,14 @@ message PBGameQuery {
string gameId = 1;
}
//已匹配玩家直接游戏
message PBGamePlay {
string gameId = 1; //游戏id
int32 round = 2; //当前游戏回合数
int64 value = 3; //当前游戏赌注
repeated string address = 4; //玩家地址
}
//根据状态和游戏人数查找
message QueryPBGameListByStatusAndPlayerNum {
int32 status = 1;
......
......@@ -97,7 +97,7 @@ func testQuitRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
payload := &pty.PBGameQuit{GameId: "123"}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(pty.PokerBullX),
ActionName: pty.CreatequitTx,
ActionName: pty.CreateQuitTx,
Payload: types.MustPBToJSON(payload),
}
var res string
......
......@@ -12,6 +12,7 @@ const (
PBGameActionContinue
PBGameActionQuit
PBGameActionQuery
PBGameActionPlay
)
const (
......@@ -30,6 +31,8 @@ const (
TyLogPBGameQuit = 723
// TyLogPBGameQuery log for query PBgame
TyLogPBGameQuery = 724
// TyLogPBGamePlay log for play PBgame
TyLogPBGamePlay = 725
)
//包的名字可以通过配置文件来配置
......@@ -56,8 +59,10 @@ const (
CreateStartTx = "Start"
// CreateContinueTx 创建继续交易
CreateContinueTx = "Continue"
// CreatequitTx 创建退出交易
CreatequitTx = "Quit"
// CreateQuitTx 创建退出交易
CreateQuitTx = "Quit"
// CreatePlayTx 创建已匹配玩家交易
CreatePlayTx = "Play"
)
const (
......@@ -67,6 +72,8 @@ const (
DefaultCount = int32(20)
// MaxPlayerNum 最大玩家数
MaxPlayerNum = 5
// MinPlayerNum 最小玩家数
MinPlayerNum = 2
// MinPlayValue 最小赌注
MinPlayValue = 10 * types.Coin
// DefaultStyle 默认游戏类型
......@@ -80,5 +87,7 @@ const (
// DeveloperFee 开发者佣金
DeveloperFee = int64(0.005 * float64(types.Coin))
// WinnerReturn 赢家回报率
WinnerReturn = types.Coin - DeveloperFee
WinnerReturn = types.Coin - DeveloperFee - PlatformFee
// PlatformSignAddress 平台签名地址
PlatformSignAddress = "1Geb4ppNiAwMKKyrJgcis3JA57FkqsXvdR"
)
This diff is collapsed.
......@@ -46,6 +46,7 @@ func (t *PokerBullType) GetTypeMap() map[string]int32 {
"Continue": PBGameActionContinue,
"Quit": PBGameActionQuit,
"Query": PBGameActionQuery,
"Play": PBGameActionPlay,
}
}
......@@ -56,5 +57,6 @@ func (t *PokerBullType) GetLogMap() map[int64]*types.LogInfo {
TyLogPBGameContinue: {Ty: reflect.TypeOf(ReceiptPBGame{}), Name: "TyLogPBGameContinue"},
TyLogPBGameQuit: {Ty: reflect.TypeOf(ReceiptPBGame{}), Name: "TyLogPBGameQuit"},
TyLogPBGameQuery: {Ty: reflect.TypeOf(ReceiptPBGame{}), Name: "TyLogPBGameQuery"},
TyLogPBGamePlay: {Ty: reflect.TypeOf(ReceiptPBGame{}), Name: "TyLogPBGamePlay"},
}
}
......@@ -47,5 +47,5 @@ func (ticket *Ticket) Query_MinerSourceList(param *types.ReqString) (types.Messa
// Query_RandNumHash query randnumhash
func (ticket *Ticket) Query_RandNumHash(param *types.ReqRandHash) (types.Message, error) {
return ticket.GetRandNum(param.Height, param.BlockNum)
return ticket.GetRandNum(param.Hash, param.BlockNum)
}
......@@ -18,19 +18,20 @@ const (
)
// GetRandNum for ticket executor
func (ticket *Ticket) GetRandNum(height int64, blockNum int64) (types.Message, error) {
tlog.Debug("GetRandNum", "height", height, "blockNum", blockNum)
func (ticket *Ticket) GetRandNum(blockHash []byte, blockNum int64) (types.Message, error) {
tlog.Debug("GetRandNum", "blockHash", blockHash, "blockNum", blockNum)
if blockNum < minBlockNum {
blockNum = minBlockNum
} else if blockNum > maxBlockNum {
blockNum = maxBlockNum
}
if blockNum >= height {
return nil, types.ErrNotFound
if len(blockHash) == 0 {
return nil, types.ErrBlockNotFound
}
txActions, err := ticket.getTxActions(height, blockNum)
txActions, err := ticket.getTxActions(blockHash, blockNum)
if err != nil {
return nil, err
}
......@@ -54,20 +55,36 @@ func (ticket *Ticket) GetRandNum(height int64, blockNum int64) (types.Message, e
return &types.ReplyHash{Hash: modify}, nil
}
func (ticket *Ticket) getTxActions(height int64, blockNum int64) ([]*tickettypes.TicketAction, error) {
func (ticket *Ticket) getTxActions(blockHash []byte, blockNum int64) ([]*tickettypes.TicketAction, error) {
var txActions []*tickettypes.TicketAction
var reqHashes types.ReqHashes
currHash := blockHash
tlog.Debug("getTxActions", "blockHash", blockHash, "blockNum", blockNum)
tlog.Debug("getTxActions", "height", height, "blockNum", blockNum)
//根据blockHash,查询block,循环blockNum
for blockNum > 0 {
req := types.ReqHash{Hash: currHash}
req := &types.ReqBlocks{Start: height - blockNum + 1, End: height, IsDetail: false, Pid: []string{""}}
tempBlock, err := ticket.GetAPI().GetBlockOverview(&req)
if err != nil {
return txActions, err
}
reqHashes.Hashes = append(reqHashes.Hashes, currHash)
currHash = tempBlock.Head.ParentHash
if tempBlock.Head.Height < 0 && blockNum > 1 {
return txActions, types.ErrBlockNotFound
}
blockNum--
}
blockDetails, err := ticket.GetAPI().GetBlocks(req)
blockDetails, err := ticket.GetAPI().GetBlockByHashes(&reqHashes)
if err != nil {
tlog.Error("getTxActions", "height", height, "blockNum", blockNum, "err", err)
tlog.Error("getTxActions", "blockHash", blockHash, "blockNum", blockNum, "err", err)
return txActions, err
}
for _, block := range blockDetails.Items {
//tlog.Debug("getTxActions", "blockHeight", block.Block.Height, "blockhash", block.Block.Hash())
tlog.Debug("getTxActions", "blockHeight", block.Block.Height, "blockhash", block.Block.Hash())
ticketAction, err := ticket.getMinerTx(block.Block)
if err != nil {
return txActions, err
......
......@@ -7,13 +7,13 @@ package commands
import (
"encoding/json"
"fmt"
"math"
"os"
"strconv"
"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"
tokenty "github.com/33cn/plugin/plugin/dapp/token/types"
"github.com/spf13/cobra"
......@@ -72,31 +72,7 @@ func addCreateTokenTransferFlags(cmd *cobra.Command) {
}
func createTokenTransfer(cmd *cobra.Command, args []string) {
toAddr, _ := cmd.Flags().GetString("to")
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
payload := &types.AssetsTransfer{
To: toAddr,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(tokenty.TokenX),
ActionName: "Transfer",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
commands.CreateAssetTransfer(cmd, args, tokenty.TokenX)
}
// CreateTokenTransferExecCmd create raw transfer tx
......@@ -124,41 +100,7 @@ func addCreateTokenSendToExecFlags(cmd *cobra.Command) {
}
func createTokenSendToExec(cmd *cobra.Command, args []string) {
paraName, _ := cmd.Flags().GetString("paraName")
exec, _ := cmd.Flags().GetString("exec")
exec = getRealExecName(paraName, exec)
to, err := GetExecAddr(exec)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
payload := &types.AssetsTransferToExec{
To: to,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
ExecName: exec,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(tokenty.TokenX),
ActionName: "TransferToExec",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
commands.CreateAssetSendToExec(cmd, args, tokenty.TokenX)
}
// CreateTokenWithdrawCmd create raw withdraw tx
......@@ -186,40 +128,7 @@ func addCreateTokenWithdrawFlags(cmd *cobra.Command) {
}
func createTokenWithdraw(cmd *cobra.Command, args []string) {
exec, _ := cmd.Flags().GetString("exec")
paraName, _ := cmd.Flags().GetString("paraName")
exec = getRealExecName(paraName, exec)
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
exec = getRealExecName(paraName, exec)
execAddr, err := GetExecAddr(exec)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
payload := &types.AssetsWithdraw{
To: execAddr,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
ExecName: exec,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(tokenty.TokenX),
ActionName: "Withdraw",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
commands.CreateAssetWithdraw(cmd, args, tokenty.TokenX)
}
// GetTokensPreCreatedCmd get precreated tokens
......
......@@ -20,6 +20,11 @@ chain33背后故事: [chain33诞生记](https://mp.weixin.qq.com/s/9g5ZFDKJi9uzR
视频教程: [视频教程](https://chain.33.cn/document/90)
# 感谢
[腾讯玄武实验室](https://github.com/33cn/chain33/issues?utf8=%E2%9C%93&q=label%3A%E8%85%BE%E8%AE%AF%E7%8E%84%E6%AD%A6%E5%AE%9E%E9%AA%8C%E5%AE%A4)
## Building from source
环境要求: Go (version 1.9 or later)
......
......@@ -276,8 +276,8 @@ func (acc *DB) loadAccountsHistory(api client.QueueProtocolAPI, addrs []string,
// GetBalance 获取某个状态下账户余额
func (acc *DB) GetBalance(api client.QueueProtocolAPI, in *types.ReqBalance) ([]*types.Account, error) {
switch in.GetExecer() {
case types.ExecName("coins"):
// load account
if in.AssetExec == in.Execer || "" == in.Execer {
addrs := in.GetAddresses()
var exaddrs []string
for _, addr := range addrs {
......@@ -306,34 +306,35 @@ func (acc *DB) GetBalance(api client.QueueProtocolAPI, in *types.ReqBalance) ([]
}
}
return accounts, nil
default:
execaddress := address.ExecAddress(in.GetExecer())
addrs := in.GetAddresses()
var accounts []*types.Account
for _, addr := range addrs {
var account *types.Account
var err error
if len(in.StateHash) == 0 {
account, err = acc.LoadExecAccountQueue(api, addr, execaddress)
if err != nil {
log.Error("GetBalance", "err", err.Error())
continue
}
} else {
hash, err := common.FromHex(in.StateHash)
if err != nil {
return nil, err
}
account, err = acc.LoadExecAccountHistoryQueue(api, addr, execaddress, hash)
if err != nil {
log.Error("GetBalance", "err", err.Error())
continue
}
}
// load exec account
execaddress := address.ExecAddress(in.GetExecer())
addrs := in.GetAddresses()
var accounts []*types.Account
for _, addr := range addrs {
var account *types.Account
var err error
if len(in.StateHash) == 0 {
account, err = acc.LoadExecAccountQueue(api, addr, execaddress)
if err != nil {
log.Error("GetBalance", "err", err.Error())
continue
}
} else {
hash, err := common.FromHex(in.StateHash)
if err != nil {
return nil, err
}
account, err = acc.LoadExecAccountHistoryQueue(api, addr, execaddress, hash)
if err != nil {
log.Error("GetBalance", "err", err.Error())
continue
}
accounts = append(accounts, account)
}
return accounts, nil
accounts = append(accounts, account)
}
return accounts, nil
}
// GetExecBalance 通过account模块获取地址账户在合约中的余额
......
......@@ -108,21 +108,24 @@ function start_chain33() {
echo "=========== #transfer to miner addr ============="
hash=$(${CLI} send coins transfer -a 10000 -n test -t 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944)
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
echo "=========== #transfer to token amdin ============="
hash=$(${CLI} send coins transfer -a 10 -n test -t 1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944)
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
......@@ -131,10 +134,12 @@ function start_chain33() {
signData=$(${CLI} wallet sign -d "${rawData}" -k 0xc34b5d9d44ac7b754806f761d3d4d2c4fe5214f6b074c19f069c4f5c2a29c8cc)
hash=$(${CLI} wallet send -d "${signData}")
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
......
......@@ -121,21 +121,24 @@ function start_chain33() {
echo "=========== #transfer to miner addr ============="
hash=$(${CLI} send coins transfer -a 10000 -n test -t 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944)
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
echo "=========== #transfer to token amdin ============="
hash=$(${CLI} send coins transfer -a 10 -n test -t 1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944)
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
......@@ -144,10 +147,12 @@ function start_chain33() {
signData=$(${CLI} wallet sign -d "${rawData}" -k 0xc34b5d9d44ac7b754806f761d3d4d2c4fe5214f6b074c19f069c4f5c2a29c8cc)
hash=$(${CLI} wallet send -d "${signData}")
echo "${hash}"
sleep ${chain33BlockTime}
txs=$(${CLI} tx query_hash -s "${hash}" | jq ".txs")
if [ "${txs}" == "null" ]; then
echo "transferTokenAdmin cannot find tx"
result=$(${CLI} tx query -s "${hash}" | jq '.receipt.tyName')
if [[ ${result} != '"ExecOk"' ]]; then
echo "Failed"
${CLI} tx query -s "${hash}" | jq '.' | cat
exit 1
fi
......
......@@ -206,7 +206,20 @@ func (c *channelClient) GetAddrOverview(parm *types.ReqAddr) (*types.AddrOvervie
// GetBalance get balance
func (c *channelClient) GetBalance(in *types.ReqBalance) ([]*types.Account, error) {
return c.accountdb.GetBalance(c.QueueProtocolAPI, in)
// in.AssetExec & in.AssetSymbol 新增参数,
// 不填时兼容原来的调用
if in.AssetExec == "" || in.AssetSymbol == "" {
in.AssetSymbol = "bty"
in.AssetExec = "coins"
return c.accountdb.GetBalance(c.QueueProtocolAPI, in)
}
acc, err := account.NewAccountDB(in.AssetExec, in.AssetSymbol, nil)
if err != nil {
log.Error("GetBalance", "Error", err.Error())
return nil, err
}
return acc.GetBalance(c.QueueProtocolAPI, in)
}
// GetAllExecBalance get balance of exec
......@@ -288,3 +301,49 @@ func (c *channelClient) GetExecBalance(in *types.ReqGetExecBalance) (*types.Repl
}
return resp, nil
}
// GetAssetBalance 通用的获得资产的接口
func (c *channelClient) GetAssetBalance(in *types.ReqBalance) ([]*types.Account, error) {
if in.AssetSymbol == "" || in.AssetExec == "" {
return nil, types.ErrInvalidParam
}
acc, err := account.NewAccountDB(in.AssetExec, in.AssetSymbol, nil)
if err != nil {
return nil, err
}
// load balance
if in.AssetExec == in.Execer || in.Execer == "" {
addrs := in.GetAddresses()
var queryAddrs []string
for _, addr := range addrs {
if err := address.CheckAddress(addr); err != nil {
addr = string(acc.AccountKey(addr))
}
queryAddrs = append(queryAddrs, addr)
}
accounts, err := acc.LoadAccounts(c.QueueProtocolAPI, queryAddrs)
if err != nil {
log.Error("GetAssetBalance", "err", err.Error(), "exec", in.AssetExec, "symbol", in.AssetSymbol,
"address", queryAddrs)
return nil, err
}
return accounts, nil
}
// load exec balance
execaddress := address.ExecAddress(in.GetExecer())
addrs := in.GetAddresses()
var accounts []*types.Account
for _, addr := range addrs {
acc, err := acc.LoadExecAccountQueue(c.QueueProtocolAPI, addr, execaddress)
if err != nil {
log.Error("GetAssetBalance for exector", "err", err.Error(), "exec", in.AssetExec,
"symbol", in.AssetSymbol, "address", addr, "where", in.Execer)
continue
}
accounts = append(accounts, acc)
}
return accounts, nil
}
......@@ -361,3 +361,66 @@ func TestChannelClient_GetBalance(t *testing.T) {
// assert.NotNil(t, data)
// assert.Nil(t, err)
// }
func testChannelClient_GetAssetBalanceCoin(t *testing.T) {
api := new(mocks.QueueProtocolAPI)
db := new(account.DB)
client := &channelClient{
QueueProtocolAPI: api,
accountdb: db,
}
head := &types.Header{StateHash: []byte("sdfadasds")}
api.On("GetLastHeader").Return(head, nil)
var acc = &types.Account{Addr: "1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt", Balance: 100}
accv := types.Encode(acc)
storevalue := &types.StoreReplyValue{}
storevalue.Values = append(storevalue.Values, accv)
api.On("StoreGet", mock.Anything).Return(storevalue, nil)
var addrs = []string{"1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt"}
var in = &types.ReqBalance{
AssetSymbol: "bty",
AssetExec: "coins",
Execer: "coins",
Addresses: addrs,
}
data, err := client.GetAssetBalance(in)
assert.Nil(t, err)
assert.Equal(t, acc.Addr, data[0].Addr)
}
func testChannelClient_GetAssetBalanceOther(t *testing.T) {
api := new(mocks.QueueProtocolAPI)
db := new(account.DB)
client := &channelClient{
QueueProtocolAPI: api,
accountdb: db,
}
head := &types.Header{StateHash: []byte("sdfadasds")}
api.On("GetLastHeader").Return(head, nil)
var acc = &types.Account{Addr: "1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt", Balance: 100}
accv := types.Encode(acc)
storevalue := &types.StoreReplyValue{}
storevalue.Values = append(storevalue.Values, accv)
api.On("StoreGet", mock.Anything).Return(storevalue, nil)
var addrs = []string{"1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt"}
var in = &types.ReqBalance{
AssetSymbol: "bty",
AssetExec: "coins",
Execer: types.ExecName("ticket"),
Addresses: addrs,
}
data, err := client.GetAssetBalance(in)
assert.Nil(t, err)
assert.Equal(t, acc.Addr, data[0].Addr)
}
func TestChannelClient_GetAssetBalance(t *testing.T) {
testChannelClient_GetAssetBalanceCoin(t)
testChannelClient_GetAssetBalanceOther(t)
}
......@@ -741,19 +741,12 @@ func (c *Chain33) GetWalletStatus(in types.ReqNil, result *interface{}) error {
// GetBalance get balance
func (c *Chain33) GetBalance(in types.ReqBalance, result *interface{}) error {
balances, err := c.cli.GetBalance(&in)
if err != nil {
return err
}
var accounts []*rpctypes.Account
for _, balance := range balances {
accounts = append(accounts, &rpctypes.Account{Addr: balance.GetAddr(),
Balance: balance.GetBalance(),
Currency: balance.GetCurrency(),
Frozen: balance.GetFrozen()})
}
*result = accounts
*result = fmtAccount(balances)
return nil
}
......@@ -1209,3 +1202,14 @@ func convertBlockDetails(details []*types.BlockDetail, retDetails *rpctypes.Bloc
}
return nil
}
func fmtAccount(balances []*types.Account) []*rpctypes.Account {
var accounts []*rpctypes.Account
for _, balance := range balances {
accounts = append(accounts, &rpctypes.Account{Addr: balance.GetAddr(),
Balance: balance.GetBalance(),
Currency: balance.GetCurrency(),
Frozen: balance.GetFrozen()})
}
return accounts
}
......@@ -10,6 +10,7 @@ import (
"encoding/hex"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client/mocks"
"github.com/33cn/chain33/common"
rpctypes "github.com/33cn/chain33/rpc/types"
......@@ -373,6 +374,7 @@ func newTestChain33(api *mocks.QueueProtocolAPI) *Chain33 {
return &Chain33{
cli: channelClient{
QueueProtocolAPI: api,
accountdb: account.NewCoinsAccount(),
},
}
}
......@@ -1290,3 +1292,58 @@ func TestChain33_GetExecBalance(t *testing.T) {
err = client.GetExecBalance(in, &testResult2)
assert.NotNil(t, err)
}
func TestChain33_GetBalance(t *testing.T) {
api := new(mocks.QueueProtocolAPI)
client := newTestChain33(api)
var addrs = []string{"1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt"}
cases := []struct {
In types.ReqBalance
}{
{In: types.ReqBalance{
Execer: types.ExecName("coins"),
Addresses: addrs,
}},
{In: types.ReqBalance{
Execer: types.ExecName("ticket"),
Addresses: addrs,
}},
{In: types.ReqBalance{
AssetSymbol: "bty",
AssetExec: "coins",
Execer: types.ExecName("ticket"),
Addresses: addrs,
}},
{In: types.ReqBalance{
AssetSymbol: "bty",
AssetExec: "coins",
Execer: types.ExecName("coins"),
Addresses: addrs,
}},
}
for _, c := range cases {
c := c
t.Run("test GetBalance", func(t *testing.T) {
head := &types.Header{StateHash: []byte("sdfadasds")}
api.On("GetLastHeader").Return(head, nil)
var acc = &types.Account{Addr: "1Jn2qu84Z1SUUosWjySggBS9pKWdAP3tZt", Balance: 100}
accv := types.Encode(acc)
storevalue := &types.StoreReplyValue{}
storevalue.Values = append(storevalue.Values, accv)
api.On("StoreGet", mock.Anything).Return(storevalue, nil)
var data interface{}
err := client.GetBalance(c.In, &data)
assert.Nil(t, err)
result := data.([]*rpctypes.Account)
assert.Equal(t, 1, len(result))
//t.Error("result", "x", result)
assert.Equal(t, acc.Addr, result[0].Addr)
assert.Equal(t, int64(100), result[0].Balance)
})
}
}
// 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 commands
import (
"encoding/json"
"fmt"
"math"
"os"
"strings"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/rpc/jsonclient"
rpcTypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
"github.com/spf13/cobra"
)
// AssetCmd command
func AssetCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "asset",
Short: "Asset query",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
GetAssetBalanceCmd(),
)
return cmd
}
// GetAssetBalanceCmd query asset balance
func GetAssetBalanceCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "balance",
Short: "Query asset balance",
Run: assetBalance,
}
addAssetBalanceFlags(cmd)
return cmd
}
func addAssetBalanceFlags(cmd *cobra.Command) {
cmd.Flags().StringP("addr", "a", "", "account addr")
cmd.MarkFlagRequired("addr")
cmd.Flags().StringP("exec", "e", "", getExecuterNameString())
cmd.Flags().StringP("asset_exec", "", "coins", "the asset executor")
cmd.MarkFlagRequired("asset_exec")
cmd.Flags().StringP("asset_symbol", "", "bty", "the asset symbol")
cmd.MarkFlagRequired("asset_symbol")
cmd.Flags().IntP("height", "", -1, "block height")
}
func assetBalance(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
addr, _ := cmd.Flags().GetString("addr")
execer, _ := cmd.Flags().GetString("exec")
asset_symbol, _ := cmd.Flags().GetString("asset_symbol")
asset_exec, _ := cmd.Flags().GetString("asset_exec")
height, _ := cmd.Flags().GetInt("height")
err := address.CheckAddress(addr)
if err != nil {
if err = address.CheckMultiSignAddress(addr); err != nil {
fmt.Fprintln(os.Stderr, types.ErrInvalidAddress)
return
}
}
if execer == "" {
execer = asset_exec
}
if ok := types.IsAllowExecName([]byte(execer), []byte(execer)); !ok {
fmt.Fprintln(os.Stderr, types.ErrExecNameNotAllow)
return
}
stateHash := ""
if height >= 0 {
params := types.ReqBlocks{
Start: int64(height),
End: int64(height),
IsDetail: false,
}
var res rpcTypes.Headers
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.GetHeaders", params, &res)
_, err := ctx.RunResult()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
h := res.Items[0]
stateHash = h.StateHash
}
var addrs []string
addrs = append(addrs, addr)
params := types.ReqBalance{
Addresses: addrs,
Execer: execer,
StateHash: stateHash,
AssetExec: asset_exec,
AssetSymbol: asset_symbol,
}
var res []*rpcTypes.Account
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.GetBalance", params, &res)
ctx.SetResultCb(parseGetBalanceRes)
ctx.Run()
}
// CreateAssetSendToExec 通用的创建 send_exec 交易, 额外指定资产合约
func CreateAssetSendToExec(cmd *cobra.Command, args []string, fromExec string) {
paraName, _ := cmd.Flags().GetString("paraName")
exec, _ := cmd.Flags().GetString("exec")
exec = getRealExecName(paraName, exec)
to, err := GetExecAddr(exec)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
payload := &types.AssetsTransferToExec{
To: to,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
ExecName: exec,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpcTypes.CreateTxIn{
Execer: types.ExecName(fromExec),
ActionName: "TransferToExec",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
}
// CreateAssetWithdraw 通用的创建 withdraw 交易, 额外指定资产合约
func CreateAssetWithdraw(cmd *cobra.Command, args []string, fromExec string) {
exec, _ := cmd.Flags().GetString("exec")
paraName, _ := cmd.Flags().GetString("paraName")
exec = getRealExecName(paraName, exec)
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
exec = getRealExecName(paraName, exec)
execAddr, err := GetExecAddr(exec)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
payload := &types.AssetsWithdraw{
To: execAddr,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
ExecName: exec,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpcTypes.CreateTxIn{
Execer: types.ExecName(fromExec),
ActionName: "Withdraw",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
}
// CreateAssetTransfer 通用的创建 transfer 交易, 额外指定资产合约
func CreateAssetTransfer(cmd *cobra.Command, args []string, fromExec string) {
toAddr, _ := cmd.Flags().GetString("to")
amount, _ := cmd.Flags().GetFloat64("amount")
note, _ := cmd.Flags().GetString("note")
symbol, _ := cmd.Flags().GetString("symbol")
payload := &types.AssetsTransfer{
To: toAddr,
Amount: int64(math.Trunc((amount+0.0000001)*1e4)) * 1e4,
Note: []byte(note),
Cointoken: symbol,
}
data, err := json.Marshal(&payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
params := &rpcTypes.CreateTxIn{
Execer: types.ExecName(fromExec),
ActionName: "Transfer",
Payload: data,
}
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, nil)
ctx.RunWithoutMarshal()
}
// GetExecAddr 获取执行器地址
func GetExecAddr(exec string) (string, error) {
if ok := types.IsAllowExecName([]byte(exec), []byte(exec)); !ok {
return "", types.ErrExecNameNotAllow
}
addrResult := address.ExecAddress(exec)
result := addrResult
return result, nil
}
func getRealExecName(paraName string, name string) string {
if strings.HasPrefix(name, "user.p.") {
return name
}
return paraName + name
}
......@@ -205,6 +205,8 @@ type ReqBalance struct {
//执行器名称
Execer string `protobuf:"bytes,2,opt,name=execer,proto3" json:"execer,omitempty"`
StateHash string `protobuf:"bytes,3,opt,name=stateHash,proto3" json:"stateHash,omitempty"`
AssetExec string `protobuf:"bytes,4,opt,name=asset_exec,json=assetExec,proto3" json:"asset_exec,omitempty"`
AssetSymbol string `protobuf:"bytes,5,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -256,6 +258,20 @@ func (m *ReqBalance) GetStateHash() string {
return ""
}
func (m *ReqBalance) GetAssetExec() string {
if m != nil {
return m.AssetExec
}
return ""
}
func (m *ReqBalance) GetAssetSymbol() string {
if m != nil {
return m.AssetSymbol
}
return ""
}
// Account 的列表
type Accounts struct {
Acc []*Account `protobuf:"bytes,1,rep,name=acc,proto3" json:"acc,omitempty"`
......@@ -403,28 +419,30 @@ func init() {
func init() { proto.RegisterFile("account.proto", fileDescriptor_8e28828dcb8d24f0) }
var fileDescriptor_8e28828dcb8d24f0 = []byte{
// 360 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x41, 0x4f, 0x32, 0x31,
0x10, 0x4d, 0x59, 0x60, 0xd9, 0x21, 0x1f, 0x87, 0x1e, 0xc8, 0x86, 0x7c, 0xc6, 0xcd, 0x9e, 0xf6,
0x60, 0x96, 0xc4, 0xf5, 0x0f, 0x40, 0x62, 0xe2, 0xcd, 0xa4, 0xf1, 0xc4, 0xc9, 0x52, 0x06, 0x21,
0x62, 0x77, 0x6d, 0x8b, 0x11, 0x7f, 0x80, 0xbf, 0xdb, 0xb4, 0x74, 0x61, 0x91, 0x68, 0xbc, 0xf5,
0xcd, 0xeb, 0xbc, 0xf7, 0x66, 0x5a, 0xf8, 0xc7, 0x85, 0x28, 0xb7, 0xd2, 0xe4, 0x95, 0x2a, 0x4d,
0x49, 0x3b, 0x66, 0x57, 0xa1, 0x4e, 0x9f, 0x21, 0x9c, 0xec, 0xeb, 0x74, 0x04, 0x3d, 0xb1, 0x55,
0x0a, 0xa5, 0xd8, 0xc5, 0x24, 0x21, 0x59, 0x87, 0x1d, 0x30, 0x8d, 0x21, 0x9c, 0xf3, 0x0d, 0x97,
0x02, 0xe3, 0x56, 0x42, 0xb2, 0x80, 0xd5, 0x90, 0x0e, 0xa1, 0xbb, 0x54, 0xe5, 0x07, 0xca, 0x38,
0x70, 0x84, 0x47, 0x94, 0x42, 0x9b, 0x2f, 0x16, 0x2a, 0x6e, 0x27, 0x24, 0x8b, 0x98, 0x3b, 0xa7,
0x9f, 0x04, 0x46, 0x0c, 0x05, 0xae, 0x2b, 0x73, 0xfb, 0x8e, 0xc2, 0x1b, 0x3f, 0x28, 0x2e, 0xf5,
0x12, 0x95, 0x0d, 0x80, 0xb6, 0x6c, 0xdb, 0x88, 0x6b, 0x3b, 0x60, 0x9a, 0x42, 0xbb, 0x52, 0xf8,
0xe6, 0xdc, 0xfb, 0xd7, 0x83, 0xdc, 0xa5, 0xcf, 0xbd, 0x02, 0x73, 0x1c, 0xcd, 0x20, 0xdc, 0x07,
0x36, 0x2e, 0xcb, 0xf9, 0xb5, 0x9a, 0x4e, 0x97, 0x30, 0xf4, 0x39, 0xbe, 0x67, 0xa8, 0x7d, 0xc8,
0xdf, 0x7c, 0x5a, 0xbf, 0xfb, 0x3c, 0x02, 0x30, 0x7c, 0x9d, 0xfa, 0x55, 0xfd, 0x87, 0xc8, 0xae,
0x01, 0xb5, 0x46, 0x1d, 0x93, 0x24, 0xc8, 0x22, 0x76, 0x2c, 0xd8, 0x45, 0xda, 0x69, 0x51, 0x39,
0xd1, 0x88, 0x79, 0x64, 0xbb, 0xb4, 0xe1, 0x06, 0xef, 0xb8, 0x5e, 0xb9, 0xb9, 0x22, 0x76, 0x2c,
0xa4, 0x57, 0xd0, 0xf3, 0xae, 0x9a, 0x26, 0x10, 0x70, 0x21, 0x9c, 0xf2, 0x79, 0x26, 0x4b, 0xa5,
0xf7, 0xd0, 0x6f, 0x2c, 0xbe, 0x61, 0x49, 0x4e, 0x2c, 0x33, 0x08, 0xfd, 0x67, 0xf9, 0x69, 0x40,
0x4f, 0xa7, 0x33, 0x18, 0x4c, 0x36, 0x1b, 0xab, 0x59, 0x0f, 0x59, 0xbf, 0x3b, 0x39, 0xbe, 0x3b,
0xbd, 0x39, 0xb1, 0x8d, 0x5b, 0x2e, 0x20, 0xf5, 0x9a, 0x0d, 0x86, 0x35, 0xaf, 0x4d, 0x2f, 0x67,
0x17, 0x4f, 0x6b, 0xb3, 0xda, 0xce, 0x73, 0x51, 0xbe, 0x8c, 0x8b, 0x42, 0xc8, 0xb1, 0x58, 0xf1,
0xb5, 0x2c, 0x8a, 0xb1, 0xeb, 0x9c, 0x77, 0xdd, 0x4f, 0x2e, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff,
0x7b, 0xae, 0x1c, 0x1e, 0xda, 0x02, 0x00, 0x00,
// 399 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xc1, 0x8e, 0xd3, 0x30,
0x10, 0x95, 0x9b, 0x76, 0xb3, 0x99, 0xc2, 0x1e, 0x7c, 0x58, 0x59, 0x2b, 0x56, 0x84, 0x9c, 0x72,
0x40, 0xa9, 0x44, 0xf8, 0x81, 0x56, 0x42, 0xe2, 0x86, 0x64, 0x38, 0xf5, 0x82, 0x1c, 0x77, 0x4a,
0x2b, 0xd2, 0x24, 0xd8, 0x2e, 0xa2, 0x7c, 0x00, 0x1f, 0xc2, 0x97, 0x22, 0x4f, 0x9d, 0x36, 0xa5,
0x02, 0xed, 0xad, 0xf3, 0xde, 0xcc, 0xbc, 0xe7, 0xd7, 0x09, 0x3c, 0x57, 0x5a, 0xb7, 0xfb, 0xc6,
0x15, 0x9d, 0x69, 0x5d, 0xcb, 0x27, 0xee, 0xd0, 0xa1, 0xcd, 0xbe, 0x42, 0x3c, 0x3f, 0xe2, 0xfc,
0x01, 0x6e, 0xf5, 0xde, 0x18, 0x6c, 0xf4, 0x41, 0xb0, 0x94, 0xe5, 0x13, 0x79, 0xaa, 0xb9, 0x80,
0xb8, 0x52, 0xb5, 0x6a, 0x34, 0x8a, 0x51, 0xca, 0xf2, 0x48, 0xf6, 0x25, 0xbf, 0x87, 0x9b, 0xb5,
0x69, 0x7f, 0x62, 0x23, 0x22, 0x22, 0x42, 0xc5, 0x39, 0x8c, 0xd5, 0x6a, 0x65, 0xc4, 0x38, 0x65,
0x79, 0x22, 0xe9, 0x77, 0xf6, 0x8b, 0xc1, 0x83, 0x44, 0x8d, 0xdb, 0xce, 0xbd, 0xfb, 0x81, 0x3a,
0x08, 0x7f, 0x32, 0xaa, 0xb1, 0x6b, 0x34, 0xde, 0x00, 0x7a, 0xd8, 0x8f, 0x31, 0x1a, 0x3b, 0xd5,
0x3c, 0x83, 0x71, 0x67, 0xf0, 0x3b, 0xa9, 0x4f, 0xdf, 0xdc, 0x15, 0xe4, 0xbe, 0x08, 0x1b, 0x24,
0x71, 0x3c, 0x87, 0xf8, 0x68, 0xd8, 0x91, 0x97, 0xeb, 0xb6, 0x9e, 0xce, 0xd6, 0x70, 0x1f, 0x7c,
0xfc, 0xed, 0xa1, 0xd7, 0x61, 0x4f, 0xd3, 0x19, 0xfd, 0x5f, 0xe7, 0x37, 0x03, 0x90, 0xf8, 0x6d,
0x11, 0xb2, 0x7a, 0x01, 0x89, 0xcf, 0x01, 0xad, 0x45, 0x2b, 0x58, 0x1a, 0xe5, 0x89, 0x3c, 0x03,
0x3e, 0x49, 0xff, 0x5c, 0x34, 0xb4, 0x35, 0x91, 0xa1, 0xf2, 0x53, 0xd6, 0x29, 0x87, 0xef, 0x95,
0xdd, 0xd0, 0xc3, 0x12, 0x79, 0x06, 0xf8, 0x23, 0x80, 0xb2, 0x16, 0xdd, 0x67, 0xdf, 0x1d, 0xd2,
0x4e, 0x08, 0xf1, 0x11, 0xf3, 0x57, 0xf0, 0xec, 0x48, 0xdb, 0xc3, 0xae, 0x6a, 0x6b, 0x31, 0xa1,
0x86, 0x29, 0x61, 0x1f, 0x09, 0xca, 0x5e, 0xc3, 0x6d, 0x30, 0x6e, 0x79, 0x0a, 0x91, 0xd2, 0x9a,
0xbc, 0x5d, 0x3f, 0xcb, 0x53, 0xd9, 0x07, 0x98, 0x0e, 0xfe, 0xbb, 0x81, 0x69, 0x76, 0x61, 0x3a,
0x87, 0x38, 0xdc, 0xdb, 0xbf, 0x32, 0x0a, 0x74, 0xb6, 0x84, 0xbb, 0x79, 0x5d, 0xfb, 0x9d, 0x7d,
0x4c, 0xfd, 0xe9, 0xb0, 0xf3, 0xe9, 0xf0, 0xb7, 0x17, 0xb2, 0x62, 0x44, 0x06, 0x79, 0xd8, 0x39,
0x60, 0xe4, 0xb0, 0x6d, 0xf1, 0x72, 0xf9, 0xf8, 0x65, 0xeb, 0x36, 0xfb, 0xaa, 0xd0, 0xed, 0x6e,
0x56, 0x96, 0xba, 0x99, 0xe9, 0x8d, 0xda, 0x36, 0x65, 0x39, 0xa3, 0xc9, 0xea, 0x86, 0x3e, 0x86,
0xf2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x61, 0x7c, 0x60, 0x1d, 0x03, 0x00, 0x00,
}
......@@ -38,8 +38,10 @@ message ReqBalance {
//地址列表
repeated string addresses = 1;
//执行器名称
string execer = 2;
string stateHash = 3;
string execer = 2;
string stateHash = 3;
string asset_exec = 4;
string asset_symbol = 5;
}
// Account 的列表
......
......@@ -60,6 +60,7 @@ func init() {
commands.VersionCmd(),
sendCmd,
closeCmd,
commands.AssetCmd(),
)
}
......
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