Skip to main content

High Level API Specification

Introduction#

This document specifies a user-friendly API to be used in the client libraries. The main implementation will be in Rust which will receive automatically compiled client libraries in other languages via C or Webassembly bindings. There are also many crates to support developers creating foreign function interfaces with native bindings.

Builder#

The data structure to initialize the instance of the Higher level client library. This is always called first when starting a new interaction with the library. Note: This is the common approach to do initialization in Rust. Different languages might use different methods such as just calling an initialization function directly.

Parameters#

ParameterRequiredDefault ValueTypeDefinition
network✘Testnet&strOptional, the network type can be "testnet" or "mainnet". If no node url is provided, some default nodes are used for the specified network. Nodes that aren't in this network will be ignored.
node✘None&strThe URL of a node to connect to; format: https://node:port
primary_node✘None&str, auth_name_pwd: Option<(&str, &str)>The URL of a node to always connect first to, if multiple nodes are available. Optional JWT and or name and password for basic authentication; format: https://node:port, Some("JWT"), Some("name", "password")
primary_pow_node✘None&str, auth_name_pwd: Option<(&str, &str)>The URL of a node to always connect first to when submitting a message with remote PoW, if multiple nodes are available. Will override primary_node in that case. With optional JWT or name and password for basic authentication; format: https://node:port, Some("JWT"), Some("name", "password")
permanode✘None&str, auth_name_pwd: Option<(&str, &str)>The URL of a permanode. With optional JWT or name and password for basic authentication; format: https://node:port, Some("JWT"), Some("name", "password")
node_auth✘None&str, Option
, Option<(&str, &str)>
The URL of a node to connect to with optional JWT and or name and password for basic authentication; format: https://node:port, Some("JWT"), Some("name", "password")
nodes✘None&[&str]A list of nodes to connect to; nodes are added with the https://node:port format. The amount of nodes specified in quorum_size are randomly selected from this node list to check for quorum based on the quorum threshold. If quorum_size is not given the full list of nodes is checked.
node_sync_interval✘Duration::from_secs(60)std::time::DurationThe interval in milliseconds to check for node health and sync
node_sync_disabled✘falseboolIf disabled also unhealty nodes will be used
node_pool_urlsNone✘&[String]A list of node_pool_urls from which nodes are added. The amount of nodes specified in quorum_size are randomly selected from this node list to check for quorum based on the quorum threshold. If quorum_size is not given the full list of nodes is checked.
quorum✘falseboolDefine if quorum should be used for the requests
quorum_size✘3usizeDefine how many nodes should be used for quorum
quorum_threshold✘66usizeDefine the % of nodes that need to return the same response to accept it
request_timeout✘Duration::from_secs(30)std::time::DurationThe amount of seconds a request can be outstanding to a node before it's considered timed out
api_timeout✘Api::GetInfo: Duration::from_secs(2)),
Api::GetHealth: Duration::from_secs(2),
Api::GetPeers: Duration::from_secs(2),
Api::GetMilestone: Duration::from_secs(2),
Api::GetTips: Duration::from_secs(2),
Api::PostMessage: Duration::from_secs(2),
Api::PostMessageWithRemotePow: Duration::from_secs(30),
Api::GetOutput: Duration::from_secs(2)
HashMap<Api,
std::time::Duration>
The amount of milliseconds a request to a specific Api endpoint can be outstanding to a node before it's considered timed out.
local_pow✘TrueboolIf not defined it defaults to local PoW to offload node load times
tips_interval✘15u64Time interval during PoW when new tips get requested.
mqtt_broker_options✘True,
Duration::from_secs(30),
True
BrokerOptionsIf not defined the default values will be used
  • Note that there must be at least one node to build the instance successfully.

Return#

Finalize the builder with finish() will run the instance in the background. Users don’t need to worry about the return object handling.

On initialization#

On initialisation, call getNodeInfo API. Check the health of each node in the node list, and put healty nodes, matching the PoW settings and network in a synced nodelist.

