交易是来自账户私钥签名后的指令,账户可以通过发起交易来更新TRON网络的状态,最简单的交易是将TRX从一个账户转移到另一个账户。

改变TRON网络状态的交易需要广播到整个网络,任何一个节点都可以广播交易请求。超级节点收到交易后执行交易,并将其打包进区块,然后将区块广播到网络的其他节点。

交易只有被超级节点打包进区块后,并且区块被确认了,交易才最终被确认。

一个交易的格式如下:

{
    "raw_data": 
    {
        "contract": [{<-->}],
        "ref_block_bytes": "c145",
        "ref_block_hash": "c56bd8a3b3341d9d",
        "expiration": 1646796363000,
        "timestamp": 1646796304152,
        "fee_limit":10000000000
    },
    "signature":["47b1f77b3e30cfbbfa41d795dd34475865240617dd1c5a7bad526f5fd89e52cd057c80b665cc2431efab53520e2b1b92a0425033baee915df858ca1c588b0a1800" ] 
}

交易主要包括以下字段:

  • raw_data.contract - 交易的主体内容,contract是一个列表,但目前只用到一个元素,不同类型的交易,contract内容不一样,如果是TRX转账类型交易,contract会包含转账金额,接收人等信息,TRON支持多种类型的contract,具体参考下面交易的类型章节。
  • raw_data.ref_block_bytes - 交易引用块的高度,使用了引用块高度的第6到8(不包含)字节,共2字节。引用块用于TRON TAPOS机制,可以防止分叉链交易重放, 引用块一般选择最新固化的区块。
  • raw_data.ref_block_hash - 交易引用块的hash,使用了引用块hash的第8到16(不包含)字节,共8字节,引用块用于TRON TAPOS机制,可以防止分叉链交易重放, 引用块一般选择最新固化的区块。
  • raw_data.expiration - 交易过期时间,超过这个时间,交易将不再被打包。如果用户调用java-tron节点的API创建交易体,节点会自动设置过期时间,一般是在创建交易时节点最高区块的时间戳基础上再增加60秒,可以在节点的配置文件中修改过期时间间隔,最大不能超过24小时。
  • raw_data.timestamp - 交易时间戳,设置交易创建时间。
  • raw_data.fee_limit - 执行智能合约交易所允许消耗的最大能量费用,只有智能合约部署和调用交易需要设置,其他交易无需设置。
  • signature - 发送方对交易的签名,交易有了签名就可以证明该交易来自发送方,并非恶意者所伪造。

交易的类型

TRON网络支持多种不同类型的交易,比如TRX转账交易、TRC10转账交易、创建智能合约交易、触发智能合约交易、质押TRX交易等等。

创建不同类型的交易,需要调用不同的API接口, 例如部署合约交易的类型是CreateSmartContract,需要调用wallet/deploycontractAPI来创建交易,质押TRX获取资源交易的类型是FreezeBalanceContract,需要调用 wallet/freezebalanceAPI来创建交易:

$ curl -X POST https://api.shasta.trongrid.io/wallet/freezebalance -d '{"owner_address":"TCrkRWJuHP4VgQF3xwLNBAjVVXvxRRGpbA","frozen_balance": 2100000,"frozen_duration": 3,"resource" : "BANDWIDTH","visible":true}' | jq
{
  "visible": true,
  "txID": "e54bab34838a59e85d5684e46a2e8e512cd11dfb07b35a9728adeaf3d2666fa6",
  "raw_data": {
    "contract": [
      {
        "parameter": {
          "value": {
            "frozen_duration": 3,
            "frozen_balance": 2100000,
            "owner_address": "TCrkRWJuHP4VgQF3xwLNBAjVVXvxRRGpbA"
          },
          "type_url": "type.googleapis.com/protocol.FreezeBalanceContract"
        },
        "type": "FreezeBalanceContract"
      }
    ],
    "ref_block_bytes": "7139",
    "ref_block_hash": "d291dee525445093",
    "expiration": 1646902209000,
    "timestamp": 1646902151591
  },
  "raw_data_hex": "0a0271392208d291dee52544509340e8d39598f72f5a58080b12540a32747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e467265657a6542616c616e6365436f6e7472616374121e0a15411fafb1e96dfe4f609e2259bfaf8c77b60c535b9310a0968001180370a7939298f72f"
}

更多交易类型请参考:TRON网络交易类型,更多的HTTP API请参考:HTTP API

交易的生命周期

交易在其生命周期中会依次经历以下几个阶段:

  1. 交易被创建及签名
  2. 交易被广播到TRON网络,节点对其验证并执行通过后,将其放入交易缓存池中
  3. 产块节点从交易缓存池中取出交易,将其打包进新区块,然后将区块广播到TRON网络
  4. 交易将被“确认”。一个交易是否被确认取决于这个交易所在的区块是否被确认,TRON的区块确认机制是某个区块产出后,19个不同的产块节点基于这个区块产出了后续区块,那么这个区块视为被确认。

创建交易

对于创建交易,有很多的库和工具可以选择,下面以tronweb创建一个TRX转账的交易为例说明如何创建交易:

