bytesLen) {
+ if (off < 0 || length < 0 || Long.sum(off, length) > bytesLen) {
throw new IndexOutOfBoundsException("invalid 'off' and/or 'length': " + off + "; " + length);
}
diff --git a/rskj-core/src/main/java/org/ethereum/crypto/HashUtil.java b/rskj-core/src/main/java/org/ethereum/crypto/HashUtil.java
index aad79a2df3c..8350a22cbf5 100644
--- a/rskj-core/src/main/java/org/ethereum/crypto/HashUtil.java
+++ b/rskj-core/src/main/java/org/ethereum/crypto/HashUtil.java
@@ -20,6 +20,8 @@
package org.ethereum.crypto;
import co.rsk.core.RskAddress;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.ethereum.crypto.cryptohash.Keccak256;
@@ -59,6 +61,10 @@ public static byte[] sha256(byte[] input) {
}
public static byte[] keccak256(byte[] input) {
+ return keccak256(Bytes.of(input));
+ }
+
+ public static byte[] keccak256(BytesSlice input) {
Keccak256 digest = new Keccak256();
digest.update(input);
return digest.digest();
@@ -125,7 +131,7 @@ public static byte[] calcNewAddr(byte[] addr, byte[] nonce) {
* @param salt - salt to make different result addresses
* @return new address
*/
- public static byte[] calcSaltAddr(RskAddress senderAddress, byte[] initCode, byte[] salt) {
+ public static byte[] calcSaltAddr(RskAddress senderAddress, BytesSlice initCode, byte[] salt) {
// 0xff is of length 1
// keccak-256 of the address is of length 32
// Then we add the lengths of the senderAddress and the salt
diff --git a/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/Digest.java b/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/Digest.java
index cd90ab04fb5..45fdf680e92 100644
--- a/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/Digest.java
+++ b/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/Digest.java
@@ -21,6 +21,8 @@
package org.ethereum.crypto.cryptohash;
+import co.rsk.core.types.bytes.BytesSlice;
+
/**
* This interface documents the API for a hash function. This
* interface somewhat mimics the standard {@code
@@ -88,7 +90,7 @@ public interface Digest {
*
* @param inbuf the data bytes
*/
- public void update(byte[] inbuf);
+ public void update(BytesSlice inbuf);
/**
* Insert some more bytes.
@@ -97,7 +99,7 @@ public interface Digest {
* @param off the data offset in {@code inbuf}
* @param len the data length (in bytes)
*/
- public void update(byte[] inbuf, int off, int len);
+ public void update(BytesSlice inbuf, int off, int len);
/**
* Finalize the current hash computation and return the hash value
diff --git a/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/DigestEngine.java b/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/DigestEngine.java
index cbbd5ca56d7..21caf1930d4 100644
--- a/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/DigestEngine.java
+++ b/rskj-core/src/main/java/org/ethereum/crypto/cryptohash/DigestEngine.java
@@ -21,6 +21,9 @@
package org.ethereum.crypto.cryptohash;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
+
/**
*
This class is a template which can be used to implement hash
* functions. It takes care of some of the API, and also provides an
@@ -141,7 +144,8 @@ public byte[] digest()
/** @see org.ethereum.crypto.cryptohash.Digest */
public byte[] digest(byte[] input)
{
- update(input, 0, input.length);
+ BytesSlice bytesSlice = Bytes.of(input);
+ update(bytesSlice, 0, bytesSlice.length());
return digest();
}
@@ -181,21 +185,20 @@ public void update(byte input)
}
/** @see org.ethereum.crypto.cryptohash.Digest */
- public void update(byte[] input)
+ public void update(BytesSlice input)
{
- update(input, 0, input.length);
+ update(input, 0, input.length());
}
/** @see org.ethereum.crypto.cryptohash.Digest */
- public void update(byte[] input, int offset, int len)
+ public void update(BytesSlice input, int offset, int len)
{
while (len > 0) {
int copyLen = blockLen - inputLen;
if (copyLen > len) {
copyLen = len;
}
- System.arraycopy(input, offset, inputBuf, inputLen,
- copyLen);
+ input.arraycopy(offset, inputBuf, inputLen, copyLen);
offset += copyLen;
inputLen += copyLen;
len -= copyLen;
diff --git a/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java b/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java
index ed4f2448868..69bab39773c 100644
--- a/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java
+++ b/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java
@@ -19,6 +19,8 @@
package org.ethereum.util;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import org.bouncycastle.util.encoders.Hex;
import org.ethereum.db.ByteArrayWrapper;
@@ -38,6 +40,7 @@ private ByteUtil() {
}
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+ public static final BytesSlice EMPTY_BYTES_SLICE = Bytes.of(EMPTY_BYTE_ARRAY);
private static final byte[] ZERO_BYTE_ARRAY = new byte[]{0};
/**
diff --git a/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java b/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java
index 622ca1e7c3d..78589947a43 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java
@@ -17,29 +17,29 @@
* along with this program. If not, see .
*/
-
package org.ethereum.vm;
+import co.rsk.core.types.bytes.BytesSlice;
+
/**
* @author Roman Mandeleil
* @since 03.07.2014
*/
public class CallCreate {
- final byte[] data;
- final byte[] destination;
- final long gasLimit;
- final byte[] value;
-
+ private final BytesSlice data;
+ private final byte[] destination;
+ private final long gasLimit;
+ private final byte[] value;
- public CallCreate(byte[] data, byte[] destination, long gasLimit, byte[] value) {
+ public CallCreate(BytesSlice data, byte[] destination, long gasLimit, byte[] value) {
this.data = data;
this.destination = destination;
this.gasLimit = gasLimit;
this.value = value;
}
- public byte[] getData() {
+ public BytesSlice getData() {
return data;
}
diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java
index 47246e2bbc2..54554327862 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/VM.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java
@@ -22,6 +22,7 @@
import co.rsk.config.VmConfig;
import co.rsk.core.RskAddress;
import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import co.rsk.crypto.Keccak256;
import co.rsk.rpc.netty.ExecTimeoutContext;
import org.bouncycastle.util.BigIntegers;
@@ -628,7 +629,7 @@ protected void doSHA3() {
// EXECUTION PHASE
DataWord memOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
- byte[] buffer = program.memoryChunk(memOffsetData.intValue(), lengthData.intValue());
+ BytesSlice buffer = program.memorySlice(memOffsetData.intValue(), lengthData.intValue());
byte[] encoded = HashUtil.keccak256(buffer);
DataWord word = DataWord.valueOf(encoded);
@@ -1192,8 +1193,7 @@ protected void doLOG(){
// Int32 address values guaranteed by previous MAX_MEMORY checks
byte[] data = program.memoryChunk(memStart.intValue(), memOffset.intValue());
- LogInfo logInfo =
- new LogInfo(address.getLast20Bytes(), topics, data);
+ LogInfo logInfo = new LogInfo(address.getLast20Bytes(), topics, data);
if (isLogEnabled) {
hint = logInfo.toString();
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Memory.java b/rskj-core/src/main/java/org/ethereum/vm/program/Memory.java
index 32853f6d120..0d9afa9bfed 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/Memory.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/Memory.java
@@ -19,6 +19,8 @@
package org.ethereum.vm.program;
+import co.rsk.core.types.bytes.BoundaryUtils;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.program.listener.ProgramListener;
import org.ethereum.vm.program.listener.ProgramListenerAware;
@@ -29,16 +31,18 @@
import static java.lang.Math.ceil;
import static java.lang.Math.min;
import static java.lang.String.format;
-import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
-import static org.ethereum.util.ByteUtil.oneByteToHexString;
+import static org.ethereum.util.ByteUtil.*;
public class Memory implements ProgramListenerAware {
private static final int CHUNK_SIZE = 1024;
private static final int WORD_SIZE = 32;
- private List chunks = new LinkedList<>();
+ private final List chunks = new LinkedList<>();
+
private int softSize;
+ private int version; // versioning for memory changes
+
private ProgramListener traceListener;
@Override
@@ -46,6 +50,16 @@ public void setTraceListener(ProgramListener traceListener) {
this.traceListener = traceListener;
}
+ public BytesSlice readSlice(int address, int size) {
+ if (size <= 0) {
+ return EMPTY_BYTES_SLICE;
+ }
+
+ extend(address, size);
+
+ return new MemorySlice(address, size, version);
+ }
+
public byte[] read(int address, int size) {
if (size <= 0) {
return EMPTY_BYTE_ARRAY;
@@ -76,6 +90,8 @@ public byte[] read(int address, int size) {
}
public void write(int address, byte[] data, int dataSize, boolean limited) {
+ version++;
+
if (data.length < dataSize) {
dataSize = data.length;
}
@@ -112,7 +128,6 @@ public void write(int address, byte[] data, int dataSize, boolean limited) {
}
}
-
public void extendAndWrite(int address, int allocSize, byte[] data) {
extend(address, allocSize);
write(address, data, data.length, false);
@@ -224,4 +239,59 @@ private void addChunks(int num) {
chunks.add(new byte[CHUNK_SIZE]);
}
}
+
+ private final class MemorySlice implements BytesSlice {
+ private final int address;
+ private final int size;
+ private final int memVersion;
+
+ MemorySlice(int address, int size, int memVersion) {
+ this.address = address;
+ this.size = size;
+ this.memVersion = memVersion;
+ }
+
+ @Override
+ public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
+ BoundaryUtils.checkArraycopyParams(length(), srcPos, dest, destPos, length);
+ checkVersion();
+
+ int chunkIndex = (address + srcPos) / CHUNK_SIZE;
+ int chunkOffset = (address + srcPos) % CHUNK_SIZE;
+
+ int toGrab = length;
+ int start = destPos;
+
+ while (toGrab > 0) {
+ int copied = grabMax(chunkIndex, chunkOffset, toGrab, dest, start);
+
+ // read next chunk from the start
+ ++chunkIndex;
+ chunkOffset = 0;
+
+ // mark remind
+ toGrab -= copied;
+ start += copied;
+ }
+ }
+
+ @Override
+ public int length() {
+ return size;
+ }
+
+ @Override
+ public byte byteAt(int index) {
+ BoundaryUtils.checkArrayIndexParam(length(), index);
+ checkVersion();
+
+ return readByte(address + index);
+ }
+
+ private void checkVersion() {
+ if (this.memVersion != Memory.this.version) {
+ throw new IllegalStateException("Memory was changed during slice lifetime");
+ }
+ }
+ }
}
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java
index e326c26a5cf..c61266c280a 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java
@@ -22,6 +22,7 @@
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import co.rsk.crypto.Keccak256;
import co.rsk.pcc.NativeContract;
import co.rsk.peg.Bridge;
@@ -386,6 +387,10 @@ public DataWord memoryLoad(DataWord addr) {
return memory.readWord(addr.intValue());
}
+ public BytesSlice memorySlice(int offset, int size) {
+ return memory.readSlice(offset, size);
+ }
+
public byte[] memoryChunk(int offset, int size) {
return memory.read(offset, size);
}
@@ -467,7 +472,7 @@ public void createContract(DataWord value, DataWord memStart, DataWord memSize)
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) {
RskAddress senderAddress = new RskAddress(getOwnerAddress());
- byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
+ BytesSlice programCode = memorySlice(memStart.intValue(), memSize.intValue());
byte[] newAddressBytes = HashUtil.calcSaltAddr(senderAddress, programCode, salt.getData());
byte[] nonce = getStorage().getNonce(senderAddress).toByteArray();
@@ -503,7 +508,7 @@ private void createContract( RskAddress senderAddress, byte[] nonce, DataWord va
if (byTestingSuite()) {
// This keeps track of the contracts created for a test
- getResult().addCallCreate(programCode, EMPTY_BYTE_ARRAY,
+ getResult().addCallCreate(Bytes.of(programCode), EMPTY_BYTE_ARRAY,
gasLimit,
value.getNoLeadZeroesData());
}
@@ -752,7 +757,7 @@ public void callToAddress(MessageCall msg) {
return;
}
- byte[] data = memoryChunk(msg.getInDataOffs().intValue(), msg.getInDataSize().intValue());
+ BytesSlice data = memorySlice(msg.getInDataOffs().intValue(), msg.getInDataSize().intValue());
// FETCH THE SAVED STORAGE
RskAddress codeAddress = new RskAddress(msg.getCodeAddress());
@@ -851,7 +856,7 @@ private boolean executeCode(
Repository track,
byte[] programCode,
RskAddress senderAddress,
- byte[] data) {
+ BytesSlice data) {
returnDataBuffer = null; // reset return buffer right before the call
ProgramResult childResult;
@@ -1418,7 +1423,7 @@ public void callToPrecompiledAddress(MessageCall msg, PrecompiledContract contra
if (byTestingSuite()) {
// This keeps track of the calls created for a test
- this.getResult().addCallCreate(data,
+ this.getResult().addCallCreate(Bytes.of(data),
codeAddress.getBytes(),
msg.getGas().longValueSafe(),
msg.getEndowment().getNoLeadZeroesData());
@@ -1673,6 +1678,7 @@ public StackTooLargeException(String message) {
/**
* used mostly for testing reasons
*/
+ @VisibleForTesting
public byte[] getMemory() {
return memory.read(0, memory.size());
}
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java b/rskj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java
index 370c301a7e2..7cdda0610e3 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/ProgramResult.java
@@ -18,6 +18,7 @@
*/
package org.ethereum.vm.program;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.core.SignatureCache;
import org.ethereum.core.Transaction;
import org.ethereum.vm.CallCreate;
@@ -194,7 +195,7 @@ public List getCallCreateList() {
return callCreateList;
}
- public void addCallCreate(byte[] data, byte[] destination, long gasLimit, byte[] value) {
+ public void addCallCreate(BytesSlice data, byte[] destination, long gasLimit, byte[] value) {
getCallCreateList().add(new CallCreate(data, destination, gasLimit, value));
}
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactory.java b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactory.java
index 63befefe62e..d59cbb28c84 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactory.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactory.java
@@ -19,6 +19,7 @@
package org.ethereum.vm.program.invoke;
import co.rsk.core.Coin;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.core.Block;
import org.ethereum.core.Repository;
import org.ethereum.core.SignatureCache;
@@ -39,7 +40,7 @@ ProgramInvoke createProgramInvoke(Transaction tx, int txindex, Block block,
ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord callerAddress,
DataWord inValue, long inGas,
- Coin balanceInt, byte[] dataIn,
+ Coin balanceInt, BytesSlice dataIn,
Repository repository, BlockStore blockStore,
boolean isStaticCall, boolean byTestingSuite);
}
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java
index 258c3ecf416..7500d681bb7 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeFactoryImpl.java
@@ -21,6 +21,7 @@
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.core.Block;
import org.ethereum.core.Repository;
import org.ethereum.core.SignatureCache;
@@ -138,7 +139,7 @@ public ProgramInvoke createProgramInvoke(Transaction tx, int txindex, Block bloc
byte[] minGasPrice = minimumGasPrice != null ? minimumGasPrice.getBytes() : ByteUtil.EMPTY_BYTE_ARRAY;
- return new ProgramInvokeImpl(addr.getBytes(), origin, caller, balance.getBytes(), txGasPrice.getBytes(), gas, callValue.getBytes(), data,
+ return new ProgramInvokeImpl(addr.getBytes(), origin, caller, balance.getBytes(), txGasPrice.getBytes(), gas, callValue.getBytes(), Bytes.of(data),
lastHash, coinbase, timestamp, number, txindex,difficulty, gaslimit, minGasPrice,
repository, blockStore);
}
@@ -150,7 +151,7 @@ public ProgramInvoke createProgramInvoke(Transaction tx, int txindex, Block bloc
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord callerAddress,
DataWord inValue,
long inGas,
- Coin balanceInt, byte[] dataIn,
+ Coin balanceInt, BytesSlice dataIn,
Repository repository, BlockStore blockStore,
boolean isStaticCall, boolean byTestingSuite) {
@@ -163,7 +164,7 @@ public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, Da
long agas = inGas;
DataWord callValue = inValue;
- byte[] data = dataIn;
+ BytesSlice data = dataIn;
DataWord lastHash = program.getPrevHash();
DataWord coinbase = program.getCoinbase();
DataWord timestamp = program.getTimestamp();
@@ -198,7 +199,7 @@ public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, Da
txGasPrice.longValue(),
agas,
Bytes.of(callValue.getNoLeadZeroesData()),
- data == null ? "" : ByteUtil.toHexString(data),
+ data == null ? "" : data.toHexString(),
Bytes.of(lastHash.getData()),
Bytes.of(coinbase.getLast20Bytes()),
timestamp.longValue(),
diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeImpl.java b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeImpl.java
index 4127dfa833b..485fd97a5d1 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeImpl.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/program/invoke/ProgramInvokeImpl.java
@@ -19,13 +19,14 @@
package org.ethereum.vm.program.invoke;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.core.Repository;
import org.ethereum.db.BlockStore;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.program.Program;
import java.math.BigInteger;
-import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
@@ -35,7 +36,7 @@
*/
public class ProgramInvokeImpl implements ProgramInvoke {
- private BlockStore blockStore;
+ private final BlockStore blockStore;
/**
* TRANSACTION env **
*/
@@ -45,9 +46,8 @@ public class ProgramInvokeImpl implements ProgramInvoke {
private final DataWord balance;
private final DataWord txGasPrice;
private final DataWord callValue;
- private long gas;
-
- byte[] msgData;
+ private final long gas;
+ private final BytesSlice msgData;
/**
* BLOCK env **
@@ -73,7 +73,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord txGasPrice,
long gas,
- DataWord callValue, byte[] msgData,
+ DataWord callValue, BytesSlice msgData,
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord transactionIndex, DataWord
difficulty,
DataWord gaslimit, DataWord minimumGasPrice, Repository repository, int callDeep, BlockStore blockStore,
@@ -114,7 +114,7 @@ public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] ba
byte[] gaslimit, byte[] minimumGasPrice,
Repository repository, BlockStore blockStore,
boolean byTestingSuite) {
- this(address, origin, caller, balance, txGasPrice, gas, callValue, msgData, lastHash, coinbase,
+ this(address, origin, caller, balance, txGasPrice, gas, callValue, Bytes.of(msgData), lastHash, coinbase,
timestamp, number, transactionIndex, difficulty, gaslimit, minimumGasPrice, repository, blockStore);
this.byTestingSuite = byTestingSuite;
@@ -122,7 +122,7 @@ public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] ba
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
- byte[] txGasPrice, byte[] gas, byte[] callValue, byte[] msgData,
+ byte[] txGasPrice, byte[] gas, byte[] callValue, BytesSlice msgData,
byte[] lastHash, byte[] coinbase, long timestamp, long number, int transactionIndex, byte[] difficulty,
byte[] gaslimit, byte[] minimumGasPrice,
Repository repository, BlockStore blockStore) {
@@ -209,16 +209,16 @@ public DataWord getDataValue(DataWord indexData) {
int index = tempIndex.intValue(); // possible overflow is caught below
int size = 32; // maximum datavalue size
- if (msgData == null || index >= msgData.length
+ if (msgData == null || index >= msgData.length()
|| tempIndex.compareTo(maxMsgData) == 1) {
return DataWord.ZERO;
}
- if (index + size > msgData.length) {
- size = msgData.length - index;
+ if (index + size > msgData.length()) {
+ size = msgData.length() - index;
}
byte[] data = new byte[32];
- System.arraycopy(msgData, index, data, 0, size);
+ msgData.arraycopy(index, data, 0, size);
return DataWord.valueOf(data);
}
@@ -226,10 +226,10 @@ public DataWord getDataValue(DataWord indexData) {
@Override
public DataWord getDataSize() {
- if (msgData == null || msgData.length == 0) {
+ if (msgData == null || msgData.length() == 0) {
return DataWord.ZERO;
}
- int size = msgData.length;
+ int size = msgData.length();
return DataWord.valueOf(size);
}
@@ -245,14 +245,14 @@ public byte[] getDataCopy(DataWord offsetData, DataWord lengthData) {
if (msgData == null) {
return data;
}
- if (offset > msgData.length) {
+ if (offset > msgData.length()) {
return data;
}
- if (offset + length > msgData.length) {
- length = msgData.length - offset;
+ if (offset + length > msgData.length()) {
+ length = msgData.length() - offset;
}
- System.arraycopy(msgData, offset, data, 0, length);
+ msgData.arraycopy(offset, data, 0, length);
return data;
}
@@ -388,7 +388,7 @@ public boolean equals(Object o) {
if (minimumGasPrice != null ? !minimumGasPrice.equals(that.minimumGasPrice) : that.minimumGasPrice != null) {
return false;
}
- if (!Arrays.equals(msgData, that.msgData)) {
+ if (!BytesSlice.equals(msgData, that.msgData)) {
return false;
}
if (number != null ? !number.equals(that.number) : that.number != null) {
@@ -416,7 +416,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
int result = Objects.hash(address, origin, caller, balance, txGasPrice, callValue, gas, prevHash, coinbase, timestamp, number, difficulty, gaslimit, minimumGasPrice, storage, repository, byTransaction, byTestingSuite);
- result = 31 * result + Arrays.hashCode(msgData);
+ result = 31 * result + BytesSlice.hashCode(msgData);
return result;
}
@@ -430,7 +430,7 @@ public String toString() {
", gas=" + gas +
", txGasPrice=" + txGasPrice +
", callValue=" + callValue +
- ", msgData=" + Arrays.toString(msgData) +
+ ", msgData=" + msgData +
", prevHash=" + prevHash +
", coinbase=" + coinbase +
", timestamp=" + timestamp +
diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java
index 05c7d41ca3c..2a478392b42 100644
--- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java
+++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java
@@ -359,7 +359,7 @@ void getHashShouldReuseCalculatedValue() {
header.getHash();
}
- hashUtilMocked.verify(() -> HashUtil.keccak256(ArgumentMatchers.any()));
+ hashUtilMocked.verify(() -> HashUtil.keccak256((byte[]) ArgumentMatchers.any()));
}
}
diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java
index 16ae34501ea..fcb213c0b94 100644
--- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java
+++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java
@@ -24,6 +24,7 @@
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
import co.rsk.core.TransactionExecutorFactory;
+import co.rsk.core.types.bytes.Bytes;
import co.rsk.db.*;
import co.rsk.peg.BridgeSupportFactory;
import co.rsk.peg.BtcBlockStoreWithCache.Factory;
@@ -1443,7 +1444,7 @@ private static Transaction createStrangeTransaction(
private static byte[] sha3(byte[] input) {
Keccak256 digest = new Keccak256();
- digest.update(input);
+ digest.update(Bytes.of(input));
return digest.digest();
}
diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/trace/TraceTransformerTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/trace/TraceTransformerTest.java
index da578fc7499..26e3612a050 100644
--- a/rskj-core/src/test/java/co/rsk/rpc/modules/trace/TraceTransformerTest.java
+++ b/rskj-core/src/test/java/co/rsk/rpc/modules/trace/TraceTransformerTest.java
@@ -18,6 +18,7 @@
package co.rsk.rpc.modules.trace;
+import co.rsk.core.types.bytes.Bytes;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.program.invoke.ProgramInvoke;
import org.ethereum.vm.program.invoke.ProgramInvokeImpl;
@@ -42,7 +43,7 @@ void getActionFromInvokeData() {
null,
gas,
callValue,
- data,
+ Bytes.of(data),
null, null, null, null, null, null, null,
null, null, 0, null, false, false);
diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestProgramInvokeFactory.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestProgramInvokeFactory.java
index 3af60cfbe7c..bacd665bb6a 100644
--- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestProgramInvokeFactory.java
+++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestProgramInvokeFactory.java
@@ -20,6 +20,8 @@
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import org.ethereum.core.Block;
import org.ethereum.core.Repository;
import org.ethereum.core.SignatureCache;
@@ -53,7 +55,7 @@ public ProgramInvoke createProgramInvoke(Transaction tx, int txindex, Block bloc
@Override
public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord callerAddress,
DataWord inValue, long inGas,
- Coin balanceInt, byte[] dataIn,
+ Coin balanceInt, BytesSlice dataIn,
Repository repository, BlockStore blockStore,
boolean isStaticCall, boolean byTestingSuite) {
return null;
@@ -113,7 +115,7 @@ private ProgramInvoke generalInvoke(Transaction tx, int txindex, Block block, Re
Coin minimumGasPrice = block.getMinimumGasPrice();
return new ProgramInvokeImpl(addr.getBytes(), origin.getBytes(), caller.getBytes(), balance.getBytes(),
- txGasPrice.getBytes(), gas, callValue.getBytes(), data, lastHash, coinbase,
+ txGasPrice.getBytes(), gas, callValue.getBytes(), Bytes.of(data), lastHash, coinbase,
timestamp, number, txindex, difficulty, gaslimit, minimumGasPrice.getBytes(), repository, blockStore);
}
diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java
index 1109dcec96c..32c8af7b1a2 100644
--- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java
+++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java
@@ -558,13 +558,13 @@ public List runTestCase(TestingCase testCase) {
boolean assertData = Arrays.equals(
expectedCallCreate.getData(),
- resultCallCreate.getData());
+ resultCallCreate.getData().copyArray());
if (!assertData) {
String output =
String.format("Call/Create data is different. Expected: [ %s ], result: [ %s ]",
ByteUtil.toHexString(expectedCallCreate.getData()),
- ByteUtil.toHexString(resultCallCreate.getData()));
+ resultCallCreate.getData().toHexString());
logger.info(output);
results.add(output);
}
diff --git a/rskj-core/src/test/java/org/ethereum/util/HashUtilTest.java b/rskj-core/src/test/java/org/ethereum/util/HashUtilTest.java
index 83ee72e22f5..1dd7019a00c 100644
--- a/rskj-core/src/test/java/org/ethereum/util/HashUtilTest.java
+++ b/rskj-core/src/test/java/org/ethereum/util/HashUtilTest.java
@@ -20,6 +20,7 @@
package org.ethereum.util;
import co.rsk.core.RskAddress;
+import co.rsk.core.types.bytes.Bytes;
import org.bouncycastle.util.encoders.Hex;
import org.ethereum.crypto.HashUtil;
import org.junit.jupiter.api.Assertions;
@@ -199,9 +200,9 @@ void testToPrintableHash_NullPointerExceptionForNull() {
private void runTestCalSaltAddr(String address, String saltString, String code, String expected){
RskAddress r = new RskAddress(address);
byte[] salt = Hex.decode(saltString);
- byte[] init_code = Hex.decode(code);
+ byte[] initCode = Hex.decode(code);
- String result = ByteUtil.toHexString(HashUtil.calcSaltAddr(r,init_code,salt));
+ String result = ByteUtil.toHexString(HashUtil.calcSaltAddr(r, Bytes.of(initCode), salt));
assertEquals(expected.toUpperCase(), result.toUpperCase());
}
}
diff --git a/rskj-core/src/test/java/org/ethereum/vm/MemoryTest.java b/rskj-core/src/test/java/org/ethereum/vm/MemoryTest.java
index 89a68ad2b70..611efff84f4 100644
--- a/rskj-core/src/test/java/org/ethereum/vm/MemoryTest.java
+++ b/rskj-core/src/test/java/org/ethereum/vm/MemoryTest.java
@@ -19,16 +19,15 @@
package org.ethereum.vm;
+import co.rsk.core.types.bytes.BytesSlice;
+import org.bouncycastle.util.encoders.Hex;
import org.ethereum.vm.program.Memory;
import org.junit.jupiter.api.Test;
-import org.bouncycastle.util.encoders.Hex;
import java.util.Arrays;
import static java.lang.Math.ceil;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
class MemoryTest {
@@ -530,6 +529,34 @@ void memoryWriteLimited_3(){
assertEquals(10, zero);
}
+ @Test
+ void memorySliceMatchesWithWritten() {
+ Memory memory = new Memory();
+ memory.write(0, new byte[]{1, 2, 3, 4}, 4, false);
+ BytesSlice memSlice = memory.readSlice(0, 4);
+
+ assertArrayEquals(new byte[]{1, 2, 3, 4}, memSlice.copyArray());
+ assertArrayEquals(memory.read(0, 4), memSlice.copyArray());
+ }
+
+ @Test
+ void memorySliceMatchesWithWrittenAndExpansion() {
+ Memory memory = new Memory();
+ memory.write(0, new byte[]{1, 2, 3, 4}, 4, false);
+ BytesSlice memSlice = memory.readSlice(0, 8);
+ assertArrayEquals(new byte[]{1, 2, 3, 4, 0, 0, 0, 0}, memSlice.copyArray());
+ assertArrayEquals(memory.read(0, 8), memSlice.copyArray());
+ }
+ @Test
+ void memoryWriteInvalidatesSlice() {
+ Memory memory = new Memory();
+ memory.write(0, new byte[]{1, 2, 3, 4}, 4, false);
+ BytesSlice memSlice = memory.readSlice(0, 4);
+ memory.write(0, new byte[]{4, 3, 2, 1}, 4, false);
+
+ assertThrows(IllegalStateException.class, () -> memSlice.byteAt(0), "Memory was changed during slice lifetime");
+ assertThrows(IllegalStateException.class, () -> memSlice.arraycopy(0, new byte[4], 0, 4), "Memory was changed during slice lifetime");
+ }
}