Skip to content

Commit

Permalink
Merge pull request #362 from 0xPolygonMiden/bobbin-tx-id
Browse files Browse the repository at this point in the history
Add `TransactionId` struct
  • Loading branch information
bobbinth authored Dec 15, 2023
2 parents 3a91399 + 4a73cee commit a31c95b
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 2 deletions.
2 changes: 2 additions & 0 deletions objects/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod executed_tx;
mod prepared_tx;
mod proven_tx;
mod script;
mod transaction_id;
mod tx_result;
mod tx_witness;
#[cfg(not(feature = "testing"))]
Expand All @@ -29,6 +30,7 @@ pub use executed_tx::ExecutedTransaction;
pub use prepared_tx::PreparedTransaction;
pub use proven_tx::ProvenTransaction;
pub use script::TransactionScript;
pub use transaction_id::TransactionId;
pub use tx_result::TransactionResult;
pub use tx_witness::TransactionWitness;

Expand Down
9 changes: 7 additions & 2 deletions objects/src/transaction/proven_tx.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::{AccountId, ConsumedNoteInfo, Digest, NoteEnvelope, Vec};

use super::{AccountId, ConsumedNoteInfo, Digest, NoteEnvelope, TransactionId, Vec};
use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
use miden_verifier::ExecutionProof;
use vm_processor::DeserializationError;
Expand Down Expand Up @@ -54,6 +53,12 @@ impl ProvenTransaction {

// ACCESSORS
// --------------------------------------------------------------------------------------------

/// Returns unique identifier of this transaction.
pub fn id(&self) -> TransactionId {
self.into()
}

/// Returns the account ID.
pub fn account_id(&self) -> AccountId {
self.account_id
Expand Down
147 changes: 147 additions & 0 deletions objects/src/transaction/transaction_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use super::{
Digest, Felt, Hasher, ProvenTransaction, TransactionResult, Vec, Word, WORD_SIZE, ZERO,
};
use crate::utils::serde::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
};

// TRANSACTION ID
// ================================================================================================

/// A unique identifier of a transaction.
///
/// Transaction ID is computed as:
///
/// hash(init_account_hash, final_account_hash, input_notes_hash, output_notes_hash)
///
/// This achieves the following properties:
/// - Transactions are identical if and only if they have the same ID.
/// - Computing transaction ID can be done solely from public transaction data.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TransactionId(Digest);

impl TransactionId {
/// Returns a new [TransactionId] instantiated from the provided transaction components.
pub fn new(
init_account_hash: Digest,
final_account_hash: Digest,
input_notes_hash: Digest,
output_notes_hash: Digest,
) -> Self {
let mut elements = [ZERO; 4 * WORD_SIZE];
elements[..4].copy_from_slice(init_account_hash.as_elements());
elements[4..8].copy_from_slice(final_account_hash.as_elements());
elements[8..12].copy_from_slice(input_notes_hash.as_elements());
elements[12..].copy_from_slice(output_notes_hash.as_elements());
Self(Hasher::hash_elements(&elements))
}

/// Returns the elements of this transaction ID.
pub fn as_elements(&self) -> &[Felt] {
self.0.as_elements()
}

/// Returns the digest defining this transaction ID.
pub fn inner(&self) -> Digest {
self.0
}
}

// CONVERSIONS INTO TRANSACTION ID
// ================================================================================================

impl From<&ProvenTransaction> for TransactionId {
fn from(tx: &ProvenTransaction) -> Self {
// TODO: move input/output note hash computations into a more central location
let input_notes_hash = {
let mut elements: Vec<Felt> = Vec::with_capacity(tx.consumed_notes().len() * 8);
for note in tx.consumed_notes().iter() {
elements.extend_from_slice(note.nullifier().as_elements());
elements.extend_from_slice(note.script_root().as_elements());
}
Hasher::hash_elements(&elements)
};
let output_notes_hash = {
let mut elements: Vec<Felt> = Vec::with_capacity(tx.created_notes().len() * 8);
for note in tx.created_notes().iter() {
elements.extend_from_slice(note.note_hash().as_elements());
elements.extend_from_slice(&Word::from(note.metadata()));
}
Hasher::hash_elements(&elements)
};
Self::new(
tx.initial_account_hash(),
tx.final_account_hash(),
input_notes_hash,
output_notes_hash,
)
}
}

impl From<&TransactionResult> for TransactionId {
fn from(tx: &TransactionResult) -> Self {
let input_notes_hash = tx.consumed_notes().commitment();
let output_notes_hash = tx.created_notes().commitment();
Self::new(
tx.initial_account_hash(),
tx.final_account_hash(),
input_notes_hash,
output_notes_hash,
)
}
}

impl From<Word> for TransactionId {
fn from(value: Word) -> Self {
Self(value.into())
}
}

impl From<Digest> for TransactionId {
fn from(value: Digest) -> Self {
Self(value)
}
}

// CONVERSIONS FROM TRANSACTION ID
// ================================================================================================

impl From<TransactionId> for Word {
fn from(id: TransactionId) -> Self {
id.0.into()
}
}

impl From<TransactionId> for [u8; 32] {
fn from(id: TransactionId) -> Self {
id.0.into()
}
}

impl From<&TransactionId> for Word {
fn from(id: &TransactionId) -> Self {
id.0.into()
}
}

impl From<&TransactionId> for [u8; 32] {
fn from(id: &TransactionId) -> Self {
id.0.into()
}
}

// SERIALIZATION
// ================================================================================================

impl Serializable for TransactionId {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.0.to_bytes());
}
}

impl Deserializable for TransactionId {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let id = Digest::read_from(source)?;
Ok(Self(id))
}
}

0 comments on commit a31c95b

Please sign in to comment.