Skip to content

feat(vm): close shielded TRC20 progressively#6829

Closed
317787106 wants to merge 1 commit into
tronprotocol:release_v4.8.2from
317787106:feature/close_shield_contract
Closed

feat(vm): close shielded TRC20 progressively#6829
317787106 wants to merge 1 commit into
tronprotocol:release_v4.8.2from
317787106:feature/close_shield_contract

Conversation

@317787106

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds a committee governance proposal CLOSE_SHIELDED_TRC20_TRANSACTION (id 80, value range 0–3) that progressively disables the shielded TRC20 precompiled contracts:

Value Effect
0 Off — no behavior change (default)
1 Disable mint (verifyMintProof)
2 Disable mint + transfer (verifyTransferProof)
3 Disable mint + transfer + burn (verifyBurnProof)

When a level is engaged, the corresponding proof precompile short-circuits to (success, 32 zero bytes) before any verification; Program tags the call with a specific runtime error (shield trc20 mint/transfer/burn not allowed), and VMActuator preserves that reason and marks the call as REVERT (no state change) instead of overwriting it with the generic "REVERT opcode executed".

The parameter is:

  • persisted in DynamicPropertiesStore (CLOSE_SHIELDED_TRC20_TRANSACTION),
  • applied through ProposalService,
  • loaded into the VM via VMConfig/ConfigLoader,
  • exposed in Wallet.getChainParameters as getCloseShieldedTRC20Transaction,
  • configurable through committee.closeShieldedTRC20Transaction (see reference.conf),
  • validation gated to fork v4.8.2.

Design note — why close via "success + revert" instead of failing the precompile (energy cost)

The proof precompile's execute() deliberately returns success (true, 32 zero bytes) when a level is closed, rather than throwing an exception, returning (false, …), or having the contract lookup return null. The reason is energy accounting in Program.callToPrecompiledAddress:

  • Success branch refunds the caller's unused energy: refundEnergy(msg.getEnergy() − requiredEnergy) — only the precompile's fixed cost (getEnergyForData, e.g. 150000) is consumed.
  • Failure branch (an exception thrown from execute() / the contract lookup, execute() returning false, or no contract found) is the "spend all energy on failure" path: refundEnergy(0), which burns the entire remaining energy of the call.

So if the close were implemented by making the precompile fail, every blocked mint/transfer/burn call would penalize the caller with a full-energy burn. Instead we let execute() return normally, then set runtimeError (shield mint/transfer/burn not allowed) on the result afterwards and let VMActuator turn it into a REVERT. This yields the same revert outcome (no state change) while refunding the caller's unused energy, avoiding the all-energy-consumed penalty.

Why are these changes required?

Governance needs a controlled, staged way to wind down shielded TRC20 support rather than an all-or-nothing switch. Closing in the order mint → transfer → burn lets the network first stop new shielded notes from being created, then stop moving them, while keeping burn open last so existing holders can still unshield (exit) their funds before the feature is fully closed. Default 0 keeps current behavior, so the change is inert until a proposal is approved post-4.8.2.

This PR has been tested by:

  • Unit Tests
    • ProposalUtilTest — proposal 80 is rejected before fork v4.8.2 (even with earlier forks active) and validates the 0–3 range afterwards.
    • ProposalServiceTest — approving the proposal persists the value into the dynamic properties store.
    • PrecompiledContractsTest — each proof precompile short-circuits to (success, zero) at its matching close level.
    • RpcApiServicesTest — store initialization with the new key.
  • reference.conf bean-parity, key-name, and comment-coverage gates

Follow up

N/A

Extra details

Adapted to the v4.8.2 codebase: VMConfig lives in common, and the committee parameter is wired through the CommitteeConfig bean + reference.conf (replacing the old Constant.COMMITTEE_* / Args.hasPath style). Proposal code 80 was unused on this branch (it skips 79 → 81), so no renumbering was needed.

Add committee governance proposal CLOSE_SHIELDED_TRC20_TRANSACTION
(id 80, value range 0-3) that progressively disables the shielded
TRC20 precompiled contracts:

  0 -> off (default, no behavior change)
  1 -> disable mint (verifyMintProof)
  2 -> disable mint + transfer (verifyTransferProof)
  3 -> disable mint + transfer + burn (verifyBurnProof)

When a level is engaged, the corresponding proof precompile
short-circuits to (success, 32 zero bytes) before any verification so
the caller's unused energy is refunded instead of being fully burned;
Program tags the call with a runtime error (shield mint/transfer/burn
not allowed) and VMActuator turns that into a REVERT with no state
change. Validation is gated to fork v4.8.2.

The parameter is persisted in DynamicPropertiesStore, applied through
ProposalService, loaded into the VM via VMConfig/ConfigLoader, exposed
in Wallet.getChainParameters, and configurable through
committee.closeShieldedTRC20Transaction (reference.conf).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@317787106 317787106 changed the title feature(vm): close shielded TRC20 progressively feat(vm): close shielded TRC20 progressively Jun 9, 2026
@317787106 317787106 closed this Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant