Skip to content

Commit

Permalink
feat: support resource leeway parameter when simulating Soroban trans…
Browse files Browse the repository at this point in the history
…actions. (#846)
  • Loading branch information
overcat committed Dec 16, 2023
1 parent 0ffbce0 commit a44fb38
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 6 deletions.
8 changes: 8 additions & 0 deletions stellar_sdk/base_soroban_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import copy
import dataclasses
import uuid
from typing import TYPE_CHECKING

Expand All @@ -12,6 +13,13 @@
from .transaction_envelope import TransactionEnvelope


@dataclasses.dataclass
class ResourceLeeway:
"""Describes additional resource leeways for transaction simulation."""

cpu_instructions: int


class Durability(Enum):
TEMPORARY = "temporary"
PERSISTENT = "persistent"
Expand Down
11 changes: 11 additions & 0 deletions stellar_sdk/soroban_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ class GetHealthResponse(BaseModel):


# simulate_transaction
class ResourceConfig(BaseModel):
"""ResourceConfig represents the additional resource leeways for transaction simulation."""

instruction_lee_way: int = Field(alias="instructionLeeway")
model_config = ConfigDict(populate_by_name=True)


class SimulateTransactionRequest(BaseModel):
"""Response for JSON-RPC method simulateTransaction.
Expand All @@ -159,6 +166,10 @@ class SimulateTransactionRequest(BaseModel):
"""

transaction: str
resource_config: Optional[ResourceConfig] = Field(
alias="resourceConfig", default=None
)
model_config = ConfigDict(populate_by_name=True)


class SimulateTransactionCost(BaseModel):
Expand Down
16 changes: 14 additions & 2 deletions stellar_sdk/soroban_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .address import Address
from .base_soroban_server import (
Durability,
ResourceLeeway,
_assemble_transaction,
_generate_unique_request_id,
)
Expand Down Expand Up @@ -157,7 +158,9 @@ def get_transaction(self, transaction_hash: str) -> GetTransactionResponse:
return self._post(request, GetTransactionResponse)

def simulate_transaction(
self, transaction_envelope: TransactionEnvelope
self,
transaction_envelope: TransactionEnvelope,
addl_resources: Optional[ResourceLeeway] = None,
) -> SimulateTransactionResponse:
"""Submit a trial contract invocation to get back return values, expected ledger footprint, and expected costs.
Expand All @@ -168,6 +171,7 @@ def simulate_transaction(
:class:`InvokeHostFunction <stellar_sdk.operation.InvokeHostFunction>` or
:class:`ExtendFootprintTTL <stellar_sdk.operation.RestoreFootprint>` operation.
Any provided footprint will be ignored.
:param addl_resources: Additional resource include in the simulation.
:return: A :class:`SimulateTransactionResponse <stellar_sdk.soroban_rpc.SimulateTransactionResponse>` object
contains the cost, footprint, result/auth requirements (if applicable), and error of the transaction.
"""
Expand All @@ -176,10 +180,18 @@ def simulate_transaction(
if isinstance(transaction_envelope, str)
else transaction_envelope.to_xdr()
)
resource_config = None
if addl_resources:
resource_config = ResourceConfig(
instruction_lee_way=addl_resources.cpu_instructions,
)

request = Request[SimulateTransactionRequest](
id=_generate_unique_request_id(),
method="simulateTransaction",
params=SimulateTransactionRequest(transaction=xdr),
params=SimulateTransactionRequest(
transaction=xdr, resource_config=resource_config
),
)
return self._post(request, SimulateTransactionResponse)

Expand Down
15 changes: 13 additions & 2 deletions stellar_sdk/soroban_server_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .address import Address
from .base_soroban_server import (
Durability,
ResourceLeeway,
_assemble_transaction,
_generate_unique_request_id,
)
Expand Down Expand Up @@ -157,7 +158,9 @@ async def get_transaction(self, transaction_hash: str) -> GetTransactionResponse
return await self._post(request, GetTransactionResponse)

async def simulate_transaction(
self, transaction_envelope: TransactionEnvelope
self,
transaction_envelope: TransactionEnvelope,
addl_resources: Optional[ResourceLeeway] = None,
) -> SimulateTransactionResponse:
"""Submit a trial contract invocation to get back return values, expected ledger footprint, and expected costs.
Expand All @@ -168,6 +171,7 @@ async def simulate_transaction(
:class:`InvokeHostFunction <stellar_sdk.operation.InvokeHostFunction>` or
:class:`ExtendFootprintTTL <stellar_sdk.operation.RestoreFootprint>` operation.
Any provided footprint will be ignored.
:param addl_resources: Additional resource include in the simulation.
:return: A :class:`SimulateTransactionResponse <stellar_sdk.soroban_rpc.SimulateTransactionResponse>` object
contains the cost, footprint, result/auth requirements (if applicable), and error of the transaction.
"""
Expand All @@ -176,10 +180,17 @@ async def simulate_transaction(
if isinstance(transaction_envelope, str)
else transaction_envelope.to_xdr()
)
resource_config = None
if addl_resources:
resource_config = ResourceConfig(
instruction_lee_way=addl_resources.cpu_instructions,
)
request = Request[SimulateTransactionRequest](
id=_generate_unique_request_id(),
method="simulateTransaction",
params=SimulateTransactionRequest(transaction=xdr),
params=SimulateTransactionRequest(
transaction=xdr, resource_config=resource_config
),
)
return await self._post(request, SimulateTransactionResponse)

Expand Down
49 changes: 48 additions & 1 deletion tests/test_soroban_server_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from stellar_sdk import Account, Keypair, Network, TransactionBuilder, scval
from stellar_sdk import xdr as stellar_xdr
from stellar_sdk.address import Address
from stellar_sdk.base_soroban_server import ResourceLeeway
from stellar_sdk.exceptions import (
AccountNotFoundException,
PrepareTransactionException,
Expand Down Expand Up @@ -418,7 +419,53 @@ async def test_simulate_transaction(self):
assert len(request_data["id"]) == 32
assert request_data["jsonrpc"] == "2.0"
assert request_data["method"] == "simulateTransaction"
assert request_data["params"] == {"transaction": transaction.to_xdr()}
assert request_data["params"] == {
"transaction": transaction.to_xdr(),
"resourceConfig": None,
}

async def test_simulate_transaction_with_addl_resources(self):
result = {
"transactionData": "AAAAAAAAAAIAAAAGAAAAAcWLK/vE8FTnMk9r8gytPgJuQbutGm0gw9fUkY3tFlQRAAAAFAAAAAEAAAAAAAAAB300Hyg0HZG+Qie3zvsxLvugrNtFqd3AIntWy9bg2YvZAAAAAAAAAAEAAAAGAAAAAcWLK/vE8FTnMk9r8gytPgJuQbutGm0gw9fUkY3tFlQRAAAAEAAAAAEAAAACAAAADwAAAAdDb3VudGVyAAAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAQAAAAAAFcLDAAAF8AAAAQgAAAMcAAAAAAAAAJw=",
"events": [
"AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAo=",
"AAAAAQAAAAAAAAABxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAABQ=",
],
"minResourceFee": "58595",
"results": [
{
"auth": [
"AAAAAAAAAAAAAAABxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAoAAAAA"
],
"xdr": "AAAAAwAAABQ=",
}
],
"cost": {"cpuInsns": "1240100", "memBytes": "161637"},
"latestLedger": "1479",
}
data = {
"jsonrpc": "2.0",
"id": "e1fabdcdf0244a2a9adfab94d7748b6c",
"result": result,
}
transaction = _build_soroban_transaction(None, [])
with aioresponses() as m:
m.post(PRC_URL, payload=data)
async with SorobanServerAsync(PRC_URL) as client:
assert (
await client.simulate_transaction(
transaction, ResourceLeeway(1000000)
)
) == SimulateTransactionResponse.model_validate(result)

request_data = m.requests[("POST", URL(PRC_URL))][0].kwargs["json"]
assert len(request_data["id"]) == 32
assert request_data["jsonrpc"] == "2.0"
assert request_data["method"] == "simulateTransaction"
assert request_data["params"] == {
"transaction": transaction.to_xdr(),
"resourceConfig": {"instructionLeeway": 1000000},
}

async def test_prepare_transaction_without_auth_and_soroban_data(self):
data = {
Expand Down
46 changes: 45 additions & 1 deletion tests/test_soroban_server_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from stellar_sdk import Account, Keypair, Network, TransactionBuilder, scval
from stellar_sdk import xdr as stellar_xdr
from stellar_sdk.address import Address
from stellar_sdk.base_soroban_server import ResourceLeeway
from stellar_sdk.exceptions import (
AccountNotFoundException,
PrepareTransactionException,
Expand Down Expand Up @@ -407,7 +408,50 @@ def test_simulate_transaction(self):
assert len(request_data["id"]) == 32
assert request_data["jsonrpc"] == "2.0"
assert request_data["method"] == "simulateTransaction"
assert request_data["params"] == {"transaction": transaction.to_xdr()}
assert request_data["params"] == {
"transaction": transaction.to_xdr(),
"resourceConfig": None,
}

def test_simulate_transaction_with_addl_resources(self):
result = {
"transactionData": "AAAAAAAAAAIAAAAGAAAAAcWLK/vE8FTnMk9r8gytPgJuQbutGm0gw9fUkY3tFlQRAAAAFAAAAAEAAAAAAAAAB300Hyg0HZG+Qie3zvsxLvugrNtFqd3AIntWy9bg2YvZAAAAAAAAAAEAAAAGAAAAAcWLK/vE8FTnMk9r8gytPgJuQbutGm0gw9fUkY3tFlQRAAAAEAAAAAEAAAACAAAADwAAAAdDb3VudGVyAAAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAQAAAAAAFcLDAAAF8AAAAQgAAAMcAAAAAAAAAJw=",
"events": [
"AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAo=",
"AAAAAQAAAAAAAAABxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAABQ=",
],
"minResourceFee": "58595",
"results": [
{
"auth": [
"AAAAAAAAAAAAAAABxYsr+8TwVOcyT2vyDK0+Am5Bu60abSDD19SRje0WVBEAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAAFi3xKLI8peqjz0kcSgf38zsr+SOVmMxPsGOEqc+ypihAAAAAwAAAAoAAAAA"
],
"xdr": "AAAAAwAAABQ=",
}
],
"cost": {"cpuInsns": "1240100", "memBytes": "161637"},
"latestLedger": "1479",
}
data = {
"jsonrpc": "2.0",
"id": "e1fabdcdf0244a2a9adfab94d7748b6c",
"result": result,
}
transaction = _build_soroban_transaction(None, [])
with requests_mock.Mocker() as m:
m.post(PRC_URL, json=data)
assert SorobanServer(PRC_URL).simulate_transaction(
transaction, ResourceLeeway(1000000)
) == SimulateTransactionResponse.model_validate(result)

request_data = m.last_request.json()
assert len(request_data["id"]) == 32
assert request_data["jsonrpc"] == "2.0"
assert request_data["method"] == "simulateTransaction"
assert request_data["params"] == {
"transaction": transaction.to_xdr(),
"resourceConfig": {"instructionLeeway": 1000000},
}

def test_prepare_transaction_without_auth_and_soroban_data(self):
data = {
Expand Down

0 comments on commit a44fb38

Please sign in to comment.