From 8bbb22de7da65ffa10b3dd36213df253d35c56e9 Mon Sep 17 00:00:00 2001 From: tamirms Date: Tue, 21 Apr 2020 17:27:12 +0200 Subject: [PATCH 1/5] Update Horizon Transaction response (#275) In the Horizon 1.1.0 release the following attributes were added to the Horizon transaction response: fee_account (the account which paid the transaction fee) fee_bump_transaction (only present in Protocol 13 fee bump transactions) inner_transaction (only present in Protocol 13 fee bump transactions) In the upcoming Horizon 1.2.0 release a base 64 encoding of the bytes in a transaction memo will be included in the Horizon transaction response. This commit updates the Transaction class in the Java SDK so that it can be used to parse the new Horizon transaction response. --- .../responses/TransactionDeserializer.java | 38 ++++---- .../sdk/responses/TransactionResponse.java | 95 ++++++++++++++++++ .../TransactionDeserializerTest.java | 96 ++++++++++++++++++- .../TransactionPageDeserializerTest.java | 13 +++ 4 files changed, 224 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java index 428ef7e93..9bf14bf9d 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java @@ -1,12 +1,7 @@ package org.stellar.sdk.responses; import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; +import com.google.gson.*; import org.stellar.sdk.Memo; import org.stellar.sdk.xdr.TransactionEnvelope; @@ -35,19 +30,28 @@ 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")) { - // 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); + JsonObject jsonResponse = json.getAsJsonObject(); + + if (jsonResponse.has("memo_bytes")) { + // we obtain the memo text from the "memo_bytes" field because the original byte sequence may not be valid utf8 + String memoBase64 = json.getAsJsonObject().get("memo_bytes").getAsString(); + memo = Memo.text(base64Encoding.decode(memoBase64)); + } else { + // memo_bytes is not available because horizon is running a version older than 1.2.0 + // so we will recover the bytes from the xdr + String envelopeXdr = json.getAsJsonObject().get("envelope_xdr").getAsString(); + 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().getBytes()); } - 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/responses/TransactionResponse.java b/src/main/java/org/stellar/sdk/responses/TransactionResponse.java index 1a7de478d..4d5c9c08d 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionResponse.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionResponse.java @@ -1,9 +1,12 @@ package org.stellar.sdk.responses; +import com.google.common.base.Optional; import com.google.gson.annotations.SerializedName; import org.stellar.sdk.Memo; +import java.util.List; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -21,6 +24,8 @@ public class TransactionResponse extends Response implements Pageable { private final String createdAt; @SerializedName("source_account") private final String sourceAccount; + @SerializedName("fee_account") + private final String feeAccount; @SerializedName("successful") private final Boolean successful; @SerializedName("paging_token") @@ -39,6 +44,12 @@ public class TransactionResponse extends Response implements Pageable { private final String resultXdr; @SerializedName("result_meta_xdr") private final String resultMetaXdr; + @SerializedName("signatures") + private final List signatures; + @SerializedName("fee_bump_transaction") + private final FeeBumpTransaction feeBumpTransaction; + @SerializedName("inner_transaction") + private final InnerTransaction innerTransaction; @SerializedName("_links") private final Links links; @@ -51,6 +62,7 @@ public class TransactionResponse extends Response implements Pageable { Long ledger, String createdAt, String sourceAccount, + String feeAccount, Boolean successful, String pagingToken, Long sourceAccountSequence, @@ -61,12 +73,16 @@ public class TransactionResponse extends Response implements Pageable { String resultXdr, String resultMetaXdr, Memo memo, + List signatures, + FeeBumpTransaction feeBumpTransaction, + InnerTransaction innerTransaction, Links links ) { this.hash = hash; this.ledger = ledger; this.createdAt = createdAt; this.sourceAccount = sourceAccount; + this.feeAccount = feeAccount; this.successful = successful; this.pagingToken = pagingToken; this.sourceAccountSequence = sourceAccountSequence; @@ -77,6 +93,9 @@ public class TransactionResponse extends Response implements Pageable { this.resultXdr = resultXdr; this.resultMetaXdr = resultMetaXdr; this.memo = memo; + this.signatures = signatures; + this.feeBumpTransaction = feeBumpTransaction; + this.innerTransaction = innerTransaction; this.links = links; } @@ -96,6 +115,22 @@ public String getSourceAccount() { return sourceAccount; } + public String getFeeAccount() { + return feeAccount; + } + + public List getSignatures() { + return signatures; + } + + public Optional getFeeBump() { + return Optional.fromNullable(this.feeBumpTransaction); + } + + public Optional getInner() { + return Optional.fromNullable(this.innerTransaction); + } + public String getPagingToken() { return pagingToken; } @@ -148,6 +183,66 @@ public Links getLinks() { return links; } + /** + * FeeBumpTransaction is only present in a TransactionResponse if the transaction is a fee bump transaction or is + * wrapped by a fee bump transaction. The object has two fields: the hash of the fee bump transaction and the + * signatures present in the fee bump transaction envelope. + */ + public static class FeeBumpTransaction { + @SerializedName("hash") + private final String hash; + @SerializedName("signatures") + private final List signatures; + + FeeBumpTransaction(String hash, List signatures) { + this.hash = hash; + this.signatures = signatures; + } + + public String getHash() { + return hash; + } + + public List getSignatures() { + return signatures; + } + } + + /** + * InnerTransaction is only present in a TransactionResponse if the transaction is a fee bump transaction or is + * wrapped by a fee bump transaction. The object has three fields: the hash of the inner transaction wrapped by the + * fee bump transaction, the max fee set in the inner transaction, and the signatures present in the inner + * transaction envelope. + */ + public static class InnerTransaction { + @SerializedName("hash") + private final String hash; + @SerializedName("signatures") + private final List signatures; + @SerializedName("max_fee") + private final Long maxFee; + + + InnerTransaction(String hash, List signatures, Long maxFee) { + this.hash = hash; + this.signatures = signatures; + this.maxFee = maxFee; + } + + public String getHash() { + return hash; + } + + public List getSignatures() { + return signatures; + } + + public Long getMaxFee() { + return maxFee; + } + + } + /** * Links connected to transaction. */ diff --git a/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java index 38a74e27e..ff61680ef 100644 --- a/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/TransactionDeserializerTest.java @@ -1,5 +1,7 @@ package org.stellar.sdk.responses; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; import junit.framework.TestCase; import org.junit.Test; @@ -7,6 +9,30 @@ import org.stellar.sdk.MemoNone; public class TransactionDeserializerTest extends TestCase { + @Test + public void testDeserializeFeeBump() { + TransactionResponse transaction = GsonSingleton.getInstance().fromJson(jsonFeeBump, TransactionResponse.class); + assertEquals(transaction.getHash(), "3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352"); + assertEquals(transaction.getLedger(), Long.valueOf(123)); + assertEquals(transaction.isSuccessful(), Boolean.TRUE); + assertEquals(transaction.getSourceAccount(), "GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX"); + assertEquals(transaction.getFeeAccount(), "GABAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGKJ"); + assertEquals(transaction.getSourceAccountSequence(), Long.valueOf(97)); + assertEquals(transaction.getMaxFee(), Long.valueOf(776)); + assertEquals(transaction.getFeeCharged(), Long.valueOf(123)); + assertEquals(transaction.getOperationCount(), Integer.valueOf(1)); + assertEquals(transaction.getSignatures(), ImmutableList.of("Hh4e")); + + TransactionResponse.FeeBumpTransaction feeBumpTransaction = transaction.getFeeBump().get(); + assertEquals(feeBumpTransaction.getHash(), "3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352"); + assertEquals(feeBumpTransaction.getSignatures(), ImmutableList.of("Hh4e")); + + TransactionResponse.InnerTransaction innerTransaction = transaction.getInner().get(); + assertEquals(innerTransaction.getHash(), "e98869bba8bce08c10b78406202127f3888c25454cd37b02600862452751f526"); + assertEquals(innerTransaction.getMaxFee(), Long.valueOf(99)); + assertEquals(innerTransaction.getSignatures(), ImmutableList.of("FBQU")); + } + @Test public void testDeserialize() { TransactionResponse transaction = GsonSingleton.getInstance().fromJson(json, TransactionResponse.class); @@ -23,7 +49,9 @@ public void testDeserialize() { assertEquals(transaction.getEnvelopeXdr(), "AAAAAKgfpXwD1fWpPmZL+GkzWcBmhRQH7ouPsoTN3RoaGCfrAAAAZAAIbkcAAB9WAAAAAAAAAANRBBZE6D1qyGjISUGLY5Ldvp31PwAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAP1qe44j+i4uIT+arbD4QDQBt8ryEeJd7a0jskQ3nwDeAAAAAAAAAADA7RnarSzCwj3OT+M2btCMFpVBdqxJS+Sr00qBjtFv7gAAAABLCs/QAAAAAAAAAAEaGCfrAAAAQG/56Cj2J8W/KCZr+oC4sWND1CTGWfaccHNtuibQH8kZIb+qBSDY94g7hiaAXrlIeg9b7oz/XuP3x9MWYw2jtwM="); assertEquals(transaction.getResultXdr(), "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA="); assertEquals(transaction.getResultMetaXdr(), "AAAAAAAAAAEAAAACAAAAAAAN+SAAAAAAAAAAAMDtGdqtLMLCPc5P4zZu0IwWlUF2rElL5KvTSoGO0W/uAAAAAEsKz9AADfkgAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAN+SAAAAAAAAAAAP1qe44j+i4uIT+arbD4QDQBt8ryEeJd7a0jskQ3nwDeAAHp6WMr55YACD1BAAAAHgAAAAoAAAAAAAAAAAAAAAABAAAAAAAACgAAAAARC07BokpLTOF+/vVKBwiAlop7hHGJTNeGGlY4MoPykwAAAAEAAAAAK+Lzfd3yDD+Ov0GbYu1g7SaIBrKZeBUxoCunkLuI7aoAAAABAAAAAERmsKL73CyLV/HvjyQCERDXXpWE70Xhyb6MR5qPO3yQAAAAAQAAAABSORGwAdyuanN3sNOHqNSpACyYdkUM3L8VafUu69EvEgAAAAEAAAAAeCzqJNkMM/jLvyuMIfyFHljBlLCtDyj17RMycPuNtRMAAAABAAAAAIEi4R7juq15ymL00DNlAddunyFT4FyUD4muC4t3bobdAAAAAQAAAACaNpLL5YMfjOTdXVEqrAh99LM12sN6He6pHgCRAa1f1QAAAAEAAAAAqB+lfAPV9ak+Zkv4aTNZwGaFFAfui4+yhM3dGhoYJ+sAAAABAAAAAMNJrEvdMg6M+M+n4BDIdzsVSj/ZI9SvAp7mOOsvAD/WAAAAAQAAAADbHA6xiKB1+G79mVqpsHMOleOqKa5mxDpP5KEp/Xdz9wAAAAEAAAAAAAAAAA=="); - + assertEquals(transaction.getSignatures(), ImmutableList.of("b/noKPYnxb8oJmv6gLixY0PUJMZZ9pxwc226JtAfyRkhv6oFINj3iDuGJoBeuUh6D1vujP9e4/fH0xZjDaO3Aw==")); + assertEquals(transaction.getFeeBump(), Optional.absent()); + assertEquals(transaction.getInner(), Optional.absent()); assertTrue(transaction.getMemo() instanceof MemoHash); MemoHash memo = (MemoHash) transaction.getMemo(); assertEquals("51041644e83d6ac868c849418b6392ddbe9df53f000000000000000000000000", memo.getHexValue()); @@ -136,4 +164,70 @@ public void testDeserializeWithoutMemo() { " \"b/noKPYnxb8oJmv6gLixY0PUJMZZ9pxwc226JtAfyRkhv6oFINj3iDuGJoBeuUh6D1vujP9e4/fH0xZjDaO3Aw==\"\n" + " ]\n" + "}"; + + String jsonFeeBump = "{\n" + + " \"_links\": {\n" + + " \"self\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\"\n" + + " },\n" + + " \"account\": {\n" + + " \"href\": \"http://localhost/accounts/GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX\"\n" + + " },\n" + + " \"ledger\": {\n" + + " \"href\": \"http://localhost/ledgers/123\"\n" + + " },\n" + + " \"operations\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352/operations{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"effects\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352/effects{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"http://localhost/transactions?order=asc\\u0026cursor=528280981504\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"http://localhost/transactions?order=desc\\u0026cursor=528280981504\"\n" + + " },\n" + + " \"transaction\": {\n" + + " \"href\": \"http://localhost/transactions/3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\"\n" + + " }\n" + + " },\n" + + " \"id\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"paging_token\": \"528280981504\",\n" + + " \"successful\": true,\n" + + " \"hash\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"ledger\": 123,\n" + + " \"created_at\": \"2020-04-21T10:21:26Z\",\n" + + " \"source_account\": \"GABQGAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2MX\",\n" + + " \"source_account_sequence\": \"97\",\n" + + " \"fee_account\": \"GABAEAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGKJ\",\n" + + " \"fee_charged\": 123,\n" + + " \"max_fee\": 776,\n" + + " \"operation_count\": 1,\n" + + " \"envelope_xdr\": \"AAAABQAAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMIAAAAAgAAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGMAAAAAAAAAYQAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAEAAAAAAAAACwAAAAAAAABiAAAAAAAAAAECAgICAAAAAxQUFAAAAAAAAAAAAQMDAwMAAAADHh4eAA==\",\n" + + " \"result_xdr\": \"AAAAAAAAAHsAAAAB6Yhpu6i84IwQt4QGICEn84iMJUVM03sCYAhiRSdR9SYAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAAAAAAAAAAAAA=\",\n" + + " \"result_meta_xdr\": \"AAAAAQAAAAAAAAAA\",\n" + + " \"fee_meta_xdr\": \"AAAAAA==\",\n" + + " \"memo_type\": \"none\",\n" + + " \"signatures\": [\n" + + " \"Hh4e\"\n" + + " ],\n" + + " \"valid_after\": \"1970-01-01T00:00:02Z\",\n" + + " \"valid_before\": \"1970-01-01T00:00:04Z\",\n" + + " \"fee_bump_transaction\": {\n" + + " \"hash\": \"3dfef7d7226995b504f2827cc63d45ad41e9687bb0a8abcf08ba755fedca0352\",\n" + + " \"signatures\": [\n" + + " \"Hh4e\"\n" + + " ]\n" + + " },\n" + + " \"inner_transaction\": {\n" + + " \"hash\": \"e98869bba8bce08c10b78406202127f3888c25454cd37b02600862452751f526\",\n" + + " \"signatures\": [\n" + + " \"FBQU\"\n" + + " ],\n" + + " \"max_fee\": \"99\"\n" + + " }\n" + + "}"; } diff --git a/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java index 1d8db01a1..4f5a5cd0e 100644 --- a/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/TransactionPageDeserializerTest.java @@ -20,6 +20,12 @@ public void testDeserialize() { assertEquals(transactionsPage.getRecords().get(0).getLinks().getAccount().getHref(), "/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7"); assertEquals(transactionsPage.getRecords().get(9).getSourceAccount(), "GAENIE5LBJIXLMJIAJ7225IUPA6CX7EGHUXRX5FLCZFFAQSG2ZUYSWFK"); + // Transaction without memo_bytes field + assertTrue(transactionsPage.getRecords().get(7).getMemo() instanceof MemoText); + memoText = (MemoText) transactionsPage.getRecords().get(7).getMemo(); + assertEquals(memoText.getText(), "helpsdf"); + + // Empty memo_text assertTrue(transactionsPage.getRecords().get(2).getMemo() instanceof MemoText); memoText = (MemoText) transactionsPage.getRecords().get(2).getMemo(); @@ -73,6 +79,7 @@ public void testDeserialize() { " \"result_meta_xdr\": \"AAAAAAAAAAMAAAACAAAAAAAAAAMAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2AAAAAAvrwgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAAAAAAAAGUcmKO5465JxTSLQOQljwk2SfqAJmZSG6JH6wtqpwhDeC2s5t4PNQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAADAAAAAAAAAAABlHJijueOuScU0i0DkJY8JNkn6gCZmUhuiR+sLaqcIQAAAAAL68IAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAW8Qst5i4N4Yk6l+FVBBKetKRTqyTcWDNSbcV08F4WNg3gtrObeDzUAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAwAAAAAAAAAAAZRyYo7njrknFNItA5CWPCTZJ+oAmZlIbokfrC2qnCEAAAAAC+vCAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\n" + " \"memo_type\": \"text\",\n" + " \"memo\": \"hello world\",\n" + + " \"memo_bytes\": \"aGVsbG8gd29ybGQ=\",\n" + " \"signatures\": [\n" + " \"SsKlst02jsaBp35vXlI300Qbz39+HbwwNDX/gfM+MwaG/dFF7bgqpkKsDAsh/qJDiN3NOW695IZB9Mj+JAsHBA==\"\n" + " ]\n" + @@ -117,6 +124,7 @@ public void testDeserialize() { " \"result_meta_xdr\": \"AAAAAAAAAAMAAAACAAAAAAAAHqEAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAvrwgAAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAHqEAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2DeC2s4+MeHwAAAADAAAAAgAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAB6hAAAAAAAAAACzMOD+8iU8qo+qbTYewT8lxKE/s1cE3FOCVWxsqJ74GwAAAAAL68IAAAAeoQAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAB6hAAAAAAAAAAAW8Qst5i4N4Yk6l+FVBBKetKRTqyTcWDNSbcV08F4WNg3gtrODoLZ8AAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAeoQAAAAAAAAAASZcLtOTqf+cdbsq8HmLMkeqU06LN94UTWXuSBem5Z88AAAAAC+vCAAAAHqEAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAeoQAAAAAAAAAAFvELLeYuDeGJOpfhVQQSnrSkU6sk3FgzUm3FdPBeFjYN4Lazd7T0fAAAAAMAAAACAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAA=\",\n" + " \"memo_type\": \"text\",\n" + " \"memo\": \"testpool,faucet,sdf\",\n" + + " \"memo_bytes\": \"dGVzdHBvb2wsZmF1Y2V0LHNkZg==\",\n" + " \"signatures\": [\n" + " \"NIEHl40QqRYihBtnVhBpsm4TmZb8AqGsdPHD0feb+xqL3RhxpmWwB7e5472MTGNJkXbz1lxHi9zTAXDWn9bIBw==\"\n" + " ]\n" + @@ -160,6 +168,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAFAAAAAAAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHq8AAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2DeC2s3e09BgAAAADAAAAAwAAAAAAAAABAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"1sNxaxHezfPxDOpq1x1z+TYvgtdYkwDp5dpzx1teQB1zimkfbmKwYLk7C8bTpXC9zcJ7f+vathwE/e/GHHXwDw==\"\n" + " ]\n" + @@ -203,6 +212,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK11sXJ6SgAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHrcAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAACtdb1ePEoAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"UQc3yHhlEA5DbWt8xu8R9EqujHH2uSvHeErZdfWPqY3uooA0qbPA8yVxFxvnLOyQ+qhYKy6pf8yRRgt8Nl7tBg==\"\n" + " ]\n" + @@ -246,6 +256,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK11sXTKRwAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHr8AAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAFa627TBpEAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"YLL32xpSbpqVchHKnQ02HY4VILJunUkY8M3alv/4XDTv9fvLiU8Sn8HpYhXXfRr7Z5j7lPe6NuIprpMhxRo3BA==\"\n" + " ]\n" + @@ -289,6 +300,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAEAAAAAH6Ue1GOPj6Hb/ROPyIFCJpQPMujihEIvJSfK0UfMDIgAAK1+KLf6bgAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAABAAAAAQAAHsIAAAAAAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAIIaZeLAP8AAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"I8CjxDCxl70UDSzP914x2GW7r/OuTtKCQEbbYCm+2kRMGGcaRfi2tIUPPyxWUqq/B6oZRWC+NTIzQBwfHJdlDA==\"\n" + " ]\n" + @@ -332,6 +344,7 @@ public void testDeserialize() { " \"result_xdr\": \"AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=\",\n" + " \"result_meta_xdr\": \"AAAAAAAAAAEAAAACAAAAAQAAHzYAAAAAAAAAABbxCy3mLg3hiTqX4VUEEp60pFOrJNxYM1JtxXTwXhY2AAAABJwsBgAAAAADAAAABwAAAAAAAAABAAAAAB+lHtRjj4+h2/0Tj8iBQiaUDzLo4oRCLyUnytFHzAyIAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAHzYAAAAAAAAAAEmXC7Tk6n/nHW7KvB5izJHqlNOizfeFE1l7kgXpuWfPDeC2rud0rogAAB6hAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA\",\n" + " \"memo_type\": \"text\",\n" + + " \"memo_bytes\": \"\",\n" + " \"signatures\": [\n" + " \"NkV853/bo6msX7/TKL1m1+4de4vhO7fp4Ci4ZwBLuIerqX5ozLbPf0il3/JEBBQd1HtpyS5ZMHhvbqEQTLXHAA==\"\n" + " ]\n" + From 6a50ca4cfef3d7216880318b5361695c6ec907d5 Mon Sep 17 00:00:00 2001 From: tamirms Date: Tue, 21 Apr 2020 17:28:31 +0200 Subject: [PATCH 2/5] Add support for CAP 18 trustline flags in effects and account response (#274) The upcoming stellar-core protocol 13 release will add support for CAP 18 ("Fine-Grained Control of Authorization"). Horizon has added a is_authorized_to_maintain_liabilities field to the account balance attributes. Also, there is now a new effect which indicates a trustline was authorized to maintain liabilities. See https://github.com/stellar/go/issues/2357 for more details. This PR updates the java SDK so that it's able to parse the new Horizon account balance and effects responses. --- .../sdk/responses/AccountResponse.java | 9 +- .../sdk/responses/EffectDeserializer.java | 2 + .../sdk/responses/effects/EffectResponse.java | 1 + ...edToMaintainLiabilitiesEffectResponse.java | 13 +++ .../responses/AccountDeserializerTest.java | 99 +++++++++++++++++++ .../sdk/responses/EffectDeserializerTest.java | 37 +++++++ 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java diff --git a/src/main/java/org/stellar/sdk/responses/AccountResponse.java b/src/main/java/org/stellar/sdk/responses/AccountResponse.java index 6535ad869..fe119b21e 100644 --- a/src/main/java/org/stellar/sdk/responses/AccountResponse.java +++ b/src/main/java/org/stellar/sdk/responses/AccountResponse.java @@ -193,10 +193,12 @@ public static class Balance { private final String sellingLiabilities; @SerializedName("is_authorized") private final Boolean isAuthorized; + @SerializedName("is_authorized_to_maintain_liabilities") + private final Boolean isAuthorizedToMaintainLiabilities; @SerializedName("last_modified_ledger") private final Integer lastModifiedLedger; - Balance(String assetType, String assetCode, String assetIssuer, String balance, String limit, String buyingLiabilities, String sellingLiabilities, Boolean isAuthorized, Integer lastModifiedLedger) { + Balance(String assetType, String assetCode, String assetIssuer, String balance, String limit, String buyingLiabilities, String sellingLiabilities, Boolean isAuthorized, Boolean isAuthorizedToMaintainLiabilities, Integer lastModifiedLedger) { this.assetType = checkNotNull(assetType, "assertType cannot be null"); this.balance = checkNotNull(balance, "balance cannot be null"); this.limit = limit; @@ -205,6 +207,7 @@ public static class Balance { this.buyingLiabilities = checkNotNull(buyingLiabilities, "buyingLiabilities cannot be null"); this.sellingLiabilities = checkNotNull(sellingLiabilities, "sellingLiabilities cannot be null"); this.isAuthorized = isAuthorized; + this.isAuthorizedToMaintainLiabilities = isAuthorizedToMaintainLiabilities; this.lastModifiedLedger = lastModifiedLedger; } @@ -248,6 +251,10 @@ public Boolean getAuthorized() { return isAuthorized; } + public Boolean getAuthorizedToMaintainLiabilities() { + return isAuthorizedToMaintainLiabilities; + } + public Integer getLastModifiedLedger() { return lastModifiedLedger; } diff --git a/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java b/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java index 6d67550db..52000d08a 100644 --- a/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/EffectDeserializer.java @@ -55,6 +55,8 @@ public EffectResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializ return gson.fromJson(json, TrustlineAuthorizedEffectResponse.class); case 24: return gson.fromJson(json, TrustlineDeauthorizedEffectResponse.class); + case 25: + return gson.fromJson(json, TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.class); // Trading effects case 30: return gson.fromJson(json, OfferCreatedEffectResponse.class); diff --git a/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java b/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java index 25e8ce2ba..83f9f37fe 100644 --- a/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java +++ b/src/main/java/org/stellar/sdk/responses/effects/EffectResponse.java @@ -52,6 +52,7 @@ public String getAccount() { *
  • trustline_removed
  • *
  • trustline_updated
  • *
  • trustline_authorized
  • + *
  • trustline_authorized_to_maintain_liabilities
  • *
  • trustline_deauthorized
  • *
  • offer_created
  • *
  • offer_removed
  • diff --git a/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java b/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java new file mode 100644 index 000000000..65d019a38 --- /dev/null +++ b/src/main/java/org/stellar/sdk/responses/effects/TrustlineAuthorizedToMaintainLiabilitiesEffectResponse.java @@ -0,0 +1,13 @@ +package org.stellar.sdk.responses.effects; + +/** + * Represents trustline_authorized_to_maintain_liabilities effect response. + * @see Effect documentation + * @see org.stellar.sdk.requests.EffectsRequestBuilder + * @see org.stellar.sdk.Server#effects() + */ +public class TrustlineAuthorizedToMaintainLiabilitiesEffectResponse extends TrustlineAuthorizationResponse { + TrustlineAuthorizedToMaintainLiabilitiesEffectResponse(String trustor, String assetType, String assetCode) { + super(trustor, assetType, assetCode); + } +} diff --git a/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java index 42f1b83b6..8c6ab314d 100644 --- a/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/AccountDeserializerTest.java @@ -7,6 +7,27 @@ import java.util.Arrays; public class AccountDeserializerTest extends TestCase { + @Test + public void testDeserializeBalanceAuth() { + AccountResponse account = GsonSingleton.getInstance().fromJson(jsonAuthorizedToMaintainLiabilities, AccountResponse.class); + + assertEquals(account.getBalances()[0].getAssetType(), "credit_alphanum4"); + assertEquals(account.getBalances()[0].getAssetCode(), "ABC"); + assertEquals(account.getBalances()[0].getAssetIssuer(), "GCRA6COW27CY5MTKIA7POQ2326C5ABYCXODBN4TFF5VL4FMBRHOT3YHU"); + assertEquals(account.getBalances()[0].getBalance(), "1001.0000000"); + assertEquals(account.getBalances()[0].getLimit(), "12000.4775807"); + assertEquals(account.getBalances()[0].getBuyingLiabilities(), "100.1234567"); + assertEquals(account.getBalances()[0].getSellingLiabilities(), "100.7654321"); + assertEquals(account.getBalances()[0].getAuthorized(), Boolean.FALSE); + assertEquals(account.getBalances()[0].getAuthorizedToMaintainLiabilities(), Boolean.TRUE); + + assertEquals(account.getBalances()[1].getAssetType(), "native"); + assertEquals(account.getBalances()[1].getBalance(), "20.0000300"); + assertEquals(account.getBalances()[1].getBuyingLiabilities(), "5.1234567"); + assertEquals(account.getBalances()[1].getSellingLiabilities(), "1.7654321"); + assertEquals(account.getBalances()[1].getLimit(), null); + } + @Test public void testDeserialize() { AccountResponse account = GsonSingleton.getInstance().fromJson(json, AccountResponse.class); @@ -77,6 +98,84 @@ public void testDeserializeV9() { assertEquals(account.getBalances()[1].getLimit(), null); } + String jsonAuthorizedToMaintainLiabilities = "{\n" + + " \"_links\": {\n" + + " \"effects\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/effects{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"offers\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/offers{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"operations\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/operations{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " },\n" + + " \"self\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\"\n" + + " },\n" + + " \"transactions\": {\n" + + " \"href\": \"/accounts/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7/transactions{?cursor,limit,order}\",\n" + + " \"templated\": true\n" + + " }\n" + + " },"+ + " \"id\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"paging_token\": \"1\",\n" + + " \"account_id\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"sequence\": 2319149195853854,\n" + + " \"subentry_count\": 0,\n" + + " \"inflation_destination\": \"GAGRSA6QNQJN2OQYCBNQGMFLO4QLZFNEHIFXOMTQVSUTWVTWT66TOFSC\",\n" + + " \"home_domain\": \"stellar.org\",\n" + + " \"thresholds\": {\n" + + " \"low_threshold\": 10,\n" + + " \"med_threshold\": 20,\n" + + " \"high_threshold\": 30\n" + + " },\n" + + " \"flags\": {\n" + + " \"auth_required\": false,\n" + + " \"auth_revocable\": true,\n" + + " \"auth_immutable\": true\n" + + " },\n" + + " \"balances\": [\n" + + " {\n" + + " \"balance\": \"1001.0000000\",\n" + + " \"buying_liabilities\": \"100.1234567\",\n" + + " \"selling_liabilities\": \"100.7654321\",\n" + + " \"limit\": \"12000.4775807\",\n" + + " \"asset_type\": \"credit_alphanum4\",\n" + + " \"asset_code\": \"ABC\",\n" + + " \"asset_issuer\": \"GCRA6COW27CY5MTKIA7POQ2326C5ABYCXODBN4TFF5VL4FMBRHOT3YHU\",\n" + + " \"is_authorized\": false,\n" + + " \"is_authorized_to_maintain_liabilities\": true\n" + + " },"+ + " {\n" + + " \"asset_type\": \"native\",\n" + + " \"balance\": \"20.0000300\",\n" + + " \"buying_liabilities\": \"5.1234567\",\n" + + " \"selling_liabilities\": \"1.7654321\"\n" + + " }\n" + + " ],\n" + + " \"signers\": [\n" + + " {\n" + + " \"public_key\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"key\": \"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7\",\n" + + " \"weight\": 0,\n" + + " \"type\": \"ed25519_public_key\"\n" + + " },\n" + + " {\n" + + " \"public_key\": \"GCR2KBCIU6KQXSQY5F5GZYC4WLNHCHCKW4NEGXNEZRYWLTNZIRJJY7D2\",\n" + + " \"key\": \"GCR2KBCIU6KQXSQY5F5GZYC4WLNHCHCKW4NEGXNEZRYWLTNZIRJJY7D2\",\n" + + " \"weight\": 1,\n" + + " \"type\": \"ed25519_public_key\"\n" + + " }\n" + + " ],\n" + + " \"data\": {\n" + + " \"entry1\": \"dGVzdA==\",\n" + + " \"entry2\": \"dGVzdDI=\"\n" + + " }" + + "}"; + String json = "{\n" + " \"_links\": {\n" + " \"effects\": {\n" + diff --git a/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java index 5346b2155..6122697e1 100644 --- a/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/EffectDeserializerTest.java @@ -516,6 +516,43 @@ public void testDeserializeTrustlineAuthorizedEffect() { assertEquals(effect.getLinks().getPrecedes().getHref(), "http://horizon-testnet.stellar.org/effects?order=asc&cursor=33788507721730-2"); } + @Test + public void testDeserializeTrustlineAuthorizedToMaintainLiabilitiesEffect() { + String json = "{\n" + + " \"_links\": {\n" + + " \"operation\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/operations/33788507721730\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/effects?order=desc\\u0026cursor=33788507721730-2\"\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"http://horizon-testnet.stellar.org/effects?order=asc\\u0026cursor=33788507721730-2\"\n" + + " }\n" + + " },\n" + + " \"id\": \"0000033788507721730-0000000002\",\n" + + " \"paging_token\": \"33788507721730-2\",\n" + + " \"account\": \"GA6U5X6WOPNKKDKQULBR7IDHDBAQKOWPHYEC7WSXHZBFEYFD3XVZAKOO\",\n" + + " \"type\": \"trustline_authorized_to_maintain_liabilities\",\n" + + " \"type_i\": 25,\n" + + " \"asset_type\": \"credit_alphanum12\",\n" + + " \"asset_code\": \"TESTTEST\",\n" + + " \"trustor\": \"GB3E4AB4VWXJDUVN4Z3CPBU5HTMWVEQXONZYVDFMHQD6333KHCOL3UBR\"\n" + + " }"; + + TrustlineAuthorizedToMaintainLiabilitiesEffectResponse effect = (TrustlineAuthorizedToMaintainLiabilitiesEffectResponse) GsonSingleton.getInstance().fromJson(json, EffectResponse.class); + + assertEquals(effect.getAccount(), "GA6U5X6WOPNKKDKQULBR7IDHDBAQKOWPHYEC7WSXHZBFEYFD3XVZAKOO"); + assertEquals(effect.getAssetType(), "credit_alphanum12"); + assertEquals(effect.getAssetCode(), "TESTTEST"); + assertEquals(effect.getTrustor(), "GB3E4AB4VWXJDUVN4Z3CPBU5HTMWVEQXONZYVDFMHQD6333KHCOL3UBR"); + + assertEquals(effect.getLinks().getOperation().getHref(), "http://horizon-testnet.stellar.org/operations/33788507721730"); + assertEquals(effect.getLinks().getSucceeds().getHref(), "http://horizon-testnet.stellar.org/effects?order=desc&cursor=33788507721730-2"); + assertEquals(effect.getLinks().getPrecedes().getHref(), "http://horizon-testnet.stellar.org/effects?order=asc&cursor=33788507721730-2"); + } + + @Test public void testDeserializeTrustlineDeauthorizedEffect() { String json = "{\n" + From 549ae54197d290f0a4f49c28532360d6b06cdc70 Mon Sep 17 00:00:00 2001 From: tamirms Date: Tue, 28 Apr 2020 09:14:20 +0200 Subject: [PATCH 3/5] Implement partial protocol 13 support (#276) * Add support for new trustline flags * Update strkey to handle muxed accounts * Update Transaction class, specifically the conversion code to and from XDR --- .../stellar/sdk/AccountMergeOperation.java | 6 +- .../org/stellar/sdk/AllowTrustOperation.java | 45 ++- src/main/java/org/stellar/sdk/Asset.java | 8 +- .../stellar/sdk/CreateAccountOperation.java | 2 +- src/main/java/org/stellar/sdk/Operation.java | 4 +- .../PathPaymentStrictReceiveOperation.java | 4 +- .../sdk/PathPaymentStrictSendOperation.java | 4 +- .../org/stellar/sdk/PaymentOperation.java | 4 +- .../org/stellar/sdk/SetOptionsOperation.java | 2 +- src/main/java/org/stellar/sdk/StrKey.java | 145 +++++-- .../java/org/stellar/sdk/Transaction.java | 135 +++++-- .../responses/TransactionDeserializer.java | 19 +- .../AllowTrustOperationResponse.java | 10 +- .../org/stellar/sdk/xdr/AllowTrustOp.java | 13 +- .../stellar/sdk/xdr/AuthenticatedMessage.java | 8 +- .../sdk/xdr/ChangeTrustResultCode.java | 2 +- .../org/stellar/sdk/xdr/CryptoKeyType.java | 7 +- .../org/stellar/sdk/xdr/Curve25519Public.java | 2 +- .../org/stellar/sdk/xdr/Curve25519Secret.java | 2 +- .../org/stellar/sdk/xdr/EnvelopeType.java | 8 +- .../stellar/sdk/xdr/FeeBumpTransaction.java | 194 +++++++++ .../sdk/xdr/FeeBumpTransactionEnvelope.java | 73 ++++ .../org/stellar/sdk/xdr/HmacSha256Key.java | 2 +- .../org/stellar/sdk/xdr/HmacSha256Mac.java | 2 +- .../sdk/xdr/InnerTransactionResult.java | 238 +++++++++++ .../sdk/xdr/InnerTransactionResultPair.java | 62 +++ .../org/stellar/sdk/xdr/LedgerCloseMeta.java | 2 +- .../sdk/xdr/ManageBuyOfferResultCode.java | 9 +- .../sdk/xdr/ManageSellOfferResultCode.java | 20 +- .../org/stellar/sdk/xdr/MuxedAccount.java | 137 +++++++ .../java/org/stellar/sdk/xdr/Operation.java | 24 +- .../org/stellar/sdk/xdr/OperationResult.java | 2 +- .../stellar/sdk/xdr/OperationResultCode.java | 6 +- .../sdk/xdr/PathPaymentStrictReceiveOp.java | 16 +- .../PathPaymentStrictReceiveResultCode.java | 33 +- .../sdk/xdr/PathPaymentStrictSendOp.java | 20 +- .../xdr/PathPaymentStrictSendResultCode.java | 32 +- .../java/org/stellar/sdk/xdr/PaymentOp.java | 16 +- .../stellar/sdk/xdr/SurveyResponseBody.java | 4 +- .../java/org/stellar/sdk/xdr/Transaction.java | 12 +- .../stellar/sdk/xdr/TransactionEnvelope.java | 94 +++-- .../stellar/sdk/xdr/TransactionResult.java | 22 +- .../sdk/xdr/TransactionResultCode.java | 14 +- .../sdk/xdr/TransactionSignaturePayload.java | 21 +- .../org/stellar/sdk/xdr/TransactionV0.java | 181 +++++++++ .../sdk/xdr/TransactionV0Envelope.java | 73 ++++ .../sdk/xdr/TransactionV1Envelope.java | 73 ++++ .../org/stellar/sdk/xdr/TrustLineFlags.java | 7 +- .../java/org/stellar/sdk/OperationTest.java | 8 +- src/test/java/org/stellar/sdk/StrKeyTest.java | 369 ++++++++++++++++-- .../responses/OperationDeserializerTest.java | 47 ++- .../sdk/xdr/TransactionDecodeTest.java | 6 +- xdr/Stellar-ledger-entries.x | 10 +- xdr/Stellar-ledger.x | 3 +- xdr/Stellar-overlay.x | 12 +- xdr/Stellar-transaction.x | 288 ++++++++++---- xdr/Stellar-types.x | 14 +- 57 files changed, 2220 insertions(+), 356 deletions(-) create mode 100644 src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java create mode 100644 src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java create mode 100644 src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java create mode 100644 src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java create mode 100644 src/main/java/org/stellar/sdk/xdr/MuxedAccount.java create mode 100644 src/main/java/org/stellar/sdk/xdr/TransactionV0.java create mode 100644 src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java create mode 100644 src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java diff --git a/src/main/java/org/stellar/sdk/AccountMergeOperation.java b/src/main/java/org/stellar/sdk/AccountMergeOperation.java index 4c0a0baa5..3be2246d4 100644 --- a/src/main/java/org/stellar/sdk/AccountMergeOperation.java +++ b/src/main/java/org/stellar/sdk/AccountMergeOperation.java @@ -28,7 +28,7 @@ public String getDestination() { @Override OperationBody toOperationBody() { OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody(); - body.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + body.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); body.setDiscriminant(OperationType.ACCOUNT_MERGE); return body; } @@ -43,8 +43,8 @@ public static class Builder { private String mSourceAccount; Builder(OperationBody op) { - destination = StrKey.encodeStellarAccountId( - op.getDestination().getAccountID().getEd25519().getUint256() + destination = StrKey.encodeStellarMuxedAccount( + op.getDestination() ); } diff --git a/src/main/java/org/stellar/sdk/AllowTrustOperation.java b/src/main/java/org/stellar/sdk/AllowTrustOperation.java index 21ec896a2..02a682d8c 100644 --- a/src/main/java/org/stellar/sdk/AllowTrustOperation.java +++ b/src/main/java/org/stellar/sdk/AllowTrustOperation.java @@ -14,11 +14,13 @@ public class AllowTrustOperation extends Operation { private final String trustor; private final String assetCode; private final boolean authorize; + private final boolean authorizeToMaintainLiabilities; - private AllowTrustOperation(String trustor, String assetCode, boolean authorize) { + private AllowTrustOperation(String trustor, String assetCode, boolean authorize, boolean authorizeToMaintainLiabilities) { this.trustor = checkNotNull(trustor, "trustor cannot be null"); this.assetCode = checkNotNull(assetCode, "assetCode cannot be null"); this.authorize = authorize; + this.authorizeToMaintainLiabilities = authorizeToMaintainLiabilities; } /** @@ -62,8 +64,16 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { asset.setAssetCode12(assetCode12); } op.setAsset(asset); + Uint32 flag = new Uint32(); // authorize - op.setAuthorize(authorize); + if (authorize) { + flag.setUint32(TrustLineFlags.AUTHORIZED_FLAG.getValue()); + } else if (authorizeToMaintainLiabilities) { + flag.setUint32(TrustLineFlags.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG.getValue()); + } else { + flag.setUint32(0); + } + op.setAuthorize(flag); org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody(); body.setDiscriminant(OperationType.ALLOW_TRUST); @@ -79,11 +89,12 @@ public static class Builder { private final String trustor; private final String assetCode; private final boolean authorize; + private boolean authorizeToMaintainLiabilities; private String mSourceAccount; Builder(AllowTrustOp op) { - trustor = StrKey.encodeStellarAccountId(op.getTrustor().getAccountID().getEd25519().getUint256()); + trustor = StrKey.encodeStellarAccountId(op.getTrustor()); switch (op.getAsset().getDiscriminant()) { case ASSET_TYPE_CREDIT_ALPHANUM4: assetCode = new String(op.getAsset().getAssetCode4().getAssetCode4()).trim(); @@ -94,7 +105,20 @@ public static class Builder { default: throw new RuntimeException("Unknown asset code"); } - authorize = op.getAuthorize(); + + int flag = op.getAuthorize().getUint32().intValue(); + if (flag == TrustLineFlags.AUTHORIZED_FLAG.getValue()) { + authorize = true; + authorizeToMaintainLiabilities = false; + } else if (flag == TrustLineFlags.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG.getValue()) { + authorize = false; + authorizeToMaintainLiabilities = true; + } else if (flag != 0) { + throw new IllegalArgumentException("invalid authorize flag "+flag); + } else { + authorize = false; + authorizeToMaintainLiabilities = false; + } } /** @@ -123,7 +147,9 @@ public Builder setSourceAccount(String sourceAccount) { * Builds an operation */ public AllowTrustOperation build() { - AllowTrustOperation operation = new AllowTrustOperation(trustor, assetCode, authorize); + AllowTrustOperation operation = new AllowTrustOperation( + trustor, assetCode, authorize, authorizeToMaintainLiabilities + ); if (mSourceAccount != null) { operation.setSourceAccount(mSourceAccount); } @@ -133,7 +159,13 @@ public AllowTrustOperation build() { @Override public int hashCode() { - return Objects.hashCode(this.getSourceAccount(), this.assetCode, this.authorize, this.trustor); + return Objects.hashCode( + this.getSourceAccount(), + this.assetCode, + this.authorize, + this.authorizeToMaintainLiabilities, + this.trustor + ); } @Override @@ -145,6 +177,7 @@ public boolean equals(Object object) { AllowTrustOperation other = (AllowTrustOperation) object; return Objects.equal(this.assetCode, other.assetCode) && Objects.equal(this.authorize, other.authorize) && + Objects.equal(this.authorizeToMaintainLiabilities, other.authorizeToMaintainLiabilities) && Objects.equal(this.trustor, other.trustor) && Objects.equal(this.getSourceAccount(), other.getSourceAccount()); } diff --git a/src/main/java/org/stellar/sdk/Asset.java b/src/main/java/org/stellar/sdk/Asset.java index 24629645d..1fe37a741 100644 --- a/src/main/java/org/stellar/sdk/Asset.java +++ b/src/main/java/org/stellar/sdk/Asset.java @@ -41,15 +41,11 @@ public static Asset fromXdr(org.stellar.sdk.xdr.Asset xdr) { return new AssetTypeNative(); case ASSET_TYPE_CREDIT_ALPHANUM4: String assetCode4 = Util.paddedByteArrayToString(xdr.getAlphaNum4().getAssetCode().getAssetCode4()); - accountId = StrKey.encodeStellarAccountId( - xdr.getAlphaNum4().getIssuer().getAccountID().getEd25519().getUint256() - ); + accountId = StrKey.encodeStellarAccountId(xdr.getAlphaNum4().getIssuer()); return new AssetTypeCreditAlphaNum4(assetCode4, accountId); case ASSET_TYPE_CREDIT_ALPHANUM12: String assetCode12 = Util.paddedByteArrayToString(xdr.getAlphaNum12().getAssetCode().getAssetCode12()); - accountId = StrKey.encodeStellarAccountId( - xdr.getAlphaNum12().getIssuer().getAccountID().getEd25519().getUint256() - ); + accountId = StrKey.encodeStellarAccountId(xdr.getAlphaNum12().getIssuer()); return new AssetTypeCreditAlphaNum12(assetCode12, accountId); default: throw new IllegalArgumentException("Unknown asset type " + xdr.getDiscriminant()); diff --git a/src/main/java/org/stellar/sdk/CreateAccountOperation.java b/src/main/java/org/stellar/sdk/CreateAccountOperation.java index 938ed2e04..40726c209 100644 --- a/src/main/java/org/stellar/sdk/CreateAccountOperation.java +++ b/src/main/java/org/stellar/sdk/CreateAccountOperation.java @@ -64,7 +64,7 @@ public static class Builder { * @param op {@link CreateAccountOp} */ Builder(CreateAccountOp op) { - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarAccountId(op.getDestination()); startingBalance = Operation.fromXdrAmount(op.getStartingBalance().getInt64().longValue()); } diff --git a/src/main/java/org/stellar/sdk/Operation.java b/src/main/java/org/stellar/sdk/Operation.java index 55cbc3c0b..25261dc0e 100644 --- a/src/main/java/org/stellar/sdk/Operation.java +++ b/src/main/java/org/stellar/sdk/Operation.java @@ -37,7 +37,7 @@ protected static String fromXdrAmount(long value) { public org.stellar.sdk.xdr.Operation toXdr() { org.stellar.sdk.xdr.Operation xdr = new org.stellar.sdk.xdr.Operation(); if (getSourceAccount() != null) { - xdr.setSourceAccount(StrKey.encodeToXDRAccountId(mSourceAccount)); + xdr.setSourceAccount(StrKey.encodeToXDRMuxedAccount(mSourceAccount)); } xdr.setBody(toOperationBody()); return xdr; @@ -115,7 +115,7 @@ public static Operation fromXdr(org.stellar.sdk.xdr.Operation xdr) { } if (xdr.getSourceAccount() != null) { operation.setSourceAccount( - StrKey.encodeStellarAccountId(xdr.getSourceAccount().getAccountID().getEd25519().getUint256()) + StrKey.encodeStellarMuxedAccount(xdr.getSourceAccount()) ); } return operation; diff --git a/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java b/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java index cf678e63a..13d2fca89 100644 --- a/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java +++ b/src/main/java/org/stellar/sdk/PathPaymentStrictReceiveOperation.java @@ -92,7 +92,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { sendMax.setInt64(Operation.toXdrAmount(this.sendMax)); op.setSendMax(sendMax); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // destAsset op.setDestAsset(destAsset.toXdr()); // destAmount @@ -130,7 +130,7 @@ public static class Builder { Builder(PathPaymentStrictReceiveOp op) { sendAsset = Asset.fromXdr(op.getSendAsset()); sendMax = Operation.fromXdrAmount(op.getSendMax().getInt64().longValue()); - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); destAsset = Asset.fromXdr(op.getDestAsset()); destAmount = Operation.fromXdrAmount(op.getDestAmount().getInt64().longValue()); path = new Asset[op.getPath().length]; diff --git a/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java b/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java index 38ed62bb0..f0b5dfefa 100644 --- a/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java +++ b/src/main/java/org/stellar/sdk/PathPaymentStrictSendOperation.java @@ -92,7 +92,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { sendAmount.setInt64(Operation.toXdrAmount(this.sendAmount)); op.setSendAmount(sendAmount); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // destAsset op.setDestAsset(destAsset.toXdr()); // destMin @@ -130,7 +130,7 @@ public static class Builder { Builder(PathPaymentStrictSendOp op) { sendAsset = Asset.fromXdr(op.getSendAsset()); sendAmount = Operation.fromXdrAmount(op.getSendAmount().getInt64().longValue()); - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); destAsset = Asset.fromXdr(op.getDestAsset()); destMin = Operation.fromXdrAmount(op.getDestMin().getInt64().longValue()); path = new Asset[op.getPath().length]; diff --git a/src/main/java/org/stellar/sdk/PaymentOperation.java b/src/main/java/org/stellar/sdk/PaymentOperation.java index d8256b300..28b65b560 100644 --- a/src/main/java/org/stellar/sdk/PaymentOperation.java +++ b/src/main/java/org/stellar/sdk/PaymentOperation.java @@ -51,7 +51,7 @@ org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() { PaymentOp op = new PaymentOp(); // destination - op.setDestination(StrKey.encodeToXDRAccountId(this.destination)); + op.setDestination(StrKey.encodeToXDRMuxedAccount(this.destination)); // asset op.setAsset(asset.toXdr()); // amount @@ -81,7 +81,7 @@ public static class Builder { * @param op {@link PaymentOp} */ Builder(PaymentOp op) { - destination = StrKey.encodeStellarAccountId(op.getDestination().getAccountID().getEd25519().getUint256()); + destination = StrKey.encodeStellarMuxedAccount(op.getDestination()); asset = Asset.fromXdr(op.getAsset()); amount = Operation.fromXdrAmount(op.getAmount().getInt64().longValue()); } diff --git a/src/main/java/org/stellar/sdk/SetOptionsOperation.java b/src/main/java/org/stellar/sdk/SetOptionsOperation.java index ecbda7f81..073c342ca 100644 --- a/src/main/java/org/stellar/sdk/SetOptionsOperation.java +++ b/src/main/java/org/stellar/sdk/SetOptionsOperation.java @@ -190,7 +190,7 @@ public static class Builder { Builder(SetOptionsOp op) { if (op.getInflationDest() != null) { - inflationDestination = StrKey.encodeStellarAccountId(op.getInflationDest().getAccountID().getEd25519().getUint256()); + inflationDestination = StrKey.encodeStellarAccountId(op.getInflationDest()); } if (op.getClearFlags() != null) { clearFlags = op.getClearFlags().getUint32(); diff --git a/src/main/java/org/stellar/sdk/StrKey.java b/src/main/java/org/stellar/sdk/StrKey.java index be866b4d8..222622a3e 100644 --- a/src/main/java/org/stellar/sdk/StrKey.java +++ b/src/main/java/org/stellar/sdk/StrKey.java @@ -2,17 +2,19 @@ import com.google.common.io.BaseEncoding; import com.google.common.base.Optional; -import org.stellar.sdk.xdr.AccountID; -import org.stellar.sdk.xdr.PublicKey; -import org.stellar.sdk.xdr.PublicKeyType; -import org.stellar.sdk.xdr.Uint256; +import org.stellar.sdk.xdr.*; import java.io.*; import java.util.Arrays; class StrKey { + + public static final int ACCOUNT_ID_ADDRESS_LENGTH = 56; + public static final int MUXED_ACCOUNT_ADDRESS_LENGTH = 69; + public enum VersionByte { ACCOUNT_ID((byte)(6 << 3)), // G + MUXED_ACCOUNT((byte)(12 << 3)), // M SEED((byte)(18 << 3)), // S PRE_AUTH_TX((byte)(19 << 3)), // T SHA256_HASH((byte)(23 << 3)); // X @@ -41,17 +43,74 @@ public static String encodeStellarAccountId(byte[] data) { return String.valueOf(encoded); } + public static String encodeStellarAccountId(AccountID accountID) { + char[] encoded = encodeCheck(VersionByte.ACCOUNT_ID, accountID.getAccountID().getEd25519().getUint256()); + return String.valueOf(encoded); + } + + + public static String encodeStellarMuxedAccount(MuxedAccount account) { + if (account.getDiscriminant().equals(CryptoKeyType.KEY_TYPE_ED25519)) { + return encodeStellarAccountId(account.getEd25519().getUint256()); + } else if (account.getDiscriminant().equals(CryptoKeyType.KEY_TYPE_MUXED_ED25519)) { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + + try { + account.getMed25519().encode(new XdrDataOutputStream(byteStream)); + } catch (IOException e) { + throw new IllegalArgumentException("invalid muxed account", e); + } + + char[] encoded = encodeCheck(VersionByte.MUXED_ACCOUNT, byteStream.toByteArray()); + return String.valueOf(encoded); + } + throw new IllegalArgumentException("invalid muxed account type: "+account.getDiscriminant()); + } + + public static AccountID encodeToXDRAccountId(String data) { AccountID accountID = new AccountID(); PublicKey publicKey = new PublicKey(); publicKey.setDiscriminant(PublicKeyType.PUBLIC_KEY_TYPE_ED25519); - Uint256 uint256 = new Uint256(); - uint256.setUint256(decodeStellarAccountId(data)); - publicKey.setEd25519(uint256); + try { + publicKey.setEd25519(Uint256.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarAccountId(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } accountID.setAccountID(publicKey); return accountID; } + public static MuxedAccount encodeToXDRMuxedAccount(String data) { + if (data.length() == ACCOUNT_ID_ADDRESS_LENGTH) { + MuxedAccount accountID = new MuxedAccount(); + accountID.setDiscriminant(CryptoKeyType.KEY_TYPE_ED25519); + try { + accountID.setEd25519(Uint256.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarAccountId(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } + return accountID; + } else if (data.length() == MUXED_ACCOUNT_ADDRESS_LENGTH) { + MuxedAccount muxedAccount = new MuxedAccount(); + muxedAccount.setDiscriminant(CryptoKeyType.KEY_TYPE_MUXED_ED25519); + try { + muxedAccount.setMed25519(MuxedAccount.MuxedAccountMed25519.decode( + new XdrDataInputStream(new ByteArrayInputStream(decodeStellarMuxedAccount(data))) + )); + } catch (IOException e) { + throw new IllegalArgumentException("invalid address: "+data, e); + } + return muxedAccount; + } + throw new IllegalArgumentException("invalid address length: "+data); + } + + public static VersionByte decodeVersionByte(String data) { byte[] decoded = StrKey.base32Encoding.decode(java.nio.CharBuffer.wrap(data.toCharArray())); byte decodedVersionByte = decoded[0]; @@ -66,6 +125,11 @@ public static byte[] decodeStellarAccountId(String data) { return decodeCheck(VersionByte.ACCOUNT_ID, data.toCharArray()); } + public static byte[] decodeStellarMuxedAccount(String data) { + return decodeCheck(VersionByte.MUXED_ACCOUNT, data.toCharArray()); + } + + public static char[] encodeStellarSecretSeed(byte[] data) { return encodeCheck(VersionByte.SEED, data); } @@ -102,6 +166,10 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { outputStream.write(checksum); byte unencoded[] = outputStream.toByteArray(); + if (VersionByte.SEED != versionByte) { + return StrKey.base32Encoding.encode(unencoded).toCharArray(); + } + // Why not use base32Encoding.encode here? // We don't want secret seed to be stored as String in memory because of security reasons. It's impossible // to erase it from memory when we want it to be erased (ASAP). @@ -110,18 +178,16 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { charOutputStream.write(unencoded); char[] charsEncoded = charArrayWriter.toCharArray(); - if (VersionByte.SEED == versionByte) { - Arrays.fill(unencoded, (byte) 0); - Arrays.fill(payload, (byte) 0); - Arrays.fill(checksum, (byte) 0); - - // Clean charArrayWriter internal buffer - int bufferSize = charArrayWriter.size(); - char[] zeros = new char[bufferSize]; - Arrays.fill(zeros, '0'); - charArrayWriter.reset(); - charArrayWriter.write(zeros); - } + Arrays.fill(unencoded, (byte) 0); + Arrays.fill(payload, (byte) 0); + Arrays.fill(checksum, (byte) 0); + + // Clean charArrayWriter internal buffer + int bufferSize = charArrayWriter.size(); + char[] zeros = new char[bufferSize]; + Arrays.fill(zeros, '0'); + charArrayWriter.reset(); + charArrayWriter.write(zeros); return charsEncoded; } catch (IOException e) { @@ -132,12 +198,34 @@ protected static char[] encodeCheck(VersionByte versionByte, byte[] data) { protected static byte[] decodeCheck(VersionByte versionByte, char[] encoded) { byte[] bytes = new byte[encoded.length]; for (int i = 0; i < encoded.length; i++) { - if (encoded[i] > 127) { - throw new IllegalArgumentException("Illegal characters in encoded char array."); - } bytes[i] = (byte) encoded[i]; } + // The minimal binary decoded length is 3 bytes (version byte and 2-byte CRC) which, + // in unpadded base32 (since each character provides 5 bits) corresponds to ceiling(8*3/5) = 5 + if (bytes.length < 5) { + throw new IllegalArgumentException("Encoded char array must have a length of at least 5."); + } + + int leftoverBits = (bytes.length * 5) % 8; + // 1. Make sure there is no full unused leftover byte at the end + // (i.e. there shouldn't be 5 or more leftover bits) + if (leftoverBits >= 5) { + throw new IllegalArgumentException("Encoded char array has leftover character."); + } + + if (leftoverBits > 0) { + byte lastChar = bytes[bytes.length-1]; + byte decodedLastChar = b32Table[lastChar]; + + + + byte leftoverBitsMask = (byte)(0x0f >> (4 - leftoverBits)); + if ((decodedLastChar & leftoverBitsMask) != 0) { + throw new IllegalArgumentException("Unused bits should be set to 0."); + } + } + byte[] decoded = StrKey.base32Encoding.decode(java.nio.CharBuffer.wrap(encoded)); byte decodedVersionByte = decoded[0]; byte[] payload = Arrays.copyOfRange(decoded, 0, decoded.length-2); @@ -189,4 +277,17 @@ protected static byte[] calculateChecksum(byte[] bytes) { (byte)crc, (byte)(crc >>> 8)}; } + + private static final byte[] b32Table = decodingTable(); + private static byte[] decodingTable() { + byte[] table = new byte[256]; + for (int i=0; i <256; i++) { + table[i] = (byte)0xff; + } + String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + for(int i=0; i < alphabet.length(); i++) { + table[(int)alphabet.charAt(i)] = (byte)i; + } + return table; + } } diff --git a/src/main/java/org/stellar/sdk/Transaction.java b/src/main/java/org/stellar/sdk/Transaction.java index c653879ff..71742939a 100644 --- a/src/main/java/org/stellar/sdk/Transaction.java +++ b/src/main/java/org/stellar/sdk/Transaction.java @@ -88,6 +88,27 @@ public byte[] hash() { return Util.hash(this.signatureBase()); } + private org.stellar.sdk.xdr.Transaction convertV0Tx(TransactionV0 v0Tx) { + org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction(); + + v1Tx.setMemo(v0Tx.getMemo()); + v1Tx.setFee(v0Tx.getFee()); + v1Tx.setOperations(v0Tx.getOperations()); + v1Tx.setSeqNum(v0Tx.getSeqNum()); + v1Tx.setTimeBounds(v0Tx.getTimeBounds()); + + org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); + ext.setDiscriminant(0); + v1Tx.setExt(ext); + + MuxedAccount sourceAccount = new MuxedAccount(); + sourceAccount.setDiscriminant(CryptoKeyType.KEY_TYPE_ED25519); + sourceAccount.setEd25519(v0Tx.getSourceAccountEd25519()); + v1Tx.setSourceAccount(sourceAccount); + + return v1Tx; + } + /** * Returns signature base. */ @@ -101,7 +122,7 @@ public byte[] signatureBase() { // Transaction XDR bytes ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream(); XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream); - org.stellar.sdk.xdr.Transaction.encode(xdrOutputStream, this.toXdr()); + convertV0Tx(this.toXdr()).encode(xdrOutputStream); outputStream.write(txOutputStream.toByteArray()); return outputStream.toByteArray(); @@ -154,14 +175,14 @@ public Operation[] getOperations() { /** * Generates Transaction XDR object. */ - public org.stellar.sdk.xdr.Transaction toXdr() { + private TransactionV0 toXdr() { // fee - org.stellar.sdk.xdr.Uint32 fee = new org.stellar.sdk.xdr.Uint32(); + Uint32 fee = new Uint32(); fee.setUint32(mFee); // sequenceNumber - org.stellar.sdk.xdr.Int64 sequenceNumberUint = new org.stellar.sdk.xdr.Int64(); + Int64 sequenceNumberUint = new Int64(); sequenceNumberUint.setInt64(mSequenceNumber); - org.stellar.sdk.xdr.SequenceNumber sequenceNumber = new org.stellar.sdk.xdr.SequenceNumber(); + SequenceNumber sequenceNumber = new SequenceNumber(); sequenceNumber.setSequenceNumber(sequenceNumberUint); // operations org.stellar.sdk.xdr.Operation[] operations = new org.stellar.sdk.xdr.Operation[mOperations.length]; @@ -169,13 +190,13 @@ public org.stellar.sdk.xdr.Transaction toXdr() { operations[i] = mOperations[i].toXdr(); } // ext - org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); + TransactionV0.TransactionV0Ext ext = new TransactionV0.TransactionV0Ext(); ext.setDiscriminant(0); - org.stellar.sdk.xdr.Transaction transaction = new org.stellar.sdk.xdr.Transaction(); + TransactionV0 transaction = new TransactionV0(); transaction.setFee(fee); transaction.setSeqNum(sequenceNumber); - transaction.setSourceAccount(StrKey.encodeToXDRAccountId(this.mSourceAccount)); + transaction.setSourceAccountEd25519(StrKey.encodeToXDRAccountId(this.mSourceAccount).getAccountID().getEd25519()); transaction.setOperations(operations); transaction.setMemo(mMemo.toXdr()); transaction.setTimeBounds(mTimeBounds == null ? null : mTimeBounds.toXdr()); @@ -197,31 +218,53 @@ public static Transaction fromEnvelopeXdr(String envelope, Network network) thro return fromEnvelopeXdr(transactionEnvelope, network); } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope - * @return - */ - public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { - org.stellar.sdk.xdr.Transaction tx = envelope.getTx(); - int mFee = tx.getFee().getUint32(); - Long mSequenceNumber = tx.getSeqNum().getSequenceNumber().getInt64(); - Memo mMemo = Memo.fromXdr(tx.getMemo()); - TimeBounds mTimeBounds = TimeBounds.fromXdr(tx.getTimeBounds()); - - Operation[] mOperations = new Operation[tx.getOperations().length]; - for (int i = 0; i < tx.getOperations().length; i++) { - mOperations[i] = Operation.fromXdr(tx.getOperations()[i]); + public static Transaction fromV0EnvelopeXdr(TransactionV0Envelope envelope, Network network) { + int mFee = envelope.getTx().getFee().getUint32(); + Long mSequenceNumber = envelope.getTx().getSeqNum().getSequenceNumber().getInt64(); + Memo mMemo = Memo.fromXdr(envelope.getTx().getMemo()); + TimeBounds mTimeBounds = TimeBounds.fromXdr(envelope.getTx().getTimeBounds()); + + Operation[] mOperations = new Operation[envelope.getTx().getOperations().length]; + for (int i = 0; i < envelope.getTx().getOperations().length; i++) { + mOperations[i] = Operation.fromXdr(envelope.getTx().getOperations()[i]); + } + + Transaction transaction = new Transaction( + StrKey.encodeStellarAccountId(envelope.getTx().getSourceAccountEd25519().getUint256()), + mFee, + mSequenceNumber, + mOperations, + mMemo, + mTimeBounds, + network + ); + + for (DecoratedSignature signature : envelope.getSignatures()) { + transaction.mSignatures.add(signature); + } + + return transaction; + } + + public static Transaction fromV1EnvelopeXdr(TransactionV1Envelope envelope, Network network) { + int mFee = envelope.getTx().getFee().getUint32(); + Long mSequenceNumber = envelope.getTx().getSeqNum().getSequenceNumber().getInt64(); + Memo mMemo = Memo.fromXdr(envelope.getTx().getMemo()); + TimeBounds mTimeBounds = TimeBounds.fromXdr(envelope.getTx().getTimeBounds()); + + Operation[] mOperations = new Operation[envelope.getTx().getOperations().length]; + for (int i = 0; i < envelope.getTx().getOperations().length; i++) { + mOperations[i] = Operation.fromXdr(envelope.getTx().getOperations()[i]); } Transaction transaction = new Transaction( - StrKey.encodeStellarAccountId(tx.getSourceAccount().getAccountID().getEd25519().getUint256()), - mFee, - mSequenceNumber, - mOperations, - mMemo, - mTimeBounds, - network + StrKey.encodeStellarMuxedAccount(envelope.getTx().getSourceAccount()), + mFee, + mSequenceNumber, + mOperations, + mMemo, + mTimeBounds, + network ); for (DecoratedSignature signature : envelope.getSignatures()) { @@ -231,17 +274,35 @@ public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network return transaction; } + /** + * Creates a Transaction instance from previously build TransactionEnvelope + * @param envelope + * @return + */ + public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { + switch (envelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + return fromV1EnvelopeXdr(envelope.getV1(), network); + case ENVELOPE_TYPE_TX_V0: + return fromV0EnvelopeXdr(envelope.getV0(), network); + default: + throw new IllegalArgumentException("transaction type is not supported: "+envelope.getDiscriminant()); + } + } + /** * Generates TransactionEnvelope XDR object. */ - public org.stellar.sdk.xdr.TransactionEnvelope toEnvelopeXdr() { - org.stellar.sdk.xdr.TransactionEnvelope xdr = new org.stellar.sdk.xdr.TransactionEnvelope(); - org.stellar.sdk.xdr.Transaction transaction = this.toXdr(); - xdr.setTx(transaction); + public TransactionEnvelope toEnvelopeXdr() { + TransactionEnvelope xdr = new TransactionEnvelope(); + TransactionV0Envelope v0Envelope = new TransactionV0Envelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_V0); + v0Envelope.setTx(this.toXdr()); DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; signatures = mSignatures.toArray(signatures); - xdr.setSignatures(signatures); + v0Envelope.setSignatures(signatures); + xdr.setV0(v0Envelope); return xdr; } @@ -250,10 +311,10 @@ public org.stellar.sdk.xdr.TransactionEnvelope toEnvelopeXdr() { */ public String toEnvelopeXdrBase64() { try { - org.stellar.sdk.xdr.TransactionEnvelope envelope = this.toEnvelopeXdr(); + TransactionEnvelope envelope = this.toEnvelopeXdr(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream); - org.stellar.sdk.xdr.TransactionEnvelope.encode(xdrOutputStream, envelope); + TransactionEnvelope.encode(xdrOutputStream, envelope); BaseEncoding base64Encoding = BaseEncoding.base64(); return base64Encoding.encode(outputStream.toByteArray()); diff --git a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java index 9bf14bf9d..771d0f762 100644 --- a/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java +++ b/src/main/java/org/stellar/sdk/responses/TransactionDeserializer.java @@ -12,6 +12,23 @@ import java.lang.reflect.Type; public class TransactionDeserializer implements JsonDeserializer { + + private Memo extractTextMemo(TransactionEnvelope transactionEnvelope) { + switch (transactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + return Memo.text(transactionEnvelope.getV1().getTx().getMemo().getText().getBytes()); + case ENVELOPE_TYPE_TX_V0: + return Memo.text(transactionEnvelope.getV0().getTx().getMemo().getText().getBytes()); + case ENVELOPE_TYPE_TX_FEE_BUMP: + return Memo.text( + transactionEnvelope.getFeeBump().getTx().getInnerTx() + .getV1().getTx().getMemo().getText().getBytes() + ); + default: + throw new IllegalArgumentException("invalid transaction type: "+transactionEnvelope.getDiscriminant()); + } + } + @Override public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // Create new Gson object with adapters needed in Transaction @@ -50,7 +67,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().getBytes()); + memo = extractTextMemo(transactionEnvelope); } } else { String memoValue = json.getAsJsonObject().get("memo").getAsString(); diff --git a/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java b/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java index 991283960..9d648ffa5 100644 --- a/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java +++ b/src/main/java/org/stellar/sdk/responses/operations/AllowTrustOperationResponse.java @@ -24,9 +24,12 @@ public class AllowTrustOperationResponse extends OperationResponse { protected final String assetIssuer; @SerializedName("authorize") protected final boolean authorize; + @SerializedName("authorize_to_maintain_liabilities") + protected final boolean authorizeToMaintainLiabilities; - AllowTrustOperationResponse(boolean authorize, String assetIssuer, String assetCode, String assetType, String trustee, String trustor) { + AllowTrustOperationResponse(boolean authorize, boolean authorizeToMaintainLiabilities, String assetIssuer, String assetCode, String assetType, String trustee, String trustor) { this.authorize = authorize; + this.authorizeToMaintainLiabilities = authorizeToMaintainLiabilities; this.assetIssuer = assetIssuer; this.assetCode = assetCode; this.assetType = assetType; @@ -46,6 +49,11 @@ public boolean isAuthorize() { return authorize; } + public boolean isAuthorizedToMaintainLiabilities() { + return authorizeToMaintainLiabilities; + } + + public Asset getAsset() { if (assetType.equals("native")) { return new AssetTypeNative(); diff --git a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java index 6cd680e19..e836b2fae 100644 --- a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java +++ b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java @@ -26,7 +26,8 @@ // } // asset; // -// bool authorize; +// // 0, or any bitwise combination of TrustLineFlags +// uint32 authorize; // }; // =========================================================================== @@ -46,17 +47,17 @@ public AllowTrustOpAsset getAsset() { public void setAsset(AllowTrustOpAsset value) { this.asset = value; } - private Boolean authorize; - public Boolean getAuthorize() { + private Uint32 authorize; + public Uint32 getAuthorize() { return this.authorize; } - public void setAuthorize(Boolean value) { + public void setAuthorize(Uint32 value) { this.authorize = value; } public static void encode(XdrDataOutputStream stream, AllowTrustOp encodedAllowTrustOp) throws IOException{ AccountID.encode(stream, encodedAllowTrustOp.trustor); AllowTrustOpAsset.encode(stream, encodedAllowTrustOp.asset); - stream.writeInt(encodedAllowTrustOp.authorize ? 1 : 0); + Uint32.encode(stream, encodedAllowTrustOp.authorize); } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); @@ -65,7 +66,7 @@ public static AllowTrustOp decode(XdrDataInputStream stream) throws IOException AllowTrustOp decodedAllowTrustOp = new AllowTrustOp(); decodedAllowTrustOp.trustor = AccountID.decode(stream); decodedAllowTrustOp.asset = AllowTrustOpAsset.decode(stream); - decodedAllowTrustOp.authorize = stream.readInt() == 1 ? true : false; + decodedAllowTrustOp.authorize = Uint32.decode(stream); return decodedAllowTrustOp; } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java index 73d1c7855..559567c30 100644 --- a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java @@ -14,10 +14,10 @@ // { // case 0: // struct -// { -// uint64 sequence; -// StellarMessage message; -// HmacSha256Mac mac; +// { +// uint64 sequence; +// StellarMessage message; +// HmacSha256Mac mac; // } v0; // }; diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java index 52f3523ab..e351dae2e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java @@ -20,7 +20,7 @@ // // cannot create with a limit of 0 // CHANGE_TRUST_LOW_RESERVE = // -4, // not enough funds to create a new trust line, -// CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed +// CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java index bdbfab00d..bfaf14e5c 100644 --- a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java +++ b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java @@ -13,7 +13,10 @@ // { // KEY_TYPE_ED25519 = 0, // KEY_TYPE_PRE_AUTH_TX = 1, -// KEY_TYPE_HASH_X = 2 +// KEY_TYPE_HASH_X = 2, +// // MUXED enum values for supported type are derived from the enum values +// // above by ORing them with 0x100 +// KEY_TYPE_MUXED_ED25519 = 0x100 // }; // =========================================================================== @@ -21,6 +24,7 @@ public enum CryptoKeyType implements XdrElement { KEY_TYPE_ED25519(0), KEY_TYPE_PRE_AUTH_TX(1), KEY_TYPE_HASH_X(2), + KEY_TYPE_MUXED_ED25519(256), ; private int mValue; @@ -38,6 +42,7 @@ public static CryptoKeyType decode(XdrDataInputStream stream) throws IOException case 0: return KEY_TYPE_ED25519; case 1: return KEY_TYPE_PRE_AUTH_TX; case 2: return KEY_TYPE_HASH_X; + case 256: return KEY_TYPE_MUXED_ED25519; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java index e3c00fccd..5d70e454d 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java @@ -12,7 +12,7 @@ // struct Curve25519Public // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java index 68677bc24..491c1b404 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java @@ -12,7 +12,7 @@ // struct Curve25519Secret // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java index 3ff079281..90bfba5e6 100644 --- a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java +++ b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java @@ -11,18 +11,22 @@ // enum EnvelopeType // { +// ENVELOPE_TYPE_TX_V0 = 0, // ENVELOPE_TYPE_SCP = 1, // ENVELOPE_TYPE_TX = 2, // ENVELOPE_TYPE_AUTH = 3, -// ENVELOPE_TYPE_SCPVALUE = 4 +// ENVELOPE_TYPE_SCPVALUE = 4, +// ENVELOPE_TYPE_TX_FEE_BUMP = 5 // }; // =========================================================================== public enum EnvelopeType implements XdrElement { + ENVELOPE_TYPE_TX_V0(0), ENVELOPE_TYPE_SCP(1), ENVELOPE_TYPE_TX(2), ENVELOPE_TYPE_AUTH(3), ENVELOPE_TYPE_SCPVALUE(4), + ENVELOPE_TYPE_TX_FEE_BUMP(5), ; private int mValue; @@ -37,10 +41,12 @@ public int getValue() { public static EnvelopeType decode(XdrDataInputStream stream) throws IOException { int value = stream.readInt(); switch (value) { + case 0: return ENVELOPE_TYPE_TX_V0; case 1: return ENVELOPE_TYPE_SCP; case 2: return ENVELOPE_TYPE_TX; case 3: return ENVELOPE_TYPE_AUTH; case 4: return ENVELOPE_TYPE_SCPVALUE; + case 5: return ENVELOPE_TYPE_TX_FEE_BUMP; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java new file mode 100644 index 000000000..69c65bd4a --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java @@ -0,0 +1,194 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// struct FeeBumpTransaction +// { +// MuxedAccount feeSource; +// int64 fee; +// union switch (EnvelopeType type) +// { +// case ENVELOPE_TYPE_TX: +// TransactionV1Envelope v1; +// } +// innerTx; +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class FeeBumpTransaction implements XdrElement { + public FeeBumpTransaction () {} + private MuxedAccount feeSource; + public MuxedAccount getFeeSource() { + return this.feeSource; + } + public void setFeeSource(MuxedAccount value) { + this.feeSource = value; + } + private Int64 fee; + public Int64 getFee() { + return this.fee; + } + public void setFee(Int64 value) { + this.fee = value; + } + private FeeBumpTransactionInnerTx innerTx; + public FeeBumpTransactionInnerTx getInnerTx() { + return this.innerTx; + } + public void setInnerTx(FeeBumpTransactionInnerTx value) { + this.innerTx = value; + } + private FeeBumpTransactionExt ext; + public FeeBumpTransactionExt getExt() { + return this.ext; + } + public void setExt(FeeBumpTransactionExt value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransaction encodedFeeBumpTransaction) throws IOException{ + MuxedAccount.encode(stream, encodedFeeBumpTransaction.feeSource); + Int64.encode(stream, encodedFeeBumpTransaction.fee); + FeeBumpTransactionInnerTx.encode(stream, encodedFeeBumpTransaction.innerTx); + FeeBumpTransactionExt.encode(stream, encodedFeeBumpTransaction.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransaction decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransaction decodedFeeBumpTransaction = new FeeBumpTransaction(); + decodedFeeBumpTransaction.feeSource = MuxedAccount.decode(stream); + decodedFeeBumpTransaction.fee = Int64.decode(stream); + decodedFeeBumpTransaction.innerTx = FeeBumpTransactionInnerTx.decode(stream); + decodedFeeBumpTransaction.ext = FeeBumpTransactionExt.decode(stream); + return decodedFeeBumpTransaction; + } + @Override + public int hashCode() { + return Objects.hashCode(this.feeSource, this.fee, this.innerTx, this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransaction)) { + return false; + } + + FeeBumpTransaction other = (FeeBumpTransaction) object; + return Objects.equal(this.feeSource, other.feeSource) && Objects.equal(this.fee, other.fee) && Objects.equal(this.innerTx, other.innerTx) && Objects.equal(this.ext, other.ext); + } + + public static class FeeBumpTransactionInnerTx { + public FeeBumpTransactionInnerTx () {} + EnvelopeType type; + public EnvelopeType getDiscriminant() { + return this.type; + } + public void setDiscriminant(EnvelopeType value) { + this.type = value; + } + private TransactionV1Envelope v1; + public TransactionV1Envelope getV1() { + return this.v1; + } + public void setV1(TransactionV1Envelope value) { + this.v1 = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionInnerTx encodedFeeBumpTransactionInnerTx) throws IOException { + //Xdrgen::AST::Identifier + //EnvelopeType + stream.writeInt(encodedFeeBumpTransactionInnerTx.getDiscriminant().getValue()); + switch (encodedFeeBumpTransactionInnerTx.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + TransactionV1Envelope.encode(stream, encodedFeeBumpTransactionInnerTx.v1); + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionInnerTx decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionInnerTx decodedFeeBumpTransactionInnerTx = new FeeBumpTransactionInnerTx(); + EnvelopeType discriminant = EnvelopeType.decode(stream); + decodedFeeBumpTransactionInnerTx.setDiscriminant(discriminant); + switch (decodedFeeBumpTransactionInnerTx.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + decodedFeeBumpTransactionInnerTx.v1 = TransactionV1Envelope.decode(stream); + break; + } + return decodedFeeBumpTransactionInnerTx; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v1, this.type); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionInnerTx)) { + return false; + } + + FeeBumpTransactionInnerTx other = (FeeBumpTransactionInnerTx) object; + return Objects.equal(this.v1, other.v1) && Objects.equal(this.type, other.type); + } + + } + public static class FeeBumpTransactionExt { + public FeeBumpTransactionExt () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionExt encodedFeeBumpTransactionExt) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedFeeBumpTransactionExt.getDiscriminant().intValue()); + switch (encodedFeeBumpTransactionExt.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionExt decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionExt decodedFeeBumpTransactionExt = new FeeBumpTransactionExt(); + Integer discriminant = stream.readInt(); + decodedFeeBumpTransactionExt.setDiscriminant(discriminant); + switch (decodedFeeBumpTransactionExt.getDiscriminant()) { + case 0: + break; + } + return decodedFeeBumpTransactionExt; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionExt)) { + return false; + } + + FeeBumpTransactionExt other = (FeeBumpTransactionExt) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java new file mode 100644 index 000000000..d19e67cc6 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct FeeBumpTransactionEnvelope +// { +// FeeBumpTransaction tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class FeeBumpTransactionEnvelope implements XdrElement { + public FeeBumpTransactionEnvelope () {} + private FeeBumpTransaction tx; + public FeeBumpTransaction getTx() { + return this.tx; + } + public void setTx(FeeBumpTransaction value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, FeeBumpTransactionEnvelope encodedFeeBumpTransactionEnvelope) throws IOException{ + FeeBumpTransaction.encode(stream, encodedFeeBumpTransactionEnvelope.tx); + int signaturessize = encodedFeeBumpTransactionEnvelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedFeeBumpTransactionEnvelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static FeeBumpTransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + FeeBumpTransactionEnvelope decodedFeeBumpTransactionEnvelope = new FeeBumpTransactionEnvelope(); + decodedFeeBumpTransactionEnvelope.tx = FeeBumpTransaction.decode(stream); + int signaturessize = stream.readInt(); + decodedFeeBumpTransactionEnvelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedFeeBumpTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedFeeBumpTransactionEnvelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransactionEnvelope)) { + return false; + } + + FeeBumpTransactionEnvelope other = (FeeBumpTransactionEnvelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java index fc2b0e716..248437704 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java @@ -12,7 +12,7 @@ // struct HmacSha256Key // { -// opaque key[32]; +// opaque key[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java index 1bfbcfd8f..f20647c21 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java @@ -12,7 +12,7 @@ // struct HmacSha256Mac // { -// opaque mac[32]; +// opaque mac[32]; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java new file mode 100644 index 000000000..6da5783c3 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java @@ -0,0 +1,238 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct InnerTransactionResult +// { +// // Always 0. Here for binary compatibility. +// int64 feeCharged; +// +// union switch (TransactionResultCode code) +// { +// // txFEE_BUMP_INNER_SUCCESS is not included +// case txSUCCESS: +// case txFAILED: +// OperationResult results<>; +// case txTOO_EARLY: +// case txTOO_LATE: +// case txMISSING_OPERATION: +// case txBAD_SEQ: +// case txBAD_AUTH: +// case txINSUFFICIENT_BALANCE: +// case txNO_ACCOUNT: +// case txINSUFFICIENT_FEE: +// case txBAD_AUTH_EXTRA: +// case txINTERNAL_ERROR: +// case txNOT_SUPPORTED: +// // txFEE_BUMP_INNER_FAILED is not included +// void; +// } +// result; +// +// // reserved for future use +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class InnerTransactionResult implements XdrElement { + public InnerTransactionResult () {} + private Int64 feeCharged; + public Int64 getFeeCharged() { + return this.feeCharged; + } + public void setFeeCharged(Int64 value) { + this.feeCharged = value; + } + private InnerTransactionResultResult result; + public InnerTransactionResultResult getResult() { + return this.result; + } + public void setResult(InnerTransactionResultResult value) { + this.result = value; + } + private InnerTransactionResultExt ext; + public InnerTransactionResultExt getExt() { + return this.ext; + } + public void setExt(InnerTransactionResultExt value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResult encodedInnerTransactionResult) throws IOException{ + Int64.encode(stream, encodedInnerTransactionResult.feeCharged); + InnerTransactionResultResult.encode(stream, encodedInnerTransactionResult.result); + InnerTransactionResultExt.encode(stream, encodedInnerTransactionResult.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResult decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResult decodedInnerTransactionResult = new InnerTransactionResult(); + decodedInnerTransactionResult.feeCharged = Int64.decode(stream); + decodedInnerTransactionResult.result = InnerTransactionResultResult.decode(stream); + decodedInnerTransactionResult.ext = InnerTransactionResultExt.decode(stream); + return decodedInnerTransactionResult; + } + @Override + public int hashCode() { + return Objects.hashCode(this.feeCharged, this.result, this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResult)) { + return false; + } + + InnerTransactionResult other = (InnerTransactionResult) object; + return Objects.equal(this.feeCharged, other.feeCharged) && Objects.equal(this.result, other.result) && Objects.equal(this.ext, other.ext); + } + + public static class InnerTransactionResultResult { + public InnerTransactionResultResult () {} + TransactionResultCode code; + public TransactionResultCode getDiscriminant() { + return this.code; + } + public void setDiscriminant(TransactionResultCode value) { + this.code = value; + } + private OperationResult[] results; + public OperationResult[] getResults() { + return this.results; + } + public void setResults(OperationResult[] value) { + this.results = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultResult encodedInnerTransactionResultResult) throws IOException { + //Xdrgen::AST::Identifier + //TransactionResultCode + stream.writeInt(encodedInnerTransactionResultResult.getDiscriminant().getValue()); + switch (encodedInnerTransactionResultResult.getDiscriminant()) { + case txSUCCESS: + case txFAILED: + int resultssize = encodedInnerTransactionResultResult.getResults().length; + stream.writeInt(resultssize); + for (int i = 0; i < resultssize; i++) { + OperationResult.encode(stream, encodedInnerTransactionResultResult.results[i]); + } + break; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultResult decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultResult decodedInnerTransactionResultResult = new InnerTransactionResultResult(); + TransactionResultCode discriminant = TransactionResultCode.decode(stream); + decodedInnerTransactionResultResult.setDiscriminant(discriminant); + switch (decodedInnerTransactionResultResult.getDiscriminant()) { + case txSUCCESS: + case txFAILED: + int resultssize = stream.readInt(); + decodedInnerTransactionResultResult.results = new OperationResult[resultssize]; + for (int i = 0; i < resultssize; i++) { + decodedInnerTransactionResultResult.results[i] = OperationResult.decode(stream); + } + break; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + break; + } + return decodedInnerTransactionResultResult; + } + @Override + public int hashCode() { + return Objects.hashCode(Arrays.hashCode(this.results), this.code); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultResult)) { + return false; + } + + InnerTransactionResultResult other = (InnerTransactionResultResult) object; + return Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); + } + + } + public static class InnerTransactionResultExt { + public InnerTransactionResultExt () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultExt encodedInnerTransactionResultExt) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedInnerTransactionResultExt.getDiscriminant().intValue()); + switch (encodedInnerTransactionResultExt.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultExt decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultExt decodedInnerTransactionResultExt = new InnerTransactionResultExt(); + Integer discriminant = stream.readInt(); + decodedInnerTransactionResultExt.setDiscriminant(discriminant); + switch (decodedInnerTransactionResultExt.getDiscriminant()) { + case 0: + break; + } + return decodedInnerTransactionResultExt; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultExt)) { + return false; + } + + InnerTransactionResultExt other = (InnerTransactionResultExt) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java new file mode 100644 index 000000000..d8edc64ef --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java @@ -0,0 +1,62 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// struct InnerTransactionResultPair +// { +// Hash transactionHash; // hash of the inner transaction +// InnerTransactionResult result; // result for the inner transaction +// }; + +// =========================================================================== +public class InnerTransactionResultPair implements XdrElement { + public InnerTransactionResultPair () {} + private Hash transactionHash; + public Hash getTransactionHash() { + return this.transactionHash; + } + public void setTransactionHash(Hash value) { + this.transactionHash = value; + } + private InnerTransactionResult result; + public InnerTransactionResult getResult() { + return this.result; + } + public void setResult(InnerTransactionResult value) { + this.result = value; + } + public static void encode(XdrDataOutputStream stream, InnerTransactionResultPair encodedInnerTransactionResultPair) throws IOException{ + Hash.encode(stream, encodedInnerTransactionResultPair.transactionHash); + InnerTransactionResult.encode(stream, encodedInnerTransactionResultPair.result); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static InnerTransactionResultPair decode(XdrDataInputStream stream) throws IOException { + InnerTransactionResultPair decodedInnerTransactionResultPair = new InnerTransactionResultPair(); + decodedInnerTransactionResultPair.transactionHash = Hash.decode(stream); + decodedInnerTransactionResultPair.result = InnerTransactionResult.decode(stream); + return decodedInnerTransactionResultPair; + } + @Override + public int hashCode() { + return Objects.hashCode(this.transactionHash, this.result); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof InnerTransactionResultPair)) { + return false; + } + + InnerTransactionResultPair other = (InnerTransactionResultPair) object; + return Objects.equal(this.transactionHash, other.transactionHash) && Objects.equal(this.result, other.result); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java index 9aac2d562..991db5d71 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java @@ -13,7 +13,7 @@ // union LedgerCloseMeta switch (int v) // { // case 0: -// LedgerCloseMetaV0 v0; +// LedgerCloseMetaV0 v0; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java index 1e8c7a882..d96b0a4ee 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java @@ -20,14 +20,15 @@ // MANAGE_BUY_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying // MANAGE_BUY_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell // MANAGE_BUY_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy -// MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying -// MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell -// MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user +// MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying +// MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell +// MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user // MANAGE_BUY_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling // MANAGE_BUY_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // // // update errors -// MANAGE_BUY_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer +// MANAGE_BUY_OFFER_NOT_FOUND = +// -11, // offerID does not match an existing offer // // MANAGE_BUY_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer // }; diff --git a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java index c5d501b53..c7e990600 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java @@ -15,21 +15,25 @@ // MANAGE_SELL_OFFER_SUCCESS = 0, // // // codes considered as "failure" for the operation -// MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid -// MANAGE_SELL_OFFER_SELL_NO_TRUST = -2, // no trust line for what we're selling -// MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying +// MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid +// MANAGE_SELL_OFFER_SELL_NO_TRUST = +// -2, // no trust line for what we're selling +// MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying // MANAGE_SELL_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell // MANAGE_SELL_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy -// MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying -// MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell -// MANAGE_SELL_OFFER_CROSS_SELF = -8, // would cross an offer from the same user +// MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying +// MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell +// MANAGE_SELL_OFFER_CROSS_SELF = +// -8, // would cross an offer from the same user // MANAGE_SELL_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling // MANAGE_SELL_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // // // update errors -// MANAGE_SELL_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer +// MANAGE_SELL_OFFER_NOT_FOUND = +// -11, // offerID does not match an existing offer // -// MANAGE_SELL_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer +// MANAGE_SELL_OFFER_LOW_RESERVE = +// -12 // not enough funds to create a new Offer // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java new file mode 100644 index 000000000..b44095e34 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java @@ -0,0 +1,137 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; + +// === xdr source ============================================================ + +// union MuxedAccount switch (CryptoKeyType type) +// { +// case KEY_TYPE_ED25519: +// uint256 ed25519; +// case KEY_TYPE_MUXED_ED25519: +// struct +// { +// uint64 id; +// uint256 ed25519; +// } med25519; +// }; + +// =========================================================================== +public class MuxedAccount implements XdrElement { + public MuxedAccount () {} + CryptoKeyType type; + public CryptoKeyType getDiscriminant() { + return this.type; + } + public void setDiscriminant(CryptoKeyType value) { + this.type = value; + } + private Uint256 ed25519; + public Uint256 getEd25519() { + return this.ed25519; + } + public void setEd25519(Uint256 value) { + this.ed25519 = value; + } + private MuxedAccountMed25519 med25519; + public MuxedAccountMed25519 getMed25519() { + return this.med25519; + } + public void setMed25519(MuxedAccountMed25519 value) { + this.med25519 = value; + } + public static void encode(XdrDataOutputStream stream, MuxedAccount encodedMuxedAccount) throws IOException { + //Xdrgen::AST::Identifier + //CryptoKeyType + stream.writeInt(encodedMuxedAccount.getDiscriminant().getValue()); + switch (encodedMuxedAccount.getDiscriminant()) { + case KEY_TYPE_ED25519: + Uint256.encode(stream, encodedMuxedAccount.ed25519); + break; + case KEY_TYPE_MUXED_ED25519: + MuxedAccountMed25519.encode(stream, encodedMuxedAccount.med25519); + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static MuxedAccount decode(XdrDataInputStream stream) throws IOException { + MuxedAccount decodedMuxedAccount = new MuxedAccount(); + CryptoKeyType discriminant = CryptoKeyType.decode(stream); + decodedMuxedAccount.setDiscriminant(discriminant); + switch (decodedMuxedAccount.getDiscriminant()) { + case KEY_TYPE_ED25519: + decodedMuxedAccount.ed25519 = Uint256.decode(stream); + break; + case KEY_TYPE_MUXED_ED25519: + decodedMuxedAccount.med25519 = MuxedAccountMed25519.decode(stream); + break; + } + return decodedMuxedAccount; + } + @Override + public int hashCode() { + return Objects.hashCode(this.ed25519, this.med25519, this.type); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof MuxedAccount)) { + return false; + } + + MuxedAccount other = (MuxedAccount) object; + return Objects.equal(this.ed25519, other.ed25519) && Objects.equal(this.med25519, other.med25519) && Objects.equal(this.type, other.type); + } + + public static class MuxedAccountMed25519 { + public MuxedAccountMed25519 () {} + private Uint64 id; + public Uint64 getId() { + return this.id; + } + public void setId(Uint64 value) { + this.id = value; + } + private Uint256 ed25519; + public Uint256 getEd25519() { + return this.ed25519; + } + public void setEd25519(Uint256 value) { + this.ed25519 = value; + } + public static void encode(XdrDataOutputStream stream, MuxedAccountMed25519 encodedMuxedAccountMed25519) throws IOException{ + Uint64.encode(stream, encodedMuxedAccountMed25519.id); + Uint256.encode(stream, encodedMuxedAccountMed25519.ed25519); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static MuxedAccountMed25519 decode(XdrDataInputStream stream) throws IOException { + MuxedAccountMed25519 decodedMuxedAccountMed25519 = new MuxedAccountMed25519(); + decodedMuxedAccountMed25519.id = Uint64.decode(stream); + decodedMuxedAccountMed25519.ed25519 = Uint256.decode(stream); + return decodedMuxedAccountMed25519; + } + @Override + public int hashCode() { + return Objects.hashCode(this.id, this.ed25519); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof MuxedAccountMed25519)) { + return false; + } + + MuxedAccountMed25519 other = (MuxedAccountMed25519) object; + return Objects.equal(this.id, other.id) && Objects.equal(this.ed25519, other.ed25519); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/Operation.java b/src/main/java/org/stellar/sdk/xdr/Operation.java index 7dd4f5681..016280f5b 100644 --- a/src/main/java/org/stellar/sdk/xdr/Operation.java +++ b/src/main/java/org/stellar/sdk/xdr/Operation.java @@ -15,7 +15,7 @@ // // sourceAccount is the account used to run the operation // // if not set, the runtime defaults to "sourceAccount" specified at // // the transaction level -// AccountID* sourceAccount; +// MuxedAccount* sourceAccount; // // union switch (OperationType type) // { @@ -36,7 +36,7 @@ // case ALLOW_TRUST: // AllowTrustOp allowTrustOp; // case ACCOUNT_MERGE: -// AccountID destination; +// MuxedAccount destination; // case INFLATION: // void; // case MANAGE_DATA: @@ -54,11 +54,11 @@ // =========================================================================== public class Operation implements XdrElement { public Operation () {} - private AccountID sourceAccount; - public AccountID getSourceAccount() { + private MuxedAccount sourceAccount; + public MuxedAccount getSourceAccount() { return this.sourceAccount; } - public void setSourceAccount(AccountID value) { + public void setSourceAccount(MuxedAccount value) { this.sourceAccount = value; } private OperationBody body; @@ -71,7 +71,7 @@ public void setBody(OperationBody value) { public static void encode(XdrDataOutputStream stream, Operation encodedOperation) throws IOException{ if (encodedOperation.sourceAccount != null) { stream.writeInt(1); - AccountID.encode(stream, encodedOperation.sourceAccount); + MuxedAccount.encode(stream, encodedOperation.sourceAccount); } else { stream.writeInt(0); } @@ -84,7 +84,7 @@ public static Operation decode(XdrDataInputStream stream) throws IOException { Operation decodedOperation = new Operation(); int sourceAccountPresent = stream.readInt(); if (sourceAccountPresent != 0) { - decodedOperation.sourceAccount = AccountID.decode(stream); + decodedOperation.sourceAccount = MuxedAccount.decode(stream); } decodedOperation.body = OperationBody.decode(stream); return decodedOperation; @@ -168,11 +168,11 @@ public AllowTrustOp getAllowTrustOp() { public void setAllowTrustOp(AllowTrustOp value) { this.allowTrustOp = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private ManageDataOp manageDataOp; @@ -233,7 +233,7 @@ public static void encode(XdrDataOutputStream stream, OperationBody encodedOpera AllowTrustOp.encode(stream, encodedOperationBody.allowTrustOp); break; case ACCOUNT_MERGE: - AccountID.encode(stream, encodedOperationBody.destination); + MuxedAccount.encode(stream, encodedOperationBody.destination); break; case INFLATION: break; @@ -284,7 +284,7 @@ public static OperationBody decode(XdrDataInputStream stream) throws IOException decodedOperationBody.allowTrustOp = AllowTrustOp.decode(stream); break; case ACCOUNT_MERGE: - decodedOperationBody.destination = AccountID.decode(stream); + decodedOperationBody.destination = MuxedAccount.decode(stream); break; case INFLATION: break; diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResult.java b/src/main/java/org/stellar/sdk/xdr/OperationResult.java index b7c5e6133..392aefdcf 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResult.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResult.java @@ -40,7 +40,7 @@ // case BUMP_SEQUENCE: // BumpSequenceResult bumpSeqResult; // case MANAGE_BUY_OFFER: -// ManageBuyOfferResult manageBuyOfferResult; +// ManageBuyOfferResult manageBuyOfferResult; // case PATH_PAYMENT_STRICT_SEND: // PathPaymentStrictSendResult pathPaymentStrictSendResult; // } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java index f432d7fcd..91185fe7a 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java @@ -13,9 +13,9 @@ // { // opINNER = 0, // inner object result is valid // -// opBAD_AUTH = -1, // too few valid signatures / wrong network -// opNO_ACCOUNT = -2, // source account was not found -// opNOT_SUPPORTED = -3, // operation not supported at this time +// opBAD_AUTH = -1, // too few valid signatures / wrong network +// opNO_ACCOUNT = -2, // source account was not found +// opNOT_SUPPORTED = -3, // operation not supported at this time // opTOO_MANY_SUBENTRIES = -4, // max number of subentries already reached // opEXCEEDED_WORK_LIMIT = -5 // operation did too much work // }; diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java index 07cae0689..14e543105 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java @@ -18,9 +18,9 @@ // // send (excluding fees). // // The operation will fail if can't be met // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destAmount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destAmount; // amount they end up with // // Asset path<5>; // additional hops it must go through to get there // }; @@ -42,11 +42,11 @@ public Int64 getSendMax() { public void setSendMax(Int64 value) { this.sendMax = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset destAsset; @@ -73,7 +73,7 @@ public void setPath(Asset[] value) { public static void encode(XdrDataOutputStream stream, PathPaymentStrictReceiveOp encodedPathPaymentStrictReceiveOp) throws IOException{ Asset.encode(stream, encodedPathPaymentStrictReceiveOp.sendAsset); Int64.encode(stream, encodedPathPaymentStrictReceiveOp.sendMax); - AccountID.encode(stream, encodedPathPaymentStrictReceiveOp.destination); + MuxedAccount.encode(stream, encodedPathPaymentStrictReceiveOp.destination); Asset.encode(stream, encodedPathPaymentStrictReceiveOp.destAsset); Int64.encode(stream, encodedPathPaymentStrictReceiveOp.destAmount); int pathsize = encodedPathPaymentStrictReceiveOp.getPath().length; @@ -89,7 +89,7 @@ public static PathPaymentStrictReceiveOp decode(XdrDataInputStream stream) throw PathPaymentStrictReceiveOp decodedPathPaymentStrictReceiveOp = new PathPaymentStrictReceiveOp(); decodedPathPaymentStrictReceiveOp.sendAsset = Asset.decode(stream); decodedPathPaymentStrictReceiveOp.sendMax = Int64.decode(stream); - decodedPathPaymentStrictReceiveOp.destination = AccountID.decode(stream); + decodedPathPaymentStrictReceiveOp.destination = MuxedAccount.decode(stream); decodedPathPaymentStrictReceiveOp.destAsset = Asset.decode(stream); decodedPathPaymentStrictReceiveOp.destAmount = Int64.decode(stream); int pathsize = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java index 6053e2f88..d05eb229d 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java @@ -15,18 +15,27 @@ // PATH_PAYMENT_STRICT_RECEIVE_SUCCESS = 0, // success // // // codes considered as "failure" for the operation -// PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input -// PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = -2, // not enough funds in source account -// PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = -3, // no trust line on source account -// PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer -// PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = -5, // destination account does not exist -// PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = -6, // dest missing a trust line for asset -// PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = -7, // dest not authorized to hold asset -// PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = -8, // dest would go above their limit -// PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset -// PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path -// PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = -11, // would cross one of its own offers -// PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax +// PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input +// PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = +// -2, // not enough funds in source account +// PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = +// -3, // no trust line on source account +// PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = +// -4, // source not authorized to transfer +// PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = +// -5, // destination account does not exist +// PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = +// -6, // dest missing a trust line for asset +// PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = +// -7, // dest not authorized to hold asset +// PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = +// -8, // dest would go above their limit +// PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset +// PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = +// -10, // not enough offers to satisfy path +// PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = +// -11, // would cross one of its own offers +// PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java index 8380c756c..242f0cec2 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java @@ -16,11 +16,11 @@ // Asset sendAsset; // asset we pay with // int64 sendAmount; // amount of sendAsset to send (excluding fees) // -// AccountID destination; // recipient of the payment -// Asset destAsset; // what they end up with -// int64 destMin; // the minimum amount of dest asset to -// // be received -// // The operation will fail if it can't be met +// MuxedAccount destination; // recipient of the payment +// Asset destAsset; // what they end up with +// int64 destMin; // the minimum amount of dest asset to +// // be received +// // The operation will fail if it can't be met // // Asset path<5>; // additional hops it must go through to get there // }; @@ -42,11 +42,11 @@ public Int64 getSendAmount() { public void setSendAmount(Int64 value) { this.sendAmount = value; } - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset destAsset; @@ -73,7 +73,7 @@ public void setPath(Asset[] value) { public static void encode(XdrDataOutputStream stream, PathPaymentStrictSendOp encodedPathPaymentStrictSendOp) throws IOException{ Asset.encode(stream, encodedPathPaymentStrictSendOp.sendAsset); Int64.encode(stream, encodedPathPaymentStrictSendOp.sendAmount); - AccountID.encode(stream, encodedPathPaymentStrictSendOp.destination); + MuxedAccount.encode(stream, encodedPathPaymentStrictSendOp.destination); Asset.encode(stream, encodedPathPaymentStrictSendOp.destAsset); Int64.encode(stream, encodedPathPaymentStrictSendOp.destMin); int pathsize = encodedPathPaymentStrictSendOp.getPath().length; @@ -89,7 +89,7 @@ public static PathPaymentStrictSendOp decode(XdrDataInputStream stream) throws I PathPaymentStrictSendOp decodedPathPaymentStrictSendOp = new PathPaymentStrictSendOp(); decodedPathPaymentStrictSendOp.sendAsset = Asset.decode(stream); decodedPathPaymentStrictSendOp.sendAmount = Int64.decode(stream); - decodedPathPaymentStrictSendOp.destination = AccountID.decode(stream); + decodedPathPaymentStrictSendOp.destination = MuxedAccount.decode(stream); decodedPathPaymentStrictSendOp.destAsset = Asset.decode(stream); decodedPathPaymentStrictSendOp.destMin = Int64.decode(stream); int pathsize = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java index e95ca29f2..375941336 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java @@ -15,18 +15,26 @@ // PATH_PAYMENT_STRICT_SEND_SUCCESS = 0, // success // // // codes considered as "failure" for the operation -// PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input -// PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = -2, // not enough funds in source account -// PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = -3, // no trust line on source account -// PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer -// PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = -5, // destination account does not exist -// PATH_PAYMENT_STRICT_SEND_NO_TRUST = -6, // dest missing a trust line for asset -// PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = -7, // dest not authorized to hold asset -// PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit -// PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset -// PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path -// PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = -11, // would cross one of its own offers -// PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin +// PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input +// PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = +// -2, // not enough funds in source account +// PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = +// -3, // no trust line on source account +// PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = +// -4, // source not authorized to transfer +// PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = +// -5, // destination account does not exist +// PATH_PAYMENT_STRICT_SEND_NO_TRUST = +// -6, // dest missing a trust line for asset +// PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = +// -7, // dest not authorized to hold asset +// PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit +// PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset +// PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = +// -10, // not enough offers to satisfy path +// PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = +// -11, // would cross one of its own offers +// PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java index 101058a50..940b8adb2 100644 --- a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java @@ -12,19 +12,19 @@ // struct PaymentOp // { -// AccountID destination; // recipient of the payment -// Asset asset; // what they end up with -// int64 amount; // amount they end up with +// MuxedAccount destination; // recipient of the payment +// Asset asset; // what they end up with +// int64 amount; // amount they end up with // }; // =========================================================================== public class PaymentOp implements XdrElement { public PaymentOp () {} - private AccountID destination; - public AccountID getDestination() { + private MuxedAccount destination; + public MuxedAccount getDestination() { return this.destination; } - public void setDestination(AccountID value) { + public void setDestination(MuxedAccount value) { this.destination = value; } private Asset asset; @@ -42,7 +42,7 @@ public void setAmount(Int64 value) { this.amount = value; } public static void encode(XdrDataOutputStream stream, PaymentOp encodedPaymentOp) throws IOException{ - AccountID.encode(stream, encodedPaymentOp.destination); + MuxedAccount.encode(stream, encodedPaymentOp.destination); Asset.encode(stream, encodedPaymentOp.asset); Int64.encode(stream, encodedPaymentOp.amount); } @@ -51,7 +51,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static PaymentOp decode(XdrDataInputStream stream) throws IOException { PaymentOp decodedPaymentOp = new PaymentOp(); - decodedPaymentOp.destination = AccountID.decode(stream); + decodedPaymentOp.destination = MuxedAccount.decode(stream); decodedPaymentOp.asset = Asset.decode(stream); decodedPaymentOp.amount = Int64.decode(stream); return decodedPaymentOp; diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java index 25e5556b2..852308b82 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java @@ -12,8 +12,8 @@ // union SurveyResponseBody switch (SurveyMessageCommandType type) // { -// case SURVEY_TOPOLOGY: -// TopologyResponseBody topologyResponseBody; +// case SURVEY_TOPOLOGY: +// TopologyResponseBody topologyResponseBody; // }; // =========================================================================== diff --git a/src/main/java/org/stellar/sdk/xdr/Transaction.java b/src/main/java/org/stellar/sdk/xdr/Transaction.java index cc72a3392..f9f9c435a 100644 --- a/src/main/java/org/stellar/sdk/xdr/Transaction.java +++ b/src/main/java/org/stellar/sdk/xdr/Transaction.java @@ -14,7 +14,7 @@ // struct Transaction // { // // account used to run the transaction -// AccountID sourceAccount; +// MuxedAccount sourceAccount; // // // the fee the sourceAccount will pay // uint32 fee; @@ -41,11 +41,11 @@ // =========================================================================== public class Transaction implements XdrElement { public Transaction () {} - private AccountID sourceAccount; - public AccountID getSourceAccount() { + private MuxedAccount sourceAccount; + public MuxedAccount getSourceAccount() { return this.sourceAccount; } - public void setSourceAccount(AccountID value) { + public void setSourceAccount(MuxedAccount value) { this.sourceAccount = value; } private Uint32 fee; @@ -91,7 +91,7 @@ public void setExt(TransactionExt value) { this.ext = value; } public static void encode(XdrDataOutputStream stream, Transaction encodedTransaction) throws IOException{ - AccountID.encode(stream, encodedTransaction.sourceAccount); + MuxedAccount.encode(stream, encodedTransaction.sourceAccount); Uint32.encode(stream, encodedTransaction.fee); SequenceNumber.encode(stream, encodedTransaction.seqNum); if (encodedTransaction.timeBounds != null) { @@ -113,7 +113,7 @@ public void encode(XdrDataOutputStream stream) throws IOException { } public static Transaction decode(XdrDataInputStream stream) throws IOException { Transaction decodedTransaction = new Transaction(); - decodedTransaction.sourceAccount = AccountID.decode(stream); + decodedTransaction.sourceAccount = MuxedAccount.decode(stream); decodedTransaction.fee = Uint32.decode(stream); decodedTransaction.seqNum = SequenceNumber.decode(stream); int timeBoundsPresent = stream.readInt(); diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java index ff55d0a09..4d966c830 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java @@ -7,59 +7,89 @@ import java.io.IOException; import com.google.common.base.Objects; -import java.util.Arrays; // === xdr source ============================================================ -// struct TransactionEnvelope +// union TransactionEnvelope switch (EnvelopeType type) // { -// Transaction tx; -// /* Each decorated signature is a signature over the SHA256 hash of -// * a TransactionSignaturePayload */ -// DecoratedSignature signatures<20>; +// case ENVELOPE_TYPE_TX_V0: +// TransactionV0Envelope v0; +// case ENVELOPE_TYPE_TX: +// TransactionV1Envelope v1; +// case ENVELOPE_TYPE_TX_FEE_BUMP: +// FeeBumpTransactionEnvelope feeBump; // }; // =========================================================================== public class TransactionEnvelope implements XdrElement { public TransactionEnvelope () {} - private Transaction tx; - public Transaction getTx() { - return this.tx; + EnvelopeType type; + public EnvelopeType getDiscriminant() { + return this.type; } - public void setTx(Transaction value) { - this.tx = value; + public void setDiscriminant(EnvelopeType value) { + this.type = value; } - private DecoratedSignature[] signatures; - public DecoratedSignature[] getSignatures() { - return this.signatures; + private TransactionV0Envelope v0; + public TransactionV0Envelope getV0() { + return this.v0; } - public void setSignatures(DecoratedSignature[] value) { - this.signatures = value; + public void setV0(TransactionV0Envelope value) { + this.v0 = value; + } + private TransactionV1Envelope v1; + public TransactionV1Envelope getV1() { + return this.v1; + } + public void setV1(TransactionV1Envelope value) { + this.v1 = value; + } + private FeeBumpTransactionEnvelope feeBump; + public FeeBumpTransactionEnvelope getFeeBump() { + return this.feeBump; + } + public void setFeeBump(FeeBumpTransactionEnvelope value) { + this.feeBump = value; + } + public static void encode(XdrDataOutputStream stream, TransactionEnvelope encodedTransactionEnvelope) throws IOException { + //Xdrgen::AST::Identifier + //EnvelopeType + stream.writeInt(encodedTransactionEnvelope.getDiscriminant().getValue()); + switch (encodedTransactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX_V0: + TransactionV0Envelope.encode(stream, encodedTransactionEnvelope.v0); + break; + case ENVELOPE_TYPE_TX: + TransactionV1Envelope.encode(stream, encodedTransactionEnvelope.v1); + break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransactionEnvelope.encode(stream, encodedTransactionEnvelope.feeBump); + break; } - public static void encode(XdrDataOutputStream stream, TransactionEnvelope encodedTransactionEnvelope) throws IOException{ - Transaction.encode(stream, encodedTransactionEnvelope.tx); - int signaturessize = encodedTransactionEnvelope.getSignatures().length; - stream.writeInt(signaturessize); - for (int i = 0; i < signaturessize; i++) { - DecoratedSignature.encode(stream, encodedTransactionEnvelope.signatures[i]); - } } public void encode(XdrDataOutputStream stream) throws IOException { encode(stream, this); } public static TransactionEnvelope decode(XdrDataInputStream stream) throws IOException { - TransactionEnvelope decodedTransactionEnvelope = new TransactionEnvelope(); - decodedTransactionEnvelope.tx = Transaction.decode(stream); - int signaturessize = stream.readInt(); - decodedTransactionEnvelope.signatures = new DecoratedSignature[signaturessize]; - for (int i = 0; i < signaturessize; i++) { - decodedTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream); - } + TransactionEnvelope decodedTransactionEnvelope = new TransactionEnvelope(); + EnvelopeType discriminant = EnvelopeType.decode(stream); + decodedTransactionEnvelope.setDiscriminant(discriminant); + switch (decodedTransactionEnvelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX_V0: + decodedTransactionEnvelope.v0 = TransactionV0Envelope.decode(stream); + break; + case ENVELOPE_TYPE_TX: + decodedTransactionEnvelope.v1 = TransactionV1Envelope.decode(stream); + break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + decodedTransactionEnvelope.feeBump = FeeBumpTransactionEnvelope.decode(stream); + break; + } return decodedTransactionEnvelope; } @Override public int hashCode() { - return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + return Objects.hashCode(this.v0, this.v1, this.feeBump, this.type); } @Override public boolean equals(Object object) { @@ -68,6 +98,6 @@ public boolean equals(Object object) { } TransactionEnvelope other = (TransactionEnvelope) object; - return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + return Objects.equal(this.v0, other.v0) && Objects.equal(this.v1, other.v1) && Objects.equal(this.feeBump, other.feeBump) && Objects.equal(this.type, other.type); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java index 7ce372e11..f0c210268 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java @@ -17,6 +17,9 @@ // // union switch (TransactionResultCode code) // { +// case txFEE_BUMP_INNER_SUCCESS: +// case txFEE_BUMP_INNER_FAILED: +// InnerTransactionResultPair innerResultPair; // case txSUCCESS: // case txFAILED: // OperationResult results<>; @@ -96,6 +99,13 @@ public TransactionResultCode getDiscriminant() { public void setDiscriminant(TransactionResultCode value) { this.code = value; } + private InnerTransactionResultPair innerResultPair; + public InnerTransactionResultPair getInnerResultPair() { + return this.innerResultPair; + } + public void setInnerResultPair(InnerTransactionResultPair value) { + this.innerResultPair = value; + } private OperationResult[] results; public OperationResult[] getResults() { return this.results; @@ -108,6 +118,10 @@ public static void encode(XdrDataOutputStream stream, TransactionResultResult en //TransactionResultCode stream.writeInt(encodedTransactionResultResult.getDiscriminant().getValue()); switch (encodedTransactionResultResult.getDiscriminant()) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + InnerTransactionResultPair.encode(stream, encodedTransactionResultResult.innerResultPair); + break; case txSUCCESS: case txFAILED: int resultssize = encodedTransactionResultResult.getResults().length; @@ -128,6 +142,10 @@ public static TransactionResultResult decode(XdrDataInputStream stream) throws I TransactionResultCode discriminant = TransactionResultCode.decode(stream); decodedTransactionResultResult.setDiscriminant(discriminant); switch (decodedTransactionResultResult.getDiscriminant()) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + decodedTransactionResultResult.innerResultPair = InnerTransactionResultPair.decode(stream); + break; case txSUCCESS: case txFAILED: int resultssize = stream.readInt(); @@ -143,7 +161,7 @@ public static TransactionResultResult decode(XdrDataInputStream stream) throws I } @Override public int hashCode() { - return Objects.hashCode(Arrays.hashCode(this.results), this.code); + return Objects.hashCode(this.innerResultPair, Arrays.hashCode(this.results), this.code); } @Override public boolean equals(Object object) { @@ -152,7 +170,7 @@ public boolean equals(Object object) { } TransactionResultResult other = (TransactionResultResult) object; - return Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); + return Objects.equal(this.innerResultPair, other.innerResultPair) && Arrays.equals(this.results, other.results) && Objects.equal(this.code, other.code); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java index 914ca9ff9..ab6763de8 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java @@ -11,7 +11,8 @@ // enum TransactionResultCode // { -// txSUCCESS = 0, // all operations succeeded +// txFEE_BUMP_INNER_SUCCESS = 1, // fee bump inner transaction succeeded +// txSUCCESS = 0, // all operations succeeded // // txFAILED = -1, // one of the operations failed (none were applied) // @@ -25,11 +26,15 @@ // txNO_ACCOUNT = -8, // source account not found // txINSUFFICIENT_FEE = -9, // fee is too small // txBAD_AUTH_EXTRA = -10, // unused signatures attached to transaction -// txINTERNAL_ERROR = -11 // an unknown error occured +// txINTERNAL_ERROR = -11, // an unknown error occured +// +// txNOT_SUPPORTED = -12, // transaction type not supported +// txFEE_BUMP_INNER_FAILED = -13 // fee bump inner transaction failed // }; // =========================================================================== public enum TransactionResultCode implements XdrElement { + txFEE_BUMP_INNER_SUCCESS(1), txSUCCESS(0), txFAILED(-1), txTOO_EARLY(-2), @@ -42,6 +47,8 @@ public enum TransactionResultCode implements XdrElement { txINSUFFICIENT_FEE(-9), txBAD_AUTH_EXTRA(-10), txINTERNAL_ERROR(-11), + txNOT_SUPPORTED(-12), + txFEE_BUMP_INNER_FAILED(-13), ; private int mValue; @@ -56,6 +63,7 @@ public int getValue() { public static TransactionResultCode decode(XdrDataInputStream stream) throws IOException { int value = stream.readInt(); switch (value) { + case 1: return txFEE_BUMP_INNER_SUCCESS; case 0: return txSUCCESS; case -1: return txFAILED; case -2: return txTOO_EARLY; @@ -68,6 +76,8 @@ public static TransactionResultCode decode(XdrDataInputStream stream) throws IOE case -9: return txINSUFFICIENT_FEE; case -10: return txBAD_AUTH_EXTRA; case -11: return txINTERNAL_ERROR; + case -12: return txNOT_SUPPORTED; + case -13: return txFEE_BUMP_INNER_FAILED; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java index d549cca34..916faee0c 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java @@ -15,9 +15,11 @@ // Hash networkId; // union switch (EnvelopeType type) // { +// // Backwards Compatibility: Use ENVELOPE_TYPE_TX to sign ENVELOPE_TYPE_TX_V0 // case ENVELOPE_TYPE_TX: // Transaction tx; -// /* All other values of type are invalid */ +// case ENVELOPE_TYPE_TX_FEE_BUMP: +// FeeBumpTransaction feeBump; // } // taggedTransaction; // }; @@ -82,6 +84,13 @@ public Transaction getTx() { public void setTx(Transaction value) { this.tx = value; } + private FeeBumpTransaction feeBump; + public FeeBumpTransaction getFeeBump() { + return this.feeBump; + } + public void setFeeBump(FeeBumpTransaction value) { + this.feeBump = value; + } public static void encode(XdrDataOutputStream stream, TransactionSignaturePayloadTaggedTransaction encodedTransactionSignaturePayloadTaggedTransaction) throws IOException { //Xdrgen::AST::Identifier //EnvelopeType @@ -90,6 +99,9 @@ public static void encode(XdrDataOutputStream stream, TransactionSignaturePayloa case ENVELOPE_TYPE_TX: Transaction.encode(stream, encodedTransactionSignaturePayloadTaggedTransaction.tx); break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransaction.encode(stream, encodedTransactionSignaturePayloadTaggedTransaction.feeBump); + break; } } public void encode(XdrDataOutputStream stream) throws IOException { @@ -103,12 +115,15 @@ public static TransactionSignaturePayloadTaggedTransaction decode(XdrDataInputSt case ENVELOPE_TYPE_TX: decodedTransactionSignaturePayloadTaggedTransaction.tx = Transaction.decode(stream); break; + case ENVELOPE_TYPE_TX_FEE_BUMP: + decodedTransactionSignaturePayloadTaggedTransaction.feeBump = FeeBumpTransaction.decode(stream); + break; } return decodedTransactionSignaturePayloadTaggedTransaction; } @Override public int hashCode() { - return Objects.hashCode(this.tx, this.type); + return Objects.hashCode(this.tx, this.feeBump, this.type); } @Override public boolean equals(Object object) { @@ -117,7 +132,7 @@ public boolean equals(Object object) { } TransactionSignaturePayloadTaggedTransaction other = (TransactionSignaturePayloadTaggedTransaction) object; - return Objects.equal(this.tx, other.tx) && Objects.equal(this.type, other.type); + return Objects.equal(this.tx, other.tx) && Objects.equal(this.feeBump, other.feeBump) && Objects.equal(this.type, other.type); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java new file mode 100644 index 000000000..265650718 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java @@ -0,0 +1,181 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV0 +// { +// uint256 sourceAccountEd25519; +// uint32 fee; +// SequenceNumber seqNum; +// TimeBounds* timeBounds; +// Memo memo; +// Operation operations; +// union switch (int v) +// { +// case 0: +// void; +// } +// ext; +// }; + +// =========================================================================== +public class TransactionV0 implements XdrElement { + public TransactionV0 () {} + private Uint256 sourceAccountEd25519; + public Uint256 getSourceAccountEd25519() { + return this.sourceAccountEd25519; + } + public void setSourceAccountEd25519(Uint256 value) { + this.sourceAccountEd25519 = value; + } + private Uint32 fee; + public Uint32 getFee() { + return this.fee; + } + public void setFee(Uint32 value) { + this.fee = value; + } + private SequenceNumber seqNum; + public SequenceNumber getSeqNum() { + return this.seqNum; + } + public void setSeqNum(SequenceNumber value) { + this.seqNum = value; + } + private TimeBounds timeBounds; + public TimeBounds getTimeBounds() { + return this.timeBounds; + } + public void setTimeBounds(TimeBounds value) { + this.timeBounds = value; + } + private Memo memo; + public Memo getMemo() { + return this.memo; + } + public void setMemo(Memo value) { + this.memo = value; + } + private Operation[] operations; + public Operation[] getOperations() { + return this.operations; + } + public void setOperations(Operation[] value) { + this.operations = value; + } + private TransactionV0Ext ext; + public TransactionV0Ext getExt() { + return this.ext; + } + public void setExt(TransactionV0Ext value) { + this.ext = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0 encodedTransactionV0) throws IOException{ + Uint256.encode(stream, encodedTransactionV0.sourceAccountEd25519); + Uint32.encode(stream, encodedTransactionV0.fee); + SequenceNumber.encode(stream, encodedTransactionV0.seqNum); + if (encodedTransactionV0.timeBounds != null) { + stream.writeInt(1); + TimeBounds.encode(stream, encodedTransactionV0.timeBounds); + } else { + stream.writeInt(0); + } + Memo.encode(stream, encodedTransactionV0.memo); + int operationssize = encodedTransactionV0.getOperations().length; + stream.writeInt(operationssize); + for (int i = 0; i < operationssize; i++) { + Operation.encode(stream, encodedTransactionV0.operations[i]); + } + TransactionV0Ext.encode(stream, encodedTransactionV0.ext); + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0 decode(XdrDataInputStream stream) throws IOException { + TransactionV0 decodedTransactionV0 = new TransactionV0(); + decodedTransactionV0.sourceAccountEd25519 = Uint256.decode(stream); + decodedTransactionV0.fee = Uint32.decode(stream); + decodedTransactionV0.seqNum = SequenceNumber.decode(stream); + int timeBoundsPresent = stream.readInt(); + if (timeBoundsPresent != 0) { + decodedTransactionV0.timeBounds = TimeBounds.decode(stream); + } + decodedTransactionV0.memo = Memo.decode(stream); + int operationssize = stream.readInt(); + decodedTransactionV0.operations = new Operation[operationssize]; + for (int i = 0; i < operationssize; i++) { + decodedTransactionV0.operations[i] = Operation.decode(stream); + } + decodedTransactionV0.ext = TransactionV0Ext.decode(stream); + return decodedTransactionV0; + } + @Override + public int hashCode() { + return Objects.hashCode(this.sourceAccountEd25519, this.fee, this.seqNum, this.timeBounds, this.memo, Arrays.hashCode(this.operations), this.ext); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0)) { + return false; + } + + TransactionV0 other = (TransactionV0) object; + return Objects.equal(this.sourceAccountEd25519, other.sourceAccountEd25519) && Objects.equal(this.fee, other.fee) && Objects.equal(this.seqNum, other.seqNum) && Objects.equal(this.timeBounds, other.timeBounds) && Objects.equal(this.memo, other.memo) && Arrays.equals(this.operations, other.operations) && Objects.equal(this.ext, other.ext); + } + + public static class TransactionV0Ext { + public TransactionV0Ext () {} + Integer v; + public Integer getDiscriminant() { + return this.v; + } + public void setDiscriminant(Integer value) { + this.v = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0Ext encodedTransactionV0Ext) throws IOException { + //Xdrgen::AST::Typespecs::Int + //Integer + stream.writeInt(encodedTransactionV0Ext.getDiscriminant().intValue()); + switch (encodedTransactionV0Ext.getDiscriminant()) { + case 0: + break; + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0Ext decode(XdrDataInputStream stream) throws IOException { + TransactionV0Ext decodedTransactionV0Ext = new TransactionV0Ext(); + Integer discriminant = stream.readInt(); + decodedTransactionV0Ext.setDiscriminant(discriminant); + switch (decodedTransactionV0Ext.getDiscriminant()) { + case 0: + break; + } + return decodedTransactionV0Ext; + } + @Override + public int hashCode() { + return Objects.hashCode(this.v); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0Ext)) { + return false; + } + + TransactionV0Ext other = (TransactionV0Ext) object; + return Objects.equal(this.v, other.v); + } + + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java new file mode 100644 index 000000000..90577e8e9 --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV0Envelope +// { +// TransactionV0 tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class TransactionV0Envelope implements XdrElement { + public TransactionV0Envelope () {} + private TransactionV0 tx; + public TransactionV0 getTx() { + return this.tx; + } + public void setTx(TransactionV0 value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV0Envelope encodedTransactionV0Envelope) throws IOException{ + TransactionV0.encode(stream, encodedTransactionV0Envelope.tx); + int signaturessize = encodedTransactionV0Envelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedTransactionV0Envelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV0Envelope decode(XdrDataInputStream stream) throws IOException { + TransactionV0Envelope decodedTransactionV0Envelope = new TransactionV0Envelope(); + decodedTransactionV0Envelope.tx = TransactionV0.decode(stream); + int signaturessize = stream.readInt(); + decodedTransactionV0Envelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedTransactionV0Envelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedTransactionV0Envelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV0Envelope)) { + return false; + } + + TransactionV0Envelope other = (TransactionV0Envelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java new file mode 100644 index 000000000..f11777a5b --- /dev/null +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java @@ -0,0 +1,73 @@ +// Automatically generated by xdrgen +// DO NOT EDIT or your changes may be overwritten + +package org.stellar.sdk.xdr; + + +import java.io.IOException; + +import com.google.common.base.Objects; +import java.util.Arrays; + +// === xdr source ============================================================ + +// struct TransactionV1Envelope +// { +// Transaction tx; +// /* Each decorated signature is a signature over the SHA256 hash of +// * a TransactionSignaturePayload */ +// DecoratedSignature signatures<20>; +// }; + +// =========================================================================== +public class TransactionV1Envelope implements XdrElement { + public TransactionV1Envelope () {} + private Transaction tx; + public Transaction getTx() { + return this.tx; + } + public void setTx(Transaction value) { + this.tx = value; + } + private DecoratedSignature[] signatures; + public DecoratedSignature[] getSignatures() { + return this.signatures; + } + public void setSignatures(DecoratedSignature[] value) { + this.signatures = value; + } + public static void encode(XdrDataOutputStream stream, TransactionV1Envelope encodedTransactionV1Envelope) throws IOException{ + Transaction.encode(stream, encodedTransactionV1Envelope.tx); + int signaturessize = encodedTransactionV1Envelope.getSignatures().length; + stream.writeInt(signaturessize); + for (int i = 0; i < signaturessize; i++) { + DecoratedSignature.encode(stream, encodedTransactionV1Envelope.signatures[i]); + } + } + public void encode(XdrDataOutputStream stream) throws IOException { + encode(stream, this); + } + public static TransactionV1Envelope decode(XdrDataInputStream stream) throws IOException { + TransactionV1Envelope decodedTransactionV1Envelope = new TransactionV1Envelope(); + decodedTransactionV1Envelope.tx = Transaction.decode(stream); + int signaturessize = stream.readInt(); + decodedTransactionV1Envelope.signatures = new DecoratedSignature[signaturessize]; + for (int i = 0; i < signaturessize; i++) { + decodedTransactionV1Envelope.signatures[i] = DecoratedSignature.decode(stream); + } + return decodedTransactionV1Envelope; + } + @Override + public int hashCode() { + return Objects.hashCode(this.tx, Arrays.hashCode(this.signatures)); + } + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof TransactionV1Envelope)) { + return false; + } + + TransactionV1Envelope other = (TransactionV1Envelope) object; + return Objects.equal(this.tx, other.tx) && Arrays.equals(this.signatures, other.signatures); + } +} diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java index ccc1a962d..5148ea545 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java @@ -12,12 +12,16 @@ // enum TrustLineFlags // { // // issuer has authorized account to perform transactions with its credit -// AUTHORIZED_FLAG = 1 +// AUTHORIZED_FLAG = 1, +// // issuer has authorized account to maintain and reduce liabilities for its +// // credit +// AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG = 2 // }; // =========================================================================== public enum TrustLineFlags implements XdrElement { AUTHORIZED_FLAG(1), + AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG(2), ; private int mValue; @@ -33,6 +37,7 @@ public static TrustLineFlags decode(XdrDataInputStream stream) throws IOExceptio int value = stream.readInt(); switch (value) { case 1: return AUTHORIZED_FLAG; + case 2: return AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG; default: throw new RuntimeException("Unknown enum value: " + value); } diff --git a/src/test/java/org/stellar/sdk/OperationTest.java b/src/test/java/org/stellar/sdk/OperationTest.java index 5a0800d9d..be1a5249c 100644 --- a/src/test/java/org/stellar/sdk/OperationTest.java +++ b/src/test/java/org/stellar/sdk/OperationTest.java @@ -551,9 +551,9 @@ public void testManageSellOfferOperation_BadArithmeticRegression() throws IOExce byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); org.stellar.sdk.xdr.TransactionEnvelope transactionEnvelope = org.stellar.sdk.xdr.TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); - ManageSellOfferOperation op = (ManageSellOfferOperation) Operation.fromXdr(transactionEnvelope.getTx().getOperations()[0]); + ManageSellOfferOperation op = (ManageSellOfferOperation) Operation.fromXdr(transactionEnvelope.getV0().getTx().getOperations()[0]); assertEquals("3397.893306099996", op.getPrice()); } @@ -567,9 +567,9 @@ public void testManageBuyOfferOperation_BadArithmeticRegression() throws IOExcep byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); org.stellar.sdk.xdr.TransactionEnvelope transactionEnvelope = org.stellar.sdk.xdr.TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); - ManageBuyOfferOperation op = (ManageBuyOfferOperation) Operation.fromXdr(transactionEnvelope.getTx().getOperations()[0]); + ManageBuyOfferOperation op = (ManageBuyOfferOperation) Operation.fromXdr(transactionEnvelope.getV0().getTx().getOperations()[0]); assertEquals("3397.893306099996", op.getPrice()); } diff --git a/src/test/java/org/stellar/sdk/StrKeyTest.java b/src/test/java/org/stellar/sdk/StrKeyTest.java index 3937a4028..cace981be 100644 --- a/src/test/java/org/stellar/sdk/StrKeyTest.java +++ b/src/test/java/org/stellar/sdk/StrKeyTest.java @@ -1,56 +1,355 @@ package org.stellar.sdk; +import com.google.common.primitives.UnsignedLongs; import org.junit.Test; +import org.stellar.sdk.xdr.AccountID; +import org.stellar.sdk.xdr.CryptoKeyType; +import org.stellar.sdk.xdr.MuxedAccount; import java.io.IOException; import static org.junit.Assert.*; public class StrKeyTest { - @Test - public void testDecodeEncode() throws IOException, FormatException { - String seed = "SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"; - byte[] secret = StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); - char[] encoded = StrKey.encodeCheck(StrKey.VersionByte.SEED, secret); - assertEquals(seed, String.valueOf(encoded)); + @Test + public void testDecodeEncode() throws IOException, FormatException { + String seed = "SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"; + byte[] secret = StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); + char[] encoded = StrKey.encodeCheck(StrKey.VersionByte.SEED, secret); + assertEquals(seed, String.valueOf(encoded)); + } + + @Test() + public void testDecodeInvalidVersionByte() { + String address = "GCZHXL5HXQX5ABDM26LHYRCQZ5OJFHLOPLZX47WEBP3V2PF5AVFK2A5D"; + try { + StrKey.decodeCheck(StrKey.VersionByte.SEED, address.toCharArray()); + fail(); + } catch (FormatException e) { + } + } + + @Test() + public void testDecodeInvalidSeed() { + String seed = "SAA6NXOBOXP3RXGAXBW6PGFI5BPK4ODVAWITS4VDOMN5C2M4B66ZML"; + try { + StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); + fail(); + } catch (Exception e) { + } + } + + @Test() + public void testDecodedVersionByte() { + assertEquals(StrKey.decodeVersionByte("MBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG"), StrKey.VersionByte.MUXED_ACCOUNT); + assertEquals(StrKey.decodeVersionByte("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"), StrKey.VersionByte.ACCOUNT_ID); + assertEquals(StrKey.decodeVersionByte("SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"), StrKey.VersionByte.SEED); + assertEquals(StrKey.decodeVersionByte("TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"), StrKey.VersionByte.PRE_AUTH_TX); + assertEquals(StrKey.decodeVersionByte("XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"), StrKey.VersionByte.SHA256_HASH); + } + + @Test() + public void testDecodedVersionByteThrows() { + try { + StrKey.decodeVersionByte("INVALIDBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6INVALID"); + fail(); + } catch (FormatException e) { + assertEquals("Version byte is invalid", e.getMessage()); + } + } + + private byte[] rawBytes(int... values) { + byte[] data = new byte[values.length]; + for (int i = 0; i < values.length; i++) { + data[i] = (byte) values[i]; + } + return data; + } + + @Test + public void testRoundTripAccountIdFromBytes() { + byte[] data = rawBytes( + 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, + 0xd0, 0xf4, 0xed, 0x88, 0xc7, 0x79, 0xe4, 0xfe, + 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, + 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03 + ); + String accountId = "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5"; + assertEquals( + accountId, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.ACCOUNT_ID, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, accountId.toCharArray())); + } + + @Test + public void testRoundTripSeedFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String seed = "SBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHOKR"; + assertEquals( + seed, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.SEED, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray())); + } + + @Test + public void testRoundTripHashTxFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String hashTx = "TBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWHXL7"; + assertEquals( + hashTx, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.PRE_AUTH_TX, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.PRE_AUTH_TX, hashTx.toCharArray())); + } + + @Test + public void testRoundTripHashXFromBytes() { + byte[] data = rawBytes( + 0x69, 0xa8, 0xc4, 0xcb, 0xb9, 0xf6, 0x4e, 0x8a, + 0x07, 0x98, 0xf6, 0xe1, 0xac, 0x65, 0xd0, 0x6c, + 0x31, 0x62, 0x92, 0x90, 0x56, 0xbc, 0xf4, 0xcd, + 0xb7, 0xd3, 0x73, 0x8d, 0x18, 0x55, 0xf3, 0x63 + ); + String hashX = "XBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG"; + assertEquals( + hashX, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.SHA256_HASH, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.SHA256_HASH, hashX.toCharArray())); + } + + @Test + public void testRoundTripMuxedFromBytes() { + byte[] data = rawBytes( + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x0c, 0x34, 0xbf, 0x93, 0xad, 0x0d, 0x99, + 0x71, 0xd0, 0x4c, 0xcc, 0x90, 0xf7, 0x05, 0x51, + 0x1c, 0x83, 0x8a, 0xad, 0x97, 0x34, 0xa4, 0xa2, + 0xfb, 0x0d, 0x7a, 0x03, 0xfc, 0x7f, 0xe8, 0x9a + ); + String muxed = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + assertEquals( + muxed, + String.valueOf(StrKey.encodeCheck(StrKey.VersionByte.MUXED_ACCOUNT, data)) + ); + assertArrayEquals(data, StrKey.decodeCheck(StrKey.VersionByte.MUXED_ACCOUNT, muxed.toCharArray())); + } + + + @Test + public void testDecodeEmpty() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Encoded char array must have a length of at least 5.", e.getMessage()); + } + } + + @Test + public void testCorruptedChecksum() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHE55".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Checksum invalid", e.getMessage()); + } + } + + @Test + public void testCorruptedPayload() { + try { + StrKey.decodeCheck(StrKey.VersionByte.ACCOUNT_ID, "GA3D5KRYM6CB7OWOOOORR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5".toCharArray()); + fail(); + } catch (Exception e) { + assertEquals("Unused bits should be set to 0.", e.getMessage()); + } + } + + + @Test + public void testMuxedDecodeErrors() { + // non-canonical representation due to extra character + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOGA".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Encoded char array has leftover character.")); + + } + + // non-canonical representation due to leftover bits set to 1 (some of the test strkeys are too short for a muxed account + + // 1 unused bit (length 69) + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOH".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + + // 4 unused bits (length 68) + + // 'B' is equivalent to 0b00001 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNB".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'C' is equivalent to 0b00010 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNC".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'E' is equivalent to 0b00100 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNE".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodeInvalidVersionByte() { - String address = "GCZHXL5HXQX5ABDM26LHYRCQZ5OJFHLOPLZX47WEBP3V2PF5AVFK2A5D"; - try { - StrKey.decodeCheck(StrKey.VersionByte.SEED, address.toCharArray()); - fail(); - } catch (FormatException e) {} + // 'I' is equivalent to 0b01000 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNI".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodeInvalidSeed() { - String seed = "SAA6NXOBOXP3RXGAXBW6PGFI5BPK4ODVAWITS4VDOMN5C2M4B66ZML"; - try { - StrKey.decodeCheck(StrKey.VersionByte.SEED, seed.toCharArray()); - fail(); - } catch (Exception e) {} + // '7' is equivalent to 0b11111 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN7".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodedVersionByte() { - assertEquals(StrKey.decodeVersionByte("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"), StrKey.VersionByte.ACCOUNT_ID); - assertEquals(StrKey.decodeVersionByte("SDJHRQF4GCMIIKAAAQ6IHY42X73FQFLHUULAPSKKD4DFDM7UXWWCRHBE"), StrKey.VersionByte.SEED); - assertEquals(StrKey.decodeVersionByte("TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"), StrKey.VersionByte.PRE_AUTH_TX); - assertEquals(StrKey.decodeVersionByte("XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"), StrKey.VersionByte.SHA256_HASH); + // '6' is equivalent to 0b11110 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN6".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - @Test() - public void testDecodedVersionByteThrows() { - try { - StrKey.decodeVersionByte("INVALIDBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6INVALID"); - fail(); - } catch (FormatException e) { - assertEquals("Version byte is invalid", e.getMessage()); - } + // '4' is equivalent to 0b11100 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKN4".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + + } + + // 'Y' is equivalent to 0b11000 + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNY".toCharArray() + ); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Unused bits should be set to 0.")); + } - // TODO more tests + // 'Q' is equivalent to 0b10000, so there should be no unused bits error + // However, there will be a checksum error + try { + StrKey.decodeCheck( + StrKey.VersionByte.MUXED_ACCOUNT, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNQ".toCharArray() + ); + } catch (Exception e) { + assertEquals("Checksum invalid", e.getMessage()); + } + } + + @Test + public void testEncodeToXdrRoundTrip() { + String address = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ"; + byte[] ed25519 = rawBytes( + 63, 12, 52, 191, 147, 173, 13, 153, 113, 208, + 76, 204, 144, 247, 5, 81, 28, 131, 138, 173, 151, 52, + 164, 162, 251, 13, 122, 3, 252, 127, 232, 154 + ); + AccountID account = StrKey.encodeToXDRAccountId(address); + assertArrayEquals(ed25519, account.getAccountID().getEd25519().getUint256()); + assertEquals(address, StrKey.encodeStellarAccountId(account)); + + MuxedAccount muxedAccount = StrKey.encodeToXDRMuxedAccount(address); + assertEquals(CryptoKeyType.KEY_TYPE_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getEd25519().getUint256()); + assertEquals(address, StrKey.encodeStellarMuxedAccount(muxedAccount)); + + String muxedAddress = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + muxedAccount = StrKey.encodeToXDRMuxedAccount(muxedAddress); + assertEquals(CryptoKeyType.KEY_TYPE_MUXED_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getMed25519().getEd25519().getUint256()); + long memoId = UnsignedLongs.parseUnsignedLong("9223372036854775808"); + assertEquals(memoId, muxedAccount.getMed25519().getId().getUint64().longValue()); + assertEquals(muxedAddress, StrKey.encodeStellarMuxedAccount(muxedAccount)); + + muxedAddress = "MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6"; + muxedAccount = StrKey.encodeToXDRMuxedAccount(muxedAddress); + assertEquals(CryptoKeyType.KEY_TYPE_MUXED_ED25519, muxedAccount.getDiscriminant()); + assertArrayEquals(ed25519, muxedAccount.getMed25519().getEd25519().getUint256()); + assertEquals(0, muxedAccount.getMed25519().getId().getUint64().longValue()); + assertEquals(muxedAddress, StrKey.encodeStellarMuxedAccount(muxedAccount)); + } + } diff --git a/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java b/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java index dd822e80e..b56cbcef6 100644 --- a/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java +++ b/src/test/java/org/stellar/sdk/responses/OperationDeserializerTest.java @@ -175,7 +175,8 @@ public void testDeserializeAllowTrustOperation() { " \"asset_issuer\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + " \"trustee\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + " \"trustor\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\",\n" + - " \"authorize\": true\n" + + " \"authorize\": true\n," + + " \"authorize_to_maintain_liabilities\": false\n" + " }"; AllowTrustOperationResponse operation = (AllowTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class); @@ -183,6 +184,50 @@ public void testDeserializeAllowTrustOperation() { assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM"); assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F"); assertEquals(operation.isAuthorize(), true); + assertEquals(operation.isAuthorizedToMaintainLiabilities(), false); + assertEquals(operation.getAsset(), Asset.createNonNativeAsset("EUR", "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM")); + } + + @Test + public void testDeserializeAllowTrustOperationAuthorizeToMaintainLiabilities() { + String json = "{\n" + + " \"_links\": {\n" + + " \"self\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/operations/3602979345141761\"\n" + + " },\n" + + " \"transaction\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/transactions/1f265c075e8559ee4c21a32ae53337658e52d7504841adad4144c37143ea01a2\"\n" + + " },\n" + + " \"effects\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/operations/3602979345141761/effects\"\n" + + " },\n" + + " \"succeeds\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/effects?order=desc\\u0026cursor=3602979345141761\"\n" + + " },\n" + + " \"precedes\": {\n" + + " \"href\": \"//horizon-testnet.stellar.org/effects?order=asc\\u0026cursor=3602979345141761\"\n" + + " }\n" + + " },\n" + + " \"id\": \"3602979345141761\",\n" + + " \"paging_token\": \"3602979345141761\",\n" + + " \"source_account\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"type\": \"allow_trust\",\n" + + " \"type_i\": 7,\n" + + " \"asset_type\": \"credit_alphanum4\",\n" + + " \"asset_code\": \"EUR\",\n" + + " \"asset_issuer\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"trustee\": \"GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM\",\n" + + " \"trustor\": \"GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F\",\n" + + " \"authorize\": false\n," + + " \"authorize_to_maintain_liabilities\": true\n" + + " }"; + + AllowTrustOperationResponse operation = (AllowTrustOperationResponse) GsonSingleton.getInstance().fromJson(json, OperationResponse.class); + + assertEquals(operation.getTrustee(), "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM"); + assertEquals(operation.getTrustor(), "GDZ55LVXECRTW4G36EZPTHI4XIYS5JUC33TUS22UOETVFVOQ77JXWY4F"); + assertEquals(operation.isAuthorize(), false); + assertEquals(operation.isAuthorizedToMaintainLiabilities(), true); assertEquals(operation.getAsset(), Asset.createNonNativeAsset("EUR", "GDIROJW2YHMSFZJJ4R5XWWNUVND5I45YEWS5DSFKXCHMADZ5V374U2LM")); } diff --git a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java index 442e31879..9f14070df 100644 --- a/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/TransactionDecodeTest.java @@ -22,7 +22,7 @@ public void testDecodeTxBody() throws IOException { byte[] bytes = base64Encoding.decode(txBody); TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(new Long(2373025265623291L), transactionEnvelope.getTx().getSeqNum().getSequenceNumber().getInt64()); + assertEquals(new Long(2373025265623291L), transactionEnvelope.getV0().getTx().getSeqNum().getSequenceNumber().getInt64()); } @Test @@ -56,8 +56,8 @@ public void testTransactionEnvelopeWithMemo() throws IOException { byte[] bytes = base64Encoding.decode(transactionEnvelopeToDecode); TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - assertEquals(1, transactionEnvelope.getTx().getOperations().length); - assertTrue(Arrays.equals(new byte[]{'G', 'O', 'L', 'D'}, transactionEnvelope.getTx().getOperations()[0].getBody().getPaymentOp().getAsset().getAlphaNum4().getAssetCode().getAssetCode4())); + assertEquals(1, transactionEnvelope.getV0().getTx().getOperations().length); + assertTrue(Arrays.equals(new byte[]{'G', 'O', 'L', 'D'}, transactionEnvelope.getV0().getTx().getOperations()[0].getBody().getPaymentOp().getAsset().getAlphaNum4().getAssetCode().getAssetCode4())); } @Test diff --git a/xdr/Stellar-ledger-entries.x b/xdr/Stellar-ledger-entries.x index b511de272..433d4b656 100644 --- a/xdr/Stellar-ledger-entries.x +++ b/xdr/Stellar-ledger-entries.x @@ -160,11 +160,15 @@ struct AccountEntry enum TrustLineFlags { // issuer has authorized account to perform transactions with its credit - AUTHORIZED_FLAG = 1 + AUTHORIZED_FLAG = 1, + // issuer has authorized account to maintain and reduce liabilities for its + // credit + AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG = 2 }; // mask for all trustline flags const MASK_TRUSTLINE_FLAGS = 1; +const MASK_TRUSTLINE_FLAGS_V13 = 3; struct TrustLineEntry { @@ -287,9 +291,11 @@ struct LedgerEntry // the respective envelopes enum EnvelopeType { + ENVELOPE_TYPE_TX_V0 = 0, ENVELOPE_TYPE_SCP = 1, ENVELOPE_TYPE_TX = 2, ENVELOPE_TYPE_AUTH = 3, - ENVELOPE_TYPE_SCPVALUE = 4 + ENVELOPE_TYPE_SCPVALUE = 4, + ENVELOPE_TYPE_TX_FEE_BUMP = 5 }; } diff --git a/xdr/Stellar-ledger.x b/xdr/Stellar-ledger.x index b8eafa2a8..6c2bb88e4 100644 --- a/xdr/Stellar-ledger.x +++ b/xdr/Stellar-ledger.x @@ -366,7 +366,6 @@ struct LedgerCloseMetaV0 union LedgerCloseMeta switch (int v) { case 0: - LedgerCloseMetaV0 v0; + LedgerCloseMetaV0 v0; }; - } diff --git a/xdr/Stellar-overlay.x b/xdr/Stellar-overlay.x index 5e59acd7d..a4ab6b077 100644 --- a/xdr/Stellar-overlay.x +++ b/xdr/Stellar-overlay.x @@ -172,8 +172,8 @@ struct TopologyResponseBody union SurveyResponseBody switch (SurveyMessageCommandType type) { - case SURVEY_TOPOLOGY: - TopologyResponseBody topologyResponseBody; +case SURVEY_TOPOLOGY: + TopologyResponseBody topologyResponseBody; }; union StellarMessage switch (MessageType type) @@ -220,10 +220,10 @@ union AuthenticatedMessage switch (uint32 v) { case 0: struct -{ - uint64 sequence; - StellarMessage message; - HmacSha256Mac mac; + { + uint64 sequence; + StellarMessage message; + HmacSha256Mac mac; } v0; }; } diff --git a/xdr/Stellar-transaction.x b/xdr/Stellar-transaction.x index 74d7a9baf..bee28cbad 100644 --- a/xdr/Stellar-transaction.x +++ b/xdr/Stellar-transaction.x @@ -7,6 +7,19 @@ namespace stellar { +// Source or destination of a payment operation +union MuxedAccount switch (CryptoKeyType type) +{ +case KEY_TYPE_ED25519: + uint256 ed25519; +case KEY_TYPE_MUXED_ED25519: + struct + { + uint64 id; + uint256 ed25519; + } med25519; +}; + struct DecoratedSignature { SignatureHint hint; // last 4 bytes of the public key, used as a hint @@ -55,9 +68,9 @@ struct CreateAccountOp */ struct PaymentOp { - AccountID destination; // recipient of the payment - Asset asset; // what they end up with - int64 amount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset asset; // what they end up with + int64 amount; // amount they end up with }; /* PathPaymentStrictReceive @@ -78,9 +91,9 @@ struct PathPaymentStrictReceiveOp // send (excluding fees). // The operation will fail if can't be met - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destAmount; // amount they end up with + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destAmount; // amount they end up with Asset path<5>; // additional hops it must go through to get there }; @@ -101,16 +114,15 @@ struct PathPaymentStrictSendOp Asset sendAsset; // asset we pay with int64 sendAmount; // amount of sendAsset to send (excluding fees) - AccountID destination; // recipient of the payment - Asset destAsset; // what they end up with - int64 destMin; // the minimum amount of dest asset to - // be received - // The operation will fail if it can't be met + MuxedAccount destination; // recipient of the payment + Asset destAsset; // what they end up with + int64 destMin; // the minimum amount of dest asset to + // be received + // The operation will fail if it can't be met Asset path<5>; // additional hops it must go through to get there }; - /* Creates, updates or deletes an offer Threshold: med @@ -232,7 +244,8 @@ struct AllowTrustOp } asset; - bool authorize; + // 0, or any bitwise combination of TrustLineFlags + uint32 authorize; }; /* Inflation @@ -285,7 +298,7 @@ struct Operation // sourceAccount is the account used to run the operation // if not set, the runtime defaults to "sourceAccount" specified at // the transaction level - AccountID* sourceAccount; + MuxedAccount* sourceAccount; union switch (OperationType type) { @@ -306,7 +319,7 @@ struct Operation case ALLOW_TRUST: AllowTrustOp allowTrustOp; case ACCOUNT_MERGE: - AccountID destination; + MuxedAccount destination; case INFLATION: void; case MANAGE_DATA: @@ -353,6 +366,36 @@ struct TimeBounds // maximum number of operations per transaction const MAX_OPS_PER_TX = 100; +// TransactionV0 is a transaction with the AccountID discriminant stripped off, +// leaving a raw ed25519 public key to identify the source account. This is used +// for backwards compatibility starting from the protocol 12/13 boundary. If an +// "old-style" TransactionEnvelope containing a Transaction is parsed with this +// XDR definition, it will be parsed as a "new-style" TransactionEnvelope +// containing a TransactionV0. +struct TransactionV0 +{ + uint256 sourceAccountEd25519; + uint32 fee; + SequenceNumber seqNum; + TimeBounds* timeBounds; + Memo memo; + Operation operations; + union switch (int v) + { + case 0: + void; + } + ext; +}; + +struct TransactionV0Envelope +{ + TransactionV0 tx; + /* Each decorated signature is a signature over the SHA256 hash of + * a TransactionSignaturePayload */ + DecoratedSignature signatures<20>; +}; + /* a transaction is a container for a set of operations - is executed by an account - fees are collected from the account @@ -363,7 +406,7 @@ const MAX_OPS_PER_TX = 100; struct Transaction { // account used to run the transaction - AccountID sourceAccount; + MuxedAccount sourceAccount; // the fee the sourceAccount will pay uint32 fee; @@ -387,27 +430,65 @@ struct Transaction ext; }; -struct TransactionSignaturePayload +struct TransactionV1Envelope { - Hash networkId; + Transaction tx; + /* Each decorated signature is a signature over the SHA256 hash of + * a TransactionSignaturePayload */ + DecoratedSignature signatures<20>; +}; + +struct FeeBumpTransaction +{ + MuxedAccount feeSource; + int64 fee; union switch (EnvelopeType type) { case ENVELOPE_TYPE_TX: - Transaction tx; - /* All other values of type are invalid */ + TransactionV1Envelope v1; } - taggedTransaction; + innerTx; + union switch (int v) + { + case 0: + void; + } + ext; }; -/* A TransactionEnvelope wraps a transaction with signatures. */ -struct TransactionEnvelope +struct FeeBumpTransactionEnvelope { - Transaction tx; + FeeBumpTransaction tx; /* Each decorated signature is a signature over the SHA256 hash of * a TransactionSignaturePayload */ DecoratedSignature signatures<20>; }; +/* A TransactionEnvelope wraps a transaction with signatures. */ +union TransactionEnvelope switch (EnvelopeType type) +{ +case ENVELOPE_TYPE_TX_V0: + TransactionV0Envelope v0; +case ENVELOPE_TYPE_TX: + TransactionV1Envelope v1; +case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransactionEnvelope feeBump; +}; + +struct TransactionSignaturePayload +{ + Hash networkId; + union switch (EnvelopeType type) + { + // Backwards Compatibility: Use ENVELOPE_TYPE_TX to sign ENVELOPE_TYPE_TX_V0 + case ENVELOPE_TYPE_TX: + Transaction tx; + case ENVELOPE_TYPE_TX_FEE_BUMP: + FeeBumpTransaction feeBump; + } + taggedTransaction; +}; + /* Operation Results section */ /* This result is used when offers are taken during an operation */ @@ -484,18 +565,27 @@ enum PathPaymentStrictReceiveResultCode PATH_PAYMENT_STRICT_RECEIVE_SUCCESS = 0, // success // codes considered as "failure" for the operation - PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input - PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = -2, // not enough funds in source account - PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = -3, // no trust line on source account - PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer - PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = -5, // destination account does not exist - PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = -6, // dest missing a trust line for asset - PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = -7, // dest not authorized to hold asset - PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = -8, // dest would go above their limit - PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset - PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path - PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = -11, // would cross one of its own offers - PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax + PATH_PAYMENT_STRICT_RECEIVE_MALFORMED = -1, // bad input + PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED = + -2, // not enough funds in source account + PATH_PAYMENT_STRICT_RECEIVE_SRC_NO_TRUST = + -3, // no trust line on source account + PATH_PAYMENT_STRICT_RECEIVE_SRC_NOT_AUTHORIZED = + -4, // source not authorized to transfer + PATH_PAYMENT_STRICT_RECEIVE_NO_DESTINATION = + -5, // destination account does not exist + PATH_PAYMENT_STRICT_RECEIVE_NO_TRUST = + -6, // dest missing a trust line for asset + PATH_PAYMENT_STRICT_RECEIVE_NOT_AUTHORIZED = + -7, // dest not authorized to hold asset + PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL = + -8, // dest would go above their limit + PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER = -9, // missing issuer on one asset + PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS = + -10, // not enough offers to satisfy path + PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF = + -11, // would cross one of its own offers + PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX = -12 // could not satisfy sendmax }; struct SimplePaymentResult @@ -527,18 +617,26 @@ enum PathPaymentStrictSendResultCode PATH_PAYMENT_STRICT_SEND_SUCCESS = 0, // success // codes considered as "failure" for the operation - PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input - PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = -2, // not enough funds in source account - PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = -3, // no trust line on source account - PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = -4, // source not authorized to transfer - PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = -5, // destination account does not exist - PATH_PAYMENT_STRICT_SEND_NO_TRUST = -6, // dest missing a trust line for asset - PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = -7, // dest not authorized to hold asset - PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit - PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset - PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = -10, // not enough offers to satisfy path - PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = -11, // would cross one of its own offers - PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin + PATH_PAYMENT_STRICT_SEND_MALFORMED = -1, // bad input + PATH_PAYMENT_STRICT_SEND_UNDERFUNDED = + -2, // not enough funds in source account + PATH_PAYMENT_STRICT_SEND_SRC_NO_TRUST = + -3, // no trust line on source account + PATH_PAYMENT_STRICT_SEND_SRC_NOT_AUTHORIZED = + -4, // source not authorized to transfer + PATH_PAYMENT_STRICT_SEND_NO_DESTINATION = + -5, // destination account does not exist + PATH_PAYMENT_STRICT_SEND_NO_TRUST = + -6, // dest missing a trust line for asset + PATH_PAYMENT_STRICT_SEND_NOT_AUTHORIZED = + -7, // dest not authorized to hold asset + PATH_PAYMENT_STRICT_SEND_LINE_FULL = -8, // dest would go above their limit + PATH_PAYMENT_STRICT_SEND_NO_ISSUER = -9, // missing issuer on one asset + PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS = + -10, // not enough offers to satisfy path + PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF = + -11, // would cross one of its own offers + PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN = -12 // could not satisfy destMin }; union PathPaymentStrictSendResult switch (PathPaymentStrictSendResultCode code) @@ -563,21 +661,25 @@ enum ManageSellOfferResultCode MANAGE_SELL_OFFER_SUCCESS = 0, // codes considered as "failure" for the operation - MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid - MANAGE_SELL_OFFER_SELL_NO_TRUST = -2, // no trust line for what we're selling - MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying + MANAGE_SELL_OFFER_MALFORMED = -1, // generated offer would be invalid + MANAGE_SELL_OFFER_SELL_NO_TRUST = + -2, // no trust line for what we're selling + MANAGE_SELL_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying MANAGE_SELL_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell MANAGE_SELL_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy - MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying - MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell - MANAGE_SELL_OFFER_CROSS_SELF = -8, // would cross an offer from the same user + MANAGE_SELL_OFFER_LINE_FULL = -6, // can't receive more of what it's buying + MANAGE_SELL_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell + MANAGE_SELL_OFFER_CROSS_SELF = + -8, // would cross an offer from the same user MANAGE_SELL_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling MANAGE_SELL_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // update errors - MANAGE_SELL_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer + MANAGE_SELL_OFFER_NOT_FOUND = + -11, // offerID does not match an existing offer - MANAGE_SELL_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer + MANAGE_SELL_OFFER_LOW_RESERVE = + -12 // not enough funds to create a new Offer }; enum ManageOfferEffect @@ -624,14 +726,15 @@ enum ManageBuyOfferResultCode MANAGE_BUY_OFFER_BUY_NO_TRUST = -3, // no trust line for what we're buying MANAGE_BUY_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell MANAGE_BUY_OFFER_BUY_NOT_AUTHORIZED = -5, // not authorized to buy - MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying - MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell - MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user + MANAGE_BUY_OFFER_LINE_FULL = -6, // can't receive more of what it's buying + MANAGE_BUY_OFFER_UNDERFUNDED = -7, // doesn't hold what it's trying to sell + MANAGE_BUY_OFFER_CROSS_SELF = -8, // would cross an offer from the same user MANAGE_BUY_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling MANAGE_BUY_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying // update errors - MANAGE_BUY_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer + MANAGE_BUY_OFFER_NOT_FOUND = + -11, // offerID does not match an existing offer MANAGE_BUY_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer }; @@ -683,7 +786,7 @@ enum ChangeTrustResultCode // cannot create with a limit of 0 CHANGE_TRUST_LOW_RESERVE = -4, // not enough funds to create a new trust line, - CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed + CHANGE_TRUST_SELF_NOT_ALLOWED = -5 // trusting self is not allowed }; union ChangeTrustResult switch (ChangeTrustResultCode code) @@ -811,9 +914,9 @@ enum OperationResultCode { opINNER = 0, // inner object result is valid - opBAD_AUTH = -1, // too few valid signatures / wrong network - opNO_ACCOUNT = -2, // source account was not found - opNOT_SUPPORTED = -3, // operation not supported at this time + opBAD_AUTH = -1, // too few valid signatures / wrong network + opNO_ACCOUNT = -2, // source account was not found + opNOT_SUPPORTED = -3, // operation not supported at this time opTOO_MANY_SUBENTRIES = -4, // max number of subentries already reached opEXCEEDED_WORK_LIMIT = -5 // operation did too much work }; @@ -848,7 +951,7 @@ case opINNER: case BUMP_SEQUENCE: BumpSequenceResult bumpSeqResult; case MANAGE_BUY_OFFER: - ManageBuyOfferResult manageBuyOfferResult; + ManageBuyOfferResult manageBuyOfferResult; case PATH_PAYMENT_STRICT_SEND: PathPaymentStrictSendResult pathPaymentStrictSendResult; } @@ -859,7 +962,8 @@ default: enum TransactionResultCode { - txSUCCESS = 0, // all operations succeeded + txFEE_BUMP_INNER_SUCCESS = 1, // fee bump inner transaction succeeded + txSUCCESS = 0, // all operations succeeded txFAILED = -1, // one of the operations failed (none were applied) @@ -873,7 +977,54 @@ enum TransactionResultCode txNO_ACCOUNT = -8, // source account not found txINSUFFICIENT_FEE = -9, // fee is too small txBAD_AUTH_EXTRA = -10, // unused signatures attached to transaction - txINTERNAL_ERROR = -11 // an unknown error occured + txINTERNAL_ERROR = -11, // an unknown error occured + + txNOT_SUPPORTED = -12, // transaction type not supported + txFEE_BUMP_INNER_FAILED = -13 // fee bump inner transaction failed +}; + +// InnerTransactionResult must be binary compatible with TransactionResult +// because it is be used to represent the result of a Transaction. +struct InnerTransactionResult +{ + // Always 0. Here for binary compatibility. + int64 feeCharged; + + union switch (TransactionResultCode code) + { + // txFEE_BUMP_INNER_SUCCESS is not included + case txSUCCESS: + case txFAILED: + OperationResult results<>; + case txTOO_EARLY: + case txTOO_LATE: + case txMISSING_OPERATION: + case txBAD_SEQ: + case txBAD_AUTH: + case txINSUFFICIENT_BALANCE: + case txNO_ACCOUNT: + case txINSUFFICIENT_FEE: + case txBAD_AUTH_EXTRA: + case txINTERNAL_ERROR: + case txNOT_SUPPORTED: + // txFEE_BUMP_INNER_FAILED is not included + void; + } + result; + + // reserved for future use + union switch (int v) + { + case 0: + void; + } + ext; +}; + +struct InnerTransactionResultPair +{ + Hash transactionHash; // hash of the inner transaction + InnerTransactionResult result; // result for the inner transaction }; struct TransactionResult @@ -882,6 +1033,9 @@ struct TransactionResult union switch (TransactionResultCode code) { + case txFEE_BUMP_INNER_SUCCESS: + case txFEE_BUMP_INNER_FAILED: + InnerTransactionResultPair innerResultPair; case txSUCCESS: case txFAILED: OperationResult results<>; diff --git a/xdr/Stellar-types.x b/xdr/Stellar-types.x index b7a616aef..8f7d5c206 100644 --- a/xdr/Stellar-types.x +++ b/xdr/Stellar-types.x @@ -18,7 +18,10 @@ enum CryptoKeyType { KEY_TYPE_ED25519 = 0, KEY_TYPE_PRE_AUTH_TX = 1, - KEY_TYPE_HASH_X = 2 + KEY_TYPE_HASH_X = 2, + // MUXED enum values for supported type are derived from the enum values + // above by ORing them with 0x100 + KEY_TYPE_MUXED_ED25519 = 0x100 }; enum PublicKeyType @@ -60,22 +63,21 @@ typedef PublicKey NodeID; struct Curve25519Secret { - opaque key[32]; + opaque key[32]; }; struct Curve25519Public { - opaque key[32]; + opaque key[32]; }; struct HmacSha256Key { - opaque key[32]; + opaque key[32]; }; struct HmacSha256Mac { - opaque mac[32]; + opaque mac[32]; }; - } From 610b56746a501d88e4ed57d97522cd656c7b916d Mon Sep 17 00:00:00 2001 From: tamirms Date: Mon, 4 May 2020 19:57:31 +0200 Subject: [PATCH 4/5] Implement FeeBumpTransaction builder, update SEP10 and SEP29 (#278) Implement FeeBumpTransaction builder Update SEP 10 implementation to reject fee bump transactions and muxed accounts Update SEP 29 implementation to support fee bump transactions --- .../org/stellar/sdk/AbstractTransaction.java | 134 +++ .../org/stellar/sdk/FeeBumpTransaction.java | 204 +++++ .../java/org/stellar/sdk/Sep10Challenge.java | 27 +- src/main/java/org/stellar/sdk/Server.java | 105 ++- .../java/org/stellar/sdk/Transaction.java | 240 ++--- .../stellar/sdk/FeeBumpTransactionTest.java | 197 +++++ .../java/org/stellar/sdk/OperationTest.java | 1 + .../org/stellar/sdk/Sep10ChallengeTest.java | 829 ++++++++++-------- src/test/java/org/stellar/sdk/ServerTest.java | 121 ++- .../java/org/stellar/sdk/TransactionTest.java | 81 +- .../stellar/sdk/xdr/InflationDecodeTest.java | 2 +- 11 files changed, 1336 insertions(+), 605 deletions(-) create mode 100644 src/main/java/org/stellar/sdk/AbstractTransaction.java create mode 100644 src/main/java/org/stellar/sdk/FeeBumpTransaction.java create mode 100644 src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java diff --git a/src/main/java/org/stellar/sdk/AbstractTransaction.java b/src/main/java/org/stellar/sdk/AbstractTransaction.java new file mode 100644 index 000000000..556167a05 --- /dev/null +++ b/src/main/java/org/stellar/sdk/AbstractTransaction.java @@ -0,0 +1,134 @@ +package org.stellar.sdk; + +import com.google.common.io.BaseEncoding; +import org.stellar.sdk.xdr.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class AbstractTransaction { + protected final Network mNetwork; + protected List mSignatures; + public static final int MIN_BASE_FEE = 100; + + + AbstractTransaction(Network network) { + this.mNetwork = checkNotNull(network, "network cannot be null"); + this.mSignatures = new ArrayList(); + } + + /** + * Adds a new signature ed25519PublicKey to this transaction. + * @param signer {@link KeyPair} object representing a signer + */ + public void sign(KeyPair signer) { + checkNotNull(signer, "signer cannot be null"); + byte[] txHash = this.hash(); + mSignatures.add(signer.signDecorated(txHash)); + } + + /** + * Adds a new sha256Hash signature to this transaction by revealing preimage. + * @param preimage the sha256 hash of preimage should be equal to signer hash + */ + public void sign(byte[] preimage) { + checkNotNull(preimage, "preimage cannot be null"); + org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature(); + signature.setSignature(preimage); + + byte[] hash = Util.hash(preimage); + byte[] signatureHintBytes = Arrays.copyOfRange(hash, hash.length - 4, hash.length); + SignatureHint signatureHint = new SignatureHint(); + signatureHint.setSignatureHint(signatureHintBytes); + + DecoratedSignature decoratedSignature = new DecoratedSignature(); + decoratedSignature.setHint(signatureHint); + decoratedSignature.setSignature(signature); + + mSignatures.add(decoratedSignature); + } + + /** + * Returns transaction hash. + */ + public byte[] hash() { + return Util.hash(this.signatureBase()); + } + + /** + * Returns transaction hash encoded as a hexadecimal string. + */ + public String hashHex() { + return BaseEncoding.base16().lowerCase().encode(this.hash()); + } + + /** + * Returns signature base. + */ + public abstract byte[] signatureBase(); + + public Network getNetwork() { + return mNetwork; + } + + public List getSignatures() { + return mSignatures; + } + + public abstract TransactionEnvelope toEnvelopeXdr(); + + /** + * Returns base64-encoded TransactionEnvelope XDR object. Transaction need to have at least one signature. + */ + public String toEnvelopeXdrBase64() { + try { + TransactionEnvelope envelope = this.toEnvelopeXdr(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream); + TransactionEnvelope.encode(xdrOutputStream, envelope); + + BaseEncoding base64Encoding = BaseEncoding.base64(); + return base64Encoding.encode(outputStream.toByteArray()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Creates a AbstractTransaction instance from previously build TransactionEnvelope + * @param envelope + * @return + */ + public static AbstractTransaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { + switch (envelope.getDiscriminant()) { + case ENVELOPE_TYPE_TX: + return Transaction.fromV1EnvelopeXdr(envelope.getV1(), network); + case ENVELOPE_TYPE_TX_V0: + return Transaction.fromV0EnvelopeXdr(envelope.getV0(), network); + case ENVELOPE_TYPE_TX_FEE_BUMP: + return FeeBumpTransaction.fromFeeBumpTransactionEnvelope(envelope.getFeeBump(), network); + default: + throw new IllegalArgumentException("transaction type is not supported: "+envelope.getDiscriminant()); + } + } + + /** + * Creates a Transaction instance from previously build TransactionEnvelope + * @param envelope Base-64 encoded TransactionEnvelope + * @return + * @throws IOException + */ + public static AbstractTransaction fromEnvelopeXdr(String envelope, Network network) throws IOException { + BaseEncoding base64Encoding = BaseEncoding.base64(); + byte[] bytes = base64Encoding.decode(envelope); + + TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); + return fromEnvelopeXdr(transactionEnvelope, network); + } +} diff --git a/src/main/java/org/stellar/sdk/FeeBumpTransaction.java b/src/main/java/org/stellar/sdk/FeeBumpTransaction.java new file mode 100644 index 000000000..77a328152 --- /dev/null +++ b/src/main/java/org/stellar/sdk/FeeBumpTransaction.java @@ -0,0 +1,204 @@ +package org.stellar.sdk; + +import com.google.common.base.Objects; +import org.stellar.sdk.xdr.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents Fee Bump Transaction in Stellar network. + */ +public class FeeBumpTransaction extends AbstractTransaction { + private final long mFee; + private final String mFeeAccount; + private final Transaction mInner; + + FeeBumpTransaction(String feeAccount, long fee, Transaction innerTransaction) { + super(innerTransaction.getNetwork()); + this.mFeeAccount = checkNotNull(feeAccount, "feeAccount cannot be null"); + this.mInner = checkNotNull(innerTransaction, "innerTransaction cannot be null"); + this.mFee = fee; + } + + public long getFee() { + return mFee; + } + + public String getFeeAccount() { + return mFeeAccount; + } + + + public Transaction getInnerTransaction() { + return mInner; + } + + public static FeeBumpTransaction fromFeeBumpTransactionEnvelope(FeeBumpTransactionEnvelope envelope, Network network) { + Transaction inner = Transaction.fromV1EnvelopeXdr(envelope.getTx().getInnerTx().getV1(), network); + String feeAccount = StrKey.encodeStellarMuxedAccount(envelope.getTx().getFeeSource()); + long fee = envelope.getTx().getFee().getInt64(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction(feeAccount, fee, inner); + for (DecoratedSignature signature : envelope.getSignatures()) { + feeBump.mSignatures.add(signature); + } + + return feeBump; + } + + private org.stellar.sdk.xdr.FeeBumpTransaction toXdr() { + org.stellar.sdk.xdr.FeeBumpTransaction xdr = new org.stellar.sdk.xdr.FeeBumpTransaction(); + xdr.setExt(new org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionExt()); + xdr.getExt().setDiscriminant(0); + + Int64 xdrFee = new Int64(); + xdrFee.setInt64(mFee); + xdr.setFee(xdrFee); + + xdr.setFeeSource(StrKey.encodeToXDRMuxedAccount(this.mFeeAccount)); + + org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionInnerTx innerXDR = new org.stellar.sdk.xdr.FeeBumpTransaction.FeeBumpTransactionInnerTx(); + innerXDR.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + innerXDR.setV1(this.mInner.toEnvelopeXdr().getV1()); + xdr.setInnerTx(innerXDR); + return xdr; + } + + @Override + public byte[] signatureBase() { + try { + TransactionSignaturePayload payload = new TransactionSignaturePayload(); + TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction taggedTransaction = new TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction(); + taggedTransaction.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_FEE_BUMP); + taggedTransaction.setFeeBump(this.toXdr()); + Hash hash = new Hash(); + hash.setHash(mNetwork.getNetworkId()); + payload.setNetworkId(hash); + payload.setTaggedTransaction(taggedTransaction); + ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream(); + XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream); + payload.encode(xdrOutputStream); + return txOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Generates TransactionEnvelope XDR object. + */ + @Override + public TransactionEnvelope toEnvelopeXdr() { + TransactionEnvelope xdr = new TransactionEnvelope(); + FeeBumpTransactionEnvelope feeBumpEnvelope = new FeeBumpTransactionEnvelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_FEE_BUMP); + + feeBumpEnvelope.setTx(this.toXdr()); + + DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; + signatures = mSignatures.toArray(signatures); + feeBumpEnvelope.setSignatures(signatures); + + xdr.setFeeBump(feeBumpEnvelope); + return xdr; + } + + /** + * Builds a new FeeBumpTransaction object. + */ + public static class Builder { + private final Transaction mInner; + private Long mBaseFee; + private String mFeeAccount; + + + /** + * Construct a new fee bump transaction builder. + * + * @param inner The inner transaction which will be fee bumped. + */ + public Builder(Transaction inner) { + this.mInner = checkNotNull(inner, "inner cannot be null"); + EnvelopeType txType = this.mInner.toEnvelopeXdr().getDiscriminant(); + if (this.mInner.toEnvelopeXdr().getDiscriminant() != EnvelopeType.ENVELOPE_TYPE_TX) { + throw new IllegalArgumentException("invalid transaction type: " + txType); + } + } + + public FeeBumpTransaction.Builder setBaseFee(long baseFee) { + if (this.mBaseFee != null) { + throw new RuntimeException("base fee has been already set."); + } + + if (baseFee < MIN_BASE_FEE) { + throw new IllegalArgumentException("baseFee cannot be smaller than the BASE_FEE (" + MIN_BASE_FEE + "): " + baseFee); + } + + long innerBaseFee = this.mInner.getFee(); + long numOperations = this.mInner.getOperations().length; + if (numOperations > 0) { + innerBaseFee = innerBaseFee / numOperations; + } + + if (baseFee < innerBaseFee) { + throw new IllegalArgumentException("base fee cannot be lower than provided inner transaction base fee"); + } + + long maxFee = baseFee * (numOperations + 1); + if (maxFee < 0) { + throw new IllegalArgumentException("fee overflows 64 bit int"); + } + + this.mBaseFee = maxFee; + return this; + } + + public FeeBumpTransaction.Builder setFeeAccount(String feeAccount) { + if (this.mFeeAccount != null) { + throw new RuntimeException("fee account has been already been set."); + } + + this.mFeeAccount = checkNotNull(feeAccount, "feeAccount cannot be null"); + return this; + } + + public FeeBumpTransaction build() { + return new FeeBumpTransaction( + checkNotNull(this.mFeeAccount, "fee account has to be set. you must call setFeeAccount()."), + checkNotNull(this.mBaseFee, "base fee has to be set. you must call setBaseFee()."), + this.mInner + ); + } + + } + + @Override + public int hashCode() { + return Objects.hashCode( + this.mFee, + this.mInner, + this.mNetwork, + this.mFeeAccount, + this.mSignatures + ); + } + + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof FeeBumpTransaction)) { + return false; + } + + FeeBumpTransaction other = (FeeBumpTransaction) object; + return Objects.equal(this.mFee, other.mFee) && + Objects.equal(this.mFeeAccount, other.mFeeAccount) && + Objects.equal(this.mInner, other.mInner) && + Objects.equal(this.mNetwork, other.mNetwork) && + Objects.equal(this.mSignatures, other.mSignatures); + } + +} diff --git a/src/main/java/org/stellar/sdk/Sep10Challenge.java b/src/main/java/org/stellar/sdk/Sep10Challenge.java index 30d2e3954..47ffd8243 100644 --- a/src/main/java/org/stellar/sdk/Sep10Challenge.java +++ b/src/main/java/org/stellar/sdk/Sep10Challenge.java @@ -21,32 +21,37 @@ public class Sep10Challenge { * @param anchorName The name of the anchor which will be included in the ManageData operation. * @param timebounds The lifetime of the challenge token. */ - public static String newChallenge( + public static Transaction newChallenge( KeyPair signer, Network network, String clientAccountId, String anchorName, TimeBounds timebounds - ) { + ) throws InvalidSep10ChallengeException { byte[] nonce = new byte[48]; SecureRandom random = new SecureRandom(); random.nextBytes(nonce); BaseEncoding base64Encoding = BaseEncoding.base64(); byte[] encodedNonce = base64Encoding.encode(nonce).getBytes(); + + if (StrKey.decodeVersionByte(clientAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException(clientAccountId+" is not a valid account id"); + } + Account sourceAccount = new Account(signer.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) .setSourceAccount(clientAccountId) .build(); Transaction transaction = new Transaction.Builder(sourceAccount, network) .addTimeBounds(timebounds) - .setOperationFee(100) + .setBaseFee(100) .addOperation(operation) .build(); transaction.sign(signer); - return transaction.toEnvelopeXdrBase64(); + return transaction; } /** @@ -69,7 +74,15 @@ public static String newChallenge( */ public static ChallengeTransaction readChallengeTransaction(String challengeXdr, String serverAccountId, Network network) throws InvalidSep10ChallengeException, IOException { // decode the received input as a base64-urlencoded XDR representation of Stellar transaction envelope - Transaction transaction = Transaction.fromEnvelopeXdr(challengeXdr, network); + AbstractTransaction parsed = Transaction.fromEnvelopeXdr(challengeXdr, network); + if (!(parsed instanceof Transaction)) { + throw new InvalidSep10ChallengeException("Transaction cannot be a fee bump transaction"); + } + Transaction transaction = (Transaction)parsed; + + if (StrKey.decodeVersionByte(serverAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException("serverAccountId: "+serverAccountId+" is not a valid account id"); + } // verify that transaction source account is equal to the server's signing key if (!serverAccountId.equals(transaction.getSourceAccount())) { @@ -113,6 +126,10 @@ public static ChallengeTransaction readChallengeTransaction(String challengeXdr, throw new InvalidSep10ChallengeException("Operation should have a source account."); } + if (StrKey.decodeVersionByte(clientAccountId) != StrKey.VersionByte.ACCOUNT_ID) { + throw new InvalidSep10ChallengeException("clientAccountId: "+clientAccountId+" is not a valid account id"); + } + // verify manage data value if (manageDataOperation.getValue().length != 64) { throw new InvalidSep10ChallengeException("Random nonce encoded as base64 should be 64 bytes long."); diff --git a/src/main/java/org/stellar/sdk/Server.java b/src/main/java/org/stellar/sdk/Server.java index 8e54e5d03..578d89f48 100644 --- a/src/main/java/org/stellar/sdk/Server.java +++ b/src/main/java/org/stellar/sdk/Server.java @@ -6,6 +6,8 @@ import okhttp3.Response; import org.stellar.sdk.requests.*; import org.stellar.sdk.responses.*; +import org.stellar.sdk.xdr.CryptoKeyType; +import org.stellar.sdk.xdr.MuxedAccount; import java.io.Closeable; import java.io.IOException; @@ -228,19 +230,7 @@ private void setNetwork(Network network) { } } - /** - * Submits transaction to the network - * - * @param transaction transaction to submit to the network - * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck - * @return {@link SubmitTransactionResponse} - * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. - * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. - * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an - * account which requires a memo. - * @throws IOException - */ - public SubmitTransactionResponse submitTransaction(Transaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + private void checkTransactionNetwork(AbstractTransaction transaction) throws IOException { Optional network = getNetwork(); if (!network.isPresent()) { this.root(); @@ -250,13 +240,20 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction, bool if (!network.get().equals(transaction.getNetwork())) { throw new NetworkMismatchException(network.get(), transaction.getNetwork()); } + } - if (!skipMemoRequiredCheck) { - checkMemoRequired(transaction); - } - + /** + * Submits a base64 encoded transaction envelope to the network + * + * @param transactionXdr base64 encoded transaction envelope to submit to the network + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws IOException + */ + public SubmitTransactionResponse submitTransactionXdr(String transactionXdr) throws IOException { HttpUrl transactionsURI = serverURI.newBuilder().addPathSegment("transactions").build(); - RequestBody requestBody = new FormBody.Builder().add("tx", transaction.toEnvelopeXdrBase64()).build(); + RequestBody requestBody = new FormBody.Builder().add("tx", transactionXdr).build(); Request submitTransactionRequest = new Request.Builder().url(transactionsURI).post(requestBody).build(); Response response = null; @@ -285,7 +282,51 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction, bool } /** - * Submits transaction to the network + * Submits a transaction to the network + * + * @param transaction transaction to submit to the network + * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(Transaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + this.checkTransactionNetwork(transaction); + + if (!skipMemoRequiredCheck) { + checkMemoRequired(transaction); + } + + return this.submitTransactionXdr(transaction.toEnvelopeXdrBase64()); + } + + /** + * Submits a fee bump transaction to the network + * + * @param transaction transaction to submit to the network + * @param skipMemoRequiredCheck set to true to skip memoRequiredCheck + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(FeeBumpTransaction transaction, boolean skipMemoRequiredCheck) throws IOException, AccountRequiresMemoException { + this.checkTransactionNetwork(transaction); + + if (!skipMemoRequiredCheck) { + checkMemoRequired(transaction.getInnerTransaction()); + } + + return this.submitTransactionXdr(transaction.toEnvelopeXdrBase64()); + } + + /** + * Submits a transaction to the network * * This function will always check if the destination account requires a memo in the transaction as * defined in SEP-0029 @@ -303,6 +344,29 @@ public SubmitTransactionResponse submitTransaction(Transaction transaction) thro return submitTransaction(transaction, false); } + /** + * Submits a fee bump transaction to the network + * + * This function will always check if the destination account requires a memo in the transaction as + * defined in SEP-0029 + * If you want to skip this check, use {@link Server#submitTransaction(Transaction, boolean)}. + * + * @param transaction transaction to submit to the network. + * @return {@link SubmitTransactionResponse} + * @throws SubmitTransactionTimeoutResponseException When Horizon returns a Timeout or connection timeout occured. + * @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned. + * @throws AccountRequiresMemoException when a transaction is trying to submit an operation to an + * account which requires a memo. + * @throws IOException + */ + public SubmitTransactionResponse submitTransaction(FeeBumpTransaction transaction) throws IOException, AccountRequiresMemoException { + return submitTransaction(transaction, false); + } + + private boolean hashMemoId(String muxedAccount) { + return StrKey.encodeToXDRMuxedAccount(muxedAccount).getDiscriminant() == CryptoKeyType.KEY_TYPE_MUXED_ED25519; + } + /** * checkMemoRequired implements a memo required check as defined in * SEP-0029 @@ -332,9 +396,10 @@ private void checkMemoRequired(Transaction transaction) throws IOException, Acco } else { continue; } - if (destinations.contains(destination)) { + if (destinations.contains(destination) || hashMemoId(destination)) { continue; } + destinations.add(destination); AccountResponse.Data data; try { diff --git a/src/main/java/org/stellar/sdk/Transaction.java b/src/main/java/org/stellar/sdk/Transaction.java index 71742939a..43a4f29cb 100644 --- a/src/main/java/org/stellar/sdk/Transaction.java +++ b/src/main/java/org/stellar/sdk/Transaction.java @@ -1,10 +1,8 @@ package org.stellar.sdk; import com.google.common.base.Objects; -import com.google.common.io.BaseEncoding; import org.stellar.sdk.xdr.*; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -19,121 +17,60 @@ /** * Represents Transaction in Stellar network. */ -public class Transaction { - private final int mFee; +public class Transaction extends AbstractTransaction { + private final long mFee; private final String mSourceAccount; private final long mSequenceNumber; private final Operation[] mOperations; private final Memo mMemo; private final TimeBounds mTimeBounds; - private final Network mNetwork; - private List mSignatures; + private EnvelopeType envelopeType = EnvelopeType.ENVELOPE_TYPE_TX_V0; Transaction( String sourceAccount, - int fee, + long fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds, Network network ) { + super(network); this.mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null"); this.mSequenceNumber = checkNotNull(sequenceNumber, "sequenceNumber cannot be null"); this.mOperations = checkNotNull(operations, "operations cannot be null"); checkArgument(operations.length > 0, "At least one operation required"); this.mFee = fee; - this.mSignatures = new ArrayList(); this.mMemo = memo != null ? memo : Memo.none(); this.mTimeBounds = timeBounds; - this.mNetwork = checkNotNull(network, "network cannot be null"); } - /** - * Adds a new signature ed25519PublicKey to this transaction. - * @param signer {@link KeyPair} object representing a signer - */ - public void sign(KeyPair signer) { - checkNotNull(signer, "signer cannot be null"); - byte[] txHash = this.hash(); - mSignatures.add(signer.signDecorated(txHash)); - } - - /** - * Adds a new sha256Hash signature to this transaction by revealing preimage. - * @param preimage the sha256 hash of preimage should be equal to signer hash - */ - public void sign(byte[] preimage) { - checkNotNull(preimage, "preimage cannot be null"); - org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature(); - signature.setSignature(preimage); - - byte[] hash = Util.hash(preimage); - byte[] signatureHintBytes = Arrays.copyOfRange(hash, hash.length - 4, hash.length); - SignatureHint signatureHint = new SignatureHint(); - signatureHint.setSignatureHint(signatureHintBytes); - - DecoratedSignature decoratedSignature = new DecoratedSignature(); - decoratedSignature.setHint(signatureHint); - decoratedSignature.setSignature(signature); - - mSignatures.add(decoratedSignature); - } - - /** - * Returns transaction hash. - */ - public byte[] hash() { - return Util.hash(this.signatureBase()); - } - - private org.stellar.sdk.xdr.Transaction convertV0Tx(TransactionV0 v0Tx) { - org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction(); - - v1Tx.setMemo(v0Tx.getMemo()); - v1Tx.setFee(v0Tx.getFee()); - v1Tx.setOperations(v0Tx.getOperations()); - v1Tx.setSeqNum(v0Tx.getSeqNum()); - v1Tx.setTimeBounds(v0Tx.getTimeBounds()); - - org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); - ext.setDiscriminant(0); - v1Tx.setExt(ext); - - MuxedAccount sourceAccount = new MuxedAccount(); - sourceAccount.setDiscriminant(CryptoKeyType.KEY_TYPE_ED25519); - sourceAccount.setEd25519(v0Tx.getSourceAccountEd25519()); - v1Tx.setSourceAccount(sourceAccount); - - return v1Tx; + // setEnvelopeType is only used in tests which is why this method is package protected + void setEnvelopeType(EnvelopeType envelopeType) { + this.envelopeType = envelopeType; } - /** - * Returns signature base. - */ + @Override public byte[] signatureBase() { try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - // Hashed NetworkID - outputStream.write(mNetwork.getNetworkId()); - // Envelope Type - 4 bytes - outputStream.write(ByteBuffer.allocate(4).putInt(EnvelopeType.ENVELOPE_TYPE_TX.getValue()).array()); - // Transaction XDR bytes + TransactionSignaturePayload payload = new TransactionSignaturePayload(); + TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction taggedTransaction = new TransactionSignaturePayload.TransactionSignaturePayloadTaggedTransaction(); + taggedTransaction.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + taggedTransaction.setTx(this.toV1Xdr()); + Hash hash = new Hash(); + hash.setHash(mNetwork.getNetworkId()); + payload.setNetworkId(hash); + payload.setTaggedTransaction(taggedTransaction); ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream(); XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream); - convertV0Tx(this.toXdr()).encode(xdrOutputStream); - outputStream.write(txOutputStream.toByteArray()); - - return outputStream.toByteArray(); - } catch (IOException exception) { - return null; + payload.encode(xdrOutputStream); + return txOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); } } - public Network getNetwork() { - return mNetwork; - } public String getSourceAccount() { return mSourceAccount; @@ -143,10 +80,6 @@ public long getSequenceNumber() { return mSequenceNumber; } - public List getSignatures() { - return mSignatures; - } - public Memo getMemo() { return mMemo; } @@ -161,7 +94,7 @@ public TimeBounds getTimeBounds() { /** * Returns fee paid for transaction in stroops (1 stroop = 0.0000001 XLM). */ - public int getFee() { + public long getFee() { return mFee; } @@ -178,7 +111,7 @@ public Operation[] getOperations() { private TransactionV0 toXdr() { // fee Uint32 fee = new Uint32(); - fee.setUint32(mFee); + fee.setUint32((int)mFee); // sequenceNumber Int64 sequenceNumberUint = new Int64(); sequenceNumberUint.setInt64(mSequenceNumber); @@ -204,18 +137,36 @@ private TransactionV0 toXdr() { return transaction; } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope Base-64 encoded TransactionEnvelope - * @return - * @throws IOException - */ - public static Transaction fromEnvelopeXdr(String envelope, Network network) throws IOException { - BaseEncoding base64Encoding = BaseEncoding.base64(); - byte[] bytes = base64Encoding.decode(envelope); + private org.stellar.sdk.xdr.Transaction toV1Xdr() { - TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes))); - return fromEnvelopeXdr(transactionEnvelope, network); + // fee + Uint32 fee = new Uint32(); + fee.setUint32((int)mFee); + // sequenceNumber + Int64 sequenceNumberUint = new Int64(); + sequenceNumberUint.setInt64(mSequenceNumber); + SequenceNumber sequenceNumber = new SequenceNumber(); + sequenceNumber.setSequenceNumber(sequenceNumberUint); + // operations + org.stellar.sdk.xdr.Operation[] operations = new org.stellar.sdk.xdr.Operation[mOperations.length]; + for (int i = 0; i < mOperations.length; i++) { + operations[i] = mOperations[i].toXdr(); + } + // ext + org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt(); + ext.setDiscriminant(0); + + + org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction(); + v1Tx.setFee(fee); + v1Tx.setSeqNum(sequenceNumber); + v1Tx.setSourceAccount(StrKey.encodeToXDRMuxedAccount(mSourceAccount)); + v1Tx.setOperations(operations); + v1Tx.setMemo(mMemo.toXdr()); + v1Tx.setTimeBounds(mTimeBounds == null ? null : mTimeBounds.toXdr()); + v1Tx.setExt(ext); + + return v1Tx; } public static Transaction fromV0EnvelopeXdr(TransactionV0Envelope envelope, Network network) { @@ -274,79 +225,48 @@ public static Transaction fromV1EnvelopeXdr(TransactionV1Envelope envelope, Netw return transaction; } - /** - * Creates a Transaction instance from previously build TransactionEnvelope - * @param envelope - * @return - */ - public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope, Network network) { - switch (envelope.getDiscriminant()) { - case ENVELOPE_TYPE_TX: - return fromV1EnvelopeXdr(envelope.getV1(), network); - case ENVELOPE_TYPE_TX_V0: - return fromV0EnvelopeXdr(envelope.getV0(), network); - default: - throw new IllegalArgumentException("transaction type is not supported: "+envelope.getDiscriminant()); - } - } - /** * Generates TransactionEnvelope XDR object. */ + @Override public TransactionEnvelope toEnvelopeXdr() { TransactionEnvelope xdr = new TransactionEnvelope(); - TransactionV0Envelope v0Envelope = new TransactionV0Envelope(); - xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_V0); - v0Envelope.setTx(this.toXdr()); - DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()]; signatures = mSignatures.toArray(signatures); - v0Envelope.setSignatures(signatures); - xdr.setV0(v0Envelope); - return xdr; - } - - /** - * Returns base64-encoded TransactionEnvelope XDR object. Transaction need to have at least one signature. - */ - public String toEnvelopeXdrBase64() { - try { - TransactionEnvelope envelope = this.toEnvelopeXdr(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream); - TransactionEnvelope.encode(xdrOutputStream, envelope); - BaseEncoding base64Encoding = BaseEncoding.base64(); - return base64Encoding.encode(outputStream.toByteArray()); - } catch (IOException e) { - throw new AssertionError(e); + if (this.envelopeType == EnvelopeType.ENVELOPE_TYPE_TX) { + TransactionV1Envelope v1Envelope = new TransactionV1Envelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX); + v1Envelope.setTx(this.toV1Xdr()); + v1Envelope.setSignatures(signatures); + xdr.setV1(v1Envelope); + } else if (this.envelopeType == EnvelopeType.ENVELOPE_TYPE_TX_V0) { + TransactionV0Envelope v0Envelope = new TransactionV0Envelope(); + xdr.setDiscriminant(EnvelopeType.ENVELOPE_TYPE_TX_V0); + v0Envelope.setTx(this.toXdr()); + v0Envelope.setSignatures(signatures); + xdr.setV0(v0Envelope); + } else { + throw new RuntimeException("invalid envelope type: "+this.envelopeType); } + + return xdr; } /** * Builds a new Transaction object. */ public static class Builder { - private static final int BASE_FEE = 100; private final TransactionBuilderAccount mSourceAccount; private Memo mMemo; private TimeBounds mTimeBounds; List mOperations; private boolean timeoutSet; - private static Integer defaultOperationFee; - private Integer operationFee; + private Integer mBaseFee; private Network mNetwork; public static final long TIMEOUT_INFINITE = 0; - public static void setDefaultOperationFee(int opFee) { - if (opFee < BASE_FEE) { - throw new IllegalArgumentException("DefaultOperationFee cannot be smaller than the BASE_FEE (" + BASE_FEE + "): " + opFee); - } - - defaultOperationFee = opFee; - } - /** * Construct a new transaction builder. * @param sourceAccount The source account for this transaction. This account is the account @@ -358,7 +278,6 @@ public Builder(TransactionBuilderAccount sourceAccount, Network network) { mSourceAccount = sourceAccount; mOperations = Collections.synchronizedList(new ArrayList()); mNetwork = checkNotNull(network, "Network cannot be null"); - operationFee = defaultOperationFee; } public int getOperationsCount() { @@ -445,12 +364,12 @@ public Builder setTimeout(long timeout) { return this; } - public Builder setOperationFee(int operationFee) { - if (operationFee < BASE_FEE) { - throw new IllegalArgumentException("OperationFee cannot be smaller than the BASE_FEE (" + BASE_FEE + "): " + operationFee); + public Builder setBaseFee(int baseFee) { + if (baseFee < MIN_BASE_FEE) { + throw new IllegalArgumentException("baseFee cannot be smaller than the BASE_FEE (" + MIN_BASE_FEE + "): " + baseFee); } - this.operationFee = operationFee; + this.mBaseFee = baseFee; return this; } @@ -463,9 +382,8 @@ public Transaction build() { throw new RuntimeException("TimeBounds has to be set or you must call setTimeout(TIMEOUT_INFINITE)."); } - if (operationFee == null) { - System.out.println("[TransactionBuilder] The `operationFee` parameter of `TransactionBuilder` is required. Setting to BASE_FEE=" + BASE_FEE + ". Future versions of this library will error if not provided."); - operationFee = BASE_FEE; + if (mBaseFee == null) { + throw new RuntimeException("mBaseFee has to be set. you must call setBaseFee()."); } if (mNetwork == null) { @@ -476,7 +394,7 @@ public Transaction build() { operations = mOperations.toArray(operations); Transaction transaction = new Transaction( mSourceAccount.getAccountId(), - operations.length * operationFee, + operations.length * mBaseFee, mSourceAccount.getIncrementedSequenceNumber(), operations, mMemo, @@ -498,7 +416,8 @@ public int hashCode() { Arrays.hashCode(this.mOperations), this.mMemo, this.mTimeBounds, - this.mSignatures + this.mSignatures, + this.mNetwork ); } @@ -515,6 +434,7 @@ public boolean equals(Object object) { Arrays.equals(this.mOperations, other.mOperations) && Objects.equal(this.mMemo, other.mMemo) && Objects.equal(this.mTimeBounds, other.mTimeBounds) && + Objects.equal(this.mNetwork, other.mNetwork) && Objects.equal(this.mSignatures, other.mSignatures); } } diff --git a/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java b/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java new file mode 100644 index 000000000..e000b8af4 --- /dev/null +++ b/src/test/java/org/stellar/sdk/FeeBumpTransactionTest.java @@ -0,0 +1,197 @@ +package org.stellar.sdk; + +import org.junit.Test; +import org.stellar.sdk.xdr.EnvelopeType; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class FeeBumpTransactionTest { + + private Transaction createInnerTransaction(int baseFee) { + KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); + + Account account = new Account(source.getAccountId(), 2908908335136768L); + Transaction inner = new Transaction.Builder(account, Network.TESTNET) + .addOperation(new PaymentOperation.Builder( + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + new AssetTypeNative(), + "200" + ).build()) + .setBaseFee(baseFee) + .addTimeBounds(new TimeBounds(10, 11)) + .build(); + + inner.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + inner.sign(source); + return inner; + } + + private Transaction createInnerTransaction() { + return createInnerTransaction(Transaction.MIN_BASE_FEE); + } + + @Test + public void testRequiresFeeAccount() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee account has to be set. you must call setFeeAccount().", e.getMessage()); + } + } + + @Test + public void testSetFeeAccountMultipleTimes() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .setFeeAccount("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee account has been already been set.", e.getMessage()); + } + } + + @Test + public void testRequiresBaseFee() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee has to be set. you must call setBaseFee().", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeMultipleTimes() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .setBaseFee(Transaction.MIN_BASE_FEE) + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee has been already set.", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeBelowNetworkMinimum() { + Transaction inner = createInnerTransaction(); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE -1) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("baseFee cannot be smaller than the BASE_FEE (100): 99", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeBelowInner() { + Transaction inner = createInnerTransaction(Transaction.MIN_BASE_FEE+1); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("base fee cannot be lower than provided inner transaction base fee", e.getMessage()); + } + } + + + @Test + public void testSetBaseFeeOverflowsLong() { + Transaction inner = createInnerTransaction(Transaction.MIN_BASE_FEE+1); + + try { + new FeeBumpTransaction.Builder(inner) + .setBaseFee(Long.MAX_VALUE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + fail(); + } catch (RuntimeException e) { + assertEquals("fee overflows 64 bit int", e.getMessage()); + } + } + + @Test + public void testSetBaseFeeEqualToInner() { + Transaction inner = createInnerTransaction(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals(Transaction.MIN_BASE_FEE*2, feeBump.getFee()); + } + + @Test + public void testHash() { + Transaction inner = createInnerTransaction(); + assertEquals("95dcf35a43a1a05bcd50f3eb148b31127829a9460dc32a17c4a7f7c4677409d4", inner.hashHex()); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals("382b1588ee8b315177a34ae96ebcaeb81c0ad3e04fee7c6b5a583b826517e1e4", feeBump.hashHex()); + } + + @Test + public void testRoundTripXdr() throws IOException { + Transaction inner = createInnerTransaction(); + + FeeBumpTransaction feeBump = new FeeBumpTransaction.Builder(inner) + .setBaseFee(Transaction.MIN_BASE_FEE * 2) + .setFeeAccount("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3") + .build(); + + assertEquals(Transaction.MIN_BASE_FEE * 4, feeBump.getFee()); + assertEquals("GDQNY3PBOJOKYZSRMK2S7LHHGWZIUISD4QORETLMXEWXBI7KFZZMKTL3", feeBump.getFeeAccount()); + assertEquals(inner, feeBump.getInnerTransaction()); + assertEquals(0, feeBump.getSignatures().size()); + + FeeBumpTransaction fromXdr = (FeeBumpTransaction) AbstractTransaction.fromEnvelopeXdr( + feeBump.toEnvelopeXdrBase64(), Network.TESTNET + ); + + assertEquals(feeBump, fromXdr); + + + KeyPair signer = KeyPair.random(); + feeBump.sign(signer); + fromXdr = (FeeBumpTransaction) AbstractTransaction.fromEnvelopeXdr( + feeBump.toEnvelopeXdrBase64(), Network.TESTNET + ); + assertEquals(feeBump, fromXdr); + + assertEquals(1, feeBump.getSignatures().size()); + signer.verify(feeBump.hash(), feeBump.getSignatures().get(0).getSignature().getSignature()); + } +} diff --git a/src/test/java/org/stellar/sdk/OperationTest.java b/src/test/java/org/stellar/sdk/OperationTest.java index be1a5249c..9ad3b091b 100644 --- a/src/test/java/org/stellar/sdk/OperationTest.java +++ b/src/test/java/org/stellar/sdk/OperationTest.java @@ -438,6 +438,7 @@ public void testSetOptionsOperationPreAuthTxSigner() { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); // GC5SIC4E3V56VOHJ3OZAX5SJDTWY52JYI2AFK6PUGSXFVRJQYQXXZBZF diff --git a/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java b/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java index 42878c49a..2d7b759f9 100644 --- a/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java +++ b/src/test/java/org/stellar/sdk/Sep10ChallengeTest.java @@ -1,7 +1,10 @@ package org.stellar.sdk; +import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import org.junit.Test; +import org.stellar.sdk.xdr.EnvelopeType; +import org.stellar.sdk.xdr.TransactionEnvelope; import java.io.IOException; import java.security.SecureRandom; @@ -15,7 +18,7 @@ public class Sep10ChallengeTest { @Test - public void testChallenge() throws IOException { + public void testChallenge() throws InvalidSep10ChallengeException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -23,15 +26,14 @@ public void testChallenge() throws IOException { long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "angkor wat", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "angkor wat", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); assertEquals(Network.TESTNET, transaction.getNetwork()); assertEquals(100, transaction.getFee()); @@ -50,10 +52,33 @@ public void testChallenge() throws IOException { assertEquals(1, transaction.getSignatures().size()); assertTrue( - server.verify(transaction.hash(), transaction.getSignatures().get(0).getSignature().getSignature()) + server.verify(transaction.hash(), transaction.getSignatures().get(0).getSignature().getSignature()) ); } + @Test + public void testNewChallengeRejectsMuxedClientAccount() throws InvalidSep10ChallengeException { + try { + KeyPair server = KeyPair.random(); + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Sep10Challenge.newChallenge( + server, + Network.TESTNET, + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + "angkor wat", + timeBounds + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } + + } + @Test public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); @@ -64,16 +89,126 @@ public void testReadChallengeTransactionValidSignedByServer() throws InvalidSep1 long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); - assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(challenge, Network.TESTNET), client.getAccountId()), challengeTransaction); + Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); + } + + @Test + public void testReadChallengeTransactionAcceptsBothV0AndV1() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + + transaction.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX_V0); + TransactionEnvelope v0 = transaction.toEnvelopeXdr(); + String v0Base64 = transaction.toEnvelopeXdrBase64(); + assertEquals(EnvelopeType.ENVELOPE_TYPE_TX_V0, v0.getDiscriminant()); + + transaction.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + TransactionEnvelope v1 = transaction.toEnvelopeXdr(); + String v1Base64 = transaction.toEnvelopeXdrBase64(); + assertEquals(EnvelopeType.ENVELOPE_TYPE_TX, v1.getDiscriminant()); + + for (String envelopeBase64 : ImmutableList.of(v0Base64, v1Base64)) { + Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction( + envelopeBase64, + server.getAccountId(), + Network.TESTNET + ); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); + } + } + + @Test + public void testReadChallengeTransactionRejectsMuxedServer() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + + try { + Sep10Challenge.readChallengeTransaction( + transaction.toEnvelopeXdrBase64(), + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Network.TESTNET + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("serverAccountId: MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } + } + + @Test + public void testReadChallengeTransactionRejectsMuxedClient() throws InvalidSep10ChallengeException, IOException { + KeyPair server = KeyPair.random(); + KeyPair client = KeyPair.random(); + Network network = Network.TESTNET; + + long now = System.currentTimeMillis() / 1000L; + long end = now + 300; + TimeBounds timeBounds = new TimeBounds(now, end); + + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds + ); + Operation[] operations = transaction.getOperations(); + operations[0].setSourceAccount("MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"); + Transaction withMuxedClient = new Transaction( + transaction.getSourceAccount(), + transaction.getFee(), + transaction.getSequenceNumber(), + operations, + transaction.getMemo(), + transaction.getTimeBounds(), + transaction.getNetwork() + ); + withMuxedClient.getSignatures().addAll(transaction.mSignatures); + + try { + Sep10Challenge.readChallengeTransaction( + withMuxedClient.toEnvelopeXdrBase64(), + "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG", + Network.TESTNET + ); + fail(); + } catch (InvalidSep10ChallengeException e) { + assertEquals("serverAccountId: MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG is not a valid account id", e.getMessage()); + } } @Test @@ -86,19 +221,18 @@ public void testReadChallengeTransactionValidSignedByServerAndClient() throws In long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(client); Sep10Challenge.ChallengeTransaction challengeTransaction = Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); - assertEquals(new Sep10Challenge.ChallengeTransaction(Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdrBase64(), Network.TESTNET), client.getAccountId()), challengeTransaction); + assertEquals(new Sep10Challenge.ChallengeTransaction(transaction, client.getAccountId()), challengeTransaction); } @Test @@ -120,18 +254,18 @@ public void testReadChallengeTransactionInvalidNotSignedByServer() throws IOExce Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(client); @@ -153,15 +287,15 @@ public void testReadChallengeTransactionInvalidCorrupted() throws InvalidSep10Ch long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + client.getAccountId(), + "Stellar Test", + timeBounds ); - challenge = challenge.replace("A", "B"); + String challenge = transaction.toEnvelopeXdrBase64().replace("A", "B"); try { Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); @@ -172,7 +306,7 @@ public void testReadChallengeTransactionInvalidCorrupted() throws InvalidSep10Ch } @Test - public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws IOException { + public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -180,17 +314,17 @@ public void testReadChallengeTransactionInvalidServerAccountIDMismatch() throws long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); // assertThrows requires Java 8+ try { - Sep10Challenge.readChallengeTransaction(challenge, KeyPair.random().getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), KeyPair.random().getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction source account is not equal to server's account.", e.getMessage()); @@ -216,18 +350,18 @@ public void testReadChallengeTransactionInvalidSeqNoNotZero() throws IOException Account sourceAccount = new Account(server.getAccountId(), 100L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -259,17 +393,17 @@ public void testReadChallengeTransactionInvalidTimeboundsInfinite() throws IOExc Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{operation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -297,17 +431,17 @@ public void testReadChallengeTransactionInvalidNoTimeBounds() throws IOException Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation operation = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{operation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - null, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + null, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -321,7 +455,7 @@ public void testReadChallengeTransactionInvalidNoTimeBounds() throws IOException } @Test - public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOException { + public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -330,16 +464,16 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOExc long end = current + 600; TimeBounds timeBounds = new TimeBounds(start, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); try { - Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction is not within range of the specified timebounds.", e.getMessage()); @@ -347,7 +481,7 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooEarly() throws IOExc } @Test - public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws IOException { + public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair client = KeyPair.random(); @@ -356,16 +490,16 @@ public void testReadChallengeTransactionInvalidTimeBoundsTooLate() throws IOExce long end = current - 300; TimeBounds timeBounds = new TimeBounds(start, end); - String challenge = Sep10Challenge.newChallenge( - server, - Network.TESTNET, - client.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + Network.TESTNET, + client.getAccountId(), + "Stellar Test", + timeBounds ); try { - Sep10Challenge.readChallengeTransaction(challenge, server.getAccountId(), Network.TESTNET); + Sep10Challenge.readChallengeTransaction(transaction.toEnvelopeXdrBase64(), server.getAccountId(), Network.TESTNET); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction is not within range of the specified timebounds.", e.getMessage()); @@ -391,22 +525,22 @@ public void testReadChallengeTransactionInvalidTooManyOperations() throws IOExce Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); ManageDataOperation manageDataOperation2 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1, manageDataOperation2}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -431,18 +565,18 @@ public void testReadChallengeTransactionInvalidOperationWrongType() throws IOExc Account sourceAccount = new Account(server.getAccountId(), -1L); SetOptionsOperation setOptionsOperation = new SetOptionsOperation.Builder() - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{setOptionsOperation}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -473,17 +607,17 @@ public void testReadChallengeTransactionInvalidOperationNoSourceAccount() throws Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .build(); + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -515,18 +649,18 @@ public void testReadChallengeTransactionInvalidDataValueWrongEncodedLength() thr Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -553,18 +687,18 @@ public void testReadChallengeTransactionInvalidDataValueCorruptBase64() throws I byte[] encodedNonce = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes("UTF-8"); Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -597,18 +731,18 @@ public void testReadChallengeTransactionInvalidDataValueWrongByteLength() throws Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(client.getAccountId()) - .build(); + .setSourceAccount(client.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(server); String challenge = transaction.toEnvelopeXdrBase64(); @@ -642,26 +776,26 @@ public void testVerifyChallengeTransactionThresholdInvalidNotSignedByServer() th Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(masterClient.getAccountId()) - .build(); + .setSourceAccount(masterClient.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(masterClient); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 6; @@ -684,19 +818,18 @@ public void testVerifyChallengeTransactionThresholdValidServerAndClientKeyMeetin long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); Set signers = new HashSet(Collections.singletonList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 255) + new Sep10Challenge.Signer(masterClient.getAccountId(), 255) )); int threshold = 255; @@ -715,23 +848,22 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -750,22 +882,21 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 3; @@ -785,15 +916,14 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); @@ -801,12 +931,12 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4), - new Sep10Challenge.Signer(preauthTxHash, 10), - new Sep10Challenge.Signer(xHash, 10), - new Sep10Challenge.Signer(unknownSignerType, 10) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4), + new Sep10Challenge.Signer(preauthTxHash, 10), + new Sep10Challenge.Signer(xHash, 10), + new Sep10Challenge.Signer(unknownSignerType, 10) )); int threshold = 3; @@ -815,7 +945,7 @@ public void testVerifyChallengeTransactionThresholdValidServerAndMultipleClientK } @Test - public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClientKeyNotMeetingThreshold() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClientKeyNotMeetingThreshold() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -826,22 +956,21 @@ public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClien long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -854,7 +983,7 @@ public void testVerifyChallengeTransactionThresholdInvalidServerAndMultipleClien } @Test - public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -865,24 +994,23 @@ public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized( long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); transaction.sign(KeyPair.random()); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 1), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), - new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) + new Sep10Challenge.Signer(masterClient.getAccountId(), 1), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 2), + new Sep10Challenge.Signer(signerClient2.getAccountId(), 4) )); int threshold = 7; @@ -895,7 +1023,7 @@ public void testVerifyChallengeTransactionThresholdInvalidClientKeyUnrecognized( } @Test - public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -906,15 +1034,14 @@ public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOE long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -930,7 +1057,7 @@ public void testVerifyChallengeTransactionThresholdInvalidNoSigners() throws IOE } @Test - public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() throws IOException { + public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -941,15 +1068,14 @@ public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() t long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -958,9 +1084,9 @@ public void testVerifyChallengeTransactionThresholdInvalidNoPublicKeySigners() t String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(preauthTxHash, 1), - new Sep10Challenge.Signer(xHash, 2), - new Sep10Challenge.Signer(unknownSignerType, 2) + new Sep10Challenge.Signer(preauthTxHash, 1), + new Sep10Challenge.Signer(xHash, 2), + new Sep10Challenge.Signer(unknownSignerType, 2) )); int threshold = 3; @@ -982,21 +1108,20 @@ public void testVerifyChallengeTransactionThresholdWeightsAddToMoreThan8Bits() t long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList( - new Sep10Challenge.Signer(masterClient.getAccountId(), 255), - new Sep10Challenge.Signer(signerClient1.getAccountId(), 1) + new Sep10Challenge.Signer(masterClient.getAccountId(), 255), + new Sep10Challenge.Signer(signerClient1.getAccountId(), 1) )); int threshold = 1; @@ -1025,18 +1150,18 @@ public void testVerifyChallengeTransactionSignersInvalidServer() throws IOExcept Account sourceAccount = new Account(server.getAccountId(), -1L); ManageDataOperation manageDataOperation1 = new ManageDataOperation.Builder(anchorName + " auth", encodedNonce) - .setSourceAccount(masterClient.getAccountId()) - .build(); + .setSourceAccount(masterClient.getAccountId()) + .build(); Operation[] operations = new Operation[]{manageDataOperation1}; Transaction transaction = new Transaction( - sourceAccount.getAccountId(), - 100 * operations.length, - sourceAccount.getIncrementedSequenceNumber(), - operations, - Memo.none(), - timeBounds, - network + sourceAccount.getAccountId(), + 100 * operations.length, + sourceAccount.getIncrementedSequenceNumber(), + operations, + Memo.none(), + timeBounds, + network ); transaction.sign(masterClient); @@ -1062,15 +1187,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientMasterKey() long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(masterClient); Set signers = new HashSet(Collections.singletonList(masterClient.getAccountId())); @@ -1079,7 +1203,7 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientMasterKey() } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1090,15 +1214,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() thro long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), signerClient2.getAccountId(), KeyPair.random().getAccountId())); try { Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); @@ -1109,7 +1232,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndNoClient() thro } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnrecognized() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnrecognized() throws InvalidSep10ChallengeException, IOException { Network network = Network.TESTNET; KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); @@ -1120,15 +1243,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientKeyUnreco long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, Network.TESTNET); transaction.sign(masterClient); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -1155,15 +1277,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndMultipleClientSig long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); transaction.sign(signerClient2); @@ -1184,15 +1305,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndMultipleClientSig long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient2); transaction.sign(signerClient1); @@ -1213,15 +1333,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersNotM long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId())); @@ -1240,15 +1359,14 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersIgno long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), server.getAccountId())); @@ -1258,7 +1376,7 @@ public void testVerifyChallengeTransactionSignersValidServerAndClientSignersIgno @Test - public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgnoresServerSigner() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgnoresServerSigner() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1268,17 +1386,17 @@ public void testVerifyChallengeTransactionSignersInvalidServerNoClientSignersIgn long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), signerClient1.getAccountId(), server.getAccountId())); try { - Sep10Challenge.verifyChallengeTransactionSigners(challenge, server.getAccountId(), network, signers); + Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); fail(); } catch (InvalidSep10ChallengeException e) { assertEquals("Transaction not signed by any client signer.", e.getMessage()); @@ -1297,28 +1415,27 @@ public void testVerifyChallengeTransactionSignersValidIgnorePreauthTxHashAndXHas long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); String preauthTxHash = "TAQCSRX2RIDJNHFIFHWD63X7D7D6TRT5Y2S6E3TEMXTG5W3OECHZ2OG4"; String xHash = "XDRPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; String unknownSignerType = "?ARPF6NZRR7EEVO7ESIWUDXHAOMM2QSKIQQBJK6I2FB7YKDZES5UCLWD"; Set signers = new HashSet(Arrays.asList(masterClient.getAccountId(), - signerClient1.getAccountId(), preauthTxHash, xHash, unknownSignerType)); + signerClient1.getAccountId(), preauthTxHash, xHash, unknownSignerType)); Set signersFound = Sep10Challenge.verifyChallengeTransactionSigners(transaction.toEnvelopeXdrBase64(), server.getAccountId(), network, signers); assertEquals(new HashSet(Collections.singletonList(signerClient1.getAccountId())), signersFound); } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsDuplicateSignatures() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsDuplicateSignatures() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1328,15 +1445,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); transaction.sign(signerClient1); @@ -1350,7 +1466,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa } @Test - public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsSignerSeed() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFailsSignerSeed() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1360,15 +1476,14 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); Set signers = new HashSet(Collections.singletonList(new String(signerClient1.getSecretSeed()))); @@ -1381,7 +1496,7 @@ public void testVerifyChallengeTransactionSignersInvalidServerAndClientSignersFa } @Test - public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1391,15 +1506,14 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws I long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); try { @@ -1411,7 +1525,7 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersNull() throws I } @Test - public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() throws IOException { + public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() throws InvalidSep10ChallengeException, IOException { KeyPair server = KeyPair.random(); KeyPair masterClient = KeyPair.random(); KeyPair signerClient1 = KeyPair.random(); @@ -1421,15 +1535,14 @@ public void testVerifyChallengeTransactionSignersInvalidNoSignersEmptySet() thro long end = now + 300; TimeBounds timeBounds = new TimeBounds(now, end); - String challenge = Sep10Challenge.newChallenge( - server, - network, - masterClient.getAccountId(), - "Stellar Test", - timeBounds + Transaction transaction = Sep10Challenge.newChallenge( + server, + network, + masterClient.getAccountId(), + "Stellar Test", + timeBounds ); - Transaction transaction = Transaction.fromEnvelopeXdr(challenge, network); transaction.sign(signerClient1); try { diff --git a/src/test/java/org/stellar/sdk/ServerTest.java b/src/test/java/org/stellar/sdk/ServerTest.java index d5b33a03b..dfa1656a7 100644 --- a/src/test/java/org/stellar/sdk/ServerTest.java +++ b/src/test/java/org/stellar/sdk/ServerTest.java @@ -13,6 +13,7 @@ import org.stellar.sdk.responses.SubmitTransactionTimeoutResponseException; import org.stellar.sdk.responses.SubmitTransactionUnknownResponseException; import org.stellar.sdk.responses.operations.OperationResponse; +import org.stellar.sdk.xdr.EnvelopeType; import java.io.IOException; import java.net.URISyntaxException; @@ -173,6 +174,7 @@ Transaction buildTransaction(Network network) throws IOException { Transaction.Builder builder = new Transaction.Builder(account, network) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addMemo(Memo.text("Hello world!")) + .setBaseFee(Transaction.MIN_BASE_FEE) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE); assertEquals(1, builder.getOperationsCount()); @@ -332,6 +334,18 @@ public void testNextPage() throws IOException, URISyntaxException { public static final String DESTINATION_ACCOUNT_NO_MEMO_REQUIRED = "GDYC2D4P2SRC5DCEDDK2OUFESSPCTZYLDOEF6NYHR2T7X5GUTEABCQC2"; public static final String DESTINATION_ACCOUNT_NO_FOUND = "GD2OVSQPGD5FBJPMW4YN3FGDJ7JDFKNOMJT35T4H52FLHXJK5MFSR5RA"; public static final String DESTINATION_ACCOUNT_FETCH_ERROR = "GB7WNQUTDLD6YJ4MR3KQN3Y6ZIDIGTA7GRKNH47HOGMP2ETFGRSLD6OG"; + public static final String DESTINATION_ACCOUNT_MEMO_ID = "MCAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITKNOG"; + + private FeeBumpTransaction feeBump(Transaction inner) { + inner.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX); + KeyPair signer = KeyPair.fromSecretSeed("SA5ZEFDVFZ52GRU7YUGR6EDPBNRU2WLA6IQFQ7S2IH2DG3VFV3DOMV2Q"); + FeeBumpTransaction tx = new FeeBumpTransaction.Builder(inner) + .setFeeAccount(signer.getAccountId()) + .setBaseFee(FeeBumpTransaction.MIN_BASE_FEE*10) + .build(); + tx.sign(signer); + return tx; + } @Test public void testCheckMemoRequiredWithMemo() throws IOException, AccountRequiresMemoException { @@ -350,10 +364,34 @@ public void testCheckMemoRequiredWithMemo() throws IOException, AccountRequiresM .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .addMemo(new MemoText("Hello, Stellar.")) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); + } + + @Test + public void testCheckMemoRequiredWithMemoIdAddress() throws IOException, AccountRequiresMemoException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.setDispatcher(buildTestCheckMemoRequiredMockDispatcher()); + mockWebServer.start(); + HttpUrl baseUrl = mockWebServer.url(""); + Server server = new Server(baseUrl.toString()); + + KeyPair source = KeyPair.fromSecretSeed("SDQXFKA32UVQHUTLYJ42N56ZUEM5PNVVI4XE7EA5QFMLA2DHDCQX3GPY"); + Account account = new Account(source.getAccountId(), 1L); + Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) + .addOperation(new PaymentOperation.Builder(DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeNative(), "10").build()) + .addOperation(new PathPaymentStrictReceiveOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) + .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_ID, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) + .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_ID).build()) + .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(100) + .build(); + transaction.sign(source); + server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); } @Test @@ -372,10 +410,11 @@ public void testCheckMemoRequiredWithSkipCheck() throws IOException, AccountRequ .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction, true); + server.submitTransaction(feeBump(transaction), true); } @Test @@ -394,7 +433,7 @@ public void testCheckMemoRequiredWithPaymentOperationNoMemo() throws IOException .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -405,6 +444,15 @@ public void testCheckMemoRequiredWithPaymentOperationNoMemo() throws IOException assertEquals(0, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(0, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); + } } @Test @@ -423,7 +471,7 @@ public void testCheckMemoRequiredWithPathPaymentStrictReceiveOperationNoMemo() t .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -434,6 +482,15 @@ public void testCheckMemoRequiredWithPathPaymentStrictReceiveOperationNoMemo() t assertEquals(1, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_B, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(1, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_B, e.getAccountId()); + } } @Test @@ -452,7 +509,7 @@ public void testCheckMemoRequiredWithPathPaymentStrictSendOperationNoMemo() thro .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -463,6 +520,15 @@ public void testCheckMemoRequiredWithPathPaymentStrictSendOperationNoMemo() thro assertEquals(2, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(2, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); + } } @Test @@ -481,7 +547,7 @@ public void testCheckMemoRequiredWithAccountMergeOperationNoMemo() throws IOExce .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_MEMO_REQUIRED, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -492,6 +558,15 @@ public void testCheckMemoRequiredWithAccountMergeOperationNoMemo() throws IOExce assertEquals(3, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_D, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(3, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_D, e.getAccountId()); + } } @Test @@ -510,7 +585,7 @@ public void testCheckMemoRequiredTwoOperationsWithSameDestination() throws IOExc .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -521,6 +596,15 @@ public void testCheckMemoRequiredTwoOperationsWithSameDestination() throws IOExc assertEquals(2, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(2, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_C, e.getAccountId()); + } } @Test @@ -540,7 +624,7 @@ public void testCheckMemoRequiredNoDestinationOperation() throws IOException { .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_C, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_MEMO_REQUIRED).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -551,6 +635,15 @@ public void testCheckMemoRequiredNoDestinationOperation() throws IOException { assertEquals(1, e.getOperationIndex()); assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (AccountRequiresMemoException e) { + assertEquals("Destination account requires a memo in the transaction.", e.getMessage()); + assertEquals(1, e.getOperationIndex()); + assertEquals(DESTINATION_ACCOUNT_MEMO_REQUIRED_A, e.getAccountId()); + } } @Test @@ -569,10 +662,11 @@ public void testCheckMemoRequiredAccountNotFound() throws IOException, AccountRe .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_NO_FOUND, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_NO_FOUND).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); server.submitTransaction(transaction); + server.submitTransaction(feeBump(transaction)); } @Test @@ -593,7 +687,7 @@ public void testCheckMemoRequiredFetchAccountError() throws IOException, Account .addOperation(new PathPaymentStrictSendOperation.Builder(new AssetTypeNative(), "10", DESTINATION_ACCOUNT_MEMO_REQUIRED_D, new AssetTypeCreditAlphaNum4("BTC", "GA7GYB3QGLTZNHNGXN3BMANS6TC7KJT3TCGTR763J4JOU4QHKL37RVV2"), "5").build()) .addOperation(new AccountMergeOperation.Builder(DESTINATION_ACCOUNT_MEMO_REQUIRED_D).build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); try { @@ -602,6 +696,13 @@ public void testCheckMemoRequiredFetchAccountError() throws IOException, Account } catch (ErrorResponse e) { assertEquals(400, e.getCode()); } + + try { + server.submitTransaction(feeBump(transaction)); + fail(); + } catch (ErrorResponse e) { + assertEquals(400, e.getCode()); + } } private Dispatcher buildTestCheckMemoRequiredMockDispatcher() { diff --git a/src/test/java/org/stellar/sdk/TransactionTest.java b/src/test/java/org/stellar/sdk/TransactionTest.java index aa122b4cd..2fb469d28 100644 --- a/src/test/java/org/stellar/sdk/TransactionTest.java +++ b/src/test/java/org/stellar/sdk/TransactionTest.java @@ -13,53 +13,21 @@ public class TransactionTest { @Test - public void testDefaultBaseFee() throws FormatException { - // GBPMKIRA2OQW2XZZQUCQILI5TMVZ6JNRKM423BSAISDM7ZFWQ6KWEBC4 - KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); - KeyPair destination = KeyPair.fromAccountId("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR"); - - Transaction.Builder.setDefaultOperationFee(2345); - - Account account = new Account(source.getAccountId(), 2908908335136768L); - Transaction transaction = new Transaction.Builder(account, Network.TESTNET) - .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) + public void testMissingOperationFee() { + long sequenceNumber = 2908908335136768L; + Account account = new Account("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR", sequenceNumber); + try { + new Transaction.Builder(account, Network.TESTNET) + .addOperation(new CreateAccountOperation.Builder("GDW6AUTBXTOC7FIKUO5BOO3OGLK4SF7ZPOBLMQHMZDI45J2Z6VXRB5NR", "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .build(); - - transaction.sign(source); - - assertEquals( - "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAJKQAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA7eBSYbzcL5UKo7oXO24y1ckX+XuCtkDsyNHOp1n1bxAAAAAEqBfIAAAAAAAAAAABtoeVYgAAAEDf8XAGz9uOmfL0KJBP29eSXz/CZqZtl0Mm8jHye3xwLgo2HJDfvCJdijGKsx34AfNl6hvX+Cq3IVk062sLSuoK", - transaction.toEnvelopeXdrBase64()); - - Transaction transaction2 = Transaction.fromEnvelopeXdr( - transaction.toEnvelopeXdr(), - Network.TESTNET - ); - - assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); - assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); - assertEquals(transaction.getFee(), transaction2.getFee()); - assertEquals( - ((CreateAccountOperation) transaction.getOperations()[0]).getStartingBalance(), - ((CreateAccountOperation) transaction2.getOperations()[0]).getStartingBalance() - ); - } - - @Test - public void testDefaultBaseFeeThrows() { - try { - Transaction.Builder.setDefaultOperationFee(99); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { + fail("expected RuntimeException"); + } catch (RuntimeException e) { // expected } - - // should succeed - Transaction.Builder.setDefaultOperationFee(100); } - @Test + @Test public void testBuilderSuccessTestnet() throws FormatException { // GBPMKIRA2OQW2XZZQUCQILI5TMVZ6JNRKM423BSAISDM7ZFWQ6KWEBC4 KeyPair source = KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS"); @@ -70,6 +38,7 @@ public void testBuilderSuccessTestnet() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -82,7 +51,7 @@ public void testBuilderSuccessTestnet() throws FormatException { assertEquals(transaction.getSequenceNumber(), sequenceNumber + 1); assertEquals(transaction.getFee(), 100); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -106,6 +75,7 @@ public void testBuilderMemoText() throws FormatException { .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addMemo(Memo.text("Hello world!")) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -114,7 +84,7 @@ public void testBuilderMemoText() throws FormatException { "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAAZAAKVaMAAAABAAAAAAAAAAEAAAAMSGVsbG8gd29ybGQhAAAAAQAAAAAAAAAAAAAAAO3gUmG83C+VCqO6FztuMtXJF/l7grZA7MjRzqdZ9W8QAAAABKgXyAAAAAAAAAAAAbaHlWIAAABAxzofBhoayuUnz8t0T1UNWrTgmJ+lCh9KaeOGu2ppNOz9UGw0abGLhv+9oWQsstaHx6YjwWxL+8GBvwBUVWRlBQ==", transaction.toEnvelopeXdrBase64()); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -137,6 +107,7 @@ public void testBuilderTimeBounds() throws FormatException, IOException { .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 1337)) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -152,7 +123,7 @@ public void testBuilderTimeBounds() throws FormatException, IOException { assertEquals(decodedTransaction.getTimeBounds().getMinTime().getTimePoint().getUint64().longValue(), 42); assertEquals(decodedTransaction.getTimeBounds().getMaxTime().getTimePoint().getUint64().longValue(), 1337); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -174,7 +145,7 @@ public void testBuilderBaseFee() throws FormatException { Account account = new Account(source.getAccountId(), 2908908335136768L); Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) - .setOperationFee(200) + .setBaseFee(200) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .build(); @@ -184,7 +155,7 @@ public void testBuilderBaseFee() throws FormatException { "AAAAAF7FIiDToW1fOYUFBC0dmyufJbFTOa2GQESGz+S2h5ViAAAAyAAKVaMAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA7eBSYbzcL5UKo7oXO24y1ckX+XuCtkDsyNHOp1n1bxAAAAAEqBfIAAAAAAAAAAABtoeVYgAAAED1Mbd0oou0sfNFRuxsSqvrwJ9RzzTSnX8sTmlbMKcV0V3Kl1eDKoerD+xZ1pNQwOJZrAG2yapXyg60PQfDUcMN", transaction.toEnvelopeXdrBase64()); - Transaction transaction2 = Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); + Transaction transaction2 = (Transaction)Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdr(), Network.TESTNET); assertEquals(transaction.getSourceAccount(), transaction2.getSourceAccount()); assertEquals(transaction.getSequenceNumber(), transaction2.getSequenceNumber()); @@ -203,7 +174,7 @@ public void testBuilderBaseFeeThrows() throws FormatException { Account account = new Account(source.getAccountId(), 2908908335136768L); Transaction.Builder builder = new Transaction.Builder(account, Network.TESTNET); try { - builder.setOperationFee(99); + builder.setBaseFee(99); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected @@ -217,6 +188,7 @@ public void testBuilderWithTimeBoundsButNoTimeout() throws IOException { .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 1337)) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); } @@ -227,6 +199,7 @@ public void testBuilderRequiresTimeoutOrTimeBounds() throws IOException { new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addMemo(Memo.hash("abcdef")) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); fail(); } catch (RuntimeException exception) { @@ -260,6 +233,7 @@ public void testBuilderTimeoutSetsTimeBounds() throws IOException { Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .setTimeout(10) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals(0, transaction.getTimeBounds().getMinTime()); @@ -290,6 +264,7 @@ public void testBuilderFailsWhenSettingTimeoutAndMaxTimeNotSet() throws IOExcept .addOperation(new CreateAccountOperation.Builder(KeyPair.random().getAccountId(), "2000").build()) .addTimeBounds(new TimeBounds(42, 0)) .setTimeout(10) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals(42, transaction.getTimeBounds().getMinTime()); @@ -310,7 +285,7 @@ public void testBuilderTimeBoundsNoMaxTime() throws FormatException, IOException .addTimeBounds(new TimeBounds(42, 0)) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) .addMemo(Memo.hash("abcdef")) - .setOperationFee(100) + .setBaseFee(100) .build(); transaction.sign(source); @@ -338,6 +313,7 @@ public void testBuilderSuccessPublic() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); transaction.sign(source); @@ -356,6 +332,7 @@ public void testSha256HashSigning() throws FormatException { Transaction transaction = new Transaction.Builder(account, Network.PUBLIC) .addOperation(new PaymentOperation.Builder(destination.getAccountId(), new AssetTypeNative(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); byte[] preimage = new byte[64]; @@ -381,6 +358,7 @@ public void testToBase64EnvelopeXdrBuilderNoSignatures() throws FormatException, Transaction transaction = new Transaction.Builder(account, Network.TESTNET) .addOperation(new CreateAccountOperation.Builder(destination.getAccountId(), "2000").build()) .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) .build(); assertEquals( @@ -396,9 +374,10 @@ public void testNoOperations() throws FormatException, IOException { Account account = new Account(source.getAccountId(), 2908908335136768L); try { - Transaction transaction = new Transaction.Builder(account, Network.TESTNET).setTimeout( - Transaction.Builder.TIMEOUT_INFINITE - ).build(); + Transaction transaction = new Transaction.Builder(account, Network.TESTNET) + .setTimeout(Transaction.Builder.TIMEOUT_INFINITE) + .setBaseFee(Transaction.MIN_BASE_FEE) + .build(); fail(); } catch (RuntimeException exception) { assertTrue(exception.getMessage().contains("At least one operation required")); diff --git a/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java index 4506e33b4..be34a716f 100644 --- a/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/InflationDecodeTest.java @@ -11,7 +11,7 @@ public class InflationDecodeTest { @Test public void testDecodeInflationOperation() throws Exception { - org.stellar.sdk.Transaction tx = org.stellar.sdk.Transaction.fromEnvelopeXdr( + org.stellar.sdk.Transaction tx = (org.stellar.sdk.Transaction) org.stellar.sdk.Transaction.fromEnvelopeXdr( "AAAAAALC+FwxReetNDfMNvY5LOS1qSe7QqrfQPS28dnIV95NAAAAZAAAAAAAAATSAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAJAAAAAAAAAAA=", Network.TESTNET ); From c17b72325702fc0c97b4387ea8cf4d2ee0ffb3f6 Mon Sep 17 00:00:00 2001 From: tamirms Date: Mon, 4 May 2020 20:34:46 +0200 Subject: [PATCH 5/5] Bump version to 0.16 and update changelog (#279) Bump version to 0.16 and update changelog --- CHANGELOG.md | 12 ++++++++++++ build.gradle | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b7c3181..b1f4ae0c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ 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.16.0 + +* Update XDR definitions and auto-generated classes to support upcoming protocol 13 release ([#276](https://github.com/stellar/java-stellar-sdk/pull/276)). +* Extend StrKey implementation to handle [CAP 27 Muxed Accounts](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0027.md) ([#276](https://github.com/stellar/java-stellar-sdk/pull/276)). +* Update `TransactionResponse` to include new fields which are relevant to [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#275](https://github.com/stellar/java-stellar-sdk/pull/275)). +* Update `AccountResponse.Balance`, `AllowTrustOperationResponse`, and create `TrustlineAuthorizedToMaintainLiabilitiesEffectResponse` to support [CAP 18 Fine-Grained Control of Authorization](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0018.md) ([#274](https://github.com/stellar/java-stellar-sdk/pull/274)). +* Add `FeeBumpTransaction` and `FeeBumpTransaction.Builder` for parsing and creating [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). +* Add methods to `Server` for submitting [CAP 15 Fee-Bump Transactions](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md) ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). +* Update SEP 10 implementation to reject fee-bump transactions and transactions with multiplexed addresses ([#278] (https://github.com/stellar/java-stellar-sdk/pull/278)). +* Update SEP 29 implementation to handle bump transactions ([#278](https://github.com/stellar/java-stellar-sdk/pull/278)). + + ## 0.15.0 - Add SEP0029 (memo required) support. (https://github.com/stellar/java-stellar-sdk/issues/272) diff --git a/build.gradle b/build.gradle index d33c92fb7..9eb66e711 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.15.0' +version = '0.16.0' group = 'stellar' jar {