TRON 私链

搭建独立的 TRON 私有测试网络,包括至少一个超级代表(SR)出块节点与一个普通全节点。适用于本地开发、集成测试及对齐主网链参数的治理提议流程。

📘

前置阅读

TRON 私链是 TRON 协议的完全隔离运行实例——拥有独立的创世区块、独立的超级代表(SR)集合以及专属的账本记录。私链的典型用途包括:端到端集成测试、智能合约上线前的预发布演练,以及需要在链上频繁执行操作但不想消耗主网真实 TRX 的开发场景。

最小规模的私链通常由一个出块 SR 全节点至少一个普通同步全节点(负责同步区块、广播交易)组成。本文以双节点最小配置为例展开说明,若要扩展到包含多个超级代表的集群网络,只需沿用相同的配置模式即可。

准备工作

在开始部署前,请确保具备以下条件:

  • 根据部署平台,安装 Oracle JDK 8(x86_64)或 17(ARM64)环境。
  • 准备至少两个 TRON 账户地址及其对应的私钥:一个用作超级代表账户,另一个或多个作为预充值的测试账户。可使用 TronWeb、命令行工具 wallet-cliTronLink 钱包生成这些地址。

由于该私链完全隔离,创世区块中配置的超级代表地址将作为网络唯一的区块生产者,直到通过标准的超级代表选举流程选出更多 SR。

部署步骤

部署私链节点的基本流程与主网全节点一致,唯一的区别在于配置文件。两个节点运行相同的 FullNode.jar,仅在创世块定义、节点发现种子以及链上参数覆盖上有差异。

1. 创建部署目录

为超级代表(SR)和普通全节点分别创建独立的存放目录,以防数据库和配置文件冲突:

mkdir SR FullNode

2. 将 FullNode.jar 放入对应目录

两个节点使用完全相同的 JAR 包。可根据部署节点中的指导自行编译或下载发布版:

cp FullNode.jar SR/
cp FullNode.jar FullNode/

3. 将配置文件放入对应目录

主网默认配置文件模板复制到两个目录中,并进行重命名以示区分:

cp config.conf SR/supernode.conf
cp config.conf FullNode/fullnode.conf

4. 编辑各配置文件

请分别修改两份配置文件中的以下关键参数项:

配置文件键名SR 节点(supernode.conf普通全节点(fullnode.conf修改原因
localwitnessSR 地址的私钥(保留为空列表)SR 节点必须加载此私钥以对区块进行共识签名
genesis.block.witnessesSR 地址与 SR 相同创世块的初始出块代表,两端配置必须一致
genesis.block.Assets预充值账户及其初始 TRX 余额与 SR 相同创世块的初始资金分配,两端配置必须一致
p2p.version任意正整数(切勿使用 11111与 SR 相同11111 是主网的 P2P 版本;自定义值可隔离网络,两端必须一致
seed.node(留空)SR 节点的 IP 地址和 listen.port引导全节点去连接并发现 SR 节点
needSyncCheckfalsetrue首个启动的出块节点需跳过同步检查,普通节点则需进行同步
node.discovery.enabletruetrue启用节点发现,以便节点间能正常建立 P2P 握手连接
block.proposalExpireTime600000(10 分钟)与 SR 相同缩短治理提议的过期时间以加快测试进度(主网默认值为 3 天)
block.maintenanceTimeInterval300000(5 分钟)与 SR 相同缩短维护周期以加快投票生效(主网默认值为 6 小时)
committee.allowSameTokenName11允许存在同名的 TRC-10 Token
committee.allowTvmTransferTrc1011允许 TVM 转移 TRC-10 Token

5. 单机部署时的端口调整

如果超级代表节点和普通全节点运行在同一台服务器上,为避免端口冲突,请为各自配置独立的端口:

  • listen.port:P2P 网络监听端口
  • http 端口组:HTTP RPC 端口
  • rpc 端口组:gRPC 端口

如果两个节点部署在不同的服务器上,可以直接使用配置文件中的默认端口。

6. 启动节点

# 启动超级代表(SR)出块节点
cd SR
java -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -jar FullNode.jar --witness -c supernode.conf

# 启动普通全节点
cd ../FullNode
java -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -jar FullNode.jar -c fullnode.conf

启动几秒钟后,全节点日志中应能看到节点成功连接至 SR 节点并开始同步新产生的区块。

将链参数与主网对齐

全新启动的私链会采用初始默认参数,这与主网当前的实际运行环境(如能量单价、出块奖励、智能合约执行上限、动态能量模型等)不同。为了让私链测试行为更接近主网,需要以超级代表(SR)身份提交两笔链上治理提议来更新这些参数。

之所以要将修改拆分为两笔治理提议,是因为诸如 #30#70 这类基础开关参数必须先被激活生效,链才会接受依赖于它们的具体参数值。因此第一笔治理提议负责激活开关,第二笔治理提议在开关激活后的基础上设置具体数值。

以下是使用 TronWeb 提交和批准治理提议的示例脚本:

const { TronWeb } = require('tronweb');
const tronWeb = new TronWeb({
    fullHost: 'http://localhost:16887',   // 替换为 SR 节点的实际 HTTP 端口(主网默认为 8090)
    privateKey: 'c741f5c0...7d220dc'      // 超级代表(SR)账户的 Hex 格式私钥
});

// 治理提议 1 — 必须首先执行,以激活基础链参数和功能开关
const parametersForProposal1 = [
    { key: 9, value: 1 }, { key: 10, value: 1 }, { key: 11, value: 420 },
    { key: 19, value: 90000000000 }, { key: 15, value: 1 }, { key: 18, value: 1 },
    { key: 16, value: 1 }, { key: 20, value: 1 }, { key: 26, value: 1 },
    { key: 30, value: 1 }, { key: 5, value: 16000000 }, { key: 31, value: 160000000 },
    { key: 32, value: 1 }, { key: 39, value: 1 }, { key: 41, value: 1 },
    { key: 3, value: 1000 }, { key: 47, value: 10000000000 }, { key: 49, value: 1 },
    { key: 13, value: 80 }, { key: 7, value: 1000000 }, { key: 61, value: 600 },
    { key: 63, value: 1 }, { key: 65, value: 1 }, { key: 66, value: 1 },
    { key: 67, value: 1 }, { key: 68, value: 1000000 }, { key: 69, value: 1 },
    { key: 70, value: 14 }, { key: 71, value: 1 }, { key: 76, value: 1 }
];

// 治理提议 2 — 必须在治理提议 1 获批并到期生效后才能执行
const parametersForProposal2 = [
    { key: 47, value: 15000000000 }, { key: 59, value: 1 }, { key: 72, value: 1 },
    { key: 73, value: 3000000000 }, { key: 74, value: 2000 }, { key: 75, value: 12000 },
    { key: 77, value: 1 }, { key: 78, value: 864000 }
];

async function modifyChainParameters(parameters, proposalID) {
    parameters.sort((a, b) => a.key - b.key);
    const unsignedTxn = await tronWeb.transactionBuilder.createProposal(
        parameters,
        '41D0B696...5C593C00F0'   // 超级代表(SR)的 42 位 Hex 格式地址
    );
    const signedTxn = await tronWeb.trx.sign(unsignedTxn);
    const receipt = await tronWeb.trx.sendRawTransaction(signedTxn);
    console.log('治理提议已提交:', receipt);

    setTimeout(async () => {
        console.log('批准治理提议,提议 ID:', proposalID);
        const unsignedVote = await tronWeb.transactionBuilder.voteProposal(
            proposalID, true, tronWeb.defaultAddress.hex
        );
        const signedVote = await tronWeb.trx.sign(unsignedVote);
        await tronWeb.trx.sendRawTransaction(signedVote);
    }, 1000);
}

// 步骤 1:提交并批准治理提议 1
modifyChainParameters(parametersForProposal1, 1);

运行治理提议 1 的脚本后,可使用接口 wallet/listproposals 查询其过期时间。在 expiration_time 截止后,如果该治理提议的状态(state)变为 APPROVED,说明参数已正式并入链状态。此时即可运行治理提议 2:

modifyChainParameters(parametersForProposal2, 2);

当两个治理提议均生效后,私链参数即与目标配置对齐。可随时通过 wallet/getchainparameters 接口验证当前参数值。

完整的链上参数定义及各自的作用说明,请参阅网络参数


相关资源