Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not define 256 as a static constant, so that whenever word is 0, it is pushed directly onto the stack?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if a 256 DataWord constant is defined, it still needs to be cloned when pushed onto the stack. The overhead involved is nearly equivalent to creating a new instance from scratch, so the constant approach was not adopted.

} else {
program.stackPush(DataWord.of((byte) clz));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work overall! One minor suggestion: since DataWord.of(byte num) directly assigns bb[31] = num, casting clz to (byte) when it's in [128, 255] introduces a subtle signed/unsigned ambiguity. Would it be worth simplifying both branches into one using new DataWord(int), which handles the full range cleanly?

}
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 @@ -700,6 +708,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
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,81 @@ public void testPush0() throws ContractValidateException {
VMConfig.initAllowTvmShangHai(0);
}

@Test
public void testCLZ() throws ContractValidateException {
VMConfig.initAllowTvmOsaka(1);

invoke = new ProgramInvokeMockImpl();
Protocol.Transaction trx = Protocol.Transaction.getDefaultInstance();
InternalTransaction interTrx =
new InternalTransaction(trx, InternalTransaction.TrxType.TRX_UNKNOWN_TYPE);

// CLZ(0) = 256
byte[] op = buildCLZBytecode(new byte[32]);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(256), program.getStack().pop());

// CLZ(0x80...00) = 0 (highest bit set)
byte[] val = new byte[32];
val[0] = (byte) 0x80;
op = buildCLZBytecode(val);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(0), program.getStack().pop());

// CLZ(0xFF...FF) = 0
val = new byte[32];
for (int i = 0; i < 32; i++) {
val[i] = (byte) 0xFF;
}
op = buildCLZBytecode(val);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(0), program.getStack().pop());

// CLZ(0x40...00) = 1
val = new byte[32];
val[0] = (byte) 0x40;
op = buildCLZBytecode(val);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(1), program.getStack().pop());

// CLZ(0x7F...FF) = 1
val = new byte[32];
for (int i = 0; i < 32; i++) {
val[i] = (byte) 0xFF;
}
val[0] = (byte) 0x7F;
op = buildCLZBytecode(val);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(1), program.getStack().pop());

// CLZ(1) = 255
val = new byte[32];
val[31] = 0x01;
op = buildCLZBytecode(val);
program = new Program(op, op, invoke, interTrx);
testOperations(program);
Assert.assertEquals(new DataWord(255), program.getStack().pop());

// Verify energy cost = LOW_TIER(5) + PUSH32 cost(3) = 8
Assert.assertEquals(8, program.getResult().getEnergyUsed());

VMConfig.initAllowTvmOsaka(0);
}

// Build bytecode: PUSH32 <value> CLZ
private byte[] buildCLZBytecode(byte[] value) {
byte[] op = new byte[34];
op[0] = 0x7f; // PUSH32
System.arraycopy(value, 0, op, 1, 32);
op[33] = Op.CLZ;
return op;
}

@Test
public void testSuicideCost() throws ContractValidateException {
invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], new byte[21]);
Expand Down
Loading