Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions algorithms/benches/snark/varuna.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.


#[macro_use]
extern crate criterion;

Expand Down
4 changes: 2 additions & 2 deletions ledger/block/src/transaction/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<N: Network> ToBytes for Transaction<N> {
// 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.
Expand All @@ -111,7 +111,7 @@ impl<N: Network> ToBytes for Transaction<N> {
// 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.
Expand Down
18 changes: 17 additions & 1 deletion ledger/block/src/transaction/deployment/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ use super::*;
impl<N: Network> FromBytes for Deployment<N> {
/// Reads the deployment from a buffer.
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
// Read the id variant.
let id_variant = u8::read_le(&mut reader)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be made backwards compatible?

// Read the deployment id.
let id = match id_variant {
0 => None,
1 => Some(DeploymentID::<N>::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,
Expand Down Expand Up @@ -82,14 +90,22 @@ impl<N: Network> FromBytes for Deployment<N> {
};

// 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}")))
}
}

impl<N: Network> ToBytes for Deployment<N> {
/// Writes the deployment to a buffer.
fn write_le<W: Write>(&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)?;
Expand Down
25 changes: 22 additions & 3 deletions ledger/block/src/transaction/deployment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -30,6 +32,8 @@ use snarkvm_synthesizer_snark::{Certificate, VerifyingKey};

#[derive(Clone)]
pub struct Deployment<N: Network> {
/// The deployment id
id: OnceLock<DeploymentID<N>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a design be possible where this is just an Option<...> like we did for the previous similar caching specified in the issue?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would require mutable constructs to deployment all over the code base as when we call to_deployment_id it's probably best to cache the id if not computed yet once, this also implies a redundant recursive chain of callers that will have to take an &mut. We could probably do a RefCell though?

/// The edition.
edition: u16,
/// The program.
Expand Down Expand Up @@ -59,14 +63,20 @@ impl<N: Network> Eq for Deployment<N> {}
impl<N: Network> Deployment<N> {
/// Initializes a new deployment.
pub fn new(
deployment_id: Option<DeploymentID<N>>,
edition: u16,
program: Program<N>,
verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
program_checksum: Option<[U8<N>; 32]>,
program_owner: Option<Address<N>>,
) -> Result<Self> {
// 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.
Expand Down Expand Up @@ -206,7 +216,14 @@ impl<N: Network> Deployment<N> {

/// Returns the deployment ID.
pub fn to_deployment_id(&self) -> Result<Field<N>> {
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)
}
}

Expand Down Expand Up @@ -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::<CurrentNetwork>::new(
None,
edition % 2,
deployment.program().clone(),
deployment.verifying_keys().clone(),
Expand Down Expand Up @@ -340,6 +358,7 @@ function compute:
.clone();
// Create a new deployment with the desired edition.
Deployment::<CurrentNetwork>::new(
None,
edition,
deployment.program().clone(),
deployment.verifying_keys().clone(),
Expand Down
8 changes: 7 additions & 1 deletion ledger/block/src/transaction/deployment/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ impl<N: Network> Serialize for Deployment<N> {
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)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not backwards compatible.

}
deployment.serialize_field("edition", &self.edition)?;
deployment.serialize_field("program", &self.program)?;
deployment.serialize_field("verifying_keys", &self.verifying_keys)?;
Expand All @@ -52,6 +56,8 @@ impl<'de, N: Network> Deserialize<'de> for Deployment<N> {

// 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::<D>(&mut deployment, "edition")?,
// Retrieve the program.
Expand Down
23 changes: 21 additions & 2 deletions ledger/block/src/transaction/execution/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ use super::*;
impl<N: Network> FromBytes for Execution<N> {
/// Reads the execution from a buffer.
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
// 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::<N>::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.
Expand Down Expand Up @@ -51,14 +59,25 @@ impl<N: Network> FromBytes for Execution<N> {
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<N: Network> ToBytes for Execution<N> {
/// Writes the execution to a buffer.
fn write_le<W: Write>(&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.
Expand Down
32 changes: 27 additions & 5 deletions ledger/block/src/transaction/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ mod bytes;
mod serialize;
mod string;

use crate::{Transaction, Transition};
use std::sync::OnceLock;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move down to line 26 so external dependencies are in the same place


use crate::{Transaction, Transition, transaction::ExecutionID};
use console::{account::Field, network::prelude::*, program::ProgramID};
use snarkvm_synthesizer_snark::Proof;

use indexmap::IndexMap;

#[derive(Clone, Default, PartialEq, Eq)]
pub struct Execution<N: Network> {
/// The execution id
id: OnceLock<ExecutionID<N>>,
/// The transitions.
transitions: IndexMap<N::TransitionID, Transition<N>>,
/// The global state root.
Expand All @@ -36,17 +40,28 @@ pub struct Execution<N: Network> {
impl<N: Network> Execution<N> {
/// 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<ExecutionID<N>>,
transitions: impl Iterator<Item = Transition<N>>,
global_state_root: N::StateRoot,
proof: Option<Proof<N>>,
) -> Result<Self> {
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.
Expand All @@ -70,7 +85,14 @@ impl<N: Network> Execution<N> {

/// Returns the execution ID.
pub fn to_execution_id(&self) -> Result<Field<N>> {
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)
}
}

Expand Down Expand Up @@ -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")
Expand Down
16 changes: 14 additions & 2 deletions ledger/block/src/transaction/execution/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ impl<N: Network> Serialize for Execution<N> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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::<Vec<&Transition<N>>>())?;
execution.serialize_field("global_state_root", &self.global_state_root)?;
Expand All @@ -41,6 +47,9 @@ impl<'de, N: Network> Deserialize<'de> for Execution<N> {
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::<D>(&mut execution, "transitions")?;
// Retrieve the global state root.
Expand All @@ -50,7 +59,10 @@ impl<'de, N: Network> Deserialize<'de> for Execution<N> {
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::<Self>::deserialize_with_size_encoding(deserializer, "execution"),
}
Expand Down
18 changes: 13 additions & 5 deletions ledger/block/src/transaction/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<N: Network> Transaction<N> {
/// Returns the Merkle leaf for the given ID of a function or transition in the transaction.
pub fn to_leaf(&self, id: &Field<N>) -> Result<TransactionLeaf<N>> {
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.
Expand All @@ -48,7 +48,7 @@ impl<N: Network> Transaction<N> {
// 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() {
Expand Down Expand Up @@ -92,11 +92,11 @@ impl<N: Network> Transaction<N> {
pub fn to_tree(&self) -> Result<TransactionTree<N>> {
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.
Expand Down Expand Up @@ -252,8 +252,16 @@ impl<N: Network> Transaction<N> {
pub fn deployment_tree_v2(deployment: &Deployment<N>) -> Result<DeploymentTree<N>> {
// 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.
Expand Down
Loading