RPC.md — Networking, Transaction Bytes & the Estimate Strategy¶
Living reference for internal/rpc. Tier-1 authority remains
docs/ARCHITECTURE.md §5.4/§5.7; this file documents the implemented
contracts.
Connection discipline (client.go)¶
- gRPC only.
net/httpnever appears on the network surface (rpc/tvm/state/ script); HTTPS is sanctioned solely for managed binary downloads (compiler/solcmgr, audit). - One
grpc.ClientConnper (network, service):rpc.Dialopens two connections — FullNodeprotocol.Wallet(broadcast + unsolidified reads) andprotocol.WalletSolidity(solidified reads) — and never multiplexes them. Both endpoints come from the network'ssail.tomlentry. - Keepalive 30 s/10 s; every unary call gets a default 10 s deadline when the
caller supplies none (
WithCallTimeoutoverrides). - Retries are transport-only:
codes.Unavailable, 3 attempts, 200 ms backoff. Application errors — including node-side validation failures — surface on the first attempt, always. - TRON public gRPC endpoints are plaintext by protocol convention (ports 50051/50061), hence insecure transport credentials.
Transaction byte rules (txbuilder.go)¶
Protocol constants with dedicated tests (CLAUDE.md §3.9):
ref_block_bytes = bytes 6–7 of the 8-byte big-endian block height
ref_block_hash = bytes 8–15 of the 32-byte block id
expiration = timestamp + window (default window: 60 s)
txid = SHA-256(serialized raw_data)
TestTxID_MatchesRealTransaction re-serializes real mainnet transactions
through the generated pb types and reproduces their on-chain txids byte for
byte — proving wire compatibility of our serialization, not just the hash
rule.
Chain parameters (chainparams.go)¶
getEnergyFee (sun per energy unit) and getTransactionFee (sun per
bandwidth byte) are read via GetChainParameters; the per-contract
dynamic-energy factor (10⁴ precision) via GetContractInfo →
contract_state.energy_factor. All are cached with a session TTL (default
10 min — well inside TRON's 6-hour maintenance cycles). No numeric network
price exists anywhere else in the codebase; a missing key is an error, never
a silent default.
sun estimate strategy (estimate.go, ARCHITECTURE §5.4)¶
- Primary —
EstimateEnergy. Availability is gated by the node'svm.estimateEnergysetting, so detection is by runtime capability: the node's own refusal (its characteristic "does not support estimate energy" result, or an absent method) triggers the fallback and is memoized for the process. Version strings are never consulted. Semantics (Erratum E4, Nile-confirmed): the node'senergy_requiredis a fee-limit binary-search upper bound with 1-TRX granularity (up to ≈2× the exact figure for cheap calls) — precisely the right number for a fee-limit-safe energy budget, but NOT the exactE_total; the exact breakdown comes from the fallback path or local metering. - Fallback —
TriggerConstantContract.energy_usedis the penalty-inclusive total; the breakdown isbasic = energy_used − energy_penalty. Cross-check: recomposing⌊basic × (10000 + energy_factor)/10000⌋with the chain-read factor must reproduce the total; a mismatch setsReconciled=falsewith the signedDiscrepancyEnergy— reported, never hidden. - Bandwidth = serialized size of the real transaction (built against the
node's current block via txbuilder, with a placeholder 65-byte signature)
plus the 64-byte protocol result margin (java-tron's
MAX_RESULT_SIZE_IN_TX— a size constant, not a price). - TRX burn (worst case, no staked resources) =
Energy × getEnergyFee + Bandwidth × getTransactionFee.
Output is a table (source labeled; fallback shows the basic/penalty breakdown;
discrepancies render as a warning) or the full JSON document under --json.
Hermetic testing (rpctest/)¶
An in-process gRPC server registers both services on one loopback listener
(production code still opens its two separate conns against it) with
programmable handlers and per-method call counters. Canned profiles:
ProfileWithEstimateEnergy and ProfileWithoutEstimateEnergy (which answers
with the real node's refusal message). Every rpc test — including the e2e
flows driving the real binary — runs against it; nothing in make check or
make e2e touches the real network. Real-network coverage lives behind
//go:build differential (make differential, requires NILE_GRPC,
NILE_SOLIDITY_GRPC and — for the signing test — NILE_PRIVATE_KEY).