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 0dc8fb31ada..8a6011fc605 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -694,6 +694,10 @@ public Pair execute(byte[] data) { int expLen = parseLen(data, 1); int modLen = parseLen(data, 2); + if (baseLen == 0 && modLen == 0 && expLen > UPPER_BOUND) { + MUtil.checkCPUTimeForModExp(); + } + if (VMConfig.allowTvmOsaka() && (baseLen > UPPER_BOUND || expLen > UPPER_BOUND || modLen > UPPER_BOUND)) { return Pair.of(false, EMPTY_BYTE_ARRAY); 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 3ed968e1afa..41822df2391 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 @@ -1625,6 +1625,9 @@ public void createContract2(DataWord value, DataWord memStart, DataWord memSize, stackPushZero(); return; } + if (getCallDeep() == MAX_DEPTH) { + MUtil.checkCPUTimeForCreate2(); + } if (VMConfig.allowTvmIstanbul()) { senderAddress = getContextAddress(); } else { diff --git a/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java b/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java index c94f28b3a2f..e07360e6863 100644 --- a/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java +++ b/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java @@ -64,4 +64,16 @@ public static void checkCPUTime() { throw new OutOfTimeException("CPU timeout for 0x0a executing"); } } + + public static void checkCPUTimeForCreate2() { + if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_8_1_1)) { + throw new OutOfTimeException("CPU timeout for create2 executing"); + } + } + + public static void checkCPUTimeForModExp() { + if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_8_1_1)) { + throw new OutOfTimeException("CPU timeout for modExp executing"); + } + } } diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index 5349ef8d875..233f1d9ef7a 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -29,7 +29,8 @@ public enum ForkBlockVersionEnum { VERSION_4_8_0(32, 1596780000000L, 80), VERSION_4_8_0_1(33, 1596780000000L, 70), VERSION_4_8_1(34, 1596780000000L, 80), - VERSION_4_8_2(35, 1596780000000L, 80); + VERSION_4_8_1_1(35, 1596780000000L, 70), + VERSION_4_8_2(36, 1596780000000L, 80); // if add a version, modify BLOCK_VERSION simultaneously @Getter @@ -78,7 +79,7 @@ public class ChainConstant { public static final int SINGLE_REPEAT = 1; public static final int BLOCK_FILLED_SLOTS_NUMBER = 128; public static final int MAX_FROZEN_NUMBER = 1; - public static final int BLOCK_VERSION = 35; + public static final int BLOCK_VERSION = 36; public static final long FROZEN_PERIOD = 86_400_000L; public static final long DELEGATE_PERIOD = 3 * 86_400_000L; public static final long TRX_PRECISION = 1000_000L; 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..f531370aea5 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 @@ -1,6 +1,7 @@ package org.tron.core.config.args; import static java.lang.System.exit; +import static org.fusesource.jansi.Ansi.ansi; import static org.tron.common.math.Maths.max; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; import static org.tron.core.Constant.ENERGY_LIMIT_IN_CONSTANT_TX; @@ -36,6 +37,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.fusesource.jansi.AnsiConsole; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.arch.Arch; diff --git a/framework/src/main/java/org/tron/program/Version.java b/framework/src/main/java/org/tron/program/Version.java index 3ce7ce20312..73e4f1e826b 100644 --- a/framework/src/main/java/org/tron/program/Version.java +++ b/framework/src/main/java/org/tron/program/Version.java @@ -2,8 +2,8 @@ public class Version { - public static final String VERSION_NAME = "GreatVoyage-v4.8.0.1-1-g44a4bc8263"; - public static final String VERSION_CODE = "18636"; + public static final String VERSION_NAME = "GreatVoyage-v4.8.1-6-g52d7d9d23e"; + public static final String VERSION_CODE = "18643"; private static final String VERSION = "4.8.2"; public static String getVersion() { diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2ModExpForkTest.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2ModExpForkTest.java new file mode 100644 index 00000000000..bef91eaef37 --- /dev/null +++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2ModExpForkTest.java @@ -0,0 +1,177 @@ +package org.tron.common.runtime.vm; + +import java.util.Arrays; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.runtime.InternalTransaction; +import org.tron.common.utils.ForkController; +import org.tron.core.Constant; +import org.tron.core.config.Parameter.ForkBlockVersionEnum; +import org.tron.core.config.args.Args; +import org.tron.core.exception.ContractValidateException; +import org.tron.core.store.StoreFactory; +import org.tron.core.vm.PrecompiledContracts; +import org.tron.core.vm.PrecompiledContracts.PrecompiledContract; +import org.tron.core.vm.config.ConfigLoader; +import org.tron.core.vm.config.VMConfig; +import org.tron.core.vm.program.Program; +import org.tron.core.vm.program.Program.OutOfTimeException; +import org.tron.core.vm.program.invoke.ProgramInvokeMockImpl; +import org.tron.core.vm.utils.MUtil; +import org.tron.protos.Protocol; + + +@Slf4j +public class Create2ModExpForkTest extends BaseTest { + + // mirrors the private Program.MAX_DEPTH + private static final int MAX_CALL_DEPTH = 64; + + // mirrors PrecompiledContracts.ModExp.UPPER_BOUND + private static final int MOD_EXP_UPPER_BOUND = 1024; + + // ModExp precompile address (0x05) + private static final DataWord MOD_EXP_ADDR = new DataWord( + "0000000000000000000000000000000000000000000000000000000000000005"); + + @BeforeClass + public static void init() { + Args.setParam(new String[] {"--output-directory", dbPath(), "--debug"}, Constant.TEST_CONF); + CommonParameter.getInstance().setDebug(true); + } + + @AfterClass + public static void destroy() { + ConfigLoader.disable = false; + VMConfig.initVmHardFork(false); + VMConfig.initAllowTvmCompatibleEvm(0); + Args.clearParam(); + } + + @Before + public void setUp() { + ForkController.instance().init(chainBaseManager); + deactivateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + } + + @Test + public void checkCPUTimeForCreate2_isGatedByFork() { + MUtil.checkCPUTimeForCreate2(); + + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + OutOfTimeException ex = + Assert.assertThrows(OutOfTimeException.class, MUtil::checkCPUTimeForCreate2); + Assert.assertEquals("CPU timeout for create2 executing", ex.getMessage()); + } + + @Test + public void checkCPUTimeForModExp_isGatedByFork() { + MUtil.checkCPUTimeForModExp(); + + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + OutOfTimeException ex = + Assert.assertThrows(OutOfTimeException.class, MUtil::checkCPUTimeForModExp); + Assert.assertEquals("CPU timeout for modExp executing", ex.getMessage()); + } + + @Test + public void modExp_degenerateInput_throwsOnlyAfterFork() { + PrecompiledContract modExp = PrecompiledContracts.getContractForAddress(MOD_EXP_ADDR); + byte[] data = buildModExpInput(MOD_EXP_UPPER_BOUND + 1); + + Pair out = modExp.execute(data); + Assert.assertTrue(out.getLeft()); + + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + OutOfTimeException ex = + Assert.assertThrows(OutOfTimeException.class, () -> modExp.execute(data)); + Assert.assertEquals("CPU timeout for modExp executing", ex.getMessage()); + } + + @Test + public void modExp_atUpperBound_doesNotThrowAfterFork() { + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + PrecompiledContract modExp = PrecompiledContracts.getContractForAddress(MOD_EXP_ADDR); + Pair out = modExp.execute(buildModExpInput(MOD_EXP_UPPER_BOUND)); + Assert.assertTrue(out.getLeft()); + } + + @Test + public void createContract2_atMaxDepth_legacyPath_throwsAfterFork() + throws ContractValidateException { + VMConfig.initAllowTvmCompatibleEvm(0); + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + Program program = buildProgramAtMaxDepth(); + OutOfTimeException ex = Assert.assertThrows(OutOfTimeException.class, + () -> program.createContract2( + DataWord.ZERO(), DataWord.ZERO(), DataWord.ZERO(), DataWord.ZERO())); + Assert.assertEquals("CPU timeout for create2 executing", ex.getMessage()); + } + + @Test + public void createContract2_atMaxDepth_compatibleEvmOn_doesNotThrow() + throws ContractValidateException { + VMConfig.initAllowTvmCompatibleEvm(1); + activateFork(ForkBlockVersionEnum.VERSION_4_8_1_1); + + Program program = buildProgramAtMaxDepth(); + program.createContract2(DataWord.ZERO(), DataWord.ZERO(), DataWord.ZERO(), DataWord.ZERO()); + Assert.assertEquals(DataWord.ZERO(), program.getStack().pop()); + } + + // ---- helpers --------------------------------------------------------------------------------- + + private Program buildProgramAtMaxDepth() throws ContractValidateException { + StoreFactory.init(); + StoreFactory storeFactory = StoreFactory.getInstance(); + storeFactory.setChainBaseManager(chainBaseManager); + byte[] ops = new byte[] {0}; + ProgramInvokeMockImpl invoke = new ProgramInvokeMockImpl(storeFactory, ops, ops) { + @Override + public int getCallDeep() { + return MAX_CALL_DEPTH; + } + }; + Program program = new Program(ops, ops, invoke, + new InternalTransaction(Protocol.Transaction.getDefaultInstance(), + InternalTransaction.TrxType.TRX_UNKNOWN_TYPE)); + program.setRootTransactionId(new byte[32]); + return program; + } + + private byte[] buildModExpInput(int expLen) { + byte[] data = new byte[96]; + byte[] expLenWord = new DataWord(expLen).getData(); + System.arraycopy(expLenWord, 0, data, 32, 32); + return data; + } + + private void activateFork(ForkBlockVersionEnum forkVersion) { + byte[] stats = new byte[27]; + Arrays.fill(stats, (byte) 1); + chainBaseManager.getDynamicPropertiesStore().statsByVersion(forkVersion.getValue(), stats); + long maintenanceTimeInterval = + chainBaseManager.getDynamicPropertiesStore().getMaintenanceTimeInterval(); + long hardForkTime = ((forkVersion.getHardForkTime() - 1) / maintenanceTimeInterval + 1) + * maintenanceTimeInterval; + chainBaseManager.getDynamicPropertiesStore().saveLatestBlockHeaderTimestamp(hardForkTime + 1); + } + + private void deactivateFork(ForkBlockVersionEnum forkVersion) { + chainBaseManager.getDynamicPropertiesStore() + .statsByVersion(forkVersion.getValue(), new byte[27]); + chainBaseManager.getDynamicPropertiesStore().saveLatestBlockHeaderTimestamp(0L); + } +}