Node metadataDescription
networkIf this parameter does not match the global builder parameter, don't add it to the synced nodelist.
powIf the global local_pow parameter is set to false, then put only nodes with the PoW feature in the synced nodelist.

Sync Process#

When a Client instance (The instance which is used for calling the client APIs) is built, the status of each node listed is checked. If the returned status of the node information is healthy, which means the node is synced, then this node will be pushed into a synced_nodes list. The rust-like pseudo code of synced_nodes construction process follows. The process of syncing a node is repeated every 60 seconds or at the interval specified in the node_sync_interval argument of the initializer if set.

synced_nodes = Vec::new()for node in node_pool_urls{   status = Client.get_info(node).await?;   if status == healthy{      synced_nodes.push(node)   }}

General high level API

Here is the high level abstraction API collection with sensible default values for users easy to use.

message()#

A generic send function for easily sending a message.

Parameters#

ParameterRequiredDefaultTypeDefinition
seed✘NoneSeedThe seed of the account we are going to spend, only needed for transactions
account_index✘0usizeThe account index, responsible for the value ✘ in the Bip32Path m/44'/4218'/✘'/0'/0'.
initial_address_index✘0usizeThe index from where to start looking for balance. Responsible for the value ✘ in the Bip32Path m/44'/4218'/0'/0'/✘'.
input✘NoneUtxoInputUsers can manually select their UtxoInputs instead of having automatically selected inputs.
input_range✘0..100RangeCustom range to search for the input addresses if custom inputs are provided.
output✘Noneaddress: &[String],
amount: u64
Address to send to and amount to send. Address needs to be Bech32 encoded.
output_hex✘Noneaddress: &str,
amount: u64
Address to send to and amount to send. Address needs to be hex encoded.
index✘None&[u8] / &strAn optional indexation key for an indexation payload. 1-64 bytes long.
data✘NoneVec<u8>Optional data for the indexation payload.
parents✘NoneMessageId1-8 optional parents MessageId to be used.

Depending on the provided values this function will create a message with:

  • no payload
  • an indexation payload
  • a transaction payload
  • a transaction payload containing an indexation payload

Return#

The Message object we build.

Implementation Details#

  • Validate inputs, such as address and seed to check if they are correct.
  • Check if account balance is bigger or equal to the value using method similar to get_balance();
  • Build and validate the message with signed transaction payload accordingly;
  • Get tips using get_tips();
  • Perform proof-of-work locally (if not set to remote);
  • Send the message using post_messages();

get_message()#

(GET /api/v1/messages)

Endpoint collection all about GET messages.

Parameters#

ParameterRequiredTypeDefinition
message_idβœ”MessageIdThe identifier of message.
indexβœ”&[u8] / &strAn indexation key.

Returns#

Depend on the final calling method, users could get different results they need:

  • metadata(&MessageId): Return MessageMetadata of the message.
  • data(&MessageId): Return a Message object.
  • raw(&MessageId): Return the raw data of given message.
  • children(&MessageId): Return the list of MessageIds that reference a message by its identifier.
  • index(&[u8] | &str) : Return the list of MessageIds that have this str as indexation key

find_messages()#

Find all messages by provided message IDs.

Parameters#

ParameterRequiredTypeDefinition
indexation_keys✘[&[u8] / &str]The index key of the indexation payload.
message_ids✘[MessageId]The identifier of message.

Returns#

A vector of Message Object.

get_unspent_address()#

Return a valid unspent public Bech32 encoded address.

Parameters#

ParameterRequiredDefaultTypeDefinition
seedβœ”-SeedThe seed we want to use.
account_index✘0usizeThe account index, responsible for the value ✘ in the Bip32Path m/44'/4218'/✘'/0'/0'.
initial_address_index✘0usizeStart index of the addresses to search. Responsible for the value ✘ in the Bip32Path m/44'/4218'/0'/0'/✘'.

Return#

