added RPC explorer commands

This commit is contained in:
viraladmin 2026-06-12 10:27:28 -06:00
parent 38dabfa27f
commit a4c4518adf
57 changed files with 790 additions and 210 deletions

View File

@ -181,8 +181,8 @@ impl BurnTransaction {
let hash = &self.unsigned_burn.hash().await; let hash = &self.unsigned_burn.hash().await;
let signature = &self.signature; let signature = &self.signature;
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -176,8 +176,8 @@ impl CollateralClaimTransaction {
let signature = &self.signature; let signature = &self.signature;
// Collateral-claim transactions remain in the mempool table until mined or removed. // Collateral-claim transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -182,8 +182,8 @@ impl IssueTokenTransaction {
let signature = &self.signature; let signature = &self.signature;
// Issue-token transactions remain in the mempool table until mined or removed. // Issue-token transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -208,8 +208,8 @@ impl ContractPaymentTransaction {
let signature = &self.signature; let signature = &self.signature;
// Loan-payment transactions remain in the mempool table until mined or removed. // Loan-payment transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -326,8 +326,8 @@ impl LoanContractTransaction {
let signature2 = &self.signature2; let signature2 = &self.signature2;
// Loan contracts remain in the mempool table until mined or removed. // Loan contracts remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -253,8 +253,8 @@ impl MarketingTransaction {
let signature = &self.signature; let signature = &self.signature;
// Marketing transactions remain in the mempool table until mined or removed. // Marketing transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -230,8 +230,8 @@ impl CreateNftTransaction {
let signature = &self.signature; let signature = &self.signature;
// NFT-creation transactions remain in the mempool table until mined or removed. // NFT-creation transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -327,8 +327,8 @@ impl SwapTransaction {
let signature2 = &self.signature2; let signature2 = &self.signature2;
// Swap transactions remain in the mempool table until mined or removed. // Swap transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -204,8 +204,8 @@ impl CreateTokenTransaction {
let signature = &self.signature; let signature = &self.signature;
// Token-creation transactions remain in the mempool table until mined or removed. // Token-creation transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -226,8 +226,8 @@ impl TransferTransaction {
let signature = &self.signature; let signature = &self.signature;
// Transfer transactions remain in the mempool table until mined or removed. // Transfer transactions remain in the mempool table until mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -182,8 +182,8 @@ impl VanityAddressTransaction {
// Vanity transactions are written to the vanity mempool table // Vanity transactions are written to the vanity mempool table
// until the transaction is mined or removed. // until the transaction is mined or removed.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client
.execute( .execute(

View File

@ -3,10 +3,10 @@ use crate::orphans::structs::UndoTransactions;
use crate::orphans::torrent_candidates::hydrate_torrent_candidates; use crate::orphans::torrent_candidates::hydrate_torrent_candidates;
use crate::records::block_height::get_block_height::get_height; use crate::records::block_height::get_block_height::get_height;
use crate::records::memory::response_channels::reserve_entry_with_context; use crate::records::memory::response_channels::reserve_entry_with_context;
use crate::rpc::command_maps::RPC_TORRENT_BY_HEIGHT;
use crate::records::memory::torrent_status::{ use crate::records::memory::torrent_status::{
get_torrent_status, set_torrent_status, TorrentStatus, get_torrent_status, set_torrent_status, TorrentStatus,
}; };
use crate::rpc::command_maps::RPC_TORRENT_BY_HEIGHT;
use crate::torrent::structs::Torrent; use crate::torrent::structs::Torrent;
use crate::torrent::torrenting_system::save_torrent::{ use crate::torrent::torrenting_system::save_torrent::{
list_staged_torrents_for_height, read_staged_torrent, list_staged_torrents_for_height, read_staged_torrent,

View File

@ -3,8 +3,9 @@ use crate::blocks::loans::LoanContractTransaction;
use crate::common::network_paths_and_settings::block_extension_and_paths; use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::add_payments_db::remove_payment; use crate::records::record_chain::add_payments_db::remove_payment;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid; use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
@ -79,22 +80,24 @@ pub async fn undo_borrower_transaction(
let _ = let _ =
balance_sheet_operation_with_db(db, borrower, payback_amount, &loan_coin, operand_addition); balance_sheet_operation_with_db(db, borrower, payback_amount, &loan_coin, operand_addition);
// Remove the payment transaction lookup from the txid tree. // Remove the payment transaction lookup from the txid tree.
let txid_tree = db let txid_tree = db
.open_tree("txid") .open_tree("txid")
.map_err(|e| format!("Failed to open txid tree: {e}"))?; .map_err(|e| format!("Failed to open txid tree: {e}"))?;
let tx_hash = transaction.unsigned_contract_payment.hash().await; let tx_hash = transaction.unsigned_contract_payment.hash().await;
txid_tree let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?;
.remove(decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?) let _ =
.map_err(|e| format!("Failed to remove borrower txid: {e}"))?; remove_wallet_transaction_index(db, &[borrower, &lender, mining_receiver], &tx_hash_bytes);
txid_tree
.remove(tx_hash_bytes.clone())
.map_err(|e| format!("Failed to remove borrower txid: {e}"))?;
// Loan payments involving NFTs also add a provenance entry for the loan // Loan payments involving NFTs also add a provenance entry for the loan
// asset, so remove it if this loan coin is tracked as an NFT. // asset, so remove it if this loan coin is tracked as an NFT.
let nft_tree = db let nft_tree = db
.open_tree("nfts") .open_tree("nfts")
.map_err(|e| format!("Failed to open nfts tree: {e}"))?; .map_err(|e| format!("Failed to open nfts tree: {e}"))?;
let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?; if nft_tree.contains_key(loan_coin.as_bytes()).unwrap_or(false) {
if nft_tree.contains_key(loan_coin.as_bytes()).unwrap_or(false) {
let _ = remove_nft_history_entry(db, &loan_coin, &tx_hash_bytes); let _ = remove_nft_history_entry(db, &loan_coin, &tx_hash_bytes);
} }

View File

@ -5,6 +5,7 @@ use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::records::record_chain::token_provenance::remove_token_history_entry; use crate::records::record_chain::token_provenance::remove_token_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_burn_transaction(transaction: BurnTransaction, mining_receiver: &str, db: &Db) { pub async fn undo_burn_transaction(transaction: BurnTransaction, mining_receiver: &str, db: &Db) {
@ -53,6 +54,11 @@ pub async fn undo_burn_transaction(transaction: BurnTransaction, mining_receiver
); );
let hash_binary = decode(&transaction.unsigned_burn.hash().await).unwrap(); let hash_binary = decode(&transaction.unsigned_burn.hash().await).unwrap();
let _ = remove_wallet_transaction_index(
db,
&[&transaction.unsigned_burn.address, mining_receiver],
&hash_binary,
);
// Delete the txid lookup inserted when the burn was saved. // Delete the txid lookup inserted when the burn was saved.
let txid_tree = db.open_tree("txid").unwrap(); let txid_tree = db.open_tree("txid").unwrap();

View File

@ -1,108 +1,108 @@
use crate::blocks::collateral::CollateralClaimTransaction; use crate::blocks::collateral::CollateralClaimTransaction;
use crate::blocks::loans::LoanContractTransaction; use crate::blocks::loans::LoanContractTransaction;
use crate::common::network_paths_and_settings::block_extension_and_paths; use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid; use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::rpc::responses::RpcResponse; use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::sled::Db; use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
pub async fn undo_collateral_transaction(
transaction: CollateralClaimTransaction, pub async fn undo_collateral_transaction(
mining_receiver: &str, transaction: CollateralClaimTransaction,
db: &Db, mining_receiver: &str,
) -> Result<(), String> { db: &Db,
// restore balances and contract state for a collateral ) -> Result<(), String> {
// claim that is being removed during orphan rollback // restore balances and contract state for a collateral
let operand_subtraction = "subtraction"; // claim that is being removed during orphan rollback
let operand_addition = "addition"; let operand_subtraction = "subtraction";
let ( let operand_addition = "addition";
_network_name, let (
_padded_base_coin, _network_name,
type_str, _padded_base_coin,
_torrentpath, type_str,
_wallet_path, _torrentpath,
_blockpath, _wallet_path,
_db_path, _blockpath,
_balance_path, _db_path,
_log_path, _balance_path,
) = block_extension_and_paths(); _log_path,
) = block_extension_and_paths();
// reload the original loan contract so the collateral
// asset and amount can be restored correctly // reload the original loan contract so the collateral
let contract_hash = decode(&transaction.unsigned_collateral_claim.contract_hash) // asset and amount can be restored correctly
.map_err(|e| format!("Error decoding contract hash: {e}"))?; let contract_hash = decode(&transaction.unsigned_collateral_claim.contract_hash)
let contract = request_transaction_by_txid(db, contract_hash.clone()).await; .map_err(|e| format!("Error decoding contract hash: {e}"))?;
let contract = request_transaction_by_txid(db, contract_hash.clone()).await;
let loan_txtype = 7;
let loan_tx = match contract { let loan_txtype = 7;
RpcResponse::Binary(contract_bytes) => { let loan_tx = match contract {
if contract_bytes.is_empty() { RpcResponse::Binary(contract_bytes) => {
return Err("Invalid loan contract: empty transaction bytes".to_string()); if contract_bytes.is_empty() {
} return Err("Invalid loan contract: empty transaction bytes".to_string());
if contract_bytes[0] != loan_txtype { }
return Err( if contract_bytes[0] != loan_txtype {
"Invalid loan contract: referenced transaction is not a loan contract" return Err(
.to_string(), "Invalid loan contract: referenced transaction is not a loan contract"
); .to_string(),
} );
LoanContractTransaction::from_bytes(loan_txtype, &contract_bytes[1..]) }
.await LoanContractTransaction::from_bytes(loan_txtype, &contract_bytes[1..])
.map_err(|e| e.to_string())? .await
} .map_err(|e| e.to_string())?
}; }
};
let collateral = loan_tx.unsigned_loan_contract.collateral;
let collateral_amount = loan_tx.unsigned_loan_contract.collateral_amount; let collateral = loan_tx.unsigned_loan_contract.collateral;
let collateral_holding = format!( let collateral_amount = loan_tx.unsigned_loan_contract.collateral_amount;
"collateral_{}", let collateral_holding = format!(
transaction.unsigned_collateral_claim.contract_hash "collateral_{}",
); transaction.unsigned_collateral_claim.contract_hash
let claimer = &transaction.unsigned_collateral_claim.address; );
let txfee = transaction.unsigned_collateral_claim.txfee; let claimer = &transaction.unsigned_collateral_claim.address;
let txfee = transaction.unsigned_collateral_claim.txfee;
// reverse the fee and move the collateral back into the
// contract holding wallet until the claim exists again // reverse the fee and move the collateral back into the
let _ = // contract holding wallet until the claim exists again
balance_sheet_operation_with_db(db, mining_receiver, txfee, &type_str, operand_subtraction); let _ =
let _ = balance_sheet_operation_with_db(db, claimer, txfee, &type_str, operand_addition); balance_sheet_operation_with_db(db, mining_receiver, txfee, &type_str, operand_subtraction);
let _ = balance_sheet_operation_with_db( let _ = balance_sheet_operation_with_db(db, claimer, txfee, &type_str, operand_addition);
db, let _ = balance_sheet_operation_with_db(
claimer, db,
collateral_amount, claimer,
&collateral, collateral_amount,
operand_subtraction, &collateral,
); operand_subtraction,
let _ = balance_sheet_operation_with_db( );
db, let _ = balance_sheet_operation_with_db(
&collateral_holding, db,
collateral_amount, &collateral_holding,
&collateral, collateral_amount,
operand_addition, &collateral,
); operand_addition,
);
// Remove the collateral-claim transaction lookup from the txid tree.
let txid_tree = db.open_tree("txid").unwrap(); // Remove the collateral-claim transaction lookup from the txid tree.
let tx_hash = transaction.unsigned_collateral_claim.hash().await; let txid_tree = db.open_tree("txid").unwrap();
txid_tree let tx_hash = transaction.unsigned_collateral_claim.hash().await;
.remove(decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?) let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?;
.unwrap(); let _ = remove_wallet_transaction_index(db, &[claimer, mining_receiver], &tx_hash_bytes);
txid_tree.remove(tx_hash_bytes.clone()).unwrap();
// NFT collateral claims write provenance for the collateral asset.
let nft_tree = db.open_tree("nfts").unwrap(); // NFT collateral claims write provenance for the collateral asset.
let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?; let nft_tree = db.open_tree("nfts").unwrap();
if nft_tree if nft_tree
.contains_key(collateral.as_bytes()) .contains_key(collateral.as_bytes())
.unwrap_or(false) .unwrap_or(false)
{ {
let _ = remove_nft_history_entry(db, &collateral, &tx_hash_bytes); let _ = remove_nft_history_entry(db, &collateral, &tx_hash_bytes);
} }
// Mark the loan contract active again because the collateral claim no // Mark the loan contract active again because the collateral claim no
// longer exists after rollback. // longer exists after rollback.
let loan_tree = db.open_tree("loan").unwrap(); let loan_tree = db.open_tree("loan").unwrap();
loan_tree.insert(contract_hash, "true".as_bytes()).unwrap(); loan_tree.insert(contract_hash, "true".as_bytes()).unwrap();
Ok(()) Ok(())
} }

View File

@ -4,6 +4,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::{remove_nft_history_entry, remove_nft_origin}; use crate::records::record_chain::nft_provenance::{remove_nft_history_entry, remove_nft_origin};
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
const NFT_UNIT: u64 = 100_000_000; const NFT_UNIT: u64 = 100_000_000;
@ -43,6 +44,7 @@ pub async fn undo_create_nft_transaction(
); );
let _ = balance_sheet_operation_with_db(db, creator, *txfee, &type_str, operand_addition); let _ = balance_sheet_operation_with_db(db, creator, *txfee, &type_str, operand_addition);
let hash_binary = decode(&transaction.unsigned_create_nft.hash().await).unwrap(); let hash_binary = decode(&transaction.unsigned_create_nft.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[creator, mining_receiver], &hash_binary);
// Remove the create-NFT transaction lookup from the txid tree. // Remove the create-NFT transaction lookup from the txid tree.
let tree = db.open_tree("txid").unwrap(); let tree = db.open_tree("txid").unwrap();

View File

@ -3,6 +3,7 @@ use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::token_provenance::clear_token_history; use crate::records::record_chain::token_provenance::clear_token_history;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_create_token_transaction( pub async fn undo_create_token_transaction(
@ -46,6 +47,7 @@ pub async fn undo_create_token_transaction(
let ticker_binary = &transaction.unsigned_create_token.ticker.as_bytes(); let ticker_binary = &transaction.unsigned_create_token.ticker.as_bytes();
let hash_binary = decode(&transaction.unsigned_create_token.hash().await).unwrap(); let hash_binary = decode(&transaction.unsigned_create_token.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[creator, mining_receiver], &hash_binary);
// Remove the create-token transaction lookup from the txid tree. // Remove the create-token transaction lookup from the txid tree.
let tree = db.open_tree("txid").unwrap(); let tree = db.open_tree("txid").unwrap();

View File

@ -3,6 +3,7 @@ use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::token_provenance::remove_token_history_entry; use crate::records::record_chain::token_provenance::remove_token_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_issue_token_transaction( pub async fn undo_issue_token_transaction(
@ -43,6 +44,7 @@ pub async fn undo_issue_token_transaction(
let _ = balance_sheet_operation_with_db(db, creator, *number, ticker, operand_subtraction); let _ = balance_sheet_operation_with_db(db, creator, *number, ticker, operand_subtraction);
let hash_binary = decode(&transaction.unsigned_issue_token.hash().await).unwrap(); let hash_binary = decode(&transaction.unsigned_issue_token.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[creator, mining_receiver], &hash_binary);
// Delete the issued-token transaction lookup and provenance record. // Delete the issued-token transaction lookup and provenance record.
let txid_tree = db.open_tree("txid").unwrap(); let txid_tree = db.open_tree("txid").unwrap();

View File

@ -3,6 +3,7 @@ use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_loan_creation_transaction( pub async fn undo_loan_creation_transaction(
@ -66,6 +67,7 @@ pub async fn undo_loan_creation_transaction(
); );
let hash_binary = decode(hash).unwrap(); let hash_binary = decode(hash).unwrap();
let _ = remove_wallet_transaction_index(db, &[lender, borrower, mining_receiver], &hash_binary);
// delete the txid and remove the active loan record // delete the txid and remove the active loan record
// so the contract no longer exists on-chain // so the contract no longer exists on-chain

View File

@ -2,6 +2,7 @@ use crate::blocks::marketing::MarketingTransaction;
use crate::common::network_paths_and_settings::block_extension_and_paths; use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_marketing_transaction( pub async fn undo_marketing_transaction(
@ -40,6 +41,7 @@ pub async fn undo_marketing_transaction(
let _ = balance_sheet_operation_with_db(db, advertiser, *txfee, &type_str, operand_addition); let _ = balance_sheet_operation_with_db(db, advertiser, *txfee, &type_str, operand_addition);
let hash = decode(&transaction.unsigned_marketing.hash().await).unwrap(); let hash = decode(&transaction.unsigned_marketing.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[advertiser, mining_receiver], &hash);
// Remove the marketing transaction lookup from the txid tree. // Remove the marketing transaction lookup from the txid tree.
let tree = db.open_tree("txid").unwrap(); let tree = db.open_tree("txid").unwrap();

View File

@ -5,6 +5,7 @@ use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::rewards_tx::{ use crate::records::record_chain::rewards_tx::{
remove_reward_credit_marker, reward_credit_applied, remove_reward_credit_marker, reward_credit_applied,
}; };
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_rewards_transaction( pub async fn undo_rewards_transaction(
@ -38,6 +39,7 @@ pub async fn undo_rewards_transaction(
} }
let hash = decode(transaction.unsigned.hash().await).unwrap(); let hash = decode(transaction.unsigned.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[mining_receiver], &hash);
// Remove the reward transaction lookup from the txid tree. // Remove the reward transaction lookup from the txid tree.
let tree = db.open_tree("txid").unwrap(); let tree = db.open_tree("txid").unwrap();

View File

@ -4,6 +4,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_swap_transaction(transaction: SwapTransaction, mining_receiver: &str, db: &Db) { pub async fn undo_swap_transaction(transaction: SwapTransaction, mining_receiver: &str, db: &Db) {
@ -84,6 +85,7 @@ pub async fn undo_swap_transaction(transaction: SwapTransaction, mining_receiver
// Convert the txid hash back to bytes for tree lookup/removal. // Convert the txid hash back to bytes for tree lookup/removal.
let hash = decode(&transaction.unsigned_swap.hash().await).unwrap(); let hash = decode(&transaction.unsigned_swap.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[sender1, sender2, mining_receiver], &hash);
// Remove the txid lookup for the rolled-back swap. // Remove the txid lookup for the rolled-back swap.
let tree = db.open_tree("txid").unwrap(); let tree = db.open_tree("txid").unwrap();

View File

@ -4,6 +4,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::nft_provenance::remove_nft_history_entry; use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::sled::Db; use crate::sled::Db;
pub async fn undo_transfer_transaction( pub async fn undo_transfer_transaction(
@ -51,6 +52,7 @@ pub async fn undo_transfer_transaction(
let _ = balance_sheet_operation_with_db(db, sender, *txfee, &type_str, operand_addition); let _ = balance_sheet_operation_with_db(db, sender, *txfee, &type_str, operand_addition);
let hash = decode(&transaction.unsigned_transfer.hash().await).unwrap(); let hash = decode(&transaction.unsigned_transfer.hash().await).unwrap();
let _ = remove_wallet_transaction_index(db, &[sender, receiver, mining_receiver], &hash);
// Remove the txid lookup so the rolled-back transfer no longer resolves as // Remove the txid lookup so the rolled-back transfer no longer resolves as
// an on-chain transaction. // an on-chain transaction.

View File

@ -2,6 +2,7 @@ use crate::blocks::vanity::VanityAddressTransaction;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::wallet_tx_index::remove_wallet_transaction_index;
use crate::records::wallet_registry::{ use crate::records::wallet_registry::{
register_or_update_vanity_address, remove_registered_vanity_for_owner, register_or_update_vanity_address, remove_registered_vanity_for_owner,
take_previous_vanity_for_txid, VanityRegistrationResult, take_previous_vanity_for_txid, VanityRegistrationResult,
@ -63,6 +64,7 @@ pub async fn undo_vanity_transaction(
.map_err(|err| format!("Could not open txid tree during vanity undo: {err}"))?; .map_err(|err| format!("Could not open txid tree during vanity undo: {err}"))?;
let txkey = decode(&txhash) let txkey = decode(&txhash)
.map_err(|err| format!("Could not decode vanity txhash during undo: {err}"))?; .map_err(|err| format!("Could not decode vanity txhash during undo: {err}"))?;
let _ = remove_wallet_transaction_index(db, &[&owner_address, mining_receiver], &txkey);
tree.remove(txkey) tree.remove(txkey)
.map_err(|err| format!("Could not remove vanity txid mapping during undo: {err}"))?; .map_err(|err| format!("Could not remove vanity txid mapping during undo: {err}"))?;

View File

@ -351,13 +351,12 @@ impl Connection {
} }
let message_type = RPC_BLOCK_HEIGHT; // Block-height request used as a lightweight checkup ping. let message_type = RPC_BLOCK_HEIGHT; // Block-height request used as a lightweight checkup ping.
let (checkup_key, _checkup_tx, checkup_rx_mutex) = let (checkup_key, _checkup_tx, checkup_rx_mutex) = reserve_entry_with_context(
reserve_entry_with_context( command_map.clone(),
command_map.clone(), Some(RPC_BLOCK_HEIGHT),
Some(RPC_BLOCK_HEIGHT), Some(format!("{ip}:{port}")),
Some(format!("{ip}:{port}")), )
) .await;
.await;
// Send a lightweight ping message and wait for the reply // Send a lightweight ping message and wait for the reply
// routed back through the shared response hashmap. // routed back through the shared response hashmap.

View File

@ -27,9 +27,7 @@ async fn connect_client() -> Result<Client> {
} }
pub async fn db_client() -> Result<Arc<Client>> { pub async fn db_client() -> Result<Arc<Client>> {
let slot = DB let slot = DB.get().ok_or_else(|| anyhow!("DB not initialized"))?;
.get()
.ok_or_else(|| anyhow!("DB not initialized"))?;
Ok(slot.read().await.clone()) Ok(slot.read().await.clone())
} }
@ -69,7 +67,7 @@ pub async fn init_db() -> Result<()> {
pub async fn setup_mempool() -> Result<()> { pub async fn setup_mempool() -> Result<()> {
// Create or migrate the mempool schema, deduplicate any stale rows, // Create or migrate the mempool schema, deduplicate any stale rows,
// add the selection indexes, and start from an empty live mempool. // add the selection indexes, and start from an empty live mempool.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
let schema = r#" let schema = r#"
@ -404,7 +402,7 @@ pub async fn setup_mempool() -> Result<()> {
pub async fn clear_mempool() -> Result<()> { pub async fn clear_mempool() -> Result<()> {
// Startup clears any leftover mempool rows so a node restart begins // Startup clears any leftover mempool rows so a node restart begins
// from a clean pending-transaction state. // from a clean pending-transaction state.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
client client

View File

@ -1,10 +1,24 @@
use super::*; use super::*;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
fn index_selected_wallet_transaction(
pending_effects: &mut PendingEffects,
addresses: &[&str],
block_height: u32,
entry_index: usize,
txid: &[u8],
) -> Result<()> {
let entry_index =
u32::try_from(entry_index).map_err(|_| anyhow!("Wallet transaction index overflowed"))?;
index_wallet_transaction(pending_effects, addresses, block_height, entry_index, txid)
.map_err(|err| anyhow!(err))
}
pub async fn select_transactions_for_block(limit: i64) -> Result<SelectedMempoolBatch> { pub async fn select_transactions_for_block(limit: i64) -> Result<SelectedMempoolBatch> {
// Pull the highest-priority unprocessed rows across all mempool // Pull the highest-priority unprocessed rows across all mempool
// tables, keeping the original bytes for block-file streaming later. // tables, keeping the original bytes for block-file streaming later.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
let rows = client let rows = client
.query( .query(
@ -383,6 +397,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[sender, receiver, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Token { SelectedMempoolTransaction::Token {
fee, fee,
@ -412,8 +433,8 @@ pub async fn apply_selected_transaction_math(
// Local mined blocks need to persist the token hard-limit // Local mined blocks need to persist the token hard-limit
// metadata just like the downloaded-block save path does. // metadata just like the downloaded-block save path does.
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
let token_row = client let token_row = client
.query_opt( .query_opt(
"SELECT hard_limit FROM token WHERE hash = $1 LIMIT 1", "SELECT hard_limit FROM token WHERE hash = $1 LIMIT 1",
@ -443,6 +464,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[creator, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::IssueToken { SelectedMempoolTransaction::IssueToken {
fee, fee,
@ -474,6 +502,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[creator, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Burn { SelectedMempoolTransaction::Burn {
fee, fee,
@ -502,6 +537,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[address, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Nft { SelectedMempoolTransaction::Nft {
fee, fee,
@ -568,6 +610,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[creator, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Marketing { SelectedMempoolTransaction::Marketing {
fee, fee,
@ -587,6 +636,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[advertiser, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Vanity { SelectedMempoolTransaction::Vanity {
fee, fee,
@ -610,6 +666,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[address, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Swap { SelectedMempoolTransaction::Swap {
fee1, fee1,
@ -683,6 +746,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[sender1, sender2, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Lender { SelectedMempoolTransaction::Lender {
fee, fee,
@ -745,6 +815,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[lender, borrower, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Borrower { SelectedMempoolTransaction::Borrower {
fee, fee,
@ -806,6 +883,14 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
let lender_address = binary_to_string(lender.clone());
index_selected_wallet_transaction(
pending_effects,
&[address, &lender_address, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
SelectedMempoolTransaction::Collateral { SelectedMempoolTransaction::Collateral {
fee, fee,
@ -854,6 +939,13 @@ pub async fn apply_selected_transaction_math(
decode(hash)?, decode(hash)?,
format!("{block_number}:{tx_index}").into_bytes(), format!("{block_number}:{tx_index}").into_bytes(),
); );
index_selected_wallet_transaction(
pending_effects,
&[address, &miner],
block_number,
tx_index,
&decode(hash)?,
)?;
} }
} }
} }
@ -924,7 +1016,7 @@ pub async fn stream_selected_transaction_originals(
} }
pub async fn delete_selected_transactions(batch: &SelectedMempoolBatch) -> Result<()> { pub async fn delete_selected_transactions(batch: &SelectedMempoolBatch) -> Result<()> {
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
// Each transaction kind still lives in a separate SQL table, so deletion // Each transaction kind still lives in a separate SQL table, so deletion

View File

@ -335,7 +335,9 @@ impl NodeInfo {
} }
if !address_map.contains_key(&edit.address) { if !address_map.contains_key(&edit.address) {
if let Some(existing_node) = address_map.values_mut().find(|node| node.ip == edit.ip) { if let Some(existing_node) =
address_map.values_mut().find(|node| node.ip == edit.ip)
{
if existing_node.deleted_timestamp == 0 && edit.ip != GENESIS_IP { if existing_node.deleted_timestamp == 0 && edit.ip != GENESIS_IP {
penalize_duplicate_ip = true; penalize_duplicate_ip = true;
} }
@ -344,20 +346,17 @@ impl NodeInfo {
if !penalize_duplicate_ip { if !penalize_duplicate_ip {
// Persist the new node locally. Network-map entries are bare // Persist the new node locally. Network-map entries are bare
// IP membership records, separate from live socket keys. // IP membership records, separate from live socket keys.
address_map.insert( address_map.insert(edit.address.clone(), {
edit.address.clone(), let mut node = NodeInfo::new(
{ edit.ip.clone(),
let mut node = NodeInfo::new( blocks_mined,
edit.ip.clone(), edit.modified_by.clone(),
blocks_mined, edit.modified_timestamp,
edit.modified_by.clone(), edit.modified_signature.clone(),
edit.modified_timestamp, );
edit.modified_signature.clone(), node.monitoring = monitors.clone();
); node
node.monitoring = monitors.clone(); });
node
},
);
state_changed = true; state_changed = true;
} }
} }

View File

@ -113,13 +113,8 @@ impl NodeInfo {
.unwrap(), .unwrap(),
); );
let mut node = NodeInfo::new( let mut node =
ip, NodeInfo::new(ip, blocks_mined, added_by, added_timestamp, added_signature);
blocks_mined,
added_by,
added_timestamp,
added_signature,
);
node.deleted_timestamp = deleted_timestamp; node.deleted_timestamp = deleted_timestamp;
node.deleted_block = deleted_block; node.deleted_block = deleted_block;
node.monitoring = Vec::new(); node.monitoring = Vec::new();

View File

@ -112,12 +112,13 @@ pub async fn reserve_transient_entry_with_context(
tokio::spawn(async move { tokio::spawn(async move {
let received = { let received = {
let mut rx = rx.lock().await; let mut rx = rx.lock().await;
matches!(crate::timeout(Duration::from_secs(30), rx.recv()).await, Ok(Some(_))) matches!(
crate::timeout(Duration::from_secs(30), rx.recv()).await,
Ok(Some(_))
)
}; };
if !received { if !received {
warn!( warn!("[rpc_trace] transient uid expired without reply: uid={key:?}");
"[rpc_trace] transient uid expired without reply: uid={key:?}"
);
} }
delete_entry(map, key).await; delete_entry(map, key).await;
}); });

View File

@ -3,6 +3,7 @@ use crate::blocks::loans::LoanContractTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid; use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
@ -106,6 +107,17 @@ pub async fn process_borrower(
let txkey = decode(txhash).map_err(|e| format!("Error decoding borrower txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding borrower txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[
&transaction.unsigned_contract_payment.address,
&lender,
&miner,
],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
pending_effects.append_tree_if_key_exists( pending_effects.append_tree_if_key_exists(
"nfts", "nfts",
"nft_history", "nft_history",

View File

@ -3,6 +3,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -70,6 +71,13 @@ pub async fn process_burn(
let txkey = decode(txhash).map_err(|e| format!("Error decoding burn txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding burn txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_burn.address, &miner],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -3,6 +3,7 @@ use crate::blocks::loans::LoanContractTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid; use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
@ -96,6 +97,13 @@ pub async fn process_collateral(
let txkey = decode(txhash).map_err(|e| format!("Error decoding hex: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding hex: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_collateral_claim.address, &miner],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
pending_effects.append_tree_if_key_exists( pending_effects.append_tree_if_key_exists(
"nfts", "nfts",
"nft_history", "nft_history",

View File

@ -2,6 +2,7 @@ use crate::blocks::issue_token::IssueTokenTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -66,7 +67,14 @@ pub async fn process_issue_token(
// transaction back to its block and index position. // transaction back to its block and index position.
let txkey = txhash_bytes; let txkey = txhash_bytes;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey.clone(), txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_issue_token.creator, &miner],
block_header_number,
**index as u32,
&txkey,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -2,6 +2,7 @@ use crate::blocks::loans::LoanContractTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -75,6 +76,17 @@ pub async fn process_lender(
let txkey = decode(&txhash).map_err(|e| format!("Error decoding hex: {e}"))?; let txkey = decode(&txhash).map_err(|e| format!("Error decoding hex: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[
&transaction.unsigned_loan_contract.lender,
&transaction.unsigned_loan_contract.borrower,
&miner,
],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
let loankey = decode(&txhash).map_err(|e| format!("Error decoding hex: {e}"))?; let loankey = decode(&txhash).map_err(|e| format!("Error decoding hex: {e}"))?;
pending_effects.set_tree("loan", loankey, b"true".to_vec()); pending_effects.set_tree("loan", loankey, b"true".to_vec());
pending_effects.append_tree_if_key_exists( pending_effects.append_tree_if_key_exists(

View File

@ -2,6 +2,7 @@ use crate::blocks::marketing::MarketingTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -23,6 +24,8 @@ pub async fn process_marketing(
// Serialize the marketing transaction and compute its txid before // Serialize the marketing transaction and compute its txid before
// applying the fee transfer side effects. // applying the fee transfer side effects.
let txhash = transaction.unsigned_marketing.hash().await; let txhash = transaction.unsigned_marketing.hash().await;
let txhash_bytes =
decode(&txhash).map_err(|e| format!("Error decoding marketing txhash: {e}"))?;
let transaction_bytes = match transaction.to_bytes().await { let transaction_bytes = match transaction.to_bytes().await {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(e) => return Err(e.to_string()), Err(e) => return Err(e.to_string()),
@ -46,8 +49,15 @@ pub async fn process_marketing(
// Record the txid location so RPC lookups can resolve the saved // Record the txid location so RPC lookups can resolve the saved
// transaction back to its block and index position. // transaction back to its block and index position.
let txkey = decode(txhash).map_err(|e| format!("Error decoding marketing txid key: {e}"))?; let txkey = txhash_bytes.clone();
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_marketing.advertiser, &miner],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -22,3 +22,4 @@ pub mod token_provenance;
pub mod token_tx; pub mod token_tx;
pub mod transfer_tx; pub mod transfer_tx;
pub mod vanity_tx; pub mod vanity_tx;
pub mod wallet_tx_index;

View File

@ -3,6 +3,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -97,6 +98,13 @@ pub async fn process_nft(
let txkey = decode(txhash).map_err(|e| format!("Error decoding nft txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding nft txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_create_nft.creator, &miner],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -4,6 +4,7 @@ use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db; use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::PendingEffects; use crate::records::record_chain::pending_effects::PendingEffects;
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::records::unpack_block::load_by_block_number::load_block; use crate::records::unpack_block::load_by_block_number::load_block;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
@ -72,7 +73,7 @@ pub async fn process_rewards(
mut binary_data: Vec<u8>, mut binary_data: Vec<u8>,
_db: &Db, _db: &Db,
index_counter: Arc<Mutex<&mut usize>>, index_counter: Arc<Mutex<&mut usize>>,
_miner: String, miner: String,
block_header_number: u32, block_header_number: u32,
pending_effects: &mut PendingEffects, pending_effects: &mut PendingEffects,
) -> Result<Vec<u8>, String> { ) -> Result<Vec<u8>, String> {
@ -94,6 +95,13 @@ pub async fn process_rewards(
// block/index, but does not make the reward spendable yet. // block/index, but does not make the reward spendable yet.
let txkey = decode(txhash).map_err(|e| format!("Error decoding rewards txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding rewards txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey.clone(), txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&miner],
block_header_number,
**index as u32,
&txkey,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -3,6 +3,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -120,6 +121,17 @@ pub async fn process_swap(
let txkey = decode(txhash).map_err(|e| format!("Error decoding swap txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding swap txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[
&transaction.unsigned_swap.sender1,
&transaction.unsigned_swap.sender2,
&miner,
],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
pending_effects.append_tree_if_key_exists( pending_effects.append_tree_if_key_exists(
"nfts", "nfts",
"nft_history", "nft_history",

View File

@ -2,6 +2,7 @@ use crate::blocks::token::CreateTokenTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -77,6 +78,13 @@ pub async fn process_token(
// transaction back to its block and index position. // transaction back to its block and index position.
let txkey = txhash_bytes; let txkey = txhash_bytes;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey.clone(), txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[&transaction.unsigned_create_token.creator, &miner],
block_header_number,
**index as u32,
&txkey,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -3,6 +3,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -69,6 +70,17 @@ pub async fn process_transfer(
let txkey = decode(txhash).map_err(|e| format!("Error decoding transfer txid key: {e}"))?; let txkey = decode(txhash).map_err(|e| format!("Error decoding transfer txid key: {e}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey, txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[
&transaction.unsigned_transfer.sender,
&transaction.unsigned_transfer.receiver,
&miner,
],
block_header_number,
**index as u32,
&txhash_bytes,
)?;
pending_effects.append_tree_if_key_exists( pending_effects.append_tree_if_key_exists(
"nfts", "nfts",
"nft_history", "nft_history",

View File

@ -2,6 +2,7 @@ use crate::blocks::vanity::VanityAddressTransaction;
use crate::decode; use crate::decode;
use crate::records::memory::mempool::BASECOIN; use crate::records::memory::mempool::BASECOIN;
use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects}; use crate::records::record_chain::pending_effects::{BalanceOperand, PendingEffects};
use crate::records::record_chain::wallet_tx_index::index_wallet_transaction;
use crate::sled::Db; use crate::sled::Db;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
@ -55,7 +56,14 @@ pub async fn process_vanity(
let txkey = decode(&txhash) let txkey = decode(&txhash)
.map_err(|err| format!("Could not decode vanity transaction hash: {err}"))?; .map_err(|err| format!("Could not decode vanity transaction hash: {err}"))?;
let txvalue = format!("{block_header_number}:{}", **index); let txvalue = format!("{block_header_number}:{}", **index);
pending_effects.set_tree("txid", txkey, txvalue.into_bytes()); pending_effects.set_tree("txid", txkey.clone(), txvalue.into_bytes());
index_wallet_transaction(
pending_effects,
&[owner_address, &miner],
block_header_number,
**index as u32,
&txkey,
)?;
Ok(binary_data) Ok(binary_data)
} }

View File

@ -0,0 +1,111 @@
use crate::records::record_chain::pending_effects::PendingEffects;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
pub const WALLET_TX_INDEX_TREE: &str = "wallet_tx_index";
fn wallet_tx_index_key(
address: &str,
block_height: u32,
entry_index: u32,
) -> Result<Vec<u8>, String> {
let address_bytes = Wallet::short_address_to_bytes(address)
.ok_or_else(|| format!("Invalid wallet history address: {address}"))?;
let mut key = Vec::with_capacity(Wallet::SHORT_ADDRESS_BYTES_LENGTH + 8);
key.extend_from_slice(&address_bytes);
key.extend_from_slice(&block_height.to_be_bytes());
key.extend_from_slice(&entry_index.to_be_bytes());
Ok(key)
}
pub fn index_wallet_transaction(
pending_effects: &mut PendingEffects,
addresses: &[&str],
block_height: u32,
entry_index: u32,
txid: &[u8],
) -> Result<(), String> {
let mut indexed_addresses: Vec<Vec<u8>> = Vec::new();
for address in addresses {
let address_bytes = Wallet::short_address_to_bytes(address)
.ok_or_else(|| format!("Invalid wallet history address: {address}"))?;
if indexed_addresses
.iter()
.any(|stored| stored == &address_bytes)
{
continue;
}
indexed_addresses.push(address_bytes);
let key = wallet_tx_index_key(address, block_height, entry_index)?;
pending_effects.set_tree(WALLET_TX_INDEX_TREE, key, txid.to_vec());
}
Ok(())
}
fn txid_block_height(db: &Db, txid: &[u8]) -> Result<Option<u32>, String> {
let tree = db
.open_tree("txid")
.map_err(|err| format!("Failed to open txid tree: {err}"))?;
let Some(value) = tree
.get(txid)
.map_err(|err| format!("Failed to read txid tree: {err}"))?
else {
return Ok(None);
};
let value = String::from_utf8_lossy(&value);
let Some((block_height, _entry_index)) = value.split_once(':') else {
return Err("Invalid txid location value.".to_string());
};
block_height
.parse::<u32>()
.map(Some)
.map_err(|err| format!("Invalid txid block height: {err}"))
}
pub fn remove_wallet_transaction_index(
db: &Db,
addresses: &[&str],
txid: &[u8],
) -> Result<(), String> {
let Some(block_height) = txid_block_height(db, txid)? else {
return Ok(());
};
let tree = db
.open_tree(WALLET_TX_INDEX_TREE)
.map_err(|err| format!("Failed to open wallet transaction index: {err}"))?;
let mut indexed_addresses: Vec<Vec<u8>> = Vec::new();
for address in addresses {
let address_bytes = Wallet::short_address_to_bytes(address)
.ok_or_else(|| format!("Invalid wallet history address: {address}"))?;
if indexed_addresses
.iter()
.any(|stored| stored == &address_bytes)
{
continue;
}
indexed_addresses.push(address_bytes.clone());
let mut prefix = Vec::with_capacity(Wallet::SHORT_ADDRESS_BYTES_LENGTH + 4);
prefix.extend_from_slice(&address_bytes);
prefix.extend_from_slice(&block_height.to_be_bytes());
let keys = tree
.scan_prefix(prefix)
.filter_map(|entry| match entry {
Ok((key, value)) if value.as_ref() == txid => Some(key.to_vec()),
_ => None,
})
.collect::<Vec<_>>();
for key in keys {
tree.remove(key)
.map_err(|err| format!("Failed to remove wallet transaction index: {err}"))?;
}
}
Ok(())
}

View File

@ -36,9 +36,9 @@ use crate::rpc::responses::RpcResponse;
use crate::rpc::server::connection_memory_manager::remove_key_from_memory; use crate::rpc::server::connection_memory_manager::remove_key_from_memory;
use crate::rpc::server::rpc_command_loop::start_loop; use crate::rpc::server::rpc_command_loop::start_loop;
use crate::sled::Db; use crate::sled::Db;
use crate::sleep;
use crate::startup::network_broadcast::announce_self_to_network; use crate::startup::network_broadcast::announce_self_to_network;
use crate::startup::remote_height::request_remote_height; use crate::startup::remote_height::request_remote_height;
use crate::sleep;
use crate::timeout; use crate::timeout;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
use crate::Arc; use crate::Arc;

View File

@ -65,9 +65,9 @@ pub async fn register_connected_wallet(
let response_text = String::from_utf8_lossy(&response); let response_text = String::from_utf8_lossy(&response);
match response_text.trim() { match response_text.trim() {
"1" => match register_short_address(db, &short_address_bytes, &public_key_bytes) { "1" => match register_short_address(db, &short_address_bytes, &public_key_bytes) {
Ok(WalletRegistrationResult::Inserted | WalletRegistrationResult::AlreadyRegistered) => { Ok(
Ok(()) WalletRegistrationResult::Inserted | WalletRegistrationResult::AlreadyRegistered,
} ) => Ok(()),
Ok(WalletRegistrationResult::Conflict) => { Ok(WalletRegistrationResult::Conflict) => {
Err("Local wallet registry conflicts with startup wallet".to_string()) Err("Local wallet registry conflicts with startup wallet".to_string())
} }

View File

@ -54,7 +54,10 @@ pub const RPC_WALLET_REGISTRY_SYNC: u8 = 39;
pub const RPC_VANITY_LOOKUP: u8 = 40; pub const RPC_VANITY_LOOKUP: u8 = 40;
pub const RPC_TORRENT_CANDIDATES: u8 = 41; pub const RPC_TORRENT_CANDIDATES: u8 = 41;
pub const RPC_BLOCK_HASH_AT_HEIGHT: u8 = 42; pub const RPC_BLOCK_HASH_AT_HEIGHT: u8 = 42;
pub const RPC_WALLET_REGISTRATION_STATUS: u8 = 43;
pub const RPC_ADDRESS_HISTORY: u8 = 44;
pub const RPC_VANITY_OWNER_LOOKUP: u8 = 45; pub const RPC_VANITY_OWNER_LOOKUP: u8 = 45;
pub const RPC_LATEST_ADDRESS_HISTORY: u8 = 46;
pub const RPC_REPLY: u8 = 255; pub const RPC_REPLY: u8 = 255;
pub const MAX_RPC_REPLY_BYTES: usize = 64 * 1024 * 1024; pub const MAX_RPC_REPLY_BYTES: usize = 64 * 1024 * 1024;

View File

@ -48,8 +48,8 @@ pub async fn add_network_node(
read_bytes_from_stream::read_signature_from_stream(connections_key, stream_locked.clone()) read_bytes_from_stream::read_signature_from_stream(connections_key, stream_locked.clone())
.await?; .await?;
let monitor_count = let monitor_count =
read_bytes_from_stream::read_u16_from_stream(connections_key, stream_locked.clone()) read_bytes_from_stream::read_u16_from_stream(connections_key, stream_locked.clone()).await?
.await? as usize; as usize;
let mut monitors = Vec::with_capacity(monitor_count); let mut monitors = Vec::with_capacity(monitor_count);
for _ in 0..monitor_count { for _ in 0..monitor_count {
let monitor_bytes = read_bytes_from_stream::read_short_address_from_stream( let monitor_bytes = read_bytes_from_stream::read_short_address_from_stream(

View File

@ -0,0 +1,81 @@
use crate::records::record_chain::wallet_tx_index::WALLET_TX_INDEX_TREE;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
use std::ops::Bound;
const TXID_LENGTH: usize = 32;
const MAX_ADDRESS_HISTORY_TXIDS: usize = 10_000;
fn prefix_successor(prefix: &[u8]) -> Option<Vec<u8>> {
let mut next = prefix.to_vec();
for index in (0..next.len()).rev() {
if next[index] != u8::MAX {
next[index] += 1;
next.truncate(index + 1);
return Some(next);
}
}
None
}
pub async fn lookup(address_bytes: Vec<u8>, skip: u32, limit: u32, db: &Db) -> RpcResponse {
if address_bytes.len() != Wallet::SHORT_ADDRESS_BYTES_LENGTH {
return RpcResponse::Binary(b"error: Invalid wallet address bytes".to_vec());
}
let limit = (limit as usize).min(MAX_ADDRESS_HISTORY_TXIDS);
if limit == 0 {
return RpcResponse::Binary(Vec::new());
}
let tree = match db.open_tree(WALLET_TX_INDEX_TREE) {
Ok(tree) => tree,
Err(err) => {
return RpcResponse::Binary(
format!("error: Failed to open wallet transaction index: {err}").into_bytes(),
);
}
};
let start = Bound::Included(address_bytes.clone());
let end = prefix_successor(&address_bytes)
.map(Bound::Excluded)
.unwrap_or(Bound::Unbounded);
let mut skipped = 0usize;
let mut found = 0usize;
let mut txids = Vec::with_capacity(limit * TXID_LENGTH);
for entry in tree.range((start, end)).rev() {
let (_key, value) = match entry {
Ok(entry) => entry,
Err(err) => {
return RpcResponse::Binary(
format!("error: Failed to read wallet transaction index: {err}").into_bytes(),
);
}
};
if value.len() != TXID_LENGTH {
continue;
}
if skipped < skip as usize {
skipped += 1;
continue;
}
txids.extend_from_slice(&value);
found += 1;
if found >= limit {
break;
}
}
RpcResponse::Binary(txids)
}
pub async fn latest(address_bytes: Vec<u8>, limit: u32, db: &Db) -> RpcResponse {
lookup(address_bytes, 0, limit, db).await
}

View File

@ -2,6 +2,7 @@
pub mod add_network_node; pub mod add_network_node;
pub mod address_coin_lookup; pub mod address_coin_lookup;
pub mod address_complete_balance_sheet; pub mod address_complete_balance_sheet;
pub mod address_history;
pub mod bad_rpc_call; pub mod bad_rpc_call;
pub mod block_by_hash; pub mod block_by_hash;
pub mod block_by_height; pub mod block_by_height;
@ -41,6 +42,7 @@ pub mod validate_address;
pub mod validate_message; pub mod validate_message;
pub mod validate_torrent; pub mod validate_torrent;
pub mod wallet_register; pub mod wallet_register;
pub mod wallet_registration_status;
pub mod wallet_registry_sync; pub mod wallet_registry_sync;
pub mod wallet_vanity_lookup; pub mod wallet_vanity_lookup;
pub mod wallet_vanity_owner_lookup; pub mod wallet_vanity_owner_lookup;

View File

@ -1,6 +1,6 @@
use crate::records::block_height::get_block_height::get_height;
use crate::records::memory::connections::CONNECTIONS; use crate::records::memory::connections::CONNECTIONS;
use crate::records::memory::network_mapping::NodeInfo; use crate::records::memory::network_mapping::NodeInfo;
use crate::records::block_height::get_block_height::get_height;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;

View File

@ -15,9 +15,7 @@ use crate::common::types::{
}; };
use crate::records::memory::connections::get_client_type_from_memory; use crate::records::memory::connections::get_client_type_from_memory;
use crate::records::memory::enums::ClientType; use crate::records::memory::enums::ClientType;
use crate::records::memory::response_channels::{ use crate::records::memory::response_channels::{reserve_transient_entry_with_context, Command};
reserve_transient_entry_with_context, Command,
};
use crate::rpc::command_maps::RPC_SUBMIT_TRANSACTION; use crate::rpc::command_maps::RPC_SUBMIT_TRANSACTION;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;

View File

@ -0,0 +1,18 @@
use crate::records::wallet_registry::short_address_exists;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
pub async fn lookup(short_address_bytes: Vec<u8>, db: &Db) -> RpcResponse {
if short_address_bytes.len() != Wallet::SHORT_ADDRESS_BYTES_LENGTH {
return RpcResponse::Binary(b"0".to_vec());
}
match short_address_exists(db, &short_address_bytes) {
Ok(true) => RpcResponse::Binary(b"1".to_vec()),
Ok(false) => RpcResponse::Binary(b"0".to_vec()),
Err(err) => RpcResponse::Binary(
format!("error: Failed to lookup wallet registration status: {err}").into_bytes(),
),
}
}

View File

@ -817,6 +817,53 @@ pub async fn start_loop(
.send(&stream_locked, Some(&connections_key), uid) .send(&stream_locked, Some(&connections_key), uid)
.await; .await;
} }
43 => {
// request whether a canonical short wallet address is registered
let (uid, _) = read_bytes_from_stream::read_uid_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let short_address = read_bytes_from_stream::read_short_address_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let result = commands::wallet_registration_status::lookup(short_address, &db).await;
result
.send(&stream_locked, Some(&connections_key), uid)
.await;
}
44 => {
// request confirmed transaction ids indexed to a wallet address
let (uid, _) = read_bytes_from_stream::read_uid_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let short_address = read_bytes_from_stream::read_short_address_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let skip = read_bytes_from_stream::read_u32_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let limit = read_bytes_from_stream::read_u32_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let result =
commands::address_history::lookup(short_address, skip, limit, &db).await;
result
.send(&stream_locked, Some(&connections_key), uid)
.await;
}
45 => { 45 => {
// request the canonical short address that owns a registered vanity address // request the canonical short address that owns a registered vanity address
let (uid, _) = read_bytes_from_stream::read_uid_from_stream( let (uid, _) = read_bytes_from_stream::read_uid_from_stream(
@ -850,6 +897,29 @@ pub async fn start_loop(
.send(&stream_locked, Some(&connections_key), uid) .send(&stream_locked, Some(&connections_key), uid)
.await; .await;
} }
46 => {
// request the latest confirmed transaction ids for a wallet address
let (uid, _) = read_bytes_from_stream::read_uid_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let short_address = read_bytes_from_stream::read_short_address_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let limit = read_bytes_from_stream::read_u32_from_stream(
&connections_key,
stream_locked.clone(),
)
.await?;
let result = commands::address_history::latest(short_address, limit, &db).await;
result
.send(&stream_locked, Some(&connections_key), uid)
.await;
}
255 => { 255 => {
commands::route_reply::route_reply( commands::route_reply::route_reply(
&connections_key, &connections_key,

View File

@ -3,12 +3,13 @@ use crate::decode;
use crate::io; use crate::io;
use crate::records::memory::response_channels::Byte3; use crate::records::memory::response_channels::Byte3;
use crate::rpc::command_maps::{ use crate::rpc::command_maps::{
MAX_RPC_REPLY_BYTES, RPC_ADDRESS_TOTAL_BALANCE, RPC_BLOCK_BY_HASH, RPC_BLOCK_BY_HEIGHT, MAX_RPC_REPLY_BYTES, RPC_ADDRESS_HISTORY, RPC_ADDRESS_TOTAL_BALANCE, RPC_BLOCK_BY_HASH,
RPC_BLOCK_HEIGHT, RPC_BLOCK_IP, RPC_CONTRACT_BY_ADDRESS, RPC_DIFFICULTY, RPC_LARGEST_TX_FEE, RPC_BLOCK_BY_HEIGHT, RPC_BLOCK_HEIGHT, RPC_BLOCK_IP, RPC_CONTRACT_BY_ADDRESS, RPC_DIFFICULTY,
RPC_LOAN_CONTRACT, RPC_MEMPOOL_TX_BY_ADDRESS, RPC_MEMPOOL_TX_BY_SIGNATURE, RPC_LARGEST_TX_FEE, RPC_LATEST_ADDRESS_HISTORY, RPC_LOAN_CONTRACT, RPC_MEMPOOL_TX_BY_ADDRESS,
RPC_MEMPOOL_TX_COUNT, RPC_NETWORK_INFO, RPC_NFT_DETAILS, RPC_NFT_LIST, RPC_REGISTER_WALLET, RPC_MEMPOOL_TX_BY_SIGNATURE, RPC_MEMPOOL_TX_COUNT, RPC_NETWORK_INFO, RPC_NFT_DETAILS,
RPC_TIME, RPC_TOKEN_DETAILS, RPC_TOKEN_LIST, RPC_TORRENT_BY_HEIGHT, RPC_TOTAL_CONFIRMED_TX, RPC_NFT_LIST, RPC_REGISTER_WALLET, RPC_TIME, RPC_TOKEN_DETAILS, RPC_TOKEN_LIST,
RPC_TRANSACTION_BY_TXID, RPC_UNBLOCK_IP, RPC_VANITY_LOOKUP, RPC_VANITY_OWNER_LOOKUP, RPC_TORRENT_BY_HEIGHT, RPC_TOTAL_CONFIRMED_TX, RPC_TRANSACTION_BY_TXID, RPC_UNBLOCK_IP,
RPC_VANITY_LOOKUP, RPC_VANITY_OWNER_LOOKUP, RPC_WALLET_REGISTRATION_STATUS,
}; };
use crate::standalone_tools::transaction_creator::create_transaction_request; use crate::standalone_tools::transaction_creator::create_transaction_request;
use crate::timeout; use crate::timeout;
@ -461,6 +462,51 @@ async fn build_request_bytes(
bin_msg.extend_from_slice(&address_bytes); bin_msg.extend_from_slice(&address_bytes);
} }
// Lookup canonical short owner address by registered vanity address. // Lookup canonical short owner address by registered vanity address.
43 => {
let command_number: u8 = RPC_WALLET_REGISTRATION_STATUS;
let address_bytes = Wallet::normalize_to_short_address(&command_input)
.and_then(|short| Wallet::short_address_to_bytes(&short))
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Invalid wallet address")
})?;
bin_msg.extend_from_slice(&command_number.to_le_bytes());
bin_msg.extend_from_slice(&hashmap_key);
bin_msg.extend_from_slice(&address_bytes);
}
// Lookup canonical short owner address by registered vanity address.
44 => {
let command_number: u8 = RPC_ADDRESS_HISTORY;
let (address, rest) = command_input.split_once('|').ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Address history input must be address|skip|limit",
)
})?;
let (skip, limit) = rest.split_once('|').ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Address history input must be address|skip|limit",
)
})?;
let address_bytes = Wallet::normalize_to_short_address(address)
.and_then(|short| Wallet::short_address_to_bytes(&short))
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Invalid wallet address")
})?;
let skip = skip
.parse::<u32>()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Invalid history skip"))?;
let limit = limit.parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "Invalid history limit")
})?;
bin_msg.extend_from_slice(&command_number.to_le_bytes());
bin_msg.extend_from_slice(&hashmap_key);
bin_msg.extend_from_slice(&address_bytes);
bin_msg.extend_from_slice(&skip.to_le_bytes());
bin_msg.extend_from_slice(&limit.to_le_bytes());
}
// Lookup canonical short owner address by registered vanity address.
45 => { 45 => {
let command_number: u8 = RPC_VANITY_OWNER_LOOKUP; let command_number: u8 = RPC_VANITY_OWNER_LOOKUP;
let address_bytes = Wallet::normalize_to_short_address(&command_input) let address_bytes = Wallet::normalize_to_short_address(&command_input)
@ -472,6 +518,29 @@ async fn build_request_bytes(
bin_msg.extend_from_slice(&hashmap_key); bin_msg.extend_from_slice(&hashmap_key);
bin_msg.extend_from_slice(&address_bytes); bin_msg.extend_from_slice(&address_bytes);
} }
// Lookup newest confirmed transaction ids tied to an address.
46 => {
let command_number: u8 = RPC_LATEST_ADDRESS_HISTORY;
let (address, limit) = command_input.split_once('|').ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Latest address history input must be address|limit",
)
})?;
let address_bytes = Wallet::normalize_to_short_address(address)
.and_then(|short| Wallet::short_address_to_bytes(&short))
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Invalid wallet address")
})?;
let limit = limit.parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "Invalid history limit")
})?;
bin_msg.extend_from_slice(&command_number.to_le_bytes());
bin_msg.extend_from_slice(&hashmap_key);
bin_msg.extend_from_slice(&address_bytes);
bin_msg.extend_from_slice(&limit.to_le_bytes());
}
_ => { _ => {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,

View File

@ -1,11 +1,10 @@
use crate::common::binary_conversions::{binary_to_ip, binary_to_string, ip_to_binary}; use crate::common::binary_conversions::{binary_to_ip, binary_to_string, ip_to_binary};
use crate::log::warn;
use crate::common::network_startup::get_ip_and_port; use crate::common::network_startup::get_ip_and_port;
use crate::log::warn;
use crate::records::memory::network_mapping::structs::{ use crate::records::memory::network_mapping::structs::{
SignedNodeEdit, NODE_ADDED_BY_OFFSET, NODE_ADDED_SIGNATURE_OFFSET, SignedNodeEdit, NODE_ADDED_BY_OFFSET, NODE_ADDED_SIGNATURE_OFFSET, NODE_ADDED_TIMESTAMP_OFFSET,
NODE_ADDED_TIMESTAMP_OFFSET, NODE_BLOCKS_MINED_OFFSET, NODE_DELETED_BLOCK_OFFSET, NODE_BLOCKS_MINED_OFFSET, NODE_DELETED_BLOCK_OFFSET, NODE_DELETED_TIMESTAMP_OFFSET,
NODE_DELETED_TIMESTAMP_OFFSET, NODE_IP_OFFSET, NODE_MONITOR_COUNT_OFFSET, NODE_IP_OFFSET, NODE_MONITOR_COUNT_OFFSET, NODE_RECORD_FIXED_BYTES,
NODE_RECORD_FIXED_BYTES,
}; };
use crate::records::memory::network_mapping::NodeInfo; use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::response_channels::{reserve_entry, Command}; use crate::records::memory::response_channels::{reserve_entry, Command};

View File

@ -13,9 +13,12 @@ pub async fn request_remote_height(
) -> Result<u32, String> { ) -> Result<u32, String> {
// request the remote node's current chain height using // request the remote node's current chain height using
// the standard reply-channel request/response flow // the standard reply-channel request/response flow
let (hashmap_key, _tx, rx) = let (hashmap_key, _tx, rx) = reserve_entry_with_context(
reserve_entry_with_context(map.clone(), Some(RPC_BLOCK_HEIGHT), Some(connections_key.clone())) map.clone(),
.await; Some(RPC_BLOCK_HEIGHT),
Some(connections_key.clone()),
)
.await;
// message format is the height command plus the unique // message format is the height command plus the unique
// reply key used to route the response back here // reply key used to route the response back here