From a023511ba27684c96371514027eb369299d26565 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Thu, 18 Nov 2021 13:22:53 +0200 Subject: [PATCH 1/2] Standardize output hash - Standardized hash for a transaction output and an unblinded output as well as for the output hash calculation for transaction input. - Refactored `transaction.rs` code layout to be modular in the file system. --- .../src/conversions/output_features.rs | 6 +- .../src/conversions/transaction.rs | 6 +- .../src/conversions/transaction_input.rs | 2 +- .../src/conversions/transaction_kernel.rs | 9 +- .../src/conversions/transaction_output.rs | 6 +- .../src/conversions/unblinded_output.rs | 2 +- .../src/grpc/base_node_grpc_server.rs | 69 +- .../src/automation/commands.rs | 2 +- .../src/automation/error.rs | 7 +- .../src/grpc/wallet_grpc_server.rs | 2 +- .../src/common/merge_mining.rs | 6 +- .../src/common/mining.rs | 6 +- applications/test_faucet/src/main.rs | 5 +- .../comms_interface/comms_response.rs | 14 +- .../comms_interface/inbound_handlers.rs | 25 +- .../comms_interface/local_interface.rs | 28 +- base_layer/core/src/base_node/rpc/service.rs | 10 +- .../states/horizon_state_sync/error.rs | 19 +- .../horizon_state_synchronization.rs | 37 +- base_layer/core/src/blocks/block.rs | 29 +- base_layer/core/src/blocks/genesis_block.rs | 30 +- base_layer/core/src/chain_storage/async_db.rs | 26 +- .../src/chain_storage/blockchain_backend.rs | 16 +- .../src/chain_storage/blockchain_database.rs | 52 +- .../core/src/chain_storage/db_transaction.rs | 32 +- .../core/src/chain_storage/lmdb_db/lmdb_db.rs | 35 +- .../core/src/chain_storage/lmdb_db/mod.rs | 16 +- .../core/src/chain_storage/pruned_output.rs | 2 +- .../tests/blockchain_database.rs | 18 +- .../core/src/consensus/consensus_manager.rs | 12 +- base_layer/core/src/mempool/async_mempool.rs | 8 +- base_layer/core/src/mempool/error.rs | 8 +- base_layer/core/src/mempool/mempool.rs | 8 +- .../core/src/mempool/mempool_storage.rs | 13 +- base_layer/core/src/mempool/mod.rs | 2 +- .../priority/prioritized_transaction.rs | 11 +- .../core/src/mempool/reorg_pool/reorg_pool.rs | 22 +- .../mempool/reorg_pool/reorg_pool_storage.rs | 13 +- base_layer/core/src/mempool/rpc/service.rs | 11 +- base_layer/core/src/mempool/service/handle.rs | 7 +- .../src/mempool/service/inbound_handlers.rs | 15 +- .../core/src/mempool/service/initializer.rs | 41 +- .../core/src/mempool/service/local_service.rs | 18 +- .../src/mempool/service/outbound_interface.rs | 14 +- .../core/src/mempool/service/request.rs | 2 +- .../core/src/mempool/service/service.rs | 41 +- .../core/src/mempool/sync_protocol/mod.rs | 46 +- .../core/src/mempool/sync_protocol/test.rs | 35 +- .../unconfirmed_pool/unconfirmed_pool.rs | 4 +- base_layer/core/src/proto/transaction.rs | 27 +- .../core/src/test_helpers/block_spec.rs | 2 +- .../core/src/test_helpers/blockchain.rs | 42 +- base_layer/core/src/test_helpers/mod.rs | 22 +- .../core/src/transactions/aggregated_body.rs | 2 +- .../core/src/transactions/coinbase_builder.rs | 4 +- base_layer/core/src/transactions/mod.rs | 2 +- .../core/src/transactions/test_helpers.rs | 4 +- .../core/src/transactions/transaction.rs | 1870 ----------------- .../transaction_entities/error.rs | 63 + .../full_rewind_result.rs | 63 + .../transaction_entities/kernel_builder.rs | 102 + .../transaction_entities/kernel_features.rs | 39 + .../transaction_entities/kernel_sum.rs | 35 + .../transactions/transaction_entities/mod.rs | 533 +++++ .../transaction_entities/output_features.rs | 152 ++ .../transaction_entities/output_flags.rs | 63 + .../transaction_entities/rewind_result.rs | 55 + .../transaction_entities/transaction.rs | 175 ++ .../transaction_builder.rs | 124 ++ .../transaction_entities/transaction_input.rs | 224 ++ .../transaction_kernel.rs | 139 ++ .../transaction_output.rs | 368 ++++ .../transaction_entities/unblinded_output.rs | 228 ++ .../transactions/transaction_protocol/mod.rs | 20 +- .../transaction_protocol/recipient.rs | 10 +- .../transaction_protocol/sender.rs | 4 +- .../transaction_protocol/single_receiver.rs | 10 +- .../transaction_initializer.rs | 4 +- .../block_validators/async_validator.rs | 2 +- .../src/validation/block_validators/test.rs | 10 +- base_layer/core/src/validation/error.rs | 10 +- base_layer/core/src/validation/helpers.rs | 2 +- base_layer/core/src/validation/mocks.rs | 17 +- base_layer/core/src/validation/test.rs | 9 +- base_layer/core/src/validation/traits.rs | 8 +- .../src/validation/transaction_validators.rs | 2 +- base_layer/core/tests/async_db.rs | 2 +- base_layer/core/tests/base_node_rpc.rs | 6 +- base_layer/core/tests/block_validation.rs | 29 +- .../core/tests/helpers/block_builders.rs | 2 +- base_layer/core/tests/helpers/database.rs | 2 +- .../core/tests/helpers/sample_blockchains.rs | 2 +- .../core/tests/helpers/test_block_builder.rs | 2 +- .../core/tests/helpers/test_blockchain.rs | 2 +- base_layer/core/tests/mempool.rs | 2 +- base_layer/core/tests/node_comms_interface.rs | 2 +- base_layer/core/tests/node_service.rs | 20 +- base_layer/wallet/src/error.rs | 2 +- .../src/output_manager_service/error.rs | 10 +- .../src/output_manager_service/handle.rs | 2 +- .../recovery/standard_outputs_recoverer.rs | 2 +- .../src/output_manager_service/service.rs | 2 +- .../storage/database.rs | 2 +- .../output_manager_service/storage/models.rs | 2 +- .../storage/sqlite_db/mod.rs | 4 +- .../storage/sqlite_db/output_sql.rs | 2 +- .../wallet/src/transaction_service/error.rs | 26 +- .../wallet/src/transaction_service/handle.rs | 25 +- .../transaction_broadcast_protocol.rs | 31 +- .../protocols/transaction_receive_protocol.rs | 2 +- .../protocols/transaction_send_protocol.rs | 2 +- .../wallet/src/transaction_service/service.rs | 2 +- .../transaction_service/storage/database.rs | 27 +- .../src/transaction_service/storage/models.rs | 3 +- .../transaction_service/storage/sqlite_db.rs | 2 +- .../tasks/send_finalized_transaction.rs | 23 +- .../src/utxo_scanner_service/utxo_scanning.rs | 14 +- base_layer/wallet/src/wallet.rs | 2 +- .../tests/output_manager_service/service.rs | 2 +- base_layer/wallet/tests/support/comms_rpc.rs | 4 +- base_layer/wallet/tests/support/utils.rs | 14 +- .../tests/transaction_service/service.rs | 2 +- .../tests/transaction_service/storage.rs | 2 +- base_layer/wallet/tests/wallet/mod.rs | 2 +- .../wallet_ffi/src/callback_handler_tests.rs | 15 +- base_layer/wallet_ffi/src/lib.rs | 4 +- 126 files changed, 3151 insertions(+), 2446 deletions(-) delete mode 100644 base_layer/core/src/transactions/transaction.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/error.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/full_rewind_result.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/kernel_builder.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/kernel_features.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/kernel_sum.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/mod.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/output_features.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/output_flags.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/rewind_result.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/transaction.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/transaction_builder.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/transaction_input.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/transaction_kernel.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/transaction_output.rs create mode 100644 base_layer/core/src/transactions/transaction_entities/unblinded_output.rs diff --git a/applications/tari_app_grpc/src/conversions/output_features.rs b/applications/tari_app_grpc/src/conversions/output_features.rs index 553219b958..bdf5cbe367 100644 --- a/applications/tari_app_grpc/src/conversions/output_features.rs +++ b/applications/tari_app_grpc/src/conversions/output_features.rs @@ -20,9 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_rpc as grpc; use std::convert::TryFrom; -use tari_core::transactions::transaction::{OutputFeatures, OutputFlags}; + +use tari_core::transactions::transaction_entities::{OutputFeatures, OutputFlags}; + +use crate::tari_rpc as grpc; impl TryFrom for OutputFeatures { type Error = String; diff --git a/applications/tari_app_grpc/src/conversions/transaction.rs b/applications/tari_app_grpc/src/conversions/transaction.rs index 6aadee2066..05ada07310 100644 --- a/applications/tari_app_grpc/src/conversions/transaction.rs +++ b/applications/tari_app_grpc/src/conversions/transaction.rs @@ -20,14 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_rpc as grpc; use std::convert::{TryFrom, TryInto}; + use tari_common_types::transaction::{self as tx, TxId}; use tari_core::{ crypto::{ristretto::RistrettoSecretKey, tari_utilities::ByteArray}, - transactions::transaction::Transaction, + transactions::transaction_entities::Transaction, }; +use crate::tari_rpc as grpc; + impl From for grpc::Transaction { fn from(source: Transaction) -> Self { Self { diff --git a/applications/tari_app_grpc/src/conversions/transaction_input.rs b/applications/tari_app_grpc/src/conversions/transaction_input.rs index 48eebe04ad..fd9becc3eb 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_input.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_input.rs @@ -23,7 +23,7 @@ use crate::tari_rpc as grpc; use std::convert::{TryFrom, TryInto}; use tari_common_types::types::{Commitment, PublicKey}; -use tari_core::transactions::transaction::TransactionInput; +use tari_core::transactions::transaction_entities::TransactionInput; use tari_crypto::{ script::{ExecutionStack, TariScript}, tari_utilities::{ByteArray, Hashable}, diff --git a/applications/tari_app_grpc/src/conversions/transaction_kernel.rs b/applications/tari_app_grpc/src/conversions/transaction_kernel.rs index 7bf8664487..ee8437140b 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_kernel.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_kernel.rs @@ -20,14 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_rpc as grpc; use std::convert::{TryFrom, TryInto}; + +use tari_crypto::tari_utilities::{ByteArray, Hashable}; + use tari_common_types::types::Commitment; use tari_core::transactions::{ tari_amount::MicroTari, - transaction::{KernelFeatures, TransactionKernel}, + transaction_entities::{KernelFeatures, TransactionKernel}, }; -use tari_crypto::tari_utilities::{ByteArray, Hashable}; + +use crate::tari_rpc as grpc; impl TryFrom for TransactionKernel { type Error = String; diff --git a/applications/tari_app_grpc/src/conversions/transaction_output.rs b/applications/tari_app_grpc/src/conversions/transaction_output.rs index 7b783e3498..33f825382a 100644 --- a/applications/tari_app_grpc/src/conversions/transaction_output.rs +++ b/applications/tari_app_grpc/src/conversions/transaction_output.rs @@ -20,17 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_rpc as grpc; use std::convert::{TryFrom, TryInto}; + use tari_common_types::types::{BulletRangeProof, Commitment, PublicKey}; use tari_core::{ crypto::{ script::TariScript, tari_utilities::{ByteArray, Hashable}, }, - transactions::transaction::TransactionOutput, + transactions::transaction_entities::TransactionOutput, }; +use crate::tari_rpc as grpc; + impl TryFrom for TransactionOutput { type Error = String; diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index f452928e12..2225267f73 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -28,7 +28,7 @@ use tari_core::{ script::{ExecutionStack, TariScript}, tari_utilities::ByteArray, }, - transactions::{tari_amount::MicroTari, transaction::UnblindedOutput}, + transactions::{tari_amount::MicroTari, transaction_entities::UnblindedOutput}, }; impl From for grpc::UnblindedOutput { diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index ac4f6c6a53..41168f7e24 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -1,3 +1,39 @@ +use std::{ + cmp, + convert::{TryFrom, TryInto}, +}; + +use either::Either; +use futures::{channel::mpsc, SinkExt}; +use log::*; +use tari_crypto::tari_utilities::{message_format::MessageFormat, Hashable}; +use tokio::task; +use tonic::{Request, Response, Status}; + +use tari_app_grpc::{ + tari_rpc, + tari_rpc::{CalcType, Sorting}, +}; +use tari_app_utilities::consts; +use tari_common_types::types::Signature; +use tari_comms::{Bytes, CommsNode}; +use tari_core::{ + base_node::{ + comms_interface::{Broadcast, CommsInterfaceError}, + LocalNodeCommsInterface, + StateMachineHandle, + }, + blocks::{Block, BlockHeader, NewBlockTemplate}, + chain_storage::ChainStorageError, + consensus::{emission::Emission, ConsensusManager, NetworkConsensus}, + crypto::tari_utilities::{hex::Hex, ByteArray}, + iterators::NonOverlappingIntegerPairIter, + mempool::{service::LocalMempoolService, TxStorageResponse}, + proof_of_work::PowAlgorithm, + transactions::transaction_entities::Transaction, +}; +use tari_p2p::{auto_update::SoftwareUpdaterHandle, services::liveness::LivenessHandle}; + // Copyright 2019. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -26,39 +62,6 @@ use crate::{ helpers::{mean, median}, }, }; -use either::Either; -use futures::{channel::mpsc, SinkExt}; -use log::*; -use std::{ - cmp, - convert::{TryFrom, TryInto}, -}; -use tari_app_grpc::{ - tari_rpc, - tari_rpc::{CalcType, Sorting}, -}; -use tari_app_utilities::consts; -use tari_common_types::types::Signature; -use tari_comms::{Bytes, CommsNode}; -use tari_core::{ - base_node::{ - comms_interface::{Broadcast, CommsInterfaceError}, - LocalNodeCommsInterface, - StateMachineHandle, - }, - blocks::{Block, BlockHeader, NewBlockTemplate}, - chain_storage::ChainStorageError, - consensus::{emission::Emission, ConsensusManager, NetworkConsensus}, - crypto::tari_utilities::{hex::Hex, ByteArray}, - iterators::NonOverlappingIntegerPairIter, - mempool::{service::LocalMempoolService, TxStorageResponse}, - proof_of_work::PowAlgorithm, - transactions::transaction::Transaction, -}; -use tari_crypto::tari_utilities::{message_format::MessageFormat, Hashable}; -use tari_p2p::{auto_update::SoftwareUpdaterHandle, services::liveness::LivenessHandle}; -use tokio::task; -use tonic::{Request, Response, Status}; const LOG_TARGET: &str = "tari::base_node::grpc"; const GET_TOKENS_IN_CIRCULATION_MAX_HEIGHTS: usize = 1_000_000; diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 8468ec8a18..27cf5f54a8 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -54,7 +54,7 @@ use tari_core::{ tari_utilities::hex::Hex, transactions::{ tari_amount::{uT, MicroTari, Tari}, - transaction::{TransactionOutput, UnblindedOutput}, + transaction_entities::{TransactionOutput, UnblindedOutput}, }, }; use tari_wallet::{ diff --git a/applications/tari_console_wallet/src/automation/error.rs b/applications/tari_console_wallet/src/automation/error.rs index e3600782fc..368a40d7fc 100644 --- a/applications/tari_console_wallet/src/automation/error.rs +++ b/applications/tari_console_wallet/src/automation/error.rs @@ -23,12 +23,15 @@ use std::num::{ParseFloatError, ParseIntError}; use log::*; +use thiserror::Error; +use tokio::task::JoinError; + use tari_common::exit_codes::ExitCodes; use tari_core::{ tari_utilities::hex::HexError, transactions::{ tari_amount::{MicroTariError, TariConversionError}, - transaction::TransactionError, + transaction_entities::TransactionError, }, }; use tari_wallet::{ @@ -36,8 +39,6 @@ use tari_wallet::{ output_manager_service::error::OutputManagerError, transaction_service::error::TransactionServiceError, }; -use thiserror::Error; -use tokio::task::JoinError; pub const LOG_TARGET: &str = "wallet::automation::error"; diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 18e19d6a18..2934a4c84d 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -43,7 +43,7 @@ use tari_common_types::types::{BlockHash, Signature}; use tari_comms::{types::CommsPublicKey, CommsNode}; use tari_core::{ tari_utilities::{hex::Hex, ByteArray}, - transactions::{tari_amount::MicroTari, transaction::UnblindedOutput}, + transactions::{tari_amount::MicroTari, transaction_entities::UnblindedOutput}, }; use tari_crypto::tari_utilities::Hashable; use tari_wallet::{ diff --git a/applications/tari_merge_mining_proxy/src/common/merge_mining.rs b/applications/tari_merge_mining_proxy/src/common/merge_mining.rs index 7d232069b7..fcaf5de8c9 100644 --- a/applications/tari_merge_mining_proxy/src/common/merge_mining.rs +++ b/applications/tari_merge_mining_proxy/src/common/merge_mining.rs @@ -20,14 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::error::MmProxyError; use std::convert::TryFrom; + use tari_app_grpc::tari_rpc as grpc; use tari_core::{ blocks::NewBlockTemplate, - transactions::transaction::{TransactionKernel, TransactionOutput}, + transactions::transaction_entities::{TransactionKernel, TransactionOutput}, }; +use crate::error::MmProxyError; + pub fn add_coinbase( coinbase: grpc::Transaction, block_template: grpc::NewBlockTemplate, diff --git a/applications/tari_stratum_transcoder/src/common/mining.rs b/applications/tari_stratum_transcoder/src/common/mining.rs index bf4a714b2b..38390bcae1 100644 --- a/applications/tari_stratum_transcoder/src/common/mining.rs +++ b/applications/tari_stratum_transcoder/src/common/mining.rs @@ -20,14 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::error::StratumTranscoderProxyError; use std::convert::TryFrom; + use tari_app_grpc::tari_rpc as grpc; use tari_core::{ blocks::NewBlockTemplate, - transactions::transaction::{TransactionKernel, TransactionOutput}, + transactions::transaction_entities::{TransactionKernel, TransactionOutput}, }; +use crate::error::StratumTranscoderProxyError; + pub fn add_coinbase( coinbase: Option, mut block: NewBlockTemplate, diff --git a/applications/test_faucet/src/main.rs b/applications/test_faucet/src/main.rs index 3b463d7d22..a3d2946eef 100644 --- a/applications/test_faucet/src/main.rs +++ b/applications/test_faucet/src/main.rs @@ -9,17 +9,16 @@ use std::{fs::File, io::Write}; use serde::Serialize; -use tari_crypto::script; +use tari_crypto::{script, tari_utilities::hex::Hex}; use tokio::{sync::mpsc, task}; use tari_common_types::types::{Commitment, PrivateKey}; use tari_core::transactions::{ tari_amount::{MicroTari, T}, test_helpers, - transaction::{KernelFeatures, OutputFeatures, TransactionKernel, TransactionOutput}, + transaction_entities::{KernelFeatures, OutputFeatures, TransactionKernel, TransactionOutput}, CryptoFactories, }; -use tari_crypto::tari_utilities::hex::Hex; const NUM_KEYS: usize = 4000; diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs index e5a1fbaa1c..017dadce18 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_response.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -20,14 +20,20 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::{self, Display, Formatter}; + +use serde::{Deserialize, Serialize}; + +use tari_common_types::{chain_metadata::ChainMetadata, types::HashOutput}; + use crate::{ blocks::{Block, BlockHeader, ChainHeader, HistoricalBlock, NewBlockTemplate}, proof_of_work::Difficulty, - transactions::transaction::{TransactionKernel, TransactionOutput}, + transactions::transaction_entities::{ + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }; -use serde::{Deserialize, Serialize}; -use std::fmt::{self, Display, Formatter}; -use tari_common_types::{chain_metadata::ChainMetadata, types::HashOutput}; /// API Response enum #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index 6dfec0b3b4..b3b625d132 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -1,3 +1,16 @@ +use std::{ + fmt::{Display, Error, Formatter}, + sync::Arc, +}; + +use log::*; +use strum_macros::Display; +use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex}; +use tokio::sync::Semaphore; + +use tari_common_types::types::{BlockHash, HashOutput}; +use tari_comms::peer_manager::NodeId; + // Copyright 2019. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -32,18 +45,8 @@ use crate::{ consensus::{ConsensusConstants, ConsensusManager}, mempool::{async_mempool, Mempool}, proof_of_work::{Difficulty, PowAlgorithm}, - transactions::transaction::TransactionKernel, -}; -use log::*; -use std::{ - fmt::{Display, Error, Formatter}, - sync::Arc, + transactions::transaction_entities::transaction_kernel::TransactionKernel, }; -use strum_macros::Display; -use tari_common_types::types::{BlockHash, HashOutput}; -use tari_comms::peer_manager::NodeId; -use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex}; -use tokio::sync::Semaphore; const LOG_TARGET: &str = "c::bn::comms_interface::inbound_handler"; const MAX_HEADERS_PER_RESPONSE: u32 = 100; diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs index 04e72860a3..c5cc6b71b6 100644 --- a/base_layer/core/src/base_node/comms_interface/local_interface.rs +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -20,31 +20,35 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::{ops::RangeInclusive, sync::Arc}; + +use tokio::sync::broadcast; + +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{BlockHash, Commitment, HashOutput, Signature}, +}; +use tari_service_framework::{reply_channel::SenderService, Service}; + use crate::{ base_node::comms_interface::{ + comms_request::GetNewBlockTemplateRequest, error::CommsInterfaceError, BlockEvent, Broadcast, NodeCommsRequest, NodeCommsResponse, }, - blocks::{Block, HistoricalBlock, NewBlockTemplate}, + blocks::{Block, ChainHeader, HistoricalBlock, NewBlockTemplate}, proof_of_work::PowAlgorithm, - transactions::transaction::TransactionKernel, + transactions::transaction_entities::{ + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }; -use std::{ops::RangeInclusive, sync::Arc}; -use tari_common_types::{chain_metadata::ChainMetadata, types::BlockHash}; -use tari_service_framework::{reply_channel::SenderService, Service}; -use tokio::sync::broadcast; pub type BlockEventSender = broadcast::Sender>; pub type BlockEventReceiver = broadcast::Receiver>; -use crate::{ - base_node::comms_interface::comms_request::GetNewBlockTemplateRequest, - blocks::ChainHeader, - transactions::transaction::TransactionOutput, -}; -use tari_common_types::types::{Commitment, HashOutput, Signature}; /// The InboundNodeCommsInterface provides an interface to request information from the current local node by other /// internal services. diff --git a/base_layer/core/src/base_node/rpc/service.rs b/base_layer/core/src/base_node/rpc/service.rs index 7edca0efa9..44400f0e3a 100644 --- a/base_layer/core/src/base_node/rpc/service.rs +++ b/base_layer/core/src/base_node/rpc/service.rs @@ -1,3 +1,8 @@ +use std::convert::TryFrom; + +use tari_common_types::types::Signature; +use tari_comms::protocol::rpc::{Request, Response, RpcStatus}; + // Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -44,11 +49,8 @@ use crate::{ }, types::{Signature as SignatureProto, Transaction as TransactionProto}, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use std::convert::TryFrom; -use tari_common_types::types::Signature; -use tari_comms::protocol::rpc::{Request, Response, RpcStatus}; const LOG_TARGET: &str = "c::base_node::rpc"; diff --git a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/error.rs b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/error.rs index adb65def9f..8dd46d70f7 100644 --- a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/error.rs +++ b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/error.rs @@ -20,20 +20,23 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - base_node::{comms_interface::CommsInterfaceError, state_machine_service::states::helpers::BaseNodeRequestError}, - chain_storage::{ChainStorageError, MmrTree}, - transactions::transaction::TransactionError, - validation::ValidationError, -}; use std::num::TryFromIntError; + +use thiserror::Error; +use tokio::task; + use tari_comms::{ connectivity::ConnectivityError, protocol::rpc::{RpcError, RpcStatus}, }; use tari_mmr::error::MerkleMountainRangeError; -use thiserror::Error; -use tokio::task; + +use crate::{ + base_node::{comms_interface::CommsInterfaceError, state_machine_service::states::helpers::BaseNodeRequestError}, + chain_storage::{ChainStorageError, MmrTree}, + transactions::transaction_entities::error::TransactionError, + validation::ValidationError, +}; #[derive(Debug, Error)] pub enum HorizonSyncError { diff --git a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs index 52c1556bb6..a5883cfbb5 100644 --- a/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs +++ b/base_layer/core/src/base_node/state_machine_service/states/horizon_state_sync/horizon_state_synchronization.rs @@ -20,7 +20,22 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::error::HorizonSyncError; +use std::{ + convert::{TryFrom, TryInto}, + sync::Arc, +}; + +use croaring::Bitmap; +use futures::StreamExt; +use log::*; +use tari_crypto::{ + commitment::HomomorphicCommitment, + tari_utilities::{hex::Hex, Hashable}, +}; + +use tari_common_types::types::{HashDigest, RangeProofService}; +use tari_mmr::{MerkleMountainRange, MutableMmr}; + use crate::{ base_node::{ state_machine_service::{ @@ -39,21 +54,13 @@ use crate::{ SyncUtxosRequest, SyncUtxosResponse, }, - transactions::transaction::{TransactionKernel, TransactionOutput}, -}; -use croaring::Bitmap; -use futures::StreamExt; -use log::*; -use std::{ - convert::{TryFrom, TryInto}, - sync::Arc, -}; -use tari_common_types::types::{HashDigest, RangeProofService}; -use tari_crypto::{ - commitment::HomomorphicCommitment, - tari_utilities::{hex::Hex, Hashable}, + transactions::transaction_entities::{ + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }; -use tari_mmr::{MerkleMountainRange, MutableMmr}; + +use super::error::HorizonSyncError; const LOG_TARGET: &str = "c::bn::state_machine_service::states::horizon_state_sync"; diff --git a/base_layer/core/src/blocks/block.rs b/base_layer/core/src/blocks/block.rs index 2c6457566e..f5a17d3380 100644 --- a/base_layer/core/src/blocks/block.rs +++ b/base_layer/core/src/blocks/block.rs @@ -23,6 +23,18 @@ // Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, // Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. +use std::{ + fmt, + fmt::{Display, Formatter}, +}; + +use log::*; +use serde::{Deserialize, Serialize}; +use tari_crypto::tari_utilities::Hashable; +use thiserror::Error; + +use tari_common_types::types::BlockHash; + use crate::{ blocks::BlockHeader, consensus::ConsensusConstants, @@ -31,19 +43,16 @@ use crate::{ transactions::{ aggregated_body::AggregateBody, tari_amount::MicroTari, - transaction::{Transaction, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, + transaction_entities::{ + error::TransactionError, + transaction::Transaction, + transaction_input::TransactionInput, + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, CryptoFactories, }, }; -use log::*; -use serde::{Deserialize, Serialize}; -use std::{ - fmt, - fmt::{Display, Formatter}, -}; -use tari_common_types::types::BlockHash; -use tari_crypto::tari_utilities::Hashable; -use thiserror::Error; #[derive(Clone, Debug, PartialEq, Error)] pub enum BlockValidationError { diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index d892ba41ff..d1c9bab91a 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -20,6 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use chrono::DateTime; +use tari_crypto::{ + script::TariScript, + tari_utilities::{hash::Hashable, hex::*}, +}; + +use tari_common::configuration::Network; +use tari_common_types::types::{BulletRangeProof, Commitment, PrivateKey, PublicKey, Signature, BLOCK_HASH_LENGTH}; + // This file is used to store the genesis block use crate::{ blocks::{block::Block, BlockHeader, BlockHeaderAccumulatedData, ChainBlock}, @@ -27,17 +38,15 @@ use crate::{ transactions::{ aggregated_body::AggregateBody, tari_amount::MicroTari, - transaction::{KernelFeatures, OutputFeatures, OutputFlags, TransactionKernel, TransactionOutput}, + transaction_entities::{ + output_features::OutputFeatures, + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + KernelFeatures, + OutputFlags, + }, }, }; -use chrono::DateTime; -use std::sync::Arc; -use tari_common::configuration::Network; -use tari_common_types::types::{BulletRangeProof, Commitment, PrivateKey, PublicKey, Signature, BLOCK_HASH_LENGTH}; -use tari_crypto::{ - script::TariScript, - tari_utilities::{hash::Hashable, hex::*}, -}; /// Returns the genesis block for the selected network. pub fn get_genesis_block(network: Network) -> ChainBlock { @@ -465,9 +474,10 @@ pub fn get_igor_genesis_block_raw() -> Block { #[cfg(test)] mod test { - use super::*; use crate::transactions::CryptoFactories; + use super::*; + #[test] fn weatherwax_genesis_sanity_check() { let block = get_weatherwax_genesis_block(); diff --git a/base_layer/core/src/chain_storage/async_db.rs b/base_layer/core/src/chain_storage/async_db.rs index 269e096da5..99e2bcbd4d 100644 --- a/base_layer/core/src/chain_storage/async_db.rs +++ b/base_layer/core/src/chain_storage/async_db.rs @@ -20,6 +20,18 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::{mem, ops::RangeBounds, sync::Arc, time::Instant}; + +use croaring::Bitmap; +use log::*; +use rand::{rngs::OsRng, RngCore}; + +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{BlockHash, Commitment, HashOutput, Signature}, +}; +use tari_mmr::pruned_hashset::PrunedHashSet; + use crate::{ blocks::{ Block, @@ -51,17 +63,11 @@ use crate::{ common::rolling_vec::RollingVec, proof_of_work::{PowAlgorithm, TargetDifficultyWindow}, tari_utilities::epoch_time::EpochTime, - transactions::transaction::{TransactionKernel, TransactionOutput}, -}; -use croaring::Bitmap; -use log::*; -use rand::{rngs::OsRng, RngCore}; -use std::{mem, ops::RangeBounds, sync::Arc, time::Instant}; -use tari_common_types::{ - chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, HashOutput, Signature}, + transactions::transaction_entities::{ + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }; -use tari_mmr::pruned_hashset::PrunedHashSet; const LOG_TARGET: &str = "c::bn::async_db"; diff --git a/base_layer/core/src/chain_storage/blockchain_backend.rs b/base_layer/core/src/chain_storage/blockchain_backend.rs index bd1a3acdef..17595aae6c 100644 --- a/base_layer/core/src/chain_storage/blockchain_backend.rs +++ b/base_layer/core/src/chain_storage/blockchain_backend.rs @@ -1,3 +1,11 @@ +use croaring::Bitmap; + +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{Commitment, HashOutput, Signature}, +}; +use tari_mmr::Hash; + use crate::{ blocks::{ Block, @@ -20,14 +28,8 @@ use crate::{ MmrTree, UtxoMinedInfo, }, - transactions::transaction::{TransactionInput, TransactionKernel}, + transactions::transaction_entities::{transaction_input::TransactionInput, transaction_kernel::TransactionKernel}, }; -use croaring::Bitmap; -use tari_common_types::{ - chain_metadata::ChainMetadata, - types::{Commitment, HashOutput, Signature}, -}; -use tari_mmr::Hash; /// Identify behaviour for Blockchain database backends. Implementations must support `Send` and `Sync` so that /// `BlockchainDatabase` can be thread-safe. The backend *must* also execute transactions atomically; i.e., every diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index 5b3b07abf8..e2a189b54a 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -1,3 +1,24 @@ +use std::{ + cmp, + cmp::Ordering, + collections::VecDeque, + convert::TryFrom, + mem, + ops::{Bound, RangeBounds}, + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, + time::Instant, +}; + +use croaring::Bitmap; +use log::*; +use tari_crypto::tari_utilities::{hex::Hex, ByteArray, Hashable}; + +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{BlockHash, Commitment, HashDigest, HashOutput, Signature}, +}; +use tari_mmr::{pruned_hashset::PrunedHashSet, MerkleMountainRange, MutableMmr}; + // Copyright 2019. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -56,7 +77,7 @@ use crate::{ consensus::{chain_strength_comparer::ChainStrengthComparer, ConsensusConstants, ConsensusManager}, proof_of_work::{monero_rx::MoneroPowData, PowAlgorithm, TargetDifficultyWindow}, tari_utilities::epoch_time::EpochTime, - transactions::transaction::TransactionKernel, + transactions::transaction_entities::transaction_kernel::TransactionKernel, validation::{ helpers::calc_median_timestamp, DifficultyCalculator, @@ -66,24 +87,6 @@ use crate::{ ValidationError, }, }; -use croaring::Bitmap; -use log::*; -use std::{ - cmp, - cmp::Ordering, - collections::VecDeque, - convert::TryFrom, - mem, - ops::{Bound, RangeBounds}, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, - time::Instant, -}; -use tari_common_types::{ - chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, HashDigest, HashOutput, Signature}, -}; -use tari_crypto::tari_utilities::{hex::Hex, ByteArray, Hashable}; -use tari_mmr::{pruned_hashset::PrunedHashSet, MerkleMountainRange, MutableMmr}; const LOG_TARGET: &str = "c::cs::database"; @@ -2129,7 +2132,11 @@ fn convert_to_option_bounds>(bounds: T) -> (Option, Opt #[cfg(test)] mod test { - use super::*; + use std::{collections::HashMap, sync}; + + use tari_common::configuration::Network; + use tari_test_utils::unpack_enum; + use crate::{ block_specs, consensus::{ @@ -2151,9 +2158,8 @@ mod test { }, validation::{header_validator::HeaderValidator, mocks::MockValidator}, }; - use std::{collections::HashMap, sync}; - use tari_common::configuration::Network; - use tari_test_utils::unpack_enum; + + use super::*; #[test] fn lmdb_fetch_monero_seeds() { diff --git a/base_layer/core/src/chain_storage/db_transaction.rs b/base_layer/core/src/chain_storage/db_transaction.rs index 970988d655..066528f73b 100644 --- a/base_layer/core/src/chain_storage/db_transaction.rs +++ b/base_layer/core/src/chain_storage/db_transaction.rs @@ -1,3 +1,18 @@ +use std::{ + fmt, + fmt::{Display, Error, Formatter}, + sync::Arc, +}; + +use croaring::Bitmap; +use tari_crypto::tari_utilities::{ + hex::{to_hex, Hex}, + Hashable, +}; + +use tari_common_types::types::{BlockHash, Commitment, HashOutput}; +use tari_mmr::pruned_hashset::PrunedHashSet; + // Copyright 2019. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -22,20 +37,11 @@ use crate::{ blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainBlock, ChainHeader}, chain_storage::{error::ChainStorageError, MmrTree}, - transactions::transaction::{TransactionKernel, TransactionOutput}, -}; -use croaring::Bitmap; -use std::{ - fmt, - fmt::{Display, Error, Formatter}, - sync::Arc, -}; -use tari_common_types::types::{BlockHash, Commitment, HashOutput}; -use tari_crypto::tari_utilities::{ - hex::{to_hex, Hex}, - Hashable, + transactions::transaction_entities::{ + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }; -use tari_mmr::pruned_hashset::PrunedHashSet; #[derive(Debug)] pub struct DbTransaction { diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index 3b80e8dacf..0d3e18a6a5 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -24,6 +24,22 @@ // let's ignore this clippy error in this module #![allow(clippy::ptr_arg)] +use std::{convert::TryFrom, fmt, fs, fs::File, ops::Deref, path::Path, sync::Arc, time::Instant}; + +use croaring::Bitmap; +use fs2::FileExt; +use lmdb_zero::{ConstTransaction, Database, Environment, ReadTransaction, WriteTransaction}; +use log::*; +use serde::{Deserialize, Serialize}; +use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex, ByteArray}; + +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{BlockHash, Commitment, HashDigest, HashOutput, Signature, BLOCK_HASH_LENGTH}, +}; +use tari_mmr::{pruned_hashset::PrunedHashSet, Hash, MerkleMountainRange, MutableMmr}; +use tari_storage::lmdb_store::{db, LMDBBuilder, LMDBConfig, LMDBStore}; + use crate::{ blocks::{ Block, @@ -71,22 +87,13 @@ use crate::{ crypto::tari_utilities::hex::to_hex, transactions::{ aggregated_body::AggregateBody, - transaction::{TransactionInput, TransactionKernel, TransactionOutput}, + transaction_entities::{ + transaction_input::TransactionInput, + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, + }, }, }; -use croaring::Bitmap; -use fs2::FileExt; -use lmdb_zero::{ConstTransaction, Database, Environment, ReadTransaction, WriteTransaction}; -use log::*; -use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, fs, fs::File, ops::Deref, path::Path, sync::Arc, time::Instant}; -use tari_common_types::{ - chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, HashDigest, HashOutput, Signature, BLOCK_HASH_LENGTH}, -}; -use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex, ByteArray}; -use tari_mmr::{pruned_hashset::PrunedHashSet, Hash, MerkleMountainRange, MutableMmr}; -use tari_storage::lmdb_store::{db, LMDBBuilder, LMDBConfig, LMDBStore}; type DatabaseRef = Arc>; diff --git a/base_layer/core/src/chain_storage/lmdb_db/mod.rs b/base_layer/core/src/chain_storage/lmdb_db/mod.rs index 7947da342d..41291e9a38 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/mod.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/mod.rs @@ -20,15 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod lmdb; -#[allow(clippy::module_inception)] -mod lmdb_db; +use serde::{Deserialize, Serialize}; -use crate::transactions::transaction::{TransactionInput, TransactionKernel, TransactionOutput}; pub use lmdb_db::{create_lmdb_database, create_recovery_lmdb_database, LMDBDatabase}; -use serde::{Deserialize, Serialize}; use tari_common_types::types::HashOutput; +use crate::transactions::transaction_entities::{ + transaction_input::TransactionInput, + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, +}; + +mod lmdb; +#[allow(clippy::module_inception)] +mod lmdb_db; + #[derive(Serialize, Deserialize, Debug)] pub(crate) struct TransactionOutputRowData { pub output: Option, diff --git a/base_layer/core/src/chain_storage/pruned_output.rs b/base_layer/core/src/chain_storage/pruned_output.rs index 20f54c37ef..c69e35565a 100644 --- a/base_layer/core/src/chain_storage/pruned_output.rs +++ b/base_layer/core/src/chain_storage/pruned_output.rs @@ -19,7 +19,7 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::transactions::transaction::TransactionOutput; +use crate::transactions::transaction_entities::transaction_output::TransactionOutput; use tari_common_types::types::HashOutput; use tari_crypto::tari_utilities::Hashable; diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index b7716373b8..4bcd6099ec 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -20,6 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use tari_common::configuration::Network; +use tari_test_utils::unpack_enum; + use crate::{ blocks::{Block, BlockHeader, NewBlockTemplate}, chain_storage::{BlockchainDatabase, ChainStorageError}, @@ -33,12 +38,9 @@ use crate::{ }, transactions::{ tari_amount::T, - transaction::{Transaction, UnblindedOutput}, + transaction_entities::{transaction::Transaction, unblinded_output::UnblindedOutput}, }, }; -use std::sync::Arc; -use tari_common::configuration::Network; -use tari_test_utils::unpack_enum; fn setup() -> BlockchainDatabase { create_new_blockchain() @@ -237,9 +239,10 @@ mod fetch_headers { } mod find_headers_after_hash { - use super::*; use crate::chain_storage::ChainStorageError; + use super::*; + #[test] fn it_returns_none_given_empty_vec() { let db = setup(); @@ -350,18 +353,19 @@ mod fetch_block_hashes_from_header_tip { } mod add_block { - use super::*; use crate::{ chain_storage::ChainStorageError, crypto::tari_utilities::hex::Hex, transactions::{ tari_amount::T, test_helpers::{schema_to_transaction, TransactionSchema}, - transaction::OutputFeatures, + transaction_entities::output_features::OutputFeatures, }, txn_schema, }; + use super::*; + #[test] fn it_does_not_allow_duplicate_commitments_in_the_utxo_set() { let db = setup(); diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs index 925bfbe0bd..df78fe2b00 100644 --- a/base_layer/core/src/consensus/consensus_manager.rs +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -20,6 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use thiserror::Error; + +use tari_common::configuration::Network; + #[cfg(feature = "base_node")] use crate::{ blocks::ChainBlock, @@ -27,7 +33,6 @@ use crate::{ proof_of_work::PowAlgorithm, proof_of_work::TargetDifficultyWindow, }; - use crate::{ consensus::{ emission::{Emission, EmissionSchedule}, @@ -35,11 +40,8 @@ use crate::{ NetworkConsensus, }, proof_of_work::DifficultyAdjustmentError, - transactions::{tari_amount::MicroTari, transaction::TransactionKernel}, + transactions::{tari_amount::MicroTari, transaction_entities::transaction_kernel::TransactionKernel}, }; -use std::sync::Arc; -use tari_common::configuration::Network; -use thiserror::Error; #[derive(Debug, Error)] #[allow(clippy::large_enum_variant)] diff --git a/base_layer/core/src/mempool/async_mempool.rs b/base_layer/core/src/mempool/async_mempool.rs index d99da619f1..0955e2e58f 100644 --- a/base_layer/core/src/mempool/async_mempool.rs +++ b/base_layer/core/src/mempool/async_mempool.rs @@ -20,13 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use tari_common_types::types::Signature; + use crate::{ blocks::Block, mempool::{error::MempoolError, Mempool, StateResponse, StatsResponse, TxStorageResponse}, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use std::sync::Arc; -use tari_common_types::types::Signature; macro_rules! make_async { ($fn:ident($($param1:ident:$ptype1:ty,$param2:ident:$ptype2:ty),+) -> $rtype:ty) => { diff --git a/base_layer/core/src/mempool/error.rs b/base_layer/core/src/mempool/error.rs index d49723daa0..0c21845f02 100644 --- a/base_layer/core/src/mempool/error.rs +++ b/base_layer/core/src/mempool/error.rs @@ -20,13 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use thiserror::Error; + +use tari_service_framework::reply_channel::TransportChannelError; + use crate::{ chain_storage::ChainStorageError, mempool::{reorg_pool::ReorgPoolError, unconfirmed_pool::UnconfirmedPoolError}, - transactions::transaction::TransactionError, + transactions::transaction_entities::error::TransactionError, }; -use tari_service_framework::reply_channel::TransportChannelError; -use thiserror::Error; #[derive(Debug, Error)] pub enum MempoolError { diff --git a/base_layer/core/src/mempool/mempool.rs b/base_layer/core/src/mempool/mempool.rs index 444bf491b6..8b45ac0ede 100644 --- a/base_layer/core/src/mempool/mempool.rs +++ b/base_layer/core/src/mempool/mempool.rs @@ -20,6 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::{Arc, RwLock}; + +use tari_common_types::types::Signature; + use crate::{ blocks::Block, consensus::ConsensusManager, @@ -31,11 +35,9 @@ use crate::{ StatsResponse, TxStorageResponse, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, validation::MempoolTransactionValidation, }; -use std::sync::{Arc, RwLock}; -use tari_common_types::types::Signature; /// The Mempool consists of an Unconfirmed Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool and is responsible /// for managing and maintaining all unconfirmed transactions have not yet been included in a block, and transactions diff --git a/base_layer/core/src/mempool/mempool_storage.rs b/base_layer/core/src/mempool/mempool_storage.rs index a50e25a7ae..d5f3d90a56 100644 --- a/base_layer/core/src/mempool/mempool_storage.rs +++ b/base_layer/core/src/mempool/mempool_storage.rs @@ -20,6 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use log::*; +use tari_crypto::tari_utilities::{hex::Hex, Hashable}; + +use tari_common_types::types::Signature; + use crate::{ blocks::Block, consensus::ConsensusManager, @@ -32,13 +39,9 @@ use crate::{ StatsResponse, TxStorageResponse, }, - transactions::{transaction::Transaction, weight::TransactionWeight}, + transactions::{transaction_entities::transaction::Transaction, weight::TransactionWeight}, validation::{MempoolTransactionValidation, ValidationError}, }; -use log::*; -use std::sync::Arc; -use tari_common_types::types::Signature; -use tari_crypto::tari_utilities::{hex::Hex, Hashable}; pub const LOG_TARGET: &str = "c::mp::mempool_storage"; diff --git a/base_layer/core/src/mempool/mod.rs b/base_layer/core/src/mempool/mod.rs index 4e12849f71..a471535aec 100644 --- a/base_layer/core/src/mempool/mod.rs +++ b/base_layer/core/src/mempool/mod.rs @@ -72,7 +72,7 @@ mod sync_protocol; #[cfg(feature = "base_node")] pub use sync_protocol::MempoolSyncInitializer; -use crate::transactions::transaction::Transaction; +use crate::transactions::transaction_entities::transaction::Transaction; use core::fmt::{Display, Error, Formatter}; use serde::{Deserialize, Serialize}; use tari_common_types::types::Signature; diff --git a/base_layer/core/src/mempool/priority/prioritized_transaction.rs b/base_layer/core/src/mempool/priority/prioritized_transaction.rs index 7051db9858..51d2718924 100644 --- a/base_layer/core/src/mempool/priority/prioritized_transaction.rs +++ b/base_layer/core/src/mempool/priority/prioritized_transaction.rs @@ -20,13 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use tari_crypto::tari_utilities::message_format::MessageFormat; + +use tari_common_types::types::HashOutput; + use crate::{ mempool::priority::PriorityError, - transactions::{transaction::Transaction, weight::TransactionWeight}, + transactions::{transaction_entities::transaction::Transaction, weight::TransactionWeight}, }; -use std::sync::Arc; -use tari_common_types::types::HashOutput; -use tari_crypto::tari_utilities::message_format::MessageFormat; /// Create a unique unspent transaction priority based on the transaction fee, maturity of the oldest input UTXO and the /// excess_sig. The excess_sig is included to ensure the the priority key unique so it can be used with a BTreeMap. diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs index f86e04d7ed..06d166eb08 100644 --- a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs @@ -20,18 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::{sync::Arc, time::Duration}; + +use serde::{Deserialize, Serialize}; + +use tari_common::configuration::seconds; +use tari_common_types::types::Signature; + use crate::{ blocks::Block, mempool::{ consts::{MEMPOOL_REORG_POOL_CACHE_TTL, MEMPOOL_REORG_POOL_STORAGE_CAPACITY}, reorg_pool::{ReorgPoolError, ReorgPoolStorage}, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use serde::{Deserialize, Serialize}; -use std::{sync::Arc, time::Duration}; -use tari_common::configuration::seconds; -use tari_common_types::types::Signature; /// Configuration for the ReorgPool #[derive(Clone, Copy, Deserialize, Serialize)] @@ -113,15 +116,18 @@ impl ReorgPool { #[cfg(test)] mod test { - use super::*; + use std::{thread, time::Duration}; + + use tari_common::configuration::Network; + use crate::{ consensus::ConsensusManagerBuilder, test_helpers::create_orphan_block, transactions::tari_amount::MicroTari, tx, }; - use std::{thread, time::Duration}; - use tari_common::configuration::Network; + + use super::*; #[test] fn test_insert_rlu_and_ttl() { diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs index 4acbe7f4ba..db358cc9f5 100644 --- a/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs @@ -20,13 +20,20 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{blocks::Block, mempool::reorg_pool::reorg_pool::ReorgPoolConfig, transactions::transaction::Transaction}; -use log::*; use std::sync::Arc; -use tari_common_types::types::Signature; + +use log::*; use tari_crypto::tari_utilities::hex::Hex; use ttl_cache::TtlCache; +use tari_common_types::types::Signature; + +use crate::{ + blocks::Block, + mempool::reorg_pool::reorg_pool::ReorgPoolConfig, + transactions::transaction_entities::transaction::Transaction, +}; + pub const LOG_TARGET: &str = "c::mp::reorg_pool::reorg_pool_storage"; /// Reorg makes use of ReorgPoolStorage to provide thread save access to its TtlCache. diff --git a/base_layer/core/src/mempool/rpc/service.rs b/base_layer/core/src/mempool/rpc/service.rs index 1444107e07..cbf25353f1 100644 --- a/base_layer/core/src/mempool/rpc/service.rs +++ b/base_layer/core/src/mempool/rpc/service.rs @@ -20,14 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::convert::{TryFrom, TryInto}; + +use log::*; + +use tari_comms::protocol::rpc::{Request, Response, RpcStatus}; + use crate::{ mempool::{rpc::MempoolService, service::MempoolHandle}, proto, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use log::*; -use std::convert::{TryFrom, TryInto}; -use tari_comms::protocol::rpc::{Request, Response, RpcStatus}; const LOG_TARGET: &str = "c::mempool::rpc"; diff --git a/base_layer/core/src/mempool/service/handle.rs b/base_layer/core/src/mempool/service/handle.rs index 6eebf2b958..0534d2b4c9 100644 --- a/base_layer/core/src/mempool/service/handle.rs +++ b/base_layer/core/src/mempool/service/handle.rs @@ -20,6 +20,9 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use tari_common_types::types::Signature; +use tari_service_framework::{reply_channel::TrySenderService, Service}; + use crate::{ mempool::{ service::{MempoolRequest, MempoolResponse}, @@ -28,10 +31,8 @@ use crate::{ StatsResponse, TxStorageResponse, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use tari_common_types::types::Signature; -use tari_service_framework::{reply_channel::TrySenderService, Service}; #[derive(Clone)] pub struct MempoolHandle { diff --git a/base_layer/core/src/mempool/service/inbound_handlers.rs b/base_layer/core/src/mempool/service/inbound_handlers.rs index 7f3f90c44a..5e438cc3b2 100644 --- a/base_layer/core/src/mempool/service/inbound_handlers.rs +++ b/base_layer/core/src/mempool/service/inbound_handlers.rs @@ -20,6 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use log::*; +use tari_crypto::tari_utilities::hex::Hex; +use tokio::sync::broadcast; + +use tari_comms::peer_manager::NodeId; + use crate::{ base_node::comms_interface::BlockEvent, chain_storage::BlockAddResult, @@ -30,13 +38,8 @@ use crate::{ MempoolStateEvent, TxStorageResponse, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use log::*; -use std::sync::Arc; -use tari_comms::peer_manager::NodeId; -use tari_crypto::tari_utilities::hex::Hex; -use tokio::sync::broadcast; pub const LOG_TARGET: &str = "c::mp::service::inbound_handlers"; diff --git a/base_layer/core/src/mempool/service/initializer.rs b/base_layer/core/src/mempool/service/initializer.rs index a295daf96a..2f237ffe28 100644 --- a/base_layer/core/src/mempool/service/initializer.rs +++ b/base_layer/core/src/mempool/service/initializer.rs @@ -20,26 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - base_node::{comms_interface::LocalNodeCommsInterface, StateMachineHandle}, - mempool::{ - mempool::Mempool, - proto as mempool_proto, - service::{ - inbound_handlers::MempoolInboundHandlers, - local_service::LocalMempoolService, - outbound_interface::OutboundMempoolServiceInterface, - service::{MempoolService, MempoolStreams}, - MempoolHandle, - }, - MempoolServiceConfig, - }, - proto, - transactions::transaction::Transaction, -}; +use std::{convert::TryFrom, sync::Arc}; + use futures::{Stream, StreamExt}; use log::*; -use std::{convert::TryFrom, sync::Arc}; +use tokio::sync::{broadcast, mpsc}; + use tari_comms_dht::Dht; use tari_p2p::{ comms_connector::{PeerMessage, SubscriptionFactory}, @@ -54,7 +40,24 @@ use tari_service_framework::{ ServiceInitializer, ServiceInitializerContext, }; -use tokio::sync::{broadcast, mpsc}; + +use crate::{ + base_node::{comms_interface::LocalNodeCommsInterface, StateMachineHandle}, + mempool::{ + mempool::Mempool, + proto as mempool_proto, + service::{ + inbound_handlers::MempoolInboundHandlers, + local_service::LocalMempoolService, + outbound_interface::OutboundMempoolServiceInterface, + service::{MempoolService, MempoolStreams}, + MempoolHandle, + }, + MempoolServiceConfig, + }, + proto, + transactions::transaction_entities::transaction::Transaction, +}; const LOG_TARGET: &str = "c::bn::mempool_service::initializer"; const SUBSCRIPTION_LABEL: &str = "Mempool"; diff --git a/base_layer/core/src/mempool/service/local_service.rs b/base_layer/core/src/mempool/service/local_service.rs index 05f3ac6779..76979a4899 100644 --- a/base_layer/core/src/mempool/service/local_service.rs +++ b/base_layer/core/src/mempool/service/local_service.rs @@ -20,6 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use tokio::sync::broadcast; + +use tari_common_types::types::Signature; +use tari_service_framework::{reply_channel::SenderService, Service}; + use crate::{ mempool::{ service::{MempoolRequest, MempoolResponse, MempoolServiceError}, @@ -28,11 +33,8 @@ use crate::{ StatsResponse, TxStorageResponse, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use tari_common_types::types::Signature; -use tari_service_framework::{reply_channel::SenderService, Service}; -use tokio::sync::broadcast; pub type LocalMempoolRequester = SenderService>; @@ -116,14 +118,16 @@ impl LocalMempoolService { #[cfg(test)] mod test { + use futures::StreamExt; + use tokio::{sync::broadcast, task}; + + use tari_service_framework::reply_channel::{unbounded, Receiver}; + use crate::mempool::{ service::{local_service::LocalMempoolService, MempoolRequest, MempoolResponse}, MempoolServiceError, StatsResponse, }; - use futures::StreamExt; - use tari_service_framework::reply_channel::{unbounded, Receiver}; - use tokio::{sync::broadcast, task}; pub type LocalMempoolRequestStream = Receiver>; diff --git a/base_layer/core/src/mempool/service/outbound_interface.rs b/base_layer/core/src/mempool/service/outbound_interface.rs index 8606a8ce02..45437d35fb 100644 --- a/base_layer/core/src/mempool/service/outbound_interface.rs +++ b/base_layer/core/src/mempool/service/outbound_interface.rs @@ -20,19 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use log::*; +use tokio::sync::mpsc::UnboundedSender; + +use tari_common_types::types::Signature; +use tari_comms::peer_manager::NodeId; +use tari_service_framework::{reply_channel::SenderService, Service}; + use crate::{ mempool::{ service::{MempoolRequest, MempoolResponse, MempoolServiceError}, StatsResponse, TxStorageResponse, }, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, }; -use log::*; -use tari_common_types::types::Signature; -use tari_comms::peer_manager::NodeId; -use tari_service_framework::{reply_channel::SenderService, Service}; -use tokio::sync::mpsc::UnboundedSender; pub const LOG_TARGET: &str = "c::mp::service::outbound_interface"; diff --git a/base_layer/core/src/mempool/service/request.rs b/base_layer/core/src/mempool/service/request.rs index a6d6910024..30296c2a01 100644 --- a/base_layer/core/src/mempool/service/request.rs +++ b/base_layer/core/src/mempool/service/request.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::transactions::transaction::Transaction; +use crate::transactions::transaction_entities::transaction::Transaction; use core::fmt::{Display, Error, Formatter}; use serde::{Deserialize, Serialize}; use tari_common_types::{types::Signature, waiting_requests::RequestKey}; diff --git a/base_layer/core/src/mempool/service/service.rs b/base_layer/core/src/mempool/service/service.rs index 137dd9d0a0..28f65fcdda 100644 --- a/base_layer/core/src/mempool/service/service.rs +++ b/base_layer/core/src/mempool/service/service.rs @@ -20,6 +20,27 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::{convert::TryInto, sync::Arc, time::Duration}; + +use futures::{pin_mut, stream::StreamExt, Stream}; +use log::*; +use rand::rngs::OsRng; +use tari_crypto::tari_utilities::hex::Hex; +use tokio::{ + sync::{mpsc, oneshot::Sender as OneshotSender}, + task, +}; + +use tari_common_types::waiting_requests::{generate_request_key, RequestKey, WaitingRequests}; +use tari_comms::peer_manager::NodeId; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + envelope::NodeDestination, + outbound::{DhtOutboundError, OutboundEncryption, OutboundMessageRequester}, +}; +use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; +use tari_service_framework::{reply_channel, reply_channel::RequestContext}; + use crate::{ base_node::{ comms_interface::{BlockEvent, BlockEventReceiver}, @@ -36,25 +57,7 @@ use crate::{ MempoolServiceConfig, }, proto, - transactions::transaction::Transaction, -}; -use futures::{pin_mut, stream::StreamExt, Stream}; -use log::*; -use rand::rngs::OsRng; -use std::{convert::TryInto, sync::Arc, time::Duration}; -use tari_common_types::waiting_requests::{generate_request_key, RequestKey, WaitingRequests}; -use tari_comms::peer_manager::NodeId; -use tari_comms_dht::{ - domain_message::OutboundDomainMessage, - envelope::NodeDestination, - outbound::{DhtOutboundError, OutboundEncryption, OutboundMessageRequester}, -}; -use tari_crypto::tari_utilities::hex::Hex; -use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; -use tari_service_framework::{reply_channel, reply_channel::RequestContext}; -use tokio::{ - sync::{mpsc, oneshot::Sender as OneshotSender}, - task, + transactions::transaction_entities::transaction::Transaction, }; const LOG_TARGET: &str = "c::mempool::service::service"; diff --git a/base_layer/core/src/mempool/sync_protocol/mod.rs b/base_layer/core/src/mempool/sync_protocol/mod.rs index dc133f744d..c5d3bd4e3a 100644 --- a/base_layer/core/src/mempool/sync_protocol/mod.rs +++ b/base_layer/core/src/mempool/sync_protocol/mod.rs @@ -63,23 +63,6 @@ //! | END | //! ``` -#[cfg(test)] -mod test; - -mod error; -use error::MempoolProtocolError; - -mod initializer; -pub use initializer::MempoolSyncInitializer; - -use crate::{ - mempool::{async_mempool, proto, Mempool, MempoolServiceConfig}, - proto as shared_proto, - transactions::transaction::Transaction, -}; -use futures::{stream, SinkExt, Stream, StreamExt}; -use log::*; -use prost::Message; use std::{ convert::TryFrom, iter, @@ -88,6 +71,19 @@ use std::{ Arc, }, }; + +use futures::{stream, SinkExt, Stream, StreamExt}; +use log::*; +use prost::Message; +use tari_crypto::tari_utilities::{hex::Hex, ByteArray}; +use tokio::{ + io::{AsyncRead, AsyncWrite}, + sync::Semaphore, + task, +}; + +use error::MempoolProtocolError; +pub use initializer::MempoolSyncInitializer; use tari_comms::{ connectivity::{ConnectivityEvent, ConnectivityEventRx}, framing, @@ -98,13 +94,19 @@ use tari_comms::{ Bytes, PeerConnection, }; -use tari_crypto::tari_utilities::{hex::Hex, ByteArray}; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - sync::Semaphore, - task, + +use crate::{ + mempool::{async_mempool, proto, Mempool, MempoolServiceConfig}, + proto as shared_proto, + transactions::transaction_entities::transaction::Transaction, }; +#[cfg(test)] +mod test; + +mod error; +mod initializer; + const MAX_FRAME_SIZE: usize = 3 * 1024 * 1024; // 3 MiB const LOG_TARGET: &str = "c::mempool::sync_protocol"; diff --git a/base_layer/core/src/mempool/sync_protocol/test.rs b/base_layer/core/src/mempool/sync_protocol/test.rs index 8f094e635f..031e1cd0a9 100644 --- a/base_layer/core/src/mempool/sync_protocol/test.rs +++ b/base_layer/core/src/mempool/sync_protocol/test.rs @@ -20,19 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - consensus::ConsensusManager, - mempool::{ - async_mempool, - proto, - sync_protocol::{MempoolPeerProtocol, MempoolSyncProtocol, MAX_FRAME_SIZE, MEMPOOL_SYNC_PROTOCOL}, - Mempool, - }, - transactions::{tari_amount::uT, test_helpers::create_tx, transaction::Transaction}, - validation::mocks::MockValidator, -}; -use futures::{Sink, SinkExt, Stream, StreamExt}; use std::{fmt, io, iter::repeat_with, sync::Arc}; + +use futures::{Sink, SinkExt, Stream, StreamExt}; +use tari_crypto::tari_utilities::ByteArray; +use tokio::{ + sync::{broadcast, mpsc}, + task, +}; + use tari_common::configuration::Network; use tari_comms::{ connectivity::{ConnectivityEvent, ConnectivityEventTx}, @@ -45,10 +41,17 @@ use tari_comms::{ Bytes, BytesMut, }; -use tari_crypto::tari_utilities::ByteArray; -use tokio::{ - sync::{broadcast, mpsc}, - task, + +use crate::{ + consensus::ConsensusManager, + mempool::{ + async_mempool, + proto, + sync_protocol::{MempoolPeerProtocol, MempoolSyncProtocol, MAX_FRAME_SIZE, MEMPOOL_SYNC_PROTOCOL}, + Mempool, + }, + transactions::{tari_amount::uT, test_helpers::create_tx, transaction_entities::transaction::Transaction}, + validation::mocks::MockValidator, }; pub fn create_transactions(n: usize) -> Vec { diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index 5e979b296b..bdf79e3bb8 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -27,7 +27,7 @@ use crate::{ priority::{FeePriority, PrioritizedTransaction}, unconfirmed_pool::UnconfirmedPoolError, }, - transactions::{transaction::Transaction, weight::TransactionWeight}, + transactions::{transaction_entities::Transaction, weight::TransactionWeight}, }; use log::*; use serde::{Deserialize, Serialize}; @@ -487,7 +487,7 @@ mod test { fee::Fee, tari_amount::MicroTari, test_helpers::{TestParams, UtxoTestParams}, - transaction::KernelFeatures, + transaction_entities::KernelFeatures, weight::TransactionWeight, CryptoFactories, SenderTransactionProtocol, diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 93bbfbd46c..4ee0f6e7c1 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -22,29 +22,32 @@ //! Impls for transaction proto +use std::convert::{TryFrom, TryInto}; + +use tari_crypto::{ + script::{ExecutionStack, TariScript}, + tari_utilities::{ByteArray, ByteArrayError}, +}; + +use tari_common_types::types::{BlindingFactor, BulletRangeProof, Commitment, PublicKey}; + use crate::{ proto, tari_utilities::convert::try_convert_all, transactions::{ aggregated_body::AggregateBody, tari_amount::MicroTari, - transaction::{ + transaction_entities::{ + output_features::OutputFeatures, + transaction::Transaction, + transaction_input::TransactionInput, + transaction_kernel::TransactionKernel, + transaction_output::TransactionOutput, KernelFeatures, - OutputFeatures, OutputFlags, - Transaction, - TransactionInput, - TransactionKernel, - TransactionOutput, }, }, }; -use std::convert::{TryFrom, TryInto}; -use tari_common_types::types::{BlindingFactor, BulletRangeProof, Commitment, PublicKey}; -use tari_crypto::{ - script::{ExecutionStack, TariScript}, - tari_utilities::{ByteArray, ByteArrayError}, -}; //---------------------------------- TransactionKernel --------------------------------------------// diff --git a/base_layer/core/src/test_helpers/block_spec.rs b/base_layer/core/src/test_helpers/block_spec.rs index c1f8fa19ae..2cd7ae721f 100644 --- a/base_layer/core/src/test_helpers/block_spec.rs +++ b/base_layer/core/src/test_helpers/block_spec.rs @@ -22,7 +22,7 @@ use crate::{ proof_of_work::Difficulty, - transactions::{tari_amount::MicroTari, transaction::Transaction}, + transactions::{tari_amount::MicroTari, transaction_entities::transaction::Transaction}, }; pub struct BlockSpecs { diff --git a/base_layer/core/src/test_helpers/blockchain.rs b/base_layer/core/src/test_helpers/blockchain.rs index 66afe677c9..462c972621 100644 --- a/base_layer/core/src/test_helpers/blockchain.rs +++ b/base_layer/core/src/test_helpers/blockchain.rs @@ -20,7 +20,24 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::{create_block, mine_to_difficulty}; +use std::{ + collections::HashMap, + fs, + ops::Deref, + path::{Path, PathBuf}, + sync::Arc, +}; + +use croaring::Bitmap; + +use tari_common::configuration::Network; +use tari_common_types::{ + chain_metadata::ChainMetadata, + types::{Commitment, HashOutput, Signature}, +}; +use tari_storage::lmdb_store::LMDBConfig; +use tari_test_utils::paths::create_temporary_data_path; + use crate::{ blocks::{ genesis_block::get_weatherwax_genesis_block, @@ -55,7 +72,11 @@ use crate::{ proof_of_work::{AchievedTargetDifficulty, Difficulty, PowAlgorithm}, test_helpers::{block_spec::BlockSpecs, create_consensus_rules, BlockSpec}, transactions::{ - transaction::{TransactionInput, TransactionKernel, UnblindedOutput}, + transaction_entities::{ + transaction_input::TransactionInput, + transaction_kernel::TransactionKernel, + unblinded_output::UnblindedOutput, + }, CryptoFactories, }, validation::{ @@ -64,21 +85,8 @@ use crate::{ DifficultyCalculator, }, }; -use croaring::Bitmap; -use std::{ - collections::HashMap, - fs, - ops::Deref, - path::{Path, PathBuf}, - sync::Arc, -}; -use tari_common::configuration::Network; -use tari_common_types::{ - chain_metadata::ChainMetadata, - types::{Commitment, HashOutput, Signature}, -}; -use tari_storage::lmdb_store::LMDBConfig; -use tari_test_utils::paths::create_temporary_data_path; + +use super::{create_block, mine_to_difficulty}; /// Create a new blockchain database containing no blocks. pub fn create_new_blockchain() -> BlockchainDatabase { diff --git a/base_layer/core/src/test_helpers/mod.rs b/base_layer/core/src/test_helpers/mod.rs index 9523fe0a3e..93653f7e6f 100644 --- a/base_layer/core/src/test_helpers/mod.rs +++ b/base_layer/core/src/test_helpers/mod.rs @@ -23,11 +23,14 @@ //! Common test helper functions that are small and useful enough to be included in the main crate, rather than the //! integration test folder. -#[macro_use] -mod block_spec; -pub use block_spec::{BlockSpec, BlockSpecs}; +use std::{iter, path::Path, sync::Arc}; -pub mod blockchain; +use rand::{distributions::Alphanumeric, Rng}; + +pub use block_spec::{BlockSpec, BlockSpecs}; +use tari_common::configuration::Network; +use tari_comms::PeerManager; +use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; use crate::{ blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainHeader}, @@ -35,16 +38,15 @@ use crate::{ crypto::tari_utilities::Hashable, proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ - transaction::{Transaction, UnblindedOutput}, + transaction_entities::{transaction::Transaction, unblinded_output::UnblindedOutput}, CoinbaseBuilder, CryptoFactories, }, }; -use rand::{distributions::Alphanumeric, Rng}; -use std::{iter, path::Path, sync::Arc}; -use tari_common::configuration::Network; -use tari_comms::PeerManager; -use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; + +#[macro_use] +mod block_spec; +pub mod blockchain; pub fn create_consensus_rules() -> ConsensusManager { ConsensusManager::builder(Network::LocalNet).build() diff --git a/base_layer/core/src/transactions/aggregated_body.rs b/base_layer/core/src/transactions/aggregated_body.rs index f32fa6d4b8..1592115d7b 100644 --- a/base_layer/core/src/transactions/aggregated_body.rs +++ b/base_layer/core/src/transactions/aggregated_body.rs @@ -24,7 +24,7 @@ use crate::{ transactions::{ crypto_factories::CryptoFactories, tari_amount::MicroTari, - transaction::{ + transaction_entities::{ KernelFeatures, KernelSum, OutputFlags, diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index c34153c2bd..e6e3450465 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -39,7 +39,7 @@ use crate::{ transactions::{ crypto_factories::CryptoFactories, tari_amount::{uT, MicroTari}, - transaction::{ + transaction_entities::{ KernelBuilder, KernelFeatures, OutputFeatures, @@ -251,7 +251,7 @@ mod test { crypto_factories::CryptoFactories, tari_amount::uT, test_helpers::TestParams, - transaction::{KernelFeatures, OutputFeatures, OutputFlags, TransactionError}, + transaction_entities::{KernelFeatures, OutputFeatures, OutputFlags, TransactionError}, transaction_protocol::RewindData, CoinbaseBuilder, }, diff --git a/base_layer/core/src/transactions/mod.rs b/base_layer/core/src/transactions/mod.rs index ee63b708c2..474306cbd1 100644 --- a/base_layer/core/src/transactions/mod.rs +++ b/base_layer/core/src/transactions/mod.rs @@ -8,7 +8,7 @@ pub use coinbase_builder::{CoinbaseBuildError, CoinbaseBuilder}; pub mod fee; pub mod tari_amount; -pub mod transaction; +pub mod transaction_entities; mod format_currency; pub use format_currency::format_currency; diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index 7a8c9dbf76..9037366d4d 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -26,7 +26,7 @@ use crate::{ crypto_factories::CryptoFactories, fee::Fee, tari_amount::MicroTari, - transaction::{ + transaction_entities::{ KernelBuilder, KernelFeatures, OutputFeatures, @@ -305,7 +305,7 @@ macro_rules! txn_schema { to:$outputs, fee:$fee, lock:0, - features: $crate::transactions::transaction::OutputFeatures::default() + features: $crate::transactions::transaction_entities::OutputFeatures::default() ) }; diff --git a/base_layer/core/src/transactions/transaction.rs b/base_layer/core/src/transactions/transaction.rs deleted file mode 100644 index c399e6473c..0000000000 --- a/base_layer/core/src/transactions/transaction.rs +++ /dev/null @@ -1,1870 +0,0 @@ -// Copyright 2018 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use tari_common_types::types::HashOutput; -use tari_crypto::script::ScriptContext; - -use crate::{ - consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized, ConsensusEncodingWrapper}, - transactions::{ - aggregated_body::AggregateBody, - crypto_factories::CryptoFactories, - tari_amount::{uT, MicroTari}, - transaction_protocol::{build_challenge, RewindData, TransactionMetadata}, - weight::TransactionWeight, - }, -}; -use blake2::Digest; -use integer_encoding::{VarInt, VarIntReader, VarIntWriter}; -use rand::rngs::OsRng; -use serde::{Deserialize, Serialize}; -use std::{ - cmp::{max, min, Ordering}, - fmt, - fmt::{Display, Formatter}, - hash::{Hash, Hasher}, - io, - io::{Read, Write}, - ops::{Add, Shl}, -}; -use tari_common_types::types::{ - BlindingFactor, - Challenge, - ComSignature, - Commitment, - CommitmentFactory, - HashDigest, - MessageHash, - PrivateKey, - PublicKey, - RangeProof, - RangeProofService, - Signature, -}; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PublicKeyTrait, SecretKey}, - range_proof::{ - FullRewindResult as CryptoFullRewindResult, - RangeProofError, - RangeProofService as RangeProofServiceTrait, - RewindResult as CryptoRewindResult, - REWIND_USER_MESSAGE_LENGTH, - }, - ristretto::pedersen::PedersenCommitmentFactory, - script::{ExecutionStack, ScriptError, StackItem, TariScript}, - signatures::CommitmentSignatureError, - tari_utilities::{hex::Hex, message_format::MessageFormat, ByteArray, Hashable}, -}; -use thiserror::Error; - -// Tx_weight(inputs(12,500), outputs(500), kernels(1)) = 126,510 still well enough below block weight of 127,795 -pub const MAX_TRANSACTION_INPUTS: usize = 12_500; -pub const MAX_TRANSACTION_OUTPUTS: usize = 500; -pub const MAX_TRANSACTION_RECIPIENTS: usize = 15; - -//-------------------------------------- Output features --------------------------------------------------// - -bitflags! { - /// Options for a kernel's structure or use. - /// TODO: expand to accommodate Tari DAN transaction types, such as namespace and validator node registrations - #[derive(Deserialize, Serialize)] - pub struct KernelFeatures: u8 { - /// Coinbase transaction - const COINBASE_KERNEL = 1u8; - } -} - -impl KernelFeatures { - pub fn create_coinbase() -> KernelFeatures { - KernelFeatures::COINBASE_KERNEL - } -} - -/// Options for UTXO's -#[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] -pub struct OutputFeatures { - /// Flags are the feature flags that differentiate between outputs, eg Coinbase all of which has different rules - pub flags: OutputFlags, - /// the maturity of the specific UTXO. This is the min lock height at which an UTXO can be spent. Coinbase UTXO - /// require a min maturity of the Coinbase_lock_height, this should be checked on receiving new blocks. - pub maturity: u64, -} - -impl OutputFeatures { - /// The version number to use in consensus encoding. In future, this value could be dynamic. - const CONSENSUS_ENCODING_VERSION: u8 = 0; - - /// Encodes output features using deprecated bincode encoding - pub fn to_v1_bytes(&self) -> Vec { - // unreachable panic: serialized_size is infallible because it uses DefaultOptions - let encode_size = bincode::serialized_size(self).expect("unreachable"); - let mut buf = Vec::with_capacity(encode_size as usize); - // unreachable panic: Vec's Write impl is infallible - bincode::serialize_into(&mut buf, self).expect("unreachable"); - buf - } - - /// Encodes output features using consensus encoding - pub fn to_consensus_bytes(&self) -> Vec { - let mut buf = Vec::with_capacity(self.consensus_encode_exact_size()); - // unreachable panic: Vec's Write impl is infallible - self.consensus_encode(&mut buf).expect("unreachable"); - buf - } - - pub fn create_coinbase(maturity_height: u64) -> OutputFeatures { - OutputFeatures { - flags: OutputFlags::COINBASE_OUTPUT, - maturity: maturity_height, - } - } - - /// Create an `OutputFeatures` with the given maturity and all other values at their default setting - pub fn with_maturity(maturity: u64) -> OutputFeatures { - OutputFeatures { - maturity, - ..OutputFeatures::default() - } - } -} - -impl ConsensusEncoding for OutputFeatures { - fn consensus_encode(&self, writer: &mut W) -> Result { - let mut written = writer.write_varint(Self::CONSENSUS_ENCODING_VERSION)?; - written += writer.write_varint(self.maturity)?; - written += self.flags.consensus_encode(writer)?; - Ok(written) - } -} -impl ConsensusEncodingSized for OutputFeatures { - fn consensus_encode_exact_size(&self) -> usize { - Self::CONSENSUS_ENCODING_VERSION.required_space() + - self.flags.consensus_encode_exact_size() + - self.maturity.required_space() - } -} - -impl ConsensusDecoding for OutputFeatures { - fn consensus_decode(reader: &mut R) -> Result { - // Changing the order of these operations is consensus breaking - let version = reader.read_varint::()?; - if version != Self::CONSENSUS_ENCODING_VERSION { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "Invalid version. Expected {} but got {}", - Self::CONSENSUS_ENCODING_VERSION, - version - ), - )); - } - // Decode safety: read_varint will stop reading the varint after 10 bytes - let maturity = reader.read_varint()?; - let flags = OutputFlags::consensus_decode(reader)?; - Ok(Self { flags, maturity }) - } -} - -impl Default for OutputFeatures { - fn default() -> Self { - OutputFeatures { - flags: OutputFlags::empty(), - maturity: 0, - } - } -} - -impl PartialOrd for OutputFeatures { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for OutputFeatures { - fn cmp(&self, other: &Self) -> Ordering { - self.maturity.cmp(&other.maturity) - } -} - -impl Display for OutputFeatures { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "OutputFeatures: Flags = {:?}, Maturity = {}", - self.flags, self.maturity - ) - } -} - -bitflags! { - #[derive(Deserialize, Serialize)] - pub struct OutputFlags: u8 { - /// Output is a coinbase output, must not be spent until maturity - const COINBASE_OUTPUT = 0b0000_0001; - } -} - -impl ConsensusEncoding for OutputFlags { - fn consensus_encode(&self, writer: &mut W) -> Result { - writer.write(&self.bits.to_le_bytes()) - } -} - -impl ConsensusEncodingSized for OutputFlags { - fn consensus_encode_exact_size(&self) -> usize { - 1 - } -} - -impl ConsensusDecoding for OutputFlags { - fn consensus_decode(reader: &mut R) -> Result { - let mut buf = [0u8; 1]; - reader.read_exact(&mut buf)?; - // SAFETY: we have 3 options here: - // 1. error if unsupported flags are used, meaning that every new flag will be a hard fork - // 2. truncate unsupported flags, means different hashes will be produced for the same block - // 3. ignore unsupported flags, which could be set at any time and persisted to the blockchain. - // Once those flags are defined at some point in the future, depending on the functionality of the flag, - // a consensus rule may be needed that ignores flags prior to a given block height. - // Option 3 is used here - Ok(unsafe { OutputFlags::from_bits_unchecked(u8::from_le_bytes(buf)) }) - } -} - -//---------------------------------------- TransactionError ----------------------------------------------------// - -#[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] -pub enum TransactionError { - #[error("Error validating the transaction: {0}")] - ValidationError(String), - #[error("Signature is invalid: {0}")] - InvalidSignatureError(String), - #[error("Transaction kernel does not contain a signature")] - NoSignatureError, - #[error("A range proof construction or verification has produced an error: {0}")] - RangeProofError(#[from] RangeProofError), - #[error("An error occurred while performing a commitment signature: {0}")] - SigningError(#[from] CommitmentSignatureError), - #[error("Invalid kernel in body")] - InvalidKernel, - #[error("Invalid coinbase in body")] - InvalidCoinbase, - #[error("Invalid coinbase maturity in body")] - InvalidCoinbaseMaturity, - #[error("More than one coinbase in body")] - MoreThanOneCoinbase, - #[error("No coinbase in body")] - NoCoinbase, - #[error("Input maturity not reached")] - InputMaturity, - #[error("Tari script error : {0}")] - ScriptError(#[from] ScriptError), - #[error("Error performing conversion: {0}")] - ConversionError(String), - #[error("The script offset in body does not balance")] - ScriptOffset, - #[error("Error executing script: {0}")] - ScriptExecutionError(String), -} - -//----------------------------------------- UnblindedOutput ----------------------------------------------------// - -/// An unblinded output is one where the value and spending key (blinding factor) are known. This can be used to -/// build both inputs and outputs (every input comes from an output) -// TODO: Try to get rid of 'Serialize' and 'Deserialize' traits here; see related comment at 'struct RawTransactionInfo' -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UnblindedOutput { - pub value: MicroTari, - pub spending_key: BlindingFactor, - pub features: OutputFeatures, - pub script: TariScript, - pub input_data: ExecutionStack, - pub script_private_key: PrivateKey, - pub sender_offset_public_key: PublicKey, - pub metadata_signature: ComSignature, - pub script_lock_height: u64, -} - -impl UnblindedOutput { - /// Creates a new un-blinded output - #[allow(clippy::too_many_arguments)] - pub fn new( - value: MicroTari, - spending_key: BlindingFactor, - features: OutputFeatures, - script: TariScript, - input_data: ExecutionStack, - script_private_key: PrivateKey, - sender_offset_public_key: PublicKey, - metadata_signature: ComSignature, - script_lock_height: u64, - ) -> UnblindedOutput { - UnblindedOutput { - value, - spending_key, - features, - script, - input_data, - script_private_key, - sender_offset_public_key, - metadata_signature, - script_lock_height, - } - } - - /// Commits an UnblindedOutput into a Transaction input - pub fn as_transaction_input(&self, factory: &CommitmentFactory) -> Result { - let commitment = factory.commit(&self.spending_key, &self.value.into()); - let script_nonce_a = PrivateKey::random(&mut OsRng); - let script_nonce_b = PrivateKey::random(&mut OsRng); - let nonce_commitment = factory.commit(&script_nonce_b, &script_nonce_a); - - let challenge = TransactionInput::build_script_challenge( - &nonce_commitment, - &self.script, - &self.input_data, - &PublicKey::from_secret_key(&self.script_private_key), - &commitment, - ); - let script_signature = ComSignature::sign( - self.value.into(), - &self.script_private_key + &self.spending_key, - script_nonce_a, - script_nonce_b, - &challenge, - factory, - ) - .map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?; - - Ok(TransactionInput { - features: self.features.clone(), - commitment, - script: self.script.clone(), - input_data: self.input_data.clone(), - script_signature, - sender_offset_public_key: self.sender_offset_public_key.clone(), - }) - } - - pub fn as_transaction_output(&self, factories: &CryptoFactories) -> Result { - if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) - { - return Err(TransactionError::ValidationError( - "Value provided is outside the range allowed by the range proof".into(), - )); - } - let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); - let output = TransactionOutput { - features: self.features.clone(), - commitment, - proof: RangeProof::from_bytes( - &factories - .range_proof - .construct_proof(&self.spending_key, self.value.into())?, - ) - .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?, - script: self.script.clone(), - sender_offset_public_key: self.sender_offset_public_key.clone(), - metadata_signature: self.metadata_signature.clone(), - }; - - Ok(output) - } - - pub fn as_rewindable_transaction_output( - &self, - factories: &CryptoFactories, - rewind_data: &RewindData, - ) -> Result { - if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) - { - return Err(TransactionError::ValidationError( - "Value provided is outside the range allowed by the range proof".into(), - )); - } - let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); - - let proof_bytes = factories.range_proof.construct_proof_with_rewind_key( - &self.spending_key, - self.value.into(), - &rewind_data.rewind_key, - &rewind_data.rewind_blinding_key, - &rewind_data.proof_message, - )?; - - let proof = RangeProof::from_bytes(&proof_bytes) - .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?; - - let output = TransactionOutput { - features: self.features.clone(), - commitment, - proof, - script: self.script.clone(), - sender_offset_public_key: self.sender_offset_public_key.clone(), - metadata_signature: self.metadata_signature.clone(), - }; - - Ok(output) - } - - pub fn metadata_byte_size(&self) -> usize { - self.features.consensus_encode_exact_size() + - ConsensusEncodingWrapper::wrap(&self.script).consensus_encode_exact_size() - } -} - -// These implementations are used for order these outputs for UTXO selection which will be done by comparing the values -impl Eq for UnblindedOutput {} - -impl PartialEq for UnblindedOutput { - fn eq(&self, other: &UnblindedOutput) -> bool { - self.value == other.value - } -} - -impl Hash for UnblindedOutput { - fn hash(&self, state: &mut H) { - self.value.hash(state); - } -} - -impl PartialOrd for UnblindedOutput { - fn partial_cmp(&self, other: &Self) -> Option { - self.value.partial_cmp(&other.value) - } -} - -impl Ord for UnblindedOutput { - fn cmp(&self, other: &Self) -> Ordering { - self.value.cmp(&other.value) - } -} - -//---------------------------------------- TransactionInput ----------------------------------------------------// - -/// A transaction input. -/// -/// Primarily a reference to an output being spent by the transaction. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TransactionInput { - /// The features of the output being spent. We will check maturity for all outputs. - pub features: OutputFeatures, - /// The commitment referencing the output being spent. - pub commitment: Commitment, - /// The serialised script - pub script: TariScript, - /// The script input data, if any - pub input_data: ExecutionStack, - /// A signature with k_s, signing the script, input data, and mined height - pub script_signature: ComSignature, - /// The offset public key, K_O - pub sender_offset_public_key: PublicKey, -} - -/// An input for a transaction that spends an existing output -impl TransactionInput { - /// Create a new Transaction Input - pub fn new( - features: OutputFeatures, - commitment: Commitment, - script: TariScript, - input_data: ExecutionStack, - script_signature: ComSignature, - sender_offset_public_key: PublicKey, - ) -> TransactionInput { - TransactionInput { - features, - commitment, - script, - input_data, - script_signature, - sender_offset_public_key, - } - } - - pub fn build_script_challenge( - nonce_commitment: &Commitment, - script: &TariScript, - input_data: &ExecutionStack, - script_public_key: &PublicKey, - commitment: &Commitment, - ) -> Vec { - Challenge::new() - .chain(nonce_commitment.as_bytes()) - .chain(script.as_bytes().as_slice()) - .chain(input_data.as_bytes().as_slice()) - .chain(script_public_key.as_bytes()) - .chain(commitment.as_bytes()) - .finalize() - .to_vec() - } - - /// Accessor method for the commitment contained in an input - pub fn commitment(&self) -> &Commitment { - &self.commitment - } - - /// Checks if the given un-blinded input instance corresponds to this blinded Transaction Input - pub fn opened_by(&self, input: &UnblindedOutput, factory: &CommitmentFactory) -> bool { - factory.open(&input.spending_key, &input.value.into(), &self.commitment) - } - - /// This will check if the input and the output is the same transactional output by looking at the commitment and - /// features and script. This will ignore all other output and input fields - pub fn is_equal_to(&self, output: &TransactionOutput) -> bool { - self.output_hash() == output.hash() - } - - /// This will run the script contained in the TransactionInput, returning either a script error or the resulting - /// public key. - pub fn run_script(&self, context: Option) -> Result { - let context = context.unwrap_or_default(); - match self.script.execute_with_context(&self.input_data, &context)? { - StackItem::PublicKey(pubkey) => Ok(pubkey), - _ => Err(TransactionError::ScriptExecutionError( - "The script executed successfully but it did not leave a public key on the stack".to_string(), - )), - } - } - - pub fn validate_script_signature( - &self, - public_script_key: &PublicKey, - factory: &CommitmentFactory, - ) -> Result<(), TransactionError> { - let challenge = TransactionInput::build_script_challenge( - self.script_signature.public_nonce(), - &self.script, - &self.input_data, - public_script_key, - &self.commitment, - ); - if self - .script_signature - .verify_challenge(&(&self.commitment + public_script_key), &challenge, factory) - { - Ok(()) - } else { - Err(TransactionError::InvalidSignatureError( - "Verifying script signature".to_string(), - )) - } - } - - /// This will run the script and verify the script signature. If its valid, it will return the resulting public key - /// from the script. - pub fn run_and_verify_script( - &self, - factory: &CommitmentFactory, - context: Option, - ) -> Result { - let key = self.run_script(context)?; - self.validate_script_signature(&key, factory)?; - Ok(key) - } - - /// Returns true if this input is mature at the given height, otherwise false - pub fn is_mature_at(&self, block_height: u64) -> bool { - self.features.maturity <= block_height - } - - /// Returns the hash of the output data contained in this input. - /// This hash matches the hash of a transaction output that this input spends. - pub fn output_hash(&self) -> Vec { - HashDigest::new() - .chain(self.features.to_v1_bytes()) - .chain(self.commitment.as_bytes()) - .chain(self.script.as_bytes()) - .finalize() - .to_vec() - } -} - -/// Implement the canonical hashing function for TransactionInput for use in ordering -impl Hashable for TransactionInput { - fn hash(&self) -> Vec { - HashDigest::new() - .chain(self.features.to_v1_bytes()) - .chain(self.commitment.as_bytes()) - .chain(self.script.as_bytes()) - .chain(self.sender_offset_public_key.as_bytes()) - .chain(self.script_signature.u().as_bytes()) - .chain(self.script_signature.v().as_bytes()) - .chain(self.script_signature.public_nonce().as_bytes()) - .chain(self.input_data.as_bytes()) - .finalize() - .to_vec() - } -} - -impl Display for TransactionInput { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - write!( - fmt, - "{} [{:?}], Script hash: ({}), Offset_Pubkey: ({})", - self.commitment.to_hex(), - self.features, - self.script, - self.sender_offset_public_key.to_hex() - ) - } -} - -impl PartialOrd for TransactionInput { - fn partial_cmp(&self, other: &Self) -> Option { - self.commitment.partial_cmp(&other.commitment) - } -} - -impl Ord for TransactionInput { - fn cmp(&self, other: &Self) -> Ordering { - self.commitment.cmp(&other.commitment) - } -} - -//---------------------------------------- TransactionOutput ----------------------------------------------------// - -/// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a -/// blinded value for the output while the range proof guarantees the commitment includes a positive value without -/// overflow and the ownership of the private key. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TransactionOutput { - /// Options for an output's structure or use - pub features: OutputFeatures, - /// The homomorphic commitment representing the output amount - pub commitment: Commitment, - /// A proof that the commitment is in the right range - pub proof: RangeProof, - /// The script that will be executed when spending this output - pub script: TariScript, - /// Tari script offset pubkey, K_O - pub sender_offset_public_key: PublicKey, - /// UTXO signature with the script offset private key, k_O - pub metadata_signature: ComSignature, -} - -/// An output for a transaction, includes a range proof and Tari script metadata -impl TransactionOutput { - /// Create new Transaction Output - pub fn new( - features: OutputFeatures, - commitment: Commitment, - proof: RangeProof, - script: TariScript, - sender_offset_public_key: PublicKey, - metadata_signature: ComSignature, - ) -> TransactionOutput { - TransactionOutput { - features, - commitment, - proof, - script, - sender_offset_public_key, - metadata_signature, - } - } - - /// Accessor method for the commitment contained in an output - pub fn commitment(&self) -> &Commitment { - &self.commitment - } - - /// Accessor method for the range proof contained in an output - pub fn proof(&self) -> &RangeProof { - &self.proof - } - - /// Verify that range proof is valid - pub fn verify_range_proof(&self, prover: &RangeProofService) -> Result { - Ok(prover.verify(&self.proof.0, &self.commitment)) - } - - /// Verify that the metadata signature is valid - pub fn verify_metadata_signature(&self) -> Result<(), TransactionError> { - let challenge = TransactionOutput::build_metadata_signature_challenge( - &self.script, - &self.features, - &self.sender_offset_public_key, - self.metadata_signature.public_nonce(), - &self.commitment, - ); - if !self.metadata_signature.verify_challenge( - &(&self.commitment + &self.sender_offset_public_key), - &challenge, - &PedersenCommitmentFactory::default(), - ) { - return Err(TransactionError::InvalidSignatureError( - "Metadata signature not valid!".to_string(), - )); - } - Ok(()) - } - - /// Attempt to rewind the range proof to reveal the proof message and committed value - pub fn rewind_range_proof_value_only( - &self, - prover: &RangeProofService, - rewind_public_key: &PublicKey, - rewind_blinding_public_key: &PublicKey, - ) -> Result { - Ok(prover - .rewind_proof_value_only( - &self.proof.0, - &self.commitment, - rewind_public_key, - rewind_blinding_public_key, - )? - .into()) - } - - /// Attempt to fully rewind the range proof to reveal the proof message, committed value and blinding factor - pub fn full_rewind_range_proof( - &self, - prover: &RangeProofService, - rewind_key: &PrivateKey, - rewind_blinding_key: &PrivateKey, - ) -> Result { - Ok(prover - .rewind_proof_commitment_data(&self.proof.0, &self.commitment, rewind_key, rewind_blinding_key)? - .into()) - } - - /// This will check if the input and the output is the same commitment by looking at the commitment and features. - /// This will ignore the output range proof - #[inline] - pub fn is_equal_to(&self, output: &TransactionInput) -> bool { - self.commitment == output.commitment && self.features == output.features - } - - /// Returns true if the output is a coinbase, otherwise false - pub fn is_coinbase(&self) -> bool { - self.features.flags.contains(OutputFlags::COINBASE_OUTPUT) - } - - /// Convenience function that returns the challenge for the metadata commitment signature - pub fn get_metadata_signature_challenge(&self, partial_commitment_nonce: Option<&PublicKey>) -> MessageHash { - let nonce_commitment = match partial_commitment_nonce { - None => self.metadata_signature.public_nonce().clone(), - Some(partial_nonce) => self.metadata_signature.public_nonce() + partial_nonce, - }; - TransactionOutput::build_metadata_signature_challenge( - &self.script, - &self.features, - &self.sender_offset_public_key, - &nonce_commitment, - &self.commitment, - ) - } - - /// Convenience function that calculates the challenge for the metadata commitment signature - pub fn build_metadata_signature_challenge( - script: &TariScript, - features: &OutputFeatures, - sender_offset_public_key: &PublicKey, - public_commitment_nonce: &Commitment, - commitment: &Commitment, - ) -> MessageHash { - Challenge::new() - .chain(public_commitment_nonce.as_bytes()) - .chain(script.as_bytes()) - // TODO: Use consensus encoded bytes #testnet_reset - .chain(features.to_v1_bytes()) - .chain(sender_offset_public_key.as_bytes()) - .chain(commitment.as_bytes()) - .finalize() - .to_vec() - } - - // Create commitment signature for the metadata - fn create_metadata_signature( - value: &MicroTari, - spending_key: &BlindingFactor, - script: &TariScript, - output_features: &OutputFeatures, - sender_offset_public_key: &PublicKey, - partial_commitment_nonce: Option<&PublicKey>, - sender_offset_private_key: Option<&PrivateKey>, - ) -> Result { - let nonce_a = PrivateKey::random(&mut OsRng); - let nonce_b = PrivateKey::random(&mut OsRng); - let nonce_commitment = PedersenCommitmentFactory::default().commit(&nonce_b, &nonce_a); - let nonce_commitment = match partial_commitment_nonce { - None => nonce_commitment, - Some(partial_nonce) => &nonce_commitment + partial_nonce, - }; - let value = PrivateKey::from(value.as_u64()); - let commitment = PedersenCommitmentFactory::default().commit(spending_key, &value); - let e = TransactionOutput::build_metadata_signature_challenge( - script, - output_features, - sender_offset_public_key, - &nonce_commitment, - &commitment, - ); - let secret_x = match sender_offset_private_key { - None => spending_key.clone(), - Some(key) => &spending_key.clone() + key, - }; - Ok(ComSignature::sign( - value, - secret_x, - nonce_a, - nonce_b, - &e, - &PedersenCommitmentFactory::default(), - )?) - } - - /// Create partial commitment signature for the metadata, usually done by the receiver - pub fn create_partial_metadata_signature( - value: &MicroTari, - spending_key: &BlindingFactor, - script: &TariScript, - output_features: &OutputFeatures, - sender_offset_public_key: &PublicKey, - partial_commitment_nonce: &PublicKey, - ) -> Result { - TransactionOutput::create_metadata_signature( - value, - spending_key, - script, - output_features, - sender_offset_public_key, - Some(partial_commitment_nonce), - None, - ) - } - - /// Create final commitment signature for the metadata, signing with both keys - pub fn create_final_metadata_signature( - value: &MicroTari, - spending_key: &BlindingFactor, - script: &TariScript, - output_features: &OutputFeatures, - sender_offset_private_key: &PrivateKey, - ) -> Result { - let sender_offset_public_key = PublicKey::from_secret_key(sender_offset_private_key); - TransactionOutput::create_metadata_signature( - value, - spending_key, - script, - output_features, - &sender_offset_public_key, - None, - Some(sender_offset_private_key), - ) - } - - pub fn witness_hash(&self) -> Vec { - HashDigest::new() - .chain(self.proof.as_bytes()) - .chain(self.metadata_signature.u().as_bytes()) - .chain(self.metadata_signature.v().as_bytes()) - .chain(self.metadata_signature.public_nonce().as_bytes()) - .finalize() - .to_vec() - } -} - -/// Implement the canonical hashing function for TransactionOutput for use in ordering. -/// -/// We can exclude the range proof from this hash. The rationale for this is: -/// a) It is a significant performance boost, since the RP is the biggest part of an output -/// b) Range proofs are committed to elsewhere and so we'd be hashing them twice (and as mentioned, this is slow) -/// c) TransactionInputs will now have the same hash as UTXOs, which makes locating STXOs easier when doing reorgs -impl Hashable for TransactionOutput { - fn hash(&self) -> Vec { - HashDigest::new() - // TODO: use consensus encoding #testnet_reset - .chain(self.features.to_v1_bytes()) - .chain(self.commitment.as_bytes()) - // .chain(range proof) // See docs as to why we exclude this - .chain(self.script.as_bytes()) - .finalize() - .to_vec() - } -} - -impl Default for TransactionOutput { - fn default() -> Self { - TransactionOutput::new( - OutputFeatures::default(), - CommitmentFactory::default().zero(), - RangeProof::default(), - TariScript::default(), - PublicKey::default(), - ComSignature::default(), - ) - } -} - -impl Display for TransactionOutput { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - let proof = self.proof.to_hex(); - let proof = if proof.len() > 32 { - format!( - "{}..{}", - proof[0..16].to_string(), - proof[proof.len() - 16..proof.len()].to_string() - ) - } else { - proof - }; - write!( - fmt, - "{} [{:?}], Script: ({}), Offset Pubkey: ({}), Metadata Signature: ({}, {}, {}), Proof: {}", - self.commitment.to_hex(), - self.features, - self.script, - self.sender_offset_public_key.to_hex(), - self.metadata_signature.u().to_hex(), - self.metadata_signature.v().to_hex(), - self.metadata_signature.public_nonce().to_hex(), - proof - ) - } -} - -impl PartialOrd for TransactionOutput { - fn partial_cmp(&self, other: &Self) -> Option { - self.commitment.partial_cmp(&other.commitment) - } -} - -impl Ord for TransactionOutput { - fn cmp(&self, other: &Self) -> Ordering { - self.commitment.cmp(&other.commitment) - } -} - -/// A wrapper struct to hold the result of a successful range proof rewinding to reveal the committed value and proof -/// message -#[derive(Debug, PartialEq)] -pub struct RewindResult { - pub committed_value: MicroTari, - pub proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], -} - -impl RewindResult { - pub fn new(committed_value: MicroTari, proof_message: [u8; REWIND_USER_MESSAGE_LENGTH]) -> Self { - Self { - committed_value, - proof_message, - } - } -} - -impl From for RewindResult { - fn from(crr: CryptoRewindResult) -> Self { - Self { - committed_value: crr.committed_value.into(), - proof_message: crr.proof_message, - } - } -} - -/// A wrapper struct to hold the result of a successful range proof full rewinding to reveal the committed value, proof -/// message and blinding factor -#[derive(Debug, PartialEq)] -pub struct FullRewindResult { - pub committed_value: MicroTari, - pub proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], - pub blinding_factor: BlindingFactor, -} - -impl FullRewindResult { - pub fn new( - committed_value: MicroTari, - proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], - blinding_factor: BlindingFactor, - ) -> Self { - Self { - committed_value, - proof_message, - blinding_factor, - } - } -} - -impl From> for FullRewindResult { - fn from(crr: CryptoFullRewindResult) -> Self { - Self { - committed_value: crr.committed_value.into(), - proof_message: crr.proof_message, - blinding_factor: crr.blinding_factor, - } - } -} - -//---------------------------------------- Transaction Kernel ----------------------------------------------------// - -/// The transaction kernel tracks the excess for a given transaction. For an explanation of what the excess is, and -/// why it is necessary, refer to the -/// [Mimblewimble TLU post](https://tlu.tarilabs.com/protocols/mimblewimble-1/sources/PITCHME.link.html?highlight=mimblewimble#mimblewimble). -/// The kernel also tracks other transaction metadata, such as the lock height for the transaction (i.e. the earliest -/// this transaction can be mined) and the transaction fee, in cleartext. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TransactionKernel { - /// Options for a kernel's structure or use - pub features: KernelFeatures, - /// Fee originally included in the transaction this proof is for. - pub fee: MicroTari, - /// This kernel is not valid earlier than lock_height blocks - /// The max lock_height of all *inputs* to this transaction - pub lock_height: u64, - /// Remainder of the sum of all transaction commitments (minus an offset). If the transaction is well-formed, - /// amounts plus fee will sum to zero, and the excess is hence a valid public key. - pub excess: Commitment, - /// An aggregated signature of the metadata in this kernel, signed by the individual excess values and the offset - /// excess of the sender. - pub excess_sig: Signature, -} - -impl TransactionKernel { - pub fn is_coinbase(&self) -> bool { - self.features.contains(KernelFeatures::COINBASE_KERNEL) - } - - pub fn verify_signature(&self) -> Result<(), TransactionError> { - let excess = self.excess.as_public_key(); - let r = self.excess_sig.get_public_nonce(); - let m = TransactionMetadata { - lock_height: self.lock_height, - fee: self.fee, - }; - let c = build_challenge(r, &m); - if self.excess_sig.verify_challenge(excess, &c) { - Ok(()) - } else { - Err(TransactionError::InvalidSignatureError( - "Verifying kernel signature".to_string(), - )) - } - } - - /// This method was used to sort kernels. It has been replaced, and will be removed in future - pub fn deprecated_cmp(&self, other: &Self) -> Ordering { - self.features - .cmp(&other.features) - .then(self.fee.cmp(&other.fee)) - .then(self.lock_height.cmp(&other.lock_height)) - .then(self.excess.cmp(&other.excess)) - .then(self.excess_sig.cmp(&other.excess_sig)) - } -} - -impl Hashable for TransactionKernel { - /// Produce a canonical hash for a transaction kernel. The hash is given by - /// $$ H(feature_bits | fee | lock_height | P_excess | R_sum | s_sum) - fn hash(&self) -> Vec { - HashDigest::new() - .chain(&[self.features.bits]) - .chain(u64::from(self.fee).to_le_bytes()) - .chain(self.lock_height.to_le_bytes()) - .chain(self.excess.as_bytes()) - .chain(self.excess_sig.get_public_nonce().as_bytes()) - .chain(self.excess_sig.get_signature().as_bytes()) - .finalize() - .to_vec() - } -} - -impl Display for TransactionKernel { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - write!( - fmt, - "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\n", - self.fee, - self.lock_height, - self.features, - self.excess.to_hex(), - self.excess_sig - .to_json() - .unwrap_or_else(|_| "Failed to serialize signature".into()), - ) - } -} - -impl PartialOrd for TransactionKernel { - fn partial_cmp(&self, other: &Self) -> Option { - self.excess_sig.partial_cmp(&other.excess_sig) - } -} - -impl Ord for TransactionKernel { - fn cmp(&self, other: &Self) -> Ordering { - self.excess_sig.cmp(&other.excess_sig) - } -} - -/// A version of Transaction kernel with optional fields. This struct is only used in constructing transaction kernels -pub struct KernelBuilder { - features: KernelFeatures, - fee: MicroTari, - lock_height: u64, - excess: Option, - excess_sig: Option, -} - -/// Implementation of the transaction kernel -impl KernelBuilder { - /// Creates an empty transaction kernel - pub fn new() -> KernelBuilder { - KernelBuilder::default() - } - - /// Build a transaction kernel with the provided features - pub fn with_features(mut self, features: KernelFeatures) -> KernelBuilder { - self.features = features; - self - } - - /// Build a transaction kernel with the provided fee - pub fn with_fee(mut self, fee: MicroTari) -> KernelBuilder { - self.fee = fee; - self - } - - /// Build a transaction kernel with the provided lock height - pub fn with_lock_height(mut self, lock_height: u64) -> KernelBuilder { - self.lock_height = lock_height; - self - } - - /// Add the excess (sum of public spend keys minus the offset) - pub fn with_excess(mut self, excess: &Commitment) -> KernelBuilder { - self.excess = Some(excess.clone()); - self - } - - /// Add the excess signature - pub fn with_signature(mut self, signature: &Signature) -> KernelBuilder { - self.excess_sig = Some(signature.clone()); - self - } - - pub fn build(self) -> Result { - if self.excess.is_none() || self.excess_sig.is_none() { - return Err(TransactionError::NoSignatureError); - } - Ok(TransactionKernel { - features: self.features, - fee: self.fee, - lock_height: self.lock_height, - excess: self.excess.unwrap(), - excess_sig: self.excess_sig.unwrap(), - }) - } -} - -impl Default for KernelBuilder { - fn default() -> Self { - KernelBuilder { - features: KernelFeatures::empty(), - fee: MicroTari::from(0), - lock_height: 0, - excess: None, - excess_sig: None, - } - } -} - -/// This struct holds the result of calculating the sum of the kernels in a Transaction -/// and returns the summed commitments and the total fees -#[derive(Default)] -pub struct KernelSum { - pub sum: Commitment, - pub fees: MicroTari, -} - -//---------------------------------------- Transaction ----------------------------------------------------// - -/// A transaction which consists of a kernel offset and an aggregate body made up of inputs, outputs and kernels. -/// This struct is used to describe single transactions only. The common part between transactions and Tari blocks is -/// accessible via the `body` field, but single transactions also need to carry the public offset around with them so -/// that these can be aggregated into block offsets. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Transaction { - /// This kernel offset will be accumulated when transactions are aggregated to prevent the "subset" problem where - /// kernels can be linked to inputs and outputs by testing a series of subsets and see which produce valid - /// transactions. - pub offset: BlindingFactor, - /// The constituents of a transaction which has the same structure as the body of a block. - pub body: AggregateBody, - /// A scalar offset that links outputs and inputs to prevent cut-through, enforcing the correct application of - /// the output script. - pub script_offset: BlindingFactor, -} - -impl Transaction { - /// Create a new transaction from the provided inputs, outputs, kernels and offset - pub fn new( - inputs: Vec, - outputs: Vec, - kernels: Vec, - offset: BlindingFactor, - script_offset: BlindingFactor, - ) -> Self { - Self { - offset, - body: AggregateBody::new(inputs, outputs, kernels), - script_offset, - } - } - - /// Validate this transaction by checking the following: - /// 1. The sum of inputs, outputs and fees equal the (public excess value + offset) - /// 1. The signature signs the canonical message with the private excess - /// 1. Range proofs of the outputs are valid - /// - /// This function does NOT check that inputs come from the UTXO set - #[allow(clippy::erasing_op)] // This is for 0 * uT - pub fn validate_internal_consistency( - &self, - bypass_range_proof_verification: bool, - factories: &CryptoFactories, - reward: Option, - prev_header: Option, - height: Option, - ) -> Result<(), TransactionError> { - let reward = reward.unwrap_or_else(|| 0 * uT); - self.body.validate_internal_consistency( - &self.offset, - &self.script_offset, - bypass_range_proof_verification, - reward, - factories, - prev_header, - height, - ) - } - - pub fn body(&self) -> &AggregateBody { - &self.body - } - - /// Returns the byte size or weight of a transaction - pub fn calculate_weight(&self, transaction_weight: &TransactionWeight) -> u64 { - self.body.calculate_weight(transaction_weight) - } - - /// Returns the minimum maturity of the input UTXOs - pub fn min_input_maturity(&self) -> u64 { - self.body.inputs().iter().fold(u64::MAX, |min_maturity, input| { - min(min_maturity, input.features.maturity) - }) - } - - /// Returns the maximum maturity of the input UTXOs - pub fn max_input_maturity(&self) -> u64 { - self.body - .inputs() - .iter() - .fold(0, |max_maturity, input| max(max_maturity, input.features.maturity)) - } - - /// Returns the maximum time lock of the kernels inside of the transaction - pub fn max_kernel_timelock(&self) -> u64 { - self.body.max_kernel_timelock() - } - - /// Returns the height of the minimum height where the transaction is spendable. This is calculated from the - /// transaction kernel lock_heights and the maturity of the input UTXOs. - pub fn min_spendable_height(&self) -> u64 { - max(self.max_kernel_timelock(), self.max_input_maturity()) - } - - /// This function adds two transactions together. It does not do cut-through. Calling Tx1 + Tx2 will result in - /// vut-through being applied. - pub fn add_no_cut_through(mut self, other: Self) -> Self { - self.offset = self.offset + other.offset; - self.script_offset = self.script_offset + other.script_offset; - let (mut inputs, mut outputs, mut kernels) = other.body.dissolve(); - self.body.add_inputs(&mut inputs); - self.body.add_outputs(&mut outputs); - self.body.add_kernels(&mut kernels); - self - } - - pub fn first_kernel_excess_sig(&self) -> Option<&Signature> { - Some(&self.body.kernels().first()?.excess_sig) - } -} - -impl Add for Transaction { - type Output = Self; - - fn add(mut self, other: Self) -> Self { - self = self.add_no_cut_through(other); - self - } -} - -impl Display for Transaction { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - fmt.write_str("-------------- Transaction --------------\n")?; - fmt.write_str("--- Offset ---\n")?; - fmt.write_str(&format!("{}\n", self.offset.to_hex()))?; - fmt.write_str("--- Script Offset ---\n")?; - fmt.write_str(&format!("{}\n", self.script_offset.to_hex()))?; - fmt.write_str("--- Body ---\n")?; - fmt.write_str(&format!("{}\n", self.body)) - } -} - -//---------------------------------------- Transaction Builder ----------------------------------------------------// -pub struct TransactionBuilder { - body: AggregateBody, - offset: Option, - script_offset: Option, - reward: Option, -} - -impl TransactionBuilder { - /// Create an new empty TransactionBuilder - pub fn new() -> Self { - Self::default() - } - - /// Update the offset of an existing transaction - pub fn add_offset(&mut self, offset: BlindingFactor) -> &mut Self { - self.offset = Some(offset); - self - } - - /// Update the script offset of an existing transaction - pub fn add_script_offset(&mut self, script_offset: BlindingFactor) -> &mut Self { - self.script_offset = Some(script_offset); - self - } - - /// Add an input to an existing transaction - pub fn add_input(&mut self, input: TransactionInput) -> &mut Self { - self.body.add_input(input); - self - } - - /// Add an output to an existing transaction - pub fn add_output(&mut self, output: TransactionOutput) -> &mut Self { - self.body.add_output(output); - self - } - - /// Moves a series of inputs to an existing transaction, leaving `inputs` empty - pub fn add_inputs(&mut self, inputs: &mut Vec) -> &mut Self { - self.body.add_inputs(inputs); - self - } - - /// Moves a series of outputs to an existing transaction, leaving `outputs` empty - pub fn add_outputs(&mut self, outputs: &mut Vec) -> &mut Self { - self.body.add_outputs(outputs); - self - } - - /// Set the kernel of a transaction. Currently only one kernel is allowed per transaction - pub fn with_kernel(&mut self, kernel: TransactionKernel) -> &mut Self { - self.body.set_kernel(kernel); - self - } - - pub fn with_reward(&mut self, reward: MicroTari) -> &mut Self { - self.reward = Some(reward); - self - } - - /// Build the transaction. - pub fn build( - self, - factories: &CryptoFactories, - prev_header: Option, - height: Option, - ) -> Result { - if let (Some(script_offset), Some(offset)) = (self.script_offset, self.offset) { - let (i, o, k) = self.body.dissolve(); - let tx = Transaction::new(i, o, k, offset, script_offset); - tx.validate_internal_consistency(true, factories, self.reward, prev_header, height)?; - Ok(tx) - } else { - Err(TransactionError::ValidationError( - "Transaction validation failed".into(), - )) - } - } -} - -impl Default for TransactionBuilder { - fn default() -> Self { - Self { - offset: None, - body: AggregateBody::empty(), - reward: None, - script_offset: None, - } - } -} - -//----------------------------------------- Tests ----------------------------------------------------// - -#[cfg(test)] -mod test { - use crate::{ - transactions::{ - tari_amount::T, - test_helpers, - test_helpers::{TestParams, UtxoTestParams}, - transaction::OutputFeatures, - }, - txn_schema, - }; - use rand::{self, rngs::OsRng}; - use tari_common_types::types::{BlindingFactor, PrivateKey, PublicKey}; - use tari_crypto::{ - keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, - ristretto::pedersen::PedersenCommitmentFactory, - script, - script::ExecutionStack, - }; - - use super::*; - - #[test] - fn input_and_output_hash_match() { - let test_params = TestParams::new(); - let factory = PedersenCommitmentFactory::default(); - - let i = test_params.create_unblinded_output(Default::default()); - let output = i.as_transaction_output(&CryptoFactories::default()).unwrap(); - let input = i.as_transaction_input(&factory).unwrap(); - assert_eq!(output.hash(), input.output_hash()); - } - - #[test] - fn unblinded_input() { - let test_params = TestParams::new(); - let factory = PedersenCommitmentFactory::default(); - - let i = test_params.create_unblinded_output(Default::default()); - let input = i - .as_transaction_input(&factory) - .expect("Should be able to create transaction input"); - assert_eq!(input.features, OutputFeatures::default()); - assert!(input.opened_by(&i, &factory)); - } - - #[test] - fn with_maturity() { - let features = OutputFeatures::with_maturity(42); - assert_eq!(features.maturity, 42); - assert_eq!(features.flags, OutputFlags::empty()); - } - - #[test] - fn range_proof_verification() { - let factories = CryptoFactories::new(32); - // Directly test the tx_output verification - let test_params_1 = TestParams::new(); - let test_params_2 = TestParams::new(); - let output_features = OutputFeatures::default(); - - // For testing the max range has been limited to 2^32 so this value is too large. - let unblinded_output1 = test_params_1.create_unblinded_output(UtxoTestParams { - value: (2u64.pow(32) - 1u64).into(), - ..Default::default() - }); - let script = unblinded_output1.script.clone(); - let tx_output1 = unblinded_output1.as_transaction_output(&factories).unwrap(); - assert!(tx_output1.verify_range_proof(&factories.range_proof).unwrap()); - - let unblinded_output2 = test_params_2.create_unblinded_output(UtxoTestParams { - value: (2u64.pow(32) + 1u64).into(), - ..Default::default() - }); - let tx_output2 = unblinded_output2.as_transaction_output(&factories); - match tx_output2 { - Ok(_) => panic!("Range proof should have failed to verify"), - Err(e) => assert_eq!( - e, - TransactionError::ValidationError( - "Value provided is outside the range allowed by the range proof".to_string() - ) - ), - } - - let value = 2u64.pow(32) + 1; - let v = PrivateKey::from(value); - let c = factories.commitment.commit(&test_params_2.spend_key, &v); - let proof = factories - .range_proof - .construct_proof(&test_params_2.spend_key, 2u64.pow(32) + 1) - .unwrap(); - - let tx_output3 = TransactionOutput::new( - output_features.clone(), - c, - RangeProof::from_bytes(&proof).unwrap(), - script.clone(), - test_params_2.sender_offset_public_key, - TransactionOutput::create_final_metadata_signature( - &value.into(), - &test_params_2.spend_key, - &script, - &output_features, - &test_params_2.sender_offset_private_key, - ) - .unwrap(), - ); - assert!(!tx_output3.verify_range_proof(&factories.range_proof).unwrap()); - } - - #[test] - fn sender_signature_verification() { - let test_params = TestParams::new(); - let factories = CryptoFactories::new(32); - let unblinded_output = test_params.create_unblinded_output(Default::default()); - - let mut tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); - assert!(tx_output.verify_metadata_signature().is_ok()); - tx_output.script = TariScript::default(); - assert!(tx_output.verify_metadata_signature().is_err()); - - tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); - assert!(tx_output.verify_metadata_signature().is_ok()); - tx_output.features = OutputFeatures::create_coinbase(0); - assert!(tx_output.verify_metadata_signature().is_err()); - - tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); - assert!(tx_output.verify_metadata_signature().is_ok()); - tx_output.sender_offset_public_key = PublicKey::default(); - assert!(tx_output.verify_metadata_signature().is_err()); - } - - #[test] - fn kernel_hash() { - let s = PrivateKey::from_hex("6c6eebc5a9c02e1f3c16a69ba4331f9f63d0718401dea10adc4f9d3b879a2c09").unwrap(); - let r = PublicKey::from_hex("28e8efe4e5576aac931d358d0f6ace43c55fa9d4186d1d259d1436caa876d43b").unwrap(); - let sig = Signature::new(r, s); - let excess = Commitment::from_hex("9017be5092b85856ce71061cadeb20c2d1fabdf664c4b3f082bf44cf5065e650").unwrap(); - let k = KernelBuilder::new() - .with_signature(&sig) - .with_fee(100.into()) - .with_excess(&excess) - .with_lock_height(500) - .build() - .unwrap(); - assert_eq!( - &k.hash().to_hex(), - "fe25e4e961d5efec889c489d43e40a1334bf9b4408be4c2e8035a523f231a732" - ); - } - - #[test] - fn kernel_metadata() { - let s = PrivateKey::from_hex("df9a004360b1cf6488d8ff7fb625bc5877f4b013f9b2b20d84932172e605b207").unwrap(); - let r = PublicKey::from_hex("5c6bfaceaa1c83fa4482a816b5f82ca3975cb9b61b6e8be4ee8f01c5f1bee561").unwrap(); - let sig = Signature::new(r, s); - let excess = Commitment::from_hex("e0bd3f743b566272277c357075b0584fc840d79efac49e9b3b6dbaa8a351bc0c").unwrap(); - let k = KernelBuilder::new() - .with_signature(&sig) - .with_fee(100.into()) - .with_excess(&excess) - .with_lock_height(500) - .build() - .unwrap(); - assert_eq!( - &k.hash().to_hex(), - "f1e7348b0952d8afbec6bfaa07a1cbc9c45e51e022242d3faeb0f190e2a9dd07" - ) - } - - #[test] - fn check_timelocks() { - let factories = CryptoFactories::new(32); - let k = BlindingFactor::random(&mut OsRng); - let v = PrivateKey::from(2u64.pow(32) + 1); - let c = factories.commitment.commit(&k, &v); - - let script = TariScript::default(); - let input_data = ExecutionStack::default(); - let script_signature = ComSignature::default(); - let offset_pub_key = PublicKey::default(); - let mut input = TransactionInput::new( - OutputFeatures::default(), - c, - script, - input_data, - script_signature, - offset_pub_key, - ); - - let mut kernel = test_helpers::create_test_kernel(0.into(), 0); - let mut tx = Transaction::new(Vec::new(), Vec::new(), Vec::new(), 0.into(), 0.into()); - - // lets add time locks - input.features.maturity = 5; - kernel.lock_height = 2; - tx.body.add_input(input.clone()); - tx.body.add_kernel(kernel.clone()); - assert_eq!(tx.body.check_stxo_rules(1), Err(TransactionError::InputMaturity)); - assert_eq!(tx.body.check_stxo_rules(5), Ok(())); - - assert_eq!(tx.max_input_maturity(), 5); - assert_eq!(tx.max_kernel_timelock(), 2); - assert_eq!(tx.min_spendable_height(), 5); - - input.features.maturity = 4; - kernel.lock_height = 3; - tx.body.add_input(input.clone()); - tx.body.add_kernel(kernel.clone()); - - assert_eq!(tx.max_input_maturity(), 5); - assert_eq!(tx.max_kernel_timelock(), 3); - assert_eq!(tx.min_spendable_height(), 5); - - input.features.maturity = 2; - kernel.lock_height = 10; - tx.body.add_input(input); - tx.body.add_kernel(kernel); - - assert_eq!(tx.max_input_maturity(), 5); - assert_eq!(tx.max_kernel_timelock(), 10); - assert_eq!(tx.min_spendable_height(), 10); - } - - #[test] - fn test_validate_internal_consistency() { - let (tx, _, _) = test_helpers::create_tx(5000.into(), 3.into(), 1, 2, 1, 4); - - let factories = CryptoFactories::default(); - assert!(tx - .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) - .is_ok()); - } - - #[test] - #[allow(clippy::identity_op)] - fn check_cut_through() { - let (tx, _, outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2); - - assert_eq!(tx.body.inputs().len(), 2); - assert_eq!(tx.body.outputs().len(), 2); - assert_eq!(tx.body.kernels().len(), 1); - - let factories = CryptoFactories::default(); - assert!(tx - .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) - .is_ok()); - - let schema = txn_schema!(from: vec![outputs[1].clone()], to: vec![1 * T, 2 * T]); - let (tx2, _outputs, _) = test_helpers::spend_utxos(schema); - - assert_eq!(tx2.body.inputs().len(), 1); - assert_eq!(tx2.body.outputs().len(), 3); - assert_eq!(tx2.body.kernels().len(), 1); - - let tx3 = tx + tx2; - let mut tx3_cut_through = tx3.clone(); - // check that all inputs are as we expect them to be - assert_eq!(tx3.body.inputs().len(), 3); - assert_eq!(tx3.body.outputs().len(), 5); - assert_eq!(tx3.body.kernels().len(), 2); - - // Do manual cut-through on tx3 - let double_inputs: Vec = tx3_cut_through - .body - .inputs() - .clone() - .iter() - .filter(|input| tx3_cut_through.body.outputs_mut().iter().any(|o| o.is_equal_to(input))) - .cloned() - .collect(); - for input in double_inputs { - tx3_cut_through.body.outputs_mut().retain(|x| !input.is_equal_to(x)); - tx3_cut_through.body.inputs_mut().retain(|x| *x != input); - } - - // Validate basis transaction where cut-through has not been applied. - assert!(tx3 - .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) - .is_ok()); - - // tx3_cut_through has manual cut-through, it should not be possible so this should fail - assert!(tx3_cut_through - .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) - .is_err()); - } - - #[test] - fn check_duplicate_inputs_outputs() { - let (tx, _, _outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2); - assert!(!tx.body.contains_duplicated_outputs()); - assert!(!tx.body.contains_duplicated_inputs()); - - let input = tx.body.inputs()[0].clone(); - let output = tx.body.outputs()[0].clone(); - - let mut broken_tx_1 = tx.clone(); - let mut broken_tx_2 = tx; - - broken_tx_1.body.add_input(input); - broken_tx_2.body.add_output(output); - - assert!(broken_tx_1.body.contains_duplicated_inputs()); - assert!(broken_tx_2.body.contains_duplicated_outputs()); - } - - #[test] - fn inputs_not_malleable() { - let (mut inputs, outputs) = test_helpers::create_unblinded_txos(5000.into(), 1, 1, 2, 15.into()); - let mut stack = inputs[0].input_data.clone(); - inputs[0].script = script!(Drop Nop); - inputs[0].input_data.push(StackItem::Hash([0; 32])).unwrap(); - let mut tx = test_helpers::create_transaction_with(1, 15.into(), inputs, outputs); - - stack - .push(StackItem::Hash(*b"Pls put this on tha tari network")) - .unwrap(); - - tx.body.inputs_mut()[0].input_data = stack; - - let factories = CryptoFactories::default(); - let err = tx - .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) - .unwrap_err(); - assert!(matches!(err, TransactionError::InvalidSignatureError(_))); - } - - #[test] - fn test_output_rewinding() { - let test_params = TestParams::new(); - let factories = CryptoFactories::new(32); - let v = MicroTari::from(42); - let rewind_key = PrivateKey::random(&mut OsRng); - let rewind_blinding_key = PrivateKey::random(&mut OsRng); - let random_key = PrivateKey::random(&mut OsRng); - let rewind_public_key = PublicKey::from_secret_key(&rewind_key); - let rewind_blinding_public_key = PublicKey::from_secret_key(&rewind_blinding_key); - let public_random_key = PublicKey::from_secret_key(&random_key); - let proof_message = b"testing12345678910111"; - - let rewind_data = RewindData { - rewind_key: rewind_key.clone(), - rewind_blinding_key: rewind_blinding_key.clone(), - proof_message: proof_message.to_owned(), - }; - - let unblinded_output = test_params.create_unblinded_output(UtxoTestParams { - value: v, - ..Default::default() - }); - let output = unblinded_output - .as_rewindable_transaction_output(&factories, &rewind_data) - .unwrap(); - - assert_eq!( - output.rewind_range_proof_value_only( - &factories.range_proof, - &public_random_key, - &rewind_blinding_public_key - ), - Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) - ); - assert_eq!( - output.rewind_range_proof_value_only(&factories.range_proof, &rewind_public_key, &public_random_key), - Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) - ); - - let rewind_result = output - .rewind_range_proof_value_only(&factories.range_proof, &rewind_public_key, &rewind_blinding_public_key) - .unwrap(); - - assert_eq!(rewind_result.committed_value, v); - assert_eq!(&rewind_result.proof_message, proof_message); - - assert_eq!( - output.full_rewind_range_proof(&factories.range_proof, &random_key, &rewind_blinding_key), - Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) - ); - assert_eq!( - output.full_rewind_range_proof(&factories.range_proof, &rewind_key, &random_key), - Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) - ); - - let full_rewind_result = output - .full_rewind_range_proof(&factories.range_proof, &rewind_key, &rewind_blinding_key) - .unwrap(); - assert_eq!(full_rewind_result.committed_value, v); - assert_eq!(&full_rewind_result.proof_message, proof_message); - assert_eq!(full_rewind_result.blinding_factor, test_params.spend_key); - } - mod output_features { - use super::*; - - #[test] - fn consensus_encode_minimal() { - let features = OutputFeatures::with_maturity(0); - let mut buf = Vec::new(); - let written = features.consensus_encode(&mut buf).unwrap(); - assert_eq!(buf.len(), 3); - assert_eq!(written, 3); - } - - #[test] - fn consensus_encode_decode() { - let features = OutputFeatures::create_coinbase(u64::MAX); - let known_size = features.consensus_encode_exact_size(); - let mut buf = Vec::with_capacity(known_size); - assert_eq!(known_size, 12); - let written = features.consensus_encode(&mut buf).unwrap(); - assert_eq!(buf.len(), 12); - assert_eq!(written, 12); - let decoded_features = OutputFeatures::consensus_decode(&mut &buf[..]).unwrap(); - assert_eq!(features, decoded_features); - } - - #[test] - fn consensus_decode_bad_flags() { - let data = [0x00u8, 0x00, 0x02]; - let features = OutputFeatures::consensus_decode(&mut &data[..]).unwrap(); - // Assert the flag data is preserved - assert_eq!(features.flags.bits & 0x02, 0x02); - } - - #[test] - fn consensus_decode_bad_maturity() { - let data = [0x00u8, 0xFF]; - let err = OutputFeatures::consensus_decode(&mut &data[..]).unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof); - } - - #[test] - fn consensus_decode_attempt_maturity_overflow() { - let data = [0x00u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; - let err = OutputFeatures::consensus_decode(&mut &data[..]).unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::InvalidData); - } - } -} diff --git a/base_layer/core/src/transactions/transaction_entities/error.rs b/base_layer/core/src/transactions/transaction_entities/error.rs new file mode 100644 index 0000000000..543930df18 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/error.rs @@ -0,0 +1,63 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::crypto::{range_proof::RangeProofError, script::ScriptError, signatures::CommitmentSignatureError}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +//---------------------------------------- TransactionError ----------------------------------------------------// +#[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] +pub enum TransactionError { + #[error("Error validating the transaction: {0}")] + ValidationError(String), + #[error("Signature is invalid: {0}")] + InvalidSignatureError(String), + #[error("Transaction kernel does not contain a signature")] + NoSignatureError, + #[error("A range proof construction or verification has produced an error: {0}")] + RangeProofError(#[from] RangeProofError), + #[error("An error occurred while performing a commitment signature: {0}")] + SigningError(#[from] CommitmentSignatureError), + #[error("Invalid kernel in body")] + InvalidKernel, + #[error("Invalid coinbase in body")] + InvalidCoinbase, + #[error("Invalid coinbase maturity in body")] + InvalidCoinbaseMaturity, + #[error("More than one coinbase in body")] + MoreThanOneCoinbase, + #[error("No coinbase in body")] + NoCoinbase, + #[error("Input maturity not reached")] + InputMaturity, + #[error("Tari script error : {0}")] + ScriptError(#[from] ScriptError), + #[error("Error performing conversion: {0}")] + ConversionError(String), + #[error("The script offset in body does not balance")] + ScriptOffset, + #[error("Error executing script: {0}")] + ScriptExecutionError(String), +} diff --git a/base_layer/core/src/transactions/transaction_entities/full_rewind_result.rs b/base_layer/core/src/transactions/transaction_entities/full_rewind_result.rs new file mode 100644 index 0000000000..2a0a4904d0 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/full_rewind_result.rs @@ -0,0 +1,63 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::range_proof::{FullRewindResult as CryptoFullRewindResult, REWIND_USER_MESSAGE_LENGTH}, + transactions::tari_amount::MicroTari, +}; +use tari_common_types::types::BlindingFactor; + +/// A wrapper struct to hold the result of a successful range proof full rewinding to reveal the committed value, proof +/// message and blinding factor +#[derive(Debug, PartialEq)] +pub struct FullRewindResult { + pub committed_value: MicroTari, + pub proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], + pub blinding_factor: BlindingFactor, +} + +impl FullRewindResult { + pub fn new( + committed_value: MicroTari, + proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], + blinding_factor: BlindingFactor, + ) -> Self { + Self { + committed_value, + proof_message, + blinding_factor, + } + } +} + +impl From> for FullRewindResult { + fn from(crr: CryptoFullRewindResult) -> Self { + Self { + committed_value: crr.committed_value.into(), + proof_message: crr.proof_message, + blinding_factor: crr.blinding_factor, + } + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/kernel_builder.rs b/base_layer/core/src/transactions/transaction_entities/kernel_builder.rs new file mode 100644 index 0000000000..7fd8f04769 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/kernel_builder.rs @@ -0,0 +1,102 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::transactions::{ + tari_amount::MicroTari, + transaction_entities::{KernelFeatures, TransactionError, TransactionKernel}, +}; +use tari_common_types::types::{Commitment, Signature}; + +/// A version of Transaction kernel with optional fields. This struct is only used in constructing transaction kernels +pub struct KernelBuilder { + features: KernelFeatures, + fee: MicroTari, + lock_height: u64, + excess: Option, + excess_sig: Option, +} + +/// Implementation of the transaction kernel +impl KernelBuilder { + /// Creates an empty transaction kernel + pub fn new() -> KernelBuilder { + KernelBuilder::default() + } + + /// Build a transaction kernel with the provided features + pub fn with_features(mut self, features: KernelFeatures) -> KernelBuilder { + self.features = features; + self + } + + /// Build a transaction kernel with the provided fee + pub fn with_fee(mut self, fee: MicroTari) -> KernelBuilder { + self.fee = fee; + self + } + + /// Build a transaction kernel with the provided lock height + pub fn with_lock_height(mut self, lock_height: u64) -> KernelBuilder { + self.lock_height = lock_height; + self + } + + /// Add the excess (sum of public spend keys minus the offset) + pub fn with_excess(mut self, excess: &Commitment) -> KernelBuilder { + self.excess = Some(excess.clone()); + self + } + + /// Add the excess signature + pub fn with_signature(mut self, signature: &Signature) -> KernelBuilder { + self.excess_sig = Some(signature.clone()); + self + } + + pub fn build(self) -> Result { + if self.excess.is_none() || self.excess_sig.is_none() { + return Err(TransactionError::NoSignatureError); + } + Ok(TransactionKernel { + features: self.features, + fee: self.fee, + lock_height: self.lock_height, + excess: self.excess.unwrap(), + excess_sig: self.excess_sig.unwrap(), + }) + } +} + +impl Default for KernelBuilder { + fn default() -> Self { + KernelBuilder { + features: KernelFeatures::empty(), + fee: MicroTari::from(0), + lock_height: 0, + excess: None, + excess_sig: None, + } + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/kernel_features.rs b/base_layer/core/src/transactions/transaction_entities/kernel_features.rs new file mode 100644 index 0000000000..b6df7b168f --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/kernel_features.rs @@ -0,0 +1,39 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serde::{Deserialize, Serialize}; + +bitflags! { + /// Options for a kernel's structure or use. + /// TODO: expand to accommodate Tari DAN transaction types, such as namespace and validator node registrations + #[derive(Deserialize, Serialize)] + pub struct KernelFeatures: u8 { + /// Coinbase transaction + const COINBASE_KERNEL = 1u8; + } +} + +impl KernelFeatures { + pub fn create_coinbase() -> KernelFeatures { + KernelFeatures::COINBASE_KERNEL + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/kernel_sum.rs b/base_layer/core/src/transactions/transaction_entities/kernel_sum.rs new file mode 100644 index 0000000000..73e755872f --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/kernel_sum.rs @@ -0,0 +1,35 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::transactions::tari_amount::MicroTari; +use tari_common_types::types::Commitment; + +/// This struct holds the result of calculating the sum of the kernels in a Transaction +/// and returns the summed commitments and the total fees +#[derive(Default)] +pub struct KernelSum { + pub sum: Commitment, + pub fees: MicroTari, +} diff --git a/base_layer/core/src/transactions/transaction_entities/mod.rs b/base_layer/core/src/transactions/transaction_entities/mod.rs new file mode 100644 index 0000000000..b5d06cde93 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/mod.rs @@ -0,0 +1,533 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use blake2::Digest; +pub use error::TransactionError; +pub use full_rewind_result::FullRewindResult; +pub use kernel_builder::KernelBuilder; +pub use kernel_features::KernelFeatures; +pub use kernel_sum::KernelSum; +pub use output_features::OutputFeatures; +pub use output_flags::OutputFlags; +pub use rewind_result::RewindResult; +use tari_common_types::types::{Commitment, HashDigest}; +use tari_crypto::{script::TariScript, tari_utilities::ByteArray}; +pub use transaction::Transaction; +pub use transaction_builder::TransactionBuilder; +pub use transaction_input::TransactionInput; +pub use transaction_kernel::TransactionKernel; +pub use transaction_output::TransactionOutput; +pub use unblinded_output::UnblindedOutput; + +pub(crate) mod error; +pub(crate) mod full_rewind_result; +pub(crate) mod kernel_builder; +pub(crate) mod kernel_features; +pub(crate) mod kernel_sum; +pub(crate) mod output_features; +pub(crate) mod output_flags; +pub(crate) mod rewind_result; +pub(crate) mod transaction; +pub(crate) mod transaction_builder; +pub(crate) mod transaction_input; +pub(crate) mod transaction_kernel; +pub(crate) mod transaction_output; +pub(crate) mod unblinded_output; + +// Tx_weight(inputs(12,500), outputs(500), kernels(1)) = 126,510 still well enough below block weight of 127,795 +pub const MAX_TRANSACTION_INPUTS: usize = 12_500; +pub const MAX_TRANSACTION_OUTPUTS: usize = 500; +pub const MAX_TRANSACTION_RECIPIENTS: usize = 15; + +//---------------------------------------- Crate functions ----------------------------------------------------// + +/// Implement the canonical hashing function for TransactionOutput and UnblindedOutput for use in +/// ordering as well as for the output hash calculation for TransactionInput. +/// +/// We can exclude the range proof from this hash. The rationale for this is: +/// a) It is a significant performance boost, since the RP is the biggest part of an output +/// b) Range proofs are committed to elsewhere and so we'd be hashing them twice (and as mentioned, this is slow) +/// c) TransactionInputs will now have the same hash as UTXOs, which makes locating STXOs easier when doing reorgs +pub fn hash_output(features: &OutputFeatures, commitment: &Commitment, script: &TariScript) -> Vec { + HashDigest::new() + // TODO: use consensus encoding #testnet_reset + .chain(features.to_v1_bytes()) + .chain(commitment.as_bytes()) + // .chain(range proof) // See docs as to why we exclude this + .chain(script.as_bytes()) + .finalize() + .to_vec() +} + +//----------------------------------------- Tests ----------------------------------------------------// + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + transactions::{ + tari_amount::{MicroTari, T}, + test_helpers, + test_helpers::{TestParams, UtxoTestParams}, + transaction_entities::OutputFeatures, + transaction_protocol::RewindData, + CryptoFactories, + }, + txn_schema, + }; + use rand::{self, rngs::OsRng}; + use tari_common_types::types::{BlindingFactor, ComSignature, PrivateKey, PublicKey, RangeProof, Signature}; + use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, + range_proof::{RangeProofError, RangeProofService}, + ristretto::pedersen::PedersenCommitmentFactory, + script, + script::{ExecutionStack, StackItem}, + tari_utilities::{hex::Hex, Hashable}, + }; + + #[test] + fn input_and_output_and_unblinded_output_hash_match() { + let test_params = TestParams::new(); + let factory = PedersenCommitmentFactory::default(); + + let i = test_params.create_unblinded_output(Default::default()); + let output = i.as_transaction_output(&CryptoFactories::default()).unwrap(); + let input = i.as_transaction_input(&factory).unwrap(); + assert_eq!(output.hash(), input.output_hash()); + assert_eq!(output.hash(), i.hash()); + } + + #[test] + fn unblinded_input() { + let test_params = TestParams::new(); + let factory = PedersenCommitmentFactory::default(); + + let i = test_params.create_unblinded_output(Default::default()); + let input = i + .as_transaction_input(&factory) + .expect("Should be able to create transaction input"); + assert_eq!(input.features, OutputFeatures::default()); + assert!(input.opened_by(&i, &factory)); + } + + #[test] + fn with_maturity() { + let features = OutputFeatures::with_maturity(42); + assert_eq!(features.maturity, 42); + assert_eq!(features.flags, OutputFlags::empty()); + } + + #[test] + fn range_proof_verification() { + let factories = CryptoFactories::new(32); + // Directly test the tx_output verification + let test_params_1 = TestParams::new(); + let test_params_2 = TestParams::new(); + let output_features = OutputFeatures::default(); + + // For testing the max range has been limited to 2^32 so this value is too large. + let unblinded_output1 = test_params_1.create_unblinded_output(UtxoTestParams { + value: (2u64.pow(32) - 1u64).into(), + ..Default::default() + }); + let script = unblinded_output1.script.clone(); + let tx_output1 = unblinded_output1.as_transaction_output(&factories).unwrap(); + assert!(tx_output1.verify_range_proof(&factories.range_proof).unwrap()); + + let unblinded_output2 = test_params_2.create_unblinded_output(UtxoTestParams { + value: (2u64.pow(32) + 1u64).into(), + ..Default::default() + }); + let tx_output2 = unblinded_output2.as_transaction_output(&factories); + match tx_output2 { + Ok(_) => panic!("Range proof should have failed to verify"), + Err(e) => assert_eq!( + e, + TransactionError::ValidationError( + "Value provided is outside the range allowed by the range proof".to_string() + ) + ), + } + + let value = 2u64.pow(32) + 1; + let v = PrivateKey::from(value); + let c = factories.commitment.commit(&test_params_2.spend_key, &v); + let proof = factories + .range_proof + .construct_proof(&test_params_2.spend_key, 2u64.pow(32) + 1) + .unwrap(); + + let tx_output3 = TransactionOutput::new( + output_features.clone(), + c, + RangeProof::from_bytes(&proof).unwrap(), + script.clone(), + test_params_2.sender_offset_public_key, + TransactionOutput::create_final_metadata_signature( + &value.into(), + &test_params_2.spend_key, + &script, + &output_features, + &test_params_2.sender_offset_private_key, + ) + .unwrap(), + ); + assert!(!tx_output3.verify_range_proof(&factories.range_proof).unwrap()); + } + + #[test] + fn sender_signature_verification() { + let test_params = TestParams::new(); + let factories = CryptoFactories::new(32); + let unblinded_output = test_params.create_unblinded_output(Default::default()); + + let mut tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); + assert!(tx_output.verify_metadata_signature().is_ok()); + tx_output.script = TariScript::default(); + assert!(tx_output.verify_metadata_signature().is_err()); + + tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); + assert!(tx_output.verify_metadata_signature().is_ok()); + tx_output.features = OutputFeatures::create_coinbase(0); + assert!(tx_output.verify_metadata_signature().is_err()); + + tx_output = unblinded_output.as_transaction_output(&factories).unwrap(); + assert!(tx_output.verify_metadata_signature().is_ok()); + tx_output.sender_offset_public_key = PublicKey::default(); + assert!(tx_output.verify_metadata_signature().is_err()); + } + + #[test] + fn kernel_hash() { + let s = PrivateKey::from_hex("6c6eebc5a9c02e1f3c16a69ba4331f9f63d0718401dea10adc4f9d3b879a2c09").unwrap(); + let r = PublicKey::from_hex("28e8efe4e5576aac931d358d0f6ace43c55fa9d4186d1d259d1436caa876d43b").unwrap(); + let sig = Signature::new(r, s); + let excess = Commitment::from_hex("9017be5092b85856ce71061cadeb20c2d1fabdf664c4b3f082bf44cf5065e650").unwrap(); + let k = KernelBuilder::new() + .with_signature(&sig) + .with_fee(100.into()) + .with_excess(&excess) + .with_lock_height(500) + .build() + .unwrap(); + assert_eq!( + &k.hash().to_hex(), + "fe25e4e961d5efec889c489d43e40a1334bf9b4408be4c2e8035a523f231a732" + ); + } + + #[test] + fn kernel_metadata() { + let s = PrivateKey::from_hex("df9a004360b1cf6488d8ff7fb625bc5877f4b013f9b2b20d84932172e605b207").unwrap(); + let r = PublicKey::from_hex("5c6bfaceaa1c83fa4482a816b5f82ca3975cb9b61b6e8be4ee8f01c5f1bee561").unwrap(); + let sig = Signature::new(r, s); + let excess = Commitment::from_hex("e0bd3f743b566272277c357075b0584fc840d79efac49e9b3b6dbaa8a351bc0c").unwrap(); + let k = KernelBuilder::new() + .with_signature(&sig) + .with_fee(100.into()) + .with_excess(&excess) + .with_lock_height(500) + .build() + .unwrap(); + assert_eq!( + &k.hash().to_hex(), + "f1e7348b0952d8afbec6bfaa07a1cbc9c45e51e022242d3faeb0f190e2a9dd07" + ) + } + + #[test] + fn check_timelocks() { + let factories = CryptoFactories::new(32); + let k = BlindingFactor::random(&mut OsRng); + let v = PrivateKey::from(2u64.pow(32) + 1); + let c = factories.commitment.commit(&k, &v); + + let script = TariScript::default(); + let input_data = ExecutionStack::default(); + let script_signature = ComSignature::default(); + let offset_pub_key = PublicKey::default(); + let mut input = TransactionInput::new( + OutputFeatures::default(), + c, + script, + input_data, + script_signature, + offset_pub_key, + ); + + let mut kernel = test_helpers::create_test_kernel(0.into(), 0); + let mut tx = Transaction::new(Vec::new(), Vec::new(), Vec::new(), 0.into(), 0.into()); + + // lets add time locks + input.features.maturity = 5; + kernel.lock_height = 2; + tx.body.add_input(input.clone()); + tx.body.add_kernel(kernel.clone()); + assert_eq!(tx.body.check_stxo_rules(1), Err(TransactionError::InputMaturity)); + assert_eq!(tx.body.check_stxo_rules(5), Ok(())); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 2); + assert_eq!(tx.min_spendable_height(), 5); + + input.features.maturity = 4; + kernel.lock_height = 3; + tx.body.add_input(input.clone()); + tx.body.add_kernel(kernel.clone()); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 3); + assert_eq!(tx.min_spendable_height(), 5); + + input.features.maturity = 2; + kernel.lock_height = 10; + tx.body.add_input(input); + tx.body.add_kernel(kernel); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 10); + assert_eq!(tx.min_spendable_height(), 10); + } + + #[test] + fn test_validate_internal_consistency() { + let (tx, _, _) = test_helpers::create_tx(5000.into(), 3.into(), 1, 2, 1, 4); + + let factories = CryptoFactories::default(); + assert!(tx + .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) + .is_ok()); + } + + #[test] + #[allow(clippy::identity_op)] + fn check_cut_through() { + let (tx, _, outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2); + + assert_eq!(tx.body.inputs().len(), 2); + assert_eq!(tx.body.outputs().len(), 2); + assert_eq!(tx.body.kernels().len(), 1); + + let factories = CryptoFactories::default(); + assert!(tx + .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) + .is_ok()); + + let schema = txn_schema!(from: vec![outputs[1].clone()], to: vec![1 * T, 2 * T]); + let (tx2, _outputs, _) = test_helpers::spend_utxos(schema); + + assert_eq!(tx2.body.inputs().len(), 1); + assert_eq!(tx2.body.outputs().len(), 3); + assert_eq!(tx2.body.kernels().len(), 1); + + let tx3 = tx + tx2; + let mut tx3_cut_through = tx3.clone(); + // check that all inputs are as we expect them to be + assert_eq!(tx3.body.inputs().len(), 3); + assert_eq!(tx3.body.outputs().len(), 5); + assert_eq!(tx3.body.kernels().len(), 2); + + // Do manual cut-through on tx3 + let double_inputs: Vec = tx3_cut_through + .body + .inputs() + .clone() + .iter() + .filter(|input| tx3_cut_through.body.outputs_mut().iter().any(|o| o.is_equal_to(input))) + .cloned() + .collect(); + for input in double_inputs { + tx3_cut_through.body.outputs_mut().retain(|x| !input.is_equal_to(x)); + tx3_cut_through.body.inputs_mut().retain(|x| *x != input); + } + + // Validate basis transaction where cut-through has not been applied. + assert!(tx3 + .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) + .is_ok()); + + // tx3_cut_through has manual cut-through, it should not be possible so this should fail + assert!(tx3_cut_through + .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) + .is_err()); + } + + #[test] + fn check_duplicate_inputs_outputs() { + let (tx, _, _outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2); + assert!(!tx.body.contains_duplicated_outputs()); + assert!(!tx.body.contains_duplicated_inputs()); + + let input = tx.body.inputs()[0].clone(); + let output = tx.body.outputs()[0].clone(); + + let mut broken_tx_1 = tx.clone(); + let mut broken_tx_2 = tx; + + broken_tx_1.body.add_input(input); + broken_tx_2.body.add_output(output); + + assert!(broken_tx_1.body.contains_duplicated_inputs()); + assert!(broken_tx_2.body.contains_duplicated_outputs()); + } + + #[test] + fn inputs_not_malleable() { + let (mut inputs, outputs) = test_helpers::create_unblinded_txos(5000.into(), 1, 1, 2, 15.into()); + let mut stack = inputs[0].input_data.clone(); + inputs[0].script = script!(Drop Nop); + inputs[0].input_data.push(StackItem::Hash([0; 32])).unwrap(); + let mut tx = test_helpers::create_transaction_with(1, 15.into(), inputs, outputs); + + stack + .push(StackItem::Hash(*b"Pls put this on tha tari network")) + .unwrap(); + + tx.body.inputs_mut()[0].input_data = stack; + + let factories = CryptoFactories::default(); + let err = tx + .validate_internal_consistency(false, &factories, None, None, Some(u64::MAX)) + .unwrap_err(); + assert!(matches!(err, TransactionError::InvalidSignatureError(_))); + } + + #[test] + fn test_output_rewinding() { + let test_params = TestParams::new(); + let factories = CryptoFactories::new(32); + let v = MicroTari::from(42); + let rewind_key = PrivateKey::random(&mut OsRng); + let rewind_blinding_key = PrivateKey::random(&mut OsRng); + let random_key = PrivateKey::random(&mut OsRng); + let rewind_public_key = PublicKey::from_secret_key(&rewind_key); + let rewind_blinding_public_key = PublicKey::from_secret_key(&rewind_blinding_key); + let public_random_key = PublicKey::from_secret_key(&random_key); + let proof_message = b"testing12345678910111"; + + let rewind_data = RewindData { + rewind_key: rewind_key.clone(), + rewind_blinding_key: rewind_blinding_key.clone(), + proof_message: proof_message.to_owned(), + }; + + let unblinded_output = test_params.create_unblinded_output(UtxoTestParams { + value: v, + ..Default::default() + }); + let output = unblinded_output + .as_rewindable_transaction_output(&factories, &rewind_data) + .unwrap(); + + assert_eq!( + output.rewind_range_proof_value_only( + &factories.range_proof, + &public_random_key, + &rewind_blinding_public_key + ), + Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) + ); + assert_eq!( + output.rewind_range_proof_value_only(&factories.range_proof, &rewind_public_key, &public_random_key), + Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) + ); + + let rewind_result = output + .rewind_range_proof_value_only(&factories.range_proof, &rewind_public_key, &rewind_blinding_public_key) + .unwrap(); + + assert_eq!(rewind_result.committed_value, v); + assert_eq!(&rewind_result.proof_message, proof_message); + + assert_eq!( + output.full_rewind_range_proof(&factories.range_proof, &random_key, &rewind_blinding_key), + Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) + ); + assert_eq!( + output.full_rewind_range_proof(&factories.range_proof, &rewind_key, &random_key), + Err(TransactionError::RangeProofError(RangeProofError::InvalidRewind)) + ); + + let full_rewind_result = output + .full_rewind_range_proof(&factories.range_proof, &rewind_key, &rewind_blinding_key) + .unwrap(); + assert_eq!(full_rewind_result.committed_value, v); + assert_eq!(&full_rewind_result.proof_message, proof_message); + assert_eq!(full_rewind_result.blinding_factor, test_params.spend_key); + } + mod output_features { + use std::io; + + use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}; + + use super::*; + + #[test] + fn consensus_encode_minimal() { + let features = OutputFeatures::with_maturity(0); + let mut buf = Vec::new(); + let written = features.consensus_encode(&mut buf).unwrap(); + assert_eq!(buf.len(), 3); + assert_eq!(written, 3); + } + + #[test] + fn consensus_encode_decode() { + let features = OutputFeatures::create_coinbase(u64::MAX); + let known_size = features.consensus_encode_exact_size(); + let mut buf = Vec::with_capacity(known_size); + assert_eq!(known_size, 12); + let written = features.consensus_encode(&mut buf).unwrap(); + assert_eq!(buf.len(), 12); + assert_eq!(written, 12); + let decoded_features = OutputFeatures::consensus_decode(&mut &buf[..]).unwrap(); + assert_eq!(features, decoded_features); + } + + #[test] + fn consensus_decode_bad_flags() { + let data = [0x00u8, 0x00, 0x02]; + let features = OutputFeatures::consensus_decode(&mut &data[..]).unwrap(); + // Assert the flag data is preserved + assert_eq!(features.flags.bits() & 0x02, 0x02); + } + + #[test] + fn consensus_decode_bad_maturity() { + let data = [0x00u8, 0xFF]; + let err = OutputFeatures::consensus_decode(&mut &data[..]).unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof); + } + + #[test] + fn consensus_decode_attempt_maturity_overflow() { + let data = [0x00u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let err = OutputFeatures::consensus_decode(&mut &data[..]).unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::InvalidData); + } + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/output_features.rs b/base_layer/core/src/transactions/transaction_entities/output_features.rs new file mode 100644 index 0000000000..c1c00a652d --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/output_features.rs @@ -0,0 +1,152 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}, + transactions::transaction_entities::OutputFlags, +}; +use integer_encoding::{VarInt, VarIntReader, VarIntWriter}; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt, + fmt::{Display, Formatter}, + io, + io::{Read, Write}, +}; + +/// Options for UTXO's +#[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] +pub struct OutputFeatures { + /// Flags are the feature flags that differentiate between outputs, eg Coinbase all of which has different rules + pub flags: OutputFlags, + /// the maturity of the specific UTXO. This is the min lock height at which an UTXO can be spent. Coinbase UTXO + /// require a min maturity of the Coinbase_lock_height, this should be checked on receiving new blocks. + pub maturity: u64, +} + +impl OutputFeatures { + /// The version number to use in consensus encoding. In future, this value could be dynamic. + const CONSENSUS_ENCODING_VERSION: u8 = 0; + + /// Encodes output features using deprecated bincode encoding + pub fn to_v1_bytes(&self) -> Vec { + // unreachable panic: serialized_size is infallible because it uses DefaultOptions + let encode_size = bincode::serialized_size(self).expect("unreachable"); + let mut buf = Vec::with_capacity(encode_size as usize); + // unreachable panic: Vec's Write impl is infallible + bincode::serialize_into(&mut buf, self).expect("unreachable"); + buf + } + + /// Encodes output features using consensus encoding + pub fn to_consensus_bytes(&self) -> Vec { + let mut buf = Vec::with_capacity(self.consensus_encode_exact_size()); + // unreachable panic: Vec's Write impl is infallible + self.consensus_encode(&mut buf).expect("unreachable"); + buf + } + + pub fn create_coinbase(maturity_height: u64) -> OutputFeatures { + OutputFeatures { + flags: OutputFlags::COINBASE_OUTPUT, + maturity: maturity_height, + } + } + + /// Create an `OutputFeatures` with the given maturity and all other values at their default setting + pub fn with_maturity(maturity: u64) -> OutputFeatures { + OutputFeatures { + maturity, + ..OutputFeatures::default() + } + } +} + +impl ConsensusEncoding for OutputFeatures { + fn consensus_encode(&self, writer: &mut W) -> Result { + let mut written = writer.write_varint(Self::CONSENSUS_ENCODING_VERSION)?; + written += writer.write_varint(self.maturity)?; + written += self.flags.consensus_encode(writer)?; + Ok(written) + } +} + +impl ConsensusEncodingSized for OutputFeatures { + fn consensus_encode_exact_size(&self) -> usize { + Self::CONSENSUS_ENCODING_VERSION.required_space() + + self.flags.consensus_encode_exact_size() + + self.maturity.required_space() + } +} + +impl ConsensusDecoding for OutputFeatures { + fn consensus_decode(reader: &mut R) -> Result { + // Changing the order of these operations is consensus breaking + let version = reader.read_varint::()?; + if version != Self::CONSENSUS_ENCODING_VERSION { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Invalid version. Expected {} but got {}", + Self::CONSENSUS_ENCODING_VERSION, + version + ), + )); + } + // Decode safety: read_varint will stop reading the varint after 10 bytes + let maturity = reader.read_varint()?; + let flags = OutputFlags::consensus_decode(reader)?; + Ok(Self { flags, maturity }) + } +} + +impl Default for OutputFeatures { + fn default() -> Self { + OutputFeatures { + flags: OutputFlags::empty(), + maturity: 0, + } + } +} + +impl PartialOrd for OutputFeatures { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for OutputFeatures { + fn cmp(&self, other: &Self) -> Ordering { + self.maturity.cmp(&other.maturity) + } +} + +impl Display for OutputFeatures { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "OutputFeatures: Flags = {:?}, Maturity = {}", + self.flags, self.maturity + ) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/output_flags.rs b/base_layer/core/src/transactions/transaction_entities/output_flags.rs new file mode 100644 index 0000000000..965995c0e9 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/output_flags.rs @@ -0,0 +1,63 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}; +use serde::{Deserialize, Serialize}; +use std::{io, io::Read}; + +bitflags! { + #[derive(Deserialize, Serialize)] + pub struct OutputFlags: u8 { + /// Output is a coinbase output, must not be spent until maturity + const COINBASE_OUTPUT = 0b0000_0001; + } +} + +impl ConsensusEncoding for OutputFlags { + fn consensus_encode(&self, writer: &mut W) -> Result { + writer.write(&self.bits.to_le_bytes()) + } +} + +impl ConsensusEncodingSized for OutputFlags { + fn consensus_encode_exact_size(&self) -> usize { + 1 + } +} + +impl ConsensusDecoding for OutputFlags { + fn consensus_decode(reader: &mut R) -> Result { + let mut buf = [0u8; 1]; + reader.read_exact(&mut buf)?; + // SAFETY: we have 3 options here: + // 1. error if unsupported flags are used, meaning that every new flag will be a hard fork + // 2. truncate unsupported flags, means different hashes will be produced for the same block + // 3. ignore unsupported flags, which could be set at any time and persisted to the blockchain. + // Once those flags are defined at some point in the future, depending on the functionality of the flag, + // a consensus rule may be needed that ignores flags prior to a given block height. + // Option 3 is used here + Ok(unsafe { OutputFlags::from_bits_unchecked(u8::from_le_bytes(buf)) }) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/rewind_result.rs b/base_layer/core/src/transactions/transaction_entities/rewind_result.rs new file mode 100644 index 0000000000..a1b68d84f4 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/rewind_result.rs @@ -0,0 +1,55 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::range_proof::{RewindResult as CryptoRewindResult, REWIND_USER_MESSAGE_LENGTH}, + transactions::tari_amount::MicroTari, +}; + +/// A wrapper struct to hold the result of a successful range proof rewinding to reveal the committed value and proof +/// message +#[derive(Debug, PartialEq)] +pub struct RewindResult { + pub committed_value: MicroTari, + pub proof_message: [u8; REWIND_USER_MESSAGE_LENGTH], +} + +impl RewindResult { + pub fn new(committed_value: MicroTari, proof_message: [u8; REWIND_USER_MESSAGE_LENGTH]) -> Self { + Self { + committed_value, + proof_message, + } + } +} + +impl From for RewindResult { + fn from(crr: CryptoRewindResult) -> Self { + Self { + committed_value: crr.committed_value.into(), + proof_message: crr.proof_message, + } + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/transaction.rs b/base_layer/core/src/transactions/transaction_entities/transaction.rs new file mode 100644 index 0000000000..27e25a6b48 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/transaction.rs @@ -0,0 +1,175 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::tari_utilities::hex::Hex, + transactions::{ + aggregated_body::AggregateBody, + tari_amount::{uT, MicroTari}, + transaction_entities::{TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, + weight::TransactionWeight, + CryptoFactories, + }, +}; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::{max, min}, + fmt::{Display, Formatter}, + ops::Add, +}; +use tari_common_types::types::{BlindingFactor, HashOutput, Signature}; + +/// A transaction which consists of a kernel offset and an aggregate body made up of inputs, outputs and kernels. +/// This struct is used to describe single transactions only. The common part between transactions and Tari blocks is +/// accessible via the `body` field, but single transactions also need to carry the public offset around with them so +/// that these can be aggregated into block offsets. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Transaction { + /// This kernel offset will be accumulated when transactions are aggregated to prevent the "subset" problem where + /// kernels can be linked to inputs and outputs by testing a series of subsets and see which produce valid + /// transactions. + pub offset: BlindingFactor, + /// The constituents of a transaction which has the same structure as the body of a block. + pub body: AggregateBody, + /// A scalar offset that links outputs and inputs to prevent cut-through, enforcing the correct application of + /// the output script. + pub script_offset: BlindingFactor, +} + +impl Transaction { + /// Create a new transaction from the provided inputs, outputs, kernels and offset + pub fn new( + inputs: Vec, + outputs: Vec, + kernels: Vec, + offset: BlindingFactor, + script_offset: BlindingFactor, + ) -> Self { + Self { + offset, + body: AggregateBody::new(inputs, outputs, kernels), + script_offset, + } + } + + /// Validate this transaction by checking the following: + /// 1. The sum of inputs, outputs and fees equal the (public excess value + offset) + /// 1. The signature signs the canonical message with the private excess + /// 1. Range proofs of the outputs are valid + /// + /// This function does NOT check that inputs come from the UTXO set + #[allow(clippy::erasing_op)] // This is for 0 * uT + pub fn validate_internal_consistency( + &self, + bypass_range_proof_verification: bool, + factories: &CryptoFactories, + reward: Option, + prev_header: Option, + height: Option, + ) -> Result<(), TransactionError> { + let reward = reward.unwrap_or_else(|| 0 * uT); + self.body.validate_internal_consistency( + &self.offset, + &self.script_offset, + bypass_range_proof_verification, + reward, + factories, + prev_header, + height, + ) + } + + pub fn body(&self) -> &AggregateBody { + &self.body + } + + /// Returns the byte size or weight of a transaction + pub fn calculate_weight(&self, transaction_weight: &TransactionWeight) -> u64 { + self.body.calculate_weight(transaction_weight) + } + + /// Returns the minimum maturity of the input UTXOs + pub fn min_input_maturity(&self) -> u64 { + self.body.inputs().iter().fold(u64::MAX, |min_maturity, input| { + min(min_maturity, input.features.maturity) + }) + } + + /// Returns the maximum maturity of the input UTXOs + pub fn max_input_maturity(&self) -> u64 { + self.body + .inputs() + .iter() + .fold(0, |max_maturity, input| max(max_maturity, input.features.maturity)) + } + + /// Returns the maximum time lock of the kernels inside of the transaction + pub fn max_kernel_timelock(&self) -> u64 { + self.body.max_kernel_timelock() + } + + /// Returns the height of the minimum height where the transaction is spendable. This is calculated from the + /// transaction kernel lock_heights and the maturity of the input UTXOs. + pub fn min_spendable_height(&self) -> u64 { + max(self.max_kernel_timelock(), self.max_input_maturity()) + } + + /// This function adds two transactions together. It does not do cut-through. Calling Tx1 + Tx2 will result in + /// vut-through being applied. + pub fn add_no_cut_through(mut self, other: Self) -> Self { + self.offset = self.offset + other.offset; + self.script_offset = self.script_offset + other.script_offset; + let (mut inputs, mut outputs, mut kernels) = other.body.dissolve(); + self.body.add_inputs(&mut inputs); + self.body.add_outputs(&mut outputs); + self.body.add_kernels(&mut kernels); + self + } + + pub fn first_kernel_excess_sig(&self) -> Option<&Signature> { + Some(&self.body.kernels().first()?.excess_sig) + } +} + +impl Add for Transaction { + type Output = Self; + + fn add(mut self, other: Self) -> Self { + self = self.add_no_cut_through(other); + self + } +} + +impl Display for Transaction { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + fmt.write_str("-------------- Transaction --------------\n")?; + fmt.write_str("--- Offset ---\n")?; + fmt.write_str(&format!("{}\n", self.offset.to_hex()))?; + fmt.write_str("--- Script Offset ---\n")?; + fmt.write_str(&format!("{}\n", self.script_offset.to_hex()))?; + fmt.write_str("--- Body ---\n")?; + fmt.write_str(&format!("{}\n", self.body)) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/transaction_builder.rs b/base_layer/core/src/transactions/transaction_entities/transaction_builder.rs new file mode 100644 index 0000000000..f58a3d6162 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/transaction_builder.rs @@ -0,0 +1,124 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::transactions::{ + aggregated_body::AggregateBody, + tari_amount::MicroTari, + transaction_entities::{Transaction, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, + CryptoFactories, +}; +use tari_common_types::types::{BlindingFactor, HashOutput}; + +//---------------------------------------- Transaction Builder ----------------------------------------------------// +pub struct TransactionBuilder { + body: AggregateBody, + offset: Option, + script_offset: Option, + reward: Option, +} + +impl TransactionBuilder { + /// Create an new empty TransactionBuilder + pub fn new() -> Self { + Self::default() + } + + /// Update the offset of an existing transaction + pub fn add_offset(&mut self, offset: BlindingFactor) -> &mut Self { + self.offset = Some(offset); + self + } + + /// Update the script offset of an existing transaction + pub fn add_script_offset(&mut self, script_offset: BlindingFactor) -> &mut Self { + self.script_offset = Some(script_offset); + self + } + + /// Add an input to an existing transaction + pub fn add_input(&mut self, input: TransactionInput) -> &mut Self { + self.body.add_input(input); + self + } + + /// Add an output to an existing transaction + pub fn add_output(&mut self, output: TransactionOutput) -> &mut Self { + self.body.add_output(output); + self + } + + /// Moves a series of inputs to an existing transaction, leaving `inputs` empty + pub fn add_inputs(&mut self, inputs: &mut Vec) -> &mut Self { + self.body.add_inputs(inputs); + self + } + + /// Moves a series of outputs to an existing transaction, leaving `outputs` empty + pub fn add_outputs(&mut self, outputs: &mut Vec) -> &mut Self { + self.body.add_outputs(outputs); + self + } + + /// Set the kernel of a transaction. Currently only one kernel is allowed per transaction + pub fn with_kernel(&mut self, kernel: TransactionKernel) -> &mut Self { + self.body.set_kernel(kernel); + self + } + + pub fn with_reward(&mut self, reward: MicroTari) -> &mut Self { + self.reward = Some(reward); + self + } + + /// Build the transaction. + pub fn build( + self, + factories: &CryptoFactories, + prev_header: Option, + height: Option, + ) -> Result { + if let (Some(script_offset), Some(offset)) = (self.script_offset, self.offset) { + let (i, o, k) = self.body.dissolve(); + let tx = Transaction::new(i, o, k, offset, script_offset); + tx.validate_internal_consistency(true, factories, self.reward, prev_header, height)?; + Ok(tx) + } else { + Err(TransactionError::ValidationError( + "Transaction validation failed".into(), + )) + } + } +} + +impl Default for TransactionBuilder { + fn default() -> Self { + Self { + offset: None, + body: AggregateBody::empty(), + reward: None, + script_offset: None, + } + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/transaction_input.rs b/base_layer/core/src/transactions/transaction_entities/transaction_input.rs new file mode 100644 index 0000000000..1012402022 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/transaction_input.rs @@ -0,0 +1,224 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::{ + commitment::HomomorphicCommitmentFactory, + script::{ExecutionStack, StackItem, TariScript}, + tari_utilities::{hex::Hex, ByteArray, Hashable}, + }, + transactions::{ + transaction_entities, + transaction_entities::{ + transaction_output::TransactionOutput, + OutputFeatures, + TransactionError, + UnblindedOutput, + }, + }, +}; +use blake2::Digest; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, +}; +use tari_common_types::types::{Challenge, ComSignature, Commitment, CommitmentFactory, HashDigest, PublicKey}; +use tari_crypto::script::ScriptContext; + +/// A transaction input. +/// +/// Primarily a reference to an output being spent by the transaction. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransactionInput { + /// The features of the output being spent. We will check maturity for all outputs. + pub features: OutputFeatures, + /// The commitment referencing the output being spent. + pub commitment: Commitment, + /// The serialised script + pub script: TariScript, + /// The script input data, if any + pub input_data: ExecutionStack, + /// A signature with k_s, signing the script, input data, and mined height + pub script_signature: ComSignature, + /// The offset public key, K_O + pub sender_offset_public_key: PublicKey, +} + +/// An input for a transaction that spends an existing output +impl TransactionInput { + /// Create a new Transaction Input + pub fn new( + features: OutputFeatures, + commitment: Commitment, + script: TariScript, + input_data: ExecutionStack, + script_signature: ComSignature, + sender_offset_public_key: PublicKey, + ) -> TransactionInput { + TransactionInput { + features, + commitment, + script, + input_data, + script_signature, + sender_offset_public_key, + } + } + + pub fn build_script_challenge( + nonce_commitment: &Commitment, + script: &TariScript, + input_data: &ExecutionStack, + script_public_key: &PublicKey, + commitment: &Commitment, + ) -> Vec { + Challenge::new() + .chain(nonce_commitment.as_bytes()) + .chain(script.as_bytes().as_slice()) + .chain(input_data.as_bytes().as_slice()) + .chain(script_public_key.as_bytes()) + .chain(commitment.as_bytes()) + .finalize() + .to_vec() + } + + /// Accessor method for the commitment contained in an input + pub fn commitment(&self) -> &Commitment { + &self.commitment + } + + /// Checks if the given un-blinded input instance corresponds to this blinded Transaction Input + pub fn opened_by(&self, input: &UnblindedOutput, factory: &CommitmentFactory) -> bool { + factory.open(&input.spending_key, &input.value.into(), &self.commitment) + } + + /// This will check if the input and the output is the same transactional output by looking at the commitment and + /// features and script. This will ignore all other output and input fields + pub fn is_equal_to(&self, output: &TransactionOutput) -> bool { + self.output_hash() == output.hash() + } + + /// This will run the script contained in the TransactionInput, returning either a script error or the resulting + /// public key. + pub fn run_script(&self, context: Option) -> Result { + let context = context.unwrap_or_default(); + match self.script.execute_with_context(&self.input_data, &context)? { + StackItem::PublicKey(pubkey) => Ok(pubkey), + _ => Err(TransactionError::ScriptExecutionError( + "The script executed successfully but it did not leave a public key on the stack".to_string(), + )), + } + } + + pub fn validate_script_signature( + &self, + public_script_key: &PublicKey, + factory: &CommitmentFactory, + ) -> Result<(), TransactionError> { + let challenge = TransactionInput::build_script_challenge( + self.script_signature.public_nonce(), + &self.script, + &self.input_data, + public_script_key, + &self.commitment, + ); + if self + .script_signature + .verify_challenge(&(&self.commitment + public_script_key), &challenge, factory) + { + Ok(()) + } else { + Err(TransactionError::InvalidSignatureError( + "Verifying script signature".to_string(), + )) + } + } + + /// This will run the script and verify the script signature. If its valid, it will return the resulting public key + /// from the script. + pub fn run_and_verify_script( + &self, + factory: &CommitmentFactory, + context: Option, + ) -> Result { + let key = self.run_script(context)?; + self.validate_script_signature(&key, factory)?; + Ok(key) + } + + /// Returns true if this input is mature at the given height, otherwise false + pub fn is_mature_at(&self, block_height: u64) -> bool { + self.features.maturity <= block_height + } + + /// Returns the hash of the output data contained in this input. + /// This hash matches the hash of a transaction output that this input spends. + pub fn output_hash(&self) -> Vec { + transaction_entities::hash_output(&self.features, &self.commitment, &self.script) + } +} + +/// Implement the canonical hashing function for TransactionInput for use in ordering +impl Hashable for TransactionInput { + fn hash(&self) -> Vec { + HashDigest::new() + .chain(self.features.to_v1_bytes()) + .chain(self.commitment.as_bytes()) + .chain(self.script.as_bytes()) + .chain(self.sender_offset_public_key.as_bytes()) + .chain(self.script_signature.u().as_bytes()) + .chain(self.script_signature.v().as_bytes()) + .chain(self.script_signature.public_nonce().as_bytes()) + .chain(self.input_data.as_bytes()) + .finalize() + .to_vec() + } +} + +impl Display for TransactionInput { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + fmt, + "{} [{:?}], Script hash: ({}), Offset_Pubkey: ({})", + self.commitment.to_hex(), + self.features, + self.script, + self.sender_offset_public_key.to_hex() + ) + } +} + +impl PartialOrd for TransactionInput { + fn partial_cmp(&self, other: &Self) -> Option { + self.commitment.partial_cmp(&other.commitment) + } +} + +impl Ord for TransactionInput { + fn cmp(&self, other: &Self) -> Ordering { + self.commitment.cmp(&other.commitment) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/transaction_kernel.rs b/base_layer/core/src/transactions/transaction_entities/transaction_kernel.rs new file mode 100644 index 0000000000..939bd9851a --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/transaction_kernel.rs @@ -0,0 +1,139 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::tari_utilities::{hex::Hex, message_format::MessageFormat, ByteArray, Hashable}, + transactions::{ + tari_amount::MicroTari, + transaction_entities::{KernelFeatures, TransactionError}, + transaction_protocol::{build_challenge, TransactionMetadata}, + }, +}; +use blake2::Digest; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, +}; +use tari_common_types::types::{Commitment, HashDigest, Signature}; + +/// The transaction kernel tracks the excess for a given transaction. For an explanation of what the excess is, and +/// why it is necessary, refer to the +/// [Mimblewimble TLU post](https://tlu.tarilabs.com/protocols/mimblewimble-1/sources/PITCHME.link.html?highlight=mimblewimble#mimblewimble). +/// The kernel also tracks other transaction metadata, such as the lock height for the transaction (i.e. the earliest +/// this transaction can be mined) and the transaction fee, in cleartext. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransactionKernel { + /// Options for a kernel's structure or use + pub features: KernelFeatures, + /// Fee originally included in the transaction this proof is for. + pub fee: MicroTari, + /// This kernel is not valid earlier than lock_height blocks + /// The max lock_height of all *inputs* to this transaction + pub lock_height: u64, + /// Remainder of the sum of all transaction commitments (minus an offset). If the transaction is well-formed, + /// amounts plus fee will sum to zero, and the excess is hence a valid public key. + pub excess: Commitment, + /// An aggregated signature of the metadata in this kernel, signed by the individual excess values and the offset + /// excess of the sender. + pub excess_sig: Signature, +} + +impl TransactionKernel { + pub fn is_coinbase(&self) -> bool { + self.features.contains(KernelFeatures::COINBASE_KERNEL) + } + + pub fn verify_signature(&self) -> Result<(), TransactionError> { + let excess = self.excess.as_public_key(); + let r = self.excess_sig.get_public_nonce(); + let m = TransactionMetadata { + lock_height: self.lock_height, + fee: self.fee, + }; + let c = build_challenge(r, &m); + if self.excess_sig.verify_challenge(excess, &c) { + Ok(()) + } else { + Err(TransactionError::InvalidSignatureError( + "Verifying kernel signature".to_string(), + )) + } + } + + /// This method was used to sort kernels. It has been replaced, and will be removed in future + pub fn deprecated_cmp(&self, other: &Self) -> Ordering { + self.features + .cmp(&other.features) + .then(self.fee.cmp(&other.fee)) + .then(self.lock_height.cmp(&other.lock_height)) + .then(self.excess.cmp(&other.excess)) + .then(self.excess_sig.cmp(&other.excess_sig)) + } +} + +impl Hashable for TransactionKernel { + /// Produce a canonical hash for a transaction kernel. The hash is given by + /// $$ H(feature_bits | fee | lock_height | P_excess | R_sum | s_sum) + fn hash(&self) -> Vec { + HashDigest::new() + .chain(&[self.features.bits()]) + .chain(u64::from(self.fee).to_le_bytes()) + .chain(self.lock_height.to_le_bytes()) + .chain(self.excess.as_bytes()) + .chain(self.excess_sig.get_public_nonce().as_bytes()) + .chain(self.excess_sig.get_signature().as_bytes()) + .finalize() + .to_vec() + } +} + +impl Display for TransactionKernel { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + fmt, + "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\n", + self.fee, + self.lock_height, + self.features, + self.excess.to_hex(), + self.excess_sig + .to_json() + .unwrap_or_else(|_| "Failed to serialize signature".into()), + ) + } +} + +impl PartialOrd for TransactionKernel { + fn partial_cmp(&self, other: &Self) -> Option { + self.excess_sig.partial_cmp(&other.excess_sig) + } +} + +impl Ord for TransactionKernel { + fn cmp(&self, other: &Self) -> Ordering { + self.excess_sig.cmp(&other.excess_sig) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/transaction_output.rs b/base_layer/core/src/transactions/transaction_entities/transaction_output.rs new file mode 100644 index 0000000000..204278b27a --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/transaction_output.rs @@ -0,0 +1,368 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + crypto::{ + commitment::HomomorphicCommitmentFactory, + range_proof::RangeProofService as RangeProofServiceTrait, + ristretto::pedersen::PedersenCommitmentFactory, + script::TariScript, + tari_utilities::{hex::Hex, ByteArray, Hashable}, + }, + transactions::{ + tari_amount::MicroTari, + transaction_entities, + transaction_entities::{ + full_rewind_result::FullRewindResult, + rewind_result::RewindResult, + OutputFeatures, + OutputFlags, + TransactionError, + TransactionInput, + }, + }, +}; +use blake2::Digest; +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, +}; +use tari_common_types::types::{ + BlindingFactor, + Challenge, + ComSignature, + Commitment, + CommitmentFactory, + HashDigest, + MessageHash, + PrivateKey, + PublicKey, + RangeProof, + RangeProofService, +}; +use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; + +/// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a +/// blinded value for the output while the range proof guarantees the commitment includes a positive value without +/// overflow and the ownership of the private key. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransactionOutput { + /// Options for an output's structure or use + pub features: OutputFeatures, + /// The homomorphic commitment representing the output amount + pub commitment: Commitment, + /// A proof that the commitment is in the right range + pub proof: RangeProof, + /// The script that will be executed when spending this output + pub script: TariScript, + /// Tari script offset pubkey, K_O + pub sender_offset_public_key: PublicKey, + /// UTXO signature with the script offset private key, k_O + pub metadata_signature: ComSignature, +} + +/// An output for a transaction, includes a range proof and Tari script metadata +impl TransactionOutput { + /// Create new Transaction Output + pub fn new( + features: OutputFeatures, + commitment: Commitment, + proof: RangeProof, + script: TariScript, + sender_offset_public_key: PublicKey, + metadata_signature: ComSignature, + ) -> TransactionOutput { + TransactionOutput { + features, + commitment, + proof, + script, + sender_offset_public_key, + metadata_signature, + } + } + + /// Accessor method for the commitment contained in an output + pub fn commitment(&self) -> &Commitment { + &self.commitment + } + + /// Accessor method for the range proof contained in an output + pub fn proof(&self) -> &RangeProof { + &self.proof + } + + /// Verify that range proof is valid + pub fn verify_range_proof(&self, prover: &RangeProofService) -> Result { + Ok(prover.verify(&self.proof.0, &self.commitment)) + } + + /// Verify that the metadata signature is valid + pub fn verify_metadata_signature(&self) -> Result<(), TransactionError> { + let challenge = TransactionOutput::build_metadata_signature_challenge( + &self.script, + &self.features, + &self.sender_offset_public_key, + self.metadata_signature.public_nonce(), + &self.commitment, + ); + if !self.metadata_signature.verify_challenge( + &(&self.commitment + &self.sender_offset_public_key), + &challenge, + &PedersenCommitmentFactory::default(), + ) { + return Err(TransactionError::InvalidSignatureError( + "Metadata signature not valid!".to_string(), + )); + } + Ok(()) + } + + /// Attempt to rewind the range proof to reveal the proof message and committed value + pub fn rewind_range_proof_value_only( + &self, + prover: &RangeProofService, + rewind_public_key: &PublicKey, + rewind_blinding_public_key: &PublicKey, + ) -> Result { + Ok(prover + .rewind_proof_value_only( + &self.proof.0, + &self.commitment, + rewind_public_key, + rewind_blinding_public_key, + )? + .into()) + } + + /// Attempt to fully rewind the range proof to reveal the proof message, committed value and blinding factor + pub fn full_rewind_range_proof( + &self, + prover: &RangeProofService, + rewind_key: &PrivateKey, + rewind_blinding_key: &PrivateKey, + ) -> Result { + Ok(prover + .rewind_proof_commitment_data(&self.proof.0, &self.commitment, rewind_key, rewind_blinding_key)? + .into()) + } + + /// This will check if the input and the output is the same commitment by looking at the commitment and features. + /// This will ignore the output range proof + #[inline] + pub fn is_equal_to(&self, output: &TransactionInput) -> bool { + self.commitment == output.commitment && self.features == output.features + } + + /// Returns true if the output is a coinbase, otherwise false + pub fn is_coinbase(&self) -> bool { + self.features.flags.contains(OutputFlags::COINBASE_OUTPUT) + } + + /// Convenience function that returns the challenge for the metadata commitment signature + pub fn get_metadata_signature_challenge(&self, partial_commitment_nonce: Option<&PublicKey>) -> MessageHash { + let nonce_commitment = match partial_commitment_nonce { + None => self.metadata_signature.public_nonce().clone(), + Some(partial_nonce) => self.metadata_signature.public_nonce() + partial_nonce, + }; + TransactionOutput::build_metadata_signature_challenge( + &self.script, + &self.features, + &self.sender_offset_public_key, + &nonce_commitment, + &self.commitment, + ) + } + + /// Convenience function that calculates the challenge for the metadata commitment signature + pub fn build_metadata_signature_challenge( + script: &TariScript, + features: &OutputFeatures, + sender_offset_public_key: &PublicKey, + public_commitment_nonce: &Commitment, + commitment: &Commitment, + ) -> MessageHash { + Challenge::new() + .chain(public_commitment_nonce.as_bytes()) + .chain(script.as_bytes()) + // TODO: Use consensus encoded bytes #testnet_reset + .chain(features.to_v1_bytes()) + .chain(sender_offset_public_key.as_bytes()) + .chain(commitment.as_bytes()) + .finalize() + .to_vec() + } + + // Create commitment signature for the metadata + fn create_metadata_signature( + value: &MicroTari, + spending_key: &BlindingFactor, + script: &TariScript, + output_features: &OutputFeatures, + sender_offset_public_key: &PublicKey, + partial_commitment_nonce: Option<&PublicKey>, + sender_offset_private_key: Option<&PrivateKey>, + ) -> Result { + let nonce_a = PrivateKey::random(&mut OsRng); + let nonce_b = PrivateKey::random(&mut OsRng); + let nonce_commitment = PedersenCommitmentFactory::default().commit(&nonce_b, &nonce_a); + let nonce_commitment = match partial_commitment_nonce { + None => nonce_commitment, + Some(partial_nonce) => &nonce_commitment + partial_nonce, + }; + let value = PrivateKey::from(value.as_u64()); + let commitment = PedersenCommitmentFactory::default().commit(spending_key, &value); + let e = TransactionOutput::build_metadata_signature_challenge( + script, + output_features, + sender_offset_public_key, + &nonce_commitment, + &commitment, + ); + let secret_x = match sender_offset_private_key { + None => spending_key.clone(), + Some(key) => &spending_key.clone() + key, + }; + Ok(ComSignature::sign( + value, + secret_x, + nonce_a, + nonce_b, + &e, + &PedersenCommitmentFactory::default(), + )?) + } + + /// Create partial commitment signature for the metadata, usually done by the receiver + pub fn create_partial_metadata_signature( + value: &MicroTari, + spending_key: &BlindingFactor, + script: &TariScript, + output_features: &OutputFeatures, + sender_offset_public_key: &PublicKey, + partial_commitment_nonce: &PublicKey, + ) -> Result { + TransactionOutput::create_metadata_signature( + value, + spending_key, + script, + output_features, + sender_offset_public_key, + Some(partial_commitment_nonce), + None, + ) + } + + /// Create final commitment signature for the metadata, signing with both keys + pub fn create_final_metadata_signature( + value: &MicroTari, + spending_key: &BlindingFactor, + script: &TariScript, + output_features: &OutputFeatures, + sender_offset_private_key: &PrivateKey, + ) -> Result { + let sender_offset_public_key = PublicKey::from_secret_key(sender_offset_private_key); + TransactionOutput::create_metadata_signature( + value, + spending_key, + script, + output_features, + &sender_offset_public_key, + None, + Some(sender_offset_private_key), + ) + } + + pub fn witness_hash(&self) -> Vec { + HashDigest::new() + .chain(self.proof.as_bytes()) + .chain(self.metadata_signature.u().as_bytes()) + .chain(self.metadata_signature.v().as_bytes()) + .chain(self.metadata_signature.public_nonce().as_bytes()) + .finalize() + .to_vec() + } +} + +/// Implement the canonical hashing function for TransactionOutput for use in ordering. +impl Hashable for TransactionOutput { + fn hash(&self) -> Vec { + transaction_entities::hash_output(&self.features, &self.commitment, &self.script) + } +} + +impl Default for TransactionOutput { + fn default() -> Self { + TransactionOutput::new( + OutputFeatures::default(), + CommitmentFactory::default().zero(), + RangeProof::default(), + TariScript::default(), + PublicKey::default(), + ComSignature::default(), + ) + } +} + +impl Display for TransactionOutput { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + let proof = self.proof.to_hex(); + let proof = if proof.len() > 32 { + format!( + "{}..{}", + proof[0..16].to_string(), + proof[proof.len() - 16..proof.len()].to_string() + ) + } else { + proof + }; + write!( + fmt, + "{} [{:?}], Script: ({}), Offset Pubkey: ({}), Metadata Signature: ({}, {}, {}), Proof: {}", + self.commitment.to_hex(), + self.features, + self.script, + self.sender_offset_public_key.to_hex(), + self.metadata_signature.u().to_hex(), + self.metadata_signature.v().to_hex(), + self.metadata_signature.public_nonce().to_hex(), + proof + ) + } +} + +impl PartialOrd for TransactionOutput { + fn partial_cmp(&self, other: &Self) -> Option { + self.commitment.partial_cmp(&other.commitment) + } +} + +impl Ord for TransactionOutput { + fn cmp(&self, other: &Self) -> Ordering { + self.commitment.cmp(&other.commitment) + } +} diff --git a/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs b/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs new file mode 100644 index 0000000000..f3acae2c6b --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs @@ -0,0 +1,228 @@ +// Copyright 2018 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use crate::{ + consensus::{ConsensusEncodingSized, ConsensusEncodingWrapper}, + crypto::{ + commitment::HomomorphicCommitmentFactory, + range_proof::{RangeProofError, RangeProofService}, + script::{ExecutionStack, TariScript}, + }, + tari_utilities::Hashable, + transactions::{ + tari_amount::MicroTari, + transaction_entities, + transaction_entities::{ + transaction_input::TransactionInput, + transaction_output::TransactionOutput, + OutputFeatures, + TransactionError, + }, + transaction_protocol::RewindData, + CryptoFactories, + }, +}; +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; +use std::{cmp::Ordering, ops::Shl}; +use tari_common_types::types::{BlindingFactor, ComSignature, CommitmentFactory, PrivateKey, PublicKey, RangeProof}; +use tari_crypto::{ + keys::{PublicKey as PublicKeyTrait, SecretKey}, + tari_utilities::ByteArray, +}; + +/// An unblinded output is one where the value and spending key (blinding factor) are known. This can be used to +/// build both inputs and outputs (every input comes from an output) +// TODO: Try to get rid of 'Serialize' and 'Deserialize' traits here; see related comment at 'struct RawTransactionInfo' +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UnblindedOutput { + pub value: MicroTari, + pub spending_key: BlindingFactor, + pub features: OutputFeatures, + pub script: TariScript, + pub input_data: ExecutionStack, + pub script_private_key: PrivateKey, + pub sender_offset_public_key: PublicKey, + pub metadata_signature: ComSignature, + pub script_lock_height: u64, +} + +impl UnblindedOutput { + /// Creates a new un-blinded output + #[allow(clippy::too_many_arguments)] + pub fn new( + value: MicroTari, + spending_key: BlindingFactor, + features: OutputFeatures, + script: TariScript, + input_data: ExecutionStack, + script_private_key: PrivateKey, + sender_offset_public_key: PublicKey, + metadata_signature: ComSignature, + script_lock_height: u64, + ) -> UnblindedOutput { + UnblindedOutput { + value, + spending_key, + features, + script, + input_data, + script_private_key, + sender_offset_public_key, + metadata_signature, + script_lock_height, + } + } + + /// Commits an UnblindedOutput into a Transaction input + pub fn as_transaction_input(&self, factory: &CommitmentFactory) -> Result { + let commitment = factory.commit(&self.spending_key, &self.value.into()); + let script_nonce_a = PrivateKey::random(&mut OsRng); + let script_nonce_b = PrivateKey::random(&mut OsRng); + let nonce_commitment = factory.commit(&script_nonce_b, &script_nonce_a); + + let challenge = TransactionInput::build_script_challenge( + &nonce_commitment, + &self.script, + &self.input_data, + &PublicKey::from_secret_key(&self.script_private_key), + &commitment, + ); + let script_signature = ComSignature::sign( + self.value.into(), + &self.script_private_key + &self.spending_key, + script_nonce_a, + script_nonce_b, + &challenge, + factory, + ) + .map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?; + + Ok(TransactionInput { + features: self.features.clone(), + commitment, + script: self.script.clone(), + input_data: self.input_data.clone(), + script_signature, + sender_offset_public_key: self.sender_offset_public_key.clone(), + }) + } + + pub fn as_transaction_output(&self, factories: &CryptoFactories) -> Result { + if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) + { + return Err(TransactionError::ValidationError( + "Value provided is outside the range allowed by the range proof".into(), + )); + } + let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); + let output = TransactionOutput { + features: self.features.clone(), + commitment, + proof: RangeProof::from_bytes( + &factories + .range_proof + .construct_proof(&self.spending_key, self.value.into())?, + ) + .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?, + script: self.script.clone(), + sender_offset_public_key: self.sender_offset_public_key.clone(), + metadata_signature: self.metadata_signature.clone(), + }; + + Ok(output) + } + + pub fn as_rewindable_transaction_output( + &self, + factories: &CryptoFactories, + rewind_data: &RewindData, + ) -> Result { + if factories.range_proof.range() < 64 && self.value >= MicroTari::from(1u64.shl(&factories.range_proof.range())) + { + return Err(TransactionError::ValidationError( + "Value provided is outside the range allowed by the range proof".into(), + )); + } + let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); + + let proof_bytes = factories.range_proof.construct_proof_with_rewind_key( + &self.spending_key, + self.value.into(), + &rewind_data.rewind_key, + &rewind_data.rewind_blinding_key, + &rewind_data.proof_message, + )?; + + let proof = RangeProof::from_bytes(&proof_bytes) + .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?; + + let output = TransactionOutput { + features: self.features.clone(), + commitment, + proof, + script: self.script.clone(), + sender_offset_public_key: self.sender_offset_public_key.clone(), + metadata_signature: self.metadata_signature.clone(), + }; + + Ok(output) + } + + pub fn metadata_byte_size(&self) -> usize { + self.features.consensus_encode_exact_size() + + ConsensusEncodingWrapper::wrap(&self.script).consensus_encode_exact_size() + } +} + +// These implementations are used for order these outputs for UTXO selection which will be done by comparing the values +impl Eq for UnblindedOutput {} + +impl PartialEq for UnblindedOutput { + fn eq(&self, other: &UnblindedOutput) -> bool { + self.value == other.value + } +} + +impl PartialOrd for UnblindedOutput { + fn partial_cmp(&self, other: &Self) -> Option { + self.value.partial_cmp(&other.value) + } +} + +impl Ord for UnblindedOutput { + fn cmp(&self, other: &Self) -> Ordering { + self.value.cmp(&other.value) + } +} + +/// Implement the canonical hashing function for UnblindedOutput for use in ordering. +impl Hashable for UnblindedOutput { + fn hash(&self) -> Vec { + let factories = CryptoFactories::default(); + let commitment = factories.commitment.commit_value(&self.spending_key, self.value.into()); + transaction_entities::hash_output(&self.features, &commitment, &self.script) + } +} diff --git a/base_layer/core/src/transactions/transaction_protocol/mod.rs b/base_layer/core/src/transactions/transaction_protocol/mod.rs index 62907deb92..2ed49efa68 100644 --- a/base_layer/core/src/transactions/transaction_protocol/mod.rs +++ b/base_layer/core/src/transactions/transaction_protocol/mod.rs @@ -82,17 +82,8 @@ // #![allow(clippy::op_ref)] -pub mod proto; -pub mod recipient; -pub mod sender; -pub mod single_receiver; -pub mod transaction_initializer; - -use crate::transactions::{tari_amount::*, transaction::TransactionError}; use digest::Digest; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{MessageHash, PrivateKey, PublicKey}; -use tari_comms::types::Challenge; use tari_crypto::{ range_proof::{RangeProofError, REWIND_USER_MESSAGE_LENGTH}, signatures::SchnorrSignatureError, @@ -100,6 +91,17 @@ use tari_crypto::{ }; use thiserror::Error; +use tari_common_types::types::{MessageHash, PrivateKey, PublicKey}; +use tari_comms::types::Challenge; + +use crate::transactions::{tari_amount::*, transaction_entities::error::TransactionError}; + +pub mod proto; +pub mod recipient; +pub mod sender; +pub mod single_receiver; +pub mod transaction_initializer; + #[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] pub enum TransactionProtocolError { #[error("The current state is not yet completed, cannot transition to next state: `{0}`")] diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index 2136b48a0d..2ddf1c5e02 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -24,9 +24,11 @@ use std::{collections::HashMap, fmt}; use serde::{Deserialize, Serialize}; +use tari_common_types::types::{MessageHash, PrivateKey, PublicKey, Signature}; + use crate::transactions::{ crypto_factories::CryptoFactories, - transaction::{OutputFeatures, TransactionOutput}, + transaction_entities::{output_features::OutputFeatures, transaction_output::TransactionOutput}, transaction_protocol::{ sender::{SingleRoundSenderData as SD, TransactionSenderMessage}, single_receiver::SingleReceiverTransactionProtocol, @@ -34,7 +36,6 @@ use crate::transactions::{ TransactionProtocolError, }, }; -use tari_common_types::types::{MessageHash, PrivateKey, PublicKey, Signature}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[allow(clippy::large_enum_variant)] @@ -212,13 +213,15 @@ mod test { keys::{PublicKey as PK, SecretKey as SecretKeyTrait}, }; + use tari_common_types::types::{PrivateKey, PublicKey, Signature}; + use crate::{ crypto::script::TariScript, transactions::{ crypto_factories::CryptoFactories, tari_amount::*, test_helpers::TestParams, - transaction::OutputFeatures, + transaction_entities::output_features::OutputFeatures, transaction_protocol::{ build_challenge, sender::{SingleRoundSenderData, TransactionSenderMessage}, @@ -228,7 +231,6 @@ mod test { ReceiverTransactionProtocol, }, }; - use tari_common_types::types::{PrivateKey, PublicKey, Signature}; #[test] fn single_round_recipient() { diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 3d626270ac..dc90972098 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -26,7 +26,7 @@ use crate::{ crypto_factories::CryptoFactories, fee::Fee, tari_amount::*, - transaction::{ + transaction_entities::{ KernelBuilder, KernelFeatures, OutputFeatures, @@ -756,7 +756,7 @@ mod test { crypto_factories::CryptoFactories, tari_amount::*, test_helpers::{create_test_input, create_unblinded_output, TestParams}, - transaction::{KernelFeatures, OutputFeatures, TransactionOutput}, + transaction_entities::{KernelFeatures, OutputFeatures, TransactionOutput}, transaction_protocol::{ sender::SenderTransactionProtocol, single_receiver::SingleReceiverTransactionProtocol, diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index ab26e3e80c..93356a4a5f 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -27,9 +27,11 @@ use tari_crypto::{ tari_utilities::byte_array::ByteArray, }; +use tari_common_types::types::{PrivateKey as SK, PublicKey, RangeProof, Signature}; + use crate::transactions::{ crypto_factories::CryptoFactories, - transaction::{OutputFeatures, TransactionOutput}, + transaction_entities::{output_features::OutputFeatures, transaction_output::TransactionOutput}, transaction_protocol::{ build_challenge, recipient::RecipientSignedMessage as RD, @@ -38,7 +40,6 @@ use crate::transactions::{ TransactionProtocolError as TPE, }, }; -use tari_common_types::types::{PrivateKey as SK, PublicKey, RangeProof, Signature}; /// SingleReceiverTransactionProtocol represents the actions taken by the single receiver in the one-round Tari /// transaction protocol. The procedure is straightforward. Upon receiving the sender's information, the receiver: @@ -142,10 +143,12 @@ mod test { script::TariScript, }; + use tari_common_types::types::{PrivateKey, PublicKey}; + use crate::transactions::{ crypto_factories::CryptoFactories, tari_amount::*, - transaction::OutputFeatures, + transaction_entities::output_features::OutputFeatures, transaction_protocol::{ build_challenge, sender::SingleRoundSenderData, @@ -154,7 +157,6 @@ mod test { TransactionProtocolError, }, }; - use tari_common_types::types::{PrivateKey, PublicKey}; fn generate_output_parms() -> (PrivateKey, PrivateKey, OutputFeatures) { let r = PrivateKey::random(&mut OsRng); diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index 7657275782..4f831f1922 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -26,7 +26,7 @@ use crate::{ crypto_factories::CryptoFactories, fee::Fee, tari_amount::*, - transaction::{ + transaction_entities::{ OutputFeatures, TransactionInput, TransactionOutput, @@ -652,7 +652,7 @@ mod test { fee::Fee, tari_amount::*, test_helpers::{create_test_input, create_unblinded_output, TestParams, UtxoTestParams}, - transaction::{OutputFeatures, MAX_TRANSACTION_INPUTS}, + transaction_entities::{OutputFeatures, MAX_TRANSACTION_INPUTS}, transaction_protocol::{ sender::SenderState, transaction_initializer::SenderTransactionInitializer, diff --git a/base_layer/core/src/validation/block_validators/async_validator.rs b/base_layer/core/src/validation/block_validators/async_validator.rs index d8f5cb9dfe..7fd1617f64 100644 --- a/base_layer/core/src/validation/block_validators/async_validator.rs +++ b/base_layer/core/src/validation/block_validators/async_validator.rs @@ -28,7 +28,7 @@ use crate::{ iterators::NonOverlappingIntegerPairIter, transactions::{ aggregated_body::AggregateBody, - transaction::{KernelSum, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, + transaction_entities::{KernelSum, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, CryptoFactories, }, validation::{ diff --git a/base_layer/core/src/validation/block_validators/test.rs b/base_layer/core/src/validation/block_validators/test.rs index 2f59a67c11..2b5fcbbd75 100644 --- a/base_layer/core/src/validation/block_validators/test.rs +++ b/base_layer/core/src/validation/block_validators/test.rs @@ -20,6 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + +use tari_common::configuration::Network; +use tari_test_utils::unpack_enum; + use crate::{ consensus::{ConsensusConstantsBuilder, ConsensusManager}, test_helpers::{ @@ -30,16 +35,13 @@ use crate::{ aggregated_body::AggregateBody, tari_amount::T, test_helpers::schema_to_transaction, - transaction::TransactionError, + transaction_entities::error::TransactionError, CoinbaseBuilder, CryptoFactories, }, txn_schema, validation::{block_validators::BlockValidator, ValidationError}, }; -use std::sync::Arc; -use tari_common::configuration::Network; -use tari_test_utils::unpack_enum; fn setup_with_rules(rules: ConsensusManager) -> (TestBlockchain, BlockValidator) { let blockchain = TestBlockchain::create(rules.clone()); diff --git a/base_layer/core/src/validation/error.rs b/base_layer/core/src/validation/error.rs index ff31cded40..369cf1831a 100644 --- a/base_layer/core/src/validation/error.rs +++ b/base_layer/core/src/validation/error.rs @@ -20,15 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use thiserror::Error; +use tokio::task; + +use tari_common_types::types::HashOutput; + use crate::{ blocks::{BlockHeaderValidationError, BlockValidationError}, chain_storage::ChainStorageError, proof_of_work::{monero_rx::MergeMineError, PowError}, - transactions::transaction::TransactionError, + transactions::transaction_entities::error::TransactionError, }; -use tari_common_types::types::HashOutput; -use thiserror::Error; -use tokio::task; #[derive(Debug, Error)] pub enum ValidationError { diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index f8d54f88a6..3a8f4b0152 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -41,7 +41,7 @@ use crate::{ }, transactions::{ tari_amount::MicroTari, - transaction::{KernelSum, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, + transaction_entities::{KernelSum, TransactionError, TransactionInput, TransactionKernel, TransactionOutput}, CryptoFactories, }, validation::ValidationError, diff --git a/base_layer/core/src/validation/mocks.rs b/base_layer/core/src/validation/mocks.rs index 7e9543dc11..c8cd2d2e9f 100644 --- a/base_layer/core/src/validation/mocks.rs +++ b/base_layer/core/src/validation/mocks.rs @@ -20,11 +20,20 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use async_trait::async_trait; + +use tari_common_types::{chain_metadata::ChainMetadata, types::Commitment}; + use crate::{ blocks::{Block, BlockHeader, ChainBlock}, chain_storage::BlockchainBackend, proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty, PowAlgorithm}, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, validation::{ error::ValidationError, BlockSyncBodyValidation, @@ -36,12 +45,6 @@ use crate::{ PostOrphanBodyValidation, }, }; -use async_trait::async_trait; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, -}; -use tari_common_types::{chain_metadata::ChainMetadata, types::Commitment}; #[derive(Clone)] pub struct MockValidator { diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 65d66da23c..b3e3c7f103 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -25,6 +25,7 @@ use std::sync::Arc; use tari_crypto::{commitment::HomomorphicCommitment, script}; use tari_common::configuration::Network; +use tari_common_types::types::Commitment; use crate::{ blocks::{BlockHeader, BlockHeaderAccumulatedData, ChainBlock, ChainHeader}, @@ -36,12 +37,16 @@ use crate::{ transactions::{ tari_amount::{uT, MicroTari}, test_helpers::{create_random_signature_from_s_key, create_utxo}, - transaction::{KernelBuilder, KernelFeatures, OutputFeatures, TransactionKernel}, + transaction_entities::{ + kernel_builder::KernelBuilder, + output_features::OutputFeatures, + transaction_kernel::TransactionKernel, + KernelFeatures, + }, CryptoFactories, }, validation::{header_iter::HeaderIter, ChainBalanceValidator, FinalHorizonStateValidation}, }; -use tari_common_types::types::Commitment; #[test] fn header_iter_empty_and_invalid_height() { diff --git a/base_layer/core/src/validation/traits.rs b/base_layer/core/src/validation/traits.rs index dde06df434..25839f8f61 100644 --- a/base_layer/core/src/validation/traits.rs +++ b/base_layer/core/src/validation/traits.rs @@ -20,15 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use async_trait::async_trait; + +use tari_common_types::{chain_metadata::ChainMetadata, types::Commitment}; + use crate::{ blocks::{Block, BlockHeader, ChainBlock}, chain_storage::BlockchainBackend, proof_of_work::AchievedTargetDifficulty, - transactions::transaction::Transaction, + transactions::transaction_entities::transaction::Transaction, validation::{error::ValidationError, DifficultyCalculator}, }; -use async_trait::async_trait; -use tari_common_types::{chain_metadata::ChainMetadata, types::Commitment}; /// A validator that determines if a block body is valid, assuming that the header has already been /// validated diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index 0d5b23d80f..c0d384f98d 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -24,7 +24,7 @@ use log::*; use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase}, - transactions::{transaction::Transaction, CryptoFactories}, + transactions::{transaction_entities::Transaction, CryptoFactories}, validation::{ helpers::{check_inputs_are_utxos, check_not_duplicate_txos}, MempoolTransactionValidation, diff --git a/base_layer/core/tests/async_db.rs b/base_layer/core/tests/async_db.rs index 380db7f73a..180d3993ec 100644 --- a/base_layer/core/tests/async_db.rs +++ b/base_layer/core/tests/async_db.rs @@ -38,7 +38,7 @@ use tari_core::{ transactions::{ tari_amount::T, test_helpers::schema_to_transaction, - transaction::{TransactionOutput, UnblindedOutput}, + transaction_entities::{TransactionOutput, UnblindedOutput}, CryptoFactories, }, txn_schema, diff --git a/base_layer/core/tests/base_node_rpc.rs b/base_layer/core/tests/base_node_rpc.rs index d89e5d2b30..23e2f267fb 100644 --- a/base_layer/core/tests/base_node_rpc.rs +++ b/base_layer/core/tests/base_node_rpc.rs @@ -44,9 +44,9 @@ use std::convert::TryFrom; +use randomx_rs::RandomXFlag; use tempfile::{tempdir, TempDir}; -use randomx_rs::RandomXFlag; use tari_common::configuration::Network; use tari_comms::protocol::rpc::mock::RpcRequestMock; use tari_core::{ @@ -62,6 +62,7 @@ use tari_core::{ rpc::{BaseNodeWalletRpcService, BaseNodeWalletService}, state_machine_service::states::{ListeningInfo, StateInfo, StatusInfo}, }, + blocks::ChainBlock, consensus::{ConsensusManager, ConsensusManagerBuilder, NetworkConsensus}, crypto::tari_utilities::Hashable, proto::{ @@ -72,7 +73,7 @@ use tari_core::{ transactions::{ tari_amount::{uT, T}, test_helpers::schema_to_transaction, - transaction::{TransactionOutput, UnblindedOutput}, + transaction_entities::{TransactionOutput, UnblindedOutput}, CryptoFactories, }, txn_schema, @@ -82,7 +83,6 @@ use crate::helpers::{ block_builders::{chain_block, create_genesis_block_with_coinbase_value}, nodes::{BaseNodeBuilder, NodeInterfaces}, }; -use tari_core::blocks::ChainBlock; mod helpers; diff --git a/base_layer/core/tests/block_validation.rs b/base_layer/core/tests/block_validation.rs index c838e88aa4..a689152002 100644 --- a/base_layer/core/tests/block_validation.rs +++ b/base_layer/core/tests/block_validation.rs @@ -20,19 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::helpers::{ - block_builders::{ - chain_block_with_coinbase, - chain_block_with_new_coinbase, - create_coinbase, - create_genesis_block_with_utxos, - find_header_with_achieved_difficulty, - }, - test_blockchain::TestBlockchain, -}; +use std::sync::Arc; + use monero::blockdata::block::Block as MoneroBlock; use rand::{rngs::OsRng, RngCore}; -use std::sync::Arc; +use tari_crypto::{inputs, script}; + use tari_common::configuration::Network; use tari_core::{ blocks::{Block, BlockHeaderAccumulatedData, BlockHeaderValidationError, BlockValidationError, ChainBlock}, @@ -51,7 +44,7 @@ use tari_core::{ aggregated_body::AggregateBody, tari_amount::{uT, T}, test_helpers::{create_unblinded_output, schema_to_transaction, spend_utxos, TestParams, UtxoTestParams}, - transaction::OutputFeatures, + transaction_entities::OutputFeatures, CryptoFactories, }, txn_schema, @@ -67,7 +60,17 @@ use tari_core::{ ValidationError, }, }; -use tari_crypto::{inputs, script}; + +use crate::helpers::{ + block_builders::{ + chain_block_with_coinbase, + chain_block_with_new_coinbase, + create_coinbase, + create_genesis_block_with_utxos, + find_header_with_achieved_difficulty, + }, + test_blockchain::TestBlockchain, +}; mod helpers; diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 66347d7918..455c3063ed 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -48,7 +48,7 @@ use tari_core::{ TestParams, TransactionSchema, }, - transaction::{ + transaction_entities::{ KernelBuilder, KernelFeatures, OutputFeatures, diff --git a/base_layer/core/tests/helpers/database.rs b/base_layer/core/tests/helpers/database.rs index e1132445a3..e4af43b58f 100644 --- a/base_layer/core/tests/helpers/database.rs +++ b/base_layer/core/tests/helpers/database.rs @@ -23,7 +23,7 @@ use tari_core::{ blocks::{Block, BlockHeader, NewBlockTemplate}, consensus::{emission::Emission, ConsensusManager}, - transactions::{tari_amount::MicroTari, transaction::Transaction, CryptoFactories}, + transactions::{tari_amount::MicroTari, transaction_entities::Transaction, CryptoFactories}, }; use crate::helpers::block_builders::create_coinbase; diff --git a/base_layer/core/tests/helpers/sample_blockchains.rs b/base_layer/core/tests/helpers/sample_blockchains.rs index 10f0d7baae..6f2844cbe3 100644 --- a/base_layer/core/tests/helpers/sample_blockchains.rs +++ b/base_layer/core/tests/helpers/sample_blockchains.rs @@ -29,7 +29,7 @@ use tari_core::{ test_helpers::blockchain::{create_store_with_consensus, TempDatabase}, transactions::{ tari_amount::{uT, T}, - transaction::UnblindedOutput, + transaction_entities::UnblindedOutput, CryptoFactories, }, txn_schema, diff --git a/base_layer/core/tests/helpers/test_block_builder.rs b/base_layer/core/tests/helpers/test_block_builder.rs index 2d036d212e..2e2aeccdfc 100644 --- a/base_layer/core/tests/helpers/test_block_builder.rs +++ b/base_layer/core/tests/helpers/test_block_builder.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -use tari_core::transactions::transaction::Transaction; +use tari_core::transactions::transaction_entities::Transaction; #[derive(Default)] pub struct TestBlockBuilder {} diff --git a/base_layer/core/tests/helpers/test_blockchain.rs b/base_layer/core/tests/helpers/test_blockchain.rs index ed6ab1234e..1c2ebf81eb 100644 --- a/base_layer/core/tests/helpers/test_blockchain.rs +++ b/base_layer/core/tests/helpers/test_blockchain.rs @@ -33,7 +33,7 @@ use tari_core::{ chain_storage::{BlockAddResult, BlockchainDatabase, ChainStorageError}, consensus::ConsensusManager, test_helpers::blockchain::TempDatabase, - transactions::{transaction::UnblindedOutput, CryptoFactories}, + transactions::{transaction_entities::UnblindedOutput, CryptoFactories}, }; use crate::helpers::{ diff --git a/base_layer/core/tests/mempool.rs b/base_layer/core/tests/mempool.rs index 444ac1f815..2e25653a67 100644 --- a/base_layer/core/tests/mempool.rs +++ b/base_layer/core/tests/mempool.rs @@ -51,7 +51,7 @@ use tari_core::{ fee::Fee, tari_amount::{uT, MicroTari, T}, test_helpers::{create_unblinded_output, schema_to_transaction, spend_utxos, TestParams}, - transaction::{KernelBuilder, OutputFeatures, Transaction, TransactionOutput}, + transaction_entities::{KernelBuilder, OutputFeatures, Transaction, TransactionOutput}, transaction_protocol::{build_challenge, TransactionMetadata}, CryptoFactories, }, diff --git a/base_layer/core/tests/node_comms_interface.rs b/base_layer/core/tests/node_comms_interface.rs index 9e1bce86f1..e8975ffc9c 100644 --- a/base_layer/core/tests/node_comms_interface.rs +++ b/base_layer/core/tests/node_comms_interface.rs @@ -38,7 +38,7 @@ use tari_core::{ transactions::{ tari_amount::MicroTari, test_helpers::{create_utxo, spend_utxos}, - transaction::{OutputFeatures, TransactionOutput, UnblindedOutput}, + transaction_entities::{OutputFeatures, TransactionOutput, UnblindedOutput}, CryptoFactories, }, txn_schema, diff --git a/base_layer/core/tests/node_service.rs b/base_layer/core/tests/node_service.rs index 3b0ca36393..15da1b7ac1 100644 --- a/base_layer/core/tests/node_service.rs +++ b/base_layer/core/tests/node_service.rs @@ -20,16 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[allow(dead_code)] -mod helpers; -use crate::helpers::block_builders::{construct_chained_blocks, create_coinbase}; +use std::{sync::Arc, time::Duration}; + +use randomx_rs::RandomXFlag; +use tari_crypto::tari_utilities::Hashable; +use tempfile::tempdir; + use helpers::{ block_builders::{append_block, chain_block, create_genesis_block, create_genesis_block_with_utxos}, event_stream::event_stream_next, nodes::{create_network_with_2_base_nodes_with_config, random_node_identity, wait_until_online, BaseNodeBuilder}, }; -use randomx_rs::RandomXFlag; -use std::{sync::Arc, time::Duration}; use tari_common::configuration::Network; use tari_comms::protocol::messaging::MessagingEvent; use tari_core::{ @@ -45,7 +46,7 @@ use tari_core::{ transactions::{ tari_amount::{uT, T}, test_helpers::{schema_to_transaction, spend_utxos}, - transaction::OutputFeatures, + transaction_entities::OutputFeatures, CryptoFactories, }, txn_schema, @@ -55,10 +56,13 @@ use tari_core::{ mocks::MockValidator, }, }; -use tari_crypto::tari_utilities::Hashable; use tari_p2p::services::liveness::LivenessConfig; use tari_test_utils::unpack_enum; -use tempfile::tempdir; + +use crate::helpers::block_builders::{construct_chained_blocks, create_coinbase}; + +#[allow(dead_code)] +mod helpers; #[tokio::test] async fn propagate_and_forward_many_valid_blocks() { diff --git a/base_layer/wallet/src/error.rs b/base_layer/wallet/src/error.rs index 30321105c2..61c879570f 100644 --- a/base_layer/wallet/src/error.rs +++ b/base_layer/wallet/src/error.rs @@ -39,7 +39,7 @@ use tari_comms::{ peer_manager::{node_id::NodeIdError, PeerManagerError}, }; use tari_comms_dht::store_forward::StoreAndForwardError; -use tari_core::transactions::transaction::TransactionError; +use tari_core::transactions::transaction_entities::TransactionError; use tari_crypto::tari_utilities::{hex::HexError, ByteArrayError}; use tari_key_manager::error::KeyManagerError; use tari_p2p::{initialization::CommsInitializationError, services::liveness::error::LivenessError}; diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index 2d287bda0c..1e7eeb84e8 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -20,20 +20,22 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{base_node_service::error::BaseNodeServiceError, error::WalletStorageError}; use diesel::result::Error as DieselError; +use tari_crypto::{script::ScriptError, tari_utilities::ByteArrayError}; +use thiserror::Error; + use tari_common::exit_codes::ExitCodes; use tari_comms::{connectivity::ConnectivityError, peer_manager::node_id::NodeIdError, protocol::rpc::RpcError}; use tari_comms_dht::outbound::DhtOutboundError; use tari_core::transactions::{ - transaction::TransactionError, + transaction_entities::TransactionError, transaction_protocol::TransactionProtocolError, CoinbaseBuildError, }; -use tari_crypto::{script::ScriptError, tari_utilities::ByteArrayError}; use tari_key_manager::error::{KeyManagerError, MnemonicError}; use tari_service_framework::reply_channel::TransportChannelError; -use thiserror::Error; + +use crate::{base_node_service::error::BaseNodeServiceError, error::WalletStorageError}; #[derive(Debug, Error)] pub enum OutputManagerError { diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 37afdbd641..51aa1e2853 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -33,7 +33,7 @@ use tari_common_types::{ }; use tari_core::transactions::{ tari_amount::MicroTari, - transaction::{Transaction, TransactionOutput, UnblindedOutput}, + transaction_entities::{Transaction, TransactionOutput, UnblindedOutput}, transaction_protocol::sender::TransactionSenderMessage, ReceiverTransactionProtocol, SenderTransactionProtocol, diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index b7b35ae8f7..13877f7434 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -26,7 +26,7 @@ use log::*; use rand::rngs::OsRng; use tari_common_types::types::{PrivateKey, PublicKey}; use tari_core::transactions::{ - transaction::{TransactionOutput, UnblindedOutput}, + transaction_entities::{TransactionOutput, UnblindedOutput}, CryptoFactories, }; use tari_crypto::{ diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 2572402e69..6a1ec6b711 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -55,7 +55,7 @@ use tari_core::{ transactions::{ fee::Fee, tari_amount::MicroTari, - transaction::{KernelFeatures, OutputFeatures, Transaction, TransactionOutput, UnblindedOutput}, + transaction_entities::{KernelFeatures, OutputFeatures, Transaction, TransactionOutput, UnblindedOutput}, transaction_protocol::sender::TransactionSenderMessage, CoinbaseBuilder, CryptoFactories, diff --git a/base_layer/wallet/src/output_manager_service/storage/database.rs b/base_layer/wallet/src/output_manager_service/storage/database.rs index 78c40bfb40..6b2768e387 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database.rs @@ -38,7 +38,7 @@ use tari_common_types::{ transaction::TxId, types::{BlindingFactor, Commitment, HashOutput}, }; -use tari_core::transactions::{tari_amount::MicroTari, transaction::TransactionOutput}; +use tari_core::transactions::{tari_amount::MicroTari, transaction_entities::TransactionOutput}; use tari_key_manager::cipher_seed::CipherSeed; const LOG_TARGET: &str = "wallet::output_manager_service::database"; diff --git a/base_layer/wallet/src/output_manager_service/storage/models.rs b/base_layer/wallet/src/output_manager_service/storage/models.rs index 7f0a8805c9..0062610363 100644 --- a/base_layer/wallet/src/output_manager_service/storage/models.rs +++ b/base_layer/wallet/src/output_manager_service/storage/models.rs @@ -25,7 +25,7 @@ use std::cmp::Ordering; use tari_common_types::types::{BlockHash, Commitment, HashOutput, PrivateKey}; use tari_core::{ tari_utilities::hash::Hashable, - transactions::{transaction::UnblindedOutput, transaction_protocol::RewindData, CryptoFactories}, + transactions::{transaction_entities::UnblindedOutput, transaction_protocol::RewindData, CryptoFactories}, }; use tari_crypto::script::{ExecutionStack, TariScript}; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 78a7fcddaf..605391970f 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -45,7 +45,7 @@ use tari_common_types::{ transaction::TxId, types::{Commitment, PrivateKey}, }; -use tari_core::transactions::transaction::TransactionOutput; +use tari_core::transactions::transaction_entities::TransactionOutput; use tari_key_manager::cipher_seed::CipherSeed; use crate::{ @@ -1654,7 +1654,7 @@ mod test { use tari_core::transactions::{ tari_amount::MicroTari, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, + transaction_entities::{OutputFeatures, TransactionInput, UnblindedOutput}, CryptoFactories, }; use tari_crypto::script; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index f02317119a..dedf3c246e 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -49,7 +49,7 @@ use tari_core::{ tari_utilities::hash::Hashable, transactions::{ tari_amount::MicroTari, - transaction::{OutputFeatures, OutputFlags, UnblindedOutput}, + transaction_entities::{OutputFeatures, OutputFlags, UnblindedOutput}, CryptoFactories, }, }; diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs index 94541e42aa..dd13ba6ea1 100644 --- a/base_layer/wallet/src/transaction_service/error.rs +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -20,26 +20,28 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - error::WalletStorageError, - output_manager_service::error::OutputManagerError, - transaction_service::{ - storage::{database::DbKey, sqlite_db::CompletedTransactionConversionError}, - utc::NegativeDurationError, - }, -}; use diesel::result::Error as DieselError; use futures::channel::oneshot::Canceled; use serde_json::Error as SerdeJsonError; +use tari_crypto::tari_utilities::ByteArrayError; +use thiserror::Error; +use tokio::sync::broadcast::error::RecvError; + use tari_common_types::transaction::{TransactionConversionError, TransactionDirectionError, TxId}; use tari_comms::{connectivity::ConnectivityError, peer_manager::node_id::NodeIdError, protocol::rpc::RpcError}; use tari_comms_dht::outbound::DhtOutboundError; -use tari_core::transactions::{transaction::TransactionError, transaction_protocol::TransactionProtocolError}; -use tari_crypto::tari_utilities::ByteArrayError; +use tari_core::transactions::{transaction_entities::TransactionError, transaction_protocol::TransactionProtocolError}; use tari_p2p::services::liveness::error::LivenessError; use tari_service_framework::reply_channel::TransportChannelError; -use thiserror::Error; -use tokio::sync::broadcast::error::RecvError; + +use crate::{ + error::WalletStorageError, + output_manager_service::error::OutputManagerError, + transaction_service::{ + storage::{database::DbKey, sqlite_db::CompletedTransactionConversionError}, + utc::NegativeDurationError, + }, +}; #[derive(Debug, Error)] pub enum TransactionServiceError { diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index 7a34b0bd7d..0f91f8fee8 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -20,21 +20,24 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::transaction_service::{ - error::TransactionServiceError, - storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, -}; -use aes_gcm::Aes256Gcm; use std::{collections::HashMap, fmt, sync::Arc}; -use tari_common_types::transaction::TxId; -use tari_comms::types::CommsPublicKey; -use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction}; -use tari_service_framework::reply_channel::SenderService; + +use aes_gcm::Aes256Gcm; use tokio::sync::broadcast; use tower::Service; -use tari_common_types::types::PublicKey; -use tari_core::transactions::transaction::TransactionOutput; +use tari_common_types::{transaction::TxId, types::PublicKey}; +use tari_comms::types::CommsPublicKey; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction_entities::{Transaction, TransactionOutput}, +}; +use tari_service_framework::reply_channel::SenderService; + +use crate::transaction_service::{ + error::TransactionServiceError, + storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, +}; /// API Request enum #[allow(clippy::large_enum_variant)] diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs index 46d37f3ead..7bcf3ccb3e 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_broadcast_protocol.rs @@ -20,22 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - connectivity_service::WalletConnectivityInterface, - transaction_service::{ - error::{TransactionServiceError, TransactionServiceProtocolError}, - handle::TransactionEvent, - service::TransactionServiceResources, - storage::{database::TransactionBackend, models::CompletedTransaction}, - }, -}; -use futures::FutureExt; -use log::*; use std::{ convert::TryFrom, sync::Arc, time::{Duration, Instant}, }; + +use futures::FutureExt; +use log::*; +use tari_crypto::tari_utilities::hex::Hex; +use tokio::{sync::watch, time::sleep}; + use tari_common_types::{ transaction::{TransactionStatus, TxId}, types::Signature, @@ -45,10 +40,18 @@ use tari_core::{ proto::wallet_rpc::{TxLocation, TxQueryResponse, TxSubmissionRejectionReason, TxSubmissionResponse}, rpc::BaseNodeWalletRpcClient, }, - transactions::transaction::Transaction, + transactions::transaction_entities::Transaction, +}; + +use crate::{ + connectivity_service::WalletConnectivityInterface, + transaction_service::{ + error::{TransactionServiceError, TransactionServiceProtocolError}, + handle::TransactionEvent, + service::TransactionServiceResources, + storage::{database::TransactionBackend, models::CompletedTransaction}, + }, }; -use tari_crypto::tari_utilities::hex::Hex; -use tokio::{sync::watch, time::sleep}; const LOG_TARGET: &str = "wallet::transaction_service::protocols::broadcast_protocol"; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs index c02f61e2a2..2be90c9b48 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs @@ -42,7 +42,7 @@ use tokio::sync::{mpsc, oneshot}; use crate::connectivity_service::WalletConnectivityInterface; use tari_common_types::types::HashOutput; use tari_core::transactions::{ - transaction::Transaction, + transaction_entities::Transaction, transaction_protocol::{recipient::RecipientState, sender::TransactionSenderMessage}, }; use tari_crypto::tari_utilities::Hashable; diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index 13d2cd3d35..34d68644df 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -54,7 +54,7 @@ use tari_comms_dht::{ }; use tari_core::transactions::{ tari_amount::MicroTari, - transaction::KernelFeatures, + transaction_entities::KernelFeatures, transaction_protocol::{proto, recipient::RecipientSignedMessage, sender::SingleRoundSenderData}, SenderTransactionProtocol, }; diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 44d162a0fb..0b84803f1d 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -73,7 +73,7 @@ use tari_core::{ proto::base_node as base_node_proto, transactions::{ tari_amount::MicroTari, - transaction::{KernelFeatures, OutputFeatures, Transaction, TransactionOutput, UnblindedOutput}, + transaction_entities::{KernelFeatures, OutputFeatures, Transaction, TransactionOutput, UnblindedOutput}, transaction_protocol::{ proto, recipient::RecipientSignedMessage, diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index 8d97ebe971..1b58703013 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -20,30 +20,31 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::transaction_service::{ - error::TransactionStorageError, - storage::models::{CompletedTransaction, InboundTransaction, OutboundTransaction}, -}; -use aes_gcm::Aes256Gcm; -use chrono::Utc; -use log::*; - -use crate::transaction_service::storage::{ - models::WalletTransaction, - sqlite_db::{InboundTransactionSenderInfo, UnconfirmedTransactionInfo}, -}; use std::{ collections::HashMap, fmt, fmt::{Display, Error, Formatter}, sync::Arc, }; + +use aes_gcm::Aes256Gcm; +use chrono::Utc; +use log::*; + use tari_common_types::{ transaction::{TransactionDirection, TransactionStatus, TxId}, types::{BlindingFactor, BlockHash}, }; use tari_comms::types::CommsPublicKey; -use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction}; +use tari_core::transactions::{tari_amount::MicroTari, transaction_entities::Transaction}; + +use crate::transaction_service::{ + error::TransactionStorageError, + storage::{ + models::{CompletedTransaction, InboundTransaction, OutboundTransaction, WalletTransaction}, + sqlite_db::{InboundTransactionSenderInfo, UnconfirmedTransactionInfo}, + }, +}; const LOG_TARGET: &str = "wallet::transaction_service::database"; diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index a0925364a8..3b9189205a 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -22,6 +22,7 @@ use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; + use tari_common_types::{ transaction::{TransactionDirection, TransactionStatus, TxId}, types::{BlockHash, PrivateKey, Signature}, @@ -29,7 +30,7 @@ use tari_common_types::{ use tari_comms::types::CommsPublicKey; use tari_core::transactions::{ tari_amount::MicroTari, - transaction::Transaction, + transaction_entities::Transaction, ReceiverTransactionProtocol, SenderTransactionProtocol, }; diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 45c6370855..a276ce2737 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -2083,7 +2083,7 @@ mod test { use tari_core::transactions::{ tari_amount::MicroTari, test_helpers::{create_unblinded_output, TestParams}, - transaction::{OutputFeatures, Transaction}, + transaction_entities::{OutputFeatures, Transaction}, transaction_protocol::sender::TransactionSenderMessage, CryptoFactories, ReceiverTransactionProtocol, diff --git a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs index 6603457751..8c504d460b 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_finalized_transaction.rs @@ -1,3 +1,16 @@ +use std::time::Duration; + +use log::*; + +use tari_common_types::transaction::TxId; +use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageResponse}, +}; +use tari_core::transactions::{transaction_entities::Transaction, transaction_protocol::proto}; +use tari_p2p::tari_message::TariMessageType; + // Copyright 2020. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the @@ -24,16 +37,6 @@ use crate::transaction_service::{ error::TransactionServiceError, tasks::wait_on_dial::wait_on_dial, }; -use log::*; -use std::time::Duration; -use tari_common_types::transaction::TxId; -use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; -use tari_comms_dht::{ - domain_message::OutboundDomainMessage, - outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageResponse}, -}; -use tari_core::transactions::{transaction::Transaction, transaction_protocol::proto}; -use tari_p2p::tari_message::TariMessageType; const LOG_TARGET: &str = "wallet::transaction_service::tasks::send_finalized_transaction"; const LOG_TARGET_STRESS: &str = "stress_test::send_finalized_transaction"; diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs index d5afe96fd1..d3571c4b19 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs @@ -33,11 +33,17 @@ use chrono::Utc; use futures::StreamExt; use log::*; use serde::{Deserialize, Serialize}; -use tokio::{sync::broadcast, task, time}; +use tokio::{ + sync::{broadcast, watch}, + task, + time, + time::MissedTickBehavior, +}; use tari_common_types::{transaction::TxId, types::HashOutput}; use tari_comms::{ - peer_manager::NodeId, + connectivity::ConnectivityRequester, + peer_manager::{NodeId, Peer}, protocol::rpc::{RpcError, RpcStatus}, types::CommsPublicKey, NodeIdentity, @@ -52,7 +58,7 @@ use tari_core::{ tari_utilities::Hashable, transactions::{ tari_amount::MicroTari, - transaction::{TransactionOutput, UnblindedOutput}, + transaction_entities::{TransactionOutput, UnblindedOutput}, CryptoFactories, }, }; @@ -70,8 +76,6 @@ use crate::{ utxo_scanner_service::{error::UtxoScannerError, handle::UtxoScannerEvent}, WalletSqlite, }; -use tari_comms::{connectivity::ConnectivityRequester, peer_manager::Peer}; -use tokio::{sync::watch, time::MissedTickBehavior}; pub const LOG_TARGET: &str = "wallet::utxo_scanning"; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 11b044b65b..169dfc2703 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -47,7 +47,7 @@ use tari_core::{ consensus::NetworkConsensus, transactions::{ tari_amount::MicroTari, - transaction::{OutputFeatures, UnblindedOutput}, + transaction_entities::{OutputFeatures, UnblindedOutput}, CryptoFactories, }, }; diff --git a/base_layer/wallet/tests/output_manager_service/service.rs b/base_layer/wallet/tests/output_manager_service/service.rs index a420a1c4ca..f88dd9e25b 100644 --- a/base_layer/wallet/tests/output_manager_service/service.rs +++ b/base_layer/wallet/tests/output_manager_service/service.rs @@ -45,7 +45,7 @@ use tari_core::{ fee::Fee, tari_amount::{uT, MicroTari}, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction::OutputFeatures, + transaction_entities::OutputFeatures, transaction_protocol::sender::TransactionSenderMessage, CryptoFactories, SenderTransactionProtocol, diff --git a/base_layer/wallet/tests/support/comms_rpc.rs b/base_layer/wallet/tests/support/comms_rpc.rs index 05188342f8..5163e6180f 100644 --- a/base_layer/wallet/tests/support/comms_rpc.rs +++ b/base_layer/wallet/tests/support/comms_rpc.rs @@ -60,7 +60,7 @@ use tari_core::{ }, }, tari_utilities::Hashable, - transactions::transaction::{Transaction, TransactionOutput}, + transactions::transaction_entities::{Transaction, TransactionOutput}, }; use tokio::time::sleep; @@ -662,7 +662,7 @@ mod test { rpc::{BaseNodeWalletRpcClient, BaseNodeWalletRpcServer}, }, proto::base_node::{ChainMetadata, TipInfoResponse}, - transactions::transaction::Transaction, + transactions::transaction_entities::Transaction, }; use tokio::time::Duration; diff --git a/base_layer/wallet/tests/support/utils.rs b/base_layer/wallet/tests/support/utils.rs index e0eb6e7312..16c62e2e1f 100644 --- a/base_layer/wallet/tests/support/utils.rs +++ b/base_layer/wallet/tests/support/utils.rs @@ -20,17 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rand::{CryptoRng, Rng}; use std::{fmt::Debug, thread, time::Duration}; + +use rand::{CryptoRng, Rng}; +use tari_crypto::{ + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, + script, +}; + use tari_common_types::types::{CommitmentFactory, PrivateKey, PublicKey}; use tari_core::transactions::{ tari_amount::MicroTari, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, -}; -use tari_crypto::{ - keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, - script, + transaction_entities::{OutputFeatures, TransactionInput, UnblindedOutput}, }; pub fn assert_change(mut func: F, to: T, poll_count: usize) diff --git a/base_layer/wallet/tests/transaction_service/service.rs b/base_layer/wallet/tests/transaction_service/service.rs index 3d25188ddc..741e022469 100644 --- a/base_layer/wallet/tests/transaction_service/service.rs +++ b/base_layer/wallet/tests/transaction_service/service.rs @@ -91,7 +91,7 @@ use tari_core::{ fee::Fee, tari_amount::*, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction::{KernelBuilder, KernelFeatures, OutputFeatures, Transaction}, + transaction_entities::{KernelBuilder, KernelFeatures, OutputFeatures, Transaction}, transaction_protocol::{proto, recipient::RecipientSignedMessage, sender::TransactionSenderMessage}, CryptoFactories, ReceiverTransactionProtocol, diff --git a/base_layer/wallet/tests/transaction_service/storage.rs b/base_layer/wallet/tests/transaction_service/storage.rs index 54d51c3c92..64c157997a 100644 --- a/base_layer/wallet/tests/transaction_service/storage.rs +++ b/base_layer/wallet/tests/transaction_service/storage.rs @@ -33,7 +33,7 @@ use tari_common_types::{ use tari_core::transactions::{ tari_amount::{uT, MicroTari}, test_helpers::{create_unblinded_output, TestParams}, - transaction::{OutputFeatures, Transaction}, + transaction_entities::{OutputFeatures, Transaction}, transaction_protocol::sender::TransactionSenderMessage, CryptoFactories, ReceiverTransactionProtocol, diff --git a/base_layer/wallet/tests/wallet/mod.rs b/base_layer/wallet/tests/wallet/mod.rs index 376d7f1f5c..bd6e6f8d79 100644 --- a/base_layer/wallet/tests/wallet/mod.rs +++ b/base_layer/wallet/tests/wallet/mod.rs @@ -43,7 +43,7 @@ use tari_comms_dht::{store_forward::SafConfig, DhtConfig}; use tari_core::transactions::{ tari_amount::{uT, MicroTari}, test_helpers::{create_unblinded_output, TestParams}, - transaction::OutputFeatures, + transaction_entities::OutputFeatures, CryptoFactories, }; use tari_key_manager::cipher_seed::CipherSeed; diff --git a/base_layer/wallet_ffi/src/callback_handler_tests.rs b/base_layer/wallet_ffi/src/callback_handler_tests.rs index 2659eeb1a3..e544fab345 100644 --- a/base_layer/wallet_ffi/src/callback_handler_tests.rs +++ b/base_layer/wallet_ffi/src/callback_handler_tests.rs @@ -22,14 +22,17 @@ #[cfg(test)] mod test { - use crate::{callback_handler::CallbackHandler, output_manager_service_mock::MockOutputManagerService}; - use chrono::Utc; - use rand::rngs::OsRng; use std::{ sync::{Arc, Mutex}, thread, time::Duration, }; + + use chrono::Utc; + use rand::rngs::OsRng; + use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; + use tokio::{runtime::Runtime, sync::broadcast, time::Instant}; + use tari_common_types::{ transaction::{TransactionDirection, TransactionStatus}, types::{BlindingFactor, PrivateKey, PublicKey}, @@ -37,11 +40,10 @@ mod test { use tari_comms_dht::event::DhtEvent; use tari_core::transactions::{ tari_amount::{uT, MicroTari}, - transaction::Transaction, + transaction_entities::Transaction, ReceiverTransactionProtocol, SenderTransactionProtocol, }; - use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; use tari_service_framework::reply_channel; use tari_shutdown::Shutdown; use tari_wallet::{ @@ -59,7 +61,8 @@ mod test { }, }, }; - use tokio::{runtime::Runtime, sync::broadcast, time::Instant}; + + use crate::{callback_handler::CallbackHandler, output_manager_service_mock::MockOutputManagerService}; struct CallbackState { pub received_tx_callback_called: bool, diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 6bd2cd6136..321c7ddceb 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -158,7 +158,7 @@ use tari_comms::{ types::CommsSecretKey, }; use tari_comms_dht::{store_forward::SafConfig, DbConnectionUrl, DhtConfig}; -use tari_core::transactions::{tari_amount::MicroTari, transaction::OutputFeatures, CryptoFactories}; +use tari_core::transactions::{tari_amount::MicroTari, transaction_entities::OutputFeatures, CryptoFactories}; use tari_key_manager::cipher_seed::CipherSeed; use tari_p2p::{ transport::{TorConfig, TransportType, TransportType::Tor}, @@ -210,7 +210,7 @@ pub type TariTransportType = tari_p2p::transport::TransportType; pub type TariPublicKey = tari_comms::types::CommsPublicKey; pub type TariPrivateKey = tari_comms::types::CommsSecretKey; pub type TariCommsConfig = tari_p2p::initialization::P2pConfig; -pub type TariTransactionKernel = tari_core::transactions::transaction::TransactionKernel; +pub type TariTransactionKernel = tari_core::transactions::transaction_entities::TransactionKernel; pub struct TariContacts(Vec); From a2a5acdbc0b93d380879a11d4c315f1eec92b4f9 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Fri, 19 Nov 2021 08:54:50 +0200 Subject: [PATCH 2/2] alternative proposal --- .../transactions/transaction_entities/mod.rs | 2 +- .../transaction_entities/unblinded_output.rs | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/base_layer/core/src/transactions/transaction_entities/mod.rs b/base_layer/core/src/transactions/transaction_entities/mod.rs index b5d06cde93..34a1d76771 100644 --- a/base_layer/core/src/transactions/transaction_entities/mod.rs +++ b/base_layer/core/src/transactions/transaction_entities/mod.rs @@ -118,7 +118,7 @@ mod test { let output = i.as_transaction_output(&CryptoFactories::default()).unwrap(); let input = i.as_transaction_input(&factory).unwrap(); assert_eq!(output.hash(), input.output_hash()); - assert_eq!(output.hash(), i.hash()); + assert_eq!(output.hash(), i.hash(&CryptoFactories::default())); } #[test] diff --git a/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs b/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs index f3acae2c6b..7e4208db3b 100644 --- a/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs +++ b/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs @@ -30,7 +30,6 @@ use crate::{ range_proof::{RangeProofError, RangeProofService}, script::{ExecutionStack, TariScript}, }, - tari_utilities::Hashable, transactions::{ tari_amount::MicroTari, transaction_entities, @@ -195,6 +194,13 @@ impl UnblindedOutput { self.features.consensus_encode_exact_size() + ConsensusEncodingWrapper::wrap(&self.script).consensus_encode_exact_size() } + + // Note: The Hashable trait is not used here due to the dependency on `CryptoFactories`, and `commitment` us not + // Note: added to the struct to ensure the atomic nature between `commitment`, `spending_key` and `value`. + pub fn hash(&self, factories: &CryptoFactories) -> Vec { + let commitment = factories.commitment.commit_value(&self.spending_key, self.value.into()); + transaction_entities::hash_output(&self.features, &commitment, &self.script) + } } // These implementations are used for order these outputs for UTXO selection which will be done by comparing the values @@ -217,12 +223,3 @@ impl Ord for UnblindedOutput { self.value.cmp(&other.value) } } - -/// Implement the canonical hashing function for UnblindedOutput for use in ordering. -impl Hashable for UnblindedOutput { - fn hash(&self) -> Vec { - let factories = CryptoFactories::default(); - let commitment = factories.commitment.commit_value(&self.spending_key, self.value.into()); - transaction_entities::hash_output(&self.features, &commitment, &self.script) - } -}