From 308c69a59e01f402ca57d8a542e8dfba549d3c03 Mon Sep 17 00:00:00 2001 From: nkramer44 Date: Tue, 29 Oct 2024 15:16:23 -0400 Subject: [PATCH 1/2] add WalletLocator. make MessageKey serializable with empty string --- .../model/ledger/AccountRootObject.java | 8 +++++++ .../xrpl4j/model/transactions/AccountSet.java | 11 ++++++++-- .../binary/BinarySerializationTests.java | 18 ++++++++++----- .../json/AccountSetJsonTests.java | 22 +++++++++---------- .../org/xrpl/xrpl4j/tests/AccountSetIT.java | 15 ++++++++++++- 5 files changed, 53 insertions(+), 21 deletions(-) diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AccountRootObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AccountRootObject.java index 3639c6562..97b7d9c5e 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AccountRootObject.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AccountRootObject.java @@ -239,6 +239,14 @@ default LedgerEntryType ledgerEntryType() { @JsonProperty("AMMID") Optional ammId(); + /** + * An arbitrary 256-bit value that users can set. + * + * @return An {@link Optional} {@link String}. + */ + @JsonProperty("WalletLocator") + Optional walletLocator(); + /** * The unique ID of this {@link AccountRootObject} ledger object. * diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java index 800ee3e78..f9dc56b18 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java @@ -281,6 +281,7 @@ default AccountSet normalizeSetFlag() { * @return An {@link Optional} of type {@link String} containing the messaging public key. */ @JsonProperty("MessageKey") + @JsonInclude(Include.NON_ABSENT) Optional messageKey(); /** @@ -310,6 +311,12 @@ default AccountSet normalizeSetFlag() { @JsonProperty("NFTokenMinter") Optional
mintAccount(); + @JsonProperty("WalletLocator") + Optional walletLocator(); + + @JsonProperty("WalletSize") + Optional walletSize(); + /** * Check email hash length. */ @@ -318,8 +325,8 @@ default void checkEmailHashLength() { emailHash() .ifPresent(hash -> Preconditions.checkArgument( - hash.length() == 32, - String.format("emailHash must be 32 characters (128 bits), but was %s characters long.", hash.length()) + hash.isEmpty() || hash.length() == 32, + String.format("emailHash must be 0 or 32 characters (128 bits), but was %s characters long.", hash.length()) ) ); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java index 7e9ebd316..298e019a6 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java @@ -137,32 +137,38 @@ public void serializeAccountSetTransactionWithNetworkId() throws JsonProcessingE } @Test - public void serializeAccountSetTransactionWithDomain() throws JsonProcessingException { + public void serializeAccountSetTransactionWithPresentOptionalStringFields() throws JsonProcessingException { AccountSet accountSet = AccountSet.builder() .account(Address.of("rpP2GdsQwenNnFPefbXFgiTvEgJWQpq8Rw")) .fee(XrpCurrencyAmount.ofDrops(10)) .sequence(UnsignedInteger.valueOf(10598)) .networkId(NetworkId.of(UnsignedInteger.MAX_VALUE)) .domain("ABCD") + .messageKey("03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB") + .emailHash("F9879D71855B5FF21E4963273A886BFC") + .walletLocator("F9879D71855B5FF21E4963273A886BFCF9879D71855B5FF21E4963273A886BFC") .build(); - String expectedBinary = "12000321FFFFFFFF240000296668400000000000000A73007702ABCD81140F3D0C7D2CFAB2EC8" + - "295451F0B3CA038E8E9CDCD"; + String expectedBinary = "12000321FFFFFFFF240000296641F9879D71855B5FF21E4963273A886BFC57F98" + + "79D71855B5FF21E4963273A886BFCF9879D71855B5FF21E4963273A886BFC68400000000000000A722103AB" + + "40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB73007702ABCD81140F3D0C7D2" + + "CFAB2EC8295451F0B3CA038E8E9CDCD"; assertSerializesAndDeserializes(accountSet, expectedBinary); } @Test - public void serializeAccountSetTransactionWithEmptyStringDomain() throws JsonProcessingException { + public void serializeAccountSetTransactionWithEmptyStrings() throws JsonProcessingException { AccountSet accountSet = AccountSet.builder() .account(Address.of("rpP2GdsQwenNnFPefbXFgiTvEgJWQpq8Rw")) .fee(XrpCurrencyAmount.ofDrops(10)) .sequence(UnsignedInteger.valueOf(10598)) .networkId(NetworkId.of(UnsignedInteger.MAX_VALUE)) .domain("") + .messageKey("") .build(); - String expectedBinary = "12000321FFFFFFFF240000296668400000000000000A7300770081140F3D0C7D2CFAB2EC829545" + - "1F0B3CA038E8E9CDCD"; + String expectedBinary = "12000321FFFFFFFF240000296668400000000000000A72007300" + + "770081140F3D0C7D2CFAB2EC8295451F0B3CA038E8E9CDCD"; assertSerializesAndDeserializes(accountSet, expectedBinary); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java index 9487cc2fd..9ef3c5018 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java @@ -57,6 +57,8 @@ public void fullyPopulatedAccountSet() throws JSONException, JsonProcessingExcep .flags(AccountSetTransactionFlags.of(TransactionFlags.FULLY_CANONICAL_SIG.getValue())) .mintAccount(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) .networkId(NetworkId.of(1024)) + .walletLocator("ABCD") + .walletSize(UnsignedInteger.ONE) .build(); String json = "{\n" + @@ -74,6 +76,8 @@ public void fullyPopulatedAccountSet() throws JSONException, JsonProcessingExcep " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" + " \"NFTokenMinter\" : \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + " \"NetworkID\": 1024,\n" + + " \"WalletSize\": 1,\n" + + " \"WalletLocator\": \"ABCD\",\n" + " \"EmailHash\":\"f9879d71855b5ff21e4963273a886bfc\"\n" + "}"; @@ -81,17 +85,15 @@ public void fullyPopulatedAccountSet() throws JSONException, JsonProcessingExcep } @Test - public void accountSetWithEmptyDomain() throws JSONException, JsonProcessingException { + public void accountSetWithEmptyOptionalStringFields() throws JSONException, JsonProcessingException { AccountSet accountSet = AccountSet.builder() .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) .fee(XrpCurrencyAmount.ofDrops(12)) .sequence(UnsignedInteger.valueOf(5)) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) - .messageKey("03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB") .transferRate(UnsignedInteger.valueOf(1000000001)) .tickSize(UnsignedInteger.valueOf(15)) .clearFlag(AccountSetFlag.DEFAULT_RIPPLE) - .emailHash("f9879d71855b5ff21e4963273a886bfc") .signingPublicKey( PublicKey.fromBase16EncodedPublicKey("02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC") ) @@ -107,32 +109,29 @@ public void accountSetWithEmptyDomain() throws JSONException, JsonProcessingExce " \"Sequence\":5,\n" + " \"Flags\":2147483648,\n" + " \"SetFlag\":5,\n" + - " \"MessageKey\":\"03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB\",\n" + " \"TransferRate\":1000000001,\n" + " \"TickSize\":15,\n" + " \"ClearFlag\":8,\n" + " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" + " \"NFTokenMinter\" : \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"NetworkID\": 1024,\n" + - " \"EmailHash\":\"f9879d71855b5ff21e4963273a886bfc\"\n" + + " \"NetworkID\": 1024\n" + "}"; assertCanSerializeAndDeserialize(accountSet, json); } @Test - public void accountSetWithEmptyStringDomain() throws JSONException, JsonProcessingException { + public void accountSetWithEmptyStringFields() throws JSONException, JsonProcessingException { AccountSet accountSet = AccountSet.builder() .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) .fee(XrpCurrencyAmount.ofDrops(12)) .domain("") + .messageKey("") .sequence(UnsignedInteger.valueOf(5)) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) - .messageKey("03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB") .transferRate(UnsignedInteger.valueOf(1000000001)) .tickSize(UnsignedInteger.valueOf(15)) .clearFlag(AccountSetFlag.DEFAULT_RIPPLE) - .emailHash("f9879d71855b5ff21e4963273a886bfc") .signingPublicKey( PublicKey.fromBase16EncodedPublicKey("02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC") ) @@ -149,14 +148,13 @@ public void accountSetWithEmptyStringDomain() throws JSONException, JsonProcessi " \"Flags\":2147483648,\n" + " \"Domain\":\"\",\n" + " \"SetFlag\":5,\n" + - " \"MessageKey\":\"03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB\",\n" + + " \"MessageKey\":\"\",\n" + " \"TransferRate\":1000000001,\n" + " \"TickSize\":15,\n" + " \"ClearFlag\":8,\n" + " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" + " \"NFTokenMinter\" : \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"NetworkID\": 1024,\n" + - " \"EmailHash\":\"f9879d71855b5ff21e4963273a886bfc\"\n" + + " \"NetworkID\": 1024\n" + "}"; assertCanSerializeAndDeserialize(accountSet, json); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java index 582e3892f..4ff0d5afb 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java @@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.base.Strings; import com.google.common.primitives.UnsignedInteger; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; @@ -393,7 +394,7 @@ void submitAndRetrieveAccountSetWithZeroClearFlagAndSetFlag() } @Test - void setAndUnsetDomain() throws JsonRpcClientErrorException, JsonProcessingException { + void setAndUnsetDomainAndMessageKey() throws JsonRpcClientErrorException, JsonProcessingException { KeyPair keyPair = constructRandomAccount(); /////////////////////// @@ -409,6 +410,9 @@ void setAndUnsetDomain() throws JsonRpcClientErrorException, JsonProcessingExcep .sequence(accountInfo.accountData().sequence()) .signingPublicKey(keyPair.publicKey()) .domain("ABCD") + .messageKey("03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB") + .emailHash("F9879D71855B5FF21E4963273A886BFC") + .walletLocator("F9879D71855B5FF21E4963273A886BFCF9879D71855B5FF21E4963273A886BFC") .build(); SingleSignedTransaction signedSetDomain = signatureService.sign( @@ -429,6 +433,9 @@ void setAndUnsetDomain() throws JsonRpcClientErrorException, JsonProcessingExcep () -> this.getValidatedAccountInfo(keyPair.publicKey().deriveAddress()) ); assertThat(accountInfo.accountData().domain()).isNotEmpty().isEqualTo(setDomain.domain()); + assertThat(accountInfo.accountData().messageKey()).isNotEmpty().isEqualTo(setDomain.messageKey()); + assertThat(accountInfo.accountData().emailHash()).isNotEmpty().isEqualTo(setDomain.emailHash()); + assertThat(accountInfo.accountData().walletLocator()).isNotEmpty().isEqualTo(setDomain.walletLocator()); AccountSet clearDomain = AccountSet.builder() .account(keyPair.publicKey().deriveAddress()) @@ -436,6 +443,9 @@ void setAndUnsetDomain() throws JsonRpcClientErrorException, JsonProcessingExcep .sequence(accountInfo.accountData().sequence()) .signingPublicKey(keyPair.publicKey()) .domain("") + .messageKey("") + .emailHash(Strings.repeat("0", 32)) + .walletLocator(Strings.repeat("0", 64)) .build(); SingleSignedTransaction signedClearDomain = signatureService.sign( @@ -457,6 +467,9 @@ void setAndUnsetDomain() throws JsonRpcClientErrorException, JsonProcessingExcep () -> this.getValidatedAccountInfo(keyPair.publicKey().deriveAddress()) ); assertThat(accountInfo.accountData().domain()).isEmpty(); + assertThat(accountInfo.accountData().messageKey()).isEmpty(); + assertThat(accountInfo.accountData().emailHash()).isEmpty(); + assertThat(accountInfo.accountData().walletLocator()).isEmpty(); } ////////////////////// From 7690e6f427ab9028d5c88da79f2a8df0dbf2510e Mon Sep 17 00:00:00 2001 From: nkramer44 Date: Tue, 29 Oct 2024 17:22:23 -0400 Subject: [PATCH 2/2] PR comments --- .../xrpl4j/model/transactions/AccountSet.java | 15 +++++++++++++-- .../transactions/json/AccountSetJsonTests.java | 9 +++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java index f9dc56b18..8814086c5 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AccountSet.java @@ -311,9 +311,20 @@ default AccountSet normalizeSetFlag() { @JsonProperty("NFTokenMinter") Optional
mintAccount(); + /** + * An arbitrary 256-bit value. If specified, the value is stored as part of the account but has no inherent meaning + * or requirements. + * + * @return The 256-bit value as a hex encoded {@link String}. + */ @JsonProperty("WalletLocator") Optional walletLocator(); + /** + * Not used. This field is valid in AccountSet transactions but does nothing. + * + * @return An optionally present {@link UnsignedInteger}. + */ @JsonProperty("WalletSize") Optional walletSize(); @@ -325,8 +336,8 @@ default void checkEmailHashLength() { emailHash() .ifPresent(hash -> Preconditions.checkArgument( - hash.isEmpty() || hash.length() == 32, - String.format("emailHash must be 0 or 32 characters (128 bits), but was %s characters long.", hash.length()) + hash.length() == 32, + String.format("emailHash must be 32 characters (128 bits), but was %s characters long.", hash.length()) ) ); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java index 9ef3c5018..4f38e2afc 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/json/AccountSetJsonTests.java @@ -21,6 +21,7 @@ */ import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.base.Strings; import com.google.common.primitives.UnsignedInteger; import org.json.JSONException; import org.junit.jupiter.api.Test; @@ -127,6 +128,8 @@ public void accountSetWithEmptyStringFields() throws JSONException, JsonProcessi .fee(XrpCurrencyAmount.ofDrops(12)) .domain("") .messageKey("") + .emailHash(Strings.repeat("0", 32)) + .walletLocator(Strings.repeat("0", 64)) .sequence(UnsignedInteger.valueOf(5)) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) .transferRate(UnsignedInteger.valueOf(1000000001)) @@ -154,6 +157,8 @@ public void accountSetWithEmptyStringFields() throws JSONException, JsonProcessi " \"ClearFlag\":8,\n" + " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" + " \"NFTokenMinter\" : \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"WalletLocator\" : \"" + Strings.repeat("0", 64) + "\",\n" + + " \"EmailHash\" : \"" + Strings.repeat("0", 32) + "\",\n" + " \"NetworkID\": 1024\n" + "}"; @@ -172,7 +177,7 @@ public void testJsonWithUnsetFlags() throws JsonProcessingException, JSONExcepti .transferRate(UnsignedInteger.valueOf(1000000001)) .tickSize(UnsignedInteger.valueOf(15)) .clearFlag(AccountSetFlag.DEFAULT_RIPPLE) - .emailHash("f9879d71855b5ff21e4963273a886bfc") + .emailHash(Strings.repeat("0", 32)) .signingPublicKey( PublicKey.fromBase16EncodedPublicKey("02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC") ) @@ -194,7 +199,7 @@ public void testJsonWithUnsetFlags() throws JsonProcessingException, JSONExcepti " \"ClearFlag\":8,\n" + " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" + " \"NFTokenMinter\" : \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"EmailHash\":\"f9879d71855b5ff21e4963273a886bfc\"\n" + + " \"EmailHash\":\"" + Strings.repeat("0", 32) + "\"\n" + "}"; assertCanSerializeAndDeserialize(accountSet, json);