From 7ee881500716855f1a4f6c62f84fec4d5b35afc8 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Sun, 7 Jun 2026 02:30:53 +0800 Subject: [PATCH] feature(vm): close shielded TRC20 progressively 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 --- .../org/tron/core/actuator/VMActuator.java | 14 ++++++- .../org/tron/core/utils/ProposalUtil.java | 13 +++++++ .../tron/core/vm/PrecompiledContracts.java | 27 +++++++++++++ .../org/tron/core/vm/config/ConfigLoader.java | 1 + .../org/tron/core/vm/program/Program.java | 25 ++++++++++++ .../core/store/DynamicPropertiesStore.java | 23 +++++++++++ .../common/parameter/CommonParameter.java | 3 ++ .../core/config/args/CommitteeConfig.java | 1 + .../org/tron/core/vm/config/VMConfig.java | 10 +++++ common/src/main/resources/reference.conf | 1 + .../src/main/java/org/tron/core/Wallet.java | 5 +++ .../java/org/tron/core/config/args/Args.java | 1 + .../tron/core/consensus/ProposalService.java | 4 ++ .../runtime/vm/PrecompiledContractsTest.java | 38 ++++++++++++++++++ .../core/actuator/utils/ProposalUtilTest.java | 39 +++++++++++++++++++ .../core/services/ProposalServiceTest.java | 19 +++++++++ .../core/services/RpcApiServicesTest.java | 1 + 17 files changed, 223 insertions(+), 2 deletions(-) diff --git a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java index 1b0e8a6637f..a33c267c993 100644 --- a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java @@ -6,6 +6,9 @@ import static org.tron.common.math.Maths.floorDiv; import static org.tron.common.math.Maths.max; import static org.tron.common.math.Maths.min; +import static org.tron.core.vm.PrecompiledContracts.BURN_NOT_ALLOWED; +import static org.tron.core.vm.PrecompiledContracts.MINT_NOT_ALLOWED; +import static org.tron.core.vm.PrecompiledContracts.TRANSFER_NOT_ALLOWED; import static org.tron.protos.contract.Common.ResourceCode.ENERGY; import com.google.protobuf.ByteString; @@ -231,7 +234,12 @@ public void execute(Object object) throws ContractExeException { return; } - if (result.getException() != null || result.isRevert()) { + String errorString = result.getRuntimeError(); + boolean shieldedProofNotAllowed = MINT_NOT_ALLOWED.equals(errorString) + || TRANSFER_NOT_ALLOWED.equals(errorString) + || BURN_NOT_ALLOWED.equals(errorString); + + if (result.getException() != null || result.isRevert() || shieldedProofNotAllowed) { result.getDeleteAccounts().clear(); result.getLogInfoList().clear(); //result.resetFutureRefund(); @@ -243,6 +251,9 @@ public void execute(Object object) throws ContractExeException { } result.setRuntimeError(result.getException().getMessage()); throw result.getException(); + } else if (shieldedProofNotAllowed) { + // Runtime error already set by the precompile; just mark the revert (no energy burn). + result.setRevert(); } else { result.setRuntimeError("REVERT opcode executed"); } @@ -254,7 +265,6 @@ public void execute(Object object) throws ContractExeException { .parseLogInfos(program.getResult().getLogInfoList(), rootRepository); program.getResult().setTriggerList(triggers); } - } } else { rootRepository.commit(); diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 74d332c5611..b687f7a946f 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -13,6 +13,7 @@ import org.tron.core.config.Parameter.ForkBlockVersionEnum; import org.tron.core.exception.ContractValidateException; import org.tron.core.store.DynamicPropertiesStore; +import org.tron.core.vm.PrecompiledContracts; public class ProposalUtil { @@ -941,6 +942,17 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case CLOSE_SHIELDED_TRC20_TRANSACTION: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) { + throw new ContractValidateException( + "Bad chain parameter id [CLOSE_SHIELDED_TRC20_TRANSACTION]"); + } + if (value < 0 || value > 3) { + throw new ContractValidateException( + "This value[CLOSE_SHIELDED_TRC20_TRANSACTION] is only allowed to be in the range 0-3"); + } + break; + } default: break; } @@ -1018,6 +1030,7 @@ public enum ProposalType { // current value, value range ALLOW_CANCEL_ALL_UNFREEZE_V2(77), // 0, 1 MAX_DELEGATE_LOCK_PERIOD(78), // (86400, 10512000] ALLOW_OLD_REWARD_OPT(79), // 0, 1 + CLOSE_SHIELDED_TRC20_TRANSACTION(80), // 0, {0,1,2,3} ALLOW_ENERGY_ADJUSTMENT(81), // 0, 1 MAX_CREATE_ACCOUNT_TX_SIZE(82), // [500, 10000] ALLOW_TVM_CANCUN(83), // 0, 1 diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index 3993e8ed835..fccdcb21991 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -212,6 +212,15 @@ public class PrecompiledContracts { private static final DataWord p256VerifyAddr = new DataWord( "0000000000000000000000000000000000000000000000000000000000000100"); + public static final String MINT_NOT_ALLOWED = "shield trc20 mint not allowed"; + public static final String TRANSFER_NOT_ALLOWED = "shield trc20 transfer not allowed"; + public static final String BURN_NOT_ALLOWED = "shield trc20 burn not allowed"; + + // Staged values of closeShieldedTRC20Transaction(): operation (mint < transfer < burn); + public static final long CLOSE_SHIELDED_TRC20_MINT = 1; + public static final long CLOSE_SHIELDED_TRC20_TRANSFER = 2; + public static final long CLOSE_SHIELDED_TRC20_BURN = 3; + public static PrecompiledContract getOptimizedContractForConstant(PrecompiledContract contract) { try { Constructor constructor = contract.getClass().getDeclaredConstructor(); @@ -1385,6 +1394,12 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { + // Mint closed (CLOSE_SHIELDED_TRC20_TRANSACTION level >= 1). Return success (the Boolean + // is the CALL success flag) so the caller's unused energy is refunded -- failing here + // would "spend all energy". Zero payload + a REVERT (set via runtimeError) still revert. + if (VMConfig.closeShieldedTRC20Transaction() >= CLOSE_SHIELDED_TRC20_MINT) { + return Pair.of(true, DataWord.ZERO().getData()); + } if (data == null) { return Pair.of(true, DataWord.ZERO().getData()); } @@ -1463,6 +1478,12 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { + // Transfer closed (CLOSE_SHIELDED_TRC20_TRANSACTION level >= 2). Return success (the Boolean + // is the CALL success flag) so the caller's unused energy is refunded -- failing here + // would "spend all energy". Zero payload + a REVERT (set via runtimeError) still revert. + if (VMConfig.closeShieldedTRC20Transaction() >= CLOSE_SHIELDED_TRC20_TRANSFER) { + return Pair.of(true, DataWord.ZERO().getData()); + } if (data == null) { return Pair.of(true, DataWord.ZERO().getData()); } @@ -1740,6 +1761,12 @@ public long getEnergyForData(byte[] data) { @Override public Pair execute(byte[] data) { + // Burn closed (CLOSE_SHIELDED_TRC20_TRANSACTION level >= 3). Return success (the Boolean + // is the CALL success flag) so the caller's unused energy is refunded -- failing here + // would "spend all energy". Zero payload + a REVERT (set via runtimeError) still revert. + if (VMConfig.closeShieldedTRC20Transaction() >= CLOSE_SHIELDED_TRC20_BURN) { + return Pair.of(true, DataWord.ZERO().getData()); + } if (data == null) { return Pair.of(true, DataWord.ZERO().getData()); } diff --git a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java index 881eb861bea..393ec216c0c 100644 --- a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java +++ b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java @@ -24,6 +24,7 @@ public static void load(StoreFactory storeFactory) { VMConfig.initAllowTvmConstantinople(ds.getAllowTvmConstantinople()); VMConfig.initAllowTvmSolidity059(ds.getAllowTvmSolidity059()); VMConfig.initAllowShieldedTRC20Transaction(ds.getAllowShieldedTRC20Transaction()); + VMConfig.initCloseShieldedTRC20Transaction(ds.getCloseShieldedTRC20Transaction()); VMConfig.initAllowTvmIstanbul(ds.getAllowTvmIstanbul()); VMConfig.initAllowTvmFreeze(ds.getAllowTvmFreeze()); VMConfig.initAllowTvmVote(ds.getAllowTvmVote()); diff --git a/actuator/src/main/java/org/tron/core/vm/program/Program.java b/actuator/src/main/java/org/tron/core/vm/program/Program.java index 41822df2391..99c26b72b57 100644 --- a/actuator/src/main/java/org/tron/core/vm/program/Program.java +++ b/actuator/src/main/java/org/tron/core/vm/program/Program.java @@ -1743,6 +1743,8 @@ public void callToPrecompiledAddress(MessageCall msg, this.refundEnergy(msg.getEnergy().longValue() - requiredEnergy, CALL_PRE_COMPILED); this.stackPushOne(); returnDataBuffer = out.getRight(); + + markClosedShieldedTRC20Precompile(contract); deposit.commit(); } else { // spend all energy on failure, push zero and revert state changes @@ -1761,6 +1763,29 @@ public void callToPrecompiledAddress(MessageCall msg, } } + // When the shielded TRC20 kill-switch is engaged, the proof precompile returns success so the + // caller's unused energy is refunded (see PrecompiledContracts); we only flag the closed + // operation here via a runtime error, which VMActuator later turns into a REVERT. + private void markClosedShieldedTRC20Precompile( + PrecompiledContracts.PrecompiledContract contract) { + long phase = VMConfig.closeShieldedTRC20Transaction(); + if (phase == 0) { + return; + } + if (contract instanceof PrecompiledContracts.VerifyMintProof + && phase >= PrecompiledContracts.CLOSE_SHIELDED_TRC20_MINT) { + this.result.setRuntimeError(PrecompiledContracts.MINT_NOT_ALLOWED); + } + if (contract instanceof PrecompiledContracts.VerifyTransferProof + && phase >= PrecompiledContracts.CLOSE_SHIELDED_TRC20_TRANSFER) { + this.result.setRuntimeError(PrecompiledContracts.TRANSFER_NOT_ALLOWED); + } + if (contract instanceof PrecompiledContracts.VerifyBurnProof + && phase >= PrecompiledContracts.CLOSE_SHIELDED_TRC20_BURN) { + this.result.setRuntimeError(PrecompiledContracts.BURN_NOT_ALLOWED); + } + } + public boolean byTestingSuite() { return invoke.byTestingSuite(); } diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 0f74f20d379..96203623d57 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -139,6 +139,8 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_SHIELDED_TRC20_TRANSACTION = "ALLOW_SHIELDED_TRC20_TRANSACTION" .getBytes(); + private static final byte[] CLOSE_SHIELDED_TRC20_TRANSACTION = + "CLOSE_SHIELDED_TRC20_TRANSACTION".getBytes(); private static final byte[] ALLOW_TVM_ISTANBUL = "ALLOW_TVM_ISTANBUL".getBytes(); private static final byte[] ALLOW_TVM_CONSTANTINOPLE = "ALLOW_TVM_CONSTANTINOPLE".getBytes(); private static final byte[] ALLOW_TVM_SOLIDITY_059 = "ALLOW_TVM_SOLIDITY_059".getBytes(); @@ -727,6 +729,13 @@ private DynamicPropertiesStore(@Value("properties") String dbName) { CommonParameter.getInstance().getAllowShieldedTRC20Transaction()); } + try { + this.getCloseShieldedTRC20Transaction(); + } catch (IllegalArgumentException e) { + this.saveCloseShieldedTRC20Transaction( + CommonParameter.getInstance().getCloseShieldedTRC20Transaction()); + } + try { this.getAllowTvmIstanbul(); } catch (IllegalArgumentException e) { @@ -2060,6 +2069,20 @@ public long getAllowShieldedTRC20Transaction() { () -> new IllegalArgumentException(msg)); } + public void saveCloseShieldedTRC20Transaction(long closeShieldedTRC20Transaction) { + this.put(DynamicPropertiesStore.CLOSE_SHIELDED_TRC20_TRANSACTION, + new BytesCapsule(ByteArray.fromLong(closeShieldedTRC20Transaction))); + } + + public long getCloseShieldedTRC20Transaction() { + String msg = "not found CLOSE_SHIELDED_TRC20_TRANSACTION"; + return Optional.ofNullable(getUnchecked(CLOSE_SHIELDED_TRC20_TRANSACTION)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElseThrow( + () -> new IllegalArgumentException(msg)); + } + public void saveAllowTvmIstanbul(long allowTVMIstanbul) { this.put(DynamicPropertiesStore.ALLOW_TVM_ISTANBUL, new BytesCapsule(ByteArray.fromLong(allowTVMIstanbul))); diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index eeb92fdbd60..1a66a24c5bc 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -531,6 +531,9 @@ public class CommonParameter { public long allowShieldedTRC20Transaction; @Getter @Setter + public long closeShieldedTRC20Transaction; + @Getter + @Setter public long allowTvmIstanbul; @Getter @Setter diff --git a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java index 660fa289e3b..5b565ee482c 100644 --- a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java +++ b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java @@ -28,6 +28,7 @@ public class CommitteeConfig { private long allowTvmSolidity059 = 0; private long forbidTransferToContract = 0; private long allowShieldedTRC20Transaction = 0; + private long closeShieldedTRC20Transaction = 0; private long allowMarketTransaction = 0; private long allowTransactionFeePool = 0; private long allowBlackHoleOptimization = 0; diff --git a/common/src/main/java/org/tron/core/vm/config/VMConfig.java b/common/src/main/java/org/tron/core/vm/config/VMConfig.java index 94c1e50284e..5c93e81ae44 100644 --- a/common/src/main/java/org/tron/core/vm/config/VMConfig.java +++ b/common/src/main/java/org/tron/core/vm/config/VMConfig.java @@ -23,6 +23,8 @@ public class VMConfig { private static boolean ALLOW_SHIELDED_TRC20_TRANSACTION = false; + private static long CLOSE_SHIELDED_TRC20_TRANSACTION = 0; + private static boolean ALLOW_TVM_ISTANBUL = false; private static boolean ALLOW_TVM_FREEZE = false; @@ -100,6 +102,10 @@ public static void initAllowShieldedTRC20Transaction(long allow) { ALLOW_SHIELDED_TRC20_TRANSACTION = allow == 1; } + public static void initCloseShieldedTRC20Transaction(long close) { + CLOSE_SHIELDED_TRC20_TRANSACTION = close; + } + public static void initAllowTvmIstanbul(long allow) { ALLOW_TVM_ISTANBUL = allow == 1; } @@ -208,6 +214,10 @@ public static boolean allowShieldedTRC20Transaction() { return ALLOW_SHIELDED_TRC20_TRANSACTION; } + public static long closeShieldedTRC20Transaction() { + return CLOSE_SHIELDED_TRC20_TRANSACTION; + } + public static boolean allowTvmIstanbul() { return ALLOW_TVM_ISTANBUL; } diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index f1e4907274f..d9edb73c947 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -830,6 +830,7 @@ committee = { allowTvmSolidity059 = 0 # getAllowTvmSolidity059, #32: enable Solidity 0.5.9 TVM rules forbidTransferToContract = 0 # getForbidTransferToContract, #35: forbid direct transfers to contracts allowShieldedTRC20Transaction = 0 # getAllowShieldedTRC20Transaction, #39: enable shielded TRC20 transfers + closeShieldedTRC20Transaction = 0 # getCloseShieldedTRC20Transaction, #80: progressively close shielded TRC20 mint(1)/transfer(2)/burn(3); range 0-3 allowTvmIstanbul = 0 # getAllowTvmIstanbul, #41: enable Istanbul TVM rules allowMarketTransaction = 0 # getAllowMarketTransaction, #44: enable market transactions allowProtoFilterNum = 0 # getAllowProtoFilterNum, #24: enable protobuf field-number filtering diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index b705b26edc2..e503b09580f 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1507,6 +1507,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getAllowHardenExchangeCalculation()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getCloseShieldedTRC20Transaction") + .setValue(dbManager.getDynamicPropertiesStore().getCloseShieldedTRC20Transaction()) + .build()); + return builder.build(); } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 0bca242606e..30e2399973a 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -453,6 +453,7 @@ private static void applyCommitteeConfig(CommitteeConfig cc) { PARAMETER.allowTvmSolidity059 = cc.getAllowTvmSolidity059(); PARAMETER.forbidTransferToContract = cc.getForbidTransferToContract(); PARAMETER.allowShieldedTRC20Transaction = cc.getAllowShieldedTRC20Transaction(); + PARAMETER.closeShieldedTRC20Transaction = cc.getCloseShieldedTRC20Transaction(); PARAMETER.allowMarketTransaction = cc.getAllowMarketTransaction(); PARAMETER.allowTransactionFeePool = cc.getAllowTransactionFeePool(); PARAMETER.allowBlackHoleOptimization = cc.getAllowBlackHoleOptimization(); diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index 543deab2fc6..51de8af9105 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -360,6 +360,10 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowOldRewardOpt(entry.getValue()); break; } + case CLOSE_SHIELDED_TRC20_TRANSACTION: { + manager.getDynamicPropertiesStore().saveCloseShieldedTRC20Transaction(entry.getValue()); + break; + } case ALLOW_ENERGY_ADJUSTMENT: { manager.getDynamicPropertiesStore().saveAllowEnergyAdjustment(entry.getValue()); break; diff --git a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java index d5a50ea4f9d..434361112b6 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java @@ -373,6 +373,44 @@ public void tvmFreezeV2SwitchTest() { Assert.assertNotNull(totalAcquiredResourcePcc); } + @Test + public void closeShieldedTRC20TransactionTest() { + // With the kill-switch engaged at the matching level, each shielded proof + // precompile short-circuits to (success, 32 zero bytes) as the very first step of + // execute(), before the null/size guards run. The input contents and length are + // therefore irrelevant here; these arrays are just non-null placeholders. + byte[] zero = DataWord.ZERO().getData(); + PrecompiledContracts.VerifyMintProof mint = new PrecompiledContracts.VerifyMintProof(); + PrecompiledContracts.VerifyTransferProof transfer = + new PrecompiledContracts.VerifyTransferProof(); + PrecompiledContracts.VerifyBurnProof burn = new PrecompiledContracts.VerifyBurnProof(); + byte[] mintData = new byte[1]; + byte[] transferData = new byte[1]; + byte[] burnData = new byte[1]; + try { + // level 1 closes mint + VMConfig.initCloseShieldedTRC20Transaction(1); + Assert.assertEquals(1L, VMConfig.closeShieldedTRC20Transaction()); + Pair r = mint.execute(mintData); + Assert.assertTrue(r.getLeft()); + Assert.assertArrayEquals(zero, r.getRight()); + + // level 2 also closes transfer + VMConfig.initCloseShieldedTRC20Transaction(2); + r = transfer.execute(transferData); + Assert.assertTrue(r.getLeft()); + Assert.assertArrayEquals(zero, r.getRight()); + + // level 3 also closes burn + VMConfig.initCloseShieldedTRC20Transaction(3); + r = burn.execute(burnData); + Assert.assertTrue(r.getLeft()); + Assert.assertArrayEquals(zero, r.getRight()); + } finally { + VMConfig.initCloseShieldedTRC20Transaction(0); + } + } + @Test public void delegatableResourceTest() { VMConfig.initAllowTvmFreezeV2(1L); diff --git a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java index 16a3cb3a5bb..01b5ae447eb 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java @@ -331,6 +331,15 @@ public void validateCheck() { "[ALLOW_STRICT_MATH] has been valid, no need to propose again", e34.getMessage()); + // CLOSE_SHIELDED_TRC20_TRANSACTION requires VERSION_4_8_2. VERSION_4_7_7 + // is already active here but 4_8_2 is not, so the proposal is still rejected. + ContractValidateException e35 = Assert.assertThrows(ContractValidateException.class, + () -> ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.CLOSE_SHIELDED_TRC20_TRANSACTION.getCode(), 1)); + Assert.assertEquals( + "Bad chain parameter id [CLOSE_SHIELDED_TRC20_TRANSACTION]", + e35.getMessage()); + testEnergyAdjustmentProposal(); testConsensusLogicOptimizationProposal(); @@ -349,11 +358,41 @@ public void validateCheck() { testAllowHardenExchangeCalculationProposal(); + testCloseShieldedTRC20TransactionProposal(); + forkUtils.getManager().getDynamicPropertiesStore() .statsByVersion(ForkBlockVersionEnum.ENERGY_LIMIT.getValue(), stats); forkUtils.reset(); } + private void testCloseShieldedTRC20TransactionProposal() { + // VERSION_4_8_2 has already been activated by the preceding helpers, so the + // validator only enforces the 0-3 value range from here on. + long code = ProposalType.CLOSE_SHIELDED_TRC20_TRANSACTION.getCode(); + String rangeError = + "This value[CLOSE_SHIELDED_TRC20_TRANSACTION] is only allowed to be in the range 0-3"; + + // below range -> rejected + ContractValidateException e1 = Assert.assertThrows(ContractValidateException.class, + () -> ProposalUtil.validator(dynamicPropertiesStore, forkUtils, code, -1)); + Assert.assertEquals(rangeError, e1.getMessage()); + + // above range -> rejected + ContractValidateException e2 = Assert.assertThrows(ContractValidateException.class, + () -> ProposalUtil.validator(dynamicPropertiesStore, forkUtils, code, 4)); + Assert.assertEquals(rangeError, e2.getMessage()); + + // every value in [0, 3] is accepted + for (long value = 0; value <= 3; value++) { + final long v = value; + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, code, v); + } catch (ContractValidateException e) { + Assert.fail("value " + v + " should be valid: " + e.getMessage()); + } + } + } + private void testEnergyAdjustmentProposal() { // Should fail because cannot pass the fork controller check ContractValidateException e1 = Assert.assertThrows(ContractValidateException.class, diff --git a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java index 5732e6f1cde..cb25c01d31d 100644 --- a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java @@ -72,6 +72,25 @@ public void test() { } } + @Test + public void testCloseShieldedTRC20Transaction() { + long code = ProposalType.CLOSE_SHIELDED_TRC20_TRANSACTION.getCode(); + + // process persists the proposed value into the dynamic properties store + Proposal proposal = Proposal.newBuilder().putParameters(code, 2).build(); + boolean result = ProposalService.process(dbManager, new ProposalCapsule(proposal)); + Assert.assertTrue(result); + Assert.assertEquals(2L, + dbManager.getDynamicPropertiesStore().getCloseShieldedTRC20Transaction()); + + // a subsequent proposal overwrites the stored value + proposal = Proposal.newBuilder().putParameters(code, 3).build(); + result = ProposalService.process(dbManager, new ProposalCapsule(proposal)); + Assert.assertTrue(result); + Assert.assertEquals(3L, + dbManager.getDynamicPropertiesStore().getCloseShieldedTRC20Transaction()); + } + @Test public void testUpdateEnergyFee() { String preHistory = dbManager.getDynamicPropertiesStore().getEnergyPriceHistory(); diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index c3ac5800971..8f817cfb0d6 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -207,6 +207,7 @@ public static void init() throws IOException { manager.getAccountStore().put(ownerCapsule.createDbKey(), ownerCapsule); manager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); manager.getDynamicPropertiesStore().saveAllowShieldedTRC20Transaction(1); + manager.getDynamicPropertiesStore().saveCloseShieldedTRC20Transaction(0); APP_FIXTURE.startApp(); }