From cc696b67bf2434335bfb6ec98b1f8442dd90cbec Mon Sep 17 00:00:00 2001 From: aionick Date: Wed, 17 Jul 2019 16:58:21 -0400 Subject: [PATCH] Fvm now defines its own data word --- aion_fastvm | 2 +- .../src/org/aion/vm/ExternalStateForFvm.java | 120 +++++++++--------- .../org/aion/vm/FvmTransactionExecutor.java | 7 +- 3 files changed, 66 insertions(+), 63 deletions(-) diff --git a/aion_fastvm b/aion_fastvm index fb1ea36cfb..58e773bcf1 160000 --- a/aion_fastvm +++ b/aion_fastvm @@ -1 +1 @@ -Subproject commit fb1ea36cfb0df8eda176f23780cfc92b5129f4ed +Subproject commit 58e773bcf1803e418d963dc76454e50b71d7929f diff --git a/modVM/src/org/aion/vm/ExternalStateForFvm.java b/modVM/src/org/aion/vm/ExternalStateForFvm.java index f9249e0799..31023253dd 100644 --- a/modVM/src/org/aion/vm/ExternalStateForFvm.java +++ b/modVM/src/org/aion/vm/ExternalStateForFvm.java @@ -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; @@ -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 repository, AionAddress miner, DataWord blockDifficulty, boolean isLocalCall, boolean allowNonceIncrement, boolean isFork040enabled, long blockNumber, long blockTimestamp, long blockEnergyLimit) { + public ExternalStateForFvm(RepositoryCache 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; @@ -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."); } @@ -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); } @@ -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()); } /** @@ -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(); } /** @@ -477,45 +477,8 @@ public byte[] getBlockHashByNumber(long blockNumber) { * *

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. - * - *

Otherwise it is aligned to be 16 bytes. - * - *

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. - * - *

Otherwise it is aligned to be 16 bytes. - * - *

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) { @@ -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. + * + *

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; + } } diff --git a/modVM/src/org/aion/vm/FvmTransactionExecutor.java b/modVM/src/org/aion/vm/FvmTransactionExecutor.java index 28fd60fbca..8976db4a3f 100644 --- a/modVM/src/org/aion/vm/FvmTransactionExecutor.java +++ b/modVM/src/org/aion/vm/FvmTransactionExecutor.java @@ -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; @@ -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); } }