Skip to content

Commit

Permalink
Refactor utxo return address result
Browse files Browse the repository at this point in the history
  • Loading branch information
coderofstuff committed Mar 25, 2024
1 parent 56a4fcb commit 393f432
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 48 deletions.
12 changes: 4 additions & 8 deletions components/consensusmanager/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

use kaspa_consensus_core::{
acceptance_data::AcceptanceData,
api::{BlockCount, BlockValidationFutures, ConsensusApi, ConsensusStats, DynConsensus},
api::{BlockCount, BlockValidationFutures, ConsensusApi, ConsensusStats, DynConsensus, ReturnAddress},
block::Block,
blockstatus::BlockStatus,
daa_score_timestamp::DaaScoreTimestamp,
errors::consensus::ConsensusResult,
header::Header,
pruning::{PruningPointProof, PruningPointTrustedData, PruningPointsList},
trusted::{ExternalGhostdagData, TrustedBlock},
tx::{MutableTransaction, ScriptPublicKey, Transaction, TransactionOutpoint, UtxoEntry},
tx::{MutableTransaction, Transaction, TransactionOutpoint, UtxoEntry},
BlockHashSet, BlueWorkType, ChainPath, Hash,
};
use kaspa_utils::sync::rwlock::*;
Expand Down Expand Up @@ -291,12 +291,8 @@ impl ConsensusSessionOwned {
self.clone().spawn_blocking(|c| c.get_chain_block_samples()).await
}

