From e91874e2574cdc0585340af43a570251100b3e86 Mon Sep 17 00:00:00 2001 From: s8sato <49983831+s8sato@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:24:30 +0900 Subject: [PATCH] Add optional nonce to distinguish transactions. Close #1493 Signed-off-by: s8sato <49983831+s8sato@users.noreply.github.com> --- Cargo.lock | 19 +++++++++++++++++++ client/Cargo.toml | 1 + client/src/client.rs | 37 +++++++++++++++++++++++++++++++++++++ client/src/config.rs | 10 +++++++--- client_cli/src/main.rs | 2 +- data_model/src/lib.rs | 21 +++++++++++++++++++++ 6 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e903ca2f483..7a2e7dd14a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,6 +896,15 @@ dependencies = [ "synstructure", ] +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] + [[package]] name = "ff" version = "0.10.1" @@ -1396,6 +1405,15 @@ dependencies = [ "bytes", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "int_traits" version = "0.1.1" @@ -1451,6 +1469,7 @@ dependencies = [ "color-eyre", "criterion", "eyre", + "fastrand", "futures", "http", "iroha_config", diff --git a/client/Cargo.toml b/client/Cargo.toml index e1bb1b836c8..a089ca73e3d 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -27,6 +27,7 @@ iroha_version = { version = "=0.1.0", path = "../version" } iroha_telemetry = { path = "../telemetry" } eyre = "0.6.5" +fastrand = "1.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" attohttpc = "0.16.3" diff --git a/client/src/client.rs b/client/src/client.rs index 320750fb136..52525f80058 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -38,6 +38,8 @@ pub struct Client { pub account_id: AccountId, /// Http headers which will be appended to each request pub headers: http_client::Headers, + /// If `true` add nonce, which make different hashes for transactions which occur repeatedly and simultaneously + pub add_transaction_nonce: bool, } /// Representation of `Iroha` client. @@ -57,6 +59,7 @@ impl Client { ), account_id: configuration.account_id.clone(), headers: HashMap::default(), + add_transaction_nonce: configuration.add_transaction_nonce, } } @@ -75,6 +78,7 @@ impl Client { ), account_id: configuration.account_id.clone(), headers, + add_transaction_nonce: configuration.add_transaction_nonce, } } @@ -87,11 +91,13 @@ impl Client { instructions: Vec, metadata: UnlimitedMetadata, ) -> Result { + let nonce = self.add_transaction_nonce.then(|| fastrand::u32(..)); Transaction::with_metadata( instructions, self.account_id.clone(), self.proposed_transaction_ttl_ms, metadata, + nonce, ) .sign(&self.key_pair) } @@ -645,3 +651,34 @@ pub mod uri { /// Get pending transactions. pub const PENDING_TRANSACTIONS: &str = "/pending_transactions"; } + +#[cfg(test)] +mod tests { + #![allow(clippy::restriction)] + use super::*; + + #[test] + fn txs_same_except_for_nonce_have_different_hashes() { + let keys = KeyPair::generate().unwrap(); + let cfg = Configuration { + public_key: keys.public_key, + private_key: keys.private_key, + add_transaction_nonce: true, + ..Configuration::default() + }; + let client = Client::new(&cfg); + + let build_transaction = || { + client + .build_transaction(vec![], UnlimitedMetadata::new()) + .unwrap() + }; + let tx1 = build_transaction(); + let mut tx2 = build_transaction(); + + tx2.payload.creation_time = tx1.payload.creation_time; + assert_ne!(tx1.hash(), tx2.hash()); + tx2.payload.nonce = tx1.payload.nonce; + assert_eq!(tx1.hash(), tx2.hash()); + } +} diff --git a/client/src/config.rs b/client/src/config.rs index c70a8dbc403..2179e6c2997 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -11,6 +11,7 @@ const DEFAULT_TORII_API_URL: &str = "127.0.0.1:8080"; const DEFAULT_TRANSACTION_TIME_TO_LIVE_MS: u64 = 100_000; const DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS: u64 = 10_000; const DEFAULT_MAX_INSTRUCTION_NUMBER: u64 = 2_u64.pow(12); +const DEFAULT_ADD_TRANSACTION_NONCE: bool = false; /// `Configuration` provides an ability to define client parameters such as `TORII_URL`. // TODO: design macro to load config from env. @@ -30,13 +31,15 @@ pub struct Configuration { pub torii_api_url: String, /// Proposed transaction TTL in milliseconds. pub transaction_time_to_live_ms: u64, - /// `Logger` configuration. - #[config(inner)] - pub logger_configuration: LoggerConfiguration, /// Transaction status wait timeout in milliseconds. pub transaction_status_timeout_ms: u64, /// Maximum number of instructions per transaction pub max_instruction_number: u64, + /// If `true` add nonce, which make different hashes for transactions which occur repeatedly and simultaneously + pub add_transaction_nonce: bool, + /// `Logger` configuration. + #[config(inner)] + pub logger_configuration: LoggerConfiguration, } impl Default for Configuration { @@ -49,6 +52,7 @@ impl Default for Configuration { transaction_time_to_live_ms: DEFAULT_TRANSACTION_TIME_TO_LIVE_MS, transaction_status_timeout_ms: DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS, max_instruction_number: DEFAULT_MAX_INSTRUCTION_NUMBER, + add_transaction_nonce: DEFAULT_ADD_TRANSACTION_NONCE, logger_configuration: LoggerConfiguration::default(), } } diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 785197d3cc6..2f8e48a6980 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -14,7 +14,7 @@ use iroha_crypto::prelude::*; use iroha_data_model::prelude::*; use structopt::StructOpt; -/// Metadata wrapper, which can be captured from cli arguments (from user suplied file). +/// Metadata wrapper, which can be captured from cli arguments (from user supplied file). #[derive(Debug, Clone)] pub struct Metadata(pub UnlimitedMetadata); diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 4c2513adcab..e2372eb0290 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -2039,6 +2039,8 @@ pub mod transaction { pub creation_time: u64, /// The transaction will be dropped after this time if it is still in a `Queue`. pub time_to_live_ms: u64, + /// Random value to make different hashes for transactions which occur repeatedly and simultaneously + pub nonce: Option, /// Metadata. pub metadata: UnlimitedMetadata, } @@ -2116,6 +2118,23 @@ pub mod transaction { account_id, proposed_ttl_ms, UnlimitedMetadata::new(), + None, + ) + } + + /// [`Transaction`] constructor with nonce. + pub fn with_nonce( + instructions: Vec, + account_id: ::Id, + proposed_ttl_ms: u64, + nonce: u32, + ) -> Transaction { + Transaction::with_metadata( + instructions, + account_id, + proposed_ttl_ms, + UnlimitedMetadata::new(), + Some(nonce), ) } @@ -2125,6 +2144,7 @@ pub mod transaction { account_id: ::Id, proposed_ttl_ms: u64, metadata: UnlimitedMetadata, + nonce: Option, ) -> Transaction { #[allow(clippy::cast_possible_truncation, clippy::expect_used)] Transaction { @@ -2136,6 +2156,7 @@ pub mod transaction { .expect("Failed to get System Time.") .as_millis() as u64, time_to_live_ms: proposed_ttl_ms, + nonce, metadata, }, signatures: BTreeSet::new(),