TRON private chain
Set up an isolated TRON network with at least one block-producing SR node and one regular Fullnode for development, testing, or staging, including the proposal sequence for calibrating the chain parameters under test.
Prerequisites
A private TRON chain is an isolated instance of the protocol — your own genesis block, your own SR set, your own ledger. It is useful for end-to-end integration testing, contract staging, and building features that need on-chain operations without spending Mainnet TRX.
A minimal private chain has one block-producing SR Fullnode plus at least one regular Fullnode to receive synced blocks and broadcast transactions. This page walks through the two-node minimum; scaling up to more SRs follows the same pattern.
Preparation
Before starting, have:
- Oracle JDK 8 (x86_64) or 17 (ARM64), matching the platform you'll deploy on.
- Node.js 18 or later and npm, for the TronWeb governance-proposal script later in this guide.
- At least two TRON addresses with their private keys saved — one for the SR, one or more for funded test accounts. Generate them with TronWeb, wallet-cli, or TronLink.
Because the chain is isolated, the SR address you choose for the genesis block becomes the network's only block producer until you add more SRs through the standard SR election flow.
Deployment
The deployment process is identical to a Mainnet node — the difference is entirely in the configuration file. Each node still uses the standard FullNode.jar; only the genesis block, peer-discovery seeds, and chain-parameter overrides change.
1. Create deployment directories
Keep the SR and Fullnode in separate directories so their databases and configs don't collide:
mkdir SR FullNode2. Place FullNode.jar in each directory
Use the same JAR for both. Build it from source or download a release as described in Deploy a node:
cp FullNode.jar SR/
cp FullNode.jar FullNode/3. Place the configuration file in each directory
Obtain framework/src/main/resources/config.conf from the java-tron release tag or source commit used to build FullNode.jar, copy it into both directories, and rename each copy. The configuration must match the JAR's source version; use the master configuration only to inspect current fields, not with a JAR built from another version.
cp config.conf SR/supernode.conf
cp config.conf FullNode/fullnode.conf4. Edit each configuration file
Set the following items differently between the two configurations:
| Config key | SR (supernode.conf) | Fullnode (fullnode.conf) | Why |
|---|---|---|---|
localwitness | Private key of the SR address | (leave empty) | SR signs blocks with this key |
genesis.block.witnesses | The SR address | Same as SR's | Genesis bootstrap; both nodes must agree |
genesis.block.assets | Pre-funded addresses with their TRX balances | Same as SR's | Genesis bootstrap |
node.p2p.version | A custom positive integer not used by a public network | Same as SR's | Avoid Mainnet 11111, Shasta 1, and Nile 201910292; both nodes must use the same value |
seed.node.ip.list | Remove every public seed from the template | Keep only the SR node's IP:listen.port or hostname:listen.port | Lets the regular Fullnode discover the SR without attempting to contact public-network seeds |
block.needSyncCheck | false | true | The first SR skips the sync check; everyone else must catch up |
node.discovery.enable | true | true | Without this, the node won't be discovered by peers |
block.proposalExpireTime | 600000 (10 min) | Same as SR's | Speeds up proposals during testing — Mainnet uses 259,200,000 ms (3 days) |
block.maintenanceTimeInterval | 300000 (5 min) | Same as SR's | Faster Maintenance Period boundary — Mainnet uses 21,600,000 ms (6 hours) |
committee.allowCreationOfContracts | 1 | 1 | Enable contract creation at private-chain startup and satisfy prerequisites for later TVM parameters |
committee.allowSameTokenName | 1 | 1 | Allow duplicate TRC-10 token names |
committee.allowTvmTransferTrc10 | 1 | 1 | Allow TVM transfers of TRC-10 tokens |
5. Adjust ports if running on the same machine
If both nodes run on the same host, give each its own ports:
listen.port— P2P listenerhttpblock ports — HTTP RPCrpcblock ports — gRPC
If the two nodes are on different machines, you can leave the ports at defaults.
6. Start each node
Open two terminals and, in each one, navigate to the deployment root that contains the SR and FullNode directories.
Start the SR node in the first terminal:
cd SR
java -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -jar FullNode.jar --witness -c supernode.confStart the regular Fullnode in the second terminal:
cd FullNode
java -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -jar FullNode.jar -c fullnode.confYou should see the Fullnode connect to the SR within a few seconds and begin syncing newly produced blocks.
Calibrate key private-chain parameters
A fresh private chain starts with default parameters that may differ from the target network in Energy price, block rewards, contract execution limits, Dynamic Energy, and other behavior. To make test results comparable, use SR proposals to calibrate the parameters your application depends on.
Chain parameters change through governance and vary by java-tron version. Before submitting a proposal, query wallet/getchainparameters on the target network and confirm each parameter ID and dependency in ProposalUtil.ProposalType from the java-tron version you run. Do not treat this example as a permanently current, exhaustive Mainnet parameter list.
The following two sets are a Mainnet snapshot from 2026-08-04 for test chains running java-tron 4.8.1 or later, where parameter #94 is supported. The first proposal enables prerequisites; submit the second only after the first has expired and taken effect.
Install the TronWeb dependency required by the script:
npm install tronweb@6The script connects to the SR node on the default HTTP port 8090. If you changed the SR's HTTP port, set PRIVATE_FULL_HOST to its actual endpoint.
const { TronWeb } = require('tronweb');
const privateKey = process.env.TRON_PRIVATE_KEY;
if (!/^[0-9a-fA-F]{64}$/.test(privateKey || '')) {
throw new Error('Set TRON_PRIVATE_KEY to the 64-character hex private key of the SR');
}
const tronWeb = new TronWeb({
fullHost: process.env.PRIVATE_FULL_HOST || 'http://localhost:8090',
privateKey
});
// Proposal 1 — must run first; activates #30 and #70 among others
const parametersForProposal1 = [
{ key: 11, value: 100 },
{ key: 19, value: 180000000000 }, { key: 16, value: 1 },
{ key: 20, value: 1 }, { key: 26, value: 1 },
{ key: 30, value: 1 }, { key: 5, value: 8000000 }, { key: 31, value: 128000000 },
{ 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 }
];
// Proposal 2 — depends on Proposal 1 having taken effect
const parametersForProposal2 = [
{ key: 47, value: 15000000000 }, { key: 59, value: 1 }, { key: 72, value: 1 },
{ key: 73, value: 5000000000 }, { key: 74, value: 2000 }, { key: 75, value: 34000 },
{ key: 77, value: 1 }, { key: 78, value: 864000 }, { key: 79, value: 1 },
{ key: 81, value: 1 }, { key: 82, value: 1000 }, { key: 83, value: 1 },
{ key: 87, value: 1 }, { key: 88, value: 1 }, { key: 89, value: 1 },
{ key: 94, value: 1 }
];
async function broadcast(unsignedTxn) {
const signedTxn = await tronWeb.trx.sign(unsignedTxn);
const result = await tronWeb.trx.sendRawTransaction(signedTxn);
if (!result.result || !result.txid) {
throw new Error(`Broadcast failed: ${JSON.stringify(result)}`);
}
return result.txid;
}
async function createProposal(parameters) {
const sorted = [...parameters].sort((a, b) => a.key - b.key);
const unsignedTxn = await tronWeb.transactionBuilder.createProposal(
sorted,
tronWeb.defaultAddress.base58
);
console.log('Proposal creation txID:', await broadcast(unsignedTxn));
}
async function approveProposal(proposalID) {
const unsignedTxn = await tronWeb.transactionBuilder.voteProposal(
proposalID,
true,
tronWeb.defaultAddress.base58
);
console.log('Proposal approval txID:', await broadcast(unsignedTxn));
}
async function main() {
const [action, argument] = process.argv.slice(2);
if (action === 'create' && (argument === '1' || argument === '2')) {
const parameters = argument === '1' ? parametersForProposal1 : parametersForProposal2;
await createProposal(parameters);
} else if (action === 'approve' && /^\d+$/.test(argument || '')) {
await approveProposal(Number(argument));
} else {
throw new Error('Usage: node proposal.js create <1|2> | approve <proposal_id>');
}
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});Save the script as proposal.js. Before running it, replace YOUR_SR_PRIVATE_KEY with the SR account's 64-character hexadecimal private key:
export TRON_PRIVATE_KEY="YOUR_SR_PRIVATE_KEY"
# Set this only if the SR node does not use the default HTTP endpoint:
# export PRIVATE_FULL_HOST="http://localhost:YOUR_SR_HTTP_PORT"Then process each proposal in this order:
- Run
node proposal.js create 1and record the transactiontxID. - Confirm that the creation transaction is on-chain, then call
wallet/listproposalsto find the actualproposal_idit created. Do not assume the ID is1. - Run
node proposal.js approve <proposal_id>. On a multi-SR private chain, enough current active SRs must approve separately. - Wait until
expiration_timeand verify that the state isAPPROVED. Then repeat the process for the second set, usingnode proposal.js create 2.
Finally, call wallet/getchainparameters to verify the values that actually took effect. If Mainnet is not your target, or you run a different java-tron version, adjust the parameter set against that target network and source version.
For the full list of parameters and what each controls, see Network parameters.
Related resources
- Deploy a node — End-to-end deployment for a single node
- Becoming a Super Representative — Stand for election as an SR
- SR best practices — Operating an SR node in production
- Network parameters — Full reference of proposable chain parameters
- Resource Model: Bandwidth & Energy — TRON's three system resources
Updated 4 days ago