From 734dd74587cbd7d7c89c0207e865a9614e4f7bbf Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Tue, 9 Jul 2024 19:23:57 +0800 Subject: [PATCH] feat: Add `Server.loadAccount` to load the `Account` object used for building transactions, supporting `MuxedAccount`. (#616) * feat: Add `Server.loadAccount` to load the `Account` object used for building transactions, supporting `MuxedAccount`. * feat: Add support for `MuxedAccount` to `SorobanServer.getAccount`. --- CHANGELOG.md | 2 + .../main/java/network/lightsail/Payment.java | 3 +- readme.md | 2 +- src/main/java/org/stellar/sdk/Server.java | 27 +++++++++ .../java/org/stellar/sdk/SorobanServer.java | 11 ++-- .../org/stellar/sdk/SorobanServerTest.java | 55 +++++++++++++++++++ 6 files changed, 92 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d1dcbe0..f6ec7a121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps. - Removed all functions that return `Optional` value. - refactor!: remove `rateLimitLimit`, `rateLimitRemaining`, and `rateLimitReset` from the `Response`. - feat: add `MuxedAccount` class to represent a multiplexed account on Stellar's network. +- feat: Add `Server.loadAccount` to load the `Account` object used for building transactions, supporting `MuxedAccount`. +- feat: Add support for `MuxedAccount` to `SorobanServer.getAccount`. ## 0.44.0 ### Update diff --git a/examples/src/main/java/network/lightsail/Payment.java b/examples/src/main/java/network/lightsail/Payment.java index 40fb6864e..6678240da 100644 --- a/examples/src/main/java/network/lightsail/Payment.java +++ b/examples/src/main/java/network/lightsail/Payment.java @@ -13,7 +13,6 @@ import org.stellar.sdk.exception.BadResponseException; import org.stellar.sdk.exception.NetworkException; import org.stellar.sdk.operations.PaymentOperation; -import org.stellar.sdk.responses.AccountResponse; import org.stellar.sdk.responses.TransactionResponse; /** Create, sign, and submit a transaction using Java Stellar SDK. */ @@ -33,7 +32,7 @@ public static void main(String[] args) { // Transactions require a valid sequence number that is specific to this account. // We can fetch the current sequence number for the source account from Horizon. - AccountResponse sourceAccount = server.accounts().account(sourceKeypair.getAccountId()); + TransactionBuilderAccount sourceAccount = server.loadAccount(sourceKeypair.getAccountId()); // Build a payment operation, send 350.1234567 XLM to receiver PaymentOperation paymentOperation = diff --git a/readme.md b/readme.md index fc6667306..6892f5205 100644 --- a/readme.md +++ b/readme.md @@ -36,7 +36,7 @@ For some examples on how to use this library, take a look at the [Get Started do Javadoc is available at https://javadoc.io/doc/network.lightsail/stellar-sdk ## Examples -This repository contains a number of helpful examples. You can find them in the [examples](./examples) directory. +This repository contains a number of helpful examples. You can find them in the [examples](./examples/src/main/java/network/lightsail) directory. ## Integrate into Android project If you want to integrate this SDK on Android platforms with API level 28 and above, you don't need any additional configuration. diff --git a/src/main/java/org/stellar/sdk/Server.java b/src/main/java/org/stellar/sdk/Server.java index 7ff1fd623..0209caf6a 100644 --- a/src/main/java/org/stellar/sdk/Server.java +++ b/src/main/java/org/stellar/sdk/Server.java @@ -83,6 +83,33 @@ public Server(String serverURI, OkHttpClient httpClient, OkHttpClient submitHttp this.submitHttpClient = submitHttpClient; } + /** + * Fetches an account's most current state in the ledger, then creates and returns an {@link + * Account} object. + * + * @param address The address of the account to load, muxed accounts are supported. + * @return {@link Account} object + * @throws org.stellar.sdk.exception.NetworkException All the exceptions below are subclasses of + * NetworkError + * @throws org.stellar.sdk.exception.BadRequestException if the request fails due to a bad request + * (4xx) + * @throws org.stellar.sdk.exception.BadResponseException if the request fails due to a bad + * response from the server (5xx) + * @throws TooManyRequestsException if the request fails due to too many requests sent to the + * server + * @throws org.stellar.sdk.exception.RequestTimeoutException When Horizon returns a Timeout + * or connection timeout occurred + * @throws org.stellar.sdk.exception.UnknownResponseException if the server returns an unknown + * status code + * @throws org.stellar.sdk.exception.ConnectionErrorException When the request cannot be executed + * due to cancellation or connectivity problems, etc. + */ + public TransactionBuilderAccount loadAccount(String address) { + MuxedAccount muxedAccount = new MuxedAccount(address); + AccountResponse accountResponse = this.accounts().account(muxedAccount.getAccountId()); + return new Account(address, accountResponse.getSequenceNumber()); + } + /** * @return {@link RootRequestBuilder} instance. */ diff --git a/src/main/java/org/stellar/sdk/SorobanServer.java b/src/main/java/org/stellar/sdk/SorobanServer.java index 44edc5095..2fabce527 100644 --- a/src/main/java/org/stellar/sdk/SorobanServer.java +++ b/src/main/java/org/stellar/sdk/SorobanServer.java @@ -94,7 +94,7 @@ public SorobanServer(String serverURI, OkHttpClient httpClient) { * number for the account, so you can build a successful transaction with {@link * TransactionBuilder}. * - * @param accountId The public address of the account to load. + * @param address The address of the account to load, muxed accounts are supported. * @return An {@link Account} object containing the sequence number and current state of the * account. * @throws org.stellar.sdk.exception.NetworkException All the exceptions below are subclasses of @@ -106,10 +106,11 @@ public SorobanServer(String serverURI, OkHttpClient httpClient) { * @throws ConnectionErrorException When the request cannot be executed due to cancellation or * connectivity problems, etc. */ - public TransactionBuilderAccount getAccount(String accountId) { + public TransactionBuilderAccount getAccount(String address) { + MuxedAccount muxedAccount = new MuxedAccount(address); LedgerKey.LedgerKeyAccount ledgerKeyAccount = LedgerKey.LedgerKeyAccount.builder() - .accountID(KeyPair.fromAccountId(accountId).getXdrAccountId()) + .accountID(KeyPair.fromAccountId(muxedAccount.getAccountId()).getXdrAccountId()) .build(); LedgerKey ledgerKey = LedgerKey.builder().account(ledgerKeyAccount).discriminant(LedgerEntryType.ACCOUNT).build(); @@ -118,7 +119,7 @@ public TransactionBuilderAccount getAccount(String accountId) { List entries = getLedgerEntriesResponse.getEntries(); if (entries == null || entries.isEmpty()) { - throw new AccountNotFoundException(accountId); + throw new AccountNotFoundException(muxedAccount.getAccountId()); } LedgerEntry.LedgerEntryData ledgerEntryData; try { @@ -127,7 +128,7 @@ public TransactionBuilderAccount getAccount(String accountId) { throw new IllegalArgumentException("Invalid ledgerEntryData: " + entries.get(0).getXdr(), e); } long sequence = ledgerEntryData.getAccount().getSeqNum().getSequenceNumber().getInt64(); - return new Account(accountId, sequence); + return new Account(address, sequence); } /** diff --git a/src/test/java/org/stellar/sdk/SorobanServerTest.java b/src/test/java/org/stellar/sdk/SorobanServerTest.java index 07ad1eee8..dacc732aa 100644 --- a/src/test/java/org/stellar/sdk/SorobanServerTest.java +++ b/src/test/java/org/stellar/sdk/SorobanServerTest.java @@ -129,6 +129,61 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) mockWebServer.close(); } + @Test + public void testGetAccountMuxed() + throws IOException, SorobanRpcException, AccountNotFoundException { + String accountId = "MDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIAAAAAAAAAPCIBOR2"; + String json = + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"id\": \"ecb18f82ec12484190673502d0486b98\",\n" + + " \"result\": {\n" + + " \"entries\": [\n" + + " {\n" + + " \"key\": \"AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JA==\",\n" + + " \"xdr\": \"AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JAAAABdIcDhpAAADHAAAAAwAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAABfI8AAAAAZMK3qQ==\",\n" + + " \"lastModifiedLedgerSeq\": \"97423\",\n" + + " \"liveUntilLedgerSeq\": \"97673\"\n" + + " }\n" + + " ],\n" + + " \"latestLedger\": \"108023\"\n" + + " }\n" + + "}"; + + MockWebServer mockWebServer = new MockWebServer(); + Dispatcher dispatcher = + new Dispatcher() { + @NotNull + @Override + public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) + throws InterruptedException { + GetLedgerEntriesRequest expectedRequest = + new GetLedgerEntriesRequest( + singletonList("AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JA==")); + SorobanRpcRequest sorobanRpcRequest = + gson.fromJson( + recordedRequest.getBody().readUtf8(), + new TypeToken>() {}.getType()); + if ("POST".equals(recordedRequest.getMethod()) + && sorobanRpcRequest.getMethod().equals("getLedgerEntries") + && sorobanRpcRequest.getParams().equals(expectedRequest)) { + return new MockResponse().setResponseCode(200).setBody(json); + } + return new MockResponse().setResponseCode(404); + } + }; + mockWebServer.setDispatcher(dispatcher); + mockWebServer.start(); + + HttpUrl baseUrl = mockWebServer.url(""); + SorobanServer server = new SorobanServer(baseUrl.toString()); + TransactionBuilderAccount resp = server.getAccount(accountId); + assertEquals(resp.getAccountId(), accountId); + assertEquals(resp.getSequenceNumber().longValue(), 3418793967628L); + server.close(); + mockWebServer.close(); + } + @Test(expected = AccountNotFoundException.class) public void testGetAccountNotFoundThrows() throws IOException, SorobanRpcException, AccountNotFoundException {