diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java
index ce988888068..cb4c16d17d3 100644
--- a/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java
+++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java
@@ -23,7 +23,6 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import java.util.Arrays;
import java.util.Objects;
/**
@@ -140,8 +139,8 @@ public byte byteAt(int index) {
}
@Override
- public byte[] copyArrayOfRange(int from, int to) {
- return Arrays.copyOfRange(byteArray, from, to);
+ public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
+ System.arraycopy(byteArray, srcPos, dest, destPos, length);
}
@Override
diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java
index 5210ff8eea0..ac3616fd8f0 100644
--- a/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java
+++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java
@@ -18,11 +18,62 @@
package co.rsk.core.types.bytes;
+import java.util.Arrays;
+
/**
* A {@link BytesSlice} is a subsequence of bytes backed by another broader byte sequence.
*/
public interface BytesSlice extends HexPrintableBytes {
+ /**
+ * Copies an array from the {@link BytesSlice} source, beginning at the
+ * specified position, to the specified position of the destination array.
+ * A subsequence of array components are copied from this instance to the
+ * destination array referenced by {@code dest}. The number of components
+ * copied is equal to the {@code length} argument. The components at
+ * positions {@code srcPos} through {@code srcPos+length-1} in the source
+ * array are copied into positions {@code destPos} through
+ * {@code destPos+length-1}, respectively, of the destination
+ * array.
+ *
+ * If the underlying byte array and {@code dest} argument refer to the
+ * same array object, then the copying is performed as if the
+ * components at positions {@code srcPos} through
+ * {@code srcPos+length-1} were first copied to a temporary
+ * array with {@code length} components and then the contents of
+ * the temporary array were copied into positions
+ * {@code destPos} through {@code destPos+length-1} of the
+ * destination array.
+ *
+ * If {@code dest} is {@code null}, then a
+ * {@code NullPointerException} is thrown.
+ *
+ * Otherwise, if any of the following is true, an
+ * {@code IndexOutOfBoundsException} is
+ * thrown and the destination is not modified:
+ *
+ *
The {@code srcPos} argument is negative.
+ *
The {@code destPos} argument is negative.
+ *
The {@code length} argument is negative.
+ *
{@code srcPos+length} is greater than
+ * {@code src.length}, the length of the source array.
+ *
{@code destPos+length} is greater than
+ * {@code dest.length}, the length of the destination array.
+ *
+ *
+ *
+ * Note: this method mimics behaviour of {@link System#arraycopy(Object, int, Object, int, int)}
+ *
+ * @param srcPos starting position in the source array.
+ * @param dest the destination array.
+ * @param destPos starting position in the destination data.
+ * @param length the number of array elements to be copied.
+ * @throws IndexOutOfBoundsException if copying would cause
+ * access of data outside array bounds.
+ * @throws NullPointerException if {@code dest} is {@code null}.
+ */
+ void arraycopy(int srcPos, byte[] dest, int destPos, int length);
+
/**
* Copies the specified range of the specified array into a new array.
* The initial index of the range (from) must lie between zero
@@ -37,17 +88,30 @@ public interface BytesSlice extends HexPrintableBytes {
* greater than or equal to original.length - from. The length
* of the returned array will be to - from.
*
+ *
+ * Note: this method mimics behaviour of {@link Arrays#copyOfRange(Object[], int, int)}
+ *
* @param from the initial index of the range to be copied, inclusive
* @param to the final index of the range to be copied, exclusive.
* (This index may lie outside the array.)
* @return a new array containing the specified range from the original array,
* truncated or padded with zeros to obtain the required length
- * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
+ * @throws IndexOutOfBoundsException if {@code from < 0}
* or {@code from > original.length}
* @throws IllegalArgumentException if from > to
- * @throws NullPointerException if original is null
*/
- byte[] copyArrayOfRange(int from, int to);
+ default byte[] copyArrayOfRange(int from, int to) {
+ if (from < 0 || from > length()) {
+ throw new IndexOutOfBoundsException("invalid 'from': " + from);
+ }
+ int newLength = to - from;
+ if (newLength < 0) {
+ throw new IllegalArgumentException(from + " > " + to);
+ }
+ byte[] copy = new byte[newLength];
+ arraycopy(from, copy, 0, Math.min(length() - from, newLength));
+ return copy;
+ }
default byte[] copyArray() {
return copyArrayOfRange(0, length());
@@ -104,11 +168,17 @@ public byte byteAt(int index) {
}
@Override
- public byte[] copyArrayOfRange(int from, int to) {
- if (from < 0 || from > to || to > length()) {
- throw new IndexOutOfBoundsException("invalid 'from' and/or 'to': [" + from + ";" + to + ")");
+ public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
+ if (length < 0) {
+ throw new IndexOutOfBoundsException("invalid 'length': " + length);
+ }
+ if (srcPos < 0 || srcPos + length > length()) {
+ throw new IndexOutOfBoundsException("invalid 'srcPos' and/or 'length': [" + srcPos + ";" + length + ")");
+ }
+ if (destPos < 0 || destPos + length > dest.length) {
+ throw new IndexOutOfBoundsException("invalid 'destPos' and/or 'length': [" + destPos + ";" + length + ")");
}
- return originBytes.copyArrayOfRange(this.from + from, this.from + to);
+ originBytes.arraycopy(this.from + srcPos, dest, destPos, length);
}
@Override
diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java
index d700c0cc6a6..3f91f918eda 100644
--- a/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java
+++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java
@@ -77,7 +77,7 @@ public String toFormattedString(@Nonnull HexPrintableBytes printableBytes, int o
}
if (length > 32) {
- return printableBytes.toHexString(off, 15) + ".." + printableBytes.toHexString(off + length - 15, 15);
+ return printableBytes.toHexString(off, 16) + ".." + printableBytes.toHexString(off + length - 15, 15);
}
return printableBytes.toHexString(off, length);
}
diff --git a/rskj-core/src/main/java/co/rsk/util/StringUtils.java b/rskj-core/src/main/java/co/rsk/util/StringUtils.java
index a7c58a322b3..02ef4106555 100644
--- a/rskj-core/src/main/java/co/rsk/util/StringUtils.java
+++ b/rskj-core/src/main/java/co/rsk/util/StringUtils.java
@@ -24,6 +24,8 @@ public class StringUtils {
private static final int DEFAULT_MAX_LEN = 64;
+ private StringUtils() { /* hidden */ }
+
public static String trim(@Nullable String src) {
return trim(src, DEFAULT_MAX_LEN);
}
diff --git a/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java b/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java
index 4c6cf7a8296..50750a55ba2 100644
--- a/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java
+++ b/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java
@@ -20,6 +20,8 @@
package org.ethereum.core;
import co.rsk.core.RskAddress;
+import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -36,7 +38,6 @@
import java.util.Arrays;
import static java.lang.String.format;
-import static org.apache.commons.lang3.ArrayUtils.subarray;
import static org.apache.commons.lang3.StringUtils.stripEnd;
import static org.ethereum.util.ByteUtil.longToBytesNoLeadZeroes;
@@ -119,9 +120,9 @@ public static Type getType(String typeName) {
*/
public abstract byte[] encode(Object value);
- public abstract Object decode(byte[] encoded, int offset);
+ public abstract Object decode(BytesSlice encoded, int offset);
- public Object decode(byte[] encoded) {
+ public Object decode(BytesSlice encoded) {
return decode(encoded, 0);
}
@@ -187,12 +188,12 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return decodeInt(encoded, offset);
}
- public static BigInteger decodeInt(byte[] encoded, int offset) {
- return new BigInteger(Arrays.copyOfRange(encoded, offset, offset + 32));
+ public static BigInteger decodeInt(BytesSlice encoded, int offset) {
+ return new BigInteger(encoded.copyArrayOfRange(offset, offset + 32));
}
public static byte[] encodeInt(int i) {
@@ -222,7 +223,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0);
}
}
@@ -350,7 +351,7 @@ public Object[] decodeEventData(byte[] encodedData) {
checkFunctionType(FunctionType.event);
Param[] dataInputs = Arrays.stream(inputs).filter(i -> !i.indexed).toArray(Param[]::new);
- return decode(encodedData, dataInputs);
+ return decode(Bytes.of(encodedData), dataInputs);
}
private void checkFunctionType(FunctionType expected) {
@@ -405,7 +406,7 @@ public byte[] encodeOutputs(Object... args) {
return encodeArguments(outputs, args);
}
- private Object[] decode(byte[] encoded, Param[] params) {
+ private Object[] decode(BytesSlice encoded, Param[] params) {
Object[] ret = new Object[params.length];
int off = 0;
@@ -421,11 +422,11 @@ private Object[] decode(byte[] encoded, Param[] params) {
}
public Object[] decode(byte[] encoded) {
- return decode(subarray(encoded, 4, encoded.length), inputs);
+ return decode(Bytes.of(encoded).slice(4, encoded.length), inputs);
}
public Object[] decodeResult(byte[] encodedRet) {
- return decode(encodedRet, outputs);
+ return decode(Bytes.of(encodedRet), outputs);
}
public String formatSignature() {
diff --git a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java
index a4ef7aa34be..23d6197fc6c 100644
--- a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java
+++ b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java
@@ -20,6 +20,7 @@
package org.ethereum.solidity;
import co.rsk.core.types.bytes.Bytes;
+import co.rsk.core.types.bytes.BytesSlice;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.ethereum.util.ByteUtil;
@@ -101,9 +102,9 @@ public static SolidityType getType(String typeName) {
*/
public abstract byte[] encode(Object value);
- public abstract Object decode(byte[] encoded, int offset);
+ public abstract Object decode(BytesSlice encoded, int offset);
- public Object decode(byte[] encoded) {
+ public Object decode(BytesSlice encoded) {
return decode(encoded, 0);
}
@@ -196,7 +197,7 @@ public byte[] encodeList(List l) {
}
@Override
- public Object[] decode(byte[] encoded, int offset) {
+ public Object[] decode(BytesSlice encoded, int offset) {
Utils.validateArrayAllegedSize(encoded, offset, getFixedSize());
Object[] result = new Object[size];
for (int i = 0; i < size; i++) {
@@ -247,8 +248,8 @@ public byte[] encodeList(List l) {
}
@Override
- public Object decode(byte[] encoded, int origOffset) {
- if (encoded.length == 0) {
+ public Object decode(BytesSlice encoded, int origOffset) {
+ if (encoded.length() == 0) {
return new Object[0];
}
int len = IntType.decodeInt(encoded, origOffset).intValue();
@@ -299,7 +300,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
int len = IntType.decodeInt(encoded, offset).intValue();
offset += IntType.INT_SIZE;
return Utils.safeCopyOfRange(encoded, offset, len);
@@ -325,7 +326,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return new String((byte[]) super.decode(encoded, offset), StandardCharsets.UTF_8);
}
}
@@ -357,7 +358,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return Utils.safeCopyOfRange(encoded, offset, getFixedSize());
}
}
@@ -383,7 +384,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
BigInteger asBigInteger = (BigInteger) super.decode(encoded, offset);
return DataWord.valueOf(asBigInteger.toByteArray());
}
@@ -434,14 +435,14 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return decodeInt(encoded, offset);
}
- public static BigInteger decodeInt(byte[] encoded, int offset) {
+ public static BigInteger decodeInt(BytesSlice encoded, int offset) {
// This is here because getGasForData might send an empty payload which will produce an exception
// But currently the bridge would return the cost of RELEASE_BTC in this situation
- if (encoded.length == 0) {
+ if (encoded.length() == 0) {
return BigInteger.ZERO;
}
return new BigInteger(Utils.safeCopyOfRange(encoded, offset, INT_SIZE));
@@ -474,7 +475,7 @@ public byte[] encode(Object value) {
}
@Override
- public Object decode(byte[] encoded, int offset) {
+ public Object decode(BytesSlice encoded, int offset) {
return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0);
}
}
diff --git a/rskj-core/src/main/java/org/ethereum/util/RLP.java b/rskj-core/src/main/java/org/ethereum/util/RLP.java
index 5caf38f071f..4a0e1728baa 100644
--- a/rskj-core/src/main/java/org/ethereum/util/RLP.java
+++ b/rskj-core/src/main/java/org/ethereum/util/RLP.java
@@ -22,7 +22,10 @@
import co.rsk.core.BlockDifficulty;
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.util.RLPException;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.util.BigIntegers;
import org.ethereum.db.ByteArrayWrapper;
@@ -100,7 +103,8 @@ public class RLP {
* byte with value 0x80 plus the length of the string followed by the
* string. The range of the first byte is thus [0x80, 0xb7].
*/
- private static final int OFFSET_SHORT_ITEM = 0x80;
+ @VisibleForTesting
+ static final int OFFSET_SHORT_ITEM = 0x80;
/**
* [0xb7]
@@ -111,7 +115,8 @@ public class RLP {
* \xb9\x04\x00 followed by the string. The range of the first byte is thus
* [0xb8, 0xbf].
*/
- private static final int OFFSET_LONG_ITEM = 0xb7;
+ @VisibleForTesting
+ static final int OFFSET_LONG_ITEM = 0xb7;
/**
* [0xc0]
@@ -121,7 +126,8 @@ public class RLP {
* of the RLP encodings of the items. The range of the first byte is thus
* [0xc0, 0xf7].
*/
- private static final int OFFSET_SHORT_LIST = 0xc0;
+ @VisibleForTesting
+ static final int OFFSET_SHORT_LIST = 0xc0;
/**
* [0xf7]
@@ -131,29 +137,14 @@ public class RLP {
* followed by the concatenation of the RLP encodings of the items. The
* range of the first byte is thus [0xf8, 0xff].
*/
- private static final int OFFSET_LONG_LIST = 0xf7;
+ @VisibleForTesting
+ static final int OFFSET_LONG_LIST = 0xf7;
/* ******************************************************
* DECODING *
* ******************************************************/
- private static byte decodeOneByteItem(byte[] data, int index) {
- // null item
- if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) {
- return (byte) (data[index] - OFFSET_SHORT_ITEM);
- }
- // single byte item
- if ((data[index] & 0xFF) < OFFSET_SHORT_ITEM) {
- return data[index];
- }
- // single byte item
- if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM + 1) {
- return data[index + 1];
- }
- return 0;
- }
-
public static int decodeInt(byte[] data, int index) {
// NOTE: there are two ways zero can be encoded - 0x00 and OFFSET_SHORT_ITEM
@@ -239,108 +230,7 @@ public static int getNextElementIndex(byte[] payload, int pos) {
return -1;
}
- /**
- * Get exactly one message payload
- */
- public static void fullTraverse(byte[] msgData, int level, int startPos,
- int endPos, int levelToIndex, Queue index) {
-
- try {
-
- if (msgData == null || msgData.length == 0) {
- return;
- }
- int pos = startPos;
-
- while (pos < endPos) {
-
- if (level == levelToIndex) {
- index.add(pos);
- }
-
- // It's a list with a payload more than 55 bytes
- // data[0] - 0xF7 = how many next bytes allocated
- // for the length of the list
- if ((msgData[pos] & 0xFF) >= OFFSET_LONG_LIST) {
-
- byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_LIST);
- int length = calcLength(lengthOfLength, msgData, pos);
-
- // now we can parse an item for data[1]..data[length]
- System.out.println("-- level: [" + level
- + "] Found big list length: " + length);
-
- fullTraverse(msgData, level + 1, pos + lengthOfLength + 1,
- pos + lengthOfLength + length, levelToIndex, index);
-
- pos += lengthOfLength + length + 1;
- continue;
- }
- // It's a list with a payload less than 55 bytes
- if ((msgData[pos] & 0xFF) >= OFFSET_SHORT_LIST
- && (msgData[pos] & 0xFF) < OFFSET_LONG_LIST) {
-
- byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_LIST);
-
- System.out.println("-- level: [" + level
- + "] Found small list length: " + length);
-
- fullTraverse(msgData, level + 1, pos + 1, pos + length + 1,
- levelToIndex, index);
-
- pos += 1 + length;
- continue;
- }
- // It's an item with a payload more than 55 bytes
- // data[0] - 0xB7 = how much next bytes allocated for
- // the length of the string
- if ((msgData[pos] & 0xFF) >= OFFSET_LONG_ITEM
- && (msgData[pos] & 0xFF) < OFFSET_SHORT_LIST) {
-
- byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_ITEM);
- int length = calcLength(lengthOfLength, msgData, pos);
-
- // now we can parse an item for data[1]..data[length]
- System.out.println("-- level: [" + level
- + "] Found big item length: " + length);
- pos += lengthOfLength + length + 1;
-
- continue;
- }
- // It's an item less than 55 bytes long,
- // data[0] - 0x80 == length of the item
- if ((msgData[pos] & 0xFF) > OFFSET_SHORT_ITEM
- && (msgData[pos] & 0xFF) < OFFSET_LONG_ITEM) {
-
- byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_ITEM);
-
- System.out.println("-- level: [" + level
- + "] Found small item length: " + length);
- pos += 1 + length;
- continue;
- }
- // null item
- if ((msgData[pos] & 0xFF) == OFFSET_SHORT_ITEM) {
- System.out.println("-- level: [" + level
- + "] Found null item: ");
- pos += 1;
- continue;
- }
- // single byte item
- if ((msgData[pos] & 0xFF) < OFFSET_SHORT_ITEM) {
- System.out.println("-- level: [" + level
- + "] Found single item: ");
- pos += 1;
- continue;
- }
- }
- } catch (Throwable th) {
- throw new RuntimeException("RLP wrong encoding",
- th.fillInStackTrace());
- }
- }
-
- private static int calcLength(int lengthOfLength, byte[] msgData, int pos) {
+ static int calcLength(int lengthOfLength, byte[] msgData, int pos) {
byte pow = (byte) (lengthOfLength - 1);
int length = 0;
for (int i = 1; i <= lengthOfLength; ++i) {
@@ -350,6 +240,11 @@ private static int calcLength(int lengthOfLength, byte[] msgData, int pos) {
return length;
}
+ @Nonnull
+ public static ArrayList decode2(@CheckForNull byte[] msgData) {
+ return decode2(Bytes.of(msgData));
+ }
+
/**
* Parse wire byte[] message into RLP elements
*
@@ -358,14 +253,14 @@ private static int calcLength(int lengthOfLength, byte[] msgData, int pos) {
* - outcome of recursive RLP structure
*/
@Nonnull
- public static ArrayList decode2(@CheckForNull byte[] msgData) {
+ public static ArrayList decode2(@CheckForNull BytesSlice msgData) {
ArrayList elements = new ArrayList<>();
if (msgData == null) {
return elements;
}
- int tlength = msgData.length;
+ int tlength = msgData.length();
int position = 0;
while (position < tlength) {
@@ -382,11 +277,11 @@ public static RLPElement decodeFirstElement(@CheckForNull byte[] msgData, int po
return null;
}
- return decodeElement(msgData, position).getKey();
+ return decodeElement(Bytes.of(msgData), position).getKey();
}
- private static Pair decodeElement(byte[] msgData, int position) { // NOSONAR
- int b0 = msgData[position] & 0xff;
+ private static Pair decodeElement(BytesSlice msgData, int position) {
+ int b0 = msgData.byteAt(position) & 0xff;
if (b0 >= 192) {
int length;
@@ -403,24 +298,23 @@ private static Pair decodeElement(byte[] msgData, int posit
int endingIndex = safeAdd(length, position);
- if (endingIndex > msgData.length) {
+ if (endingIndex > msgData.length()) {
throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length");
}
- byte[] bytes = Arrays.copyOfRange(msgData, position, endingIndex);
- RLPList list = new RLPList(bytes, offset);
+ RLPList list = new RLPList(msgData.slice(position, endingIndex), offset);
return Pair.of(list, endingIndex);
}
if (b0 == EMPTY_MARK) {
- return Pair.of(new RLPItem(ByteUtil.EMPTY_BYTE_ARRAY), position + 1);
+ return Pair.of(new RLPItem(Bytes.of(ByteUtil.EMPTY_BYTE_ARRAY)), position + 1);
}
if (b0 < EMPTY_MARK) {
byte[] data = new byte[1];
- data[0] = msgData[position];
- return Pair.of(new RLPItem(data), position + 1);
+ data[0] = msgData.byteAt(position);
+ return Pair.of(new RLPItem(Bytes.of(data)), position + 1);
}
int length;
@@ -439,15 +333,13 @@ private static Pair decodeElement(byte[] msgData, int posit
}
int endingIndex = position + offset + length;
- if ( endingIndex < 0 || endingIndex > msgData.length) {
+ if (endingIndex < 0 || endingIndex > msgData.length()) {
throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length");
}
- byte[] decoded = new byte[length];
-
- System.arraycopy(msgData, position + offset, decoded, 0, length);
-
- return Pair.of(new RLPItem(decoded), position + offset + length);
+ int from = position + offset;
+ int to = from + length;
+ return Pair.of(new RLPItem(msgData.slice(from, to)), to);
}
private static int safeAdd(int a, int b) {
@@ -458,8 +350,8 @@ private static int safeAdd(int a, int b) {
}
}
- private static int bytesToLength(byte[] bytes, int position, int size) {
- if (position + size > bytes.length) {
+ private static int bytesToLength(BytesSlice bytes, int position, int size) {
+ if (position + size > bytes.length()) {
throw new RLPException("The length of the RLP item length can't possibly fit the data byte array");
}
@@ -467,7 +359,7 @@ private static int bytesToLength(byte[] bytes, int position, int size) {
for (int k = 0; k < size; k++) {
length <<= 8;
- length += bytes[position + k] & 0xff;
+ length += bytes.byteAt(position + k) & 0xff;
}
if (length < 0) {
diff --git a/rskj-core/src/main/java/org/ethereum/util/RLPItem.java b/rskj-core/src/main/java/org/ethereum/util/RLPItem.java
index 2ca8f7f81e3..49ce56965f1 100644
--- a/rskj-core/src/main/java/org/ethereum/util/RLPItem.java
+++ b/rskj-core/src/main/java/org/ethereum/util/RLPItem.java
@@ -19,29 +19,40 @@
package org.ethereum.util;
+import co.rsk.core.types.bytes.BytesSlice;
+
/**
* @author Roman Mandeleil
* @since 21.04.14
*/
public class RLPItem implements RLPElement {
- private final byte[] rlpData;
+ private final BytesSlice rlpData;
- public RLPItem(byte[] rlpData) {
+ public RLPItem(BytesSlice rlpData) {
this.rlpData = rlpData;
}
+ protected BytesSlice getRLPBytes() {
+ if (rlpData.length() == 0) {
+ return null;
+ }
+
+ return rlpData;
+ }
+
@Override
public byte[] getRLPData() {
- if (rlpData.length == 0) {
+ BytesSlice rlpBytes = getRLPBytes();
+ if (rlpBytes == null) {
return null;
}
- return rlpData;
+ return rlpBytes.copyArray();
}
@Override
public byte[] getRLPRawData() {
- return rlpData;
+ return rlpData.copyArray();
}
}
diff --git a/rskj-core/src/main/java/org/ethereum/util/RLPList.java b/rskj-core/src/main/java/org/ethereum/util/RLPList.java
index c4a5f035f53..e9ec39a2dc0 100644
--- a/rskj-core/src/main/java/org/ethereum/util/RLPList.java
+++ b/rskj-core/src/main/java/org/ethereum/util/RLPList.java
@@ -19,7 +19,8 @@
package org.ethereum.util;
-import java.util.Arrays;
+import co.rsk.core.types.bytes.BytesSlice;
+
import java.util.List;
/**
@@ -30,7 +31,7 @@ public class RLPList extends RLPItem implements RLPElement {
private List elements;
private final int offset;
- public RLPList(byte[] rlpData, int offset) {
+ public RLPList(BytesSlice rlpData, int offset) {
super(rlpData);
this.offset = offset;
}
@@ -52,8 +53,8 @@ private void checkElements() {
return;
}
- byte[] bytes = this.getRLPData();
- byte[] content = Arrays.copyOfRange(bytes, offset, bytes.length);
+ BytesSlice bytes = getRLPBytes();
+ BytesSlice content = bytes.slice(offset, bytes.length());
this.elements = RLP.decode2(content);
}
diff --git a/rskj-core/src/main/java/org/ethereum/util/Utils.java b/rskj-core/src/main/java/org/ethereum/util/Utils.java
index a2d23853a51..1236b0caeea 100644
--- a/rskj-core/src/main/java/org/ethereum/util/Utils.java
+++ b/rskj-core/src/main/java/org/ethereum/util/Utils.java
@@ -19,6 +19,7 @@
package org.ethereum.util;
+import co.rsk.core.types.bytes.BytesSlice;
import org.bouncycastle.util.encoders.DecoderException;
import org.bouncycastle.util.encoders.Hex;
import java.lang.reflect.Array;
@@ -206,15 +207,15 @@ public static boolean contains(List list, byte[] valueToFind) {
return false;
}
- public static void validateArrayAllegedSize(byte[] data, int offset, int allegedSize) {
- if (data.length < Math.addExact(allegedSize, offset)) {
+ public static void validateArrayAllegedSize(BytesSlice data, int offset, int allegedSize) {
+ if (data.length() < Math.addExact(allegedSize, offset)) {
throw new IllegalArgumentException("The specified size exceeds the size of the payload");
}
}
- public static byte[] safeCopyOfRange(byte[] data, int from, int size) {
+ public static byte[] safeCopyOfRange(BytesSlice data, int from, int size) {
validateArrayAllegedSize(data, from, size);
- return Arrays.copyOfRange(data, from, from + size);
+ return data.copyArrayOfRange(from, from + size);
}
public static boolean isDecimalString(String s) {
diff --git a/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java b/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java
index e3320bf3380..4fe0c0c870b 100644
--- a/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java
+++ b/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java
@@ -18,6 +18,7 @@
package co.rsk.core;
+import co.rsk.core.types.bytes.Bytes;
import org.bouncycastle.util.encoders.Hex;
import org.ethereum.core.CallTransaction;
import org.ethereum.solidity.SolidityType;
@@ -115,7 +116,7 @@ void decodeString() {
// string
104, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- Assertions.assertEquals("hello", type.decode(toDecode));
+ Assertions.assertEquals("hello", type.decode(Bytes.of(toDecode)));
}
@Test
diff --git a/rskj-core/src/test/java/co/rsk/core/TransactionTest.java b/rskj-core/src/test/java/co/rsk/core/TransactionTest.java
index 3985c64155d..6917aaf01e4 100644
--- a/rskj-core/src/test/java/co/rsk/core/TransactionTest.java
+++ b/rskj-core/src/test/java/co/rsk/core/TransactionTest.java
@@ -19,6 +19,7 @@
package co.rsk.core;
import co.rsk.config.TestSystemProperties;
+import co.rsk.core.types.bytes.Bytes;
import co.rsk.peg.BridgeSupportFactory;
import co.rsk.peg.RepositoryBtcBlockStoreWithCache;
import org.bouncycastle.util.BigIntegers;
@@ -283,7 +284,7 @@ protected ProgramResult executeTransaction(Transaction tx) {
track.rollback();
- System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(executor.getResult().getHReturn()));
+ System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(Bytes.of(executor.getResult().getHReturn())));
}
// now executing the JSON test transaction
diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java
index 93707953604..001aee51f35 100644
--- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java
+++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java
@@ -19,17 +19,56 @@
package co.rsk.core.types.bytes;
+import co.rsk.util.Functions;
import org.junit.jupiter.api.Test;
+import java.util.Arrays;
+
import static org.junit.jupiter.api.Assertions.*;
class BytesSliceTest {
+ @Test
+ void testBytesLength() {
+ assertEquals(0, Bytes.of(new byte[]{}).slice(0, 0).length());
+ assertEquals(0, Bytes.of(new byte[]{1}).slice(0, 0).length());
+ assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).length());
+ assertEquals(0, Bytes.of(new byte[]{1,2,3}).slice(1, 1).length());
+ assertEquals(1, Bytes.of(new byte[]{1,2,3}).slice(1, 2).length());
+ assertEquals(2, Bytes.of(new byte[]{1,2,3}).slice(0, 2).length());
+ assertEquals(3, Bytes.of(new byte[]{1,2,3}).slice(0, 3).length());
+ }
+
+ @Test
+ void testBytesAt() {
+ assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{}).slice(0, 0).byteAt(0));
+ assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(1));
+ assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(-1));
+ assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(1));
+ assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).byteAt(0));
+ assertEquals(2, Bytes.of(new byte[]{1,2}).slice(0, 2).byteAt(1));
+ assertEquals(2, Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(0));
+ assertEquals(4, Bytes.of(new byte[]{1,2,3,4}).slice(2, 4).byteAt(1));
+ }
+
+ @Test
+ void testBytesSliceArraycopy() {
+ checkArraycopy((src, srcPos, dest, destPos, length) -> Bytes.of((byte[]) src).slice(1, 4).arraycopy(srcPos, (byte[]) dest, destPos, length));
+ }
+
+ @Test
+ void testBytesSliceArraycopyMimicsSystemOne() {
+ checkArraycopy((src, srcPos, dest, destPos, length) -> System.arraycopy(Arrays.copyOfRange((byte[]) src, 1, 4), srcPos, dest, destPos, length));
+ }
+
@Test
void testCopyArrayOfRange() {
- byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6};
- byte[] expectedResult = new byte[]{3, 4, 5};
- assertArrayEquals(expectedResult, Bytes.of(bArray).slice(0, bArray.length).copyArrayOfRange(2, 5));
+ checkCopyOfRange(BytesSlice::copyArrayOfRange, (origin, from, to) -> Bytes.of(origin).slice(from, to));
+ }
+
+ @Test
+ void testCopyArrayOfRangeMimicsSystemOne() {
+ checkCopyOfRange(Arrays::copyOfRange, Arrays::copyOfRange);
}
@Test
@@ -74,4 +113,44 @@ void testEmptySlice() {
assertEquals(0, actualResult.length());
assertArrayEquals(expectedResult, actualResult.copyArray());
}
+
+ private static void checkArraycopy(Functions.Action5