Edit me

[TOC]

tx使用方法

tx命令使用方法如下:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool tx
NAME:
   chorustool tx - commands for node  operations

USAGE:
   chorustool tx command [command options] [arguments...]

COMMANDS:
     transfer    transfer node balance from one node to another
     mortgage    exchange node balance to voting power
     redemption  exchange node voting power to balance

tx命令是用来作交易的,需要两个节点才能运行,那么我们在MAC上也运行一个节点看看。

我们直接运行两个节点,并且使用命令查看:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --backend="tcp://192.168.8.179:46657" --target="chorus" info net 
listening : true
listener : Listener(@192.168.8.179:46656)

179是MAC的地址,两边可以正常通信,说明不需要配置,它们可以自己进行发现。 那么我们在MAC上创建一个账户:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --backend="tcp://192.168.8.179:46657" --target="chorus" account gen mac
privkey: 251493F77E4C07DDFF965E0E2D08B2DEB8A62C7586550EF72D5B6126923178023D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D
pubkey: 3D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D

然后创建一笔交易:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" tx transfer --privkey="16F2018E5A1DBDC82A9D0B5870A057F5A07F0347C468226167824B9BDC14C98D386563CE3189C39850970AB7528BE165DEFF3B473F70E8BB5F7A1CDE31BD950A" --peer_pubkey="3D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D" --value="1" --nonce=11
send ok : 14f789cfd00ceebbdd7553361f570148771e6440

参数中先要写上我们的私钥,然后写上对方的公钥,接着是value,即钱数,nonce表示。 send ok返回的这个值是什么呢? 也可以给自己转账:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" tx transfer --privkey="16F2018E5A1DBDC82A9D0B5870A057F5A07F0347C468226167824B9BDC14C98D386563CE3189C39850970AB7528BE165DEFF3B473F70E8BB5F7A1CDE31BD950A" --peer_pubkey="3D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D" --value="1" --nonce=11
send ok : 0aea71da754deb299cf1675c18c1baed20780153
pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" tx transfer --privkey="16F2018E5A1DBDC82A9D0B5870A057F5A07F0347C468226167824B9BDC14C98D386563CE3189C39850970AB7528BE165DEFF3B473F70E8BB5F7A1CDE31BD950A" --peer_pubkey="3D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D" --value="1" --nonce=11
send ok : 38642b48e7e1a9d4fd2bc4a92adef9207d57dbf8
pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" tx transfer --privkey="16F2018E5A1DBDC82A9D0B5870A057F5A07F0347C468226167824B9BDC14C98D386563CE3189C39850970AB7528BE165DEFF3B473F70E8BB5F7A1CDE31BD950A" --peer_pubkey="3D04641AD64C703841C1454CCD97FA51AD72503BFD9CC6DF5871BF41B8E6650D" --value="1" --nonce=11
send ok : d7fdb79173093772c7f57d9306ef2468fb89388c

我们尝试转了四次账,可以查到未出账单:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" info num_unconfirmed_txs
num of unconfirmed txs:  4

节点重启后这部分信息就会丢失:

pct@Chandler:~/go/src/github.com/Baptist-Publication/chorus/build$ ./chorustool --target="chorus" info num_unconfirmed_txs
num of unconfirmed txs:  0

tx代码解析

客户端

tx是main中的NodeCommands类实现的:

