blob: 108803e6867c3d20f4e04dcc43e0645d9a0a1cbd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You 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 org.apache.tuweni.ssz;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.crypto.Hash;
import org.apache.tuweni.units.bigints.UInt256;
import org.apache.tuweni.units.bigints.UInt384;
import java.math.BigInteger;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Simple Serialize (SSZ) encoding and decoding.
*/
public final class SSZ {
private static final Bytes TRUE = Bytes.of((byte) 1);
private static final Bytes FALSE = Bytes.of((byte) 0);
private SSZ() {}
/**
* Create the hash tree root of a set of values
*
* @param bytes 1 value or a list of homogeneous values
* @return the SSZ tree root hash of the values
*/
public static Bytes32 hashTreeRoot(Bytes... bytes) {
if (bytes.length == 1) {
if (bytes[0].size() > 32) {
return Hash.keccak256(bytes[0]);
} else {
return Bytes32.rightPad(bytes[0]);
}
} else {
Bytes hash = merkleHash(new ArrayList<>(Arrays.asList(bytes)));
return Bytes32.rightPad(hash);
}
}
/**
* Hashes a list of homogeneous values.
*
* @param values a list of homogeneous values
*
* @return the merkle hash of the list of values
*/
static Bytes merkleHash(List<Bytes> values) {
Bytes littleEndianLength = Bytes.ofUnsignedInt(values.size(), LITTLE_ENDIAN);
Bytes32 valuesLength = Bytes32.rightPad(littleEndianLength);
List<Bytes> chunks;
if (values.isEmpty()) {
chunks = new ArrayList<>();
chunks.add(Bytes.wrap(new byte[128]));
} else if (values.get(0).size() < 128) {
int itemsPerChunk = (int) Math.floor(128 / (double) values.get(0).size());
chunks = new ArrayList<>();
for (int i = 0; i * itemsPerChunk < values.size(); i++) {
Bytes[] chunkItems =
values.subList(i * itemsPerChunk, Math.min((i + 1) * itemsPerChunk, values.size())).toArray(new Bytes[0]);
chunks.add(Bytes.concatenate(chunkItems));
}
} else {
chunks = values;
}
while (chunks.size() > 1) {
if (chunks.size() % 2 == 1) {
chunks.add(Bytes.wrap(new byte[128]));
}
Iterator<Bytes> iterator = chunks.iterator();
List<Bytes> hashRound = new ArrayList<>();
while (iterator.hasNext()) {
hashRound.add(Hash.keccak256(Bytes.concatenate(iterator.next(), iterator.next())));
}
chunks = hashRound;
}
return Hash.keccak256(Bytes.concatenate(chunks.get(0), valuesLength));
}
// Encoding
/**
* Encode values to a {@link Bytes} value.
*
* @param fn A consumer that will be provided with a {@link SSZWriter} that can consume values.
* @return The SSZ encoding in a {@link Bytes} value.
*/
public static Bytes encode(Consumer<SSZWriter> fn) {
requireNonNull(fn);
BytesSSZWriter writer = new BytesSSZWriter();
fn.accept(writer);
return writer.toBytes();
}
/**
* Encode values to a {@link ByteBuffer}.
*
* @param buffer The buffer to write into, starting from its current position.
* @param fn A consumer that will be provided with a {@link SSZWriter} that can consume values.
* @param <T> The type of the buffer.
* @return The buffer.
* @throws BufferOverflowException if the writer attempts to write more than the provided buffer can hold
* @throws ReadOnlyBufferException if the provided buffer is read-only
*/
public static <T extends ByteBuffer> T encodeTo(T buffer, Consumer<SSZWriter> fn) {
requireNonNull(buffer);
requireNonNull(fn);
ByteBufferSSZWriter writer = new ByteBufferSSZWriter(buffer);
fn.accept(writer);
return buffer;
}
/**
* Encode {@link Bytes}.
*
* @param value The value to encode.
* @return The SSZ encoding in a {@link Bytes} value.
*/
public static Bytes encodeBytes(Bytes value) {
Bytes lengthBytes = encodeLong(value.size(), 32);
return Bytes.wrap(lengthBytes, value);
}
static void encodeBytesTo(Bytes value, Consumer<Bytes> appender) {
appender.accept(encodeLong(value.size(), 32));
appender.accept(value);
}
static void encodeFixedBytesTo(Bytes value, Consumer<Bytes> appender) {
appender.accept(value);
}
/**
* Encode a value to a {@link Bytes} value.
*
* @param value The value to encode.
* @return The SSZ encoding in a {@link Bytes} value.
*/
public static Bytes encodeByteArray(byte[] value) {
return encodeBytes(Bytes.wrap(value));
}
static void encodeByteArrayTo(byte[] value, Consumer<byte[]> appender) {
appender.accept(encodeLongToByteArray(value.length, 32));
appender.accept(value);
}
/**
* Encode a string to a {@link Bytes} value.
*
* @param str The string to encode.
* @return The SSZ encoding in a {@link Bytes} value.
*/
public static Bytes encodeString(String str) {
return encodeByteArray(str.getBytes(UTF_8));
}
static void encodeStringTo(String str, Consumer<byte[]> appender) {
encodeByteArrayTo(str.getBytes(UTF_8), appender);
}
/**
* Encode a two's-compliment integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @param bitLength the bit length of the encoded integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeInt(int value, int bitLength) {
return encodeLong(value, bitLength);
}
/**
* Encode a two's-compliment long integer to a {@link Bytes} value.
*
* @param value the long to encode
* @param bitLength the bit length of the integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeLong(long value, int bitLength) {
return Bytes.wrap(encodeLongToByteArray(value, bitLength));
}
static byte[] encodeLongToByteArray(long value, int bitLength) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
int zeros = (value >= 0) ? Long.numberOfLeadingZeros(value) : Long.numberOfLeadingZeros(-1 - value) - 1;
int valueBytes = 8 - (zeros / 8);
int byteLength = bitLength / 8;
checkArgument(valueBytes <= byteLength, "value is too large for the desired bitLength");
byte[] encoded = new byte[byteLength];
int shift = 0;
for (int i = 0; i < valueBytes; i++) {
encoded[i] = (byte) ((value >> shift) & 0xFF);
shift += 8;
}
if (value < 0) {
// Extend the two's-compliment integer by setting all remaining bits to 1.
for (int i = valueBytes; i < byteLength; i++) {
encoded[i] = (byte) 0xFF;
}
}
return encoded;
}
/**
* Encode a big integer to a {@link Bytes} value.
*
* @param value the big integer to encode
* @param bitLength the bit length of the integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeBigInteger(BigInteger value, int bitLength) {
return Bytes.wrap(encodeBigIntegerToByteArray(value, bitLength));
}
public static byte[] encodeBigIntegerToByteArray(BigInteger value, int bitLength) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
byte[] bytes = value.toByteArray();
int valueBytes = bytes.length;
int offset = 0;
if (value.signum() >= 0 && bytes[0] == 0) {
valueBytes = bytes.length - 1;
offset = 1;
}
int byteLength = bitLength / 8;
checkArgument(valueBytes <= byteLength, "value is too large for the desired bitLength");
byte[] encoded;
if (valueBytes == byteLength && offset == 0) {
encoded = bytes;
} else {
encoded = new byte[byteLength];
int padLength = byteLength - valueBytes;
System.arraycopy(bytes, offset, encoded, padLength, valueBytes);
if (value.signum() < 0) {
// Extend the two's-compliment integer by setting all leading bits to 1.
for (int i = 0; i < padLength; i++) {
encoded[i] = (byte) 0xFF;
}
}
}
// reverse the array to make it little endian
for (int i = 0; i < (encoded.length / 2); i++) {
byte swapped = encoded[i];
encoded[i] = encoded[encoded.length - i - 1];
encoded[encoded.length - i - 1] = swapped;
}
return encoded;
}
/**
* Encode an 8-bit two's-compliment integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large to be represented in 8 bits
*/
public static Bytes encodeInt8(int value) {
return encodeInt(value, 8);
}
/**
* Encode a 16-bit two's-compliment integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large to be represented in 16 bits
*/
public static Bytes encodeInt16(int value) {
return encodeInt(value, 16);
}
/**
* Encode a 32-bit two's-compliment integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt32(int value) {
return encodeInt(value, 32);
}
/**
* Encode a 64-bit two's-compliment integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt64(long value) {
return encodeLong(value, 64);
}
/**
* Encode an unsigned integer to a {@link Bytes} value.
*
* Note that {@code value} is a native signed int, but will be interpreted as an unsigned value.
*
* @param value the integer to encode
* @param bitLength the bit length of the encoded integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeUInt(int value, int bitLength) {
return encodeULong(value, bitLength);
}
/**
* Encode an unsigned long integer to a {@link Bytes} value.
*
* Note that {@code value} is a native signed long, but will be interpreted as an unsigned value.
*
* @param value the long to encode
* @param bitLength the bit length of the integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeULong(long value, int bitLength) {
return Bytes.wrap(encodeULongToByteArray(value, bitLength));
}
static byte[] encodeULongToByteArray(long value, int bitLength) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
int zeros = Long.numberOfLeadingZeros(value);
int valueBytes = 8 - (zeros / 8);
checkArgument(zeros == 0 || value >= 0, "Value must be positive or zero");
int byteLength = bitLength / 8;
checkArgument(valueBytes <= byteLength, "value is too large for the desired bitLength");
byte[] encoded = new byte[byteLength];
int shift = 0;
for (int i = 0; i < valueBytes; i++) {
encoded[i] = (byte) ((value >> shift) & 0xFF);
shift += 8;
}
return encoded;
}
/**
* Encode an unsigned big integer to a {@link Bytes} value.
*
* @param value the big integer to encode
* @param bitLength the bit length of the integer value (must be a multiple of 8)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large for the specified {@code bitLength}
*/
public static Bytes encodeUBigInteger(BigInteger value, int bitLength) {
return Bytes.wrap(encodeUBigIntegerToByteArray(value, bitLength));
}
public static byte[] encodeUBigIntegerToByteArray(BigInteger value, int bitLength) {
checkArgument(value.compareTo(BigInteger.ZERO) >= 0, "Value must be positive or zero");
return encodeBigIntegerToByteArray(value, bitLength);
}
/**
* Encode an 8-bit unsigned integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large to be represented in 8 bits
*/
public static Bytes encodeUInt8(int value) {
return encodeUInt(value, 8);
}
/**
* Encode a 16-bit unsigned integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if the value is too large to be represented in 16 bits
*/
public static Bytes encodeUInt16(int value) {
return encodeUInt(value, 16);
}
/**
* Encode a 32-bit unsigned integer to a {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt32(long value) {
return encodeULong(value, 32);
}
/**
* Encode a 64-bit unsigned integer to a {@link Bytes} value.
*
* Note that {@code value} is a native signed long, but will be interpreted as an unsigned value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt64(long value) {
return encodeULong(value, 64);
}
/**
* Encode a 256-bit unsigned integer to a little-endian {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt256(UInt256 value) {
return value.toBytes().reverse();
}
/**
* Encode a 384-bit unsigned integer to a little-endian {@link Bytes} value.
*
* @param value the integer to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt384(UInt384 value) {
return value.toBytes().reverse();
}
/**
* Encode a boolean to a {@link Bytes} value.
*
* @param value the boolean to encode
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeBoolean(boolean value) {
return value ? TRUE : FALSE;
}
/**
* Encode a 20-byte address to a {@link Bytes} value.
*
* @param address the address (must be exactly 20 bytes)
* @return the SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if {@code address.size != 20}
*/
public static Bytes encodeAddress(Bytes address) {
checkArgument(address.size() == 20, "address is not 20 bytes");
return address;
}
/**
* Encode a hash to a {@link Bytes} value.
*
* @param hash the hash
* @return the SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeHash(Bytes hash) {
return hash;
}
/**
* Encode a list of bytes.
*
* @param elements the bytes to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeBytesList(Bytes... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length * 2 + 1);
encodeBytesListTo(elements, encoded::add);
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of bytes.
*
* @param elements the bytes to write as a list
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeBytesList(List<? extends Bytes> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() * 2 + 1);
encodeBytesListTo(elements, encoded::add);
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeBytesListTo(Bytes[] elements, Consumer<Bytes> appender) {
// pre-calculate the list size - relies on knowing how encodeBytesTo does its serialization, but is worth it
// to avoid having to pre-serialize all the elements
long listSize = 0;
for (Bytes bytes : elements) {
listSize += 4;
listSize += bytes.size();
if (listSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Cannot serialize list: overall length is too large");
}
}
appender.accept(encodeUInt32(listSize));
for (Bytes bytes : elements) {
encodeBytesTo(bytes, appender);
}
}
static void encodeBytesListTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
// pre-calculate the list size - relies on knowing how encodeBytesTo does its serialization, but is worth it
// to avoid having to pre-serialize all the elements
long listSize = 0;
for (Bytes bytes : elements) {
listSize += 4;
listSize += bytes.size();
if (listSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Cannot serialize list: overall length is too large");
}
}
appender.accept(encodeUInt32(listSize));
for (Bytes bytes : elements) {
encodeBytesTo(bytes, appender);
}
}
static void encodeFixedBytesVectorTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
for (Bytes bytes : elements) {
appender.accept(bytes);
}
}
static void encodeBytesVectorTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
for (Bytes bytes : elements) {
appender.accept(encodeLong(bytes.size(), 32));
appender.accept(bytes);
}
}
static void encodeFixedBytesListTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
// pre-calculate the list size - relies on knowing how encodeBytesTo does its serialization, but is worth it
// to avoid having to pre-serialize all the elements
long listSize = 0;
for (Bytes bytes : elements) {
listSize += bytes.size();
if (listSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Cannot serialize list: overall length is too large");
}
}
appender.accept(encodeUInt32(listSize));
for (Bytes bytes : elements) {
encodeFixedBytesTo(bytes, appender);
}
}
/**
* Encode a list of strings.
*
* @param elements the strings to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeStringList(String... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length * 2 + 1);
encodeStringListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of strings
*
* @param elements the list of strings to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeStringList(List<String> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() * 2 + 1);
encodeStringListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeStringListTo(String[] elements, Consumer<Bytes> appender) {
Bytes[] elementBytes = new Bytes[elements.length];
for (int i = 0; i < elements.length; ++i) {
elementBytes[i] = Bytes.wrap(elements[i].getBytes(UTF_8));
}
encodeBytesListTo(elementBytes, appender);
}
static void encodeStringListTo(List<String> elements, Consumer<Bytes> appender) {
Bytes[] elementBytes = new Bytes[elements.size()];
for (int i = 0; i < elements.size(); ++i) {
elementBytes[i] = Bytes.wrap(elements.get(i).getBytes(UTF_8));
}
encodeBytesListTo(elementBytes, appender);
}
/**
* Encode a list of two's compliment integers.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeIntList(int bitLength, int... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of two's compliment integers.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the list of Integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeIntList(int bitLength, List<Integer> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeIntListTo(int bitLength, int[] elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.length, bitLength / 8));
for (int value : elements) {
appender.accept(encodeLongToByteArray(value, bitLength));
}
}
static void encodeIntListTo(int bitLength, List<Integer> elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.size(), bitLength / 8));
for (int value : elements) {
appender.accept(encodeLongToByteArray(value, bitLength));
}
}
/**
* Encode a list of two's compliment long integers.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeLongIntList(int bitLength, long... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeLongIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of two's compliment long integers.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the list of Longs to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeLongIntList(int bitLength, List<Long> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeLongIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeLongIntListTo(int bitLength, long[] elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.length, bitLength / 8));
for (long value : elements) {
appender.accept(encodeLongToByteArray(value, bitLength));
}
}
static void encodeLongIntListTo(int bitLength, List<Long> elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.size(), bitLength / 8));
for (long value : elements) {
appender.accept(encodeLongToByteArray(value, bitLength));
}
}
/**
* Encode a list of big integers.
*
* @param bitLength The bit length of the encoded integers (must be a multiple of 8).
* @param elements The integers to write.
* @return SSZ encoding in a {@link Bytes} value.
* @throws IllegalArgumentException If any values are too large for the specified {@code bitLength}.
*/
public static Bytes encodeBigIntegerList(int bitLength, BigInteger... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeBigIntegerListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of big integers.
*
* @param bitLength The bit length of the encoded integers (must be a multiple of 8).
* @param elements The list of BigIntegers to write.
* @return SSZ encoding in a {@link Bytes} value.
* @throws IllegalArgumentException If any values are too large for the specified {@code bitLength}.
*/
public static Bytes encodeBigIntegerList(int bitLength, List<BigInteger> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeBigIntegerListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeBigIntegerListTo(int bitLength, BigInteger[] elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.length, bitLength / 8));
for (BigInteger value : elements) {
appender.accept(encodeBigIntegerToByteArray(value, bitLength));
}
}
static void encodeBigIntegerListTo(int bitLength, List<BigInteger> elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.size(), bitLength / 8));
for (BigInteger value : elements) {
appender.accept(encodeBigIntegerToByteArray(value, bitLength));
}
}
/**
* Encode a list of 8-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 8 bits
*/
public static Bytes encodeInt8List(int... elements) {
return encodeIntList(8, elements);
}
/**
* Encode a list of 8-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 8 bits
*/
public static Bytes encodeInt8List(List<Integer> elements) {
return encodeIntList(8, elements);
}
/**
* Encode a list of 16-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 16 bits
*/
public static Bytes encodeInt16List(int... elements) {
return encodeIntList(16, elements);
}
/**
* Encode a list of 16-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 16 bits
*/
public static Bytes encodeInt16List(List<Integer> elements) {
return encodeIntList(16, elements);
}
/**
* Encode a list of 32-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt32List(int... elements) {
return encodeIntList(32, elements);
}
/**
* Encode a list of 32-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt32List(List<Integer> elements) {
return encodeIntList(32, elements);
}
/**
* Encode a list of 64-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt64List(long... elements) {
return encodeLongIntList(64, elements);
}
/**
* Encode a list of 64-bit two's compliment integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeInt64List(List<Long> elements) {
return encodeLongIntList(64, elements);
}
/**
* Encode a list of unsigned integers.
*
* Note that the {@code elements} are native signed ints, but will be interpreted as an unsigned values.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeUIntList(int bitLength, int... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeUIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of unsigned integers.
*
* Note that the {@code elements} are native signed ints, but will be interpreted as an unsigned values.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeUIntList(int bitLength, List<Integer> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeUIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeUIntListTo(int bitLength, int[] elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.length, bitLength / 8));
for (int value : elements) {
appender.accept(encodeULongToByteArray(value, bitLength));
}
}
static void encodeUIntListTo(int bitLength, List<Integer> elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.size(), bitLength / 8));
for (int value : elements) {
appender.accept(encodeULongToByteArray(value, bitLength));
}
}
/**
* Encode a list of unsigned long integers.
*
* Note that the {@code elements} are native signed longs, but will be interpreted as an unsigned values.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeULongIntList(int bitLength, long... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeULongIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of unsigned long integers.
*
* Note that the {@code elements} are native signed longs, but will be interpreted as an unsigned values.
*
* @param bitLength the bit length of the encoded integers (must be a multiple of 8)
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large for the specified {@code bitLength}
*/
public static Bytes encodeULongIntList(int bitLength, List<Long> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeULongIntListTo(bitLength, elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeULongIntListTo(int bitLength, long[] elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.length, bitLength / 8));
for (long value : elements) {
appender.accept(encodeULongToByteArray(value, bitLength));
}
}
static void encodeULongIntListTo(int bitLength, List<Long> elements, Consumer<byte[]> appender) {
checkArgument(bitLength % 8 == 0, "bitLength must be a multiple of 8");
appender.accept(listLengthPrefix(elements.size(), bitLength / 8));
for (long value : elements) {
appender.accept(encodeULongToByteArray(value, bitLength));
}
}
/**
* Encode a list of 8-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 8 bits
*/
public static Bytes encodeUInt8List(int... elements) {
return encodeUIntList(8, elements);
}
/**
* Encode a list of 8-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 8 bits
*/
public static Bytes encodeUInt8List(List<Integer> elements) {
return encodeUIntList(8, elements);
}
/**
* Encode a list of 16-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 16 bits
*/
public static Bytes encodeUInt16List(int... elements) {
return encodeUIntList(16, elements);
}
/**
* Encode a list of 16-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 16 bits
*/
public static Bytes encodeUInt16List(List<Integer> elements) {
return encodeUIntList(16, elements);
}
/**
* Encode a list of 32-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 32 bits
*/
public static Bytes encodeUInt32List(long... elements) {
return encodeULongIntList(32, elements);
}
/**
* Encode a list of 32-bit unsigned integers.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any values are too large to be represented in 32 bits
*/
public static Bytes encodeUInt32List(List<Long> elements) {
return encodeULongIntList(32, elements);
}
/**
* Encode a list of 64-bit unsigned integers.
*
* Note that the {@code elements} are native signed longs, but will be interpreted as an unsigned values.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt64List(long... elements) {
return encodeULongIntList(64, elements);
}
/**
* Encode a list of 64-bit unsigned integers.
*
* Note that the {@code elements} are native signed longs, but will be interpreted as an unsigned values.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt64List(List<Long> elements) {
return encodeULongIntList(64, elements);
}
/**
* Encode a list of {@link UInt256}.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt256List(UInt256... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeUInt256ListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of {@link UInt256}.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt256List(List<UInt256> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeUInt256ListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeUInt256ListTo(UInt256[] elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.length, 256 / 8)));
for (UInt256 value : elements) {
appender.accept(encodeUInt256(value));
}
}
static void encodeUInt256ListTo(List<UInt256> elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.size(), 256 / 8)));
for (UInt256 value : elements) {
appender.accept(encodeUInt256(value));
}
}
/**
* Encode a list of {@link UInt384}.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt384List(UInt384... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeUInt384ListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of {@link UInt384}.
*
* @param elements the integers to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeUInt384List(List<UInt384> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeUInt384ListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeUInt384ListTo(UInt384[] elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.length, 256 / 8)));
for (UInt384 value : elements) {
appender.accept(encodeUInt384(value));
}
}
static void encodeUInt384ListTo(List<UInt384> elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.size(), 256 / 8)));
for (UInt384 value : elements) {
appender.accept(encodeUInt384(value));
}
}
/**
* Encode a list of hashes.
*
* @param elements the hashes to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeHashList(Bytes... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeHashListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of hashes.
*
* @param elements the hashes to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeHashList(List<? extends Bytes> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeHashListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeHashListTo(Bytes[] elements, Consumer<Bytes> appender) {
int hashLength = 0;
for (Bytes bytes : elements) {
if (hashLength == 0) {
hashLength = bytes.size();
} else {
checkArgument(bytes.size() == hashLength, "Hashes must be all of the same size");
}
}
appender.accept(Bytes.wrap(listLengthPrefix(elements.length, 32)));
for (Bytes bytes : elements) {
appender.accept(bytes);
}
}
static void encodeHashListTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
int hashLength = 0;
for (Bytes bytes : elements) {
if (hashLength == 0) {
hashLength = bytes.size();
} else {
checkArgument(bytes.size() == hashLength, "Hashes must be all of the same size");
}
}
appender.accept(Bytes.wrap(listLengthPrefix(elements.size(), 32)));
for (Bytes bytes : elements) {
appender.accept(bytes);
}
}
/**
* Encode a list of addresses.
*
* @param elements the addresses to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any {@code address.size != 20}
*/
public static Bytes encodeAddressList(Bytes... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeAddressListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of addresses.
*
* @param elements the addresses to write
* @return SSZ encoding in a {@link Bytes} value
* @throws IllegalArgumentException if any {@code address.size != 20}
*/
public static Bytes encodeAddressList(List<? extends Bytes> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeAddressListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeAddressListTo(Bytes[] elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.length, 20)));
for (Bytes bytes : elements) {
appender.accept(encodeAddress(bytes));
}
}
static void encodeAddressListTo(List<? extends Bytes> elements, Consumer<Bytes> appender) {
appender.accept(Bytes.wrap(listLengthPrefix(elements.size(), 20)));
for (Bytes bytes : elements) {
appender.accept(encodeAddress(bytes));
}
}
/**
* Encode a list of booleans.
*
* @param elements the booleans to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeBooleanList(boolean... elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.length + 1);
encodeBooleanListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
/**
* Encode a list of booleans.
*
* @param elements the booleans to write
* @return SSZ encoding in a {@link Bytes} value
*/
public static Bytes encodeBooleanList(List<Boolean> elements) {
ArrayList<Bytes> encoded = new ArrayList<>(elements.size() + 1);
encodeBooleanListTo(elements, b -> encoded.add(Bytes.wrap(b)));
return Bytes.wrap(encoded.toArray(new Bytes[0]));
}
static void encodeBooleanListTo(boolean[] elements, Consumer<Bytes> appender) {
appender.accept(encodeInt32(elements.length));
for (boolean value : elements) {
appender.accept(encodeBoolean(value));
}
}
static void encodeBooleanListTo(List<Boolean> elements, Consumer<Bytes> appender) {
appender.accept(encodeInt32(elements.size()));
for (boolean value : elements) {
appender.accept(encodeBoolean(value));
}
}
private static byte[] listLengthPrefix(long nElements, int elementBytes) {
long listSize;
try {
listSize = Math.multiplyExact(nElements, (long) elementBytes);
} catch (ArithmeticException e) {
listSize = Long.MAX_VALUE;
}
if (listSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Cannot serialize list: overall length is too large");
}
return encodeLongToByteArray(listSize, 32);
}
// Decoding
/**
* Read and decode SSZ from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param fn a function that will be provided a {@link SSZReader}
* @param <T> the result type of the reading function
* @return the result from the reading function
*/
public static <T> T decode(Bytes source, Function<SSZReader, T> fn) {
requireNonNull(source);
requireNonNull(fn);
return fn.apply(new BytesSSZReader(source));
}
/**
* Read a SSZ encoded bytes from a {@link Bytes} value.
*
* Note: prefer to use {@link #decodeBytes(Bytes, int)} instead, especially when reading untrusted data.
*
* @param source the SSZ encoded bytes
* @return the bytes
* @throws InvalidSSZTypeException if the next SSZ value is not a byte array, or is too large (greater than 2^32
* bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static Bytes decodeBytes(Bytes source) {
return decode(source, SSZReader::readBytes);
}
/**
* Read a SSZ encoded bytes from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param limit the maximum number of bytes to read
* @return the bytes
* @throws InvalidSSZTypeException if the next SSZ value is not a byte array, or would exceed the limit
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static Bytes decodeBytes(Bytes source, int limit) {
return decode(source, r -> r.readBytes(limit));
}
/**
* Read a SSZ encoded string from a {@link Bytes} value.
*
* Note: prefer to use {@link #decodeString(Bytes, int)} instead, especially when reading untrusted data.
*
* @param source the SSZ encoded bytes
* @return a string
* @throws InvalidSSZTypeException if the next SSZ value is not a byte array, or is too large (greater than 2^32
* bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static String decodeString(Bytes source) {
return decode(source, SSZReader::readString);
}
/**
* Read a SSZ encoded string from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param limit the maximum number of bytes to read
* @return a string
* @throws InvalidSSZTypeException if the next SSZ value is not a byte array, or would exceed the limit
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static String decodeString(Bytes source, int limit) {
return decode(source, r -> r.readString(limit));
}
/**
* Read a SSZ encoded two's-compliment integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integer to read (a multiple of 8)
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length, or the decoded
* value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeInt(Bytes source, int bitLength) {
return decode(source, r -> r.readInt(bitLength));
}
/**
* Read a SSZ encoded two's-compliment long integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integer to read (a multiple of 8)
* @return a long
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length, or the decoded
* value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static long decodeLong(Bytes source, int bitLength) {
return decode(source, r -> r.readLong(bitLength));
}
/**
* Read a SSZ encoded two's-compliment big integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integer to read (a multiple of 8)
* @return a string
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static BigInteger decodeBigInteger(Bytes source, int bitLength) {
return decode(source, r -> r.readBigInteger(bitLength));
}
/**
* Read an 8-bit two's-compliment integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for an 8-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeInt8(Bytes source) {
return decodeInt(source, 8);
}
/**
* Read a 16-bit two's-compliment integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 16-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeInt16(Bytes source) {
return decodeInt(source, 16);
}
/**
* Read a 32-bit two's-compliment integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 32-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeInt32(Bytes source) {
return decodeInt(source, 32);
}
/**
* Read a 64-bit two's-compliment integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 64-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static long decodeInt64(Bytes source) {
return decodeLong(source, 64);
}
/**
* Read a SSZ encoded unsigned integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return an unsigned int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length, or the decoded
* value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeUInt(Bytes source, int bitLength) {
return decode(source, r -> r.readUInt(bitLength));
}
/**
* Read a SSZ encoded unsigned long integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return an unsigned long
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length, or the decoded
* value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static long decodeULong(Bytes source, int bitLength) {
return decode(source, r -> r.readULong(bitLength));
}
/**
* Read a SSZ encoded unsigned big integer from a {@link Bytes} value.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a string
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for the desired bit length
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static BigInteger decodeUnsignedBigInteger(Bytes source, int bitLength) {
return decode(source, r -> r.readBigInteger(bitLength));
}
/**
* Read an 8-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for an 8-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeUInt8(Bytes source) {
return decodeUInt(source, 8);
}
/**
* Read a 16-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 16-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static int decodeUInt16(Bytes source) {
return decodeUInt(source, 16);
}
/**
* Read a 32-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 32-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static long decodeUInt32(Bytes source) {
return decodeULong(source, 32);
}
/**
* Read a 64-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return an int
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 64-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static long decodeUInt64(Bytes source) {
return decodeLong(source, 64);
}
/**
* Read a 256-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a {@link UInt256}
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 256-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static UInt256 decodeUInt256(Bytes source) {
return decode(source, SSZReader::readUInt256);
}
/**
* Read a 384-bit unsigned integer from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a {@link UInt384}
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 384-bit int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static UInt384 decodeUInt384(Bytes source) {
return decode(source, SSZReader::readUInt384);
}
/**
* Read a boolean value from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a boolean
* @throws InvalidSSZTypeException if the decoded value is not a boolean
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static boolean decodeBoolean(Bytes source) {
return decode(source, SSZReader::readBoolean);
}
/**
* Read a 20-byte address from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return the bytes of the Address
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 20-byte address
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static Bytes decodeAddress(Bytes source) {
return decode(source, SSZReader::readAddress);
}
/**
* Read a 32-byte hash from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param hashLength the length of the hash (in bytes)
* @return the bytes of the hash
* @throws InvalidSSZTypeException if there are insufficient encoded bytes for a 32-byte hash
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static Bytes decodeHash(Bytes source, int hashLength) {
return decode(source, r -> r.readHash(hashLength));
}
/**
* Read a list of {@link Bytes} from the SSZ source.
*
* Note: prefer to use {@link #decodeBytesList(Bytes, int)} instead, especially when reading untrusted data.
*
* @param source the SSZ encoded bytes
* @return a list of {@link Bytes}
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a byte array, or
* any byte array is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Bytes> decodeBytesList(Bytes source) {
return decode(source, SSZReader::readBytesList);
}
/**
* Read a list of {@link Bytes} from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param limit the maximum number of bytes to read for each list element
* @return a list of {@link Bytes}
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a byte array, or
* any byte array is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Bytes> decodeBytesList(Bytes source, int limit) {
return decode(source, r -> r.readBytesList(limit));
}
/**
* Read a list of byte arrays from the SSZ source.
*
* Note: prefer to use {@link #decodeByteArrayList(Bytes, int)} instead, especially when reading untrusted data.
*
* @param source the SSZ encoded bytes
* @return a list of byte arrays
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a byte array, or
* any byte array is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<byte[]> decodeByteArrayList(Bytes source) {
return decode(source, SSZReader::readByteArrayList);
}
/**
* Read a list of byte arrays from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param limit The maximum number of bytes to read for each list element.
* @return a list of byte arrays
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a byte array, or
* any byte array is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<byte[]> decodeByteArrayList(Bytes source, int limit) {
return decode(source, r -> r.readByteArrayList(limit));
}
/**
* Read a list of strings from the SSZ source.
*
* Note: prefer to use {@link #decodeStringList(Bytes, int)} instead, especially when reading untrusted data.
*
* @param source the SSZ encoded bytes
* @return a list of strings
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a string, or any
* string is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<String> decodeStringList(Bytes source) {
return decode(source, SSZReader::readStringList);
}
/**
* Read a list of strings from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param limit The maximum number of bytes to read for each list element.
* @return a list of strings
* @throws InvalidSSZTypeException if the next SSZ value is not a list, any value in the list is not a string, or any
* string is too large (greater than 2^32 bytes)
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<String> decodeStringList(Bytes source, int limit) {
return decode(source, r -> r.readStringList(limit));
}
/**
* Read a list of two's-compliment int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeIntList(Bytes source, int bitLength) {
return decode(source, r -> r.readIntList(bitLength));
}
/**
* Read a list of two's-compliment long int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of longs
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Long> decodeLongIntList(Bytes source, int bitLength) {
return decode(source, r -> r.readLongIntList(bitLength));
}
/**
* Read a list of two's-compliment big integer values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, or there are insufficient encoded bytes for
* the desired bit length or any value in the list
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<BigInteger> decodeBigIntegerList(Bytes source, int bitLength) {
return decode(source, r -> r.readBigIntegerList(bitLength));
}
/**
* Read a list of 8-bit two's-compliment int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeInt8List(Bytes source) {
return decode(source, SSZReader::readInt8List);
}
/**
* Read a list of 16-bit two's-compliment int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeInt16List(Bytes source) {
return decode(source, SSZReader::readInt16List);
}
/**
* Read a list of 32-bit two's-compliment int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeInt32List(Bytes source) {
return decode(source, SSZReader::readInt32List);
}
/**
* Read a list of 64-bit two's-compliment int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Long> decodeInt64List(Bytes source) {
return decode(source, SSZReader::readInt64List);
}
/**
* Read a list of unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeUIntList(Bytes source, int bitLength) {
return decode(source, r -> r.readUIntList(bitLength));
}
/**
* Read a list of unsigned long int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of longs
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length or any value in the list, or any decoded value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Long> decodeULongIntList(Bytes source, int bitLength) {
return decode(source, r -> r.readULongIntList(bitLength));
}
/**
* Read a list of unsigned big integer values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param bitLength the bit length of the integers to read (a multiple of 8)
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, or there are insufficient encoded bytes for
* the desired bit length of any value in the list
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<BigInteger> decodeUnsignedBigIntegerList(Bytes source, int bitLength) {
return decode(source, r -> r.readUnsignedBigIntegerList(bitLength));
}
/**
* Read a list of 8-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeUInt8List(Bytes source) {
return decode(source, SSZReader::readUInt8List);
}
/**
* Read a list of 16-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into an int
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Integer> decodeUInt16List(Bytes source) {
return decode(source, SSZReader::readUInt16List);
}
/**
* Read a list of 32-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Long> decodeUInt32List(Bytes source) {
return decode(source, SSZReader::readUInt32List);
}
/**
* Read a list of 64-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of ints
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into a long
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Long> decodeUInt64List(Bytes source) {
return decode(source, SSZReader::readUInt64List);
}
/**
* Read a list of 256-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of {@link UInt256}
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into {@link UInt256}
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<UInt256> decodeUInt256List(Bytes source) {
return decode(source, SSZReader::readUInt256List);
}
/**
* Read a list of 384-bit unsigned int values from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of {@link UInt384}
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for the
* desired bit length of any value in the list, or any decoded value was too large to fit into a
* {@link UInt384}
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<UInt384> decodeUInt384List(Bytes source) {
return decode(source, SSZReader::readUInt384List);
}
/**
* Read a list of 20-byte addresses from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of 20-byte addresses
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for any
* address in the list
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Bytes> decodeAddressList(Bytes source) {
return decode(source, SSZReader::readAddressList);
}
/**
* Read a list of 32-byte hashes from the SSZ source.
*
* @param source the SSZ encoded bytes
* @param hashLength The length of the hash (in bytes).
* @return a list of 32-byte hashes
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for any
* hash in the list
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Bytes> decodeHashList(Bytes source, int hashLength) {
return decode(source, r -> r.readHashList(hashLength));
}
/**
* Read a list of booleans from the SSZ source.
*
* @param source the SSZ encoded bytes
* @return a list of booleans
* @throws InvalidSSZTypeException if the next SSZ value is not a list, there are insufficient encoded bytes for all
* the booleans in the list
* @throws EndOfSSZException if there are no more SSZ values to read
*/
public static List<Boolean> decodeBooleanList(Bytes source) {
return decode(source, SSZReader::readBooleanList);
}
}