Return a tuple with type of (String, usize) as the address and corresponding index in the account.

Implementation Details#

Following are the steps for implementing this method:

  • Start generating addresses with given account index and starting index. We will have a default gap limit of 20 at a time;
  • Check for balances on the generated addresses using find_outputs() and keep track of the positive balances;
  • Repeat the above step till there's an unspent address found;
  • Return the address with corresponding index on the wallet chain;

get_addresses()#

Return a list of addresses from the seed regardless of their validity.

Parameters#

ParameterRequiredDefaultTypeDefinition
seedβœ”NoneSeedThe seed we want to search for.
account_index✘0usizeThe account index, responsible for the value ✘ in the Bip32Path m/44'/4218'/✘'/0'/0'.
range✘Nonestd::ops::RangeRange indices of the addresses we want to search for. Default is (0..20)
get_all✘✘✘Get public and change addresses. Will return Vec<([String], bool)>, where the bool is indicating whether it's a change address

Return#

Vec<[String]>, with the public addresses

get_balance()#

Return the balance for a provided seed and its wallet account index.

Parameters#

ParameterRequiredDefaultTypeDefinition
seedβœ”-SeedThe seed we want to search for.
account_index✘0usizeThe account index, responsible for the value ✘ in the Bip32Path m/44'/4218'/✘'/0'/0'.
initial_address_index✘0usizeStart index from which to generate addresses. Default is 0. Responsible for the value ✘ in the Bip32Path m/44'/4218'/0'/0'/✘'.
gap_limit✘20usizeThe gap limit specifies how many addresses will be checked each round. If gap_limit amount of addresses in a row have no balance the function will return.

Return#

Total account balance.

Implementation Details#

Following are the steps for implementing this method:

  • Start generating addresses with given wallet account index and starting index. We will have a default gap limit of 20 at a time;
  • Check for balances on the generated addresses using find_outputs() and keep track of the positive balances;
  • Repeat the above step till an address of zero balance is found;
  • Accumulate the positive balances and return the result.

get_address_balances()#

Return the balance in iota for the given addresses; No seed or security level needed to do this since we are only checking and already know the addresses.

Parameters#

ParameterRequiredTypeDefinition
addressesβœ”[[String]]List of Bech32 encoded addresses.

Return#

A list of tuples with value of AddressBalancePair. The usize is the balance of the address accordingly.

Implementation details:#

Following are the steps for implementing this method:

  • Validate address semantics;
  • Get latest balance for the provided address using find_outputs() with addresses as parameter;
  • Return the list of Output which contains corresponding pairs of address and balance.

generate_mnemonic()#

Returns a random generated Bip39 mnemonic with the English word list.

Return#

Parsed [String].

mnemonic_to_hex_seed(mnemonic)#

Returns the seed hex encoded.

Parameters#

ParameterRequiredTypeDefinition
mnemonicβœ”[String]Bip39 mnemonic with words from the English word list.

Return#

Parsed [String].

bech32_to_hex()#

Returns a parsed hex String from bech32.

Parameters#

ParameterRequiredTypeDefinition
bech32βœ”[String]Bech32 encoded address.

Return#

Parsed [String].

hex_to_bech32()#

Returns a parsed bech32 String from hex.

Parameters#

ParameterRequiredTypeDefinition
hexβœ”[String]Hex encoded address.
bech32_hrpβœ”[Option
]
Optional bech32 hrp.

Return#

Parsed [String].

parse_bech32_address()#

Returns a valid Address parsed from a String.

Parameters#

ParameterRequiredTypeDefinition
addressβœ”[String]Bech32 encoded address.

Return#

Parsed Address.

is_address_valid()#

Parameters#

ParameterRequiredTypeDefinition
addressβœ”[String]Bech32 encoded address.

Return#

A boolean showing if the address is valid.

subscriber()#

Subscribe to a node event Topic (MQTT)

