离线构建交易
如何在未连接节点的机器上构建并签名 TRON 交易,然后单独广播。
冷钱包、多方签名仪式和隔离密钥托管场景中,交易通常需要在无网络连接的机器上构建并签名,再把已签名交易转移到联网机器广播。
离线构建时,没有全节点替你补齐 TAPOS、过期时间等字段;这些字段必须由客户端提前准备并写入交易体。本文说明需要手动填充的字段,以及使用 TronWeb 的离线构建模式。
前置阅读
离线环境需要设置哪些字段
联网调用 TronWeb 构建方法时,全节点会自动填充部分字段。离线构建时,需要自行设置:
| 字段 | 作用 | 离线填充方式 |
|---|---|---|
raw_data.ref_block_bytes | TAPOS:锚定近期区块高度 | 从预先获取的参考区块中截取 |
raw_data.ref_block_hash | TAPOS:锚定同一参考区块 ID | 从同一参考区块中截取 |
raw_data.expiration | 交易过期时间 | 通常设置为 now + N,最长 24 小时 |
raw_data.timestamp | 交易构建时间 | 设置为离线机器当前 Unix 毫秒时间戳 |
raw_data.fee_limit | 写入类智能合约交易的调用方侧 Energy 预算上限 | 调用或部署智能合约时需要设置,单位为 sun |
参考区块应位于最近 65,536 个区块窗口内。常规做法是使用最新固化区块。
双机工作流
1. 联网机器:获取参考区块
先在联网机器上通过节点 API 获取区块信息,再把结果导出给离线机器:
const block = await tronWeb.trx.getCurrentBlock();
const refBlock = {
blockID: block.blockID,
number: block.block_header.raw_data.number
};
// 将 refBlock 保存为文件或二维码,供离线机器读取2. 离线机器:构建并签名
离线端使用导入的参考区块填充 TAPOS 字段,然后本地签名:
const TronWeb = require('tronweb');
const tronWeb = new TronWeb({ fullHost: '' });
const builder = tronWeb.transactionBuilder;
const unsignedTxn = await builder.sendTrx(
"TVDGpn4h...wTppnkr", // 接收方地址
100, // 金额,单位为 sun
"TNPeeaaF...8G1NYqeL" // 发送方地址
);
// ref_block_bytes = 区块高度按 8 字节大端序表示后的第 6 至 8 字节(不含)
// ref_block_hash = 32 字节区块 ID 的第 8 至 16 字节(不含)
const blockNumBE = Buffer.alloc(8);
blockNumBE.writeBigUInt64BE(BigInt(refBlock.number));
const refBlockBytes = blockNumBE.slice(6, 8).toString('hex');
const refBlockHash = Buffer.from(refBlock.blockID, 'hex').slice(8, 16).toString('hex');
unsignedTxn.raw_data.ref_block_bytes = refBlockBytes;
unsignedTxn.raw_data.ref_block_hash = refBlockHash;
unsignedTxn.raw_data.expiration = Date.now() + 60 * 1000;
// TronWeb 会基于 Protobuf 序列化后的 raw_data 重新计算 txID。
// txID = SHA-256(protobuf(raw_data)); TRON 使用 Protobuf,不使用以太坊的 RLP。
const signedTxn = await tronWeb.trx.sign(unsignedTxn, privateKey);
// 将 signedTxn 保存为文件或二维码,带回联网环境ref_block_bytes 和 ref_block_hash 的截取规则参见交易。签名消息和 65 字节签名格式参见签名校验。
硬件钱包签名流程
硬件钱包托管私钥时,常见流程分三步:
- 联网机器构建未签名交易,并生成待签名消息哈希
SHA-256(raw_data)。 - 硬件设备展示交易信息,用户确认后用设备内私钥签名,输出 65 字节签名。
- 联网机器把
signature组装回交易体,再广播交易。
这种模式让私钥始终留在硬件设备中,联网机器只负责构建、组装和广播交易。
广播已签名交易
回到联网环境后,广播已签名交易:
const result = await tronWeb.trx.sendRawTransaction(signedTxn);
console.log(result.txid);如果广播时交易已经超过 expiration,网络会以 TAPOS_ERROR 或 EXPIRED 拒绝交易。长时间冷钱包流转时,应在广播前检查过期时间。
常见问题
- 参考区块太旧:
ref_block_bytes只有 2 字节,节点只在近期区块窗口中查找参考区块。超出窗口后,TAPOS 查找会失败并抛出TaposException。签名前应刷新参考区块,并在交易过期前完成广播。 - 离线时钟偏差:
expiration和timestamp依赖离线机器本地时钟。若本地时间与网络时间差距过大,交易可能被判定为已过期或来自未来。 - 网络不匹配:签名交易已经绑定到参考区块所在网络。使用主网参考区块签名的交易,不能广播到 Shasta 或 Nile。
相关资源
- 交易 — 完整交易结构与 TAPOS 字段
- 签名校验 — 签名校验与 65 字节上链格式
- Trident — Java SDK 的离线交易支持
- Wallet-cli — 支持离线签名流程的命令行工具
Updated 26 days ago