From 6fd9e879d49dc42b379bc696180a167eaea4b856 Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Sun, 28 Jun 2026 10:57:26 +0800 Subject: [PATCH] feat: add simulateTransaction useUpgradedAuth flag --- CHANGELOG.md | 3 ++ .../java/org/stellar/sdk/SorobanServer.java | 27 +++++++++++- .../SimulateTransactionRequest.java | 9 ++++ .../org/stellar/sdk/SorobanServerTest.java | 44 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d6d73467..7fabc757c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ accountId, signRemotely(Auth.authorizationPayloadHash(preimage))); ``` +### Update +- feat: add `useUpgradedAuth` to `SorobanServer.simulateTransaction`, mapping to the `useUpgradedAuth` flag from [Stellar RPC v27.1.0](https://github.com/stellar/stellar-rpc/releases/tag/v27.1.0) to opt simulation into recording `ADDRESS_V2` (CAP-71) auth credentials. Best-effort and transitional; older RPC servers ignore it. + ## 4.0.0-beta0 ### Update diff --git a/src/main/java/org/stellar/sdk/SorobanServer.java b/src/main/java/org/stellar/sdk/SorobanServer.java index 3778651c2..bf53e503a 100644 --- a/src/main/java/org/stellar/sdk/SorobanServer.java +++ b/src/main/java/org/stellar/sdk/SorobanServer.java @@ -507,6 +507,10 @@ public GetLatestLedgerResponse getLatestLedger() { * ignored. * @param resourceConfig Additional resource include in the simulation. * @param authMode Explicitly allows users to opt-in to non-root authorization in recording mode. + * @param useUpgradedAuth Opt simulation into recording {@code ADDRESS_V2} ("upgraded") + * authorization credentials (CAP-71) instead of the legacy {@code ADDRESS} credentials. Maps + * to the {@code useUpgradedAuth} flag introduced in Stellar RPC v27.1.0. Best-effort and + * transitional; older RPC servers silently ignore it. * @return A {@link SimulateTransactionResponse} object containing the cost, footprint, * result/auth requirements (if applicable), and error of the transaction. * @throws org.stellar.sdk.exception.NetworkException All the exceptions below are subclasses of @@ -522,16 +526,35 @@ public GetLatestLedgerResponse getLatestLedger() { public SimulateTransactionResponse simulateTransaction( Transaction transaction, @Nullable SimulateTransactionRequest.ResourceConfig resourceConfig, - @Nullable SimulateTransactionRequest.AuthMode authMode) { + @Nullable SimulateTransactionRequest.AuthMode authMode, + boolean useUpgradedAuth) { // TODO: In the future, it may be necessary to consider FeeBumpTransaction. SimulateTransactionRequest params = - new SimulateTransactionRequest(transaction.toEnvelopeXdrBase64(), resourceConfig, authMode); + new SimulateTransactionRequest( + transaction.toEnvelopeXdrBase64(), resourceConfig, authMode, useUpgradedAuth); return this.sendRequest( "simulateTransaction", params, new TypeToken>() {}); } + /** + * An alias for {@link #simulateTransaction(Transaction, + * SimulateTransactionRequest.ResourceConfig, SimulateTransactionRequest.AuthMode, boolean)} with + * {@code useUpgradedAuth} disabled. + * + * @param transaction The transaction to simulate. + * @param resourceConfig Additional resource include in the simulation. + * @param authMode Explicitly allows users to opt-in to non-root authorization in recording mode. + * @return A {@link SimulateTransactionResponse} object containing the simulation result. + */ + public SimulateTransactionResponse simulateTransaction( + Transaction transaction, + @Nullable SimulateTransactionRequest.ResourceConfig resourceConfig, + @Nullable SimulateTransactionRequest.AuthMode authMode) { + return simulateTransaction(transaction, resourceConfig, authMode, false); + } + /** * An alias for {@link #simulateTransaction(Transaction, * SimulateTransactionRequest.ResourceConfig, SimulateTransactionRequest.AuthMode)} with no diff --git a/src/main/java/org/stellar/sdk/requests/sorobanrpc/SimulateTransactionRequest.java b/src/main/java/org/stellar/sdk/requests/sorobanrpc/SimulateTransactionRequest.java index 1c493b8bc..77ea1f1ed 100644 --- a/src/main/java/org/stellar/sdk/requests/sorobanrpc/SimulateTransactionRequest.java +++ b/src/main/java/org/stellar/sdk/requests/sorobanrpc/SimulateTransactionRequest.java @@ -27,6 +27,15 @@ public class SimulateTransactionRequest { */ AuthMode authMode; + /** + * Opt simulation into recording {@code ADDRESS_V2} ("upgraded") authorization credentials + * (CAP-71) instead of the legacy {@code ADDRESS} credentials. This maps to the {@code + * useUpgradedAuth} flag introduced in Stellar RPC v27.1.0. It is best-effort and transitional: it + * only affects the recording auth modes and is silently ignored by RPC servers (or protocol + * versions) that cannot emit {@code ADDRESS_V2}. + */ + boolean useUpgradedAuth; + public enum AuthMode { /** Always enforce mode, even with an empty list. */ @SerializedName("enforce") diff --git a/src/test/java/org/stellar/sdk/SorobanServerTest.java b/src/test/java/org/stellar/sdk/SorobanServerTest.java index ad7582c21..2e69d66a4 100644 --- a/src/test/java/org/stellar/sdk/SorobanServerTest.java +++ b/src/test/java/org/stellar/sdk/SorobanServerTest.java @@ -1095,6 +1095,50 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) mockWebServer.close(); } + @Test + public void testSimulateTransactionWithUseUpgradedAuth() throws IOException, SorobanRpcException { + String filePath = + "src/test/resources/soroban_server/simulate_transaction_with_resource_leeway.json"; + String json = new String(Files.readAllBytes(Paths.get(filePath))); + Transaction transaction = buildSorobanTransaction(null, null); + MockWebServer mockWebServer = new MockWebServer(); + Dispatcher dispatcher = + new Dispatcher() { + @NotNull + @Override + public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) + throws InterruptedException { + SorobanRpcRequest sorobanRpcRequest = + gson.fromJson( + recordedRequest.getBody().readUtf8(), + new TypeToken>() {}.getType()); + if ("POST".equals(recordedRequest.getMethod()) + && sorobanRpcRequest.getMethod().equals("simulateTransaction") + && sorobanRpcRequest + .getParams() + .getTransaction() + .equals(transaction.toEnvelopeXdrBase64()) + && sorobanRpcRequest.getParams().getResourceConfig() == null + && sorobanRpcRequest.getParams().getAuthMode() == null + && sorobanRpcRequest.getParams().isUseUpgradedAuth()) { + 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()); + + SimulateTransactionResponse resp = server.simulateTransaction(transaction, null, null, true); + assertEquals(resp.getLatestLedger().longValue(), 14245L); + assertEquals(resp.getResults().size(), 1); + server.close(); + mockWebServer.close(); + } + @Test public void testPrepareTransaction() throws IOException, SorobanRpcException, PrepareTransactionException {