Required: one of

  • topic(): Add a new Topic to the list.

  • topics(): Add a vector of Topic to the list.

  • subscribe(): Subscribe to the given topics with the callback, which will be called every time when the topic is detected.

  • unsubscribe(): Unsubscribes from all subscriptions.

  • disconnect(): Disconnects the broker. This will clear the stored topic handlers and close the MQTT connection.

Returns#

Nothing apart from a Ok(()) result if successful

retry()#

Retries (promotes or reattaches) a message for provided MessageId if the node suggests it. The need to use this function should be low, because the confirmation throughput of the node is expected to be quite high.

Parameters#

ParameterRequiredTypeDefinition
message_idβœ”MessageIdThe identifier of message.

Returns:#

A tuple with the newly promoted or reattached (MessageId, Message).

Implementation Details#

Following are the steps for implementing this method:

  • Only unconfirmed messages should be allowed to retry. The method should validate the confirmation state of the provided messages. If a message id of a confirmed message is provided, the method should error out;
  • The method should also validate if a retry is necessary. This can be done by leveraging the /messages/{messageId}/metadata endpoint (already available through get_message). See this implementation for reference;
  • Use reattach or promote accordingly.

retry_until_included()#

Retries (promotes or reattaches) a message for provided MessageId until it's included (referenced by a milestone). Default interval is 5 seconds and max attempts is 10. The need to use this function should be low, because the confirmation throughput of the node is expected to be quite high.

Parameters#

ParameterRequiredTypeDefinition
message_idβœ”[&MessageId]The identifier of message.
interval✘Option<u64>The interval in which we retry the message.
max_attempts✘Option<u64>The maximum of attempts we retry the message.

Returns:#

An array of tuples with the newly reattached (MessageId, Message).

consolidate_funds()#

Function to consolidate all funds from a range of addresses to the address with the lowest index in that range

Parameters#

| Parameter | Type | Definition | | ----------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | seed | Seed | The seed we want to search for. | | account_index | usize | The account index, responsible for the value ✘ in the Bip32Path m/44'/4218'/✘'/0'/0'. | | address_range | Range | Range from which to generate public and internal addresses from which to consolidate the funds. Responsible for the value ✘ in the Bip32Path m/44'/4218'/0'/0'/✘'. |

Returns:#

The address to which the funds got consolidated.

reattach()#

Depends on find_messages, get_message and post_message.

Reattaches a message. The method should validate if a reattachment is necessary through get_message. If not, the method should error out and should not allow unnecessary reattachments.

Parameters#

ParameterRequiredTypeDefinition
message_idβœ”MessageIdThe identifier of message.

Returns#

A tuple with the newly reattached (MessageId, Message).

promote()#

Depends on find_messages, get_message and post_message.

Promotes a message. The method should validate if a promotion is necessary through get_message. If not, the method should error out and should not allow unnecessary promotions.

Parameters#

ParameterRequiredTypeDefinition
message_idβœ”MessageIdThe identifier of message.

Returns#

A tuple with the newly promoted (MessageId, Message).

Full node API

Full node API of Bee and HORNET will still be public. Users who know these relative low level Restful API can still call them directly if they are confident and think it’s good for them. Note that both Bee and HORNET haven't finalized their APIs either. Following items and signatures might change later.

get_health()#

(GET /health)

Returns the health of the node, which can be used for load-balancing or uptime monitoring.

Parameters#

None

Returns#

Boolean to indicate if node is healthy.

get_peers()#

(GET /peers)

Get information about the peers of the node.

Parameters#

None

Returns#