const unsignedTxn = await tronWeb.transactionBuilder.sendTrx("TVDGpn4hCSzJ5nkHPLetk8KQBtwaTppnkr", 100, "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL");
 >{
    "visible": false,
    "txID": "9f62a65d0616c749643c4e2620b7877efd0f04dd5b2b4cd14004570d39858d7e",
    "raw_data": {
        "contract": [
            {
                "parameter": {
                    "value": {
                        "amount": 100,
                        "owner_address": "418840e6c55b9ada326d211d818c34a994aeced808",
                        "to_address": "41d3136787e667d1e055d2cd5db4b5f6c880563049"
                    },
                    "type_url": "type.googleapis.com/protocol.TransferContract"
                },
                "type": "TransferContract"
            }
        ],
        "ref_block_bytes": "0add",
        "ref_block_hash": "6c2763abadf9ed29",
        "expiration": 1581308685000,
        "timestamp": 1581308626092
    },
    "raw_data_hex": "0a020add22086c2763abadf9ed2940c8d5deea822e5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a15418840e6c55b9ada326d211d818c34a994aeced808121541d3136787e667d1e055d2cd5db4b5f6c880563049186470ac89dbea822e"
}

签名交易

发送交易前,必须由发送方使用私钥对其进行签名。

交易签名过程

  1. 计算交易的哈希
  2. 使用发送者账户的私钥对该交易哈希进行签名
  3. 将生成的签名结果添加到交易对象中

大多数的SDK都实现了上述的交易签名流程,并封装成接口供开发者调用,以tronweb为例,用户可以直接调用sign方法完成交易的签名。

tronweb签名示例
使用tronweb对上面创建的交易进行签名:

const signedTxn = await tronWeb.trx.sign(unsignedTxn, privateKey);
>{
    "visible": false,
    "txID":"9f62a65d0616c749643c4e2620b7877efd0f04dd5b2b4cd14004570d39858d7e",
    "raw_data":
    {
        "contract": [{<-->}],
        "ref_block_bytes": "0add",
        "ref_block_hash": "6c2763abadf9ed29",
        "expiration": 1581308685000,
        "timestamp": 1581308626092 
    },
    "raw_data_hex": "0a020add22086c2763abadf9ed2940c8d5deea822e5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a15418840e6c55b9ada326d211d818c34a994aeced808121541d3136787e667d1e055d2cd5db4b5f6c880563049186470ac89dbea822e",
    "signature": [ "47b1f77b3e30cfbbfa41d795dd34475865240617dd1c5a7bad526f5fd89e52cd057c80b665cc2431efab53520e2b1b92a0425033baee915df858ca1c588b0a1800" ] 
 }

广播交易

用户将交易发送到节点后,节点会尝试在其本地执行并验证该交易, 有效的交易将被继续广播到其他节点,无效的交易被该节点丢弃,这将有效防止垃圾交易在网络中无效广播,占用网络资源。

使用tronweb对已签名的交易进行广播:

const receipt = await tronWeb.trx.sendRawTransaction(signedTxn);
>{ 
    "result": true,
    "transaction":
    { 
        "visible": false,
        "txID": "9f62a65d0616c749643c4e2620b7877efd0f04dd5b2b4cd14004570d39858d7e",
        "raw_data":
        {
            "contract": [{<-->}],
            "ref_block_bytes": "0add",
            "ref_block_hash": "6c2763abadf9ed29",
            "expiration": 1581308685000,
            "timestamp": 1581308626092 
        },
        "raw_data_hex": "0a020add22086c2763abadf9ed2940c8d5deea822e5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a15418840e6c55b9ada326d211d818c34a994aeced808121541d3136787e667d1e055d2cd5db4b5f6c880563049186470ac89dbea822e",
        "signature": [ "47b1f77b3e30cfbbfa41d795dd34475865240617dd1c5a7bad526f5fd89e52cd057c80b665cc2431efab53520e2b1b92a0425033baee915df858ca1c588b0a1800" ] 
    } 
 }

交易确认

一个交易是否被确认取决于这个交易所在的区块是否被确认,TRON的区块确认机制是某个区块产出后, 19个不同的产块节点基于这个区块产出了后续区块,那么这个区块视为被确认。

java-tron节点提供了/walletsolidty/* 接口,方便用户查询被确认的交易,/walletsolidty/*/wallet/*的区别是/wallet/*查询到的交易是说明该交易已经上链但不一定被确认,/walletsolidty/*查询到的交易说明该交易已经上链同时也已经被固化,也就是交易被确认。

不同的交易的确认方法有所不同:

  • 系统合约交易
    创建智能合约类型和触发智能合约类型以外的所有类型的交易,都属于系统合约交易。系统合约交易确认方法:
    • 通过/walletsolidity/gettransactioninfobyid或者/walletsolidity/gettransactionbyidAPI能够查询到交易,即为确认
  • 智能合约交易
    创建智能合约和触发智能合约交易,因为交易需要在虚拟机中执行,某些交易执行过程中会抛出一些异常,这些交易能够上链,但不代表交易是执行成功的, 有两种确认智能合约交易是否执行成功的方法:
    • 通过/walletsolidity/gettransactioninfobyid API查询到的返回值中receipt.result等于success
    • 通过/walletsolidity/gettransactionbyidAPI查询到的返回值中transaction.ret.contractRetsuccess
  • 内部交易
    内部交易是合约中向其他普通地址或者合约地址转账的交易,首先通过/walletsolidity/gettransactioninfobyidAPI查询到内部交易记录,使用内部交易记录中的rejected字段来判断该内部交易是否被确认,但对于HTTP和GRPC接口有所不同:
    • HTTP接口:成功的内部交易,默认不返回 rejected 字段, 失败的内部交易,rejected等于true。
    • GRPC接口:成功的内部交易,rejected字段等于false,表示这笔内部交易没有被舍弃, 失败的内部交易,rejected等于true。