From 52c8a9f02f3b025e96d455d8b539f73e1331c00e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sat, 21 Sep 2024 09:37:01 +0300 Subject: [PATCH 1/8] docs --- consensus/client/src/utxo.rs | 2 + consensus/core/src/api/mod.rs | 2 +- consensus/core/src/config/bps.rs | 2 +- consensus/core/src/tx.rs | 1 + notify/src/address/tracker.rs | 4 +- notify/src/notifier.rs | 4 +- utils/src/option.rs | 2 +- wallet/bip32/src/address_type.rs | 5 ++ wallet/core/src/account/descriptor.rs | 5 ++ wallet/core/src/account/kind.rs | 5 ++ wallet/core/src/account/pskb.rs | 5 ++ wallet/core/src/api/message.rs | 21 +++++---- wallet/core/src/api/traits.rs | 16 +++++++ wallet/core/src/api/transport.rs | 4 +- wallet/core/src/compat/mod.rs | 4 ++ wallet/core/src/cryptobox.rs | 13 +++++- wallet/core/src/events.rs | 6 +++ wallet/core/src/factory.rs | 3 ++ wallet/core/src/lib.rs | 63 ++++++++++---------------- wallet/core/src/message.rs | 1 + wallet/core/src/metrics.rs | 29 ++++-------- wallet/core/src/rpc.rs | 8 +++- wallet/core/src/settings.rs | 5 ++ wallet/core/src/utxo/processor.rs | 4 +- wallet/core/src/wallet/api.rs | 8 ++-- wallet/core/src/wallet/args.rs | 1 - wallet/core/src/wallet/maps.rs | 1 + wallet/core/src/wallet/mod.rs | 9 ++-- wallet/core/src/wasm/cryptobox.rs | 2 +- wallet/core/src/wasm/signer.rs | 4 +- wallet/keys/src/derivation/gen0/mod.rs | 2 +- wallet/keys/src/derivation/gen1/mod.rs | 3 +- wallet/keys/src/derivation/mod.rs | 4 ++ wallet/keys/src/derivation_path.rs | 6 +++ wallet/keys/src/keypair.rs | 8 +++- wallet/keys/src/lib.rs | 7 +++ wallet/keys/src/prelude.rs | 4 ++ wallet/keys/src/privatekey.rs | 6 ++- wallet/keys/src/privkeygen.rs | 4 ++ wallet/keys/src/pubkeygen.rs | 4 ++ wallet/keys/src/publickey.rs | 4 +- wallet/keys/src/secret.rs | 4 +- wallet/keys/src/types.rs | 2 +- wallet/keys/src/xprv.rs | 4 ++ wallet/keys/src/xpub.rs | 4 ++ wallet/pskt/src/bundle.rs | 5 ++ wallet/pskt/src/convert.rs | 6 +++ wallet/pskt/src/error.rs | 2 + wallet/pskt/src/global.rs | 2 + wallet/pskt/src/input.rs | 2 + wallet/pskt/src/output.rs | 2 + wallet/pskt/src/pskt.rs | 21 +++++++++ wallet/pskt/src/role.rs | 2 + wallet/pskt/src/utils.rs | 2 + 54 files changed, 243 insertions(+), 106 deletions(-) diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index 0a8b3fdb9..85bd1547b 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -119,6 +119,8 @@ impl From<&UtxoEntry> for cctx::UtxoEntry { } } +/// [`Arc`] reference to a [`UtxoEntry`] used by the wallet subsystems. +/// /// @category Wallet SDK #[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)] #[wasm_bindgen(inspectable)] diff --git a/consensus/core/src/api/mod.rs b/consensus/core/src/api/mod.rs index 91165b73d..365b8404c 100644 --- a/consensus/core/src/api/mod.rs +++ b/consensus/core/src/api/mod.rs @@ -39,7 +39,7 @@ pub struct BlockValidationFutures { /// A future triggered when DAG state which included this block has been processed by the virtual processor /// (exceptions are header-only blocks and trusted blocks which have the future completed before virtual - /// processing along with the [`block_task`]) + /// processing along with the `block_task`) pub virtual_state_task: BlockValidationFuture, } diff --git a/consensus/core/src/config/bps.rs b/consensus/core/src/config/bps.rs index c0c52a6df..5e98aac5d 100644 --- a/consensus/core/src/config/bps.rs +++ b/consensus/core/src/config/bps.rs @@ -33,7 +33,7 @@ impl Bps { } /// Returns the GHOSTDAG K value which was pre-computed for this BPS - /// (see [`calculate_ghostdag_k`] and [`gen_ghostdag_table`] for the full calculation) + /// (see [`calculate_ghostdag_k`] and `gen_ghostdag_table` for the full calculation) #[rustfmt::skip] pub const fn ghostdag_k() -> KType { match BPS { diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index bad1b679a..79babbd74 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -25,6 +25,7 @@ use crate::{ /// COINBASE_TRANSACTION_INDEX is the index of the coinbase transaction in every block pub const COINBASE_TRANSACTION_INDEX: usize = 0; +/// A 32-byte Kaspa transaction identifier. pub type TransactionId = kaspa_hashes::Hash; /// Holds details about an individual transaction output in a utxo diff --git a/notify/src/address/tracker.rs b/notify/src/address/tracker.rs index f103b163c..a2b1c64dd 100644 --- a/notify/src/address/tracker.rs +++ b/notify/src/address/tracker.rs @@ -384,11 +384,11 @@ impl Inner { } } -/// Tracker of a set of [`Address`](kaspa_addresses::Address), indexing and counting registrations +/// Tracker of a set of [`Address`], indexing and counting registrations /// /// #### Implementation design /// -/// Each [`Address`](kaspa_addresses::Address) is stored internally as a [`ScriptPubKey`](kaspa_consensus_core::tx::ScriptPublicKey). +/// Each [`Address`] is stored internally as a [`ScriptPubKey`](kaspa_consensus_core::tx::ScriptPublicKey). /// This prevents inter-network duplication and optimizes UTXOs filtering efficiency. /// /// But consequently the address network prefix gets lost and must be globally provided when querying for addresses by indexes. diff --git a/notify/src/notifier.rs b/notify/src/notifier.rs index 220fd261b..6927ea1a1 100644 --- a/notify/src/notifier.rs +++ b/notify/src/notifier.rs @@ -75,8 +75,8 @@ pub type DynNotify = Arc>; /// /// - a vector of [`DynCollector`] /// - a vector of [`Subscriber`] -/// - a pool of [`Broadcaster`] -/// - a map of [`Listener`] +/// - a pool of `Broadcaster` +/// - a map of `Listener` /// /// Collectors and subscribers form the scaffold. They are provided to the ctor, are immutable and share its /// lifespan. Both do materialize a connection to the notifier _parents_, collectors for incoming notifications diff --git a/utils/src/option.rs b/utils/src/option.rs index ff4779dc1..3e619f46f 100644 --- a/utils/src/option.rs +++ b/utils/src/option.rs @@ -1,5 +1,5 @@ pub trait OptionExtensions { - /// Substitute for unstable [Option::is_non_or] + /// Substitute for unstable [`Option::is_none_or`] fn is_none_or_ex(&self, f: impl FnOnce(&T) -> bool) -> bool; } diff --git a/wallet/bip32/src/address_type.rs b/wallet/bip32/src/address_type.rs index 63ea00361..3aecfdb75 100644 --- a/wallet/bip32/src/address_type.rs +++ b/wallet/bip32/src/address_type.rs @@ -1,5 +1,10 @@ +//! +//! Address type (`Receive` or `Change`) used in HD wallet address derivation. +//! + use std::fmt; +/// Address type used in HD wallet address derivation. pub enum AddressType { Receive = 0, Change, diff --git a/wallet/core/src/account/descriptor.rs b/wallet/core/src/account/descriptor.rs index c549b739d..c3bf97cc1 100644 --- a/wallet/core/src/account/descriptor.rs +++ b/wallet/core/src/account/descriptor.rs @@ -11,6 +11,11 @@ use kaspa_wallet_macros::declare_typescript_wasm_interface as declare; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +/// +/// Structure that represents a wallet account. This structure contains +/// properties that are common to all wallet accounts as well as +/// account-specific properties stored in a BTreeMap by each account. +/// /// @category Wallet API #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct AccountDescriptor { diff --git a/wallet/core/src/account/kind.rs b/wallet/core/src/account/kind.rs index 20e863d77..511c1f4ed 100644 --- a/wallet/core/src/account/kind.rs +++ b/wallet/core/src/account/kind.rs @@ -8,6 +8,11 @@ use std::hash::Hash; use std::str::FromStr; use workflow_wasm::convert::CastFromJs; +/// +/// Account kind is a string signature that represents an account type. +/// Account kind is used to identify the account type during +/// serialization, deserialization and various API calls. +/// /// @category Wallet SDK #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash, CastFromJs)] #[wasm_bindgen] diff --git a/wallet/core/src/account/pskb.rs b/wallet/core/src/account/pskb.rs index 8fc46088b..e71d7e479 100644 --- a/wallet/core/src/account/pskb.rs +++ b/wallet/core/src/account/pskb.rs @@ -1,3 +1,8 @@ +//! +//! Tools for interfacing wallet accounts with PSKBs. +//! (Partial Signed Kaspa Transaction Bundles). +//! + pub use crate::error::Error; use crate::imports::*; use crate::tx::PaymentOutputs; diff --git a/wallet/core/src/api/message.rs b/wallet/core/src/api/message.rs index 3b96abd1a..e27cb2b29 100644 --- a/wallet/core/src/api/message.rs +++ b/wallet/core/src/api/message.rs @@ -118,15 +118,18 @@ pub struct RetainContextRequest { #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(rename_all = "camelCase")] -pub struct RetainContextResponse { - // pub name : String, - // pub data: Option>>, - // pub is_connected: bool, - // pub is_synced: bool, - // pub is_open: bool, - // pub url: Option, - // pub is_wrpc_client: bool, - // pub network_id: Option, +pub struct RetainContextResponse {} + +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetContextRequest { + pub name: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetContextResponse { + pub data: Option>, } #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] diff --git a/wallet/core/src/api/traits.rs b/wallet/core/src/api/traits.rs index 08ebd65f6..db789c8b3 100644 --- a/wallet/core/src/api/traits.rs +++ b/wallet/core/src/api/traits.rs @@ -21,13 +21,29 @@ pub trait WalletApi: Send + Sync + AnySync { async fn register_notifications(self: Arc, channel: Receiver) -> Result; async fn unregister_notifications(self: Arc, channel_id: u64) -> Result<()>; + /// Wrapper around [`retain_context_call()`](Self::retain_context_call). async fn retain_context(self: Arc, name: &str, data: Option>) -> Result<()> { self.retain_context_call(RetainContextRequest { name: name.to_string(), data }).await?; Ok(()) } + /// Obtain earlier retained context data using the context `name` as a key. + async fn get_context(self: Arc, name: &str) -> Result>> { + Ok(self.get_context_call(GetContextRequest { name: name.to_string() }).await?.data) + } + + /// Allows user to store string key-associated context data in the wallet subsystem runtime. + /// The context data persists only during the wallet instance runtime. + /// This can be useful if you have a front-end that connects to a + /// persistent wallet instance operating in the backend (such as a browser + /// extension popup connecting to the background page) and you need to store + /// any type of runtime data in the backend (but are limited to using only + /// the wallet interface). async fn retain_context_call(self: Arc, request: RetainContextRequest) -> Result; + /// Obtain context data stored using [`retain_context()`](Self::retain_context). + async fn get_context_call(self: Arc, request: GetContextRequest) -> Result; + /// Wrapper around [`get_status_call()`](Self::get_status_call). async fn get_status(self: Arc, name: Option<&str>) -> Result { Ok(self.get_status_call(GetStatusRequest { name: name.map(String::from) }).await?) diff --git a/wallet/core/src/api/transport.rs b/wallet/core/src/api/transport.rs index 4de2d7824..c9e5f6de6 100644 --- a/wallet/core/src/api/transport.rs +++ b/wallet/core/src/api/transport.rs @@ -71,6 +71,7 @@ impl WalletApi for WalletClient { Disconnect, ChangeNetworkId, RetainContext, + GetContext, Batch, Flush, WalletEnumerate, @@ -118,7 +119,7 @@ pub trait EventHandler: Send + Sync { /// [`WalletServer`] is a server-side transport interface that declares /// API methods that can be invoked via Borsh or Serde messages containing -/// serializations created using the [`Transport`] interface. The [`WalletServer`] +/// serializations created using the [`Codec`] interface. The [`WalletServer`] /// is a counter-part to [`WalletClient`]. pub struct WalletServer { // pub wallet_api: Arc, @@ -147,6 +148,7 @@ impl WalletServer { Disconnect, ChangeNetworkId, RetainContext, + GetContext, Batch, Flush, WalletEnumerate, diff --git a/wallet/core/src/compat/mod.rs b/wallet/core/src/compat/mod.rs index 79c8e11dd..093b8845c 100644 --- a/wallet/core/src/compat/mod.rs +++ b/wallet/core/src/compat/mod.rs @@ -1,3 +1,7 @@ +//! +//! Compatibility layer for legacy wallets. +//! + pub mod gen0; pub use gen0::*; pub mod gen1; diff --git a/wallet/core/src/cryptobox.rs b/wallet/core/src/cryptobox.rs index fa9b188f3..83845eee9 100644 --- a/wallet/core/src/cryptobox.rs +++ b/wallet/core/src/cryptobox.rs @@ -1,3 +1,7 @@ +//! +//! Re-export of the `crypto_box` crate that can be used to encrypt and decrypt messages. +//! + use crate::imports::*; use crypto_box::{ aead::{Aead, AeadCore, OsRng}, @@ -5,8 +9,13 @@ use crypto_box::{ }; pub use crypto_box::{PublicKey, SecretKey}; -// https://docs.rs/crypto_box/0.9.1/crypto_box/ - +/// +/// Primitives for encrypting and decrypting messages using the `crypto_box` crate. +/// This exists primarily for the purposes of [WASM bindings](crate::wasm::cryptobox::CryptoBox) +/// to allow access to the `crypto_box` encryption functionality from within web wallets. +/// +/// +/// pub struct CryptoBox { public_key: PublicKey, codec: ChaChaBox, diff --git a/wallet/core/src/events.rs b/wallet/core/src/events.rs index 63d7d5bca..37816d8b2 100644 --- a/wallet/core/src/events.rs +++ b/wallet/core/src/events.rs @@ -245,6 +245,12 @@ impl Events { } } +/// +/// Event kind representing [`Events`] variant. +/// Used primarily by WASM bindings to identify event types +/// by their string representation. Can be obtained from the +/// event via [`Events::kind()`]. +/// #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq, Hash)] #[serde(rename_all = "kebab-case")] pub enum EventKind { diff --git a/wallet/core/src/factory.rs b/wallet/core/src/factory.rs index 515093fdc..178e331e5 100644 --- a/wallet/core/src/factory.rs +++ b/wallet/core/src/factory.rs @@ -6,6 +6,7 @@ use crate::imports::*; use crate::result::Result; use std::sync::OnceLock; +/// Wallet account loading factory. #[async_trait] pub trait Factory { fn name(&self) -> String; @@ -22,6 +23,7 @@ type FactoryMap = AHashMap static EXTERNAL: OnceLock> = OnceLock::new(); static INITIALIZED: AtomicBool = AtomicBool::new(false); +/// Global factory registry accessor. pub fn factories() -> &'static FactoryMap { static FACTORIES: OnceLock = OnceLock::new(); FACTORIES.get_or_init(|| { @@ -41,6 +43,7 @@ pub fn factories() -> &'static FactoryMap { }) } +/// Registers a new global account factory. pub fn register(kind: AccountKind, factory: Arc) { if INITIALIZED.load(Ordering::Relaxed) { panic!("Factory registrations must occur before the framework initialization"); diff --git a/wallet/core/src/lib.rs b/wallet/core/src/lib.rs index bca89d4e7..09cc3ca7f 100644 --- a/wallet/core/src/lib.rs +++ b/wallet/core/src/lib.rs @@ -1,16 +1,21 @@ //! -//! Kaspa Wallet Core - Multi-platform Rust framework for Kaspa Wallet. +//! # Kaspa Wallet Core +//! +//! Multi-platform Rust framework for Kaspa Wallet. //! //! This framework provides a series of APIs and primitives //! to simplify building applications that interface with //! the Kaspa p2p network. //! -//! Included are low-level primitives +//! For key generation and derivation, please see the +//! [`kaspa_wallet_keys`] crate. +//! +//! This crate included are low-level primitives //! such as [`UtxoProcessor`](crate::utxo::UtxoProcessor) //! and [`UtxoContext`](crate::utxo::UtxoContext) that provide //! various levels of automation as well as higher-level //! APIs such as [`Wallet`](crate::wallet::Wallet), -//! [`Account`](crate::account::Account) (managed via +//! [`Account`](crate::account::Account) (managed via the //! [`WalletApi`](crate::api::WalletApi) trait) //! that offer a fully-featured wallet implementation //! backed by a multi-platform data storage layer capable of @@ -28,15 +33,28 @@ //! to satisfy the requested amount exceeds the maximum //! allowed transaction mass. //! +//! Key generation and derivation is available in the +//! [`kaspa_wallet_keys`] crate. +//! //! The framework can operate -//! within native Rust applications as well as within the NodeJS -//! and browser environments via WASM32. +//! within native Rust applications as well as within NodeJS, Bun +//! and browser environments via the WASM32 SDK. //! -//! For JavaScript / TypeScript environments, there are two +//! WASM32 SDK documentation is available at: +//! +//! +//! For NodeJS JavaScript and TypeScript environments, there are two //! available NPM modules: //! - //! - //! +//! NOTE: for security reasons (to mitigate potential upstream vendor +//! attacks) it is always recommended to build WASM SDK from source or +//! download pre-built redistributables. +//! +//! Latest development builds of the WASM32 SDK can be found at: +//! +//! //! The `kaspa-wasm` module is a pure WASM32 module that includes //! the entire wallet framework, but does not support RPC due to an absence //! of a native WebSocket in NodeJs environment, while @@ -54,36 +72,6 @@ extern crate alloc; extern crate self as kaspa_wallet_core; -// use cfg_if::cfg_if; - -// cfg_if! { -// if #[cfg(feature = "wasm32-core")] { -// // pub mod wasm; -// // pub use wasm::*; - -// pub mod account; -// pub mod api; -// pub mod compat; -// pub mod derivation; -// pub mod deterministic; -// pub mod encryption; -// pub mod error; -// pub mod events; -// pub mod factory; -// mod imports; -// pub mod message; -// pub mod prelude; -// pub mod result; -// pub mod rpc; -// pub mod serializer; -// pub mod settings; -// pub mod storage; -// pub mod tx; -// pub mod utils; -// pub mod utxo; -// pub mod wallet; - -// } else if #[cfg(any(feature = "wasm32-sdk", not(target_arch = "wasm32")))] { pub mod account; pub mod api; pub mod compat; @@ -107,9 +95,6 @@ pub mod tx; pub mod utils; pub mod utxo; pub mod wallet; -// } - -// } #[cfg(any(feature = "wasm32-sdk", feature = "wasm32-core"))] pub mod wasm; diff --git a/wallet/core/src/message.rs b/wallet/core/src/message.rs index 160c8f040..01dc78676 100644 --- a/wallet/core/src/message.rs +++ b/wallet/core/src/message.rs @@ -5,6 +5,7 @@ use kaspa_hashes::{Hash, PersonalMessageSigningHash}; use secp256k1::{Error, XOnlyPublicKey}; +/// A personal message (text) that can be signed. #[derive(Clone)] pub struct PersonalMessage<'a>(pub &'a str); diff --git a/wallet/core/src/metrics.rs b/wallet/core/src/metrics.rs index 87a3f9913..b0edb1f88 100644 --- a/wallet/core/src/metrics.rs +++ b/wallet/core/src/metrics.rs @@ -1,6 +1,13 @@ +//! +//! Primitives for network metrics. +//! + use crate::imports::*; -// use kaspa_metrics_core::MetricsSnapshot; +/// Metrics posted by the wallet subsystem. +/// See [`UtxoProcessor::start_metrics`] to enable metrics processing. +/// This struct contains mempool size that can be used to estimate +/// current network congestion. #[derive(Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] #[serde(tag = "type", content = "data")] #[serde(rename_all = "kebab-case")] @@ -8,37 +15,19 @@ pub enum MetricsUpdate { WalletMetrics { #[serde(rename = "mempoolSize")] mempool_size: u64, - #[serde(rename = "nodePeers")] - node_peers: u32, - #[serde(rename = "networkTPS")] - network_tps: f64, }, - // NodeMetrics { - // snapshot : Box - // } } +/// [`MetricsUpdate`] variant identifier. #[derive(Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub enum MetricsUpdateKind { WalletMetrics, - // NodeMetrics } impl MetricsUpdate { pub fn kind(&self) -> MetricsUpdateKind { match self { MetricsUpdate::WalletMetrics { .. } => MetricsUpdateKind::WalletMetrics, - // MetricsUpdate::NodeMetrics { .. } => MetricsUpdateKind::NodeMetrics } } } - -// impl MetricsUpdate { -// pub fn wallet_metrics(mempool_size: u64, peers: usize) -> Self { -// MetricsUpdate::WalletMetrics { mempool_size, peers } -// } - -// pub fn node_metrics(snapshot: MetricsSnapshot) -> Self { -// MetricsUpdate::NodeMetrics(Box::new(snapshot)) -// } -// } diff --git a/wallet/core/src/rpc.rs b/wallet/core/src/rpc.rs index 999e09e30..b75f4b5b2 100644 --- a/wallet/core/src/rpc.rs +++ b/wallet/core/src/rpc.rs @@ -6,13 +6,17 @@ use std::sync::Arc; pub use kaspa_rpc_core::api::ctl::RpcCtl; pub use kaspa_rpc_core::api::rpc::RpcApi; -pub type DynRpcApi = dyn RpcApi; -pub type NotificationChannel = kaspa_utils::channel::Channel; pub use kaspa_rpc_core::notify::mode::NotificationMode; pub use kaspa_wrpc_client::client::{ConnectOptions, ConnectStrategy}; pub use kaspa_wrpc_client::Resolver; pub use kaspa_wrpc_client::WrpcEncoding; +/// Type alias for [`dyn RpcApi`](RpcApi). +pub type DynRpcApi = dyn RpcApi; +/// Type alias for a concrete [`Channel`](kaspa_utils::channel::Channel) +/// used for handling RPC [`Notification`](kaspa_rpc_core::Notification) events. +pub type NotificationChannel = kaspa_utils::channel::Channel; + /// RPC adaptor class that holds the [`RpcApi`] /// and [`RpcCtl`] instances. #[derive(Clone)] diff --git a/wallet/core/src/settings.rs b/wallet/core/src/settings.rs index 35fde4486..f861c5a13 100644 --- a/wallet/core/src/settings.rs +++ b/wallet/core/src/settings.rs @@ -13,6 +13,7 @@ use std::path::PathBuf; use workflow_core::enums::Describe; use workflow_store::fs; +/// Wallet settings enumeration. #[derive(Describe, Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)] #[serde(rename_all = "lowercase")] pub enum WalletSettings { @@ -36,6 +37,8 @@ pub trait DefaultSettings: Sized { async fn defaults() -> Vec<(Self, Value)>; } +/// Platform neutral settings store (stores the settings K:V map +/// in a file or the browser `localStorage`). #[derive(Debug, Clone)] pub struct SettingsStore where @@ -170,10 +173,12 @@ where } } +/// Returns the wallet data storage folder `~/.kaspa`. pub fn application_folder() -> Result { Ok(fs::resolve_path(storage::local::default_storage_folder())?) } +/// If missing, creates the wallet data storage folder `~/.kaspa`. pub async fn ensure_application_folder() -> Result<()> { let path = application_folder()?; log_info!("Creating application folder: `{}`", path.display()); diff --git a/wallet/core/src/utxo/processor.rs b/wallet/core/src/utxo/processor.rs index b72b9784a..f6480f333 100644 --- a/wallet/core/src/utxo/processor.rs +++ b/wallet/core/src/utxo/processor.rs @@ -604,9 +604,7 @@ impl UtxoProcessor { match kind { MetricsUpdateKind::WalletMetrics => { let mempool_size = snapshot.get(&Metric::NetworkMempoolSize) as u64; - let node_peers = snapshot.get(&Metric::NodeActivePeers) as u32; - let network_tps = snapshot.get(&Metric::NetworkTransactionsPerSecond); - let metrics = MetricsUpdate::WalletMetrics { mempool_size, node_peers, network_tps }; + let metrics = MetricsUpdate::WalletMetrics { mempool_size }; self.try_notify(Events::Metrics { network_id: self.network_id()?, metrics })?; } } diff --git a/wallet/core/src/wallet/api.rs b/wallet/core/src/wallet/api.rs index adeb00075..c7e68d1fd 100644 --- a/wallet/core/src/wallet/api.rs +++ b/wallet/core/src/wallet/api.rs @@ -62,15 +62,17 @@ impl WalletApi for super::Wallet { if let Some(data) = data { self.inner.retained_contexts.lock().unwrap().insert(name, Arc::new(data)); - Ok(RetainContextResponse {}) } else { self.inner.retained_contexts.lock().unwrap().remove(&name); - // let data = self.inner.retained_contexts.lock().unwrap().get(&name).cloned(); Ok(RetainContextResponse {}) } + } - // self.retain_context(retain); + async fn get_context_call(self: Arc, request: GetContextRequest) -> Result { + let GetContextRequest { name } = request; + let data = self.inner.retained_contexts.lock().unwrap().get(&name).map(|data| (**data).clone()); + Ok(GetContextResponse { data }) } // ------------------------------------------------------------------------------------- diff --git a/wallet/core/src/wallet/args.rs b/wallet/core/src/wallet/args.rs index a5fa378bb..f0168f740 100644 --- a/wallet/core/src/wallet/args.rs +++ b/wallet/core/src/wallet/args.rs @@ -3,7 +3,6 @@ //! use crate::imports::*; -// use crate::secret::Secret; use crate::storage::interface::CreateArgs; use crate::storage::{Hint, PrvKeyDataId}; use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/wallet/core/src/wallet/maps.rs b/wallet/core/src/wallet/maps.rs index 430f54e56..232e6fbeb 100644 --- a/wallet/core/src/wallet/maps.rs +++ b/wallet/core/src/wallet/maps.rs @@ -5,6 +5,7 @@ use crate::imports::*; +/// A thread-safe map of [`AccountId`] to [`Account`] instances. #[derive(Default, Clone)] pub struct ActiveAccountMap(Arc>>>); diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index d5f4dfadd..31393a8ee 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -59,6 +59,7 @@ pub struct MultisigWalletFileV0<'a, T: AsRef<[u8]>> { pub encrypted_mnemonics: Vec>, pub xpublic_keys: Vec<&'a str>, // includes pub keys from encrypted pub required_signatures: u16, + #[allow(dead_code)] pub cosigner_index: u8, pub ecdsa: bool, } @@ -68,6 +69,7 @@ pub struct MultisigWalletFileV1<'a, T: AsRef<[u8]>> { pub encrypted_mnemonics: Vec>, pub xpublic_keys: Vec<&'a str>, // includes pub keys from encrypted pub required_signatures: u16, + #[allow(dead_code)] pub cosigner_index: u8, pub ecdsa: bool, } @@ -81,7 +83,8 @@ pub enum WalletBusMessage { Discovery { record: TransactionRecord }, } -pub struct Inner { +/// Internal wallet state. +struct Inner { active_accounts: ActiveAccountMap, legacy_accounts: ActiveAccountMap, listener_id: Mutex>, @@ -189,10 +192,6 @@ impl Wallet { self } - pub fn inner(&self) -> &Arc { - &self.inner - } - // // Mutex used to protect concurrent access to accounts // at the wallet api level. This is a global lock that diff --git a/wallet/core/src/wasm/cryptobox.rs b/wallet/core/src/wasm/cryptobox.rs index 118020fe4..957d4fc35 100644 --- a/wallet/core/src/wasm/cryptobox.rs +++ b/wallet/core/src/wasm/cryptobox.rs @@ -106,7 +106,7 @@ impl std::ops::Deref for CryptoBoxPublicKey { /// /// CryptoBox allows for encrypting and decrypting messages using the `crypto_box` crate. /// -/// https://docs.rs/crypto_box/0.9.1/crypto_box/ +/// /// /// @category Wallet SDK /// diff --git a/wallet/core/src/wasm/signer.rs b/wallet/core/src/wasm/signer.rs index e00729ef5..157f06d90 100644 --- a/wallet/core/src/wasm/signer.rs +++ b/wallet/core/src/wasm/signer.rs @@ -50,7 +50,7 @@ pub fn js_sign_transaction(tx: &Transaction, signer: &PrivateKeyArrayT, verify_s } } -pub fn sign_transaction<'a>(tx: &'a Transaction, private_keys: &[[u8; 32]], verify_sig: bool) -> Result<&'a Transaction> { +fn sign_transaction<'a>(tx: &'a Transaction, private_keys: &[[u8; 32]], verify_sig: bool) -> Result<&'a Transaction> { let tx = sign(tx, private_keys)?; if verify_sig { let (cctx, utxos) = tx.tx_and_utxos()?; @@ -97,7 +97,7 @@ pub fn sign_script_hash(script_hash: JsValue, privkey: &PrivateKey) -> Result Result> { +fn sign_hash(sig_hash: Hash, privkey: &[u8; 32]) -> Result> { let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice())?; let schnorr_key = secp256k1::Keypair::from_seckey_slice(secp256k1::SECP256K1, privkey)?; let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref(); diff --git a/wallet/keys/src/derivation/gen0/mod.rs b/wallet/keys/src/derivation/gen0/mod.rs index d25032626..0de5da286 100644 --- a/wallet/keys/src/derivation/gen0/mod.rs +++ b/wallet/keys/src/derivation/gen0/mod.rs @@ -1,4 +1,4 @@ -//! Derivation management for legacy account derivation scheme `972` +//! Derivation management for legacy account derivation scheme based on `'972` derivation path (deprecated). mod hd; pub use hd::{PubkeyDerivationManagerV0, WalletDerivationManagerV0}; diff --git a/wallet/keys/src/derivation/gen1/mod.rs b/wallet/keys/src/derivation/gen1/mod.rs index 1822c4d7a..5ec859b59 100644 --- a/wallet/keys/src/derivation/gen1/mod.rs +++ b/wallet/keys/src/derivation/gen1/mod.rs @@ -1,3 +1,4 @@ -/// Derivation management for the Kaspa standard derivation scheme `111111'` +//! Derivation management for the Kaspa standard derivation scheme `'111111'` + mod hd; pub use hd::{PubkeyDerivationManager, WalletDerivationManager}; diff --git a/wallet/keys/src/derivation/mod.rs b/wallet/keys/src/derivation/mod.rs index a63201194..cfa80b4f7 100644 --- a/wallet/keys/src/derivation/mod.rs +++ b/wallet/keys/src/derivation/mod.rs @@ -1,3 +1,7 @@ +//! +//! Derivation utilities used by the integrated Kaspa Wallet API. +//! + pub mod gen0; pub mod gen1; pub mod traits; diff --git a/wallet/keys/src/derivation_path.rs b/wallet/keys/src/derivation_path.rs index df220ee44..a5389ca37 100644 --- a/wallet/keys/src/derivation_path.rs +++ b/wallet/keys/src/derivation_path.rs @@ -1,7 +1,13 @@ +//! +//! Implementation of the [`DerivationPath`] manager for arbitrary derivation paths. +//! + use crate::imports::*; use workflow_wasm::prelude::*; +/// /// Key derivation path +/// /// @category Wallet SDK #[derive(Clone, CastFromJs)] #[wasm_bindgen] diff --git a/wallet/keys/src/keypair.rs b/wallet/keys/src/keypair.rs index f4b39f3d3..2cc3d5760 100644 --- a/wallet/keys/src/keypair.rs +++ b/wallet/keys/src/keypair.rs @@ -2,6 +2,8 @@ //! [`keypair`](mod@self) module encapsulates [`Keypair`] and [`PrivateKey`]. //! The [`Keypair`] provides access to the secret and public keys. //! +//! # JavaScript Example +//! //! ```javascript //! //! let keypair = Keypair.random(); @@ -56,7 +58,8 @@ impl Keypair { } /// Get the [`Address`] of this Keypair's [`PublicKey`]. - /// Receives a [`NetworkType`] to determine the prefix of the address. + /// Receives a [`NetworkType`](kaspa_consensus_core::network::NetworkType) + /// to determine the prefix of the address. /// JavaScript: `let address = keypair.toAddress(NetworkType.MAINNET);`. #[wasm_bindgen(js_name = toAddress)] // pub fn to_address(&self, network_type: NetworkType) -> Result
{ @@ -67,7 +70,8 @@ impl Keypair { } /// Get `ECDSA` [`Address`] of this Keypair's [`PublicKey`]. - /// Receives a [`NetworkType`] to determine the prefix of the address. + /// Receives a [`NetworkType`](kaspa_consensus_core::network::NetworkType) + /// to determine the prefix of the address. /// JavaScript: `let address = keypair.toAddress(NetworkType.MAINNET);`. #[wasm_bindgen(js_name = toAddressECDSA)] pub fn to_address_ecdsa(&self, network: &NetworkTypeT) -> Result
{ diff --git a/wallet/keys/src/lib.rs b/wallet/keys/src/lib.rs index bec8747d0..86984e36a 100644 --- a/wallet/keys/src/lib.rs +++ b/wallet/keys/src/lib.rs @@ -1,3 +1,10 @@ +//! +//! # Kaspa Wallet Keys +//! +//! This crate provides tools for creating and managing Kaspa wallet keys. +//! This includes extended key generation and derivation. +//! + pub mod derivation; pub mod derivation_path; pub mod error; diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index 1aed7c535..a176f4038 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -1,3 +1,7 @@ +//! +//! Glob imports for the `kaspa_wallet_keys` crate. +//! + pub use crate::derivation_path::*; pub use crate::keypair::*; pub use crate::privatekey::*; diff --git a/wallet/keys/src/privatekey.rs b/wallet/keys/src/privatekey.rs index 84e2d2e3b..554bdf36e 100644 --- a/wallet/keys/src/privatekey.rs +++ b/wallet/keys/src/privatekey.rs @@ -68,7 +68,8 @@ impl PrivateKey { } /// Get the [`Address`] of the PublicKey generated from this PrivateKey. - /// Receives a [`NetworkType`] to determine the prefix of the address. + /// Receives a [`NetworkType`](kaspa_consensus_core::network::NetworkType) + /// to determine the prefix of the address. /// JavaScript: `let address = privateKey.toAddress(NetworkType.MAINNET);`. #[wasm_bindgen(js_name = toAddress)] pub fn to_address(&self, network: &NetworkTypeT) -> Result
{ @@ -80,7 +81,8 @@ impl PrivateKey { } /// Get `ECDSA` [`Address`] of the PublicKey generated from this PrivateKey. - /// Receives a [`NetworkType`] to determine the prefix of the address. + /// Receives a [`NetworkType`](kaspa_consensus_core::network::NetworkType) + /// to determine the prefix of the address. /// JavaScript: `let address = privateKey.toAddress(NetworkType.MAINNET);`. #[wasm_bindgen(js_name = toAddressECDSA)] pub fn to_address_ecdsa(&self, network: &NetworkTypeT) -> Result
{ diff --git a/wallet/keys/src/privkeygen.rs b/wallet/keys/src/privkeygen.rs index ff2f3bd8f..474dec8ec 100644 --- a/wallet/keys/src/privkeygen.rs +++ b/wallet/keys/src/privkeygen.rs @@ -1,3 +1,7 @@ +//! +//! [`PrivateKeyGenerator`] helper for generating private key derivations from an extended private key (XPrv). +//! + use crate::derivation::gen1::WalletDerivationManager; use crate::imports::*; diff --git a/wallet/keys/src/pubkeygen.rs b/wallet/keys/src/pubkeygen.rs index c05ae844f..a61eeb5ad 100644 --- a/wallet/keys/src/pubkeygen.rs +++ b/wallet/keys/src/pubkeygen.rs @@ -1,3 +1,7 @@ +//! +//! [`PublicKeyGenerator`] helper for generating public key derivations from an extended public key (XPub). +//! + use crate::derivation::gen1::WalletDerivationManager; use crate::derivation::traits::WalletDerivationManagerTrait; use crate::imports::*; diff --git a/wallet/keys/src/publickey.rs b/wallet/keys/src/publickey.rs index d63eca491..235eb8080 100644 --- a/wallet/keys/src/publickey.rs +++ b/wallet/keys/src/publickey.rs @@ -1,6 +1,6 @@ //! -//! [`keypair`](mod@self) module encapsulates [`Keypair`] and [`PrivateKey`]. -//! The [`Keypair`] provides access to the secret and public keys. +//! [`keypair`](mod@self) module encapsulates [`Keypair`](crate::keypair::Keypair) and [`PrivateKey`]. +//! The [`Keypair`](crate::keypair::Keypair) provides access to the secret and public keys. //! //! ```javascript //! diff --git a/wallet/keys/src/secret.rs b/wallet/keys/src/secret.rs index 99d94f5be..d934ce4ec 100644 --- a/wallet/keys/src/secret.rs +++ b/wallet/keys/src/secret.rs @@ -1,10 +1,10 @@ //! -//! Secret container for sensitive data. Performs zeroization on drop. +//! Secret container for sensitive data. Performs data erasure (zeroization) on drop. //! use crate::imports::*; -/// Secret container for sensitive data. Performs memory zeroization on drop. +/// Secret container for sensitive data. Performs memory erasure (zeroization) on drop. #[derive(Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct Secret(Vec); diff --git a/wallet/keys/src/types.rs b/wallet/keys/src/types.rs index c006d2872..11c567834 100644 --- a/wallet/keys/src/types.rs +++ b/wallet/keys/src/types.rs @@ -1,5 +1,5 @@ //! -//! Type aliases used by the wallet framework. +//! Key-related type aliases used by the wallet framework. //! use std::sync::Arc; diff --git a/wallet/keys/src/xprv.rs b/wallet/keys/src/xprv.rs index 3e120841b..c19e0b9cc 100644 --- a/wallet/keys/src/xprv.rs +++ b/wallet/keys/src/xprv.rs @@ -1,3 +1,7 @@ +//! +//! Extended private key ([`XPrv`]). +//! + use kaspa_bip32::{ChainCode, KeyFingerprint}; use crate::imports::*; diff --git a/wallet/keys/src/xpub.rs b/wallet/keys/src/xpub.rs index 551881d8e..8706f3fc9 100644 --- a/wallet/keys/src/xpub.rs +++ b/wallet/keys/src/xpub.rs @@ -1,3 +1,7 @@ +//! +//! Extended public key ([`XPub`]). +//! + use kaspa_bip32::{ChainCode, KeyFingerprint, Prefix}; use std::{fmt, str::FromStr}; diff --git a/wallet/pskt/src/bundle.rs b/wallet/pskt/src/bundle.rs index 6e8dc8350..6c926c666 100644 --- a/wallet/pskt/src/bundle.rs +++ b/wallet/pskt/src/bundle.rs @@ -13,6 +13,11 @@ use kaspa_txscript::{extract_script_pub_key_address, pay_to_address_script, pay_ use serde::{Deserialize, Serialize}; use std::ops::Deref; +/// +/// Bundle is a [`PSKT`] bundle - a sequence of PSKT transactions +/// meant for batch processing and transport as a +/// single serialized payload. +/// #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Bundle(pub Vec); diff --git a/wallet/pskt/src/convert.rs b/wallet/pskt/src/convert.rs index 18acf94ed..a3956c3bb 100644 --- a/wallet/pskt/src/convert.rs +++ b/wallet/pskt/src/convert.rs @@ -1,3 +1,9 @@ +//! +//! Conversion functions for converting between +//! the [`kaspa_consensus_client`], [`kaspa_consensus_core`] +//! and [`kaspa_wallet_pskt`](crate) types. +//! + use crate::error::Error; use crate::input::{Input, InputBuilder}; use crate::output::{Output, OutputBuilder}; diff --git a/wallet/pskt/src/error.rs b/wallet/pskt/src/error.rs index 11303ae4a..f3fd83570 100644 --- a/wallet/pskt/src/error.rs +++ b/wallet/pskt/src/error.rs @@ -1,3 +1,5 @@ +//! Error types for the PSKT crate. + use kaspa_txscript_errors::TxScriptError; use crate::input::InputBuilderError; diff --git a/wallet/pskt/src/global.rs b/wallet/pskt/src/global.rs index b79798776..ad98f11d3 100644 --- a/wallet/pskt/src/global.rs +++ b/wallet/pskt/src/global.rs @@ -1,3 +1,5 @@ +//! Global PSKT data. + use crate::pskt::{KeySource, Version}; use crate::utils::combine_if_no_conflicts; use derive_builder::Builder; diff --git a/wallet/pskt/src/input.rs b/wallet/pskt/src/input.rs index c99ae2542..8d01a7d48 100644 --- a/wallet/pskt/src/input.rs +++ b/wallet/pskt/src/input.rs @@ -1,3 +1,5 @@ +//! PSKT input structure. + use crate::pskt::{KeySource, PartialSigs}; use crate::utils::{combine_if_no_conflicts, Error as CombineMapErr}; use derive_builder::Builder; diff --git a/wallet/pskt/src/output.rs b/wallet/pskt/src/output.rs index e873ce4a6..36b09edae 100644 --- a/wallet/pskt/src/output.rs +++ b/wallet/pskt/src/output.rs @@ -1,3 +1,5 @@ +//! PSKT output structure. + use crate::pskt::KeySource; use crate::utils::combine_if_no_conflicts; use derive_builder::Builder; diff --git a/wallet/pskt/src/pskt.rs b/wallet/pskt/src/pskt.rs index 245609803..73f87a628 100644 --- a/wallet/pskt/src/pskt.rs +++ b/wallet/pskt/src/pskt.rs @@ -1,3 +1,7 @@ +//! +//! Partially Signed Kaspa Transaction (PSKT) +//! + use kaspa_bip32::{secp256k1, DerivationPath, KeyFingerprint}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -76,6 +80,23 @@ impl Signature { } } +/// +/// A Partially Signed Kaspa Transaction (PSKT) is a standardized format +/// that allows multiple participants to collaborate in creating and signing +/// a Kaspa transaction. PSKT enables the exchange of incomplete transaction +/// data between different wallets or entities, allowing each participant +/// to add their signature or inputs in stages. This facilitates more complex +/// transaction workflows, such as multi-signature setups or hardware wallet +/// interactions, by ensuring that sensitive data remains secure while +/// enabling cooperation across different devices or platforms without +/// exposing private keys. +/// +/// Please note that due to transaction mass limits and potential of +/// a wallet aggregating large UTXO sets, the PSKT [`Bundle`](crate::bundle::Bundle) primitive +/// is used to represent a collection of PSKTs and should be used for +/// PSKT serialization and transport. PSKT is an internal implementation +/// primitive that represents each transaction in the bundle. +/// #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PSKT { diff --git a/wallet/pskt/src/role.rs b/wallet/pskt/src/role.rs index 84f55bb04..2d6daa47d 100644 --- a/wallet/pskt/src/role.rs +++ b/wallet/pskt/src/role.rs @@ -1,3 +1,5 @@ +//! PSKT roles. + /// Initializes the PSKT with 0 inputs and 0 outputs. /// Reference: [BIP-370: Creator](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#creator) pub enum Creator {} diff --git a/wallet/pskt/src/utils.rs b/wallet/pskt/src/utils.rs index 28b7959ed..d7acbba6f 100644 --- a/wallet/pskt/src/utils.rs +++ b/wallet/pskt/src/utils.rs @@ -1,3 +1,5 @@ +//! Utility functions for the PSKT module. + use std::collections::BTreeMap; // todo optimize without cloning From b92e273b7a7565effb89df8b06bb4027c2c0eaac Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 26 Sep 2024 20:22:48 +0300 Subject: [PATCH 2/8] Export ConsensusSessionOwned --- components/consensusmanager/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/consensusmanager/src/lib.rs b/components/consensusmanager/src/lib.rs index 54bdda40b..c7ea2db9b 100644 --- a/components/consensusmanager/src/lib.rs +++ b/components/consensusmanager/src/lib.rs @@ -10,6 +10,7 @@ mod session; pub use batch::BlockProcessingBatch; pub use session::{ spawn_blocking, ConsensusInstance, ConsensusProxy, ConsensusSessionBlocking, SessionLock, SessionReadGuard, SessionWriteGuard, + ConsensusSessionOwned, }; /// Consensus controller trait. Includes methods required to start/stop/control consensus, but which should not From cd3af62f7c8d377c2fd1c44c7f5a8d2a67dfbfe0 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 26 Sep 2024 20:23:31 +0300 Subject: [PATCH 3/8] add CI pass to run `cargo doc` --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 693266885..49fe4e463 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -126,6 +126,9 @@ jobs: - name: Run cargo doc tests with features=no-asm on kaspa-hashes run: cargo test --doc --release -p kaspa-hashes --features=no-asm + - name: Run cargo doc + run: cargo doc --release --no-deps --workspace + # test-release: # name: Test Suite Release # runs-on: ${{ matrix.os }} From eed721817029f6f315db3723ff98d13bf5d2961c Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 26 Sep 2024 20:23:52 +0300 Subject: [PATCH 4/8] module rust docs --- cli/src/modules/connect.rs | 4 +- components/consensusmanager/src/session.rs | 9 ++-- consensus/client/src/error.rs | 2 + consensus/client/src/hash.rs | 7 +++ consensus/client/src/header.rs | 11 ++++ consensus/client/src/input.rs | 13 +++++ consensus/client/src/lib.rs | 14 +++++ consensus/client/src/outpoint.rs | 53 ++++++++++++------- consensus/client/src/output.rs | 13 +++++ consensus/client/src/result.rs | 2 + consensus/client/src/serializable/mod.rs | 22 ++++++++ consensus/client/src/serializable/numeric.rs | 8 ++- consensus/client/src/serializable/string.rs | 5 +- consensus/client/src/sign.rs | 4 ++ consensus/client/src/transaction.rs | 7 +++ consensus/client/src/utils.rs | 6 +++ consensus/client/src/utxo.rs | 17 ++++++ consensus/core/src/lib.rs | 6 +++ consensus/core/src/network.rs | 13 +++++ consensus/core/src/tx.rs | 8 +++ consensus/src/processes/ghostdag/protocol.rs | 2 +- .../src/processes/reachability/interval.rs | 4 +- crypto/addresses/src/lib.rs | 24 ++++++++- indexes/utxoindex/src/core/errors.rs | 4 +- indexes/utxoindex/src/index.rs | 5 +- .../src/mempool/model/frontier/search_tree.rs | 5 +- .../flows/src/flowcontext/transactions.rs | 2 +- rpc/core/src/api/connection.rs | 4 ++ rpc/core/src/api/ctl.rs | 4 ++ rpc/core/src/api/mod.rs | 4 ++ rpc/core/src/api/notifications.rs | 4 ++ rpc/core/src/api/ops.rs | 4 ++ rpc/core/src/api/rpc.rs | 8 +-- rpc/core/src/convert/block.rs | 2 + rpc/core/src/convert/mod.rs | 4 ++ rpc/core/src/convert/notification.rs | 2 + rpc/core/src/convert/scope.rs | 2 + rpc/core/src/convert/tx.rs | 2 + rpc/core/src/convert/utxo.rs | 2 + rpc/core/src/error.rs | 4 ++ rpc/core/src/lib.rs | 14 +++++ rpc/core/src/model/mod.rs | 3 ++ rpc/core/src/notify/mod.rs | 4 ++ rpc/core/src/wasm/convert.rs | 4 ++ rpc/core/src/wasm/message.rs | 4 ++ rpc/core/src/wasm/mod.rs | 2 + rpc/grpc/client/src/lib.rs | 2 +- rpc/grpc/core/src/convert/message.rs | 2 +- rpc/wrpc/client/src/client.rs | 13 +++-- rpc/wrpc/client/src/error.rs | 2 + rpc/wrpc/client/src/lib.rs | 16 ++++++ rpc/wrpc/client/src/node.rs | 2 + rpc/wrpc/client/src/parse.rs | 2 + rpc/wrpc/client/src/prelude.rs | 2 + rpc/wrpc/client/src/resolver.rs | 25 +++++++-- rpc/wrpc/client/src/result.rs | 2 + rpc/wrpc/wasm/src/client.rs | 9 +++- rpc/wrpc/wasm/src/lib.rs | 4 ++ rpc/wrpc/wasm/src/notify.rs | 4 ++ rpc/wrpc/wasm/src/resolver.rs | 4 ++ simpa/src/main.rs | 2 +- utils/src/lib.rs | 6 +++ wallet/bip32/src/lib.rs | 2 + wallet/bip32/src/private_key.rs | 1 + wallet/bip32/src/public_key.rs | 2 +- wallet/core/src/api/mod.rs | 2 + wallet/core/src/api/traits.rs | 10 +++- wallet/core/src/tx/payment.rs | 10 ++++ wallet/core/src/wallet/api.rs | 2 +- wallet/core/src/wallet/mod.rs | 10 +++- wasm/src/lib.rs | 31 ++++++++--- 71 files changed, 449 insertions(+), 65 deletions(-) diff --git a/cli/src/modules/connect.rs b/cli/src/modules/connect.rs index 024f7e693..a755915d4 100644 --- a/cli/src/modules/connect.rs +++ b/cli/src/modules/connect.rs @@ -14,11 +14,11 @@ impl Connect { let (is_public, url) = match arg_or_server_address.as_deref() { Some("public") => { tprintln!(ctx, "Connecting to a public node"); - (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) + (true, Resolver::default().get_url(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?) } None => { tprintln!(ctx, "No server set, connecting to a public node"); - (true, Resolver::default().fetch(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?.url) + (true, Resolver::default().get_url(WrpcEncoding::Borsh, network_id).await.map_err(|e| e.to_string())?) } Some(url) => { (false, wrpc_client.parse_url_with_network_type(url.to_string(), network_id.into()).map_err(|e| e.to_string())?) diff --git a/components/consensusmanager/src/session.rs b/components/consensusmanager/src/session.rs index 2643739ee..8e0c6e933 100644 --- a/components/consensusmanager/src/session.rs +++ b/components/consensusmanager/src/session.rs @@ -91,7 +91,7 @@ impl ConsensusInstance { /// Returns an unguarded *blocking* consensus session. There's no guarantee that data will not be pruned between /// two sequential consensus calls. This session doesn't hold the consensus pruning lock, so it should - /// be preferred upon [`session_blocking`] when data consistency is not important. + /// be preferred upon [`session_blocking()`](Self::session_blocking) when data consistency is not important. pub fn unguarded_session_blocking(&self) -> ConsensusSessionBlocking<'static> { ConsensusSessionBlocking::new_without_session_guard(self.consensus.clone()) } @@ -100,7 +100,7 @@ impl ConsensusInstance { /// that consensus state is consistent between operations, that is, no pruning was performed between the calls. /// The returned object is an *owned* consensus session type which can be cloned and shared across threads. /// The sharing ability is useful for spawning blocking operations on a different thread using the same - /// session object, see [`ConsensusSessionOwned::spawn_blocking`]. The caller is responsible to make sure + /// session object, see [`ConsensusSessionOwned::spawn_blocking()`](ConsensusSessionOwned::spawn_blocking). The caller is responsible to make sure /// that the overall lifetime of this session is not too long (~2 seconds max) pub async fn session(&self) -> ConsensusSessionOwned { let g = self.session_lock.read_owned().await; @@ -109,7 +109,7 @@ impl ConsensusInstance { /// Returns an unguarded consensus session. There's no guarantee that data will not be pruned between /// two sequential consensus calls. This session doesn't hold the consensus pruning lock, so it should - /// be preferred upon [`session`] when data consistency is not important. + /// be preferred upon [`session()`](Self::session) when data consistency is not important. pub fn unguarded_session(&self) -> ConsensusSessionOwned { ConsensusSessionOwned::new_without_session_guard(self.consensus.clone()) } @@ -139,7 +139,8 @@ impl Deref for ConsensusSessionBlocking<'_> { } /// An *owned* consensus session type which can be cloned and shared across threads. -/// See method `spawn_blocking` within for context on the usefulness of this type +/// See method `spawn_blocking` within for context on the usefulness of this type. +/// Please note - you must use [`ConsensusProxy`] type alias instead of this struct. #[derive(Clone)] pub struct ConsensusSessionOwned { _session_guard: Option, diff --git a/consensus/client/src/error.rs b/consensus/client/src/error.rs index e0aab2156..e632f517d 100644 --- a/consensus/client/src/error.rs +++ b/consensus/client/src/error.rs @@ -1,3 +1,5 @@ +//! The [`Error`](enum@Error) enum used by this crate + use thiserror::Error; use wasm_bindgen::{JsError, JsValue}; use workflow_wasm::jserror::JsErrorData; diff --git a/consensus/client/src/hash.rs b/consensus/client/src/hash.rs index 4402cfb1b..950194bb1 100644 --- a/consensus/client/src/hash.rs +++ b/consensus/client/src/hash.rs @@ -1,3 +1,10 @@ +//! +//! WASM bindings for transaction hashers: [`TransactionSigningHash`](native::TransactionSigningHash) +//! and [`TransactionSigningHashECDSA`](native::TransactionSigningHashECDSA). +//! + +#![allow(non_snake_case)] + use crate::imports::*; use crate::result::Result; use kaspa_hashes as native; diff --git a/consensus/client/src/header.rs b/consensus/client/src/header.rs index 56fd92845..c54e95d11 100644 --- a/consensus/client/src/header.rs +++ b/consensus/client/src/header.rs @@ -1,3 +1,9 @@ +//! +//! Implementation of the Block [`Header`] struct. +//! + +#![allow(non_snake_case)] + use crate::error::Error; use js_sys::{Array, Object}; use kaspa_consensus_core::hashing; @@ -59,10 +65,15 @@ export interface IRawHeader { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type definition for the Header-like struct: `Header | IHeader | IRawHeader`. + /// + /// @category Consensus #[wasm_bindgen(typescript_type = "Header | IHeader | IRawHeader")] pub type HeaderT; } +/// Kaspa Block Header +/// /// @category Consensus #[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)] #[serde(rename_all = "camelCase")] diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index 736696bfa..cd718375c 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -1,3 +1,9 @@ +//! +//! Implementation of the client-side [`TransactionInput`] struct used by the client-side [`Transaction`] struct. +//! + +#![allow(non_snake_case)] + use crate::imports::*; use crate::result::Result; use crate::TransactionOutpoint; @@ -33,14 +39,21 @@ export interface ITransactionInputVerboseData { } #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type representing `ITransactionInput | TransactionInput` + /// @category Consensus #[wasm_bindgen(typescript_type = "ITransactionInput | TransactionInput")] pub type TransactionInputT; + /// WASM (TypeScript) type representing `ITransactionInput[] | TransactionInput[]` + /// @category Consensus #[wasm_bindgen(typescript_type = "(ITransactionInput | TransactionInput)[]")] pub type TransactionInputArrayAsArgT; + /// WASM (TypeScript) type representing `TransactionInput[]` + /// @category Consensus #[wasm_bindgen(typescript_type = "TransactionInput[]")] pub type TransactionInputArrayAsResultT; } +/// Inner type used by [`TransactionInput`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionInputInner { diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index eb482eab1..5056454ac 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -1,3 +1,17 @@ +//! +//! # Client-side consensus primitives. +//! +//! This crate offers client-side primitives mirroring the consensus layer of the Kaspa p2p node. +//! It declares structs such as [`Transaction`], [`TransactionInput`], [`TransactionOutput`], +//! [`TransactionOutpoint`], [`UtxoEntry`], and [`UtxoEntryReference`] +//! that are used by the Wallet subsystem as well as WASM bindings. +//! +//! Unlike raw consensus primitives (used for high-performance DAG processing) the primitives +//! offered in this crate are designed to be used in client-side applications. Their internal +//! data is typically wrapped into `Arc>`, allowing for easy sharing between +//! async / threaded environments and WASM bindings. +//! + pub mod error; mod imports; mod input; diff --git a/consensus/client/src/outpoint.rs b/consensus/client/src/outpoint.rs index 06be53f6a..416d0bff6 100644 --- a/consensus/client/src/outpoint.rs +++ b/consensus/client/src/outpoint.rs @@ -1,3 +1,11 @@ +//! +//! Implementation of the client-side [`TransactionOutpoint`] used by the [`TransactionInput`] struct. +//! + +#![allow(non_snake_case)] + +use cfg_if::cfg_if; + use crate::imports::*; use crate::result::Result; @@ -14,6 +22,7 @@ export interface ITransactionOutpoint { } "#; +/// Inner type used by [`TransactionOutpoint`] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Ord, PartialOrd)] #[serde(rename_all = "camelCase")] pub struct TransactionOutpointInner { @@ -110,29 +119,35 @@ impl TransactionOutpoint { } } -#[cfg_attr(feature = "wasm32-sdk", wasm_bindgen)] -impl TransactionOutpoint { - #[cfg_attr(feature = "wasm32-sdk", wasm_bindgen(constructor))] - pub fn ctor(transaction_id: TransactionId, index: u32) -> TransactionOutpoint { - Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) } - } - - #[cfg_attr(feature = "wasm32-sdk", wasm_bindgen(js_name = "getId"))] - pub fn id_string(&self) -> String { - format!("{}-{}", self.get_transaction_id_as_string(), self.get_index()) - } +cfg_if! { + if #[cfg(feature = "wasm32-sdk")] { - #[cfg_attr(feature = "wasm32-sdk", wasm_bindgen(getter, js_name = transactionId))] - pub fn get_transaction_id_as_string(&self) -> String { - self.inner().transaction_id.to_string() - } - - #[cfg_attr(feature = "wasm32-sdk", wasm_bindgen(getter, js_name = index))] - pub fn get_index(&self) -> TransactionIndexType { - self.inner().index + #[wasm_bindgen] + impl TransactionOutpoint { + #[wasm_bindgen(constructor)] + pub fn ctor(transaction_id: TransactionId, index: u32) -> TransactionOutpoint { + Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) } + } + + #[wasm_bindgen(js_name = "getId")] + pub fn id_string(&self) -> String { + format!("{}-{}", self.get_transaction_id_as_string(), self.get_index()) + } + + #[wasm_bindgen(getter, js_name = transactionId)] + pub fn get_transaction_id_as_string(&self) -> String { + self.inner().transaction_id.to_string() + } + + #[wasm_bindgen(getter, js_name = index)] + pub fn get_index(&self) -> TransactionIndexType { + self.inner().index + } + } } } + impl std::fmt::Display for TransactionOutpoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let inner = self.inner(); diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 8f335c47d..48ec3227f 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -1,3 +1,9 @@ +//! +//! Implementation of the client-side [`TransactionOutput`] used by the [`Transaction`] struct. +//! + +#![allow(non_snake_case)] + use crate::imports::*; #[wasm_bindgen(typescript_custom_section)] @@ -28,14 +34,21 @@ export interface ITransactionOutputVerboseData { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type representing `ITransactionOutput | TransactionOutput` + /// @category Consensus #[wasm_bindgen(typescript_type = "ITransactionOutput | TransactionOutput")] pub type TransactionOutputT; + /// WASM (TypeScript) type representing `ITransactionOutput[] | TransactionOutput[]` + /// @category Consensus #[wasm_bindgen(typescript_type = "(ITransactionOutput | TransactionOutput)[]")] pub type TransactionOutputArrayAsArgT; + /// WASM (TypeScript) type representing `TransactionOutput[]` + /// @category Consensus #[wasm_bindgen(typescript_type = "TransactionOutput[]")] pub type TransactionOutputArrayAsResultT; } +/// Inner type used by [`TransactionOutput`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionOutputInner { diff --git a/consensus/client/src/result.rs b/consensus/client/src/result.rs index 4c8cb83f5..d8bff8aa1 100644 --- a/consensus/client/src/result.rs +++ b/consensus/client/src/result.rs @@ -1 +1,3 @@ +//! [`Result`] type alias that is bound to the [`Error`](super::error::Error) type from this crate. + pub type Result = std::result::Result; diff --git a/consensus/client/src/serializable/mod.rs b/consensus/client/src/serializable/mod.rs index a590ab286..ee58a6192 100644 --- a/consensus/client/src/serializable/mod.rs +++ b/consensus/client/src/serializable/mod.rs @@ -1,3 +1,24 @@ +//! +//! # Standardized JSON serialization and deserialization of Kaspa transactions. +//! +//! This module provides standardized JSON serialization and deserialization of +//! Kaspa transactions. There are two sub-modules: `numeric` and `string`. +//! +//! The `numeric` module provides serialization and deserialization of transactions +//! with all large integer values as `bigint` types in WASM or numerical values that +//! exceed the largest integer that can be represented by the JavaScript `number` type. +//! +//! The `string` module provides serialization and deserialization of transactions +//! with all large integer values as `string` types. This allows deserialization +//! via JSON in JavaScript environments and later conversion to `bigint` types. +//! +//! These data structures can be used for manual transport of transactions using JSON. +//! For more advanced use cases, please refer to `PSKT` in the [`kaspa_wallet_pskt`](https://docs.rs/kaspa_wallet_pskt) +//! crate. +//! + +#![allow(non_snake_case)] + pub mod numeric; pub mod string; @@ -80,6 +101,7 @@ export interface ISerializableTransaction { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) representation of the `ISerializableTransaction` interface. #[wasm_bindgen(extends = js_sys::Array, typescript_type = "ISerializableTransaction")] pub type SerializableTransactionT; } diff --git a/consensus/client/src/serializable/numeric.rs b/consensus/client/src/serializable/numeric.rs index 733afd54e..3725d1bb1 100644 --- a/consensus/client/src/serializable/numeric.rs +++ b/consensus/client/src/serializable/numeric.rs @@ -1,4 +1,10 @@ -//! This module implements the primitives for external transaction signing. +//! +//! This module implements transaction-related primitives for JSON serialization +//! where all large integer values (`u64`) are serialized to JSON using `serde` and +//! can exceed the largest integer value representable by the JavaScript `number` type. +//! (i.e. transactions serialized using this module can not be deserialized in JavaScript +//! but may be deserialized in other JSON-capable environments that support large integers) +//! use crate::error::Error; use crate::imports::*; diff --git a/consensus/client/src/serializable/string.rs b/consensus/client/src/serializable/string.rs index e35cdb028..af906a1cf 100644 --- a/consensus/client/src/serializable/string.rs +++ b/consensus/client/src/serializable/string.rs @@ -1,4 +1,7 @@ -//! This module implements the primitives for external transaction signing. +//! +//! This module implements transaction-related primitives for JSON serialization +//! where all large integer values (`u64`) are serialized to and from JSON as strings. +//! use crate::imports::*; use crate::result::Result; diff --git a/consensus/client/src/sign.rs b/consensus/client/src/sign.rs index c254aee07..0c28b7913 100644 --- a/consensus/client/src/sign.rs +++ b/consensus/client/src/sign.rs @@ -1,3 +1,7 @@ +//! +//! Utilities for signing transactions. +//! + use crate::transaction::Transaction; use core::iter::once; use itertools::Itertools; diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index fb6d185f0..cccbbc2ad 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -1,3 +1,7 @@ +//! +//! Declares the client-side [`Transaction`] type, which represents a Kaspa transaction. +//! + #![allow(non_snake_case)] use crate::imports::*; @@ -53,10 +57,13 @@ export interface ITransactionVerboseData { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type representing `ITransaction | Transaction` + /// @category Consensus #[wasm_bindgen(typescript_type = "ITransaction | Transaction")] pub type TransactionT; } +/// Inner type used by [`Transaction`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionInner { diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index 4f543d45b..bc3a5c9c1 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -1,3 +1,9 @@ +//! +//! Client-side utility functions and their WASM bindings. +//! + +#![allow(non_snake_case)] + use crate::imports::*; use crate::result::Result; use kaspa_addresses::*; diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index 85bd1547b..f51d5fb64 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -1,3 +1,13 @@ +//! +//! # UTXO client-side data structures. +//! +//! This module provides client-side data structures for UTXO management. +//! In particular, the [`UtxoEntry`] and [`UtxoEntryReference`] structs +//! are used to represent UTXO entries in the wallet subsystem and WASM bindings. +//! + +#![allow(non_snake_case)] + use crate::imports::*; use crate::outpoint::{TransactionOutpoint, TransactionOutpointInner}; use crate::result::Result; @@ -29,16 +39,22 @@ export interface IUtxoEntry { #[wasm_bindgen] extern "C" { + /// WASM type representing an array of [`UtxoEntryReference`] objects (i.e. `UtxoEntryReference[]`) #[wasm_bindgen(extends = Array, typescript_type = "UtxoEntryReference[]")] pub type UtxoEntryReferenceArrayT; + /// WASM type representing a UTXO entry interface (a UTXO-like object) #[wasm_bindgen(typescript_type = "IUtxoEntry")] pub type IUtxoEntry; + /// WASM type representing an array of UTXO entries (i.e. `IUtxoEntry[]`) #[wasm_bindgen(typescript_type = "IUtxoEntry[]")] pub type IUtxoEntryArray; } +/// A UTXO entry Id is a unique identifier for a UTXO entry defined by the `txid+output_index`. pub type UtxoEntryId = TransactionOutpointInner; +/// [`UtxoEntry`] struct represents a client-side UTXO entry. +/// /// @category Wallet SDK #[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)] #[serde(rename_all = "camelCase")] @@ -253,6 +269,7 @@ impl PartialOrd for UtxoEntryReference { } } +/// An extension trait to convert a JS value into a vec of UTXO entry references. pub trait TryIntoUtxoEntryReferences { fn try_into_utxo_entry_references(&self) -> Result>; } diff --git a/consensus/core/src/lib.rs b/consensus/core/src/lib.rs index 46ad3f2ce..d5d295f33 100644 --- a/consensus/core/src/lib.rs +++ b/consensus/core/src/lib.rs @@ -1,3 +1,9 @@ +//! +//! # Consensus Core +//! +//! This crate implements primitives used in the Kaspa node consensus processing. +//! + extern crate alloc; extern crate core; extern crate self as consensus_core; diff --git a/consensus/core/src/network.rs b/consensus/core/src/network.rs index d5e9abd24..48f40175a 100644 --- a/consensus/core/src/network.rs +++ b/consensus/core/src/network.rs @@ -1,3 +1,16 @@ +//! +//! # Network Types +//! +//! This module implements [`NetworkType`] (such as `mainnet`, `testnet`, `devnet`, and `simnet`) +//! and [`NetworkId`] that combines a network type with an optional numerical suffix. +//! +//! The suffix is used to differentiate between multiple networks of the same type and is used +//! explicitly with `testnet` networks, allowing declaration of testnet versions such as +//! `testnet-10`, `testnet-11`, etc. +//! + +#![allow(non_snake_case)] + use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_addresses::Prefix; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index 79babbd74..61587b842 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -1,3 +1,11 @@ +//! +//! # Transaction +//! +//! This module implements consensus [`Transaction`] structure and related types. +//! + +#![allow(non_snake_case)] + mod script_public_key; use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/consensus/src/processes/ghostdag/protocol.rs b/consensus/src/processes/ghostdag/protocol.rs index 87beeb565..8dfe4e793 100644 --- a/consensus/src/processes/ghostdag/protocol.rs +++ b/consensus/src/processes/ghostdag/protocol.rs @@ -91,7 +91,7 @@ impl pub fn ghostdag(&self, parents: &[Hash]) -> GhostdagData { assert!(!parents.is_empty(), "genesis must be added via a call to init"); diff --git a/consensus/src/processes/reachability/interval.rs b/consensus/src/processes/reachability/interval.rs index 9f8d7fbd0..b910f3ddf 100644 --- a/consensus/src/processes/reachability/interval.rs +++ b/consensus/src/processes/reachability/interval.rs @@ -89,7 +89,7 @@ impl Interval { } /// Splits this interval to exactly |sizes| parts where - /// |part_i| = sizes[i]. This method expects sum(sizes) to be exactly + /// |part_i| = sizes\[i\]. This method expects sum(sizes) to be exactly /// equal to the interval's size. pub fn split_exact(&self, sizes: &[u64]) -> Vec { assert_eq!(sizes.iter().sum::(), self.size(), "sum of sizes must be equal to the interval's size"); @@ -107,7 +107,7 @@ impl Interval { /// Splits this interval to |sizes| parts /// by the allocation rule described below. This method expects sum(sizes) /// to be smaller or equal to the interval's size. Every part_i is - /// allocated at least sizes[i] capacity. The remaining budget is + /// allocated at least sizes\[i\] capacity. The remaining budget is /// split by an exponentially biased rule described below. /// /// This rule follows the GHOSTDAG protocol behavior where the child diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index 8aca86386..ec5261323 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -1,3 +1,11 @@ +//! +//! Kaspa [`Address`] implementation. +//! +//! In it's string form, the Kaspa [`Address`] is represented by a `bech32`-encoded +//! address string combined with a network type. The `bech32` string encoding is +//! comprised of a public key, the public key version and the resulting checksum. +//! + use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use smallvec::SmallVec; @@ -11,6 +19,7 @@ use workflow_wasm::{ mod bech32; +/// Error type produced by [`Address`] operations. #[derive(Error, PartialEq, Eq, Debug, Clone)] pub enum AddressError { #[error("The address has an invalid prefix {0}")] @@ -190,7 +199,8 @@ pub const PAYLOAD_VECTOR_SIZE: usize = 36; /// Used as the underlying type for address payload, optimized for the largest version length (33). pub type PayloadVec = SmallVec<[u8; PAYLOAD_VECTOR_SIZE]>; -/// Kaspa `Address` struct that serializes to and from an address format string: `kaspa:qz0s...t8cv`. +/// Kaspa [`Address`] struct that serializes to and from an address format string: `kaspa:qz0s...t8cv`. +/// /// @category Address #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, CastFromJs)] #[wasm_bindgen(inspectable)] @@ -516,12 +526,24 @@ impl TryCastFromJs for Address { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type representing an Address-like object: `Address | string`. + /// + /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "Address | string")] pub type AddressT; + /// WASM (TypeScript) type representing an array of Address-like objects: `(Address | string)[]`. + /// + /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "(Address | string)[]")] pub type AddressOrStringArrayT; + /// WASM (TypeScript) type representing an array of [`Address`] objects: `Address[]`. + /// + /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "Address[]")] pub type AddressArrayT; + /// WASM (TypeScript) type representing an [`Address`] or an undefined value: `Address | undefined`. + /// + /// @category Address #[wasm_bindgen(typescript_type = "Address | undefined")] pub type AddressOrUndefinedT; } diff --git a/indexes/utxoindex/src/core/errors.rs b/indexes/utxoindex/src/core/errors.rs index 61aa877ab..0e0998905 100644 --- a/indexes/utxoindex/src/core/errors.rs +++ b/indexes/utxoindex/src/core/errors.rs @@ -4,7 +4,7 @@ use thiserror::Error; use crate::IDENT; use kaspa_database::prelude::StoreError; -/// Errors originating from the [`UtxoIndex`]. +/// Errors originating from the [`UtxoIndex`](crate::UtxoIndex). #[derive(Error, Debug)] pub enum UtxoIndexError { #[error("[{IDENT}]: {0}")] @@ -14,5 +14,5 @@ pub enum UtxoIndexError { DBResetError(#[from] io::Error), } -/// Results originating from the [`UtxoIndex`]. +/// Results originating from the [`UtxoIndex`](crate::UtxoIndex). pub type UtxoIndexResult = Result; diff --git a/indexes/utxoindex/src/index.rs b/indexes/utxoindex/src/index.rs index b71935afa..4461d3672 100644 --- a/indexes/utxoindex/src/index.rs +++ b/indexes/utxoindex/src/index.rs @@ -21,7 +21,8 @@ use std::{ const RESYNC_CHUNK_SIZE: usize = 2048; //Increased from 1k (used in go-kaspad), for quicker resets, while still having a low memory footprint. -/// UtxoIndex indexes [`CompactUtxoEntryCollections`] by [`ScriptPublicKey`], commits them to its owns store, and emits changes. +/// UtxoIndex indexes `CompactUtxoEntryCollections` by [`ScriptPublicKey`](kaspa_consensus_core::tx::ScriptPublicKey), +/// commits them to its owns store, and emits changes. /// Note: The UtxoIndex struct by itself is not thread save, only correct usage of the supplied RwLock via `new` makes it so. /// please follow guidelines found in the comments under `utxoindex::core::api::UtxoIndexApi` for proper thread safety. pub struct UtxoIndex { @@ -131,7 +132,7 @@ impl UtxoIndexApi for UtxoIndex { /// Deletes and reinstates the utxoindex database, syncing it from scratch via the consensus database. /// /// **Notes:** - /// 1) There is an implicit expectation that the consensus store must have [VirtualParent] tips. i.e. consensus database must be initiated. + /// 1) There is an implicit expectation that the consensus store must have VirtualParent tips. i.e. consensus database must be initiated. /// 2) resyncing while consensus notifies of utxo differences, may result in a corrupted db. fn resync(&mut self) -> UtxoIndexResult<()> { info!("Resyncing the utxoindex..."); diff --git a/mining/src/mempool/model/frontier/search_tree.rs b/mining/src/mempool/model/frontier/search_tree.rs index fc18b2118..dd6910c40 100644 --- a/mining/src/mempool/model/frontier/search_tree.rs +++ b/mining/src/mempool/model/frontier/search_tree.rs @@ -157,8 +157,9 @@ type InnerTree = BPlusTree>; /// is recomputed from subtree weights for each item insertion/removal /// /// Computing the prefix weight is a crucial operation if the tree is used for random sampling and -/// the tree is highly imbalanced in terms of weight variance. See [`Frontier::sample_inplace`] for -/// more details. +/// the tree is highly imbalanced in terms of weight variance. +/// See [`Frontier::sample_inplace()`](crate::mempool::model::frontier::Frontier::sample_inplace) +/// for more details. pub struct SearchTree { tree: InnerTree, } diff --git a/protocol/flows/src/flowcontext/transactions.rs b/protocol/flows/src/flowcontext/transactions.rs index d3112f0af..110b378b7 100644 --- a/protocol/flows/src/flowcontext/transactions.rs +++ b/protocol/flows/src/flowcontext/transactions.rs @@ -73,7 +73,7 @@ impl TransactionsSpread { /// within transaction Inv messages. /// /// The broadcast itself may happen only during a subsequent call to this function since it is done at most - /// every [`BROADCAST_INTERVAL`] milliseconds or when the queue length is larger than the Inv message + /// every `BROADCAST_INTERVAL` milliseconds or when the queue length is larger than the Inv message /// capacity. /// /// _GO-KASPAD: EnqueueTransactionIDsForPropagation_ diff --git a/rpc/core/src/api/connection.rs b/rpc/core/src/api/connection.rs index 5b4254288..3e6597313 100644 --- a/rpc/core/src/api/connection.rs +++ b/rpc/core/src/api/connection.rs @@ -1,3 +1,7 @@ +//! +//! Generic connection trait representing a connection to a client (where available). +//! + use std::sync::Arc; pub trait RpcConnection: Send + Sync { diff --git a/rpc/core/src/api/ctl.rs b/rpc/core/src/api/ctl.rs index 49241e7d9..e55d35fbf 100644 --- a/rpc/core/src/api/ctl.rs +++ b/rpc/core/src/api/ctl.rs @@ -1,3 +1,7 @@ +//! +//! Client-side RPC helper for handling connection and disconnection events. +//! + use crate::error::RpcResult; use std::sync::{Arc, Mutex}; use workflow_core::channel::Multiplexer; diff --git a/rpc/core/src/api/mod.rs b/rpc/core/src/api/mod.rs index 1373bd6e0..6876a92ee 100644 --- a/rpc/core/src/api/mod.rs +++ b/rpc/core/src/api/mod.rs @@ -1,3 +1,7 @@ +//! +//! API module for the RPC server. Implements core RPC primitives. +//! + pub mod connection; pub mod ctl; pub mod notifications; diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index e07a7c4d9..432d89789 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -1,3 +1,7 @@ +//! +//! RPC notifications that can be sent to clients. +//! + use crate::model::message::*; use derive_more::Display; use kaspa_notify::{ diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index 822798a1d..a265a6629 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -1,3 +1,7 @@ +//! +//! RPC Operations used to identify RPC methods during transport and in various RPC-related macros. +//! + use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_notify::events::EventType; use serde::{Deserialize, Serialize}; diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 85713e547..0d752a00d 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -1,8 +1,10 @@ -//! The client API +//! +//! The main [`RpcApi`] trait that defines all RPC methods available in the Rusty Kaspa p2p node. //! //! Rpc = External RPC Service -//! All data provided by the RCP server can be trusted by the client -//! No data submitted by the client to the server can be trusted +//! All data provided by the RPC server can be trusted by the client +//! No data submitted by the client to the node can be trusted +//! use crate::api::connection::DynRpcConnection; use crate::{model::*, notify::connection::ChannelConnection, RpcResult}; diff --git a/rpc/core/src/convert/block.rs b/rpc/core/src/convert/block.rs index 8888fe2bb..8cb0ab01e 100644 --- a/rpc/core/src/convert/block.rs +++ b/rpc/core/src/convert/block.rs @@ -1,3 +1,5 @@ +//! Conversion of Block related types + use std::sync::Arc; use crate::{RpcBlock, RpcError, RpcRawBlock, RpcResult, RpcTransaction}; diff --git a/rpc/core/src/convert/mod.rs b/rpc/core/src/convert/mod.rs index dee1988d5..37c019ea8 100644 --- a/rpc/core/src/convert/mod.rs +++ b/rpc/core/src/convert/mod.rs @@ -1,3 +1,7 @@ +//! +//! Data conversion utilities and structs for the RPC layer. +//! + pub mod block; pub mod notification; pub mod scope; diff --git a/rpc/core/src/convert/notification.rs b/rpc/core/src/convert/notification.rs index 362dd5ed9..6251cc1cd 100644 --- a/rpc/core/src/convert/notification.rs +++ b/rpc/core/src/convert/notification.rs @@ -1,3 +1,5 @@ +//! Conversion of Notification related types + use crate::{ convert::utxo::utxo_set_into_rpc, BlockAddedNotification, FinalityConflictNotification, FinalityConflictResolvedNotification, NewBlockTemplateNotification, Notification, PruningPointUtxoSetOverrideNotification, RpcAcceptedTransactionIds, diff --git a/rpc/core/src/convert/scope.rs b/rpc/core/src/convert/scope.rs index e38f09a1f..6d94de326 100644 --- a/rpc/core/src/convert/scope.rs +++ b/rpc/core/src/convert/scope.rs @@ -1,3 +1,5 @@ +//! Conversion of Notification Scope related types + use crate::{ NotifyBlockAddedRequest, NotifyFinalityConflictRequest, NotifyNewBlockTemplateRequest, NotifyPruningPointUtxoSetOverrideRequest, NotifySinkBlueScoreChangedRequest, NotifyUtxosChangedRequest, NotifyVirtualChainChangedRequest, diff --git a/rpc/core/src/convert/tx.rs b/rpc/core/src/convert/tx.rs index 20d41d674..9b69ca168 100644 --- a/rpc/core/src/convert/tx.rs +++ b/rpc/core/src/convert/tx.rs @@ -1,3 +1,5 @@ +//! Conversion of Transaction related types + use crate::{RpcError, RpcResult, RpcTransaction, RpcTransactionInput, RpcTransactionOutput}; use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutput}; diff --git a/rpc/core/src/convert/utxo.rs b/rpc/core/src/convert/utxo.rs index a0376580d..5fc09f690 100644 --- a/rpc/core/src/convert/utxo.rs +++ b/rpc/core/src/convert/utxo.rs @@ -1,3 +1,5 @@ +//! Conversion functions for UTXO related types. + use crate::RpcUtxoEntry; use crate::RpcUtxosByAddressesEntry; use kaspa_addresses::Prefix; diff --git a/rpc/core/src/error.rs b/rpc/core/src/error.rs index 235ea639e..9d5464dc8 100644 --- a/rpc/core/src/error.rs +++ b/rpc/core/src/error.rs @@ -1,3 +1,7 @@ +//! +//! [`RpcError`] enum used by RPC primitives. +//! + use kaspa_consensus_core::{subnets::SubnetworkConversionError, tx::TransactionId}; use kaspa_utils::networking::IpAddress; use std::{net::AddrParseError, num::TryFromIntError}; diff --git a/rpc/core/src/lib.rs b/rpc/core/src/lib.rs index 66e4ece3a..a8e61b959 100644 --- a/rpc/core/src/lib.rs +++ b/rpc/core/src/lib.rs @@ -1,3 +1,16 @@ +//! # RPC Core +//! +//! This crate provides foundational primitives used in Rusty Kaspa node RPC subsystem. +//! These include the main [`RpcApi`](api::rpc::RpcApi) trait, [`RpcApiOps`](crate::api::ops::RpcApiOps) +//! enum used in RPC method dispatching, and various data structures used in RPC method arguments. +//! +//! This crate acts as a foundation for [`kaspa_grpc_client`](https://docs.rs/kaspa_grpc_client) and +//! [`kaspa_wrpc_client`](https://docs.rs/kaspa_wrpc_client) crates, which provide gRPC and WebSocket +//! RPC client implementations. This crate is also used by WASM bindings to provide [WASM RpcClient +//! implementation](https://docs.rs/kaspa-wrpc-client/latest/kaspa_wrpc_client/wasm/struct.RpcClient.html) +//! (based on wRPC). +//! + // This attribute is required by BorshSerialize/Deserialize #![recursion_limit = "256"] @@ -9,6 +22,7 @@ pub mod notify; pub mod wasm; pub mod prelude { + //! Glob imports of the most commonly used types and traits in this crate. pub use super::api::notifications::*; pub use super::model::script_class::*; pub use super::model::*; diff --git a/rpc/core/src/model/mod.rs b/rpc/core/src/model/mod.rs index beef03257..a7c255624 100644 --- a/rpc/core/src/model/mod.rs +++ b/rpc/core/src/model/mod.rs @@ -1,3 +1,6 @@ +//! This module contains RPC-specific data structures +//! used in RPC methods. + pub mod address; pub mod block; pub mod blue_work; diff --git a/rpc/core/src/notify/mod.rs b/rpc/core/src/notify/mod.rs index 088483e8f..4e8c91fb1 100644 --- a/rpc/core/src/notify/mod.rs +++ b/rpc/core/src/notify/mod.rs @@ -1,3 +1,7 @@ +//! +//! Notification structures used by the RPC subsystem. +//! + pub mod channel; pub mod collector; pub mod connection; diff --git a/rpc/core/src/wasm/convert.rs b/rpc/core/src/wasm/convert.rs index 7bc68171b..6f76e4c1c 100644 --- a/rpc/core/src/wasm/convert.rs +++ b/rpc/core/src/wasm/convert.rs @@ -1,3 +1,7 @@ +//! +//! WASM specific conversion functions +//! + use crate::model::*; use kaspa_consensus_client::*; use std::sync::Arc; diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index 4e330c347..d3a8f57b0 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1,3 +1,7 @@ +//! +//! WASM interfaces and conversion to and from RPC messages. +//! + #![allow(non_snake_case)] use crate::error::RpcError as Error; use crate::error::RpcResult as Result; diff --git a/rpc/core/src/wasm/mod.rs b/rpc/core/src/wasm/mod.rs index 6552baa42..e3bcdc024 100644 --- a/rpc/core/src/wasm/mod.rs +++ b/rpc/core/src/wasm/mod.rs @@ -1,3 +1,5 @@ +//! WASM related conversions + pub mod convert; cfg_if::cfg_if! { diff --git a/rpc/grpc/client/src/lib.rs b/rpc/grpc/client/src/lib.rs index 74db82c4e..00dadee23 100644 --- a/rpc/grpc/client/src/lib.rs +++ b/rpc/grpc/client/src/lib.rs @@ -102,7 +102,7 @@ impl GrpcClient { /// `url`: the server to connect to /// /// `subscription_context`: it is advised to provide a clone of the same instance if multiple clients dealing with - /// [`UtxosChangedNotifications`] are connected concurrently in order to optimize the memory footprint. + /// `UtxosChangedNotifications` are connected concurrently in order to optimize the memory footprint. /// /// `reconnect`: features an automatic reconnection to the server, reactivating all subscriptions on success. /// diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs index c0e75cf03..67ac60650 100644 --- a/rpc/grpc/core/src/convert/message.rs +++ b/rpc/grpc/core/src/convert/message.rs @@ -3,7 +3,7 @@ //! Response payloads in protowire do always contain an error field and generally a set of //! fields providing the requested data. //! -//! Responses in rpc core are expressed as RpcResult, where Xxx is the called +//! Responses in rpc core are expressed as `RpcResult`, where `Xxx` is the called //! RPC method. //! //! The general conversion convention from protowire to rpc core is to consider the error diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 71147fd67..71b2fd878 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -1,3 +1,5 @@ +//! Kaspa wRPC client implementation. + use crate::imports::*; use crate::parse::parse_host; use crate::{error::Error, node::NodeDescriptor}; @@ -246,14 +248,17 @@ impl RpcResolver for Inner { const WRPC_CLIENT: &str = "wrpc-client"; -/// [`KaspaRpcClient`] allows connection to the Kaspa wRPC Server via -/// binary Borsh or JSON protocols. +/// # [`KaspaRpcClient`] connects to Kaspa wRPC endpoint via binary Borsh or JSON protocols. /// /// RpcClient has two ways to interface with the underlying RPC subsystem: /// [`Interface`] that has a [`notification()`](Interface::notification) /// method to register closures that will be invoked on server-side -/// notifications and the [`RpcClient::call`] method that allows async -/// method invocation server-side. +/// notifications and the [`RpcClient::call`] method that allows server-side +/// async method invocation. +/// +/// The node address can be supplied via a URL or a [`Resolver`] that +/// can be used to resolve a public node address dynamically. [`Resolver`] can also +/// be configured to operate against custom node clusters. /// #[derive(Clone)] pub struct KaspaRpcClient { diff --git a/rpc/wrpc/client/src/error.rs b/rpc/wrpc/client/src/error.rs index 781455ddd..657027ed0 100644 --- a/rpc/wrpc/client/src/error.rs +++ b/rpc/wrpc/client/src/error.rs @@ -1,3 +1,5 @@ +//! [`Error`](enum@Error) variants for the wRPC client library. + use thiserror::Error; use wasm_bindgen::JsError; use wasm_bindgen::JsValue; diff --git a/rpc/wrpc/client/src/lib.rs b/rpc/wrpc/client/src/lib.rs index b3f26c425..f37032682 100644 --- a/rpc/wrpc/client/src/lib.rs +++ b/rpc/wrpc/client/src/lib.rs @@ -1,3 +1,19 @@ +//! +//! # wRPC Client for Rusty Kaspa p2p Node +//! +//! This crate provides a WebSocket RPC client for Rusty Kaspa p2p node. It is based on the +//! [wRPC](https://docs.rs/workflow-rpc) crate that offers WebSocket RPC implementation +//! for Rust based on Borsh and Serde JSON serialization. wRPC is a lightweight RPC framework +//! meant to function as an IPC (Inter-Process Communication) mechanism for Rust applications. +//! +//! Rust examples on using wRPC client can be found in the +//! [examples](https://github.com/kaspanet/rusty-kaspa/tree/master/rpc/wrpc/examples) folder. +//! +//! WASM bindings for wRPC client can be found in the [`kaspa-wrpc-wasm`](https://docs.rs/kaspa-wrpc-wasm) crate. +//! +//! The main struct managing Kaspa RPC client connections is the [`KaspaRpcClient`]. +//! + pub mod client; pub mod error; mod imports; diff --git a/rpc/wrpc/client/src/node.rs b/rpc/wrpc/client/src/node.rs index ca7e19c87..f775bfd18 100644 --- a/rpc/wrpc/client/src/node.rs +++ b/rpc/wrpc/client/src/node.rs @@ -1,3 +1,5 @@ +//! Node connection endpoint as provided by the [`Resolver`]. + use crate::imports::*; /// diff --git a/rpc/wrpc/client/src/parse.rs b/rpc/wrpc/client/src/parse.rs index 35db2c768..5a497c507 100644 --- a/rpc/wrpc/client/src/parse.rs +++ b/rpc/wrpc/client/src/parse.rs @@ -1,3 +1,5 @@ +//! wRPC URL parsing and validation utilities. + use std::fmt::Display; use std::net::{Ipv4Addr, Ipv6Addr}; use std::num::ParseIntError; diff --git a/rpc/wrpc/client/src/prelude.rs b/rpc/wrpc/client/src/prelude.rs index 6a410b723..a4598e537 100644 --- a/rpc/wrpc/client/src/prelude.rs +++ b/rpc/wrpc/client/src/prelude.rs @@ -1,3 +1,5 @@ +//! Re-exports of the most commonly used types and traits. + pub use crate::client::{ConnectOptions, ConnectStrategy}; pub use crate::{KaspaRpcClient, Resolver, WrpcEncoding}; pub use kaspa_consensus_core::network::{NetworkId, NetworkType}; diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 8dcb19447..0b9af9f18 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -1,3 +1,7 @@ +//! +//! Module implementing [`Resolver`] client for obtaining public Kaspa wRPC endpoints. +//! + use std::sync::OnceLock; use crate::error::Error; @@ -78,7 +82,12 @@ impl Inner { } /// -/// Resolver is a client for obtaining public Kaspa wRPC endpoints. +/// # Resolver - a client for obtaining public Kaspa wRPC endpoints. +/// +/// This client operates against [Kaspa Resolver](https://github.com/aspectron/kaspa-resolver) service +/// that provides load-balancing and failover capabilities for Kaspa wRPC endpoints. The default +/// configuration allows access to public Kaspa nodes, while custom configurations can be supplied +/// if you are running your own custom Kaspa node cluster. /// #[derive(Debug, Clone)] pub struct Resolver { @@ -92,10 +101,15 @@ impl Default for Resolver { } impl Resolver { + /// Create a new [`Resolver`] client with the specified list of resolver URLs and an optional `tls` flag. + /// The `tls` flag can be used to enforce secure connection to the node. pub fn new(urls: Option>>, tls: bool) -> Self { Self { inner: Arc::new(Inner::new(urls, tls)) } } + /// Obtain a list of URLs in the resolver client. (This function + /// returns `None` if the resolver is configured to use public + /// node endpoints.) pub fn urls(&self) -> Option>> { if self.inner.public { None @@ -104,11 +118,12 @@ impl Resolver { } } + /// Obtain the `tls` flag in the resolver client. pub fn tls(&self) -> bool { self.inner.tls } - pub fn tls_as_str(&self) -> &'static str { + fn tls_as_str(&self) -> &'static str { if self.inner.tls { "tls" } else { @@ -140,6 +155,7 @@ impl Resolver { format!("{url}/v{CURRENT_VERSION}/kaspa/{network_id}/{tls}/wrpc/{encoding}") } + // query a single resolver service async fn fetch_node_info(&self, url: &str, encoding: Encoding, network_id: NetworkId) -> Result { let url = self.make_url(url, encoding, network_id); let node = @@ -147,7 +163,8 @@ impl Resolver { Ok(node) } - pub async fn fetch(&self, encoding: Encoding, network_id: NetworkId) -> Result { + // query multiple resolver services in random order + async fn fetch(&self, encoding: Encoding, network_id: NetworkId) -> Result { let mut urls = self.inner.urls.clone(); urls.shuffle(&mut thread_rng()); @@ -161,10 +178,12 @@ impl Resolver { Err(Error::Custom(format!("Failed to connect: {:?}", errors))) } + /// Obtain a Kaspa p2p [`NodeDescriptor`] from the resolver based on the supplied [`Encoding`] and [`NetworkId`]. pub async fn get_node(&self, encoding: Encoding, network_id: NetworkId) -> Result { self.fetch(encoding, network_id).await } + /// Returns a Kaspa wRPC URL from the resolver based on the supplied [`Encoding`] and [`NetworkId`]. pub async fn get_url(&self, encoding: Encoding, network_id: NetworkId) -> Result { let nodes = self.fetch(encoding, network_id).await?; Ok(nodes.url.clone()) diff --git a/rpc/wrpc/client/src/result.rs b/rpc/wrpc/client/src/result.rs index 32f663388..8427fd12f 100644 --- a/rpc/wrpc/client/src/result.rs +++ b/rpc/wrpc/client/src/result.rs @@ -1 +1,3 @@ +//! The [`Result`] type alias bound to the [`Error`](super::error::Error) enum used in this crate. + pub type Result = std::result::Result; diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index 81487172f..c4b213028 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -1,3 +1,10 @@ +//! +//! # WASM bindings for the [Kaspa p2p Node RPC client](KaspaRpcClient). +//! +//! This module provides a WASM interface for the Kaspa p2p Node RPC client +//! - [`RpcClient`]. +//! + #![allow(non_snake_case)] use crate::imports::*; @@ -130,7 +137,7 @@ impl TryFrom for NotificationEvent { } } -pub struct Inner { +pub(crate) struct Inner { client: Arc, resolver: Option, notification_task: AtomicBool, diff --git a/rpc/wrpc/wasm/src/lib.rs b/rpc/wrpc/wasm/src/lib.rs index e80b3baac..e746bb80a 100644 --- a/rpc/wrpc/wasm/src/lib.rs +++ b/rpc/wrpc/wasm/src/lib.rs @@ -1,3 +1,7 @@ +//! +//! WASM bindings for the [Rusty Kaspa p2p Node wRPC Client](kaspa-wrpc-client) +//! + #![allow(unused_imports)] use cfg_if::cfg_if; diff --git a/rpc/wrpc/wasm/src/notify.rs b/rpc/wrpc/wasm/src/notify.rs index 23781e314..c586f24dc 100644 --- a/rpc/wrpc/wasm/src/notify.rs +++ b/rpc/wrpc/wasm/src/notify.rs @@ -1,3 +1,7 @@ +//! Notification types and interfaces for wRPC events. + +#![allow(non_snake_case)] + use crate::imports::*; use kaspa_rpc_macros::declare_typescript_wasm_interface as declare; diff --git a/rpc/wrpc/wasm/src/resolver.rs b/rpc/wrpc/wasm/src/resolver.rs index 2ffc7ea56..7abfdb688 100644 --- a/rpc/wrpc/wasm/src/resolver.rs +++ b/rpc/wrpc/wasm/src/resolver.rs @@ -1,3 +1,7 @@ +//! [`Resolver`](NativeResolver) bindings for obtaining public Kaspa wRPC URL endpoints. + +#![allow(non_snake_case)] + use crate::client::{RpcClient, RpcConfig}; use crate::imports::*; use js_sys::Array; diff --git a/simpa/src/main.rs b/simpa/src/main.rs index 1d14a3c68..c66656be3 100644 --- a/simpa/src/main.rs +++ b/simpa/src/main.rs @@ -83,7 +83,7 @@ struct Args { ram_scale: f64, /// Logging level for all subsystems {off, error, warn, info, debug, trace} - /// -- You may also specify =,=,... to set the log level for individual subsystems + /// -- You may also specify `=,=,...` to set the log level for individual subsystems #[arg(long = "loglevel", default_value = format!("info,{}=trace", env!("CARGO_PKG_NAME")))] log_level: String, diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 4e57548e7..51981081e 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -1,3 +1,9 @@ +//! +//! # Kaspa Utilities +//! +//! General purpose utilities and various type extensions used across the Rusty Kaspa codebase. +//! + pub mod any; pub mod arc; pub mod binary_heap; diff --git a/wallet/bip32/src/lib.rs b/wallet/bip32/src/lib.rs index a406067f6..14738810e 100644 --- a/wallet/bip32/src/lib.rs +++ b/wallet/bip32/src/lib.rs @@ -32,6 +32,8 @@ pub use xkey::ExtendedKey; pub use xprivate_key::ExtendedPrivateKey; pub use xpublic_key::ExtendedPublicKey; +/// Extension for [`secp256k1::SecretKey`] that provides access +/// to [`secp256k1::PublicKey`] and the public key string representation. pub trait SecretKeyExt { fn get_public_key(&self) -> secp256k1::PublicKey; fn as_str(&self, attrs: ExtendedKeyAttrs, prefix: Prefix) -> Zeroizing; diff --git a/wallet/bip32/src/private_key.rs b/wallet/bip32/src/private_key.rs index 0d4769ee4..d5dbc3d14 100644 --- a/wallet/bip32/src/private_key.rs +++ b/wallet/bip32/src/private_key.rs @@ -4,6 +4,7 @@ use crate::Result; pub use secp256k1::SecretKey; use secp256k1::{scalar::Scalar, Secp256k1, SignOnly}; +/// Trait for private key types which can be derived using BIP32. pub trait PrivateKey: Sized { /// Public key type which corresponds to this private key. type PublicKey: PublicKey; diff --git a/wallet/bip32/src/public_key.rs b/wallet/bip32/src/public_key.rs index 28a121811..56fb17de5 100644 --- a/wallet/bip32/src/public_key.rs +++ b/wallet/bip32/src/public_key.rs @@ -3,7 +3,7 @@ use ripemd::{Digest, Ripemd160}; use secp256k1::{scalar::Scalar, Secp256k1, VerifyOnly}; use sha2::Sha256; -/// Trait for key types which can be derived using BIP32. +/// Trait for public key types which can be derived using BIP32. pub trait PublicKey: Sized { /// Initialize this key from bytes. fn from_bytes(bytes: PublicKeyBytes) -> Result; diff --git a/wallet/core/src/api/mod.rs b/wallet/core/src/api/mod.rs index f0963d610..2b3f1b91d 100644 --- a/wallet/core/src/api/mod.rs +++ b/wallet/core/src/api/mod.rs @@ -1,4 +1,6 @@ //! +//! # Wallet API +//! //! Wallet API module that provides a unified interface for all wallet operations. //! diff --git a/wallet/core/src/api/traits.rs b/wallet/core/src/api/traits.rs index db789c8b3..357665e77 100644 --- a/wallet/core/src/api/traits.rs +++ b/wallet/core/src/api/traits.rs @@ -58,7 +58,7 @@ pub trait WalletApi: Send + Sync + AnySync { /// - `is_wrpc_client` - whether the wallet is connected to a node via wRPC async fn get_status_call(self: Arc, request: GetStatusRequest) -> Result; - /// Synchronous connect call (blocking, single attempt, requires sync). + /// Synchronous connect call (blocking, single attempt, requires node sync). async fn connect(self: Arc, url: Option, network_id: &NetworkId) -> Result<()> { let retry_on_error = false; let block_async_connect = true; @@ -71,6 +71,7 @@ pub trait WalletApi: Send + Sync + AnySync { /// comprised of the `url` and a `network_id`. async fn connect_call(self: Arc, request: ConnectRequest) -> Result; + /// Request the wallet RPC subsystem to disconnect from the node. async fn disconnect(self: Arc) -> Result<()> { self.disconnect_call(DisconnectRequest {}).await?; Ok(()) @@ -96,6 +97,7 @@ pub trait WalletApi: Send + Sync + AnySync { /// Ping the wallet service. Accepts an optional `u64` value that is returned in the response. async fn ping_call(self: Arc, request: PingRequest) -> Result; + /// Wrapper around [`batch_call()`](Self::batch_call). async fn batch(self: Arc) -> Result<()> { self.batch_call(BatchRequest {}).await?; Ok(()) @@ -110,6 +112,7 @@ pub trait WalletApi: Send + Sync + AnySync { /// async fn batch_call(self: Arc, request: BatchRequest) -> Result; + /// Wrapper around [`flush_call()`](Self::flush_call). async fn flush(self: Arc, wallet_secret: Secret) -> Result<()> { self.flush_call(FlushRequest { wallet_secret }).await?; Ok(()) @@ -284,6 +287,7 @@ pub trait WalletApi: Send + Sync + AnySync { /// around this call. async fn accounts_rename_call(self: Arc, request: AccountsRenameRequest) -> Result; + /// Wrapper around [`accounts_select_call()`](Self::accounts_select_call) async fn accounts_select(self: Arc, account_id: Option) -> Result<()> { self.accounts_select_call(AccountsSelectRequest { account_id }).await?; Ok(()) @@ -420,6 +424,7 @@ pub trait WalletApi: Send + Sync + AnySync { async fn accounts_estimate_call(self: Arc, request: AccountsEstimateRequest) -> Result; /// Get a range of transaction records for a specific account id. + /// Wrapper around [`transactions_data_get_call()`](Self::transactions_data_get_call). async fn transactions_data_get_range( self: Arc, account_id: AccountId, @@ -429,8 +434,8 @@ pub trait WalletApi: Send + Sync + AnySync { self.transactions_data_get_call(TransactionsDataGetRequest::with_range(account_id, network_id, range)).await } + /// Get a range of transaction records for a specific account id. async fn transactions_data_get_call(self: Arc, request: TransactionsDataGetRequest) -> Result; - // async fn transaction_get_call(self: Arc, request: TransactionGetRequest) -> Result; /// Replaces the note of a transaction with a new note. Note is meant /// to explicitly store a user-supplied string. The note is treated @@ -455,6 +460,7 @@ pub trait WalletApi: Send + Sync + AnySync { request: TransactionsReplaceMetadataRequest, ) -> Result; + // TODO async fn address_book_enumerate_call( self: Arc, request: AddressBookEnumerateRequest, diff --git a/wallet/core/src/tx/payment.rs b/wallet/core/src/tx/payment.rs index e28c75a22..fc27b8ad9 100644 --- a/wallet/core/src/tx/payment.rs +++ b/wallet/core/src/tx/payment.rs @@ -30,12 +30,19 @@ export interface IPaymentOutput { #[wasm_bindgen] extern "C" { + /// WASM (TypeScript) type representing a single payment output (`IPaymentOutput`). + /// @category Wallet SDK #[wasm_bindgen(typescript_type = "IPaymentOutput")] pub type IPaymentOutput; + /// WASM (TypeScript) type representing multiple payment outputs (`IPaymentOutput[]`). + /// @category Wallet SDK #[wasm_bindgen(typescript_type = "IPaymentOutput[]")] pub type IPaymentOutputArray; } +/// A Rust data structure representing a payment destination. +/// A payment destination is used to signal Generator where to send the funds. +/// The destination can be a change address or a set of [`PaymentOutput`]. #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub enum PaymentDestination { Change, @@ -51,6 +58,9 @@ impl PaymentDestination { } } +/// A Rust data structure representing a single payment +/// output containing a destination address and amount. +/// /// @category Wallet SDK #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, CastFromJs)] #[wasm_bindgen(inspectable)] diff --git a/wallet/core/src/wallet/api.rs b/wallet/core/src/wallet/api.rs index c7e68d1fd..93becef42 100644 --- a/wallet/core/src/wallet/api.rs +++ b/wallet/core/src/wallet/api.rs @@ -1,5 +1,5 @@ //! -//! [`WalletApi`] trait implementation for [`Wallet`]. +//! [`WalletApi`] trait implementation for the [`Wallet`] struct. //! use crate::api::{message::*, traits::WalletApi}; diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index 31393a8ee..5ba714c1b 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -1,6 +1,14 @@ //! -//! Kaspa wallet runtime implementation. +//! # Kaspa wallet runtime implementation. +//! +//! This module contains a Rust implementation of the Kaspa wallet that +//! can be used in native Rust as well as WASM32 (Browser, NodeJs, Bun) +//! environments. +//! +//! This wallet is not meant to be used directly, but rather through the +//! use of the [`WalletApi`] trait. //! + pub mod api; pub mod args; pub mod maps; diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 77c5e16ea..ef51c5d4c 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,5 +1,5 @@ /*! -# `rusty-kaspa WASM32 bindings` +# Rusty Kaspa WASM32 bindings [github](https://github.com/kaspanet/rusty-kaspa/tree/master/wasm) [crates.io](https://crates.io/crates/kaspa-wasm) @@ -13,9 +13,9 @@ codebase within JavaScript environments such as Node.js and Web Browsers. ## Documentation -- [**integrating with Kaspa** guide](https://kaspa.aspectron.org/) -- [**Rustdoc** documentation](https://docs.rs/kaspa-wasm/latest/kaspa-wasm) -- [**JSDoc** documentation](https://kaspa.aspectron.org/jsdoc/) +- [**Integrating with Kaspa** guide](https://kaspa.aspectron.org/) +- [Rust SDK documentation (**Rustdoc**)](https://docs.rs/kaspa-wasm/) +- [TypeScript documentation (**JSDoc**)](https://kaspa.aspectron.org/docs/) Please note that while WASM directly binds JavaScript and Rust resources, their names on JavaScript side are different from their name in Rust as they conform to the 'camelCase' convention in JavaScript and @@ -25,9 +25,10 @@ to the 'snake_case' convention in Rust. The APIs are currently separated into the following groups (this will be expanded in the future): -- **Transaction API** — Bindings for primitives related to transactions. -- **RPC API** — [RPC interface bindings](rpc) for the Kaspa node using WebSocket (wRPC) connections. -- **Wallet API** — API for async core wallet processing tasks. +- **Consensus Client API** — Bindings for primitives related to transactions. +- **RPC API** — [RPC interface bindings](kaspa_wrpc_wasm::client) for the Kaspa node using WebSocket (wRPC) connections. +- **Wallet SDK** — API for async core wallet processing tasks. +- **Wallet API** — A rust implementation of the fully-featured wallet usable in the native Rust, Browser or NodeJs and Bun environments. ## NPM Modules @@ -43,6 +44,9 @@ of a native WebSocket in NodeJs environment, while the `kaspa` module includes `websocket` package dependency simulating the W3C WebSocket and due to this supports RPC. +NOTE: for security reasons it is always recommended to build WASM SDK from source or +download pre-built redistributables from releases or development builds. + ## Examples JavaScript examples for using this framework can be found at: @@ -54,8 +58,19 @@ For pre-built browser-compatible WASM32 redistributables of this framework please see the releases section of the Rusty Kaspa repository at . +## Development Builds + +The latest development builds from . +Development builds typically contain fixes and improvements that are not yet available in +stable releases. Additional information can be found at +. + ## Using RPC +No special handling is required to use the RPC client +in **Browser** or **Bun** environments due to the fact that +these environments provide native WebSocket support. + **NODEJS:** If you are building from source, to use WASM RPC client in the NodeJS environment, you need to introduce a global W3C WebSocket object before loading the WASM32 library (to simulate the browser behavior). @@ -123,7 +138,7 @@ const rpc = new RpcClient({ })(); ``` -For more details, please follow the [**integrating with Kaspa**](https://kaspa.aspectron.org/) guide. +For more details, please follow the [**Integrating with Kaspa**](https://kaspa.aspectron.org/) guide. */ From 1a83014fa8ccc0d9724de81c9497b40df14823d7 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 26 Sep 2024 20:27:51 +0300 Subject: [PATCH 5/8] lints --- components/consensusmanager/src/lib.rs | 4 ++-- consensus/client/src/hash.rs | 2 +- consensus/client/src/header.rs | 6 +++--- consensus/client/src/input.rs | 2 +- consensus/client/src/lib.rs | 10 +++++----- consensus/client/src/outpoint.rs | 9 ++++----- consensus/client/src/output.rs | 2 +- consensus/client/src/serializable/mod.rs | 16 ++++++++-------- consensus/client/src/serializable/numeric.rs | 4 ++-- consensus/client/src/serializable/string.rs | 4 ++-- consensus/client/src/sign.rs | 2 +- consensus/client/src/transaction.rs | 2 +- consensus/client/src/utils.rs | 2 +- consensus/client/src/utxo.rs | 6 +++--- consensus/core/src/lib.rs | 4 ++-- consensus/core/src/network.rs | 10 +++++----- consensus/core/src/tx.rs | 4 ++-- crypto/addresses/src/lib.rs | 18 +++++++++--------- indexes/utxoindex/src/index.rs | 2 +- .../src/mempool/model/frontier/search_tree.rs | 2 +- rpc/core/src/api/connection.rs | 2 +- rpc/core/src/api/ctl.rs | 2 +- rpc/core/src/api/mod.rs | 2 +- rpc/core/src/api/notifications.rs | 2 +- rpc/core/src/api/ops.rs | 2 +- rpc/core/src/api/rpc.rs | 4 ++-- rpc/core/src/convert/mod.rs | 2 +- rpc/core/src/error.rs | 2 +- rpc/core/src/lib.rs | 12 ++++++------ rpc/core/src/notify/mod.rs | 4 ++-- rpc/core/src/wasm/convert.rs | 4 ++-- rpc/core/src/wasm/message.rs | 2 +- rpc/wrpc/client/src/client.rs | 4 ++-- rpc/wrpc/client/src/lib.rs | 12 ++++++------ rpc/wrpc/client/src/resolver.rs | 14 +++++++------- rpc/wrpc/wasm/src/client.rs | 6 +++--- rpc/wrpc/wasm/src/lib.rs | 2 +- utils/src/lib.rs | 4 ++-- wallet/bip32/src/lib.rs | 2 +- wallet/core/src/api/mod.rs | 2 +- wallet/core/src/tx/payment.rs | 4 ++-- wallet/core/src/wallet/mod.rs | 8 ++++---- wasm/src/lib.rs | 4 ++-- 43 files changed, 106 insertions(+), 107 deletions(-) diff --git a/components/consensusmanager/src/lib.rs b/components/consensusmanager/src/lib.rs index c7ea2db9b..6d31653aa 100644 --- a/components/consensusmanager/src/lib.rs +++ b/components/consensusmanager/src/lib.rs @@ -9,8 +9,8 @@ mod session; pub use batch::BlockProcessingBatch; pub use session::{ - spawn_blocking, ConsensusInstance, ConsensusProxy, ConsensusSessionBlocking, SessionLock, SessionReadGuard, SessionWriteGuard, - ConsensusSessionOwned, + spawn_blocking, ConsensusInstance, ConsensusProxy, ConsensusSessionBlocking, ConsensusSessionOwned, SessionLock, SessionReadGuard, + SessionWriteGuard, }; /// Consensus controller trait. Includes methods required to start/stop/control consensus, but which should not diff --git a/consensus/client/src/hash.rs b/consensus/client/src/hash.rs index 950194bb1..1577689a6 100644 --- a/consensus/client/src/hash.rs +++ b/consensus/client/src/hash.rs @@ -1,7 +1,7 @@ //! //! WASM bindings for transaction hashers: [`TransactionSigningHash`](native::TransactionSigningHash) //! and [`TransactionSigningHashECDSA`](native::TransactionSigningHashECDSA). -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/header.rs b/consensus/client/src/header.rs index c54e95d11..6f04a73c4 100644 --- a/consensus/client/src/header.rs +++ b/consensus/client/src/header.rs @@ -1,6 +1,6 @@ //! //! Implementation of the Block [`Header`] struct. -//! +//! #![allow(non_snake_case)] @@ -66,14 +66,14 @@ export interface IRawHeader { #[wasm_bindgen] extern "C" { /// WASM (TypeScript) type definition for the Header-like struct: `Header | IHeader | IRawHeader`. - /// + /// /// @category Consensus #[wasm_bindgen(typescript_type = "Header | IHeader | IRawHeader")] pub type HeaderT; } /// Kaspa Block Header -/// +/// /// @category Consensus #[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)] #[serde(rename_all = "camelCase")] diff --git a/consensus/client/src/input.rs b/consensus/client/src/input.rs index cd718375c..a5018199d 100644 --- a/consensus/client/src/input.rs +++ b/consensus/client/src/input.rs @@ -1,6 +1,6 @@ //! //! Implementation of the client-side [`TransactionInput`] struct used by the client-side [`Transaction`] struct. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/lib.rs b/consensus/client/src/lib.rs index 5056454ac..3afae2f78 100644 --- a/consensus/client/src/lib.rs +++ b/consensus/client/src/lib.rs @@ -1,16 +1,16 @@ //! //! # Client-side consensus primitives. -//! +//! //! This crate offers client-side primitives mirroring the consensus layer of the Kaspa p2p node. -//! It declares structs such as [`Transaction`], [`TransactionInput`], [`TransactionOutput`], +//! It declares structs such as [`Transaction`], [`TransactionInput`], [`TransactionOutput`], //! [`TransactionOutpoint`], [`UtxoEntry`], and [`UtxoEntryReference`] //! that are used by the Wallet subsystem as well as WASM bindings. -//! +//! //! Unlike raw consensus primitives (used for high-performance DAG processing) the primitives //! offered in this crate are designed to be used in client-side applications. Their internal -//! data is typically wrapped into `Arc>`, allowing for easy sharing between +//! data is typically wrapped into `Arc>`, allowing for easy sharing between //! async / threaded environments and WASM bindings. -//! +//! pub mod error; mod imports; diff --git a/consensus/client/src/outpoint.rs b/consensus/client/src/outpoint.rs index 416d0bff6..a9b39f5e4 100644 --- a/consensus/client/src/outpoint.rs +++ b/consensus/client/src/outpoint.rs @@ -1,6 +1,6 @@ //! //! Implementation of the client-side [`TransactionOutpoint`] used by the [`TransactionInput`] struct. -//! +//! #![allow(non_snake_case)] @@ -128,17 +128,17 @@ cfg_if! { pub fn ctor(transaction_id: TransactionId, index: u32) -> TransactionOutpoint { Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) } } - + #[wasm_bindgen(js_name = "getId")] pub fn id_string(&self) -> String { format!("{}-{}", self.get_transaction_id_as_string(), self.get_index()) } - + #[wasm_bindgen(getter, js_name = transactionId)] pub fn get_transaction_id_as_string(&self) -> String { self.inner().transaction_id.to_string() } - + #[wasm_bindgen(getter, js_name = index)] pub fn get_index(&self) -> TransactionIndexType { self.inner().index @@ -147,7 +147,6 @@ cfg_if! { } } - impl std::fmt::Display for TransactionOutpoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let inner = self.inner(); diff --git a/consensus/client/src/output.rs b/consensus/client/src/output.rs index 48ec3227f..17b4a58c8 100644 --- a/consensus/client/src/output.rs +++ b/consensus/client/src/output.rs @@ -1,6 +1,6 @@ //! //! Implementation of the client-side [`TransactionOutput`] used by the [`Transaction`] struct. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/serializable/mod.rs b/consensus/client/src/serializable/mod.rs index ee58a6192..ab78d956b 100644 --- a/consensus/client/src/serializable/mod.rs +++ b/consensus/client/src/serializable/mod.rs @@ -1,21 +1,21 @@ -//! +//! //! # Standardized JSON serialization and deserialization of Kaspa transactions. -//! -//! This module provides standardized JSON serialization and deserialization of +//! +//! This module provides standardized JSON serialization and deserialization of //! Kaspa transactions. There are two sub-modules: `numeric` and `string`. -//! +//! //! The `numeric` module provides serialization and deserialization of transactions //! with all large integer values as `bigint` types in WASM or numerical values that //! exceed the largest integer that can be represented by the JavaScript `number` type. -//! +//! //! The `string` module provides serialization and deserialization of transactions //! with all large integer values as `string` types. This allows deserialization //! via JSON in JavaScript environments and later conversion to `bigint` types. -//! +//! //! These data structures can be used for manual transport of transactions using JSON. -//! For more advanced use cases, please refer to `PSKT` in the [`kaspa_wallet_pskt`](https://docs.rs/kaspa_wallet_pskt) +//! For more advanced use cases, please refer to `PSKT` in the [`kaspa_wallet_pskt`](https://docs.rs/kaspa_wallet_pskt) //! crate. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/serializable/numeric.rs b/consensus/client/src/serializable/numeric.rs index 3725d1bb1..6c24db634 100644 --- a/consensus/client/src/serializable/numeric.rs +++ b/consensus/client/src/serializable/numeric.rs @@ -1,10 +1,10 @@ -//! +//! //! This module implements transaction-related primitives for JSON serialization //! where all large integer values (`u64`) are serialized to JSON using `serde` and //! can exceed the largest integer value representable by the JavaScript `number` type. //! (i.e. transactions serialized using this module can not be deserialized in JavaScript //! but may be deserialized in other JSON-capable environments that support large integers) -//! +//! use crate::error::Error; use crate::imports::*; diff --git a/consensus/client/src/serializable/string.rs b/consensus/client/src/serializable/string.rs index af906a1cf..35c7907b2 100644 --- a/consensus/client/src/serializable/string.rs +++ b/consensus/client/src/serializable/string.rs @@ -1,7 +1,7 @@ -//! +//! //! This module implements transaction-related primitives for JSON serialization //! where all large integer values (`u64`) are serialized to and from JSON as strings. -//! +//! use crate::imports::*; use crate::result::Result; diff --git a/consensus/client/src/sign.rs b/consensus/client/src/sign.rs index 0c28b7913..4044dc570 100644 --- a/consensus/client/src/sign.rs +++ b/consensus/client/src/sign.rs @@ -1,6 +1,6 @@ //! //! Utilities for signing transactions. -//! +//! use crate::transaction::Transaction; use core::iter::once; diff --git a/consensus/client/src/transaction.rs b/consensus/client/src/transaction.rs index cccbbc2ad..17cc38126 100644 --- a/consensus/client/src/transaction.rs +++ b/consensus/client/src/transaction.rs @@ -1,6 +1,6 @@ //! //! Declares the client-side [`Transaction`] type, which represents a Kaspa transaction. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/utils.rs b/consensus/client/src/utils.rs index bc3a5c9c1..7e08556fe 100644 --- a/consensus/client/src/utils.rs +++ b/consensus/client/src/utils.rs @@ -1,6 +1,6 @@ //! //! Client-side utility functions and their WASM bindings. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/client/src/utxo.rs b/consensus/client/src/utxo.rs index f51d5fb64..bbfc1199d 100644 --- a/consensus/client/src/utxo.rs +++ b/consensus/client/src/utxo.rs @@ -1,10 +1,10 @@ //! //! # UTXO client-side data structures. -//! +//! //! This module provides client-side data structures for UTXO management. //! In particular, the [`UtxoEntry`] and [`UtxoEntryReference`] structs //! are used to represent UTXO entries in the wallet subsystem and WASM bindings. -//! +//! #![allow(non_snake_case)] @@ -54,7 +54,7 @@ extern "C" { pub type UtxoEntryId = TransactionOutpointInner; /// [`UtxoEntry`] struct represents a client-side UTXO entry. -/// +/// /// @category Wallet SDK #[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)] #[serde(rename_all = "camelCase")] diff --git a/consensus/core/src/lib.rs b/consensus/core/src/lib.rs index d5d295f33..188b2403b 100644 --- a/consensus/core/src/lib.rs +++ b/consensus/core/src/lib.rs @@ -1,8 +1,8 @@ //! //! # Consensus Core -//! +//! //! This crate implements primitives used in the Kaspa node consensus processing. -//! +//! extern crate alloc; extern crate core; diff --git a/consensus/core/src/network.rs b/consensus/core/src/network.rs index 48f40175a..18e52eacb 100644 --- a/consensus/core/src/network.rs +++ b/consensus/core/src/network.rs @@ -1,13 +1,13 @@ //! //! # Network Types -//! -//! This module implements [`NetworkType`] (such as `mainnet`, `testnet`, `devnet`, and `simnet`) +//! +//! This module implements [`NetworkType`] (such as `mainnet`, `testnet`, `devnet`, and `simnet`) //! and [`NetworkId`] that combines a network type with an optional numerical suffix. -//! +//! //! The suffix is used to differentiate between multiple networks of the same type and is used -//! explicitly with `testnet` networks, allowing declaration of testnet versions such as +//! explicitly with `testnet` networks, allowing declaration of testnet versions such as //! `testnet-10`, `testnet-11`, etc. -//! +//! #![allow(non_snake_case)] diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index 61587b842..a4dd7dd45 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -1,8 +1,8 @@ //! //! # Transaction -//! +//! //! This module implements consensus [`Transaction`] structure and related types. -//! +//! #![allow(non_snake_case)] diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index ec5261323..8e3ea385a 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -1,10 +1,10 @@ //! //! Kaspa [`Address`] implementation. -//! -//! In it's string form, the Kaspa [`Address`] is represented by a `bech32`-encoded -//! address string combined with a network type. The `bech32` string encoding is +//! +//! In it's string form, the Kaspa [`Address`] is represented by a `bech32`-encoded +//! address string combined with a network type. The `bech32` string encoding is //! comprised of a public key, the public key version and the resulting checksum. -//! +//! use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -200,7 +200,7 @@ pub const PAYLOAD_VECTOR_SIZE: usize = 36; pub type PayloadVec = SmallVec<[u8; PAYLOAD_VECTOR_SIZE]>; /// Kaspa [`Address`] struct that serializes to and from an address format string: `kaspa:qz0s...t8cv`. -/// +/// /// @category Address #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, CastFromJs)] #[wasm_bindgen(inspectable)] @@ -527,22 +527,22 @@ impl TryCastFromJs for Address { #[wasm_bindgen] extern "C" { /// WASM (TypeScript) type representing an Address-like object: `Address | string`. - /// + /// /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "Address | string")] pub type AddressT; /// WASM (TypeScript) type representing an array of Address-like objects: `(Address | string)[]`. - /// + /// /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "(Address | string)[]")] pub type AddressOrStringArrayT; /// WASM (TypeScript) type representing an array of [`Address`] objects: `Address[]`. - /// + /// /// @category Address #[wasm_bindgen(extends = js_sys::Array, typescript_type = "Address[]")] pub type AddressArrayT; /// WASM (TypeScript) type representing an [`Address`] or an undefined value: `Address | undefined`. - /// + /// /// @category Address #[wasm_bindgen(typescript_type = "Address | undefined")] pub type AddressOrUndefinedT; diff --git a/indexes/utxoindex/src/index.rs b/indexes/utxoindex/src/index.rs index 4461d3672..3b1bf2fe9 100644 --- a/indexes/utxoindex/src/index.rs +++ b/indexes/utxoindex/src/index.rs @@ -21,7 +21,7 @@ use std::{ const RESYNC_CHUNK_SIZE: usize = 2048; //Increased from 1k (used in go-kaspad), for quicker resets, while still having a low memory footprint. -/// UtxoIndex indexes `CompactUtxoEntryCollections` by [`ScriptPublicKey`](kaspa_consensus_core::tx::ScriptPublicKey), +/// UtxoIndex indexes `CompactUtxoEntryCollections` by [`ScriptPublicKey`](kaspa_consensus_core::tx::ScriptPublicKey), /// commits them to its owns store, and emits changes. /// Note: The UtxoIndex struct by itself is not thread save, only correct usage of the supplied RwLock via `new` makes it so. /// please follow guidelines found in the comments under `utxoindex::core::api::UtxoIndexApi` for proper thread safety. diff --git a/mining/src/mempool/model/frontier/search_tree.rs b/mining/src/mempool/model/frontier/search_tree.rs index dd6910c40..edf34c271 100644 --- a/mining/src/mempool/model/frontier/search_tree.rs +++ b/mining/src/mempool/model/frontier/search_tree.rs @@ -157,7 +157,7 @@ type InnerTree = BPlusTree>; /// is recomputed from subtree weights for each item insertion/removal /// /// Computing the prefix weight is a crucial operation if the tree is used for random sampling and -/// the tree is highly imbalanced in terms of weight variance. +/// the tree is highly imbalanced in terms of weight variance. /// See [`Frontier::sample_inplace()`](crate::mempool::model::frontier::Frontier::sample_inplace) /// for more details. pub struct SearchTree { diff --git a/rpc/core/src/api/connection.rs b/rpc/core/src/api/connection.rs index 3e6597313..fba2aa71a 100644 --- a/rpc/core/src/api/connection.rs +++ b/rpc/core/src/api/connection.rs @@ -1,6 +1,6 @@ //! //! Generic connection trait representing a connection to a client (where available). -//! +//! use std::sync::Arc; diff --git a/rpc/core/src/api/ctl.rs b/rpc/core/src/api/ctl.rs index e55d35fbf..d7705127b 100644 --- a/rpc/core/src/api/ctl.rs +++ b/rpc/core/src/api/ctl.rs @@ -1,6 +1,6 @@ //! //! Client-side RPC helper for handling connection and disconnection events. -//! +//! use crate::error::RpcResult; use std::sync::{Arc, Mutex}; diff --git a/rpc/core/src/api/mod.rs b/rpc/core/src/api/mod.rs index 6876a92ee..a75056a84 100644 --- a/rpc/core/src/api/mod.rs +++ b/rpc/core/src/api/mod.rs @@ -1,6 +1,6 @@ //! //! API module for the RPC server. Implements core RPC primitives. -//! +//! pub mod connection; pub mod ctl; diff --git a/rpc/core/src/api/notifications.rs b/rpc/core/src/api/notifications.rs index 432d89789..503af0de8 100644 --- a/rpc/core/src/api/notifications.rs +++ b/rpc/core/src/api/notifications.rs @@ -1,6 +1,6 @@ //! //! RPC notifications that can be sent to clients. -//! +//! use crate::model::message::*; use derive_more::Display; diff --git a/rpc/core/src/api/ops.rs b/rpc/core/src/api/ops.rs index a265a6629..26ca356eb 100644 --- a/rpc/core/src/api/ops.rs +++ b/rpc/core/src/api/ops.rs @@ -1,6 +1,6 @@ //! //! RPC Operations used to identify RPC methods during transport and in various RPC-related macros. -//! +//! use borsh::{BorshDeserialize, BorshSerialize}; use kaspa_notify::events::EventType; diff --git a/rpc/core/src/api/rpc.rs b/rpc/core/src/api/rpc.rs index 0d752a00d..cadc9e00c 100644 --- a/rpc/core/src/api/rpc.rs +++ b/rpc/core/src/api/rpc.rs @@ -1,10 +1,10 @@ -//! +//! //! The main [`RpcApi`] trait that defines all RPC methods available in the Rusty Kaspa p2p node. //! //! Rpc = External RPC Service //! All data provided by the RPC server can be trusted by the client //! No data submitted by the client to the node can be trusted -//! +//! use crate::api::connection::DynRpcConnection; use crate::{model::*, notify::connection::ChannelConnection, RpcResult}; diff --git a/rpc/core/src/convert/mod.rs b/rpc/core/src/convert/mod.rs index 37c019ea8..bc5c0e64b 100644 --- a/rpc/core/src/convert/mod.rs +++ b/rpc/core/src/convert/mod.rs @@ -1,6 +1,6 @@ //! //! Data conversion utilities and structs for the RPC layer. -//! +//! pub mod block; pub mod notification; diff --git a/rpc/core/src/error.rs b/rpc/core/src/error.rs index 9d5464dc8..0e2bfee22 100644 --- a/rpc/core/src/error.rs +++ b/rpc/core/src/error.rs @@ -1,6 +1,6 @@ //! //! [`RpcError`] enum used by RPC primitives. -//! +//! use kaspa_consensus_core::{subnets::SubnetworkConversionError, tx::TransactionId}; use kaspa_utils::networking::IpAddress; diff --git a/rpc/core/src/lib.rs b/rpc/core/src/lib.rs index a8e61b959..b379a9325 100644 --- a/rpc/core/src/lib.rs +++ b/rpc/core/src/lib.rs @@ -1,15 +1,15 @@ //! # RPC Core -//! +//! //! This crate provides foundational primitives used in Rusty Kaspa node RPC subsystem. -//! These include the main [`RpcApi`](api::rpc::RpcApi) trait, [`RpcApiOps`](crate::api::ops::RpcApiOps) +//! These include the main [`RpcApi`](api::rpc::RpcApi) trait, [`RpcApiOps`](crate::api::ops::RpcApiOps) //! enum used in RPC method dispatching, and various data structures used in RPC method arguments. -//! +//! //! This crate acts as a foundation for [`kaspa_grpc_client`](https://docs.rs/kaspa_grpc_client) and //! [`kaspa_wrpc_client`](https://docs.rs/kaspa_wrpc_client) crates, which provide gRPC and WebSocket -//! RPC client implementations. This crate is also used by WASM bindings to provide [WASM RpcClient -//! implementation](https://docs.rs/kaspa-wrpc-client/latest/kaspa_wrpc_client/wasm/struct.RpcClient.html) +//! RPC client implementations. This crate is also used by WASM bindings to provide [WASM RpcClient +//! implementation](https://docs.rs/kaspa-wrpc-client/latest/kaspa_wrpc_client/wasm/struct.RpcClient.html) //! (based on wRPC). -//! +//! // This attribute is required by BorshSerialize/Deserialize #![recursion_limit = "256"] diff --git a/rpc/core/src/notify/mod.rs b/rpc/core/src/notify/mod.rs index 4e8c91fb1..e6dc1be06 100644 --- a/rpc/core/src/notify/mod.rs +++ b/rpc/core/src/notify/mod.rs @@ -1,6 +1,6 @@ -//! +//! //! Notification structures used by the RPC subsystem. -//! +//! pub mod channel; pub mod collector; diff --git a/rpc/core/src/wasm/convert.rs b/rpc/core/src/wasm/convert.rs index 6f76e4c1c..319f74bf0 100644 --- a/rpc/core/src/wasm/convert.rs +++ b/rpc/core/src/wasm/convert.rs @@ -1,6 +1,6 @@ -//! +//! //! WASM specific conversion functions -//! +//! use crate::model::*; use kaspa_consensus_client::*; diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs index d3a8f57b0..85c085702 100644 --- a/rpc/core/src/wasm/message.rs +++ b/rpc/core/src/wasm/message.rs @@ -1,6 +1,6 @@ //! //! WASM interfaces and conversion to and from RPC messages. -//! +//! #![allow(non_snake_case)] use crate::error::RpcError as Error; diff --git a/rpc/wrpc/client/src/client.rs b/rpc/wrpc/client/src/client.rs index 71b2fd878..3ac04fa98 100644 --- a/rpc/wrpc/client/src/client.rs +++ b/rpc/wrpc/client/src/client.rs @@ -253,9 +253,9 @@ const WRPC_CLIENT: &str = "wrpc-client"; /// RpcClient has two ways to interface with the underlying RPC subsystem: /// [`Interface`] that has a [`notification()`](Interface::notification) /// method to register closures that will be invoked on server-side -/// notifications and the [`RpcClient::call`] method that allows server-side +/// notifications and the [`RpcClient::call`] method that allows server-side /// async method invocation. -/// +/// /// The node address can be supplied via a URL or a [`Resolver`] that /// can be used to resolve a public node address dynamically. [`Resolver`] can also /// be configured to operate against custom node clusters. diff --git a/rpc/wrpc/client/src/lib.rs b/rpc/wrpc/client/src/lib.rs index f37032682..ac004eccb 100644 --- a/rpc/wrpc/client/src/lib.rs +++ b/rpc/wrpc/client/src/lib.rs @@ -1,18 +1,18 @@ //! //! # wRPC Client for Rusty Kaspa p2p Node -//! +//! //! This crate provides a WebSocket RPC client for Rusty Kaspa p2p node. It is based on the //! [wRPC](https://docs.rs/workflow-rpc) crate that offers WebSocket RPC implementation //! for Rust based on Borsh and Serde JSON serialization. wRPC is a lightweight RPC framework //! meant to function as an IPC (Inter-Process Communication) mechanism for Rust applications. -//! -//! Rust examples on using wRPC client can be found in the +//! +//! Rust examples on using wRPC client can be found in the //! [examples](https://github.com/kaspanet/rusty-kaspa/tree/master/rpc/wrpc/examples) folder. -//! +//! //! WASM bindings for wRPC client can be found in the [`kaspa-wrpc-wasm`](https://docs.rs/kaspa-wrpc-wasm) crate. -//! +//! //! The main struct managing Kaspa RPC client connections is the [`KaspaRpcClient`]. -//! +//! pub mod client; pub mod error; diff --git a/rpc/wrpc/client/src/resolver.rs b/rpc/wrpc/client/src/resolver.rs index 0b9af9f18..170fe12dd 100644 --- a/rpc/wrpc/client/src/resolver.rs +++ b/rpc/wrpc/client/src/resolver.rs @@ -1,6 +1,6 @@ -//! +//! //! Module implementing [`Resolver`] client for obtaining public Kaspa wRPC endpoints. -//! +//! use std::sync::OnceLock; @@ -83,11 +83,11 @@ impl Inner { /// /// # Resolver - a client for obtaining public Kaspa wRPC endpoints. -/// +/// /// This client operates against [Kaspa Resolver](https://github.com/aspectron/kaspa-resolver) service /// that provides load-balancing and failover capabilities for Kaspa wRPC endpoints. The default /// configuration allows access to public Kaspa nodes, while custom configurations can be supplied -/// if you are running your own custom Kaspa node cluster. +/// if you are running your own custom Kaspa node cluster. /// #[derive(Debug, Clone)] pub struct Resolver { @@ -107,8 +107,8 @@ impl Resolver { Self { inner: Arc::new(Inner::new(urls, tls)) } } - /// Obtain a list of URLs in the resolver client. (This function - /// returns `None` if the resolver is configured to use public + /// Obtain a list of URLs in the resolver client. (This function + /// returns `None` if the resolver is configured to use public /// node endpoints.) pub fn urls(&self) -> Option>> { if self.inner.public { @@ -178,7 +178,7 @@ impl Resolver { Err(Error::Custom(format!("Failed to connect: {:?}", errors))) } - /// Obtain a Kaspa p2p [`NodeDescriptor`] from the resolver based on the supplied [`Encoding`] and [`NetworkId`]. + /// Obtain a Kaspa p2p [`NodeDescriptor`] from the resolver based on the supplied [`Encoding`] and [`NetworkId`]. pub async fn get_node(&self, encoding: Encoding, network_id: NetworkId) -> Result { self.fetch(encoding, network_id).await } diff --git a/rpc/wrpc/wasm/src/client.rs b/rpc/wrpc/wasm/src/client.rs index c4b213028..5982a2425 100644 --- a/rpc/wrpc/wasm/src/client.rs +++ b/rpc/wrpc/wasm/src/client.rs @@ -1,9 +1,9 @@ -//! +//! //! # WASM bindings for the [Kaspa p2p Node RPC client](KaspaRpcClient). -//! +//! //! This module provides a WASM interface for the Kaspa p2p Node RPC client //! - [`RpcClient`]. -//! +//! #![allow(non_snake_case)] diff --git a/rpc/wrpc/wasm/src/lib.rs b/rpc/wrpc/wasm/src/lib.rs index e746bb80a..61d0de19b 100644 --- a/rpc/wrpc/wasm/src/lib.rs +++ b/rpc/wrpc/wasm/src/lib.rs @@ -1,6 +1,6 @@ //! //! WASM bindings for the [Rusty Kaspa p2p Node wRPC Client](kaspa-wrpc-client) -//! +//! #![allow(unused_imports)] diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 51981081e..3d1bb5438 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -1,8 +1,8 @@ //! //! # Kaspa Utilities -//! +//! //! General purpose utilities and various type extensions used across the Rusty Kaspa codebase. -//! +//! pub mod any; pub mod arc; diff --git a/wallet/bip32/src/lib.rs b/wallet/bip32/src/lib.rs index 14738810e..1926728c4 100644 --- a/wallet/bip32/src/lib.rs +++ b/wallet/bip32/src/lib.rs @@ -32,7 +32,7 @@ pub use xkey::ExtendedKey; pub use xprivate_key::ExtendedPrivateKey; pub use xpublic_key::ExtendedPublicKey; -/// Extension for [`secp256k1::SecretKey`] that provides access +/// Extension for [`secp256k1::SecretKey`] that provides access /// to [`secp256k1::PublicKey`] and the public key string representation. pub trait SecretKeyExt { fn get_public_key(&self) -> secp256k1::PublicKey; diff --git a/wallet/core/src/api/mod.rs b/wallet/core/src/api/mod.rs index 2b3f1b91d..979ef1c72 100644 --- a/wallet/core/src/api/mod.rs +++ b/wallet/core/src/api/mod.rs @@ -1,6 +1,6 @@ //! //! # Wallet API -//! +//! //! Wallet API module that provides a unified interface for all wallet operations. //! diff --git a/wallet/core/src/tx/payment.rs b/wallet/core/src/tx/payment.rs index fc27b8ad9..c164e0d78 100644 --- a/wallet/core/src/tx/payment.rs +++ b/wallet/core/src/tx/payment.rs @@ -58,9 +58,9 @@ impl PaymentDestination { } } -/// A Rust data structure representing a single payment +/// A Rust data structure representing a single payment /// output containing a destination address and amount. -/// +/// /// @category Wallet SDK #[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, CastFromJs)] #[wasm_bindgen(inspectable)] diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index 5ba714c1b..27498c5a9 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -1,11 +1,11 @@ //! //! # Kaspa wallet runtime implementation. -//! +//! //! This module contains a Rust implementation of the Kaspa wallet that -//! can be used in native Rust as well as WASM32 (Browser, NodeJs, Bun) +//! can be used in native Rust as well as WASM32 (Browser, NodeJs, Bun) //! environments. -//! -//! This wallet is not meant to be used directly, but rather through the +//! +//! This wallet is not meant to be used directly, but rather through the //! use of the [`WalletApi`] trait. //! diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index ef51c5d4c..d8b0f06a9 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -61,8 +61,8 @@ repository at . ## Development Builds The latest development builds from . -Development builds typically contain fixes and improvements that are not yet available in -stable releases. Additional information can be found at +Development builds typically contain fixes and improvements that are not yet available in +stable releases. Additional information can be found at . ## Using RPC From 2e50f7548ff708732dda83cf0763dff7742ec51d Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Thu, 26 Sep 2024 22:07:04 +0300 Subject: [PATCH 6/8] fix typos --- crypto/txscript/src/opcodes/mod.rs | 2 +- indexes/utxoindex/src/update_container.rs | 4 +- metrics/core/src/data.rs | 59 ----------------------- mining/src/feerate/fee_estimation.ipynb | 10 ++-- 4 files changed, 8 insertions(+), 67 deletions(-) diff --git a/crypto/txscript/src/opcodes/mod.rs b/crypto/txscript/src/opcodes/mod.rs index 4406bd5b6..ad800d248 100644 --- a/crypto/txscript/src/opcodes/mod.rs +++ b/crypto/txscript/src/opcodes/mod.rs @@ -2747,7 +2747,7 @@ mod test { (1u64, vec![], false), // Case 1: 0 = locktime < txLockTime (0x800000, vec![0x7f, 0, 0], false), // Case 2: 0 < locktime < txLockTime (0x800000, vec![0x7f, 0, 0, 0, 0, 0, 0, 0, 0], true), // Case 3: locktime too big - (LOCK_TIME_THRESHOLD * 2, vec![0x7f, 0, 0, 0], true), // Case 4: lock times are inconsistant + (LOCK_TIME_THRESHOLD * 2, vec![0x7f, 0, 0, 0], true), // Case 4: lock times are inconsistent ] { let mut tx = base_tx.clone(); tx.0.lock_time = tx_lock_time; diff --git a/indexes/utxoindex/src/update_container.rs b/indexes/utxoindex/src/update_container.rs index 8555a02d4..96449dbff 100644 --- a/indexes/utxoindex/src/update_container.rs +++ b/indexes/utxoindex/src/update_container.rs @@ -25,7 +25,7 @@ impl UtxoIndexChanges { } } - /// Add a [`UtxoDiff`] the the [`UtxoIndexChanges`] struct. + /// Add a [`UtxoDiff`] the [`UtxoIndexChanges`] struct. pub fn update_utxo_diff(&mut self, utxo_diff: UtxoDiff) { let (to_add, mut to_remove) = (utxo_diff.add, utxo_diff.remove); @@ -53,7 +53,7 @@ impl UtxoIndexChanges { } } - /// Add a [`Vec<(TransactionOutpoint, UtxoEntry)>`] the the [`UtxoIndexChanges`] struct + /// Add a [`Vec<(TransactionOutpoint, UtxoEntry)>`] the [`UtxoIndexChanges`] struct /// /// Note: This is meant to be used when resyncing. pub fn add_utxos_from_vector(&mut self, utxo_vector: Vec<(TransactionOutpoint, UtxoEntry)>) { diff --git a/metrics/core/src/data.rs b/metrics/core/src/data.rs index d47941aa7..ce9dc7216 100644 --- a/metrics/core/src/data.rs +++ b/metrics/core/src/data.rs @@ -252,65 +252,6 @@ pub enum Metric { } impl Metric { - // TODO - this will be refactored at a later date - // as this requires changes and testing in /kos - // pub fn group(&self) -> &'static str { - // match self { - // Metric::NodeCpuUsage - // | Metric::NodeResidentSetSizeBytes - // | Metric::NodeVirtualMemorySizeBytes - // | Metric::NodeFileHandlesCount - // | Metric::NodeDiskIoReadBytes - // | Metric::NodeDiskIoWriteBytes - // | Metric::NodeDiskIoReadPerSec - // | Metric::NodeDiskIoWritePerSec - // | Metric::NodeBorshLiveConnections - // | Metric::NodeBorshConnectionAttempts - // | Metric::NodeBorshHandshakeFailures - // | Metric::NodeJsonLiveConnections - // | Metric::NodeJsonConnectionAttempts - // | Metric::NodeJsonHandshakeFailures - // | Metric::NodeBorshBytesTx - // | Metric::NodeBorshBytesRx - // | Metric::NodeJsonBytesTx - // | Metric::NodeJsonBytesRx - // | Metric::NodeP2pBytesTx - // | Metric::NodeP2pBytesRx - // | Metric::NodeGrpcUserBytesTx - // | Metric::NodeGrpcUserBytesRx - // | Metric::NodeTotalBytesTx - // | Metric::NodeTotalBytesRx - // | Metric::NodeBorshBytesTxPerSecond - // | Metric::NodeBorshBytesRxPerSecond - // | Metric::NodeJsonBytesTxPerSecond - // | Metric::NodeJsonBytesRxPerSecond - // | Metric::NodeP2pBytesTxPerSecond - // | Metric::NodeP2pBytesRxPerSecond - // | Metric::NodeGrpcUserBytesTxPerSecond - // | Metric::NodeGrpcUserBytesRxPerSecond - // | Metric::NodeTotalBytesTxPerSecond - // | Metric::NodeTotalBytesRxPerSecond - // | Metric::NodeActivePeers => "system", - // // -- - // Metric::NodeBlocksSubmittedCount - // | Metric::NodeHeadersProcessedCount - // | Metric::NodeDependenciesProcessedCount - // | Metric::NodeBodiesProcessedCount - // | Metric::NodeTransactionsProcessedCount - // | Metric::NodeChainBlocksProcessedCount - // | Metric::NodeMassProcessedCount - // | Metric::NodeDatabaseBlocksCount - // | Metric::NodeDatabaseHeadersCount - // | Metric::NetworkMempoolSize - // | Metric::NetworkTransactionsPerSecond - // | Metric::NetworkTipHashesCount - // | Metric::NetworkDifficulty - // | Metric::NetworkPastMedianTime - // | Metric::NetworkVirtualParentHashesCount - // | Metric::NetworkVirtualDaaScore => "kaspa", - // } - // } - pub fn is_key_performance_metric(&self) -> bool { matches!( self, diff --git a/mining/src/feerate/fee_estimation.ipynb b/mining/src/feerate/fee_estimation.ipynb index 694f47450..a8b8fbfc8 100644 --- a/mining/src/feerate/fee_estimation.ipynb +++ b/mining/src/feerate/fee_estimation.ipynb @@ -252,7 +252,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -303,7 +303,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -355,7 +355,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -396,7 +396,7 @@ "source": [ "### Outliers: solution\n", "\n", - "Compute the estimator conditioned on the event the the top most transaction captures the first slot. This decreases `total_weight` on the one hand (thus increasing `p`), while increasing `inclusion_interval` on the other, by capturing a block slot. If this estimator gives lower prediction times we switch to it, and then repeat the process with the next highest transaction. The process convegres when the estimator is no longer improving or if all block slots are captured. " + "Compute the estimator conditioned on the event the top most transaction captures the first slot. This decreases `total_weight` on the one hand (thus increasing `p`), while increasing `inclusion_interval` on the other, by capturing a block slot. If this estimator gives lower prediction times we switch to it, and then repeat the process with the next highest transaction. The process converges when the estimator is no longer improving or if all block slots are captured. " ] }, { @@ -406,7 +406,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAAD8CAYAAAB+UHOxAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAHmVJREFUeJzt3X2QXXWd5/H39z72U/op6TzQnZggEVREwR7Ah3IdIw5RxzCzYuG4Gl1qU7MwPoyzpehUyYxTM+PUuAO6A9REQeMui7DImjgLg5mAhSgBwlMMhJAQIOkkJB066Tx2p/ve7/5xTie307e7k7597+m+5/OqunXP+Z3fOed7fehPfufR3B0REYmfRNQFiIhINBQAIiIxpQAQEYkpBYCISEwpAEREYkoBICISUwoAEZGYGjcAzOwOM9tnZpuKLPtvZuZmNiucNzP7vpltM7ONZnZJQd/lZrY1/Cyf3J8hIiJn60xGAD8Grjy90czmA1cAOwqalwKLw88K4LawbytwI3AZcClwo5m1lFK4iIiUJjVeB3d/xMwWFll0E/A1YHVB2zLgJx7cXrzezJrNbB7wQWCtu/cAmNlaglC5a6x9z5o1yxcuLLZrEREZzVNPPbXf3dvG6zduABRjZp8Adrn7c2ZWuKgd2Fkw3xW2jdY+poULF7Jhw4aJlCgiEltm9tqZ9DvrADCzOuAvgY8UW1ykzcdoL7b9FQSHj1iwYMHZliciImdoIlcBvRlYBDxnZq8CHcDTZjaX4F/28wv6dgC7x2gfwd1Xununu3e2tY07ghERkQk66wBw99+5+2x3X+juCwn+uF/i7q8Da4DPhVcDXQ70uvse4EHgI2bWEp78/UjYJiIiETmTy0DvAh4DzjezLjO7dozu9wPbgW3AD4DrAMKTv38DPBl+vj10QlhERKJhU/l9AJ2dna6TwCIiZ8fMnnL3zvH66U5gEZGYUgCIiMRUVQbA4b4Bblr7Es/uPBh1KSIiU1ZVBkAu73xv3Vaefu1A1KWIiExZVRkA9dng/rYj/YMRVyIiMnVVZQCkkwlq0gkFgIjIGKoyACAYBRzuUwCIiIymegMgk+Jw30DUZYiITFlVGwC1maQOAYmIjKFqA6Auk+SIDgGJiIyqqgPgkA4BiYiMqooDQCeBRUTGUsUBoHMAIiJjqeoAONo/yFR+2qmISJSqNgDqsynyDsdO5KIuRURkSqraAKjLJAE9DkJEZDRVGwD1meB5QDoRLCJSXNUGgEYAIiJjq9oAGHoiqB4HISJSXNUGwMkRgA4BiYgUNW4AmNkdZrbPzDYVtP2jmb1oZhvN7P+aWXPBsm+Y2TYz22Jmf1DQfmXYts3Mbpj8nzJc3dA5AB0CEhEp6kxGAD8GrjytbS1wobtfBLwEfAPAzN4GXAO8PVznVjNLmlkSuAVYCrwN+HTYt2zqssEIQCeBRUSKGzcA3P0RoOe0tl+6+9Bf1vVARzi9DPipu/e7+yvANuDS8LPN3be7+wngp2Hfshm6CkiHgEREipuMcwD/GXggnG4HdhYs6wrbRmsvm2TCSCeNI/06CSwiUkxJAWBmfwkMAncONRXp5mO0F9vmCjPbYGYburu7SymPbErPAxIRGc2EA8DMlgMfBz7jpx640wXML+jWAeweo30Ed1/p7p3u3tnW1jbR8gDIpBIc0iEgEZGiJhQAZnYl8HXgE+5+rGDRGuAaM8ua2SJgMfAE8CSw2MwWmVmG4ETxmtJKH186aToHICIyitR4HczsLuCDwCwz6wJuJLjqJwusNTOA9e7+p+7+vJndA7xAcGjoenfPhdv5M+BBIAnc4e7Pl+H3DJNJJjh0XOcARESKGTcA3P3TRZpvH6P/3wJ/W6T9fuD+s6quRNl0kl4FgIhIUVV7JzBATSqhABARGUVVB0A2HbwXWC+FEREZqboDIJVgIOf0DeSjLkVEZMqp6gCoSQWPg9BhIBGRkao6ALLp4OcpAERERqruAEgpAERERlPVAVCT1iEgEZHRVHUAaAQgIjK6qg4AjQBEREZX1QGQ0QhARGRUVR0ACTNq0noekIhIMVUdABC8E0ABICIyUgwCQM8DEhEpRgEgIhJTVR8AmVSCg8cUACIip6v6AKjROwFERIqq+gDIphIc6lMAiIicrvoDIJ2kfzBP30Au6lJERKaUqg+AmvBmMF0KKiIyXNUHQG34OIgDOhEsIjLMuAFgZneY2T4z21TQ1mpma81sa/jdErabmX3fzLaZ2UYzu6RgneVh/61mtrw8P2ekoecB9Rw9UaldiohMC2cyAvgxcOVpbTcA69x9MbAunAdYCiwOPyuA2yAIDOBG4DLgUuDGodAot9rM0AhAASAiUmjcAHD3R4Ce05qXAavC6VXAVQXtP/HAeqDZzOYBfwCsdfcedz8ArGVkqJRFrUYAIiJFTfQcwBx33wMQfs8O29uBnQX9usK20drLbugQ0AEFgIjIMJN9EtiKtPkY7SM3YLbCzDaY2Ybu7u6SC0omjGwqQY8OAYmIDDPRANgbHtoh/N4XtncB8wv6dQC7x2gfwd1Xununu3e2tbVNsLzh6jJJjQBERE4z0QBYAwxdybMcWF3Q/rnwaqDLgd7wENGDwEfMrCU8+fuRsK0iatJJenQZqIjIMKnxOpjZXcAHgVlm1kVwNc93gHvM7FpgB3B12P1+4KPANuAY8AUAd+8xs78Bngz7fdvdTz+xXDbZVIKeo/2V2p2IyLQwbgC4+6dHWbSkSF8Hrh9lO3cAd5xVdZOkNp3UVUAiIqep+juBIbgX4MBRHQISESkUiwCoSSc5PpDTA+FERArEIgB0M5iIyEjxCICMAkBE5HSxCICTdwPrZjARkZNiEQA6BCQiMlKsAkB3A4uInBKLAMimExgaAYiIFIpFACTMqMsm6T6iABARGRKLAACoy6TYf0SPgxARGRKLADh/3wP8YvBP+ZftH4abLoSN90RdkohI5MZ9FtB0d/6+B7ji5b8j7X1BQ+9O+MWXgumLPhVdYSIiEav6EcD7d9xKOt83vHHgOKz7djQFiYhMEVUfADP69xZf0NtV2UJERKaYqg+Aw9k5xRc0dVS2EBGRKabqA+DRBdcxkKgZ3piuhSXfiqYgEZEpoupPAm+ZvRSA97x6C00n9tFXN4+6pX+tE8AiEntVPwKAIARue9dqzu2/k7vff7/++IuIEJMAAKhJJ0gYdB/WzWAiIhCjADAzGrK6G1hEZEhJAWBmf25mz5vZJjO7y8xqzGyRmT1uZlvN7G4zy4R9s+H8tnD5wsn4AWejNpPUCEBEJDThADCzduBLQKe7XwgkgWuAfwBucvfFwAHg2nCVa4ED7n4ecFPYr6Jq00n2KQBERIDSDwGlgFozSwF1wB7gQ8C94fJVwFXh9LJwnnD5EjOzEvd/VuoyKQWAiEhowgHg7ruA7wI7CP7w9wJPAQfdfTDs1gW0h9PtwM5w3cGw/8yJ7n8iGrIpeo6cYDCXr+RuRUSmpFIOAbUQ/Kt+EXAOUA8sLdLVh1YZY1nhdleY2QYz29Dd3T3R8oqqzybJubNf7wUQESnpENCHgVfcvdvdB4D7gPcCzeEhIYAOYHc43QXMBwiXNwE9p2/U3Ve6e6e7d7a1tZVQ3kgNNUFZe3qPT+p2RUSmo1ICYAdwuZnVhcfylwAvAA8Dnwz7LAdWh9NrwnnC5Q+5+4gRQDnNyKYBeL23b5yeIiLVr5RzAI8TnMx9GvhduK2VwNeBr5rZNoJj/LeHq9wOzAzbvwrcUELdE9KQHRoBKABEREp6FpC73wjceFrzduDSIn37gKtL2V+patIJUglj7yEFgIhIbO4EhuBu4Bk1KY0ARESIWQAA1GdTOgksIkJsA0AjABGR2AVAQzbF3kN95PMVvQBJRGTKiWUADOScnmO6GUxE4i2WAQC6F0BEJH4BUKN7AUREIIYBMCMcAew+qCuBRCTeYhcAdZkkqYTRdeBY1KWIiEQqdgFgZjTVptnZoxGAiMRb7AIAgvMAO3o0AhCReItlADTWpNmpQ0AiEnOxDICm2jSH+wbpPT4QdSkiIpGJZQA0hpeC6kSwiMRZPAOgNngxjE4Ei0icxToANAIQkTiLZQDUpBJkUwm6DmgEICLxFcsAMLPgSiBdCioiMRbLAACYoXsBRCTmYhsATXVpdvQc03sBRCS2SgoAM2s2s3vN7EUz22xm7zGzVjNba2Zbw++WsK+Z2ffNbJuZbTSzSybnJ0xMS22G/sE8e/SCeBGJqVJHAN8D/s3dLwDeCWwGbgDWuftiYF04D7AUWBx+VgC3lbjvkjTXBVcCvdJ9NMoyREQiM+EAMLNG4APA7QDufsLdDwLLgFVht1XAVeH0MuAnHlgPNJvZvAlXXqKW+gwAr+w/ElUJIiKRKmUEcC7QDfzIzJ4xsx+aWT0wx933AITfs8P+7cDOgvW7wrZI1GeSZJIJXtYIQERiqpQASAGXALe5+8XAUU4d7inGirSNOANrZivMbIOZbeju7i6hvLGZGS31aV7ZrwAQkXgqJQC6gC53fzycv5cgEPYOHdoJv/cV9J9fsH4HsPv0jbr7SnfvdPfOtra2EsobX1NNmu3dOgQkIvE04QBw99eBnWZ2fti0BHgBWAMsD9uWA6vD6TXA58KrgS4HeocOFUWluT7DroPH6R/MRVmGiEgkUiWu/0XgTjPLANuBLxCEyj1mdi2wA7g67Hs/8FFgG3As7Buplro0eYedPcc4b/aMqMsREamokgLA3Z8FOossWlKkrwPXl7K/ydZcF1wJ9HL3UQWAiMRObO8EBmgNA2DbPp0HEJH4iXUAZFIJmmrTvPj64ahLERGpuFgHAEBrfYYX9xyKugwRkYqLfQDMrM+wff9RTgzmoy5FRKSiFAANGXJ5Z7seCSEiMRP7AJjVkAVgi84DiEjMxD4AWuoyJEwBICLxE/sASCaM1vqMAkBEYif2AQDBlUAv6EogEYkZBQCwzH7D/zm+Av+rZrjpQth4T9QliYiUXanPApr2zt/3AB/uvZlMoj9o6N0Jv/hSMH3Rp6IrTESkzGI/Anj/jlvJeP/wxoHjsO7b0RQkIlIhsQ+AGf17iy/o7apsISIiFRb7ADicnVN8QVNHZQsREamw2AfAowuuYyBRM7wxXQtLvhVNQSIiFRL7k8BbZi8F4PJXbqF5YB/99fOovfKvdQJYRKpe7EcAEITAynev5s39d/Ivl6zWH38RiQUFQCibSjKrIcNTrx2IuhQRkYpQABSY11TLU68dYDCnR0OLSPVTABQ4p7mWYydyekOYiMRCyQFgZkkze8bM/jWcX2Rmj5vZVjO728wyYXs2nN8WLl9Y6r4n2znNwdVAT77aE3ElIiLlNxkjgC8Dmwvm/wG4yd0XAweAa8P2a4ED7n4ecFPYb0qZUZOmqTbNhld1HkBEql9JAWBmHcDHgB+G8wZ8CLg37LIKuCqcXhbOEy5fEvafUuY21vDEKz24e9SliIiUVakjgJuBrwFDZ01nAgfdfTCc7wLaw+l2YCdAuLw37D+lnNNcQ/eRfl5941jUpYiIlNWEA8DMPg7sc/enCpuLdPUzWFa43RVmtsHMNnR3d0+0vAmb31oHwKNbK79vEZFKKmUE8D7gE2b2KvBTgkM/NwPNZjZ0h3EHsDuc7gLmA4TLm4ARZ1vdfaW7d7p7Z1tbWwnlTUxzbXAe4JGt+yu+bxGRSppwALj7N9y9w90XAtcAD7n7Z4CHgU+G3ZYDq8PpNeE84fKHfAoeaDcz5rfU8tjLbzCg+wFEpIqV4z6ArwNfNbNtBMf4bw/bbwdmhu1fBW4ow74nxYLWOo70D/LczoNRlyIiUjaT8jA4d/8V8KtwejtwaZE+fcDVk7G/cpvfWocZPLJ1P50LW6MuR0SkLHQncBE16SRzG2v41ZZ9UZciIlI2CoBRLJxZz8auXl7v7Yu6FBGRslAAjOLNbfUArN08yisjRUSmOQXAKFrrM7TWpXlw0+tRlyIiUhYKgFGYGYvaGnhs+xv0Hh+IuhwRkUmnABjDm9vqyeWdh17UYSARqT4KgDHMbayhsSbF6md2j99ZRGSaUQCMwcx4y5wZ/HrrfroP90ddjojIpFIAjOOCuTPIufOL5zQKEJHqogAYx8yGLHMas9z3dFfUpYiITCoFwBl4y5wZbNp9iC16V7CIVBEFwBl469xGUgnjf65/NepSREQmjQLgDNRmkiye3cB9T+/icJ/uCRCR6qAAOEMXdTRz7ESO+57eFXUpIiKTQgFwhuY21TC3sYZVv32VfH7KvcdGROSsKQDOwjvnN7F9/1E9IE5EqoIC4Cy8ZfYMmuvS/PND25iCb7MUETkrCoCzkEgY717Qwu929fJrvTReRKY5BcBZumDeDGbUpLj531/SKEBEpjUFwFlKJRL83sJWnt5xkAef17kAEZm+JhwAZjbfzB42s81m9ryZfTlsbzWztWa2NfxuCdvNzL5vZtvMbKOZXTJZP6LS3j6vkZn1Gf7+gc0M5PJRlyMiMiGljAAGgb9w97cClwPXm9nbgBuAde6+GFgXzgMsBRaHnxXAbSXsO1KJhPHe82by2hvHuHP9a1GXIyIyIRMOAHff4+5Ph9OHgc1AO7AMWBV2WwVcFU4vA37igfVAs5nNm3DlEVs0s54FrXV895cvsfeQXhwvItPPpJwDMLOFwMXA48Acd98DQUgAs8Nu7cDOgtW6wrZpycz4/fPb6BvI8Vdrno+6HBGRs1ZyAJhZA/Az4CvufmisrkXaRlxGY2YrzGyDmW3o7u4utbyyaq7LcOmiVh7Y9DoPPq+Xx4vI9FJSAJhZmuCP/53ufl/YvHfo0E74vS9s7wLmF6zeAYx4y4q7r3T3TnfvbGtrK6W8irhkQQttM7Lc8LON7NOhIBGZRkq5CsiA24HN7v5PBYvWAMvD6eXA6oL2z4VXA10O9A4dKprOkgnjyrfP5Uj/IF+5+1k9J0hEpo1SRgDvAz4LfMjMng0/HwW+A1xhZluBK8J5gPuB7cA24AfAdSXse0pprc/wgcVt/PblN7jl4W1RlyMickZSE13R3R+l+HF9gCVF+jtw/UT3N9W9/ZxGdh08zn9f+xKL5zRw5YXT9gInEYkJ3Qk8ScyMJRfMZl5TDV+5+1k27eqNuiQRkTEpACZRKpngY++YRyaV4HN3PMHL3UeiLklEZFQKgElWn01x1Tvb6R/I8Sc/WM+ON45FXZKISFEKgDJoqc9w1cXtHDo+yKdWPsa2fYejLklEZAQFQJnMasjyRxe3c7hvgP9422M89dqBqEsSERlGAVBGbTOyXP3u+SQM/uQH6/n5M3qhvIhMHQqAMmuqTfPJd3cwqyHLV+5+lhtXb+LEoB4hLSLRUwBUQF0mxR9d3M7FC5pZ9dhr/PGtv2HL6zovICLRUgBUSDJhfGBxGx97xzy27z/Kx//Hr7nl4W16oYyIREYBUGHnzW7gM5ctYOHMev7xwS1cefMj/GrLvvFXFBGZZAqACNRlUnz0HfP4w4vm0XP0BJ//0ZN8/o4n2Nh1MOrSRCRGJvwsICnduW0NLJhZx3M7e1n/yht84p9/w++f38YXlyzmkgUtUZcnIlVOARCxVCLBu9/UwoXtjTzX1cvjr/Tw8K2/5Z0dTXz2PQv5+EXzqEkng84b74F134beLmjqgCXfgos+Fe0PEJFpy4KHdE5NnZ2dvmHDhgmt+9Lew/y/jdPvdQMnBvO8sOcQm3b18sbREzTVpln2rnP4/IwnWPTYN7GB46c6p2vhD7+vEBCRYczsKXfvHK+fzgFMMZlUgnfNb+Yzly3gjy9uZ3Zjlv/9+A4yv/rb4X/8AQaOByMCEZEJ0CGgKcrMmN9ax/zWOvoHc7Q//kbRft7bxe6Dx2lvrq1whSIy3SkApoFsKsnh7Bwa+0e+eH5Xfibv/85DzGuq4bJFrfzeolYuWdDCebMbSCc1wBOR0SkApolHF1zHFS//Hen8qRfPDyRqeHT+f+U/WBu7Dh7n3zfv4+fP7gYgk0xw/twZXNjexNvPaeSt8xo5r62Bprp0VD9BRKYYBcA0sWX2UgDev+NWZvTv5XB2Do8uuI49s5fyLuBd85txdw4eH2DvoT66D/fTfaSfnz+zi7ue2HFyOy11ac6b3cCb24LPm2bW0d5SS0dzHY21KcxGe8uniFQbBcA0smX20pNBUIyZ0VKXoaUuwwVzgzZ353DfIPuP9HPg2AAHjp1gz8E+Xth9iKMncsPWr8skaW+upaOllnOaa5nXVMOshixtM4LPrIbgk0np0JJINah4AJjZlcD3gCTwQ3f/TqVriBMzo7E2TWPtyEM/fQM5eo8PcKhvgMN9gxw+Psjh/gFe2HOI9dt7OD6QK7JFaKxJ0TYjy8yGLE21aZpr0zSFn+a6YF/NdZmTbTNqUtRnUtSkExphiEwhFQ0AM0sCtwBXAF3Ak2a2xt1fqGQdEqhJJ6lJJ5nTWFN0+WAuz7ETufAzyLETOY6G38f6c+w6cJxX9h+lfyDH8YEcA7mx7ylJGNRmktRnUtRnU9RnkzRkg3Coy6ZoyCapy6SoTSfJphLUpJNk0wlqUsF3NjV8/uR3Qf900kgnEiQSChqZRiK6ybPSI4BLgW3uvh3AzH4KLAMUAFNQKpmgsTZRdPRQTC7v9A3k6B/MD/s+kcszMJhnIOfBdC7PicE8R/oGOXB0gMF8MD+Q8/A7T6m3JybNSCWDTzqRIJ1MBNPJRBASyUTR6VRBWyqRIJmA5NC3GYmEkUoE30kzkonwEy5LDi0Plw31P7U83J4F04mwzoSd+piBWbDMgETCCPIs+B7qc7IvRiJxqr9Z0G/oOxGOuoa2Y5xaPrSdoXYr2E6xWoZGcMF08J+1RnUl2ngP/OJLwX09AL07g3koewhUOgDagZ0F813AZRWuQcokmbDwX/albcfdyTsM5vPk8s5gzoPvvDOYz4+czzu5XDCfcyefd/Lu5PMMm88VtJ0YzHN8IHdyX0GfsP/JdSDvjo/yPTQ9de+lr6yhGBgKpaEGK2yjMDiGlg8F2akNDbWdWt+Gbf9k15PBdGpbBbs+uV87vZaCjZxe2+lBR5HlheueKttGto23HLjj4DeZnR/lJs8qC4Bi/1QY9v8fM1sBrABYsGDBhHd07qx6/ssHzp3w+iJnKl8QPCdDJp8n56eW5fLBJ18wXRhIubzjhSHDqSA8FTxBe9BWGEQAwwPr5LrDtuMj1g0C7PT2YBmc2v7QsmBPhdOE00GDe2Hb8L6EtQxfHsz4yeUjt0/YVrj9wr4Mq+X0bZ3amY9Y3yko/eR+C9dnxG899WMK/3AVe6JOsb7D/rMIv9t69o9cGYLDQWVW6QDoAuYXzHcAuws7uPtKYCUEzwKa6I5SyQQNuhFKRKa6mzqCwz6na+oo+64r/RfySWCxmS0yswxwDbCmwjWIiEwdS74VPNixULo2aC+zio4A3H3QzP4MeJDgMtA73P35StYgIjKlDB3nj8FVQLj7/cD9ld6viMiUddGnInmsuw6Si4jElAJARCSmFAAiIjGlABARiSkFgIhITCkARERiSgEgIhJTCgARkZgyL/YUoynCzLqB16KuYwJmAaM84anqxOm3gn5vNaum3/omd28br9OUDoDpysw2uHtn1HVUQpx+K+j3VrM4/dYhOgQkIhJTCgARkZhSAJTHyqgLqKA4/VbQ761mcfqtgM4BiIjElkYAIiIxpQCYJGY238weNrPNZva8mX056prKzcySZvaMmf1r1LWUm5k1m9m9ZvZi+N/xe6KuqZzM7M/D/x1vMrO7zKwm6pomk5ndYWb7zGxTQVurma01s63hd0uUNVaCAmDyDAJ/4e5vBS4Hrjezt0VcU7l9GdgcdREV8j3g39z9AuCdVPHvNrN24EtAp7tfSPD2vmuirWrS/Ri48rS2G4B17r4YWBfOVzUFwCRx9z3u/nQ4fZjgD0R7tFWVj5l1AB8Dfhh1LeVmZo3AB4DbAdz9hLsfjLaqsksBtWaWAuqA3RHXM6nc/RGg57TmZcCqcHoVcFVFi4qAAqAMzGwhcDHweLSVlNXNwNeAfNSFVMC5QDfwo/CQ1w/NrD7qosrF3XcB3wV2AHuAXnf/ZbRVVcQcd98DwT/ogNkR11N2CoBJZmYNwM+Ar7j7oajrKQcz+ziwz92firqWCkkBlwC3ufvFwFGq+PBAeOx7GbAIOAeoN7P/FG1VUg4KgElkZmmCP/53uvt9UddTRu8DPmFmrwI/BT5kZv8r2pLKqgvocvehEd29BIFQrT4MvOLu3e4+ANwHvDfimiphr5nNAwi/90VcT9kpACaJmRnBMeLN7v5PUddTTu7+DXfvcPeFBCcHH3L3qv0Xoru/Duw0s/PDpiXACxGWVG47gMvNrC783/USqvikd4E1wPJwejmwOsJaKiIVdQFV5H3AZ4HfmdmzYds33f3+CGuSyfNF4E4zywDbgS9EXE/ZuPvjZnYv8DTB1W3PUGV3yZrZXcAHgVlm1gXcCHwHuMfMriUIwaujq7AydCewiEhM6RCQiEhMKQBERGJKASAiElMKABGRmFIAiIjElAJARCSmFAAiIjGlABARian/D+hfAG5Faoo0AAAAAElFTkSuQmCC", "text/plain": [ "
" ] From c526a87869fe460b397217955a7856864971606e Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 27 Sep 2024 03:11:58 +0300 Subject: [PATCH 7/8] replace glob import terminology with "re-exports" --- rpc/core/src/lib.rs | 2 +- wallet/core/src/prelude.rs | 3 +-- wallet/keys/src/prelude.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rpc/core/src/lib.rs b/rpc/core/src/lib.rs index b379a9325..a2ece77d4 100644 --- a/rpc/core/src/lib.rs +++ b/rpc/core/src/lib.rs @@ -22,7 +22,7 @@ pub mod notify; pub mod wasm; pub mod prelude { - //! Glob imports of the most commonly used types and traits in this crate. + //! Re-exports of the most commonly used types and traits in this crate. pub use super::api::notifications::*; pub use super::model::script_class::*; pub use super::model::*; diff --git a/wallet/core/src/prelude.rs b/wallet/core/src/prelude.rs index 0ca019434..741ea0b1e 100644 --- a/wallet/core/src/prelude.rs +++ b/wallet/core/src/prelude.rs @@ -1,6 +1,5 @@ //! -//! A module which is typically glob imported. -//! Contains most commonly used imports. +//! Re-exports of the most commonly used types and traits in this crate. //! pub use crate::account::descriptor::AccountDescriptor; diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index a176f4038..5d3af82dd 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -1,5 +1,5 @@ //! -//! Glob imports for the `kaspa_wallet_keys` crate. +//! Re-exports of the most commonly used types and traits in this crate. //! pub use crate::derivation_path::*; From 48d0c8cd6370438386569f91ad61266f99786324 Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Fri, 27 Sep 2024 03:17:28 +0300 Subject: [PATCH 8/8] cleanup --- wallet/core/src/wallet/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/wallet/core/src/wallet/mod.rs b/wallet/core/src/wallet/mod.rs index 27498c5a9..d7c9b6c76 100644 --- a/wallet/core/src/wallet/mod.rs +++ b/wallet/core/src/wallet/mod.rs @@ -67,7 +67,6 @@ pub struct MultisigWalletFileV0<'a, T: AsRef<[u8]>> { pub encrypted_mnemonics: Vec>, pub xpublic_keys: Vec<&'a str>, // includes pub keys from encrypted pub required_signatures: u16, - #[allow(dead_code)] pub cosigner_index: u8, pub ecdsa: bool, } @@ -77,7 +76,6 @@ pub struct MultisigWalletFileV1<'a, T: AsRef<[u8]>> { pub encrypted_mnemonics: Vec>, pub xpublic_keys: Vec<&'a str>, // includes pub keys from encrypted pub required_signatures: u16, - #[allow(dead_code)] pub cosigner_index: u8, pub ecdsa: bool, }