Rusty Kaspa

repository·master·Indexed 21 days ago

https://github.com/kaspanet/rusty-kaspa

A Rust-based implementation of the Kaspa full-node (kaspad) and its ancillary libraries, serving as a high-performance replacement for the original Golang node. It includes a WASM SDK for web and Node.js integration, a CLI-driven RPC interface, and the kaspa-stratum-bridge for mining connectivity. Supports multiple network environments including Mainnet, Testnet, and Devnet.

Tokens
123.7K
Snippets
421
Records
567
Agent score
74%

What's inside rusty-kaspa

  1. Overview of the Kaspa p2p Node (kaspad)

    master
    The Kaspa p2p Node (kaspad) is a high-performance peer-to-peer node library and daemon written in Rust. It is designed to support the high-BPS (Blocks Per Second) BlockDAG network of Kaspa. Developers can use it as a library or run it as a standalone daemon to interact with the Kaspa network.
  2. What is a Kaspa Archive Node?

    master

    An archive node stores the complete blockchain history, including all pruned data that normal nodes discard. Unlike standard pruned nodes that only keep recent blocks, archive nodes provide access to the full transaction history.

    Common use cases:

    • Blockchain explorers: Access to complete transaction history.
    • Research and analytics: Historical data analysis.
    • Compliance and auditing: Meeting data retention requirements.

    Warning: Running archive nodes is resource-intensive and requires specific system optimizations. Most users should use standard pruned nodes instead.

  3. How Virtual Processing parallelism works

    master

    Virtual processing (handling block UTXO data for chain blocks) follows these concurrency rules:

    1. Sequential Step: Each chain block and its associated mergeset are processed sequentially.
    2. Parallelism within a step:
      • Transaction Validation: Transactions within a single block can be validated against the UTXO set in parallel.
      • Mergeset Processing: Blocks in the mergeset and the transactions within them can be processed in parallel, following the consensus-agreed topological mergeset ordering. Developers must account for potential conflicts arising from this parallel processing according to that order.
  4. Configure RocksDB presets for Archive Nodes

    master

    Kaspad provides two RocksDB configuration presets to optimize archive node performance based on your storage hardware:

    1. Default Preset (SSD/NVMe)

    Optimized for fast storage with a lower memory footprint. It uses a 64MB write buffer and standard compression. Command:

    kaspad --archival --rocksdb-preset=default

    2. HDD Preset

    Optimized for mechanical hard drives to reduce write amplification and improve batching. Key features include:

    • 256MB write buffer (4x default).
    • BlobDB enabled to separate large values.
    • Aggressive compression: LZ4 for hot data (L0-L4) and ZSTD level 22 for cold data (L5+).
    • 12 MB/s rate limiter to prevent I/O spikes.
    • 4MB read-ahead for sequential reads.
    • Minimum RAM: 4GB required (8GB+ recommended for public RPC). Command:
    kaspad --archival --rocksdb-preset=hdd
    kaspad --archival --rocksdb-preset=hdd
  5. Handle Toccata fee adaptation and minimum fees

    master

    Upon Toccata activation, the minimum fee rate increases from 1 sompi/gram to 100 sompi/gram.

    The new fee rule is: 100 sompi * max(compute grams, 2 * transaction bytes)

    Implementation Guidance:

    • Wallets/Software: If you use the RPC fee estimation API, no changes are required. If you manually calculate fees, you must update your logic to match the new rule.
    • RPC Submission: The node enforces this higher minimum fee for transactions submitted via RPC. P2P relayed transactions follow the old policy until activation, at which point they will also be rejected if they don't meet the new rule.
    • Note: This is a node policy/mempool rule, not a consensus rule. Zero-fee transactions remain consensus-valid.
  6. Choose the correct WASM32 SDK package for your environment

    master

    The SDK is distributed in different packages depending on your target environment and required features:

    Web Browser Packages

    • KeyGen: Key & Address Generation only.
    • RPC: RPC only (reduced WASM binary size).
    • Core: RPC + Key & Address Generation + Wallet SDK.
    • Full: Full SDK + Integrated Wallet.

    NodeJS Package

    • Single Package: Contains all features (RPC, KeyGen, Wallet SDK, and Integrated Wallet).
  7. How VirtualSelectedParentChainChanged subscriptions are processed

    master

    The VirtualSelectedParentChainChanged subscription tracks changes to the parent chain. It supports three states:

    1. None: active = false.
    2. Reduced: active = true and include_accepted_transaction_ids = false.
    3. All: active = true and include_accepted_transaction_ids = true.

    Single Subscription State Transition Table

    Current StateMutation: AllMutation: ReducedMutation: None
    None— <br> + All <br> ---------------- <br> All— <br> + Reduced <br> ---------------- <br> Reduced— <br> — <br> ---------------- <br> None
    Reduced- Reduced <br> + All <br> ---------------- <br> All— <br> — <br> ---------------- <br> Reduced— <br> - Reduced <br> ---------------- <br> None
    All— <br> — <br> ---------------- <br> All+ Reduced <br> - All <br> ---------------- <br> Reduced— <br> - All <br> ---------------- <br> None
  8. How Header Processing parallelism works

    master

    Header processing parallelism is managed by the pipeline::HeaderProcessor struct. It uses a DAG-dependency mechanism to delay processing tasks until all required dependencies (parents/ancestors) are completed.

    • Task Assignment: If a header has no pending dependencies, a rayon::spawn assigns a thread-pool worker to process it.
    • Managing Store Writes:
      • Append-only stores: Most DB writes (like DbGhostdagStore) are append-only and do not require locks, provided a single worker thread 'owns' the header being processed.
      • Non-append-only stores: The reachability and relations stores are non-append-only. Currently, these are managed using serialized upgradable-read/write locks within pipeline::HeaderProcessor::commit_header.
    • Potential Optimization: If reachability algorithms become a bottleneck, a future 'Header DAG processing' unit may be introduced to batch multiple block additions into a single reindexing call.
  9. Deprecation of Transaction.mass in favor of storageMass

    master
    As part of the Toccata Hardfork, the mass field in transaction APIs is being deprecated. Developers constructing, parsing, or storing transactions should transition to using storageMass (or storage_mass in JSON) to ensure compatibility with transaction version 1 and the new fee/resource models.
  10. How Compounded VirtualSelectedParentChainChanged subscriptions work

    master

    A Compounded Subscription for VirtualSelectedParentChainChanged uses counters for the All and Reduced states to manage parent notifications.

    Mutation Logic for Compounded VirtualSelectedParentChainChanged

    MutationProcessReturn Value
    Add AllIncrement AllIf All == 1: Some(SubscribeMessage::StartEvent(NotificationType::VirtualSelectedParentChainChanged(true))). Else: None.
    Add ReducedIncrement ReducedIf Reduced == 1 and All == 0: Some(SubscribeMessage::StartEvent(NotificationType::VirtualSelectedParentChainChanged(false))). Else: None.
    Remove ReducedDecrement ReducedIf Reduced == 0 and All == 0: Some(SubscribeMessage::StopEvent(NotificationType::VirtualSelectedParentChainChanged(false))). Else: None.
    Remove AllDecrement AllIf All == 0: If Reduced > 0: Some(SubscribeMessage::StartEvent(NotificationType::VirtualSelectedParentChainChanged(false))). Else: Some(SubscribeMessage::StopEvent(NotificationType::VirtualSelectedParentChainChanged(true))). Else: None.
  11. How UtxosChanged subscriptions are processed

    master

    The UtxosChanged subscription system manages notifications for changes in UTXO sets. It supports three primary states:

    1. None: active = false and the address set is empty.
    2. Selected(S): active = true and the subscription is tracking a specific set of addresses S.
    3. All: active = true and the subscription tracks all addresses (address set is empty).

    When applying mutations to a Single Subscription, the system calculates atomic mutations and the resulting state based on the current state and the mutation type (All, Add(A), Remove(R), or None).

    Single Subscription State Transition Table

    Current StateMutation: AllMutation: Add(A)Mutation: Remove(R)Mutation: None
    None— <br> + All <br> ---------------- <br> All— <br> + A <br> ---------------- <br> Selected(A)— <br> — <br> ---------------- <br> None— <br> — <br> ---------------- <br> None
    Selected(S)- S <br> + All <br> ---------------- <br> All— <br> + (A – S) <br> ---------------- <br> Selected(A ∪ S)— <br> - (R ∩ S) <br> ---------------- <br> Selected(S – R)— <br> - S <br> ---------------- <br> None
    All— <br> — <br> ---------------- <br> All+ A <br> - All <br> ---------------- <br> Selected(A)— <br> — <br> ---------------- <br> All— <br> - All <br> ---------------- <br> None
  12. How Compounded UtxosChanged subscriptions work

    master

    A Compounded Subscription for UtxosChanged manages multiple underlying subscriptions by maintaining counters for the All state and for every individual address registered.

    To use this, call the compound function. It returns an Option<SubscribeMessage<NotificationType>>. If the returned value is Some, it must be propagated to the parent subscription.

    Mutation Logic for Compounded UtxosChanged

    MutationProcessReturn Value
    Add(All)Increment All counterIf All == 1: Some(SubscribeMessage::StartEvent(NotificationType::UtxosChanged(empty))). Else: None.
    Add(A)For each a in A: increment counter of a. If counter == 1, add a to set B.If B is not empty and All == 0: Some(SubscribeMessage::StartEvent(NotificationType::UtxosChanged(B))). Else: None.
    Remove(R)For each r in R: decrement counter of r. If counter == 0, add r to set S.If S is not empty and All == 0: Some(SubscribeMessage::StopEvent(NotificationType::UtxosChanged(S))). Else: None.
    Remove(All)Decrement All counterIf All == 0: Build S with every a in addresses having counter > 0. If S is not empty: Some(SubscribeMessage::StartEvent(NotificationType::UtxosChanged(S))). Else: Some(SubscribeMessage::StopEvent(NotificationType::UtxosChanged(empty))). Else: None.

    Note: It is recommended to clean the address set by removing addresses whose counters reach 0.