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 FullNode

2. 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.conf

4. Edit each configuration file

Set the following items differently between the two configurations:

Config keySR (supernode.conf)Fullnode (fullnode.conf)Why
localwitnessPrivate key of the SR address(leave empty)SR signs blocks with this key
genesis.block.witnessesThe SR addressSame as SR'sGenesis bootstrap; both nodes must agree
genesis.block.assetsPre-funded addresses with their TRX balancesSame as SR'sGenesis bootstrap
node.p2p.versionA custom positive integer not used by a public networkSame as SR'sAvoid Mainnet 11111, Shasta 1, and Nile 201910292; both nodes must use the same value
seed.node.ip.listRemove every public seed from the templateKeep only the SR node's IP:listen.port or hostname:listen.portLets the regular Fullnode discover the SR without attempting to contact public-network seeds
block.needSyncCheckfalsetrueThe first SR skips the sync check; everyone else must catch up
node.discovery.enabletruetrueWithout this, the node won't be discovered by peers
block.proposalExpireTime600000 (10 min)Same as SR'sSpeeds up proposals during testing — Mainnet uses 259,200,000 ms (3 days)
block.maintenanceTimeInterval300000 (5 min)Same as SR'sFaster Maintenance Period boundary — Mainnet uses 21,600,000 ms (6 hours)
committee.allowCreationOfContracts11Enable contract creation at private-chain startup and satisfy prerequisites for later TVM parameters
committee.allowSameTokenName11Allow duplicate TRC-10 token names
committee.allowTvmTransferTrc1011Allow 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 listener
  • http block ports — HTTP RPC
  • rpc block 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.conf

Start the regular Fullnode in the second terminal:

cd FullNode
java -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -jar FullNode.jar -c fullnode.conf

You 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@6

The 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:

  1. Run node proposal.js create 1 and record the transaction txID.
  2. Confirm that the creation transaction is on-chain, then call wallet/listproposals to find the actual proposal_id it created. Do not assume the ID is 1.
  3. Run node proposal.js approve <proposal_id>. On a multi-SR private chain, enough current active SRs must approve separately.
  4. Wait until expiration_time and verify that the state is APPROVED. Then repeat the process for the second set, using node 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