From 0e1dc3e818725e859a181ba48f4058964b4364dc Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Fri, 9 Feb 2024 10:34:27 +0100 Subject: [PATCH 1/2] Ledger nano generate_ed25519_public_keys (#1968) * Ledger nano generate_ed25519_public_keys * Use 1.0.3 --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- Cargo.lock | 56 +++++++++++++----------- sdk/Cargo.toml | 2 +- sdk/src/client/secret/ledger_nano.rs | 65 ++++++++++++++-------------- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 787d40e0e5..0c3dc48122 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,9 +456,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -466,21 +466,21 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -490,9 +490,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cli-wallet" @@ -742,7 +742,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 2.0.48", ] @@ -1362,9 +1362,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hidapi" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830eccace7c861211d0ad04288e5dad690d6711b0db152084da58882ee7a840a" +checksum = "9a722fb137d008dbf264f54612457f8eb6a299efbcb0138178964a0809035d74" dependencies = [ "cc", "cfg-if", @@ -1591,9 +1591,9 @@ dependencies = [ [[package]] name = "iota-ledger-nano" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fef85d9f5e9cf947d8a4f6aabb74f38ef970aade7d60112c8b8637596f07f93" +checksum = "434abd10cd26c00345c19290f87d0e66104c6ce3069a5b7e1eb6c09a47ea18ca" dependencies = [ "arrayref", "byteorder", @@ -1750,12 +1750,12 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "fe8f25ce1159c7740ff0b9b2f5cdf4a8428742ba7c112b9f20f22cd5219c7dab" dependencies = [ "hermit-abi", - "rustix", + "libc", "windows-sys 0.52.0", ] @@ -1779,9 +1779,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] @@ -1999,9 +1999,9 @@ checksum = "d4b4532cf86bfef556348ac65e561e3123879f0e7566cca6d43a6ff5326f13df" [[package]] name = "napi-derive" -version = "2.15.0" +version = "2.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7622f0dbe0968af2dacdd64870eee6dee94f93c989c841f1ad8f300cf1abd514" +checksum = "8d56bb899c164ab1be5e542ae7db8b26750c864bf2eef07295f17754e6358777" dependencies = [ "cfg-if", "convert_case", @@ -2013,9 +2013,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ec514d65fce18a959be55e7f683ac89c6cb850fb59b09e25ab777fd5a4a8d9e" +checksum = "6cf2d74ac66fd1cccb646be75fdd1c1dce8acfe20a68f61566a31da0d3eb9786" dependencies = [ "convert_case", "proc-macro2", @@ -2082,9 +2082,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -3150,6 +3150,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "strum" version = "0.25.0" diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 834d3e5c77..e83dd06b95 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -69,7 +69,7 @@ futures = { version = "0.3.30", default-features = false, features = [ "thread-pool", ], optional = true } instant = { version = "0.1.12", default-features = false, optional = true } -iota-ledger-nano = { version = "1.0.1", default-features = false, optional = true } +iota-ledger-nano = { version = "1.0.3", default-features = false, optional = true } iota_stronghold = { version = "2.0.0", default-features = false, optional = true } log = { version = "0.4.20", default-features = false, optional = true } once_cell = { version = "1.19.0", default-features = false, optional = true } diff --git a/sdk/src/client/secret/ledger_nano.rs b/sdk/src/client/secret/ledger_nano.rs index 8daff56bb2..07f9f6a754 100644 --- a/sdk/src/client/secret/ledger_nano.rs +++ b/sdk/src/client/secret/ledger_nano.rs @@ -142,39 +142,40 @@ impl SecretManage for LedgerSecretManager { &self, // https://github.com/satoshilabs/slips/blob/master/slip-0044.md // current ledger app only supports IOTA_COIN_TYPE, SHIMMER_COIN_TYPE and TESTNET_COIN_TYPE - _coin_type: u32, - _account_index: u32, - _address_indexes: Range, - _options: impl Into> + Send, + coin_type: u32, + account_index: u32, + address_indexes: Range, + options: impl Into> + Send, ) -> Result, Self::Error> { - // need an update on the ledger C lib - todo!(); - // let options = options.into().unwrap_or_default(); - // let bip32_account = account_index.harden().into(); - - // let bip32 = LedgerBIP32Index { - // bip32_index: address_indexes.start.harden().into(), - // bip32_change: u32::from(options.internal).harden().into(), - // }; - - // // lock the mutex to prevent multiple simultaneous requests to a ledger - // let lock = self.mutex.lock().await; - - // // get ledger - // let ledger = get_ledger(coin_type, bip32_account, self.is_simulator).map_err(Error::from)?; - // if ledger.is_debug_app() { - // ledger - // .set_non_interactive_mode(self.non_interactive) - // .map_err(Error::from)?; - // } - - // let addresses = ledger - // .get_addresses(options.ledger_nano_prompt, bip32, address_indexes.len()) - // .map_err(Error::from)?; - - // drop(lock); - - // Ok(addresses.into_iter().map(Ed25519Address::new).collect()) + let options = options.into().unwrap_or_default(); + let bip32_account = account_index.harden().into(); + + let bip32 = LedgerBIP32Index { + bip32_index: address_indexes.start.harden().into(), + bip32_change: u32::from(options.internal).harden().into(), + }; + + // lock the mutex to prevent multiple simultaneous requests to a ledger + let lock = self.mutex.lock().await; + + // get ledger + let ledger = get_ledger(coin_type, bip32_account, self.is_simulator).map_err(Error::from)?; + if ledger.is_debug_app() { + ledger + .set_non_interactive_mode(self.non_interactive) + .map_err(Error::from)?; + } + + let public_keys = ledger + .get_public_keys(options.ledger_nano_prompt, bip32, address_indexes.len()) + .map_err(Error::from)?; + + drop(lock); + + Ok(public_keys + .into_iter() + .map(ed25519::PublicKey::try_from_bytes) + .collect::, _>>()?) } async fn generate_evm_addresses( From 7fb722c7079aaaab7ae51fad30c0ddf5215d9f6f Mon Sep 17 00:00:00 2001 From: /alex/ Date: Fri, 9 Feb 2024 14:35:01 +0100 Subject: [PATCH 2/2] Python: update API routes (#1856) * update core api routes * Python: update core api types * Python: update core api types (part 2) * address docs related todos * fix optionals, add issuance test * adjustments * docs * align sdk * align renames * simplify * add GetOutputRaw method * update python binding * remove todo * from_dict * revert * revert nits * revert more * revert sdk changes * revert sdk changes 2 * revert sdk changes 3 * add todos to bindings core * update python binding * add todos to sdk * rename * reorder fields Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * fix merge * fix tests * hexstr Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * remove 'test_issuance.py' * node info + routes suffixes * issuance suffixes * committee, validator, validators response suffixes * remaining suffixes * nits * even more nits that aren't worth their own PR * responses.py module * fix whooopsie * parents Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * review * good catches * how? --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> Co-authored-by: Thibault Martinez --- bindings/core/src/method_handler/client.rs | 6 + .../python/examples/client/07_get_block.py | 2 +- .../python/examples/client/get_raw_block.py | 2 +- bindings/python/iota_sdk/__init__.py | 6 +- .../python/iota_sdk/client/_high_level_api.py | 11 +- .../python/iota_sdk/client/_node_core_api.py | 396 +++++++++++++++--- .../iota_sdk/client/_node_indexer_api.py | 23 +- bindings/python/iota_sdk/client/responses.py | 381 +++++++++++++++++ .../iota_sdk/secret_manager/secret_manager.py | 2 +- bindings/python/iota_sdk/types/address.py | 1 + bindings/python/iota_sdk/types/block/block.py | 1 + .../python/iota_sdk/types/block/metadata.py | 175 +------- bindings/python/iota_sdk/types/common.py | 4 +- .../python/iota_sdk/types/native_token.py | 2 +- bindings/python/iota_sdk/types/node_info.py | 47 --- bindings/python/iota_sdk/types/output.py | 6 + bindings/python/iota_sdk/types/output_data.py | 2 +- bindings/python/iota_sdk/types/output_id.py | 18 +- .../python/iota_sdk/types/output_id_proof.py | 94 +++++ .../python/iota_sdk/types/output_metadata.py | 13 +- .../python/iota_sdk/types/output_params.py | 10 +- bindings/python/iota_sdk/types/send_params.py | 16 +- bindings/python/iota_sdk/types/signature.py | 2 +- bindings/python/iota_sdk/types/slot.py | 31 +- .../iota_sdk/types/transaction_metadata.py | 162 +++++++ .../iota_sdk/types/transaction_options.py | 2 +- .../python/iota_sdk/types/utxo_changes.py | 18 - bindings/python/iota_sdk/utils.py | 2 +- .../python/iota_sdk/wallet/sync_options.py | 8 +- bindings/python/iota_sdk/wallet/wallet.py | 2 +- sdk/src/client/node_api/core/routes.rs | 8 + sdk/src/types/api/core.rs | 10 +- sdk/src/types/block/slot/commitment.rs | 2 +- sdk/src/wallet/update.rs | 9 +- 34 files changed, 1095 insertions(+), 379 deletions(-) create mode 100644 bindings/python/iota_sdk/client/responses.py create mode 100644 bindings/python/iota_sdk/types/output_id_proof.py create mode 100644 bindings/python/iota_sdk/types/transaction_metadata.py delete mode 100644 bindings/python/iota_sdk/types/utxo_changes.py diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index f337c18d4a..d8a3e31564 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -251,12 +251,18 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM .get_utxo_changes_full_by_slot_commitment_id(&commitment_id) .await?, ), + // TODO: this should be renamed to `GetCommitmentBySlot` + // https://github.com/iotaledger/iota-sdk/issues/1921 ClientMethod::GetCommitmentByIndex { slot } => { Response::SlotCommitment(client.get_slot_commitment_by_slot(slot).await?) } + // TODO: this should be renamed to `GetUtxoChangesBySlot` + // https://github.com/iotaledger/iota-sdk/issues/1921 ClientMethod::GetUtxoChangesByIndex { slot } => { Response::UtxoChanges(client.get_utxo_changes_by_slot(slot).await?) } + // TODO: this should be renamed to `GetUtxoChangesFullBySlot` + // https://github.com/iotaledger/iota-sdk/issues/1921 ClientMethod::GetUtxoChangesFullByIndex { slot } => { Response::UtxoChangesFull(client.get_utxo_changes_full_by_slot(slot).await?) } diff --git a/bindings/python/examples/client/07_get_block.py b/bindings/python/examples/client/07_get_block.py index d856995aeb..8c87fcce24 100644 --- a/bindings/python/examples/client/07_get_block.py +++ b/bindings/python/examples/client/07_get_block.py @@ -14,7 +14,7 @@ client = Client(nodes=[node_url]) # Fetch a block ID from the node -block_ids = client.get_tips() +block_ids = client.get_issuance().strong_parents print(f'Block id: {block_ids[0]}') # Get the metadata for the block diff --git a/bindings/python/examples/client/get_raw_block.py b/bindings/python/examples/client/get_raw_block.py index 51006f60ec..ac9fd62fdb 100644 --- a/bindings/python/examples/client/get_raw_block.py +++ b/bindings/python/examples/client/get_raw_block.py @@ -12,7 +12,7 @@ client = Client(nodes=[node_url]) # Fetch a block ID from the node -block_id = client.get_tips()[0] +block_id = client.get_issuance().strong_parents[0] print(f'Block id: {block_id}') # Get block raw diff --git a/bindings/python/iota_sdk/__init__.py b/bindings/python/iota_sdk/__init__.py index 11d3869198..b00f9bc274 100644 --- a/bindings/python/iota_sdk/__init__.py +++ b/bindings/python/iota_sdk/__init__.py @@ -6,6 +6,7 @@ from .common import custom_encoder from .client.client import Client, NodeIndexerAPI from .client.common import ClientError +from .client.responses import * from .client._high_level_api import GenerateAddressesOptions, GenerateAddressOptions from .utils import Utils from .wallet.wallet import Wallet, WalletOptions @@ -30,16 +31,17 @@ from .types.decayed_mana import * from .types.event import * from .types.feature import * +from .types.input import * from .types.irc_27 import * from .types.irc_30 import * from .types.filter_options import * -from .types.input import * from .types.native_token import * from .types.network_info import * from .types.node_info import * from .types.output import * from .types.output_data import * from .types.output_id import * +from .types.output_id_proof import * from .types.output_metadata import * from .types.output_params import * from .types.payload import * @@ -48,9 +50,9 @@ from .types.token_scheme import * from .types.transaction_data import * from .types.transaction_id import * +from .types.transaction_metadata import * from .types.transaction_options import * from .types.transaction_with_metadata import * from .types.unlock import * from .types.unlock_condition import * -from .types.utxo_changes import * from .types.consolidation_params import * diff --git a/bindings/python/iota_sdk/client/_high_level_api.py b/bindings/python/iota_sdk/client/_high_level_api.py index 3a0068899b..37bb3dcf26 100644 --- a/bindings/python/iota_sdk/client/_high_level_api.py +++ b/bindings/python/iota_sdk/client/_high_level_api.py @@ -4,11 +4,12 @@ from typing import List, Optional from dataclasses import dataclass from abc import ABCMeta, abstractmethod + from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId from iota_sdk.types.common import CoinType, json -from iota_sdk.types.output_metadata import OutputWithMetadata from iota_sdk.types.output_id import OutputId +from iota_sdk.types.output_metadata import OutputWithMetadata @json @@ -26,7 +27,7 @@ class Range: @json @dataclass -class GenerateAddressOptions(): +class GenerateAddressOptions: """Options for generating an address. Attributes: @@ -39,7 +40,7 @@ class GenerateAddressOptions(): @json @dataclass -class GenerateAddressesOptions(): +class GenerateAddressesOptions: """Options for generating addresses. Attributes: @@ -81,6 +82,8 @@ def _call_method(self, name, data=None): no payload. """ + # TODO: this should return `List[OutputResponse]`, not `List[OutputWithMetadata]` + # https://github.com/iotaledger/iota-sdk/issues/1921 def get_outputs( self, output_ids: List[OutputId]) -> List[OutputWithMetadata]: """Fetch OutputWithMetadata from provided OutputIds (requests are sent in parallel). @@ -96,6 +99,8 @@ def get_outputs( }) return [OutputWithMetadata.from_dict(o) for o in outputs] + # TODO: this should return `List[OutputResponse]`, not `List[OutputWithMetadata]` + # https://github.com/iotaledger/iota-sdk/issues/1921 def get_outputs_ignore_errors( self, output_ids: List[OutputId]) -> List[OutputWithMetadata]: """Try to get OutputWithMetadata from provided OutputIds. diff --git a/bindings/python/iota_sdk/client/_node_core_api.py b/bindings/python/iota_sdk/client/_node_core_api.py index 7181c491a3..be454bf8b8 100644 --- a/bindings/python/iota_sdk/client/_node_core_api.py +++ b/bindings/python/iota_sdk/client/_node_core_api.py @@ -3,15 +3,14 @@ from typing import List, Optional, Union from abc import ABCMeta, abstractmethod -from dacite import from_dict +from iota_sdk.client.responses import NodeInfoWrapper, InfoResponse, RoutesResponse, CongestionResponse, ManaRewardsResponse, CommitteeResponse, ValidatorResponse, ValidatorsResponse, IssuanceBlockHeaderResponse, BlockMetadataResponse, BlockWithMetadataResponse, OutputWithMetadataResponse, TransactionMetadataResponse, UtxoChangesResponse, UtxoChangesFullResponse from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId -from iota_sdk.types.block.metadata import BlockMetadata, BlockWithMetadata -from iota_sdk.types.common import HexStr -from iota_sdk.types.node_info import NodeInfo, NodeInfoWrapper -from iota_sdk.types.output_metadata import OutputWithMetadata, OutputMetadata +from iota_sdk.types.common import HexStr, EpochIndex, SlotIndex from iota_sdk.types.output_id import OutputId +from iota_sdk.types.output_metadata import OutputMetadata +from iota_sdk.types.slot import SlotCommitment, SlotCommitmentId from iota_sdk.types.transaction_id import TransactionId @@ -40,8 +39,11 @@ def _call_method(self, name, data=None): no payload. """ - def get_health(self, url: str): - """ Get node health. + # Node routes. + + def get_health(self, url: str) -> bool: + """Returns the health of the node. + GET /health Args: url: The node's url. @@ -50,30 +52,128 @@ def get_health(self, url: str): 'url': url }) - def get_node_info(self, url: str, auth=None) -> NodeInfo: - """Get node info. + # TODO: this is not strictly following the 2.0 Core API Spec (or maybe the TIP isn't updated yet) + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_info(self) -> NodeInfoWrapper: + """Returns general information about the node together with its URL. + GET /api/core/v3/info + """ + return NodeInfoWrapper.from_dict(self._call_method('getInfo')) + + # TODO: this is not strictly following the 2.0 Core API Spec (or maybe the TIP isn't updated yet) + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_node_info(self, url: str, auth=None) -> InfoResponse: + """Returns general information about the node. + GET /api/core/v3/info Args: url: The node's url. auth: A JWT or username/password authentication object. + + Returns: + The node info. """ - return NodeInfo.from_dict(self._call_method('getNodeInfo', { + return InfoResponse.from_dict(self._call_method('getNodeInfo', { 'url': url, 'auth': auth })) - def get_info(self) -> NodeInfoWrapper: - """Return node information together with the url of the used node. + # TODO: this should made be available + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_routes(self) -> RoutesResponse: + """Returns the available API route groups of the node. + GET /api/routes + """ + + def call_plugin_route(self, base_plugin_path: str, method: str, + endpoint: str, query_params: Optional[List[str]] = None, request: Optional[str] = None): + """Extension method which provides request methods for plugins. + + Args: + base_plugin_path: The base path of the routes provided by the plugin. + method: The HTTP method. + endpoint: The endpoint to query provided by the plugin. + query_params: The parameters of the query. + request: The request object sent to the endpoint of the plugin. + """ + if query_params is None: + query_params = [] + return self._call_method('callPluginRoute', { + 'basePluginPath': base_plugin_path, + 'method': method, + 'endpoint': endpoint, + 'queryParams': query_params, + 'request': request, + }) + + # Accounts routes. + + def get_account_congestion(self, account_id: HexStr) -> CongestionResponse: + """Checks if the account is ready to issue a block. + GET /api/core/v3/accounts/{bech32Address}/congestion + """ + return CongestionResponse.from_dict(self._call_method('getAccountCongestion', { + 'accountId': account_id + })) + + # Rewards routes. + + def get_output_mana_rewards( + self, output_id: OutputId, slot_index: SlotIndex) -> ManaRewardsResponse: + """Returns the total available Mana rewards of an account or delegation output decayed up to `epochEnd` index + provided in the response. + Note that rewards for an epoch only become available at the beginning of the next epoch. If the end epoch of a + staking feature is equal or greater than the current epoch, the rewards response will not include the potential + future rewards for those epochs. `epochStart` and `epochEnd` indicates the actual range for which reward value + is returned and decayed for. + GET /api/core/v3/rewards/{outputId} + """ + return ManaRewardsResponse.from_dict(self._call_method('getOutputManaRewards', { + 'outputId': output_id, + 'slotIndex': slot_index + })) + + # Committee routes. + + def get_committee(self, epoch_index: EpochIndex) -> CommitteeResponse: + """Returns the information of committee members at the given epoch index. If epoch index is not provided, the + current committee members are returned. + GET /api/core/v3/committee/?epochIndex + """ + return CommitteeResponse.from_dict(self._call_method('getCommittee', { + 'epochIndex': epoch_index + })) + + # Validators routes. + + def get_validators(self, page_size, cursor) -> ValidatorsResponse: + """Returns information of all registered validators and if they are active. + GET JSON to /api/core/v3/validators + """ + return ValidatorsResponse.from_dict(self._call_method('getValidators', { + 'pageSize': page_size, + 'cursor': cursor + })) + + def get_validator(self, account_id: HexStr) -> ValidatorResponse: + """Return information about a validator. + GET /api/core/v3/validators/{bech32Address} """ - return from_dict(NodeInfoWrapper, self._call_method('getInfo')) + return ValidatorResponse.from_dict(self._call_method('getValidator', { + 'accountId': account_id + })) + + # Block routes. - def get_tips(self) -> List[BlockId]: - """Request tips from the node. + def get_issuance(self) -> IssuanceBlockHeaderResponse: + """Returns information that is ideal for attaching a block in the network. + GET /api/core/v3/blocks/issuance """ - return self._call_method('getTips') + return IssuanceBlockHeaderResponse.from_dict(self._call_method('getIssuance')) def post_block(self, block: Block) -> BlockId: - """Post a block. + """Returns the BlockId of the submitted block. + POST JSON to /api/core/v3/blocks Args: block: The block to post. @@ -81,76 +181,130 @@ def post_block(self, block: Block) -> BlockId: Returns: The block id of the posted block. """ - return BlockId(self._call_method('postBlock', { + return self._call_method('postBlock', { 'block': block - })) + }) + + def post_block_raw(self, block: Block) -> BlockId: + """Returns the BlockId of the submitted block. + POST /api/core/v3/blocks + + Returns: + The corresponding block id of the block. + """ + return self._call_method('postBlockRaw', { + 'block': block + }) def get_block(self, block_id: BlockId) -> Block: - """Get the block corresponding to the given block id. + """Finds a block by its ID and returns it as object. + GET /api/core/v3/blocks/{blockId} + + Returns: + The corresponding block. """ return Block.from_dict(self._call_method('getBlock', { 'blockId': block_id })) - def get_block_metadata(self, block_id: BlockId) -> BlockMetadata: - """Get the block metadata corresponding to the given block id. + def get_block_raw(self, block_id: BlockId) -> List[int]: + """Finds a block by its ID and returns it as raw bytes. + GET /api/core/v3/blocks/{blockId} + + Returns: + The corresponding raw bytes of a block. """ - return BlockMetadata.from_dict(self._call_method('getBlockMetadata', { + return self._call_method('getBlockRaw', { 'blockId': block_id - })) + }) - def get_block_with_metadata(self, block_id: BlockId) -> BlockWithMetadata: - """Get a block with its metadata corresponding to the given block id. + def get_block_metadata(self, block_id: BlockId) -> BlockMetadataResponse: + """Returns the metadata of a block. + GET /api/core/v3/blocks/{blockId}/metadata + + Returns: + The corresponding block metadata. """ - return BlockWithMetadata.from_dict(self._call_method('getBlockWithMetadata', { + return BlockMetadataResponse.from_dict(self._call_method('getBlockMetadata', { 'blockId': block_id })) - def get_block_raw(self, block_id: BlockId) -> List[int]: - """Get the raw bytes of the block corresponding to the given block id. + def get_block_with_metadata(self, block_id: BlockId) -> BlockWithMetadataResponse: + """Returns a block with its metadata. + GET /api/core/v2/blocks/{blockId}/full + + Returns: + The corresponding block with it metadata. """ - return self._call_method('getBlockRaw', { + return BlockWithMetadataResponse.from_dict(self._call_method('getBlockWithMetadata', { 'blockId': block_id - }) + })) - def post_block_raw(self, block_bytes: List[int]) -> BlockId: - """Post a block as raw bytes. + # UTXO routes. + + # TODO: this should return `OutputResponse`, not OutputWithMetadataResponse + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_output( + self, output_id: Union[OutputId, HexStr]) -> OutputWithMetadataResponse: + """Finds an output by its ID and returns it as object. + GET /api/core/v3/outputs/{outputId} Returns: - The corresponding block id of the block. + The corresponding output. """ - return BlockId(self._call_method('postBlockRaw', { - 'blockBytes': block_bytes + output_id_str = output_id.output_id if isinstance( + output_id, OutputId) else output_id + return OutputWithMetadataResponse.from_dict(self._call_method('getOutput', { + 'outputId': output_id_str })) - def get_output( - self, output_id: Union[OutputId, HexStr]) -> OutputWithMetadata: - """Get the output corresponding to the given output id. + # TODO: this should be made available + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_output_raw( + self, output_id: Union[OutputId, HexStr]) -> List[int]: + """Finds an output by its ID and returns it as raw bytes. + GET /api/core/v3/outputs/{outputId} Returns: - The output itself with its metadata. + The raw bytes of the corresponding output. """ output_id_str = output_id.output_id if isinstance( output_id, OutputId) else output_id - return OutputWithMetadata.from_dict(self._call_method('getOutput', { + return self._call_method('getOutputRaw', { 'outputId': output_id_str - })) + }) def get_output_metadata( self, output_id: Union[OutputId, HexStr]) -> OutputMetadata: - """Get the output metadata corresponding to the given output id. + """Finds output metadata by output ID. + GET /api/core/v3/outputs/{outputId}/metadata Returns: The output metadata. """ output_id_str = output_id.output_id if isinstance( output_id, OutputId) else output_id - return from_dict(OutputMetadata, self._call_method('getOutputMetadata', { + return OutputMetadata.from_dict(self._call_method('getOutputMetadata', { + 'outputId': output_id_str + })) + + def get_output_with_metadata( + self, output_id: Union[OutputId, HexStr]) -> OutputWithMetadataResponse: + """Finds an output with its metadata by output ID. + GET /api/core/v3/outputs/{outputId}/full + + Returns: + The corresponding output. + """ + output_id_str = output_id.output_id if isinstance( + output_id, OutputId) else output_id + return OutputWithMetadataResponse.from_dict(self._call_method('getOutputWithMetadata', { 'outputId': output_id_str })) def get_included_block(self, transaction_id: TransactionId) -> Block: - """Returns the included block of the given transaction. + """Returns the earliest confirmed block containing the transaction with the given ID. + GET /api/core/v3/transactions/{transactionId}/included-block Returns: The included block. @@ -159,34 +313,146 @@ def get_included_block(self, transaction_id: TransactionId) -> Block: 'transactionId': transaction_id })) + # TODO: this should be made available + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_included_block_raw(self, transaction_id: TransactionId) -> List[int]: + """Returns the earliest confirmed block containing the transaction with the given ID, as raw bytes. + GET /api/core/v3/transactions/{transactionId}/included-block + + Returns: + The raw bytes of the included block. + """ + return self._call_method('getIncludedBlockRaw', { + 'transactionId': transaction_id + }) + def get_included_block_metadata( - self, transaction_id: TransactionId) -> BlockMetadata: - """Returns the metadata of the included block of the given transaction. + self, transaction_id: TransactionId) -> BlockMetadataResponse: + """Returns the metadata of the earliest block containing the tx that was confirmed. + GET /api/core/v3/transactions/{transactionId}/included-block/metadata Returns: The metadata of the included block. """ - return BlockMetadata.from_dict(self._call_method('getIncludedBlockMetadata', { + return BlockMetadataResponse.from_dict(self._call_method('getIncludedBlockMetadata', { 'transactionId': transaction_id })) - def call_plugin_route(self, base_plugin_path: str, method: str, - endpoint: str, query_params: Optional[List[str]] = None, request: Optional[str] = None): - """Extension method which provides request methods for plugins. + def get_transaction_metadata( + self, transaction_id: TransactionId) -> TransactionMetadataResponse: + """Finds the metadata of a transaction. + GET /api/core/v3/transactions/{transactionId}/metadata - Args: - base_plugin_path: The base path of the routes provided by the plugin. - method: The HTTP method. - endpoint: The endpoint to query provided by the plugin. - query_params: The parameters of the query. - request: The request object sent to the endpoint of the plugin. + Returns: + The transaction metadata. """ - if query_params is None: - query_params = [] - return self._call_method('callPluginRoute', { - 'basePluginPath': base_plugin_path, - 'method': method, - 'endpoint': endpoint, - 'queryParams': query_params, - 'request': request, + return TransactionMetadataResponse.from_dict(self._call_method('getTransactionMetadata', { + 'transactionId': transaction_id + })) + + # Commitments routes. + + def get_commitment( + self, commitment_id: SlotCommitmentId) -> SlotCommitment: + """Finds a slot commitment by its ID and returns it as object. + GET /api/core/v3/commitments/{commitmentId} + + Returns: + The corresponding slot commitment. + """ + return SlotCommitment.from_dict(self._call_method('getCommitment', { + 'commitmentId': commitment_id + })) + + # TODO: this should be made available + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_commitment_raw( + self, commitment_id: SlotCommitmentId) -> List[int]: + """Finds a slot commitment by its ID and returns it as raw bytes. + GET /api/core/v3/commitments/{commitmentId} + + Returns: + The raw bytes of the corresponding slot commitment. + """ + return self._call_method('getCommitmentRaw', { + 'commitmentId': commitment_id + }) + + def get_utxo_changes( + self, commitment_id: SlotCommitmentId) -> UtxoChangesResponse: + """Get all UTXO changes of a given slot by slot commitment ID. + GET /api/core/v3/commitments/{commitmentId}/utxo-changes + + Returns: + The corresponding UTXO changes. + """ + return UtxoChangesResponse.from_dict(self._call_method('getUtxoChanges', { + 'commitmentId': commitment_id + })) + + def get_utxo_changes_full( + self, commitment_id: SlotCommitmentId) -> UtxoChangesFullResponse: + """Get all full UTXO changes of a given slot by slot commitment ID. + GET /api/core/v3/commitments/{commitmentId}/utxo-changes/full + + Returns: + The full UTXO changes. + """ + return UtxoChangesFullResponse.from_dict(self._call_method('getUtxoChangesFull', { + 'commitmentId': commitment_id + })) + + # TODO: call method name needs to be changed to `getCommitmentBySlot` + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_slot_commitment_by_slot( + self, slot: SlotIndex) -> SlotCommitment: + """Finds a slot commitment by slot index and returns it as object. + GET /api/core/v3/commitments/by-slot/{slot} + + Returns: + The corresponding slot commitment. + """ + return SlotCommitment.from_dict(self._call_method('getCommitmentByIndex', { + 'slot': slot + })) + + # TODO: this should be made available + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_slot_commitment_by_slot_raw( + self, slot: SlotIndex) -> List[int]: + """Finds a slot commitment by slot index and returns it as raw bytes. + GET /api/core/v3/commitments/by-slot/{slot} + + Returns: + The raw bytes of the corresponding slot commitment. + """ + return self._call_method('getCommitmentBySlotRaw', { + 'slot': slot }) + + # TODO: call method name needs to be changed to `getUxoChangesBySlot` + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_utxo_changes_by_slot(self, slot: SlotIndex) -> UtxoChangesResponse: + """Get all UTXO changes of a given slot by its index. + GET /api/core/v3/commitments/by-slot/{slot}/utxo-changes + + Returns: + The corresponding UTXO changes. + """ + return UtxoChangesResponse.from_dict(self._call_method('getUtxoChangesByIndex', { + 'slot': slot + })) + + # TODO: call method name needs to be changed to `getUxoChangesFullBySlot` + # https://github.com/iotaledger/iota-sdk/issues/1921 + def get_utxo_changes_full_by_slot( + self, slot: SlotIndex) -> UtxoChangesFullResponse: + """Get all full UTXO changes of a given slot by its index. + GET /api/core/v3/commitments/by-slot/{slot}/utxo-changes/full + + Returns: + The full UTXO changes. + """ + return UtxoChangesFullResponse.from_dict(self._call_method('getUtxoChangesFullByIndex', { + 'slot': slot + })) diff --git a/bindings/python/iota_sdk/client/_node_indexer_api.py b/bindings/python/iota_sdk/client/_node_indexer_api.py index db684098d4..ef93bfb1c8 100644 --- a/bindings/python/iota_sdk/client/_node_indexer_api.py +++ b/bindings/python/iota_sdk/client/_node_indexer_api.py @@ -2,33 +2,14 @@ # SPDX-License-Identifier: Apache-2.0 from dataclasses import dataclass -from typing import Dict, Optional +from typing import Optional from abc import ABCMeta, abstractmethod +from iota_sdk.client.responses import OutputIdsResponse from iota_sdk.types.common import HexStr, json, SlotIndex from iota_sdk.types.output_id import OutputId -@json -@dataclass -class OutputIdsResponse: - """Response type for output IDs. - - Attributes: - committed_slot: The committed slot at which these outputs were available at. - page_size: The maximum amount of items returned in one call. If there are more items, a cursor to the next page is returned too. - cursor: The cursor to the next page of results. - items: The query results. - """ - - def __init__(self, output_dict: Dict): - self.committed_slot = output_dict["committedSlot"] - self.page_size = output_dict["pageSize"] - self.cursor = output_dict["cursor"] - self.items = [OutputId.from_string( - output_id) for output_id in output_dict["items"]] - - class NodeIndexerAPI(metaclass=ABCMeta): """Node indexer API. """ diff --git a/bindings/python/iota_sdk/client/responses.py b/bindings/python/iota_sdk/client/responses.py new file mode 100644 index 0000000000..cd3a9bf24a --- /dev/null +++ b/bindings/python/iota_sdk/client/responses.py @@ -0,0 +1,381 @@ +# Copyright 2024 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from typing import Dict, List, Optional +from dataclasses import dataclass, field +from dataclasses_json import config + +from iota_sdk.types.block.block import Block +from iota_sdk.types.block.id import BlockId +from iota_sdk.types.block.metadata import BlockFailureReason, BlockState +from iota_sdk.types.common import HexStr, json, EpochIndex, SlotIndex +from iota_sdk.types.node_info import NodeInfoBaseToken, NodeInfoMetrics, NodeInfoStatus, ProtocolParameters +from iota_sdk.types.output import Output, deserialize_output +from iota_sdk.types.output_id import OutputId, OutputWithId +from iota_sdk.types.output_id_proof import OutputIdProof +from iota_sdk.types.output_metadata import OutputMetadata +from iota_sdk.types.slot import SlotCommitment, SlotCommitmentId +from iota_sdk.types.transaction_id import TransactionId +from iota_sdk.types.transaction_metadata import TransactionFailureReason, TransactionState + + +@json +@dataclass +class OutputIdsResponse: + """Response type for output IDs. + + Attributes: + committed_slot: The committed slot at which these outputs were available at. + page_size: The maximum amount of items returned in one call. If there are more items, a cursor to the next page is returned too. + cursor: The cursor to the next page of results. + items: The query results. + """ + + def __init__(self, output_dict: Dict): + self.committed_slot = output_dict["committedSlot"] + self.page_size = output_dict["pageSize"] + self.cursor = output_dict["cursor"] + self.items = [OutputId.from_string( + output_id) for output_id in output_dict["items"]] + + +@json +@dataclass +class CommitteeMember: + """Information of a committee member. + + Attributes: + address: Account address of the validator. + pool_stake: The total stake of the pool, including delegators. + validator_stake: The stake of a validator. + fixed_cost: The fixed cost of the validator, which it receives as part of its Mana rewards. + """ + address: str + pool_stake: int = field(metadata=config( + encoder=str + )) + validator_stake: int = field(metadata=config( + encoder=str + )) + fixed_cost: int = field(metadata=config( + encoder=str + )) + + +@json +@dataclass +class CommitteeResponse: + """The validator information of the committee. + Response of GET /api/core/v3/committee + + Attributes: + committee: The validators of the committee. + total_stake: The total amount of delegated and staked IOTA coins in the selected committee. + total_validator_stake: The total amount of staked IOTA coins in the selected committee. + epoch: The epoch index of the committee. + """ + committee: List[CommitteeMember] + total_stake: int = field(metadata=config( + encoder=str + )) + total_validator_stake: int = field(metadata=config( + encoder=str + )) + epoch: EpochIndex + + +@json +@dataclass +class ValidatorResponse: + """Information of a validator. + Response of GET /api/core/v3/validators/{bech32Address} + + Attributes: + address: Account address of the validator. + staking_end_epoch: The epoch index until which the validator registered to stake. + pool_stake: The total stake of the pool, including delegators. + validator_stake: The stake of a validator. + fixed_cost: The fixed cost of the validator, which it receives as part of its Mana rewards. + active: Shows whether the validator was active recently. + latest_supported_protocol_version: The latest protocol version the validator supported. + latest_supported_protocol_hash: The protocol hash of the latest supported protocol of the validator. + """ + address: str + staking_end_epoch: EpochIndex + pool_stake: int = field(metadata=config( + encoder=str + )) + validator_stake: int = field(metadata=config( + encoder=str + )) + fixed_cost: int = field(metadata=config( + encoder=str + )) + active: bool + latest_supported_protocol_version: int + latest_supported_protocol_hash: HexStr + + +@json +@dataclass +class ValidatorsResponse: + """A paginated list of all registered validators ready for the next epoch and indicates if they were active recently + (are eligible for committee selection). + Response of GET /api/core/v3/validators + + Attributes: + stakers: List of registered validators ready for the next epoch. + page_size: The number of validators returned per one API request with pagination. + cursor: The cursor that needs to be provided as cursor query parameter to request the next page. If empty, this was the last page. + """ + stakers: List[ValidatorResponse] + page_size: int + cursor: Optional[str] = None + + +@json +@dataclass +class IssuanceBlockHeaderResponse: + """Information that is used to attach a block in the network. + Response of GET /api/core/v3/blocks/issuance + + Attributes: + strong_parents: Blocks that are strongly directly approved. + latest_parent_block_issuing_time: Latest issuing time of the returned parents. + latest_finalized_slot: The slot index of the latest finalized slot. + latest_commitment: The latest slot commitment. + weak_parents: Blocks that are weakly directly approved. + shallow_like_parents: Blocks that are directly referenced to adjust opinion. + """ + strong_parents: List[BlockId] + latest_parent_block_issuing_time: int = field(metadata=config( + encoder=str + )) + latest_finalized_slot: SlotIndex + latest_commitment: SlotCommitment + weak_parents: Optional[List[BlockId]] = None + shallow_like_parents: Optional[List[BlockId]] = None + + +@json +@dataclass +class CongestionResponse: + """Provides the cost and readiness to issue estimates. + Response of GET /api/core/v3/accounts/{accountId}/congestion. + + Attributes: + slot: The slot index for which the congestion estimate is provided. + ready: Indicates if a node is ready to issue a block in a current congestion or should wait. + reference_mana_cost: The cost in mana for issuing a block in a current congestion estimated based on RMC and slot index. + block_issuance_credits: The Block Issuance Credits of the requested account. + """ + slot: SlotIndex + ready: bool + reference_mana_cost: int = field(metadata=config( + encoder=str + )) + block_issuance_credits: int = field(metadata=config( + encoder=str + )) + + +@json +@dataclass +class ManaRewardsResponse: + """The mana rewards of an account or delegation output. + Response of GET /api/core/v3/rewards/{outputId}. + + Attributes: + start_epoch: First epoch for which rewards can be claimed. This value is useful for checking if rewards have expired (by comparing against the staking or delegation start) or would expire soon (by checking its relation to the rewards retention period). + end_epoch: Last epoch for which rewards can be claimed. + rewards: Amount of totally available decayed rewards the requested output may claim. + latest_committed_epoch_pool_rewards: Rewards of the latest committed epoch of the staking pool to which this validator or delegator belongs. The ratio of this value and the maximally possible rewards for the latest committed epoch can be used to determine how well the validator of this staking pool performed in that epoch. Note that if the pool was not part of the committee in the latest committed epoch, this value is 0. + """ + start_epoch: EpochIndex + end_epoch: EpochIndex + rewards: int = field(metadata=config( + encoder=str + )) + latest_committed_epoch_pool_rewards: int = field(metadata=config( + encoder=str + )) + + +@json +@dataclass +class ProtocolParametersResponse: + """Protocol Parameters with start epoch. + + Attributes: + start_epoch: The start epoch of the set of protocol parameters. + parameters: The protocol parameters. + """ + start_epoch: EpochIndex + parameters: ProtocolParameters + + +# TODO: rename sdk-wide to `NodeInfoResponse`? +@json +@dataclass +class InfoResponse: + """General information about the node. + GET /api/core/v3/info. + + Attributes: + name: The name of the node (e.g. Hornet). + version: The semantic version of the node. + status: The status of the node. + metrics: Node metrics. + protocol_parameters: Supported protocol versions by the node. + base_token: Gives info about the base token the network uses. + """ + name: str + version: str + status: NodeInfoStatus + metrics: NodeInfoMetrics + protocol_parameters: List[ProtocolParametersResponse] + base_token: NodeInfoBaseToken + + +# TODO: rename sdk-wide to `NodeInfoResponseWithUrl`? +@json +@dataclass +class NodeInfoWrapper: + """General information about the node and its URL. + GET /api/core/v3/info. + + Attributes: + node_info: A NodeInfo object. + url: The URL of the node. + """ + node_info: InfoResponse + url: str + + +@json +@dataclass +class RoutesResponse: + """API route groups of the node. + GET /api/routes. + + Attributes: + routes: The available API route groups of the node. + """ + routes: List[str] + + +@json +@dataclass +class OutputResponse: + """An output with its output id proof. + Response of GET /api/core/v3/outputs/{output_id}. + + Attributes: + output: One of the possible outputs. + output_id_proof: The associated Output ID proof. + """ + output: Output = field(metadata=config( + decoder=deserialize_output + )) + output_id_proof: OutputIdProof + + +@json +@dataclass +class OutputWithMetadataResponse: + """An output with its output id proof and its metadata. + Response of GET /api/core/v3/outputs/{output_id}/full. + + Attributes: + output: One of the possible outputs. + output_id_proof: The associated Output ID proof. + metadata: The metadata of an output. + """ + output: Output = field(metadata=config( + decoder=deserialize_output + )) + output_id_proof: OutputIdProof + metadata: OutputMetadata + + +@json +@dataclass +class TransactionMetadataResponse: + """Response of a GET transaction metadata REST API call. + + Attributes: + transaction_id: The identifier of the transaction. Hex-encoded with 0x prefix. + transaction_state: If 'pending', the transaction is not included yet. If 'accepted', the transaction is included. If 'confirmed' means transaction is included and its included block is confirmed. If 'finalized' means transaction is included, its included block is finalized and cannot be reverted anymore. If 'failed' means transaction is issued but failed due to the transaction failure reason. + transaction_failure_reason: The optional transaction failure reason. + """ + transaction_id: TransactionId + transaction_state: TransactionState + transaction_failure_reason: Optional[TransactionFailureReason] = None + + +@json +@dataclass +class UtxoChangesResponse: + """All UTXO changes that happened at a specific slot. + Response of + - GET /api/core/v3/commitments/{commitmentId}/utxo-changes + - GET /api/core/v3/commitments/by-slot/{slot}/utxo-changes + + Arguments: + commitment_id: The commitment ID of the requested slot that contains the changes. Hex-encoded with 0x prefix. + created_outputs: The created outputs of the given slot. + consumed_outputs: The consumed outputs of the given slot. + """ + commitment_id: SlotCommitmentId + created_outputs: List[OutputId] + consumed_outputs: List[OutputId] + + +@json +@dataclass +class UtxoChangesFullResponse: + """All full UTXO changes that happened at a specific slot. + Response of + - GET /api/core/v3/commitments/{commitmentId}/utxo-changes/full + - GET /api/core/v3/commitments/by-slot/{slot}/utxo-changes/full + + Arguments: + commitment_id: The commitment ID of the requested slot that contains the changes. Hex-encoded with 0x prefix. + created_outputs: The created outputs of the given slot. + consumed_outputs: The consumed outputs of the given slot. + """ + commitment_id: SlotCommitmentId + created_outputs: List[OutputWithId] + consumed_outputs: List[OutputWithId] + + +@json +@dataclass +class BlockMetadataResponse: + """The metadata of a block. + Response of GET /api/core/v3/blocks/{blockId}/metadata. + + Attributes: + block_id: The identifier of the block. Hex-encoded with 0x prefix. + block_state: If pending, the block is stored but not confirmed. If confirmed, the block is confirmed with the first level of knowledge. If finalized, the block is included and cannot be reverted anymore. If rejected, the block is rejected by the node, and user should reissue payload if it contains one. If failed, the block is not successfully issued due to failure reason. + block_failure_reason: The optional block failure reason. + transaction_metadata: The optional metadata of a given transaction. + """ + block_id: BlockId + block_state: BlockState + block_failure_reason: Optional[BlockFailureReason] = None + transaction_metadata: Optional[TransactionMetadataResponse] = None + + +@json +@dataclass +class BlockWithMetadataResponse: + """A block with its metadata. + Response of GET /api/core/v3/blocks/{blockId}/full. + + Attributes: + block: The block. + metadata: The block metadata. + """ + block: Block + metadata: BlockMetadataResponse diff --git a/bindings/python/iota_sdk/secret_manager/secret_manager.py b/bindings/python/iota_sdk/secret_manager/secret_manager.py index 1925fbd635..041fee2780 100644 --- a/bindings/python/iota_sdk/secret_manager/secret_manager.py +++ b/bindings/python/iota_sdk/secret_manager/secret_manager.py @@ -85,7 +85,7 @@ class SecretManagerError(Exception): """ -class SecretManager(): +class SecretManager: """Secret manager wrapper. """ diff --git a/bindings/python/iota_sdk/types/address.py b/bindings/python/iota_sdk/types/address.py index 1bb1eeb958..63ced66c56 100644 --- a/bindings/python/iota_sdk/types/address.py +++ b/bindings/python/iota_sdk/types/address.py @@ -4,6 +4,7 @@ from enum import IntEnum from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, TypeAlias, Union + from iota_sdk.types.common import HexStr, json diff --git a/bindings/python/iota_sdk/types/block/block.py b/bindings/python/iota_sdk/types/block/block.py index 3938565ed0..beb38d6deb 100644 --- a/bindings/python/iota_sdk/types/block/block.py +++ b/bindings/python/iota_sdk/types/block/block.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field from typing import Any, Dict, TypeAlias, Union from dataclasses_json import config + from iota_sdk.utils import Utils from iota_sdk.types.common import HexStr, json, SlotIndex from iota_sdk.types.node_info import ProtocolParameters diff --git a/bindings/python/iota_sdk/types/block/metadata.py b/bindings/python/iota_sdk/types/block/metadata.py index 521f3d23b3..7326ecdcf4 100644 --- a/bindings/python/iota_sdk/types/block/metadata.py +++ b/bindings/python/iota_sdk/types/block/metadata.py @@ -5,9 +5,10 @@ from enum import Enum, IntEnum from dataclasses import dataclass from typing import Optional + from iota_sdk.types.common import json -from iota_sdk.types.block.block import Block from iota_sdk.types.block.id import BlockId +from iota_sdk.types.transaction_metadata import TransactionFailureReason, TransactionState @json @@ -48,23 +49,6 @@ class BlockState(Enum): Failed = 5 -class TransactionState(Enum): - """Describes the state of a transaction. - - Attributes: - Pending: Not included yet. - Accepted: Included. - Confirmed: Included and its included block is confirmed. - Finalized: Included, its included block is finalized and cannot be reverted anymore. - Failed: Not successfully issued due to failure reason. - """ - Pending = 0 - Accepted = 1 - Confirmed = 2 - Finalized = 3 - Failed = 4 - - class BlockFailureReason(IntEnum): """Describes the reason of a block failure. @@ -113,158 +97,3 @@ def __str__(self): 12: "The block payload is invalid.", 255: "The block is invalid." }[self.value] - - -class TransactionFailureReason(Enum): - """Represents the possible reasons for a failing transaction. - """ - Null = 0 - TypeInvalid = 1 - Conflicting = 2 - InputAlreadySpent = 3 - InputCreationAfterTxCreation = 4 - UnlockSignatureInvalid = 5 - CommitmentInputMissing = 6 - CommitmentInputReferenceInvalid = 7 - BicInputReferenceInvalid = 8 - RewardInputReferenceInvalid = 9 - StakingRewardCalculationFailure = 10 - DelegationRewardCalculationFailure = 11 - InputOutputBaseTokenMismatch = 12 - ManaOverflow = 13 - InputOutputManaMismatch = 14 - ManaDecayCreationIndexExceedsTargetIndex = 15 - NativeTokenAmountLessThanZero = 16 - NativeTokenSumExceedsUint256 = 17 - NativeTokenSumUnbalanced = 18 - MultiAddressLengthUnlockLengthMismatch = 19 - MultiAddressUnlockThresholdNotReached = 20 - NestedMultiUnlock = 21 - SenderFeatureNotUnlocked = 22 - IssuerFeatureNotUnlocked = 23 - StakingRewardInputMissing = 24 - StakingBlockIssuerFeatureMissing = 25 - StakingCommitmentInputMissing = 26 - StakingRewardClaimingInvalid = 27 - StakingFeatureRemovedBeforeUnbonding = 28 - StakingFeatureModifiedBeforeUnbonding = 29 - StakingStartEpochInvalid = 30 - StakingEndEpochTooEarly = 31 - BlockIssuerCommitmentInputMissing = 32 - BlockIssuanceCreditInputMissing = 33 - BlockIssuerNotExpired = 34 - BlockIssuerExpiryTooEarly = 35 - ManaMovedOffBlockIssuerAccount = 36 - AccountLocked = 37 - TimelockCommitmentInputMissing = 38 - TimelockNotExpired = 39 - ExpirationCommitmentInputMissing = 40 - ExpirationNotUnlockable = 41 - ReturnAmountNotFulFilled = 42 - NewChainOutputHasNonZeroedId = 43 - ChainOutputImmutableFeaturesChanged = 44 - ImplicitAccountDestructionDisallowed = 45 - MultipleImplicitAccountCreationAddresses = 46 - AccountInvalidFoundryCounter = 47 - FoundryTransitionWithoutAccount = 48 - FoundrySerialInvalid = 49 - DelegationCommitmentInputMissing = 50 - DelegationRewardInputMissing = 51 - DelegationRewardsClaimingInvalid = 52 - DelegationOutputTransitionedTwice = 53 - DelegationModified = 54 - DelegationStartEpochInvalid = 55 - DelegationAmountMismatch = 56 - DelegationEndEpochNotZero = 57 - DelegationEndEpochInvalid = 58 - CapabilitiesNativeTokenBurningNotAllowed = 59 - CapabilitiesManaBurningNotAllowed = 60 - CapabilitiesAccountDestructionNotAllowed = 61 - CapabilitiesAnchorDestructionNotAllowed = 62 - CapabilitiesFoundryDestructionNotAllowed = 63 - CapabilitiesNftDestructionNotAllowed = 64 - SemanticValidationFailed = 255 - - def __str__(self): - return { - 0: "Null.", - 1: "Transaction type is invalid.", - 2: "Transaction is conflicting.", - 3: "Input already spent.", - 4: "Input creation slot after tx creation slot.", - 5: "Signature in unlock is invalid.", - 6: "Commitment input required with reward or BIC input.", - 7: "Commitment input references an invalid or non-existent commitment.", - 8: "BIC input reference cannot be loaded.", - 9: "Reward input does not reference a staking account or a delegation output.", - 10: "Staking rewards could not be calculated due to storage issues or overflow.", - 11: "Delegation rewards could not be calculated due to storage issues or overflow.", - 12: "Inputs and outputs do not spend/deposit the same amount of base tokens.", - 13: "Under- or overflow in Mana calculations.", - 14: "Inputs and outputs do not contain the same amount of Mana.", - 15: "Mana decay creation slot/epoch index exceeds target slot/epoch index.", - 16: "Native token amount must be greater than zero.", - 17: "Native token sum exceeds max value of a uint256.", - 18: "Native token sums are unbalanced.", - 19: "Multi address length and multi unlock length do not match.", - 20: "Multi address unlock threshold not reached.", - 21: "Multi unlocks can't be nested.", - 22: "Sender feature is not unlocked.", - 23: "Issuer feature is not unlocked.", - 24: "Staking feature removal or resetting requires a reward input.", - 25: "Block issuer feature missing for account with staking feature.", - 26: "Staking feature validation requires a commitment input.", - 27: "Staking feature must be removed or reset in order to claim rewards.", - 28: "Staking feature can only be removed after the unbonding period.", - 29: "Staking start epoch, fixed cost and staked amount cannot be modified while bonded.", - 30: "Staking start epoch must be the epoch of the transaction.", - 31: "Staking end epoch must be set to the transaction epoch plus the unbonding period.", - 32: "Commitment input missing for block issuer feature.", - 33: "Block issuance credit input missing for account with block issuer feature.", - 34: "Block issuer feature has not expired.", - 35: "Block issuer feature expiry set too early.", - 36: "Mana cannot be moved off block issuer accounts except with manalocks.", - 37: "Account is locked due to negative block issuance credits.", - 38: "Transaction's containing a timelock condition require a commitment input.", - 39: "Timelock not expired.", - 40: "Transaction's containing an expiration condition require a commitment input.", - 41: "Expiration unlock condition cannot be unlocked.", - 42: "Return amount not fulfilled.", - 43: "New chain output has non-zeroed ID.", - 44: "Immutable features in chain output modified during transition.", - 45: "Cannot destroy implicit account; must be transitioned to account.", - 46: "Multiple implicit account creation addresses on the input side.", - 47: "Foundry counter in account decreased or did not increase by the number of new foundries.", - 48: "Foundry output transitioned without accompanying account on input or output side.", - 49: "Foundry output serial number is invalid.", - 50: "Delegation output validation requires a commitment input.", - 51: "Delegation output cannot be destroyed without a reward input.", - 52: "Invalid delegation mana rewards claiming.", - 53: "Delegation output attempted to be transitioned twice.", - 54: "Delegated amount, validator ID and start epoch cannot be modified.", - 55: "Invalid start epoch.", - 56: "Delegated amount does not match amount.", - 57: "End epoch must be set to zero at output genesis.", - 58: "Delegation end epoch does not match current epoch.", - 59: "Native token burning is not allowed by the transaction capabilities.", - 60: "Mana burning is not allowed by the transaction capabilities.", - 61: "Account destruction is not allowed by the transaction capabilities.", - 62: "Anchor destruction is not allowed by the transaction capabilities.", - 63: "Foundry destruction is not allowed by the transaction capabilities.", - 64: "NFT destruction is not allowed by the transaction capabilities.", - 255: "Semantic validation failed.", - }[self.value] - - -@json -@dataclass -class BlockWithMetadata: - """Represents a block with its metadata. - Response of GET /api/core/v3/blocks/{blockId}/full. - - Attributes: - block: The block. - metadata: The block metadata. - """ - block: Block - metadata: BlockMetadata diff --git a/bindings/python/iota_sdk/types/common.py b/bindings/python/iota_sdk/types/common.py index 6591e84be4..dfd3755666 100644 --- a/bindings/python/iota_sdk/types/common.py +++ b/bindings/python/iota_sdk/types/common.py @@ -71,7 +71,7 @@ def __int__(self): @json @dataclass -class Node(): +class Node: """Represents a node in the network. Attributes: @@ -128,7 +128,7 @@ def hex_str_decoder(value: str) -> int: @json @dataclass -class AddressAndAmount(): +class AddressAndAmount: """Parameters to send a certain amount of coins to an address. Attributes: diff --git a/bindings/python/iota_sdk/types/native_token.py b/bindings/python/iota_sdk/types/native_token.py index c382c31070..a44f83e23e 100644 --- a/bindings/python/iota_sdk/types/native_token.py +++ b/bindings/python/iota_sdk/types/native_token.py @@ -9,7 +9,7 @@ @json @dataclass -class NativeToken(): +class NativeToken: """A native token. Attributes: diff --git a/bindings/python/iota_sdk/types/node_info.py b/bindings/python/iota_sdk/types/node_info.py index 63f7803f4d..f88e237790 100644 --- a/bindings/python/iota_sdk/types/node_info.py +++ b/bindings/python/iota_sdk/types/node_info.py @@ -304,19 +304,6 @@ class ProtocolParameters: chain_switching_threshold: int -@json -@dataclass -class ProtocolParametersResponse: - """Protocol Parameters with start epoch. - - Attributes: - start_epoch: The start epoch of the set of protocol parameters. - parameters: The protocol parameters. - """ - start_epoch: EpochIndex - parameters: ProtocolParameters - - @json @dataclass class NodeInfoBaseToken: @@ -334,37 +321,3 @@ class NodeInfoBaseToken: unit: str decimals: int subunit: Optional[str] = None - - -@json -@dataclass -class NodeInfo: - """Response from the /info endpoint. - - Attributes: - name: The name of the node (e.g. Hornet). - version: The semantic version of the node. - status: The status of the node. - metrics: Node metrics. - protocol_parameters: Supported protocol versions by the node. - base_token: Gives info about the base token the network uses. - """ - name: str - version: str - status: NodeInfoStatus - metrics: NodeInfoMetrics - protocol_parameters: List[ProtocolParametersResponse] - base_token: NodeInfoBaseToken - - -@json -@dataclass -class NodeInfoWrapper: - """NodeInfo wrapper which contains the node info and the url from the node. - - Attributes: - node_info: A NodeInfo object. - url: The URL of the node. - """ - node_info: NodeInfo - url: str diff --git a/bindings/python/iota_sdk/types/output.py b/bindings/python/iota_sdk/types/output.py index 51720d9e75..1c2a24632f 100644 --- a/bindings/python/iota_sdk/types/output.py +++ b/bindings/python/iota_sdk/types/output.py @@ -36,6 +36,7 @@ class OutputType(IntEnum): @dataclass class BasicOutput: """Describes a basic output. + Attributes: amount : The base coin amount of the output. @@ -73,6 +74,7 @@ class BasicOutput: @dataclass class AccountOutput: """Describes an account output. + Attributes: amount : The base coin amount of the output. @@ -123,6 +125,7 @@ class AccountOutput: @dataclass class AnchorOutput: """Describes an anchor output. + Attributes: amount : The base coin amount of the output. @@ -174,6 +177,7 @@ class AnchorOutput: @dataclass class FoundryOutput: """Describes a foundry output. + Attributes: amount : The base coin amount of the output. @@ -214,6 +218,7 @@ class FoundryOutput: @dataclass class NftOutput: """Describes an NFT output. + Attributes: amount : The base coin amount of the output. @@ -259,6 +264,7 @@ class NftOutput: @dataclass class DelegationOutput: """An output which delegates its contained IOTA coins as voting power to a validator. + Attributes: amount: The amount of IOTA coins held by the output. delegated_amount: The amount of delegated IOTA coins. diff --git a/bindings/python/iota_sdk/types/output_data.py b/bindings/python/iota_sdk/types/output_data.py index 189449764f..f750f8cd78 100644 --- a/bindings/python/iota_sdk/types/output_data.py +++ b/bindings/python/iota_sdk/types/output_data.py @@ -14,7 +14,7 @@ @json @dataclass -class OutputData(): +class OutputData: """Output data. Attributes: diff --git a/bindings/python/iota_sdk/types/output_id.py b/bindings/python/iota_sdk/types/output_id.py index b666779fbd..81a49cf512 100644 --- a/bindings/python/iota_sdk/types/output_id.py +++ b/bindings/python/iota_sdk/types/output_id.py @@ -1,7 +1,10 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk.types.common import HexStr +from dataclasses import dataclass + +from iota_sdk.types.common import HexStr, json +from iota_sdk.types.output import Output from iota_sdk.types.transaction_id import TransactionId @@ -57,3 +60,16 @@ def from_string(cls, output_id: HexStr): def __repr__(self): return self.output_id + + +@json +@dataclass +class OutputWithId: + """An Output with its ID. + + Arguments: + output: Output, + output_id: OutputId, + """ + output: Output + output_id: OutputId diff --git a/bindings/python/iota_sdk/types/output_id_proof.py b/bindings/python/iota_sdk/types/output_id_proof.py new file mode 100644 index 0000000000..c9d85d3f92 --- /dev/null +++ b/bindings/python/iota_sdk/types/output_id_proof.py @@ -0,0 +1,94 @@ +# Copyright 2024 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +from enum import IntEnum +from typing import Any, Dict, TypeAlias, Union +from dataclasses import dataclass, field +from dataclasses_json import config +from iota_sdk.types.common import HexStr, json, SlotIndex + + +class TreeNodeType(IntEnum): + """Tree node types. + + Attributes: + HashableNode (0): Denotes a HashableNode. + LeafHash (1): Denotes a LeafHash. + Valuehash (2): Denotes a Valuehash. + """ + HashableNode = 0 + LeafHash = 1 + ValueHash = 2 + + +def deserialize_proof(d: Dict[str, Any]) -> OutputCommitmentProof: + """ + Takes a dictionary as input and returns an instance of a specific class based on the value of the 'type' key in the dictionary. + + Arguments: + * `d`: A dictionary that is expected to have a key called 'type' which specifies the type of the returned value. + """ + node_type = d['type'] + if node_type == TreeNodeType.HashableNode: + return HashableNode.from_dict(d) + if node_type == TreeNodeType.LeafHash: + return LeafHash.from_dict(d) + if node_type == TreeNodeType.ValueHash: + return ValueHash.from_dict(d) + raise Exception(f'invalid node type: {node_type}') + + +@json +@dataclass +class HashableNode: + """Node contains the hashes of the left and right children of a node in the tree. + """ + type: int = field(default_factory=lambda: int( + TreeNodeType.HashableNode), init=False) + l: OutputCommitmentProof = field(metadata=config( + decoder=deserialize_proof + )) + r: OutputCommitmentProof = field(metadata=config( + decoder=deserialize_proof + )) + + +@json +@dataclass +class LeafHash: + """Leaf Hash contains the hash of a leaf in the tree. + """ + type: int = field(default_factory=lambda: int( + TreeNodeType.LeafHash), init=False) + hash: HexStr + + +@json +@dataclass +class ValueHash: + """Value Hash contains the hash of the value for which the proof is being computed. + """ + type: int = field(default_factory=lambda: int( + TreeNodeType.ValueHash), init=False) + hash: HexStr + + +@json +@dataclass +class OutputIdProof: + """The proof of the output identifier. + + Attributes: + slot: The slot index of the output. + output_index: The index of the output within the corresponding transaction. + transaction_commitment: The commitment of the transaction that created the output. Hex-encoded with 0x prefix. + output_commitment_proof: The proof of the output commitment. Hex-encoded with 0x prefix. + """ + slot: SlotIndex + output_index: int + transaction_commitment: HexStr + output_commitment_proof: OutputCommitmentProof + + +OutputCommitmentProof: TypeAlias = Union[HashableNode, LeafHash, ValueHash] diff --git a/bindings/python/iota_sdk/types/output_metadata.py b/bindings/python/iota_sdk/types/output_metadata.py index 947d844281..381ef075c0 100644 --- a/bindings/python/iota_sdk/types/output_metadata.py +++ b/bindings/python/iota_sdk/types/output_metadata.py @@ -2,12 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from typing import Dict, Optional, Union +from typing import Dict, Optional from dataclasses import dataclass, field from dataclasses_json import config from iota_sdk.types.block.id import BlockId from iota_sdk.types.common import SlotIndex, json -from iota_sdk.types.output import AccountOutput, BasicOutput, DelegationOutput, FoundryOutput, NftOutput, deserialize_output +from iota_sdk.types.output import Output, deserialize_output from iota_sdk.types.output_id import OutputId from iota_sdk.types.slot import SlotCommitmentId from iota_sdk.types.transaction_id import TransactionId @@ -17,6 +17,7 @@ @dataclass class OutputMetadata: """Metadata about an output. + Response of GET /api/core/v3/outputs/{output_id}/metadata. Attributes: output_id: The ID of the output. @@ -41,12 +42,10 @@ class OutputWithMetadata: metadata: The `OutputMetadata` object that belongs to `output`. output: An `Output` object. """ - + output: Output = field(metadata=config( + decoder=deserialize_output + )) metadata: OutputMetadata - output: Union[AccountOutput, FoundryOutput, - NftOutput, BasicOutput, DelegationOutput] = field(metadata=config( - decoder=deserialize_output - )) @classmethod def from_dict(cls, data_dict: Dict) -> OutputWithMetadata: diff --git a/bindings/python/iota_sdk/types/output_params.py b/bindings/python/iota_sdk/types/output_params.py index 2303396596..21ec61a9d3 100644 --- a/bindings/python/iota_sdk/types/output_params.py +++ b/bindings/python/iota_sdk/types/output_params.py @@ -12,7 +12,7 @@ @json @dataclass -class Assets(): +class Assets: """Assets for OutputParams. """ nft_id: Optional[HexStr] = None @@ -20,7 +20,7 @@ class Assets(): @json @dataclass -class Features(): +class Features: """Features for OutputParams. """ tag: Optional[HexStr] = None @@ -32,7 +32,7 @@ class Features(): @json @dataclass -class Unlocks(): +class Unlocks: """Unlocks for OutputParams. """ expiration_slot_index: Optional[int] = field(default=None, metadata=config( @@ -52,7 +52,7 @@ class ReturnStrategy(str, Enum): @json @dataclass -class StorageDeposit(): +class StorageDeposit: """Storage deposit options for OutputParams. """ return_strategy: Optional[ReturnStrategy] = None @@ -61,7 +61,7 @@ class StorageDeposit(): @json @dataclass -class OutputParams(): +class OutputParams: """Params for `Wallet.prepare_output()`. """ recipient_address: str diff --git a/bindings/python/iota_sdk/types/send_params.py b/bindings/python/iota_sdk/types/send_params.py index 5c22399794..1439021ca4 100644 --- a/bindings/python/iota_sdk/types/send_params.py +++ b/bindings/python/iota_sdk/types/send_params.py @@ -12,7 +12,7 @@ @json @dataclass -class SendParams(): +class SendParams: """Parameters for sending base coins. Attributes: @@ -33,7 +33,7 @@ class SendParams(): @json @dataclass -class SendNativeTokenParams(): +class SendNativeTokenParams: """Parameters for sending a native token Attributes: @@ -50,7 +50,7 @@ class SendNativeTokenParams(): @json @dataclass -class SendNftParams(): +class SendNftParams: """Parameters for sending NFTs. Attributes: @@ -63,7 +63,7 @@ class SendNftParams(): @json @dataclass -class CreateNativeTokenParams(): +class CreateNativeTokenParams: """Parameters for creating native tokens. Attributes: @@ -86,7 +86,7 @@ class CreateNativeTokenParams(): @json @dataclass -class CreateDelegationParams(): +class CreateDelegationParams: """Parameters for creating a delegation output. Attributes: @@ -103,7 +103,7 @@ class CreateDelegationParams(): @json @dataclass -class BeginStakingParams(): +class BeginStakingParams: """Parameters for beginning staking. Attributes: @@ -120,7 +120,7 @@ class BeginStakingParams(): @json @dataclass -class MintNftParams(): +class MintNftParams: """Parameters for minting NFTs. Attributes: @@ -141,7 +141,7 @@ class MintNftParams(): @json @dataclass -class CreateAccountOutputParams(): +class CreateAccountOutputParams: """Parameters for creating accounts. Attributes: diff --git a/bindings/python/iota_sdk/types/signature.py b/bindings/python/iota_sdk/types/signature.py index 6bff6dadf4..7ed3d5a515 100644 --- a/bindings/python/iota_sdk/types/signature.py +++ b/bindings/python/iota_sdk/types/signature.py @@ -26,7 +26,7 @@ class Ed25519Signature: @json @dataclass -class Bip44(): +class Bip44: """A BIP44 chain. Attributes: diff --git a/bindings/python/iota_sdk/types/slot.py b/bindings/python/iota_sdk/types/slot.py index fa7389e691..89767a27bc 100644 --- a/bindings/python/iota_sdk/types/slot.py +++ b/bindings/python/iota_sdk/types/slot.py @@ -1,7 +1,36 @@ # Copyright 2024 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk.types.common import IdWithSlotIndex +from dataclasses import dataclass, field +from dataclasses_json import config + +from iota_sdk.types.common import HexStr, IdWithSlotIndex, json + + +@json +@dataclass +class SlotCommitment: + """Contains a summary of a slot. + It is linked to the commitment of the previous slot, which forms a commitment chain. + + Attributes: + protocol_version: The version of the protocol running. + slot: The slot index of this commitment. It is calculated based on genesis timestamp and the duration of a slot. + previous_commitment_id: The commitment ID of the previous slot. + roots_id: The digest of multiple sparse merkle tree roots of this slot. + cumulative_weight: The sum of previous slot commitment cumulative weight and weight of issuers of accepted blocks within this slot. It is just an indication of "committed into" this slot, and can not strictly be used for evaluating the switching of a chain. + reference_mana_cost: Reference Mana Cost (RMC) to be used in the slot with index at `index + Max Committable Age`. + """ + protocol_version: int + slot: int + previous_commitment_id: HexStr + roots_id: HexStr + cumulative_weight: int = field(metadata=config( + encoder=str + )) + reference_mana_cost: int = field(metadata=config( + encoder=str + )) class SlotCommitmentId(IdWithSlotIndex): diff --git a/bindings/python/iota_sdk/types/transaction_metadata.py b/bindings/python/iota_sdk/types/transaction_metadata.py new file mode 100644 index 0000000000..9d3a07c443 --- /dev/null +++ b/bindings/python/iota_sdk/types/transaction_metadata.py @@ -0,0 +1,162 @@ +# Copyright 2024 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +class TransactionState(Enum): + """Describes the state of a transaction. + + Attributes: + Pending: Not included yet. + Accepted: Included. + Confirmed: Included and its included block is confirmed. + Finalized: Included, its included block is finalized and cannot be reverted anymore. + Failed: Not successfully issued due to failure reason. + """ + Pending = 0 + Accepted = 1 + Confirmed = 2 + Finalized = 3 + Failed = 4 + + +class TransactionFailureReason(Enum): + """Represents the possible reasons for a failing transaction. + """ + Null = 0 + TypeInvalid = 1 + Conflicting = 2 + InputAlreadySpent = 3 + InputCreationAfterTxCreation = 4 + UnlockSignatureInvalid = 5 + CommitmentInputMissing = 6 + CommitmentInputReferenceInvalid = 7 + BicInputReferenceInvalid = 8 + RewardInputReferenceInvalid = 9 + StakingRewardCalculationFailure = 10 + DelegationRewardCalculationFailure = 11 + InputOutputBaseTokenMismatch = 12 + ManaOverflow = 13 + InputOutputManaMismatch = 14 + ManaDecayCreationIndexExceedsTargetIndex = 15 + NativeTokenAmountLessThanZero = 16 + NativeTokenSumExceedsUint256 = 17 + NativeTokenSumUnbalanced = 18 + MultiAddressLengthUnlockLengthMismatch = 19 + MultiAddressUnlockThresholdNotReached = 20 + NestedMultiUnlock = 21 + SenderFeatureNotUnlocked = 22 + IssuerFeatureNotUnlocked = 23 + StakingRewardInputMissing = 24 + StakingBlockIssuerFeatureMissing = 25 + StakingCommitmentInputMissing = 26 + StakingRewardClaimingInvalid = 27 + StakingFeatureRemovedBeforeUnbonding = 28 + StakingFeatureModifiedBeforeUnbonding = 29 + StakingStartEpochInvalid = 30 + StakingEndEpochTooEarly = 31 + BlockIssuerCommitmentInputMissing = 32 + BlockIssuanceCreditInputMissing = 33 + BlockIssuerNotExpired = 34 + BlockIssuerExpiryTooEarly = 35 + ManaMovedOffBlockIssuerAccount = 36 + AccountLocked = 37 + TimelockCommitmentInputMissing = 38 + TimelockNotExpired = 39 + ExpirationCommitmentInputMissing = 40 + ExpirationNotUnlockable = 41 + ReturnAmountNotFulFilled = 42 + NewChainOutputHasNonZeroedId = 43 + ChainOutputImmutableFeaturesChanged = 44 + ImplicitAccountDestructionDisallowed = 45 + MultipleImplicitAccountCreationAddresses = 46 + AccountInvalidFoundryCounter = 47 + FoundryTransitionWithoutAccount = 48 + FoundrySerialInvalid = 49 + DelegationCommitmentInputMissing = 50 + DelegationRewardInputMissing = 51 + DelegationRewardsClaimingInvalid = 52 + DelegationOutputTransitionedTwice = 53 + DelegationModified = 54 + DelegationStartEpochInvalid = 55 + DelegationAmountMismatch = 56 + DelegationEndEpochNotZero = 57 + DelegationEndEpochInvalid = 58 + CapabilitiesNativeTokenBurningNotAllowed = 59 + CapabilitiesManaBurningNotAllowed = 60 + CapabilitiesAccountDestructionNotAllowed = 61 + CapabilitiesAnchorDestructionNotAllowed = 62 + CapabilitiesFoundryDestructionNotAllowed = 63 + CapabilitiesNftDestructionNotAllowed = 64 + SemanticValidationFailed = 255 + + def __str__(self): + return { + 0: "Null.", + 1: "Transaction type is invalid.", + 2: "Transaction is conflicting.", + 3: "Input already spent.", + 4: "Input creation slot after tx creation slot.", + 5: "Signature in unlock is invalid.", + 6: "Commitment input required with reward or BIC input.", + 7: "Commitment input references an invalid or non-existent commitment.", + 8: "BIC input reference cannot be loaded.", + 9: "Reward input does not reference a staking account or a delegation output.", + 10: "Staking rewards could not be calculated due to storage issues or overflow.", + 11: "Delegation rewards could not be calculated due to storage issues or overflow.", + 12: "Inputs and outputs do not spend/deposit the same amount of base tokens.", + 13: "Under- or overflow in Mana calculations.", + 14: "Inputs and outputs do not contain the same amount of Mana.", + 15: "Mana decay creation slot/epoch index exceeds target slot/epoch index.", + 16: "Native token amount must be greater than zero.", + 17: "Native token sum exceeds max value of a uint256.", + 18: "Native token sums are unbalanced.", + 19: "Multi address length and multi unlock length do not match.", + 20: "Multi address unlock threshold not reached.", + 21: "Multi unlocks can't be nested.", + 22: "Sender feature is not unlocked.", + 23: "Issuer feature is not unlocked.", + 24: "Staking feature removal or resetting requires a reward input.", + 25: "Block issuer feature missing for account with staking feature.", + 26: "Staking feature validation requires a commitment input.", + 27: "Staking feature must be removed or reset in order to claim rewards.", + 28: "Staking feature can only be removed after the unbonding period.", + 29: "Staking start epoch, fixed cost and staked amount cannot be modified while bonded.", + 30: "Staking start epoch must be the epoch of the transaction.", + 31: "Staking end epoch must be set to the transaction epoch plus the unbonding period.", + 32: "Commitment input missing for block issuer feature.", + 33: "Block issuance credit input missing for account with block issuer feature.", + 34: "Block issuer feature has not expired.", + 35: "Block issuer feature expiry set too early.", + 36: "Mana cannot be moved off block issuer accounts except with manalocks.", + 37: "Account is locked due to negative block issuance credits.", + 38: "Transaction's containing a timelock condition require a commitment input.", + 39: "Timelock not expired.", + 40: "Transaction's containing an expiration condition require a commitment input.", + 41: "Expiration unlock condition cannot be unlocked.", + 42: "Return amount not fulfilled.", + 43: "New chain output has non-zeroed ID.", + 44: "Immutable features in chain output modified during transition.", + 45: "Cannot destroy implicit account; must be transitioned to account.", + 46: "Multiple implicit account creation addresses on the input side.", + 47: "Foundry counter in account decreased or did not increase by the number of new foundries.", + 48: "Foundry output transitioned without accompanying account on input or output side.", + 49: "Foundry output serial number is invalid.", + 50: "Delegation output validation requires a commitment input.", + 51: "Delegation output cannot be destroyed without a reward input.", + 52: "Invalid delegation mana rewards claiming.", + 53: "Delegation output attempted to be transitioned twice.", + 54: "Delegated amount, validator ID and start epoch cannot be modified.", + 55: "Invalid start epoch.", + 56: "Delegated amount does not match amount.", + 57: "End epoch must be set to zero at output genesis.", + 58: "Delegation end epoch does not match current epoch.", + 59: "Native token burning is not allowed by the transaction capabilities.", + 60: "Mana burning is not allowed by the transaction capabilities.", + 61: "Account destruction is not allowed by the transaction capabilities.", + 62: "Anchor destruction is not allowed by the transaction capabilities.", + 63: "Foundry destruction is not allowed by the transaction capabilities.", + 64: "NFT destruction is not allowed by the transaction capabilities.", + 255: "Semantic validation failed.", + }[self.value] diff --git a/bindings/python/iota_sdk/types/transaction_options.py b/bindings/python/iota_sdk/types/transaction_options.py index 4274ba9cf2..c15b1ba006 100644 --- a/bindings/python/iota_sdk/types/transaction_options.py +++ b/bindings/python/iota_sdk/types/transaction_options.py @@ -62,7 +62,7 @@ def to_dict(self) -> dict: @json @dataclass -class TransactionOptions(): +class TransactionOptions: """Transaction options. Attributes: diff --git a/bindings/python/iota_sdk/types/utxo_changes.py b/bindings/python/iota_sdk/types/utxo_changes.py deleted file mode 100644 index 71fea39610..0000000000 --- a/bindings/python/iota_sdk/types/utxo_changes.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2023 IOTA Stiftung -# SPDX-License-Identifier: Apache-2.0 - -from typing import List -from dataclasses import dataclass -from iota_sdk.types.common import json -from iota_sdk.types.output_id import OutputId - - -@json -@dataclass -class UtxoChanges(): - """Response of GET /api/core/v2/milestone/{milestone_index}/utxo-changes. - Returns all UTXO changes that happened at a specific milestone. - """ - index: int - created_outputs: List[OutputId] - consumed_outputs: List[OutputId] diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index ff22fcc128..c9ec807df0 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -27,7 +27,7 @@ # pylint: disable=too-many-public-methods -class Utils(): +class Utils: """Utility functions. """ diff --git a/bindings/python/iota_sdk/wallet/sync_options.py b/bindings/python/iota_sdk/wallet/sync_options.py index 487cb7dac9..1365ccb811 100644 --- a/bindings/python/iota_sdk/wallet/sync_options.py +++ b/bindings/python/iota_sdk/wallet/sync_options.py @@ -8,7 +8,7 @@ @json @dataclass -class WalletSyncOptions(): +class WalletSyncOptions: """Specifies what outputs should be synced for the ed25519 address from the wallet. Attributes: @@ -24,7 +24,7 @@ class WalletSyncOptions(): @json @dataclass -class AccountSyncOptions(): +class AccountSyncOptions: """Specifies what outputs should be synced for the address of an account output. Attributes: @@ -42,7 +42,7 @@ class AccountSyncOptions(): @json @dataclass -class NftSyncOptions(): +class NftSyncOptions: """Specifies what outputs should be synced for the address of an nft output. Attributes: @@ -58,7 +58,7 @@ class NftSyncOptions(): @json @dataclass -class SyncOptions(): +class SyncOptions: """The synchronization options. **Attributes** diff --git a/bindings/python/iota_sdk/wallet/wallet.py b/bindings/python/iota_sdk/wallet/wallet.py index 33788554ee..510dc70a24 100644 --- a/bindings/python/iota_sdk/wallet/wallet.py +++ b/bindings/python/iota_sdk/wallet/wallet.py @@ -46,7 +46,7 @@ class WalletOptions: # pylint: disable=too-many-public-methods -class Wallet(): +class Wallet: """An IOTA Wallet. Attributes: diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index 47fe8bb0b8..51121bbf8c 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -309,6 +309,8 @@ impl ClientInner { // Commitments routes. + // TODO: rename this to `get_commitment` + // https://github.com/iotaledger/iota-sdk/issues/1921 /// Finds a slot commitment by its ID and returns it as object. /// GET /api/core/v3/commitments/{commitmentId} pub async fn get_slot_commitment_by_id(&self, slot_commitment_id: &SlotCommitmentId) -> Result { @@ -317,6 +319,8 @@ impl ClientInner { self.get_request(path, None, false, true).await } + // TODO: rename this to `get_commitment_raw` + // https://github.com/iotaledger/iota-sdk/issues/1921 /// Finds a slot commitment by its ID and returns it as raw bytes. /// GET /api/core/v3/commitments/{commitmentId} pub async fn get_slot_commitment_by_id_raw(&self, slot_commitment_id: &SlotCommitmentId) -> Result> { @@ -325,6 +329,8 @@ impl ClientInner { self.get_request_bytes(path, None).await } + // TODO: rename this to `get_utxo_changes` + // https://github.com/iotaledger/iota-sdk/issues/1921 /// Get all UTXO changes of a given slot by slot commitment ID. /// GET /api/core/v3/commitments/{commitmentId}/utxo-changes pub async fn get_utxo_changes_by_slot_commitment_id( @@ -336,6 +342,8 @@ impl ClientInner { self.get_request(path, None, false, true).await } + // TODO: rename this to `get_utxo_changes_full` + // https://github.com/iotaledger/iota-sdk/issues/1921 /// Get all full UTXO changes of a given slot by slot commitment ID. /// GET /api/core/v3/commitments/{commitmentId}/utxo-changes/full pub async fn get_utxo_changes_full_by_slot_commitment_id( diff --git a/sdk/src/types/api/core.rs b/sdk/src/types/api/core.rs index 9c6341e413..244eb9fc27 100644 --- a/sdk/src/types/api/core.rs +++ b/sdk/src/types/api/core.rs @@ -283,7 +283,7 @@ pub struct IssuanceBlockHeaderResponse { /// Blocks that are directly referenced to adjust opinion. #[serde(default, skip_serializing_if = "BTreeSet::is_empty")] pub shallow_like_parents: BTreeSet, - // Latest issuing time of the returned parents. + /// Latest issuing time of the returned parents. #[serde(with = "string")] pub latest_parent_block_issuing_time: u64, /// The slot index of the latest finalized slot. @@ -456,20 +456,22 @@ pub struct BlockWithMetadataResponse { pub metadata: BlockMetadataResponse, } +// TODO: needs to be aligned with TIP-48. +// https://github.com/iotaledger/iota-sdk/issues/1921 /// Response of GET /api/core/v3/outputs/{output_id}. /// An output and its metadata. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct OutputWithMetadataResponse { - pub metadata: OutputMetadata, pub output: Output, + pub metadata: OutputMetadata, } impl From<&OutputWithMetadata> for OutputWithMetadataResponse { fn from(value: &OutputWithMetadata) -> Self { Self { - metadata: value.metadata, output: value.output().clone(), + metadata: value.metadata, } } } @@ -516,8 +518,8 @@ pub struct UtxoChangesFullResponse { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct OutputWithId { - pub output_id: OutputId, pub output: Output, + pub output_id: OutputId, } /// Contains the generic [`Output`] with associated [`OutputIdProof`]. diff --git a/sdk/src/types/block/slot/commitment.rs b/sdk/src/types/block/slot/commitment.rs index f61960c741..45b3d2f6a5 100644 --- a/sdk/src/types/block/slot/commitment.rs +++ b/sdk/src/types/block/slot/commitment.rs @@ -19,7 +19,7 @@ use crate::types::block::{ serde(rename_all = "camelCase") )] pub struct SlotCommitment { - // The version of the protocol running. + /// The protocol version. protocol_version: u8, /// The slot index of this commitment. /// It is calculated based on genesis timestamp and the duration of a slot. diff --git a/sdk/src/wallet/update.rs b/sdk/src/wallet/update.rs index 9a32401fec..54a2fe8df1 100644 --- a/sdk/src/wallet/update.rs +++ b/sdk/src/wallet/update.rs @@ -16,7 +16,6 @@ use crate::{ }; #[cfg(feature = "events")] use crate::{ - types::api::core::OutputWithMetadataResponse, types::block::payload::signed_transaction::dto::SignedTransactionPayloadDto, wallet::events::types::{NewOutputEvent, SpentOutputEvent, TransactionInclusionEvent, WalletEvent}, }; @@ -113,13 +112,7 @@ where transaction: transaction .as_ref() .map(|tx| SignedTransactionPayloadDto::from(&tx.payload)), - transaction_inputs: transaction.as_ref().map(|tx| { - tx.inputs - .clone() - .into_iter() - .map(OutputWithMetadataResponse::from) - .collect() - }), + transaction_inputs: transaction.as_ref().map(|tx| tx.inputs.clone()), }))) .await; }