From e7a7e15025e2cd7f5110d54e4a276fa1bb53e909 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Mon, 9 Dec 2019 22:05:26 +0100 Subject: [PATCH 01/13] Use bytes to represent memo text --- src/main/java/org/stellar/sdk/Memo.java | 9 +++++++ src/main/java/org/stellar/sdk/MemoText.java | 25 ++++++++++++------- .../responses/TransactionDeserializer.java | 21 ++++++++++++---- src/main/java/org/stellar/sdk/xdr/Memo.java | 21 ++++++++++------ .../sdk/xdr/TransactionDecodeTest.java | 18 +++++++++++++ xdr/Stellar-transaction.x | 2 +- 6 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/stellar/sdk/Memo.java b/src/main/java/org/stellar/sdk/Memo.java index b0a5f9f35..bf90a8c2c 100644 --- a/src/main/java/org/stellar/sdk/Memo.java +++ b/src/main/java/org/stellar/sdk/Memo.java @@ -30,6 +30,15 @@ public static MemoText text(String text) { return new MemoText(text); } + /** + * Creates new {@link MemoText} instance. + * @param text + */ + public static MemoText text(byte[] text) { + return new MemoText(text); + } + + /** * Creates new {@link MemoId} instance. * @param id diff --git a/src/main/java/org/stellar/sdk/MemoText.java b/src/main/java/org/stellar/sdk/MemoText.java index 0dcb4b2c3..b5ddcf508 100644 --- a/src/main/java/org/stellar/sdk/MemoText.java +++ b/src/main/java/org/stellar/sdk/MemoText.java @@ -4,6 +4,7 @@ import org.stellar.sdk.xdr.MemoType; import java.nio.charset.Charset; +import java.util.Arrays; import static com.google.common.base.Preconditions.checkNotNull; @@ -11,19 +12,25 @@ * Represents MEMO_TEXT. */ public class MemoText extends Memo { - private String text; + private byte[] text; public MemoText(String text) { - this.text = checkNotNull(text, "text cannot be null"); + this(checkNotNull(text, "text cannot be null").getBytes((Charset.forName("UTF-8")))); + } - int length = text.getBytes((Charset.forName("UTF-8"))).length; - if (length > 28) { - throw new MemoTooLongException("text must be <= 28 bytes. length=" + String.valueOf(length)); + public MemoText(byte[] text) { + this.text = checkNotNull(text, "text cannot be null"); + if (this.text.length > 28) { + throw new MemoTooLongException("text must be <= 28 bytes. length=" + String.valueOf(this.text.length)); } } public String getText() { - return text; + return new String(text, Charset.forName("UTF-8")); + } + + public byte[] getBytes() { + return this.text; } @Override @@ -36,7 +43,7 @@ org.stellar.sdk.xdr.Memo toXdr() { @Override public int hashCode() { - return Objects.hashCode(this.text); + return Arrays.hashCode(this.text); } @Override @@ -44,11 +51,11 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MemoText memoText = (MemoText) o; - return Objects.equal(this.text, memoText.text); + return Arrays.equals(this.text, memoText.text); } @Override public String toString() { - return text == null ? "" : text; + return text == null ? "" : this.getText(); } } diff --git a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java index 098f07e65..2f2c81774 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java @@ -9,7 +9,11 @@ import com.google.gson.JsonParseException; import org.stellar.sdk.Memo; +import org.stellar.sdk.xdr.TransactionEnvelope; +import org.stellar.sdk.xdr.XdrDataInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.lang.reflect.Type; public class TransactionDeserializer implements JsonDeserializer { @@ -31,12 +35,19 @@ public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeser // representation of a transaction. That's why we need to handle a special case // here. if (memoType.equals("text")) { - JsonElement memoField = json.getAsJsonObject().get("memo"); - if (memoField != null) { - memo = Memo.text(memoField.getAsString()); - } else { - memo = Memo.text(""); + // we obtain the memo text from the xdr because the bytes may not be valid utf8 + String envelopeXdr = json.getAsJsonObject().get("envelope_xdr").getAsString(); + BaseEncoding base64Encoding = BaseEncoding.base64(); + byte[] bytes = base64Encoding.decode(envelopeXdr); + TransactionEnvelope transactionEnvelope = null; + try { + transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); + } catch (IOException e) { + // JsonDeserializer cannot throw IOExceptions + // so we must throw it as a runtime exception + throw new RuntimeException(e); } + memo = Memo.text(transactionEnvelope.getTx().getMemo().getText()); } else { String memoValue = json.getAsJsonObject().get("memo").getAsString(); BaseEncoding base64Encoding = BaseEncoding.base64(); diff --git a/src/main/java/org/stellar/sdk/xdr/Memo.java b/src/main/java/org/stellar/sdk/xdr/Memo.java index d7e343850..e9d334db2 100644 --- a/src/main/java/org/stellar/sdk/xdr/Memo.java +++ b/src/main/java/org/stellar/sdk/xdr/Memo.java @@ -7,6 +7,7 @@ import java.io.IOException; import com.google.common.base.Objects; +import java.util.Arrays; // === xdr source ============================================================ @@ -15,7 +16,7 @@ // case MEMO_NONE: // void; // case MEMO_TEXT: -// string text<28>; +// opaque text<28>; // case MEMO_ID: // uint64 id; // case MEMO_HASH: @@ -34,11 +35,11 @@ public MemoType getDiscriminant() { public void setDiscriminant(MemoType value) { this.type = value; } - private String text; - public String getText() { + private byte[] text; + public byte[] getText() { return this.text; } - public void setText(String value) { + public void setText(byte[] value) { this.text = value; } private Uint64 id; @@ -70,7 +71,9 @@ public static void encode(XdrDataOutputStream stream, Memo encodedMemo) throws I case MEMO_NONE: break; case MEMO_TEXT: - stream.writeString(encodedMemo.text); + int textsize = encodedMemo.text.length; + stream.writeInt(textsize); + stream.write(encodedMemo.getText(), 0, textsize); break; case MEMO_ID: Uint64.encode(stream, encodedMemo.id); @@ -94,7 +97,9 @@ public static Memo decode(XdrDataInputStream stream) throws IOException { case MEMO_NONE: break; case MEMO_TEXT: - decodedMemo.text = stream.readString(); + int textsize = stream.readInt(); + decodedMemo.text = new byte[textsize]; + stream.read(decodedMemo.text, 0, textsize); break; case MEMO_ID: decodedMemo.id = Uint64.decode(stream); @@ -110,7 +115,7 @@ public static Memo decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.text, this.id, this.hash, this.retHash, this.type); + return Objects.hashCode(Arrays.hashCode(this.text), this.id, this.hash, this.retHash, this.type); } @Override public boolean equals(Object object) { @@ -119,6 +124,6 @@ public boolean equals(Object object) { } Memo other = (Memo) object; - return Objects.equal(this.text, other.text) && Objects.equal(this.id, other.id) && Objects.equal(this.hash, other.hash) && Objects.equal(this.retHash, other.retHash) && Objects.equal(this.type, other.type); + return Arrays.equals(this.text, other.text) && Objects.equal(this.id, other.id) && Objects.equal(this.hash, other.hash) && Objects.equal(this.retHash, other.retHash) && Objects.equal(this.type, other.type); } } diff --git a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java index 6eab65c87..f0d33cf02 100644 --- a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java @@ -2,8 +2,11 @@ import com.google.common.io.BaseEncoding; import org.junit.Test; +import org.stellar.sdk.Memo; +import org.stellar.sdk.MemoText; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; @@ -59,4 +62,19 @@ public void testTransactionEnvelopeWithMemo() throws IOException { assertTrue(Arrays.equals(new byte[]{'G', 'O', 'L', 'D'}, transactionEnvelope.getTx().getOperations()[0].getBody().getPaymentOp().getAsset().getAlphaNum4().getAssetCode().getAssetCode4())); } + @Test + public void testRoundtrip() throws IOException { + String txBody = "AAAAAM6jLgjKjuXxWkir4M7v0NqoOfODXcFnn6AGlP+d4RxAAAAAZAAIiE4AAAABAAAAAAAAAAEAAAAcyKMl+WDSzuttWkF2DvzKAkkEqeSZ4cZihjGJEAAAAAEAAAAAAAAAAQAAAAAgECmBaDwiRPE1z2vAE36J+45toU/ZxdvpR38tc0HvmgAAAAAAAAAAAJiWgAAAAAAAAAABneEcQAAAAECeXDKebJoAbST1T2AbDBui9K0TbSM8sfbhXUAZ2ROAoCRs5cG1pRvY+ityyPWFEKPd7+3qEupavkAZ/+L7/28G"; + BaseEncoding base64Encoding = BaseEncoding.base64(); + byte[] bytes = base64Encoding.decode(txBody); + + TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + + transactionEnvelope.encode(new XdrDataOutputStream(byteOutputStream)); + String serialized = base64Encoding.encode(byteOutputStream.toByteArray()); + // fails here + assertEquals(serialized, txBody); + } + } diff --git a/xdr/Stellar-transaction.x b/xdr/Stellar-transaction.x index 74d7a9baf..1894fb3aa 100644 --- a/xdr/Stellar-transaction.x +++ b/xdr/Stellar-transaction.x @@ -335,7 +335,7 @@ union Memo switch (MemoType type) case MEMO_NONE: void; case MEMO_TEXT: - string text<28>; + opaque text<28>; case MEMO_ID: uint64 id; case MEMO_HASH: From 4c75bbd34470ebac074614012fb6d644e295d500 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Mon, 9 Dec 2019 22:09:55 +0100 Subject: [PATCH 02/13] Update changelog and bump version. --- CHANGELOG.md | 4 ++++ build.gradle | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e1621fa7..965a33327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps. A breaking change will get clearly notified in this log. +## 0.12.0 + +* Represent memo text contents as bytes because a memo text may not be valid UTF-8 string. + ## 0.11.0 * Fix bug in `org.stellar.sdk.requests.OperationsRequestBuilder.operation(long operationId)`. The method submitted an HTTP request to Horizon with the following path, /operation/ , but the correct path is /operations/ diff --git a/build.gradle b/build.gradle index 68fe7db9d..48a73eb62 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ apply plugin: 'com.github.ben-manes.versions' // gradle dependencyUpdates -Drevi apply plugin: 'project-report' // gradle htmlDependencyReport sourceCompatibility = 1.6 -version = '0.11.0' +version = '0.12.0' group = 'stellar' jar { From 9739b4bd877e4c72809d1f7da7e56a250c0cd4d1 Mon Sep 17 00:00:00 2001 From: tamirms Date: Mon, 9 Dec 2019 22:49:08 +0100 Subject: [PATCH 03/13] Update src/main/java/org/stellar/sdk/MemoText.java Co-Authored-By: Eric Saunders --- src/main/java/org/stellar/sdk/MemoText.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/stellar/sdk/MemoText.java b/src/main/java/org/stellar/sdk/MemoText.java index b5ddcf508..be214dfa7 100644 --- a/src/main/java/org/stellar/sdk/MemoText.java +++ b/src/main/java/org/stellar/sdk/MemoText.java @@ -26,7 +26,7 @@ public MemoText(byte[] text) { } public String getText() { - return new String(text, Charset.forName("UTF-8")); + return new String(this.text, Charset.forName("UTF-8")); } public byte[] getBytes() { From 8935a894a6aa30bdaa13e4e0366b8f7c6a0e83a0 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Mon, 9 Dec 2019 23:00:20 +0100 Subject: [PATCH 04/13] include link to github issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 965a33327..73626fa06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps. ## 0.12.0 -* Represent memo text contents as bytes because a memo text may not be valid UTF-8 string. +* Represent memo text contents as bytes because a memo text may not be valid UTF-8 string (https://github.com/stellar/java-stellar-sdk/issues/257). ## 0.11.0 From 26d3977d0b3d364f17b1965cfbb7b2e360f3b165 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Tue, 10 Dec 2019 12:31:38 +0100 Subject: [PATCH 05/13] Remove comment --- src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java index f0d33cf02..dc7b19644 100644 --- a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java @@ -73,7 +73,6 @@ public void testRoundtrip() throws IOException { transactionEnvelope.encode(new XdrDataOutputStream(byteOutputStream)); String serialized = base64Encoding.encode(byteOutputStream.toByteArray()); - // fails here assertEquals(serialized, txBody); } From bc0c06eca951634e5c6e7601f041ffb89db1c701 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Tue, 10 Dec 2019 23:44:48 +0100 Subject: [PATCH 06/13] Regenerate code using xdrgen with fix --- .../org/stellar/sdk/ManageDataOperation.java | 6 ++++-- src/main/java/org/stellar/sdk/MemoText.java | 2 +- .../org/stellar/sdk/SetOptionsOperation.java | 6 ++++-- src/main/java/org/stellar/sdk/xdr/Error.java | 19 +++++++++++------- src/main/java/org/stellar/sdk/xdr/Hello.java | 19 +++++++++++------- src/main/java/org/stellar/sdk/xdr/Memo.java | 2 +- .../java/org/stellar/sdk/xdr/String32.java | 20 +++++++++++-------- .../java/org/stellar/sdk/xdr/String64.java | 20 +++++++++++-------- xdr/Stellar-transaction.x | 2 +- 9 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/stellar/sdk/ManageDataOperation.java b/src/main/java/org/stellar/sdk/ManageDataOperation.java index 38e5fb5be..cc4ed2052 100644 --- a/src/main/java/org/stellar/sdk/ManageDataOperation.java +++ b/src/main/java/org/stellar/sdk/ManageDataOperation.java @@ -6,6 +6,8 @@ import org.stellar.sdk.xdr.OperationType; import org.stellar.sdk.xdr.String64; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.Arrays; import static com.google.common.base.Preconditions.checkNotNull; @@ -41,7 +43,7 @@ public byte[] getValue() { org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { ManageDataOp op = new ManageDataOp(); String64 name = new String64(); - name.setString64(this.name); + name.setString64(this.name.getBytes(Charset.forName("UTF-8"))); op.setDataName(name); if (value != null) { @@ -68,7 +70,7 @@ public static class Builder { * @param op {@link ManageDataOp} */ Builder(ManageDataOp op) { - name = op.getDataName().getString64(); + name = new String(op.getDataName().getString64(), Charset.forName("UTF-8")); if (op.getDataValue() != null) { value = op.getDataValue().getDataValue(); } else { diff --git a/src/main/java/org/stellar/sdk/MemoText.java b/src/main/java/org/stellar/sdk/MemoText.java index be214dfa7..8f20d9f03 100644 --- a/src/main/java/org/stellar/sdk/MemoText.java +++ b/src/main/java/org/stellar/sdk/MemoText.java @@ -15,7 +15,7 @@ public class MemoText extends Memo { private byte[] text; public MemoText(String text) { - this(checkNotNull(text, "text cannot be null").getBytes((Charset.forName("UTF-8")))); + this(checkNotNull(text, "text cannot be null").getBytes(Charset.forName("UTF-8"))); } public MemoText(byte[] text) { diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index ba2574305..87ab45ba0 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -4,6 +4,8 @@ import org.stellar.sdk.xdr.*; +import java.nio.charset.Charset; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -148,7 +150,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { } if (homeDomain != null) { String32 homeDomain = new String32(); - homeDomain.setString32(this.homeDomain); + homeDomain.setString32(this.homeDomain.getBytes(Charset.forName("UTF-8"))); op.setHomeDomain(homeDomain); } if (signer != null) { @@ -206,7 +208,7 @@ public static class Builder { highThreshold = op.getHighThreshold().getUint32().intValue(); } if (op.getHomeDomain() != null) { - homeDomain = op.getHomeDomain().getString32(); + homeDomain = new String(op.getHomeDomain().getString32(), Charset.forName("UTF-8")); } if (op.getSigner() != null) { signer = op.getSigner().getKey(); diff --git a/src/main/java/org/stellar/sdk/xdr/Error.java b/src/main/java/org/stellar/sdk/xdr/Error.java index 7d3fc80d9..84c117815 100644 --- a/src/main/java/org/stellar/sdk/xdr/Error.java +++ b/src/main/java/org/stellar/sdk/xdr/Error.java @@ -7,6 +7,7 @@ import java.io.IOException; import com.google.common.base.Objects; +import java.util.Arrays; // === xdr source ============================================================ @@ -26,16 +27,18 @@ public ErrorCode getCode() { public void setCode(ErrorCode value) { this.code = value; } - private String msg; - public String getMsg() { + private byte[] msg; + public byte[] getMsg() { return this.msg; } - public void setMsg(String value) { + public void setMsg(byte[] value) { this.msg = value; } public static void encode(XdrDataOutputStream stream, Error encodedError) throws IOException{ ErrorCode.encode(stream, encodedError.code); - stream.writeString(encodedError.msg); + int msgsize = encodedError.msg.length; + stream.writeInt(msgsize); + stream.write(encodedError.getMsg(), 0, msgsize); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); @@ -43,12 +46,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { public static Error decode(XdrDataInputStream stream) throws IOException { Error decodedError = new Error(); decodedError.code = ErrorCode.decode(stream); - decodedError.msg = stream.readString(); + int msgsize = stream.readInt(); + decodedError.msg = new byte[msgsize]; + stream.read(decodedError.msg, 0, msgsize); return decodedError; } @Override public int hashCode() { - return Objects.hashCode(this.code, this.msg); + return Objects.hashCode(this.code, Arrays.hashCode(this.msg)); } @Override public boolean equals(Object object) { @@ -57,6 +62,6 @@ public boolean equals(Object object) { } Error other = (Error) object; - return Objects.equal(this.code, other.code) && Objects.equal(this.msg, other.msg); + return Objects.equal(this.code, other.code) && Arrays.equals(this.msg, other.msg); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Hello.java b/src/main/java/org/stellar/sdk/xdr/Hello.java index 4a5b59db5..b4c9cf942 100644 --- a/src/main/java/org/stellar/sdk/xdr/Hello.java +++ b/src/main/java/org/stellar/sdk/xdr/Hello.java @@ -7,6 +7,7 @@ import java.io.IOException; import com.google.common.base.Objects; +import java.util.Arrays; // === xdr source ============================================================ @@ -54,11 +55,11 @@ public Hash getNetworkID() { public void setNetworkID(Hash value) { this.networkID = value; } - private String versionStr; - public String getVersionStr() { + private byte[] versionStr; + public byte[] getVersionStr() { return this.versionStr; } - public void setVersionStr(String value) { + public void setVersionStr(byte[] value) { this.versionStr = value; } private Integer listeningPort; @@ -94,7 +95,9 @@ public static void encode(XdrDataOutputStream stream, Hello encodedHello) throws Uint32.encode(stream, encodedHello.overlayVersion); Uint32.encode(stream, encodedHello.overlayMinVersion); Hash.encode(stream, encodedHello.networkID); - stream.writeString(encodedHello.versionStr); + int versionStrsize = encodedHello.versionStr.length; + stream.writeInt(versionStrsize); + stream.write(encodedHello.getVersionStr(), 0, versionStrsize); stream.writeInt(encodedHello.listeningPort); NodeID.encode(stream, encodedHello.peerID); AuthCert.encode(stream, encodedHello.cert); @@ -109,7 +112,9 @@ public static Hello decode(XdrDataInputStream stream) throws IOException { decodedHello.overlayVersion = Uint32.decode(stream); decodedHello.overlayMinVersion = Uint32.decode(stream); decodedHello.networkID = Hash.decode(stream); - decodedHello.versionStr = stream.readString(); + int versionStrsize = stream.readInt(); + decodedHello.versionStr = new byte[versionStrsize]; + stream.read(decodedHello.versionStr, 0, versionStrsize); decodedHello.listeningPort = stream.readInt(); decodedHello.peerID = NodeID.decode(stream); decodedHello.cert = AuthCert.decode(stream); @@ -118,7 +123,7 @@ public static Hello decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.ledgerVersion, this.overlayVersion, this.overlayMinVersion, this.networkID, this.versionStr, this.listeningPort, this.peerID, this.cert, this.nonce); + return Objects.hashCode(this.ledgerVersion, this.overlayVersion, this.overlayMinVersion, this.networkID, Arrays.hashCode(this.versionStr), this.listeningPort, this.peerID, this.cert, this.nonce); } @Override public boolean equals(Object object) { @@ -127,6 +132,6 @@ public boolean equals(Object object) { } Hello other = (Hello) object; - return Objects.equal(this.ledgerVersion, other.ledgerVersion) && Objects.equal(this.overlayVersion, other.overlayVersion) && Objects.equal(this.overlayMinVersion, other.overlayMinVersion) && Objects.equal(this.networkID, other.networkID) && Objects.equal(this.versionStr, other.versionStr) && Objects.equal(this.listeningPort, other.listeningPort) && Objects.equal(this.peerID, other.peerID) && Objects.equal(this.cert, other.cert) && Objects.equal(this.nonce, other.nonce); + return Objects.equal(this.ledgerVersion, other.ledgerVersion) && Objects.equal(this.overlayVersion, other.overlayVersion) && Objects.equal(this.overlayMinVersion, other.overlayMinVersion) && Objects.equal(this.networkID, other.networkID) && Arrays.equals(this.versionStr, other.versionStr) && Objects.equal(this.listeningPort, other.listeningPort) && Objects.equal(this.peerID, other.peerID) && Objects.equal(this.cert, other.cert) && Objects.equal(this.nonce, other.nonce); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Memo.java b/src/main/java/org/stellar/sdk/xdr/Memo.java index e9d334db2..d1075ea68 100644 --- a/src/main/java/org/stellar/sdk/xdr/Memo.java +++ b/src/main/java/org/stellar/sdk/xdr/Memo.java @@ -16,7 +16,7 @@ // case MEMO_NONE: // void; // case MEMO_TEXT: -// opaque text<28>; +// string text<28>; // case MEMO_ID: // uint64 id; // case MEMO_HASH: diff --git a/src/main/java/org/stellar/sdk/xdr/String32.java b/src/main/java/org/stellar/sdk/xdr/String32.java index 3b16cf7df..3c2463cdd 100644 --- a/src/main/java/org/stellar/sdk/xdr/String32.java +++ b/src/main/java/org/stellar/sdk/xdr/String32.java @@ -6,7 +6,7 @@ import java.io.IOException; -import com.google.common.base.Objects; +import java.util.Arrays; // === xdr source ============================================================ @@ -14,27 +14,31 @@ // =========================================================================== public class String32 implements XdrElement { - private String string32; - public String getString32() { + private byte[] string32; + public byte[] getString32() { return this.string32; } - public void setString32(String value) { + public void setString32(byte[] value) { this.string32 = value; } public static void encode(XdrDataOutputStream stream, String32 encodedString32) throws IOException { - stream.writeString(encodedString32.string32); + int string32size = encodedString32.string32.length; + stream.writeInt(string32size); + stream.write(encodedString32.getString32(), 0, string32size); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static String32 decode(XdrDataInputStream stream) throws IOException { String32 decodedString32 = new String32(); - decodedString32.string32 = stream.readString(); + int string32size = stream.readInt(); + decodedString32.string32 = new byte[string32size]; + stream.read(decodedString32.string32, 0, string32size); return decodedString32; } @Override public int hashCode() { - return Objects.hashCode(this.string32); + return Arrays.hashCode(this.string32); } @Override public boolean equals(Object object) { @@ -43,6 +47,6 @@ public boolean equals(Object object) { } String32 other = (String32) object; - return Objects.equal(this.string32, other.string32); + return Arrays.equals(this.string32, other.string32); } } diff --git a/src/main/java/org/stellar/sdk/xdr/String64.java b/src/main/java/org/stellar/sdk/xdr/String64.java index f5cffd245..95478bc31 100644 --- a/src/main/java/org/stellar/sdk/xdr/String64.java +++ b/src/main/java/org/stellar/sdk/xdr/String64.java @@ -6,7 +6,7 @@ import java.io.IOException; -import com.google.common.base.Objects; +import java.util.Arrays; // === xdr source ============================================================ @@ -14,27 +14,31 @@ // =========================================================================== public class String64 implements XdrElement { - private String string64; - public String getString64() { + private byte[] string64; + public byte[] getString64() { return this.string64; } - public void setString64(String value) { + public void setString64(byte[] value) { this.string64 = value; } public static void encode(XdrDataOutputStream stream, String64 encodedString64) throws IOException { - stream.writeString(encodedString64.string64); + int string64size = encodedString64.string64.length; + stream.writeInt(string64size); + stream.write(encodedString64.getString64(), 0, string64size); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static String64 decode(XdrDataInputStream stream) throws IOException { String64 decodedString64 = new String64(); - decodedString64.string64 = stream.readString(); + int string64size = stream.readInt(); + decodedString64.string64 = new byte[string64size]; + stream.read(decodedString64.string64, 0, string64size); return decodedString64; } @Override public int hashCode() { - return Objects.hashCode(this.string64); + return Arrays.hashCode(this.string64); } @Override public boolean equals(Object object) { @@ -43,6 +47,6 @@ public boolean equals(Object object) { } String64 other = (String64) object; - return Objects.equal(this.string64, other.string64); + return Arrays.equals(this.string64, other.string64); } } diff --git a/xdr/Stellar-transaction.x b/xdr/Stellar-transaction.x index 1894fb3aa..74d7a9baf 100644 --- a/xdr/Stellar-transaction.x +++ b/xdr/Stellar-transaction.x @@ -335,7 +335,7 @@ union Memo switch (MemoType type) case MEMO_NONE: void; case MEMO_TEXT: - opaque text<28>; + string text<28>; case MEMO_ID: uint64 id; case MEMO_HASH: From 45254128a7be4ffa2e74822b5ef4fb39177f83ef Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 07:39:02 +0100 Subject: [PATCH 07/13] Use updated version of xdrgen --- .../org/stellar/sdk/xdr/XdrDataInputStream.java | 7 ------- .../org/stellar/sdk/xdr/XdrDataOutputStream.java | 6 ------ .../stellar/sdk/{xdr => }/XdrDataStreamTest.java | 15 +++++++++------ 3 files changed, 9 insertions(+), 19 deletions(-) rename src/test/java/org/stellar/sdk/{xdr => }/XdrDataStreamTest.java (75%) diff --git a/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java b/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java index da75c84d9..ae317dd58 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java @@ -21,13 +21,6 @@ public XdrDataInputStream(InputStream in) { mIn = (XdrInputStream) super.in; } - public String readString() throws IOException { - int l = readInt(); - byte[] bytes = new byte[l]; - read(bytes); - return new String(bytes, Charset.forName("UTF-8")); - } - public int[] readIntArray() throws IOException { int l = readInt(); return readIntArray(l); diff --git a/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java b/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java index 672a01a76..9bb857a64 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java @@ -14,12 +14,6 @@ public XdrDataOutputStream(OutputStream out) { mOut = (XdrOutputStream) super.out; } - public void writeString(String s) throws IOException { - byte[] chars = s.getBytes(Charset.forName("UTF-8")); - writeInt(chars.length); - write(chars); - } - public void writeIntArray(int[] a) throws IOException { writeInt(a.length); writeIntArray(a, a.length); diff --git a/src/test/java/org/stellar/sdk/xdr/XdrDataStreamTest.java b/src/test/java/org/stellar/sdk/XdrDataStreamTest.java similarity index 75% rename from src/test/java/org/stellar/sdk/xdr/XdrDataStreamTest.java rename to src/test/java/org/stellar/sdk/XdrDataStreamTest.java index 748cd9e58..093f2d4a8 100644 --- a/src/test/java/org/stellar/sdk/xdr/XdrDataStreamTest.java +++ b/src/test/java/org/stellar/sdk/XdrDataStreamTest.java @@ -1,12 +1,13 @@ -package org.stellar.sdk.xdr; +package org.stellar.sdk; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import static org.junit.Assert.assertEquals; import org.junit.Test; +import org.stellar.sdk.xdr.XdrDataInputStream; +import org.stellar.sdk.xdr.XdrDataOutputStream; public class XdrDataStreamTest { @@ -16,15 +17,17 @@ public static String backAndForthXdrStreaming(String inputString) throws IOExcep //String to XDR ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(byteOutputStream); - xdrOutputStream.writeString(inputString); + + org.stellar.sdk.xdr.Memo xdrMemo = Memo.text(inputString).toXdr(); + xdrMemo.encode(xdrOutputStream); byte[] xdrByteOutput = byteOutputStream.toByteArray(); //XDR back to String XdrDataInputStream xdrInputStream = new XdrDataInputStream(new ByteArrayInputStream(xdrByteOutput)); - String outputString = xdrInputStream.readString(); + xdrMemo = org.stellar.sdk.xdr.Memo.decode(xdrInputStream); - return outputString; + return Memo.text(xdrMemo.getText()).getText(); } @@ -43,7 +46,7 @@ public void backAndForthXdrStreamingWithNonStandardAscii() throws IOException { @Test public void backAndForthXdrStreamingWithAllNonStandardAscii() throws IOException { - String memo = "øûý™€♠♣♥†‡µ¢£€"; + String memo = "øûý™€♠♣♥†‡"; assertEquals(memo, backAndForthXdrStreaming(memo)); } } From 4b57b71003d118347b5a195801bab62cfd8fde50 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 22:20:14 +0100 Subject: [PATCH 08/13] Use XdrString class --- .../org/stellar/sdk/ManageDataOperation.java | 9 +-- src/main/java/org/stellar/sdk/Memo.java | 2 +- src/main/java/org/stellar/sdk/MemoText.java | 3 +- .../org/stellar/sdk/SetOptionsOperation.java | 4 +- .../responses/TransactionDeserializer.java | 2 +- src/main/java/org/stellar/sdk/xdr/Error.java | 19 +++---- src/main/java/org/stellar/sdk/xdr/Hello.java | 19 +++---- src/main/java/org/stellar/sdk/xdr/Memo.java | 19 +++---- .../java/org/stellar/sdk/xdr/String32.java | 20 +++---- .../java/org/stellar/sdk/xdr/String64.java | 20 +++---- .../java/org/stellar/sdk/xdr/XdrString.java | 57 +++++++++++++++++++ .../org/stellar/sdk/XdrDataStreamTest.java | 3 +- .../sdk/xdr/TransactionDecodeTest.java | 2 - 13 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 src/main/java/org/stellar/sdk/xdr/XdrString.java diff --git a/src/main/java/org/stellar/sdk/ManageDataOperation.java b/src/main/java/org/stellar/sdk/ManageDataOperation.java index cc4ed2052..60dd37e64 100644 --- a/src/main/java/org/stellar/sdk/ManageDataOperation.java +++ b/src/main/java/org/stellar/sdk/ManageDataOperation.java @@ -1,10 +1,7 @@ package org.stellar.sdk; import com.google.common.base.Objects; -import org.stellar.sdk.xdr.DataValue; -import org.stellar.sdk.xdr.ManageDataOp; -import org.stellar.sdk.xdr.OperationType; -import org.stellar.sdk.xdr.String64; +import org.stellar.sdk.xdr.*; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; @@ -43,7 +40,7 @@ public byte[] getValue() { org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { ManageDataOp op = new ManageDataOp(); String64 name = new String64(); - name.setString64(this.name.getBytes(Charset.forName("UTF-8"))); + name.setString64(new XdrString(this.name)); op.setDataName(name); if (value != null) { @@ -70,7 +67,7 @@ public static class Builder { * @param op {@link ManageDataOp} */ Builder(ManageDataOp op) { - name = new String(op.getDataName().getString64(), Charset.forName("UTF-8")); + name = op.getDataName().getString64().toString(); if (op.getDataValue() != null) { value = op.getDataValue().getDataValue(); } else { diff --git a/src/main/java/org/stellar/sdk/Memo.java b/src/main/java/org/stellar/sdk/Memo.java index bf90a8c2c..6fb34f083 100644 --- a/src/main/java/org/stellar/sdk/Memo.java +++ b/src/main/java/org/stellar/sdk/Memo.java @@ -87,7 +87,7 @@ public static Memo fromXdr(org.stellar.sdk.xdr.Memo memo) { case MEMO_ID: return id(memo.getId().getUint64().longValue()); case MEMO_TEXT: - return text(memo.getText()); + return text(memo.getText().getBytes()); case MEMO_HASH: return hash(memo.getHash().getHash()); case MEMO_RETURN: diff --git a/src/main/java/org/stellar/sdk/MemoText.java b/src/main/java/org/stellar/sdk/MemoText.java index 8f20d9f03..2862be6de 100644 --- a/src/main/java/org/stellar/sdk/MemoText.java +++ b/src/main/java/org/stellar/sdk/MemoText.java @@ -2,6 +2,7 @@ import com.google.common.base.Objects; import org.stellar.sdk.xdr.MemoType; +import org.stellar.sdk.xdr.XdrString; import java.nio.charset.Charset; import java.util.Arrays; @@ -37,7 +38,7 @@ public byte[] getBytes() { org.stellar.sdk.xdr.Memo toXdr() { org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo(); memo.setDiscriminant(MemoType.MEMO_TEXT); - memo.setText(text); + memo.setText(new XdrString(text)); return memo; } diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index 87ab45ba0..8ef24b732 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -150,7 +150,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { } if (homeDomain != null) { String32 homeDomain = new String32(); - homeDomain.setString32(this.homeDomain.getBytes(Charset.forName("UTF-8"))); + homeDomain.setString32(new XdrString(this.homeDomain)); op.setHomeDomain(homeDomain); } if (signer != null) { @@ -208,7 +208,7 @@ public static class Builder { highThreshold = op.getHighThreshold().getUint32().intValue(); } if (op.getHomeDomain() != null) { - homeDomain = new String(op.getHomeDomain().getString32(), Charset.forName("UTF-8")); + homeDomain = op.getHomeDomain().getString32().toString(); } if (op.getSigner() != null) { signer = op.getSigner().getKey(); diff --git a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java index 2f2c81774..428ef7e93 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java @@ -47,7 +47,7 @@ public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeser // so we must throw it as a runtime exception throw new RuntimeException(e); } - memo = Memo.text(transactionEnvelope.getTx().getMemo().getText()); + memo = Memo.text(transactionEnvelope.getTx().getMemo().getText().getBytes()); } else { String memoValue = json.getAsJsonObject().get("memo").getAsString(); BaseEncoding base64Encoding = BaseEncoding.base64(); diff --git a/src/main/java/org/stellar/sdk/xdr/Error.java b/src/main/java/org/stellar/sdk/xdr/Error.java index 84c117815..63e80384d 100644 --- a/src/main/java/org/stellar/sdk/xdr/Error.java +++ b/src/main/java/org/stellar/sdk/xdr/Error.java @@ -7,7 +7,6 @@ import java.io.IOException; import com.google.common.base.Objects; -import java.util.Arrays; // === xdr source ============================================================ @@ -27,18 +26,16 @@ public ErrorCode getCode() { public void setCode(ErrorCode value) { this.code = value; } - private byte[] msg; - public byte[] getMsg() { + private XdrString msg; + public XdrString getMsg() { return this.msg; } - public void setMsg(byte[] value) { + public void setMsg(XdrString value) { this.msg = value; } public static void encode(XdrDataOutputStream stream, Error encodedError) throws IOException{ ErrorCode.encode(stream, encodedError.code); - int msgsize = encodedError.msg.length; - stream.writeInt(msgsize); - stream.write(encodedError.getMsg(), 0, msgsize); + encodedError.msg.encode(stream); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); @@ -46,14 +43,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { public static Error decode(XdrDataInputStream stream) throws IOException { Error decodedError = new Error(); decodedError.code = ErrorCode.decode(stream); - int msgsize = stream.readInt(); - decodedError.msg = new byte[msgsize]; - stream.read(decodedError.msg, 0, msgsize); + decodedError.msg = XdrString.decode(stream); return decodedError; } @Override public int hashCode() { - return Objects.hashCode(this.code, Arrays.hashCode(this.msg)); + return Objects.hashCode(this.code, this.msg); } @Override public boolean equals(Object object) { @@ -62,6 +57,6 @@ public boolean equals(Object object) { } Error other = (Error) object; - return Objects.equal(this.code, other.code) && Arrays.equals(this.msg, other.msg); + return Objects.equal(this.code, other.code) && Objects.equal(this.msg, other.msg); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Hello.java b/src/main/java/org/stellar/sdk/xdr/Hello.java index b4c9cf942..4f3ff2782 100644 --- a/src/main/java/org/stellar/sdk/xdr/Hello.java +++ b/src/main/java/org/stellar/sdk/xdr/Hello.java @@ -7,7 +7,6 @@ import java.io.IOException; import com.google.common.base.Objects; -import java.util.Arrays; // === xdr source ============================================================ @@ -55,11 +54,11 @@ public Hash getNetworkID() { public void setNetworkID(Hash value) { this.networkID = value; } - private byte[] versionStr; - public byte[] getVersionStr() { + private XdrString versionStr; + public XdrString getVersionStr() { return this.versionStr; } - public void setVersionStr(byte[] value) { + public void setVersionStr(XdrString value) { this.versionStr = value; } private Integer listeningPort; @@ -95,9 +94,7 @@ public static void encode(XdrDataOutputStream stream, Hello encodedHello) throws Uint32.encode(stream, encodedHello.overlayVersion); Uint32.encode(stream, encodedHello.overlayMinVersion); Hash.encode(stream, encodedHello.networkID); - int versionStrsize = encodedHello.versionStr.length; - stream.writeInt(versionStrsize); - stream.write(encodedHello.getVersionStr(), 0, versionStrsize); + encodedHello.versionStr.encode(stream); stream.writeInt(encodedHello.listeningPort); NodeID.encode(stream, encodedHello.peerID); AuthCert.encode(stream, encodedHello.cert); @@ -112,9 +109,7 @@ public static Hello decode(XdrDataInputStream stream) throws IOException { decodedHello.overlayVersion = Uint32.decode(stream); decodedHello.overlayMinVersion = Uint32.decode(stream); decodedHello.networkID = Hash.decode(stream); - int versionStrsize = stream.readInt(); - decodedHello.versionStr = new byte[versionStrsize]; - stream.read(decodedHello.versionStr, 0, versionStrsize); + decodedHello.versionStr = XdrString.decode(stream); decodedHello.listeningPort = stream.readInt(); decodedHello.peerID = NodeID.decode(stream); decodedHello.cert = AuthCert.decode(stream); @@ -123,7 +118,7 @@ public static Hello decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(this.ledgerVersion, this.overlayVersion, this.overlayMinVersion, this.networkID, Arrays.hashCode(this.versionStr), this.listeningPort, this.peerID, this.cert, this.nonce); + return Objects.hashCode(this.ledgerVersion, this.overlayVersion, this.overlayMinVersion, this.networkID, this.versionStr, this.listeningPort, this.peerID, this.cert, this.nonce); } @Override public boolean equals(Object object) { @@ -132,6 +127,6 @@ public boolean equals(Object object) { } Hello other = (Hello) object; - return Objects.equal(this.ledgerVersion, other.ledgerVersion) && Objects.equal(this.overlayVersion, other.overlayVersion) && Objects.equal(this.overlayMinVersion, other.overlayMinVersion) && Objects.equal(this.networkID, other.networkID) && Arrays.equals(this.versionStr, other.versionStr) && Objects.equal(this.listeningPort, other.listeningPort) && Objects.equal(this.peerID, other.peerID) && Objects.equal(this.cert, other.cert) && Objects.equal(this.nonce, other.nonce); + return Objects.equal(this.ledgerVersion, other.ledgerVersion) && Objects.equal(this.overlayVersion, other.overlayVersion) && Objects.equal(this.overlayMinVersion, other.overlayMinVersion) && Objects.equal(this.networkID, other.networkID) && Objects.equal(this.versionStr, other.versionStr) && Objects.equal(this.listeningPort, other.listeningPort) && Objects.equal(this.peerID, other.peerID) && Objects.equal(this.cert, other.cert) && Objects.equal(this.nonce, other.nonce); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Memo.java b/src/main/java/org/stellar/sdk/xdr/Memo.java index d1075ea68..5abc3bfef 100644 --- a/src/main/java/org/stellar/sdk/xdr/Memo.java +++ b/src/main/java/org/stellar/sdk/xdr/Memo.java @@ -7,7 +7,6 @@ import java.io.IOException; import com.google.common.base.Objects; -import java.util.Arrays; // === xdr source ============================================================ @@ -35,11 +34,11 @@ public MemoType getDiscriminant() { public void setDiscriminant(MemoType value) { this.type = value; } - private byte[] text; - public byte[] getText() { + private XdrString text; + public XdrString getText() { return this.text; } - public void setText(byte[] value) { + public void setText(XdrString value) { this.text = value; } private Uint64 id; @@ -71,9 +70,7 @@ public static void encode(XdrDataOutputStream stream, Memo encodedMemo) throws I case MEMO_NONE: break; case MEMO_TEXT: - int textsize = encodedMemo.text.length; - stream.writeInt(textsize); - stream.write(encodedMemo.getText(), 0, textsize); + encodedMemo.text.encode(stream); break; case MEMO_ID: Uint64.encode(stream, encodedMemo.id); @@ -97,9 +94,7 @@ public static Memo decode(XdrDataInputStream stream) throws IOException { case MEMO_NONE: break; case MEMO_TEXT: - int textsize = stream.readInt(); - decodedMemo.text = new byte[textsize]; - stream.read(decodedMemo.text, 0, textsize); + decodedMemo.text = XdrString.decode(stream); break; case MEMO_ID: decodedMemo.id = Uint64.decode(stream); @@ -115,7 +110,7 @@ public static Memo decode(XdrDataInputStream stream) throws IOException { } @Override public int hashCode() { - return Objects.hashCode(Arrays.hashCode(this.text), this.id, this.hash, this.retHash, this.type); + return Objects.hashCode(this.text, this.id, this.hash, this.retHash, this.type); } @Override public boolean equals(Object object) { @@ -124,6 +119,6 @@ public boolean equals(Object object) { } Memo other = (Memo) object; - return Arrays.equals(this.text, other.text) && Objects.equal(this.id, other.id) && Objects.equal(this.hash, other.hash) && Objects.equal(this.retHash, other.retHash) && Objects.equal(this.type, other.type); + return Objects.equal(this.text, other.text) && Objects.equal(this.id, other.id) && Objects.equal(this.hash, other.hash) && Objects.equal(this.retHash, other.retHash) && Objects.equal(this.type, other.type); } } diff --git a/src/main/java/org/stellar/sdk/xdr/String32.java b/src/main/java/org/stellar/sdk/xdr/String32.java index 3c2463cdd..d1188c537 100644 --- a/src/main/java/org/stellar/sdk/xdr/String32.java +++ b/src/main/java/org/stellar/sdk/xdr/String32.java @@ -6,7 +6,7 @@ import java.io.IOException; -import java.util.Arrays; +import com.google.common.base.Objects; // === xdr source ============================================================ @@ -14,31 +14,27 @@ // =========================================================================== public class String32 implements XdrElement { - private byte[] string32; - public byte[] getString32() { + private XdrString string32; + public XdrString getString32() { return this.string32; } - public void setString32(byte[] value) { + public void setString32(XdrString value) { this.string32 = value; } public static void encode(XdrDataOutputStream stream, String32 encodedString32) throws IOException { - int string32size = encodedString32.string32.length; - stream.writeInt(string32size); - stream.write(encodedString32.getString32(), 0, string32size); + encodedString32.string32.encode(stream); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static String32 decode(XdrDataInputStream stream) throws IOException { String32 decodedString32 = new String32(); - int string32size = stream.readInt(); - decodedString32.string32 = new byte[string32size]; - stream.read(decodedString32.string32, 0, string32size); + decodedString32.string32 = XdrString.decode(stream); return decodedString32; } @Override public int hashCode() { - return Arrays.hashCode(this.string32); + return Objects.hashCode(this.string32); } @Override public boolean equals(Object object) { @@ -47,6 +43,6 @@ public boolean equals(Object object) { } String32 other = (String32) object; - return Arrays.equals(this.string32, other.string32); + return Objects.equal(this.string32, other.string32); } } diff --git a/src/main/java/org/stellar/sdk/xdr/String64.java b/src/main/java/org/stellar/sdk/xdr/String64.java index 95478bc31..a26f79327 100644 --- a/src/main/java/org/stellar/sdk/xdr/String64.java +++ b/src/main/java/org/stellar/sdk/xdr/String64.java @@ -6,7 +6,7 @@ import java.io.IOException; -import java.util.Arrays; +import com.google.common.base.Objects; // === xdr source ============================================================ @@ -14,31 +14,27 @@ // =========================================================================== public class String64 implements XdrElement { - private byte[] string64; - public byte[] getString64() { + private XdrString string64; + public XdrString getString64() { return this.string64; } - public void setString64(byte[] value) { + public void setString64(XdrString value) { this.string64 = value; } public static void encode(XdrDataOutputStream stream, String64 encodedString64) throws IOException { - int string64size = encodedString64.string64.length; - stream.writeInt(string64size); - stream.write(encodedString64.getString64(), 0, string64size); + encodedString64.string64.encode(stream); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static String64 decode(XdrDataInputStream stream) throws IOException { String64 decodedString64 = new String64(); - int string64size = stream.readInt(); - decodedString64.string64 = new byte[string64size]; - stream.read(decodedString64.string64, 0, string64size); + decodedString64.string64 = XdrString.decode(stream); return decodedString64; } @Override public int hashCode() { - return Arrays.hashCode(this.string64); + return Objects.hashCode(this.string64); } @Override public boolean equals(Object object) { @@ -47,6 +43,6 @@ public boolean equals(Object object) { } String64 other = (String64) object; - return Arrays.equals(this.string64, other.string64); + return Objects.equal(this.string64, other.string64); } } diff --git a/src/main/java/org/stellar/sdk/xdr/XdrString.java b/src/main/java/org/stellar/sdk/xdr/XdrString.java new file mode 100644 index 000000000..a87cc90a4 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/XdrString.java @@ -0,0 +1,57 @@ +package org.stellar.sdk.xdr; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; + +public class XdrString implements XdrElement { + private byte[] bytes; + private String text; + + public XdrString(byte[] bytes) { + this.bytes = bytes; + this.text = new String(bytes, Charset.forName("UTF-8")); + } + + public XdrString(String text) { + this.bytes = text.getBytes(Charset.forName("UTF-8")); + this.text = text; + } + + @Override + public void encode(XdrDataOutputStream stream) throws IOException { + stream.writeInt(this.bytes.length); + stream.write(this.bytes, 0, this.bytes.length); + } + + public static XdrString decode(XdrDataInputStream stream) throws IOException { + int size = stream.readInt(); + byte[] bytes = new byte[size]; + stream.read(bytes); + return new XdrString(bytes); + } + + public byte[] getBytes() { + return this.bytes; + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.bytes); + } + + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof XdrString)) { + return false; + } + + XdrString other = (XdrString) object; + return Arrays.equals(this.bytes, other.bytes); + } + + @Override + public String toString() { + return this.text; + } +} diff --git a/src/test/java/org/stellar/sdk/XdrDataStreamTest.java b/src/test/java/org/stellar/sdk/XdrDataStreamTest.java index 093f2d4a8..e1bc10712 100644 --- a/src/test/java/org/stellar/sdk/XdrDataStreamTest.java +++ b/src/test/java/org/stellar/sdk/XdrDataStreamTest.java @@ -27,8 +27,7 @@ public static String backAndForthXdrStreaming(String inputString) throws IOExcep XdrDataInputStream xdrInputStream = new XdrDataInputStream(new ByteArrayInputStream(xdrByteOutput)); xdrMemo = org.stellar.sdk.xdr.Memo.decode(xdrInputStream); - return Memo.text(xdrMemo.getText()).getText(); - + return xdrMemo.getText().toString(); } @Test diff --git a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java index dc7b19644..442e31879 100644 --- a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java @@ -2,8 +2,6 @@ import com.google.common.io.BaseEncoding; import org.junit.Test; -import org.stellar.sdk.Memo; -import org.stellar.sdk.MemoText; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; From 0362c70faf283d86fc8a424c92c7262d3b801177 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 23:02:35 +0100 Subject: [PATCH 09/13] Use XdrString in MemoText --- src/main/java/org/stellar/sdk/MemoText.java | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/stellar/sdk/MemoText.java b/src/main/java/org/stellar/sdk/MemoText.java index 2862be6de..e355a7fc2 100644 --- a/src/main/java/org/stellar/sdk/MemoText.java +++ b/src/main/java/org/stellar/sdk/MemoText.java @@ -1,50 +1,51 @@ package org.stellar.sdk; -import com.google.common.base.Objects; import org.stellar.sdk.xdr.MemoType; import org.stellar.sdk.xdr.XdrString; -import java.nio.charset.Charset; -import java.util.Arrays; - import static com.google.common.base.Preconditions.checkNotNull; /** * Represents MEMO_TEXT. */ public class MemoText extends Memo { - private byte[] text; + private XdrString text; public MemoText(String text) { - this(checkNotNull(text, "text cannot be null").getBytes(Charset.forName("UTF-8"))); + this(new XdrString(checkNotNull(text, "text cannot be null"))); } public MemoText(byte[] text) { + this(new XdrString(checkNotNull(text, "text cannot be null"))); + } + + public MemoText(XdrString text) { this.text = checkNotNull(text, "text cannot be null"); - if (this.text.length > 28) { - throw new MemoTooLongException("text must be <= 28 bytes. length=" + String.valueOf(this.text.length)); + int length = this.text.getBytes().length; + if (length > 28) { + throw new MemoTooLongException("text must be <= 28 bytes. length=" + String.valueOf(length)); } } public String getText() { - return new String(this.text, Charset.forName("UTF-8")); + return this.text.toString(); } public byte[] getBytes() { - return this.text; + return this.text.getBytes(); } @Override org.stellar.sdk.xdr.Memo toXdr() { org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo(); memo.setDiscriminant(MemoType.MEMO_TEXT); - memo.setText(new XdrString(text)); + memo.setText(this.text); return memo; } @Override public int hashCode() { - return Arrays.hashCode(this.text); + return this.text.hashCode(); } @Override @@ -52,7 +53,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MemoText memoText = (MemoText) o; - return Arrays.equals(this.text, memoText.text); + return this.text.equals(memoText.text); } @Override From 39fddb10ffe36f1025b7fb46796bde7cb098bfce Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 23:27:17 +0100 Subject: [PATCH 10/13] Use newest xdrgen --- src/main/java/org/stellar/sdk/xdr/Error.java | 2 +- src/main/java/org/stellar/sdk/xdr/Hello.java | 2 +- src/main/java/org/stellar/sdk/xdr/Memo.java | 2 +- src/main/java/org/stellar/sdk/xdr/String32.java | 2 +- src/main/java/org/stellar/sdk/xdr/String64.java | 2 +- src/main/java/org/stellar/sdk/xdr/XdrString.java | 11 ++++++----- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/stellar/sdk/xdr/Error.java b/src/main/java/org/stellar/sdk/xdr/Error.java index 63e80384d..319085d20 100644 --- a/src/main/java/org/stellar/sdk/xdr/Error.java +++ b/src/main/java/org/stellar/sdk/xdr/Error.java @@ -43,7 +43,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { public static Error decode(XdrDataInputStream stream) throws IOException { Error decodedError = new Error(); decodedError.code = ErrorCode.decode(stream); - decodedError.msg = XdrString.decode(stream); + decodedError.msg = XdrString.decode(stream, 100); return decodedError; } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/Hello.java b/src/main/java/org/stellar/sdk/xdr/Hello.java index 4f3ff2782..c188a5f8c 100644 --- a/src/main/java/org/stellar/sdk/xdr/Hello.java +++ b/src/main/java/org/stellar/sdk/xdr/Hello.java @@ -109,7 +109,7 @@ public static Hello decode(XdrDataInputStream stream) throws IOException { decodedHello.overlayVersion = Uint32.decode(stream); decodedHello.overlayMinVersion = Uint32.decode(stream); decodedHello.networkID = Hash.decode(stream); - decodedHello.versionStr = XdrString.decode(stream); + decodedHello.versionStr = XdrString.decode(stream, 100); decodedHello.listeningPort = stream.readInt(); decodedHello.peerID = NodeID.decode(stream); decodedHello.cert = AuthCert.decode(stream); diff --git a/src/main/java/org/stellar/sdk/xdr/Memo.java b/src/main/java/org/stellar/sdk/xdr/Memo.java index 5abc3bfef..a6099df79 100644 --- a/src/main/java/org/stellar/sdk/xdr/Memo.java +++ b/src/main/java/org/stellar/sdk/xdr/Memo.java @@ -94,7 +94,7 @@ public static Memo decode(XdrDataInputStream stream) throws IOException { case MEMO_NONE: break; case MEMO_TEXT: - decodedMemo.text = XdrString.decode(stream); + decodedMemo.text = XdrString.decode(stream, 28); break; case MEMO_ID: decodedMemo.id = Uint64.decode(stream); diff --git a/src/main/java/org/stellar/sdk/xdr/String32.java b/src/main/java/org/stellar/sdk/xdr/String32.java index d1188c537..ea7115b30 100644 --- a/src/main/java/org/stellar/sdk/xdr/String32.java +++ b/src/main/java/org/stellar/sdk/xdr/String32.java @@ -29,7 +29,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static String32 decode(XdrDataInputStream stream) throws IOException { String32 decodedString32 = new String32(); - decodedString32.string32 = XdrString.decode(stream); + decodedString32.string32 = XdrString.decode(stream, 32); return decodedString32; } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/String64.java b/src/main/java/org/stellar/sdk/xdr/String64.java index a26f79327..a2a145bfc 100644 --- a/src/main/java/org/stellar/sdk/xdr/String64.java +++ b/src/main/java/org/stellar/sdk/xdr/String64.java @@ -29,7 +29,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static String64 decode(XdrDataInputStream stream) throws IOException { String64 decodedString64 = new String64(); - decodedString64.string64 = XdrString.decode(stream); + decodedString64.string64 = XdrString.decode(stream, 64); return decodedString64; } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/XdrString.java b/src/main/java/org/stellar/sdk/xdr/XdrString.java index a87cc90a4..d15e01e7e 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrString.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrString.java @@ -1,21 +1,19 @@ package org.stellar.sdk.xdr; import java.io.IOException; +import java.io.InvalidClassException; import java.nio.charset.Charset; import java.util.Arrays; public class XdrString implements XdrElement { private byte[] bytes; - private String text; public XdrString(byte[] bytes) { this.bytes = bytes; - this.text = new String(bytes, Charset.forName("UTF-8")); } public XdrString(String text) { this.bytes = text.getBytes(Charset.forName("UTF-8")); - this.text = text; } @Override @@ -24,8 +22,11 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(this.bytes, 0, this.bytes.length); } - public static XdrString decode(XdrDataInputStream stream) throws IOException { + public static XdrString decode(XdrDataInputStream stream, int maxSize) throws IOException { int size = stream.readInt(); + if (size > maxSize) { + throw new InvalidClassException("String length "+size+" exceeds max size "+maxSize); + } byte[] bytes = new byte[size]; stream.read(bytes); return new XdrString(bytes); @@ -52,6 +53,6 @@ public boolean equals(Object object) { @Override public String toString() { - return this.text; + return new String(bytes, Charset.forName("UTF-8")); } } From ca37d2aed0522d791c59a74fff0dc20335aba472 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 23:38:13 +0100 Subject: [PATCH 11/13] Check string lengths in ManageDataOperation and SetOptionsOperation --- src/main/java/org/stellar/sdk/ManageDataOperation.java | 4 ++++ src/main/java/org/stellar/sdk/SetOptionsOperation.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/org/stellar/sdk/ManageDataOperation.java b/src/main/java/org/stellar/sdk/ManageDataOperation.java index 60dd37e64..289a87d1c 100644 --- a/src/main/java/org/stellar/sdk/ManageDataOperation.java +++ b/src/main/java/org/stellar/sdk/ManageDataOperation.java @@ -20,6 +20,10 @@ public class ManageDataOperation extends Operation { private ManageDataOperation(String name, byte[] value) { this.name = checkNotNull(name, "name cannot be null"); this.value = value; + + if (new XdrString(this.name).getBytes().length > 64) { + throw new IllegalArgumentException("name cannot exceed 64 bytes"); + } } /** diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index 8ef24b732..c30713560 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -38,6 +38,11 @@ private SetOptionsOperation(String inflationDestination, Integer clearFlags, Int this.homeDomain = homeDomain; this.signer = signer; this.signerWeight = signerWeight; + + if (new XdrString(this.homeDomain).getBytes().length > 32) { + throw new IllegalArgumentException("home domain cannot exceed 32 bytes"); + } + } /** From 43456def22268924fd27c53875b74e8b17600f1e Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 23:41:20 +0100 Subject: [PATCH 12/13] Update changelog --- CHANGELOG.md | 2 ++ src/main/java/org/stellar/sdk/ManageDataOperation.java | 2 -- src/main/java/org/stellar/sdk/SetOptionsOperation.java | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73626fa06..afbbcdd42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps. ## 0.12.0 * Represent memo text contents as bytes because a memo text may not be valid UTF-8 string (https://github.com/stellar/java-stellar-sdk/issues/257). +* Validate name length when constructing org.stellar.sdk.ManageDataOperation instances. +* Validate home domain length when constructing org.stellar.sdk.SetOptionsOperation instances. ## 0.11.0 diff --git a/src/main/java/org/stellar/sdk/ManageDataOperation.java b/src/main/java/org/stellar/sdk/ManageDataOperation.java index 289a87d1c..d11422bd0 100644 --- a/src/main/java/org/stellar/sdk/ManageDataOperation.java +++ b/src/main/java/org/stellar/sdk/ManageDataOperation.java @@ -3,8 +3,6 @@ import com.google.common.base.Objects; import org.stellar.sdk.xdr.*; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; import java.util.Arrays; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index c30713560..1ffc8070f 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -4,8 +4,6 @@ import org.stellar.sdk.xdr.*; -import java.nio.charset.Charset; - import static com.google.common.base.Preconditions.checkNotNull; /** From 7547fd826d2507bbd14607ea7a7bd488e78d4989 Mon Sep 17 00:00:00 2001 From: Tamir Sen Date: Wed, 11 Dec 2019 23:43:56 +0100 Subject: [PATCH 13/13] fix null pointer exception --- src/main/java/org/stellar/sdk/SetOptionsOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index 1ffc8070f..ecbda7f81 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -37,7 +37,7 @@ private SetOptionsOperation(String inflationDestination, Integer clearFlags, Int this.signer = signer; this.signerWeight = signerWeight; - if (new XdrString(this.homeDomain).getBytes().length > 32) { + if (this.homeDomain != null && new XdrString(this.homeDomain).getBytes().length > 32) { throw new IllegalArgumentException("home domain cannot exceed 32 bytes"); }