pub struct PeerDto {    pub id: String,    #[serde(rename = "multiAddresses")]    pub multi_addresses: Vec<String/>,    pub alias: Option<String/>,    pub relation: RelationDto,    pub connected: bool,    pub gossip: Option<GossipDto/>,}

get_info()#

(GET /api/v1/info)

Returns information about the node.

Parameters#

None

Returns#

A Response Object similar to this:

pub struct NodeInfoWrapper {    pub nodeinfo: NodeInfo,    pub url: String,}pub struct NodeInfo {    pub name: String,    pub version: String,    pub is_healthy: bool,    pub network_id: String,    pub latest_milestone_index: usize,    pub min_pow_score: f64,    pub messages_per_second: f64,    pub referenced_messages_per_second: f64,    pub referenced_rate: f64,    pub latest_milestone_timestamp: u64,    pub confirmed_milestone_index: usize,    pub pruning_index: usize,    pub features: Vec<String/>,}

get_tips()#

(GET /tips)

Returns two non-lazy tips. In case the node can only provide one tip, tip1 and tip2 are identical.

Parameters#

None

Returns#

A tuple with two MessageId:

(MessageId, MessageId)

post_message()#

(POST /message)

Submit a message. The node takes care of missing fields and tries to build the message. On success, the message will be stored in the Tangle. This endpoint will return the identifier of the message.

Parameters#

ParameterRequiredTypeDefinition
messageβœ”MessageThe message object.

Returns#

The MessageId of the message object.

get_output()#

(GET /outputs)

Get the producer of the output, the corresponding address, amount and spend status of an output. This information can only be retrieved for outputs which are part of a confirmed transaction.

Parameters#

ParameterRequiredTypeDefinition
output_idβœ”UtxoInputIdentifier of the output.

Returns#

An OutputMetadata that contains various information about the output.

get_address()#

(GET /addresses)

Parameters#

ParameterRequiredTypeDefinition
addressβœ”[String]The address to search for.

Returns#

Depend on the final calling method, users could get different outputs they need:

