Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions applications/tari_app_utilities/src/fee_tables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2025 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use tari_engine::fees::FeeTable;
use tari_ootle_common_types::Network;

const TESTNET_FEE_TABLE: FeeTable = FeeTable {
per_transaction_weight_cost: 1,
per_module_call_cost: 1,
per_byte_storage_cost: 1,
per_event_cost: 1,
per_log_cost: 1,
per_signature_verification_cost: 10,
};

// TODO: finalize these values
const MAINNET_FEE_TABLE: FeeTable = FeeTable {
per_transaction_weight_cost: 1,
per_module_call_cost: 1,
per_byte_storage_cost: 1,
per_event_cost: 1,
per_log_cost: 1,
per_signature_verification_cost: 10,
};

pub const fn get_fee_table_by_network(network: Network) -> &'static FeeTable {
match network {
Network::LocalNet => &TESTNET_FEE_TABLE,
Network::Igor => &TESTNET_FEE_TABLE,
Network::Esmeralda => &TESTNET_FEE_TABLE,
Network::StageNet => &TESTNET_FEE_TABLE,
Network::NextNet => &TESTNET_FEE_TABLE,
Network::MainNet => &MAINNET_FEE_TABLE,
}
}
1 change: 1 addition & 0 deletions applications/tari_app_utilities/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
pub mod common;
pub mod configuration;
pub mod epoch_oracle_config;
pub mod fee_tables;
pub mod keypair;
pub mod p2p_config;
pub mod seed_peer;
Expand Down
36 changes: 8 additions & 28 deletions applications/tari_indexer/src/dry_run/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
use std::collections::HashMap;

