Skip to content

Commit

Permalink
Add optional nonce to distinguish transactions. Close hyperledger#1493
Browse files Browse the repository at this point in the history
Signed-off-by: s8sato <49983831+s8sato@users.noreply.github.com>
  • Loading branch information
s8sato committed Oct 26, 2021
1 parent 8fa1de7 commit e91874e
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 4 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 37 additions & 0 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -57,6 +59,7 @@ impl Client {
),
account_id: configuration.account_id.clone(),
headers: HashMap::default(),
add_transaction_nonce: configuration.add_transaction_nonce,
}
}

Expand All @@ -75,6 +78,7 @@ impl Client {
),
account_id: configuration.account_id.clone(),
headers,
add_transaction_nonce: configuration.add_transaction_nonce,
}
}

Expand All @@ -87,11 +91,13 @@ impl Client {
instructions: Vec<Instruction>,
metadata: UnlimitedMetadata,
) -> Result<Transaction> {
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)
}
Expand Down Expand Up @@ -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());
}
}
10 changes: 7 additions & 3 deletions client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 {
Expand All @@ -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(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion client_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
21 changes: 21 additions & 0 deletions data_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
/// Metadata.
pub metadata: UnlimitedMetadata,
}
Expand Down Expand Up @@ -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<Instruction>,
account_id: <Account as Identifiable>::Id,
proposed_ttl_ms: u64,
nonce: u32,
) -> Transaction {
Transaction::with_metadata(
instructions,
account_id,
proposed_ttl_ms,
UnlimitedMetadata::new(),
Some(nonce),
)
}

Expand All @@ -2125,6 +2144,7 @@ pub mod transaction {
account_id: <Account as Identifiable>::Id,
proposed_ttl_ms: u64,
metadata: UnlimitedMetadata,
nonce: Option<u32>,
) -> Transaction {
#[allow(clippy::cast_possible_truncation, clippy::expect_used)]
Transaction {
Expand All @@ -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(),
Expand Down

0 comments on commit e91874e

Please sign in to comment.