交易签名与广播——API 工作流

TRON 中所有修改链上状态的操作都遵循三步工作流:构建未签名交易、在客户端用发送方私钥签名、广播已签名交易。本页介绍交易完整生命周期,附操作示例与常见开发陷阱。

TRON 中所有改变链上状态的操作(转账、智能合约交互、质押、投票等)都遵循同一套交易流程:

  • 构建(Create):生成包含业务操作和链上时序信息的未签名交易。
  • 签名(Sign):在客户端用发送方私钥对交易签名。
  • 广播(Broadcast):把已签名交易发送到 TRON 网络,等待超级代表(SR)打包。

查询余额、读取合约状态、获取区块信息等只读操作不改变链上状态,不需要签名和广播。下面拆解写入交易的完整工作流:

📘

前置阅读


三步工作流

构建未签名交易有多种方式:调用节点 API、用各语言 SDK、用本地加密工具包。下面以调用节点 API 为例:

flowchart LR
    %% 全局样式定义
    classDef boxStyle fill:#fff,stroke:#2b7a78,stroke-width:1px,color:#333;
    classDef textStyle fill:none,stroke:none,color:#666,font-size:13px;

    subgraph Step1 ["1. 构建"]
        direction TB
        A1["通过 POST 方法<br>调用节点 API<br>获取未签名的 raw_data"]
        A2["⬇ 未签名交易"]:::textStyle
        A1 ~~~ A2
    end
    
    subgraph Step2 ["2. 签名"]
        direction TB
        B1["SHA-256(raw_data)<br>+ secp256k1 签名"]
        B2["⬇ 已签名交易"]:::textStyle
        B1 ~~~ B2
    end
    
    subgraph Step3 ["3. 广播"]
        direction TB
        C1["将已签名交易 POST 到节点<br>/wallet/broadcasttransaction"]
        C2["⬇ txid"]:::textStyle
        C1 ~~~ C2
    end

    Step1 --> Step2 --> Step3
    
    %% 应用样式
    class A1,B1,C1 boxStyle;
📘

注意

交易签名必须在客户端本地执行。私钥不应离开本地设备,TRON 节点和 TronGrid 都不需要接触私钥。



第 1 步——构建未签名交易

TRON 节点为不同交易类型提供对应的构建 Endpoint。客户端提交描述操作的 JSON 请求,节点返回完整但未签名的交易对象。

业务操作构建 Endpoint
转账 TRX/wallet/createtransaction
转账 TRC-10 Token/wallet/transferasset
调用智能合约(包含 TRC-20 转账)/wallet/triggersmartcontract
质押 TRX(Stake 2.0)/wallet/freezebalancev2
为超级代表(SR)投票/wallet/votewitnessaccount
部署智能合约/wallet/deploycontract

返回的 JSON 有三个关键顶层字段:

  • txIDraw_dataSHA-256 哈希,即交易哈希,也是密码学签名的对象。
  • raw_data:结构化的交易体(包含交易类型、参数、TAPOS 引用块及过期时间等)。
  • raw_data_hexraw_data 经 Protobuf 序列化后的十六进制字符串。

节点还会在交易体里自动填充两个相互独立的时序字段:

  • TAPOS 引用ref_block_bytesref_block_hash):把交易锚定到近期区块,防止跨分叉重放。被引用区块如果因链重组或分叉脱离规范链,交易在打包时会因 TAPOS 校验失败被拒绝。
  • 过期时间expiration):交易的独立截止时间戳,默认有效期约 60 秒,在构建交易时确定。如果交易由节点构建(如 /wallet/createtransaction 等接口),默认值取自该节点 config.conf 中的 trx.expiration.timeInMilliseconds;如果使用 SDK(TronWeb、Trident、GoTron 等)在客户端构建,expiration 由 SDK 设置,多数 SDK 支持按交易覆盖。交易若未在截止前打包进区块,会被网络判定为过期;该截止时间相对链头区块的上限约为 24 小时。
⚠️

重要

在构建交易后的 50 秒内完成签名并广播,否则交易很可能因超时过期被网络拒绝。


第 2 步——对交易进行签名

交易签名在客户端本地完成,通常包含三个子步骤:

  1. 计算 raw_dataSHA-256 哈希,结果与 txID 相同。
  2. 用发送方的 Secp256k1 私钥对该哈希签名。
  3. 把生成的 65 字节签名追加到交易对象的 signature 数组(transaction.signature[])。

