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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added the `miden::standards::assets::non_fungible_asset::validate` MASM procedure, which validates a non-fungible asset's composition and the binding of its value to the asset class, and used it in the `NonFungibleFaucet` burn procedure ([#3308](https://github.com/0xMiden/protocol/pull/3308)).

### Changes
- Introduced the `DoubleWord` newtype (8 `Felt`s) and used it internally to replace ad-hoc `[Felt; 8]` / `Vec<Felt>` representations ([#3319](https://github.com/0xMiden/protocol/pull/3319)).

- [BREAKING] Transaction fees are now paid by the authentication procedure creating a public TX_FEE note before the transaction summary is created, so the fee payment is covered by the signature (`miden::standards::fee`). The payment asset and conversion rate are committed to via the auth args (see `FeeConversionInfo`); on zero-base-fee chains no note is created ([#2899](https://github.com/0xMiden/protocol/discussions/2899)).

Expand Down
14 changes: 6 additions & 8 deletions crates/miden-agglayer/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use alloc::vec;
use alloc::vec::Vec;

use miden_core::{Felt, ONE, Word, ZERO};
use miden_protocol::DoubleWord;
use miden_protocol::account::component::{AccountComponentCode, AccountComponentMetadata};
use miden_protocol::account::{
Account,
Expand Down Expand Up @@ -467,9 +468,9 @@ impl AggLayerBridge {
Self::assert_bridge_account(bridge_account)?;

// Compute the expected GER hash: poseidon2::merge(GER_LOWER, GER_UPPER)
let ger_lower: Word = ger.to_elements()[0..4].try_into().unwrap();
let ger_upper: Word = ger.to_elements()[4..8].try_into().unwrap();
let ger_hash = Poseidon2::merge(&[ger_lower, ger_upper]);
let ger_words = DoubleWord::try_from(ger.to_elements().as_slice())
.expect("exit root should contain 8 felts");
let ger_hash = Poseidon2::merge(&[ger_words.lo(), ger_words.hi()]);

// Get the value stored by the GER hash. If this GER was registered, the value would be
// equal to [1, 0, 0, 0]
Expand Down Expand Up @@ -514,11 +515,8 @@ impl AggLayerBridge {
.get_item(root_hi_slot)
.expect("should be able to read LET root hi");

let mut root = Vec::with_capacity(8);
root.extend(root_lo.to_vec());
root.extend(root_hi.to_vec());

Ok(root)
let dword = DoubleWord::new(root_lo, root_hi);
Ok(dword.to_vec())
}

/// Returns the number of leaves in the Local Exit Tree (LET) frontier.
Expand Down
29 changes: 10 additions & 19 deletions crates/miden-protocol/src/account/storage/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use alloc::vec::Vec;

use super::map::EMPTY_STORAGE_MAP_ROOT;
use super::{AccountStorage, Felt, StorageSlotType, Word};
use crate::ZERO;
use crate::DoubleWord;
use crate::account::{StorageSlot, StorageSlotId, StorageSlotName};
use crate::crypto::SequentialCommit;
use crate::errors::AccountError;
Expand Down Expand Up @@ -215,7 +215,7 @@ impl SequentialCommit for AccountStorageHeader {
type Commitment = Word;

fn to_elements(&self) -> Vec<Felt> {
self.slots().flat_map(|slot| slot.to_elements()).collect()
self.slots().flat_map(|slot| slot.to_dword()).collect()
}
}

Expand Down Expand Up @@ -297,23 +297,14 @@ impl StorageSlotHeader {
self.value
}

/// Returns this storage slot header as field elements.
/// Returns this storage slot header as a [`DoubleWord`].
///
/// This is done by converting this storage slot into 8 field elements as follows:
/// ```text
/// [[0, slot_type, slot_id_suffix, slot_id_prefix], SLOT_VALUE]
/// ```
pub(crate) fn to_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS] {
/// The low word is `[0, slot_type, slot_id_suffix, slot_id_prefix]` and the high word is the
/// slot value.
pub(crate) fn to_dword(&self) -> DoubleWord {
let id = self.id();
let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS];
elements[0..4].copy_from_slice(&[
Felt::ZERO,
self.r#type.as_felt(),
id.suffix(),
id.prefix(),
]);
elements[4..8].copy_from_slice(self.value.as_elements());
elements
let header_word = Word::new([Felt::ZERO, self.r#type.as_felt(), id.suffix(), id.prefix()]);
DoubleWord::new(header_word, self.value)
}
}

Expand Down Expand Up @@ -503,15 +494,15 @@ mod tests {
);

// Serialize the single slot to elements
let elements = slot1.to_elements();
let elements = slot1.to_dword();

// Create slot names map using the slot's ID
let mut slot_names = BTreeMap::new();
slot_names.insert(slot1.id(), slot_name1.clone());

// Test from_elements with provided slot names on raw slot elements.
let reconstructed_header =
AccountStorageHeader::try_from_elements(&elements, &slot_names).unwrap();
AccountStorageHeader::try_from_elements(elements.as_elements(), &slot_names).unwrap();

// Verify that the original slot names are preserved.
assert_eq!(reconstructed_header.slots().count(), 1);
Expand Down
2 changes: 1 addition & 1 deletion crates/miden-protocol/src/account/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ impl SequentialCommit for AccountStorage {
slot.content().slot_type(),
slot.content().value(),
)
.to_elements()
.to_dword()
})
.collect()
}
Expand Down
8 changes: 3 additions & 5 deletions crates/miden-protocol/src/asset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::utils::serde::{
DeserializationError,
Serializable,
};
use super::{Felt, Word};
use super::{DoubleWord, Felt, Word};
use crate::account::AccountId;

mod asset_amount;
Expand Down Expand Up @@ -182,10 +182,8 @@ impl Asset {
/// The first four elements contain the asset ID and the last four elements contain the asset
/// value.
pub fn as_elements(&self) -> [Felt; 8] {
let mut elements = [Felt::ZERO; 8];
elements[0..4].copy_from_slice(self.to_id_word().as_elements());
elements[4..8].copy_from_slice(self.to_value_word().as_elements());
elements
let dword = DoubleWord::new(self.to_id_word(), self.to_value_word());
dword.into()
}

/// Returns the inner [`FungibleAsset`].
Expand Down
11 changes: 3 additions & 8 deletions crates/miden-protocol/src/batch/kernel.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use alloc::vec::Vec;

use miden_core::program::Kernel;

use crate::batch::{BatchId, ProposedBatch};
use crate::utils::serde::Deserializable;
use crate::utils::sync::LazyLock;
use crate::vm::{AdviceInputs, Package, Program, ProgramInfo, StackInputs};
use crate::{Felt, Word};
use crate::{DoubleWord, Word};

// CONSTANTS
// ================================================================================================
Expand Down Expand Up @@ -75,11 +73,8 @@ impl BatchKernel {
/// - `BLOCK_COMMITMENT` is the commitment of the batch's reference block.
/// - `BATCH_ID` is the batch's [`BatchId`].
pub fn build_input_stack(block_commitment: Word, batch_id: BatchId) -> StackInputs {
let mut inputs: Vec<Felt> = Vec::with_capacity(8);
inputs.extend_from_slice(block_commitment.as_elements());
inputs.extend_from_slice(batch_id.as_word().as_elements());

StackInputs::new(&inputs).expect("number of stack inputs should be <= 16")
let inputs = DoubleWord::new(block_commitment, batch_id.as_word());
StackInputs::new(inputs.as_elements()).expect("number of stack inputs should be <= 16")
}

// ADVICE BUILDER
Expand Down
2 changes: 2 additions & 0 deletions crates/miden-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod note;
pub mod package;
mod protocol;
pub mod transaction;
pub mod types;

#[cfg(any(feature = "testing", test))]
pub mod testing;
Expand All @@ -34,6 +35,7 @@ pub use miden_crypto::hash::poseidon2::Poseidon2 as Hasher;
pub use miden_crypto::word;
pub use miden_crypto::word::{Word, WordError};
pub use protocol::ProtocolLib;
pub use types::DoubleWord;

pub mod assembly {
pub use miden_assembly::ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName};
Expand Down
22 changes: 6 additions & 16 deletions crates/miden-protocol/src/transaction/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::vm::{
StackInputs,
StackOutputs,
};
use crate::{Felt, Hasher, Word};
use crate::{DoubleWord, Felt, Hasher, Word};

mod procedures {
include!(concat!(env!("OUT_DIR"), "/procedures.rs"));
Expand Down Expand Up @@ -431,23 +431,13 @@ impl TransactionKernel {
)
})?;

if account_update_data.len() != 8 {
return Err(TransactionOutputError::AccountUpdateCommitment(
let account_update = DoubleWord::try_from(account_update_data.as_ref()).map_err(|_| {
TransactionOutputError::AccountUpdateCommitment(
"expected account update commitment advice map entry to contain exactly 8 elements"
.into(),
));
}

// SAFETY: We just asserted that the data is of length 8 so slicing the data into two words
// is fine.
let final_account_commitment = Word::from(
<[Felt; 4]>::try_from(&account_update_data[0..4])
.expect("we should have sliced off exactly four elements"),
);
let account_patch_commitment = Word::from(
<[Felt; 4]>::try_from(&account_update_data[4..8])
.expect("we should have sliced off exactly four elements"),
);
)
})?;
let (final_account_commitment, account_patch_commitment) = account_update.into_tuple();

let computed_account_update_commitment =
Hasher::merge(&[final_account_commitment, account_patch_commitment]);
Expand Down
Loading
Loading