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 12c22f62f9b..01381f4efe8 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;
@@ -37,6 +39,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..eb8fd00ded5 100644
--- a/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java
+++ b/rskj-core/src/main/java/org/ethereum/vm/CallCreate.java
@@ -20,26 +20,28 @@
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 BytesSlice data;
final byte[] destination;
final long gasLimit;
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 2f829c8b426..b47e8eb01c7 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;
@@ -627,7 +628,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);
@@ -1191,8 +1192,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..72ed443cb2f 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,8 +31,7 @@
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 {
@@ -46,6 +47,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);
+ }
+
public byte[] read(int address, int size) {
if (size <= 0) {
return EMPTY_BYTE_ARRAY;
@@ -224,4 +235,48 @@ 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;
+
+ MemorySlice(int address, int size) {
+ this.address = address;
+ this.size = size;
+ }
+
+ @Override
+ public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
+ BoundaryUtils.checkArraycopyParams(length(), srcPos, dest, destPos, length);
+
+ 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);
+ return readByte(address + index);
+ }
+ }
}
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 33ef0607727..b2cdfffdc67 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;
@@ -362,6 +363,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);
}
@@ -443,7 +448,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();
@@ -479,7 +484,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());
}
@@ -715,7 +720,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());
@@ -814,7 +819,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;
@@ -1382,7 +1387,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());
@@ -1628,6 +1633,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