Skip to content

Commit

Permalink
refactor: move TransactionBuilder out of http feature
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic committed Oct 24, 2024
1 parent 486615e commit d29826d
Showing 1 changed file with 137 additions and 151 deletions.
288 changes: 137 additions & 151 deletions crates/iroha_data_model/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use core::{
};

use derive_more::{DebugCustom, Display};
#[cfg(feature = "http")]
pub use http::*;
use iroha_crypto::{Signature, SignatureOf};
use iroha_data_model_derive::model;
use iroha_macro::FromVariant;
Expand Down Expand Up @@ -178,6 +176,15 @@ mod model {
// NOTE: Using `Box` reduces memory use by 10%
pub error: Option<Box<error::TransactionRejectionReason>>,
}

/// Structure that represents the initial state of a transaction before the transaction receives any signatures.
#[derive(Debug, Clone)]
#[repr(transparent)]
#[must_use]
pub struct TransactionBuilder {
/// [`Transaction`] payload.
pub(super) payload: TransactionPayload,
}
}

impl<A: Instruction> FromIterator<A> for Executable {
Expand Down Expand Up @@ -322,6 +329,132 @@ impl TransactionSignature {
}
}

impl TransactionBuilder {
#[cfg(feature = "std")]
fn new_with_time(chain: ChainId, authority: AccountId, creation_time_ms: u64) -> Self {
Self {
payload: TransactionPayload {
chain,
authority,
creation_time_ms,
nonce: None,
time_to_live_ms: None,
instructions: Vec::<InstructionBox>::new().into(),
metadata: Metadata::default(),
},
}
}

/// Construct [`Self`], using the time from [`TimeSource`]
// we don't want to expose this to non-tests
#[inline]
#[cfg(all(feature = "std", feature = "transparent_api"))]
pub fn new_with_time_source(
chain_id: ChainId,
authority: AccountId,
time_source: &iroha_primitives::time::TimeSource,
) -> Self {
let creation_time_ms = time_source
.get_unix_time()
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");

Self::new_with_time(chain_id, authority, creation_time_ms)
}

/// Construct [`Self`].
#[inline]
#[cfg(feature = "std")]
pub fn new(chain_id: ChainId, authority: AccountId) -> Self {
use std::time::SystemTime;

// can't delegate to `new_with_time_source`, because it's gated behind "transparent_api"
let creation_time_ms = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");
Self::new_with_time(chain_id, authority, creation_time_ms)
}
}

impl TransactionBuilder {
/// Set instructions for this transaction
pub fn with_instructions<T: Instruction>(
mut self,
instructions: impl IntoIterator<Item = T>,
) -> Self {
self.payload.instructions = instructions
.into_iter()
.map(Into::into)
.collect::<Vec<InstructionBox>>()
.into();
self
}

/// Add wasm to this transaction
pub fn with_wasm(mut self, wasm: WasmSmartContract) -> Self {
self.payload.instructions = wasm.into();
self
}

/// Set executable for this transaction
pub fn with_executable(mut self, executable: Executable) -> Self {
self.payload.instructions = executable;
self
}

/// Adds metadata to the `Transaction`
pub fn with_metadata(mut self, metadata: Metadata) -> Self {
self.payload.metadata = metadata;
self
}

/// Set nonce for [`Transaction`]
pub fn set_nonce(&mut self, nonce: NonZeroU32) -> &mut Self {
self.payload.nonce = Some(nonce);
self
}

/// Set time-to-live for [`Transaction`]
pub fn set_ttl(&mut self, time_to_live: Duration) -> &mut Self {
let ttl: u64 = time_to_live
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");

self.payload.time_to_live_ms = if ttl == 0 {
// TODO: This is not correct, 0 is not the same as None
None
} else {
Some(NonZeroU64::new(ttl).expect("Can't be 0"))
};

self
}

/// Set creation time of transaction
pub fn set_creation_time(&mut self, value: Duration) -> &mut Self {
self.payload.creation_time_ms = u64::try_from(value.as_millis())
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");
self
}

/// Sign transaction with provided key pair.
#[must_use]
pub fn sign(self, private_key: &iroha_crypto::PrivateKey) -> SignedTransaction {
let signature = TransactionSignature(SignatureOf::new(private_key, &self.payload));

SignedTransactionV1 {
signature,
payload: self.payload,
}
.into()
}
}

