交易
TRON 上的交易结构、生命周期及 TAPOS 重放保护机制。
交易是账户发出的、带有密码学签名的链上指令。它能转账 TRX、创建账户、部署或调用智能合约、质押 TRX 获取资源,也能提交治理投票和委员会提案。
一笔交易要真正生效,需要先在本地构建并签名,再广播给全节点校验,由轮值超级代表(SR)打包进区块,最后等待所在区块固化。本文说明交易的结构、生命周期,以及 TAPOS 如何防止交易在分叉链上被重放。
前置阅读
交易结构
TRON 交易主要由两个顶层字段构成:raw_data 和 signature。
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_bytes | TAPOS 防重放字段,取自参考区块高度的字节区间 [6, 8),即包含第 6 字节、不包含第 8 字节。 |
raw_data.ref_block_hash | TAPOS 防重放字段,取自参考区块 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 中包含两个时间字段:expiration 和 timestamp。开发和调试时应重点关注 expiration,因为只有它决定交易还能否被打包。
timestamp 只是交易在客户端或节点构建时的本地记录,不参与过期判断,也不是交易上链后的权威确认时间。
raw_data.expiration:交易过期时间
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:构建时间记录
raw_data.timestamp:构建时间记录timestamp 记录节点接收请求并构建未签名交易时的本地系统时钟。使用时需要注意:
- 它不参与
expiration截止时间的计算。 - 它不决定交易是否能被打包进区块。
- 交易真正的链上时间由打包该交易的区块时间戳决定。
在网络短暂拥堵或交易延迟广播时,timestamp 与 expiration 的差值可能大于 60 秒。客户端逻辑不应硬编码假设 expiration - timestamp == 60,000。
如果需要在本地隔离环境中离线构建交易,参见构建离线交易。
交易类型
TRON 在协议层使用不同系统合约体(Contract Type)表示不同交易类型。每种类型都有对应的数据结构和节点构建 API。
| 交易类型 | 协议系统合约体 | 推荐的 HTTP 构建 API |
|---|---|---|
| TRX 转账 | TransferContract | wallet/createtransaction |
| TRC-10 转账 | TransferAssetContract | wallet/transferasset |
| 智能合约部署 | CreateSmartContract | wallet/deploycontract |
| 智能合约调用 | TriggerSmartContract | wallet/triggersmartcontract |
| 质押 TRX 获取资源 | FreezeBalanceV2Contract | wallet/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)。
交易生命周期
一笔交易从本地构建到链上确认,通常经历四个阶段:
- 创建与签名:客户端构建
raw_data,使用发送方私钥签名,并把签名写入signature列表。 - 广播与校验:客户端通过 RPC/HTTP 接口把签名交易发送给全节点。全节点校验签名、资源和基础参数,通过后将交易放入 Mempool。
- 打包进区块:轮值 SR 在自己的出块槽位从 Mempool 中取出交易,执行状态转换,并把交易写入新区块。
- 区块固化:后续 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 | 获取某笔交易的执行回执,包括执行状态、资源消耗和日志 |
相关资源
- 内部交易 — 智能合约执行期间触发的 TRX 或 TRC-10 转移记录
- 签名校验 — 多签授权、签名恢复与地址验证
- 构建离线交易 — 在隔离环境中构建并签名交易
- TRX 转账 recipe — 使用 TronWeb 发送首笔交易
Updated 11 days ago