Wallet Library Specifications
The wallet.rs
library is a stateful package with a standardized interface to build applications with IOTA value transactions. The package is compatible with different platforms such as web, desktop, and mobile.
The package introduces the concept of an account . An account is a reference to, or a label for, a seed. It has certain properties such as addresses and messages. An account also maintains various behaviours, including moving funds, looking for new messages, and making copies of message histories. Additionally, it provides a degree of financial privacy and thus does not incur any overhead.
A similar account package was used before but it became obsolete with the introduction of Ed25519 signatures. The previous account package was also limited to a single account, whereas the new package manages multiple accounts.
For IOTA, the motivation to use this package was to offer a simplified (stateful) approach to handle IOTA payments.
Seeds should be stored and managed separately in a secure enclave and should never leave the secure environment. Secure enclaves include software enclaves such as IOTAβs Rust-based Stronghold
library or hardware enclaves such as a Ledger Nano
.
The secure enclave should have the ability to generate addresses and sign messages upon receipt, and return the output in a new message. If the secure enclave is initialized with a pre-generated seed, the sender process should immediately remove the seed traces from memory.
The primary language is Rust. Therefore, you should follow the standard Rust naming conventions. For reference, all interfaces (types) use CamelCase while all function and variable names use snake_case.
Account configuration or initialization object. It should support parameters accepted by high level client libraries.
Property | Required | Type | Description |
---|
seed | β | string | BIP-39 mnemonic. When importing an account from Stronghold backup, the seed will not be required. |
id | β | string | SHA-256 hash of the first address on the seed (m/44'/0'/0'/0'/0'). Required for referencing a seed in Stronghold. The ID should be provided by Stronghold. |
index | β | number | Account index in BIP-44 derivation path |
alias | β | string | Account name. If not provided, Account + { index } should be used. When importing an account from Stronghold backup, the alias will be required from Stronghold. |
pow | β | local ,remote | Proof of work settings. Defaults to local . local PoW should be performed on device; remote PoW should be performed on the node. |
nodes | β | node[] | A list of nodes to connect to. |
quorum_size | β | number | If multiple nodes are provided, quorum size determines the number of nodes to query to check for quorum. |
quorum_threshold | β | number | Minimum number of nodes from the quorum pool that need to agree to consider a result true. |
network | β | mainnet |devnet | comnet | IOTA public network. |
type | β | default or ledger | Account type. Required for differentiating ledger vs non-ledger accounts. |
provider | β | string | Node URL. |
created_at | β | Date | Time of account creation |
messages | β | Message[] Messages associated with account. Accounts can be initialized with locally stored messages. | |
addresses | β | Address[] | Address history associated with the account. Accounts can be initialized with locally stored address history |
Property | Required | Type | Description |
---|
id | β | string | SHA-256 hash of the first address on the seed (m/44'/0'/0'/0/0). Required for referencing a seed in Stronghold. |
alias | β | string | Account name. |
created_at | β | number | Account creation time. |
last_synced_at | β | string | Time the account was last synced with the Tangle. |
sync() | β | function | Syncs account with the Tangle. |
reattach() | β | function | Reattaches unconfirmed transaction to the Tangle. |
total_balance() | β | function | Gets total account balance. |
available_balance() | β | function | Gets available account balance. |
set_alias() | β | function | Updates account name. |
list_messages() | β | function | Gets messages. |
list_received_messages() | β | function | Gets all received messages. |
list_sent_messages() | β | function | Gets all sent messages. |
list_failed_messages() | β | function | Gets all failed messages. |
list_unconfirmed_messages() | β | function | Gets all unconfirmed messages. |
get_message() | β | function | Gets message for providedID. |
list_addresses() | β | function | Gets all addresses. |
list_unspent_addresses() | β | function | Gets all unspent input addresses. |
generate_address() | β | function | Gets the latest unused address. |
Property | Required | Type | Description |
---|
deposit_address | β | Address | Deposit address. Only exposed on successful completion of account syncing process. |
send() | β | function | Send transaction method. Only exposed on successful completion of account syncing process. |
retry() | β | function | Rebroadcasts failed transaction. Only exposed on successful completion of account syncing process. |
Property | Required | Type | Description |
---|
accounts | β | Account[] | Account objects. |
add_account() | β | function | Adds a new account. |
remove_account() | β | function | Removes an account. |
sync_accounts() | β | function | Syncs all stored accounts with the Tangle. |
move() | β | function | Inititates an internal transaction between accounts. |
backup() | β | function | Creates a backup to a provided destination. |
import_accounts() | β | function | Imports backed up accounts. |
get_account() | β | function | Returns the account associated with the provided address. |
reattach() | β | function | Reattaches an unconfirmed transaction. |
Useful reference for address management in Hierarchical Deterministic (HD) wallets.
Property | Required | Type | Description |
---|
address | β | string | Address (Bech32) string. |
balance | β | number | Address balance. |
index | β | number | Address index. |
internal | β | boolean | Determines if an address is a public or an internal (change) address. See the concept of chain node for more details. |
checksum | β | string | Address checksum. |
Property | Required | Type | Description |
---|
url | β | string | Node URL. |
pow | β | boolean | Determines if the node accepts proof of work. |
username | β | string | Node username. Only required if node requires authorisation. |
password | β | string | Node password. Only required if node requires authorisation. |
network | β | mainnet | devnet | comnet | IOTA public network name. |
Property | Required | Type | Description |
---|
format(type: string):string | β | function | Transaction timestamp in various formats. For example: MM-DD-YYYY, DD MM YYYY hh:mm:ss. |
Transfer object required for creating a transaction. It allows end-users to specify the transaction amount and recipient address.
Currently, it is not possible to send multiple payloads as part of the message. That is why the tag property is omitted from this interface. You can find more details in this GitHub pull request.
Property | Required | Type | Description |
---|
amount | β | number | Transfer amount. |
address | β | string | Transfer address. |
indexation_key | β | Indexation Payload | (Optional) Indexation payload. |
Property | Required | Type | Description |
---|
with_denomination():string | β | function | Transaction amount with unit. |
without_denomination():number | β | function | Transaction amount without unit. |
Property | Required | Type | Description |
---|
type | β | number | Input type. Defaults to 0 . |
id | β | string | BLAKE2b-256 hash of the transaction. |
output_index | β | number | Index of the output on the referenced transaction. |
Property | Required | Type | Description |
---|
type | β | number | Set to value 0 to denote an Ed25519 address. |
address | β | string | If type is set to 0 , it should contain an Ed25519 address. |
Property | Required | Type | Description |
---|
type | β | number | Output type. Defaults to 0 . |
address | β | OutputAddress | Output address. |
amount | β | number | Amount of tokens to deposit. |
Property | Required | Type | Description |
---|
type | β | number | Set to 2 to denote a unsigned data payload. |
data | β | string | Data of unsigned payload. |
Property | Required | Type | Description |
---|
type | β | number | Set to 3 to denote a signed data payload. |
data | β | string | Data of signed data payload. |
public_key | β | string | Ed25519 public key used to verify the signature. |
signature | β | string | Signature of signing data. |
Property | Required | Type | Description |
---|
index | β | string | Indexation key. |
data | β | string | Indexation data. |
Property | Required | Type | Description |
---|
type | β | number | Transaction type. Defaults to 0. |
inputs_count | β | number | Amount of inputs proceeding. |
inputs | β | Input[] | Transaction inputs. |
outputs_count | β | number | Amount of outputs proceeding. |
outputs | β | Output[] | Output address. |
payload_length | β | number | Length of optional payload. |
payload | β | UnsignedDataPayload | SignedDataPayload | IndexationPayload | Payload containing data. As multiple payloads are not yet supported, only unsigned data payload should be used. |
Property | Required | Type | Description |
---|
type | β | number | Set to value 1 to denote an Ed25519 signature. |
public_key | β | number | Public key of the Ed25519 keypair which is used to verify the signature. |
signature | β | string | Signature signing the serialized unsigned transaction. |
Property | Required | Type | Description |
---|
type | β | number | Set to value 0 to denote a signature unlock block. |
signature | β | Ed25519Signature | An unlock block containing signature(s) unlocking input(s). |
Property | Required | Type | Description |
---|
type | β | number | Set to value 1 to denote a reference unlock block. |
reference | β | number | Index of a previous unlock block. |
Property | Required | Type | Description | |
---|
type | β | number | Payload type. Defaults to 0 . | |
transaction | β | UnsignedTransaction | Essence data making up a transaction by defining its inputs and outputs and an optional payload. | |
unblock_blocks_count | β | number | Number of inputs specifed. | |
unblock_blocks | β | SignatureUnblockBlock | ReferenceUnblockBlock | Holds the unlock blocks unlocking inputs within an Unsigned Transaction |
Property | Required | Type | Description |
---|
version | β | number | Message version. Defaults to 1 . |
parents | β | string[] | Message ids this message references. |
payload_length | β | number | Length of the payload. |
payload | β | SignedTransactionPayload | |
UnsignedDataPayload | | | |
SignedDataPayload | Transaction amount (exposed as a custom type with additional methods). | | |
timestamp | β | Timestamp | Transaction timestamp (exposed as a custom type with additional methods). |
nonce | β | string | Transaction nonce. |
confirmed | β | boolean | Determines if the transaction is confirmed. |
broadcasted | β | boolean | Determines if the transaction was broadcasted to the network. This will be true if the transaction was fetched from the network or if the transaction was successfully broadcasted from the client itself. This property may only be required for clients with persistent storage. |
incoming | β | boolean | Determines if the message is an incoming transaction or not. |
value | β | number | Message transfer value. |
Property | Required | Type | Description |
---|
get(key: string):Account | β | function | Gets the account object for provided account name or ID. |
getAll(): Account[] | β | function | Gets all account objects from storage. |
set(key: string, payload: string):void | β | function | Stores account in storage. |
remove(key: string): void | β | function | Removes account from storage. |
Using Stronghold for storage is currently under research/development.
You should consider multiple storage options should for managing data that requires persistence:
- You can use a simple key-value storage could be leveraged for wallet basic metadata, such as user settings or theming options.
- For transactions and address data management you could use a relational database such as SQLite.
What follows is an Entity Relationship Diagram (ERD) that shows the logical representation of the data. An account is the basic entity in this database design. It has a one-to-many relationship with addresses. This means an account could have multiple addresses , but an address can only belong to a single account. An account has a many-to-many relationship with transactions . Therefore, an account could have multiple transactions, but it is possible that a transaction belongs to multiple accounts. To accommodate this behaviour, an additional table that stores account IDs against transaction IDs (hashes) was added.
A storage adapter is required by the Rust layer to handle all the storage operations (read/write) from that layer. A generic storage adapter is defined in the storage adapter section.
The package should have a default opinionated storage mechanism but should also provide the ability for users to override the storage by specifying an adapter. As a default option, a relational database such as SQLite can be used.
See storage adapter for adapter interface.
Initializes account
There are several scenarios in which an account can be initialized:
- Seed generated outside the Stronghold: In this case, the account should be initialized with a seed. It should communicate with the Stronghold using the
import_accounts
method and should expect an ID as a response. - Seed generated inside the Stronghold: In this case, the account should be initialized without a seed. It should communicate with the Stronghold using its
create_account
method and should expect an βidβ in response; - Importing accounts from Stronghold backup: In this case, the account should receive all the initialization properties from the
Stronghold
. Please note that during backup, these configuration settings should be passed to the Stronghold
. See import_accounts().
The following should be considered when initializing an account:
- An account should never be initialized directly. The only way an account can be initialized is through the add_account() method.
- An account should always be initialized after a successful response from the
Stronghold
. If the Stronghold
fails to create an account , the account initialization should error out. If the Stronghold
successfully creates an account , the account should be stored in the persistent storage. Upon a successful store operation, the user should be returned an account object. - If a provider property is not passed, a random node should be selected from the nodes property.
- If a type property is not passed, default should be used as an account type.
- quorum_size and quorum_threshold should be validated. For example, quorum_size should not be greater than the number of nodes provided by the user.
- The nodes property should validate and remove duplicate node URLs.
- All the properties of the returned account object should be read-only. It should not be possible to manipulate them directly.
Name | Required | Type | Description |
---|
config | β | AccountConfig | Initialization method receives a configuration object. |
Name | Type | Description |
---|
account | Account | Account instance. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Sync addresses with the Tangle. The method ensures that the wallet's local state contains all used addresses and an unused address.
The following should be considered when implementing this method:
- The updated address history should not be written down in the database/persistent storage. Instead, the method should only return the updated address history (with transaction hashes). This ensures that there are no partial writes to the database.
- To sync addresses for an account from scratch, gap_limit = 10 should be sent as arguments.
- To sync addresses from the latest address, gap_limit = 1 should be sent as arguments.
Name | Required | Type | Description |
---|
gap_limit | β | number | Number of address indexes that are generated. |
Name | Type | Description |
---|
addresses | Address[] | Address history up to latest unused address. |
ids | string[] | Message IDs associated with the addresses. |
Sync messages with the Tangle. The method should ensure that the wallet's local state has all messages associated with the address history.
The following should be considered when implementing this method:
- The updated message history should not be written down in the database/persistent storage. Instead, the method should only return the updated message history (with message IDs).
- This method should check if there are any local messages (with βbroadcasted: falseβ) matching the messages fetched from the network. If there are such messages, their βbroadcastedβ property should be set to true.
- For newly-confirmed messages, the method should ensure that it updates the βconfirmedβ property of all its reattachments.
Name | Required | Type | Description |
---|
ids | β | string[] | Message IDs. New message IDs should be calculated by running a difference of local message IDs with latest message IDs on the Tangle. |
Name | Type | Description |
---|
messages | Message[] | Message history |
Name | Description |
---|
Access modifiers | Private |
Errors | List of error messages [TBD] |
Required client library methods | find_messages() |
Select inputs for funds transfer.
This method should only be used internally by send(). The input selection method should also ensure that the recipient address doesn't match the remainder address.
See Input Selection Process for implementation details.
Name | Required | Type | Description |
---|
threshold | β | number | Amount user wants to spend. |
address | β | string | Recipient address. |
Name | Type | Description |
---|
inputs | Address[] | Selected Inputs |
remainder | Address | Remainder address object. Empty or null if there's no need for a remainder |
Name | Description |
---|
Access modifiers | Private |
Errors | List of error messages [TBD] |
Required client library methods | None |
Sends a message to the Tangle.
This method should only be used after a successful response from sync().
Currently, it is not possible to send multiple payloads.
If you want to send a value transaction, please follow this process:
- Ensure amount is not set to zero.
- Ensure amount does not exceed the total balance.
- Ensure recipient address has correct checksum.
- Validate data property semantics and size.
- Select inputs by using select_inputs().
- Pass the serialized unsigned transaction to the
Stronghold
for signing with its βsignTransactionβ method. - Perform proof-of-work. The pow property in the account object should determine if the proof of work should be offloaded.
- Once proof-of-work is successfully performed, the message should be validated and stored in the persistent storage.
- After persisting the transaction, the transaction should be broadcast to the network.
- In the event of a broadcast error, there should be three attempts for automatic rebroadcasting. If all attempts fail, the send process should terminate, and it should be left to the user to retry the failed message. For failed messages, the βbroadcastedβ property in the transaction objects should be set to false.
Name | Required | Type | Description |
---|
transfer | β | Transfer | Transfer object. |
Name | Type | Description |
---|
message | Message | Newly made message. |
Name | Description |
---|
Access modifiers | Private |
Errors | List of error messages [TBD] |
Required client library methods | find_messages() | send() |
Rebroadcasts failed message.
This method should only be used after a successful response from sync().
If you want to retry broadcasting a failed message, you can use the following process:
- Get the message by using get_message().
- Rebroadcast the message.
- Update the account in persistent storage.
Name | Required | Type | Description |
---|
id | β | string | Message ID |
Name | Type | Description |
---|
message | Message | Newly made message. |
Name | Description |
---|
Access modifiers | Private |
Errors | List of error messages [TBD] |
Required client library methods | post_message() |
Syncs an account with the Tangle. The account syncing process should ensure that the latest metadata (balance, messages) associated with an account is retrieved from the Tangle and stored locally.
Please note that it is a proposed design decision to enforce account syncing before every send. An alternative way would be to have the send method always exposed and internally ensuring that the account is synced before every message.
If you want to sync an account, you can use the following process:
- Sync addresses using check_for_new_used_addresses().
- Sync messages using sync_addresses_and_messages().
- Store updated addresses and messages information in persistent storage (if not explicitly set otherwise by the user).
Name | Required | Type | Description |
---|
index | β | number | Address index. By default the number of addresses stored for this account should be used as an index. |
gap_limit | β | number | Number of address indexes that are generated. |
skip_persistence | β | boolean | Skips write to the database. This will be useful if a user wants to scan the Tangle for further addresses to find balance. You can find more details in the snapshot transition feature provided by Trinity. |
Reattaches unconfirmed message to the Tangle.
The following should be considered when implementing this method:
- Only an unconfirmed message can be reattached. The method should validate the confirmation state of the provided transaction. If a confirmed message ID is provided, the method should throw an error.
- The method should also validate if a reattachment is necessary, by checking if the message falls below max depth. The criteria for whether the message has fallen below max depth is determined through its timestamp. If 11 minutes have passed since the timestamp of the most recent reattachment, the message can be reattached. See this implementation for reference.
- Once reattached, the message should be stored in the persistent storage.
- If the message was reattached via polling, a reattachment event should be emitted to notify all subscribers.
Name | Required | Type | Description |
---|
id | β | string | Message ID. |
Name | Type | Description |
---|
message | Message | Newly reattached message. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | reattach() |
Gets total account balance.
The total balance should be read directly from local storage. To read the latest account balance from the network, sync() should be used first.
Type | Description |
---|
Value | Account total balance. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets available account balance. The available account balance is the amount users are allowed to spend. It should subtract the pending balance from the total balance.
For example, if a user with 50i total account balance has made a transaction spending 30i, the available balance should be 20i (i.e. 50i - 30i).
The available balance should be read directly from local storage. If you want to read the latest account balance from the network, you should use sync() first.
Type | Description |
---|
Value | The accounts available balance. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Updates an account's alias/name.
Name | Required | Type | Description |
---|
alias | β | string | New account name. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets messages. Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.
Name | Required | Type | Description |
---|
count | β | number | Number of (most recent) messages. |
from | β | number | Subset of messages. For example: count = 10, from = 5, it should return ten messages skipping the most recent five messages. |
Name | Type | Description |
---|
messages | Message[] | All messages. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all received messages.
Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.
Name | Required | Type | Description |
---|
count | β | number | Number of most recent received messages. |
from | β | number | Subset of received messages. |
Name | Type | Description |
---|
messages | Message[] | All received messages. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all sent messages.
Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.
Name | Required | Type | Description |
---|
count | β | number | Number of (most recent) sent messages. |
from | β | number | Subset of sent messages. |
Name | Type | Description |
---|
messages | Message[] | All sent messages. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all failed (broadcasted = false) messages. Messages should be read directly from local storage.
Name | Required | Type | Description |
---|
count | β | number | Number of (most recent) failed messages. |
from | β | number | Subset of failed messages. |
Name | Type | Description |
---|
messages | Message[] | All failed messages. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all unconfirmed (confirmed = false) messages. Messages should be read directly from local storage.
Name | Required | Type | Description |
---|
count | β | number | Number of (most recent) unconfirmed messages. |
from | β | number | Subset of unconfirmed messages. |
Name | Type | Description |
---|
messages | Message[] | All unconfirmed messages. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets message for provided ID.
Messages should be read directly from local storage. To ensure the local database is updated with the latest messages, you should use sync() first.
Name | Required | Type | Description |
---|
id | β | string | Message ID. |
Name | Type | Description |
---|
message | Message | Message object. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all addresses.
Name | Type | Description |
---|
addresses | Address[] | All addresses. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets all unspent input addresses
Name | Type | Description |
---|
addresses | Address[] | All unspent input addresses. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Gets the latest unused address.
Name | Type | Description |
---|
address | Address | A new address object. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
An account manager class should be publicly available for users. With the account manager, the user can create, update, delete or manage multiple accounts. The implementation details of a specific account should be abstracted using this account manager wrapper.
Initializes the account manager. Account manager initialization should validate the adapter object semantics and return an AccountManager instance.
Name | Required | Type | Description |
---|
adapter | β | Adapter | Initialisation method receives an optional storage adapter. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Adds new account
See account initialisation for detailed implementation guidelines.
Name | Required | Type | Description |
---|
config | β | AccountConfig | Account configuration object. |
Name | Type | Description |
---|
accounts | Account | Newly created account. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Removes an account.
The following should be considered when removing an account:
- An account should first be removed from the
Stronghold
using its removeAccount method. - Once the account references have been removed from the
Stronghold
, the account should be deleted from the persistent storage.
Name | Required | Type | Description |
---|
identifier | β | { address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number } | Identifier. Could be one of address, alias, ID or index. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Syncs all stored accounts with the Tangle. Syncing should get the latest balance for all accounts and should find any new messages associated with the stored account.
See Accounts Syncing Process for further details.
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | sync() |
Moves funds from one account to another. This method should use the send() method from the sender account and initiate a message to the receiver account.
Name | Required | Type | Description |
---|
from | β | { address: <string> } | { alias: <string> } | { ID: <number>| { index: <number> } | Identifier. Could be one of address, alias, ID or index. |
to | β | { address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number> } | Identifier. Could be one of address, alias, ID or index. |
amount | β | number | Transaction amount. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Safely creates a backup of the accounts to a destination. The file could simply be JSON containing the address & transaction histories for accounts.
This method should provide the Stronghold
instance with the metadata of all accounts.
Name | Required | Type | Description |
---|
destination | β | string | Path where the backup should be stored. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Import (backed up) accounts.
The implementation details are not finalized.
Name | Required | Type | Description |
---|
accounts | β | Account[] | Account object. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Returns the account associated with the provided identifier.
Name | Required | Type | Description |
---|
identifier | β | { address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number> } | Identifier. Could be one of address, alias, ID or index. |
Name | Type | Description |
---|
account | Account | Account associated with identifier. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Reattaches an unconfirmed message.
See reattach() method for implementation details. This method is a wrapper method provided for convenience. A user could directly access the reattach() method on an account object.
Name | Required | Type | Description |
---|
identifier | β | { address: <string> } | { alias: <string> } | { ID: <number> } | { index: <number } | Identifier. Could be one of address, alias, ID or index. |
id | β | string | Message ID. |
Name | Type | Description |
---|
message | Message | Newly reattached message. |
Name | Description |
---|
Access modifiers | Public |
Errors | List of error messages [TBD] |
Required client library methods | None |
Events can have two categories:
- Reactive messages emitted from the node software whenever the state on the node changes. For example, emitting new messages received by the node. Clients (Wallet) can subscribe to these events to get notified if any relevant change occurs on the node. For further details, please visit the Firefly GitHub repository.
- Messages emitted from the wallet library whenever there are any important state changes. Please note that in cases where a user triggered action leads to a state change, the messages will not be emitted. For example, if a user explicitly triggers a sync() action leading to a state change, an explicit event is not necessary.
On every update sent from the node software via an event, the wallet library should update internal (persistent) storage and should also emit events via category 2 events.
Event | Returned Data |
---|
< Address : Balance> | Index 1: Address | Index 2: New balance on the address |
Event | Returned Data |
---|
<Address : Message> | Index 1: Address | Index 2: Id of the new message |
Event | Returned Data | |
---|
<MessageId> | Index 1: Message Id | Index 2: Confirmation state |
They could be triggered via events from category 1 or through polling.
Event | Returned Data |
---|
balances | [{ accountId, address, balance }] |
Event | Returned Data |
---|
messages | [{ accountId, messages }] |
Event | Returned Data |
---|
confirmations | [{ accountId, messages }] |
Event | Returned Data |
---|
reattachments | [{ accountId, messages }] |
Event | Returned Data |
---|
broadcasts | [{ accountId, messages }] |
Event | Returned Data |
---|
error | { type, error } |
To maintain the financial privacy of wallet users, you should enforce strategies in the application/wallet that will guarantee a certain level of anonymity:
- The wallet should only use a single address per message. If an address has already been used in a message, it should not be used as a deposit address. Instead, a new address should be generated.- The input selection strategy should expose as little information as possible. Please see the input selection process for further details.
Some other privacy enhancing techniques can be found in this document.
The goal of input selection is to avoid remainder addresses. The remainder output leaves a clue to the user's future spends. There should be a standardized input selection strategy used by the wallet.
The steps for input selection are as follows:
- Try to select an input with an exact match. For example, if a user intends to spend X iotas, the wallet should try to find an address that has X iotas as available balance.
- If the previous step fails, try to select a combination of inputs that satisfy the amount leaving no change. For example, consider a scenario where the wallet with account name Foo has three addresses A, B and C with 10, 20 and 50 IOTA respectively. If a user intends to spend X = 30 IOTA, the application should search for an exact match (step no. 1). In this case, no address balance matches X. Therefore, the wallet should search for a subset of addresses with an accumulated balance of X. In this scenario, A and B.
- If both the previous steps fail, the wallet should select a combination of inputs that produce the minimum remainder.
A reference implementation of different input selection algorithms for Bitcoin can be found in this project.
The implementation of step no. 2 is also quite similar to the subset sum problem. Given a total and a set of non-negative numbers (inputs), we need to determine if there is a subset which adds up to the total.
The account syncing process should detect all used accounts on a seed with their corresponding address and message history. Once, all accounts and histories are detected, the wallet should accumulate the total balance. The syncing process should work as follows:
- Start with the account at index 0, generate gap limit number of addresses. This defaults to 20.
- Check for messages and balances on the generated addresses.
- If there are no messages and balances of 0 on all addresses, the process for generating addresses and finding messages and balances should be stopped.
- If any address has balance or associated messages, generate gap limit number of addresses from the index of the last address with messages or balance.
- Steps (1-4) should also be performed for the account at index 1. The general idea is that n + 1 accounts should be checked if account n has any messages or balance.
Treat accounts like addresses. Only allow 1 latest unused account.
Scenario 1: The wallet message and address history stored in Stronghold backup
- Start syncing from the latest address index stored in the Stronghold backup
- Run the βFull syncβ function to resync from index 0 across all accounts
- Run the βFind more historyβ function to sync a further 50 addresses
Scenario 2: User has no backup file
- Start syncing from account 0 address 0
A background process that automatically performs several tasks periodically should be part of the wallet library. The goal of the background process is to perform the following tasks:
- Sync accounts: The background process should sync all accounts with the network. This should be done using the
sync_accounts()
method.- If new messages are detected, a messages event should be used to notify all subscribers.
- If new balances are detected, a balances event should be used to notify all subscribers.
- If new confirmations are detected, a confirmations event should be used to notify all subscribers.
If there are multiple failed messages, priority should be given to the old ones.
- Reattach: The background process should check if there are any unconfirmed messages that require reattachment. The detailed implementation flow for reattachment can be found in the reattach section.
The following should be considered for implementation:
- Invoking a task explicitly that is already being performed through polling should lead to an error. For example, if the polling process is already syncing accounts, and a user explicitly calls sync(), it should throw an error.
- Errors during the polling process should be communicated to subscribers via error events.
The background process should have a recurring checker that sequentially performs all the above tasks. The implementation should ensure that future tasks can easily be added to the background process. For reference, see Trinity's implementation of the poll component.