use log::{debug, info};
use tari_engine::{fees::FeeTable, state_store::new_memory_store, transaction::TransactionProcessorConfig};
use tari_engine::{state_store::new_memory_store, transaction::TransactionProcessorConfig};
use tari_engine_types::{
commit_result::ExecuteResult,
substate::{Substate, SubstateId},
virtual_substate::{VirtualSubstate, VirtualSubstateId, VirtualSubstates},
};
use tari_epoch_manager::{service::EpochManagerHandle, EpochManagerReader};
use tari_ootle_app_utilities::transaction_executor::{TariTransactionProcessor, TransactionExecutor as _};
use tari_ootle_app_utilities::{
fee_tables::get_fee_table_by_network,
transaction_executor::{TariTransactionProcessor, TransactionExecutor as _},
};
use tari_ootle_common_types::{Epoch, PeerAddress, SubstateRequirement};
use tari_template_manager::implementation::TemplateManager;
use tari_transaction::Transaction;
Expand Down Expand Up @@ -81,7 +84,9 @@ impl DryRunTransactionProcessor {
let epoch = self.epoch_manager.current_epoch().await?;
let found_substates = self.fetch_input_substates(&transaction, epoch).await?;

let payload_processor = self.build_payload_processor(&transaction);
let fee_table = get_fee_table_by_network(self.config.network);
let payload_processor =
TariTransactionProcessor::new(self.config.clone(), self.template_manager.clone(), fee_table.clone());

let virtual_substates = self.get_virtual_substates(&transaction, epoch).await?;

Expand All @@ -96,31 +101,6 @@ impl DryRunTransactionProcessor {
Ok(exec_output.result)
}

fn build_payload_processor(
&self,
transaction: &Transaction,
) -> TariTransactionProcessor<TemplateManager<PeerAddress>> {
// simulate fees if the transaction requires it
let fee_table = if Self::transaction_includes_fees(transaction) {
// TODO: should match the VN fee table, should the fee table values be a consensus constant?
FeeTable {
per_transaction_weight_cost: 1,
per_module_call_cost: 1,
per_byte_storage_cost: 1,
per_event_cost: 1,
per_log_cost: 1,
}
} else {
FeeTable::zero_rated()
};

TariTransactionProcessor::new(self.config.clone(), self.template_manager.clone(), fee_table)
}

fn transaction_includes_fees(transaction: &Transaction) -> bool {
!transaction.fee_instructions().is_empty()
}

async fn fetch_input_substates(
&self,
transaction: &Transaction,
Expand Down
6 changes: 4 additions & 2 deletions applications/tari_indexer/src/storage_sqlite/store_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ use tari_ootle_storage_sqlite::{error::SqliteStorageError, SqliteTransaction};
use tari_ootle_wallet_sdk::models::WalletUtxoUpdate;
use tari_template_lib::{
models::ResourceAddress,
prelude::{crypto::UtxoTag, RistrettoPublicKeyBytes},
types::TemplateAddress,
types::{
crypto::{RistrettoPublicKeyBytes, UtxoTag},
TemplateAddress,
},
};
use tari_transaction::{Transaction, TransactionId};

Expand Down
6 changes: 4 additions & 2 deletions applications/tari_indexer/src/substate_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ use tari_ootle_common_types::{
use tari_ootle_wallet_sdk::models::WalletUtxoUpdate;
use tari_template_lib::{
models::ResourceAddress,
prelude::{crypto::UtxoTag, RistrettoPublicKeyBytes},
types::TemplateAddress,
types::{
crypto::{RistrettoPublicKeyBytes, UtxoTag},
TemplateAddress,
},
};
use tari_validator_node_rpc::client::{SubstateResult, TariValidatorNodeRpcClientFactory};

Expand Down
16 changes: 6 additions & 10 deletions applications/tari_validator_node/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use tari_consensus::consensus_constants::ConsensusConstants;
#[cfg(not(feature = "metrics"))]
use tari_consensus::traits::hooks::NoopHooks;
use tari_crypto::tari_utilities::ByteArray;
use tari_engine::{fees::FeeTable, transaction::TransactionProcessorConfig};
use tari_engine::transaction::TransactionProcessorConfig;
use tari_engine_types::ToByteType;
use tari_epoch_manager::{
service::{EpochManagerConfig, EpochManagerHandle},
Expand All @@ -54,6 +54,7 @@ use tari_ootle_app_utilities::{
common::verify_correct_network,
configuration::convert_network_to_l1_network,
epoch_oracle_config::{BaseLayerOracleConfig, EpochOracleType},
fee_tables::get_fee_table_by_network,
keypair::RistrettoKeypair,
seed_peer::SeedPeer,
template_download_queue::TemplateDownloadQueue,
Expand Down Expand Up @@ -269,13 +270,6 @@ pub async fn spawn_services(

info!(target: LOG_TARGET, "Payload processor initializing");
// Payload processor
let fee_table = FeeTable {
per_transaction_weight_cost: 1,
per_module_call_cost: 1,
per_byte_storage_cost: 1,
per_event_cost: 1,
per_log_cost: 1,
};

let (tx_hotstuff_events, _) = broadcast::channel(100);
// Consensus gossip
Expand Down Expand Up @@ -305,12 +299,13 @@ pub async fn spawn_services(
message_logger.clone(),
);

// Consensus
// Transaction executor
let fee_table = get_fee_table_by_network(config.network);
let payload_processor = TariTransactionProcessor::new(
TransactionProcessorConfig::new(config.network)
.with_template_binary_max_size_bytes(consensus_constants.template_binary_max_size_bytes),
template_manager.clone(),
fee_table,
fee_table.clone(),
);
let transaction_executor = TarBlockTransactionExecutor::new(
payload_processor.clone(),
Expand All @@ -330,6 +325,7 @@ pub async fn spawn_services(
.as_ref()
.map(|pk| pk.to_byte_type());

// Consensus
let signing_service = consensus::TariSignatureService::new(keypair.clone());
let (consensus_join_handle, consensus_handle) = consensus::spawn(
config.network,
Expand Down
112 changes: 112 additions & 0 deletions crates/common_types/src/engine_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2025 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use blake2::{
digest::{consts, generic_array::GenericArray},
Blake2b,
Digest,
};
use tari_crypto::ristretto::RistrettoSchnorr;
use tari_engine_types::{ConvertFromByteType, FromByteType, ToByteType};
use tari_template_lib_types::crypto::{PublicKey, RistrettoPublicKeyBytes, SignaturePayload};

pub trait GetVerifier {
fn get_verifier(&self) -> &dyn Verifier;
}

impl GetVerifier for SignaturePayload {
fn get_verifier(&self) -> &dyn Verifier {
match self {
SignaturePayload::RistrettoSchnorrBlake2b(_) => &RistrettoSchnorrBlake2bVerifier,
}
}
}

pub trait Verifier {
fn verify(&self, domain: &[u8], message: &[u8], public_key: &PublicKey, signature: &SignaturePayload) -> bool;
}

/// A verifier for Ristretto Schnorr signatures with Blake2b hashing.
pub struct RistrettoSchnorrBlake2bVerifier;

impl RistrettoSchnorrBlake2bVerifier {
pub fn compute_challenge(
domain: &[u8],
message: &[u8],
public_key: &RistrettoPublicKeyBytes,
nonce: &RistrettoPublicKeyBytes,
) -> GenericArray<u8, consts::U64> {
Blake2b::<consts::U64>::new()
// Domain
.chain_update(domain)
// Fiat-Shamir - note that we force this on the user to avoid security pitfalls.
.chain_update(nonce.as_bytes())
.chain_update(public_key.as_bytes())
// Message
.chain_update(message)
.finalize()
}
}

impl Verifier for RistrettoSchnorrBlake2bVerifier {
fn verify(&self, domain: &[u8], message: &[u8], public_key: &PublicKey, signature: &SignaturePayload) -> bool {
let sig = signature
.ristretto_schnorr_blake2b()
.expect("Expected Ristretto Schnorr signature");
let ristretto_public_key = public_key.ristretto25519().expect("Expected Ristretto PublicKey");

let Ok(pk) = ristretto_public_key.try_from_byte_type() else {
return false;
};

let Ok(sig) = RistrettoSchnorr::convert_from_byte_type(sig) else {
return false;
};

// NOTE: this is general purpose signature verification, and we want to user to specify a domain, without
// forcing tari-style domains to be mixed in.
let message = Self::compute_challenge(
domain,
message,
ristretto_public_key,
&sig.get_public_nonce().to_byte_type(),
);
sig.verify_raw_uniform(&pk, &message)
}
}

#[cfg(test)]
mod tests {
use rand::rngs::OsRng;
use tari_crypto::{
keys::PublicKey as _,
ristretto::{RistrettoPublicKey, RistrettoSchnorr},
};
use tari_engine_types::ToByteType;

use super::*;

#[test]
fn it_verifies_a_valid_signature() {
let (secret_key, public_key) = RistrettoPublicKey::random_keypair(&mut OsRng);

let message = b"Hello, world!";
let domain = b"Test domain";

let (nonce, public_nonce) = RistrettoPublicKey::random_keypair(&mut OsRng);

let hashed_message = RistrettoSchnorrBlake2bVerifier::compute_challenge(
domain,
message,
&public_key.to_byte_type(),
&public_nonce.to_byte_type(),
);
let signature = RistrettoSchnorr::sign_raw_uniform(&secret_key, nonce, &hashed_message).unwrap();

let signature_payload = SignaturePayload::RistrettoSchnorrBlake2b(signature.to_byte_type());
let public_key = PublicKey::Ristretto25519(public_key.to_byte_type());

let verifier = RistrettoSchnorrBlake2bVerifier;
assert!(verifier.verify(domain, message, &public_key, &signature_payload));
}
}
2 changes: 2 additions & 0 deletions crates/common_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod committee;
mod consensus_constants;
pub mod crypto;
pub mod displayable;
mod engine_signature;
mod epoch;
mod era;
mod extra_data;
Expand Down Expand Up @@ -35,6 +36,7 @@ mod vote_power;

pub use bytes::*;
pub use consensus_constants::*;
pub use engine_signature::*;
pub use epoch::Epoch;
pub use era::*;
pub use extra_data::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license.workspace = true
[dependencies]
tari_bor = { workspace = true, default-features = true, features = ["std"] }
tari_common_types = { workspace = true }
tari_crypto = { workspace = true, features = ["borsh"] }
tari_crypto = { workspace = true, features = ["serde", "borsh"] }
tari_ootle_common_types = { workspace = true }
tari_engine_types = { workspace = true }
tari_template_abi = { workspace = true, features = ["std"] }
Expand Down
15 changes: 14 additions & 1 deletion crates/engine/src/fees/fee_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tari_bor::{encode_into_writer, ByteCounter};
use tari_engine_types::fees::FeeSource;

use super::FeeTable;
use crate::runtime::{RuntimeModule, RuntimeModuleError, StateTracker};
use crate::runtime::{RuntimeEvent, RuntimeModule, RuntimeModuleError, StateTracker};

pub struct FeeModule {
initial_cost: u64,
Expand Down Expand Up @@ -67,4 +67,17 @@ impl RuntimeModule for FeeModule {

Ok(())
}

fn on_runtime_event(&self, track: &StateTracker, call: &RuntimeEvent) -> Result<(), RuntimeModuleError> {
match call {
RuntimeEvent::SignatureVerified => {
track.add_fee_charge(
FeeSource::SignatureVerification,
self.fee_table.per_signature_verification_cost(),
);
},
}
Comment thread
sdbondi marked this conversation as resolved.

Ok(())
}
}
6 changes: 6 additions & 0 deletions crates/engine/src/fees/fee_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct FeeTable {
pub per_byte_storage_cost: u64,
pub per_event_cost: u64,
pub per_log_cost: u64,
pub per_signature_verification_cost: u64,
}

impl FeeTable {
Expand All @@ -18,6 +19,7 @@ impl FeeTable {
per_byte_storage_cost: 0,
per_event_cost: 0,
per_log_cost: 0,
per_signature_verification_cost: 0,
}
}

Expand All @@ -40,4 +42,8 @@ impl FeeTable {
pub fn per_log_cost(&self) -> u64 {
self.per_log_cost
}

pub fn per_signature_verification_cost(&self) -> u64 {
self.per_signature_verification_cost
}
}
Loading
Loading