From 588929b7b68ee14bba983382e3f9d87b7f39fd88 Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 8 Oct 2025 14:15:44 -0400 Subject: [PATCH 1/5] feat: dig coins returned from peer, validation failing --- Cargo.lock | 1 + Cargo.toml | 1 + src/wallet.rs | 47 ++++++++++++++--------------------------------- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 195936f..ecc930d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,6 +982,7 @@ dependencies = [ "aes-gcm", "base64 0.21.7", "bip39", + "chia", "chia-wallet-sdk", "datalayer-driver", "dirs", diff --git a/Cargo.toml b/Cargo.toml index b213c4d..38a100c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ rust-version = "1.70" [dependencies] datalayer-driver = "1.0.1" chia-wallet-sdk = "0.29.0" +chia = "0.26.0" bip39 = "2.0" thiserror = "1.0" tokio = { version = "1.0", features = ["full"] } diff --git a/src/wallet.rs b/src/wallet.rs index 457f231..181617b 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -5,8 +5,9 @@ use aes_gcm::{ }; use base64::{engine::general_purpose, Engine as _}; use bip39::{Language, Mnemonic}; -use chia_wallet_sdk::driver::{Cat, CatInfo, Puzzle}; -use chia_wallet_sdk::prelude::{Allocator, ToClvm}; +use chia_wallet_sdk::driver::{Cat, Puzzle}; +use chia_wallet_sdk::prelude::{Allocator, ToClvm, TreeHash}; +use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::types::MAINNET_CONSTANTS; use datalayer_driver::{ address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, @@ -155,27 +156,6 @@ impl Wallet { Ok(master_public_key_to_first_puzzle_hash(&master_pk)) } - /// Get the DIG token outer puzzle hash - pub async fn get_dig_coin_outer_puzzle_hash( - &self, - outer_puzzle: Option, - ) -> Result { - let dig_cat_outer_ph = match outer_puzzle { - Some(outer_puzzle) => outer_puzzle.puzzle_hash(), - None => { - let cat_outer_puzzle = self.get_cat_coin_outer_puzzle().await?; - cat_outer_puzzle.puzzle_hash() - } - }; - let bytes = Bytes32::new(dig_cat_outer_ph.to_bytes()); - Ok(bytes) - } - - pub async fn get_cat_coin_outer_puzzle(&self) -> Result { - let p2_ph = self.get_owner_puzzle_hash().await?; - Ok(CatInfo::new(*DIG_COIN_ASSET_ID, None, p2_ph)) - } - /// Get the owner public key as an address pub async fn get_owner_public_key(&self) -> Result { let owner_puzzle_hash = self.get_owner_puzzle_hash().await?; @@ -302,13 +282,14 @@ impl Wallet { omit_coins: Vec, verbose: bool, ) -> Result, WalletError> { - let dig_cat = self.get_cat_coin_outer_puzzle().await?; - let dig_cat_ph = self.get_dig_coin_outer_puzzle_hash(Some(dig_cat)).await?; + let p2 = self.get_owner_puzzle_hash().await?; + let dig_cat_ph = CatArgs::curry_tree_hash(*DIG_COIN_ASSET_ID, TreeHash::from(p2)); + let dig_cat_ph_bytes = Bytes32::from(dig_cat_ph.to_bytes()); // Get unspent coin states from the DataLayer-Driver async API let coin_states = datalayer_driver::async_api::get_all_unspent_coins( peer, - dig_cat_ph, + dig_cat_ph_bytes, None, // previous_height - start from genesis datalayer_driver::constants::get_mainnet_genesis_challenge(), // Use mainnet for now ) @@ -345,7 +326,7 @@ impl Wallet { Err(_) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::CoinSetError("Coin state rejected".to_string()) ); @@ -357,7 +338,7 @@ impl Wallet { Err(error) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::NetworkError(format!( "Failed to get coin state: {}", @@ -379,7 +360,7 @@ impl Wallet { Err(_) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::CoinSetError("Coin state rejected".to_string()) ); @@ -391,7 +372,7 @@ impl Wallet { Err(error) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::NetworkError(format!( "Failed to get puzzle and solution: {}", @@ -410,7 +391,7 @@ impl Wallet { Err(error) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::CoinSetError(format!( "Failed to parse puzzle and solution: {}", @@ -430,7 +411,7 @@ impl Wallet { Err(error) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::CoinSetError(format!( "Failed to parse puzzle and solution: {}", @@ -457,7 +438,7 @@ impl Wallet { Err(error) => { if verbose { eprintln!( - "coin_id {} | {}", + "ERROR: coin_id {} | {}", coin_id, WalletError::CoinSetError(format!( "Failed to parse CAT and prove lineage: {}", From 057e60de4bf40be59d1a0a20e5445314d3c14b19 Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 8 Oct 2025 16:45:42 -0400 Subject: [PATCH 2/5] feat: fetch dig balance working --- src/wallet.rs | 128 ++++++++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 181617b..865b935 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -9,12 +9,7 @@ use chia_wallet_sdk::driver::{Cat, Puzzle}; use chia_wallet_sdk::prelude::{Allocator, ToClvm, TreeHash}; use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::types::MAINNET_CONSTANTS; -use datalayer_driver::{ - address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, - master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, - puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, - Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, -}; +use datalayer_driver::{address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, UnspentCoinStates}; use hex_literal::hex; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -22,6 +17,7 @@ use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; +use chia::protocol::CoinState; pub static DIG_MIN_HEIGHT: u32 = 5777842; pub static DIG_COIN_ASSET_ID: Lazy = Lazy::new(|| { @@ -287,7 +283,7 @@ impl Wallet { let dig_cat_ph_bytes = Bytes32::from(dig_cat_ph.to_bytes()); // Get unspent coin states from the DataLayer-Driver async API - let coin_states = datalayer_driver::async_api::get_all_unspent_coins( + let unspent_coin_states = datalayer_driver::async_api::get_all_unspent_coins( peer, dig_cat_ph_bytes, None, // previous_height - start from genesis @@ -299,42 +295,46 @@ impl Wallet { // Convert coin states to coins and filter out omitted coins let omit_coin_ids: Vec = omit_coins.iter().map(get_coin_id).collect(); - let available_coins: Vec = coin_states + let available_coin_states: Vec = unspent_coin_states .coin_states .into_iter() - .map(|cs| cs.coin) - .filter(|coin| !omit_coin_ids.contains(&get_coin_id(coin))) + .filter(|coin_state| !omit_coin_ids.contains(&get_coin_id(&coin_state.coin))) .collect(); let mut proved_dig_token_coins: Vec = vec![]; let mut allocator = Allocator::new(); - for coin in &available_coins { + for coin_state in &available_coin_states { + let coin = &coin_state.coin; let coin_id = coin.coin_id(); - let parent_state = match peer + let coin_created_height = match coin_state.created_height { + Some(height) => height, + None => { + if verbose { + eprintln!( + "ERROR: coin_id {} | {}", + coin_id, + WalletError::CoinSetError("Cannot determine coin creation height".to_string()) + ); + } + continue; + } + }; + + + // 1) Request parent coin state + let parent_state_result = peer .request_coin_state( vec![coin.parent_coin_info], None, MAINNET_CONSTANTS.genesis_challenge, false, ) - .await - { - Ok(response) => match response { - Ok(parent_state) => parent_state, - Err(_) => { - if verbose { - eprintln!( - "ERROR: coin_id {} | {}", - coin_id, - WalletError::CoinSetError("Coin state rejected".to_string()) - ); - } - - continue; - } - }, + .await; + + let parent_state_response = match parent_state_result { + Ok(response) => response, Err(error) => { if verbose { eprintln!( @@ -346,29 +346,31 @@ impl Wallet { )) ); } - continue; } }; - let parent_puzzle_and_solution = match peer - .request_puzzle_and_solution(parent_state.coin_ids[0], DIG_MIN_HEIGHT) - .await - { - Ok(response) => match response { - Ok(puzzle_and_solution) => puzzle_and_solution, - Err(_) => { - if verbose { - eprintln!( - "ERROR: coin_id {} | {}", - coin_id, - WalletError::CoinSetError("Coin state rejected".to_string()) - ); - } - - continue; + let parent_state = match parent_state_response { + Ok(state) => state, + Err(_) => { + if verbose { + eprintln!( + "ERROR: coin_id {} | {}", + coin_id, + WalletError::CoinSetError("Coin state rejected".to_string()) + ); } - }, + continue; + } + }; + + // 2) Request parent puzzle and solution + let parent_puzzle_and_solution_result = peer + .request_puzzle_and_solution(parent_state.coin_ids[0], coin_created_height) + .await; + + let parent_puzzle_and_solution_response = match parent_puzzle_and_solution_result { + Ok(response) => response, Err(error) => { if verbose { eprintln!( @@ -380,14 +382,27 @@ impl Wallet { )) ); } + continue; + } + }; + let parent_puzzle_and_solution = match parent_puzzle_and_solution_response { + Ok(v) => v, + Err(_) => { + if verbose { + eprintln!( + "ERROR: coin_id {} | {}", + coin_id, + WalletError::CoinSetError("Parent puzzle solution rejected".to_string()) + ); + } continue; } }; - let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) - { - Ok(puzzle_ptr) => puzzle_ptr, + // 3) Convert puzzle to CLVM + let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) { + Ok(ptr) => ptr, Err(error) => { if verbose { eprintln!( @@ -399,14 +414,14 @@ impl Wallet { )) ); } - continue; } }; let parent_puzzle = Puzzle::parse(&allocator, parent_puzzle_ptr); - let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) - { + + // 4) Convert solution to CLVM + let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) { Ok(solution) => solution, Err(error) => { if verbose { @@ -419,18 +434,18 @@ impl Wallet { )) ); } - continue; } }; - // this instantiation proves the lineage of the coin. not used beyond that - match Cat::parse_children( + // 5) Parse CAT to prove lineage + let cat_parse_result = Cat::parse_children( &mut allocator, parent_state.coin_states[0].coin, parent_puzzle, parent_solution, - ) { + ); + match cat_parse_result { Ok(_) => { // lineage proved. append coin in question proved_dig_token_coins.push(*coin); @@ -446,7 +461,6 @@ impl Wallet { )) ); } - continue; } } From 8aee56de358a5ea6f81093e6db2fe090e099736c Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 8 Oct 2025 17:00:22 -0400 Subject: [PATCH 3/5] chore: formatting --- src/wallet.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 865b935..36fc156 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -5,11 +5,18 @@ use aes_gcm::{ }; use base64::{engine::general_purpose, Engine as _}; use bip39::{Language, Mnemonic}; +use chia::protocol::CoinState; +use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::driver::{Cat, Puzzle}; use chia_wallet_sdk::prelude::{Allocator, ToClvm, TreeHash}; -use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::types::MAINNET_CONSTANTS; -use datalayer_driver::{address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, UnspentCoinStates}; +use datalayer_driver::{ + address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, + master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, + puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, + Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, + UnspentCoinStates, +}; use hex_literal::hex; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -17,7 +24,6 @@ use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; -use chia::protocol::CoinState; pub static DIG_MIN_HEIGHT: u32 = 5777842; pub static DIG_COIN_ASSET_ID: Lazy = Lazy::new(|| { @@ -315,14 +321,15 @@ impl Wallet { eprintln!( "ERROR: coin_id {} | {}", coin_id, - WalletError::CoinSetError("Cannot determine coin creation height".to_string()) + WalletError::CoinSetError( + "Cannot determine coin creation height".to_string() + ) ); } continue; } }; - // 1) Request parent coin state let parent_state_result = peer .request_coin_state( @@ -393,7 +400,9 @@ impl Wallet { eprintln!( "ERROR: coin_id {} | {}", coin_id, - WalletError::CoinSetError("Parent puzzle solution rejected".to_string()) + WalletError::CoinSetError( + "Parent puzzle solution rejected".to_string() + ) ); } continue; @@ -401,7 +410,8 @@ impl Wallet { }; // 3) Convert puzzle to CLVM - let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) { + let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) + { Ok(ptr) => ptr, Err(error) => { if verbose { @@ -421,7 +431,8 @@ impl Wallet { let parent_puzzle = Puzzle::parse(&allocator, parent_puzzle_ptr); // 4) Convert solution to CLVM - let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) { + let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) + { Ok(solution) => solution, Err(error) => { if verbose { From babe1b8c5a07f41df78b42e5b51455ce9dc60dfd Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 8 Oct 2025 17:00:22 -0400 Subject: [PATCH 4/5] chore: formatting --- src/wallet.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 865b935..2d35b51 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -5,11 +5,17 @@ use aes_gcm::{ }; use base64::{engine::general_purpose, Engine as _}; use bip39::{Language, Mnemonic}; +use chia::protocol::CoinState; +use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::driver::{Cat, Puzzle}; use chia_wallet_sdk::prelude::{Allocator, ToClvm, TreeHash}; -use chia::puzzles::cat::CatArgs; use chia_wallet_sdk::types::MAINNET_CONSTANTS; -use datalayer_driver::{address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, UnspentCoinStates}; +use datalayer_driver::{ + address_to_puzzle_hash, connect_random, get_coin_id, master_public_key_to_first_puzzle_hash, + master_public_key_to_wallet_synthetic_key, master_secret_key_to_wallet_synthetic_secret_key, + puzzle_hash_to_address, secret_key_to_public_key, sign_message, verify_signature, Bytes, + Bytes32, Coin, CoinSpend, NetworkType, Peer, PublicKey, SecretKey, Signature, +}; use hex_literal::hex; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -17,7 +23,6 @@ use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; -use chia::protocol::CoinState; pub static DIG_MIN_HEIGHT: u32 = 5777842; pub static DIG_COIN_ASSET_ID: Lazy = Lazy::new(|| { @@ -315,14 +320,15 @@ impl Wallet { eprintln!( "ERROR: coin_id {} | {}", coin_id, - WalletError::CoinSetError("Cannot determine coin creation height".to_string()) + WalletError::CoinSetError( + "Cannot determine coin creation height".to_string() + ) ); } continue; } }; - // 1) Request parent coin state let parent_state_result = peer .request_coin_state( @@ -393,7 +399,9 @@ impl Wallet { eprintln!( "ERROR: coin_id {} | {}", coin_id, - WalletError::CoinSetError("Parent puzzle solution rejected".to_string()) + WalletError::CoinSetError( + "Parent puzzle solution rejected".to_string() + ) ); } continue; @@ -401,7 +409,8 @@ impl Wallet { }; // 3) Convert puzzle to CLVM - let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) { + let parent_puzzle_ptr = match parent_puzzle_and_solution.puzzle.to_clvm(&mut allocator) + { Ok(ptr) => ptr, Err(error) => { if verbose { @@ -421,7 +430,8 @@ impl Wallet { let parent_puzzle = Puzzle::parse(&allocator, parent_puzzle_ptr); // 4) Convert solution to CLVM - let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) { + let parent_solution = match parent_puzzle_and_solution.solution.to_clvm(&mut allocator) + { Ok(solution) => solution, Err(error) => { if verbose { From 6c52b0329329628977b939adb6a94bc8c9ed7cb3 Mon Sep 17 00:00:00 2001 From: William Wills Date: Wed, 8 Oct 2025 17:26:07 -0400 Subject: [PATCH 5/5] chore: bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecc930d..29c7110 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -977,7 +977,7 @@ dependencies = [ [[package]] name = "dig-wallet" -version = "0.3.0" +version = "0.4.0" dependencies = [ "aes-gcm", "base64 0.21.7", diff --git a/Cargo.toml b/Cargo.toml index 38a100c..733f2a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dig-wallet" -version = "0.3.0" +version = "0.4.0" edition = "2021" authors = ["DIG Network "] description = "A comprehensive Rust wallet implementation for Chia blockchain with full DataLayer-Driver integration"