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 d785951027b..be0a7cc3401 100644 --- a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java @@ -217,6 +217,9 @@ public void execute(Object object) throws ContractExeException { } else { result.spendEnergy(saveCodeEnergy); if (VMConfig.allowTvmConstantinople()) { + CreateSmartContract createContract = + ContractCapsule.getSmartContractFromTransaction(trx); + checkContractHashFields(createContract.getNewContract()); rootRepository.saveCode(program.getContractAddress().getNoLeadZeroesData(), code); } } @@ -330,6 +333,7 @@ private void create() if (contract == null) { throw new ContractValidateException("Cannot get CreateSmartContract from transaction"); } + SmartContract newSmartContract; if (VMConfig.allowTvmCompatibleEvm()) { newSmartContract = contract.getNewContract().toBuilder().setVersion(1).build(); @@ -455,6 +459,12 @@ private void create() } + static void checkContractHashFields(SmartContract contract) { + if (!contract.getCodeHash().isEmpty() || !contract.getTrxHash().isEmpty()) { + MUtil.checkCPUTimeForContractHashFields(); + } + } + /** * ** */ 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 e07360e6863..e281896ec83 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 @@ -59,6 +59,12 @@ public static boolean isNotNullOrEmpty(String str) { return !isNullOrEmpty(str); } + public static void checkCPUTimeForContractHashFields() { + if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_8_2_2)) { + throw new OutOfTimeException("CPU timeout for contract hash fields"); + } + } + public static void checkCPUTime() { if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_7_1)) { throw new OutOfTimeException("CPU timeout for 0x0a 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 233f1d9ef7a..0f9402641e9 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -30,7 +30,8 @@ public enum ForkBlockVersionEnum { VERSION_4_8_0_1(33, 1596780000000L, 70), VERSION_4_8_1(34, 1596780000000L, 80), VERSION_4_8_1_1(35, 1596780000000L, 70), - VERSION_4_8_2(36, 1596780000000L, 80); + VERSION_4_8_2(36, 1596780000000L, 80), + VERSION_4_8_2_2(37, 1596780000000L, 70); // if add a version, modify BLOCK_VERSION simultaneously @Getter @@ -79,7 +80,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 = 36; + public static final int BLOCK_VERSION = 37; 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/test/java/org/tron/core/actuator/ContractHashValidationTest.java b/framework/src/test/java/org/tron/core/actuator/ContractHashValidationTest.java new file mode 100644 index 00000000000..a2dd0a51e4e --- /dev/null +++ b/framework/src/test/java/org/tron/core/actuator/ContractHashValidationTest.java @@ -0,0 +1,67 @@ +package org.tron.core.actuator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.tron.common.utils.ForkController; +import org.tron.core.config.Parameter.ForkBlockVersionEnum; +import org.tron.core.vm.program.Program.OutOfTimeException; +import org.tron.protos.contract.SmartContractOuterClass.SmartContract; + +public class ContractHashValidationTest { + + @Test + public void acceptsHashFieldsBeforeActivation() { + SmartContract contract = SmartContract.newBuilder() + .setCodeHash(ByteString.copyFromUtf8("code")) + .setTrxHash(ByteString.copyFromUtf8("transaction")) + .build(); + + runWithActivation(false, () -> VMActuator.checkContractHashFields(contract)); + } + + @Test + public void rejectsCodeHashAfterActivation() { + SmartContract contract = SmartContract.newBuilder() + .setCodeHash(ByteString.copyFromUtf8("code")) + .build(); + + OutOfTimeException exception = assertThrows(OutOfTimeException.class, + () -> runWithActivation(true, () -> VMActuator.checkContractHashFields(contract))); + + assertEquals("CPU timeout for contract hash fields", exception.getMessage()); + } + + @Test + public void rejectsTransactionHashAfterActivation() { + SmartContract contract = SmartContract.newBuilder() + .setTrxHash(ByteString.copyFromUtf8("transaction")) + .build(); + + OutOfTimeException exception = assertThrows(OutOfTimeException.class, + () -> runWithActivation(true, () -> VMActuator.checkContractHashFields(contract))); + + assertEquals("CPU timeout for contract hash fields", exception.getMessage()); + } + + @Test + public void acceptsEmptyHashFieldsAfterActivation() { + runWithActivation(true, + () -> VMActuator.checkContractHashFields(SmartContract.getDefaultInstance())); + } + + private void runWithActivation(boolean activated, Runnable action) { + ForkController controller = mock(ForkController.class); + when(controller.pass(ForkBlockVersionEnum.VERSION_4_8_2_2)).thenReturn(activated); + try (MockedStatic controllerMock = Mockito.mockStatic(ForkController.class)) { + controllerMock.when(ForkController::instance).thenReturn(controller); + action.run(); + } + } +}