Skip to content

Commit

Permalink
fix little-endian order for encoding BASIC_DATA_LEAF values (hyperled…
Browse files Browse the repository at this point in the history
…ger#70)

Signed-off-by: Luis Pinto <[email protected]>
  • Loading branch information
lu-pinto authored Sep 26, 2024
1 parent c05a1a5 commit 3bcad45
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,56 +20,105 @@

public class SuffixTreeEncoder {
private static final int VERSION_OFFSET = 0;
private static final int VERSION_BYTE_SIZE = 1;
private static final int CODE_SIZE_OFFSET = 5;
private static final int CODE_SIZE_BYTE_SIZE = 3;
private static final int NONCE_OFFSET = 8;
private static final int NONCE_BYTE_SIZE = 8;
private static final int BALANCE_OFFSET = 16;
private static final int BALANCE_BYTE_SIZE = 16;

private static final Bytes32 VERSION_VALUE_MASK;
private static final Bytes32 CODE_SIZE_VALUE_MASK;
private static final Bytes32 NONCE_VALUE_MASK;
private static final Bytes32 BALANCE_VALUE_MASK;

static {
VERSION_VALUE_MASK =
encodeIntoBasicDataLeaf(Bytes.repeat((byte) 0xff, 1), VERSION_OFFSET).not();
CODE_SIZE_VALUE_MASK =
encodeIntoBasicDataLeaf(Bytes.repeat((byte) 0xff, 3), CODE_SIZE_OFFSET).not();
NONCE_VALUE_MASK = encodeIntoBasicDataLeaf(Bytes.repeat((byte) 0xff, 8), NONCE_OFFSET).not();
BALANCE_VALUE_MASK =
encodeIntoBasicDataLeaf(Bytes.repeat((byte) 0xff, 16), BALANCE_OFFSET).not();
VERSION_VALUE_MASK = createMask(VERSION_OFFSET, VERSION_BYTE_SIZE);
CODE_SIZE_VALUE_MASK = createMask(CODE_SIZE_OFFSET, CODE_SIZE_BYTE_SIZE);
NONCE_VALUE_MASK = createMask(NONCE_OFFSET, NONCE_BYTE_SIZE);
BALANCE_VALUE_MASK = createMask(BALANCE_OFFSET, BALANCE_BYTE_SIZE);
}

private static Bytes32 createMask(final int offset, final int size) {
Bytes value = Bytes.repeat((byte) 0xff, size);
return encodeIntoBasicDataLeaf(value, offset).not();
}

public static Bytes32 eraseVersion(final Bytes32 value) {
return value.and(VERSION_VALUE_MASK);
}

public static Bytes32 eraseCodeSize(final Bytes32 value) {
private static Bytes32 eraseCodeSize(final Bytes32 value) {
return value.and(CODE_SIZE_VALUE_MASK);
}

public static Bytes32 eraseNonce(final Bytes32 value) {
private static Bytes32 eraseNonce(final Bytes32 value) {
return value.and(NONCE_VALUE_MASK);
}

public static Bytes32 eraseBalance(final Bytes32 value) {
private static Bytes32 eraseBalance(final Bytes32 value) {
return value.and(BALANCE_VALUE_MASK);
}

public static Bytes32 addVersionIntoValue(final Bytes32 value, final Bytes version) {
/**
* Sets the new version and encodes it inside `value`.
*
* @param value on which to set the version part
* @param version value for the new version
* @return updated value with the new version set
*/
public static Bytes32 setVersionInValue(Bytes32 value, final Bytes version) {
checkSize(version, VERSION_BYTE_SIZE);
value = eraseVersion(value);
return value.or(encodeIntoBasicDataLeaf(version, VERSION_OFFSET));
}

public static Bytes32 addCodeSizeIntoValue(final Bytes32 value, final Bytes codeSize) {
/**
* Sets the new code size and encodes it inside `value`.
*
* @param value on which to set the code size part
* @param codeSize value for the new code size
* @return updated value with the new code size set
*/
public static Bytes32 setCodeSizeInValue(Bytes32 value, final Bytes codeSize) {
checkSize(codeSize, CODE_SIZE_BYTE_SIZE);
value = eraseCodeSize(value);
return value.or(encodeIntoBasicDataLeaf(codeSize, CODE_SIZE_OFFSET));
}

public static Bytes32 addNonceIntoValue(final Bytes32 value, final Bytes nonce) {
/**
* Sets the new nonce and encodes it inside `value`.
*
* @param value on which to set the nonce part
* @param nonce value for the new nonce
* @return updated value with the new nonce set
*/
public static Bytes32 setNonceInValue(Bytes32 value, final Bytes nonce) {
checkSize(nonce, NONCE_BYTE_SIZE);
value = eraseNonce(value);
return value.or(encodeIntoBasicDataLeaf(nonce, NONCE_OFFSET));
}

public static Bytes32 addBalanceIntoValue(final Bytes32 value, final Bytes balance) {
/**
* Sets the new balance and encodes it inside `value`.
*
* @param value on which to set the balance part
* @param balance value for the new balance
* @return updated value with the new balance set
*/
public static Bytes32 setBalanceInValue(Bytes32 value, final Bytes balance) {
checkSize(balance, BALANCE_BYTE_SIZE);
value = eraseBalance(value);
return value.or(encodeIntoBasicDataLeaf(balance, BALANCE_OFFSET));
}

private static void checkSize(final Bytes value, final int requiredSize) {
if (value.size() != requiredSize) {
throw new IllegalArgumentException("value should have size=" + requiredSize);
}
}

/**
* Encoding of a field into the BasicDataLeaf 32 byte value, using Little-Endian order.
*
Expand All @@ -78,21 +127,21 @@ public static Bytes32 addBalanceIntoValue(final Bytes32 value, final Bytes balan
* @throws IllegalArgumentException if `value` does not fit within 32 bytes after being encoded
* @return encoded BasicDataLeaf value
*/
public static Bytes32 encodeIntoBasicDataLeaf(final Bytes value, final int byteShift) {
Bytes32 value32Bytes = Bytes32.leftPad(value);
static Bytes32 encodeIntoBasicDataLeaf(final Bytes value, final int byteShift) {
Bytes32 value32Bytes = Bytes32.rightPad(value);
if (byteShift == 0) {
return value32Bytes;
} else if (byteShift < 0) {
throw new IllegalArgumentException(
"invalid byteShift " + byteShift + " must be greater than zero");
} else if (value32Bytes.numberOfLeadingZeroBytes() < byteShift) {
int valueSizeBytes = 32 - value32Bytes.numberOfLeadingZeroBytes() + byteShift;
} else if (value32Bytes.numberOfTrailingZeroBytes() < byteShift) {
int valueSizeBytes = 32 - value32Bytes.numberOfTrailingZeroBytes() + byteShift;
throw new IllegalArgumentException(
"value must be 32 bytes but becomes "
+ valueSizeBytes
+ " bytes with byteShift "
+ byteShift);
}
return value32Bytes.shiftLeft(byteShift * 8);
return value32Bytes.shiftRight(byteShift * 8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,32 @@

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ArgumentConverter;
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

public class SuffixTreeEncoderTest {

public static Stream<Arguments> valuesStart() {
private static class StringToBytes32Converter implements ArgumentConverter {
@Override
public Object convert(Object source, ParameterContext context)
throws ArgumentConversionException {
if (!(source instanceof CharSequence)) {
throw new ArgumentConversionException("source must be a String");
}
String hexString = (String) source;
return Bytes32.fromHexString(hexString);
}
}

public static Stream<Arguments> valuesEnd() {
return Stream.of(
Arguments.of(Bytes32.ZERO, Bytes32.ZERO),
Arguments.of(
Expand Down Expand Up @@ -59,7 +78,7 @@ public static Stream<Arguments> valuesMiddle() {
Arguments.of(
Bytes.of(0xff),
Bytes32.fromHexString(
"0x00000000000000000000000000000000FF000000000000000000000000000000")),
"0x000000000000000000000000000000FF00000000000000000000000000000000")),
Arguments.of(
Bytes.repeat((byte) 0xff, 12),
Bytes32.fromHexString(
Expand All @@ -71,10 +90,10 @@ public static Stream<Arguments> valuesMiddle() {
Arguments.of(
Bytes.fromHexString("0x1123d3"),
Bytes32.fromHexString(
"0x0000000000000000000000000000001123D30000000000000000000000000000")));
"0x00000000000000000000000000001123D3000000000000000000000000000000")));
}

public static Stream<Arguments> valuesEnd() {
public static Stream<Arguments> valuesStart() {
return Stream.of(
Arguments.of(Bytes32.ZERO, Bytes32.ZERO),
Arguments.of(
Expand Down Expand Up @@ -129,4 +148,132 @@ void encodeBytesOutsideRange(final Bytes value, final int byteShift) {
IllegalArgumentException.class,
() -> SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, byteShift));
}

@ParameterizedTest
@ValueSource(
strings = {
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
})
void setVersionInValue(
@ConvertWith(StringToBytes32Converter.class) final Bytes32 basicDataLeafValue) {
Bytes version = Bytes.of(10);
assertEquals(
Bytes.fromHexString("0x0A00000000000000000000000000000000000000000000000000000000000000"),
SuffixTreeEncoder.setVersionInValue(basicDataLeafValue, version));
}

@Test
void setVersionInValue_differentSizeThrows() {
Bytes version = Bytes.ofUnsignedShort(10);
assertThrows(
IllegalArgumentException.class,
() -> SuffixTreeEncoder.setVersionInValue(Bytes32.ZERO, version));
}

@Test
void setVersionInValue_fullLeaf() {
final Bytes32 basicDataLeafValue =
Bytes32.fromHexString("0xAB343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234");
Bytes version = Bytes.of(10);
assertEquals(
Bytes.fromHexString("0x0A343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234"),
SuffixTreeEncoder.setVersionInValue(basicDataLeafValue, version));
}

@ParameterizedTest
@ValueSource(
strings = {
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000AC000000000000000000000000000000000000000000000000"
})
void setCodeSizeInValue(
@ConvertWith(StringToBytes32Converter.class) final Bytes32 basicDataLeafValue) {
Bytes codeSize = Bytes.repeat((byte) 0, 3).or(Bytes.of(54));
assertEquals(
Bytes.fromHexString("0x0000000000000036000000000000000000000000000000000000000000000000"),
SuffixTreeEncoder.setCodeSizeInValue(basicDataLeafValue, codeSize));
}

@Test
void setCodeSizeInValue_differentSizeThrows() {
Bytes codeSize = Bytes.ofUnsignedLong(54);
assertThrows(
IllegalArgumentException.class,
() -> SuffixTreeEncoder.setCodeSizeInValue(Bytes32.ZERO, codeSize));
}

@Test
void setCodeSizeInValue_fullLeaf() {
final Bytes32 basicDataLeafValue =
Bytes32.fromHexString("0xAB343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234");
Bytes codeSize = Bytes.repeat((byte) 0, 3).or(Bytes.of(54));
assertEquals(
Bytes.fromHexString("0xAB343123BD0000364BC13213EF23434FF213124423247124FF12312EE12AC234"),
SuffixTreeEncoder.setCodeSizeInValue(basicDataLeafValue, codeSize));
}

@ParameterizedTest
@ValueSource(
strings = {
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000100000000000000000000000000000000"
})
void setNonceInValue(
@ConvertWith(StringToBytes32Converter.class) final Bytes32 basicDataLeafValue) {
Bytes nonce = Bytes.repeat((byte) 0, 8).or(Bytes.of(20));
assertEquals(
Bytes.fromHexString("0x0000000000000000000000000000001400000000000000000000000000000000"),
SuffixTreeEncoder.setNonceInValue(basicDataLeafValue, nonce));
}

@Test
void setNonceInValue_differentSizeThrows() {
Bytes nonce = UInt256.valueOf(54);
assertThrows(
IllegalArgumentException.class,
() -> SuffixTreeEncoder.setNonceInValue(Bytes32.ZERO, nonce));
}

@Test
void setNonceInValue_fullLeaf() {
final Bytes32 basicDataLeafValue =
Bytes32.fromHexString("0xAB343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234");
Bytes nonce = Bytes.repeat((byte) 0, 8).or(Bytes.of(20));
assertEquals(
Bytes.fromHexString("0xAB343123BD1231210000000000000014F213124423247124FF12312EE12AC234"),
SuffixTreeEncoder.setNonceInValue(basicDataLeafValue, nonce));
}

@ParameterizedTest
@ValueSource(
strings = {
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000000000000000000000000000000000000000000F2"
})
void setBalanceInValue(
@ConvertWith(StringToBytes32Converter.class) final Bytes32 basicDataLeafValue) {
Bytes balance = Bytes.repeat((byte) 0, 16).or(Bytes.fromHexString("0x3635C9ADC5DEA00000"));
assertEquals(
Bytes.fromHexString("0x00000000000000000000000000000000000000000000003635C9ADC5DEA00000"),
SuffixTreeEncoder.setBalanceInValue(basicDataLeafValue, balance));
}

@Test
void setBalanceInValue_differentSizeThrows() {
Bytes balance = Bytes.repeat((byte) 0, 32).or(Bytes.fromHexString("0x3635C9ADC5DEA00000"));
assertThrows(
IllegalArgumentException.class,
() -> SuffixTreeEncoder.setBalanceInValue(Bytes32.ZERO, balance));
}

@Test
void setBalanceInValue_fullLeaf() {
final Bytes32 basicDataLeafValue =
Bytes32.fromHexString("0xAB343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234");
Bytes balance = Bytes.repeat((byte) 0, 16).or(Bytes.fromHexString("0x3635C9ADC5DEA00000"));
assertEquals(
Bytes.fromHexString("0xAB343123BD1231214BC13213EF23434f000000000000003635C9ADC5DEA00000"),
SuffixTreeEncoder.setBalanceInValue(basicDataLeafValue, balance));
}
}

0 comments on commit 3bcad45

Please sign in to comment.