iota-wallet 1.0.0-rc.4

A stateful package for IOTA value transactions
Documentation
// Copyright 2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::{
    fmt::{Debug, Formatter, Result},
    path::PathBuf,
};

#[cfg(feature = "participation")]
use iota_client::{node_api::participation::types::EventId, node_manager::node::Node};
use iota_client::{node_manager::node::NodeAuth, secret::GenerateAddressOptions};
use serde::{Deserialize, Serialize};

use super::account_method::AccountMethod;
#[cfg(feature = "events")]
use crate::events::types::{WalletEvent, WalletEventType};
use crate::{
    account::{operations::syncing::SyncOptions, types::AccountIdentifier},
    ClientOptions,
};

/// The messages that can be sent to the actor.
#[derive(Clone, Serialize, Deserialize)]
#[serde(tag = "cmd", content = "payload", rename_all = "camelCase")]
#[allow(clippy::large_enum_variant)]
pub enum Message {
    /// Creates an account.
    /// Expected response: [`Account`](crate::message_interface::Response::Account)
    CreateAccount {
        /// The account alias.
        alias: Option<String>,
        /// The bech32 HRP.
        #[serde(rename = "bech32Hrp")]
        bech32_hrp: Option<String>,
    },
    /// Read account.
    /// Expected response: [`Account`](crate::message_interface::Response::Account)
    GetAccount(AccountIdentifier),
    /// Return the account indexes.
    /// Expected response: [`AccountIndexes`](crate::message_interface::Response::AccountIndexes)
    GetAccountIndexes,
    /// Read accounts.
    /// Expected response: [`Accounts`](crate::message_interface::Response::Accounts)
    GetAccounts,
    /// Consume an account method.
    /// Returns [`Response`](crate::message_interface::Response)
    CallAccountMethod {
        /// The account identifier.
        #[serde(rename = "accountId")]
        account_id: AccountIdentifier,
        /// The account method to call.
        method: AccountMethod,
    },
    /// Backup storage. Password must be the current one, when Stronghold is used as SecretManager.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "stronghold")]
    Backup {
        /// The backup destination.
        destination: PathBuf,
        /// Stronghold file password.
        password: String,
    },
    /// Change the Stronghold password to another one and also re-encrypt the values in the loaded snapshot with it.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "stronghold")]
    ChangeStrongholdPassword {
        #[serde(rename = "currentPassword")]
        current_password: String,
        #[serde(rename = "newPassword")]
        new_password: String,
    },
    /// Clears the Stronghold password from memory.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "stronghold")]
    ClearStrongholdPassword,
    /// Checks if the Stronghold password is available.
    /// Expected response:
    /// [`StrongholdPasswordIsAvailable`](crate::message_interface::Response::StrongholdPasswordIsAvailable)
    #[cfg(feature = "stronghold")]
    IsStrongholdPasswordAvailable,
    /// Find accounts with unspent outputs
    /// Expected response: [`Accounts`](crate::message_interface::Response::Accounts)
    RecoverAccounts {
        #[serde(rename = "accountStartIndex")]
        /// The index of the first account to search for.
        account_start_index: u32,
        #[serde(rename = "accountGapLimit")]
        /// The number of accounts to search for, after the last account with unspent outputs.
        account_gap_limit: u32,
        #[serde(rename = "addressGapLimit")]
        /// The number of addresses to search for, after the last address with unspent outputs, in
        /// each account.
        address_gap_limit: u32,
        #[serde(rename = "syncOptions")]
        /// Optional parameter to specify the sync options. The `address_start_index` and `force_syncing`
        /// fields will be overwritten to skip existing addresses.
        sync_options: Option<SyncOptions>,
    },
    /// Restore a backup from a Stronghold file
    /// Replaces client_options, coin_type, secret_manager and accounts. Returns an error if accounts were already
    /// created If Stronghold is used as secret_manager, the existing Stronghold file will be overwritten. If a
    /// mnemonic was stored, it will be gone.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "stronghold")]
    RestoreBackup {
        /// The path to the backed up Stronghold.
        source: PathBuf,
        /// Stronghold file password.
        password: String,
    },
    /// Removes the latest account (account with the largest account index).
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    RemoveLatestAccount,
    /// Generates a new mnemonic.
    /// Expected response: [`GeneratedMnemonic`](crate::message_interface::Response::GeneratedMnemonic)
    GenerateMnemonic,
    /// Checks if the given mnemonic is valid.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    VerifyMnemonic(String),
    /// Updates the client options for all accounts.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    SetClientOptions(Box<ClientOptions>),
    /// Generate an address without storing it
    /// Expected response: [`Bech32Address`](crate::message_interface::Response::Bech32Address)
    GenerateAddress {
        /// Account index
        #[serde(rename = "accountIndex")]
        account_index: u32,
        /// Internal address
        internal: bool,
        /// Account index
        #[serde(rename = "addressIndex")]
        address_index: u32,
        /// Options
        options: Option<GenerateAddressOptions>,
        /// Bech32 HRP
        #[serde(rename = "bech32Hrp")]
        bech32_hrp: Option<String>,
    },
    /// Get the ledger nano status
    /// Expected response: [`LedgerNanoStatus`](crate::message_interface::Response::LedgerNanoStatus)
    #[cfg(feature = "ledger_nano")]
    GetLedgerNanoStatus,
    /// Get the node information
    /// Expected response: [`NodeInfo`](crate::message_interface::Response::NodeInfo)
    GetNodeInfo {
        /// Url
        url: Option<String>,
        /// Node authentication
        auth: Option<NodeAuth>,
    },
    /// Set the stronghold password.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    SetStrongholdPassword(String),
    /// Set the stronghold password clear interval.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    SetStrongholdPasswordClearInterval(Option<u64>),
    /// Store a mnemonic into the Stronghold vault.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    StoreMnemonic(String),
    /// Start background syncing.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    StartBackgroundSync {
        /// Sync options
        options: Option<SyncOptions>,
        /// Interval in milliseconds
        #[serde(rename = "intervalInMilliseconds")]
        interval_in_milliseconds: Option<u64>,
    },
    /// Stop background syncing.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    StopBackgroundSync,
    /// Emits an event for testing if the event system is working
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "events")]
    EmitTestEvent(WalletEvent),
    /// Transforms bech32 to hex
    /// Expected response: [`HexAddress`](crate::message_interface::Response::HexAddress)
    Bech32ToHex(String),
    /// Transforms a hex encoded address to a bech32 encoded address
    /// Expected response: [`Bech32Address`](crate::message_interface::Response::Bech32Address)
    HexToBech32 {
        /// Hex encoded bech32 address
        hex: String,
        /// Human readable part
        #[serde(rename = "bech32Hrp")]
        bech32_hrp: Option<String>,
    },
    // Remove all listeners of this type. Empty vec clears all listeners
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "events")]
    ClearListeners(Vec<WalletEventType>),
    /// Stores participation information locally and returns the event.
    ///
    /// This will NOT store the node url and auth inside the client options.
    /// Expected response: [`ParticipationEvent`](crate::message_interface::Response::ParticipationEvent)
    #[cfg(feature = "participation")]
    RegisterParticipationEvent {
        #[serde(rename = "eventId")]
        event_id: EventId,
        nodes: Vec<Node>,
    },
    /// Removes a previously registered participation event from local storage.
    /// Expected response: [`Ok`](crate::message_interface::Response::Ok)
    #[cfg(feature = "participation")]
    DeregisterParticipationEvent {
        #[serde(rename = "eventId")]
        event_id: EventId,
    },
    /// Expected response: [`ParticipationEvent`](crate::message_interface::Response::ParticipationEvent)
    #[cfg(feature = "participation")]
    GetParticipationEvent {
        #[serde(rename = "eventId")]
        event_id: EventId,
    },
    /// Expected response: [`ParticipationEventStatus`](crate::message_interface::Response::ParticipationEventStatus)
    #[cfg(feature = "participation")]
    GetParticipationEventStatus {
        #[serde(rename = "eventId")]
        event_id: EventId,
    },
    /// Expected response: [`ParticipationEvents`](crate::message_interface::Response::ParticipationEvents)
    #[cfg(feature = "participation")]
    GetParticipationEvents,
}

