交易

TRON 上的交易结构、生命周期及 TAPOS 重放保护机制。

交易是账户发出的、带有密码学签名的链上指令。它能转账 TRX、创建账户、部署或调用智能合约、质押 TRX 获取资源,也能提交治理投票和委员会提案。

一笔交易要真正生效,需要先在本地构建并签名,再广播给全节点校验,由轮值超级代表(SR)打包进区块,最后等待所在区块固化。本文说明交易的结构、生命周期,以及 TAPOS 如何防止交易在分叉链上被重放。

📘

前置阅读


交易结构

TRON 交易主要由两个顶层字段构成:raw_datasignature

  • raw_data:被签名的交易载荷,包含系统合约体、TAPOS 参考区块、过期时间、备注和 fee_limit 等字段。
  • signature:对 raw_data 的授权签名列表。普通交易通常只有一个签名;多签账户可能包含多个签名。
{
  "raw_data": {
    "contract": [
      /* 合约对象 — 参见下方的交易类型 */
    ],
    "ref_block_bytes": "c145",
    "ref_block_hash": "c56bd8a3b3341d9d",
    "expiration": 1646796363000,
    "data": "74657374",
    "timestamp": 1646796304152,
    "fee_limit": 10000000000
  },
  "signature": [
    "47b1f77b...8b0a1800"
  ]
}

核心字段如下:

字段说明
raw_data.contract交易体。由于历史兼容性设计,该列表目前只允许包含一个系统合约体。不同交易类型使用不同合约体;完整列表见 ContractType
raw_data.ref_block_bytesTAPOS 防重放字段,取自参考区块高度的字节区间 [6, 8),即包含第 6 字节、不包含第 8 字节。
raw_data.ref_block_hashTAPOS 防重放字段,取自参考区块 ID 的字节区间 [8, 16),即包含第 8 字节、不包含第 16 字节。
raw_data.expiration交易过期时间,使用 Unix 毫秒时间戳。超过该时间后,节点会丢弃交易,不再打包上链。通过节点 API 构建时通常默认为 60 秒,最大可配置为 24 小时。
raw_data.data可选备注信息,通常使用十六进制字节流表示。
raw_data.timestamp交易创建时的本地记录时间戳。该字段不决定交易有效性;交易在链上的权威时间来自打包它的区块时间戳。
raw_data.fee_limit写入类智能合约交易的调用方侧 Energy 预算上限,单位为 sun。设置规范参见 FeeLimit 与能量成本
signature发送方对 raw_data 的 ECDSA 签名数据。多签交易会在该列表中包含多个签名。

过期时间与时间戳

raw_data 中包含两个时间字段:expirationtimestamp。开发和调试时应重点关注 expiration,因为只有它决定交易还能否被打包。

timestamp 只是交易在客户端或节点构建时的本地记录,不参与过期判断,也不是交易上链后的权威确认时间。

raw_data.expiration:交易过期时间

expiration 是交易能否被打包的硬性截止时间,单位为 Unix 毫秒时间戳。超过该时间后,节点会在校验或打包阶段丢弃交易。

其时间边界如下:

  • 默认过期值:最新区块时间戳之后 60 秒。
  • 最大截止上限:最新区块时间戳之后 24 小时。

全节点构建交易时,按以下逻辑计算过期时间:

expiration = headBlockTimestamp + trx.expiration.timeInMilliseconds

计算基准是当前规范链的区块头时间戳,不是节点本地系统时钟。因此,使用默认配置构建的交易,大约有 60 秒可被打包。

节点配置文件 config.conf 中的 trx.expiration.timeInMilliseconds 用于控制默认过期时间:

# trx.expiration.timeInMilliseconds = 60000

默认配置中该行处于注释状态,节点构建未签名交易时使用内置的 60 秒默认值。

调整过期时间

调整范围执行路径与方式
节点全局调整停止节点,在 config.conf 中取消注释 trx.expiration.timeInMilliseconds 并设置目标值(1 ms – 86,400,000 ms,即 24 小时),然后重启节点。重启后,该节点构建的交易会使用新的默认过期时间。
单笔交易微调使用 SDK 为单笔交易覆盖过期时间。例如 TronWeb 提供 extendExpiration 方法。即使通过 SDK 延长,广播时仍受 24 小时上限约束。

多签交易经常需要延长过期时间:交易要在多个签名人之间流转并收集签名,通常无法在默认 60 秒内完成广播。

raw_data.timestamp:构建时间记录

timestamp 记录节点接收请求并构建未签名交易时的本地系统时钟。使用时需要注意:

  • 它不参与 expiration 截止时间的计算。
  • 它不决定交易是否能被打包进区块。
  • 交易真正的链上时间由打包该交易的区块时间戳决定。

