Skip to content

Commit

Permalink
refactor: make the functions related to Soroban as consistent as poss…
Browse files Browse the repository at this point in the history
…ible with other SDKs (JS/Java). (#758)
  • Loading branch information
overcat authored Aug 26, 2023
1 parent bac5ffc commit eb97527
Show file tree
Hide file tree
Showing 51 changed files with 2,206 additions and 1,938 deletions.
25 changes: 0 additions & 25 deletions .xdr/update_xdr.py

This file was deleted.

19 changes: 12 additions & 7 deletions examples/soroban_auth_with_stellar_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,32 @@
from stellar_sdk.soroban import SorobanServer
from stellar_sdk.soroban.authorization_entry import AuthorizationEntry
from stellar_sdk.soroban.soroban_rpc import GetTransactionStatus
from stellar_sdk.soroban.types import Address, Uint32
from stellar_sdk.soroban.types import Address, Uint32, Enum, Symbol

rpc_server_url = "https://rpc-futurenet.stellar.org:443/"
rpc_server_url = "http://100.83.15.43:8000/soroban/rpc"
soroban_server = SorobanServer(rpc_server_url)
network_passphrase = Network.FUTURENET_NETWORK_PASSPHRASE
network_passphrase = Network.STANDALONE_NETWORK_PASSPHRASE

# https://github.com/stellar/soroban-examples/tree/v0.6.0/auth
contract_id = "CDNTSPC5WR5MF5J42B5JTVFWR6NXVYBP7VUMB74J7IYT6JYDWT67NV5T"
contract_id = "CCHYCJVXSZAQZBQ7Y26C7F7VKMKLD3FPMV744IOEMKCXIITJCAJ6CWNU"
tx_submitter_kp = Keypair.from_secret(
"SAAPYAPTTRZMCUZFPG3G66V4ZMHTK4TWA6NS7U4F7Z3IMUD52EK4DDEV"
)
op_invoker_kp = Keypair.from_secret(
"SAEZSI6DY7AXJFIYA4PM6SIBNEYYXIEM2MSOTHFGKHDW32MBQ7KVO6EN"
)

func_name = "increment"
args = [Address(op_invoker_kp.public_key), Uint32(10)]
func_name = "initialize"
args = [
Address(op_invoker_kp.public_key),
Enum("Other", Symbol("XLM")),
Uint32(18),
Uint32(1),
]

source = soroban_server.load_account(tx_submitter_kp.public_key)
tx = (
TransactionBuilder(source, network_passphrase, base_fee=200)
TransactionBuilder(source, network_passphrase, base_fee=500000)
.add_time_bounds(0, 0)
.append_invoke_contract_function_op(
contract_id=contract_id,
Expand Down
21 changes: 12 additions & 9 deletions examples/soroban_auth_with_transaction_invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@
from stellar_sdk import xdr as stellar_xdr
from stellar_sdk.soroban import SorobanServer
from stellar_sdk.soroban.soroban_rpc import GetTransactionStatus
from stellar_sdk.soroban.types import Address, Uint32
from stellar_sdk.soroban.types import Address, Uint32, Enum, Symbol

rpc_server_url = "https://rpc-futurenet.stellar.org:443/"
rpc_server_url = "http://100.83.15.43:8000/soroban/rpc"
soroban_server = SorobanServer(rpc_server_url)
network_passphrase = Network.FUTURENET_NETWORK_PASSPHRASE
network_passphrase = Network.STANDALONE_NETWORK_PASSPHRASE

# https://github.com/stellar/soroban-examples/tree/v0.6.0/auth
contract_id = "CDNTSPC5WR5MF5J42B5JTVFWR6NXVYBP7VUMB74J7IYT6JYDWT67NV5T"
contract_id = "CAYQSZQXD6AFSXTV7YJKJHWIX2CZ3Q6Z5NPXSI7DDUE67NPRT3UMZMHM"
tx_submitter_kp = Keypair.from_secret(
"SAAPYAPTTRZMCUZFPG3G66V4ZMHTK4TWA6NS7U4F7Z3IMUD52EK4DDEV"
)

func_name = "increment"
args = [Address(tx_submitter_kp.public_key), Uint32(10)]

func_name = "initialize"
args = [
Address(tx_submitter_kp.public_key),
Enum("Other", Symbol("XLM")),
Uint32(18),
Uint32(1),
]
source = soroban_server.load_account(tx_submitter_kp.public_key)
tx = (
TransactionBuilder(source, network_passphrase, base_fee=100)
TransactionBuilder(source, network_passphrase, base_fee=500000)
.add_time_bounds(0, 0)
.append_invoke_contract_function_op(
contract_id=contract_id,
Expand Down
2 changes: 2 additions & 0 deletions stellar_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import scval
from .__version__ import (
__author__,
__author_email__,
Expand Down Expand Up @@ -30,6 +31,7 @@
from .server_async import *
from .signer import *
from .signer_key import *
from .soroban_server import *
from .strkey import *
from .time_bounds import *
from .transaction import *
Expand Down
43 changes: 30 additions & 13 deletions stellar_sdk/soroban/types/address.py → stellar_sdk/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@
from enum import IntEnum
from typing import Union

from ... import xdr as stellar_xdr
from ...strkey import StrKey
from ...xdr import Hash
from .base import BaseScValAlias
from . import xdr as stellar_xdr
from .strkey import StrKey
from .xdr import Hash

__all__ = ["Address", "AddressType"]
__all__ = ["Address"]


class AddressType(IntEnum):
"""Represents an Address type."""

ACCOUNT = 0
"""An account address, address looks like ``GBJCHUKZMTFSLOMNC7P4TS4VJJBTCYL3XKSOLXAUJSD56C4LHND5TWUC``."""

CONTRACT = 1
"""An account address, address looks like ``CCJZ5DGASBWQXR5MPFCJXMBI333XE5U3FSJTNQU7RIKE3P5GN2K2WYD5``."""


class Address(BaseScValAlias):
class Address:
"""Represents a single address in the Stellar network.
An address can represent an account or a contract.
Expand All @@ -25,13 +29,26 @@ class Address(BaseScValAlias):

def __init__(self, address: str):
if StrKey.is_valid_ed25519_public_key(address):
address_type = AddressType.ACCOUNT
self.type = AddressType.ACCOUNT
self.key = StrKey.decode_ed25519_public_key(address)
elif StrKey.is_valid_contract(address):
address_type = AddressType.CONTRACT
self.type = AddressType.CONTRACT
self.key = StrKey.decode_contract(address)
else:
raise ValueError("Unsupported address type.")

@property
def address(self) -> str:
"""Returns the encoded address.
:return: The encoded address.
"""
if self.type == AddressType.ACCOUNT:
return StrKey.encode_ed25519_public_key(self.key)
elif self.type == AddressType.CONTRACT:
return StrKey.encode_contract(self.key)
else:
raise ValueError("Unsupported address type.")
self.type = address_type
self.address = address

@staticmethod
def from_raw_account(account: Union[bytes, str]) -> "Address":
Expand Down Expand Up @@ -64,12 +81,12 @@ def to_xdr_sc_address(self) -> stellar_xdr.SCAddress:
account = stellar_xdr.AccountID(
stellar_xdr.PublicKey(
stellar_xdr.PublicKeyType.PUBLIC_KEY_TYPE_ED25519,
stellar_xdr.Uint256(StrKey.decode_ed25519_public_key(self.address)),
stellar_xdr.Uint256(self.key),
)
)
return stellar_xdr.SCAddress.from_sc_address_type_account(account)
elif self.type == AddressType.CONTRACT:
contract = Hash(StrKey.decode_contract(self.address))
contract = Hash(self.key)
return stellar_xdr.SCAddress.from_sc_address_type_contract(contract)
else:
raise ValueError("Unsupported address type.")
Expand Down Expand Up @@ -106,7 +123,7 @@ def from_xdr_sc_val(cls, sc_val: stellar_xdr.SCVal) -> "Address":
def __eq__(self, other: object) -> bool:
if not isinstance(other, self.__class__):
return NotImplemented
return self.address == other.address and self.type == other.type
return self.key == other.key and self.type == other.type

def __str__(self):
return f"<Address [type={self.type.name}, address={self.address}]>"
37 changes: 37 additions & 0 deletions stellar_sdk/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@
"NotPageableError",
"StreamClientError",
"FeatureNotEnabledError",
"SorobanRpcErrorResponse",
"AccountNotFoundException",
"PrepareTransactionException",
]

from .soroban_rpc import SimulateTransactionResponse

# TODO: remove the following lines
# The following is kept for compatibility
ValueError = ValueError
TypeError = TypeError
Expand Down Expand Up @@ -170,6 +176,37 @@ class FeatureNotEnabledError(SdkError):
"""The feature is not enabled."""


class SorobanRpcErrorResponse(BaseRequestError):
"""The exception is thrown when the RPC server returns an error response."""

def __init__(
self, code: int, message: Optional[str], data: Optional[str] = None
) -> None:
super().__init__(message)
self.code = code
self.data = data
self.message = message


class AccountNotFoundException(SdkError):
"""The exception is thrown when trying to load an account that doesn't exist on the Stellar network."""

def __init__(self, account_id: str) -> None:
super().__init__(f"Account not found, account_id: {account_id}")
self.account_id = account_id


class PrepareTransactionException(SdkError):
"""The exception is thrown when trying to prepare a transaction."""

def __init__(
self, message: str, simulate_transaction_response: SimulateTransactionResponse
) -> None:
super().__init__(message)
self.message = message
self.simulate_transaction_response = simulate_transaction_response


def raise_request_exception(response: Response) -> None:
status_code = response.status_code
if status_code == 200:
Expand Down
17 changes: 17 additions & 0 deletions stellar_sdk/operation/bump_footprint_expiration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@


class BumpFootprintExpiration(Operation):
"""The :class:`BumpFootprintExpiration` object, which represents a BumpFootprintExpiration
operation on Stellar's network.
Threshold: Medium
See `BumpFootprintExpirationOp <https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#bumpfootprintexpirationop>`_.
:param ledgers_to_expire: The number of ledgers past the LCL (last closed ledger)
by which to extend the validity of the ledger keys in this transaction.
:param source: The source account for the operation. Defaults to the transaction's source account.
"""

_XDR_OPERATION_TYPE: stellar_xdr.OperationType = (
stellar_xdr.OperationType.BUMP_FOOTPRINT_EXPIRATION
)
Expand All @@ -16,6 +28,11 @@ def __init__(
self, ledgers_to_expire: int, source: Optional[Union[MuxedAccount, str]] = None
) -> None:
super().__init__(source)
if ledgers_to_expire < 0 or ledgers_to_expire > 2**32 - 1:
raise ValueError(
f"`ledgers_to_expire` value must be between 0 and 2**32-1, got {ledgers_to_expire}"
)

self.ledgers_to_expire: int = ledgers_to_expire

def _to_operation_body(self) -> stellar_xdr.OperationBody:
Expand Down
48 changes: 19 additions & 29 deletions stellar_sdk/operation/invoke_host_function.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,44 @@
from typing import List, Optional, Union
from typing import List, Optional, Sequence, Union

from .. import xdr as stellar_xdr
from ..muxed_account import MuxedAccount
from ..soroban.authorization_entry import AuthorizationEntry
from .operation import Operation

__all__ = ["InvokeHostFunction"]


class InvokeHostFunction(Operation):
"""The :class:`InvokeHostFunction` object, which represents a InvokeHostFunction
operation on Stellar's network.
Threshold: Medium
See `Interacting with Soroban via Stellar <https://soroban.stellar.org/docs/fundamentals-and-concepts/invoking-contracts-with-transactions>`_.
:param host_function: The host function to invoke.
:param auth: The authorizations required to execute the host function.
:param source: The source account for the operation. Defaults to the transaction's source account.
"""

_XDR_OPERATION_TYPE: stellar_xdr.OperationType = (
stellar_xdr.OperationType.INVOKE_HOST_FUNCTION
)

def __init__(
self,
host_function: stellar_xdr.HostFunction,
auth: List[AuthorizationEntry],
auth: Sequence[stellar_xdr.SorobanAuthorizationEntry] = None,
source: Optional[Union[MuxedAccount, str]] = None,
):
super().__init__(source)
self.host_function = host_function
self._auth: List[AuthorizationEntry] = auth

# auth setter
@property
def auth(self) -> List[AuthorizationEntry]:
return self._auth

@auth.setter
def auth(self, value: Union[List[AuthorizationEntry], List[str]]):
for v in value:
if isinstance(v, str):
self._auth.append(
AuthorizationEntry.from_xdr_object(
stellar_xdr.SorobanAuthorizationEntry.from_xdr(v)
)
)
elif isinstance(v, AuthorizationEntry):
self._auth.append(v)
else:
raise TypeError("Only AuthorizationEntry or str is allowed")
self.auth: List[stellar_xdr.SorobanAuthorizationEntry] = (
list(auth) if auth else []
)

def _to_operation_body(self) -> stellar_xdr.OperationBody:
invoke_host_function_op = stellar_xdr.InvokeHostFunctionOp(
host_function=self.host_function,
auth=[auth.to_xdr_object() for auth in self.auth],
host_function=self.host_function, auth=self.auth
)
body = stellar_xdr.OperationBody(
type=self._XDR_OPERATION_TYPE,
Expand All @@ -59,10 +52,7 @@ def from_xdr_object(cls, xdr_object: stellar_xdr.Operation) -> "InvokeHostFuncti
source = Operation.get_source_from_xdr_obj(xdr_object)
assert xdr_object.body.invoke_host_function_op is not None
host_function = xdr_object.body.invoke_host_function_op.host_function
auth = [
AuthorizationEntry.from_xdr_object(auth)
for auth in xdr_object.body.invoke_host_function_op.auth
]
auth = xdr_object.body.invoke_host_function_op.auth
return cls(
host_function=host_function,
auth=auth,
Expand Down
10 changes: 10 additions & 0 deletions stellar_sdk/operation/restore_footprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@


class RestoreFootprint(Operation):
"""The :class:`RestoreFootprint` object, which represents a RestoreFootprint
operation on Stellar's network.
Threshold: Medium
See `RestoreFootprintOp <https://soroban.stellar.org/docs/fundamentals-and-concepts/state-expiration#restorefootprintop>`_.
:param source: The source account for the operation. Defaults to the transaction's source account.
"""

_XDR_OPERATION_TYPE: stellar_xdr.OperationType = (
stellar_xdr.OperationType.RESTORE_FOOTPRINT
)
Expand Down
Loading

0 comments on commit eb97527

Please sign in to comment.