Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use bytes to represent memo text #259

Merged
merged 13 commits into from
Dec 13, 2019
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (https://github.com/stellar/java-stellar-sdk/issues/257).

## 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/<id> , but the correct path is /operations/<id>
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/org/stellar/sdk/ManageDataOperation.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
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;
import java.util.Arrays;

import static com.google.common.base.Preconditions.checkNotNull;
Expand Down Expand Up @@ -41,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);
name.setString64(new XdrString(this.name));
op.setDataName(name);

if (value != null) {
Expand All @@ -68,7 +67,7 @@ public static class Builder {
* @param op {@link ManageDataOp}
*/
Builder(ManageDataOp op) {
name = op.getDataName().getString64();
name = op.getDataName().getString64().toString();
if (op.getDataValue() != null) {
value = op.getDataValue().getDataValue();
} else {
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/org/stellar/sdk/Memo.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -78,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:
Expand Down
28 changes: 18 additions & 10 deletions src/main/java/org/stellar/sdk/MemoText.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,61 @@

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 String text;
private byte[] text;
Copy link
Contributor

@leighmcculloch leighmcculloch Dec 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If text was an XdrString then we'd keep the UTF-8 conversion knowledge in one place, in the XdrString class. Right now that knowledge is spread out across XdrString and line 19 of this class, and any other class like MemoText that follows this pattern in the future.


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(this.text, Charset.forName("UTF-8"));
}

public byte[] getBytes() {
return this.text;
}

@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(text);
memo.setText(new XdrString(text));
return memo;
}

@Override
public int hashCode() {
return Objects.hashCode(this.text);
return Arrays.hashCode(this.text);
}

@Override
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();
}
}
6 changes: 4 additions & 2 deletions src/main/java/org/stellar/sdk/SetOptionsOperation.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.stellar.sdk.xdr.*;


import java.nio.charset.Charset;

import static com.google.common.base.Preconditions.checkNotNull;

/**
Expand Down Expand Up @@ -148,7 +150,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
}
if (homeDomain != null) {
String32 homeDomain = new String32();
homeDomain.setString32(this.homeDomain);
homeDomain.setString32(new XdrString(this.homeDomain));
op.setHomeDomain(homeDomain);
}
if (signer != null) {
Expand Down Expand Up @@ -206,7 +208,7 @@ public static class Builder {
highThreshold = op.getHighThreshold().getUint32().intValue();
}
if (op.getHomeDomain() != null) {
homeDomain = op.getHomeDomain().getString32();
homeDomain = op.getHomeDomain().getString32().toString();
}
if (op.getSigner() != null) {
signer = op.getSigner().getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TransactionResponse> {
Expand All @@ -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<TransactionResponse> cannot throw IOExceptions
// so we must throw it as a runtime exception
throw new RuntimeException(e);
}
memo = Memo.text(transactionEnvelope.getTx().getMemo().getText().getBytes());
} else {
String memoValue = json.getAsJsonObject().get("memo").getAsString();
BaseEncoding base64Encoding = BaseEncoding.base64();
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/stellar/sdk/xdr/Error.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ public ErrorCode getCode() {
public void setCode(ErrorCode value) {
this.code = value;
}
private String msg;
public String getMsg() {
private XdrString msg;
public XdrString getMsg() {
return this.msg;
}
public void setMsg(String value) {
public void setMsg(XdrString value) {
this.msg = value;
}
public static void encode(XdrDataOutputStream stream, Error encodedError) throws IOException{
ErrorCode.encode(stream, encodedError.code);
stream.writeString(encodedError.msg);
encodedError.msg.encode(stream);
}
public void encode(XdrDataOutputStream stream) throws IOException {
encode(stream, this);
}
public static Error decode(XdrDataInputStream stream) throws IOException {
Error decodedError = new Error();
decodedError.code = ErrorCode.decode(stream);
decodedError.msg = stream.readString();
decodedError.msg = XdrString.decode(stream);
return decodedError;
}
@Override
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/stellar/sdk/xdr/Hello.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ public Hash getNetworkID() {
public void setNetworkID(Hash value) {
this.networkID = value;
}
private String versionStr;
public String getVersionStr() {
private XdrString versionStr;
public XdrString getVersionStr() {
return this.versionStr;
}
public void setVersionStr(String value) {
public void setVersionStr(XdrString value) {
this.versionStr = value;
}
private Integer listeningPort;
Expand Down Expand Up @@ -94,7 +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);
stream.writeString(encodedHello.versionStr);
encodedHello.versionStr.encode(stream);
stream.writeInt(encodedHello.listeningPort);
NodeID.encode(stream, encodedHello.peerID);
AuthCert.encode(stream, encodedHello.cert);
Expand All @@ -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 = stream.readString();
decodedHello.versionStr = XdrString.decode(stream);
decodedHello.listeningPort = stream.readInt();
decodedHello.peerID = NodeID.decode(stream);
decodedHello.cert = AuthCert.decode(stream);
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/stellar/sdk/xdr/Memo.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public MemoType getDiscriminant() {
public void setDiscriminant(MemoType value) {
this.type = value;
}
private String text;
public String getText() {
private XdrString text;
public XdrString getText() {
return this.text;
}
public void setText(String value) {
public void setText(XdrString value) {
this.text = value;
}
private Uint64 id;
Expand Down Expand Up @@ -70,7 +70,7 @@ public static void encode(XdrDataOutputStream stream, Memo encodedMemo) throws I
case MEMO_NONE:
break;
case MEMO_TEXT:
stream.writeString(encodedMemo.text);
encodedMemo.text.encode(stream);
break;
case MEMO_ID:
Uint64.encode(stream, encodedMemo.id);
Expand All @@ -94,7 +94,7 @@ public static Memo decode(XdrDataInputStream stream) throws IOException {
case MEMO_NONE:
break;
case MEMO_TEXT:
decodedMemo.text = stream.readString();
decodedMemo.text = XdrString.decode(stream);
break;
case MEMO_ID:
decodedMemo.id = Uint64.decode(stream);
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/stellar/sdk/xdr/String32.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@

// ===========================================================================
public class String32 implements XdrElement {
private String string32;
public String getString32() {
private XdrString string32;
public XdrString getString32() {
return this.string32;
}
public void setString32(String value) {
public void setString32(XdrString value) {
this.string32 = value;
}
public static void encode(XdrDataOutputStream stream, String32 encodedString32) throws IOException {
stream.writeString(encodedString32.string32);
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();
decodedString32.string32 = stream.readString();
decodedString32.string32 = XdrString.decode(stream);
return decodedString32;
}
@Override
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/stellar/sdk/xdr/String64.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@

// ===========================================================================
public class String64 implements XdrElement {
private String string64;
public String getString64() {
private XdrString string64;
public XdrString getString64() {
return this.string64;
}
public void setString64(String value) {
public void setString64(XdrString value) {
this.string64 = value;
}
public static void encode(XdrDataOutputStream stream, String64 encodedString64) throws IOException {
stream.writeString(encodedString64.string64);
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();
decodedString64.string64 = stream.readString();
decodedString64.string64 = XdrString.decode(stream);
return decodedString64;
}
@Override
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading