From eb8b8152ecc4cd9ccf49a7fe23fe0e2c77ff2c63 Mon Sep 17 00:00:00 2001 From: Stanimal Date: Tue, 16 Nov 2021 21:30:38 +0400 Subject: [PATCH 01/13] fix: remove delay from last request latency call --- Cargo.lock | 4 -- .../horizon_state_synchronization.rs | 4 +- .../sync/header_sync/synchronizer.rs | 2 +- .../wallet/src/base_node_service/monitor.rs | 2 +- .../src/utxo_scanner_service/utxo_scanning.rs | 2 +- .../tests/transaction_service/service.rs | 8 ++-- common/Cargo.toml | 6 --- comms/rpc_macros/src/generator.rs | 4 +- comms/src/protocol/rpc/client/mod.rs | 40 +++++++++---------- comms/src/protocol/rpc/client/tests.rs | 34 +++++++++++++++- .../src/protocol/rpc/test/greeting_service.rs | 4 +- comms/src/protocol/rpc/test/smoke.rs | 2 +- 12 files changed, 66 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d674d81979..06ba280191 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4513,8 +4513,6 @@ dependencies = [ "log", "log4rs 1.0.0", "multiaddr", - "opentelemetry", - "opentelemetry-jaeger", "path-clean", "prost-build", "serde 1.0.130", @@ -4527,8 +4525,6 @@ dependencies = [ "thiserror", "toml 0.5.8", "tracing", - "tracing-opentelemetry", - "tracing-subscriber", ] [[package]] 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..75ff753d50 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 @@ -159,7 +159,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { remote_num_kernels - local_num_kernels, ); - let latency = client.get_last_request_latency().await?; + let latency = client.get_last_request_latency(); debug!( target: LOG_TARGET, "Initiating kernel sync with peer `{}` (latency = {}ms)", @@ -287,7 +287,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { let end = remote_num_outputs; let end_hash = to_header.hash(); - let latency = client.get_last_request_latency().await?; + let latency = client.get_last_request_latency(); debug!( target: LOG_TARGET, "Initiating output sync with peer `{}` (latency = {}ms)", diff --git a/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs b/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs index 4ff044218f..ca594087b6 100644 --- a/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs +++ b/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs @@ -218,7 +218,7 @@ impl<'a, B: BlockchainBackend + 'static> HeaderSynchronizer<'a, B> { mut conn: PeerConnection, ) -> Result<(), BlockHeaderSyncError> { let mut client = conn.connect_rpc::().await?; - let latency = client.get_last_request_latency().await?; + let latency = client.get_last_request_latency(); debug!( target: LOG_TARGET, "Initiating header sync with peer `{}` (sync latency = {}ms)", diff --git a/base_layer/wallet/src/base_node_service/monitor.rs b/base_layer/wallet/src/base_node_service/monitor.rs index 11003eff8b..9a7df7630b 100644 --- a/base_layer/wallet/src/base_node_service/monitor.rs +++ b/base_layer/wallet/src/base_node_service/monitor.rs @@ -150,7 +150,7 @@ where timer.elapsed().as_millis() ); - let latency = match client.get_last_request_latency().await? { + let latency = match client.get_last_request_latency() { Some(latency) => latency, None => continue, }; 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..ed21d9d029 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs @@ -268,7 +268,7 @@ where TBackend: WalletBackend + 'static .connect_rpc_using_builder(BaseNodeSyncRpcClient::builder().with_deadline(Duration::from_secs(60))) .await?; - let latency = client.get_last_request_latency().await?; + let latency = client.get_last_request_latency(); self.publish_event(UtxoScannerEvent::ConnectedToBaseNode( peer.clone(), latency.unwrap_or_default(), diff --git a/base_layer/wallet/tests/transaction_service/service.rs b/base_layer/wallet/tests/transaction_service/service.rs index d9b058ad49..eb0593bb67 100644 --- a/base_layer/wallet/tests/transaction_service/service.rs +++ b/base_layer/wallet/tests/transaction_service/service.rs @@ -944,7 +944,7 @@ fn test_htlc_send_and_claim() { let bob_connection = run_migration_and_create_sqlite_connection(&bob_db_path, 16).unwrap(); let shutdown = Shutdown::new(); - let (alice_ts, alice_oms, _alice_comms, mut alice_connectivity) = setup_transaction_service( + let (mut alice_ts, mut alice_oms, _alice_comms, mut alice_connectivity) = setup_transaction_service( &mut runtime, alice_node_identity, vec![], @@ -998,10 +998,8 @@ fn test_htlc_send_and_claim() { .expect("Alice sending HTLC transaction") }); - let mut alice_ts_clone2 = alice_ts.clone(); - let mut alice_oms_clone = alice_oms.clone(); runtime.block_on(async move { - let completed_tx = alice_ts_clone2 + let completed_tx = alice_ts .get_completed_transaction(tx_id) .await .expect("Could not find completed HTLC tx"); @@ -1009,7 +1007,7 @@ fn test_htlc_send_and_claim() { let fees = completed_tx.fee; assert_eq!( - alice_oms_clone.get_balance().await.unwrap().pending_incoming_balance, + alice_oms.get_balance().await.unwrap().pending_incoming_balance, initial_wallet_value - value - fees ); }); diff --git a/common/Cargo.toml b/common/Cargo.toml index 8dd805cedd..67cb608d9b 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -27,12 +27,6 @@ sha2 = "0.9.5" path-clean = "0.1.0" tari_storage = { version = "^0.21", path = "../infrastructure/storage"} tracing = "0.1.26" -tracing-opentelemetry = "0.15.0" -tracing-subscriber = "0.2.20" - -# network tracing, rt-tokio for async batch export -opentelemetry = { version = "0.16", default-features = false, features = ["trace","rt-tokio"] } -opentelemetry-jaeger = { version="0.15", features=["rt-tokio"]} anyhow = { version = "1.0", optional = true } git2 = { version = "0.8", optional = true } diff --git a/comms/rpc_macros/src/generator.rs b/comms/rpc_macros/src/generator.rs index 312e318284..d3fe17df88 100644 --- a/comms/rpc_macros/src/generator.rs +++ b/comms/rpc_macros/src/generator.rs @@ -208,8 +208,8 @@ impl RpcCodeGenerator { #client_methods - pub async fn get_last_request_latency(&mut self) -> Result, #dep_mod::RpcError> { - self.inner.get_last_request_latency().await + pub fn get_last_request_latency(&mut self) -> Option { + self.inner.get_last_request_latency() } pub async fn ping(&mut self) -> Result { diff --git a/comms/src/protocol/rpc/client/mod.rs b/comms/src/protocol/rpc/client/mod.rs index bdaa57c368..52abf1049f 100644 --- a/comms/src/protocol/rpc/client/mod.rs +++ b/comms/src/protocol/rpc/client/mod.rs @@ -73,7 +73,7 @@ use std::{ use tari_shutdown::{Shutdown, ShutdownSignal}; use tokio::{ io::{AsyncRead, AsyncWrite}, - sync::{mpsc, oneshot, Mutex}, + sync::{mpsc, oneshot, watch, Mutex}, time, }; use tower::{Service, ServiceExt}; @@ -105,7 +105,8 @@ impl RpcClient { let (request_tx, request_rx) = mpsc::channel(1); let shutdown = Shutdown::new(); let shutdown_signal = shutdown.to_signal(); - let connector = ClientConnector::new(request_tx, shutdown); + let (last_request_latency_tx, last_request_latency_rx) = watch::channel(None); + let connector = ClientConnector::new(request_tx, last_request_latency_rx, shutdown); let (ready_tx, ready_rx) = oneshot::channel(); let tracing_id = tracing::Span::current().id(); task::spawn({ @@ -116,6 +117,7 @@ impl RpcClient { config, node_id, request_rx, + last_request_latency_tx, framed, ready_tx, protocol_name, @@ -172,7 +174,7 @@ impl RpcClient { } /// Return the latency of the last request - pub fn get_last_request_latency(&mut self) -> impl Future, RpcError>> + '_ { + pub fn get_last_request_latency(&mut self) -> Option { self.connector.get_last_request_latency() } @@ -315,13 +317,19 @@ impl Default for RpcClientConfig { #[derive(Clone)] pub struct ClientConnector { inner: mpsc::Sender, + last_request_latency_rx: watch::Receiver>, shutdown: Arc>, } impl ClientConnector { - pub(self) fn new(sender: mpsc::Sender, shutdown: Shutdown) -> Self { + pub(self) fn new( + sender: mpsc::Sender, + last_request_latency_rx: watch::Receiver>, + shutdown: Shutdown, + ) -> Self { Self { inner: sender, + last_request_latency_rx, shutdown: Arc::new(Mutex::new(shutdown)), } } @@ -331,14 +339,8 @@ impl ClientConnector { lock.trigger(); } - pub async fn get_last_request_latency(&mut self) -> Result, RpcError> { - let (reply, reply_rx) = oneshot::channel(); - self.inner - .send(ClientRequest::GetLastRequestLatency(reply)) - .await - .map_err(|_| RpcError::ClientClosed)?; - - reply_rx.await.map_err(|_| RpcError::RequestCancelled) + pub fn get_last_request_latency(&mut self) -> Option { + *self.last_request_latency_rx.borrow() } pub async fn send_ping(&mut self) -> Result { @@ -391,12 +393,12 @@ struct RpcClientWorker { config: RpcClientConfig, node_id: NodeId, request_rx: mpsc::Receiver, + last_request_latency_tx: watch::Sender>, framed: CanonicalFraming, // Request ids are limited to u16::MAX because varint encoding is used over the wire and the magnitude of the value // sent determines the byte size. A u16 will be more than enough for the purpose next_request_id: u16, ready_tx: Option>>, - last_request_latency: Option, protocol_id: ProtocolId, shutdown_signal: ShutdownSignal, } @@ -404,10 +406,12 @@ struct RpcClientWorker { impl RpcClientWorker where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId { + #[allow(clippy::too_many_arguments)] pub(self) fn new( config: RpcClientConfig, node_id: NodeId, request_rx: mpsc::Receiver, + last_request_latency_tx: watch::Sender>, framed: CanonicalFraming, ready_tx: oneshot::Sender>, protocol_id: ProtocolId, @@ -420,7 +424,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId framed, next_request_id: 0, ready_tx: Some(ready_tx), - last_request_latency: None, + last_request_latency_tx, protocol_id, shutdown_signal, } @@ -454,7 +458,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId self.protocol_name(), latency ); - self.last_request_latency = Some(latency); + let _ = self.last_request_latency_tx.send(Some(latency)); if let Some(r) = self.ready_tx.take() { let _ = r.send(Ok(())); } @@ -514,9 +518,6 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId SendRequest { request, reply } => { self.do_request_response(request, reply).await?; }, - GetLastRequestLatency(reply) => { - let _ = reply.send(self.last_request_latency); - }, SendPing(reply) => { self.do_ping_pong(reply).await?; }, @@ -647,7 +648,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId let resp = match self.read_response(request_id).await { Ok(resp) => { if let Some(t) = timer.take() { - self.last_request_latency = Some(t.elapsed()); + let _ = self.last_request_latency_tx.send(Some(t.elapsed())); } event!(Level::TRACE, "Message received"); trace!( @@ -821,7 +822,6 @@ pub enum ClientRequest { request: BaseRequest, reply: oneshot::Sender, RpcStatus>>>, }, - GetLastRequestLatency(oneshot::Sender>), SendPing(oneshot::Sender>), } diff --git a/comms/src/protocol/rpc/client/tests.rs b/comms/src/protocol/rpc/client/tests.rs index 0dc4a4c595..d12faeb7d4 100644 --- a/comms/src/protocol/rpc/client/tests.rs +++ b/comms/src/protocol/rpc/client/tests.rs @@ -25,7 +25,7 @@ use crate::{ protocol::{ rpc::{ test::{ - greeting_service::{GreetingClient, GreetingServer, GreetingService}, + greeting_service::{GreetingClient, GreetingServer, GreetingService, SlowStreamRequest}, mock::create_mocked_rpc_context, }, NamedProtocolService, @@ -39,9 +39,11 @@ use crate::{ runtime::task, test_utils::mocks::{new_peer_connection_mock_pair, PeerConnectionMockState}, }; +use std::{env, time::Duration}; use tari_shutdown::Shutdown; use tari_test_utils::{async_assert_eventually, unpack_enum}; use tokio::sync::mpsc; +use tokio_stream::StreamExt; async fn setup(num_concurrent_sessions: usize) -> (PeerConnection, PeerConnectionMockState, Shutdown) { let (conn1, conn1_state, conn2, conn2_state) = new_peer_connection_mock_pair().await; @@ -171,3 +173,33 @@ mod lazy_pool { unpack_enum!(RpcClientPoolError::PeerConnectionDropped { .. } = err); } } + +mod last_request_latency { + use super::*; + + #[runtime::test] + async fn it_returns_the_latency_until_the_first_response() { + let (mut conn, _, _shutdown) = setup(1).await; + + let mut client = conn.connect_rpc::().await.unwrap(); + + let resp = client + .slow_stream(SlowStreamRequest { + num_items: 100, + item_size: 10, + delay_ms: 10, + }) + .await + .unwrap(); + + resp.collect::>().await.into_iter().for_each(|r| { + r.unwrap(); + }); + + let latency = client.get_last_request_latency().unwrap(); + // CI could be really slow, so to prevent flakiness exclude the assert + if env::var("CI").is_err() { + assert!(latency < Duration::from_millis(100)); + } + } +} diff --git a/comms/src/protocol/rpc/test/greeting_service.rs b/comms/src/protocol/rpc/test/greeting_service.rs index d974f433cc..00d3aad61e 100644 --- a/comms/src/protocol/rpc/test/greeting_service.rs +++ b/comms/src/protocol/rpc/test/greeting_service.rs @@ -447,8 +447,8 @@ impl GreetingClient { self.inner.server_streaming(request, 8).await } - pub async fn get_last_request_latency(&mut self) -> Result, RpcError> { - self.inner.get_last_request_latency().await + pub fn get_last_request_latency(&mut self) -> Option { + self.inner.get_last_request_latency() } pub async fn ping(&mut self) -> Result { diff --git a/comms/src/protocol/rpc/test/smoke.rs b/comms/src/protocol/rpc/test/smoke.rs index 8ca1bfce52..0fc1f05256 100644 --- a/comms/src/protocol/rpc/test/smoke.rs +++ b/comms/src/protocol/rpc/test/smoke.rs @@ -135,7 +135,7 @@ async fn request_response_errors_and_streaming() { .unwrap(); // Latency is available "for free" as part of the connect protocol - assert!(client.get_last_request_latency().await.unwrap().is_some()); + assert!(client.get_last_request_latency().is_some()); let resp = client .say_hello(SayHelloRequest { From 1fdc13d105efc591e4dbf88a7503bda0998d318e Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Fri, 19 Nov 2021 08:37:16 +0200 Subject: [PATCH 02/13] v0.21.2 --- Cargo.lock | 48 +++++++++---------- applications/tari_app_grpc/Cargo.toml | 2 +- applications/tari_app_utilities/Cargo.toml | 2 +- applications/tari_base_node/Cargo.toml | 2 +- applications/tari_console_wallet/Cargo.toml | 2 +- .../tari_merge_mining_proxy/Cargo.toml | 2 +- applications/tari_mining_node/Cargo.toml | 2 +- applications/test_faucet/Cargo.toml | 2 +- base_layer/common_types/Cargo.toml | 2 +- base_layer/core/Cargo.toml | 2 +- base_layer/key_manager/Cargo.toml | 2 +- base_layer/mmr/Cargo.toml | 2 +- base_layer/p2p/Cargo.toml | 2 +- base_layer/service_framework/Cargo.toml | 2 +- base_layer/tari_stratum_ffi/Cargo.toml | 2 +- base_layer/wallet/Cargo.toml | 2 +- base_layer/wallet_ffi/Cargo.toml | 2 +- changelog.md | 7 +++ common/Cargo.toml | 2 +- comms/Cargo.toml | 2 +- comms/dht/Cargo.toml | 2 +- comms/rpc_macros/Cargo.toml | 2 +- infrastructure/derive/Cargo.toml | 2 +- infrastructure/shutdown/Cargo.toml | 2 +- infrastructure/storage/Cargo.toml | 2 +- infrastructure/test_utils/Cargo.toml | 2 +- package-lock.json | 2 +- 27 files changed, 56 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e8078ceae..eab17e3ff3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4401,7 +4401,7 @@ dependencies = [ [[package]] name = "tari_app_grpc" -version = "0.21.1" +version = "0.21.2" dependencies = [ "chrono", "prost", @@ -4416,7 +4416,7 @@ dependencies = [ [[package]] name = "tari_app_utilities" -version = "0.21.1" +version = "0.21.2" dependencies = [ "config", "dirs-next 1.0.2", @@ -4441,7 +4441,7 @@ dependencies = [ [[package]] name = "tari_base_node" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "bincode", @@ -4503,7 +4503,7 @@ dependencies = [ [[package]] name = "tari_common" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "config", @@ -4542,7 +4542,7 @@ dependencies = [ [[package]] name = "tari_common_types" -version = "0.21.1" +version = "0.21.2" dependencies = [ "digest", "futures 0.3.17", @@ -4556,7 +4556,7 @@ dependencies = [ [[package]] name = "tari_comms" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "async-trait", @@ -4606,7 +4606,7 @@ dependencies = [ [[package]] name = "tari_comms_dht" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "bitflags 1.3.2", @@ -4654,7 +4654,7 @@ dependencies = [ [[package]] name = "tari_comms_rpc_macros" -version = "0.21.1" +version = "0.21.2" dependencies = [ "futures 0.3.17", "proc-macro2 1.0.32", @@ -4669,7 +4669,7 @@ dependencies = [ [[package]] name = "tari_console_wallet" -version = "0.21.1" +version = "0.21.2" dependencies = [ "bitflags 1.3.2", "chrono", @@ -4712,7 +4712,7 @@ dependencies = [ [[package]] name = "tari_core" -version = "0.21.1" +version = "0.21.2" dependencies = [ "async-trait", "bincode", @@ -4794,7 +4794,7 @@ dependencies = [ [[package]] name = "tari_infra_derive" -version = "0.21.1" +version = "0.21.2" dependencies = [ "blake2", "proc-macro2 0.4.30", @@ -4804,7 +4804,7 @@ dependencies = [ [[package]] name = "tari_key_manager" -version = "0.21.1" +version = "0.21.2" dependencies = [ "argon2", "arrayvec 0.7.1", @@ -4824,7 +4824,7 @@ dependencies = [ [[package]] name = "tari_merge_mining_proxy" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "bincode", @@ -4870,7 +4870,7 @@ dependencies = [ [[package]] name = "tari_mining_node" -version = "0.21.1" +version = "0.21.2" dependencies = [ "bufstream", "chrono", @@ -4900,7 +4900,7 @@ dependencies = [ [[package]] name = "tari_mmr" -version = "0.21.1" +version = "0.21.2" dependencies = [ "bincode", "blake2", @@ -4919,7 +4919,7 @@ dependencies = [ [[package]] name = "tari_p2p" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "bytes 0.5.6", @@ -4963,7 +4963,7 @@ dependencies = [ [[package]] name = "tari_service_framework" -version = "0.21.1" +version = "0.21.2" dependencies = [ "anyhow", "async-trait", @@ -4980,7 +4980,7 @@ dependencies = [ [[package]] name = "tari_shutdown" -version = "0.21.1" +version = "0.21.2" dependencies = [ "futures 0.3.17", "tokio 1.13.0", @@ -4988,7 +4988,7 @@ dependencies = [ [[package]] name = "tari_storage" -version = "0.21.1" +version = "0.21.2" dependencies = [ "bincode", "bytes 0.5.6", @@ -5006,7 +5006,7 @@ dependencies = [ [[package]] name = "tari_stratum_ffi" -version = "0.21.1" +version = "0.21.2" dependencies = [ "hex", "libc", @@ -5060,7 +5060,7 @@ dependencies = [ [[package]] name = "tari_test_utils" -version = "0.21.1" +version = "0.21.2" dependencies = [ "futures 0.3.17", "futures-test", @@ -5091,7 +5091,7 @@ dependencies = [ [[package]] name = "tari_wallet" -version = "0.21.1" +version = "0.21.2" dependencies = [ "aes-gcm 0.8.0", "argon2", @@ -5137,7 +5137,7 @@ dependencies = [ [[package]] name = "tari_wallet_ffi" -version = "0.21.1" +version = "0.21.2" dependencies = [ "chrono", "env_logger 0.7.1", @@ -5190,7 +5190,7 @@ dependencies = [ [[package]] name = "test_faucet" -version = "0.21.1" +version = "0.21.2" dependencies = [ "rand 0.8.4", "serde 1.0.130", diff --git a/applications/tari_app_grpc/Cargo.toml b/applications/tari_app_grpc/Cargo.toml index f3e7262c06..a712d82f8f 100644 --- a/applications/tari_app_grpc/Cargo.toml +++ b/applications/tari_app_grpc/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "This crate is to provide a single source for all cross application grpc files and conversions to and from tari::core" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/applications/tari_app_utilities/Cargo.toml b/applications/tari_app_utilities/Cargo.toml index 48c8f0484f..a943e35544 100644 --- a/applications/tari_app_utilities/Cargo.toml +++ b/applications/tari_app_utilities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tari_app_utilities" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] edition = "2018" diff --git a/applications/tari_base_node/Cargo.toml b/applications/tari_base_node/Cargo.toml index 440ee59ea8..491835884c 100644 --- a/applications/tari_base_node/Cargo.toml +++ b/applications/tari_base_node/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "The tari full base node implementation" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index e7827d6e9d..e336dba15f 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tari_console_wallet" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] edition = "2018" diff --git a/applications/tari_merge_mining_proxy/Cargo.toml b/applications/tari_merge_mining_proxy/Cargo.toml index fc058caee9..fffe158f96 100644 --- a/applications/tari_merge_mining_proxy/Cargo.toml +++ b/applications/tari_merge_mining_proxy/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "The tari merge miner proxy for xmrig" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [features] diff --git a/applications/tari_mining_node/Cargo.toml b/applications/tari_mining_node/Cargo.toml index 8d3dadd3db..1876371903 100644 --- a/applications/tari_mining_node/Cargo.toml +++ b/applications/tari_mining_node/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "The tari mining node implementation" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/applications/test_faucet/Cargo.toml b/applications/test_faucet/Cargo.toml index 1e5ee98504..0905df40b0 100644 --- a/applications/test_faucet/Cargo.toml +++ b/applications/test_faucet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_faucet" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] edition = "2018" diff --git a/base_layer/common_types/Cargo.toml b/base_layer/common_types/Cargo.toml index a15d105675..d7b1739226 100644 --- a/base_layer/common_types/Cargo.toml +++ b/base_layer/common_types/Cargo.toml @@ -3,7 +3,7 @@ name = "tari_common_types" authors = ["The Tari Development Community"] description = "Tari cryptocurrency common types" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/base_layer/core/Cargo.toml b/base_layer/core/Cargo.toml index 3f9b98c03c..c64bb16bf4 100644 --- a/base_layer/core/Cargo.toml +++ b/base_layer/core/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [features] diff --git a/base_layer/key_manager/Cargo.toml b/base_layer/key_manager/Cargo.toml index 18c64eb6d5..d80eeb5038 100644 --- a/base_layer/key_manager/Cargo.toml +++ b/base_layer/key_manager/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "Tari cryptocurrency wallet key management" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/base_layer/mmr/Cargo.toml b/base_layer/mmr/Cargo.toml index 62663d291f..6c5aa8111d 100644 --- a/base_layer/mmr/Cargo.toml +++ b/base_layer/mmr/Cargo.toml @@ -4,7 +4,7 @@ authors = ["The Tari Development Community"] description = "A Merkle Mountain Range implementation" repository = "https://github.com/tari-project/tari" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [features] diff --git a/base_layer/p2p/Cargo.toml b/base_layer/p2p/Cargo.toml index e3a7b6f07d..782551c063 100644 --- a/base_layer/p2p/Cargo.toml +++ b/base_layer/p2p/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tari_p2p" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development community"] description = "Tari base layer-specific peer-to-peer communication features" repository = "https://github.com/tari-project/tari" diff --git a/base_layer/service_framework/Cargo.toml b/base_layer/service_framework/Cargo.toml index bc288cf93e..82461264b1 100644 --- a/base_layer/service_framework/Cargo.toml +++ b/base_layer/service_framework/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tari_service_framework" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] description = "The Tari communication stack service framework" repository = "https://github.com/tari-project/tari" diff --git a/base_layer/tari_stratum_ffi/Cargo.toml b/base_layer/tari_stratum_ffi/Cargo.toml index 805be3e4b9..87c950085f 100644 --- a/base_layer/tari_stratum_ffi/Cargo.toml +++ b/base_layer/tari_stratum_ffi/Cargo.toml @@ -3,7 +3,7 @@ name = "tari_stratum_ffi" authors = ["The Tari Development Community"] description = "Tari cryptocurrency miningcore C FFI bindings" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/base_layer/wallet/Cargo.toml b/base_layer/wallet/Cargo.toml index 13c6e527e3..18c7c003be 100644 --- a/base_layer/wallet/Cargo.toml +++ b/base_layer/wallet/Cargo.toml @@ -3,7 +3,7 @@ name = "tari_wallet" authors = ["The Tari Development Community"] description = "Tari cryptocurrency wallet library" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/base_layer/wallet_ffi/Cargo.toml b/base_layer/wallet_ffi/Cargo.toml index 49ddf8016e..3980446a7a 100644 --- a/base_layer/wallet_ffi/Cargo.toml +++ b/base_layer/wallet_ffi/Cargo.toml @@ -3,7 +3,7 @@ name = "tari_wallet_ffi" authors = ["The Tari Development Community"] description = "Tari cryptocurrency wallet C FFI bindings" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/changelog.md b/changelog.md index d2f69f8cfd..c8d5710ad2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ # Changelog +### [0.21.2](https://github.com/tari-project/tari/compare/v0.21.1...v0.21.2) (2021-11-19) + +### Features + +* add atomic swap refund transaction handling ([#3573](https://github.com/tari-project/tari/issues/3573)) ([337bc6f](https://github.com/tari-project/tari/commit/337bc6f1b11abc0f53cdc3a82a0aa5110e1fe856)) +* improve wallet connectivity status for console wallet ([#3577](https://github.com/tari-project/tari/issues/3577)) ([e191e27](https://github.com/tari-project/tari/commit/e191e27ed79aff5cfe4d76effe77473a03eb31f6)) + ### [0.21.1](https://github.com/tari-project/tari/compare/v0.21.0...v0.21.1) (2021-11-17) diff --git a/common/Cargo.toml b/common/Cargo.toml index 4ed687e6da..404fd38de2 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [features] diff --git a/comms/Cargo.toml b/comms/Cargo.toml index 091275ca46..6092edf614 100644 --- a/comms/Cargo.toml +++ b/comms/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/comms/dht/Cargo.toml b/comms/dht/Cargo.toml index a02696fdbb..b3dec63a37 100644 --- a/comms/dht/Cargo.toml +++ b/comms/dht/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tari_comms_dht" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] description = "Tari comms DHT module" repository = "https://github.com/tari-project/tari" diff --git a/comms/rpc_macros/Cargo.toml b/comms/rpc_macros/Cargo.toml index 52a73902a4..b680d347df 100644 --- a/comms/rpc_macros/Cargo.toml +++ b/comms/rpc_macros/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [lib] diff --git a/infrastructure/derive/Cargo.toml b/infrastructure/derive/Cargo.toml index 6d7208e964..7e87f9b943 100644 --- a/infrastructure/derive/Cargo.toml +++ b/infrastructure/derive/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [lib] diff --git a/infrastructure/shutdown/Cargo.toml b/infrastructure/shutdown/Cargo.toml index 4a32a93b4b..1d8aceee97 100644 --- a/infrastructure/shutdown/Cargo.toml +++ b/infrastructure/shutdown/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/infrastructure/storage/Cargo.toml b/infrastructure/storage/Cargo.toml index 42590b5b0d..238a176433 100644 --- a/infrastructure/storage/Cargo.toml +++ b/infrastructure/storage/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.21.1" +version = "0.21.2" edition = "2018" [dependencies] diff --git a/infrastructure/test_utils/Cargo.toml b/infrastructure/test_utils/Cargo.toml index 0fb9b1f8b2..8e043be38a 100644 --- a/infrastructure/test_utils/Cargo.toml +++ b/infrastructure/test_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tari_test_utils" description = "Utility functions used in Tari test functions" -version = "0.21.1" +version = "0.21.2" authors = ["The Tari Development Community"] edition = "2018" license = "BSD-3-Clause" diff --git a/package-lock.json b/package-lock.json index fd3ecdaa80..e7f85a0e63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4 +1,4 @@ { "lockfileVersion": 1, - "version": "0.21.1" + "version": "0.21.2" } From 889796a45875d72c4a2bc670b96846d22e359fe1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 19 Nov 2021 09:30:36 +0200 Subject: [PATCH 03/13] fix: allow bullet proof value only rewinding in atomic swaps (#3586) Description --- This PR allows us to do bulletproof value-only rewinding on HTLC atomic swap utxo. Motivation and Context --- Currently it is not possible to do bulletproof rewinding on the value only on an HTLC atomic swap utxo due to the way the commitment blinding factor and bulletproof rewinding keys are created. Currently, the two bulletproof rewinding keys are created as: ``` let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&commitment_blinding_factor))?; let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; ``` This means that if you share the rewind key, which is used to do value only rewinding, that a person can calculate the blinding key which is used to do full rewinding and expose the commitment blinding factor. by changing the calculation order we prevent this and only allow full rewinding by something who needs to be able to do this. ``` let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key ))?; let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&commitment_blinding_factor))?; ``` How Has This Been Tested? --- All current test pass --- base_layer/wallet/src/output_manager_service/service.rs | 4 ++-- base_layer/wallet/src/transaction_service/service.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 2572402e69..fe2181bdf6 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1230,8 +1230,8 @@ where ) .as_bytes(), )?; - let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&spending_key))?; - let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; + let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&spending_key))?; + let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key))?; let rewound = output.full_rewind_range_proof(&self.resources.factories.range_proof, &rewind_key, &blinding_key)?; diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 44d162a0fb..03d0dc5f1e 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -859,8 +859,9 @@ where .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; let sender_message = TransactionSenderMessage::new_single_round_message(stp.get_single_round_message()?); - let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&spend_key))?; - let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; + let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&spend_key))?; + let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key))?; + let rewind_data = RewindData { rewind_key: rewind_key.clone(), rewind_blinding_key: blinding_key.clone(), From 8e271d769b7fc540bd78e326f0e0e8155e9de88f Mon Sep 17 00:00:00 2001 From: Philip Robinson Date: Fri, 19 Nov 2021 10:07:14 +0200 Subject: [PATCH 04/13] fix: update daily test start times and seed phrase (#3584) Description --- - This PR updates the daily test star times to be from 1am so that the tests are done before standup. - Updated the recovery seed phrase to a wallet with 1234567890uT for each spotting when recovery misses some outputs - Updates the pruning horizon to 20. How Has This Been Tested? --- YOLO --- .../daily_tests/automatic_recovery_test.js | 14 +++++++------- applications/daily_tests/automatic_sync_test.js | 2 +- applications/daily_tests/cron_jobs.js | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/applications/daily_tests/automatic_recovery_test.js b/applications/daily_tests/automatic_recovery_test.js index e9a11a6db2..f18a8025a8 100644 --- a/applications/daily_tests/automatic_recovery_test.js +++ b/applications/daily_tests/automatic_recovery_test.js @@ -16,7 +16,7 @@ async function main() { description: "Seed words to use during recovery", type: "string", default: - "pigeon marble letter canal hard close kit cash coin still melt random require long shaft antenna tent turkey neck divert enrich iron analyst abandon", + "cactus pool fuel skull chair casino season disorder flat crash wrist whisper decorate narrow oxygen remember minor among happy cricket embark blue ship sick", }) .option("log", { alias: "l", @@ -34,19 +34,19 @@ async function main() { .alias("help", "h").argv; for (let i = 0; i < argv.numWallets; i++) { - let { identity, timeDiffMinutes, height, blockRate, recoveredAmount } = + let { identity, timeDiffMinutes, numOutputs, rate, recoveredAmount } = await run(argv); console.log( "Wallet (Pubkey:", identity.public_key, - ") recovered to a block height of", - height, - "completed in", + ") scanned", + numOutputs, + "outputs, completed in", timeDiffMinutes, "minutes (", - blockRate, - "blocks/min).", + rate, + "outputs/min).", recoveredAmount, "ยตT recovered for instance ", i, diff --git a/applications/daily_tests/automatic_sync_test.js b/applications/daily_tests/automatic_sync_test.js index a8dc49052a..ec5e130ca5 100644 --- a/applications/daily_tests/automatic_sync_test.js +++ b/applications/daily_tests/automatic_sync_test.js @@ -61,7 +61,7 @@ async function run(options) { "true"; // Set pruning horizon in config file if `pruned` command line arg is present if (options.syncType === SyncType.Pruned) { - process.env[`TARI_BASE_NODE__${NETWORK}__PRUNING_HORIZON`] = 1000; + process.env[`TARI_BASE_NODE__${NETWORK}__PRUNING_HORIZON`] = 20; } if (options.forceSyncPeer) { diff --git a/applications/daily_tests/cron_jobs.js b/applications/daily_tests/cron_jobs.js index daed608a6b..a8038b9123 100644 --- a/applications/daily_tests/cron_jobs.js +++ b/applications/daily_tests/cron_jobs.js @@ -53,7 +53,7 @@ async function runWalletRecoveryTest(instances) { recoveredAmount, } = await walletRecoveryTest({ seedWords: - "abandon rely pave boil case broken volume bracket own false sketch ordinary gown bitter strong unhappy shoulder salad season student public will monkey inquiry", + "cactus pool fuel skull chair casino season disorder flat crash wrist whisper decorate narrow oxygen remember minor among happy cricket embark blue ship sick", log: LOG_FILE, numWallets: instances, baseDir, @@ -126,13 +126,13 @@ async function main() { }); // ------------------------- CRON ------------------------- // - new CronJob("0 7 * * *", () => runWalletRecoveryTest(1)).start(); + new CronJob("0 2 * * *", () => runWalletRecoveryTest(1)).start(); //new CronJob("30 7 * * *", () => runWalletRecoveryTest(5)).start(); - new CronJob("0 6 * * *", () => + new CronJob("0 1 * * *", () => runBaseNodeSyncTest(SyncType.Archival) ).start(); - new CronJob("30 6 * * *", () => runBaseNodeSyncTest(SyncType.Pruned)).start(); - new CronJob("0 4 * * *", () => + new CronJob("30 1 * * *", () => runBaseNodeSyncTest(SyncType.Pruned)).start(); + new CronJob("0 0 * * *", () => git.pull(__dirname).catch((err) => { failed("Failed to update git repo"); console.error(err); From 476c512127cb9bd2ff6d3d1294d2a3d9d1628767 Mon Sep 17 00:00:00 2001 From: David Main <51991544+StriderDM@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:12:32 +0200 Subject: [PATCH 05/13] refactor: clean up unwraps in wallet_ffi (#3585) Description --- Remove unwrap() in functions of wallet_ffi, code is considered best-effort to try to prevent a panic since the nature of handling invalid pointers or invalidly cast pointers passed in results in undefined behavior. The function signature of log_debug_message() was modified to take into account an error occurring. PR is a breaking change due to this. Fixed potential panic when unwrapping parse() for MultiAddr. Fixed missing documentation for init_logging(). Motivation and Context --- Removal of calls to unwrap() and bug fixes. How Has This Been Tested? --- cargo test --all --- base_layer/wallet_ffi/src/error.rs | 6 + base_layer/wallet_ffi/src/lib.rs | 574 ++++++++++++++++++++++++----- base_layer/wallet_ffi/wallet.h | 2 +- 3 files changed, 482 insertions(+), 100 deletions(-) diff --git a/base_layer/wallet_ffi/src/error.rs b/base_layer/wallet_ffi/src/error.rs index e842530d11..6b17d8f552 100644 --- a/base_layer/wallet_ffi/src/error.rs +++ b/base_layer/wallet_ffi/src/error.rs @@ -41,6 +41,8 @@ const LOG_TARGET: &str = "wallet_ffi::error"; pub enum InterfaceError { #[error("An error has occurred due to one of the parameters being null: `{0}`")] NullError(String), + #[error("An invalid pointer was passed into the function")] + PointerError(String), #[error("An error has occurred when checking the length of the allocated object")] AllocationError, #[error("An error because the supplied position was out of range")] @@ -101,6 +103,10 @@ impl From for LibWalletError { code: 8, message: "Balance Unavailable".to_string(), }, + InterfaceError::PointerError(ref p) => Self { + code: 9, + message: format!("Pointer error on {}:{:?}", p, v), + }, } } } diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 6bd2cd6136..e24dec8445 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -283,7 +283,7 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_hex( error_out: *mut c_int, ) -> *mut c_char { let mut error = 0; - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); ptr::swap(error_out, &mut error as *mut c_int); if kernel.is_null() { error = LibWalletError::from(InterfaceError::NullError("kernel".to_string())).code; @@ -291,7 +291,14 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_hex( return CString::into_raw(result); } let excess = (*kernel).excess.clone().to_hex(); - result = CString::new(excess).unwrap(); + match CString::new(excess) { + Ok(v) => result = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("kernel".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + result.into_raw() } @@ -312,7 +319,7 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_public_nonce_hex( error_out: *mut c_int, ) -> *mut c_char { let mut error = 0; - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); ptr::swap(error_out, &mut error as *mut c_int); if kernel.is_null() { error = LibWalletError::from(InterfaceError::NullError("kernel".to_string())).code; @@ -320,7 +327,15 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_public_nonce_hex( return CString::into_raw(result); } let nonce = (*kernel).excess_sig.get_public_nonce().to_hex(); - result = CString::new(nonce).unwrap(); + + match CString::new(nonce) { + Ok(v) => result = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("kernel".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + result.into_raw() } @@ -341,7 +356,7 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_signature_hex( error_out: *mut c_int, ) -> *mut c_char { let mut error = 0; - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); ptr::swap(error_out, &mut error as *mut c_int); if kernel.is_null() { error = LibWalletError::from(InterfaceError::NullError("kernel".to_string())).code; @@ -349,7 +364,7 @@ pub unsafe extern "C" fn transaction_kernel_get_excess_signature_hex( return CString::into_raw(result); } let signature = (*kernel).excess_sig.get_signature().to_hex(); - result = CString::new(signature).unwrap(); + result = CString::new(signature).expect("Hex string will not fail"); result.into_raw() } @@ -623,7 +638,16 @@ pub unsafe extern "C" fn public_key_from_hex(key: *const c_char, error_out: *mut ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } else { - key_str = CStr::from_ptr(key).to_str().unwrap().to_owned(); + match CStr::from_ptr(key).to_str() { + Ok(v) => { + key_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } let public_key = TariPublicKey::from_hex(key_str.as_str()); @@ -654,7 +678,7 @@ pub unsafe extern "C" fn public_key_from_hex(key: *const c_char, error_out: *mut #[no_mangle] pub unsafe extern "C" fn public_key_to_emoji_id(pk: *mut TariPublicKey, error_out: *mut c_int) -> *mut c_char { let mut error = 0; - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); ptr::swap(error_out, &mut error as *mut c_int); if pk.is_null() { error = LibWalletError::from(InterfaceError::NullError("key".to_string())).code; @@ -663,7 +687,7 @@ pub unsafe extern "C" fn public_key_to_emoji_id(pk: *mut TariPublicKey, error_ou } let emoji = EmojiId::from_pubkey(&(*pk)); - result = CString::new(emoji.as_str()).unwrap(); + result = CString::new(emoji.as_str()).expect("Emoji will not fail."); CString::into_raw(result) } @@ -827,7 +851,16 @@ pub unsafe extern "C" fn private_key_from_hex(key: *const c_char, error_out: *mu ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } else { - key_str = CStr::from_ptr(key).to_str().unwrap().to_owned(); + match CStr::from_ptr(key).to_str() { + Ok(v) => { + key_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + }; } let secret_key = TariPrivateKey::from_hex(key_str.as_str()); @@ -982,17 +1015,25 @@ pub unsafe extern "C" fn seed_words_get_at( ) -> *mut c_char { let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); - let mut word = CString::new("").unwrap(); + let mut word = CString::new("").expect("Blank CString will not fail."); if seed_words.is_null() { error = LibWalletError::from(InterfaceError::NullError("seed words".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); } else { - let len = (*seed_words).0.len(); - if position >= len as u32 { + let len = (*seed_words).0.len() - 1; // clamp to length + if position > len as u32 { error = LibWalletError::from(InterfaceError::PositionInvalidError).code; ptr::swap(error_out, &mut error as *mut c_int); } else { - word = CString::new((*seed_words).0[position as usize].clone()).unwrap() + match CString::new((*seed_words).0[position as usize].clone()) { + Ok(v) => { + word = v; + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("seed_words".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } } } CString::into_raw(word) @@ -1036,11 +1077,20 @@ pub unsafe extern "C" fn seed_words_push_word( ptr::swap(error_out, &mut error as *mut c_int); return SeedWordPushResult::InvalidSeedWord as u8; } else { - word_string = CStr::from_ptr(word).to_str().unwrap().to_owned(); + match CStr::from_ptr(word).to_str() { + Ok(v) => { + word_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("word".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return SeedWordPushResult::InvalidObject as u8; + }, + } } // Check word is from a word list - match MnemonicLanguage::from(word_string.as_str()) { + match MnemonicLanguage::from(&word_string) { Ok(language) => { if (*seed_words).0.len() >= MnemonicLanguage::word_count(&language) { let error_msg = "Invalid seed words object, i.e. the entire mnemonic word list, is being used"; @@ -1071,7 +1121,7 @@ pub unsafe extern "C" fn seed_words_push_word( return if let Err(e) = CipherSeed::from_mnemonic(&(*seed_words).0, None) { log::error!( target: LOG_TARGET, - "Problem building valid private seed from seed phrase: {}", + "Problem building valid private seed from seed phrase: {:?}", e ); error = LibWalletError::from(WalletError::KeyManagerError(e)).code; @@ -1132,7 +1182,16 @@ pub unsafe extern "C" fn contact_create( ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } else { - alias_string = CStr::from_ptr(alias).to_str().unwrap().to_owned(); + match CStr::from_ptr(alias).to_str() { + Ok(v) => { + alias_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("alias".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } if public_key.is_null() { @@ -1165,12 +1224,18 @@ pub unsafe extern "C" fn contact_create( pub unsafe extern "C" fn contact_get_alias(contact: *mut TariContact, error_out: *mut c_int) -> *mut c_char { let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); - let mut a = CString::new("").unwrap(); + let mut a = CString::new("").expect("Blank CString will not fail."); if contact.is_null() { error = LibWalletError::from(InterfaceError::NullError("contact".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); } else { - a = CString::new((*contact).alias.clone()).unwrap(); + match CString::new((*contact).alias.clone()) { + Ok(v) => a = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("contact".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } } CString::into_raw(a) } @@ -1849,14 +1914,21 @@ pub unsafe extern "C" fn completed_transaction_get_message( let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); let message = (*transaction).message.clone(); - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); if transaction.is_null() { error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); return result.into_raw(); } - result = CString::new(message).unwrap(); + match CString::new(message) { + Ok(v) => result = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + result.into_raw() } @@ -2131,14 +2203,21 @@ pub unsafe extern "C" fn pending_outbound_transaction_get_message( let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); let message = (*transaction).message.clone(); - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); if transaction.is_null() { error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); return result.into_raw(); } - result = CString::new(message).unwrap(); + match CString::new(message) { + Ok(v) => result = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + result.into_raw() } @@ -2331,14 +2410,21 @@ pub unsafe extern "C" fn pending_inbound_transaction_get_message( let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); let message = (*transaction).message.clone(); - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); if transaction.is_null() { error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); return result.into_raw(); } - result = CString::new(message).unwrap(); + match CString::new(message) { + Ok(v) => result = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + result.into_raw() } @@ -2443,17 +2529,36 @@ pub unsafe extern "C" fn transport_tcp_create( let listener_address_str; if !listener_address.is_null() { - listener_address_str = CStr::from_ptr(listener_address).to_str().unwrap().to_owned(); + match CStr::from_ptr(listener_address).to_str() { + Ok(v) => { + listener_address_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("listener_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("listener_address".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } - let transport = TariTransportType::Tcp { - listener_address: listener_address_str.parse::().unwrap(), - tor_socks_config: None, - }; - Box::into_raw(Box::new(transport)) + + match listener_address_str.parse::() { + Ok(v) => { + let transport = TariTransportType::Tcp { + listener_address: v, + tor_socks_config: None, + }; + Box::into_raw(Box::new(transport)) + }, + Err(_) => { + error = LibWalletError::from(InterfaceError::InvalidArgument("listener_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } } /// Creates a tor transport type @@ -2487,7 +2592,16 @@ pub unsafe extern "C" fn transport_tor_create( let control_address_str; if !control_server_address.is_null() { - control_address_str = CStr::from_ptr(control_server_address).to_str().unwrap().to_owned(); + match CStr::from_ptr(control_server_address).to_str() { + Ok(v) => { + control_address_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("control_server_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("control_server_address".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -2497,8 +2611,26 @@ pub unsafe extern "C" fn transport_tor_create( let username_str; let password_str; let authentication = if !socks_username.is_null() && !socks_password.is_null() { - username_str = CStr::from_ptr(socks_username).to_str().unwrap().to_owned(); - password_str = CStr::from_ptr(socks_password).to_str().unwrap().to_owned(); + match CStr::from_ptr(socks_username).to_str() { + Ok(v) => { + username_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("socks_username".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } + match CStr::from_ptr(socks_password).to_str() { + Ok(v) => { + password_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("socks_password".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + }; socks::Authentication::Password(username_str, password_str) } else { socks::Authentication::None @@ -2513,21 +2645,30 @@ pub unsafe extern "C" fn transport_tor_create( let identity = None; - let tor_config = TorConfig { - control_server_addr: control_address_str.parse::().unwrap(), - control_server_auth: tor_authentication, - identity, - // Proxy the onion address to an OS-assigned local port - port_mapping: tor::PortMapping::new(tor_port, "127.0.0.1:0".parse().unwrap()), - socks_address_override: None, - socks_auth: authentication, - tor_proxy_bypass_addresses: vec![], - // Prefer performance - tor_proxy_bypass_for_outbound_tcp: true, - }; - let transport = TariTransportType::Tor(tor_config); + match control_address_str.parse::() { + Ok(v) => { + let tor_config = TorConfig { + control_server_addr: v, + control_server_auth: tor_authentication, + identity, + // Proxy the onion address to an OS-assigned local port + port_mapping: tor::PortMapping::new(tor_port, "127.0.0.1:0".parse().expect("Will not fail parsing")), + socks_address_override: None, + socks_auth: authentication, + tor_proxy_bypass_addresses: vec![], + // Prefer performance + tor_proxy_bypass_for_outbound_tcp: true, + }; + let transport = TariTransportType::Tor(tor_config); - Box::into_raw(Box::new(transport)) + Box::into_raw(Box::new(transport)) + }, + Err(_) => { + error = LibWalletError::from(InterfaceError::InvalidArgument("control_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } } /// Gets the address for a memory transport type @@ -2549,11 +2690,15 @@ pub unsafe extern "C" fn transport_memory_get_address( ) -> *mut c_char { let mut error = 0; ptr::swap(error_out, &mut error as *mut c_int); - let mut address = CString::new("").unwrap(); + let mut address = CString::new("").expect("Blank CString will not fail."); if !transport.is_null() { match &*transport { - TransportType::Memory { listener_address } => { - address = CString::new(listener_address.to_string()).unwrap(); + TransportType::Memory { listener_address } => match CString::new(listener_address.to_string()) { + Ok(v) => address = v, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("transport".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, }, _ => { error = LibWalletError::from(InterfaceError::NullError("transport".to_string())).code; @@ -2626,7 +2771,16 @@ pub unsafe extern "C" fn comms_config_create( ptr::swap(error_out, &mut error as *mut c_int); let public_address_str; if !public_address.is_null() { - public_address_str = CStr::from_ptr(public_address).to_str().unwrap().to_owned(); + match CStr::from_ptr(public_address).to_str() { + Ok(v) => { + public_address_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("public_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("public_address".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -2635,7 +2789,16 @@ pub unsafe extern "C" fn comms_config_create( let database_name_string; if !database_name.is_null() { - database_name_string = CStr::from_ptr(database_name).to_str().unwrap().to_owned(); + match CStr::from_ptr(database_name).to_str() { + Ok(v) => { + database_name_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("database_name".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("database_name".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -2644,7 +2807,16 @@ pub unsafe extern "C" fn comms_config_create( let datastore_path_string; if !datastore_path.is_null() { - datastore_path_string = CStr::from_ptr(datastore_path).to_str().unwrap().to_owned(); + match CStr::from_ptr(datastore_path).to_str() { + Ok(v) => { + datastore_path_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("datastore_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("datastore_path".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -2664,7 +2836,16 @@ pub unsafe extern "C" fn comms_config_create( let network_str; if !network.is_null() { - network_str = CStr::from_ptr(network).to_str().unwrap().to_owned(); + match CStr::from_ptr(network).to_str() { + Ok(v) => { + network_str = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("network".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("network".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -2708,7 +2889,9 @@ pub unsafe extern "C" fn comms_config_create( listener_liveness_allowlist_cidrs: Vec::new(), listener_liveness_max_sessions: 0, user_agent: format!("tari/wallet/{}", env!("CARGO_PKG_VERSION")), - dns_seeds_name_server: DEFAULT_DNS_NAME_SERVER.parse().unwrap(), + dns_seeds_name_server: DEFAULT_DNS_NAME_SERVER + .parse() + .expect("Default dns name server constant should always be correct"), peer_seeds: Default::default(), dns_seeds: Default::default(), dns_seeds_use_dnssec: true, @@ -2752,8 +2935,40 @@ pub unsafe extern "C" fn comms_config_destroy(wc: *mut TariCommsConfig) { /// ------------------------------------- Wallet -------------------------------------------------/// -unsafe fn init_logging(log_path: *const c_char, num_rolling_log_files: c_uint, size_per_log_file_bytes: c_uint) { - let path = CStr::from_ptr(log_path).to_str().unwrap().to_owned(); +/// Inits logging, this function is deliberately not exposed externally in the header +/// +/// ## Arguments +/// `log_path` - Path to where the log will be stored +/// `num_rolling_log_files` - Number of rolling files to be used. +/// `size_per_log_file_bytes` - Max byte size of log file +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +/// +/// # Safety +/// None +unsafe fn init_logging( + log_path: *const c_char, + num_rolling_log_files: c_uint, + size_per_log_file_bytes: c_uint, + error_out: *mut c_int, +) { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + + let path; + match CStr::from_ptr(log_path).to_str() { + Ok(v) => { + path = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("log_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return; + }, + } let encoder = PatternEncoder::new("{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}"); let log_appender: Box = if num_rolling_log_files != 0 && size_per_log_file_bytes != 0 { let mut pattern; @@ -2771,7 +2986,7 @@ unsafe fn init_logging(log_path: *const c_char, num_rolling_log_files: c_uint, s } let roller = FixedWindowRoller::builder() .build(pattern.as_str(), num_rolling_log_files) - .unwrap(); + .expect("Should be able to create a Roller"); let size_trigger = SizeTrigger::new(size_per_log_file_bytes as u64); let policy = CompoundPolicy::new(Box::new(size_trigger), Box::new(roller)); @@ -2780,7 +2995,7 @@ unsafe fn init_logging(log_path: *const c_char, num_rolling_log_files: c_uint, s .encoder(Box::new(encoder)) .append(true) .build(path.as_str(), Box::new(policy)) - .unwrap(), + .expect("Should be able to create an appender"), ) } else { Box::new( @@ -2795,7 +3010,7 @@ unsafe fn init_logging(log_path: *const c_char, num_rolling_log_files: c_uint, s let lconfig = Config::builder() .appender(Appender::builder().build("logfile", log_appender)) .build(Root::builder().appender("logfile").build(LevelFilter::Debug)) - .unwrap(); + .expect("Should be able to create a Config"); match log4rs::init_config(lconfig) { Ok(_) => debug!(target: LOG_TARGET, "Logging started"), @@ -2889,7 +3104,11 @@ pub unsafe extern "C" fn wallet_create( } if !log_path.is_null() { - init_logging(log_path, num_rolling_log_files, size_per_log_file_bytes); + init_logging(log_path, num_rolling_log_files, size_per_log_file_bytes, error_out); + + if error > 0 { + return ptr::null_mut(); + } } let passphrase_option = if !passphrase.is_null() { @@ -3104,7 +3323,7 @@ pub unsafe extern "C" fn wallet_sign_message( error_out: *mut c_int, ) -> *mut c_char { let mut error = 0; - let mut result = CString::new("").unwrap(); + let mut result = CString::new("").expect("Blank CString will not fail."); ptr::swap(error_out, &mut error as *mut c_int); if wallet.is_null() { @@ -3121,7 +3340,11 @@ pub unsafe extern "C" fn wallet_sign_message( let nonce = TariPrivateKey::random(&mut OsRng); let secret = (*wallet).wallet.comms.node_identity().secret_key().clone(); - let message = CStr::from_ptr(msg).to_str().unwrap().to_owned(); + let message = CStr::from_ptr(msg) + .to_str() + .expect("CString should not fail here.") + .to_owned(); + let signature = (*wallet).wallet.sign_message(secret, nonce, &message); match signature { @@ -3129,7 +3352,7 @@ pub unsafe extern "C" fn wallet_sign_message( let hex_sig = s.get_signature().to_hex(); let hex_nonce = s.get_public_nonce().to_hex(); let hex_return = format!("{}|{}", hex_sig, hex_nonce); - result = CString::new(hex_return).unwrap(); + result = CString::new(hex_return).expect("CString should not fail here."); }, Err(e) => { error = LibWalletError::from(e).code; @@ -3187,34 +3410,65 @@ pub unsafe extern "C" fn wallet_verify_message_signature( return result; } - let message = CStr::from_ptr(msg).to_str().unwrap().to_owned(); - let hex = CStr::from_ptr(hex_sig_nonce).to_str().unwrap().to_owned(); + let message; + match CStr::from_ptr(msg).to_str() { + Ok(v) => { + message = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("msg".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } + let hex; + match CStr::from_ptr(hex_sig_nonce).to_str() { + Ok(v) => { + hex = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("hex_sig_nonce".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } let hex_keys: Vec<&str> = hex.split('|').collect(); if hex_keys.len() != 2 { error = LibWalletError::from(InterfaceError::PositionInvalidError).code; ptr::swap(error_out, &mut error as *mut c_int); return result; } - let secret = TariPrivateKey::from_hex(hex_keys.get(0).unwrap()); - match secret { - Ok(p) => { - let public_nonce = TariPublicKey::from_hex(hex_keys.get(1).unwrap()); - match public_nonce { - Ok(pn) => { - result = (*wallet) - .wallet - .verify_message_signature((*public_key).clone(), pn, p, message) + + if let Some(key1) = hex_keys.get(0) { + if let Some(key2) = hex_keys.get(1) { + let secret = TariPrivateKey::from_hex(key1); + match secret { + Ok(p) => { + let public_nonce = TariPublicKey::from_hex(key2); + match public_nonce { + Ok(pn) => { + result = (*wallet) + .wallet + .verify_message_signature((*public_key).clone(), pn, p, message) + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } }, Err(e) => { error = LibWalletError::from(e).code; ptr::swap(error_out, &mut error as *mut c_int); }, } - }, - Err(e) => { - error = LibWalletError::from(e).code; + } else { + error = LibWalletError::from(InterfaceError::InvalidArgument("hex_sig_nonce".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); - }, + } + } else { + error = LibWalletError::from(InterfaceError::InvalidArgument("hex_sig_nonce".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); } result @@ -3257,7 +3511,16 @@ pub unsafe extern "C" fn wallet_add_base_node_peer( let address_string; if !address.is_null() { - address_string = CStr::from_ptr(address).to_str().unwrap().to_owned(); + match CStr::from_ptr(address).to_str() { + Ok(v) => { + address_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("address".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -3530,12 +3793,30 @@ pub unsafe extern "C" fn wallet_send_transaction( return 0; } - let message_string = if !message.is_null() { - CStr::from_ptr(message).to_str().unwrap().to_owned() + let message_string; + if !message.is_null() { + match CStr::from_ptr(message).to_str() { + Ok(v) => { + message_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + message_string = CString::new("") + .expect("Blank CString will not fail") + .to_str() + .expect("CString.to_str() will not fail") + .to_owned(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); - CString::new("").unwrap().to_str().unwrap().to_owned() + message_string = CString::new("") + .expect("Blank CString will not fail") + .to_str() + .expect("CString.to_str() will not fail") + .to_owned(); }; match (*wallet) @@ -4372,12 +4653,30 @@ pub unsafe extern "C" fn wallet_import_utxo( return 0; } - let message_string = if !message.is_null() { - CStr::from_ptr(message).to_str().unwrap().to_owned() + let message_string; + if !message.is_null() { + match CStr::from_ptr(message).to_str() { + Ok(v) => { + message_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + message_string = CString::new("Imported UTXO") + .expect("CString will not fail") + .to_str() + .expect("CString.to_str() will not fail") + .to_owned(); + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); - CString::new("Imported UTXO").unwrap().to_str().unwrap().to_owned() + message_string = CString::new("Imported UTXO") + .expect("CString will not fail") + .to_str() + .expect("CString.toStr() will not fail") + .to_owned(); }; let public_script_key = PublicKey::from_secret_key(&(*spending_key)); @@ -4641,10 +4940,19 @@ pub unsafe extern "C" fn wallet_coin_split( ptr::swap(error_out, &mut error as *mut c_int); } - let message = if !msg.is_null() { - CStr::from_ptr(msg).to_str().unwrap().to_owned() + let message; + + if !msg.is_null() { + match CStr::from_ptr(msg).to_str() { + Ok(v) => { + message = v.to_owned(); + }, + _ => { + message = "Coin Split".to_string(); + }, + } } else { - "Coin Split".to_string() + message = "Coin Split".to_string() }; match (*wallet).runtime.block_on((*wallet).wallet.coin_split( @@ -4858,7 +5166,16 @@ pub unsafe extern "C" fn wallet_set_key_value( ptr::swap(error_out, &mut error as *mut c_int); return false; } else { - key_string = CStr::from_ptr(key).to_str().unwrap().to_owned(); + match CStr::from_ptr(key).to_str() { + Ok(v) => { + key_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } } let value_string; @@ -4867,7 +5184,16 @@ pub unsafe extern "C" fn wallet_set_key_value( ptr::swap(error_out, &mut error as *mut c_int); return false; } else { - value_string = CStr::from_ptr(value).to_str().unwrap().to_owned(); + match CStr::from_ptr(value).to_str() { + Ok(v) => { + value_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("value".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } } match (*wallet) @@ -4918,7 +5244,16 @@ pub unsafe extern "C" fn wallet_get_value( ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } else { - key_string = CStr::from_ptr(key).to_str().unwrap().to_owned(); + match CStr::from_ptr(key).to_str() { + Ok(v) => { + key_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } } match (*wallet) @@ -4979,7 +5314,16 @@ pub unsafe extern "C" fn wallet_clear_value( ptr::swap(error_out, &mut error as *mut c_int); return false; } else { - key_string = CStr::from_ptr(key).to_str().unwrap().to_owned(); + match CStr::from_ptr(key).to_str() { + Ok(v) => { + key_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, + } } match (*wallet) @@ -5142,7 +5486,16 @@ pub unsafe extern "C" fn file_partial_backup( let original_path_string; if !original_file_path.is_null() { - original_path_string = CStr::from_ptr(original_file_path).to_str().unwrap().to_owned(); + match CStr::from_ptr(original_file_path).to_str() { + Ok(v) => { + original_path_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("original_file_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return; + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("original_file_path".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -5152,7 +5505,16 @@ pub unsafe extern "C" fn file_partial_backup( let backup_path_string; if !backup_file_path.is_null() { - backup_path_string = CStr::from_ptr(backup_file_path).to_str().unwrap().to_owned(); + match CStr::from_ptr(backup_file_path).to_str() { + Ok(v) => { + backup_path_string = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("backup_file_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return; + }, + } } else { error = LibWalletError::from(InterfaceError::NullError("backup_file_path".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -5299,13 +5661,27 @@ pub unsafe extern "C" fn wallet_destroy(wallet: *mut TariWallet) { /// /// ## Arguments /// `msg` - A string that will be logged at the debug level. If msg is null nothing will be done. +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. /// /// # Safety /// None #[no_mangle] -pub unsafe extern "C" fn log_debug_message(msg: *const c_char) { +pub unsafe extern "C" fn log_debug_message(msg: *const c_char, error_out: *mut c_int) { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let message; if !msg.is_null() { - let message = CStr::from_ptr(msg).to_str().unwrap().to_owned(); + match CStr::from_ptr(msg).to_str() { + Ok(v) => { + message = v.to_owned(); + }, + _ => { + error = LibWalletError::from(InterfaceError::PointerError("msg".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return; + }, + } debug!(target: LOG_TARGET, "{}", message); } } diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index 7b0ee7ca16..c4be5b890c 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -716,7 +716,7 @@ void balance_destroy(struct TariBalance *balance); void file_partial_backup(const char *original_file_path, const char *backup_file_path, int *error_out); /// This function will log the provided string at debug level. To be used to have a client log messages to the LibWallet -void log_debug_message(const char *msg); +void log_debug_message(const char *msg, int *error_out); struct EmojiSet *get_emoji_set(); From f6d29951a77b9d6ed869ab5dac52d477ce8a87cb Mon Sep 17 00:00:00 2001 From: David Main <51991544+StriderDM@users.noreply.github.com> Date: Fri, 19 Nov 2021 12:18:53 +0200 Subject: [PATCH 06/13] refactor: update miningcore repository links (#3593) Description --- Updates link to miningcore repositories. Requires this PR to be merged first: https://github.com/tari-project/miningcore/pull/1 Motivation and Context --- Documentation How Has This Been Tested? --- N/A --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f8a5b0194..b34b179d2a 100644 --- a/README.md +++ b/README.md @@ -422,7 +422,7 @@ The Tari Base Node, Tari Console Wallet, Tari Stratum Transcoder and Tari Mining default installation as described in [Installing using binaries](#installing-using-binaries), all these applications will be available. -For MiningCore see [here](https://github.com/StriderDM/miningcore/tree/tari#runtime-requirements-on-linux) and [here](https://github.com/StriderDM/miningcore/tree/tari#runtime-requirements-on-windows). +For MiningCore see [here](https://github.com/tari-project/miningcore/master/tari#runtime-requirements-on-linux) and [here](https://github.com/tari-project/miningcore/tree/master#runtime-requirements-on-windows). #### Configuration prerequisites @@ -453,7 +453,7 @@ transcoder_host_address = "127.0.0.1:7879" For MiningCore: -See example configuration [here](https://github.com/StriderDM/miningcore/blob/tari/examples/tari_pool.json). +See example configuration [here](https://github.com/tari-project/miningcore/blob/master/examples/tari_pool.json). For the Tari Mining Node there are some additional settings under section **`mining_node`** that can be changed: * For SHA3 Mining: From f32a38f409bb342e0ab507af5336abe60eaca2a8 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 19 Nov 2021 13:32:28 +0200 Subject: [PATCH 07/13] fix: allow bullet proof value only rewinding off one-sided transaction (#3587) Description --- This PR allows us to do bulletproof value-only rewinding on one-sided transactions. Motivation and Context --- Currently, it is not possible to do bulletproof rewinding on the value only on a one-sided transaction utxo due to the way the commitment blinding factor and bulletproof rewinding keys are created. Currently, the two bulletproof rewinding keys are created as: ``` let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&commitment_blinding_factor))?; let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; ``` This means that if you share the rewind key, which is used to do value only rewinding, that a person can calculate the blinding key which is used to do full rewinding and expose the commitment blinding factor. by changing the calculation order we prevent this and only allow full rewinding by something who needs to be able to do this. ``` let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key ))?; let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&commitment_blinding_factor))?; ``` How Has This Been Tested? --- All current test pass --- base_layer/wallet/src/output_manager_service/service.rs | 4 ++-- base_layer/wallet/src/transaction_service/service.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index fe2181bdf6..1949e61006 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1422,8 +1422,8 @@ where ) .as_bytes(), )?; - let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&spending_key))?; - let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; + let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&spending_key))?; + let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key))?; let rewound = output.full_rewind_range_proof(&self.resources.factories.range_proof, &rewind_key, &blinding_key); diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 03d0dc5f1e..ef43ad849b 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -1014,8 +1014,8 @@ where .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; let sender_message = TransactionSenderMessage::new_single_round_message(stp.get_single_round_message()?); - let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&spend_key))?; - let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?; + let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&spend_key))?; + let rewind_key = PrivateKey::from_bytes(&hash_secret_key(&blinding_key))?; let rewind_data = RewindData { rewind_key: rewind_key.clone(), rewind_blinding_key: blinding_key.clone(), From 2ba437b4a7eed2022a7555bb72a8838eb2e608a2 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal <39146854+hansieodendaal@users.noreply.github.com> Date: Fri, 19 Nov 2021 14:06:35 +0200 Subject: [PATCH 08/13] feat: standardize output hash for unblinded output, transaction output and transaction input (#3592) Description --- - Standardized output hash calculation for unblinded output, transaction output and transaction input. - Refactored `transaction.rs` code layout to be modular in the file system. _**Note:** The only real change in the code is the standardized use of `transactions/transaction_entities/mod.rs/pub fn hash_output(...)` called from `transactions/transaction_entities/transaction_input.rs`, `transactions/transaction_entities/transaction_output.rs` and `transactions/transaction_entities/unblinded_output.rs`._ Motivation and Context --- Different output hash calculation implementations existed for output hash calculation for unblinded output vs. transaction output and transaction input. How Has This Been Tested? --- Unit testing. --- .../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 | 225 ++ .../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, 3148 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 75ff753d50..9b9f344040 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..34a1d76771 --- /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(&CryptoFactories::default())); + } + + #[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..7e4208db3b --- /dev/null +++ b/base_layer/core/src/transactions/transaction_entities/unblinded_output.rs @@ -0,0 +1,225 @@ +// 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}, + }, + 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() + } + + // 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 +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) + } +} 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 1949e61006..49ae3bf44b 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 ef43ad849b..0ecbab6d50 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 ed21d9d029..bd3796479d 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 e24dec8445..d5ec154748 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 519290c89a3a757f561bc320e3cb19097796cd46 Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Fri, 19 Nov 2021 15:13:49 +0200 Subject: [PATCH 09/13] chore: upgrade tokio deps #3581 (#3595) Description --- Upgrades dependencies from cargo audit alerts Motivation and Context --- #3581 How Has This Been Tested? --- cargo audit, cargo test --- Cargo.lock | 282 ++++-------------- base_layer/p2p/Cargo.toml | 4 +- base_layer/service_framework/Cargo.toml | 8 +- .../service_framework/src/reply_channel.rs | 2 +- base_layer/wallet/Cargo.toml | 2 +- comms/Cargo.toml | 4 +- comms/dht/Cargo.toml | 6 +- comms/rpc_macros/Cargo.toml | 2 +- comms/src/protocol/rpc/client/mod.rs | 2 +- 9 files changed, 81 insertions(+), 231 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1e6c55f05..ec15c0129c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1664,7 +1664,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.7", + "pin-project-lite", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1801,7 +1801,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.13.0", + "tokio", "tokio-util", "tracing", ] @@ -1900,7 +1900,7 @@ checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes 1.1.0", "http", - "pin-project-lite 0.2.7", + "pin-project-lite", ] [[package]] @@ -1946,9 +1946,9 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.7", + "pin-project-lite", "socket2", - "tokio 1.13.0", + "tokio", "tower-service", "tracing", "want", @@ -1961,8 +1961,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite", + "tokio", "tokio-io-timeout", ] @@ -1975,7 +1975,7 @@ dependencies = [ "bytes 1.1.0", "hyper", "native-tls", - "tokio 1.13.0", + "tokio", "tokio-native-tls", ] @@ -2851,7 +2851,7 @@ dependencies = [ "pin-project 1.0.8", "rand 0.8.4", "thiserror", - "tokio 1.13.0", + "tokio", "tokio-stream", ] @@ -2885,7 +2885,7 @@ dependencies = [ "reqwest", "thiserror", "thrift", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -3129,12 +3129,6 @@ dependencies = [ "syn 1.0.81", ] -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - [[package]] name = "pin-project-lite" version = "0.2.7" @@ -3637,11 +3631,11 @@ dependencies = [ "mime", "native-tls", "percent-encoding 2.1.0", - "pin-project-lite 0.2.7", + "pin-project-lite", "serde 1.0.130", "serde_json", "serde_urlencoded", - "tokio 1.13.0", + "tokio", "tokio-native-tls", "url 2.2.2", "wasm-bindgen", @@ -4435,7 +4429,7 @@ dependencies = [ "tari_crypto", "tari_p2p", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", ] @@ -4474,7 +4468,7 @@ dependencies = [ "tari_service_framework", "tari_shutdown", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", "tracing", "tracing-opentelemetry", @@ -4547,7 +4541,7 @@ dependencies = [ "serde 1.0.130", "tari_crypto", "thiserror", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -4590,10 +4584,10 @@ dependencies = [ "tari_test_utils", "tempfile", "thiserror", - "tokio 1.13.0", + "tokio", "tokio-stream", "tokio-util", - "tower 0.3.1", + "tower", "tower-make", "tracing", "tracing-futures", @@ -4640,10 +4634,10 @@ dependencies = [ "tari_utilities", "tempfile", "thiserror", - "tokio 1.13.0", + "tokio", "tokio-stream", - "tokio-test 0.4.2", - "tower 0.4.10", + "tokio-test", + "tower", "tower-test", "ttl_cache", ] @@ -4659,7 +4653,7 @@ dependencies = [ "syn 1.0.81", "tari_comms", "tari_test_utils", - "tokio 1.13.0", + "tokio", "tower-service", ] @@ -4696,7 +4690,7 @@ dependencies = [ "tari_shutdown", "tari_wallet", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", "tracing", "tracing-opentelemetry", @@ -4756,7 +4750,7 @@ dependencies = [ "tari_test_utils", "tempfile", "thiserror", - "tokio 1.13.0", + "tokio", "tracing", "tracing-attributes", "tracing-futures", @@ -4848,7 +4842,7 @@ dependencies = [ "tari_crypto", "tari_utilities", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", "tracing", "tracing-futures", @@ -4890,7 +4884,7 @@ dependencies = [ "tari_core", "tari_crypto", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", ] @@ -4949,9 +4943,9 @@ dependencies = [ "tari_utilities", "tempfile", "thiserror", - "tokio 1.13.0", + "tokio", "tokio-stream", - "tower 0.3.1", + "tower", "tower-service", "trust-dns-client", "webpki", @@ -4969,8 +4963,8 @@ dependencies = [ "tari_shutdown", "tari_test_utils", "thiserror", - "tokio 1.13.0", - "tower 0.3.1", + "tokio", + "tower", "tower-service", ] @@ -4979,7 +4973,7 @@ name = "tari_shutdown" version = "0.21.2" dependencies = [ "futures 0.3.17", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -5045,7 +5039,7 @@ dependencies = [ "tari_crypto", "tari_utilities", "thiserror", - "tokio 1.13.0", + "tokio", "tonic", "tonic-build", "tracing", @@ -5064,7 +5058,7 @@ dependencies = [ "rand 0.8.4", "tari_shutdown", "tempfile", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -5127,8 +5121,8 @@ dependencies = [ "tari_test_utils", "tempfile", "thiserror", - "tokio 1.13.0", - "tower 0.3.1", + "tokio", + "tower", ] [[package]] @@ -5158,7 +5152,7 @@ dependencies = [ "tari_wallet", "tempfile", "thiserror", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -5195,7 +5189,7 @@ dependencies = [ "tari_core", "tari_crypto", "tari_utilities", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -5316,22 +5310,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "pin-project-lite 0.1.12", - "slab", -] - -[[package]] -name = "tokio" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" dependencies = [ "autocfg 1.0.1", "bytes 1.1.0", @@ -5340,7 +5321,7 @@ dependencies = [ "mio", "num_cpus", "once_cell", - "pin-project-lite 0.2.7", + "pin-project-lite", "signal-hook-registry", "tokio-macros", "winapi 0.3.9", @@ -5352,15 +5333,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" dependencies = [ - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite", + "tokio", ] [[package]] name = "tokio-macros" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" dependencies = [ "proc-macro2 1.0.32", "quote 1.0.10", @@ -5374,7 +5355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio 1.13.0", + "tokio", ] [[package]] @@ -5384,7 +5365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", - "tokio 1.13.0", + "tokio", "webpki", ] @@ -5395,22 +5376,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite", + "tokio", "tokio-util", ] -[[package]] -name = "tokio-test" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0049c119b6d505c4447f5c64873636c7af6c75ab0d45fd9f618d82acb8016d" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "tokio 0.2.25", -] - [[package]] name = "tokio-test" version = "0.4.2" @@ -5420,7 +5390,7 @@ dependencies = [ "async-stream", "bytes 1.1.0", "futures-core", - "tokio 1.13.0", + "tokio", "tokio-stream", ] @@ -5433,7 +5403,7 @@ dependencies = [ "futures-util", "log", "pin-project 1.0.8", - "tokio 1.13.0", + "tokio", "tungstenite", ] @@ -5448,8 +5418,8 @@ dependencies = [ "futures-io", "futures-sink", "log", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite", + "tokio", ] [[package]] @@ -5491,10 +5461,10 @@ dependencies = [ "pin-project 1.0.8", "prost", "prost-derive", - "tokio 1.13.0", + "tokio", "tokio-stream", "tokio-util", - "tower 0.4.10", + "tower", "tower-layer", "tower-service", "tracing", @@ -5515,37 +5485,19 @@ dependencies = [ [[package]] name = "tower" -version = "0.3.1" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62" -dependencies = [ - "futures-core", - "tower-buffer", - "tower-discover", - "tower-layer", - "tower-limit", - "tower-load-shed", - "tower-retry", - "tower-service", - "tower-timeout", - "tower-util", -] - -[[package]] -name = "tower" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00e500fff5fa1131c866b246041a6bf96da9c965f8fe4128cb1421f23e93c00" +checksum = "5651b5f6860a99bd1adb59dbfe1db8beb433e73709d9032b413a77e2fb7c066a" dependencies = [ "futures-core", "futures-util", "hdrhistogram", "indexmap", "pin-project 1.0.8", - "pin-project-lite 0.2.7", + "pin-project-lite", "rand 0.8.4", "slab", - "tokio 1.13.0", + "tokio", "tokio-stream", "tokio-util", "tower-layer", @@ -5553,77 +5505,12 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-buffer" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a" -dependencies = [ - "futures-core", - "pin-project 0.4.28", - "tokio 0.2.25", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-discover" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a" -dependencies = [ - "futures-core", - "pin-project 0.4.28", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" -[[package]] -name = "tower-limit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404" -dependencies = [ - "futures-core", - "pin-project 0.4.28", - "tokio 0.2.25", - "tower-layer", - "tower-load", - "tower-service", -] - -[[package]] -name = "tower-load" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b" -dependencies = [ - "futures-core", - "log", - "pin-project 0.4.28", - "tokio 0.2.25", - "tower-discover", - "tower-service", -] - -[[package]] -name = "tower-load-shed" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e" -dependencies = [ - "futures-core", - "pin-project 0.4.28", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-make" version = "0.3.0" @@ -5633,19 +5520,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "tower-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952" -dependencies = [ - "futures-core", - "pin-project 0.4.28", - "tokio 0.2.25", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-service" version = "0.3.1" @@ -5654,42 +5528,18 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tower-test" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba4bbc2c1e4a8543c30d4c13a4c8314ed72d6e07581910f665aa13fde0153c8" +checksum = "a4546773ffeab9e4ea02b8872faa49bb616a80a7da66afc2f32688943f97efa7" dependencies = [ "futures-util", - "pin-project 0.4.28", - "tokio 0.2.25", - "tokio-test 0.2.1", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-timeout" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e" -dependencies = [ - "pin-project 0.4.28", - "tokio 0.2.25", + "pin-project 1.0.8", + "tokio", + "tokio-test", "tower-layer", "tower-service", ] -[[package]] -name = "tower-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" -dependencies = [ - "futures-core", - "futures-util", - "pin-project 0.4.28", - "tower-service", -] - [[package]] name = "tracing" version = "0.1.29" @@ -5698,7 +5548,7 @@ checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.7", + "pin-project-lite", "tracing-attributes", "tracing-core", ] @@ -5813,7 +5663,7 @@ dependencies = [ "ring", "rustls", "thiserror", - "tokio 1.13.0", + "tokio", "trust-dns-proto", "webpki", ] @@ -5841,7 +5691,7 @@ dependencies = [ "smallvec", "thiserror", "tinyvec", - "tokio 1.13.0", + "tokio", "tokio-rustls", "url 2.2.2", "webpki", @@ -6143,7 +5993,7 @@ dependencies = [ "serde 1.0.130", "serde_json", "serde_urlencoded", - "tokio 1.13.0", + "tokio", "tokio-stream", "tokio-tungstenite", "tokio-util", diff --git a/base_layer/p2p/Cargo.toml b/base_layer/p2p/Cargo.toml index 782551c063..f9ac95f29b 100644 --- a/base_layer/p2p/Cargo.toml +++ b/base_layer/p2p/Cargo.toml @@ -36,8 +36,8 @@ serde_derive = "1.0.90" thiserror = "1.0.26" tokio = { version = "1.11", features = ["macros"] } tokio-stream = { version = "0.1.7", default-features = false, features = ["time"] } -tower = "0.3.0-alpha.2" -tower-service = { version = "0.3.0-alpha.2" } +tower = "0.4.11" +tower-service = { version = "0.3.1" } trust-dns-client = { version = "0.21.0-alpha.4", features = ["dns-over-rustls"] } rustls = "0.19.1" webpki = "0.21" diff --git a/base_layer/service_framework/Cargo.toml b/base_layer/service_framework/Cargo.toml index 82461264b1..e714492ced 100644 --- a/base_layer/service_framework/Cargo.toml +++ b/base_layer/service_framework/Cargo.toml @@ -17,12 +17,12 @@ async-trait = "0.1.50" futures = { version = "^0.3.16", features = ["async-await"] } log = "0.4.8" thiserror = "1.0.26" -tokio = { version = "1.11", features = ["rt"] } -tower-service = { version = "0.3.0" } +tokio = { version = "1.14", features = ["rt"] } +tower-service = { version = "0.3" } [dev-dependencies] tari_test_utils = { version = "^0.21", path = "../../infrastructure/test_utils" } -tokio = { version = "1.11", features = ["rt-multi-thread", "macros", "time"] } +tokio = { version = "1.14", features = ["rt-multi-thread", "macros", "time"] } futures-test = { version = "0.3.3" } -tower = "0.3.1" +tower = "0.4" diff --git a/base_layer/service_framework/src/reply_channel.rs b/base_layer/service_framework/src/reply_channel.rs index 4921ed9601..55189ce597 100644 --- a/base_layer/service_framework/src/reply_channel.rs +++ b/base_layer/service_framework/src/reply_channel.rs @@ -286,7 +286,7 @@ mod test { block_on(future::join( async move { - let err = requestor.ready_and().await.unwrap().call("PING").await.unwrap_err(); + let err = requestor.ready().await.unwrap().call("PING").await.unwrap_err(); assert_eq!(err, TransportChannelError::Canceled); }, async move { diff --git a/base_layer/wallet/Cargo.toml b/base_layer/wallet/Cargo.toml index 18c7c003be..9cc95ba519 100644 --- a/base_layer/wallet/Cargo.toml +++ b/base_layer/wallet/Cargo.toml @@ -43,7 +43,7 @@ serde_json = "1.0.39" tempfile = "3.1.0" thiserror = "1.0.26" tokio = { version = "1.11", features = ["sync", "macros"] } -tower = "0.3.0-alpha.2" +tower = "0.4" [dependencies.tari_core] path = "../../base_layer/core" diff --git a/comms/Cargo.toml b/comms/Cargo.toml index 6092edf614..b24e3a3e6a 100644 --- a/comms/Cargo.toml +++ b/comms/Cargo.toml @@ -41,10 +41,10 @@ serde = "1.0.119" serde_derive = "1.0.119" snow = { version = "=0.8.0", features = ["default-resolver"] } thiserror = "1.0.26" -tokio = { version = "1.11", features = ["rt-multi-thread", "time", "sync", "signal", "net", "macros", "io-util"] } +tokio = { version = "1.14", features = ["rt-multi-thread", "time", "sync", "signal", "net", "macros", "io-util"] } tokio-stream = { version = "0.1.7", features = ["sync"] } tokio-util = { version = "0.6.7", features = ["codec", "compat"] } -tower = "0.3.1" +tower = "0.4" tracing = "0.1.26" tracing-futures = "0.2.5" yamux = "=0.9.0" diff --git a/comms/dht/Cargo.toml b/comms/dht/Cargo.toml index b3dec63a37..5b15421607 100644 --- a/comms/dht/Cargo.toml +++ b/comms/dht/Cargo.toml @@ -36,8 +36,8 @@ serde = "1.0.90" serde_derive = "1.0.90" serde_repr = "0.1.5" thiserror = "1.0.26" -tokio = { version = "1.11", features = ["rt", "macros"] } -tower = { version= "0.4.8", features=["full"] } +tokio = { version = "1.14", features = ["rt", "macros"] } +tower = { version= "0.4", features=["full"] } ttl_cache = "0.5.1" # tower-filter dependencies @@ -55,7 +55,7 @@ petgraph = "0.5.1" clap = "2.33.0" # tower-filter dependencies -tower-test = { version = "^0.3" } +tower-test = { version = "^0.4" } tokio-test = "^0.4.2" futures-util = "^0.3.1" lazy_static = "1.4.0" diff --git a/comms/rpc_macros/Cargo.toml b/comms/rpc_macros/Cargo.toml index b680d347df..cf9ebcebbc 100644 --- a/comms/rpc_macros/Cargo.toml +++ b/comms/rpc_macros/Cargo.toml @@ -25,4 +25,4 @@ tari_test_utils = { version = "^0.21", path = "../../infrastructure/test_utils" futures = "0.3.5" prost = "0.8.0" tokio = { version = "1", features = ["macros"] } -tower-service = "0.3.0" +tower-service = "0.3" diff --git a/comms/src/protocol/rpc/client/mod.rs b/comms/src/protocol/rpc/client/mod.rs index 52abf1049f..9bec5975ab 100644 --- a/comms/src/protocol/rpc/client/mod.rs +++ b/comms/src/protocol/rpc/client/mod.rs @@ -187,7 +187,7 @@ impl RpcClient { &mut self, request: BaseRequest, ) -> Result, RpcStatus>>, RpcError> { - let svc = self.connector.ready_and().await?; + let svc = self.connector.ready().await?; let resp = svc.call(request).await?; Ok(resp) } From 91fe921092991df59f65fbe4f448fba85b42e30b Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Sun, 21 Nov 2021 12:03:57 +0200 Subject: [PATCH 10/13] feat: track ping failures and disconnect (#3597) Description --- Keep track of failed pings in liveness and disconnect peer's that do not respond. Motivation and Context --- This allows dead connections to be detected and disconnected. This is mainly advantageous in tor connections which can silently disconnect. How Has This Been Tested? --- Unit test Manually - though no occurrence of this case --- .../p2p/src/services/liveness/config.rs | 3 + base_layer/p2p/src/services/liveness/error.rs | 4 +- .../p2p/src/services/liveness/service.rs | 27 ++++- base_layer/p2p/src/services/liveness/state.rs | 111 +++++++++++++----- 4 files changed, 113 insertions(+), 32 deletions(-) diff --git a/base_layer/p2p/src/services/liveness/config.rs b/base_layer/p2p/src/services/liveness/config.rs index 5e90db6528..d70314f30f 100644 --- a/base_layer/p2p/src/services/liveness/config.rs +++ b/base_layer/p2p/src/services/liveness/config.rs @@ -36,6 +36,8 @@ pub struct LivenessConfig { pub num_peers_per_round: usize, /// Peers to include in every auto ping round (Default: ) pub monitored_peers: Vec, + /// Number of ping failures to tolerate before disconnecting the peer. A value of zero disables this feature. + pub max_allowed_ping_failures: usize, } impl Default for LivenessConfig { @@ -46,6 +48,7 @@ impl Default for LivenessConfig { refresh_random_pool_interval: Duration::from_secs(2 * 60 * 60), num_peers_per_round: 8, monitored_peers: Default::default(), + max_allowed_ping_failures: 2, } } } diff --git a/base_layer/p2p/src/services/liveness/error.rs b/base_layer/p2p/src/services/liveness/error.rs index fdea8677c7..f07f8147bf 100644 --- a/base_layer/p2p/src/services/liveness/error.rs +++ b/base_layer/p2p/src/services/liveness/error.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 tari_comms::{connectivity::ConnectivityError, message::MessageError}; +use tari_comms::{connectivity::ConnectivityError, message::MessageError, PeerConnectionError}; use tari_comms_dht::{outbound::DhtOutboundError, DhtActorError}; use tari_service_framework::reply_channel::TransportChannelError; use thiserror::Error; @@ -31,6 +31,8 @@ pub enum LivenessError { DhtOutboundError(#[from] DhtOutboundError), #[error("Connectivity error: `{0}`")] ConnectivityError(#[from] ConnectivityError), + #[error("Peer connection error: `{0}`")] + PeerConnectionError(#[from] PeerConnectionError), #[error("DHT actor error: `{0}`")] DhtActorError(#[from] DhtActorError), #[error("Failed to send a pong message")] diff --git a/base_layer/p2p/src/services/liveness/service.rs b/base_layer/p2p/src/services/liveness/service.rs index bc1cd3bbd5..167992f128 100644 --- a/base_layer/p2p/src/services/liveness/service.rs +++ b/base_layer/p2p/src/services/liveness/service.rs @@ -122,6 +122,11 @@ where if let Err(err) = self.start_ping_round().await { warn!(target: LOG_TARGET, "Error when pinging peers: {}", err); } + if self.config.max_allowed_ping_failures > 0 { + if let Err(err) = self.disconnect_failed_peers().await { + error!(target: LOG_TARGET, "Error occurred while disconnecting failed peers: {}", err); + } + } }, // Incoming messages from the Comms layer @@ -179,7 +184,7 @@ where return Ok(()); } - let maybe_latency = self.state.record_pong(ping_pong_msg.nonce); + let maybe_latency = self.state.record_pong(ping_pong_msg.nonce, &node_id); debug!( target: LOG_TARGET, "Received pong from peer '{}' with useragent '{}'. {} (Trace: {})", @@ -285,6 +290,26 @@ where Ok(()) } + async fn disconnect_failed_peers(&mut self) -> Result<(), LivenessError> { + let max_allowed_ping_failures = self.config.max_allowed_ping_failures; + for node_id in self + .state + .failed_pings_iter() + .filter(|(_, n)| **n > max_allowed_ping_failures) + .map(|(node_id, _)| node_id) + { + if let Some(mut conn) = self.connectivity.get_connection(node_id.clone()).await? { + debug!( + target: LOG_TARGET, + "Disconnecting peer {} that failed {} rounds of pings", node_id, max_allowed_ping_failures + ); + conn.disconnect().await?; + } + } + self.state.clear_failed_pings(); + Ok(()) + } + fn publish_event(&mut self, event: LivenessEvent) { let _ = self.event_publisher.send(Arc::new(event)).map_err(|_| { trace!( diff --git a/base_layer/p2p/src/services/liveness/state.rs b/base_layer/p2p/src/services/liveness/state.rs index d8b5b29d20..4e89d8e91f 100644 --- a/base_layer/p2p/src/services/liveness/state.rs +++ b/base_layer/p2p/src/services/liveness/state.rs @@ -20,17 +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 super::LOG_TARGET; use crate::proto::liveness::MetadataKey; -use chrono::{NaiveDateTime, Utc}; +use log::*; use std::{ - collections::{hash_map::RandomState, HashMap}, + collections::HashMap, convert::TryInto, - time::Duration, + time::{Duration, Instant}, }; use tari_comms::peer_manager::NodeId; const LATENCY_SAMPLE_WINDOW_SIZE: usize = 25; -const MAX_INFLIGHT_TTL: Duration = Duration::from_secs(20); +const MAX_INFLIGHT_TTL: Duration = Duration::from_secs(40); /// Represents metadata in a ping/pong message. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -62,7 +63,7 @@ impl From>> for Metadata { } } -impl From for HashMap, RandomState> { +impl From for HashMap> { fn from(metadata: Metadata) -> Self { metadata.inner } @@ -71,8 +72,9 @@ impl From for HashMap, RandomState> { /// State for the LivenessService. #[derive(Default, Debug)] pub struct LivenessState { - inflight_pings: HashMap, + inflight_pings: HashMap, peer_latency: HashMap, + failed_pings: HashMap, pings_received: usize, pongs_received: usize, @@ -133,18 +135,27 @@ impl LivenessState { /// Adds a ping to the inflight ping list, while noting the current time that a ping was sent. pub fn add_inflight_ping(&mut self, nonce: u64, node_id: NodeId) { - let now = Utc::now().naive_utc(); - self.inflight_pings.insert(nonce, (node_id, now)); + self.inflight_pings.insert(nonce, (node_id, Instant::now())); self.clear_stale_inflight_pings(); } - /// Clears inflight ping requests which have not responded + /// Clears inflight ping requests which have not responded and adds them to failed_ping counter fn clear_stale_inflight_pings(&mut self) { - self.inflight_pings = self + let (inflight, expired) = self .inflight_pings .drain() - .filter(|(_, (_, time))| convert_to_std_duration(Utc::now().naive_utc() - *time) <= MAX_INFLIGHT_TTL) - .collect(); + .partition(|(_, (_, time))| time.elapsed() <= MAX_INFLIGHT_TTL); + + self.inflight_pings = inflight; + + for (_, (node_id, _)) in expired { + self.failed_pings + .entry(node_id) + .and_modify(|v| { + *v = *v + 1; + }) + .or_insert(1); + } } /// Returns true if the nonce is inflight, otherwise false @@ -153,19 +164,25 @@ impl LivenessState { } /// Records a pong. Specifically, the pong counter is incremented and - /// a latency sample is added and calculated. - pub fn record_pong(&mut self, nonce: u64) -> Option { + /// a latency sample is added and calculated. The given `peer` must match the recorded peer + pub fn record_pong(&mut self, nonce: u64, sent_by: &NodeId) -> Option { self.inc_pongs_received(); - - match self.inflight_pings.remove_entry(&nonce) { - Some((_, (node_id, sent_time))) => { - let now = Utc::now().naive_utc(); - let latency = self - .add_latency_sample(node_id, convert_to_std_duration(now - sent_time)) - .calc_average(); - Some(latency) - }, - None => None, + self.failed_pings.remove_entry(&sent_by); + + let (node_id, _) = self.inflight_pings.get(&nonce)?; + if node_id == sent_by { + self.inflight_pings + .remove(&nonce) + .map(|(node_id, sent_time)| self.add_latency_sample(node_id, sent_time.elapsed()).calc_average()) + } else { + warn!( + target: LOG_TARGET, + "Peer {} sent an nonce for another peer {}. This could indicate malicious behaviour or a bug. \ + Ignoring.", + sent_by, + node_id + ); + None } } @@ -195,11 +212,14 @@ impl LivenessState { // num_peers in map will always be > 0 .map(|latency| latency / num_peers as u32) } -} -/// Convert `chrono::Duration` to `std::time::Duration` -pub(super) fn convert_to_std_duration(old_duration: chrono::Duration) -> Duration { - Duration::from_millis(old_duration.num_milliseconds() as u64) + pub fn failed_pings_iter(&self) -> impl Iterator { + self.failed_pings.iter() + } + + pub fn clear_failed_pings(&mut self) { + self.failed_pings.clear(); + } } /// A very simple implementation for calculating average latency. Samples are added in milliseconds and the mean average @@ -299,9 +319,9 @@ mod test { let mut state = LivenessState::new(); let node_id = NodeId::default(); - state.add_inflight_ping(123, node_id); + state.add_inflight_ping(123, node_id.clone()); - let latency = state.record_pong(123).unwrap(); + let latency = state.record_pong(123, &node_id).unwrap(); assert!(latency < 50); } @@ -311,4 +331,35 @@ mod test { state.set_metadata_entry(MetadataKey::ChainMetadata, b"dummy-data".to_vec()); assert_eq!(state.metadata().get(MetadataKey::ChainMetadata).unwrap(), b"dummy-data"); } + + #[test] + fn clear_stale_inflight_pings() { + let mut state = LivenessState::new(); + + let peer1 = NodeId::default(); + state.add_inflight_ping(1, peer1.clone()); + let peer2 = NodeId::from_public_key(&Default::default()); + state.add_inflight_ping(2, peer2.clone()); + state.add_inflight_ping(3, peer2.clone()); + + assert!(state.failed_pings.get(&peer1).is_none()); + assert!(state.failed_pings.get(&peer2).is_none()); + + // MAX_INFLIGHT_TTL passes + for n in [1, 2, 3] { + let (_, time) = state.inflight_pings.get_mut(&n).unwrap(); + *time = Instant::now() - (MAX_INFLIGHT_TTL + Duration::from_secs(1)); + } + + state.clear_stale_inflight_pings(); + let n = state.failed_pings.get(&peer1).unwrap(); + assert_eq!(*n, 1); + let n = state.failed_pings.get(&peer2).unwrap(); + assert_eq!(*n, 2); + + assert!(state.record_pong(2, &peer2).is_none()); + let n = state.failed_pings.get(&peer1).unwrap(); + assert_eq!(*n, 1); + assert!(state.failed_pings.get(&peer2).is_none()); + } } From c0d625c1630da1b1b25414ecdc99bd78eccf8bba Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Sun, 21 Nov 2021 12:42:21 +0200 Subject: [PATCH 11/13] fix: be more permissive of responses for the incorrect request_id (#3588) Description --- - add more intrumentation to tari_comms - improve behaviour and logs around stream interruptions - be more permissive of responses for the incorrect request_id - reduce transaction_resend_period to 10 minutes from an hour @philipr-za Motivation and Context --- Track more metrics to diagnose issues. Formally recognise a stream interruption and continue the session vs a protocol violation which terminates the session. How Has This Been Tested? --- Existing test, stress test involving 2 wallets and a base node --- RFC/src/RFC-0250_Covenants.md | 44 +++------ applications/daily_tests/washing_machine.js | 6 +- .../src/automation/command_parser.rs | 14 ++- .../sync/header_sync/synchronizer.rs | 2 +- .../core/src/base_node/sync/rpc/tests.rs | 2 +- base_layer/core/src/mempool/rpc/test.rs | 4 +- .../tasks/txo_validation_task.rs | 4 +- .../wallet/src/transaction_service/config.rs | 2 +- .../transaction_validation_protocol.rs | 2 +- .../src/utxo_scanner_service/utxo_scanning.rs | 2 +- comms/dht/examples/memory_net/utilities.rs | 2 +- comms/dht/src/rpc/test.rs | 2 +- comms/rpc_macros/tests/macro.rs | 2 +- comms/src/connection_manager/dialer.rs | 4 + comms/src/connection_manager/listener.rs | 4 + comms/src/connection_manager/manager.rs | 20 +++- comms/src/connection_manager/metrics.rs | 82 ++++++++++++++++ comms/src/connection_manager/mod.rs | 1 + comms/src/connectivity/manager.rs | 5 +- comms/src/connectivity/metrics.rs | 21 ++-- comms/src/multiplexing/metrics.rs | 32 ++++++ comms/src/multiplexing/mod.rs | 3 + comms/src/multiplexing/yamux.rs | 11 ++- comms/src/protocol/messaging/inbound.rs | 14 +-- comms/src/protocol/messaging/metrics.rs | 74 ++++++++++++++ comms/src/protocol/messaging/mod.rs | 1 + comms/src/protocol/messaging/outbound.rs | 7 +- comms/src/protocol/rpc/client/metrics.rs | 81 +++++++++++---- comms/src/protocol/rpc/client/mod.rs | 43 +++++--- comms/src/protocol/rpc/server/error.rs | 30 +++++- comms/src/protocol/rpc/server/metrics.rs | 61 ++++++++---- comms/src/protocol/rpc/server/mod.rs | 98 +++++++++++++------ comms/src/protocol/rpc/status.rs | 12 ++- .../protocol/rpc/test/comms_integration.rs | 2 +- comms/src/protocol/rpc/test/smoke.rs | 10 +- 35 files changed, 529 insertions(+), 175 deletions(-) create mode 100644 comms/src/connection_manager/metrics.rs create mode 100644 comms/src/multiplexing/metrics.rs create mode 100644 comms/src/protocol/messaging/metrics.rs diff --git a/RFC/src/RFC-0250_Covenants.md b/RFC/src/RFC-0250_Covenants.md index d2e90367db..e3eb65cc15 100644 --- a/RFC/src/RFC-0250_Covenants.md +++ b/RFC/src/RFC-0250_Covenants.md @@ -206,58 +206,46 @@ little-endian 64-byte unsigned integer. The output set is returned unaltered. This rule is implicit for an empty (0 byte) covenant. -```yaml -op_byte: 0x20 +op_byte: 0x20
args: [] -``` ##### and(A, B) The intersection (\\(A \cap B\\)) of the resulting output set for covenant rules \\(A\\) and \\(B\\). -```yaml -op_byte: 0x21 +op_byte: 0x21
args: [Covenant, Covenant] -``` ##### or(A, B) The union (\\(A \cup B\\)) of the resulting output set for covenant rules \\(A\\) and \\(B\\). -```yaml -op_byte: 0x22 +op_byte: 0x22
args: [Covenant, Covenant] -``` ##### xor(A, B) The symmetric difference (\\(A \triangle B\\)) of the resulting output set for covenant rules \\(A\\) and \\(B\\). This is, outputs that match either \\(A\\) or \\(B\\) but not both. -```yaml -op_byte: 0x23 +op_byte: 0x23
args: [Covenant, Covenant] -``` ##### not(A) Returns the compliment of `A`. That is, all the elements of `A` are removed from the resultant output set. -```yaml -op_byte: 0x24 +op_byte: 0x24
args: [Covenant] -``` ##### empty() Returns an empty set. This will always fail and, if used alone, prevents the UTXO from ever being spent. A more useful reason to use `empty` is in conjunction a conditional e.g. `if_else(Condition(older_rel(10)), A, empty)` -```yaml -op_byte: 0x25 +op_byte: 0x25
args: [] -``` #### Filters @@ -265,46 +253,36 @@ args: [] Filters for a single output that matches the hash. This filter only returns zero or one outputs. -```yaml -op_byte: 0x30 +op_byte: 0x30
args: [Hash] -``` ##### filter_fields_preserved(fields) Filter for outputs where all given fields in the input are preserved in the output. -```yaml -op_byte: 0x31 +op_byte: 0x31
args: [Fields] -``` ##### filter_field_int_eq(field, int) Filters for outputs whose field value matches the given integer value. If the given field cannot be cast to an unsigned 64-bit integer, the transaction/block is rejected. -```yaml -op_byte: 0x32 +op_byte: 0x32
args: [Field, VarInt] -``` ##### filter_fields_hashed_eq(fields, hash) -```yaml -op_byte: 0x33 +op_byte: 0x33
args: [Fields, VarInt] -``` ##### filter_relative_height(height) Checks the block height that current [UTXO] (i.e. the current input) was mined plus `height` is greater than or equal to the current block height. If so, the `identity()` is returned, otherwise `empty()`. -```yaml -op_byte: 0x34 +op_byte: 0x34
args: [VarInt] -``` #### Encoding / Decoding diff --git a/applications/daily_tests/washing_machine.js b/applications/daily_tests/washing_machine.js index fc66b8137b..c32144aa57 100644 --- a/applications/daily_tests/washing_machine.js +++ b/applications/daily_tests/washing_machine.js @@ -129,15 +129,18 @@ function WashingMachine(options) { `๐Ÿš€ Launching washing machine (numTransactions = ${numTransactions}, numRounds = ${numRounds}, sleep = ${sleepAfterRound}s)` ); + debug(`Connecting to wallet1 at ${wallet1Grpc}...`); await this.wallet1.connect(wallet1Grpc); + debug(`Connected.`); - debug("Compiling and starting applications..."); let wallet2Process = null; // Start wallet2 if (wallet2Grpc) { this.wallet2 = new WalletClient(); + debug(`Connecting to wallet2 at ${wallet2Grpc}...`); await this.wallet2.connect(wallet2Grpc); } else { + debug("Compiling wallet2..."); const port = await getFreePort(20000, 25000); wallet2Process = createGrpcWallet( baseNodeSeed, @@ -148,6 +151,7 @@ function WashingMachine(options) { true ); wallet2Process.baseDir = "./wallet"; + debug("Starting wallet2..."); await wallet2Process.startNew(); this.wallet2 = await wallet2Process.connectClient(); } diff --git a/applications/tari_console_wallet/src/automation/command_parser.rs b/applications/tari_console_wallet/src/automation/command_parser.rs index 0ed8acf4a6..275c9720b4 100644 --- a/applications/tari_console_wallet/src/automation/command_parser.rs +++ b/applications/tari_console_wallet/src/automation/command_parser.rs @@ -288,16 +288,14 @@ fn parse_make_it_rain(mut args: SplitWhitespace) -> Result, parsed_args.push(ParsedArgument::PublicKey(pubkey)); // transaction type - let txn_type = args - .next() - .ok_or_else(|| ParseError::Empty("transaction type".to_string()))?; + let txn_type = args.next(); let negotiated = match txn_type { - "negotiated" => true, - "one_sided" => false, + Some("negotiated") | Some("interactive") => true, + Some("one_sided") | Some("one-sided") | Some("onesided") => false, _ => { - println!("Invalid data provided for , must be 'negotiated' or 'one_sided'\n"); + println!("Invalid data provided for , must be 'interactive' or 'one-sided'\n"); return Err(ParseError::Invalid( - "Invalid data provided for , must be 'negotiated' or 'one_sided'".to_string(), + "Invalid data provided for , must be 'interactive' or 'one-sided'".to_string(), )); }, }; @@ -531,7 +529,7 @@ mod test { Err(e) => match e { ParseError::Invalid(e) => assert_eq!( e, - "Invalid data provided for , must be 'negotiated' or 'one_sided'".to_string() + "Invalid data provided for , must be 'interactive' or 'one-sided'".to_string() ), _ => panic!("Expected parsing to return an error here"), }, diff --git a/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs b/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs index ca594087b6..3748a557f5 100644 --- a/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs +++ b/base_layer/core/src/base_node/sync/header_sync/synchronizer.rs @@ -315,7 +315,7 @@ impl<'a, B: BlockchainBackend + 'static> HeaderSynchronizer<'a, B> { let resp = match client.find_chain_split(request).await { Ok(r) => r, - Err(RpcError::RequestFailed(err)) if err.status_code().is_not_found() => { + Err(RpcError::RequestFailed(err)) if err.as_status_code().is_not_found() => { // This round we sent less hashes than the max, so the next round will not have any more hashes to // send. Exit early in this case. if block_hashes.len() < NUM_CHAIN_SPLIT_HEADERS { diff --git a/base_layer/core/src/base_node/sync/rpc/tests.rs b/base_layer/core/src/base_node/sync/rpc/tests.rs index 52fdfd0285..055dcf2203 100644 --- a/base_layer/core/src/base_node/sync/rpc/tests.rs +++ b/base_layer/core/src/base_node/sync/rpc/tests.rs @@ -62,7 +62,7 @@ mod sync_blocks { }; let req = rpc_request_mock.request_with_context(Default::default(), msg); let err = service.sync_blocks(req).await.unwrap_err(); - unpack_enum!(RpcStatusCode::NotFound = err.status_code()); + unpack_enum!(RpcStatusCode::NotFound = err.as_status_code()); } #[tokio::test] diff --git a/base_layer/core/src/mempool/rpc/test.rs b/base_layer/core/src/mempool/rpc/test.rs index a9cbb2ee49..3f11646463 100644 --- a/base_layer/core/src/mempool/rpc/test.rs +++ b/base_layer/core/src/mempool/rpc/test.rs @@ -124,7 +124,7 @@ mod get_tx_state_by_excess_sig { .await .unwrap_err(); - unpack_enum!(RpcStatusCode::BadRequest = status.status_code()); + unpack_enum!(RpcStatusCode::BadRequest = status.as_status_code()); } } @@ -174,6 +174,6 @@ mod submit_transaction { .await .unwrap_err(); - unpack_enum!(RpcStatusCode::BadRequest = status.status_code()); + unpack_enum!(RpcStatusCode::BadRequest = status.as_status_code()); } } diff --git a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs index c4680cfe45..363530f6fd 100644 --- a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs +++ b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs @@ -34,7 +34,7 @@ use crate::{ use log::*; use std::{collections::HashMap, convert::TryInto, sync::Arc}; use tari_common_types::types::BlockHash; -use tari_comms::protocol::rpc::{RpcError::RequestFailed, RpcStatusCode::NotFound}; +use tari_comms::protocol::rpc::RpcError::RequestFailed; use tari_core::{ base_node::rpc::BaseNodeWalletRpcClient, blocks::BlockHeader, @@ -353,7 +353,7 @@ where info!(target: LOG_TARGET, "Error asking base node for header:{}", rpc_error); match &rpc_error { RequestFailed(status) => { - if status.status_code() == NotFound { + if status.as_status_code().is_not_found() { return Ok(None); } else { return Err(rpc_error.into()); diff --git a/base_layer/wallet/src/transaction_service/config.rs b/base_layer/wallet/src/transaction_service/config.rs index 42e6f6478b..85ebedb71f 100644 --- a/base_layer/wallet/src/transaction_service/config.rs +++ b/base_layer/wallet/src/transaction_service/config.rs @@ -50,7 +50,7 @@ impl Default for TransactionServiceConfig { direct_send_timeout: Duration::from_secs(20), broadcast_send_timeout: Duration::from_secs(60), low_power_polling_timeout: Duration::from_secs(300), - transaction_resend_period: Duration::from_secs(3600), + transaction_resend_period: Duration::from_secs(600), resend_response_cooldown: Duration::from_secs(300), pending_transaction_cancellation_timeout: Duration::from_secs(259200), // 3 Days num_confirmations_required: 3, diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs index eff2c9f0cc..639a246e7a 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs @@ -327,7 +327,7 @@ where warn!(target: LOG_TARGET, "Error asking base node for header:{}", rpc_error); match &rpc_error { RequestFailed(status) => { - if status.status_code() == NotFound { + if status.as_status_code() == NotFound { return Ok(None); } else { return Err(rpc_error.into()); 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 bd3796479d..e6fb3f1011 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanning.rs @@ -354,7 +354,7 @@ where TBackend: WalletBackend + 'static // this returns the index of the vec of hashes we sent it, that is the last hash it knows of. match client.find_chain_split(request).await { Ok(_) => Ok(metadata.utxo_index + 1), - Err(RpcError::RequestFailed(err)) if err.status_code().is_not_found() => { + Err(RpcError::RequestFailed(err)) if err.as_status_code().is_not_found() => { warn!(target: LOG_TARGET, "Reorg detected: {}", err); // The node does not know of the last hash we scanned, thus we had a chain split. // We now start at 0 again. diff --git a/comms/dht/examples/memory_net/utilities.rs b/comms/dht/examples/memory_net/utilities.rs index 9cf6340ebc..c42e433272 100644 --- a/comms/dht/examples/memory_net/utilities.rs +++ b/comms/dht/examples/memory_net/utilities.rs @@ -245,7 +245,7 @@ pub async fn network_connectivity_stats(nodes: &[TestNode], wallets: &[TestNode] total += t; avg += a; println!( - "{} total connections on the network. ({} per node on average)", + "{} total connections on the network. ({} per peer on average)", total, avg / (wallets.len() + nodes.len()) ); diff --git a/comms/dht/src/rpc/test.rs b/comms/dht/src/rpc/test.rs index cd70d65a0f..ef250217a2 100644 --- a/comms/dht/src/rpc/test.rs +++ b/comms/dht/src/rpc/test.rs @@ -159,7 +159,7 @@ mod get_closer_peers { let node_id = NodeId::default(); let req = mock.request_with_context(node_id, req); let err = service.get_closer_peers(req).await.unwrap_err(); - assert_eq!(err.status_code(), RpcStatusCode::BadRequest); + assert_eq!(err.as_status_code(), RpcStatusCode::BadRequest); } } diff --git a/comms/rpc_macros/tests/macro.rs b/comms/rpc_macros/tests/macro.rs index 71f0b7053f..15d11f5876 100644 --- a/comms/rpc_macros/tests/macro.rs +++ b/comms/rpc_macros/tests/macro.rs @@ -147,7 +147,7 @@ async fn it_returns_an_error_for_invalid_method_nums() { .await .unwrap_err(); - unpack_enum!(RpcStatusCode::UnsupportedMethod = err.status_code()); + unpack_enum!(RpcStatusCode::UnsupportedMethod = err.as_status_code()); } #[tokio::test] diff --git a/comms/src/connection_manager/dialer.rs b/comms/src/connection_manager/dialer.rs index f3e04a9eeb..147ffb9299 100644 --- a/comms/src/connection_manager/dialer.rs +++ b/comms/src/connection_manager/dialer.rs @@ -27,6 +27,7 @@ use crate::{ common, dial_state::DialState, manager::{ConnectionManagerConfig, ConnectionManagerEvent}, + metrics, peer_connection, }, multiaddr::Multiaddr, @@ -193,6 +194,7 @@ where dial_result: Result, ) { let node_id = dial_state.peer().node_id.clone(); + metrics::pending_connections(Some(&node_id), ConnectionDirection::Outbound).inc(); let removed = self.cancel_signals.remove(&node_id); drop(removed); @@ -213,6 +215,8 @@ where }, } + metrics::pending_connections(Some(&node_id), ConnectionDirection::Outbound).dec(); + if self.pending_dial_requests.contains_key(&node_id) { self.reply_to_pending_requests(&node_id, dial_result.clone()); } diff --git a/comms/src/connection_manager/listener.rs b/comms/src/connection_manager/listener.rs index c79e192795..2e96471e75 100644 --- a/comms/src/connection_manager/listener.rs +++ b/comms/src/connection_manager/listener.rs @@ -32,6 +32,7 @@ use crate::{ bounded_executor::BoundedExecutor, connection_manager::{ liveness::LivenessSession, + metrics, wire_mode::{WireMode, LIVENESS_WIRE_MODE}, }, multiaddr::Multiaddr, @@ -239,6 +240,7 @@ where let span = span!(Level::TRACE, "connection_mann::listener::inbound_task",); let inbound_fut = async move { + metrics::pending_connections(None, ConnectionDirection::Inbound).inc(); match Self::read_wire_format(&mut socket, config.time_to_first_byte).await { Ok(WireMode::Comms(byte)) if byte == config.network_info.network_byte => { let this_node_id_str = node_identity.node_id().short_str(); @@ -325,6 +327,8 @@ where ); }, } + + metrics::pending_connections(None, ConnectionDirection::Inbound).dec(); } .instrument(span); diff --git a/comms/src/connection_manager/manager.rs b/comms/src/connection_manager/manager.rs index d218a883a3..b1ad6d4c96 100644 --- a/comms/src/connection_manager/manager.rs +++ b/comms/src/connection_manager/manager.rs @@ -29,6 +29,7 @@ use super::{ }; use crate::{ backoff::Backoff, + connection_manager::{metrics, ConnectionDirection}, multiplexing::Substream, noise::NoiseConfig, peer_manager::{NodeId, NodeIdentity}, @@ -397,10 +398,14 @@ where node_id.short_str(), proto_str ); + metrics::inbound_substream_counter(&node_id, &protocol).inc(); let notify_fut = self .protocols .notify(&protocol, ProtocolEvent::NewInboundSubstream(node_id, stream)); match time::timeout(Duration::from_secs(10), notify_fut).await { + Ok(Ok(_)) => { + debug!(target: LOG_TARGET, "Protocol notification for '{}' sent", proto_str); + }, Ok(Err(err)) => { error!( target: LOG_TARGET, @@ -413,12 +418,21 @@ where "Error sending NewSubstream notification for protocol '{}' because {}", proto_str, err ); }, - _ => { - debug!(target: LOG_TARGET, "Protocol notification for '{}' sent", proto_str); - }, } }, + PeerConnected(conn) => { + metrics::successful_connections(conn.peer_node_id(), conn.direction()).inc(); + self.publish_event(PeerConnected(conn)); + }, + PeerConnectFailed(peer, err) => { + metrics::failed_connections(&peer, ConnectionDirection::Outbound).inc(); + self.publish_event(PeerConnectFailed(peer, err)); + }, + PeerInboundConnectFailed(err) => { + metrics::failed_connections(&Default::default(), ConnectionDirection::Inbound).inc(); + self.publish_event(PeerInboundConnectFailed(err)); + }, event => { self.publish_event(event); }, diff --git a/comms/src/connection_manager/metrics.rs b/comms/src/connection_manager/metrics.rs new file mode 100644 index 0000000000..b7ac2b857b --- /dev/null +++ b/comms/src/connection_manager/metrics.rs @@ -0,0 +1,82 @@ +// Copyright 2021, 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::{connection_manager::ConnectionDirection, peer_manager::NodeId, protocol::ProtocolId}; +use once_cell::sync::Lazy; +use tari_metrics::{IntCounter, IntCounterVec, IntGauge, IntGaugeVec}; + +pub fn pending_connections(peer: Option<&NodeId>, direction: ConnectionDirection) -> IntGauge { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_gauge_vec( + "comms::connections::pending", + "Number of active connections by direction", + &["peer_id", "direction"], + ) + .unwrap() + }); + + METER.with_label_values(&[ + peer.map(ToString::to_string) + .unwrap_or_else(|| "unknown".to_string()) + .as_str(), + direction.as_str(), + ]) +} + +pub fn successful_connections(peer: &NodeId, direction: ConnectionDirection) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::connections::success", + "Number of active connections by direction", + &["peer_id", "direction"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), direction.as_str()]) +} + +pub fn failed_connections(peer: &NodeId, direction: ConnectionDirection) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::connections::failed", + "Number of active connections by direction", + &["peer_id", "direction"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), direction.as_str()]) +} + +pub fn inbound_substream_counter(peer: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::connections::inbound_substream_request_count", + "Number of substream requests", + &["peer_id", "protocol"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) +} diff --git a/comms/src/connection_manager/mod.rs b/comms/src/connection_manager/mod.rs index 92ef4e18f1..fd0f7a3312 100644 --- a/comms/src/connection_manager/mod.rs +++ b/comms/src/connection_manager/mod.rs @@ -23,6 +23,7 @@ mod dial_state; mod dialer; mod listener; +mod metrics; mod common; pub use common::validate_peer_addresses; diff --git a/comms/src/connectivity/manager.rs b/comms/src/connectivity/manager.rs index d8043050e7..ad75db44b0 100644 --- a/comms/src/connectivity/manager.rs +++ b/comms/src/connectivity/manager.rs @@ -510,10 +510,7 @@ impl ConnectivityManagerActor { _ => {}, } }, - #[cfg(feature = "metrics")] - NewInboundSubstream(node_id, protocol, _) => { - super::metrics::substream_request_count(node_id, protocol).inc(); - }, + _ => {}, } diff --git a/comms/src/connectivity/metrics.rs b/comms/src/connectivity/metrics.rs index 56bd8f781d..1470b4c9c7 100644 --- a/comms/src/connectivity/metrics.rs +++ b/comms/src/connectivity/metrics.rs @@ -20,32 +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::{connection_manager::ConnectionDirection, peer_manager::NodeId, protocol::ProtocolId}; +use crate::connection_manager::ConnectionDirection; use once_cell::sync::Lazy; use tari_metrics::{IntGauge, IntGaugeVec}; pub fn connections(direction: ConnectionDirection) -> IntGauge { static METER: Lazy = Lazy::new(|| { - tari_metrics::register_int_gauge_vec("comms::connections", "Number of active connections by direction", &[ - "direction", - ]) + tari_metrics::register_int_gauge_vec( + "comms::connectivity::num_connections", + "Number of active connections by direction", + &["direction"], + ) .unwrap() }); METER.with_label_values(&[direction.as_str()]) } -pub fn substream_request_count(peer: &NodeId, protocol: &ProtocolId) -> IntGauge { - static METER: Lazy = Lazy::new(|| { - tari_metrics::register_int_gauge_vec("comms::substream_request_count", "Number of substream requests", &[ - "peer", "protocol", - ]) - .unwrap() - }); - - METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) -} - pub fn uptime() -> IntGauge { static METER: Lazy = Lazy::new(|| tari_metrics::register_int_gauge("comms::uptime", "Comms uptime").unwrap()); diff --git a/comms/src/multiplexing/metrics.rs b/comms/src/multiplexing/metrics.rs new file mode 100644 index 0000000000..823501aa57 --- /dev/null +++ b/comms/src/multiplexing/metrics.rs @@ -0,0 +1,32 @@ +// Copyright 2021, 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 once_cell::sync::Lazy; +use tari_metrics::IntCounter; + +pub static TOTAL_BYTES_READ: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter("comms::substream::total_bytes_read", "The total inbound bytes").unwrap() +}); + +pub static TOTAL_BYTES_WRITTEN: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter("comms::substream::total_bytes_written", "The total outbound bytes").unwrap() +}); diff --git a/comms/src/multiplexing/mod.rs b/comms/src/multiplexing/mod.rs index 8ac082d5c4..1732ea142b 100644 --- a/comms/src/multiplexing/mod.rs +++ b/comms/src/multiplexing/mod.rs @@ -20,5 +20,8 @@ // 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. +#[cfg(feature = "metrics")] +mod metrics; + mod yamux; pub use self::yamux::{ConnectionError, Control, IncomingSubstreams, Substream, Yamux}; diff --git a/comms/src/multiplexing/yamux.rs b/comms/src/multiplexing/yamux.rs index 811f485390..a9cf697c31 100644 --- a/comms/src/multiplexing/yamux.rs +++ b/comms/src/multiplexing/yamux.rs @@ -220,12 +220,21 @@ impl StreamId for Substream { impl tokio::io::AsyncRead for Substream { fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { - Pin::new(&mut self.stream).poll_read(cx, buf) + match Pin::new(&mut self.stream).poll_read(cx, buf) { + Poll::Ready(Ok(())) => { + #[cfg(feature = "metrics")] + super::metrics::TOTAL_BYTES_READ.inc_by(buf.filled().len() as u64); + Poll::Ready(Ok(())) + }, + res => res, + } } } impl tokio::io::AsyncWrite for Substream { fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + #[cfg(feature = "metrics")] + super::metrics::TOTAL_BYTES_WRITTEN.inc_by(buf.len() as u64); Pin::new(&mut self.stream).poll_write(cx, buf) } diff --git a/comms/src/protocol/messaging/inbound.rs b/comms/src/protocol/messaging/inbound.rs index e65f81f8ec..9445445629 100644 --- a/comms/src/protocol/messaging/inbound.rs +++ b/comms/src/protocol/messaging/inbound.rs @@ -20,12 +20,8 @@ // 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::{ - message::InboundMessage, - peer_manager::NodeId, - protocol::messaging::{MessagingEvent, MessagingProtocol}, - rate_limit::RateLimit, -}; +use super::{metrics, MessagingEvent, MessagingProtocol}; +use crate::{message::InboundMessage, peer_manager::NodeId, rate_limit::RateLimit}; use futures::{future::Either, StreamExt}; use log::*; use std::{sync::Arc, time::Duration}; @@ -67,6 +63,7 @@ impl InboundMessaging { pub async fn run(self, socket: S) where S: AsyncRead + AsyncWrite + Unpin { let peer = &self.peer; + metrics::num_sessions().inc(); debug!( target: LOG_TARGET, "Starting inbound messaging protocol for peer '{}'", @@ -82,9 +79,11 @@ impl InboundMessaging { }; tokio::pin!(stream); + let inbound_count = metrics::inbound_message_count(&self.peer); while let Some(result) = stream.next().await { match result { Ok(Ok(raw_msg)) => { + inbound_count.inc(); let msg_len = raw_msg.len(); let inbound_msg = InboundMessage::new(peer.clone(), raw_msg.freeze()); debug!( @@ -112,6 +111,7 @@ impl InboundMessaging { let _ = self.messaging_events_tx.send(Arc::new(event)); }, Ok(Err(err)) => { + metrics::error_count(peer).inc(); error!( target: LOG_TARGET, "Failed to receive from peer '{}' because '{}'", @@ -122,6 +122,7 @@ impl InboundMessaging { }, Err(_) => { + metrics::error_count(peer).inc(); debug!( target: LOG_TARGET, "Inbound messaging for peer '{}' has stopped because it was inactive for {:.0?}", @@ -134,6 +135,7 @@ impl InboundMessaging { } } + metrics::num_sessions().dec(); debug!( target: LOG_TARGET, "Inbound messaging handler exited for peer `{}`", diff --git a/comms/src/protocol/messaging/metrics.rs b/comms/src/protocol/messaging/metrics.rs new file mode 100644 index 0000000000..e43fb4a7d6 --- /dev/null +++ b/comms/src/protocol/messaging/metrics.rs @@ -0,0 +1,74 @@ +// Copyright 2021, 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::peer_manager::NodeId; +use once_cell::sync::Lazy; +use tari_metrics::{IntCounter, IntCounterVec, IntGauge}; + +pub fn num_sessions() -> IntGauge { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_gauge( + "comms::messaging::num_sessions", + "The number of active messaging sessions", + ) + .unwrap() + }); + + METER.clone() +} + +pub fn outbound_message_count(peer: &NodeId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::messaging::outbound_message_count", + "The number of handshakes per peer", + &["peer_id"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str()]) +} + +pub fn inbound_message_count(peer: &NodeId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::messaging::inbound_message_count", + "The number of handshakes per peer", + &["peer_id"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str()]) +} + +pub fn error_count(peer: &NodeId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec("comms::messaging::errors", "The number of errors per peer", &[ + "peer_id", + ]) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str()]) +} diff --git a/comms/src/protocol/messaging/mod.rs b/comms/src/protocol/messaging/mod.rs index 1fa347b3a2..7fd5703e50 100644 --- a/comms/src/protocol/messaging/mod.rs +++ b/comms/src/protocol/messaging/mod.rs @@ -29,6 +29,7 @@ pub use extension::MessagingProtocolExtension; mod error; mod forward; mod inbound; +mod metrics; mod outbound; mod protocol; pub use protocol::{ diff --git a/comms/src/protocol/messaging/outbound.rs b/comms/src/protocol/messaging/outbound.rs index a2c4288b4e..98fc30a9e2 100644 --- a/comms/src/protocol/messaging/outbound.rs +++ b/comms/src/protocol/messaging/outbound.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 super::{error::MessagingProtocolError, MessagingEvent, MessagingProtocol, SendFailReason}; +use super::{error::MessagingProtocolError, metrics, MessagingEvent, MessagingProtocol, SendFailReason}; use crate::{ connection_manager::{NegotiatedSubstream, PeerConnection}, connectivity::{ConnectivityError, ConnectivityRequester}, @@ -71,6 +71,7 @@ impl OutboundMessaging { "comms::messaging::outbound", node_id = self.peer_node_id.to_string().as_str() ); + metrics::num_sessions().inc(); async move { debug!( target: LOG_TARGET, @@ -112,6 +113,7 @@ impl OutboundMessaging { ); }, Err(err) => { + metrics::error_count(&peer_node_id).inc(); error!( target: LOG_TARGET, "Outbound messaging protocol failed for peer {}: {}", peer_node_id, err @@ -119,6 +121,7 @@ impl OutboundMessaging { }, } + metrics::num_sessions().dec(); let _ = messaging_events_tx .send(MessagingEvent::OutboundProtocolExited(peer_node_id)) .await; @@ -291,7 +294,9 @@ impl OutboundMessaging { None => Either::Right(stream.map(Ok)), }; + let outbound_count = metrics::outbound_message_count(&self.peer_node_id); let stream = stream.map(|msg| { + outbound_count.inc(); msg.map(|mut out_msg| { event!(Level::DEBUG, "Message buffered for sending {}", out_msg); out_msg.reply_success(); diff --git a/comms/src/protocol/rpc/client/metrics.rs b/comms/src/protocol/rpc/client/metrics.rs index ee77a6b91f..98f99e4492 100644 --- a/comms/src/protocol/rpc/client/metrics.rs +++ b/comms/src/protocol/rpc/client/metrics.rs @@ -22,69 +22,108 @@ use crate::{peer_manager::NodeId, protocol::ProtocolId}; use once_cell::sync::Lazy; -use tari_metrics::{Histogram, HistogramVec, IntGauge, IntGaugeVec}; +use tari_metrics::{Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec}; -pub fn sessions_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { +pub fn num_sessions(peer: &NodeId, protocol: &ProtocolId) -> IntGauge { static METER: Lazy = Lazy::new(|| { tari_metrics::register_int_gauge_vec( "comms::rpc::client::num_sessions", - "The number of active clients per node per protocol", - &["peer", "protocol"], + "The number of active clients per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn handshake_errors(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { - static METER: Lazy = Lazy::new(|| { - tari_metrics::register_int_gauge_vec( +pub fn handshake_counter(peer: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::rpc::client::handshake_count", + "The number of handshakes per peer per protocol", + &["peer_id", "protocol"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) +} + +pub fn handshake_errors(peer: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( "comms::rpc::client::handshake_errors", - "The number of handshake errors per node per protocol", - &["peer", "protocol"], + "The number of handshake errors per peer per protocol", + &["peer_id", "protocol"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) +} + +pub fn client_errors(peer: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::rpc::client::error_count", + "The number of client errors per peer per protocol", + &["peer_id", "protocol"], + ) + .unwrap() + }); + + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) +} + +pub fn client_timeouts(peer: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::rpc::client::error_timeouts", + "The number of client timeouts per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn request_response_latency(node_id: &NodeId, protocol: &ProtocolId) -> Histogram { +pub fn request_response_latency(peer: &NodeId, protocol: &ProtocolId) -> Histogram { static METER: Lazy = Lazy::new(|| { tari_metrics::register_histogram_vec( "comms::rpc::client::request_response_latency", "A histogram of request to first response latency", - &["peer", "protocol"], + &["peer_id", "protocol"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn outbound_request_bytes(node_id: &NodeId, protocol: &ProtocolId) -> Histogram { +pub fn outbound_request_bytes(peer: &NodeId, protocol: &ProtocolId) -> Histogram { static METER: Lazy = Lazy::new(|| { tari_metrics::register_histogram_vec( "comms::rpc::client::outbound_request_bytes", - "Avg. request bytes per node per protocol", - &["peer", "protocol"], + "Avg. request bytes per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn inbound_response_bytes(node_id: &NodeId, protocol: &ProtocolId) -> Histogram { +pub fn inbound_response_bytes(peer: &NodeId, protocol: &ProtocolId) -> Histogram { static METER: Lazy = Lazy::new(|| { tari_metrics::register_histogram_vec( "comms::rpc::client::inbound_response_bytes", "Avg. response bytes per peer per protocol", - &["peer", "protocol"], + &["peer_id", "protocol"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[peer.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } diff --git a/comms/src/protocol/rpc/client/mod.rs b/comms/src/protocol/rpc/client/mod.rs index 9bec5975ab..b69caaa1db 100644 --- a/comms/src/protocol/rpc/client/mod.rs +++ b/comms/src/protocol/rpc/client/mod.rs @@ -462,8 +462,10 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId if let Some(r) = self.ready_tx.take() { let _ = r.send(Ok(())); } + metrics::handshake_counter(&self.node_id, &self.protocol_id).inc(); }, Err(err) => { + metrics::handshake_errors(&self.node_id, &self.protocol_id).inc(); if let Some(r) = self.ready_tx.take() { let _ = r.send(Err(err.into())); } @@ -472,8 +474,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId }, } - let session_counter = metrics::sessions_counter(&self.node_id, &self.protocol_id); - session_counter.inc(); + metrics::num_sessions(&self.node_id, &self.protocol_id).inc(); loop { tokio::select! { biased; @@ -484,6 +485,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId match req { Some(req) => { if let Err(err) = self.handle_request(req).await { + metrics::client_errors(&self.node_id, &self.protocol_id).inc(); error!(target: LOG_TARGET, "(stream={}) Unexpected error: {}. Worker is terminating.", self.stream_id(), err); break; } @@ -493,21 +495,23 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId } } } - session_counter.dec(); + metrics::num_sessions(&self.node_id, &self.protocol_id).dec(); if let Err(err) = self.framed.close().await { debug!( target: LOG_TARGET, - "(stream={}) IO Error when closing substream: {}", + "(stream: {}, peer: {}) IO Error when closing substream: {}", self.stream_id(), + self.node_id, err ); } debug!( target: LOG_TARGET, - "(stream={}) RpcClientWorker ({}) terminated.", + "(stream: {}, peer: {}) RpcClientWorker ({}) terminated.", self.stream_id(), + self.node_id, self.protocol_name() ); } @@ -552,6 +556,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId self.stream_id(), start.elapsed() ); + metrics::client_timeouts(&self.node_id, &self.protocol_id).inc(); let _ = reply.send(Err(RpcStatus::timed_out("Response timed out"))); return Ok(()); }, @@ -583,7 +588,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId Ok(()) } - #[tracing::instrument(name = "rpc_do_request_response", skip(self, reply, request), fields(request_method = ?request.method, request_size = request.message.len()))] + #[tracing::instrument(name = "rpc_do_request_response", skip(self, reply, request), fields(request_method = ?request.method, request_body_size = request.message.len()))] async fn do_request_response( &mut self, request: BaseRequest, @@ -629,6 +634,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId let mut metrics_timer = Some(latency.start_timer()); if let Err(err) = self.send_request(req).await { warn!(target: LOG_TARGET, "{}", err); + metrics::client_errors(&self.node_id, &self.protocol_id).inc(); let _ = response_tx.send(Err(err.into())); return Ok(()); } @@ -637,7 +643,9 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId if self.shutdown_signal.is_triggered() { debug!( target: LOG_TARGET, - "[{}, stream_id: {}, req_id: {}] Client connector closed. Quitting stream early", + "[peer: {}, protocol: {}, stream_id: {}, req_id: {}] Client connector closed. Quitting stream \ + early", + self.node_id, self.protocol_name(), self.stream_id(), request_id @@ -671,7 +679,17 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId "Request {} (method={}) timed out", request_id, method, ); event!(Level::ERROR, "Response timed out"); - if !response_tx.is_closed() { + metrics::client_timeouts(&self.node_id, &self.protocol_id).inc(); + if response_tx.is_closed() { + let req = proto::rpc::RpcRequest { + request_id: request_id as u32, + method, + flags: RpcMessageFlags::FIN.bits().into(), + ..Default::default() + }; + + self.send_request(req).await?; + } else { let _ = response_tx.send(Err(RpcStatus::timed_out("Response timed out"))).await; } break; @@ -713,9 +731,8 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId let req = proto::rpc::RpcRequest { request_id: request_id as u32, method, - deadline: self.config.deadline.map(|t| t.as_secs()).unwrap_or(0), flags: RpcMessageFlags::FIN.bits().into(), - payload: vec![], + ..Default::default() }; self.send_request(req).await?; @@ -773,7 +790,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId break resp; }, Err(RpcError::ResponseIdDidNotMatchRequest { actual, expected }) - if actual.saturating_add(1) == request_id => + if actual.wrapping_add(1) == request_id => { warn!( target: LOG_TARGET, @@ -783,7 +800,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId // Be lenient for a number of messages that may have been buffered to come through for the previous // request. - const MAX_ALLOWED_IGNORED: usize = 5; + const MAX_ALLOWED_IGNORED: usize = 20; if num_ignored > MAX_ALLOWED_IGNORED { return Err(RpcError::ResponseIdDidNotMatchRequest { actual, expected }); } @@ -798,7 +815,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + StreamId fn next_request_id(&mut self) -> u16 { let next_id = self.next_request_id; // request_id is allowed to wrap around back to 0 - self.next_request_id = self.next_request_id.checked_add(1).unwrap_or(0); + self.next_request_id = self.next_request_id.wrapping_add(1); next_id } diff --git a/comms/src/protocol/rpc/server/error.rs b/comms/src/protocol/rpc/server/error.rs index f5cc145de2..7259e987fb 100644 --- a/comms/src/protocol/rpc/server/error.rs +++ b/comms/src/protocol/rpc/server/error.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::protocol::rpc::handshake::RpcHandshakeError; +use crate::{proto, protocol::rpc::handshake::RpcHandshakeError}; use prost::DecodeError; use std::io; use tokio::sync::oneshot; @@ -42,7 +42,15 @@ pub enum RpcServerError { #[error("Service not found for protocol `{0}`")] ProtocolServiceNotFound(String), #[error("Unexpected incoming message")] - UnexpectedIncomingMessage, + UnexpectedIncomingMessage(proto::rpc::RpcRequest), + #[error("Unexpected incoming MALFORMED message")] + UnexpectedIncomingMessageMalformed, + #[error("Client interrupted stream")] + ClientInterruptedStream, + #[error("Service call exceeded deadline")] + ServiceCallExceededDeadline, + #[error("Stream read exceeded deadline")] + ReadStreamExceededDeadline, } impl From for RpcServerError { @@ -50,3 +58,21 @@ impl From for RpcServerError { RpcServerError::RequestCanceled } } + +impl RpcServerError { + pub fn to_debug_string(&self) -> String { + use RpcServerError::*; + match self { + DecodeError(_) => "DecodeError".to_string(), + Io(err) => { + format!("Io({:?})", err.kind()) + }, + HandshakeError(_) => "HandshakeError".to_string(), + ProtocolServiceNotFound(_) => "ProtocolServiceNotFound".to_string(), + UnexpectedIncomingMessage(_) => "UnexpectedIncomingMessage".to_string(), + err => { + format!("{:?}", err) + }, + } + } +} diff --git a/comms/src/protocol/rpc/server/metrics.rs b/comms/src/protocol/rpc/server/metrics.rs index cb0c1d7d2a..ed11db7e9e 100644 --- a/comms/src/protocol/rpc/server/metrics.rs +++ b/comms/src/protocol/rpc/server/metrics.rs @@ -20,16 +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::{peer_manager::NodeId, protocol::ProtocolId}; +use crate::{ + peer_manager::NodeId, + protocol::{ + rpc::{RpcServerError, RpcStatusCode}, + ProtocolId, + }, +}; use once_cell::sync::Lazy; use tari_metrics::{Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec}; -pub fn sessions_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { +pub fn num_sessions(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { static METER: Lazy = Lazy::new(|| { tari_metrics::register_int_gauge_vec( "comms::rpc::server::num_sessions", - "The number of active server sessions per node per protocol", - &["peer", "protocol"], + "The number of active server sessions per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); @@ -37,12 +43,12 @@ pub fn sessions_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn handshake_error_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntGauge { - static METER: Lazy = Lazy::new(|| { - tari_metrics::register_int_gauge_vec( - "comms::rpc::server::handshake_errors", - "The number of handshake errors per node per protocol", - &["peer", "protocol"], +pub fn handshake_error_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::rpc::server::handshake_error_count", + "The number of handshake errors per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); @@ -50,25 +56,46 @@ pub fn handshake_error_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntGa METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) } -pub fn error_counter(node_id: &NodeId, protocol: &ProtocolId) -> IntCounter { +pub fn error_counter(node_id: &NodeId, protocol: &ProtocolId, err: &RpcServerError) -> IntCounter { static METER: Lazy = Lazy::new(|| { tari_metrics::register_int_counter_vec( "comms::rpc::server::error_count", - "The number of RPC errors per node per protocol", - &["peer", "protocol"], + "The number of RPC errors per peer per protocol", + &["peer_id", "protocol", "error"], ) .unwrap() }); - METER.with_label_values(&[node_id.to_string().as_str(), String::from_utf8_lossy(protocol).as_ref()]) + METER.with_label_values(&[ + node_id.to_string().as_str(), + String::from_utf8_lossy(protocol).as_ref(), + err.to_debug_string().as_str(), + ]) +} + +pub fn status_error_counter(node_id: &NodeId, protocol: &ProtocolId, status_code: RpcStatusCode) -> IntCounter { + static METER: Lazy = Lazy::new(|| { + tari_metrics::register_int_counter_vec( + "comms::rpc::server::status_error_count", + "The number of RPC errors by status code per peer per protocol", + &["peer_id", "protocol", "status"], + ) + .unwrap() + }); + + METER.with_label_values(&[ + node_id.to_string().as_str(), + String::from_utf8_lossy(protocol).as_ref(), + status_code.to_debug_string().as_str(), + ]) } pub fn inbound_requests_bytes(node_id: &NodeId, protocol: &ProtocolId) -> Histogram { static METER: Lazy = Lazy::new(|| { tari_metrics::register_histogram_vec( "comms::rpc::server::inbound_request_bytes", - "Avg. request bytes per node per protocol", - &["peer", "protocol"], + "Avg. request bytes per peer per protocol", + &["peer_id", "protocol"], ) .unwrap() }); @@ -81,7 +108,7 @@ pub fn outbound_response_bytes(node_id: &NodeId, protocol: &ProtocolId) -> Histo tari_metrics::register_histogram_vec( "comms::rpc::server::outbound_response_bytes", "Avg. response bytes per peer per protocol", - &["peer", "protocol"], + &["peer_id", "protocol"], ) .unwrap() }); diff --git a/comms/src/protocol/rpc/server/mod.rs b/comms/src/protocol/rpc/server/mod.rs index 31fa62ee5a..ed02b82149 100644 --- a/comms/src/protocol/rpc/server/mod.rs +++ b/comms/src/protocol/rpc/server/mod.rs @@ -70,12 +70,12 @@ use prost::Message; use std::{ borrow::Cow, future::Future, + io, pin::Pin, sync::Arc, task::Poll, time::{Duration, Instant}, }; -use tari_metrics::IntCounter; use tokio::{sync::mpsc, time}; use tokio_stream::Stream; use tower::Service; @@ -386,10 +386,10 @@ where let node_id = node_id.clone(); self.executor .try_spawn(async move { - let sessions_counter = metrics::sessions_counter(&node_id, &service.protocol); - sessions_counter.inc(); + let num_sessions = metrics::num_sessions(&node_id, &service.protocol); + num_sessions.inc(); service.start().await; - sessions_counter.dec(); + num_sessions.dec(); }) .map_err(|_| RpcServerError::MaximumSessionsReached)?; @@ -405,7 +405,6 @@ struct ActivePeerRpcService { framed: CanonicalFraming, comms_provider: TCommsProvider, logging_context_string: Arc, - error_counter: IntCounter, } impl ActivePeerRpcService @@ -421,7 +420,6 @@ where framed: CanonicalFraming, comms_provider: TCommsProvider, ) -> Self { - let error_counter = metrics::error_counter(&node_id, &protocol); Self { logging_context_string: Arc::new(format!( "stream_id: {}, peer: {}, protocol: {}", @@ -436,7 +434,6 @@ where service, framed, comms_provider, - error_counter, } } @@ -446,7 +443,7 @@ where "({}) Rpc server started.", self.logging_context_string, ); if let Err(err) = self.run().await { - self.error_counter.inc(); + metrics::error_counter(&self.node_id, &self.protocol, &err).inc(); error!( target: LOG_TARGET, "({}) Rpc server exited with an error: {}", self.logging_context_string, err @@ -470,7 +467,13 @@ where err ); } - self.error_counter.inc(); + error!( + target: LOG_TARGET, + "(peer: {}, protocol: {}) Failed to handle request: {}", + self.node_id, + self.protocol_name(), + err + ); return Err(err); } let elapsed = start.elapsed(); @@ -489,7 +492,6 @@ where "({}) Failed to close substream after socket error: {}", self.logging_context_string, err ); } - self.error_counter.inc(); return Err(err.into()); }, } @@ -524,8 +526,8 @@ where flags: RpcMessageFlags::FIN.bits().into(), payload: status.to_details_bytes(), }; + metrics::status_error_counter(&self.node_id, &self.protocol, status.as_status_code()).inc(); self.framed.send(bad_request.to_encoded_bytes().into()).await?; - self.error_counter.inc(); return Ok(()); } @@ -573,13 +575,17 @@ where Err(_) => { warn!( target: LOG_TARGET, - "RPC service was not able to complete within the deadline ({:.0?}). Request aborted for peer '{}' \ - ({}).", + "{} RPC service was not able to complete within the deadline ({:.0?}). Request aborted", + self.logging_context_string, deadline, - self.node_id, - self.protocol_name() ); - self.error_counter.inc(); + + metrics::error_counter( + &self.node_id, + &self.protocol, + &RpcServerError::ServiceCallExceededDeadline, + ) + .inc(); return Ok(()); }, }; @@ -589,12 +595,9 @@ where self.process_body(request_id, deadline, body).await?; }, Err(err) => { - debug!( + error!( target: LOG_TARGET, - "(peer: {}, protocol: {}) Service returned an error: {}", - self.node_id, - self.protocol_name(), - err + "{} Service returned an error: {}", self.logging_context_string, err ); let resp = proto::rpc::RpcResponse { request_id, @@ -603,7 +606,7 @@ where payload: err.to_details_bytes(), }; - self.error_counter.inc(); + metrics::status_error_counter(&self.node_id, &self.protocol, err.as_status_code()).inc(); self.framed.send(resp.to_encoded_bytes().into()).await?; }, } @@ -623,18 +626,33 @@ where ) -> Result<(), RpcServerError> { let response_bytes = metrics::outbound_response_bytes(&self.node_id, &self.protocol); trace!(target: LOG_TARGET, "Service call succeeded"); + + let node_id = self.node_id.clone(); + let protocol = self.protocol.clone(); let mut stream = body .into_message() .map(|result| into_response(request_id, result)) - .flat_map(|message| stream::iter(ChunkedResponseIter::new(message))) + .flat_map(move |message| { + if !message.status.is_ok() { + metrics::status_error_counter(&node_id, &protocol, message.status).inc(); + } + stream::iter(ChunkedResponseIter::new(message)) + }) .map(|resp| Bytes::from(resp.to_encoded_bytes())); loop { // Check if the client interrupted the outgoing stream if let Err(err) = self.check_interruptions().await { - // Debug level because there are many valid reasons to interrupt a stream - debug!(target: LOG_TARGET, "Stream interrupted: {}", err); - break; + match err { + err @ RpcServerError::ClientInterruptedStream => { + debug!(target: LOG_TARGET, "Stream was interrupted: {}", err); + break; + }, + err => { + error!(target: LOG_TARGET, "Stream was interrupted: {}", err); + return Err(err); + }, + } } let next_item = log_timing( @@ -667,7 +685,12 @@ where deadline ); - self.error_counter.inc(); + metrics::error_counter( + &self.node_id, + &self.protocol, + &RpcServerError::ReadStreamExceededDeadline, + ) + .inc(); break; }, } @@ -677,7 +700,22 @@ where async fn check_interruptions(&mut self) -> Result<(), RpcServerError> { let check = future::poll_fn(|cx| match Pin::new(&mut self.framed).poll_next(cx) { - Poll::Ready(Some(Ok(_))) => Poll::Ready(Some(RpcServerError::UnexpectedIncomingMessage)), + Poll::Ready(Some(Ok(mut msg))) => { + let decoded_msg = match proto::rpc::RpcRequest::decode(&mut msg) { + Ok(msg) => msg, + Err(err) => { + error!(target: LOG_TARGET, "Client send MALFORMED response: {}", err); + return Poll::Ready(Some(RpcServerError::UnexpectedIncomingMessageMalformed)); + }, + }; + let msg_flags = RpcMessageFlags::from_bits_truncate(decoded_msg.flags as u8); + if msg_flags.is_fin() { + Poll::Ready(Some(RpcServerError::ClientInterruptedStream)) + } else { + Poll::Ready(Some(RpcServerError::UnexpectedIncomingMessage(decoded_msg))) + } + }, + Poll::Ready(Some(Err(err))) if err.kind() == io::ErrorKind::WouldBlock => Poll::Ready(None), Poll::Ready(Some(Err(err))) => Poll::Ready(Some(RpcServerError::from(err))), Poll::Ready(None) => Poll::Ready(Some(RpcServerError::StreamClosedByRemote)), Poll::Pending => Poll::Ready(None), @@ -722,7 +760,7 @@ fn into_response(request_id: u32, result: Result) -> RpcRe } RpcResponse { request_id, - status: RpcStatus::ok().status_code(), + status: RpcStatus::ok().as_status_code(), flags, payload: msg.into_bytes().unwrap_or_else(Bytes::new), } @@ -731,7 +769,7 @@ fn into_response(request_id: u32, result: Result) -> RpcRe debug!(target: LOG_TARGET, "Body contained an error: {}", err); RpcResponse { request_id, - status: err.status_code(), + status: err.as_status_code(), flags: RpcMessageFlags::FIN, payload: Bytes::from(err.to_details_bytes()), } diff --git a/comms/src/protocol/rpc/status.rs b/comms/src/protocol/rpc/status.rs index 9194d36479..d2a8e20e6d 100644 --- a/comms/src/protocol/rpc/status.rs +++ b/comms/src/protocol/rpc/status.rs @@ -114,10 +114,10 @@ impl RpcStatus { } pub fn as_code(&self) -> u32 { - self.code as u32 + self.code.as_u32() } - pub fn status_code(&self) -> RpcStatusCode { + pub fn as_status_code(&self) -> RpcStatusCode { self.code } @@ -212,6 +212,14 @@ impl RpcStatusCode { pub fn is_timeout(self) -> bool { self == Self::Timeout } + + pub fn as_u32(&self) -> u32 { + *self as u32 + } + + pub fn to_debug_string(&self) -> String { + format!("{:?}", self) + } } impl From for RpcStatusCode { diff --git a/comms/src/protocol/rpc/test/comms_integration.rs b/comms/src/protocol/rpc/test/comms_integration.rs index f43f921081..025bfcc2b9 100644 --- a/comms/src/protocol/rpc/test/comms_integration.rs +++ b/comms/src/protocol/rpc/test/comms_integration.rs @@ -87,6 +87,6 @@ async fn run_service() { mock_state.set_response_err(RpcStatus::bad_request("Insert ๐Ÿ’พ")); let err = client.request_response::<_, ()>((), 0.into()).await.unwrap_err(); unpack_enum!(RpcError::RequestFailed(status) = err); - unpack_enum!(RpcStatusCode::BadRequest = status.status_code()); + unpack_enum!(RpcStatusCode::BadRequest = status.as_status_code()); assert_eq!(mock_state.call_count(), 2); } diff --git a/comms/src/protocol/rpc/test/smoke.rs b/comms/src/protocol/rpc/test/smoke.rs index 0fc1f05256..44eb8820ec 100644 --- a/comms/src/protocol/rpc/test/smoke.rs +++ b/comms/src/protocol/rpc/test/smoke.rs @@ -152,7 +152,7 @@ async fn request_response_errors_and_streaming() { let err = client.return_error().await.unwrap_err(); unpack_enum!(RpcError::RequestFailed(status) = err); - assert_eq!(status.status_code(), RpcStatusCode::NotImplemented); + assert_eq!(status.as_status_code(), RpcStatusCode::NotImplemented); assert_eq!(status.details(), "I haven't gotten to this yet :("); let stream = client.streaming_error("Gurglesplurb".to_string()).await.unwrap(); @@ -164,7 +164,7 @@ async fn request_response_errors_and_streaming() { .into_iter() .collect::>() .unwrap_err(); - assert_eq!(status.status_code(), RpcStatusCode::BadRequest); + assert_eq!(status.as_status_code(), RpcStatusCode::BadRequest); assert_eq!(status.details(), "What does 'Gurglesplurb' mean?"); let stream = client.streaming_error2().await.unwrap(); @@ -174,7 +174,7 @@ async fn request_response_errors_and_streaming() { assert_eq!(first_reply, "This is ok"); let second_reply = results.get(1).unwrap().as_ref().unwrap_err(); - assert_eq!(second_reply.status_code(), RpcStatusCode::BadRequest); + assert_eq!(second_reply.as_status_code(), RpcStatusCode::BadRequest); assert_eq!(second_reply.details(), "This is a problem"); let pk_hex = client.get_public_key_hex().await.unwrap(); @@ -262,7 +262,7 @@ async fn response_too_big() { .await .unwrap_err(); unpack_enum!(RpcError::RequestFailed(status) = err); - unpack_enum!(RpcStatusCode::MalformedResponse = status.status_code()); + unpack_enum!(RpcStatusCode::MalformedResponse = status.as_status_code()); // Check that the exact frame size boundary works and that the session is still going let _ = client @@ -314,7 +314,7 @@ async fn timeout() { let err = client.say_hello(Default::default()).await.unwrap_err(); unpack_enum!(RpcError::RequestFailed(status) = err); - assert_eq!(status.status_code(), RpcStatusCode::Timeout); + assert_eq!(status.as_status_code(), RpcStatusCode::Timeout); *delay.write().await = Duration::from_secs(0); From 038b9a6b14538ad13b227ed0da750fcdf42be48a Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Sun, 21 Nov 2021 13:17:27 +0200 Subject: [PATCH 12/13] chore: minor clippy fixes (#3576) Description --- - remove unnecessary vec copy in protobuf conversion - clippy fixes - remove duplicate utility function Motivation and Context --- Fix some minor clippy warnings How Has This Been Tested? --- Existing tests pass --- .../src/conversions/aggregate_body.rs | 2 +- base_layer/core/src/proto/mod.rs | 2 +- base_layer/core/src/proto/transaction.rs | 2 +- base_layer/core/src/proto/utils.rs | 15 --------------- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/applications/tari_app_grpc/src/conversions/aggregate_body.rs b/applications/tari_app_grpc/src/conversions/aggregate_body.rs index bd8a288800..2b296b96ae 100644 --- a/applications/tari_app_grpc/src/conversions/aggregate_body.rs +++ b/applications/tari_app_grpc/src/conversions/aggregate_body.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::convert::TryFrom; -use tari_core::{proto::utils::try_convert_all, transactions::aggregated_body::AggregateBody}; +use tari_core::{tari_utilities::convert::try_convert_all, transactions::aggregated_body::AggregateBody}; use crate::tari_rpc as grpc; diff --git a/base_layer/core/src/proto/mod.rs b/base_layer/core/src/proto/mod.rs index e555a36de7..f097724dae 100644 --- a/base_layer/core/src/proto/mod.rs +++ b/base_layer/core/src/proto/mod.rs @@ -51,4 +51,4 @@ mod block; #[cfg(any(feature = "base_node", feature = "base_node_proto"))] mod block_header; #[cfg(any(feature = "base_node", feature = "base_node_proto"))] -pub mod utils; +mod utils; diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 4ee0f6e7c1..fc96ee43ac 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -162,7 +162,7 @@ impl TryFrom for TransactionOutput { let sender_offset_public_key = PublicKey::from_bytes(output.sender_offset_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; - let script = TariScript::from_bytes(&output.script.to_vec()).map_err(|err| err.to_string())?; + let script = TariScript::from_bytes(&output.script).map_err(|err| err.to_string())?; let metadata_signature = output .metadata_signature diff --git a/base_layer/core/src/proto/utils.rs b/base_layer/core/src/proto/utils.rs index 0fb2635014..6e8738995a 100644 --- a/base_layer/core/src/proto/utils.rs +++ b/base_layer/core/src/proto/utils.rs @@ -21,23 +21,8 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use prost_types::Timestamp; -use std::convert::TryInto; use tari_crypto::tari_utilities::epoch_time::EpochTime; -/// Tries to convert a series of `T`s to `U`s, returning an error at the first failure -pub fn try_convert_all(into_iter: I) -> Result, T::Error> -where - I: IntoIterator, - T: TryInto, -{ - let iter = into_iter.into_iter(); - let mut result = Vec::with_capacity(iter.size_hint().0); - for item in iter { - result.push(item.try_into()?); - } - Ok(result) -} - /// Utility function that converts a `prost::Timestamp` to a `chrono::DateTime` pub(crate) fn timestamp_to_datetime(timestamp: Timestamp) -> EpochTime { (timestamp.seconds as u64).into() From 57f51bc8fd23e8380307d210bdc247cf570bc083 Mon Sep 17 00:00:00 2001 From: David Main <51991544+StriderDM@users.noreply.github.com> Date: Sun, 21 Nov 2021 13:47:04 +0200 Subject: [PATCH 13/13] feat: language detection for mnemonic seed words (#3590) Description --- Implementation of a basic language detection algorithm for mnemonic words Motivation and Context --- Improve language detection for mnemonic words How Has This Been Tested? --- cargo test --all --- base_layer/key_manager/src/mnemonic.rs | 99 +++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 11 deletions(-) diff --git a/base_layer/key_manager/src/mnemonic.rs b/base_layer/key_manager/src/mnemonic.rs index 4226c8c250..6e7e6ef8f3 100644 --- a/base_layer/key_manager/src/mnemonic.rs +++ b/base_layer/key_manager/src/mnemonic.rs @@ -25,7 +25,7 @@ use crate::{ error::{KeyManagerError, MnemonicError}, mnemonic_wordlists::*, }; -use std::slice::Iter; +use std::{cmp::Ordering, slice::Iter}; use strum_macros::{Display, EnumString}; use tari_crypto::tari_utilities::bit::*; @@ -33,7 +33,7 @@ use tari_crypto::tari_utilities::bit::*; /// It can autodetect the language of the Mnemonic word sequence // TODO: Develop a language autodetection mechanism to distinguish between ChineseTraditional and ChineseSimplified -#[derive(Clone, Debug, PartialEq, EnumString, Display)] +#[derive(Clone, Debug, PartialEq, EnumString, Display, Copy)] pub enum MnemonicLanguage { ChineseSimplified, English, @@ -47,12 +47,8 @@ pub enum MnemonicLanguage { impl MnemonicLanguage { /// Detects the mnemonic language of a specific word by searching all defined mnemonic word lists pub fn from(mnemonic_word: &str) -> Result { - for language in MnemonicLanguage::iterator() { - if find_mnemonic_index_from_word(mnemonic_word, language).is_ok() { - return Ok((*language).clone()); - } - } - Err(MnemonicError::UnknownLanguage) + let words = vec![mnemonic_word.to_string()]; + detect_language(&words) } /// Returns an iterator for the MnemonicLanguage enum group to allow iteration over all defined languages @@ -158,11 +154,54 @@ pub fn from_bytes(bytes: Vec, language: &MnemonicLanguage) -> Result Result { + let count = words.iter().len(); + match count.cmp(&1) { + Ordering::Less => { + return Err(MnemonicError::UnknownLanguage); + }, + Ordering::Equal => { + let word = words.get(0).ok_or(MnemonicError::EncodeInvalidLength)?; + for language in MnemonicLanguage::iterator() { + if find_mnemonic_index_from_word(word, language).is_ok() { + return Ok(*language); + } + } + return Err(MnemonicError::UnknownLanguage); + }, + Ordering::Greater => { + for word in words { + let mut languages = Vec::with_capacity(MnemonicLanguage::iterator().len()); + // detect all languages in which a word falls into + for language in MnemonicLanguage::iterator() { + if find_mnemonic_index_from_word(word, language).is_ok() { + languages.push(*language); + } + } + // check if at least one of the languages is consistent for all other words against languages yielded + // from the initial word for this iteration + for language in languages { + let mut consistent = true; + for compare in words { + if compare != word && find_mnemonic_index_from_word(compare, &language).is_err() { + consistent = false; + } + } + if consistent { + return Ok(language); + } + } + } + }, + } + + Err(MnemonicError::UnknownLanguage) +} + /// Generates a vector of bytes that represent the provided mnemonic sequence of words, the language of the mnemonic -/// sequence is autodetected +/// sequence is detected pub fn to_bytes(mnemonic_seq: &[String]) -> Result, MnemonicError> { - let first_word = mnemonic_seq.get(0).ok_or(MnemonicError::EncodeInvalidLength)?; - let language = MnemonicLanguage::from(first_word)?; // Autodetect language + let language = self::detect_language(mnemonic_seq)?; to_bytes_with_language(mnemonic_seq, &language) } @@ -289,6 +328,44 @@ mod test { assert!(MnemonicLanguage::from(&"ใŠใ‹ใ‚ใ•ใ‚“".to_string()).is_err()); // Invalid Mnemonic Japanese word assert!(MnemonicLanguage::from(&"๋‹ต์ •๋„ˆ".to_string()).is_err()); // Invalid Mnemonic Korean word assert!(MnemonicLanguage::from(&"desvelado".to_string()).is_err()); // Invalid Mnemonic Spanish word + + // English/Spanish + English/French -> English + let words1 = vec![ + "album".to_string(), + "area".to_string(), + "opera".to_string(), + "abandon".to_string(), + ]; + assert_eq!(detect_language(&words1), Ok(MnemonicLanguage::English)); + + // English/Spanish + English/French + Italian/Spanish + let words2 = vec![ + "album".to_string(), + "area".to_string(), + "opera".to_string(), + "abandon".to_string(), + "tipico".to_string(), + ]; + assert_eq!(detect_language(&words2).is_err(), true); + + // bounds check (last word is invalid) + let words3 = vec![ + "album".to_string(), + "area".to_string(), + "opera".to_string(), + "abandon".to_string(), + "topazio".to_string(), + ]; + assert_eq!(detect_language(&words3).is_err(), true); + + // building up a word list: English/French + French -> French + let mut words = Vec::with_capacity(3); + words.push("concert".to_string()); + assert_eq!(detect_language(&words), Ok(MnemonicLanguage::English)); + words.push("abandon".to_string()); + assert_eq!(detect_language(&words), Ok(MnemonicLanguage::English)); + words.push("barbier".to_string()); + assert_eq!(detect_language(&words), Ok(MnemonicLanguage::French)); } #[test]