主流 SDK 把这三个子步骤封装为单一方法。以 TronWeb 为例,直接调用签名方法即可完成签名:

SDK签名调用方法
TronWeb (JavaScript)tronWeb.trx.sign(unsignedTx, privateKey)
Trident (Java)KeyPair.signTransaction(unsignedTx)
gotron-sdk (Go)signedTx, err := tx.Sign(privateKey)

在无法直接用主流 SDK 的环境(如硬件安全模块 HSM、硬件钱包),可从 raw_data_hex 自行计算交易哈希,用标准 Secp256k1 密码学库签名。签名原语的协议底层细节,参见交易数据结构说明

多重签名交易遵循相同流程,区别在于:所有共同签名者针对同一个 txID 分别签名,广播前把全部签名收集进 signature 数组。详情参见账户权限管理


第 3 步——广播已签名交易

把已签名的交易对象 POST/wallet/broadcasttransaction

POST /wallet/broadcasttransaction
Content-Type: application/json

{
  "txID": "...",
  "raw_data": { ... },
  "raw_data_hex": "...",
  "signature": ["..."]
}

广播成功后,接口会返回如下响应:

{ "result": true, "txid": "f947f1283f7b43c111eba662..." }

交易进入 TRON 的交易内存池(Mempool)后,等待超级代表(SR)打包进区块。要确认交易是否执行成功并上链,轮询 /walletsolidity/gettransactioninfobyid,直到交易出现在已固化区块中。

⚠️

注意

广播成功不等于交易执行成功。广播成功只代表交易被节点内存池接收;SR 打包执行时仍可能失败,例如智能合约触发 revert,或带宽、能量不足导致执行中断。需要最终状态时,必须查询已固化区块中的 receipt,不要把广播接口的成功返回当作最终状态。排障路径参见广播与 RPC 错误


第 4 步——确认交易结果

广播之后,交易还会经过接收、打包、执行和固化几个阶段。不同 API 返回的是不同阶段的数据,不应混用。

阶段推荐接口返回语义下一步
构建未签名交易CreateTransactionTriggerSmartContract 等构建接口返回 raw_dataraw_data_hextxID,交易尚未签名、尚未进入网络本地签名
收集签名权重GetSignWeight返回当前签名权重是否达到目标 permission 的 threshold达到 threshold 后广播
广播已签名交易BroadcastTransactionBroadcastHexresult: true 只表示广播节点接受交易并放入本地 mempool继续查询交易状态
查询交易本体GetTransactionById返回交易原始内容,包括 raw_data、签名和合约调用参数;不包含执行收据需要执行结果时查询 receipt
查询执行收据GetTransactionInfoById返回手续费、Energy 消耗、合约执行状态、Event 和内部交易等 receipt 信息判断执行成功或失败
查询已固化结果GetTransactionInfoById (SolidityNode)仅返回已固化区块中的 receipt需要最终状态时使用

如果业务需要最终结果,应以 SolidityNode 的 receipt 查询结果作为确认依据。查询不到 receipt 时,不要立即判定失败;应等待交易过期窗口结束,并结合区块扫描或后续查询确认交易是否上链。

对普通 TRX / TRC-10 顶层转账,交易本体通常已经包含收款地址和金额;对 TriggerSmartContract,必须读取 receipt 才能判断合约实际执行结果。TRC-20 转账应解析 receipt 中的 Transfer(address,address,uint256) Event;合约派生的 TRX / TRC-10 转账应解析 internal_transactions[]


实际示例:质押 TRX

下面是通过 Stake 2.0 质押 TRX 获取能量的完整交易生命周期示例:

1. 构建交易

POST /wallet/freezebalancev2
Content-Type: application/json

{
  "owner_address": "41e552f6487585c2b58bc2c9bb4492bc1f17132cd0",
  "frozen_balance": 1000000,
  "resource": "ENERGY"
}

接口响应如下:

{
  "visible": false,
  "txID": "f947f1283f7b43c111eba662ab5d27b31dc906b77fdb0b3e52ee626335b21108",
  "raw_data": {
    "contract": [{
      "parameter": {
        "value": {
          "resource": "ENERGY",
          "frozen_balance": 1000000,
          "owner_address": "41e552f6487585c2b58bc2c9bb4492bc1f17132cd0"
        },
        "type_url": "type.googleapis.com/protocol.FreezeBalanceV2Contract"
      },
      "type": "FreezeBalanceV2Contract"
    }],
    "ref_block_bytes": "a59c",
    "ref_block_hash": "aef359052c4aa176",
    "expiration": 1681117668000,
    "timestamp": 1681117610802
  },
  "raw_data_hex": "0a02a59c2208aef359052c4aa17640a0c5afd3f6305a59083612550a34..."
}

2. 签名交易

在本地用客户端 SDK 对上一步返回的交易对象签名:

const signedTx = await tronWeb.trx.sign(unsignedTx, privateKey);
// 此时,signedTx 对象的 signature 数组中已填入签名数据:signature[0]

3. 广播交易

把签名后的完整交易对象发送到网络:

POST /wallet/broadcasttransaction
Content-Type: application/json

{ ...signedTx }

接口响应如下:

{ "result": true, "txid": "f947f1283f7b43c111eba662ab5d27b31dc906b77fdb0b3e52ee626335b21108" }

这笔质押 1 TRX(等于 1,000,000 sun)的交易已提交至交易内存池。交易被超级代表打包入账并固化后,质押额度和对应的能量可通过 /wallet/getaccountresource 确认。


常见陷阱

  • TAPOS 引用失效ref_block_bytesref_block_hash 必须指向规范主链上的近期区块。从生成交易到打包期间若发生链重组(如微分叉)使被引用区块失效,交易打包时会因 TAPOS_ERROR 被拒绝。解决:构建交易时尽量引用已固化区块,而非链头区块。
  • 交易超时过期:与 TAPOS 独立,交易体的 expiration 字段(默认约 60 秒)限定交易打包前的生存周期。节点构建的交易默认值取自节点 config.conf 中的 trx.expiration.timeInMilliseconds;客户端 SDK 构建的交易由 SDK 设置,多数 SDK 支持按交易覆盖。expiration 相对链头区块的上限约为 24 小时。构建到广播之间延迟过长会触发过期被拒。解决:构建后立即签名并广播。
  • visible 标志不一致visible=true 时请求参数和响应中的地址为 Base58 格式(以 T 开头);visible=false 时为十六进制 Hex 格式(以 41 开头)。构建交易和本地签名两环节的 visible 格式不一致,会导致签名校验失败。详情参见地址与数据编码
  • 对错误的哈希算法签名:TRON 签名的哈希对象是 raw_dataSHA-256 的结果,不是以太坊常用的 Keccak-256,也不是对 raw_data_hex 字符串本身做哈希(尽管两者算出的字节序列有逻辑关联)。推荐:始终用 SDK 封装的签名 API,避免自行实现哈希与签名。
  • 交易过期后复用旧 txID:交易一旦过期,该 txID 即失效。从头重新构建并签名一笔新交易,不要对过期的 raw_data 重新签名再广播。
  • 把广播成功当作执行成功:接口返回 {"result": true} 只表明交易通过基础校验、进入内存池,不代表已被 SR 打包进区块并执行成功。智能合约抛 revert、账户资源(能量 / 带宽)不足等执行失败的情况,广播阶段仍返回成功。务必调用 /walletsolidity/gettransactioninfobyid 检查交易的最终收据(Receipt)和执行状态。
  • 重复广播同一笔交易:因网络波动重试时,重复广播完全相同的已签名交易通常返回相同 txid。TRON 会对已入池交易自动去重,所以传输不确定时重试广播是安全的。
  • 在服务端托管或使用用户私钥签名:不要在中心化服务器上代持或使用用户私钥。私钥应保留在客户端、钱包插件(如 TronLink、WalletAdapter)或硬件加密模块(HSM)中。

相关资源

  • 确认语义 —— 区分广播成功、交易上链、Receipt 执行结果和已固化状态
  • 地址与数据编码 —— 详细了解 Base58 与 Hex 地址转换、visible 参数的影响以及 ABI 编码规范
  • 广播与 RPC 错误 —— 排查 TAPOS_ERRORTRANSACTION_EXPIRATION_ERROR、资源不足和广播失败
  • 账户管理 —— TRON 地址格式、生成算法与密钥派生机制
  • 交易结构解析 —— 深入剖析 TRON 交易的原生数据结构与其完整生命周期
  • 账户多重签名管理 —— 多重签名配置及多个签名收集的 API 工作流