diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoder.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoder.java new file mode 100644 index 00000000..0d117d72 --- /dev/null +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoder.java @@ -0,0 +1,63 @@ +/* + * Copyright Hyperledger Besu Contributors + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.ethereum.trie.verkle.util; + +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.BALANCE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.BALANCE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.CODE_SIZE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.CODE_SIZE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.NONCE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.NONCE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.VERSION_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.VERSION_OFFSET; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; + +public class SuffixTreeDecoder { + + public static Bytes[] decodeBasicDataLeaf(final Bytes32 value) { + Bytes[] decodedFields = new Bytes[4]; + + decodedFields[0] = extractField(value, VERSION_OFFSET, VERSION_BYTE_SIZE); + decodedFields[1] = extractField(value, CODE_SIZE_OFFSET, CODE_SIZE_BYTE_SIZE); + decodedFields[2] = extractField(value, NONCE_OFFSET, NONCE_BYTE_SIZE); + decodedFields[3] = extractField(value, BALANCE_OFFSET, BALANCE_BYTE_SIZE); + + return decodedFields; + } + + public static Byte decodeVersion(final Bytes32 value) { + return value.get(VERSION_OFFSET); + } + + public static int decodeCodeSize(final Bytes32 value) { + return extractField(value, CODE_SIZE_OFFSET, CODE_SIZE_BYTE_SIZE).toInt(); + } + + public static long decodeNonce(final Bytes32 value) { + return extractField(value, NONCE_OFFSET, NONCE_BYTE_SIZE).toLong(); + } + + public static UInt256 decodeBalance(final Bytes32 value) { + return UInt256.fromBytes(extractField(value, BALANCE_OFFSET, BALANCE_BYTE_SIZE)); + } + + private static Bytes extractField(final Bytes32 value, final int offset, int size) { + return value.slice(offset, size); + } +} diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDescriptor.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDescriptor.java new file mode 100644 index 00000000..003220d6 --- /dev/null +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDescriptor.java @@ -0,0 +1,28 @@ +/* + * Copyright Hyperledger Besu Contributors + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.ethereum.trie.verkle.util; + +public class SuffixTreeDescriptor { + + protected static final int VERSION_OFFSET = 0; + protected static final int VERSION_BYTE_SIZE = 1; + protected static final int CODE_SIZE_OFFSET = 5; + protected static final int CODE_SIZE_BYTE_SIZE = 3; + protected static final int NONCE_OFFSET = 8; + protected static final int NONCE_BYTE_SIZE = 8; + protected static final int BALANCE_OFFSET = 16; + protected static final int BALANCE_BYTE_SIZE = 16; +} diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoder.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoder.java index d2e9b31c..4e05a2b7 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoder.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoder.java @@ -15,51 +15,20 @@ */ package org.hyperledger.besu.ethereum.trie.verkle.util; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.BALANCE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.BALANCE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.CODE_SIZE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.CODE_SIZE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.NONCE_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.NONCE_OFFSET; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.VERSION_BYTE_SIZE; +import static org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeDescriptor.VERSION_OFFSET; + import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.MutableBytes32; 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 = 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); - } - - private static Bytes32 eraseCodeSize(final Bytes32 value) { - return value.and(CODE_SIZE_VALUE_MASK); - } - - private static Bytes32 eraseNonce(final Bytes32 value) { - return value.and(NONCE_VALUE_MASK); - } - - private static Bytes32 eraseBalance(final Bytes32 value) { - return value.and(BALANCE_VALUE_MASK); - } /** * Sets the new version and encodes it inside `value`. @@ -70,8 +39,9 @@ private static Bytes32 eraseBalance(final Bytes32 value) { */ public static Bytes32 setVersionInValue(Bytes32 value, final Bytes version) { checkSize(version, VERSION_BYTE_SIZE); - value = eraseVersion(value); - return value.or(encodeIntoBasicDataLeaf(version, VERSION_OFFSET)); + final MutableBytes32 mutableValue = value.mutableCopy(); + mutableValue.set(VERSION_OFFSET, version); + return mutableValue; } /** @@ -81,10 +51,11 @@ public static Bytes32 setVersionInValue(Bytes32 value, final Bytes version) { * @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) { + public static Bytes32 setCodeSizeInValue(final Bytes32 value, final Bytes codeSize) { checkSize(codeSize, CODE_SIZE_BYTE_SIZE); - value = eraseCodeSize(value); - return value.or(encodeIntoBasicDataLeaf(codeSize, CODE_SIZE_OFFSET)); + final MutableBytes32 mutableValue = value.mutableCopy(); + mutableValue.set(CODE_SIZE_OFFSET, codeSize); + return mutableValue; } /** @@ -96,8 +67,9 @@ public static Bytes32 setCodeSizeInValue(Bytes32 value, final Bytes codeSize) { */ public static Bytes32 setNonceInValue(Bytes32 value, final Bytes nonce) { checkSize(nonce, NONCE_BYTE_SIZE); - value = eraseNonce(value); - return value.or(encodeIntoBasicDataLeaf(nonce, NONCE_OFFSET)); + final MutableBytes32 mutableValue = value.mutableCopy(); + mutableValue.set(NONCE_OFFSET, nonce); + return mutableValue; } /** @@ -109,8 +81,9 @@ public static Bytes32 setNonceInValue(Bytes32 value, final Bytes nonce) { */ public static Bytes32 setBalanceInValue(Bytes32 value, final Bytes balance) { checkSize(balance, BALANCE_BYTE_SIZE); - value = eraseBalance(value); - return value.or(encodeIntoBasicDataLeaf(balance, BALANCE_OFFSET)); + final MutableBytes32 mutableValue = value.mutableCopy(); + mutableValue.set(BALANCE_OFFSET, balance); + return mutableValue; } private static void checkSize(final Bytes value, final int requiredSize) { @@ -118,30 +91,4 @@ private static void checkSize(final Bytes value, final int requiredSize) { throw new IllegalArgumentException("value should have size=" + requiredSize); } } - - /** - * Encoding of a field into the BasicDataLeaf 32 byte value, using Little-Endian order. - * - * @param value to encode into a 32 byte value - * @param byteShift byte position of `value` within the final 32 byte value - * @throws IllegalArgumentException if `value` does not fit within 32 bytes after being encoded - * @return encoded BasicDataLeaf 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.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.shiftRight(byteShift * 8); - } } diff --git a/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoderTest.java b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoderTest.java new file mode 100644 index 00000000..3521a1f0 --- /dev/null +++ b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeDecoderTest.java @@ -0,0 +1,109 @@ +/* + * Copyright Hyperledger Besu Contributors + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.ethereum.trie.verkle.util; + +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SuffixTreeDecoderTest { + + public static Stream decodeValues() { + return Stream.of( + Arguments.of( + Bytes32.ZERO, + new Bytes[] { + Bytes.of(0x00), // Version + Bytes.repeat((byte) 0x00, 3), // Code Size + Bytes.repeat((byte) 0x00, 8), // Nonce + Bytes.repeat((byte) 0x00, 16) // Balance + }), + Arguments.of( + Bytes32.fromHexString( + "0x0A00000000000000000000000000000000000000000000000000000000000000"), + new Bytes[] { + Bytes.of(10), // Version + Bytes.repeat((byte) 0x00, 3), // Code Size + Bytes.repeat((byte) 0x00, 8), // Nonce + Bytes.repeat((byte) 0x00, 16) // Balance + }), + Arguments.of( + Bytes32.fromHexString( + "0x0000000000000036000000000000000000000000000000000000000000000000"), + new Bytes[] { + Bytes.of(0x00), // Version + Bytes.of(0x00, 0x00, 54), // Code Size + Bytes.repeat((byte) 0x00, 8), // Nonce + Bytes.repeat((byte) 0x00, 16) // Balance + }), + Arguments.of( + Bytes32.fromHexString( + "0x0000000000000000000000000000001400000000000000000000000000000000"), + new Bytes[] { + Bytes.of(0x00), // Version + Bytes.repeat((byte) 0x00, 3), // Code Size + Bytes.fromHexString("0x0000000000000014"), // Nonce + Bytes.repeat((byte) 0x00, 16) // Balance + }), + Arguments.of( + Bytes32.fromHexString( + "0x0000000000000000000000000000000000000000000000000000000000000A00"), + new Bytes[] { + Bytes.of(0x00), // Version + Bytes.repeat((byte) 0x00, 3), // Code Size + Bytes.repeat((byte) 0x00, 8), // Nonce + Bytes.fromHexString("0x00000000000000000000000000000a00") // Balance + }), + Arguments.of( + Bytes32.fromHexString( + "0xAB343123BD1231214BC13213EF23434FF213124423247124FF12312EE12AC234"), + new Bytes[] { + Bytes.fromHexString("0xAB"), // Version + Bytes.fromHexString("0x123121"), // Code Size + Bytes.fromHexString("0x4bc13213ef23434f"), // Nonce + Bytes.fromHexString("0xf213124423247124ff12312ee12ac234") // Balance + })); + } + + @ParameterizedTest + @MethodSource("decodeValues") + void decodeBasicDataLeafTest(final Bytes32 basicDataLeafValue, final Bytes[] expected) { + Bytes[] decodedValues = SuffixTreeDecoder.decodeBasicDataLeaf(basicDataLeafValue); + for (int i = 0; i < expected.length; i++) { + Assertions.assertThat(expected[i]).isEqualTo(decodedValues[i]); + } + } + + @ParameterizedTest + @MethodSource("decodeValues") + void decodeFieldByFieldBasicDataLeafTest( + final Bytes32 basicDataLeafValue, final Bytes[] expected) { + Assertions.assertThat(expected[0].get(SuffixTreeDescriptor.VERSION_OFFSET)) + .isEqualTo(SuffixTreeDecoder.decodeVersion(basicDataLeafValue)); + Assertions.assertThat(expected[1].toInt()) + .isEqualTo(SuffixTreeDecoder.decodeCodeSize(basicDataLeafValue)); + Assertions.assertThat(expected[2].toLong()) + .isEqualTo(SuffixTreeDecoder.decodeNonce(basicDataLeafValue)); + Assertions.assertThat(UInt256.fromBytes(expected[3])) + .isEqualTo(SuffixTreeDecoder.decodeBalance(basicDataLeafValue)); + } +} diff --git a/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoderTest.java b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoderTest.java index bbbcc305..95014070 100644 --- a/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoderTest.java +++ b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/util/SuffixTreeEncoderTest.java @@ -18,8 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.stream.Stream; - import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; @@ -29,8 +27,6 @@ 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 { @@ -47,108 +43,6 @@ public Object convert(Object source, ParameterContext context) } } - public static Stream valuesEnd() { - return Stream.of( - Arguments.of(Bytes32.ZERO, Bytes32.ZERO), - Arguments.of( - Bytes.of(0xff), - Bytes32.fromHexString( - "0x00000000000000000000000000000000000000000000000000000000000000FF")), - Arguments.of( - Bytes32.leftPad(Bytes.of(0xff)), - Bytes32.fromHexString( - "0x00000000000000000000000000000000000000000000000000000000000000FF")), - Arguments.of( - Bytes.repeat((byte) 0xff, 12), - Bytes32.fromHexString( - "0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")), - Arguments.of( - Bytes.fromHexString("0xadef"), - Bytes32.fromHexString( - "0x000000000000000000000000000000000000000000000000000000000000ADEF")), - Arguments.of( - Bytes.fromHexString("0x1123d3"), - Bytes32.fromHexString( - "0x00000000000000000000000000000000000000000000000000000000001123D3"))); - } - - public static Stream valuesMiddle() { - return Stream.of( - Arguments.of(Bytes32.ZERO, Bytes32.ZERO), - Arguments.of( - Bytes.of(0xff), - Bytes32.fromHexString( - "0x000000000000000000000000000000FF00000000000000000000000000000000")), - Arguments.of( - Bytes.repeat((byte) 0xff, 12), - Bytes32.fromHexString( - "0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000")), - Arguments.of( - Bytes.fromHexString("0xadef"), - Bytes32.fromHexString( - "0x000000000000000000000000000000ADEF000000000000000000000000000000")), - Arguments.of( - Bytes.fromHexString("0x1123d3"), - Bytes32.fromHexString( - "0x00000000000000000000000000001123D3000000000000000000000000000000"))); - } - - public static Stream valuesStart() { - return Stream.of( - Arguments.of(Bytes32.ZERO, Bytes32.ZERO), - Arguments.of( - Bytes.repeat((byte) 0xff, 12), - Bytes32.fromHexString( - "0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000")), - Arguments.of( - Bytes.of(0xff), - Bytes32.fromHexString( - "0xFF00000000000000000000000000000000000000000000000000000000000000")), - Arguments.of( - Bytes.fromHexString("0xadef"), - Bytes32.fromHexString( - "0xADEF000000000000000000000000000000000000000000000000000000000000")), - Arguments.of( - Bytes.fromHexString("0x1123d3"), - Bytes32.fromHexString( - "0x1123D30000000000000000000000000000000000000000000000000000000000"))); - } - - public static Stream valuesOutOfRange() { - return Stream.of( - Arguments.of(Bytes.repeat((byte) 0xff, 12), 25), - Arguments.of(Bytes.of(0xff), 32), - Arguments.of(Bytes.fromHexString("0xadef"), 31), - Arguments.of(Bytes.fromHexString("0x1123d3"), 30)); - } - - @ParameterizedTest - @MethodSource("valuesStart") - void encodeBytesStart(final Bytes value, final Bytes32 expected) { - assertEquals(expected, SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, 0)); - } - - @ParameterizedTest - @MethodSource("valuesMiddle") - void encodeBytesMiddle(final Bytes value, final Bytes32 expected) { - assertEquals( - expected, SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, (32 - value.size()) / 2)); - } - - @ParameterizedTest - @MethodSource("valuesEnd") - void encodeBytesEnd(final Bytes value, final Bytes32 expected) { - assertEquals(expected, SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, 32 - value.size())); - } - - @ParameterizedTest - @MethodSource("valuesOutOfRange") - void encodeBytesOutsideRange(final Bytes value, final int byteShift) { - assertThrows( - IllegalArgumentException.class, - () -> SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, byteShift)); - } - @ParameterizedTest @ValueSource( strings = {