mod candidate {
use parity_scale_codec::Input;

Expand Down Expand Up @@ -598,158 +731,11 @@ pub mod error {
}
}

#[cfg(feature = "http")]
mod http {
pub use self::model::*;
use super::*;

#[model]
mod model {
use super::*;

/// Structure that represents the initial state of a transaction before the transaction receives any signatures.
#[derive(Debug, Clone)]
#[repr(transparent)]
#[must_use]
pub struct TransactionBuilder {
/// [`Transaction`] payload.
pub(super) payload: TransactionPayload,
}
}

impl TransactionBuilder {
#[cfg(feature = "std")]
fn new_with_time(chain: ChainId, authority: AccountId, creation_time_ms: u64) -> Self {
Self {
payload: TransactionPayload {
chain,
authority,
creation_time_ms,
nonce: None,
time_to_live_ms: None,
instructions: Vec::<InstructionBox>::new().into(),
metadata: Metadata::default(),
},
}
}

/// Construct [`Self`], using the time from [`TimeSource`]
// we don't want to expose this to non-tests
#[inline]
#[cfg(all(feature = "std", feature = "transparent_api"))]
pub fn new_with_time_source(
chain_id: ChainId,
authority: AccountId,
time_source: &iroha_primitives::time::TimeSource,
) -> Self {
let creation_time_ms = time_source
.get_unix_time()
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");

Self::new_with_time(chain_id, authority, creation_time_ms)
}

/// Construct [`Self`].
#[inline]
#[cfg(feature = "std")]
pub fn new(chain_id: ChainId, authority: AccountId) -> Self {
use std::time::SystemTime;

// can't delegate to `new_with_time_source`, because it's gated behind "transparent_api"
let creation_time_ms = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");
Self::new_with_time(chain_id, authority, creation_time_ms)
}
}

impl TransactionBuilder {
/// Set instructions for this transaction
pub fn with_instructions<T: Instruction>(
mut self,
instructions: impl IntoIterator<Item = T>,
) -> Self {
self.payload.instructions = instructions
.into_iter()
.map(Into::into)
.collect::<Vec<InstructionBox>>()
.into();
self
}

/// Add wasm to this transaction
pub fn with_wasm(mut self, wasm: WasmSmartContract) -> Self {
self.payload.instructions = wasm.into();
self
}

/// Set executable for this transaction
pub fn with_executable(mut self, executable: Executable) -> Self {
self.payload.instructions = executable;
self
}

/// Adds metadata to the `Transaction`
pub fn with_metadata(mut self, metadata: Metadata) -> Self {
self.payload.metadata = metadata;
self
}

/// Set nonce for [`Transaction`]
pub fn set_nonce(&mut self, nonce: NonZeroU32) -> &mut Self {
self.payload.nonce = Some(nonce);
self
}

/// Set time-to-live for [`Transaction`]
pub fn set_ttl(&mut self, time_to_live: Duration) -> &mut Self {
let ttl: u64 = time_to_live
.as_millis()
.try_into()
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");

self.payload.time_to_live_ms = if ttl == 0 {
// TODO: This is not correct, 0 is not the same as None
None
} else {
Some(NonZeroU64::new(ttl).expect("Can't be 0"))
};

self
}

/// Set creation time of transaction
pub fn set_creation_time(&mut self, value: Duration) -> &mut Self {
self.payload.creation_time_ms = u64::try_from(value.as_millis())
.expect("INTERNAL BUG: Unix timestamp exceedes u64::MAX");
self
}

/// Sign transaction with provided key pair.
#[must_use]
pub fn sign(self, private_key: &iroha_crypto::PrivateKey) -> SignedTransaction {
let signature = TransactionSignature(SignatureOf::new(private_key, &self.payload));

SignedTransactionV1 {
signature,
payload: self.payload,
}
.into()
}
}
}

/// The prelude re-exports most commonly used traits, structs and macros from this module.
pub mod prelude {
#[cfg(feature = "http")]
pub use super::http::TransactionBuilder;
pub use super::{
error::prelude::*, CommittedTransaction, Executable, SignedTransaction, WasmSmartContract,
error::prelude::*, CommittedTransaction, Executable, SignedTransaction, TransactionBuilder,
WasmSmartContract,
};
}

Expand Down

0 comments on commit d29826d

Please sign in to comment.