From f665a5b26ce23221780c046f150050c1afe5b9cc Mon Sep 17 00:00:00 2001 From: imills Date: Tue, 28 Sep 2021 12:42:08 +0100 Subject: [PATCH 1/2] issues/53 - Implement byte serialization for CLKeyValue --- .gitignore | 1 + .../serialize/CLKeyValueJsonSerializer.java | 22 +++++++--------- .../serialization/cltypes/CLValueBuilder.java | 20 +++++++++----- .../service/serialization/util/ByteUtils.java | 10 +++++++ .../java/com/casper/sdk/types/CLKeyInfo.java | 24 ++++++++++++++--- .../java/com/casper/sdk/types/CLKeyValue.java | 22 +++++++++++++++- .../CLKeyValueJsonSerializerTest.java | 4 +-- .../types/CLValueByteSerializerTest.java | 26 ++++++++++++++++++- 8 files changed, 103 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 8ea68f1b7..8551363f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.gradle/ /.idea/ /build/ +/target/ diff --git a/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java b/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java index 1b6684a9c..c292fc5c1 100644 --- a/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java +++ b/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java @@ -1,8 +1,8 @@ package com.casper.sdk.service.json.serialize; import com.casper.sdk.service.serialization.util.ByteUtils; +import com.casper.sdk.types.CLKeyInfo; import com.casper.sdk.types.CLKeyValue; -import com.casper.sdk.types.CLValue; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; @@ -15,8 +15,6 @@ */ public class CLKeyValueJsonSerializer extends JsonSerializer { - private static final String HASH_PREFIX = "hash-"; - @Override public void serialize(final CLKeyValue value, final JsonGenerator gen, @@ -32,21 +30,21 @@ public void serialize(final CLKeyValue value, @NotNull private String buildJsonBytes(final CLKeyValue value) { - return "0" + value.getKeyType().getTag() + getValueBytes(value); + return getValueBytes(value); } - protected String getValueBytes(CLKeyValue value) { + protected String getValueBytes(final CLKeyValue value) { return value.toHex(); } - private void writeParsed(CLValue value, JsonGenerator gen) throws IOException { + private void writeParsed(CLKeyValue value, JsonGenerator gen) throws IOException { final String strParsed; if (value.getParsed() != null) { - strParsed = buildParsed(value.getParsed().toString()); - } else if (value.getBytes() != null) { - strParsed = buildParsed(ByteUtils.encodeHexString(value.getBytes())); + strParsed = buildParsed(value.getParsed().toString(), value.getKeyType()); + } else if (value.getKeyBytes() != null) { + strParsed = buildParsed(ByteUtils.encodeHexString(value.getKeyBytes()), value.getKeyType()); } else { strParsed = null; } @@ -60,11 +58,11 @@ private void writeParsed(CLValue value, JsonGenerator gen) throws IOException { } } - private String buildParsed(final String parsed) { - if (parsed.startsWith(HASH_PREFIX)) { + private String buildParsed(final String parsed, final CLKeyInfo.KeyType keyType) { + if (parsed.contains("-")) { return parsed; } else { - return HASH_PREFIX + parsed; + return keyType.getParsedName() + "-" + parsed; } } } diff --git a/src/main/java/com/casper/sdk/service/serialization/cltypes/CLValueBuilder.java b/src/main/java/com/casper/sdk/service/serialization/cltypes/CLValueBuilder.java index 295b51f4d..27afe0648 100644 --- a/src/main/java/com/casper/sdk/service/serialization/cltypes/CLValueBuilder.java +++ b/src/main/java/com/casper/sdk/service/serialization/cltypes/CLValueBuilder.java @@ -1,10 +1,14 @@ package com.casper.sdk.service.serialization.cltypes; +import com.casper.sdk.exceptions.ConversionException; +import com.casper.sdk.service.serialization.util.ByteUtils; import com.casper.sdk.types.*; import org.apache.commons.lang3.ArrayUtils; import java.math.BigInteger; +import static com.casper.sdk.service.serialization.util.ByteUtils.toByteArray; + /** * Builder to help with programmatic conversion of value types. */ @@ -50,21 +54,25 @@ public static CLValue u512(final Object value) { return buildCLValue(CLType.U512, value); } + public static CLKeyValue key(final byte[] value) { + if (value.length != 33) { + throw new ConversionException("Missing key type from byte array"); + } + return new CLKeyValue(ByteUtils.lastNBytes(value, 32), CLKeyInfo.KeyType.valueOf(value[0]), null); + } + public static CLKeyValue accountKey(final byte[] value) { - return createKey(value, CLKeyInfo.KeyType.ACCOUNT_ID); + return key(ByteUtils.concat(toByteArray(CLKeyInfo.KeyType.ACCOUNT_ID.getTag()), value)); } public static CLKeyValue hashKey(final byte[] value) { - return createKey(value, CLKeyInfo.KeyType.HASH_ID); + return key(ByteUtils.concat(toByteArray(CLKeyInfo.KeyType.HASH_ID.getTag()), value)); } public static CLKeyValue uRefKey(final byte[] value) { - return createKey(value, CLKeyInfo.KeyType.UREF_ID); + return key(ByteUtils.concat(toByteArray(CLKeyInfo.KeyType.UREF_ID.getTag()), value)); } - private static CLKeyValue createKey(byte[] value, CLKeyInfo.KeyType keyType) { - return new CLKeyValue(value, keyType, null); - } private static CLValue buildCLValue(final CLType type, final Object value) { return new CLValue(TYPES_FACTORY.getInstance(type).serialize(value), type, value); diff --git a/src/main/java/com/casper/sdk/service/serialization/util/ByteUtils.java b/src/main/java/com/casper/sdk/service/serialization/util/ByteUtils.java index c78f7bf59..54edfc20a 100644 --- a/src/main/java/com/casper/sdk/service/serialization/util/ByteUtils.java +++ b/src/main/java/com/casper/sdk/service/serialization/util/ByteUtils.java @@ -55,4 +55,14 @@ public static byte[] lastNBytes(byte[] toTruncate, final int length) { System.arraycopy(toTruncate, start, secretBytes, 0, length); return secretBytes; } + + /** + * Converts a number to a byte value in a byte array + * + * @param toByteInArray the number to convert + * @return the byte array containing a single byte value + */ + public static byte[] toByteArray(final Number toByteInArray) { + return new byte[]{toByteInArray.byteValue()}; + } } diff --git a/src/main/java/com/casper/sdk/types/CLKeyInfo.java b/src/main/java/com/casper/sdk/types/CLKeyInfo.java index 65eaaed8f..b5ad2d2f7 100644 --- a/src/main/java/com/casper/sdk/types/CLKeyInfo.java +++ b/src/main/java/com/casper/sdk/types/CLKeyInfo.java @@ -8,23 +8,39 @@ public class CLKeyInfo extends CLTypeInfo { public enum KeyType implements HasTag { /** The Account variant */ - ACCOUNT_ID(0), + ACCOUNT_ID(0, "account-hash"), /** The Hash variant */ - HASH_ID(1), + HASH_ID(1, "hash"), /** The URef variant */ - UREF_ID(2); + UREF_ID(2, "uref"); private final int tag; + private final String parsedName; - KeyType(int tag) { + KeyType(final int tag, final String parsedName) { this.tag = tag; + this.parsedName = parsedName; + } + + public static KeyType valueOf(byte tag) { + for (KeyType keyType : KeyType.values()) { + if (tag == keyType.tag) { + return keyType; + } + } + throw new IllegalArgumentException("Invalid key type: " + tag); } @Override public int getTag() { return tag; } + + public String getParsedName() { + return parsedName; + } } + private final KeyType keyType; public CLKeyInfo(final KeyType keyType) { diff --git a/src/main/java/com/casper/sdk/types/CLKeyValue.java b/src/main/java/com/casper/sdk/types/CLKeyValue.java index 7d6f47030..31671ec0b 100644 --- a/src/main/java/com/casper/sdk/types/CLKeyValue.java +++ b/src/main/java/com/casper/sdk/types/CLKeyValue.java @@ -1,6 +1,7 @@ package com.casper.sdk.types; import com.casper.sdk.service.json.serialize.CLKeyValueJsonSerializer; +import com.casper.sdk.service.serialization.util.ByteUtils; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** @@ -17,7 +18,26 @@ public CLKeyValue(final byte[] bytes, final CLKeyInfo.KeyType keyType, final Obj super(bytes, new CLKeyInfo(keyType), parsed); } + /** + * Obtains the bytes including the key type prefix byte + * + * @return the bytes + */ + @Override + public byte[] getBytes() { + return ByteUtils.concat(ByteUtils.toByteArray(getKeyType().getTag()), super.getBytes()); + } + + /** + * Obtains the bytes without the key type prefix + * + * @return the original key bytes without the key type prefix byte + */ + public byte[] getKeyBytes() { + return super.getBytes(); + } + public CLKeyInfo.KeyType getKeyType() { - return ((CLKeyInfo)getCLTypeInfo()).getKeyType(); + return ((CLKeyInfo) getCLTypeInfo()).getKeyType(); } } diff --git a/src/test/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializerTest.java b/src/test/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializerTest.java index 508026d4d..c9c48a067 100644 --- a/src/test/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializerTest.java +++ b/src/test/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializerTest.java @@ -36,7 +36,7 @@ void clAccountKeyToJson() throws IOException { assertThat(json, hasJsonPath("$.cl_type", is("Key"))); assertThat(json, hasJsonPath("$.bytes", is("00" + KEY_HEX))); - assertThat(json, hasJsonPath("$.parsed.Hash", is("hash-" + KEY_HEX))); + assertThat(json, hasJsonPath("$.parsed.Hash", is("account-hash-" + KEY_HEX))); } /** @@ -76,6 +76,6 @@ void clURefKeyToJson() throws IOException { assertThat(json, hasJsonPath("$.cl_type", is("Key"))); assertThat(json, hasJsonPath("$.bytes", is("02" + keyHex))); - assertThat(json, hasJsonPath("$.parsed.Hash", is("hash-" + keyHex))); + assertThat(json, hasJsonPath("$.parsed.Hash", is("uref-" + keyHex))); } } diff --git a/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java b/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java index c1ec7a8e1..7d3d59e93 100644 --- a/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java +++ b/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java @@ -1,7 +1,8 @@ package com.casper.sdk.service.serialization.types; -import com.casper.sdk.types.*; +import com.casper.sdk.service.serialization.cltypes.CLValueBuilder; import com.casper.sdk.service.serialization.cltypes.TypesFactory; +import com.casper.sdk.types.*; import org.junit.jupiter.api.Test; import static com.casper.sdk.service.serialization.util.ByteUtils.concat; @@ -90,6 +91,29 @@ void byteOptionArrayKeyValue() { }; assertThat(byteSerializer.toBytes(optionValue), is(expected)); + } + + + @Test + void keyValueToBytes() { + + // Create key value from bytes prefixed with the key type + final CLKeyValue clKeyValue = CLValueBuilder.key(decodeHex("012b177f0739348d33ce868b2f95bb83decf5b5dcc71279d4bec64c87f60b805d5")); + + // Assert they key type is a has + assertThat(clKeyValue.getKeyType(), is(CLKeyInfo.KeyType.HASH_ID)); + + assertThat(clKeyValue.getBytes().length, is(33)); + assertThat(clKeyValue.getBytes(), is(decodeHex("012b177f0739348d33ce868b2f95bb83decf5b5dcc71279d4bec64c87f60b805d5"))); + + assertThat(clKeyValue.getKeyBytes().length, is(32)); + assertThat(clKeyValue.getKeyBytes(), is(decodeHex("2b177f0739348d33ce868b2f95bb83decf5b5dcc71279d4bec64c87f60b805d5"))); + + // Assert that the key can be correctly serialized to bytes + final byte[] expected = decodeHex("21" + // Length of key with prefix = 33 (0x21) + "000000012b177f0739348d33ce868b2f95bb83decf5b5dcc71279d4bec64c87f60b805d5" + + "0B"); // CL Type == 11 (0x0B) + assertThat(byteSerializer.toBytes(clKeyValue), is(expected)); } } \ No newline at end of file From 3295fa8691a9e06f160b27964b1afecd9b022671 Mon Sep 17 00:00:00 2001 From: imills Date: Tue, 28 Sep 2021 12:49:05 +0100 Subject: [PATCH 2/2] issues/53 - Implement byte serialization for CLKeyValue --- .../sdk/service/json/serialize/CLKeyValueJsonSerializer.java | 2 +- .../service/serialization/types/CLValueByteSerializerTest.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java b/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java index c292fc5c1..6d38cb69f 100644 --- a/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java +++ b/src/main/java/com/casper/sdk/service/json/serialize/CLKeyValueJsonSerializer.java @@ -37,7 +37,7 @@ protected String getValueBytes(final CLKeyValue value) { return value.toHex(); } - private void writeParsed(CLKeyValue value, JsonGenerator gen) throws IOException { + private void writeParsed(final CLKeyValue value, final JsonGenerator gen) throws IOException { final String strParsed; diff --git a/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java b/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java index 7d3d59e93..db064c86a 100644 --- a/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java +++ b/src/test/java/com/casper/sdk/service/serialization/types/CLValueByteSerializerTest.java @@ -53,7 +53,6 @@ void u512ValueToBytes() { assertThat(byteSerializer.toBytes(source), is(expected)); } - @Test void optionValueToBytes() { @@ -116,4 +115,4 @@ void keyValueToBytes() { assertThat(byteSerializer.toBytes(clKeyValue), is(expected)); } -} \ No newline at end of file +}