Skip to content
Merged
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
4 changes: 4 additions & 0 deletions actuator/src/main/java/org/tron/core/actuator/VMActuator.java
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ public void execute(Object object) throws ContractExeException {
result.setRuntimeError(result.getException().getMessage());
}
logger.info("runtime result is :{}", result.getException().getMessage());
} finally {
if (isConstantCall) {
VMConfig.clearLocalSnapshot();
}
}
//use program returned fill context
context.setProgramResult(result);
Expand Down
18 changes: 17 additions & 1 deletion actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,21 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
}
break;
}
case ALLOW_OPTIMIZE_TVM_STORAGE: {
if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2_3)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_OPTIMIZE_TVM_STORAGE]");
}
if (dynamicPropertiesStore.getAllowOptimizeTvmStorage() == 1) {
throw new ContractValidateException(
"[ALLOW_OPTIMIZE_TVM_STORAGE] has been valid, no need to propose again");
}
if (value != 1) {
throw new ContractValidateException(
"This value[ALLOW_OPTIMIZE_TVM_STORAGE] is only allowed to be 1");
}
break;
}
default:
break;
}
Expand Down Expand Up @@ -1029,7 +1044,8 @@ public enum ProposalType { // current value, value range
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
ALLOW_HARDEN_EXCHANGE_CALCULATION(98), // 0, 1
ALLOW_OPTIMIZE_TVM_STORAGE(99); // 0, 1
private long code;

ProposalType(long code) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -982,9 +982,13 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
}

PairingCheck check = PairingCheck.create();
long deadlineNs = pairingDeadlineNs();

// iterating over all pairs
for (int offset = 0; offset < data.length; offset += PAIR_SIZE) {
if (deadlineNs < System.nanoTime()) {
throw Program.Exception.notEnoughTime("precompiled contract pair");
}

Pair<BN128G1, BN128G2> pair = decodePair(data, offset);

Expand All @@ -996,12 +1000,23 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
check.addPair(pair.getLeft(), pair.getRight());
}

check.run();
if (!check.run(deadlineNs)) {
throw Program.Exception.notEnoughTime("precompiled contract pair");
}
int result = check.result();

return Pair.of(true, new DataWord(result).getData());
}

/** Same skip as {@link Program#checkCPUTimeLimit}: debug and solidity nodes do not enforce it. */
private long pairingDeadlineNs() {
if (CommonParameter.getInstance().isDebug()
|| CommonParameter.getInstance().isSolidityNode()) {
return Long.MAX_VALUE;
}
return getVmShouldEndInUs() * VMConstant.ONE_THOUSAND;
}

