diff --git a/Cargo.lock b/Cargo.lock index 11fc3ad8e30..538b35477aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1275,6 +1275,22 @@ dependencies = [ "thiserror 1.0.64", ] +[[package]] +name = "dash-platform-balance-checker" +version = "2.0.0" +dependencies = [ + "anyhow", + "clap", + "dapi-grpc", + "dash-sdk", + "dpp", + "drive-proof-verifier", + "rpassword", + "rs-dapi-client", + "rs-sdk-trusted-context-provider", + "tokio", +] + [[package]] name = "dash-sdk" version = "2.0.0-rc.18" @@ -4240,7 +4256,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4534,6 +4550,17 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + [[package]] name = "rs-dapi-client" version = "2.0.0-rc.18" @@ -4581,6 +4608,16 @@ dependencies = [ "url", ] +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "rust_decimal" version = "1.36.0" diff --git a/Cargo.toml b/Cargo.toml index 1cd3125fbf2..9bf5ad30baa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,8 @@ members = [ "packages/wallet-utils-contract", "packages/token-history-contract", "packages/keyword-search-contract", - "packages/wasm-drive-verify" + "packages/wasm-drive-verify", + "packages/dash-platform-balance-checker" ] exclude = ["packages/wasm-sdk"] # This one is experimental and not ready for use diff --git a/packages/dash-platform-balance-checker/.gitignore b/packages/dash-platform-balance-checker/.gitignore new file mode 100644 index 00000000000..17b6a572c2b --- /dev/null +++ b/packages/dash-platform-balance-checker/.gitignore @@ -0,0 +1,25 @@ +# Build artifacts +/target/ +Cargo.lock + +# Output files +*_output.txt +*.out + +# Temporary documentation and analysis +ACTUAL_FINDINGS.md +BALANCE_RESULT.md +FINAL_RESULT.md +FINAL_SUMMARY.md +SUMMARY.md +TECHNICAL_FINDINGS.md + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/packages/dash-platform-balance-checker/Cargo.toml b/packages/dash-platform-balance-checker/Cargo.toml new file mode 100644 index 00000000000..ce05c9efc1e --- /dev/null +++ b/packages/dash-platform-balance-checker/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "dash-platform-balance-checker" +version = "2.0.0" +edition = "2021" + +[[bin]] +name = "dash-platform-balance-checker" +path = "src/main.rs" + +[[bin]] +name = "dash-platform-balance-simple" +path = "src/main_simple.rs" + +[[bin]] +name = "dash-platform-balance-trusted" +path = "src/main_trusted.rs" + +[dependencies] +dash-sdk = { path = "../rs-sdk" } +rs-dapi-client = { path = "../rs-dapi-client" } +dapi-grpc = { path = "../dapi-grpc" } +dpp = { path = "../rs-dpp" } +drive-proof-verifier = { path = "../rs-drive-proof-verifier" } +rs-sdk-trusted-context-provider = { path = "../rs-sdk-trusted-context-provider" } +tokio = { version = "1", features = ["full"] } +anyhow = "1.0" +clap = { version = "4.5", features = ["derive"] } +rpassword = "7.3" diff --git a/packages/dash-platform-balance-checker/README.md b/packages/dash-platform-balance-checker/README.md new file mode 100644 index 00000000000..7851d11c98e --- /dev/null +++ b/packages/dash-platform-balance-checker/README.md @@ -0,0 +1,135 @@ +# Dash Platform Balance Checker + +A collection of Rust command-line tools for checking Dash Platform balances using various SDK configurations and providers. + +## Overview + +This package contains multiple binary targets that demonstrate different approaches to fetching Dash Platform balances: + +- **dash-platform-balance-checker**: Main balance checker with full SDK integration (supports both mainnet and testnet) +- **dash-platform-balance-simple**: Direct DAPI client approach without full SDK overhead +- **dash-platform-balance-trusted**: Uses trusted HTTP context provider instead of Core RPC + +## Building + +```bash +cargo build --release +``` + +## Running + +Each binary can be run independently: + +```bash +# Main balance checker (requires identity ID) +cargo run --bin dash-platform-balance-checker [--testnet] + +# Direct DAPI implementation (hardcoded testnet example) +cargo run --bin dash-platform-balance-simple + +# With trusted provider (hardcoded testnet example) +cargo run --bin dash-platform-balance-trusted +``` + +## Dependencies + +- `dash-sdk`: Dash Platform SDK +- `rs-dapi-client`: DAPI client implementation +- `dapi-grpc`: gRPC definitions for DAPI +- `dpp`: Dash Platform Protocol +- `drive-proof-verifier`: Proof verification +- `rs-sdk-trusted-context-provider`: Trusted HTTP context provider +- `tokio`: Async runtime +- `anyhow`: Error handling + +## Usage + +### Identity ID Format + +The tools accept identity IDs in Base58 format. Example: +``` +5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk +``` + +### Examples + +#### Using the main balance checker +```bash +# Check balance on mainnet with local Core (default) +cargo run --bin dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk + +# Check balance on testnet +cargo run --bin dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk --testnet + +# Check balance with custom Core connection +cargo run --bin dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk \ + --core-host 192.168.1.100 --core-port 9998 \ + --core-user myuser --core-password mypass + +# Check balance without Core connection +cargo run --bin dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk --no-core + +# Show help +cargo run --bin dash-platform-balance-checker --help +``` + +#### Command Line Options +- `--testnet` - Use testnet instead of mainnet (default: mainnet) +- `--core-host ` - Core RPC host (default: localhost) +- `--core-port ` - Core RPC port (default: 9998 for mainnet, 19998 for testnet) +- `--core-user ` - Core RPC username (default: dashrpc) +- `--core-password ` - Core RPC password (default: password) +- `--no-core` - Skip Core connection (may limit some functionality) + +#### Expected Output +``` +Dash Platform Balance Checker - Mainnet +===================================== + +Identity ID: 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk +Parsed Identity ID: 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk + +Core connection: localhost:9998 +Core user: dashrpc + +Connecting to mainnet... +Fetching identity... + +✓ Identity found! + Balance: 1000000000 credits + Balance in DASH: 0.01 DASH + Revision: 1 + Public keys: 2 +``` + +## Network Configuration + +The main balance checker supports both networks: + +### Mainnet (default) +- https://dapi.dash.org:443 +- https://dapi-1.dash.org:443 +- https://dapi-2.dash.org:443 + +### Testnet (with --testnet flag) +- https://52.13.132.146:1443 +- https://52.89.154.48:1443 +- https://44.227.137.77:1443 +- (and others) + +## Prerequisites + +- Rust toolchain (1.70+) +- Internet connection to reach network nodes +- Dash Core node (optional but recommended): + - Default ports: 9998 (mainnet) or 19998 (testnet) + - Default credentials: dashrpc/password + - Can be skipped with `--no-core` flag + +## Documentation + +For JavaScript/Web implementations, see the `dash-platform-balance-checker-web` package. + +## License + +MIT \ No newline at end of file diff --git a/packages/dash-platform-balance-checker/src/main.rs b/packages/dash-platform-balance-checker/src/main.rs new file mode 100644 index 00000000000..8a89198523a --- /dev/null +++ b/packages/dash-platform-balance-checker/src/main.rs @@ -0,0 +1,204 @@ +use anyhow::Result; +use clap::Parser; +use dash_sdk::dapi_client::{Address, AddressList}; +use dash_sdk::dpp::identity::accessors::IdentityGettersV0; +use dash_sdk::platform::{Fetch, Identifier, Identity}; +use dash_sdk::SdkBuilder; +use std::env; +use std::str::FromStr; + +/// Number of credits per DASH +/// 1 DASH = 100,000,000,000 credits (100 billion) +const CREDITS_PER_DASH: f64 = 100_000_000_000.0; + +/// Dash Platform Balance Checker - Check identity balances on Dash Platform +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[command(after_help = "EXAMPLES: + # Check balance on mainnet using local Core + dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk + + # Check balance on testnet with custom Core (password from env var) + DASH_CORE_RPC_PASSWORD=mypass dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk \\ + --testnet --core-host 192.168.1.100 --core-port 19998 --core-user myuser + + # Check balance without Core connection + dash-platform-balance-checker 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk --no-core + +SECURITY: + Core RPC password can be provided in three ways (in order of preference): + 1. Set DASH_CORE_RPC_PASSWORD environment variable + 2. Enter interactively when prompted (most secure) + 3. Pass via --core-password flag (least secure, visible in process list)")] +struct Args { + /// Identity ID in Base58 format + identity_id: String, + + /// Use testnet instead of mainnet + #[arg(long)] + testnet: bool, + + /// Core RPC host + #[arg(long, default_value = "localhost")] + core_host: String, + + /// Core RPC port (defaults to 9998 for mainnet, 19998 for testnet) + #[arg(long)] + core_port: Option, + + /// Core RPC username + #[arg(long, default_value = "dashrpc")] + core_user: String, + + /// Core RPC password (reads from DASH_CORE_RPC_PASSWORD env var if not provided) + #[arg(long, hide = true)] + core_password: Option, + + /// Skip Core connection (may limit functionality) + #[arg(long)] + no_core: bool, +} + +impl Args { + /// Get the effective core port, using defaults based on network if not specified + fn get_core_port(&self) -> u16 { + self.core_port + .unwrap_or(if self.testnet { 19998 } else { 9998 }) + } +} + +#[tokio::main] +async fn main() -> Result<()> { + // Parse command line arguments + let args = Args::parse(); + + let network = if args.testnet { "Testnet" } else { "Mainnet" }; + let core_port = args.get_core_port(); + + // Handle Core password securely + let core_password = if args.no_core { + String::new() // Won't be used anyway + } else { + match &args.core_password { + Some(password) => { + eprintln!("Warning: Passing passwords via command line is insecure."); + eprintln!("Consider using DASH_CORE_RPC_PASSWORD environment variable instead."); + password.clone() + } + None => { + // Try environment variable first + match env::var("DASH_CORE_RPC_PASSWORD") { + Ok(password) => password, + Err(_) => { + // Prompt for password + eprint!("Enter Core RPC password: "); + match rpassword::read_password() { + Ok(password) => password, + Err(e) => { + eprintln!("Error reading password: {}", e); + return Ok(()); + } + } + } + } + } + } + }; + + println!("Dash Platform Balance Checker - {}", network); + println!("=====================================\n"); + println!("Identity ID: {}", args.identity_id); + + // Parse the identity ID + let identity_id = match Identifier::from_string( + &args.identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) { + Ok(id) => id, + Err(e) => { + eprintln!("Error: Invalid identity ID format: {}", e); + eprintln!("Identity ID must be in Base58 format"); + return Ok(()); + } + }; + + println!("Parsed Identity ID: {}", identity_id); + + // Core connection info + if !args.no_core { + println!("\nCore connection: {}:{}", args.core_host, core_port); + println!("Core user: {}", args.core_user); + } else { + println!("\nRunning without Core connection"); + } + + println!("\nConnecting to {}...", network.to_lowercase()); + + // Create SDK instance + let sdk = if args.testnet { + // Testnet configuration + let addresses = vec![ + Address::from_str("https://52.13.132.146:1443")?, + Address::from_str("https://52.89.154.48:1443")?, + Address::from_str("https://44.227.137.77:1443")?, + Address::from_str("https://52.40.219.41:1443")?, + Address::from_str("https://54.149.33.167:1443")?, + Address::from_str("https://54.187.14.232:1443")?, + Address::from_str("https://52.12.176.90:1443")?, + Address::from_str("https://52.34.144.50:1443")?, + Address::from_str("https://44.239.39.153:1443")?, + ]; + + let mut builder = SdkBuilder::new(AddressList::from_iter(addresses)); + if !args.no_core { + builder = + builder.with_core(&args.core_host, core_port, &args.core_user, &core_password); + } + builder.build()? + } else { + // Mainnet configuration + let addresses = vec![ + Address::from_str("https://dapi.dash.org:443")?, + Address::from_str("https://dapi-1.dash.org:443")?, + Address::from_str("https://dapi-2.dash.org:443")?, + ]; + + let mut builder = SdkBuilder::new(AddressList::from_iter(addresses)); + if !args.no_core { + builder = + builder.with_core(&args.core_host, core_port, &args.core_user, &core_password); + } + builder.build()? + }; + + // Fetch the identity + println!("Fetching identity..."); + match Identity::fetch(&sdk, identity_id).await { + Ok(Some(identity)) => { + println!("\n✓ Identity found!"); + println!(" Balance: {} credits", identity.balance()); + println!( + " Balance in DASH: {} DASH", + identity.balance() as f64 / CREDITS_PER_DASH + ); + println!(" Revision: {}", identity.revision()); + println!(" Public keys: {}", identity.public_keys().len()); + } + Ok(None) => { + println!("\n✗ Identity not found on {}", network.to_lowercase()); + println!( + " The identity {} does not exist on {}", + args.identity_id, + network.to_lowercase() + ); + } + Err(e) => { + println!("\n✗ Error fetching identity: {}", e); + if args.no_core { + println!(" Note: Some operations may fail without Core connection"); + } + } + } + + Ok(()) +} diff --git a/packages/dash-platform-balance-checker/src/main_simple.rs b/packages/dash-platform-balance-checker/src/main_simple.rs new file mode 100644 index 00000000000..011bed7f7be --- /dev/null +++ b/packages/dash-platform-balance-checker/src/main_simple.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use dapi_grpc::platform::v0::{ + GetIdentityBalanceRequest, GetIdentityBalanceResponse, GetIdentityRequest, GetIdentityResponse, +}; +use dpp::dashcore::Network; +use dpp::prelude::Identifier; +use rs_dapi_client::{Address, AddressList, DapiClient, RequestSettings}; +use std::str::FromStr; + +#[tokio::main] +async fn main() -> Result<()> { + println!("Dash Balance Checker - Testnet (Direct DAPI with Proofs)"); + println!("========================================================\n"); + + let identity_id_string = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; + println!("Identity ID: {}", identity_id_string); + + // Parse the identity ID properly + let identity_id = Identifier::from_string( + identity_id_string, + dpp::platform_value::string_encoding::Encoding::Base58, + )?; + println!("Parsed Identity ID: {}", identity_id); + + // Testnet addresses + let addresses = vec![ + Address::from_str("https://52.13.132.146:1443")?, + Address::from_str("https://52.89.154.48:1443")?, + Address::from_str("https://44.227.137.77:1443")?, + ]; + + // Create DAPI client + let dapi = DapiClient::new( + AddressList::from_iter(addresses), + RequestSettings::default(), + ); + + // Create request for identity (proved) + let request = GetIdentityRequest { + version: None, + id: identity_id.to_vec(), + prove: true, + }; + + println!("Fetching identity (proved)..."); + match dapi.platform.get_identity(request).await { + Ok(response) => { + if let Some(identity_bytes) = response.identity { + println!( + "✓ Identity found! Raw response length: {} bytes", + identity_bytes.len() + ); + + // Now get balance + let balance_request = GetIdentityBalanceRequest { + version: None, + id: identity_id.to_vec(), + prove: true, + }; + + println!("\nFetching balance..."); + match dapi.platform.get_identity_balance(balance_request).await { + Ok(balance_response) => { + if let Some(balance) = balance_response.balance { + let balance_value = balance.balance; + println!("\n✓ Balance found!"); + println!(" Balance: {} credits", balance_value); + println!( + " Balance in DASH: {} DASH", + balance_value as f64 / 100_000_000.0 + ); + } else { + println!("✗ No balance data in response"); + } + } + Err(e) => { + println!("✗ Error fetching balance: {}", e); + } + } + } else { + println!("✗ Identity not found"); + } + } + Err(e) => { + println!("✗ Error fetching identity: {}", e); + } + } + + Ok(()) +} diff --git a/packages/dash-platform-balance-checker/src/main_trusted.rs b/packages/dash-platform-balance-checker/src/main_trusted.rs new file mode 100644 index 00000000000..1c30d714c33 --- /dev/null +++ b/packages/dash-platform-balance-checker/src/main_trusted.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use dash_sdk::dapi_client::{Address, AddressList}; +use dash_sdk::dpp::identity::accessors::IdentityGettersV0; +use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dash_sdk::platform::{Fetch, Identifier, Identity}; +use dash_sdk::{Sdk, SdkBuilder}; +use dpp::dashcore::Network; +use rs_sdk_trusted_context_provider::TrustedHttpContextProvider; +use std::num::NonZeroUsize; +use std::str::FromStr; + +#[tokio::main] +async fn main() -> Result<()> { + println!("Dash Balance Checker - Testnet (Trusted Context Provider)"); + println!("=========================================================\n"); + + let identity_id_string = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; + println!("Identity ID: {}", identity_id_string); + + // Parse the identity ID + let identity_id = Identifier::from_string( + identity_id_string, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + )?; + println!("Parsed Identity ID: {}", identity_id); + + // Create SDK instance for testnet + println!("\nConnecting to testnet..."); + + // Testnet addresses + let addresses = vec![ + Address::from_str("https://52.13.132.146:1443")?, + Address::from_str("https://52.89.154.48:1443")?, + Address::from_str("https://44.227.137.77:1443")?, + Address::from_str("https://52.40.219.41:1443")?, + Address::from_str("https://54.149.33.167:1443")?, + ]; + + // Build SDK with trusted context provider + println!("Building SDK with trusted context provider..."); + + // Create the trusted context provider + let context_provider = TrustedHttpContextProvider::new( + Network::Testnet, + None, // devnet_name - only needed for devnet + NonZeroUsize::new(100).expect("cache size"), + )?; + + // Build SDK with mock core (context provider will handle everything) + let sdk = SdkBuilder::new(AddressList::from_iter(addresses)) + .with_core("127.0.0.1", 1, "mock", "mock") // Mock values, won't be used + .build()?; + + // Set our trusted context provider + sdk.set_context_provider(context_provider); + + // Fetch the identity + println!("Fetching identity with proof verification..."); + match Identity::fetch(&sdk, identity_id).await { + Ok(Some(identity)) => { + println!("\n✓ Identity found!"); + println!(" Balance: {} credits", identity.balance()); + println!( + " Balance in DASH: {} DASH", + identity.balance() as f64 / 100_000_000.0 + ); + println!(" Revision: {}", identity.revision()); + println!(" Public keys: {}", identity.public_keys().len()); + + // Show public key details + for (key_id, public_key) in identity.public_keys() { + println!( + " Key {}: type={}, purpose={}", + key_id, + public_key.key_type(), + public_key.purpose() + ); + } + } + Ok(None) => { + println!("\n✗ Identity not found on testnet"); + } + Err(e) => { + println!("\n✗ Error fetching identity: {}", e); + println!(" Details: {:?}", e); + } + } + + Ok(()) +}