Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6b653f8
feat(vm): implement TIP-7883 ModExp gas cost increase
yanghang8612 Apr 29, 2026
d4bdf30
test(vm): cover TIP-7883 ModExp pricing across nagydani vectors
yanghang8612 Apr 29, 2026
98ea4f7
feat(vm): implement TIP-7939 CLZ opcode
yanghang8612 May 6, 2026
9f39d7e
test(vm): cover CLZ unsigned cast and proposal-gate rejection
yanghang8612 May 6, 2026
cf144f3
refactor(vm): wrap TIP-7883 energy arithmetic with StrictMathWrapper
yanghang8612 May 7, 2026
881ef98
refactor(vm): extract TIP-7883 ModExp minimum energy constants
yanghang8612 May 8, 2026
cbec094
feat(vm): serve historical block hashes from state (TIP-2935)
yanghang8612 May 8, 2026
b5b8ee5
fix(vm): write TIP-2935 parent hash before tx loop in generateBlock
yanghang8612 May 8, 2026
7d9201f
test(vm): document TIP-7883 legacy pricing delta
yanghang8612 May 8, 2026
6ec19f5
test(vm): consolidate TIP-2935 unit tests into integration suite
yanghang8612 May 8, 2026
433f894
test(vm): reset Osaka flag after CLZ coverage
yanghang8612 May 8, 2026
4f88973
Merge pull request #6686 from yanghang8612/feat/tip-2935-historical-b…
CodeNinjaEvan May 8, 2026
7c7ff21
Merge remote-tracking branch 'origin/develop' into review/pr-6654-doc…
yanghang8612 May 8, 2026
512ce0c
Merge pull request #6656 from yanghang8612/implement-tip-7939
CodeNinjaEvan May 8, 2026
107490c
fix(net): fix RejectedExecutionException during shutdown trxHandlePoo…
0xbigapple May 8, 2026
02158fc
Merge pull request #6654 from yanghang8612/implement-tip-7883
CodeNinjaEvan May 8, 2026
da6dddb
feat(api): make API calls non-blocking (#6733)
xxo1shine May 8, 2026
54342dc
fix(actuator): remove actuator whitelist to avoid fork (#6723)
317787106 May 8, 2026
91cfae4
refactor(metrics): delete influxdb storage for metrics (#6725)
317787106 May 8, 2026
55da98e
fix(event): filter removed=true from solidity maps and sync contract …
xxo1shine May 8, 2026
fe25380
feat(sync): reduce memory pressure by deferring block deserialization…
xxo1shine May 8, 2026
9529fb8
feat(framework,actuator,common): replace fastjson with jackson (#6701)
halibobo1205 May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.protobuf.Any;
import com.google.protobuf.GeneratedMessageV3;
import lombok.Getter;
import org.tron.common.math.Maths;
import org.tron.common.utils.Commons;
import org.tron.common.utils.ForkController;
Expand All @@ -16,48 +17,36 @@

public abstract class AbstractActuator implements Actuator {

@Getter
protected Any any;
@Getter
protected ChainBaseManager chainBaseManager;
@Getter
protected Contract contract;
@Getter
protected TransactionCapsule tx;
protected ForkController forkController;

public AbstractActuator(ContractType type, Class<? extends GeneratedMessageV3> clazz) {
TransactionFactory.register(type, getClass(), clazz);
}

public Any getAny() {
return any;
}

public AbstractActuator setAny(Any any) {
this.any = any;
return this;
}

public ChainBaseManager getChainBaseManager() {
return chainBaseManager;
}

public AbstractActuator setChainBaseManager(ChainBaseManager chainBaseManager) {
this.chainBaseManager = chainBaseManager;
return this;
}

public Contract getContract() {
return contract;
}

public AbstractActuator setContract(Contract contract) {
this.contract = contract;
this.any = contract.getParameter();
return this;
}

public TransactionCapsule getTx() {
return tx;
}

public AbstractActuator setTx(TransactionCapsule tx) {
this.tx = tx;
return this;
Expand Down
24 changes: 24 additions & 0 deletions actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,29 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
}
break;
}
case ALLOW_TVM_PRAGUE: {
if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_TVM_PRAGUE]");
}
// The deployed BlockHashHistory bytecode contains PUSH0 (0x5f), which
// is itself gated on ALLOW_TVM_SHANGHAI at execution time. Refuse the
// proposal until Shanghai is enacted so an out-of-order activation
// can't leave a contract whose every STATICCALL hits InvalidOpcode.
if (dynamicPropertiesStore.getAllowTvmShangHai() != 1) {
throw new ContractValidateException(
"[ALLOW_TVM_PRAGUE] requires [ALLOW_TVM_SHANGHAI] to be enacted first");
}
if (dynamicPropertiesStore.getAllowTvmPrague() == 1) {
throw new ContractValidateException(
"[ALLOW_TVM_PRAGUE] has been valid, no need to propose again");
}
if (value != 1) {
throw new ContractValidateException(
"This value[ALLOW_TVM_PRAGUE] is only allowed to be 1");
}
break;
}
case ALLOW_HARDEN_RESOURCE_CALCULATION: {
if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
throw new ContractValidateException(
Expand Down Expand Up @@ -1003,6 +1026,7 @@ public enum ProposalType { // current value, value range
ALLOW_TVM_BLOB(89), // 0, 1
PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000)
ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1
ALLOW_TVM_PRAGUE(95), // 0, 1
ALLOW_TVM_OSAKA(96), // 0, 1
ALLOW_HARDEN_RESOURCE_CALCULATION(97), // 0, 1
ALLOW_HARDEN_EXCHANGE_CALCULATION(98); // 0, 1
Expand Down
2 changes: 2 additions & 0 deletions actuator/src/main/java/org/tron/core/vm/Op.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class Op {
public static final int SHR = 0x1c;
// (0x1d) Arithmetic shift right
public static final int SAR = 0x1d;
// (0x1e) Count leading zeros
public static final int CLZ = 0x1e;

/* Cryptographic Operations */
// (0x20) Compute SHA3-256 hash
Expand Down
12 changes: 12 additions & 0 deletions actuator/src/main/java/org/tron/core/vm/OperationActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.tron.common.crypto.Hash.sha3;
import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros;

import java.math.BigInteger;
import java.util.ArrayList;
Expand Down Expand Up @@ -287,6 +288,17 @@ public static void sarAction(Program program) {
program.step();
}

public static void clzAction(Program program) {
DataWord word = program.stackPop();
int clz = numberOfLeadingZeros(word.getData());
if (clz == 256) {
program.stackPush(new DataWord(256));
} else {
program.stackPush(DataWord.of((byte) clz));
}
program.step();
}

public static void sha3Action(Program program) {
DataWord memOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
Expand Down
20 changes: 19 additions & 1 deletion actuator/src/main/java/org/tron/core/vm/OperationRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum Version {
TRON_V1_2,
TRON_V1_3,
TRON_V1_4,
TRON_V1_5,
// add more
// TRON_V2,
// ETH
Expand All @@ -26,6 +27,7 @@ public enum Version {
tableMap.put(Version.TRON_V1_2, newTronV12OperationSet());
tableMap.put(Version.TRON_V1_3, newTronV13OperationSet());
tableMap.put(Version.TRON_V1_4, newTronV14OperationSet());
tableMap.put(Version.TRON_V1_5, newTronV15OperationSet());
}

public static JumpTable newTronV10OperationSet() {
Expand Down Expand Up @@ -63,12 +65,18 @@ public static JumpTable newTronV14OperationSet() {
return table;
}

public static JumpTable newTronV15OperationSet() {
JumpTable table = newTronV14OperationSet();
appendOsakaOperations(table);
return table;
}

// Just for warming up class to avoid out_of_time
public static void init() {}

public static JumpTable getTable() {
// always get the table which has the newest version
JumpTable table = tableMap.get(Version.TRON_V1_4);
JumpTable table = tableMap.get(Version.TRON_V1_5);

// next make the corresponding changes, exclude activating opcode
if (VMConfig.allowHigherLimitForMaxCpuTimeOfOneTx()) {
Expand Down Expand Up @@ -704,6 +712,16 @@ public static void appendCancunOperations(JumpTable table) {
tvmBlobProposal));
}

public static void appendOsakaOperations(JumpTable table) {
BooleanSupplier proposal = VMConfig::allowTvmOsaka;

table.set(new Operation(
Op.CLZ, 1, 1,
EnergyCost::getLowTierCost,
OperationActions::clzAction,
proposal));
}

public static void adjustSelfdestruct(JumpTable table) {
table.set(new Operation(
Op.SUICIDE, 1, 0,
Expand Down
70 changes: 70 additions & 0 deletions actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.tron.common.crypto.zksnark.Fp;
import org.tron.common.crypto.zksnark.PairingCheck;
import org.tron.common.es.ExecutorServiceManager;
import org.tron.common.math.StrictMathWrapper;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.ProgramResult;
import org.tron.common.runtime.vm.DataWord;
Expand Down Expand Up @@ -636,6 +637,11 @@ public static class ModExp extends PrecompiledContract {

private static final int UPPER_BOUND = 1024;

private static final long MIN_ENERGY_TIP7883 = 500L;

private static final BigInteger MIN_ENERGY_TIP7883_BI =
BigInteger.valueOf(MIN_ENERGY_TIP7883);

@Override
public long getEnergyForData(byte[] data) {

Expand All @@ -651,6 +657,10 @@ public long getEnergyForData(byte[] data) {
byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), min(expLen, 32,
VMConfig.disableJavaLangMath()));

if (VMConfig.allowTvmOsaka()) {
return getEnergyTIP7883(baseLen, modLen, expHighBytes, expLen);
}

long multComplexity = getMultComplexity(max(baseLen, modLen, VMConfig.disableJavaLangMath()));
long adjExpLen = getAdjustedExponentLength(expHighBytes, expLen);

Expand Down Expand Up @@ -734,6 +744,66 @@ private long getAdjustedExponentLength(byte[] expHighBytes, long expLen) {
}
}

/**
* TIP-7883: ModExp gas cost increase.
* New pricing formula with higher minimum cost and no divisor.
*/
private long getEnergyTIP7883(int baseLen, int modLen,
byte[] expHighBytes, int expLen) {
long multComplexity = getMultComplexityTIP7883(baseLen, modLen);
long iterCount = getIterationCountTIP7883(expHighBytes, expLen);

// use big numbers to stay safe in case of overflow
BigInteger energy = BigInteger.valueOf(multComplexity)
.multiply(BigInteger.valueOf(iterCount));

if (isLessThan(energy, MIN_ENERGY_TIP7883_BI)) {
return MIN_ENERGY_TIP7883;
}

return isLessThan(energy, BigInteger.valueOf(Long.MAX_VALUE)) ? energy.longValueExact()
: Long.MAX_VALUE;
}

/**
* TIP-7883: New multiplication complexity formula.
* Minimal complexity of 16; doubled complexity for base/modulus > 32 bytes.
*/
private long getMultComplexityTIP7883(int baseLen, int modLen) {
long maxLength = StrictMathWrapper.max(baseLen, modLen);
if (maxLength <= 32) {
return 16;
}
// ceil(maxLength / 8)
long words = StrictMathWrapper.floorDiv(StrictMathWrapper.addExact(maxLength, 7L), 8L);
return StrictMathWrapper.multiplyExact(2L, StrictMathWrapper.multiplyExact(words, words));
}

/**
* TIP-7883: New iteration count formula.
* Multiplier for exponents > 32 bytes increased from 8 to 16.
*/
private long getIterationCountTIP7883(byte[] expHighBytes, long expLen) {
int leadingZeros = numberOfLeadingZeros(expHighBytes);
long highestBit = StrictMathWrapper.subtractExact(
StrictMathWrapper.multiplyExact(8L, expHighBytes.length), leadingZeros);

if (highestBit > 0) {
highestBit = StrictMathWrapper.subtractExact(highestBit, 1L);
}

long iterCount;
if (expLen <= 32) {
iterCount = highestBit;
} else {
iterCount = StrictMathWrapper.addExact(
StrictMathWrapper.multiplyExact(16L, StrictMathWrapper.subtractExact(expLen, 32L)),
highestBit);
}

return StrictMathWrapper.max(iterCount, 1L);
}

private int parseLen(byte[] data, int idx) {
byte[] bytes = parseBytes(data, 32 * idx, 32);
return new DataWord(bytes).intValueSafe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,8 @@ public void merge(ProgramTrace programTrace) {
this.ops.addAll(programTrace.ops);
}

public String asJsonString(boolean formatted) {
return serializeFieldsOnly(this, formatted);
}

@Override
public String toString() {
return asJsonString(true);
return serializeFieldsOnly(this);
}
}
67 changes: 11 additions & 56 deletions actuator/src/main/java/org/tron/core/vm/trace/Serializers.java
Original file line number Diff line number Diff line change
@@ -1,73 +1,28 @@
package org.tron.core.vm.trace;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import java.io.IOException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.tron.common.runtime.vm.DataWord;
import org.tron.core.vm.Op;

@Slf4j(topic = "VM")
public final class Serializers {

public static String serializeFieldsOnly(Object value, boolean pretty) {
try {
ObjectMapper mapper = createMapper(pretty);
mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper));
private static final ObjectMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.visibility(PropertyAccessor.FIELD, Visibility.ANY)
.visibility(PropertyAccessor.GETTER, Visibility.NONE)
.visibility(PropertyAccessor.IS_GETTER, Visibility.NONE)
.build();

public static String serializeFieldsOnly(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (Exception e) {
logger.error("JSON serialization error: ", e);
return "{}";
}
}

private static VisibilityChecker<?> fieldsOnlyVisibilityChecker(ObjectMapper mapper) {
return mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withIsGetterVisibility(JsonAutoDetect.Visibility.NONE);
}

public static ObjectMapper createMapper(boolean pretty) {
ObjectMapper mapper = new ObjectMapper();
if (pretty) {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
}
return mapper;
}

public static class DataWordSerializer extends JsonSerializer<DataWord> {

@Override
public void serialize(DataWord energy, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(energy.value().toString());
}
}

public static class ByteArraySerializer extends JsonSerializer<byte[]> {

@Override
public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(Hex.toHexString(memory));
}
}

public static class OpCodeSerializer extends JsonSerializer<Byte> {

@Override
public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(Op.getNameOf(op));
}
}
}
Loading
Loading