Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce AccountIdBuilder #1045

Merged
merged 11 commits into from
Jan 8, 2025
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Added tracing to the `miden-tx-prover` CLI (#1014).
- Added metrics to the `miden-tx-prover` proxy (#1017).
- Implemented `to_hex` for `AccountIdPrefix` and `epoch_block_num` for `BlockHeader` (#1039).
- Introduce `AccountIdBuilder` to simplify `AccountId` generation in tests (#1045).

## 0.6.2 (2024-11-20)

Expand Down
12 changes: 6 additions & 6 deletions miden-lib/asm/kernels/transaction/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ end
export.account_vault_get_balance
# get the vault root
exec.memory::get_acct_vault_root_ptr movdn.2
# => [faucet_id_hi, faucet_id_lo, acct_vault_root_ptr, pad(13)]
# => [faucet_id_hi, faucet_id_lo, acct_vault_root_ptr, pad(14)]

# get the asset balance
exec.asset_vault::get_balance
Expand Down Expand Up @@ -570,7 +570,7 @@ export.get_note_sender
# => [sender_hi, sender_lo, pad(16)]

# truncate the stack
swapw dropw
movup.2 drop movup.2 drop
# => [sender_hi, sender_lo, pad(14)]
end

Expand Down Expand Up @@ -895,7 +895,7 @@ end
#! - foreign_account_id_{hi,lo} are the first and second felt of the ID of the foreign account
#! whose procedure is going to be executed.
#! - FOREIGN_ACCOUNT_ID is the word constructed from the foreign_account_id as follows:
#! [foreign_account_id_hi, foreign_account_lo, 0, 0].
#! [foreign_account_lo, foreign_account_id_hi, 0, 0].
#! - account_nonce is the nonce of the foreign account.
#! - VAULT_ROOT is the commitment of the foreign account's vault.
#! - STORAGE_ROOT is the commitment of the foreign account's storage.
Expand Down Expand Up @@ -926,12 +926,12 @@ export.start_foreign_context
# OS => [foreign_account_id_hi, foreign_account_id_lo, pad(14)]

# construct the word with account ID to load the core account data from the advice map
swap push.0.0
# OS => [0, 0, foreign_account_id_lo, foreign_account_id_hi, pad(14)]
push.0.0
# OS => [0, 0, foreign_account_id_hi, foreign_account_id_lo, pad(14)]

# move the core account data to the advice stack
adv.push_mapval
# OS => [0, 0, foreign_account_id_lo, foreign_account_id_hi, pad(14)]
# OS => [0, 0, foreign_account_id_hi, foreign_account_id_lo, pad(14)]
# AS => [[foreign_account_id_hi, foreign_account_lo, 0, account_nonce], VAULT_ROOT, STORAGE_ROOT, CODE_ROOT]

# store the id and nonce of the foreign account to the memory
Expand Down
10 changes: 5 additions & 5 deletions miden-lib/asm/kernels/transaction/lib/account.masm
Original file line number Diff line number Diff line change
Expand Up @@ -736,11 +736,11 @@ end
#! Validates that the account seed, provided via the advice map, satisfies the seed requirements.
#!
#! Validation is performed via the following steps:
#! 1. Compute the hash of (SEED, CODE_COMMITMENT, STORAGE_COMMITMENT, ANCHOR_BLOCK_HASH).
#! 2. Assert the least significant element of the digest is equal to the account id of the account
#! the transaction is being executed against.
#! 3. Assert the most significant element has sufficient proof of work (trailing zeros) for the
#! account type the transaction is being executed against.
#! 1. Retrieve the anchor block hash by computing the block number of the anchor block and
#! retrieving it from the chain mmr.
#! 2. Compute the hash of (SEED, CODE_COMMITMENT, STORAGE_COMMITMENT, ANCHOR_BLOCK_HASH).
#! 3. Assert the two least significant elements of the digest are equal to the account id of the
#! account the transaction is being executed against.
#!
#! Inputs: []
#! Outputs: []
Expand Down
5 changes: 1 addition & 4 deletions miden-lib/asm/kernels/transaction/lib/asset_vault.masm
Original file line number Diff line number Diff line change
Expand Up @@ -432,10 +432,7 @@ end
#! - ASSET is the non-fungible asset for which the vault key is built.
#! - ASSET_KEY is the vault key of the non-fungible asset.
proc.build_non_fungible_asset_vault_key
# Create the asset key from the non-fungible asset.
# ---------------------------------------------------------------------------------------------

# swap hash0 with faucet id
# create the asset key from the non-fungible asset by swapping hash0 with the faucet id
# => [faucet_id_hi, hash2, hash1, hash0]
swap.3
# => [hash0, hash2, hash1 faucet_id_hi]
Expand Down
4 changes: 2 additions & 2 deletions miden-lib/src/notes/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ mod tests {

let offered_asset = Asset::Fungible(
FungibleAsset::new(
AccountId::new_dummy(
AccountId::dummy(
fungible_faucet_id_bytes,
AccountType::FungibleFaucet,
AccountStorageMode::Public,
Expand All @@ -89,7 +89,7 @@ mod tests {
let requested_asset = Asset::NonFungible(
NonFungibleAsset::new(
&NonFungibleAssetDetails::new(
AccountId::new_dummy(
AccountId::dummy(
non_fungible_faucet_id_bytes,
AccountType::NonFungibleFaucet,
AccountStorageMode::Public,
Expand Down
3 changes: 2 additions & 1 deletion miden-lib/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ impl TransactionKernel {
let account_id = account_header.id();
let storage_root = account_header.storage_commitment();
let code_root = account_header.code_commitment();
// Note: keep in sync with the start_foreign_context kernel procedure
let account_key =
Digest::from([account_id.first_felt(), account_id.second_felt(), ZERO, ZERO]);
Digest::from([account_id.second_felt(), account_id.first_felt(), ZERO, ZERO]);
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

// Extend the advice inputs with the new data
advice_inputs.extend_map([
Expand Down
4 changes: 2 additions & 2 deletions miden-lib/src/transaction/procedures/kernel_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub const KERNEL0_PROCEDURES: [Digest; 33] = [
// get_note_inputs_hash
digest!("0xe6209e99b726e1ad25b89e712b30bcfa3bad45feb47b81dc51a650b02b0dcbda"),
// get_note_sender
digest!("0x9dfb0725ccb6c6f3a5c84bc11cc36e12da9631c1ae5eca40a1348fa5a97df80c"),
digest!("0xf5056720c0e58a9e3e1018d320bc04cd5c4a82ecb4563357f91688e03fe1b6cc"),
// get_note_serial_number
digest!("0xad91130ec219756213c6cadeaf8a38de8768e50c620cb7c347c531874c6054b6"),
// get_script_hash
Expand All @@ -66,7 +66,7 @@ pub const KERNEL0_PROCEDURES: [Digest; 33] = [
// get_block_number
digest!("0x17da2a77b878820854bfff2b5f9eb969a4e2e76a998f97f4967b2b1a7696437c"),
// start_foreign_context
digest!("0x8d40e8fc5f0efcc5824221577657811f9e71d7193a27ebdfb528b82204dfb511"),
digest!("0x8c3adb3aff1686205283a59d3908e074c1f4768ac4c7a778ef2b873693ca9101"),
// end_foreign_context
digest!("0x132b50feca8ecec10937c740640c59733e643e89c1d8304cf4523120e27a0428"),
// update_expiration_block_num
Expand Down
2 changes: 1 addition & 1 deletion objects/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ assembly = { workspace = true }
log = { version = "0.4", optional = true }
miden-crypto = { workspace = true }
miden-verifier = { workspace = true }
rand = { workspace = true, optional = true }
rand = { workspace = true, optional = true, features = ["small_rng"] }
thiserror = { workspace = true }
vm-core = { workspace = true }
vm-processor = { workspace = true }
Expand Down
50 changes: 27 additions & 23 deletions objects/src/accounts/account_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,18 @@ impl AccountType {
}
}

impl From<AccountId> for AccountType {
fn from(id: AccountId) -> Self {
id.account_type()
}
}

impl From<AccountIdPrefix> for AccountType {
fn from(id_prefix: AccountIdPrefix) -> Self {
id_prefix.account_type()
#[cfg(any(feature = "testing", test))]
impl rand::distributions::Distribution<AccountType> for rand::distributions::Standard {
/// Samples a uniformly random [`AccountType`] from the given `rng`.
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> AccountType {
let account_type_distribution = rand::distributions::Uniform::new_inclusive(0, 3);
PhilippGackstatter marked this conversation as resolved.
Show resolved Hide resolved
match account_type_distribution.sample(rng) {
0 => AccountType::RegularAccountImmutableCode,
1 => AccountType::RegularAccountUpdatableCode,
2 => AccountType::FungibleFaucet,
3 => AccountType::NonFungibleFaucet,
_ => unreachable!("the uniform distribution should not produce higher values"),
}
}
}

Expand Down Expand Up @@ -108,15 +111,16 @@ impl FromStr for AccountStorageMode {
}
}

impl From<AccountId> for AccountStorageMode {
fn from(id: AccountId) -> Self {
id.storage_mode()
}
}

impl From<AccountIdPrefix> for AccountStorageMode {
fn from(id_prefix: AccountIdPrefix) -> Self {
id_prefix.storage_mode()
#[cfg(any(feature = "testing", test))]
impl rand::distributions::Distribution<AccountStorageMode> for rand::distributions::Standard {
/// Samples a uniformly random [`AccountStorageMode`] from the given `rng`.
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> AccountStorageMode {
let account_type_distribution = rand::distributions::Uniform::new_inclusive(0, 1);
match account_type_distribution.sample(rng) {
0 => AccountStorageMode::Public,
1 => AccountStorageMode::Private,
_ => unreachable!("the uniform distribution should not produce higher values"),
}
}
}

Expand Down Expand Up @@ -185,9 +189,9 @@ impl From<AccountIdPrefix> for AccountIdVersion {
/// block as an anchor - which is why it is also referred to as the anchor block - and creating the
/// account's initial storage and code. Then a random seed is picked and the hash of (SEED,
/// CODE_COMMITMENT, STORAGE_COMMITMENT, ANCHOR_BLOCK_HASH) is computed. If the hash's first element
/// has the desired storage mode, account type, version and the high bit set to zero, the
/// computation part of the ID generation is done. If not, another random seed is picked and the
/// process is repeated. The first felt of the ID is then the first element of the hash.
/// has the desired storage mode, account type and version, the computation part of the ID
/// generation is done. If not, another random seed is picked and the process is repeated. The first
/// felt of the ID is then the first element of the hash.
///
/// The second felt of the ID is the second element of the hash. Its upper 16 bits are overwritten
/// with the epoch in which the ID is anchored and the lower 8 bits are zeroed. Thus, the first felt
Expand Down Expand Up @@ -354,7 +358,7 @@ impl AccountId {
/// significant end of the ID.
/// - In the second felt the anchor epoch is set to 0 and the lower 8 bits are cleared.
#[cfg(any(feature = "testing", test))]
pub fn new_dummy(
pub fn dummy(
mut bytes: [u8; 15],
account_type: AccountType,
storage_mode: AccountStorageMode,
Expand Down Expand Up @@ -855,7 +859,7 @@ mod tests {
AccountType::RegularAccountUpdatableCode,
] {
for storage_mode in [AccountStorageMode::Private, AccountStorageMode::Public] {
let id = AccountId::new_dummy(input, account_type, storage_mode);
let id = AccountId::dummy(input, account_type, storage_mode);
assert_eq!(id.account_type(), account_type);
assert_eq!(id.storage_mode(), storage_mode);
assert_eq!(id.version(), AccountIdVersion::VERSION_0);
Expand Down
6 changes: 6 additions & 0 deletions objects/src/accounts/account_id_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ impl AccountIdAnchor {

/// A "pre-genesis" [`AccountIdAnchor`] which can be used to anchor accounts created in the
/// genesis block.
///
/// This anchor should only be used for accounts included in the genesis state, but should not
/// be used as actual anchors in a running network. The reason is that this anchor has the same
/// `epoch` as the genesis block will have (epoch `0`). However, the genesis block will have a
/// different block_hash than this anchor ([`EMPTY_WORD`]) and so any account ID that would use
/// this anchor would be rejected as invalid by the transaction kernel.
pub const PRE_GENESIS: Self = Self {
epoch: 0,
block_hash: Digest::new(EMPTY_WORD),
Expand Down
2 changes: 1 addition & 1 deletion objects/src/accounts/account_id_prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ mod tests {
AccountType::RegularAccountUpdatableCode,
] {
for storage_mode in [AccountStorageMode::Private, AccountStorageMode::Public] {
let id = AccountId::new_dummy(input, account_type, storage_mode);
let id = AccountId::dummy(input, account_type, storage_mode);
let prefix = id.prefix();
assert_eq!(prefix.account_type(), account_type);
assert_eq!(prefix.storage_mode(), storage_mode);
Expand Down
2 changes: 1 addition & 1 deletion objects/src/accounts/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ impl AccountBuilder {
let account_id = {
let bytes = <[u8; 15]>::try_from(&init_seed[0..15])
.expect("we should have sliced exactly 15 bytes off");
AccountId::new_dummy(bytes, self.account_type, self.storage_mode)
AccountId::dummy(bytes, self.account_type, self.storage_mode)
};

Ok(Account::from_parts(account_id, vault, storage, code, Felt::ONE))
Expand Down
20 changes: 12 additions & 8 deletions objects/src/accounts/delta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@ mod tests {
AccountStorageMode, AccountType, StorageMapDelta,
},
assets::{Asset, AssetVault, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails},
testing::account_id::ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN,
testing::account_id::{
AccountIdBuilder, ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN,
},
ONE, ZERO,
};

Expand Down Expand Up @@ -334,20 +336,22 @@ mod tests {

let non_fungible: Asset = NonFungibleAsset::new(
&NonFungibleAssetDetails::new(
AccountId::new_dummy(
[10; 15],
AccountType::NonFungibleFaucet,
AccountStorageMode::Public,
)
.prefix(),
AccountIdBuilder::new()
.account_type(AccountType::NonFungibleFaucet)
.storage_mode(AccountStorageMode::Public)
.build_with_rng(&mut rand::thread_rng())
.prefix(),
vec![6],
)
.unwrap(),
)
.unwrap()
.into();
let fungible_2: Asset = FungibleAsset::new(
AccountId::new_dummy([10; 15], AccountType::FungibleFaucet, AccountStorageMode::Public),
AccountIdBuilder::new()
.account_type(AccountType::FungibleFaucet)
.storage_mode(AccountStorageMode::Public)
.build_with_rng(&mut rand::thread_rng()),
10,
)
.unwrap()
Expand Down
2 changes: 1 addition & 1 deletion objects/src/assets/nonfungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl NonFungibleAsset {
let mut vault_key = self.0;

// Swap first felt of faucet ID with hash0.
vault_key.swap(0, 3);
vault_key.swap(0, FAUCET_ID_POS);

// Set the fungible bit to zero by taking the bitwise `and` of the felt with the inverted
// is_faucet mask.
Expand Down
9 changes: 3 additions & 6 deletions objects/src/notes/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ use super::{
///
/// Note type and tag must be internally consistent according to the following rules:
///
/// - For off-chain notes, the most significant bit of the tag must be 0.
/// - For public notes, the second most significant bit of the tag must be 0.
/// - For encrypted notes, two most significant bits of the tag must be 00.
/// - For private and encrypted notes, the two most significant bits of the tag must be `0b11`.
/// - For public notes, the two most significant bits of the tag can be set to any value.
///
/// # Word layout & validity
///
Expand All @@ -41,7 +40,7 @@ pub struct NoteMetadata {
/// The ID of the account which created the note.
sender: AccountId,

/// Defines how the note is to be stored (e.g., on-chain or off-chain).
/// Defines how the note is to be stored (e.g. public or private).
note_type: NoteType,

/// A value which can be used by the recipient(s) to identify notes intended for them.
Expand Down Expand Up @@ -161,8 +160,6 @@ impl TryFrom<Word> for NoteMetadata {

impl Serializable for NoteMetadata {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
// TODO: Do we need a serialization format that is different from the Word encoding? It was
// previously different.
Word::from(self).write_into(target);
}
}
Expand Down
2 changes: 1 addition & 1 deletion objects/src/notes/note_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum NoteExecutionMode {
/// Tags are light-weight values used to speed up queries. The 2 most significant bits of the tags
/// have the following interpretation:
///
/// | Prefix | Execution hint | Target | Allowed [NoteType] |
/// | Prefix | Execution mode | Target | Allowed [NoteType] |
/// | ------ | :------------: | :------: | :----------------: |
/// | `0b00` | Network | Specific | [NoteType::Public] |
/// | `0b01` | Network | Use case | [NoteType::Public] |
Expand Down
Loading
Loading