Skip to content

Commit

Permalink
Support resource leeway parameter when simulating Soroban transaction…
Browse files Browse the repository at this point in the history
…s. (#561)
  • Loading branch information
overcat authored Dec 19, 2023
1 parent 406929d commit 050e8a2
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps.

## Pending

### Update
* Support resource leeway parameter when simulating Soroban transactions. ([#561](https://github.com/stellar/java-stellar-sdk/pull/561))
### Breaking changes
* The types of the following fields have changed. ([#560](https://github.com/stellar/java-stellar-sdk/pull/560))
| field | before | now |
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/org/stellar/sdk/SorobanServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,24 +315,34 @@ public GetLatestLedgerResponse getLatestLedger() throws IOException, SorobanRpcE
* @param transaction The transaction to simulate. It should include exactly one operation, which
* must be one of {@link InvokeHostFunctionOperation}, {@link ExtendFootprintTTLOperation}, or
* {@link RestoreFootprintOperation}. Any provided footprint will be ignored.
* @param resourceConfig Additional resource include in the simulation.
* @return A {@link SimulateTransactionResponse} object containing the cost, footprint,
* result/auth requirements (if applicable), and error of the transaction.
* @throws IOException If the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws SorobanRpcErrorResponse If the Soroban-RPC instance returns an error response.
*/
public SimulateTransactionResponse simulateTransaction(Transaction transaction)
public SimulateTransactionResponse simulateTransaction(
Transaction transaction, @Nullable SimulateTransactionRequest.ResourceConfig resourceConfig)
throws IOException, SorobanRpcErrorResponse {
// TODO: In the future, it may be necessary to consider FeeBumpTransaction.
SimulateTransactionRequest params =
new SimulateTransactionRequest(transaction.toEnvelopeXdrBase64());
new SimulateTransactionRequest(transaction.toEnvelopeXdrBase64(), resourceConfig);
return this.sendRequest(
"simulateTransaction",
params,
new TypeToken<SorobanRpcResponse<SimulateTransactionResponse>>() {});
}

/**
* An alias for {@link #simulateTransaction(Transaction, SimulateTransactionRequest.ResourceConfig) with no resource leeway.
*/
public SimulateTransactionResponse simulateTransaction(Transaction transaction)
throws IOException, SorobanRpcErrorResponse {
return simulateTransaction(transaction, null);
}

/**
* Submit a trial contract invocation, first run a simulation of the contract invocation as
* defined on the incoming transaction, and apply the results to a new copy of the transaction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.stellar.sdk.requests.sorobanrpc;

import java.math.BigInteger;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;

/**
Expand All @@ -13,4 +15,13 @@
@Value
public class SimulateTransactionRequest {
String transaction;

ResourceConfig resourceConfig;

@Value
@AllArgsConstructor
@Builder
public static class ResourceConfig {
BigInteger instructionLeeway;
}
}
92 changes: 92 additions & 0 deletions src/test/java/org/stellar/sdk/SorobanServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -774,6 +775,97 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest)
mockWebServer.close();
}

@Test
public void testSimulateTransactionWithResourceLeeway()
throws IOException, SorobanRpcErrorResponse {
String json =
"{\n"
+ " \"jsonrpc\": \"2.0\",\n"
+ " \"id\": \"7a469b9d6ed4444893491be530862ce3\",\n"
+ " \"result\": {\n"
+ " \"transactionData\": \"AAAAAAAAAAIAAAAGAAAAAem354u9STQWq5b3Ed1j9tOemvL7xV0NPwhn4gXg0AP8AAAAFAAAAAEAAAAH8dTe2OoI0BnhlDbH0fWvXmvprkBvBAgKIcL9busuuMEAAAABAAAABgAAAAHpt+eLvUk0FquW9xHdY/bTnpry+8VdDT8IZ+IF4NAD/AAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAABYt8SiyPKXqo89JHEoH9/M7K/kjlZjMT7BjhKnPsqYoQAAAAEAHifGAAAFlAAAAIgAAAAAAAAAAg==\",\n"
+ " \"minResourceFee\": \"58181\",\n"
+ " \"events\": [\n"
+ " \"AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAg6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAo=\",\n"
+ " \"AAAAAQAAAAAAAAAB6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAABQ=\"\n"
+ " ],\n"
+ " \"results\": [\n"
+ " {\n"
+ " \"auth\": [\n"
+ " \"AAAAAAAAAAAAAAAB6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAoAAAAA\"\n"
+ " ],\n"
+ " \"xdr\": \"AAAAAwAAABQ=\"\n"
+ " }\n"
+ " ],\n"
+ " \"cost\": { \"cpuInsns\": \"1646885\", \"memBytes\": \"1296481\" },\n"
+ " \"latestLedger\": \"14245\"\n"
+ " }\n"
+ "}\n";

Transaction transaction = buildSorobanTransaction(null, null);

BigInteger cpuInstructions = BigInteger.valueOf(20000L);

MockWebServer mockWebServer = new MockWebServer();
Dispatcher dispatcher =
new Dispatcher() {
@NotNull
@Override
public MockResponse dispatch(@NotNull RecordedRequest recordedRequest)
throws InterruptedException {
SorobanRpcRequest<SimulateTransactionRequest> sorobanRpcRequest =
gson.fromJson(
recordedRequest.getBody().readUtf8(),
new TypeToken<SorobanRpcRequest<SimulateTransactionRequest>>() {}.getType());
if ("POST".equals(recordedRequest.getMethod())
&& sorobanRpcRequest.getMethod().equals("simulateTransaction")
&& sorobanRpcRequest
.getParams()
.getTransaction()
.equals(transaction.toEnvelopeXdrBase64())
&& sorobanRpcRequest
.getParams()
.getResourceConfig()
.getInstructionLeeway()
.equals(cpuInstructions)) {
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());

SimulateTransactionRequest.ResourceConfig resourceConfig =
new SimulateTransactionRequest.ResourceConfig(cpuInstructions);
SimulateTransactionResponse resp = server.simulateTransaction(transaction, resourceConfig);
assertEquals(resp.getLatestLedger().longValue(), 14245L);
assertEquals(
resp.getTransactionData(),
"AAAAAAAAAAIAAAAGAAAAAem354u9STQWq5b3Ed1j9tOemvL7xV0NPwhn4gXg0AP8AAAAFAAAAAEAAAAH8dTe2OoI0BnhlDbH0fWvXmvprkBvBAgKIcL9busuuMEAAAABAAAABgAAAAHpt+eLvUk0FquW9xHdY/bTnpry+8VdDT8IZ+IF4NAD/AAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAABYt8SiyPKXqo89JHEoH9/M7K/kjlZjMT7BjhKnPsqYoQAAAAEAHifGAAAFlAAAAIgAAAAAAAAAAg==");
assertEquals(resp.getEvents().size(), 2);
assertEquals(
resp.getEvents().get(0),
"AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAg6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAo=");
assertEquals(
resp.getEvents().get(1),
"AAAAAQAAAAAAAAAB6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAABQ=");
assertEquals(resp.getMinResourceFee().longValue(), 58181L);
assertEquals(resp.getResults().size(), 1);
assertEquals(resp.getResults().get(0).getAuth().size(), 1);
assertEquals(
resp.getResults().get(0).getAuth().get(0),
"AAAAAAAAAAAAAAAB6bfni71JNBarlvcR3WP2056a8vvFXQ0/CGfiBeDQA/wAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAoAAAAA");
assertEquals(resp.getResults().get(0).getXdr(), "AAAAAwAAABQ=");
assertEquals(resp.getCost().getCpuInstructions().longValue(), 1646885L);
assertEquals(resp.getCost().getMemoryBytes().longValue(), 1296481L);
server.close();
mockWebServer.close();
}

@Test
public void testPrepareTransaction()
throws IOException, SorobanRpcErrorResponse, PrepareTransactionException {
Expand Down

0 comments on commit 050e8a2

Please sign in to comment.