NodeCommands = cli.Command{
   Name:     "tx",
   Usage:    "commands for node  operations",
   Category: "transaction",
   Subcommands: []cli.Command{
      {
         Name:   "transfer",
         Usage:  "transfer node balance from one node to another",
         Action: nodeAction.ChangeNodeBalance,
         Flags: []cli.Flag{
            anntoolFlags.privkey,
            anntoolFlags.peerPubkey,
            anntoolFlags.value,
            anntoolFlags.fee,
            anntoolFlags.nonce,
         },
      },

我们重点关注transfer命令,另外两个命令是投票权重的,属于共识机制部分,后面再看。 ChangeNodeBalance函数是实现tx命令的本体,它首先解析参数,然后对秘钥进行了一些运算,然后创建一个结构体并填充:

tx := &node.EcoTransferTx{}
tx.PubKey = frompubkey[:]
tx.To = topubkey[:]
tx.Amount = big.NewInt(value)
if ctx.IsSet("nonce") {
   tx.Nonce = ctx.Uint64("nonce")
} else {
   tx.Nonce, err = getNonce(chainID, frompubkey.KeyString())
}

这里这个nonce如果没有设置,是可以通过chainI’D和pubkey计算出来的。 然后根据私钥算出签名:

tx.Signature, _ = tools.TxSign(tx, &privKey)
txbytes, err := tools.TxToBytes(tx)

为签名添加一个前缀:

txbytes = types.WrapTx(node.EcoTransferTag, txbytes)
--------------------------------------------------------------------------------------
var (
   EcoTag           = []byte{'e', 'c', 'o'}
   EcoMortgageTag   = append(EcoTag, 0x01)
   EcoRedemptionTag = append(EcoTag, 0x02)
   EcoTransferTag   = append(EcoTag, 0x03)
   EcoInitAllocTag  = append(EcoTag, 0x04)
)

接着通过jsonrpc发送出去:

_, err = act.jsonRPC(chainID, txbytes)

最后输出一个merkel的hash值:

hash := merkle.SimpleHashFromBinary(txbytes)
fmt.Println("send ok :", hex.EncodeToString(hash))

这个值应该也可以在节点端通过计算账单信息得到。

服务端代码

jsonRPC和通常用到的RPC类似,传递一个参数和一个方法的名称进去就可以调用远程方法,我们在服务端需要注册一个方法,客户端调用代码如下:

func (act nodeActions) jsonRPC(chainID string, p []byte) (*types.RPCResult, error) {
   clt := rpcclient.NewClientJSONRPC(logger, commons.QueryServer)
   tmResult := new(types.RPCResult)
   _, err := clt.Call("broadcast_tx_sync", []interface{}{chainID, p}, tmResult)
   if err != nil {
      return nil, err
   }
   return tmResult, nil
}

我们在路径/home/pct/go/src/github.com/Baptist-Publication/chorus/src/chain下搜索broadcast_tx_sync这个方法可以查到一个路由表: /home/pct/go/src/github.com/Baptist-Publication/chorus/src/chain/node/routes.go

func (n *Node) rpcRoutes() map[string]*rpc.RPCFunc {
   h := newRPCHandler(n)
   return map[string]*rpc.RPCFunc{
   。。。。。。。。

      // broadcast API
      "broadcast_tx_commit": rpc.NewRPCFunc(h.BroadcastTxCommit, argsWithChainID("tx")),
      "broadcast_tx_sync":   rpc.NewRPCFunc(h.BroadcastTx, argsWithChainID("tx")),
    。。。。。。。
   }
}

这边定义了两个关于交易的回调,但是只用了下面的broadcast_tx_sync,broadcast_tx_commit这个函数没有找到调用者,应该是没有用到。 处理函数做的事情如下:

func (h *rpcHandler) BroadcastTx(chainID string, tx []byte) (agtypes.RPCResult, error) {
   org, err := h.getOrg(chainID)
   if err != nil {
      return nil, ErrInvalidChainID
   }
   if err := org.Application.CheckTx(tx); err != nil {
      return nil, err
   }
   if err := org.Angine.BroadcastTx(tx); err != nil {
      return nil, err
   }
   return &agtypes.ResultBroadcastTx{Code: 0}, nil
}
获取节点

首先是获取节点,节点都放到了Metropolis类型的池中,并用数据库保存。

检查账单

然后是检查账单,检查账单有两个实现,一个是APP中 /home/pct/go/src/github.com/Baptist-Publication/chorus/src/chain/app/remote/app.go ,一个是在节点池中: /home/pct/go/src/github.com/Baptist-Publication/chorus/src/chain/node/metropolis_checktx.go 我们重点关注后者,校验函数首先会检查账单的类型,这里就用到我们上面添加的前缀了,我们添加了eco transfer的前缀,在代码执行时就会进入下面分支:

case IsEcoTx(bs):
   if err := met.checkEcoTx(bs); err != nil {
      return err
   }
}

这个分支就会对发起的交易账单进行处理,但是从代码看只是进行了验证,并没有打包和计数的部分。

广播账单

广播账单部分直接调用了共识模块代码:

func (ang *Angine) BroadcastTx(tx []byte) error {
   return ang.mempool.CheckTx(tx)
}

这个函数先检查是否账单存在,如果存在不予处理,否则就将账单放入内存池:

mem.txs.PushBack(memTx)

内存池中的账单什么时候被使用,我们在下一章节描述。

Tags: