From e2ee5310b5d4ae089d67ca105176bf660ae0326c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 28 Nov 2024 15:49:16 +0100 Subject: [PATCH 1/3] add internal transaction builder --- lib/cli.rs | 4 + lib/cli/fields_container.rs | 75 +++ lib/cli/tests.rs | 5 +- lib/cli/transaction.rs | 4 +- lib/cli/transaction_v1_builder.rs | 605 +++++++++++++++++++++ lib/cli/transaction_v1_builder/error.rs | 51 ++ lib/error.rs | 4 +- lib/lib.rs | 2 + lib/types.rs | 2 + lib/types/initiator_addr_and_secret_key.rs | 44 ++ 10 files changed, 789 insertions(+), 7 deletions(-) create mode 100644 lib/cli/fields_container.rs create mode 100644 lib/cli/transaction_v1_builder.rs create mode 100644 lib/cli/transaction_v1_builder/error.rs create mode 100644 lib/types/initiator_addr_and_secret_key.rs diff --git a/lib/cli.rs b/lib/cli.rs index 8a516a00..98a86315 100644 --- a/lib/cli.rs +++ b/lib/cli.rs @@ -25,6 +25,7 @@ pub mod deploy; mod deploy_str_params; mod dictionary_item_str_params; mod error; +mod fields_container; mod json_args; pub mod parse; mod payment_str_params; @@ -35,6 +36,7 @@ mod tests; mod transaction; mod transaction_builder_params; mod transaction_str_params; +mod transaction_v1_builder; #[cfg(feature = "std-fs-io")] use serde::Serialize; @@ -73,6 +75,7 @@ pub use deploy::{ pub use deploy_str_params::DeployStrParams; pub use dictionary_item_str_params::DictionaryItemStrParams; pub use error::{CliError, FromDecStrErr}; +pub(crate) use fields_container::{FieldsContainer, FieldsContainerError}; pub use json_args::{ help as json_args_help, Error as JsonArgsError, ErrorDetails as JsonArgsErrorDetails, JsonArg, }; @@ -86,6 +89,7 @@ pub use transaction::{ }; pub use transaction_builder_params::TransactionBuilderParams; pub use transaction_str_params::TransactionStrParams; +pub(crate) use transaction_v1_builder::{TransactionV1Builder, TransactionV1BuilderError}; /// Retrieves a [`casper_types::Deploy`] from the network. /// diff --git a/lib/cli/fields_container.rs b/lib/cli/fields_container.rs new file mode 100644 index 00000000..c301bff3 --- /dev/null +++ b/lib/cli/fields_container.rs @@ -0,0 +1,75 @@ +use alloc::collections::BTreeMap; +use casper_types::{ + bytesrepr::{Bytes, ToBytes}, + TransactionArgs, TransactionEntryPoint, TransactionScheduling, TransactionTarget, +}; + +pub(crate) const ARGS_MAP_KEY: u16 = 0; +pub(crate) const TARGET_MAP_KEY: u16 = 1; +pub(crate) const ENTRY_POINT_MAP_KEY: u16 = 2; +pub(crate) const SCHEDULING_MAP_KEY: u16 = 3; + +#[derive(Clone, Eq, PartialEq, Debug)] +pub(crate) enum FieldsContainerError { + CouldNotSerializeField { field_index: u16 }, +} + +pub(crate) struct FieldsContainer { + pub(super) args: TransactionArgs, + pub(super) target: TransactionTarget, + pub(super) entry_point: TransactionEntryPoint, + pub(super) scheduling: TransactionScheduling, +} + +impl FieldsContainer { + pub(crate) fn new( + args: TransactionArgs, + target: TransactionTarget, + entry_point: TransactionEntryPoint, + scheduling: TransactionScheduling, + ) -> Self { + FieldsContainer { + args, + target, + entry_point, + scheduling, + } + } + + pub(crate) fn to_map(&self) -> Result, FieldsContainerError> { + let mut map: BTreeMap = BTreeMap::new(); + map.insert( + ARGS_MAP_KEY, + self.args.to_bytes().map(Into::into).map_err(|_| { + FieldsContainerError::CouldNotSerializeField { + field_index: ARGS_MAP_KEY, + } + })?, + ); + map.insert( + TARGET_MAP_KEY, + self.target.to_bytes().map(Into::into).map_err(|_| { + FieldsContainerError::CouldNotSerializeField { + field_index: TARGET_MAP_KEY, + } + })?, + ); + map.insert( + ENTRY_POINT_MAP_KEY, + self.entry_point.to_bytes().map(Into::into).map_err(|_| { + FieldsContainerError::CouldNotSerializeField { + field_index: ENTRY_POINT_MAP_KEY, + } + })?, + ); + map.insert( + SCHEDULING_MAP_KEY, + self.scheduling.to_bytes().map(Into::into).map_err(|_| { + FieldsContainerError::CouldNotSerializeField { + field_index: SCHEDULING_MAP_KEY, + } + })?, + ); + Ok(map) + } +} diff --git a/lib/cli/tests.rs b/lib/cli/tests.rs index 570d71e1..74ba7033 100644 --- a/lib/cli/tests.rs +++ b/lib/cli/tests.rs @@ -445,11 +445,10 @@ fn should_fail_to_create_deploy_with_payment_and_session_with_no_secret_key_whil mod transaction { use super::*; - use crate::Error::TransactionBuild; + use crate::{cli::TransactionV1BuilderError, Error::TransactionBuild}; use casper_types::{ bytesrepr::Bytes, PackageAddr, TransactionArgs, TransactionEntryPoint, - TransactionInvocationTarget, TransactionRuntime, TransactionTarget, - TransactionV1BuilderError, TransferTarget, + TransactionInvocationTarget, TransactionRuntime, TransactionTarget, TransferTarget, }; use once_cell::sync::Lazy; use serde_json::json; diff --git a/lib/cli/transaction.rs b/lib/cli/transaction.rs index b8953e99..d5c6544f 100644 --- a/lib/cli/transaction.rs +++ b/lib/cli/transaction.rs @@ -5,14 +5,14 @@ use crate::rpcs::v2_0_0::speculative_exec_transaction::SpeculativeExecTxnResult; #[cfg(feature = "std-fs-io")] use crate::speculative_exec_txn; use crate::{ - cli::{parse, CliError, TransactionBuilderParams, TransactionStrParams}, + cli::{parse, CliError, TransactionBuilderParams, TransactionStrParams, TransactionV1Builder}, put_transaction as put_transaction_rpc_handler, rpcs::results::PutTransactionResult, SuccessResponse, }; use casper_types::{ Digest, InitiatorAddr, SecretKey, Transaction, TransactionArgs, TransactionEntryPoint, - TransactionRuntime, TransactionV1Builder, + TransactionRuntime, }; pub fn create_transaction( diff --git a/lib/cli/transaction_v1_builder.rs b/lib/cli/transaction_v1_builder.rs new file mode 100644 index 00000000..35754060 --- /dev/null +++ b/lib/cli/transaction_v1_builder.rs @@ -0,0 +1,605 @@ +pub mod error; +use crate::{ + cli::{FieldsContainer, FieldsContainerError}, + types::InitiatorAddrAndSecretKey, +}; +use alloc::collections::BTreeSet; +use alloc::vec::Vec; +use casper_types::{ + arg_handling, + bytesrepr::{Bytes, ToBytes}, + system::auction::Reservation, + AddressableEntityHash, CLValueError, Digest, EntityVersion, InitiatorAddr, PackageHash, + PricingMode, PublicKey, RuntimeArgs, SecretKey, TimeDiff, Timestamp, TransactionArgs, + TransactionEntryPoint, TransactionInvocationTarget, TransactionRuntime, TransactionScheduling, + TransactionTarget, TransactionV1, TransactionV1Payload, TransferTarget, URef, U512, +}; +use core::marker::PhantomData; + +use alloc::collections::BTreeMap; +pub use error::TransactionV1BuilderError; + +/// A builder for constructing `TransactionV1` instances with various configuration options. +/// +/// The `TransactionV1Builder` provides a flexible API for specifying different transaction +/// parameters like the target, scheduling, entry point, and signing options. Once all the required +/// fields are set, the transaction can be built by calling [`build`](Self::build). +/// +/// # Fields +/// +/// - `args`: Arguments passed to the transaction's runtime, initialized to +/// [`RuntimeArgs::new`](RuntimeArgs::new). +/// - `target`: Specifies the target of the transaction, which can be native or other custom +/// targets. Defaults to [`TransactionTarget::Native`](TransactionTarget::Native). +/// - `scheduling`: Determines the scheduling mechanism of the transaction, e.g., standard or +/// immediate, and is initialized to +/// [`TransactionScheduling::Standard`](TransactionScheduling::Standard). +/// - `entry_point`: Defines the transaction's entry point, such as transfer or another defined +/// action. Defaults to [`TransactionEntryPoint::Transfer`](TransactionEntryPoint::Transfer). +/// - `chain_name`: The name of the blockchain where the transaction will be executed. Initially set +/// to `None` and must be provided before building the transaction. +/// +/// ## Time-Related Fields +/// - `timestamp`: The timestamp at which the transaction is created. It is either set to the +/// current time using [`Timestamp::now`](Timestamp::now) or [`Timestamp::zero`](Timestamp::zero) +/// without the `std-fs-io` feature. +/// - `ttl`: Time-to-live for the transaction, specified as a [`TimeDiff`], representing how long +/// the transaction is valid for execution. Defaults to [`Self::DEFAULT_TTL`]. +/// +/// ## Pricing and Initiator Fields +/// - `pricing_mode`: Specifies the pricing mode to use for transaction execution (e.g., fixed or +/// dynamic). Defaults to [`Self::DEFAULT_PRICING_MODE`]. +/// - `initiator_addr`: The address of the initiator who creates and signs the transaction. +/// Initially set to `None` and must be set before building. +/// +/// ## Signing Fields +/// - `secret_key`: The secret key used to sign the transaction. This field is conditional based on +/// the compilation environment: +/// - In normal mode, it holds a reference to the secret key (`Option<&'a SecretKey>`). +/// - In testing mode or with the `std` feature enabled, it holds an owned secret key +/// (`Option`). +/// +/// ## Phantom Data +/// - `_phantom_data`: Ensures the correct lifetime `'a` is respected for the builder, helping with +/// proper borrowing and memory safety. +#[derive(Debug)] +pub struct TransactionV1Builder<'a> { + /// Arguments passed to the transaction's runtime. + args: TransactionArgs, + /// The target of the transaction (e.g., native). + target: TransactionTarget, + /// Defines how the transaction is scheduled (e.g., standard, immediate). + scheduling: TransactionScheduling, + /// Specifies the entry point of the transaction (e.g., transfer). + entry_point: TransactionEntryPoint, + /// The name of the blockchain where the transaction will be executed. + chain_name: Option, + /// The timestamp of the transaction. + timestamp: Timestamp, + /// The time-to-live for the transaction, representing how long it's valid for execution. + ttl: TimeDiff, + /// The pricing mode used for the transaction's execution cost. + pricing_mode: PricingMode, + /// The address of the transaction initiator. + initiator_addr: Option, + /// The secret key used for signing the transaction (in normal mode). + #[cfg(not(test))] + secret_key: Option<&'a SecretKey>, + /// The secret key used for signing the transaction (in testing). + #[cfg(test)] + secret_key: Option, + /// Phantom data to ensure the correct lifetime for references. + _phantom_data: PhantomData<&'a ()>, +} + +impl<'a> TransactionV1Builder<'a> { + /// The default time-to-live for transactions, i.e. 30 minutes. + pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000); + /// The default pricing mode for v1 transactions, ie FIXED cost. + pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::Fixed { + gas_price_tolerance: 5, + additional_computation_factor: 0, + }; + /// The default scheduling for transactions, i.e. `Standard`. + pub const DEFAULT_SCHEDULING: TransactionScheduling = TransactionScheduling::Standard; + + /// Creates a new `TransactionV1Builder` instance with default settings. + /// + /// # Important + /// + /// Before calling [`build`](Self::build), you must ensure that either: + /// - A chain name is provided by calling [`with_chain_name`](Self::with_chain_name), + /// - An initiator address is set by calling [`with_initiator_addr`](Self::with_initiator_addr), + /// - or a secret key is set by calling [`with_secret_key`](Self::with_secret_key). + /// + /// # Default Values + /// This function sets the following default values upon creation: + /// + /// - `chain_name`: Initialized to `None`. + /// - `timestamp`: Set to the current time using [`Timestamp::now`](Timestamp::now), or + /// [`Timestamp::zero`](Timestamp::zero) if the `std-fs-io` feature is disabled. + /// - `ttl`: Defaults to [`Self::DEFAULT_TTL`]. + /// - `pricing_mode`: Defaults to [`Self::DEFAULT_PRICING_MODE`]. + /// - `initiator_addr`: Initialized to `None`. + /// - `secret_key`: Initialized to `None`. + /// + /// Additionally, the following internal fields are configured: + /// + /// - `args`: Initialized to an empty [`RuntimeArgs::new`](RuntimeArgs::new). + /// - `entry_point`: Set to + /// [`TransactionEntryPoint::Transfer`](TransactionEntryPoint::Transfer). + /// - `target`: Defaults to [`TransactionTarget::Native`](TransactionTarget::Native). + /// - `scheduling`: Defaults to + /// [`TransactionScheduling::Standard`](TransactionScheduling::Standard). + /// + /// # Testing and Additional Configuration + /// + /// # Returns + /// + /// A new `TransactionV1Builder` instance. + pub(crate) fn new() -> Self { + #[cfg(any(feature = "std-fs-io", test))] + let timestamp = Timestamp::now(); + #[cfg(not(any(feature = "std-fs-io", test)))] + let timestamp = Timestamp::zero(); + + TransactionV1Builder { + args: TransactionArgs::Named(RuntimeArgs::new()), + entry_point: TransactionEntryPoint::Transfer, + target: TransactionTarget::Native, + scheduling: TransactionScheduling::Standard, + chain_name: None, + timestamp, + ttl: Self::DEFAULT_TTL, + pricing_mode: Self::DEFAULT_PRICING_MODE, + initiator_addr: None, + secret_key: None, + _phantom_data: PhantomData, + } + } + + /// Returns a new `TransactionV1Builder` suitable for building a native transfer transaction. + pub fn new_transfer, T: Into>( + amount: A, + maybe_source: Option, + target: T, + maybe_id: Option, + ) -> Result { + let args = arg_handling::new_transfer_args(amount, maybe_source, target, maybe_id)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::Transfer; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native add_bid transaction. + pub fn new_add_bid>( + public_key: PublicKey, + delegation_rate: u8, + amount: A, + minimum_delegation_amount: Option, + maximum_delegation_amount: Option, + reserved_slots: Option, + ) -> Result { + let args = arg_handling::new_add_bid_args( + public_key, + delegation_rate, + amount, + minimum_delegation_amount, + maximum_delegation_amount, + reserved_slots, + )?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::AddBid; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native withdraw_bid + /// transaction. + pub fn new_withdraw_bid>( + public_key: PublicKey, + amount: A, + ) -> Result { + let args = arg_handling::new_withdraw_bid_args(public_key, amount)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::WithdrawBid; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native delegate transaction. + pub fn new_delegate>( + delegator: PublicKey, + validator: PublicKey, + amount: A, + ) -> Result { + let args = arg_handling::new_delegate_args(delegator, validator, amount)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::Delegate; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native undelegate transaction. + pub fn new_undelegate>( + delegator: PublicKey, + validator: PublicKey, + amount: A, + ) -> Result { + let args = arg_handling::new_undelegate_args(delegator, validator, amount)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::Undelegate; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native redelegate transaction. + pub fn new_redelegate>( + delegator: PublicKey, + validator: PublicKey, + amount: A, + new_validator: PublicKey, + ) -> Result { + let args = arg_handling::new_redelegate_args(delegator, validator, amount, new_validator)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::Redelegate; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native activate_bid + /// transaction. + pub fn new_activate_bid(validator: PublicKey) -> Result { + let args = arg_handling::new_activate_bid_args(validator)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::ActivateBid; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native change_bid_public_key + /// transaction. + pub fn new_change_bid_public_key( + public_key: PublicKey, + new_public_key: PublicKey, + ) -> Result { + let args = arg_handling::new_change_bid_public_key_args(public_key, new_public_key)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::ChangeBidPublicKey; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native add_reservations + /// transaction. + pub fn new_add_reservations(reservations: Vec) -> Result { + let args = arg_handling::new_add_reservations_args(reservations)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::AddReservations; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + /// Returns a new `TransactionV1Builder` suitable for building a native cancel_reservations + /// transaction. + pub fn new_cancel_reservations( + validator: PublicKey, + delegators: Vec, + ) -> Result { + let args = arg_handling::new_cancel_reservations_args(validator, delegators)?; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(args); + builder.target = TransactionTarget::Native; + builder.entry_point = TransactionEntryPoint::CancelReservations; + builder.scheduling = Self::DEFAULT_SCHEDULING; + Ok(builder) + } + + fn new_targeting_stored>( + id: TransactionInvocationTarget, + entry_point: E, + runtime: TransactionRuntime, + transferred_value: u64, + ) -> Self { + let target = TransactionTarget::Stored { + id, + runtime, + transferred_value, + }; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(RuntimeArgs::new()); + builder.target = target; + builder.entry_point = TransactionEntryPoint::Custom(entry_point.into()); + builder.scheduling = Self::DEFAULT_SCHEDULING; + builder + } + + /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a stored + /// entity. + pub fn new_targeting_invocable_entity>( + hash: AddressableEntityHash, + entry_point: E, + runtime: TransactionRuntime, + transferred_value: u64, + ) -> Self { + let id = TransactionInvocationTarget::new_invocable_entity(hash); + Self::new_targeting_stored(id, entry_point, runtime, transferred_value) + } + + /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a stored + /// entity via its alias. + pub fn new_targeting_invocable_entity_via_alias, E: Into>( + alias: A, + entry_point: E, + runtime: TransactionRuntime, + transferred_value: u64, + ) -> Self { + let id = TransactionInvocationTarget::new_invocable_entity_alias(alias.into()); + Self::new_targeting_stored(id, entry_point, runtime, transferred_value) + } + + /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a + /// package. + pub fn new_targeting_package>( + hash: PackageHash, + version: Option, + entry_point: E, + runtime: TransactionRuntime, + transferred_value: u64, + ) -> Self { + let id = TransactionInvocationTarget::new_package(hash, version); + Self::new_targeting_stored(id, entry_point, runtime, transferred_value) + } + + /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a + /// package via its alias. + pub fn new_targeting_package_via_alias, E: Into>( + alias: A, + version: Option, + entry_point: E, + runtime: TransactionRuntime, + transferred_value: u64, + ) -> Self { + let id = TransactionInvocationTarget::new_package_alias(alias.into(), version); + Self::new_targeting_stored(id, entry_point, runtime, transferred_value) + } + + /// Returns a new `TransactionV1Builder` suitable for building a transaction for running session + /// logic, i.e. compiled Wasm. + pub fn new_session( + is_install_upgrade: bool, + module_bytes: Bytes, + runtime: TransactionRuntime, + transferred_value: u64, + seed: Option<[u8; 32]>, + ) -> Self { + let target = TransactionTarget::Session { + is_install_upgrade, + module_bytes, + runtime, + transferred_value, + seed, + }; + let mut builder = TransactionV1Builder::new(); + builder.args = TransactionArgs::Named(RuntimeArgs::new()); + builder.target = target; + builder.entry_point = TransactionEntryPoint::Call; + builder.scheduling = Self::DEFAULT_SCHEDULING; + builder + } + + /// Sets the `chain_name` in the transaction. + /// + /// Must be provided or building will fail. + pub fn with_chain_name>(mut self, chain_name: C) -> Self { + self.chain_name = Some(chain_name.into()); + self + } + + /// Sets the `timestamp` in the transaction. + /// + /// If not provided, the timestamp will be set to the time when the builder was constructed. + pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self { + self.timestamp = timestamp; + self + } + + /// Sets the `ttl` (time-to-live) in the transaction. + /// + /// If not provided, the ttl will be set to [`Self::DEFAULT_TTL`]. + pub fn with_ttl(mut self, ttl: TimeDiff) -> Self { + self.ttl = ttl; + self + } + + /// Sets the `pricing_mode` in the transaction. + /// + /// If not provided, the pricing mode will be set to [`Self::DEFAULT_PRICING_MODE`]. + pub fn with_pricing_mode(mut self, pricing_mode: PricingMode) -> Self { + self.pricing_mode = pricing_mode; + self + } + + /// Sets the `initiator_addr` in the transaction. + /// + /// If not provided, the public key derived from the secret key used in the builder will be + /// used as the `InitiatorAddr::PublicKey` in the transaction. + pub fn with_initiator_addr>(mut self, initiator_addr: I) -> Self { + self.initiator_addr = Some(initiator_addr.into()); + self + } + + /// Sets the secret key used to sign the transaction on calling [`build`](Self::build). + /// + /// If not provided, the transaction can still be built, but will be unsigned and will be + /// invalid until subsequently signed. + pub fn with_secret_key(mut self, secret_key: &'a SecretKey) -> Self { + #[cfg(not(test))] + { + self.secret_key = Some(secret_key); + } + #[cfg(test)] + { + self.secret_key = Some( + SecretKey::from_der(secret_key.to_der().expect("should der-encode")) + .expect("should der-decode"), + ); + } + self + } + + /// Sets the runtime args in the transaction. + /// + /// NOTE: this overwrites any existing runtime args. To append to existing args, use + /// [`TransactionV1Builder::with_runtime_arg`]. + pub fn with_runtime_args(mut self, args: RuntimeArgs) -> Self { + self.args = TransactionArgs::Named(args); + self + } + + /// Sets the runtime args in the transaction. + pub fn with_chunked_args(mut self, args: Bytes) -> Self { + self.args = TransactionArgs::Bytesrepr(args); + self + } + + /// Sets the entry point for the transaction. + pub fn with_entry_point(mut self, entry_point: TransactionEntryPoint) -> Self { + self.entry_point = entry_point; + self + } + + /// Returns the new transaction, or an error if non-defaulted fields were not set. + /// + /// For more info, see [the `TransactionBuilder` documentation](TransactionV1Builder). + pub fn build(self) -> Result { + self.do_build() + } + + fn build_transaction( + chain_name: String, + timestamp: Timestamp, + ttl: TimeDiff, + pricing_mode: PricingMode, + fields: BTreeMap, + initiator_addr_and_secret_key: InitiatorAddrAndSecretKey, + ) -> TransactionV1 { + let initiator_addr = initiator_addr_and_secret_key.initiator_addr(); + let transaction_v1_payload = TransactionV1Payload::new( + chain_name, + timestamp, + ttl, + pricing_mode, + initiator_addr, + fields, + ); + let hash = Digest::hash( + transaction_v1_payload + .to_bytes() + .unwrap_or_else(|error| panic!("should serialize body: {}", error)), + ); + let mut transaction = + TransactionV1::new(hash.into(), transaction_v1_payload, BTreeSet::new()); + + if let Some(secret_key) = initiator_addr_and_secret_key.secret_key() { + transaction.sign(secret_key); + } + transaction + } + + #[cfg(not(test))] + fn do_build(self) -> Result { + let initiator_addr_and_secret_key = match (self.initiator_addr, self.secret_key) { + (Some(initiator_addr), Some(secret_key)) => InitiatorAddrAndSecretKey::Both { + initiator_addr, + secret_key, + }, + (Some(initiator_addr), None) => { + InitiatorAddrAndSecretKey::InitiatorAddr(initiator_addr) + } + (None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key), + (None, None) => return Err(TransactionV1BuilderError::MissingInitiatorAddr), + }; + + let chain_name = self + .chain_name + .ok_or(TransactionV1BuilderError::MissingChainName)?; + + let container = + FieldsContainer::new(self.args, self.target, self.entry_point, self.scheduling) + .to_map() + .map_err(|err| match err { + FieldsContainerError::CouldNotSerializeField { field_index } => { + TransactionV1BuilderError::CouldNotSerializeField { field_index } + } + })?; + + let transaction = Self::build_transaction( + chain_name, + self.timestamp, + self.ttl, + self.pricing_mode, + container, + initiator_addr_and_secret_key, + ); + + Ok(transaction) + } + + #[cfg(test)] + fn do_build(self) -> Result { + let initiator_addr_and_secret_key = match (self.initiator_addr, &self.secret_key) { + (Some(initiator_addr), Some(secret_key)) => InitiatorAddrAndSecretKey::Both { + initiator_addr, + secret_key, + }, + (Some(initiator_addr), None) => { + InitiatorAddrAndSecretKey::InitiatorAddr(initiator_addr) + } + (None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key), + (None, None) => return Err(TransactionV1BuilderError::MissingInitiatorAddr), + }; + + let chain_name = self + .chain_name + .ok_or(TransactionV1BuilderError::MissingChainName)?; + let container = + FieldsContainer::new(self.args, self.target, self.entry_point, self.scheduling) + .to_map() + .map_err(|err| match err { + FieldsContainerError::CouldNotSerializeField { field_index } => { + TransactionV1BuilderError::CouldNotSerializeField { field_index } + } + })?; + + let transaction = Self::build_transaction( + chain_name, + self.timestamp, + self.ttl, + self.pricing_mode, + container, + initiator_addr_and_secret_key, + ); + + Ok(transaction) + } +} diff --git a/lib/cli/transaction_v1_builder/error.rs b/lib/cli/transaction_v1_builder/error.rs new file mode 100644 index 00000000..dc02e83e --- /dev/null +++ b/lib/cli/transaction_v1_builder/error.rs @@ -0,0 +1,51 @@ +use core::fmt::{self, Display, Formatter}; +use std::error::Error as StdError; + +#[cfg(doc)] +use super::{TransactionV1, TransactionV1Builder}; + +/// Errors returned while building a [`TransactionV1`] using a [`TransactionV1Builder`]. +#[derive(Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum TransactionV1BuilderError { + /// Failed to build transaction due to missing initiator_addr. + /// + /// Call [`TransactionV1Builder::with_initiator_addr`] or + /// [`TransactionV1Builder::with_secret_key`] before calling [`TransactionV1Builder::build`]. + MissingInitiatorAddr, + /// Failed to build transaction due to missing chain name. + /// + /// Call [`TransactionV1Builder::with_chain_name`] before calling + /// [`TransactionV1Builder::build`]. + MissingChainName, + /// Failed to build transaction due to an error when calling `to_bytes` on one of the payload + /// `field`. + CouldNotSerializeField { + /// The field index that failed to serialize. + field_index: u16, + }, +} + +impl Display for TransactionV1BuilderError { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + match self { + TransactionV1BuilderError::MissingInitiatorAddr => { + write!( + formatter, + "transaction requires account - use `with_account` or `with_secret_key`" + ) + } + TransactionV1BuilderError::MissingChainName => { + write!( + formatter, + "transaction requires chain name - use `with_chain_name`" + ) + } + TransactionV1BuilderError::CouldNotSerializeField { field_index } => { + write!(formatter, "Cannot serialize field at index {}", field_index) + } + } + } +} + +impl StdError for TransactionV1BuilderError {} diff --git a/lib/error.rs b/lib/error.rs index 7faf9ea2..9de6b550 100644 --- a/lib/error.rs +++ b/lib/error.rs @@ -6,7 +6,7 @@ use casper_types::{bytesrepr::Error as ToBytesError, crypto, Key}; #[cfg(doc)] use casper_types::{CLValue, Deploy, DeployBuilder, TimeDiff, Timestamp, URef}; -use crate::{validation::ValidateResponseError, JsonRpcId}; +use crate::{cli::TransactionV1BuilderError, validation::ValidateResponseError, JsonRpcId}; /// Errors that may be returned by `casper_client` functions. #[derive(Error, Debug)] @@ -21,7 +21,7 @@ pub enum Error { /// Failed to build Transaction #[error(transparent)] - TransactionBuild(#[from] casper_types::TransactionV1BuilderError), + TransactionBuild(#[from] TransactionV1BuilderError), /// Invalid [`Key`] variant. #[error("expected {} but got {}", .expected_variant, .actual)] InvalidKeyVariant { diff --git a/lib/lib.rs b/lib/lib.rs index 28ca10b7..7ca7daf8 100644 --- a/lib/lib.rs +++ b/lib/lib.rs @@ -49,6 +49,8 @@ mod verbosity; mod verification; mod verification_types; +extern crate alloc; + #[cfg(any(feature = "std-fs-io", test))] use std::{ env::current_dir, diff --git a/lib/types.rs b/lib/types.rs index 27bb63ff..4dd1cfdb 100644 --- a/lib/types.rs +++ b/lib/types.rs @@ -2,10 +2,12 @@ mod auction_state; mod deploy_execution_info; +mod initiator_addr_and_secret_key; mod json_block_with_signatures; mod legacy_execution_result; pub use auction_state::AuctionState; pub use deploy_execution_info::DeployExecutionInfo; +pub(crate) use initiator_addr_and_secret_key::InitiatorAddrAndSecretKey; pub use json_block_with_signatures::JsonBlockWithSignatures; pub use legacy_execution_result::LegacyExecutionResult; diff --git a/lib/types/initiator_addr_and_secret_key.rs b/lib/types/initiator_addr_and_secret_key.rs new file mode 100644 index 00000000..73582ef9 --- /dev/null +++ b/lib/types/initiator_addr_and_secret_key.rs @@ -0,0 +1,44 @@ +use casper_types::{InitiatorAddr, PublicKey, SecretKey}; + +/// Used when constructing a deploy or transaction. +#[derive(Debug)] +pub(crate) enum InitiatorAddrAndSecretKey<'a> { + /// Provides both the initiator address and the secret key (not necessarily for the same + /// initiator address) used to sign the deploy or transaction. + Both { + /// The initiator address of the account. + initiator_addr: InitiatorAddr, + /// The secret key used to sign the deploy or transaction. + secret_key: &'a SecretKey, + }, + /// The initiator address only (no secret key). The deploy or transaction will be created + /// unsigned. + #[allow(unused)] + InitiatorAddr(InitiatorAddr), + /// The initiator address will be derived from the provided secret key, and the deploy or + /// transaction will be signed by the same secret key. + #[allow(unused)] + SecretKey(&'a SecretKey), +} + +impl<'a> InitiatorAddrAndSecretKey<'a> { + /// The address of the initiator of a `TransactionV1`. + pub fn initiator_addr(&self) -> InitiatorAddr { + match self { + InitiatorAddrAndSecretKey::Both { initiator_addr, .. } + | InitiatorAddrAndSecretKey::InitiatorAddr(initiator_addr) => initiator_addr.clone(), + InitiatorAddrAndSecretKey::SecretKey(secret_key) => { + InitiatorAddr::PublicKey(PublicKey::from(*secret_key)) + } + } + } + + /// The secret key of the initiator of a `TransactionV1`. + pub fn secret_key(&self) -> Option<&SecretKey> { + match self { + InitiatorAddrAndSecretKey::Both { secret_key, .. } + | InitiatorAddrAndSecretKey::SecretKey(secret_key) => Some(secret_key), + InitiatorAddrAndSecretKey::InitiatorAddr(_) => None, + } + } +} From 87c2dd477cc753a35bd6d3166bac832698701e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 28 Nov 2024 17:49:00 +0100 Subject: [PATCH 2/3] add internal deploy builder --- lib/cli.rs | 2 + lib/cli/deploy.rs | 17 ++- lib/cli/deploy_builder.rs | 219 ++++++++++++++++++++++++++++++ lib/cli/deploy_builder/error.rs | 42 ++++++ lib/cli/tests.rs | 4 +- lib/cli/transaction_v1_builder.rs | 45 +----- lib/error.rs | 10 +- 7 files changed, 286 insertions(+), 53 deletions(-) create mode 100644 lib/cli/deploy_builder.rs create mode 100644 lib/cli/deploy_builder/error.rs diff --git a/lib/cli.rs b/lib/cli.rs index 98a86315..c0b24b7f 100644 --- a/lib/cli.rs +++ b/lib/cli.rs @@ -22,6 +22,7 @@ /// Functions for creating Deploys. pub mod deploy; +mod deploy_builder; mod deploy_str_params; mod dictionary_item_str_params; mod error; @@ -72,6 +73,7 @@ pub use deploy::{ make_deploy, make_transfer, put_deploy, send_deploy_file, sign_deploy_file, speculative_put_deploy, speculative_send_deploy_file, speculative_transfer, transfer, }; +pub(crate) use deploy_builder::{DeployBuilder, DeployBuilderError}; pub use deploy_str_params::DeployStrParams; pub use dictionary_item_str_params::DictionaryItemStrParams; pub use error::{CliError, FromDecStrErr}; diff --git a/lib/cli/deploy.rs b/lib/cli/deploy.rs index a89dc9d7..2745c7b6 100644 --- a/lib/cli/deploy.rs +++ b/lib/cli/deploy.rs @@ -1,12 +1,17 @@ use casper_types::{ - account::AccountHash, AsymmetricType, Deploy, DeployBuilder, PublicKey, TransferTarget, - UIntParseError, URef, U512, + account::AccountHash, AsymmetricType, Deploy, PublicKey, TransferTarget, UIntParseError, URef, + U512, }; -use super::transaction::get_maybe_secret_key; -use super::{parse, CliError, DeployStrParams, PaymentStrParams, SessionStrParams}; -use crate::rpcs::results::{PutDeployResult, SpeculativeExecResult}; -use crate::{SuccessResponse, MAX_SERIALIZED_SIZE_OF_DEPLOY}; +use super::{ + parse, transaction::get_maybe_secret_key, CliError, DeployStrParams, PaymentStrParams, + SessionStrParams, +}; +use crate::{ + cli::DeployBuilder, + rpcs::results::{PutDeployResult, SpeculativeExecResult}, + SuccessResponse, MAX_SERIALIZED_SIZE_OF_DEPLOY, +}; const DEFAULT_GAS_PRICE: u64 = 1; diff --git a/lib/cli/deploy_builder.rs b/lib/cli/deploy_builder.rs new file mode 100644 index 00000000..1e6aedcb --- /dev/null +++ b/lib/cli/deploy_builder.rs @@ -0,0 +1,219 @@ +mod error; + +use casper_types::{ + bytesrepr::ToBytes, Deploy, DeployHash, DeployHeader, Digest, ExecutableDeployItem, + InitiatorAddr, PublicKey, SecretKey, TimeDiff, Timestamp, TransferTarget, URef, U512, +}; +pub use error::DeployBuilderError; +use itertools::Itertools; + +use crate::types::InitiatorAddrAndSecretKey; + +/// A builder for constructing a [`Deploy`]. +pub struct DeployBuilder<'a> { + account: Option, + secret_key: Option<&'a SecretKey>, + timestamp: Timestamp, + ttl: TimeDiff, + gas_price: u64, + dependencies: Vec, + chain_name: String, + payment: Option, + session: ExecutableDeployItem, +} + +impl<'a> DeployBuilder<'a> { + /// The default time-to-live for `Deploy`s, i.e. 30 minutes. + pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000); + /// The default gas price for `Deploy`s, i.e. `1`. + pub const DEFAULT_GAS_PRICE: u64 = 1; + + /// Returns a new `DeployBuilder`. + /// + /// # Note + /// + /// Before calling [`build`](Self::build), you must ensure + /// * that an account is provided by either calling [`with_account`](Self::with_account) or + /// [`with_secret_key`](Self::with_secret_key) + /// * that payment code is provided by calling + /// [`with_payment`](Self::with_payment) + pub fn new>(chain_name: C, session: ExecutableDeployItem) -> Self { + #[cfg(any(feature = "std-fs-io", test))] + let timestamp = Timestamp::now(); + #[cfg(not(any(feature = "std-fs-io", test)))] + let timestamp = Timestamp::zero(); + + DeployBuilder { + account: None, + secret_key: None, + timestamp, + ttl: Self::DEFAULT_TTL, + gas_price: Self::DEFAULT_GAS_PRICE, + dependencies: vec![], + chain_name: chain_name.into(), + payment: None, + session, + } + } + + /// Returns a new `DeployBuilder` with session code suitable for a transfer. + /// + /// If `maybe_source` is None, the account's main purse is used as the source of the transfer. + /// + /// # Note + /// + /// Before calling [`build`](Self::build), you must ensure + /// * that an account is provided by either calling [`with_account`](Self::with_account) or + /// [`with_secret_key`](Self::with_secret_key) + /// * that payment code is provided by calling + /// [`with_payment`](Self::with_payment) + pub fn new_transfer, A: Into, T: Into>( + chain_name: C, + amount: A, + maybe_source: Option, + target: T, + maybe_transfer_id: Option, + ) -> Self { + let session = + ExecutableDeployItem::new_transfer(amount, maybe_source, target, maybe_transfer_id); + DeployBuilder::new(chain_name, session) + } + + /// Sets the `account` in the `Deploy`. + /// + /// If not provided, the public key derived from the secret key used in the `DeployBuilder` will + /// be used as the `account` in the `Deploy`. + pub fn with_account(mut self, account: PublicKey) -> Self { + self.account = Some(account); + self + } + + /// Sets the gas price in the `Deploy` to the provided amount. + /// + /// If not provided, the `Deploy` will use `DEFAULT_GAS_PRICE` (1) as the gas price for the + /// `deploy` + pub fn with_gas_price(mut self, gas_price: u64) -> Self { + self.gas_price = gas_price; + self + } + + /// Sets the secret key used to sign the `Deploy` on calling [`build`](Self::build). + /// + /// If not provided, the `Deploy` can still be built, but will be unsigned and will be invalid + /// until subsequently signed. + pub fn with_secret_key(mut self, secret_key: &'a SecretKey) -> Self { + self.secret_key = Some(secret_key); + self + } + + /// Sets the `payment` in the `Deploy`. + pub fn with_payment(mut self, payment: ExecutableDeployItem) -> Self { + self.payment = Some(payment); + self + } + + /// Sets the `timestamp` in the `Deploy`. + /// + /// If not provided, the timestamp will be set to the time when the `DeployBuilder` was + /// constructed. + pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self { + self.timestamp = timestamp; + self + } + + /// Sets the `ttl` (time-to-live) in the `Deploy`. + /// + /// If not provided, the ttl will be set to [`Self::DEFAULT_TTL`]. + pub fn with_ttl(mut self, ttl: TimeDiff) -> Self { + self.ttl = ttl; + self + } + + #[allow(clippy::too_many_arguments)] + fn build_deploy_inner( + timestamp: Timestamp, + ttl: TimeDiff, + gas_price: u64, + dependencies: Vec, + chain_name: String, + payment: ExecutableDeployItem, + session: ExecutableDeployItem, + initiator_addr_and_secret_key: InitiatorAddrAndSecretKey, + ) -> Deploy { + let serialized_body = serialize_body(&payment, &session); + let body_hash = Digest::hash(serialized_body); + + let account = match initiator_addr_and_secret_key.initiator_addr() { + InitiatorAddr::PublicKey(public_key) => public_key, + InitiatorAddr::AccountHash(_) => unreachable!(), + }; + + let dependencies = dependencies.into_iter().unique().collect(); + let header = DeployHeader::new( + account, + timestamp, + ttl, + gas_price, + body_hash, + dependencies, + chain_name, + ); + let serialized_header = serialize_header(&header); + let hash = DeployHash::new(Digest::hash(serialized_header)); + + let mut deploy = Deploy::new(hash, header, payment, session); + + if let Some(secret_key) = initiator_addr_and_secret_key.secret_key() { + deploy.sign(secret_key); + } + deploy + } + + /// Returns the new `Deploy`, or an error if + /// [`with_payment`](Self::with_payment) wasn't previously called. + pub fn build(self) -> Result { + let initiator_addr_and_secret_key = match (self.account, self.secret_key) { + (Some(account), Some(secret_key)) => InitiatorAddrAndSecretKey::Both { + initiator_addr: InitiatorAddr::PublicKey(account), + secret_key, + }, + (Some(account), None) => { + InitiatorAddrAndSecretKey::InitiatorAddr(InitiatorAddr::PublicKey(account)) + } + (None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key), + (None, None) => return Err(DeployBuilderError::DeployMissingSessionAccount), + }; + + let payment = self + .payment + .ok_or(DeployBuilderError::DeployMissingPaymentCode)?; + let deploy = Self::build_deploy_inner( + self.timestamp, + self.ttl, + self.gas_price, + self.dependencies, + self.chain_name, + payment, + self.session, + initiator_addr_and_secret_key, + ); + Ok(deploy) + } +} + +fn serialize_header(header: &DeployHeader) -> Vec { + header + .to_bytes() + .unwrap_or_else(|error| panic!("should serialize deploy header: {}", error)) +} + +fn serialize_body(payment: &ExecutableDeployItem, session: &ExecutableDeployItem) -> Vec { + let mut buffer = Vec::with_capacity(payment.serialized_length() + session.serialized_length()); + payment + .write_bytes(&mut buffer) + .unwrap_or_else(|error| panic!("should serialize payment code: {}", error)); + session + .write_bytes(&mut buffer) + .unwrap_or_else(|error| panic!("should serialize session code: {}", error)); + buffer +} diff --git a/lib/cli/deploy_builder/error.rs b/lib/cli/deploy_builder/error.rs new file mode 100644 index 00000000..c7471d79 --- /dev/null +++ b/lib/cli/deploy_builder/error.rs @@ -0,0 +1,42 @@ +use core::fmt::{self, Display, Formatter}; +use std::error::Error as StdError; + +#[cfg(doc)] +use super::{Deploy, DeployBuilder}; + +/// Errors returned while building a [`Deploy`] using a [`DeployBuilder`]. +#[derive(Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum DeployBuilderError { + /// Failed to build `Deploy` due to missing session account. + /// + /// Call [`DeployBuilder::with_account`] or [`DeployBuilder::with_secret_key`] before + /// calling [`DeployBuilder::build`]. + DeployMissingSessionAccount, + /// Failed to build `Deploy` due to missing payment code. + /// + /// Call [`DeployBuilder::with_payment`] before + /// calling [`DeployBuilder::build`]. + DeployMissingPaymentCode, +} + +impl Display for DeployBuilderError { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + match self { + DeployBuilderError::DeployMissingSessionAccount => { + write!( + formatter, + "deploy requires session account - use `with_account` or `with_secret_key`" + ) + } + DeployBuilderError::DeployMissingPaymentCode => { + write!( + formatter, + "deploy requires payment code - use `with_payment`" + ) + } + } + } +} + +impl StdError for DeployBuilderError {} diff --git a/lib/cli/tests.rs b/lib/cli/tests.rs index 74ba7033..47418677 100644 --- a/lib/cli/tests.rs +++ b/lib/cli/tests.rs @@ -338,7 +338,7 @@ fn should_fail_to_create_deploy_with_no_session_account() { assert!(matches!( deploy.unwrap_err(), CliError::Core(Error::DeployBuild( - casper_types::DeployBuilderError::DeployMissingSessionAccount + DeployBuilderError::DeployMissingSessionAccount )) )); } @@ -384,7 +384,7 @@ fn should_fail_to_create_transfer_without_account() { assert!(matches!( transfer_deploy.unwrap_err(), CliError::Core(Error::DeployBuild( - casper_types::DeployBuilderError::DeployMissingSessionAccount + DeployBuilderError::DeployMissingSessionAccount )) )); } diff --git a/lib/cli/transaction_v1_builder.rs b/lib/cli/transaction_v1_builder.rs index 35754060..27336468 100644 --- a/lib/cli/transaction_v1_builder.rs +++ b/lib/cli/transaction_v1_builder.rs @@ -495,7 +495,7 @@ impl<'a> TransactionV1Builder<'a> { self.do_build() } - fn build_transaction( + fn build_transaction_inner( chain_name: String, timestamp: Timestamp, ttl: TimeDiff, @@ -526,46 +526,6 @@ impl<'a> TransactionV1Builder<'a> { transaction } - #[cfg(not(test))] - fn do_build(self) -> Result { - let initiator_addr_and_secret_key = match (self.initiator_addr, self.secret_key) { - (Some(initiator_addr), Some(secret_key)) => InitiatorAddrAndSecretKey::Both { - initiator_addr, - secret_key, - }, - (Some(initiator_addr), None) => { - InitiatorAddrAndSecretKey::InitiatorAddr(initiator_addr) - } - (None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key), - (None, None) => return Err(TransactionV1BuilderError::MissingInitiatorAddr), - }; - - let chain_name = self - .chain_name - .ok_or(TransactionV1BuilderError::MissingChainName)?; - - let container = - FieldsContainer::new(self.args, self.target, self.entry_point, self.scheduling) - .to_map() - .map_err(|err| match err { - FieldsContainerError::CouldNotSerializeField { field_index } => { - TransactionV1BuilderError::CouldNotSerializeField { field_index } - } - })?; - - let transaction = Self::build_transaction( - chain_name, - self.timestamp, - self.ttl, - self.pricing_mode, - container, - initiator_addr_and_secret_key, - ); - - Ok(transaction) - } - - #[cfg(test)] fn do_build(self) -> Result { let initiator_addr_and_secret_key = match (self.initiator_addr, &self.secret_key) { (Some(initiator_addr), Some(secret_key)) => InitiatorAddrAndSecretKey::Both { @@ -582,6 +542,7 @@ impl<'a> TransactionV1Builder<'a> { let chain_name = self .chain_name .ok_or(TransactionV1BuilderError::MissingChainName)?; + let container = FieldsContainer::new(self.args, self.target, self.entry_point, self.scheduling) .to_map() @@ -591,7 +552,7 @@ impl<'a> TransactionV1Builder<'a> { } })?; - let transaction = Self::build_transaction( + let transaction = Self::build_transaction_inner( chain_name, self.timestamp, self.ttl, diff --git a/lib/error.rs b/lib/error.rs index 9de6b550..c4cb27ae 100644 --- a/lib/error.rs +++ b/lib/error.rs @@ -4,9 +4,13 @@ use thiserror::Error; use casper_types::{bytesrepr::Error as ToBytesError, crypto, Key}; #[cfg(doc)] -use casper_types::{CLValue, Deploy, DeployBuilder, TimeDiff, Timestamp, URef}; +use casper_types::{CLValue, Deploy, TimeDiff, Timestamp, URef}; -use crate::{cli::TransactionV1BuilderError, validation::ValidateResponseError, JsonRpcId}; +use crate::{ + cli::{DeployBuilderError, TransactionV1BuilderError}, + validation::ValidateResponseError, + JsonRpcId, +}; /// Errors that may be returned by `casper_client` functions. #[derive(Error, Debug)] @@ -17,7 +21,7 @@ pub enum Error { /// Failed to build [`Deploy`]. #[error(transparent)] - DeployBuild(#[from] casper_types::DeployBuilderError), + DeployBuild(#[from] DeployBuilderError), /// Failed to build Transaction #[error(transparent)] From 85aeb95036c338cd9e4f19023f714c06767eb078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Fri, 29 Nov 2024 13:03:40 +0100 Subject: [PATCH 3/3] point to feat-2.0 branch --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9be06d94..00232b59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ uint = "0.9.5" tempfile = "3.8.1" [patch.crates-io] -casper-types = { git = "https://github.com/casper-network/casper-node.git", tag = "v2.0.0-rc5" } +casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" } [package.metadata.deb] features = ["vendored-openssl"]