diff --git a/algorithms/benches/snark/varuna.rs b/algorithms/benches/snark/varuna.rs index b1ccc8b36e..ed3aced9a9 100644 --- a/algorithms/benches/snark/varuna.rs +++ b/algorithms/benches/snark/varuna.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. + #[macro_use] extern crate criterion; diff --git a/ledger/block/src/transaction/bytes.rs b/ledger/block/src/transaction/bytes.rs index bbc92ac03d..17cadbde33 100644 --- a/ledger/block/src/transaction/bytes.rs +++ b/ledger/block/src/transaction/bytes.rs @@ -99,7 +99,7 @@ impl ToBytes for Transaction { // Note: We purposefully do not write out the deployment or execution ID, // and instead recompute it when reconstructing the transaction, to ensure there was no malleability. match self { - Self::Deploy(id, _, owner, deployment, fee) => { + Self::Deploy(id, owner, deployment, fee) => { // Write the variant. 0u8.write_le(&mut writer)?; // Write the ID. @@ -111,7 +111,7 @@ impl ToBytes for Transaction { // Write the fee. fee.write_le(&mut writer) } - Self::Execute(id, _, execution, fee) => { + Self::Execute(id, execution, fee) => { // Write the variant. 1u8.write_le(&mut writer)?; // Write the ID. diff --git a/ledger/block/src/transaction/deployment/bytes.rs b/ledger/block/src/transaction/deployment/bytes.rs index 0b46140e20..4dda746229 100644 --- a/ledger/block/src/transaction/deployment/bytes.rs +++ b/ledger/block/src/transaction/deployment/bytes.rs @@ -18,6 +18,14 @@ use super::*; impl FromBytes for Deployment { /// Reads the deployment from a buffer. fn read_le(mut reader: R) -> IoResult { + // Read the id variant. + let id_variant = u8::read_le(&mut reader)?; + // Read the deployment id. + let id = match id_variant { + 0 => None, + 1 => Some(DeploymentID::::read_le(&mut reader)?), + _ => return Err(error(format!("Invalid id variant '{id_variant}'"))), + }; // Read the version and ensure the version is valid. let version = match u8::read_le(&mut reader)? { 1 => DeploymentVersion::V1, @@ -82,7 +90,7 @@ impl FromBytes for Deployment { }; // Return the deployment. - Self::new(edition, program, verifying_keys, program_checksum, program_owner) + Self::new(id, edition, program, verifying_keys, program_checksum, program_owner) .map_err(|err| error(format!("{err}"))) } } @@ -90,6 +98,14 @@ impl FromBytes for Deployment { impl ToBytes for Deployment { /// Writes the deployment to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { + // Write the deployment id. + match self.id.get() { + None => 0u8.write_le(&mut writer)?, + Some(id) => { + 1u8.write_le(&mut writer)?; + id.write_le(&mut writer)?; + } + }; // Determine the version. // Note: This method checks that either both or neither of the program checksum and program owner are present. let version = self.version().map_err(error)?; diff --git a/ledger/block/src/transaction/deployment/mod.rs b/ledger/block/src/transaction/deployment/mod.rs index dbb6d2d6d4..61ba4d2dff 100644 --- a/ledger/block/src/transaction/deployment/mod.rs +++ b/ledger/block/src/transaction/deployment/mod.rs @@ -19,7 +19,9 @@ mod bytes; mod serialize; mod string; -use crate::Transaction; +use std::sync::OnceLock; + +use crate::{Transaction, transaction::DeploymentID}; use console::{ network::prelude::*, program::{Address, Identifier, ProgramID}, @@ -30,6 +32,8 @@ use snarkvm_synthesizer_snark::{Certificate, VerifyingKey}; #[derive(Clone)] pub struct Deployment { + /// The deployment id + id: OnceLock>, /// The edition. edition: u16, /// The program. @@ -59,14 +63,20 @@ impl Eq for Deployment {} impl Deployment { /// Initializes a new deployment. pub fn new( + deployment_id: Option>, edition: u16, program: Program, verifying_keys: Vec<(Identifier, (VerifyingKey, Certificate))>, program_checksum: Option<[U8; 32]>, program_owner: Option>, ) -> Result { + // Set the deployment id. + let id = OnceLock::new(); + if let Some(inner_id) = deployment_id { + let _ = id.set(inner_id); + } // Construct the deployment. - let deployment = Self { edition, program, verifying_keys, program_checksum, program_owner }; + let deployment = Self { id, edition, program, verifying_keys, program_checksum, program_owner }; // Ensure the deployment is ordered. deployment.check_is_ordered()?; // Return the deployment. @@ -206,7 +216,14 @@ impl Deployment { /// Returns the deployment ID. pub fn to_deployment_id(&self) -> Result> { - Ok(*Transaction::deployment_tree(self)?.root()) + if let Some(id) = self.id.get() { + return Ok(*id); + } + + let id = *Transaction::deployment_tree(self)?.root(); + let _ = self.id.set(id); + + Ok(id) } } @@ -296,6 +313,7 @@ function compute: // Create a new deployment with the desired edition. // Note the only valid editions for V1 deployments are 0 and 1. Deployment::::new( + None, edition % 2, deployment.program().clone(), deployment.verifying_keys().clone(), @@ -340,6 +358,7 @@ function compute: .clone(); // Create a new deployment with the desired edition. Deployment::::new( + None, edition, deployment.program().clone(), deployment.verifying_keys().clone(), diff --git a/ledger/block/src/transaction/deployment/serialize.rs b/ledger/block/src/transaction/deployment/serialize.rs index fc661072f8..f5c4603ad3 100644 --- a/ledger/block/src/transaction/deployment/serialize.rs +++ b/ledger/block/src/transaction/deployment/serialize.rs @@ -25,7 +25,11 @@ impl Serialize for Deployment { DeploymentVersion::V1 => 3, DeploymentVersion::V2 => 5, }; - let mut deployment = serializer.serialize_struct("Deployment", len)?; + let mut deployment = + serializer.serialize_struct("Deployment", len + self.id.get().is_some() as usize)?; + if let Some(id) = self.id.get() { + deployment.serialize_field("id", id)?; + } deployment.serialize_field("edition", &self.edition)?; deployment.serialize_field("program", &self.program)?; deployment.serialize_field("verifying_keys", &self.verifying_keys)?; @@ -52,6 +56,8 @@ impl<'de, N: Network> Deserialize<'de> for Deployment { // Recover the deployment. let deployment = Self::new( + serde_json::from_value(deployment.get_mut("id").unwrap_or(&mut serde_json::Value::Null).take()) + .map_err(de::Error::custom)?, // Retrieve the edition. DeserializeExt::take_from_value::(&mut deployment, "edition")?, // Retrieve the program. diff --git a/ledger/block/src/transaction/execution/bytes.rs b/ledger/block/src/transaction/execution/bytes.rs index 23c0d5013d..a6073a77b5 100644 --- a/ledger/block/src/transaction/execution/bytes.rs +++ b/ledger/block/src/transaction/execution/bytes.rs @@ -18,6 +18,14 @@ use super::*; impl FromBytes for Execution { /// Reads the execution from a buffer. fn read_le(mut reader: R) -> IoResult { + // Read the id variant. + let id_variant = u8::read_le(&mut reader)?; + // Read the execution id. + let id = match id_variant { + 0 => None, + 1 => Some(ExecutionID::::read_le(&mut reader)?), + _ => return Err(error(format!("Invalid id variant '{id_variant}'"))), + }; // Read the version. let version = u8::read_le(&mut reader)?; // Ensure the version is valid. @@ -51,14 +59,25 @@ impl FromBytes for Execution { 1 => Some(Proof::read_le(&mut reader)?), _ => return Err(error(format!("Invalid proof variant '{proof_variant}'"))), }; - // Return the new `Execution` instance. - Self::from(transitions.into_iter(), global_state_root, proof).map_err(|e| error(e.to_string())) + // Compute the new `Execution` instance. + let execution = + Self::from(id, transitions.into_iter(), global_state_root, proof).map_err(|e| error(e.to_string()))?; + + Ok(execution) } } impl ToBytes for Execution { /// Writes the execution to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { + // Write the execution id. + match self.id.get() { + None => 0u8.write_le(&mut writer)?, + Some(id) => { + 1u8.write_le(&mut writer)?; + id.write_le(&mut writer)?; + } + }; // Write the version. 1u8.write_le(&mut writer)?; // Write the number of transitions. diff --git a/ledger/block/src/transaction/execution/mod.rs b/ledger/block/src/transaction/execution/mod.rs index 2337e35c4c..8585de359f 100644 --- a/ledger/block/src/transaction/execution/mod.rs +++ b/ledger/block/src/transaction/execution/mod.rs @@ -17,7 +17,9 @@ mod bytes; mod serialize; mod string; -use crate::{Transaction, Transition}; +use std::sync::OnceLock; + +use crate::{Transaction, Transition, transaction::ExecutionID}; use console::{account::Field, network::prelude::*, program::ProgramID}; use snarkvm_synthesizer_snark::Proof; @@ -25,6 +27,8 @@ use indexmap::IndexMap; #[derive(Clone, Default, PartialEq, Eq)] pub struct Execution { + /// The execution id + id: OnceLock>, /// The transitions. transitions: IndexMap>, /// The global state root. @@ -36,17 +40,28 @@ pub struct Execution { impl Execution { /// Initialize a new `Execution` instance. pub fn new() -> Self { - Self { transitions: Default::default(), global_state_root: Default::default(), proof: None } + Self { + id: Default::default(), + transitions: Default::default(), + global_state_root: Default::default(), + proof: None, + } } /// Initializes a new `Execution` instance with the given transitions. pub fn from( + excution_id: Option>, transitions: impl Iterator>, global_state_root: N::StateRoot, proof: Option>, ) -> Result { + let id = OnceLock::new(); + if let Some(inner_id) = excution_id { + let _ = id.set(inner_id); + } + // Construct the execution. - let execution = Self { transitions: transitions.map(|t| (*t.id(), t)).collect(), global_state_root, proof }; + let execution = Self { id, transitions: transitions.map(|t| (*t.id(), t)).collect(), global_state_root, proof }; // Ensure the transitions are not empty. ensure!(!execution.transitions.is_empty(), "Execution cannot initialize from empty list of transitions"); // Return the new `Execution` instance. @@ -70,7 +85,14 @@ impl Execution { /// Returns the execution ID. pub fn to_execution_id(&self) -> Result> { - Ok(*Transaction::execution_tree(self)?.root()) + if let Some(id) = self.id.get() { + return Ok(*id); + } + + let id = *Transaction::execution_tree(self)?.root(); + let _ = self.id.set(id); + + Ok(id) } } @@ -157,7 +179,7 @@ pub mod test_helpers { // Retrieve a transaction. let transaction = block.transactions().iter().nth(index).unwrap().deref().clone(); // Retrieve the execution. - if let Transaction::Execute(_, _, execution, _) = transaction { + if let Transaction::Execute(_, execution, _) = transaction { *execution } else { panic!("Index {index} exceeded the number of executions in the genesis block") diff --git a/ledger/block/src/transaction/execution/serialize.rs b/ledger/block/src/transaction/execution/serialize.rs index f09a891460..fae75d4b07 100644 --- a/ledger/block/src/transaction/execution/serialize.rs +++ b/ledger/block/src/transaction/execution/serialize.rs @@ -20,7 +20,13 @@ impl Serialize for Execution { fn serialize(&self, serializer: S) -> Result { match serializer.is_human_readable() { true => { - let mut execution = serializer.serialize_struct("Execution", 2 + self.proof.is_some() as usize)?; + let mut execution = serializer.serialize_struct( + "Execution", + 3 + self.id.get().is_some() as usize + self.proof.is_some() as usize, + )?; + if let Some(id) = self.id.get() { + execution.serialize_field("id", id)?; + } execution .serialize_field("transitions", &self.transitions.values().collect::>>())?; execution.serialize_field("global_state_root", &self.global_state_root)?; @@ -41,6 +47,9 @@ impl<'de, N: Network> Deserialize<'de> for Execution { true => { // Parse the execution from a string into a value. let mut execution = serde_json::Value::deserialize(deserializer)?; + // Retrieve the execution id. + let id = serde_json::from_value(execution.get_mut("id").unwrap_or(&mut serde_json::Value::Null).take()) + .map_err(de::Error::custom)?; // Retrieve the transitions. let transitions: Vec<_> = DeserializeExt::take_from_value::(&mut execution, "transitions")?; // Retrieve the global state root. @@ -50,7 +59,10 @@ impl<'de, N: Network> Deserialize<'de> for Execution { serde_json::from_value(execution.get_mut("proof").unwrap_or(&mut serde_json::Value::Null).take()) .map_err(de::Error::custom)?; // Recover the execution. - Self::from(transitions.into_iter(), global_state_root, proof).map_err(de::Error::custom) + let execution = + Self::from(id, transitions.into_iter(), global_state_root, proof).map_err(de::Error::custom)?; + + Ok(execution) } false => FromBytesDeserializer::::deserialize_with_size_encoding(deserializer, "execution"), } diff --git a/ledger/block/src/transaction/merkle.rs b/ledger/block/src/transaction/merkle.rs index 6d09f92177..04e312187d 100644 --- a/ledger/block/src/transaction/merkle.rs +++ b/ledger/block/src/transaction/merkle.rs @@ -27,7 +27,7 @@ impl Transaction { /// Returns the Merkle leaf for the given ID of a function or transition in the transaction. pub fn to_leaf(&self, id: &Field) -> Result> { match self { - Self::Deploy(_, _, _, deployment, fee) => { + Self::Deploy(_, _, deployment, fee) => { // Check if the ID is the transition ID for the fee. if *id == **fee.id() { // Return the transaction leaf. @@ -48,7 +48,7 @@ impl Transaction { // Error if the function hash was not found. bail!("Function hash not found in deployment transaction"); } - Self::Execute(_, _, execution, fee) => { + Self::Execute(_, execution, fee) => { // Check if the ID is the transition ID for the fee. if let Some(fee) = fee { if *id == **fee.id() { @@ -92,11 +92,11 @@ impl Transaction { pub fn to_tree(&self) -> Result> { match self { // Compute the deployment tree. - Transaction::Deploy(_, _, _, deployment, fee) => { + Transaction::Deploy(_, _, deployment, fee) => { Self::transaction_tree(Self::deployment_tree(deployment)?, Some(fee)) } // Compute the execution tree. - Transaction::Execute(_, _, execution, fee) => { + Transaction::Execute(_, execution, fee) => { Self::transaction_tree(Self::execution_tree(execution)?, fee.as_ref()) } // Compute the fee tree. @@ -252,8 +252,16 @@ impl Transaction { pub fn deployment_tree_v2(deployment: &Deployment) -> Result> { // Ensure the number of leaves is within the Merkle tree size. Self::check_deployment_size(deployment)?; + // Get the program checksum. + let Some(program_checksum) = deployment.program_checksum() else { + bail!("Program checksum is required for V2 deployment tree"); + }; + // Get the program owner. + let Some(program_owner) = deployment.program_owner() else { + bail!("Program owner is required for V2 deployment tree"); + }; // Compute a hash of the deployment bytes. - let deployment_hash = N::hash_sha3_256(&to_bits_le!(deployment.to_bytes_le()?))?; + let deployment_hash = N::hash_sha3_256(&to_bits_le!(program_checksum, program_owner, deployment.edition()))?; // Prepare the header for the hash. let header = to_bits_le![deployment.version()? as u8, deployment_hash]; // Prepare the leaves. diff --git a/ledger/block/src/transaction/mod.rs b/ledger/block/src/transaction/mod.rs index 10072196c1..7050d5d9e8 100644 --- a/ledger/block/src/transaction/mod.rs +++ b/ledger/block/src/transaction/mod.rs @@ -50,9 +50,9 @@ type ExecutionID = Field; #[derive(Clone, PartialEq, Eq)] pub enum Transaction { /// The deploy transaction publishes an Aleo program to the network. - Deploy(N::TransactionID, DeploymentID, ProgramOwner, Box>, Fee), + Deploy(N::TransactionID, ProgramOwner, Box>, Fee), /// The execute transaction represents a call to an Aleo program. - Execute(N::TransactionID, ExecutionID, Box>, Option>), + Execute(N::TransactionID, Box>, Option>), /// The fee transaction represents a fee paid to the network, used for rejected transactions. Fee(N::TransactionID, Fee), } @@ -79,8 +79,17 @@ impl Transaction { program_owner ) } + // Reconstruct a new deployment with id. + let new_deployment = Deployment::new( + Some(deployment_id), + deployment.edition(), + deployment.program().clone(), + (*deployment.verifying_keys().clone()).to_vec(), + deployment.program_checksum(), + deployment.program_owner(), + )?; // Construct the deployment transaction. - Ok(Self::Deploy(transaction_id.into(), deployment_id, owner, Box::new(deployment), fee)) + Ok(Self::Deploy(transaction_id.into(), owner, Box::new(new_deployment), fee)) } /// Initializes a new execution transaction. @@ -93,8 +102,15 @@ impl Transaction { let execution_id = *execution_tree.root(); // Compute the transaction ID. let transaction_id = *Self::transaction_tree(execution_tree, fee.as_ref())?.root(); + // Reconstruct a new execution with id. + let new_execution = Execution::from( + Some(execution_id), + execution.transitions().cloned(), + execution.global_state_root(), + execution.proof().cloned(), + )?; // Construct the execution transaction. - Ok(Self::Execute(transaction_id.into(), execution_id, Box::new(execution), fee)) + Ok(Self::Execute(transaction_id.into(), Box::new(new_execution), fee)) } /// Initializes a new fee transaction. @@ -134,7 +150,7 @@ impl Transaction { pub fn contains_split(&self) -> bool { match self { // Case 1 - The transaction contains a transition that calls 'credits.aleo/split'. - Transaction::Execute(_, _, execution, _) => execution.transitions().any(|transition| transition.is_split()), + Transaction::Execute(_, execution, _) => execution.transitions().any(|transition| transition.is_split()), // Otherwise, return 'false'. _ => false, } @@ -145,9 +161,7 @@ impl Transaction { pub fn contains_upgrade(&self) -> bool { match self { // Case 1 - The transaction contains a transition that calls 'credits.aleo/upgrade'. - Transaction::Execute(_, _, execution, _) => { - execution.transitions().any(|transition| transition.is_upgrade()) - } + Transaction::Execute(_, execution, _) => execution.transitions().any(|transition| transition.is_upgrade()), // Otherwise, return 'false'. _ => false, } @@ -159,7 +173,7 @@ impl Transaction { #[inline] pub fn owner(&self) -> Option<&ProgramOwner> { match self { - Self::Deploy(_, _, owner, _, _) => Some(owner), + Self::Deploy(_, owner, _, _) => Some(owner), _ => None, } } @@ -168,7 +182,7 @@ impl Transaction { #[inline] pub fn deployment(&self) -> Option<&Deployment> { match self { - Self::Deploy(_, _, _, deployment, _) => Some(deployment.as_ref()), + Self::Deploy(_, _, deployment, _) => Some(deployment.as_ref()), _ => None, } } @@ -177,7 +191,7 @@ impl Transaction { #[inline] pub fn execution(&self) -> Option<&Execution> { match self { - Self::Execute(_, _, execution, _) => Some(execution), + Self::Execute(_, execution, _) => Some(execution), _ => None, } } @@ -227,9 +241,9 @@ impl Transaction { /// Returns the transaction total fee. pub fn fee_amount(&self) -> Result> { match self { - Self::Deploy(_, _, _, _, fee) => fee.amount(), - Self::Execute(_, _, _, Some(fee)) => fee.amount(), - Self::Execute(_, _, _, None) => Ok(U64::zero()), + Self::Deploy(_, _, _, fee) => fee.amount(), + Self::Execute(_, _, Some(fee)) => fee.amount(), + Self::Execute(_, _, None) => Ok(U64::zero()), Self::Fee(_, fee) => fee.amount(), } } @@ -237,9 +251,9 @@ impl Transaction { /// Returns the transaction base fee. pub fn base_fee_amount(&self) -> Result> { match self { - Self::Deploy(_, _, _, _, fee) => fee.base_amount(), - Self::Execute(_, _, _, Some(fee)) => fee.base_amount(), - Self::Execute(_, _, _, None) => Ok(U64::zero()), + Self::Deploy(_, _, _, fee) => fee.base_amount(), + Self::Execute(_, _, Some(fee)) => fee.base_amount(), + Self::Execute(_, _, None) => Ok(U64::zero()), Self::Fee(_, fee) => fee.base_amount(), } } @@ -247,9 +261,9 @@ impl Transaction { /// Returns the transaction priority fee. pub fn priority_fee_amount(&self) -> Result> { match self { - Self::Deploy(_, _, _, _, fee) => fee.priority_amount(), - Self::Execute(_, _, _, Some(fee)) => fee.priority_amount(), - Self::Execute(_, _, _, None) => Ok(U64::zero()), + Self::Deploy(_, _, _, fee) => fee.priority_amount(), + Self::Execute(_, _, Some(fee)) => fee.priority_amount(), + Self::Execute(_, _, None) => Ok(U64::zero()), Self::Fee(_, fee) => fee.priority_amount(), } } @@ -257,8 +271,8 @@ impl Transaction { /// Returns the fee transition. pub fn fee_transition(&self) -> Option> { match self { - Self::Deploy(_, _, _, _, fee) => Some(fee.clone()), - Self::Execute(_, _, _, fee) => fee.clone(), + Self::Deploy(_, _, _, fee) => Some(fee.clone()), + Self::Execute(_, _, fee) => fee.clone(), Self::Fee(_, fee) => Some(fee.clone()), } } @@ -269,9 +283,9 @@ impl Transaction { pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool { match self { // Check the fee. - Self::Deploy(_, _, _, _, fee) => fee.id() == transition_id, + Self::Deploy(_, _, _, fee) => fee.id() == transition_id, // Check the execution and fee. - Self::Execute(_, _, execution, fee) => { + Self::Execute(_, execution, fee) => { execution.contains_transition(transition_id) || fee.as_ref().is_some_and(|fee| fee.id() == transition_id) } @@ -296,12 +310,12 @@ impl Transaction { pub fn find_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition> { match self { // Check the fee. - Self::Deploy(_, _, _, _, fee) => match fee.id() == transition_id { + Self::Deploy(_, _, _, fee) => match fee.id() == transition_id { true => Some(fee.transition()), false => None, }, // Check the execution and fee. - Self::Execute(_, _, execution, fee) => execution.get_transition(transition_id).or_else(|| { + Self::Execute(_, execution, fee) => execution.get_transition(transition_id).or_else(|| { fee.as_ref().and_then(|fee| match fee.id() == transition_id { true => Some(fee.transition()), false => None, @@ -340,8 +354,8 @@ impl Transaction { /// Returns an iterator over all transitions. pub fn transitions(&self) -> impl '_ + DoubleEndedIterator> { match self { - Self::Deploy(_, _, _, _, fee) => IterWrap::Deploy(Some(fee.transition()).into_iter()), - Self::Execute(_, _, execution, fee) => { + Self::Deploy(_, _, _, fee) => IterWrap::Deploy(Some(fee.transition()).into_iter()), + Self::Execute(_, execution, fee) => { IterWrap::Execute(execution.transitions().chain(fee.as_ref().map(|fee| fee.transition()))) } Self::Fee(_, fee) => IterWrap::Fee(Some(fee.transition()).into_iter()), @@ -407,8 +421,8 @@ impl Transaction { /// Returns a consuming iterator over all transitions. pub fn into_transitions(self) -> impl DoubleEndedIterator> { match self { - Self::Deploy(_, _, _, _, fee) => IterWrap::Deploy(Some(fee.into_transition()).into_iter()), - Self::Execute(_, _, execution, fee) => { + Self::Deploy(_, _, _, fee) => IterWrap::Deploy(Some(fee.into_transition()).into_iter()), + Self::Execute(_, execution, fee) => { IterWrap::Execute(execution.into_transitions().chain(fee.map(|fee| fee.into_transition()))) } Self::Fee(_, fee) => IterWrap::Fee(Some(fee.into_transition()).into_iter()), @@ -555,18 +569,18 @@ mod tests { { match expected { // Compare against transaction IDs created using `deployment_tree`. - Transaction::Deploy(transaction_id, deployment_id, _, ref deployment, _) => { + Transaction::Deploy(transaction_id, _, ref deployment, _) => { let expected_transaction_id = *expected.clone().to_tree()?.root(); assert_eq!(expected_transaction_id, *transaction_id); let expected_deployment_id = *Transaction::deployment_tree(deployment)?.root(); - assert_eq!(expected_deployment_id, deployment_id); + assert_eq!(expected_deployment_id, deployment.to_deployment_id()?); } // Compare against transaction IDs created using `execution_tree`. - Transaction::Execute(transaction_id, execution_id, ref execution, _) => { + Transaction::Execute(transaction_id, ref execution, _) => { let expected_transaction_id = *expected.clone().to_tree()?.root(); assert_eq!(expected_transaction_id, *transaction_id); let expected_execution_id = *Transaction::execution_tree(execution)?.root(); - assert_eq!(expected_execution_id, execution_id); + assert_eq!(expected_execution_id, execution.to_execution_id()?); } _ => panic!("Unexpected test case."), }; diff --git a/ledger/block/src/transaction/serialize.rs b/ledger/block/src/transaction/serialize.rs index 2a1753f170..462f40a3d3 100644 --- a/ledger/block/src/transaction/serialize.rs +++ b/ledger/block/src/transaction/serialize.rs @@ -22,7 +22,7 @@ impl Serialize for Transaction { // and instead recompute it when reconstructing the transaction, to ensure there was no malleability. match serializer.is_human_readable() { true => match self { - Self::Deploy(id, _, owner, deployment, fee) => { + Self::Deploy(id, owner, deployment, fee) => { let mut transaction = serializer.serialize_struct("Transaction", 5)?; transaction.serialize_field("type", "deploy")?; transaction.serialize_field("id", &id)?; @@ -31,7 +31,7 @@ impl Serialize for Transaction { transaction.serialize_field("fee", &fee)?; transaction.end() } - Self::Execute(id, _, execution, fee) => { + Self::Execute(id, execution, fee) => { let mut transaction = serializer.serialize_struct("Transaction", 3 + fee.is_some() as usize)?; transaction.serialize_field("type", "execute")?; transaction.serialize_field("id", &id)?; diff --git a/ledger/block/src/transactions/confirmed/mod.rs b/ledger/block/src/transactions/confirmed/mod.rs index c27a147bdb..b81e5b7687 100644 --- a/ledger/block/src/transactions/confirmed/mod.rs +++ b/ledger/block/src/transactions/confirmed/mod.rs @@ -49,7 +49,7 @@ impl ConfirmedTransaction { ) -> Result { // Retrieve the program and fee from the deployment transaction, and ensure the transaction is a deploy transaction. let (program, fee) = match &transaction { - Transaction::Deploy(_, _, _, deployment, fee) => (deployment.program(), fee), + Transaction::Deploy(_, _, deployment, fee) => (deployment.program(), fee), Transaction::Execute(..) | Transaction::Fee(..) => { bail!("Transaction '{}' is not a deploy transaction", transaction.id()) } @@ -321,9 +321,8 @@ impl ConfirmedTransaction { match self { Self::AcceptedDeploy(_, transaction, _) => Ok(transaction.id()), Self::AcceptedExecute(_, transaction, _) => Ok(transaction.id()), - Self::RejectedDeploy(_, fee_transaction, rejected, _) - | Self::RejectedExecute(_, fee_transaction, rejected, _) => { - Ok(rejected.to_unconfirmed_id(&fee_transaction.fee_transition())?.into()) + Self::RejectedDeploy(_, _, rejected, _) | Self::RejectedExecute(_, _, rejected, _) => { + Ok(rejected.to_unconfirmed_id().into()) } } } @@ -641,6 +640,7 @@ mod test { let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), true, rng); let rejected = Rejected::new_deployment( + deployment_transaction.id(), *deployment_transaction.owner().unwrap(), deployment_transaction.deployment().unwrap().clone(), ); @@ -652,6 +652,7 @@ mod test { let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, rng); let rejected = Rejected::new_deployment( + deployment_transaction.id(), *deployment_transaction.owner().unwrap(), deployment_transaction.deployment().unwrap().clone(), ); @@ -663,6 +664,7 @@ mod test { let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, rng); let rejected = Rejected::new_deployment( + deployment_transaction.id(), *deployment_transaction.owner().unwrap(), deployment_transaction.deployment().unwrap().clone(), ); @@ -674,6 +676,7 @@ mod test { let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, rng); let rejected = Rejected::new_deployment( + deployment_transaction.id(), *deployment_transaction.owner().unwrap(), deployment_transaction.deployment().unwrap().clone(), ); @@ -685,7 +688,8 @@ mod test { // Ensure that the unconfirmed transaction of a rejected execute is not equivalent to its confirmed transaction. let execution_transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0); - let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone()); + let rejected = + Rejected::new_execution(execution_transaction.id(), execution_transaction.execution().unwrap().clone()); let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap(); let rejected_execute = ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap(); @@ -694,7 +698,8 @@ mod test { let execution_transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0); - let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone()); + let rejected = + Rejected::new_execution(execution_transaction.id(), execution_transaction.execution().unwrap().clone()); let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap(); let rejected_execute = ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap(); diff --git a/ledger/block/src/transactions/rejected/bytes.rs b/ledger/block/src/transactions/rejected/bytes.rs index 6216f8a975..72d61ceaba 100644 --- a/ledger/block/src/transactions/rejected/bytes.rs +++ b/ledger/block/src/transactions/rejected/bytes.rs @@ -21,18 +21,22 @@ impl FromBytes for Rejected { let variant = u8::read_le(&mut reader)?; match variant { 0 => { + // Read the unconfirmed deployment id. + let id = N::TransactionID::read_le(&mut reader)?; // Read the program owner. let program_owner = ProgramOwner::read_le(&mut reader)?; // Read the deployment. let deployment = Deployment::read_le(&mut reader)?; // Return the rejected deployment. - Ok(Self::new_deployment(program_owner, deployment)) + Ok(Self::new_deployment(id, program_owner, deployment)) } 1 => { + // Read the unconfirmed execution id. + let id = N::TransactionID::read_le(&mut reader)?; // Read the execution. let execution = Execution::read_le(&mut reader)?; // Return the rejected execution. - Ok(Self::new_execution(execution)) + Ok(Self::new_execution(id, execution)) } 2.. => Err(error(format!("Failed to decode rejected transaction variant {variant}"))), } @@ -43,17 +47,21 @@ impl ToBytes for Rejected { /// Writes the rejected transaction to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { match self { - Self::Deployment(program_owner, deployment) => { + Self::Deployment(id, program_owner, deployment) => { // Write the variant. 0u8.write_le(&mut writer)?; + // Write the unconfirmed deployment id. + id.write_le(&mut writer)?; // Write the program owner. program_owner.write_le(&mut writer)?; // Write the deployment. deployment.write_le(&mut writer) } - Self::Execution(execution) => { + Self::Execution(id, execution) => { // Write the variant. 1u8.write_le(&mut writer)?; + // Write the unconfirmed execution id. + id.write_le(&mut writer)?; // Write the execution. execution.write_le(&mut writer) } diff --git a/ledger/block/src/transactions/rejected/mod.rs b/ledger/block/src/transactions/rejected/mod.rs index 28a449af47..097f8fef34 100644 --- a/ledger/block/src/transactions/rejected/mod.rs +++ b/ledger/block/src/transactions/rejected/mod.rs @@ -19,24 +19,24 @@ mod string; use super::*; -use crate::{Deployment, Execution, Fee}; +use crate::{Deployment, Execution}; /// A wrapper around the rejected deployment or execution. #[derive(Clone, PartialEq, Eq)] pub enum Rejected { - Deployment(ProgramOwner, Box>), - Execution(Box>), + Deployment(N::TransactionID, ProgramOwner, Box>), + Execution(N::TransactionID, Box>), } impl Rejected { /// Initializes a rejected deployment. - pub fn new_deployment(program_owner: ProgramOwner, deployment: Deployment) -> Self { - Self::Deployment(program_owner, Box::new(deployment)) + pub fn new_deployment(id: N::TransactionID, program_owner: ProgramOwner, deployment: Deployment) -> Self { + Self::Deployment(id, program_owner, Box::new(deployment)) } /// Initializes a rejected execution. - pub fn new_execution(execution: Execution) -> Self { - Self::Execution(Box::new(execution)) + pub fn new_execution(id: N::TransactionID, execution: Execution) -> Self { + Self::Execution(id, Box::new(execution)) } /// Returns true if the rejected transaction is a deployment. @@ -52,46 +52,44 @@ impl Rejected { /// Returns the program owner of the rejected deployment. pub fn program_owner(&self) -> Option<&ProgramOwner> { match self { - Self::Deployment(program_owner, _) => Some(program_owner), - Self::Execution(_) => None, + Self::Deployment(_, program_owner, _) => Some(program_owner), + Self::Execution(_, _) => None, } } /// Returns the rejected deployment. pub fn deployment(&self) -> Option<&Deployment> { match self { - Self::Deployment(_, deployment) => Some(deployment), - Self::Execution(_) => None, + Self::Deployment(_, _, deployment) => Some(deployment), + Self::Execution(_, _) => None, } } /// Returns the rejected execution. pub fn execution(&self) -> Option<&Execution> { match self { - Self::Deployment(_, _) => None, - Self::Execution(execution) => Some(execution), + Self::Deployment(_, _, _) => None, + Self::Execution(_, execution) => Some(execution), } } /// Returns the rejected ID. pub fn to_id(&self) -> Result> { match self { - Self::Deployment(_, deployment) => deployment.to_deployment_id(), - Self::Execution(execution) => execution.to_execution_id(), + Self::Deployment(_, _, deployment) => deployment.to_deployment_id(), + Self::Execution(_, execution) => execution.to_execution_id(), } } /// Returns the unconfirmed transaction ID, which is defined as the transaction ID prior to confirmation. /// When a transaction is rejected, its fee transition is used to construct the confirmed transaction ID, /// changing the original transaction ID. - pub fn to_unconfirmed_id(&self, fee: &Option>) -> Result> { - // Compute the deployment or execution tree. - let tree = match self { - Self::Deployment(_, deployment) => Transaction::deployment_tree(deployment)?, - Self::Execution(execution) => Transaction::execution_tree(execution)?, - }; - // Construct the transaction tree and return the unconfirmed transaction ID. - Ok(*Transaction::transaction_tree(tree, fee.as_ref())?.root()) + pub fn to_unconfirmed_id(&self) -> Field { + // Retrieve the deployment or execution id. + match self { + Self::Deployment(id, _, _) => **id, + Self::Execution(id, _) => **id, + } } } @@ -110,13 +108,13 @@ pub mod test_helpers { rng: &mut TestRng, ) -> Rejected { // Sample a deploy transaction. - let deployment = match crate::transaction::test_helpers::sample_deployment_transaction( + let (id, deployment) = match crate::transaction::test_helpers::sample_deployment_transaction( version, edition, is_fee_private, rng, ) { - Transaction::Deploy(_, _, _, deployment, _) => (*deployment).clone(), + Transaction::Deploy(id, _, deployment, _) => (id, (*deployment).clone()), _ => unreachable!(), }; @@ -126,20 +124,20 @@ pub mod test_helpers { let program_owner = ProgramOwner::new(&private_key, deployment_id, rng).unwrap(); // Return the rejected deployment. - Rejected::new_deployment(program_owner, deployment) + Rejected::new_deployment(id, program_owner, deployment) } /// Samples a rejected execution. pub(crate) fn sample_rejected_execution(is_fee_private: bool, rng: &mut TestRng) -> Rejected { // Sample an execute transaction. - let execution = + let (id, execution) = match crate::transaction::test_helpers::sample_execution_transaction_with_fee(is_fee_private, rng, 0) { - Transaction::Execute(_, _, execution, _) => execution, + Transaction::Execute(id, execution, _) => (id, execution), _ => unreachable!(), }; // Return the rejected execution. - Rejected::new_execution(*execution) + Rejected::new_execution(id, *execution) } /// Sample a list of randomly rejected transactions. diff --git a/ledger/block/src/transactions/rejected/serialize.rs b/ledger/block/src/transactions/rejected/serialize.rs index 42dcab9a0d..06e8be6123 100644 --- a/ledger/block/src/transactions/rejected/serialize.rs +++ b/ledger/block/src/transactions/rejected/serialize.rs @@ -20,16 +20,18 @@ impl Serialize for Rejected { fn serialize(&self, serializer: S) -> Result { match serializer.is_human_readable() { true => match self { - Self::Deployment(program_owner, deployment) => { - let mut object = serializer.serialize_struct("Rejected", 3)?; + Self::Deployment(id, program_owner, deployment) => { + let mut object = serializer.serialize_struct("Rejected", 4)?; object.serialize_field("type", "deployment")?; + object.serialize_field("id", id)?; object.serialize_field("program_owner", program_owner)?; object.serialize_field("deployment", deployment)?; object.end() } - Self::Execution(execution) => { - let mut object = serializer.serialize_struct("Rejected", 2)?; + Self::Execution(id, execution) => { + let mut object = serializer.serialize_struct("Rejected", 3)?; object.serialize_field("type", "execution")?; + object.serialize_field("id", id)?; object.serialize_field("execution", execution)?; object.end() } @@ -53,6 +55,8 @@ impl<'de, N: Network> Deserialize<'de> for Rejected { // Recover the rejected transaction. match type_ { Some("deployment") => { + // Parse the unconfirmed deployment id. + let id: N::TransactionID = DeserializeExt::take_from_value::(&mut object, "id")?; // Parse the program owner. let program_owner: ProgramOwner = DeserializeExt::take_from_value::(&mut object, "program_owner")?; @@ -60,13 +64,15 @@ impl<'de, N: Network> Deserialize<'de> for Rejected { let deployment: Deployment = DeserializeExt::take_from_value::(&mut object, "deployment")?; // Return the rejected deployment. - Ok(Self::new_deployment(program_owner, deployment)) + Ok(Self::new_deployment(id, program_owner, deployment)) } Some("execution") => { + // Parse the unconfirmed execution id. + let id: N::TransactionID = DeserializeExt::take_from_value::(&mut object, "id")?; // Parse the execution. let execution: Execution = DeserializeExt::take_from_value::(&mut object, "execution")?; // Return the rejected execution. - Ok(Self::new_execution(execution)) + Ok(Self::new_execution(id, execution)) } _ => Err(de::Error::custom("Invalid rejected transaction type")), } diff --git a/ledger/block/src/transition/mod.rs b/ledger/block/src/transition/mod.rs index cab6cd3c77..9d72eaa6d1 100644 --- a/ledger/block/src/transition/mod.rs +++ b/ledger/block/src/transition/mod.rs @@ -523,7 +523,7 @@ pub mod test_helpers { /// Samples a random transition. pub(crate) fn sample_transition(rng: &mut TestRng) -> Transition { - if let Transaction::Execute(_, _, execution, _) = + if let Transaction::Execute(_, execution, _) = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0) { execution.into_transitions().next().unwrap() diff --git a/ledger/src/tests.rs b/ledger/src/tests.rs index 4c8e461a27..8285dfa00b 100644 --- a/ledger/src/tests.rs +++ b/ledger/src/tests.rs @@ -552,10 +552,10 @@ finalize failed_assert: assert_eq!(next_block.transactions().len(), 1); let confirmed_transaction = next_block.transactions().iter().next().unwrap(); assert!(confirmed_transaction.is_rejected()); - if let Transaction::Execute(_, _, execution, fee) = failed_assert_transaction { + if let Transaction::Execute(id, execution, fee) = failed_assert_transaction { let fee_transaction = Transaction::from_fee(fee.unwrap()).unwrap(); let expected_confirmed_transaction = - ConfirmedTransaction::RejectedExecute(0, fee_transaction, Rejected::new_execution(*execution), vec![]); + ConfirmedTransaction::RejectedExecute(0, fee_transaction, Rejected::new_execution(id, *execution), vec![]); assert_eq!(confirmed_transaction, &expected_confirmed_transaction); } @@ -974,6 +974,7 @@ finalize foo: let mutated_transitions = std::iter::once(sample).chain(execution_to_mutate.transitions().cloned()); // Create a mutated execution. let mutated_execution = Execution::from( + None, mutated_transitions, execution_to_mutate.global_state_root(), execution_to_mutate.proof().cloned(), diff --git a/ledger/store/src/block/mod.rs b/ledger/store/src/block/mod.rs index e6b5aad0dd..175c08ff3c 100644 --- a/ledger/store/src/block/mod.rs +++ b/ledger/store/src/block/mod.rs @@ -1561,7 +1561,7 @@ mod tests { let previous_hash = ::BlockHash::default(); let fee_id = fee.id(); - let unconfirmed_id = rejected.to_unconfirmed_id(&fee.fee_transition()).unwrap().into(); + let unconfirmed_id = rejected.to_unconfirmed_id().into(); // Construct the block. let block = Block::new_beacon( diff --git a/ledger/store/src/transaction/deployment.rs b/ledger/store/src/transaction/deployment.rs index c3a12e503c..af3a7a2cfe 100644 --- a/ledger/store/src/transaction/deployment.rs +++ b/ledger/store/src/transaction/deployment.rs @@ -197,7 +197,7 @@ pub trait DeploymentStorage: Clone + Send + Sync { fn insert(&self, transaction: &Transaction) -> Result<()> { // Ensure the transaction is a deployment. let (transaction_id, owner, deployment, fee) = match transaction { - Transaction::Deploy(transaction_id, _, owner, deployment, fee) => (transaction_id, owner, deployment, fee), + Transaction::Deploy(transaction_id, owner, deployment, fee) => (transaction_id, owner, deployment, fee), Transaction::Execute(..) => bail!("Attempted to insert an execute transaction into deployment storage."), Transaction::Fee(..) => bail!("Attempted to insert fee transaction into deployment storage."), }; @@ -611,7 +611,7 @@ pub trait DeploymentStorage: Clone + Send + Sync { } // Return the deployment. - Ok(Some(Deployment::new(edition, program, verifying_keys, program_checksum, program_owner)?)) + Ok(Some(Deployment::new(None, edition, program, verifying_keys, program_checksum, program_owner)?)) } /// Returns the fee for the given `transaction ID`. @@ -1053,7 +1053,7 @@ mod tests { for transaction in transactions { let transaction_id = transaction.id(); let (program_id, edition) = match transaction { - Transaction::Deploy(_, _, _, ref deployment, _) => (*deployment.program_id(), deployment.edition()), + Transaction::Deploy(_, _, ref deployment, _) => (*deployment.program_id(), deployment.edition()), _ => panic!("Incorrect transaction type"), }; let fee_id = *transaction.fee_transition().unwrap().id(); diff --git a/ledger/store/src/transaction/execution.rs b/ledger/store/src/transaction/execution.rs index 23b4fa51f8..0b00081e77 100644 --- a/ledger/store/src/transaction/execution.rs +++ b/ledger/store/src/transaction/execution.rs @@ -122,7 +122,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { // Ensure the transaction is a execution. let (transaction_id, execution, fee) = match transaction { Transaction::Deploy(..) => bail!("Attempted to insert a deploy transaction into execution storage."), - Transaction::Execute(transaction_id, _, execution, fee) => (transaction_id, execution, fee), + Transaction::Execute(transaction_id, execution, fee) => (transaction_id, execution, fee), Transaction::Fee(..) => bail!("Attempted to insert a fee transaction into execution storage."), }; @@ -232,7 +232,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { } // Return the execution. - Ok(Some(Execution::from(transitions.into_iter(), global_state_root, proof)?)) + Ok(Some(Execution::from(None, transitions.into_iter(), global_state_root, proof)?)) } /// Returns the transaction for the given `transaction ID`. @@ -262,7 +262,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { } // Construct the execution. - let execution = Execution::from(transitions.into_iter(), global_state_root, proof)?; + let execution = Execution::from(None, transitions.into_iter(), global_state_root, proof)?; // Construct the transaction. let transaction = match has_fee { diff --git a/ledger/test-helpers/src/lib.rs b/ledger/test-helpers/src/lib.rs index f849333252..9b0691121c 100644 --- a/ledger/test-helpers/src/lib.rs +++ b/ledger/test-helpers/src/lib.rs @@ -178,6 +178,7 @@ function compute: .clone(); // Create a new deployment with the desired edition. Deployment::::new( + None, edition % 2, deployment.program().clone(), deployment.verifying_keys().clone(), @@ -222,6 +223,7 @@ function compute: .clone(); // Create a new deployment with the desired edition. Deployment::::new( + None, edition, deployment.program().clone(), deployment.verifying_keys().clone(), @@ -239,8 +241,8 @@ pub fn sample_rejected_deployment( rng: &mut TestRng, ) -> Rejected { // Sample a deploy transaction. - let deployment = match crate::sample_deployment_transaction(version, edition, is_fee_private, rng) { - Transaction::Deploy(_, _, _, deployment, _) => (*deployment).clone(), + let (id, deployment) = match crate::sample_deployment_transaction(version, edition, is_fee_private, rng) { + Transaction::Deploy(id, _, deployment, _) => (id, (*deployment).clone()), _ => unreachable!(), }; @@ -250,7 +252,7 @@ pub fn sample_rejected_deployment( let program_owner = ProgramOwner::new(&private_key, deployment_id, rng).unwrap(); // Return the rejected deployment. - Rejected::new_deployment(program_owner, deployment) + Rejected::new_deployment(id, program_owner, deployment) } /******************************************* Execution ********************************************/ @@ -262,7 +264,7 @@ pub fn sample_execution(rng: &mut TestRng, index: usize) -> Execution Execution Rejected { // Sample an execute transaction. - let execution = match crate::sample_execution_transaction_with_fee(is_fee_private, rng, 0) { - Transaction::Execute(_, _, execution, _) => execution, + let (id, execution) = match crate::sample_execution_transaction_with_fee(is_fee_private, rng, 0) { + Transaction::Execute(id, execution, _) => (id, execution), _ => unreachable!(), }; // Return the rejected execution. - Rejected::new_execution(*execution) + Rejected::new_execution(id, *execution) } /********************************************** Fee ***********************************************/ diff --git a/synthesizer/process/src/stack/deploy.rs b/synthesizer/process/src/stack/deploy.rs index 1515d8e30b..0e67d8cb8d 100644 --- a/synthesizer/process/src/stack/deploy.rs +++ b/synthesizer/process/src/stack/deploy.rs @@ -51,7 +51,7 @@ impl Stack { finish!(timer); // Return the deployment. - Deployment::new(*self.program_edition, self.program.clone(), verifying_keys, None, None) + Deployment::new(None, *self.program_edition, self.program.clone(), verifying_keys, None, None) } /// Checks each function in the program on the given verifying key and certificate. diff --git a/synthesizer/process/src/trace/mod.rs b/synthesizer/process/src/trace/mod.rs index 4c6fac7722..15d2a8494e 100644 --- a/synthesizer/process/src/trace/mod.rs +++ b/synthesizer/process/src/trace/mod.rs @@ -185,7 +185,7 @@ impl Trace { rng, )?; // Return the execution. - Execution::from(self.transitions.iter().cloned(), global_state_root, Some(proof)) + Execution::from(None, self.transitions.iter().cloned(), global_state_root, Some(proof)) } /// Returns a new fee with a proof, for the current inclusion assignment and global state root. diff --git a/synthesizer/process/src/verify_fee.rs b/synthesizer/process/src/verify_fee.rs index 9ac0516343..11e8838549 100644 --- a/synthesizer/process/src/verify_fee.rs +++ b/synthesizer/process/src/verify_fee.rs @@ -285,7 +285,7 @@ mod tests { for transaction in transactions { match transaction { - Transaction::Deploy(_, _, _, deployment, fee) => { + Transaction::Deploy(_, _, deployment, fee) => { // Compute the deployment ID. let deployment_id = deployment.to_deployment_id().unwrap(); // Verify the fee. @@ -293,7 +293,7 @@ mod tests { .verify_fee(ConsensusVersion::V8, VarunaVersion::V1, InclusionVersion::V0, &fee, deployment_id) .unwrap(); } - Transaction::Execute(_, _, execution, fee) => { + Transaction::Execute(_, execution, fee) => { // Compute the execution ID. let execution_id = execution.to_execution_id().unwrap(); // Verify the fee. diff --git a/synthesizer/src/vm/execute.rs b/synthesizer/src/vm/execute.rs index 667348e532..707fd50e1c 100644 --- a/synthesizer/src/vm/execute.rs +++ b/synthesizer/src/vm/execute.rs @@ -352,8 +352,8 @@ mod tests { assert_eq!(2914, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(1463, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -393,8 +393,8 @@ mod tests { assert_eq!(2970, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(1519, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -595,8 +595,8 @@ finalize test: assert_eq!(2867, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(1416, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -634,8 +634,8 @@ finalize test: assert_eq!(3759, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(2308, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -668,8 +668,8 @@ finalize test: assert_eq!(2871, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(1420, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -702,8 +702,8 @@ finalize test: assert_eq!(2891, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(1440, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -740,8 +740,8 @@ finalize test: assert_eq!(3571, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(2120, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -778,8 +778,8 @@ finalize test: assert_eq!(2232, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. - assert!(matches!(transaction, Transaction::Execute(_, _, _, _))); - if let Transaction::Execute(_, _, execution, _) = &transaction { + assert!(matches!(transaction, Transaction::Execute(_, _, _))); + if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); assert_eq!(2197, execution_size_in_bytes, "Update me if serialization has changed"); } @@ -936,7 +936,7 @@ finalize test: vm.add_next_block(&next_block).unwrap(); // Execute the parent program. - let Transaction::Execute(_, _, execution, _) = vm + let Transaction::Execute(_, execution, _) = vm .execute(&caller_private_key, ("parent.aleo", "test"), Vec::>::new().iter(), None, 0, None, rng) .unwrap() else { @@ -1067,7 +1067,7 @@ finalize test: } // Execute the program. - let Transaction::Execute(_, _, execution, _) = vm + let Transaction::Execute(_, execution, _) = vm .execute( &caller_private_key, (format!("test_{}.aleo", Transaction::::MAX_TRANSITIONS - 1), "test"), diff --git a/synthesizer/src/vm/finalize.rs b/synthesizer/src/vm/finalize.rs index 1a3fcb9520..3e2a18b1be 100644 --- a/synthesizer/src/vm/finalize.rs +++ b/synthesizer/src/vm/finalize.rs @@ -339,7 +339,7 @@ impl> VM { let outcome = match transaction { // The finalize operation here involves appending the 'stack', // and adding the program to the finalize tree. - Transaction::Deploy(_, _, program_owner, deployment, fee) => { + Transaction::Deploy(id, program_owner, deployment, fee) => { // Define the closure for processing a rejected deployment. let process_rejected_deployment = |fee: &Fee, @@ -351,7 +351,7 @@ impl> VM { Transaction::from_fee(fee.clone()).map(|fee_tx| (fee_tx, finalize)) }) .map(|(fee_tx, finalize)| { - let rejected = Rejected::new_deployment(*program_owner, deployment); + let rejected = Rejected::new_deployment(*id, *program_owner, deployment); ConfirmedTransaction::rejected_deploy(counter, fee_tx, rejected, finalize) .map_err(|e| e.to_string()) }) @@ -397,7 +397,7 @@ impl> VM { } // The finalize operation here involves calling 'update_key_value', // and update the respective leaves of the finalize tree. - Transaction::Execute(_, _, execution, fee) => { + Transaction::Execute(id, execution, fee) => { // Determine if the transaction is safe for execution, and proceed to execute it. match Self::prepare_for_execution(state, store, execution) .and_then(|_| process.finalize_execution(state, store, execution, fee.as_ref())) @@ -416,7 +416,7 @@ impl> VM { }) { Ok((fee_tx, finalize)) => { // Construct the rejected execution. - let rejected = Rejected::new_execution(*execution.clone()); + let rejected = Rejected::new_execution(*id, *execution.clone()); // Construct the rejected execute transaction. ConfirmedTransaction::rejected_execute(counter, fee_tx, rejected, finalize) .map_err(|e| e.to_string()) @@ -470,7 +470,7 @@ impl> VM { // Add the transition public keys to the set of produced transition public keys. tpks.extend(confirmed_transaction.transaction().transition_public_keys()); // Add the program owner to the set of deployment payers and the program ID to the set of deployments. - if let Transaction::Deploy(_, _, _, deployment, fee) = confirmed_transaction.transaction() { + if let Transaction::Deploy(_, _, deployment, fee) = confirmed_transaction.transaction() { fee.payer().map(|payer| deployment_payers.insert(payer)); deployments.insert(*deployment.program_id()); } @@ -633,7 +633,7 @@ impl> VM { ConfirmedTransaction::AcceptedDeploy(_, transaction, finalize) => { // Extract the deployment and fee from the transaction. let (deployment, fee) = match transaction { - Transaction::Deploy(_, _, _, deployment, fee) => (deployment, fee), + Transaction::Deploy(_, _, deployment, fee) => (deployment, fee), // Note: This will abort the entire atomic batch. _ => return Err("Expected deploy transaction".to_string()), }; @@ -660,7 +660,7 @@ impl> VM { ConfirmedTransaction::AcceptedExecute(_, transaction, finalize) => { // Extract the execution and fee from the transaction. let (execution, fee) = match transaction { - Transaction::Execute(_, _, execution, fee) => (execution, fee), + Transaction::Execute(_, execution, fee) => (execution, fee), // Note: This will abort the entire atomic batch. _ => return Err("Expected execute transaction".to_string()), }; @@ -868,7 +868,7 @@ impl> VM { } // If the transaction is a deployment, ensure that it is not another deployment in the block from the same public fee payer. - if let Transaction::Deploy(_, _, _, _, fee) = transaction { + if let Transaction::Deploy(_, _, _, fee) = transaction { // If any public deployment payer has already deployed in this block, abort the transaction. if let Some(payer) = fee.payer() { if deployment_payers.contains(&payer) { @@ -942,7 +942,7 @@ impl> VM { // Add the transition public keys to the set of produced transition public keys. tpks.extend(transaction.transition_public_keys()); // Add the program owner to the set of deployment payers and the program ID to the set of deployments. - if let Transaction::Deploy(_, _, _, deployment, fee) = transaction { + if let Transaction::Deploy(_, _, deployment, fee) = transaction { fee.payer().map(|payer| deployment_payers.insert(payer)); deployments.insert(*deployment.program_id()); } @@ -1699,10 +1699,10 @@ finalize transfer_public: finalize: &[FinalizeOperation], ) -> ConfirmedTransaction { match transaction { - Transaction::Execute(_, _, execution, fee) => ConfirmedTransaction::RejectedExecute( + Transaction::Execute(id, execution, fee) => ConfirmedTransaction::RejectedExecute( index, Transaction::from_fee(fee.clone().unwrap()).unwrap(), - Rejected::new_execution(*execution.clone()), + Rejected::new_execution(*id, *execution.clone()), finalize.to_vec(), ), _ => panic!("only reject execution transactions"), @@ -2401,12 +2401,12 @@ function ped_hash: // Ensure that the transaction is rejected. assert_eq!(confirmed_transactions.len(), 1); assert!(transaction.is_execute()); - if let Transaction::Execute(_, _, execution, fee) = transaction { + if let Transaction::Execute(id, execution, fee) = transaction { let fee_transaction = Transaction::from_fee(fee.unwrap()).unwrap(); let expected_confirmed_transaction = ConfirmedTransaction::RejectedExecute( 0, fee_transaction, - Rejected::new_execution(*execution), + Rejected::new_execution(id, *execution), vec![], ); diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 6f0cdd373c..711410d9b6 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -1547,7 +1547,7 @@ function do: let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); // Destructure the deployment transaction. - let Transaction::Deploy(_, _, program_owner, deployment, fee) = transaction else { + let Transaction::Deploy(_, program_owner, deployment, fee) = transaction else { panic!("Expected a deployment transaction"); }; @@ -1560,17 +1560,18 @@ function do: vks_with_overreport.push((*id, (vk, cert.clone()))); } + let deployment_id = deployment.as_ref().to_deployment_id().unwrap(); + // Each additional constraint costs 25 microcredits, so we need to increase the fee by 25 microcredits. let required_fee = *fee.base_amount().unwrap() + 25; // Authorize a new fee. - let fee_authorization = vm - .authorize_fee_public(&private_key, required_fee, 0, deployment.as_ref().to_deployment_id().unwrap(), rng) - .unwrap(); + let fee_authorization = vm.authorize_fee_public(&private_key, required_fee, 0, deployment_id, rng).unwrap(); // Compute the fee. let fee = vm.execute_fee_authorization(fee_authorization, None, rng).unwrap(); // Create a new deployment transaction with the overreported verifying keys. let adjusted_deployment = Deployment::new( + Some(deployment_id), deployment.edition(), deployment.program().clone(), vks_with_overreport, @@ -1617,7 +1618,7 @@ function do: let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); // Destructure the deployment transaction. - let Transaction::Deploy(txid, _, program_owner, deployment, fee) = transaction else { + let Transaction::Deploy(txid, program_owner, deployment, fee) = transaction else { panic!("Expected a deployment transaction"); }; @@ -1632,6 +1633,7 @@ function do: // Create a new deployment transaction with the underreported verifying keys. let adjusted_deployment = Deployment::new( + None, deployment.edition(), deployment.program().clone(), vks_with_underreport, @@ -1639,9 +1641,7 @@ function do: deployment.program_owner(), ) .unwrap(); - let deployment_id = adjusted_deployment.to_deployment_id().unwrap(); - let adjusted_transaction = - Transaction::Deploy(txid, deployment_id, program_owner, Box::new(adjusted_deployment), fee); + let adjusted_transaction = Transaction::Deploy(txid, program_owner, Box::new(adjusted_deployment), fee); // Verify the deployment transaction. It should error when enforcing the first constraint over the vk limit. let result = vm.check_transaction(&adjusted_transaction, None, rng); @@ -1700,7 +1700,7 @@ function do: let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); // Destructure the deployment transaction. - let Transaction::Deploy(txid, _, program_owner, deployment, fee) = transaction else { + let Transaction::Deploy(txid, program_owner, deployment, fee) = transaction else { panic!("Expected a deployment transaction"); }; @@ -1713,6 +1713,7 @@ function do: // Create a new deployment transaction with the underreported verifying keys. let adjusted_deployment = Deployment::new( + None, deployment.edition(), deployment.program().clone(), vks_with_underreport, @@ -1720,9 +1721,7 @@ function do: deployment.program_owner(), ) .unwrap(); - let deployment_id = adjusted_deployment.to_deployment_id().unwrap(); - let adjusted_transaction = - Transaction::Deploy(txid, deployment_id, program_owner, Box::new(adjusted_deployment), fee); + let adjusted_transaction = Transaction::Deploy(txid, program_owner, Box::new(adjusted_deployment), fee); // Verify the deployment transaction. It should error when synthesizing the first variable over the vk limit. let result = vm.check_transaction(&adjusted_transaction, None, rng); @@ -2657,7 +2656,7 @@ finalize transfer_public_to_private: ) .unwrap(); let proof = Proof::::from_str(execution.get("proof").unwrap().as_str().unwrap()).unwrap(); - let new_execution = Execution::from(new_transitions.into_iter(), global_state_root, Some(proof)).unwrap(); + let new_execution = Execution::from(None, new_transitions.into_iter(), global_state_root, Some(proof)).unwrap(); let authorization = vm .authorize_fee_public(&caller_private_key, 10_000_000, 0, new_execution.to_execution_id().unwrap(), rng) .unwrap(); @@ -2921,7 +2920,7 @@ function add_thrice: ) .unwrap(); let proof = Proof::::from_str(execution.get("proof").unwrap().as_str().unwrap()).unwrap(); - let new_execution = Execution::from(new_transitions.into_iter(), global_state_root, Some(proof)).unwrap(); + let new_execution = Execution::from(None, new_transitions.into_iter(), global_state_root, Some(proof)).unwrap(); let authorization = vm .authorize_fee_public(&caller_private_key, 10_000_000, 0, new_execution.to_execution_id().unwrap(), rng) .unwrap(); diff --git a/synthesizer/src/vm/tests/test_v8.rs b/synthesizer/src/vm/tests/test_v8.rs index 10beb624df..3a3226da28 100644 --- a/synthesizer/src/vm/tests/test_v8.rs +++ b/synthesizer/src/vm/tests/test_v8.rs @@ -387,6 +387,7 @@ function dummy: let deployment_0 = transaction_0.deployment().expect("Expected a deployment transaction"); // Generate a second deployment transaction with an incremented edition and a different fee. let deployment_1 = Deployment::new( + None, deployment_0.edition() + 1, deployment_0.program().clone(), deployment_0.verifying_keys().clone(), @@ -432,6 +433,7 @@ function dummy: // Redeploy the program with the other private key, using the original deployment. let deployment = Deployment::new( + None, 1, program.clone(), deployment_0.verifying_keys().clone(), diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index 6471ea9960..a78473244d 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -180,14 +180,17 @@ impl> VM { // Next, verify the deployment or execution. match transaction { - Transaction::Deploy(id, deployment_id, owner, deployment, _) => { + Transaction::Deploy(id, owner, deployment, _) => { // Sanity check that the program is not `credits.aleo`. ensure!( deployment.program_id() != &ProgramID::from_str("credits.aleo")?, "Cannot deploy 'credits.aleo'" ); // Verify the signature corresponds to the transaction ID. - ensure!(owner.verify(*deployment_id), "Invalid owner signature for deployment transaction '{id}'"); + ensure!( + owner.verify(deployment.to_deployment_id()?), + "Invalid owner signature for deployment transaction '{id}'" + ); // If the `CONSENSUS_VERSION` is less than `V8`, ensure that // - the deployment edition is zero. // If the `CONSENSUS_VERSION` is less than `V9` ensure that @@ -377,9 +380,9 @@ impl> VM { } } } - Transaction::Execute(id, execution_id, execution, _) => { + Transaction::Execute(id, execution, _) => { // Ensure the execution was not previously rejected (replay attack prevention). - if self.block_store().contains_rejected_deployment_or_execution_id(execution_id)? { + if self.block_store().contains_rejected_deployment_or_execution_id(&execution.to_execution_id()?)? { bail!("Transaction '{id}' contains a previously rejected execution") } // Verify the execution. @@ -412,7 +415,7 @@ impl> VM { let current_height = self.block_store().current_block_height(); let consensus_version = N::CONSENSUS_VERSION(current_height)?; match transaction { - Transaction::Deploy(id, deployment_id, _, deployment, fee) => { + Transaction::Deploy(id, _, deployment, fee) => { // Ensure the rejected ID is not present. ensure!(rejected_id.is_none(), "Transaction '{id}' should not have a rejected ID (deployment)"); // Compute the minimum deployment cost. @@ -422,9 +425,9 @@ impl> VM { bail!("Transaction '{id}' has an insufficient base fee (deployment) - requires {cost} microcredits") } // Verify the fee. - self.check_fee_internal(fee, *deployment_id, is_partially_verified)?; + self.check_fee_internal(fee, deployment.to_deployment_id()?, is_partially_verified)?; } - Transaction::Execute(id, execution_id, execution, fee) => { + Transaction::Execute(id, execution, fee) => { // Ensure the rejected ID is not present. ensure!(rejected_id.is_none(), "Transaction '{id}' should not have a rejected ID (execution)"); // If the transaction contains only 1 transition, and the transition is a split or upgrade, then the fee can be skipped. @@ -459,7 +462,7 @@ impl> VM { ensure!(*fee.base_amount()? == 0, "Transaction '{id}' has a non-zero base fee (execution)"); } // Verify the fee. - self.check_fee_internal(fee, *execution_id, is_partially_verified)?; + self.check_fee_internal(fee, execution.to_execution_id()?, is_partially_verified)?; } else { // Ensure the fee can be safely skipped. ensure!(!is_fee_required, "Transaction '{id}' is missing a fee (execution)"); @@ -763,7 +766,7 @@ mod tests { for transaction in transactions { match transaction { - Transaction::Execute(_, _, execution, _) => { + Transaction::Execute(_, execution, _) => { // Ensure the proof exists. assert!(execution.proof().is_some()); // Verify the execution. @@ -800,7 +803,7 @@ mod tests { for transaction in transactions { match transaction { - Transaction::Execute(_, _, execution, Some(fee)) => { + Transaction::Execute(_, execution, Some(fee)) => { let execution_id = execution.to_execution_id().unwrap(); // Ensure the proof exists. @@ -1042,6 +1045,7 @@ function compute: // Construct the mutated execution. let mutated_execution = Execution::from( + None, [mutated_transition].into_iter(), execution.global_state_root(), execution.proof().cloned(),