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 f604013325e..d785951027b 100644 --- a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java @@ -120,7 +120,7 @@ public void validate(Object object) throws ContractValidateException { } // Load Config - ConfigLoader.load(context.getStoreFactory()); + ConfigLoader.load(context.getStoreFactory(), isConstantCall); // Warm up registry class OperationRegistry.init(); trx = context.getTrxCap().getInstance(); diff --git a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java index 3641548b3e5..a2c4b59fdc5 100644 --- a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java +++ b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java @@ -365,6 +365,10 @@ public static long getVoteWitnessCost(Program program) { } public static long getVoteWitnessCost2(Program program) { + if (!VMConfig.allowEnergyAdjustment()) { + return getVoteWitnessCost(program); + } + Stack stack = program.getStack(); long oldMemSize = program.getMemSize(); DataWord amountArrayLength = stack.get(stack.size() - 1).clone(); @@ -388,6 +392,10 @@ public static long getVoteWitnessCost2(Program program) { } public static long getVoteWitnessCost3(Program program) { + if (!VMConfig.allowTvmOsaka()) { + return getVoteWitnessCost2(program); + } + Stack stack = program.getStack(); long oldMemSize = program.getMemSize(); BigInteger amountArrayLength = stack.get(stack.size() - 1).value(); 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..35480935742 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 @@ -13,40 +13,48 @@ public class ConfigLoader { //only for unit test public static boolean disable = false; - public static void load(StoreFactory storeFactory) { + // isolate=true: a constant call bound to a non-HEAD (solidity/PBFT) snapshot installs its + // snapshot into a thread-local view instead of the process-wide global, so it cannot pollute + // the flags the block-processing path reads concurrently. + public static void load(StoreFactory storeFactory, boolean isolate) { if (!disable) { DynamicPropertiesStore ds = storeFactory.getChainBaseManager().getDynamicPropertiesStore(); VMConfig.setVmTrace(CommonParameter.getInstance().isVmTrace()); if (ds != null) { VMConfig.initVmHardFork(checkForEnergyLimit(ds)); - VMConfig.initAllowMultiSign(ds.getAllowMultiSign()); - VMConfig.initAllowTvmTransferTrc10(ds.getAllowTvmTransferTrc10()); - VMConfig.initAllowTvmConstantinople(ds.getAllowTvmConstantinople()); - VMConfig.initAllowTvmSolidity059(ds.getAllowTvmSolidity059()); - VMConfig.initAllowShieldedTRC20Transaction(ds.getAllowShieldedTRC20Transaction()); - VMConfig.initAllowTvmIstanbul(ds.getAllowTvmIstanbul()); - VMConfig.initAllowTvmFreeze(ds.getAllowTvmFreeze()); - VMConfig.initAllowTvmVote(ds.getAllowTvmVote()); - VMConfig.initAllowTvmLondon(ds.getAllowTvmLondon()); - VMConfig.initAllowTvmCompatibleEvm(ds.getAllowTvmCompatibleEvm()); - VMConfig.initAllowHigherLimitForMaxCpuTimeOfOneTx( - ds.getAllowHigherLimitForMaxCpuTimeOfOneTx()); - VMConfig.initAllowTvmFreezeV2(ds.supportUnfreezeDelay() ? 1 : 0); - VMConfig.initAllowOptimizedReturnValueOfChainId( - ds.getAllowOptimizedReturnValueOfChainId()); - VMConfig.initAllowDynamicEnergy(ds.getAllowDynamicEnergy()); - VMConfig.initDynamicEnergyThreshold(ds.getDynamicEnergyThreshold()); - VMConfig.initDynamicEnergyIncreaseFactor(ds.getDynamicEnergyIncreaseFactor()); - VMConfig.initDynamicEnergyMaxFactor(ds.getDynamicEnergyMaxFactor()); - VMConfig.initAllowTvmShangHai(ds.getAllowTvmShangHai()); - VMConfig.initAllowEnergyAdjustment(ds.getAllowEnergyAdjustment()); - VMConfig.initAllowStrictMath(ds.getAllowStrictMath()); - VMConfig.initAllowTvmCancun(ds.getAllowTvmCancun()); - VMConfig.initDisableJavaLangMath(ds.getConsensusLogicOptimization()); - VMConfig.initAllowTvmBlob(ds.getAllowTvmBlob()); - VMConfig.initAllowTvmSelfdestructRestriction(ds.getAllowTvmSelfdestructRestriction()); - VMConfig.initAllowTvmOsaka(ds.getAllowTvmOsaka()); - VMConfig.initAllowHardenResourceCalculation(ds.getAllowHardenResourceCalculation()); + VMConfig.Snapshot snapshot = new VMConfig.Snapshot(); + snapshot.allowMultiSign = ds.getAllowMultiSign() == 1; + snapshot.allowTvmTransferTrc10 = ds.getAllowTvmTransferTrc10() == 1; + snapshot.allowTvmConstantinople = ds.getAllowTvmConstantinople() == 1; + snapshot.allowTvmSolidity059 = ds.getAllowTvmSolidity059() == 1; + snapshot.allowShieldedTRC20Transaction = ds.getAllowShieldedTRC20Transaction() == 1; + snapshot.allowTvmIstanbul = ds.getAllowTvmIstanbul() == 1; + snapshot.allowTvmFreeze = ds.getAllowTvmFreeze() == 1; + snapshot.allowTvmVote = ds.getAllowTvmVote() == 1; + snapshot.allowTvmLondon = ds.getAllowTvmLondon() == 1; + snapshot.allowTvmCompatibleEvm = ds.getAllowTvmCompatibleEvm() == 1; + snapshot.allowHigherLimitForMaxCpuTimeOfOneTx = + ds.getAllowHigherLimitForMaxCpuTimeOfOneTx() == 1; + snapshot.allowTvmFreezeV2 = ds.supportUnfreezeDelay(); + snapshot.allowOptimizedReturnValueOfChainId = ds.getAllowOptimizedReturnValueOfChainId() == 1; + snapshot.allowDynamicEnergy = ds.getAllowDynamicEnergy() == 1; + snapshot.dynamicEnergyThreshold = ds.getDynamicEnergyThreshold(); + snapshot.dynamicEnergyIncreaseFactor = ds.getDynamicEnergyIncreaseFactor(); + snapshot.dynamicEnergyMaxFactor = ds.getDynamicEnergyMaxFactor(); + snapshot.allowTvmShanghai = ds.getAllowTvmShangHai() == 1; + snapshot.allowEnergyAdjustment = ds.getAllowEnergyAdjustment() == 1; + snapshot.allowStrictMath = ds.getAllowStrictMath() == 1; + snapshot.allowTvmCancun = ds.getAllowTvmCancun() == 1; + snapshot.disableJavaLangMath = ds.getConsensusLogicOptimization() == 1; + snapshot.allowTvmBlob = ds.getAllowTvmBlob() == 1; + snapshot.allowTvmSelfdestructRestriction = ds.getAllowTvmSelfdestructRestriction() == 1; + snapshot.allowTvmOsaka = ds.getAllowTvmOsaka() == 1; + snapshot.allowHardenResourceCalculation = ds.getAllowHardenResourceCalculation() == 1; + if (isolate) { + VMConfig.setLocalSnapshot(snapshot); + } else { + VMConfig.setGlobalSnapshot(snapshot); + } } } } 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..304ced33698 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 @@ -13,57 +13,74 @@ public class VMConfig { @Setter private static boolean vmTrace = false; - private static boolean ALLOW_TVM_TRANSFER_TRC10 = false; - - private static boolean ALLOW_TVM_CONSTANTINOPLE = false; - - private static boolean ALLOW_MULTI_SIGN = false; - - private static boolean ALLOW_TVM_SOLIDITY_059 = false; - - private static boolean ALLOW_SHIELDED_TRC20_TRANSACTION = false; - - private static boolean ALLOW_TVM_ISTANBUL = false; - - private static boolean ALLOW_TVM_FREEZE = false; - - private static boolean ALLOW_TVM_VOTE = false; - - private static boolean ALLOW_TVM_LONDON = false; - - private static boolean ALLOW_TVM_COMPATIBLE_EVM = false; - - private static boolean ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = false; - - private static boolean ALLOW_TVM_FREEZE_V2 = false; - - private static boolean ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID = false; - - private static boolean ALLOW_DYNAMIC_ENERGY = false; - - private static long DYNAMIC_ENERGY_THRESHOLD = 0L; - - private static long DYNAMIC_ENERGY_INCREASE_FACTOR = 0L; - - private static long DYNAMIC_ENERGY_MAX_FACTOR = 0L; - - private static boolean ALLOW_TVM_SHANGHAI = false; - - private static boolean ALLOW_ENERGY_ADJUSTMENT = false; - - private static boolean ALLOW_STRICT_MATH = false; - - private static boolean ALLOW_TVM_CANCUN = false; - - private static Boolean DISABLE_JAVA_LANG_MATH = false; - - private static boolean ALLOW_TVM_BLOB = false; - - private static boolean ALLOW_TVM_SELFDESTRUCT_RESTRICTION = false; - - private static boolean ALLOW_TVM_OSAKA = false; - - private static boolean ALLOW_HARDEN_RESOURCE_CALCULATION = false; + /** + * Snapshot of all chain/store-derived VM config flags. The block-processing (HEAD) path + * installs it as the process-wide {@link #globalSnapshot}; a constant call executing against a + * non-HEAD (solidity/PBFT) snapshot installs its own view into {@link #localSnapshot} so it never + * overwrites the flags the consensus path relies on. + */ + public static class Snapshot { + public boolean allowTvmTransferTrc10; + public boolean allowTvmConstantinople; + public boolean allowMultiSign; + public boolean allowTvmSolidity059; + public boolean allowShieldedTRC20Transaction; + public boolean allowTvmIstanbul; + public boolean allowTvmFreeze; + public boolean allowTvmVote; + public boolean allowTvmLondon; + public boolean allowTvmCompatibleEvm; + public boolean allowHigherLimitForMaxCpuTimeOfOneTx; + public boolean allowTvmFreezeV2; + public boolean allowOptimizedReturnValueOfChainId; + public boolean allowDynamicEnergy; + public long dynamicEnergyThreshold; + public long dynamicEnergyIncreaseFactor; + public long dynamicEnergyMaxFactor; + public boolean allowTvmShanghai; + public boolean allowEnergyAdjustment; + public boolean allowStrictMath; + public boolean allowTvmCancun; + public boolean disableJavaLangMath; + public boolean allowTvmBlob; + public boolean allowTvmSelfdestructRestriction; + public boolean allowTvmOsaka; + public boolean allowHardenResourceCalculation; + } + + // HEAD / block-processing config, written by the consensus path; read by everyone with no + // thread-local override. volatile so a wholesale install is safely published across threads. + private static volatile Snapshot globalSnapshot = new Snapshot(); + + // Per-thread override used only by constant calls bound to a non-HEAD (solidity/PBFT) snapshot. + private static final ThreadLocal localSnapshot = new ThreadLocal<>(); + + private static Snapshot current() { + Snapshot local = localSnapshot.get(); + return local != null ? local : globalSnapshot; + } + + /** + * Install the process-wide (HEAD / block-processing) config and drop any thread-local view. + */ + public static void setGlobalSnapshot(Snapshot snapshot) { + globalSnapshot = snapshot; + localSnapshot.remove(); + } + + /** + * Install a thread-local config view for a constant call executing against a non-HEAD snapshot. + */ + public static void setLocalSnapshot(Snapshot snapshot) { + localSnapshot.set(snapshot); + } + + /** + * Drop the thread-local config view so this thread falls back to the global config. + */ + public static void clearLocalSnapshot() { + localSnapshot.remove(); + } private VMConfig() { } @@ -80,108 +97,111 @@ public static void initVmHardFork(boolean pass) { CommonParameter.ENERGY_LIMIT_HARD_FORK = pass; } + // The init* setters below mutate the global (HEAD) config in place. They are kept for tests and + // legacy callers; production config loading goes through ConfigLoader -> setGlobalSnapshot, which + // publishes a fresh Snapshot wholesale via the volatile field. public static void initAllowMultiSign(long allow) { - ALLOW_MULTI_SIGN = allow == 1; + globalSnapshot.allowMultiSign = allow == 1; } public static void initAllowTvmTransferTrc10(long allow) { - ALLOW_TVM_TRANSFER_TRC10 = allow == 1; + globalSnapshot.allowTvmTransferTrc10 = allow == 1; } public static void initAllowTvmConstantinople(long allow) { - ALLOW_TVM_CONSTANTINOPLE = allow == 1; + globalSnapshot.allowTvmConstantinople = allow == 1; } public static void initAllowTvmSolidity059(long allow) { - ALLOW_TVM_SOLIDITY_059 = allow == 1; + globalSnapshot.allowTvmSolidity059 = allow == 1; } public static void initAllowShieldedTRC20Transaction(long allow) { - ALLOW_SHIELDED_TRC20_TRANSACTION = allow == 1; + globalSnapshot.allowShieldedTRC20Transaction = allow == 1; } public static void initAllowTvmIstanbul(long allow) { - ALLOW_TVM_ISTANBUL = allow == 1; + globalSnapshot.allowTvmIstanbul = allow == 1; } public static void initAllowTvmFreeze(long allow) { - ALLOW_TVM_FREEZE = allow == 1; + globalSnapshot.allowTvmFreeze = allow == 1; } public static void initAllowTvmVote(long allow) { - ALLOW_TVM_VOTE = allow == 1; + globalSnapshot.allowTvmVote = allow == 1; } public static void initAllowTvmLondon(long allow) { - ALLOW_TVM_LONDON = allow == 1; + globalSnapshot.allowTvmLondon = allow == 1; } public static void initAllowTvmCompatibleEvm(long allow) { - ALLOW_TVM_COMPATIBLE_EVM = allow == 1; + globalSnapshot.allowTvmCompatibleEvm = allow == 1; } public static void initAllowHigherLimitForMaxCpuTimeOfOneTx(long allow) { - ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = allow == 1; + globalSnapshot.allowHigherLimitForMaxCpuTimeOfOneTx = allow == 1; } public static void initAllowTvmFreezeV2(long allow) { - ALLOW_TVM_FREEZE_V2 = allow == 1; + globalSnapshot.allowTvmFreezeV2 = allow == 1; } public static void initAllowOptimizedReturnValueOfChainId(long allow) { - ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID = allow == 1; + globalSnapshot.allowOptimizedReturnValueOfChainId = allow == 1; } public static void initAllowDynamicEnergy(long allow) { - ALLOW_DYNAMIC_ENERGY = allow == 1; + globalSnapshot.allowDynamicEnergy = allow == 1; } public static void initDynamicEnergyThreshold(long threshold) { - DYNAMIC_ENERGY_THRESHOLD = threshold; + globalSnapshot.dynamicEnergyThreshold = threshold; } public static void initDynamicEnergyIncreaseFactor(long increaseFactor) { - DYNAMIC_ENERGY_INCREASE_FACTOR = increaseFactor; + globalSnapshot.dynamicEnergyIncreaseFactor = increaseFactor; } public static void initDynamicEnergyMaxFactor(long maxFactor) { - DYNAMIC_ENERGY_MAX_FACTOR = maxFactor; + globalSnapshot.dynamicEnergyMaxFactor = maxFactor; } public static void initAllowTvmShangHai(long allow) { - ALLOW_TVM_SHANGHAI = allow == 1; + globalSnapshot.allowTvmShanghai = allow == 1; } public static void initAllowEnergyAdjustment(long allow) { - ALLOW_ENERGY_ADJUSTMENT = allow == 1; + globalSnapshot.allowEnergyAdjustment = allow == 1; } public static void initAllowStrictMath(long allow) { - ALLOW_STRICT_MATH = allow == 1; + globalSnapshot.allowStrictMath = allow == 1; } public static void initAllowTvmCancun(long allow) { - ALLOW_TVM_CANCUN = allow == 1; + globalSnapshot.allowTvmCancun = allow == 1; } public static void initDisableJavaLangMath(long allow) { - DISABLE_JAVA_LANG_MATH = allow == 1; + globalSnapshot.disableJavaLangMath = allow == 1; } public static void initAllowTvmBlob(long allow) { - ALLOW_TVM_BLOB = allow == 1; + globalSnapshot.allowTvmBlob = allow == 1; } public static void initAllowTvmSelfdestructRestriction(long allow) { - ALLOW_TVM_SELFDESTRUCT_RESTRICTION = allow == 1; + globalSnapshot.allowTvmSelfdestructRestriction = allow == 1; } public static void initAllowTvmOsaka(long allow) { - ALLOW_TVM_OSAKA = allow == 1; + globalSnapshot.allowTvmOsaka = allow == 1; } public static void initAllowHardenResourceCalculation(long allow) { - ALLOW_HARDEN_RESOURCE_CALCULATION = allow == 1; + globalSnapshot.allowHardenResourceCalculation = allow == 1; } public static boolean getEnergyLimitHardFork() { @@ -189,106 +209,106 @@ public static boolean getEnergyLimitHardFork() { } public static boolean allowTvmTransferTrc10() { - return ALLOW_TVM_TRANSFER_TRC10; + return current().allowTvmTransferTrc10; } public static boolean allowTvmConstantinople() { - return ALLOW_TVM_CONSTANTINOPLE; + return current().allowTvmConstantinople; } public static boolean allowMultiSign() { - return ALLOW_MULTI_SIGN; + return current().allowMultiSign; } public static boolean allowTvmSolidity059() { - return ALLOW_TVM_SOLIDITY_059; + return current().allowTvmSolidity059; } public static boolean allowShieldedTRC20Transaction() { - return ALLOW_SHIELDED_TRC20_TRANSACTION; + return current().allowShieldedTRC20Transaction; } public static boolean allowTvmIstanbul() { - return ALLOW_TVM_ISTANBUL; + return current().allowTvmIstanbul; } public static boolean allowTvmFreeze() { - return ALLOW_TVM_FREEZE; + return current().allowTvmFreeze; } public static boolean allowTvmVote() { - return ALLOW_TVM_VOTE; + return current().allowTvmVote; } public static boolean allowTvmLondon() { - return ALLOW_TVM_LONDON; + return current().allowTvmLondon; } public static boolean allowTvmCompatibleEvm() { - return ALLOW_TVM_COMPATIBLE_EVM; + return current().allowTvmCompatibleEvm; } public static boolean allowHigherLimitForMaxCpuTimeOfOneTx() { - return ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX; + return current().allowHigherLimitForMaxCpuTimeOfOneTx; } public static boolean allowTvmFreezeV2() { - return ALLOW_TVM_FREEZE_V2; + return current().allowTvmFreezeV2; } public static boolean allowOptimizedReturnValueOfChainId() { - return ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID; + return current().allowOptimizedReturnValueOfChainId; } public static boolean allowDynamicEnergy() { - return ALLOW_DYNAMIC_ENERGY; + return current().allowDynamicEnergy; } public static long getDynamicEnergyThreshold() { - return DYNAMIC_ENERGY_THRESHOLD; + return current().dynamicEnergyThreshold; } public static long getDynamicEnergyIncreaseFactor() { - return DYNAMIC_ENERGY_INCREASE_FACTOR; + return current().dynamicEnergyIncreaseFactor; } public static long getDynamicEnergyMaxFactor() { - return DYNAMIC_ENERGY_MAX_FACTOR; + return current().dynamicEnergyMaxFactor; } public static boolean allowTvmShanghai() { - return ALLOW_TVM_SHANGHAI; + return current().allowTvmShanghai; } public static boolean allowEnergyAdjustment() { - return ALLOW_ENERGY_ADJUSTMENT; + return current().allowEnergyAdjustment; } public static boolean allowStrictMath() { - return ALLOW_STRICT_MATH; + return current().allowStrictMath; } public static boolean allowTvmCancun() { - return ALLOW_TVM_CANCUN; + return current().allowTvmCancun; } public static boolean disableJavaLangMath() { - return DISABLE_JAVA_LANG_MATH; + return current().disableJavaLangMath; } public static boolean allowTvmBlob() { - return ALLOW_TVM_BLOB; + return current().allowTvmBlob; } public static boolean allowTvmSelfdestructRestriction() { - return ALLOW_TVM_SELFDESTRUCT_RESTRICTION; + return current().allowTvmSelfdestructRestriction; } public static boolean allowTvmOsaka() { - return ALLOW_TVM_OSAKA; + return current().allowTvmOsaka; } public static boolean allowHardenResourceCalculation() { - return ALLOW_HARDEN_RESOURCE_CALCULATION; + return current().allowHardenResourceCalculation; } } diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 079b8e6f3e9..ac54cb2b7ff 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -202,6 +202,7 @@ import org.tron.core.store.VotesStore; import org.tron.core.store.WitnessStore; import org.tron.core.utils.TransactionUtil; +import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.program.Program; import org.tron.core.zen.ShieldedTRC20ParametersBuilder; import org.tron.core.zen.ShieldedTRC20ParametersBuilder.ShieldedTRC20ParametersType; @@ -3157,8 +3158,14 @@ public Transaction callConstantContract(TransactionCapsule trxCap, StoreFactory.getInstance(), true, false); VMActuator vmActuator = new VMActuator(true); - vmActuator.validate(context); - vmActuator.execute(context); + try { + vmActuator.validate(context); + vmActuator.execute(context); + } finally { + // constant call runs on a pooled RPC worker; drop its thread-local VM config view so it + // can never leak into a later (block/broadcast) execution on the same thread. + VMConfig.clearLocalSnapshot(); + } ProgramResult result = context.getProgramResult(); if (!isEstimating && result.getException() != null diff --git a/framework/src/test/java/org/tron/common/runtime/vm/VMConfigIsolationTest.java b/framework/src/test/java/org/tron/common/runtime/vm/VMConfigIsolationTest.java new file mode 100644 index 00000000000..845db6dd6af --- /dev/null +++ b/framework/src/test/java/org/tron/common/runtime/vm/VMConfigIsolationTest.java @@ -0,0 +1,108 @@ +package org.tron.common.runtime.vm; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.tron.core.vm.config.VMConfig; + +public class VMConfigIsolationTest { + + // Tests mutate the process-wide static VMConfig; snapshot it before each test and restore it + // after so this class never pollutes other VM tests sharing the same JVM fork (forkEvery=100). + private VMConfig.Snapshot savedGlobal; + + @Before + public void snapshotConfig() { + VMConfig.clearLocalSnapshot(); + savedGlobal = snapshotGlobal(); + } + + @After + public void restoreConfig() { + VMConfig.clearLocalSnapshot(); + VMConfig.setGlobalSnapshot(savedGlobal); + } + + /** + * A constant call's thread-local config view must not pollute the global config that the + * (concurrent) block-processing path reads. This is the core Problem-2 guarantee. + */ + @Test + public void testLocalConfigDoesNotPolluteGlobalAcrossThreads() throws InterruptedException { + VMConfig.initAllowTvmOsaka(1); // global (HEAD) view: activated + assertTrue(VMConfig.allowTvmOsaka()); // no thread-local -> reads global + + VMConfig.Snapshot local = new VMConfig.Snapshot(); + local.allowTvmOsaka = false; // simulate a not-yet-solidified snapshot + VMConfig.setLocalSnapshot(local); + + // this thread now sees its own (solidity) view... + assertFalse(VMConfig.allowTvmOsaka()); + + // ...but another thread (e.g. block processing) must still see the global HEAD value. + AtomicBoolean otherThreadSaw = new AtomicBoolean(false); + Thread t = new Thread(() -> otherThreadSaw.set(VMConfig.allowTvmOsaka())); + t.start(); + t.join(); + assertTrue("global config must be unaffected by another thread's local view", + otherThreadSaw.get()); + + // after dropping the local view, this thread falls back to the global value again. + VMConfig.clearLocalSnapshot(); + assertTrue(VMConfig.allowTvmOsaka()); + } + + /** + * A block/broadcast load (setGlobalSnapshot) must drop any thread-local view, so the consensus + * path can never read a constant call's leaked snapshot left on the same pooled worker thread. + */ + @Test + public void testSetGlobalConfigDropsLocalView() { + VMConfig.Snapshot local = new VMConfig.Snapshot(); + local.allowTvmOsaka = true; + VMConfig.setLocalSnapshot(local); + assertTrue(VMConfig.allowTvmOsaka()); + + VMConfig.Snapshot head = new VMConfig.Snapshot(); + head.allowTvmOsaka = false; + VMConfig.setGlobalSnapshot(head); + assertFalse("setGlobalSnapshot must drop the thread-local view", VMConfig.allowTvmOsaka()); + } + + // Deep-copy the current global config through the public getters (no thread-local set here, so + // the getters read the global) so @After can restore the exact prior state. + private static VMConfig.Snapshot snapshotGlobal() { + VMConfig.Snapshot snapshot = new VMConfig.Snapshot(); + snapshot.allowTvmTransferTrc10 = VMConfig.allowTvmTransferTrc10(); + snapshot.allowTvmConstantinople = VMConfig.allowTvmConstantinople(); + snapshot.allowMultiSign = VMConfig.allowMultiSign(); + snapshot.allowTvmSolidity059 = VMConfig.allowTvmSolidity059(); + snapshot.allowShieldedTRC20Transaction = VMConfig.allowShieldedTRC20Transaction(); + snapshot.allowTvmIstanbul = VMConfig.allowTvmIstanbul(); + snapshot.allowTvmFreeze = VMConfig.allowTvmFreeze(); + snapshot.allowTvmVote = VMConfig.allowTvmVote(); + snapshot.allowTvmLondon = VMConfig.allowTvmLondon(); + snapshot.allowTvmCompatibleEvm = VMConfig.allowTvmCompatibleEvm(); + snapshot.allowHigherLimitForMaxCpuTimeOfOneTx = VMConfig.allowHigherLimitForMaxCpuTimeOfOneTx(); + snapshot.allowTvmFreezeV2 = VMConfig.allowTvmFreezeV2(); + snapshot.allowOptimizedReturnValueOfChainId = VMConfig.allowOptimizedReturnValueOfChainId(); + snapshot.allowDynamicEnergy = VMConfig.allowDynamicEnergy(); + snapshot.dynamicEnergyThreshold = VMConfig.getDynamicEnergyThreshold(); + snapshot.dynamicEnergyIncreaseFactor = VMConfig.getDynamicEnergyIncreaseFactor(); + snapshot.dynamicEnergyMaxFactor = VMConfig.getDynamicEnergyMaxFactor(); + snapshot.allowTvmShanghai = VMConfig.allowTvmShanghai(); + snapshot.allowEnergyAdjustment = VMConfig.allowEnergyAdjustment(); + snapshot.allowStrictMath = VMConfig.allowStrictMath(); + snapshot.allowTvmCancun = VMConfig.allowTvmCancun(); + snapshot.disableJavaLangMath = VMConfig.disableJavaLangMath(); + snapshot.allowTvmBlob = VMConfig.allowTvmBlob(); + snapshot.allowTvmSelfdestructRestriction = VMConfig.allowTvmSelfdestructRestriction(); + snapshot.allowTvmOsaka = VMConfig.allowTvmOsaka(); + snapshot.allowHardenResourceCalculation = VMConfig.allowHardenResourceCalculation(); + return snapshot; + } +} diff --git a/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java b/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java index 75b11f4ab9d..2c7aa238033 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java @@ -1,7 +1,9 @@ package org.tron.common.runtime.vm; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -142,11 +144,14 @@ public void testLargeArrayLengthOverflow() { zeroOffset, largeLength, 0); boolean overflowCaught = false; + VMConfig.initAllowTvmOsaka(1); // cost3 self-dispatches; exercise the v3 (BigInteger) path try { EnergyCost.getVoteWitnessCost3(program); } catch (Program.OutOfMemoryException e) { // cost3 should detect memory overflow via checkMemorySize overflowCaught = true; + } finally { + VMConfig.initAllowTvmOsaka(0); } assertTrue("cost3 should throw memoryOverflow for huge array length", overflowCaught); } @@ -161,10 +166,13 @@ public void testLargeOffsetOverflow() { new DataWord(0), new DataWord(1), 0); boolean overflowCaught = false; + VMConfig.initAllowTvmOsaka(1); // cost3 self-dispatches; exercise the v3 (BigInteger) path try { EnergyCost.getVoteWitnessCost3(program); } catch (Program.OutOfMemoryException e) { overflowCaught = true; + } finally { + VMConfig.initAllowTvmOsaka(0); } assertTrue("cost3 should throw memoryOverflow for huge offset", overflowCaught); } @@ -239,4 +247,49 @@ public void testOperationRegistryWithOsaka() { VMConfig.initAllowTvmOsaka(0); } } + + @Test + public void testCost3FallsBackToCost2WhenOsakaOff() { + // cost3 self-dispatches: with osaka off it delegates to cost2, so a cost3 left in the shared + // jump table (e.g. read by a constant call whose view has osaka off) still charges v2 energy. + String maxHex = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + DataWord huge = new DataWord(maxHex); + DataWord zero = new DataWord(0); + + VMConfig.initAllowTvmOsaka(0); + long viaCost3 = + EnergyCost.getVoteWitnessCost3(mockProgram(zero, new DataWord(1), zero, huge, 0)); + long viaCost2 = + EnergyCost.getVoteWitnessCost2(mockProgram(zero, new DataWord(1), zero, huge, 0)); + assertEquals("cost3 must equal cost2 when osaka is off", viaCost2, viaCost3); + + // sanity: with osaka on, cost3 instead runs v3 and detects the overflow that cost2 wraps away. + VMConfig.initAllowTvmOsaka(1); + try { + EnergyCost.getVoteWitnessCost3(mockProgram(zero, new DataWord(1), zero, huge, 0)); + fail("cost3 with osaka on must overflow-throw on the huge length"); + } catch (Program.OutOfMemoryException expected) { + // expected + } finally { + VMConfig.initAllowTvmOsaka(0); + } + } + + @Test + public void testCost2FallsBackToLegacyWhenEnergyAdjustmentOff() { + // cost2 self-dispatches: with energyAdjustment off it delegates to the legacy cost. + VMConfig.initAllowEnergyAdjustment(0); + try { + long viaCost2 = EnergyCost.getVoteWitnessCost2(mockProgram(0, 2, 128, 2, 0)); + long viaLegacy = EnergyCost.getVoteWitnessCost(mockProgram(0, 2, 128, 2, 0)); + assertEquals("cost2 must equal legacy cost when energyAdjustment is off", + viaLegacy, viaCost2); + + // sanity: with energyAdjustment on, cost2 differs from the legacy cost for this input. + VMConfig.initAllowEnergyAdjustment(1); + assertNotEquals(viaLegacy, EnergyCost.getVoteWitnessCost2(mockProgram(0, 2, 128, 2, 0))); + } finally { + VMConfig.initAllowEnergyAdjustment(1); + } + } }