Add additional RLP tests (#332)

mbaxter 6 years ago committed by GitHub
parent ab744a916c
commit 9f432ce7ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      ethereum/rlp/src/jmh/java/tech/pegasys/pantheon/ethereum/rlp/RLPBench.java
  2. 61
      ethereum/rlp/src/main/java/tech/pegasys/pantheon/ethereum/rlp/RLP.java
  3. 29
      ethereum/rlp/src/main/java/tech/pegasys/pantheon/ethereum/rlp/RLPDecodingHelpers.java
  4. 399
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/BytesValueRLPInputTest.java
  5. 65
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/BytesValueRLPOutputTest.java
  6. 3
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/InvalidRLPRefTest.java
  7. 5
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/RLPRefTest.java
  8. 118
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/RLPTest.java
  9. 162
      ethereum/rlp/src/test/java/tech/pegasys/pantheon/ethereum/rlp/util/RLPTestUtil.java

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.rlp;
import tech.pegasys.pantheon.ethereum.rlp.util.RLPTestUtil;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.ArrayList;
@ -62,16 +63,16 @@ public class RLPBench {
@Setup(Level.Trial)
public void prepare() {
toEncode = generate(depth, width, size);
toDecode = RLP.encode(toEncode);
toDecode = RLPTestUtil.encode(toEncode);
}
@Benchmark
public BytesValue getBenchmarkEncoding() {
return RLP.encode(toEncode);
return RLPTestUtil.encode(toEncode);
}
@Benchmark
public Object getBenchmarkDecoding() {
return RLP.decode(toDecode);
return RLPTestUtil.decode(toDecode);
}
}

@ -22,8 +22,6 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPDecodingHelpers.Kind;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.MutableBytesValue;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import io.vertx.core.buffer.Buffer;
@ -81,65 +79,6 @@ public abstract class RLP {
return new BytesValueRLPInput(BytesValue.wrapBuffer(buffer, offset), false, false);
}
/**
* Fully decodes a RLP encoded value.
*
* <p>This method is mostly intended for testing as it is often more convenient <b>and</b>
* efficient to use a {@link RLPInput} (through {@link #input(BytesValue)}) instead.
*
* @param value The RLP encoded value to decode.
* @return The output of decoding {@code value}. It will be either directly a {@link BytesValue},
* or a list whose elements are either {@link BytesValue}, or similarly composed sub-lists.
* @throws RLPException if {@code value} is not a properly formed RLP encoding.
*/
public static Object decode(final BytesValue value) {
return decode(input(value));
}
private static Object decode(final RLPInput in) {
if (!in.nextIsList()) {
return in.readBytesValue();
}
final int size = in.enterList();
final List<Object> l = new ArrayList<>(size);
for (int i = 0; i < size; i++) l.add(decode(in));
in.leaveList();
return l;
}
/**
* Fully RLP encode an object consisting of recursive lists of {@link BytesValue}.
*
* <p>This method is mostly intended for testing as it is often more convenient <b>and</b>
* efficient to use a {@link RLPOutput} (through {@link #encode(Consumer)} for instance) instead.
*
* @param obj An object that must be either directly a {@link BytesValue}, or a list whose
* elements are either {@link BytesValue}, or similarly composed sub-lists.
* @return The RLP encoding corresponding to {@code obj}.
* @throws IllegalArgumentException if {@code obj} is not a valid input (not entirely composed
* from lists and {@link BytesValue}).
*/
public static BytesValue encode(final Object obj) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
encode(obj, out);
return out.encoded();
}
private static void encode(final Object obj, final RLPOutput out) {
if (obj instanceof BytesValue) {
out.writeBytesValue((BytesValue) obj);
} else if (obj instanceof List) {
final List<?> l = (List<?>) obj;
out.startList();
for (final Object o : l) encode(o, out);
out.endList();
} else {
throw new IllegalArgumentException(
format("Invalid input type %s for RLP encoding", obj.getClass()));
}
}
/**
* Creates a {@link RLPOutput}, pass it to the provided consumer for writing, and then return the
* RLP encoded result of that writing.

@ -66,8 +66,19 @@ class RLPDecodingHelpers {
}
/** Read from the provided offset a size of the provided length, assuming this is enough bytes. */
static int extractSizeFromLong(
static int extractSizeFromLongItem(
final LongUnaryOperator getter, final long offset, final int sizeLength) {
String oversizedErrorMessage =
"RLP item at offset "
+ offset
+ " with size value consuming "
+ sizeLength
+ " bytes exceeds max supported size of "
+ Integer.MAX_VALUE;
if (sizeLength > 4) {
throw new RLPException(oversizedErrorMessage);
}
long res = 0;
int shift = 0;
for (int i = 0; i < sizeLength; i++) {
@ -77,9 +88,7 @@ class RLPDecodingHelpers {
try {
return Math.toIntExact(res);
} catch (final ArithmeticException e) {
final String msg =
"unable to extract size from long at offset " + offset + ", sizeLen=" + sizeLength;
throw new RLPException(msg, e);
throw new RLPException(oversizedErrorMessage, e);
}
}
@ -139,7 +148,7 @@ class RLPDecodingHelpers {
throw new MalformedRLPInputException("Malformed RLP item: size of payload has leading zeros");
}
final int res = RLPDecodingHelpers.extractSizeFromLong(byteGetter, item + 1, sizeLength);
final int res = RLPDecodingHelpers.extractSizeFromLongItem(byteGetter, item + 1, sizeLength);
// We should not have had the size written separately if it was less than 56 bytes long.
if (res < 56) {
@ -166,7 +175,15 @@ class RLPDecodingHelpers {
/** @return the size of the byte string holding the rlp-encoded value and metadata */
int getEncodedSize() {
return Math.toIntExact(elementEnd() - elementStart + 1);
long encodedSize = elementEnd() - elementStart + 1;
try {
return Math.toIntExact(encodedSize);
} catch (ArithmeticException e) {
String errorMessage =
String.format(
"RLP item exceeds max supported size of %d: %d", Integer.MAX_VALUE, encodedSize);
throw new RLPException(errorMessage, e);
}
}
/**

@ -12,13 +12,17 @@
*/
package tech.pegasys.pantheon.ethereum.rlp;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.Test;
public class BytesValueRLPInputTest {
@ -36,104 +40,104 @@ public class BytesValueRLPInputTest {
@Test
public void empty() {
final RLPInput in = RLP.input(BytesValue.EMPTY);
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void singleByte() {
final RLPInput in = RLP.input(h("0x01"));
assertFalse(in.isDone());
assertEquals((byte) 1, in.readByte());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readByte()).isEqualTo((byte) 1);
assertThat(in.isDone()).isTrue();
}
@Test
public void singleByteLowerBoundary() {
final RLPInput in = RLP.input(h("0x00"));
assertFalse(in.isDone());
assertEquals((byte) 0, in.readByte());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readByte()).isEqualTo((byte) 0);
assertThat(in.isDone()).isTrue();
}
@Test
public void singleByteUpperBoundary() {
final RLPInput in = RLP.input(h("0x7f"));
assertFalse(in.isDone());
assertEquals((byte) 0x7f, in.readByte());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readByte()).isEqualTo((byte) 0x7f);
assertThat(in.isDone()).isTrue();
}
@Test
public void singleShortElement() {
final RLPInput in = RLP.input(h("0x81FF"));
assertFalse(in.isDone());
assertEquals((byte) 0xFF, in.readByte());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readByte()).isEqualTo((byte) 0xFF);
assertThat(in.isDone()).isTrue();
}
@Test
public void singleBarelyShortElement() {
final RLPInput in = RLP.input(h("0xb7" + times("2b", 55)));
assertFalse(in.isDone());
assertEquals(h(times("2b", 55)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("2b", 55)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleBarelyLongElement() {
final RLPInput in = RLP.input(h("0xb838" + times("2b", 56)));
assertFalse(in.isDone());
assertEquals(h(times("2b", 56)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("2b", 56)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElement() {
final RLPInput in = RLP.input(h("0xb908c1" + times("3c", 2241)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 2241)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 2241)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElementBoundaryCase_1() {
final RLPInput in = RLP.input(h("0xb8ff" + times("3c", 255)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 255)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 255)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElementBoundaryCase_2() {
final RLPInput in = RLP.input(h("0xb90100" + times("3c", 256)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 256)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 256)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElementBoundaryCase_3() {
final RLPInput in = RLP.input(h("0xb9ffff" + times("3c", 65535)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 65535)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 65535)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElementBoundaryCase_4() {
final RLPInput in = RLP.input(h("0xba010000" + times("3c", 65536)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 65536)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 65536)));
assertThat(in.isDone()).isTrue();
}
@Test
public void singleLongElementBoundaryCase_5() {
final RLPInput in = RLP.input(h("0xbaffffff" + times("3c", 16777215)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 16777215)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 16777215)));
assertThat(in.isDone()).isTrue();
}
@Test
@ -141,9 +145,9 @@ public class BytesValueRLPInputTest {
// A RLPx Frame can have a maximum length of 0xffffff, so boundary above this
// will be not be real world scenarios.
final RLPInput in = RLP.input(h("0xbb01000000" + times("3c", 16777216)));
assertFalse(in.isDone());
assertEquals(h(times("3c", 16777216)), in.readBytesValue());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(h(times("3c", 16777216)));
assertThat(in.isDone()).isTrue();
}
@Test
@ -168,9 +172,9 @@ public class BytesValueRLPInputTest {
private void assertLongScalar(final long expected, final BytesValue toTest) {
final RLPInput in = RLP.input(toTest);
assertFalse(in.isDone());
assertEquals(expected, in.readLongScalar());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readLongScalar()).isEqualTo(expected);
assertThat(in.isDone()).isTrue();
}
@Test
@ -186,128 +190,136 @@ public class BytesValueRLPInputTest {
private void assertIntScalar(final int expected, final BytesValue toTest) {
final RLPInput in = RLP.input(toTest);
assertFalse(in.isDone());
assertEquals(expected, in.readIntScalar());
assertTrue(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.readIntScalar()).isEqualTo(expected);
assertThat(in.isDone()).isTrue();
}
@Test
public void emptyList() {
final RLPInput in = RLP.input(h("0xc0"));
assertFalse(in.isDone());
assertEquals(0, in.enterList());
assertFalse(in.isDone());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(0);
assertThat(in.isDone()).isFalse();
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void emptyByteString() {
final RLPInput in = RLP.input(h("0x80"));
assertThat(in.isDone()).isFalse();
assertThat(in.readBytesValue()).isEqualTo(BytesValue.EMPTY);
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleShortList() {
final RLPInput in = RLP.input(h("0xc22c3b"));
assertFalse(in.isDone());
assertEquals(2, in.enterList());
assertEquals((byte) 0x2c, in.readByte());
assertEquals((byte) 0x3b, in.readByte());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(2);
assertThat(in.readByte()).isEqualTo((byte) 0x2c);
assertThat(in.readByte()).isEqualTo((byte) 0x3b);
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleIntBeforeShortList() {
final RLPInput in = RLP.input(h("0x02c22c3b"));
assertFalse(in.isDone());
assertEquals(2, in.readIntScalar());
assertEquals(2, in.enterList());
assertEquals((byte) 0x2c, in.readByte());
assertEquals((byte) 0x3b, in.readByte());
assertThat(in.isDone()).isFalse();
assertThat(in.readIntScalar()).isEqualTo(2);
assertThat(in.enterList()).isEqualTo(2);
assertThat(in.readByte()).isEqualTo((byte) 0x2c);
assertThat(in.readByte()).isEqualTo((byte) 0x3b);
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleShortListUpperBoundary() {
final RLPInput in = RLP.input(h("0xf7" + times("3c", 55)));
assertFalse(in.isDone());
assertEquals(55, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(55);
for (int i = 0; i < 55; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListLowerBoundary() {
final RLPInput in = RLP.input(h("0xf838" + times("3c", 56)));
assertFalse(in.isDone());
assertEquals(56, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(56);
for (int i = 0; i < 56; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListBoundaryCase_1() {
final RLPInput in = RLP.input(h("0xf8ff" + times("3c", 255)));
assertFalse(in.isDone());
assertEquals(255, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(255);
for (int i = 0; i < 255; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListBoundaryCase_2() {
final RLPInput in = RLP.input(h("0xf90100" + times("3c", 256)));
assertFalse(in.isDone());
assertEquals(256, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(256);
for (int i = 0; i < 256; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListBoundaryCase_3() {
final RLPInput in = RLP.input(h("0xf9ffff" + times("3c", 65535)));
assertFalse(in.isDone());
assertEquals(65535, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(65535);
for (int i = 0; i < 65535; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListBoundaryCase_4() {
final RLPInput in = RLP.input(h("0xfa010000" + times("3c", 65536)));
assertFalse(in.isDone());
assertEquals(65536, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(65536);
for (int i = 0; i < 65536; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleLongListBoundaryCase_5() {
final RLPInput in = RLP.input(h("0xfaffffff" + times("3c", 16777215)));
assertFalse(in.isDone());
assertEquals(16777215, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(16777215);
for (int i = 0; i < 16777215; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
@ -315,39 +327,39 @@ public class BytesValueRLPInputTest {
// A RLPx Frame can have a maximum length of 0xffffff, so boundary above this
// will be not be real world scenarios.
final RLPInput in = RLP.input(h("0xfb01000000" + times("3c", 16777216)));
assertFalse(in.isDone());
assertEquals(16777216, in.enterList());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(16777216);
for (int i = 0; i < 16777216; i++) {
assertEquals((byte) 0x3c, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3c);
}
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleListwithBytesValue() {
final RLPInput in = RLP.input(h("0xc28180"));
assertFalse(in.isDone());
assertEquals(1, in.enterList());
assertEquals(h("0x80"), in.readBytesValue());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(1);
assertThat(in.readBytesValue()).isEqualTo(h("0x80"));
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
public void simpleNestedList() {
final RLPInput in = RLP.input(h("0xc52cc203123b"));
assertFalse(in.isDone());
assertEquals(3, in.enterList());
assertEquals((byte) 0x2c, in.readByte());
assertEquals(2, in.enterList());
assertEquals((byte) 0x03, in.readByte());
assertEquals((byte) 0x12, in.readByte());
assertThat(in.isDone()).isFalse();
assertThat(in.enterList()).isEqualTo(3);
assertThat(in.readByte()).isEqualTo((byte) 0x2c);
assertThat(in.enterList()).isEqualTo(2);
assertThat(in.readByte()).isEqualTo((byte) 0x03);
assertThat(in.readByte()).isEqualTo((byte) 0x12);
in.leaveList();
assertEquals((byte) 0x3b, in.readByte());
assertThat(in.readByte()).isEqualTo((byte) 0x3b);
in.leaveList();
assertTrue(in.isDone());
assertThat(in.isDone()).isTrue();
}
@Test
@ -355,19 +367,19 @@ public class BytesValueRLPInputTest {
// Test null value
final BytesValue nullValue = h("0x80");
final RLPInput nv = RLP.input(nullValue);
assertEquals(nv.raw(), nv.readAsRlp().raw());
assertThat(nv.readAsRlp().raw()).isEqualTo(nv.raw());
nv.reset();
assertTrue(nv.nextIsNull());
assertTrue(nv.readAsRlp().nextIsNull());
assertThat(nv.nextIsNull()).isTrue();
assertThat(nv.readAsRlp().nextIsNull()).isTrue();
// Test empty list
final BytesValue emptyList = h("0xc0");
final RLPInput el = RLP.input(emptyList);
assertEquals(emptyList, el.readAsRlp().raw());
assertThat(el.readAsRlp().raw()).isEqualTo(emptyList);
el.reset();
assertEquals(0, el.readAsRlp().enterList());
assertThat(el.readAsRlp().enterList()).isEqualTo(0);
el.reset();
assertEquals(0, el.enterList());
assertThat(el.enterList()).isEqualTo(0);
final BytesValue nestedList =
RLP.encode(
@ -388,16 +400,16 @@ public class BytesValueRLPInputTest {
final RLPInput nl = RLP.input(nestedList);
final RLPInput compare = nl.readAsRlp();
assertEquals(nl.raw(), compare.raw());
assertThat(compare.raw()).isEqualTo(nl.raw());
nl.reset();
nl.enterList();
nl.skipNext(); // 0x01
// Read the next byte that's inside the list, extract it as raw RLP and assert it's its own
// representation.
assertEquals(h("0x02"), nl.readAsRlp().raw());
assertThat(nl.readAsRlp().raw()).isEqualTo(h("0x02"));
// Extract the inner list.
assertEquals(h("0xc51112c22122"), nl.readAsRlp().raw());
assertThat(nl.readAsRlp().raw()).isEqualTo(h("0xc51112c22122"));
// Reset
nl.reset();
nl.enterList();
@ -408,29 +420,29 @@ public class BytesValueRLPInputTest {
nl.skipNext();
// Assert on the inner list of depth 3.
assertEquals(h("0xc22122"), nl.readAsRlp().raw());
assertThat(nl.readAsRlp().raw()).isEqualTo(h("0xc22122"));
}
@Test
public void raw() {
final BytesValue initial = h("0xc80102c51112c22122");
final RLPInput in = RLP.input(initial);
assertEquals(initial, in.raw());
assertThat(in.raw()).isEqualTo(initial);
}
@Test
public void reset() {
final RLPInput in = RLP.input(h("0xc80102c51112c22122"));
for (int i = 0; i < 100; i++) {
assertEquals(3, in.enterList());
assertEquals(0x01, in.readByte());
assertEquals(0x02, in.readByte());
assertEquals(3, in.enterList());
assertEquals(0x11, in.readByte());
assertEquals(0x12, in.readByte());
assertEquals(2, in.enterList());
assertEquals(0x21, in.readByte());
assertEquals(0x22, in.readByte());
assertThat(in.enterList()).isEqualTo(3);
assertThat(in.readByte()).isEqualTo((byte) 0x01);
assertThat(in.readByte()).isEqualTo((byte) 0x02);
assertThat(in.enterList()).isEqualTo(3);
assertThat(in.readByte()).isEqualTo((byte) 0x11);
assertThat(in.readByte()).isEqualTo((byte) 0x12);
assertThat(in.enterList()).isEqualTo(2);
assertThat(in.readByte()).isEqualTo((byte) 0x21);
assertThat(in.readByte()).isEqualTo((byte) 0x22);
in.reset();
}
}
@ -438,16 +450,16 @@ public class BytesValueRLPInputTest {
@Test
public void ignoreListTail() {
final RLPInput in = RLP.input(h("0xc80102c51112c22122"));
assertEquals(3, in.enterList());
assertEquals(0x01, in.readByte());
assertThat(in.enterList()).isEqualTo(3);
assertThat(in.readByte()).isEqualTo((byte) 0x01);
in.leaveList(true);
}
@Test
public void leaveListEarly() {
final RLPInput in = RLP.input(h("0xc80102c51112c22122"));
assertEquals(3, in.enterList());
assertEquals(0x01, in.readByte());
assertThat(in.enterList()).isEqualTo(3);
assertThat(in.readByte()).isEqualTo((byte) 0x01);
assertThatThrownBy(() -> in.leaveList(false))
.isInstanceOf(RLPException.class)
.hasMessageStartingWith("Not at the end of the current list");
@ -472,10 +484,10 @@ public class BytesValueRLPInputTest {
final BytesValue correctBytes =
h(
"0xB8380102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738");
assertEquals(
RLP.input(correctBytes).readBytesValue(),
assertThat(
h(
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738"));
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738"))
.isEqualTo(RLP.input(correctBytes).readBytesValue());
// Encode same value, but use 2 bytes to represent the size, and pad size value with leading
// zeroes
@ -494,10 +506,10 @@ public class BytesValueRLPInputTest {
final BytesValue correctBytes =
h(
"0xB70102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637");
assertEquals(
RLP.input(correctBytes).readBytesValue(),
assertThat(
h(
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637"));
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637"))
.isEqualTo(RLP.input(correctBytes).readBytesValue());
// Encode same value using long format
final BytesValue incorrectBytes =
@ -508,4 +520,115 @@ public class BytesValueRLPInputTest {
.hasRootCauseInstanceOf(MalformedRLPInputException.class)
.hasMessageContaining("written as a long item, but size 55 < 56 bytes");
}
@Test
public void rlpItemSizeHoldsMaxValue() {
// Size value encode max positive int. So, size is decoded, but
// RLP is malformed because the actual payload is not present
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xBB7FFFFFFF")).readBytesValue())
.isInstanceOf(CorruptedRLPInputException.class)
.hasMessageContaining("payload should start at offset 5 but input has only 5 bytes");
}
@Test
public void rlpItemSizeOverflowsSignedInt() {
// Size value encoded in 4 bytes but exceeds max positive int value
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xBB80000000")))
.isInstanceOf(RLPException.class)
.hasMessageContaining(
"RLP item at offset 1 with size value consuming 4 bytes exceeds max supported size of 2147483647");
}
@Test
public void rlpItemSizeOverflowsInt() {
// Size value is encoded with 5 bytes - overflowing int
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xBC0100000000")))
.isInstanceOf(RLPException.class)
.hasMessageContaining(
"RLP item at offset 1 with size value consuming 5 bytes exceeds max supported size of 2147483647");
}
@Test
public void rlpListSizeHoldsMaxValue() {
// Size value encode max positive int. So, size is decoded, but
// RLP is malformed because the actual payload is not present
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xFB7FFFFFFF")).readBytesValue())
.isInstanceOf(CorruptedRLPInputException.class)
.hasMessageContaining(
"Input doesn't have enough data for RLP encoding: encoding advertise a payload ending at byte 2147483652 but input has size 5");
}
@Test
public void rlpListSizeOverflowsSignedInt() {
// Size value encoded in 4 bytes but exceeds max positive int value
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xFB80000000")))
.isInstanceOf(RLPException.class)
.hasMessageContaining(
"RLP item at offset 1 with size value consuming 4 bytes exceeds max supported size of 2147483647");
}
@Test
public void rlpListSizeOverflowsInt() {
// Size value is encoded with 5 bytes - overflowing int
AssertionsForClassTypes.assertThatThrownBy(() -> RLP.input(h("0xFC0100000000")))
.isInstanceOf(RLPException.class)
.hasMessageContaining(
"RLP item at offset 1 with size value consuming 5 bytes exceeds max supported size of 2147483647");
}
@SuppressWarnings("ReturnValueIgnored")
@Test
public void decodeValueWithLeadingZerosAsScalar() {
String value = "0x8200D0";
List<Function<RLPInput, Object>> invalidDecoders =
Arrays.asList(
RLPInput::readBigIntegerScalar,
RLPInput::readIntScalar,
RLPInput::readLongScalar,
RLPInput::readUInt256Scalar);
for (Function<RLPInput, Object> decoder : invalidDecoders) {
RLPInput in = RLP.input(h(value));
AssertionsForClassTypes.assertThatThrownBy(() -> decoder.apply(in))
.isInstanceOf(MalformedRLPInputException.class)
.hasMessageContaining("Invalid scalar");
}
}
@Test
public void decodeValueWithLeadingZerosAsUnsignedInt() {
RLPInput in = RLP.input(h("0x84000000D0"));
assertThat(in.readUnsignedInt()).isEqualTo(208);
}
@Test
public void decodeValueWithLeadingZerosAsUnsignedShort() {
RLPInput in = RLP.input(h("0x8200D0"));
assertThat(in.readUnsignedShort()).isEqualTo(208);
}
@Test
public void decodeValueWithLeadingZerosAsSignedInt() {
RLPInput in = RLP.input(h("0x84000000D0"));
assertThat(in.readInt()).isEqualTo(208);
}
@Test
public void decodeValueWithLeadingZerosAsSignedLong() {
RLPInput in = RLP.input(h("0x8800000000000000D0"));
assertThat(in.readLong()).isEqualTo(208);
}
@Test
public void decodeValueWithLeadingZerosAsSignedShort() {
RLPInput in = RLP.input(h("0x8200D0"));
assertThat(in.readShort()).isEqualTo((short) 208);
}
@Test
public void decodeValueWithLeadingZerosAsBytesValue() {
RLPInput in = RLP.input(h("0x8800000000000000D0"));
assertThat(BytesValues.extractLong(in.readBytesValue())).isEqualTo(208);
}
}

@ -12,7 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.rlp;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -33,7 +33,18 @@ public class BytesValueRLPOutputTest {
@Test
public void empty() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
assertEquals(BytesValue.EMPTY, out.encoded());
assertThat(out.encoded()).isEqualTo(BytesValue.EMPTY);
assertThat(out.encoded().toString()).isEqualTo("0x");
}
@Test
public void emptyBytesString() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(BytesValue.EMPTY);
assertThat(out.encoded()).isEqualTo(RLP.NULL);
assertThat(out.encoded().toString()).isEqualTo("0x80");
}
@Test
@ -42,21 +53,21 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 1);
// Single byte should be encoded as itself
assertEquals(h("0x01"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0x01"));
}
@Test
public void singleByteLowerBoundary() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeByte((byte) 0);
assertEquals(h("0x00"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0x00"));
}
@Test
public void singleByteUpperBoundary() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeByte((byte) 0x7f);
assertEquals(h("0x7f"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0x7f"));
}
@Test
@ -65,7 +76,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0xFF);
// Bigger than single byte: 0x80 + length then value, where length is 1.
assertEquals(h("0x81FF"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0x81FF"));
}
@Test
@ -74,7 +85,7 @@ public class BytesValueRLPOutputTest {
out.writeBytesValue(h(times("2b", 55)));
// 55 bytes, so still short: 0x80 + length then value, where length is 55.
assertEquals(h("0xb7" + times("2b", 55)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb7" + times("2b", 55)));
}
@Test
@ -84,7 +95,7 @@ public class BytesValueRLPOutputTest {
// 56 bytes, so long element: 0xb7 + length of value size + value, where the value size is 56.
// 56 is 0x38 so its size is 1 byte.
assertEquals(h("0xb838" + times("2b", 56)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb838" + times("2b", 56)));
}
@Test
@ -95,49 +106,49 @@ public class BytesValueRLPOutputTest {
// 2241 bytes, so long element: 0xb7 + length of value size + value, where the value size is
// 2241,
// 2241 is 0x8c1 so its size is 2 bytes.
assertEquals(h("0xb908c1" + times("3c", 2241)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb908c1" + times("3c", 2241)));
}
@Test
public void singleLongElementBoundaryCase_1() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 255)));
assertEquals(h("0xb8ff" + times("3c", 255)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb8ff" + times("3c", 255)));
}
@Test
public void singleLongElementBoundaryCase_2() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 256)));
assertEquals(h("0xb90100" + times("3c", 256)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb90100" + times("3c", 256)));
}
@Test
public void singleLongElementBoundaryCase_3() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 65535)));
assertEquals(h("0xb9ffff" + times("3c", 65535)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xb9ffff" + times("3c", 65535)));
}
@Test
public void singleLongElementBoundaryCase_4() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 65536)));
assertEquals(h("0xba010000" + times("3c", 65536)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xba010000" + times("3c", 65536)));
}
@Test
public void singleLongElementBoundaryCase_5() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 16777215)));
assertEquals(h("0xbaffffff" + times("3c", 16777215)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xbaffffff" + times("3c", 16777215)));
}
@Test
public void singleLongElementBoundaryCase_6() {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeBytesValue(h(times("3c", 16777216)));
assertEquals(h("0xbb01000000" + times("3c", 16777216)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xbb01000000" + times("3c", 16777216)));
}
@Test(expected = IllegalStateException.class)
@ -161,7 +172,7 @@ public class BytesValueRLPOutputTest {
private void assertLongScalar(final BytesValue expected, final long toTest) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeLongScalar(toTest);
assertEquals(expected, out.encoded());
assertThat(out.encoded()).isEqualTo(expected);
}
@Test
@ -170,7 +181,7 @@ public class BytesValueRLPOutputTest {
out.startList();
out.endList();
assertEquals(h("0xc0"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xc0"));
}
@Test(expected = IllegalStateException.class)
@ -196,7 +207,7 @@ public class BytesValueRLPOutputTest {
// List with payload size = 2 (both element are single bytes)
// so 0xc0 + size then payloads
assertEquals(h("0xc22c3b"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xc22c3b"));
}
@Test
@ -207,7 +218,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xf7" + times("3c", 55)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xf7" + times("3c", 55)));
}
@Test
@ -218,7 +229,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xf838" + times("3c", 56)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xf838" + times("3c", 56)));
}
@Test
@ -229,7 +240,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xf8ff" + times("3c", 255)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xf8ff" + times("3c", 255)));
}
@Test
@ -240,7 +251,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xf90100" + times("3c", 256)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xf90100" + times("3c", 256)));
}
@Test
@ -251,7 +262,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xf9ffff" + times("3c", 65535)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xf9ffff" + times("3c", 65535)));
}
@Test
@ -262,7 +273,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xfa010000" + times("3c", 65536)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xfa010000" + times("3c", 65536)));
}
@Test
@ -273,7 +284,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xfaffffff" + times("3c", 16777215)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xfaffffff" + times("3c", 16777215)));
}
@Test
@ -284,7 +295,7 @@ public class BytesValueRLPOutputTest {
out.writeByte((byte) 0x3c);
}
out.endList();
assertEquals(h("0xfb01000000" + times("3c", 16777216)), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xfb01000000" + times("3c", 16777216)));
}
@Test
@ -302,6 +313,6 @@ public class BytesValueRLPOutputTest {
// List payload size = 5 (2 single bytes element + nested list of size 3)
// so 0xc0 + size then payloads
assertEquals(h("0xc52cc203123b"), out.encoded());
assertThat(out.encoded()).isEqualTo(h("0xc52cc203123b"));
}
}

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.ethereum.rlp;
import static org.junit.Assume.assumeTrue;
import tech.pegasys.pantheon.ethereum.rlp.util.RLPTestUtil;
import tech.pegasys.pantheon.testutil.JsonTestParameters;
import java.util.Collection;
@ -48,6 +49,6 @@ public class InvalidRLPRefTest {
/** Test RLP decoding. */
@Test(expected = RLPException.class)
public void decode() throws Exception {
RLP.decode(spec.getRLP());
RLPTestUtil.decode(spec.getRLP());
}
}

@ -15,6 +15,7 @@ package tech.pegasys.pantheon.ethereum.rlp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import tech.pegasys.pantheon.ethereum.rlp.util.RLPTestUtil;
import tech.pegasys.pantheon.testutil.JsonTestParameters;
import java.util.Collection;
@ -44,11 +45,11 @@ public class RLPRefTest {
@Test
public void encode() {
assertEquals(spec.getOut(), RLP.encode(spec.getIn()));
assertEquals(spec.getOut(), RLPTestUtil.encode(spec.getIn()));
}
@Test
public void decode() {
assertEquals(spec.getIn(), RLP.decode(spec.getOut()));
assertEquals(spec.getIn(), RLPTestUtil.decode(spec.getOut()));
}
}

@ -12,10 +12,14 @@
*/
package tech.pegasys.pantheon.ethereum.rlp;
import static junit.framework.TestCase.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import tech.pegasys.pantheon.ethereum.rlp.util.RLPTestUtil;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Random;
import org.junit.Test;
public class RLPTest {
@ -23,48 +27,146 @@ public class RLPTest {
@Test
public void calculateSize_singleByteValue() {
int size = RLP.calculateSize(BytesValue.fromHexString("0x01"));
assertEquals(1, size);
assertThat(size).isEqualTo(1);
}
@Test
public void calculateSize_minSingleByteValue() {
int size = RLP.calculateSize(BytesValue.fromHexString("0x00"));
assertThat(size).isEqualTo(1);
}
@Test
public void calculateSize_maxSingleByteValue() {
int size = RLP.calculateSize(BytesValue.fromHexString("0x7F"));
assertThat(size).isEqualTo(1);
}
@Test
public void calculateSize_smallByteString() {
// Prefix indicates a payload of size 5, with a 1 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0x85"));
assertEquals(6, size);
assertThat(size).isEqualTo(6);
}
@Test
public void calculateSize_nullByteString() {
// Prefix indicates a payload of size 0, with a 1 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0x80"));
assertThat(size).isEqualTo(1);
}
@Test
public void calculateSize_minNonNullSmallByteString() {
// Prefix indicates a payload of size 1, with a 1 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0x81"));
assertThat(size).isEqualTo(2);
}
@Test
public void calculateSize_maxSmallByteString() {
// Prefix indicates a payload of size 55, with a 1 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xB7"));
assertThat(size).isEqualTo(56);
}
@Test
public void calculateSize_longByteString() {
// Prefix indicates a payload of 56 bytes, with a 2 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xB838"));
assertEquals(58, size);
assertThat(size).isEqualTo(58);
}
@Test
public void calculateSize_longByteStringWithMultiByteSize() {
// Prefix indicates a payload of 258 bytes, with a 3 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xB90102"));
assertEquals(261, size);
assertThat(size).isEqualTo(261);
}
@Test
public void calculateSize_shortList() {
// Prefix indicates a payload of 5 bytes, with a 1 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xC5"));
assertEquals(6, size);
assertThat(size).isEqualTo(6);
}
@Test
public void calculateSize_emptyList() {
int size = RLP.calculateSize(BytesValue.fromHexString("0xC0"));
assertThat(size).isEqualTo(1);
}
@Test
public void calculateSize_minNonEmptyList() {
int size = RLP.calculateSize(BytesValue.fromHexString("0xC1"));
assertThat(size).isEqualTo(2);
}
@Test
public void calculateSize_maxShortList() {
int size = RLP.calculateSize(BytesValue.fromHexString("0xF7"));
assertThat(size).isEqualTo(56);
}
@Test
public void calculateSize_longList() {
// Prefix indicates a payload of 56 bytes, with a 2 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xF838"));
assertEquals(58, size);
assertThat(size).isEqualTo(58);
}
@Test
public void calculateSize_longListWithMultiByteSize() {
// Prefix indicates a payload of 258 bytes, with a 3 byte prefix
int size = RLP.calculateSize(BytesValue.fromHexString("0xF90102"));
assertEquals(261, size);
assertThat(size).isEqualTo(261);
}
@Test
public void calculateSize_fuzz() {
final Random random = new Random(1);
for (int i = 0; i < 1000; ++i) {
BytesValueRLPOutput out = RLPTestUtil.randomRLPValue(random.nextInt());
assertThat(RLP.calculateSize(out.encoded())).isEqualTo(out.encodedSize());
}
}
@Test
public void calculateSize_extremelyDeepNestedList() {
final int MAX_DEPTH = 20000;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
int depth = 0;
for (int i = 0; i < MAX_DEPTH; ++i) {
out.startList();
depth += 1;
}
while (depth > 0) {
out.endList();
--depth;
}
assertThat(RLP.calculateSize(out.encoded())).isEqualTo(out.encodedSize());
}
@Test
public void calculateSize_maxRLPStringLength() {
// Value represents a single item with an encoded payload size of MAX_VALUE - 5 and
// 5 bytes of metadata (payload is not actually present)
assertThat(RLP.calculateSize(h("0xBB7FFFFFFA"))).isEqualTo(Integer.MAX_VALUE);
}
@Test
public void calculateSize_overflowMaxRLPStringLength() {
// Value represents a single item with an encoded payload size of MAX_VALUE - 4 and
// 5 bytes of metadata (payload is not actually present)
assertThatThrownBy(() -> RLP.calculateSize(h("0xBB7FFFFFFB")))
.isInstanceOf(RLPException.class)
.hasMessageContaining("RLP item exceeds max supported size of 2147483647: 2147483648");
}
private static BytesValue h(final String hex) {
return BytesValue.fromHexString(hex);
}
}

@ -0,0 +1,162 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.rlp.util;
import static java.lang.String.format;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPException;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class RLPTestUtil {
/**
* Recursively decodes an RLP encoded value. Byte strings are assumed to be non-scalar (leading
* zeros are allowed).
*
* @param value The RLP encoded value to decode.
* @return The output of decoding {@code value}. It will be either directly a {@link BytesValue},
* or a list whose elements are either {@link BytesValue}, or similarly composed sub-lists.
* @throws RLPException if {@code value} is not a properly formed RLP encoding.
*/
public static Object decode(final BytesValue value) {
return decode(RLP.input(value));
}
private static Object decode(final RLPInput in) {
if (!in.nextIsList()) {
return in.readBytesValue();
}
final int size = in.enterList();
final List<Object> l = new ArrayList<>(size);
for (int i = 0; i < size; i++) l.add(decode(in));
in.leaveList();
return l;
}
/**
* Recursively RLP encode an object consisting of recursive lists of {@link BytesValue}.
* BytesValues are assumed to be non-scalar (leading zeros are not trimmed).
*
* @param obj An object that must be either directly a {@link BytesValue}, or a list whose
* elements are either {@link BytesValue}, or similarly composed sub-lists.
* @return The RLP encoding corresponding to {@code obj}.
* @throws IllegalArgumentException if {@code obj} is not a valid input (not entirely composed
* from lists and {@link BytesValue}).
*/
public static BytesValue encode(final Object obj) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
encode(obj, out);
return out.encoded();
}
private static void encode(final Object obj, final RLPOutput out) {
if (obj instanceof BytesValue) {
out.writeBytesValue((BytesValue) obj);
} else if (obj instanceof List) {
final List<?> l = (List<?>) obj;
out.startList();
for (final Object o : l) encode(o, out);
out.endList();
} else {
throw new IllegalArgumentException(
format("Invalid input type %s for RLP encoding", obj.getClass()));
}
}
/**
* Generate a random rlp-encoded value.
*
* @param randomSeed Seed to use for random generation.
* @return a random rlp-encoded value
*/
public static BytesValueRLPOutput randomRLPValue(final int randomSeed) {
final Random random = new Random(randomSeed);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
final AtomicInteger listDepth = new AtomicInteger(0);
int iterations = 0;
do {
if (iterations > 1000) {
out.endList();
listDepth.decrementAndGet();
continue;
}
iterations += 1;
writeRandomRLPData(out, random, listDepth);
} while (listDepth.get() > 0);
return out;
}
private static void writeRandomRLPData(
final RLPOutput out, final Random random, final AtomicInteger listDepth) {
switch (random.nextInt(12)) {
case 0:
// Write empty byte string
out.writeBytesValue(BytesValue.EMPTY);
break;
case 1:
// Small single byte
out.writeByte((byte) random.nextInt(128));
break;
case 2:
// Large single byte
byte value = (byte) (random.nextInt(128) + 128);
out.writeByte(value);
break;
case 3:
// Small byte string
int smallBytesSize = random.nextInt(54) + 2;
out.writeBytesValue(randomBytesValue(random, smallBytesSize));
break;
case 4:
// Large byte string
int largeBytesSize = random.nextInt(500) + 56;
out.writeBytesValue(randomBytesValue(random, largeBytesSize));
break;
case 5:
// Close list
if (listDepth.get() == 0) {
// If we're outside of a list try again
writeRandomRLPData(out, random, listDepth);
return;
}
out.endList();
listDepth.decrementAndGet();
break;
default:
// Start list
out.startList();
listDepth.incrementAndGet();
break;
}
}
private static BytesValue randomBytesValue(final Random random, final int size) {
final byte[] bytes = new byte[size];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) random.nextInt(256);
}
return BytesValue.wrap(bytes);
}
}
Loading…
Cancel
Save