Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Pending

### Update
- fix: `Asset.compareTo` orders same-code assets by the issuers' raw Ed25519 key bytes instead of their strkey text, matching the order the network requires.

## 6.0.0

**This release adds support for Protocol 30.**
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/stellar/sdk/Asset.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public static Asset fromXdr(org.stellar.sdk.xdr.Asset xdr) {
* <ol>
* <li>First compare the type (eg. native before alphanum4 before alphanum12).
* <li>If the types are equal, compare the assets codes.
* <li>If the asset codes are equal, compare the issuers.
* <li>If the asset codes are equal, compare the issuers by their raw Ed25519 public key bytes,
* as the network requires, not by their strkey text.
* </ol>
*
* @param other the object to be compared.
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/stellar/sdk/AssetTypeCreditAlphaNum12.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public int compareTo(@NonNull Asset other) {
if (!this.getCode().equals(o.getCode())) {
return this.getCode().compareTo(o.getCode());
}
return this.getIssuer().compareTo(o.getIssuer());
return Util.compareBytesUnsigned(
StrKey.decodeEd25519PublicKey(this.getIssuer()),
StrKey.decodeEd25519PublicKey(o.getIssuer()));
}
}
4 changes: 3 additions & 1 deletion src/main/java/org/stellar/sdk/AssetTypeCreditAlphaNum4.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public int compareTo(@NonNull Asset other) {
return this.getCode().compareTo(o.getCode());
}

return this.getIssuer().compareTo(o.getIssuer());
return Util.compareBytesUnsigned(
StrKey.decodeEd25519PublicKey(this.getIssuer()),
StrKey.decodeEd25519PublicKey(o.getIssuer()));
}
}
39 changes: 39 additions & 0 deletions src/test/java/org/stellar/sdk/LiquidityPoolTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.stellar.sdk;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
Expand Down Expand Up @@ -130,6 +132,43 @@ public void testLexicographicOrderAssetIssuer() {
}
}

@Test
public void testIssuersAreOrderedByRawKeyBytes() {
// Issuers of testnet pool c2a730127d006897b021d6fcb279da68b4f65f9b6a408c74eb2cdd85ea862d5e.
// GDRQ sorts after GD32 as strkey text, but before it as raw key bytes (e305... < f7a4...).
for (String code : new String[] {"USD", "BANANA"}) {
Asset assetA =
Asset.create(code + ":GDRQL6EF772CANMYX55HXMU463OLAX46ZRCPCWS4KVL66ZS4BYA7OYDL");
Asset assetB =
Asset.create(code + ":GD32JQHFWLIROCNQ564GLCB2M7T4GMQAP5UKHMVT64OQISWL2LHYK2HO");
assertTrue(assetA.compareTo(assetB) < 0);
assertTrue(assetB.compareTo(assetA) > 0);
new LiquidityPool(assetA, assetB);
assertThrows(IllegalArgumentException.class, () -> new LiquidityPool(assetB, assetA));
}
}

@Test
public void testLiquidityPoolIdWithIssuersOrderedByRawKeyBytes() {
Asset assetA = Asset.create("USD:GDRQL6EF772CANMYX55HXMU463OLAX46ZRCPCWS4KVL66ZS4BYA7OYDL");
Asset assetB = Asset.create("USD:GD32JQHFWLIROCNQ564GLCB2M7T4GMQAP5UKHMVT64OQISWL2LHYK2HO");
assertEquals(
"c2a730127d006897b021d6fcb279da68b4f65f9b6a408c74eb2cdd85ea862d5e",
new LiquidityPool(assetA, assetB).getLiquidityPoolId());
}

@Test
public void testDecodeTransactionWithIssuersOrderedByRawKeyBytes() throws IOException {
// Testnet transaction 0126748091930959a3aefac9296e1773b947f964a9b73f37ce856c4df32d346f
String envelopeXdr =
"AAAAAgAAAAADeR2SwEvKvthWOdthGC/c1Bxym7fafJmtjj5EyvmvxAAAASwAVEAXAAAAAQAAAAAAAAAAAAAAAwAAAAAAAAAGAAAAAVVTRAAAAAAA4wX4hf/0IDWYv3p7spz23LBfnsxE8VpcVVfvZlwOAfd//////////wAAAAAAAAAGAAAAAVVTRAAAAAAA96TA5bLRFwmw77hliDpn58MyAH9oo7Kz9x0ESsvSz4V//////////wAAAAAAAAAGAAAAAwAAAAAAAAABVVNEAAAAAADjBfiF//QgNZi/enuynPbcsF+ezETxWlxVV+9mXA4B9wAAAAFVU0QAAAAAAPekwOWy0RcJsO+4ZYg6Z+fDMgB/aKOys/cdBErL0s+FAAAAHn//////////AAAAAAAAAAHK+a/EAAAAQMtA5QpAy6I3ILVmPZcRA3SJhGo4CiMT7K7dXQt7Qo1Prip/OedNocMY2QloSRle4hYvhJilIiOyoD85l3+WrQM=";
AbstractTransaction transaction =
AbstractTransaction.fromEnvelopeXdr(envelopeXdr, Network.TESTNET);
assertEquals(envelopeXdr, transaction.toEnvelopeXdrBase64());
assertEquals(
"0126748091930959a3aefac9296e1773b947f964a9b73f37ce856c4df32d346f", transaction.hashHex());
}

@Value
static class OrderTestCase {
Asset assetA;
Expand Down
Loading