pub async fn async_get_utxo_return_script_public_key(
&self,
txid: Hash,
accepting_block_daa_score: u64,
) -> Option<ScriptPublicKey> {
self.clone().spawn_blocking(move |c| c.get_utxo_return_script_public_key(txid, accepting_block_daa_score)).await
pub async fn async_get_utxo_return_script_public_key(&self, txid: Hash, accepting_block_daa_score: u64) -> ReturnAddress {
self.clone().spawn_blocking(move |c| c.get_utxo_return_address(txid, accepting_block_daa_score)).await
}

/// Returns the antipast of block `hash` from the POV of `context`, i.e. `antipast(hash) ∩ past(context)`.
Expand Down
35 changes: 32 additions & 3 deletions consensus/core/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use futures_util::future::BoxFuture;
use kaspa_addresses::Address;
use kaspa_muhash::MuHash;
use std::sync::Arc;
use std::{
fmt::{Display, Formatter},
sync::Arc,
};

use crate::{
acceptance_data::AcceptanceData,
Expand All @@ -18,7 +22,7 @@ use crate::{
header::Header,
pruning::{PruningPointProof, PruningPointTrustedData, PruningPointsList},
trusted::{ExternalGhostdagData, TrustedBlock},
tx::{MutableTransaction, ScriptPublicKey, Transaction, TransactionOutpoint, UtxoEntry},
tx::{MutableTransaction, Transaction, TransactionOutpoint, UtxoEntry},
BlockHashSet, BlueWorkType, ChainPath,
};
use kaspa_hashes::Hash;
Expand All @@ -41,6 +45,31 @@ pub struct BlockValidationFutures {
pub virtual_state_task: BlockValidationFuture,
}

#[derive(Debug, Clone)]
pub enum ReturnAddress {
Found(Address),
AlreadyPruned,
TxFromCoinbase,
NoTxAtScore,
NonStandard,
NotFound(String),
}

impl Display for ReturnAddress {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = match self {
ReturnAddress::AlreadyPruned => "Transaction is already pruned".to_string(),
ReturnAddress::NoTxAtScore => "Transaction not found at given accepting daa score".to_string(),
ReturnAddress::NonStandard => "Transaction was found but not standard".to_string(),
ReturnAddress::TxFromCoinbase => "Transaction return address is coinbase".to_string(),
ReturnAddress::NotFound(reason) => format!("Transaction return address not found: {}", reason),
ReturnAddress::Found(address) => address.to_string(),
};
f.write_str(&s)
}
}

/// Abstracts the consensus external API
#[allow(unused_variables)]
pub trait ConsensusApi: Send + Sync {
Expand Down Expand Up @@ -155,7 +184,7 @@ pub trait ConsensusApi: Send + Sync {
unimplemented!()
}

fn get_utxo_return_script_public_key(&self, txid: Hash, daa_score: u64) -> Option<ScriptPublicKey> {
fn get_utxo_return_address(&self, txid: Hash, daa_score: u64) -> ReturnAddress {
unimplemented!()
}

Expand Down
81 changes: 54 additions & 27 deletions consensus/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::{
};
use kaspa_consensus_core::{
acceptance_data::AcceptanceData,
api::{stats::BlockCount, BlockValidationFutures, ConsensusApi, ConsensusStats},
api::{stats::BlockCount, BlockValidationFutures, ConsensusApi, ConsensusStats, ReturnAddress},
block::{Block, BlockTemplate, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId},
blockhash::BlockHashExtensions,
blockstatus::BlockStatus,
Expand All @@ -58,7 +58,7 @@ use kaspa_consensus_core::{
network::NetworkType,
pruning::{PruningPointProof, PruningPointTrustedData, PruningPointsList},
trusted::{ExternalGhostdagData, TrustedBlock},
tx::{MutableTransaction, ScriptPublicKey, Transaction, TransactionOutpoint, UtxoEntry},
tx::{MutableTransaction, Transaction, TransactionOutpoint, UtxoEntry},
BlockHashSet, BlueWorkType, ChainPath,
};
use kaspa_consensus_notify::root::ConsensusNotificationRoot;
Expand All @@ -73,7 +73,7 @@ use kaspa_core::{trace, warn};
use kaspa_database::prelude::StoreResultExtensions;
use kaspa_hashes::Hash;
use kaspa_muhash::MuHash;
use kaspa_txscript::caches::TxScriptCacheCounters;
use kaspa_txscript::{caches::TxScriptCacheCounters, extract_script_pub_key_address};

use std::{
future::Future,
Expand Down Expand Up @@ -615,13 +615,20 @@ impl ConsensusApi for Consensus {
sample_headers
}

fn get_utxo_return_script_public_key(&self, txid: Hash, target_daa_score: u64) -> Option<ScriptPublicKey> {
fn get_utxo_return_address(&self, txid: Hash, target_daa_score: u64) -> ReturnAddress {
// We need consistency between the past pruning points, selected chain and header store reads
let _guard = self.pruning_lock.blocking_read();

let sc_read = self.selected_chain_store.read();

let pp_hash = self.pruning_point_store.read().get().unwrap().pruning_point;

// Pruning Point hash is always expected to be in get_compact_header_data so unwrap should never fail
if target_daa_score < self.headers_store.get_compact_header_data(pp_hash).unwrap().daa_score {
// Early exit if target daa score is lower than that of pruning point's daa score:
return ReturnAddress::AlreadyPruned;
}

let pp_index = sc_read.get_by_hash(pp_hash).unwrap();
let (tip_index, tip_hash) = sc_read.get_tip().unwrap();
let tip_daa_score = self.headers_store.get_compact_header_data(tip_hash).unwrap().daa_score;
Expand All @@ -635,29 +642,28 @@ impl ConsensusApi for Consensus {
let mid = low_index + (high_index - low_index) / 2;

// 1. Get the chain block hash at that index. Error if we don't find a hash at an index
let hash = sc_read
.get_by_index(mid)
.map_err(|err| {
let hash = match sc_read.get_by_index(mid) {
Ok(hash) => hash,
Err(_) => {
trace!("Did not find a hash at index {}", mid);
err
})
.ok()?;
return ReturnAddress::NotFound(format!("Did not find a hash at index {}", mid));
}
};

// 2. Get the compact header so we have access to the daa_score. Error if we
let compact_header = self
.headers_store
.get_compact_header_data(hash)
.map_err(|err| {
let compact_header = match self.headers_store.get_compact_header_data(hash) {
Ok(compact_header) => compact_header,
Err(_) => {
trace!("Did not find a compact header with hash {}", hash);
err
})
.ok()?;
return ReturnAddress::NotFound(format!("Did not find a compact header with hash {}", hash));
}
};

// 3. Compare block daa score to our target
match compact_header.daa_score.cmp(&target_daa_score) {
cmp::Ordering::Equal => {
// We found the chain block we need
break Some(hash);
break hash;
}
cmp::Ordering::Greater => {
high_index = mid - 1;
Expand All @@ -668,38 +674,59 @@ impl ConsensusApi for Consensus {
}

if low_index > high_index {
break None;
return ReturnAddress::NoTxAtScore;
}
}?;
};

let acceptance_data = self.acceptance_data_store.get(matching_chain_block_hash).ok()?;
let (index, containing_acceptance) = acceptance_data.iter().find_map(|mbad| {
let acceptance_data = match self.acceptance_data_store.get(matching_chain_block_hash) {
Ok(acceptance_data) => acceptance_data,
Err(_) => {
return ReturnAddress::NotFound("Did not find acceptance data".to_string());
}
};
let (index, containing_acceptance) = match acceptance_data.iter().find_map(|mbad| {
let tx_arr_index =
mbad.accepted_transactions.iter().enumerate().find_map(|(index, tx)| (tx.transaction_id == txid).then_some(index));
tx_arr_index.map(|index| (index, mbad.clone()))
})?;
}) {
Some((index, containing_acceptance)) => (index, containing_acceptance),
None => {
return ReturnAddress::NotFound("Did not find containing_acceptance".to_string());
}
};

// Found Merged block containing the TXID
let tx = &self.block_transactions_store.get(containing_acceptance.block_hash).unwrap()[index];

if tx.id() != txid {
// Should never happen, but do a sanity check. This would mean something went wrong with storing block transactions
// Sanity check is necessary to guarantee that this function will never give back a wrong address (err on the side of None)
// Sanity check is necessary to guarantee that this function will never give back a wrong address (err on the side of NotFound)
warn!("Expected {} to match {} when checking block_transaction_store using array index of transaction", tx.id(), txid);
return None;
return ReturnAddress::NotFound(format!(
"Expected {} to match {} when checking block_transaction_store using array index of transaction",
tx.id(),
txid
));
}

if tx.inputs.is_empty() {
// A transaction may have no inputs (like a coinbase transaction)
return None;
return ReturnAddress::TxFromCoinbase;
}

let first_input_prev_outpoint = &tx.inputs[0].previous_outpoint;
// Expected to never fail, since we found the acceptance data and therefore there must be matching diff
let utxo_diff = self.utxo_diffs_store.get(matching_chain_block_hash).unwrap();
let removed_diffs = utxo_diff.removed();

Some(removed_diffs.get(first_input_prev_outpoint)?.script_public_key.clone())
if let Ok(address) = extract_script_pub_key_address(
&removed_diffs.get(first_input_prev_outpoint).unwrap().script_public_key,
self.config.prefix(),
) {
ReturnAddress::Found(address)
} else {
ReturnAddress::NonStandard
}
}

fn get_virtual_parents(&self) -> BlockHashSet {
Expand Down
5 changes: 4 additions & 1 deletion rpc/core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use kaspa_consensus_core::tx::TransactionId;
use kaspa_consensus_core::{api::ReturnAddress, tx::TransactionId};
use kaspa_utils::networking::IpAddress;
use std::{net::AddrParseError, num::TryFromIntError};
use thiserror::Error;
Expand Down Expand Up @@ -115,6 +115,9 @@ pub enum RpcError {

#[error("transaction query must either not filter transactions or include orphans")]
InconsistentMempoolTxQuery,

#[error("utxo return address could not be found -> {0}")]
UtxoReturnAddressNotFound(ReturnAddress),
}

impl From<String> for RpcError {
Expand Down
4 changes: 2 additions & 2 deletions rpc/grpc/core/proto/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ message KaspadRequest {
GetServerInfoRequestMessage getServerInfoRequest = 1092;
GetSyncStatusRequestMessage getSyncStatusRequest = 1094;
GetDaaScoreTimestampEstimateRequestMessage getDaaScoreTimestampEstimateRequest = 1096;
GetUtxoReturnAddressRequestMessage GetUtxoReturnAddressRequest = 1098;
GetUtxoReturnAddressRequestMessage getUtxoReturnAddressRequest = 1098;
}
}

Expand Down Expand Up @@ -119,7 +119,7 @@ message KaspadResponse {
GetServerInfoResponseMessage getServerInfoResponse = 1093;
GetSyncStatusResponseMessage getSyncStatusResponse = 1095;
GetDaaScoreTimestampEstimateResponseMessage getDaaScoreTimestampEstimateResponse = 1097;
GetUtxoReturnAddressResponseMessage GetUtxoReturnAddressResponse = 1099;
GetUtxoReturnAddressResponseMessage getUtxoReturnAddressResponse = 1099;
}
}

Expand Down
13 changes: 6 additions & 7 deletions rpc/service/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::converter::{consensus::ConsensusConverter, index::IndexConverter, pro
use crate::service::NetworkType::{Mainnet, Testnet};
use async_trait::async_trait;
use kaspa_consensus_core::api::counters::ProcessingCounters;
use kaspa_consensus_core::api::ReturnAddress;
use kaspa_consensus_core::errors::block::RuleError;
use kaspa_consensus_core::{
block::Block,
Expand Down Expand Up @@ -624,13 +625,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and
let session = self.consensus_manager.consensus().session().await;

// Convert a SPK to an Address
let return_address = if let Some(spk) =
session.async_get_utxo_return_script_public_key(request.txid, request.accepting_block_daa_score).await
{
extract_script_pub_key_address(&spk, self.config.prefix()).ok()
} else {
None
};
let return_address =
match session.async_get_utxo_return_script_public_key(request.txid, request.accepting_block_daa_score).await {
ReturnAddress::Found(address) => Some(address),
other => return Err(RpcError::UtxoReturnAddressNotFound(other)),
};

Ok(GetUtxoReturnAddressResponse { return_address })
}
Expand Down

0 comments on commit 393f432

Please sign in to comment.