Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,9 @@ public Pair<Boolean, byte[]> execute(byte[] data) {

// check if modulus is zero
if (isZero(mod)) {
if (VMConfig.allowTvmOsaka()) {
return Pair.of(true, new byte[modLen]);
}
return Pair.of(true, EMPTY_BYTE_ARRAY);
}

Expand Down
4 changes: 4 additions & 0 deletions actuator/src/main/java/org/tron/core/vm/program/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,10 @@ public ProgramTrace getTrace() {
}

public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) {
if (VMConfig.allowTvmOsaka()) {
returnDataBuffer = null; // reset return buffer right before the call
}

byte[] senderAddress;
if (VMConfig.allowTvmCompatibleEvm() && getCallDeep() == MAX_DEPTH) {
stackPushZero();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,25 @@ public void testEIP7823() {
* Build ModExp input data for energy calculation testing.
*/
private static byte[] buildModExpData(int baseLen, int expLen, int modLen, byte[] expValue) {
return buildModExpData(baseLen, expLen, modLen, new byte[]{}, expValue, new byte[]{});
}

private static byte[] buildModExpData(int baseLen, int expLen, int modLen, byte[] baseValue,
byte[] expValue, byte[] modValue) {
byte[] base = new byte[baseLen];
if (baseValue.length > 0 && baseLen > 0) {
System.arraycopy(baseValue, 0, base, 0,
StrictMathWrapper.min(baseValue.length, baseLen));
}
byte[] exp = new byte[expLen];
if (expValue.length > 0 && expLen > 0) {
System.arraycopy(expValue, 0, exp, 0, StrictMathWrapper.min(expValue.length, expLen));
}
byte[] mod = new byte[modLen];
if (modValue.length > 0 && modLen > 0) {
System.arraycopy(modValue, 0, mod, 0,
StrictMathWrapper.min(modValue.length, modLen));
}
return ByteUtil.merge(toLenBytes(baseLen), toLenBytes(expLen), toLenBytes(modLen),
base, exp, mod);
}
Expand All @@ -80,6 +93,72 @@ private static long getEnergy(int baseLen, int expLen, int modLen, byte[] expVal
return modExp.getEnergyForData(buildModExpData(baseLen, expLen, modLen, expValue));
}

@Test
public void testModExpZeroModulusOutputLengthGatedByOsaka() {
ConfigLoader.disable = true;

byte[] modLenZero = buildModExpData(1, 1, 0, new byte[]{0x02}, new byte[]{0x03},
new byte[]{});
byte[] modLenOne = buildModExpData(1, 1, 1, new byte[]{0x02}, new byte[]{0x03},
new byte[]{0x00});
byte[] modLen32 = buildModExpData(1, 1, 32, new byte[]{0x02}, new byte[]{0x03},
new byte[]{});

try {
VMConfig.initAllowTvmOsaka(0);
Pair<Boolean, byte[]> result = modExp.execute(modLenZero);
Assert.assertTrue(result.getLeft());
Assert.assertEquals(0, result.getRight().length);

result = modExp.execute(modLenOne);
Assert.assertTrue(result.getLeft());
Assert.assertEquals(0, result.getRight().length);

result = modExp.execute(modLen32);
Assert.assertTrue(result.getLeft());
Assert.assertEquals(0, result.getRight().length);

VMConfig.initAllowTvmOsaka(1);
result = modExp.execute(modLenZero);
Assert.assertTrue(result.getLeft());
Assert.assertEquals(0, result.getRight().length);

result = modExp.execute(modLenOne);
Assert.assertTrue(result.getLeft());
Assert.assertArrayEquals(new byte[1], result.getRight());

result = modExp.execute(modLen32);
Assert.assertTrue(result.getLeft());
Assert.assertArrayEquals(new byte[32], result.getRight());
} finally {
VMConfig.initAllowTvmOsaka(0);
ConfigLoader.disable = false;
}
}

@Test
public void testModExpZeroModulusEnergyMatchesNonZeroModulus() {
ConfigLoader.disable = true;

byte[] zeroModulus = buildModExpData(1, 1, 32, new byte[]{0x02}, new byte[]{0x03},
new byte[]{});
byte[] nonZeroModulus = buildModExpData(1, 1, 32, new byte[]{0x02}, new byte[]{0x03},
new byte[]{0x05});

try {
VMConfig.initAllowTvmOsaka(0);
Assert.assertEquals(modExp.getEnergyForData(nonZeroModulus),
modExp.getEnergyForData(zeroModulus));

VMConfig.initAllowTvmOsaka(1);
Assert.assertEquals(modExp.getEnergyForData(nonZeroModulus),
modExp.getEnergyForData(zeroModulus));
} finally {
VMConfig.initAllowTvmOsaka(0);
ConfigLoader.disable = false;
}
}

@Test
public void testEIP7883ModExpPricing() {
ConfigLoader.disable = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package org.tron.common.runtime.vm;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.tron.common.runtime.TVMTestResult;
import org.tron.common.runtime.TvmTestUtils;
import org.tron.common.utils.WalletUtil;
import org.tron.common.utils.client.utils.AbiUtil;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.exception.ReceiptCheckErrException;
import org.tron.core.exception.VMIllegalException;
import org.tron.core.vm.config.ConfigLoader;
import org.tron.protos.Protocol.Transaction;

@Slf4j
public class TvmIssueVerifierTest extends VMTestBase {

private static final int WORD_SIZE = 32;

private static final String ABI = loadResource("solidity/TvmIssueVerifier.abi");

private static final String BYTECODE = loadResource("solidity/TvmIssueVerifier.bin");

@Before
public void enableVmFeatures() {
ConfigLoader.disable = false;
manager.getDynamicPropertiesStore().saveAllowTvmTransferTrc10(1);
manager.getDynamicPropertiesStore().saveAllowTvmConstantinople(1);
manager.getDynamicPropertiesStore().saveAllowTvmIstanbul(1);
manager.getDynamicPropertiesStore().saveAllowTvmLondon(1);
manager.getDynamicPropertiesStore().saveAllowTvmCompatibleEvm(1);
manager.getDynamicPropertiesStore().saveAllowTvmOsaka(1);
}

@Test
public void verifyTvmOsakaFixesWithSolidity()
throws ContractExeException, ReceiptCheckErrException,
VMIllegalException, ContractValidateException {
byte[] verifierAddress = deployVerifier();

manager.getDynamicPropertiesStore().saveAllowTvmOsaka(0);

TVMTestResult modExpResult = trigger(verifierAddress, "modexpZeroModulus()",
Collections.emptyList(), 100_000_000L);
byte[] modExpReturn = modExpResult.getRuntime().getResult().getHReturn();
Assert.assertNull(modExpResult.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.ONE, word(modExpReturn, 0));
Assert.assertEquals("MODEXP zero modulus currently returns empty returndata",
BigInteger.ZERO, word(modExpReturn, 1));

TVMTestResult create2Result = trigger(verifierAddress,
"failedCreate2KeepsPriorReturnData(bytes,uint256)", Arrays.asList("00", 7L),
100_000_000L);
byte[] create2Return = create2Result.getRuntime().getResult().getHReturn();
Assert.assertNull(create2Result.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.valueOf(32), word(create2Return, 0));
Assert.assertEquals(BigInteger.ZERO, word(create2Return, 1));
Assert.assertEquals("failed CREATE2 keeps the previous 32-byte return data buffer",
BigInteger.valueOf(32), word(create2Return, 2));

TVMTestResult preOsakaCreate2SuccessResult = trigger(verifierAddress,
"successfulCreate2KeepsPriorReturnData(bytes,uint256)",
Arrays.asList(initCodeReturningRuntime("00"), 8L), 100_000_000L);
byte[] preOsakaCreate2SuccessReturn =
preOsakaCreate2SuccessResult.getRuntime().getResult().getHReturn();
Assert.assertNull(preOsakaCreate2SuccessResult.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.valueOf(32), word(preOsakaCreate2SuccessReturn, 0));
Assert.assertTrue(word(preOsakaCreate2SuccessReturn, 1).signum() != 0);
Assert.assertEquals(BigInteger.valueOf(32), word(preOsakaCreate2SuccessReturn, 2));
Assert.assertEquals(BigInteger.ONE, word(preOsakaCreate2SuccessReturn, 3));

manager.getDynamicPropertiesStore().saveAllowTvmOsaka(1);

modExpResult = trigger(verifierAddress, "modexpZeroModulus()",
Collections.emptyList(), 100_000_000L);
modExpReturn = modExpResult.getRuntime().getResult().getHReturn();
Assert.assertNull(modExpResult.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.ONE, word(modExpReturn, 0));
Assert.assertEquals("MODEXP zero modulus returns modLen bytes after Osaka",
BigInteger.ONE, word(modExpReturn, 1));

create2Result = trigger(verifierAddress,
"failedCreate2KeepsPriorReturnData(bytes,uint256)", Arrays.asList("00", 7L),
100_000_000L);
create2Return = create2Result.getRuntime().getResult().getHReturn();
Assert.assertNull(create2Result.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.valueOf(32), word(create2Return, 0));
Assert.assertEquals(BigInteger.ZERO, word(create2Return, 1));
Assert.assertEquals("failed CREATE2 clears the previous return data buffer after Osaka",
BigInteger.ZERO, word(create2Return, 2));

TVMTestResult create2SuccessResult = trigger(verifierAddress,
"successfulCreate2KeepsPriorReturnData(bytes,uint256)",
Arrays.asList(initCodeReturningRuntime("00"), 9L), 100_000_000L);
byte[] create2SuccessReturn = create2SuccessResult.getRuntime().getResult().getHReturn();
Assert.assertNull(create2SuccessResult.getRuntime().getRuntimeError());
Assert.assertEquals(BigInteger.valueOf(32), word(create2SuccessReturn, 0));
Assert.assertTrue(word(create2SuccessReturn, 1).signum() != 0);
Assert.assertEquals("successful CREATE2 clears the previous return data buffer after Osaka",
BigInteger.ZERO, word(create2SuccessReturn, 2));
Assert.assertEquals(BigInteger.ONE, word(create2SuccessReturn, 3));
}

private byte[] deployVerifier()
throws ContractExeException, ReceiptCheckErrException,
VMIllegalException, ContractValidateException {
byte[] owner = Hex.decode(OWNER_ADDRESS);
Transaction trx = TvmTestUtils.generateDeploySmartContractAndGetTransaction(
"TvmIssueVerifier", owner, ABI, BYTECODE, 0, 1_000_000_000L, 0, null);
byte[] contractAddress = WalletUtil.generateContractAddress(trx);
runtime = TvmTestUtils.processTransactionAndReturnRuntime(trx, rootRepository, null);
Assert.assertNull(runtime.getRuntimeError());
return contractAddress;
}

private TVMTestResult trigger(byte[] contractAddress, String method, List<Object> args,
long feeLimit)
throws ContractExeException, ReceiptCheckErrException,
VMIllegalException, ContractValidateException {
String input = AbiUtil.parseMethod(method, args);
return TvmTestUtils.triggerContractAndReturnTvmTestResult(Hex.decode(OWNER_ADDRESS),
contractAddress, Hex.decode(input), 0, feeLimit, manager, null);
}

private static BigInteger word(byte[] data, int index) {
int start = index * WORD_SIZE;
return new BigInteger(1, Arrays.copyOfRange(data, start, start + WORD_SIZE));
}

private static String initCodeReturningRuntime(String runtimeCode) {
byte[] runtime = Hex.decode(runtimeCode);
Assert.assertTrue(runtime.length <= 255);

byte[] initCode = new byte[12 + runtime.length];
initCode[0] = 0x60;
initCode[1] = (byte) runtime.length;
initCode[2] = 0x60;
initCode[3] = 0x0c;
initCode[4] = 0x60;
initCode[5] = 0x00;
initCode[6] = 0x39;
initCode[7] = 0x60;
initCode[8] = (byte) runtime.length;
initCode[9] = 0x60;
initCode[10] = 0x00;
initCode[11] = (byte) 0xf3;
System.arraycopy(runtime, 0, initCode, 12, runtime.length);

return Hex.toHexString(initCode);
}

private static String loadResource(String resource) {
try (InputStream in = TvmIssueVerifierTest.class.getClassLoader()
.getResourceAsStream(resource)) {
if (in == null) {
throw new IllegalStateException("Missing test resource: " + resource);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int read;
while ((read = in.read(buffer)) >= 0) {
out.write(buffer, 0, read);
}
return out.toString("UTF-8").trim();
} catch (IOException e) {
throw new IllegalStateException("Cannot read test resource: " + resource, e);
}
}
}
1 change: 1 addition & 0 deletions framework/src/test/resources/solidity/TvmIssueVerifier.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"bytes","name":"code","type":"bytes"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"failedCreate2KeepsPriorReturnData","outputs":[{"internalType":"uint256","name":"beforeSize","type":"uint256"},{"internalType":"address","name":"created","type":"address"},{"internalType":"uint256","name":"afterSize","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"modexpZeroModulus","outputs":[{"internalType":"uint256","name":"ok","type":"uint256"},{"internalType":"uint256","name":"sizeAfter","type":"uint256"},{"internalType":"bytes32","name":"copiedWord","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"code","type":"bytes"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"successfulCreate2KeepsPriorReturnData","outputs":[{"internalType":"uint256","name":"beforeSize","type":"uint256"},{"internalType":"address","name":"created","type":"address"},{"internalType":"uint256","name":"afterSize","type":"uint256"},{"internalType":"uint256","name":"createdSize","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
1 change: 1 addition & 0 deletions framework/src/test/resources/solidity/TvmIssueVerifier.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6080604052348015600f57600080fd5b506105198061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80634ecba0f014610046578063543b5255146100795780639fefb5fd146100ab575b600080fd5b610060600480360381019061005b919061036b565b6100cb565b6040516100709493929190610417565b60405180910390f35b610093600480360381019061008e919061036b565b610104565b6040516100a29392919061045c565b60405180910390f35b6100b3610136565b6040516100c2939291906104ac565b60405180910390f35b60008060008061123460005260206000602060008060045af1503d9350848651602088016000f592503d9150823b905092959194509250565b600080600061123460005260206000602060008060045af1503d9250838551602087016001f591503d90509250925092565b600080600080606367ffffffffffffffff8111156101575761015661020a565b5b6040519080825280601f01601f1916602001820160405280156101895781602001600182028036833780820191505090505b5090506020810160018152600160208201526001604082015260026060820153600360618201536000606282015360001960005260206000606383600060055af194503d935060005192505050909192565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610242826101f9565b810181811067ffffffffffffffff821117156102615761026061020a565b5b80604052505050565b60006102746101db565b90506102808282610239565b919050565b600067ffffffffffffffff8211156102a05761029f61020a565b5b6102a9826101f9565b9050602081019050919050565b82818337600083830152505050565b60006102d86102d384610285565b61026a565b9050828152602081018484840111156102f4576102f36101f4565b5b6102ff8482856102b6565b509392505050565b600082601f83011261031c5761031b6101ef565b5b813561032c8482602086016102c5565b91505092915050565b6000819050919050565b61034881610335565b811461035357600080fd5b50565b6000813590506103658161033f565b92915050565b60008060408385031215610382576103816101e5565b5b600083013567ffffffffffffffff8111156103a05761039f6101ea565b5b6103ac85828601610307565b92505060206103bd85828601610356565b9150509250929050565b6103d081610335565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610401826103d6565b9050919050565b610411816103f6565b82525050565b600060808201905061042c60008301876103c7565b6104396020830186610408565b61044660408301856103c7565b61045360608301846103c7565b95945050505050565b600060608201905061047160008301866103c7565b61047e6020830185610408565b61048b60408301846103c7565b949350505050565b6000819050919050565b6104a681610493565b82525050565b60006060820190506104c160008301866103c7565b6104ce60208301856103c7565b6104db604083018461049d565b94935050505056fea2646970667358221220c9b28608a5295f3b52702e75aa5d40b18593bd0a9ff2e03e2274edbd42642c6a64736f6c634300081e0033
55 changes: 55 additions & 0 deletions framework/src/test/resources/solidity/TvmIssueVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
pragma solidity ^0.8.25;

contract TvmIssueVerifier {
function modexpZeroModulus()
public
returns (uint256 ok, uint256 sizeAfter, bytes32 copiedWord)
{
bytes memory input = new bytes(99);

assembly {
let p := add(input, 0x20)
mstore(p, 1)
mstore(add(p, 0x20), 1)
mstore(add(p, 0x40), 1)
mstore8(add(p, 0x60), 2)
mstore8(add(p, 0x61), 3)
mstore8(add(p, 0x62), 0)
mstore(0, not(0))

ok := call(gas(), 5, 0, p, 99, 0, 32)
sizeAfter := returndatasize()
copiedWord := mload(0)
}
}

function failedCreate2KeepsPriorReturnData(bytes memory code, uint256 salt)
public
returns (uint256 beforeSize, address created, uint256 afterSize)
{
assembly {
mstore(0, 0x1234)
pop(call(gas(), 4, 0, 0, 32, 0, 32))
beforeSize := returndatasize()

created := create2(1, add(code, 0x20), mload(code), salt)
afterSize := returndatasize()
}
}

function successfulCreate2KeepsPriorReturnData(bytes memory code, uint256 salt)
public
returns (uint256 beforeSize, address created, uint256 afterSize, uint256 createdSize)
{
assembly {
mstore(0, 0x1234)
pop(call(gas(), 4, 0, 0, 32, 0, 32))
beforeSize := returndatasize()

created := create2(0, add(code, 0x20), mload(code), salt)
afterSize := returndatasize()
createdSize := extcodesize(created)
}
}

}
Loading