Skip to content

Commit

Permalink
expose fees
Browse files Browse the repository at this point in the history
  • Loading branch information
biryukovmaxim committed Jan 10, 2025
1 parent da12615 commit e3d92ff
Show file tree
Hide file tree
Showing 13 changed files with 255 additions and 43 deletions.
7 changes: 7 additions & 0 deletions components/consensusmanager/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,13 @@ impl ConsensusSessionOwned {
) -> Result<SignableTransaction, UtxoInquirerError> {
self.clone().spawn_blocking(move |c| c.get_populated_transaction(txid, accepting_block_daa_score)).await
}
pub async fn async_get_utxo_amounts(
&self,
accepting_block_hash: Hash,
outpoints: Arc<Vec<TransactionOutpoint>>,
) -> Result<Vec<u64>, UtxoInquirerError> {
self.clone().spawn_blocking(move |c| c.get_utxo_amounts(accepting_block_hash, outpoints)).await
}

/// Returns the antipast of block `hash` from the POV of `context`, i.e. `antipast(hash) ∩ past(context)`.
/// Since this might be an expensive operation for deep blocks, we allow the caller to specify a limit
Expand Down
8 changes: 7 additions & 1 deletion consensus/core/src/acceptance_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ pub struct MergesetBlockAcceptanceData {
pub struct MergesetBlockAcceptanceDataWithTx {
pub block_hash: Hash,
pub block_timestamp: u64,
pub accepted_transactions: Vec<Transaction>,
pub accepted_transactions: Vec<TransactionWithFee>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionWithFee {
pub tx: Transaction,
pub fee: u64
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
8 changes: 8 additions & 0 deletions consensus/core/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ pub trait ConsensusApi: Send + Sync {
unimplemented!()
}

fn get_utxo_amounts(
&self,
accepting_block_hash: Hash,
outpoints: Arc<Vec<TransactionOutpoint>>,
) -> Result<Vec<u64>, UtxoInquirerError> {
unimplemented!()
}

fn get_virtual_parents(&self) -> BlockHashSet {
unimplemented!()
}
Expand Down
4 changes: 3 additions & 1 deletion consensus/core/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ impl MemSizeEstimator for UtxoEntry {}
pub type TransactionIndexType = u32;

/// Represents a Kaspa transaction outpoint
#[derive(Eq, Default, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[derive(
Eq, Default, Hash, PartialEq, Debug, Copy, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize, PartialOrd, Ord,
)]
#[serde(rename_all = "camelCase")]
pub struct TransactionOutpoint {
#[serde(with = "serde_bytes_fixed_ref")]
Expand Down
10 changes: 10 additions & 0 deletions consensus/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,16 @@ impl ConsensusApi for Consensus {
sample_headers
}

fn get_utxo_amounts(
&self,
accepting_block_hash: Hash,
outpoints: Arc<Vec<TransactionOutpoint>>,
) -> Result<Vec<u64>, UtxoInquirerError> {
// We need consistency between the pruning_point_store, utxo_diffs_store, block_transactions_store, selected chain and headers store reads
let _guard = self.pruning_lock.blocking_read();
self.virtual_processor.get_utxo_amounts(accepting_block_hash, outpoints)
}

fn get_populated_transaction(&self, txid: Hash, accepting_block_daa_score: u64) -> Result<SignableTransaction, UtxoInquirerError> {
// We need consistency between the pruning_point_store, utxo_diffs_store, block_transactions_store, selected chain and headers store reads
let _guard = self.pruning_lock.blocking_read();
Expand Down
141 changes: 109 additions & 32 deletions consensus/src/pipeline/virtual_processor/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ use once_cell::unsync::Lazy;
use super::errors::{PruningImportError, PruningImportResult};
use crossbeam_channel::{Receiver as CrossbeamReceiver, Sender as CrossbeamSender};
use itertools::Itertools;
use kaspa_consensus_core::acceptance_data::MergesetBlockAcceptanceDataWithTx;
use kaspa_consensus_core::tx::ValidatedTransaction;
use kaspa_consensus_core::acceptance_data::{MergesetBlockAcceptanceDataWithTx, TransactionWithFee};
use kaspa_consensus_core::tx::{TransactionId, TransactionOutpoint, ValidatedTransaction};
use kaspa_utils::binary_heap::BinaryHeapExtensions;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use rand::{seq::SliceRandom, Rng};
Expand All @@ -98,6 +98,7 @@ use std::{
ops::Deref,
sync::{atomic::Ordering, Arc},
};
use std::collections::BTreeMap;

pub struct VirtualStateProcessor {
// Channels
Expand Down Expand Up @@ -349,40 +350,116 @@ impl VirtualStateProcessor {
.expect("expecting an open unbounded channel");
if self.notification_root.has_subscription(EventType::VirtualChainChanged) {
// check for subscriptions before the heavy lifting
let added_chain_blocks_acceptance_data =
chain_path.added.iter().copied().map(|added| self.acceptance_data_store.get(added).unwrap()).collect_vec();

let added_chain_blocks_acceptance_data: Vec<(Hash, Arc<AcceptanceData>)> =
chain_path.added.iter().copied().map(|added| self.acceptance_data_store.get(added).map(|acceptance_data| (added, acceptance_data)).unwrap()).collect_vec();
let added_chain_blocks_acceptance_data = added_chain_blocks_acceptance_data
.into_iter()
.map(|data| {
Arc::new(
data.iter()
.map(|mergeset| {
let accepted_transactions = self
.block_transactions_store
.get(mergeset.block_hash)
.unwrap()
.iter()
.cloned()
.enumerate()
.filter(|(index, _)| {
mergeset
.accepted_transactions
.map(|(accepting_block_hash, mergeset_data)| {
// Create maps to track values for fee calculation
let mut outpoint_to_value = BTreeMap::new();
let mut tx_id_input_to_outpoint: BTreeMap<(TransactionId, u32), (TransactionOutpoint, Option<u64>)> = BTreeMap::new();

let acceptance_data = mergeset_data.iter()
.map(|mined_block| {
let block_transactions = self
.block_transactions_store
.get(mined_block.block_hash)
.unwrap();

let accepted_transactions = block_transactions
.iter()
.cloned()
.enumerate()
.filter(|(index, _)| {
mined_block
.accepted_transactions
.iter()
.any(|accepted| accepted.index_within_block as usize == *index)
})
.map(|(_, tx)| {
// Collect input outpoints for later value lookup
tx.inputs.iter().enumerate().for_each(|(index, input)| {
tx_id_input_to_outpoint.insert(
(tx.id(), index as u32),
(input.previous_outpoint, None)
);
});

// Store output values
tx.outputs.iter().enumerate().for_each(|(index, out)| {
outpoint_to_value.insert(
TransactionOutpoint {
transaction_id: tx.id(),
index: index as u32
},
out.value
);
});

tx
})
.collect_vec();

let block_timestamp = self
.headers_store
.get_compact_header_data(mined_block.block_hash)
.unwrap()
.timestamp;

MergesetBlockAcceptanceDataWithTx {
block_hash: mined_block.block_hash,
block_timestamp,
accepted_transactions: accepted_transactions
.into_iter()
.map(|tx| {
// Calculate fee
let mut outpoints_requested_from_utxo = Vec::new();
let input_outpoints: Vec<_> = tx.inputs
.iter()
.map(|input| input.previous_outpoint)
.collect();

// Collect outpoints that need values from UTXO diff
let missing_outpoints: Vec<_> = input_outpoints
.iter()
.filter(|outpoint| !outpoint_to_value.contains_key(outpoint))
.cloned()
.collect();

if !missing_outpoints.is_empty() {
outpoints_requested_from_utxo.extend(missing_outpoints.clone());
let values = self
.get_utxo_amounts(accepting_block_hash, Arc::new(missing_outpoints))
.unwrap_or_else(|e| {
log::error!("Failed to get UTXO values: {}", e);
vec![0; outpoints_requested_from_utxo.len()]
});

// Store retrieved values
outpoints_requested_from_utxo
.iter()
.zip(values)
.for_each(|(outpoint, value)| {
outpoint_to_value.insert(*outpoint, value);
});
}

// Calculate fee as input_sum - output_sum
let input_sum: u64 = input_outpoints
.iter()
.any(|accepted| accepted.index_within_block as usize == *index)
.map(|outpoint| outpoint_to_value.get(outpoint).cloned().unwrap_or_default())
.sum();
let output_sum: u64 = tx.outputs.iter().map(|o| o.value).sum();
let fee = input_sum.saturating_sub(output_sum);

TransactionWithFee { tx, fee }
})
.map(|(_, tx)| tx)
.collect_vec();
let block_timestamp =
self.headers_store.get_compact_header_data(mergeset.block_hash).unwrap().timestamp;
MergesetBlockAcceptanceDataWithTx {
block_hash: mergeset.block_hash,
block_timestamp,
accepted_transactions,
}
})
.collect_vec(),
)
.collect(),
}
})
.collect_vec();

Arc::new(acceptance_data)
})
.collect();
let added_chain_block_blue_scores =
Expand Down
34 changes: 34 additions & 0 deletions consensus/src/pipeline/virtual_processor/utxo_inquirer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use kaspa_consensus_core::{
tx::{SignableTransaction, Transaction, UtxoEntry},
utxo::{utxo_diff::ImmutableUtxoDiff, utxo_inquirer::UtxoInquirerError},
};
use kaspa_consensus_core::tx::TransactionOutpoint;
use kaspa_core::{trace, warn};
use kaspa_hashes::Hash;

Expand All @@ -15,7 +16,40 @@ use crate::model::stores::{

use super::VirtualStateProcessor;

// todo get populated transactions by accepting block hash and by previous outpoints

impl VirtualStateProcessor {

pub fn get_utxo_amounts(
&self,
accepting_block_hash: Hash,
outpoints: Arc<Vec<TransactionOutpoint>>,
) -> Result<Vec<u64>, UtxoInquirerError> {
// Get the UTXO diff for the accepting block
let utxo_diff = self
.utxo_diffs_store
.get(accepting_block_hash)
.map_err(|_| UtxoInquirerError::MissingUtxoDiffForChainBlock(accepting_block_hash))?;

let removed_diffs = utxo_diff.removed();

// Collect values for each outpoint
let values: Vec<u64> = outpoints
.iter()
.map(|outpoint| {
removed_diffs
.get(outpoint)
.map(|v| v.amount)
.unwrap_or_else(|| {
log::error!("Missing UTXO entry for outpoint: {:?}", outpoint);
0
})
})
.collect();

Ok(values)
}

/// Returns the fully populated transaction with the given txid which was accepted at the provided accepting_block_daa_score.
/// The argument `accepting_block_daa_score` is expected to be the DAA score of the accepting chain block of `txid`.
///
Expand Down
19 changes: 19 additions & 0 deletions rpc/core/src/convert/tx.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Conversion of Transaction related types
use kaspa_consensus_core::acceptance_data::TransactionWithFee;
use crate::{RpcError, RpcResult, RpcTransaction, RpcTransactionInput, RpcTransactionOutput};
use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutput};

Expand All @@ -20,10 +21,28 @@ impl From<&Transaction> for RpcTransaction {
mass: item.mass(),
// TODO: Implement a populating process inspired from kaspad\app\rpc\rpccontext\verbosedata.go
verbose_data: None,
// TODO: fill
fee: 0,
}
}
}

impl From<&TransactionWithFee> for RpcTransaction {
fn from(TransactionWithFee {tx,fee}: &TransactionWithFee) -> Self {
let mut rtx = RpcTransaction::from(tx);
rtx.fee = *fee;
rtx
}
}
impl From<TransactionWithFee> for RpcTransaction {
fn from(TransactionWithFee{tx,fee}: TransactionWithFee) -> Self {
let mut rtx = RpcTransaction::from(&tx);
rtx.fee = fee;
rtx
}
}


impl From<&TransactionOutput> for RpcTransactionOutput {
fn from(item: &TransactionOutput) -> Self {
Self {
Expand Down
5 changes: 4 additions & 1 deletion rpc/core/src/model/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ pub struct RpcTransaction {
pub payload: Vec<u8>,
pub mass: u64,
pub verbose_data: Option<RpcTransactionVerboseData>,
pub fee: u64,
}

impl std::fmt::Debug for RpcTransaction {
Expand Down Expand Up @@ -332,6 +333,7 @@ impl Serializer for RpcTransaction {
store!(Vec<u8>, &self.payload, writer)?;
store!(u64, &self.mass, writer)?;
serialize!(Option<RpcTransactionVerboseData>, &self.verbose_data, writer)?;
store!(u64, &self.fee, writer)?;

Ok(())
}
Expand All @@ -349,8 +351,9 @@ impl Deserializer for RpcTransaction {
let payload = load!(Vec<u8>, reader)?;
let mass = load!(u64, reader)?;
let verbose_data = deserialize!(Option<RpcTransactionVerboseData>, reader)?;
let fee = load!(u64, reader)?;

Ok(Self { version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass, verbose_data })
Ok(Self { version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass, verbose_data, fee })
}
}

Expand Down
1 change: 1 addition & 0 deletions rpc/core/src/wasm/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ cfg_if::cfg_if! {
payload: inner.payload.clone(),
mass: inner.mass,
verbose_data: None,
fee: 0,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions rpc/grpc/core/proto/rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ message RpcTransaction {
string payload = 8;
RpcTransactionVerboseData verboseData = 9;
uint64 mass = 10;
uint64 fee = 255;
}

message RpcTransactionInput {
Expand Down
2 changes: 2 additions & 0 deletions rpc/grpc/core/src/convert/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ from!(item: &kaspa_rpc_core::RpcTransaction, protowire::RpcTransaction, {
payload: item.payload.to_rpc_hex(),
mass: item.mass,
verbose_data: item.verbose_data.as_ref().map(|x| x.into()),
fee: item.fee,
}
});

Expand Down Expand Up @@ -123,6 +124,7 @@ try_from!(item: &protowire::RpcTransaction, kaspa_rpc_core::RpcTransaction, {
payload: Vec::from_rpc_hex(&item.payload)?,
mass: item.mass,
verbose_data: item.verbose_data.as_ref().map(kaspa_rpc_core::RpcTransactionVerboseData::try_from).transpose()?,
fee: item.fee,
}
});

Expand Down
Loading

0 comments on commit e3d92ff

Please sign in to comment.