private Pair<BN128G1, BN128G2> decodePair(byte[] in, int offset) {

byte[] x = parseWord(in, offset, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public static void load(StoreFactory storeFactory, boolean isolate) {
snapshot.allowTvmSelfdestructRestriction = ds.getAllowTvmSelfdestructRestriction() == 1;
snapshot.allowTvmOsaka = ds.getAllowTvmOsaka() == 1;
snapshot.allowHardenResourceCalculation = ds.getAllowHardenResourceCalculation() == 1;
snapshot.allowOptimizeTvmStorage = ds.getAllowOptimizeTvmStorage() == 1;
if (isolate) {
VMConfig.setLocalSnapshot(snapshot);
} else {
Expand Down
148 changes: 143 additions & 5 deletions actuator/src/main/java/org/tron/core/vm/program/Storage.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,27 @@
import org.tron.common.crypto.Hash;
import org.tron.common.runtime.vm.DataWord;
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.ForkController;
import org.tron.core.capsule.StorageRowCapsule;
import org.tron.core.config.Parameter.ForkBlockVersionEnum;
import org.tron.core.store.StorageRowStore;

public class Storage {

private static final int PREFIX_BYTES = 16;
private static final int WORD_BYTES = 32;

private enum ReadKind {
NEW, OLD, EMPTY
}

@Getter
private final Map<DataWord, StorageRowCapsule> rowCache = new HashMap<>();
private final Map<DataWord, DataWord> oldRowKeyOwners = new HashMap<>();
private final Map<DataWord, ReadKind> readKinds = new HashMap<>();
private final boolean optimizeTvmStorage;
private final boolean aliasCheckEnabled;

@Getter
private byte[] addrHash;
@Getter
Expand All @@ -26,21 +39,29 @@ public class Storage {
@Setter
private int contractVersion;

public Storage(byte[] address, StorageRowStore store) {
public Storage(byte[] address, StorageRowStore store, boolean optimizeTvmStorage) {
addrHash = addrHash(address);
this.address = address;
this.store = store;
this.optimizeTvmStorage = optimizeTvmStorage;
this.aliasCheckEnabled = ForkController.instance().pass(ForkBlockVersionEnum.VERSION_4_8_2_3);
}

public Storage(Storage storage) {
this.addrHash = storage.addrHash.clone();
this.address = storage.getAddress().clone();
this.store = storage.store;
this.contractVersion = storage.contractVersion;
storage.getRowCache().forEach((DataWord rowKey, StorageRowCapsule row) -> {
this.optimizeTvmStorage = storage.optimizeTvmStorage;
this.aliasCheckEnabled = storage.aliasCheckEnabled;
storage.getRowCache().forEach((DataWord key, StorageRowCapsule row) -> {
StorageRowCapsule newRow = new StorageRowCapsule(row);
this.rowCache.put(rowKey.clone(), newRow);
this.rowCache.put(key.clone(), newRow);
});
storage.oldRowKeyOwners.forEach((DataWord legacy, DataWord owner) ->
this.oldRowKeyOwners.put(legacy.clone(), owner.clone()));
storage.readKinds.forEach((DataWord key, ReadKind kind) ->
this.readKinds.put(key.clone(), kind));
}

private byte[] compose(byte[] key, byte[] addrHash) {
Expand All @@ -53,6 +74,18 @@ private byte[] compose(byte[] key, byte[] addrHash) {
return result;
}

private byte[] getOldRowKey(DataWord key) {
return compose(key.getData(), addrHash);
}

private byte[] getNewRowKey(DataWord key) {
byte[] result = new byte[PREFIX_BYTES + WORD_BYTES];
arraycopy(addrHash, 0, result, 0, PREFIX_BYTES);
arraycopy(Hash.sha3(ByteUtil.merge(addrHash, key.getData())), 0, result, PREFIX_BYTES,
WORD_BYTES);
return result;
}

// 32 bytes
private static byte[] addrHash(byte[] address) {
return Hash.sha3(address);
Expand All @@ -71,6 +104,12 @@ public void generateAddrHash(byte[] trxId) {
}

public DataWord getValue(DataWord key) {
if (optimizeTvmStorage) {
return getOptimized(key);
}
if (aliasCheckEnabled) {
checkAlias(key);
}
if (rowCache.containsKey(key)) {
return new DataWord(rowCache.get(key).getValue());
} else {
Expand All @@ -84,17 +123,88 @@ public DataWord getValue(DataWord key) {
}

public void put(DataWord key, DataWord value) {
if (!optimizeTvmStorage && aliasCheckEnabled) {
checkAlias(key);
}
if (rowCache.containsKey(key)) {
rowCache.get(key).setValue(value.getData());
} else {
byte[] rowKey = compose(key.getData(), addrHash);
byte[] rowKey = optimizeTvmStorage ? getNewRowKey(key) : getOldRowKey(key);
StorageRowCapsule row = new StorageRowCapsule(rowKey, value.getData());
rowCache.put(key, row);
}
if (optimizeTvmStorage) {
ownOldKey(key);
}
}

private DataWord getOptimized(DataWord key) {
if (rowCache.containsKey(key)) {
return new DataWord(rowCache.get(key).getValue());
}
if (readKinds.get(key) == ReadKind.EMPTY
|| readKinds.get(key) == ReadKind.NEW) {
return null;
}

byte[] newRowKey = getNewRowKey(key);
StorageRowCapsule newRow = store.get(newRowKey);
if (newRow != null && newRow.getValue() != null) {
readKinds.put(key.clone(), ReadKind.NEW);
if (DataWord.isZero(newRow.getValue())) {
return null;
}
return cacheRead(key, newRowKey, newRow.getValue());
}

if (oldKeyTaken(key)) {
return null;
}

byte[] oldRowKey = getOldRowKey(key);
StorageRowCapsule oldRow = store.get(oldRowKey);
if (oldRow != null && oldRow.getValue() != null) {
readKinds.put(key.clone(), ReadKind.OLD);
return cacheRead(key, oldRowKey, oldRow.getValue());
}

readKinds.put(key.clone(), ReadKind.EMPTY);
return null;
}

private DataWord cacheRead(DataWord key, byte[] rowKey, byte[] value) {
StorageRowCapsule row = new StorageRowCapsule(value.clone());
row.setRowKey(rowKey);
rowCache.put(key, row);
ownOldKey(key);
return new DataWord(row.getValue());
}

private void checkAlias(DataWord key) {
DataWord oldRowKey = new DataWord(getOldRowKey(key));
DataWord owner = oldRowKeyOwners.get(oldRowKey);
if (owner == null) {
oldRowKeyOwners.put(oldRowKey, key.clone());
} else if (!owner.equals(key)) {
throw new Program.OutOfTimeException("CPU timeout for storage check");
}
}

private void ownOldKey(DataWord key) {
oldRowKeyOwners.putIfAbsent(new DataWord(getOldRowKey(key)), key.clone());
}

private boolean oldKeyTaken(DataWord key) {
DataWord owner = oldRowKeyOwners.get(new DataWord(getOldRowKey(key)));
return owner != null && !owner.equals(key);
}

public void commit() {
rowCache.forEach((DataWord rowKey, StorageRowCapsule row) -> {
if (optimizeTvmStorage) {
commitOptimized();
return;
}
rowCache.forEach((DataWord key, StorageRowCapsule row) -> {
if (row.isDirty()) {
if (new DataWord(row.getValue()).isZero()) {
this.store.delete(row.getRowKey());
Expand All @@ -104,4 +214,32 @@ public void commit() {
}
});
}

private void commitOptimized() {
rowCache.forEach((DataWord key, StorageRowCapsule row) -> {
if (!row.isDirty()) {
return;
}
putNew(key, row.getValue());
ReadKind kind = readKinds.get(key);
if (kind == null || kind == ReadKind.OLD) {
store.delete(getOldRowKey(key));
}
});
readKinds.forEach((DataWord key, ReadKind kind) -> {
StorageRowCapsule row = rowCache.get(key);
if (row != null && row.isDirty()) {
return;
}
if (kind == ReadKind.OLD) {
putNew(key, row.getValue());
store.delete(getOldRowKey(key));
}
});
}

private void putNew(DataWord key, byte[] value) {
store.put(getNewRowKey(key), new StorageRowCapsule(value));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,8 @@ public Storage getStorage(byte[] address) {
storage = parentStorage;
}
} else {
storage = new Storage(address, getStorageRowStore());
storage = new Storage(address, getStorageRowStore(),
VMConfig.allowOptimizeTvmStorage());
}
ContractCapsule contract = getContract(address);
if (contract != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.tron.core.vm;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThrows;

import java.math.BigInteger;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Test;
import org.tron.core.vm.program.Program.OutOfTimeException;

public class Bn128PairingTimeoutForkTest {

@Test
public void expiredDeadlineTimesOut() {
PrecompiledContracts.BN128Pairing pairing = pairing(0);
assertThrows(OutOfTimeException.class, () -> pairing.execute(onePair()));
}

@Test
public void futureDeadlineReturnsPairingResult() {
PrecompiledContracts.BN128Pairing pairing = pairing(Long.MAX_VALUE / 1000);
Pair<Boolean, byte[]> out = pairing.execute(onePair());
assertTrue(out.getLeft());
assertEquals(32, out.getRight().length);
}

private static PrecompiledContracts.BN128Pairing pairing(long vmShouldEndInUs) {
PrecompiledContracts.BN128Pairing pairing = new PrecompiledContracts.BN128Pairing();
pairing.setVmShouldEndInUs(vmShouldEndInUs);
return pairing;
}

private static byte[] onePair() {
byte[] input = new byte[192];
write(input, 0, BigInteger.ONE);
write(input, 32, BigInteger.valueOf(2));
write(input, 64, new BigInteger(
"11559732032986387107991004021392285783925812861821192530917403151452391805634"));
write(input, 96, new BigInteger(
"10857046999023057135944570762232829481370756359578518086990519993285655852781"));
write(input, 128, new BigInteger(
"4082367875863433681332203403145435568316851327593401208105741076214120093531"));
write(input, 160, new BigInteger(
"8495653923123431417604973247489272438418190587263600148770280649306958101930"));
return input;
}

private static void write(byte[] dest, int offset, BigInteger value) {
byte[] raw = value.toByteArray();
int src = raw.length > 32 ? raw.length - 32 : 0;
System.arraycopy(raw, src, dest, offset + 32 - (raw.length - src), raw.length - src);
}
}
Loading
Loading