Skip to content

Commit

Permalink
Fvm now defines its own data word
Browse files Browse the repository at this point in the history
  • Loading branch information
aionick committed Jul 26, 2019
1 parent 01c0b32 commit cc696b6
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 63 deletions.
120 changes: 62 additions & 58 deletions modVM/src/org/aion/vm/ExternalStateForFvm.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
import org.aion.fastvm.ExecutionContext;
import org.aion.fastvm.FastVmResultCode;
import org.aion.fastvm.FastVmTransactionResult;
import org.aion.fastvm.FvmDataWord;
import org.aion.fastvm.IExternalStateForFvm;
import org.aion.mcf.core.AccountState;
import org.aion.mcf.db.IBlockStoreBase;
import org.aion.mcf.db.InternalVmType;
import org.aion.mcf.db.RepositoryCache;
import org.aion.mcf.valid.TxNrgRule;
import org.aion.mcf.vm.DataWord;
import org.aion.mcf.vm.types.DataWordImpl;
import org.aion.mcf.vm.types.DoubleDataWord;
import org.aion.precompiled.ContractInfo;
import org.aion.precompiled.PrecompiledResultCode;
import org.aion.precompiled.PrecompiledTransactionResult;
Expand All @@ -34,9 +32,9 @@ public final class ExternalStateForFvm implements IExternalStateForFvm {
private final long blockNumber;
private final long blockTimestamp;
private final long blockEnergyLimit;
private final DataWord blockDifficulty;
private final FvmDataWord blockDifficulty;

public ExternalStateForFvm(RepositoryCache<AccountState, IBlockStoreBase> repository, AionAddress miner, DataWord blockDifficulty, boolean isLocalCall, boolean allowNonceIncrement, boolean isFork040enabled, long blockNumber, long blockTimestamp, long blockEnergyLimit) {
public ExternalStateForFvm(RepositoryCache<AccountState, IBlockStoreBase> repository, AionAddress miner, FvmDataWord blockDifficulty, boolean isLocalCall, boolean allowNonceIncrement, boolean isFork040enabled, long blockNumber, long blockTimestamp, long blockEnergyLimit) {
this.repository = repository;
this.miner = miner;
this.blockDifficulty = blockDifficulty;
Expand Down Expand Up @@ -131,10 +129,16 @@ public FastVmTransactionResult runInternalPrecompiledContractCall(ExecutionConte
* @param value The value.
*/
@Override
public void addStorageValue(AionAddress address, byte[] key, byte[] value) {
ByteArrayWrapper storageKey = alignDataToWordSize(key);
ByteArrayWrapper storageValue = alignValueToWordSizeForPut(value);
if (value == null || value.length == 0 || storageValue.isZero()) {
public void addStorageValue(AionAddress address, FvmDataWord key, FvmDataWord value) {
byte[] valueBytes = value.copyOfData();
if (valueBytes == null || valueBytes.length == 0) {
// used to ensure FVM correctness
throw new IllegalArgumentException("Put with null, empty or zero byte array values is not allowed for the FVM. For deletions, make explicit calls to the delete method.");
}

ByteArrayWrapper storageKey = new ByteArrayWrapper(key.copyOfData());
ByteArrayWrapper storageValue = alignValueToWordSizeForPut(valueBytes);
if (storageValue.isZero()) {
// used to ensure FVM correctness
throw new IllegalArgumentException("Put with null, empty or zero byte array values is not allowed for the FVM. For deletions, make explicit calls to the delete method.");
}
Expand All @@ -151,8 +155,8 @@ public void addStorageValue(AionAddress address, byte[] key, byte[] value) {
* @param key The key.
*/
@Override
public void removeStorage(AionAddress address, byte[] key) {
ByteArrayWrapper storageKey = alignDataToWordSize(key);
public void removeStorage(AionAddress address, FvmDataWord key) {
ByteArrayWrapper storageKey = new ByteArrayWrapper(key.copyOfData());
this.repository.removeStorageRow(address, storageKey);
setVmType(address);
}
Expand All @@ -166,14 +170,14 @@ public void removeStorage(AionAddress address, byte[] key) {
* @return the value.
*/
@Override
public byte[] getStorageValue(AionAddress address, byte[] key) {
ByteArrayWrapper storageKey = alignDataToWordSize(key);
public FvmDataWord getStorageValue(AionAddress address, FvmDataWord key) {
ByteArrayWrapper storageKey = new ByteArrayWrapper(key.copyOfData());
ByteArrayWrapper value = this.repository.getStorageValue(address, storageKey);
if (value != null && (value.isZero() || value.isEmpty())) {
// used to ensure FVM correctness
throw new IllegalStateException("A zero or empty value was retrieved from storage. Storing zeros is not allowed by the FVM. An incorrect put was previously performed instead of an explicit call to the delete method.");
}
return (value == null) ? DataWordImpl.ZERO.getData() : alignValueToWordSizeForGet(value);
return (value == null) ? FvmDataWord.fromBytes(new byte[FvmDataWord.SIZE]) : FvmDataWord.fromBytes(value.toBytes());
}

/**
Expand Down Expand Up @@ -451,11 +455,7 @@ public long getBlockEnergyLimit() {
*/
@Override
public long getBlockDifficulty() {
if (this.blockDifficulty instanceof DataWordImpl) {
return ((DataWordImpl) this.blockDifficulty).longValue();
} else {
return ((DoubleDataWord) this.blockDifficulty).longValue();
}
return this.blockDifficulty.toLong();
}

/**
Expand All @@ -477,45 +477,8 @@ public byte[] getBlockHashByNumber(long blockNumber) {
*
* <p>This method should only be used for putting data into storage.
*/
private ByteArrayWrapper alignValueToWordSizeForPut(byte[] value) {
if (value.length == DoubleDataWord.BYTES) {
return new ByteArrayWrapper(new DoubleDataWord(value).getData());
} else {
DataWordImpl valueAsWord = new DataWordImpl(value);
return (valueAsWord.isZero()) ? valueAsWord.toWrapper() : new ByteArrayWrapper(valueAsWord.getNoLeadZeroesData());
}
}

/**
* If data.length > 16 then data is aligned to be 32 bytes.
*
* <p>Otherwise it is aligned to be 16 bytes.
*
* <p>Takes a byte[] and outputs a {@link ByteArrayWrapper}.
*/
private ByteArrayWrapper alignDataToWordSize(byte[] data) {
if (data.length == DoubleDataWord.BYTES) {
return new ByteArrayWrapper(new DoubleDataWord(data).getData());
} else {
return new ByteArrayWrapper(new DataWordImpl(data).getData());
}
}

/**
* If data.length > 16 then data is aligned to be 32 bytes.
*
* <p>Otherwise it is aligned to be 16 bytes.
*
* <p>This method should only be used for getting data from storage.
*/
private byte[] alignValueToWordSizeForGet(ByteArrayWrapper wrappedValue) {
byte[] value = wrappedValue.getData();

if (value.length > DataWordImpl.BYTES) {
return new DoubleDataWord(value).getData();
} else {
return new DataWordImpl(value).getData();
}
private ByteArrayWrapper alignValueToWordSizeForPut(byte[] bytes) {
return (allBytesAreZero(bytes)) ? new ByteArrayWrapper(bytes) : new ByteArrayWrapper(dropLeadingZeroes(bytes));
}

private InternalVmType getVmType(AionAddress destination) {
Expand Down Expand Up @@ -578,4 +541,45 @@ private static FastVmResultCode precompiledToFvmResultCode(PrecompiledResultCode
private static PrecompiledTransactionContext toPrecompiledTransactionContext(ExecutionContext context) {
return new PrecompiledTransactionContext(context.getDestinationAddress(), context.getOriginAddress(), context.getSenderAddress(), context.getSideEffects().getExecutionLogs(), context.getSideEffects().getInternalTransactions(), context.getSideEffects().getAddressesToBeDeleted(), context.getHashOfOriginTransaction(), context.getTransactionHash(), context.getBlockNumber(), context.getTransactionEnergy(), context.getTransactionStackDepth());
}

private static boolean allBytesAreZero(byte[] bytes) {
for (byte singleByte : bytes) {
if (singleByte != 0x0) {
return false;
}
}
return true;
}

private static int findIndexOfFirstNonZeroByte(byte[] bytes) {
int indexOfFirstNonZeroByte = 0;
for (byte singleByte : bytes) {
if (singleByte != 0x0) {
return indexOfFirstNonZeroByte;
}
indexOfFirstNonZeroByte++;
}
return indexOfFirstNonZeroByte;
}

/**
* Returns the input bytes but with all leading zero bytes removed.
*
* <p>If the input bytes consists of all zero bytes then an array of length 1 whose only byte is
* a zero byte is returned.
*
* @param bytes The bytes to chop.
* @return the chopped bytes.
*/
private static byte[] dropLeadingZeroes(byte[] bytes) {
int indexOfFirstNonZeroByte = findIndexOfFirstNonZeroByte(bytes);

if (indexOfFirstNonZeroByte == bytes.length) {
return new byte[1];
}

byte[] nonZeroBytes = new byte[bytes.length - indexOfFirstNonZeroByte];
System.arraycopy(bytes, indexOfFirstNonZeroByte, nonZeroBytes, 0, bytes.length - indexOfFirstNonZeroByte);
return nonZeroBytes;
}
}
7 changes: 3 additions & 4 deletions modVM/src/org/aion/vm/FvmTransactionExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
import org.aion.fastvm.FastVirtualMachine;
import org.aion.fastvm.FastVmResultCode;
import org.aion.fastvm.FastVmTransactionResult;
import org.aion.fastvm.FvmDataWord;
import org.aion.fastvm.IExternalStateForFvm;
import org.aion.fastvm.SideEffects;
import org.aion.mcf.core.AccountState;
import org.aion.mcf.db.IBlockStoreBase;
import org.aion.mcf.db.RepositoryCache;
import org.aion.mcf.tx.TxExecSummary;
import org.aion.mcf.vm.DataWord;
import org.aion.mcf.vm.types.DataWordImpl;
import org.aion.types.AionAddress;
import org.aion.types.Log;
import org.aion.util.bytes.ByteUtil;
Expand Down Expand Up @@ -233,10 +232,10 @@ private static long computeEnergyUsed(long limit, FastVmTransactionResult result

// TODO -- this has been marked as a temporary solution for a long time, someone should
// investigate
private static DataWord getDifficultyAsDataWord(byte[] diff) {
private static FvmDataWord getDifficultyAsDataWord(byte[] diff) {
if (diff.length > 16) {
diff = Arrays.copyOfRange(diff, diff.length - 16, diff.length);
}
return new DataWordImpl(diff);
return FvmDataWord.fromBytes(diff);
}
}

0 comments on commit cc696b6

Please sign in to comment.