// Custom Debug implementation to not log secrets
impl Debug for Message {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        match self {
            Message::CreateAccount { alias, bech32_hrp } => {
                write!(f, "CreateAccount{{ alias: {alias:?}, bech32_hrp: {bech32_hrp:?} }}")
            }
            Message::GetAccountIndexes => write!(f, "GetAccountIndexes"),
            Message::GetAccount(identifier) => write!(f, "GetAccount({identifier:?})"),
            Message::GetAccounts => write!(f, "GetAccounts"),
            Message::CallAccountMethod { account_id, method } => write!(
                f,
                "CallAccountMethod{{ account_id: {account_id:?}, method: {method:?} }}"
            ),
            #[cfg(feature = "stronghold")]
            Message::ChangeStrongholdPassword {
                current_password: _,
                new_password: _,
            } => write!(
                f,
                "ChangeStrongholdPassword{{ current_password: <omitted>, new_password: <omitted> }}"
            ),
            #[cfg(feature = "stronghold")]
            Message::ClearStrongholdPassword => write!(f, "ClearStrongholdPassword"),
            #[cfg(feature = "stronghold")]
            Message::IsStrongholdPasswordAvailable => write!(f, "IsStrongholdPasswordAvailable"),
            #[cfg(feature = "stronghold")]
            Message::Backup {
                destination,
                password: _,
            } => write!(f, "Backup{{ destination: {destination:?} }}"),
            Message::RecoverAccounts {
                account_start_index,
                account_gap_limit,
                address_gap_limit,
                sync_options,
            } => write!(
                f,
                "RecoverAccounts{{ account_start_index: {account_start_index:?}, account_gap_limit: {account_gap_limit:?}, address_gap_limit: {address_gap_limit:?}, sync_options: {sync_options:?} }}"
            ),
            Message::RemoveLatestAccount => write!(f, "RemoveLatestAccount"),
            #[cfg(feature = "stronghold")]
            Message::RestoreBackup { source, password: _ } => write!(f, "RestoreBackup{{ source: {source:?} }}"),
            Message::GenerateMnemonic => write!(f, "GenerateMnemonic"),
            Message::VerifyMnemonic(_) => write!(f, "VerifyMnemonic(<omitted>)"),
            Message::SetClientOptions(options) => write!(f, "SetClientOptions({options:?})"),
            #[cfg(feature = "ledger_nano")]
            Message::GetLedgerNanoStatus => write!(f, "GetLedgerNanoStatus"),
            Message::GenerateAddress {
                account_index,
                internal,
                address_index,
                options,
                bech32_hrp,
            } => write!(
                f,
                "GenerateAddress{{ account_index: {account_index:?}, internal: {internal:?}, address_index: {address_index:?}, options: {options:?}, bech32_hrp: {bech32_hrp:?} }}"
            ),
            Message::GetNodeInfo { url, auth: _ } => write!(f, "GetNodeInfo{{ url: {url:?} }}"),
            Message::SetStrongholdPassword(_) => write!(f, "SetStrongholdPassword(<omitted>)"),
            Message::SetStrongholdPasswordClearInterval(interval_in_milliseconds) => {
                write!(f, "SetStrongholdPassword({interval_in_milliseconds:?})")
            }
            Message::StoreMnemonic(_) => write!(f, "StoreMnemonic(<omitted>)"),
            Message::StartBackgroundSync {
                options,
                interval_in_milliseconds,
            } => write!(
                f,
                "StartBackgroundSync{{ options: {options:?}, interval: {interval_in_milliseconds:?} }}"
            ),
            Message::StopBackgroundSync => write!(f, "StopBackgroundSync"),
            #[cfg(feature = "events")]
            Message::EmitTestEvent(event) => write!(f, "EmitTestEvent({event:?})"),
            Message::Bech32ToHex(bech32_address) => write!(f, "Bech32ToHex({bech32_address:?})"),
            Message::HexToBech32 { hex, bech32_hrp } => {
                write!(f, "HexToBech32{{ hex: {hex:?}, bech32_hrp: {bech32_hrp:?} }}")
            }

            #[cfg(feature = "events")]
            Message::ClearListeners(events) => write!(f, "ClearListeners({events:?})"),
            #[cfg(feature = "participation")]
            Message::RegisterParticipationEvent { event_id, nodes } => {
                write!(
                    f,
                    "RegisterParticipationEvent{{ event_id: {event_id:?}, nodes: {nodes:?} }}"
                )
            }
            #[cfg(feature = "participation")]
            Message::DeregisterParticipationEvent { event_id } => {
                write!(f, "DeregisterParticipationEvent({event_id:?})")
            }
            #[cfg(feature = "participation")]
            Message::GetParticipationEvent { event_id } => {
                write!(f, "GetParticipationEvent({event_id:?})")
            }
            #[cfg(feature = "participation")]
            Message::GetParticipationEventStatus { event_id } => {
                write!(f, "GetParticipationEventStatus({event_id:?})")
            }
            #[cfg(feature = "participation")]
            Message::GetParticipationEvents => {
                write!(f, "GetParticipationEvents")
            }
        }
    }
}