  • balance(): Return confirmed balance of the address.
  • outputs([options]): Return UtxoInput array (transaction IDs with corresponding output index).

find_outputs()#

Find all outputs based on the requests criteria.

Parameters#

ParameterRequiredTypeDefinition
output_id✘[UtxoInput]The identifier of output.
addresses✘[[String]]The Bech32 encoded address.

Returns#

A vector of OutputMetadata.

get_milestone()#

(GET /milestones)

Get the milestone by the given index.

Parameters#

ParameterRequiredTypeDefinition
indexβœ”u32Index of the milestone.

Returns#

An Milestone object.

get_milestone_utxo_changes()#

(GET /milestones/{}/utxo-changes)

Get all UTXO changes of a given milestone.

Parameters#

ParameterRequiredTypeDefinition
indexβœ”u32Index of the milestone.

Returns#

MilestoneUTXOChanges {    index: 1,    created_outputs: [],    consumed_outputs: [],}

get_receipts()#

(GET /receipts)

Get all receipts.

Returns#

Vec<ReceiptDto/>

get_receipts_migrated_at()#

(GET /receipts/{migratedAt})

Get all receipts for a given milestone index.

Returns#

Vec<ReceiptDto/>

get_treasury()#

(GET /treasury)

Get the treasury amount.

Returns#

pub struct TreasuryResponse {    #[serde(rename = "milestoneId")]    milestone_id: String,    amount: u64,}

get_included_message()#

(GET /transactions/{transactionId}/included-message)

Get the included message of the transaction.

Parameters#

ParameterRequiredTypeDefinition
transaction_idβœ”[TransactionId]The id of the transaction.

Returns#

struct Message {    parents: Vec<MessageId/>,    payload: Option<Payload/>,    nonce: u64,}

Objects

Here are the objects used in the API above. They aim to provide a secure way to handle certain data structures specified in the Iota stack.

MessageId#

MessageId is a 32 bytes array which can represent as hex string.

struct MessageId([u8; MESSAGE_ID_LENGTH]);

Seed#

pub enum Seed {    /// Ed25519 variant    Ed25519(Ed25519Seed)}

An IOTA seed that inner structure is omitted. Users can create this type by passing a String. It will verify and return an error if it’s not valid. |

Message#

The message object returned by various functions; based on the RFC for the Message object. Here's the brief overview of each components in Message type would look like:

struct Message {    parents: Vec<MessageId/>,    payload: Option<Payload/>,    nonce: u64,}
enum Payload {    Transaction(Box<Transaction/>),    Milestone(Box<Milestone/>),    Indexation(Box<Indexation/>),}
struct Transaction {    pub essence: TransactionPayloadEssence,    pub unlock_blocks: Vec<UnlockBlock/>,}
struct Milestone {    essence: MilestoneEssence,    signatures: Vec<Box<[u8]>>,}
struct Indexation {    index: String,    data: Box<[u8]>,}
struct TransactionPayloadEssence {    pub(crate) inputs: Box<[Input]>,    pub(crate) outputs: Box<[Output]>,    pub(crate) payload: Option<Payload/>,}
enum Input {    UTXO(UtxoInput(OutputId)),}
struct OutputId {    transaction_id: TransactionId,    index: u16,}
enum Output {    SignatureLockedSingle(SignatureLockedSingleOutput),}
struct SignatureLockedSingleOutput {    address: Address,    amount: u64,}
enum UnlockBlock {    Signature(SignatureUnlock),    Reference(ReferenceUnlock),}
enum SignatureUnlock {    Ed25519(Ed25519Signature),}
struct Ed25519Signature {    public_key: [u8; 32],    signature: Box<[u8]>,}
struct ReferenceUnlock(u16);

MessageMetadata#

pub struct MessageMetadata {    /// Message ID    pub message_id: String,    /// Message IDs of parents    pub parents: Vec<String/>,    /// Solid status    pub is_solid: bool,    /// Should promote    pub should_promote: Option<bool/>,    /// Should reattach    pub should_reattach: Option<bool/>,    /// Referenced by milestone index    pub referenced_by_milestone_index: Option<u32>,    /// Ledger inclusion state    pub ledger_inclusion_state: Option<String/>,}

OutputMetadata#

The metadata of an output:

pub struct OutputMetadata {    /// Message ID of the output    pub message_id: Vec<u8>,    /// Transaction ID of the output    pub transaction_id: Vec<u8>,    /// Output index.    pub output_index: u16,    /// Spend status of the output    pub is_spent: bool,    /// Corresponding address    pub address: Address,    /// Balance amount    pub amount: u64,}

Address#

An Ed25519 address can be encoded in Bech32 or Hex, with Bech32 being preferred and also used in most functions.

pub enum Address {    Ed25519(Ed25519Address),}

AddressBalancePair#

pub struct AddressBalancePair {    /// Address, bech32 encoded    pub address: String,    /// Balance in the address    pub balance: u64,    /// If dust is allowed on the address    pub dust_allowed: bool,}

Milestone#

A milestone metadata.

pub struct MilestoneMetadata {    /// Milestone index    pub milestone_index: u32,    /// Milestone ID    pub message_id: String,    /// Timestamp    pub timestamp: u64,}

Api#

pub enum Api {    /// `get_health` API    GetHealth,    /// `get_info`API    GetInfo,    /// `get_tips` API    GetTips,    /// `post_message` API    PostMessage,    /// `post_message` API with remote pow    PostMessageWithRemotePow,    /// `get_output` API    GetOutput,    /// `get_milestone` API    GetMilestone,}

BrokerOptions#

pub struct BrokerOptions {    #[serde(default = "default_broker_automatic_disconnect", rename = "automaticDisconnect")]    pub(crate) automatic_disconnect: bool,    #[serde(default = "default_broker_timeout")]    pub(crate) timeout: std::time::Duration,    #[serde(rename = "maxReconnectionAttempts", default)]    pub(crate) max_reconnection_attempts: usize,}

Topic#

A string with the exact MQTT topic to monitor, can have one of the following variations:

milestones/latestmilestones/confirmed
messagesmessages/referencedmessages/indexation/{index}messages/{messageId}/metadatatransactions/{transactionId}/included-message
outputs/{outputId}
addresses/{address}/outputsaddresses/ed25519/{address}/outputs