在网络短暂拥堵或交易延迟广播时,timestampexpiration 的差值可能大于 60 秒。客户端逻辑不应硬编码假设 expiration - timestamp == 60,000

如果需要在本地隔离环境中离线构建交易,参见构建离线交易


交易类型

TRON 在协议层使用不同系统合约体(Contract Type)表示不同交易类型。每种类型都有对应的数据结构和节点构建 API。

交易类型协议系统合约体推荐的 HTTP 构建 API
TRX 转账TransferContractwallet/createtransaction
TRC-10 转账TransferAssetContractwallet/transferasset
智能合约部署CreateSmartContractwallet/deploycontract
智能合约调用TriggerSmartContractwallet/triggersmartcontract
质押 TRX 获取资源FreezeBalanceV2Contractwallet/freezebalancev2

完整合约类型与 API 映射参见系统合约类型

⚠️

注意

一笔交易按 Protobuf 序列化后的总字节数不得超过 TRANSACTION_MAX_BYTE_SIZE(当前为 512,000 字节,即 500 KB;请勿与区块大小上限 BLOCK_SIZE = 2,000,000 字节混淆)。超过上限时,网络会抛出 TooBigTransactionException 并拒绝交易。大型合约部署或包含大量多签签名的交易更容易触发该限制;此外,创建账户类交易还有更紧的 10,000 字节上限(CREATE_ACCOUNT_TRANSACTION_MAX_BYTE_SIZE)。


交易生命周期

一笔交易从本地构建到链上确认,通常经历四个阶段:

  1. 创建与签名:客户端构建 raw_data,使用发送方私钥签名,并把签名写入 signature 列表。
  2. 广播与校验:客户端通过 RPC/HTTP 接口把签名交易发送给全节点。全节点校验签名、资源和基础参数,通过后将交易放入 Mempool。
  3. 打包进区块:轮值 SR 在自己的出块槽位从 Mempool 中取出交易,执行状态转换,并把交易写入新区块。
  4. 区块固化:后续 SR 继续在该区块之后出块。当至少 19 位不同活跃 SR 各自最近成功出块的区块编号(latestBlockNum)达到或超过该区块编号后,该区块进入已固化状态。主网通常约 1 分钟完成固化。

关于区块固化机制,参见共识与 DPoS — 区块固化


TAPOS

TAPOS(Transaction As Proof Of Stake,交易即权益证明)是 TRON 的重放保护机制。每笔交易都会引用一个近期区块;如果网络发生分叉,而该参考区块不在最终规范链上,交易会失效,无法在另一条分叉链上被重放。

交易通过 raw_data 中的两个字段声明参考区块:

字段名称提取来源物理长度
ref_block_bytes参考区块编号(block number)的字节区间 [6, 8),即低 2 字节2 字节
ref_block_hash参考区块 ID 的字节区间 [8, 16),即低 8 字节8 字节

常规开发中,SDK 通常会把参考区块设为最新固化区块,让交易锚定到已经确认的链段。

由于 ref_block_bytes 只有 2 字节,最多只能覆盖 65,536 个区块编号。节点校验交易时,会在本地近期区块窗口(RecentBlockStore)中查找参考区块;如果参考区块已被分叉淘汰(不在当前规范主链上),或确实因等待过久超出 65,536 个区块窗口,节点会抛出 TaposException 并拒绝交易。

手动构建交易时,应选择近期固化区块作为参考区块,并在交易 expiration 过期前完成广播。离线填充这些字段的方法参见构建离线交易


创建与发送交易示例

实际开发通常使用 TronWeb 等 SDK 构建、签名并广播交易。下面是一笔 TRX 转账示例:

const unsignedTxn = await tronWeb.transactionBuilder.sendTrx(
  "TVDGpn4h...wTppnkr",  // 接收方地址
  100,                   // 转账金额,单位为 sun (100 sun = 0.0001 TRX)
  "TNPeeaaF...8G1NYqeL"  // 发送方地址
);
const signedTxn = await tronWeb.trx.sign(unsignedTxn);
const result = await tronWeb.trx.sendRawTransaction(signedTxn);

成功广播后,result 会返回节点接收结果。后续可使用返回的 Transaction ID 查询交易内容和执行回执。


API

API说明
wallet/createtransaction构建未签名的 TRX 转账交易
wallet/broadcasttransaction广播已签名交易
wallet/gettransactionbyid通过交易 ID 查询某笔交易的详细内容
wallet/gettransactioninfobyid获取某笔交易的执行回执,包括执行状态、资源消耗和日志

相关资源