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

Implement AccountComponents #941

Merged
merged 44 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
abf8803
feat(core): Add account components
PhilippGackstatter Oct 29, 2024
30b3618
feat(objects): Refactor `AccountBuilder` to use components
PhilippGackstatter Oct 29, 2024
2fc6db9
feat(objects): Add `IntoAccountComponent` trait
PhilippGackstatter Oct 29, 2024
b8ff057
feat(objects): Add `AccountComponentType` enum
PhilippGackstatter Oct 30, 2024
5b0d54b
feat(objects): Add BasicFungibleFaucet component
PhilippGackstatter Oct 30, 2024
f1f6234
chore(miden-tx): Use faucet component in prove tests
PhilippGackstatter Oct 30, 2024
1f61d73
chore(objects): Rename to `AssembledAccountComponent`
PhilippGackstatter Oct 30, 2024
d0e7331
chore(objects): Rename trait to `AccountComponent`
PhilippGackstatter Oct 30, 2024
3f56b63
feat(objects): Move `AccountComponent` out of `testing`
PhilippGackstatter Oct 30, 2024
82b5ba8
feat(objects): Move `BasicFungibleFaucet` component out of `testing`
PhilippGackstatter Oct 30, 2024
50c926c
feat(objects): Create basic wallet from components
PhilippGackstatter Oct 30, 2024
cf7b803
feat(miden-lib): Rewrite if statement to unconditional assert
PhilippGackstatter Oct 30, 2024
8396114
feat(lib): Implement unconditional offset/size assertion
PhilippGackstatter Oct 30, 2024
f11f5f5
chore(tx): Remove unused imports
PhilippGackstatter Oct 30, 2024
6d6377c
feat(lib): Build account components at compile time
PhilippGackstatter Oct 31, 2024
bc9ab47
feat(lib): Use pre-compiled components in testing code
PhilippGackstatter Oct 31, 2024
6934a24
feat(objects): Use `BTreeSet<AccountType>` to express component support
PhilippGackstatter Oct 31, 2024
8c884f1
feat(objects): Remove `AccountCode::{new, compile}`
PhilippGackstatter Oct 31, 2024
2591f60
chore: Simplify component initialization
PhilippGackstatter Oct 31, 2024
f7cb0e8
chore: Add docs on `AccountComponent`
PhilippGackstatter Oct 31, 2024
fd8b27d
chore: Simplify `AccountMockComponent`
PhilippGackstatter Oct 31, 2024
e2e8538
feat: Add `AccountComponent::supports_type`
PhilippGackstatter Oct 31, 2024
af18663
feat(objects): Prevent overflow in offset+size check
PhilippGackstatter Oct 31, 2024
8ed950a
chore: Fix no-std compilation
PhilippGackstatter Oct 31, 2024
26d178c
feat(objects): Document newly introduced types
PhilippGackstatter Oct 31, 2024
dc2a89f
chore: Fix no-std compilation
PhilippGackstatter Oct 31, 2024
19f1707
chore: Add changelog
PhilippGackstatter Oct 31, 2024
2acd4b2
chore(lib): Correct auth component layout comment
PhilippGackstatter Oct 31, 2024
3eb4d1f
chore: Fix documentation links
PhilippGackstatter Oct 31, 2024
de26eb0
feat(lib): Simplify offset+size assertion
PhilippGackstatter Oct 31, 2024
124cff0
feat(lib): Remove temporary faucet offset check
PhilippGackstatter Oct 31, 2024
33028e8
chore: Add warning for `AccountCode::from_components`
PhilippGackstatter Oct 31, 2024
0a65506
chore: Rename default code to basic auth wallet
PhilippGackstatter Oct 31, 2024
4fedb70
chore(tx): Impl temporary fix for foreign account tests
PhilippGackstatter Oct 31, 2024
b84e0c1
chore: Apply review comments
PhilippGackstatter Nov 1, 2024
ae8a6b5
feat(objects): Add num storage slots < 255 check in `AccountComponent…
PhilippGackstatter Nov 1, 2024
436ee21
feat(objects): Move `AccountCode::from_components` behind `testing`
PhilippGackstatter Nov 1, 2024
2946a2f
chore(objects): Fix component validation errors in tests
PhilippGackstatter Nov 1, 2024
b9bd240
chore: Add comments on assertions in proc metadata validation
PhilippGackstatter Nov 1, 2024
6963836
chore(tx): Fix test code after rebase
PhilippGackstatter Nov 1, 2024
88f0632
chore: rearrange procedures in account component
bobbinth Nov 2, 2024
ba15a7a
chore: Simplify storage initialization
PhilippGackstatter Nov 4, 2024
1ecdd96
feat(objects): Add component type and duplicate MAST root test
PhilippGackstatter Nov 4, 2024
a48c7e2
chore: make note scripts public again
bobbinth Nov 4, 2024
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
6 changes: 1 addition & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@
- [BREAKING] Changed type of `EMPTY_STORAGE_MAP_ROOT` constant to `RpoDigst`, which references constant from `miden-crypto` (#916).
- Added `RemoteTransactionProver` struct to `miden-tx-prover` (#921).
- [BREAKING] Migrated to v0.11 version of Miden VM (#929).
- Added a proxy server for the `miden-tx-prover` service (#930).
- Added `TransactionExecutor::load_account_code()` method to support foreign procedure invocation (#936).
- Changed `RemoteTransactionProver` to lazily connect on prove (#937).
- [BREAKING] Made `TransactionMastForest` and `BasicAuthenticator` be `Send`, `Sync`; made scripts lazily initialize (#939).
- Added account codes to `TransactionWitness` (#946).
- [BREAKING] Introduce a new way to build `Account`s from `AccountComponent`s (#941).

## 0.5.1 (2024-08-28) - `miden-objects` crate only

Expand Down
4 changes: 2 additions & 2 deletions bin/bench-tx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use vm_processor::ONE;

mod utils;
use utils::{
get_account_with_default_account_code, get_new_pk_and_authenticator,
get_account_with_basic_authenticated_wallet, get_new_pk_and_authenticator,
write_bench_results_to_json, ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN,
ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN, ACCOUNT_ID_SENDER, DEFAULT_AUTH_SCRIPT,
};
Expand Down Expand Up @@ -98,7 +98,7 @@ pub fn benchmark_p2id() -> Result<TransactionMeasurements, String> {
let (target_pub_key, falcon_auth) = get_new_pk_and_authenticator();

let target_account =
get_account_with_default_account_code(target_account_id, target_pub_key, None);
get_account_with_basic_authenticated_wallet(target_account_id, target_pub_key, None);

// Create the note
let note = create_p2id_note(
Expand Down
25 changes: 9 additions & 16 deletions bin/bench-tx/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ extern crate alloc;
pub use alloc::{collections::BTreeMap, string::String};
use std::sync::Arc;

use miden_lib::transaction::TransactionKernel;
use miden_lib::accounts::{auth::RpoFalcon512, wallets::BasicWallet};
use miden_objects::{
accounts::{Account, AccountCode, AccountId, AccountStorage, AuthSecretKey, StorageSlot},
accounts::{Account, AccountId, AuthSecretKey},
assets::{Asset, AssetVault},
crypto::dsa::rpo_falcon512::SecretKey,
crypto::dsa::rpo_falcon512::{PublicKey, SecretKey},
transaction::TransactionMeasurements,
Felt, Word,
};
Expand All @@ -31,13 +31,6 @@ pub const DEFAULT_AUTH_SCRIPT: &str = "
end
";

pub const DEFAULT_ACCOUNT_CODE: &str = "
export.::miden::contracts::wallets::basic::receive_asset
export.::miden::contracts::wallets::basic::create_note
export.::miden::contracts::wallets::basic::move_asset_to_note
export.::miden::contracts::auth::basic::auth_tx_rpo_falcon512
";

// MEASUREMENTS PRINTER
// ================================================================================================

Expand Down Expand Up @@ -68,16 +61,16 @@ impl From<TransactionMeasurements> for MeasurementsPrinter {
// HELPER FUNCTIONS
// ================================================================================================

pub fn get_account_with_default_account_code(
pub fn get_account_with_basic_authenticated_wallet(
account_id: AccountId,
public_key: Word,
assets: Option<Asset>,
) -> Account {
let account_code_src = DEFAULT_ACCOUNT_CODE;
let assembler = TransactionKernel::assembler();

let account_code = AccountCode::compile(account_code_src, assembler, false).unwrap();
let account_storage = AccountStorage::new(vec![StorageSlot::Value(public_key)]).unwrap();
let (account_code, account_storage) = Account::initialize_from_components(
account_id.account_type(),
&[BasicWallet.into(), RpoFalcon512::new(PublicKey::new(public_key)).into()],
)
.unwrap();

let account_vault = match assets {
Some(asset) => AssetVault::new(&[asset]).unwrap(),
Expand Down
59 changes: 28 additions & 31 deletions miden-lib/asm/kernels/transaction/lib/account.masm
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,8 @@ end
#! - All storage offsets and sizes are in bounds
#! - All storage offsets adhere to account type specific rules
#! - All procedures not accessing storage have offset and size set to 0
#!
#! Notes:
#! - For faucets checks that no storage offset is 0 (preventing reserved storage slot access)
#! - No storage offset of a faucet account's procedure is 0 with a size != 0
#! (to prevent access to the reserved storage slot)
#!
#! Stack: []
#! Output: []
Expand All @@ -387,41 +386,40 @@ export.validate_procedure_metadata
# we do not check if num_account_procedures == 0 here because a valid
# account has between 1 and 256 procedures with associated offsets
if.true
# This branch handles procedures from faucet accounts.
while.true
# get storage offset and size from memory
dup exec.get_procedure_metadata
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# assert that storage offset is not 0
dup push.0 neq assert.err=ERR_FAUCET_INVALID_STORAGE_OFFSET
# Procedures that do not access storage are defined with (offset, size) = (0, 0).
# But we want to fail on tuples defined with a zero size but non-zero offset, since that is a logic error.
# We want to panic when we see this condition, so we use assertz rather than assert.
# So we can assert this with: (size == 0 && offset != 0) == 0.
dup.1 eq.0 dup.1 eq.0 not and assertz.err=ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# No procedure should access the reserved faucet slot (slot 0). However (0, 0) should
# still be allowed per the above.
# We want to panic when we see this condition, so we use assertz rather than assert.
# So we can assert this with: (offset == 0 && size != 0) == 0.
dup.1 eq.0 not dup.1 eq.0 and assertz.err=ERR_FAUCET_INVALID_STORAGE_OFFSET
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# assert that storage offset is in bounds
dup dup.4 lt assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# TODO: This is a temporary check for faucets. We add 1 to all faucet offsets
# to prevent access to the reserved faucet data slot. When the assembler
# will support storage offsets and sizes remove this check.
# check if the storage offset and the number of storage slots are 1
dup dup.4 eq.1 swap eq.1 and
# => [both_1, storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

if.true
# clean stack
drop drop
# => [index, num_storage_slots, num_account_procedures]
else
# assert that storage offset is in bounds
dup dup.4 lt assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# assert that storage limit is in bounds
add dup.2 lte assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS
# => [index, num_storage_slots, num_account_procedures]
end
# assert that storage limit is in bounds
add dup.2 lte assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS
# => [index, num_storage_slots, num_account_procedures]

# check if we should continue looping
add.1 dup dup.3 lt
# => [should_loop, index, num_storage_slots, num_account_procedures]
end
else
# This branch handles procedures from regular accounts.
while.true
# get storage offset and size from memory
dup exec.get_procedure_metadata
Expand All @@ -431,12 +429,11 @@ export.validate_procedure_metadata
dup dup.4 lt assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# TODO: Find a way to remove this `if` statement
# assert that if size is 0 then offset is 0
dup.1 eq.0
if.true
dup eq.0 assert.err=ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE
end
# Procedures that do not access storage are defined with (offset, size) = (0, 0).
# But we want to fail on tuples defined with a zero size but non-zero offset, since that is a logic error.
# We want to panic when we see this condition, so we use assertz rather than assert.
# So we can assert this with: (size == 0 && offset != 0) == 0.
dup.1 eq.0 dup.1 eq.0 not and assertz.err=ERR_ACCOUNT_INVALID_STORAGE_OFFSET_FOR_SIZE
# => [storage_offset, storage_size, index, num_storage_slots, num_account_procedures]

# assert that storage limit is in bounds
Expand Down
2 changes: 1 addition & 1 deletion miden-lib/asm/miden/contracts/auth/basic.masm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use.std::crypto::dsa::rpo_falcon512
# CONSTANTS
# =================================================================================================

# Slot in account storage at which the public key is stored.
# The slot in this component's storage layout where the public key is stored.
const.PUBLIC_KEY_SLOT=0

#! Authenticate a transaction using the Falcon signature scheme
Expand Down
4 changes: 2 additions & 2 deletions miden-lib/asm/miden/contracts/faucets/basic_fungible.masm
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const.ERR_FUNGIBLE_ASSET_DISTRIBUTE_WOULD_CAUSE_MAX_SUPPLY_TO_BE_EXCEEDED=0x0002
# CONSTANTS
# =================================================================================================

# Slot in account storage at which the metadata is stored.
const.METADATA_SLOT=1
# The slot in this component's storage layout where the metadata is stored.
const.METADATA_SLOT=0

# Basic authentication for the faucet owner.
export.basic::auth_tx_rpo_falcon512
Expand Down
41 changes: 40 additions & 1 deletion miden-lib/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const ASSETS_DIR: &str = "assets";
const ASM_DIR: &str = "asm";
const ASM_MIDEN_DIR: &str = "miden";
const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts";
const ASM_ACCOUNT_COMPONENTS_DIR: &str = "account_components";
const ASM_TX_KERNEL_DIR: &str = "kernels/transaction";
const KERNEL_V0_RS_FILE: &str = "src/transaction/procedures/kernel_v0.rs";

Expand Down Expand Up @@ -61,9 +62,12 @@ fn main() -> Result<()> {
compile_note_scripts(
&source_dir.join(ASM_NOTE_SCRIPTS_DIR),
&target_dir.join(ASM_NOTE_SCRIPTS_DIR),
assembler,
assembler.clone(),
)?;

// compile account components
compile_account_components(&target_dir.join(ASM_ACCOUNT_COMPONENTS_DIR), assembler)?;

Ok(())
}

Expand Down Expand Up @@ -299,6 +303,41 @@ fn compile_note_scripts(source_dir: &Path, target_dir: &Path, assembler: Assembl
Ok(())
}

// COMPILE DEFAULT ACCOUNT COMPONENTS
// ================================================================================================

const BASIC_WALLET_CODE: &str = "
export.::miden::contracts::wallets::basic::receive_asset
export.::miden::contracts::wallets::basic::create_note
export.::miden::contracts::wallets::basic::move_asset_to_note
";

const RPO_FALCON_AUTH_CODE: &str = "
export.::miden::contracts::auth::basic::auth_tx_rpo_falcon512
";

const BASIC_FUNGIBLE_FAUCET_CODE: &str = "
export.::miden::contracts::faucets::basic_fungible::distribute
export.::miden::contracts::faucets::basic_fungible::burn
";

/// Compiles the default account components into a MASL library and stores the complied files in
/// `target_dir`.
fn compile_account_components(target_dir: &Path, assembler: Assembler) -> Result<()> {
for (component_name, component_code) in [
("basic_wallet", BASIC_WALLET_CODE),
("rpo_falcon_512", RPO_FALCON_AUTH_CODE),
("basic_fungible_faucet", BASIC_FUNGIBLE_FAUCET_CODE),
] {
let component_library = assembler.clone().assemble_library([component_code])?;
let component_file_path =
target_dir.join(component_name).with_extension(Library::LIBRARY_EXTENSION);
component_library.write_to_file(component_file_path).into_diagnostic()?;
}

Ok(())
}

// HELPER FUNCTIONS
// ================================================================================================

Expand Down
36 changes: 36 additions & 0 deletions miden-lib/src/accounts/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use miden_objects::{
accounts::{AccountComponent, StorageSlot},
crypto::dsa::rpo_falcon512::PublicKey,
};

use crate::accounts::components::rpo_falcon_512_library;

/// An [`AccountComponent`] implementing the RpoFalcon512 signature scheme for authentication of
/// transactions.
///
/// Its exported procedures are:
/// - `auth_tx_rpo_falcon512`, which can be used to verify a signature provided via the advice stack
/// to authenticate a transaction.
///
/// This component supports all account types.
pub struct RpoFalcon512 {
public_key: PublicKey,
}

impl RpoFalcon512 {
/// Creates a new [`RpoFalcon512`] component with the given `public_key`.
pub fn new(public_key: PublicKey) -> Self {
Self { public_key }
}
}

impl From<RpoFalcon512> for AccountComponent {
fn from(falcon: RpoFalcon512) -> Self {
AccountComponent::new(
rpo_falcon_512_library(),
vec![StorageSlot::Value(falcon.public_key.into())],
)
.expect("falcon component should satisfy the requirements of a valid account component")
.with_supports_all_types()
}
}
42 changes: 42 additions & 0 deletions miden-lib/src/accounts/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use miden_objects::{
assembly::Library,
utils::{sync::LazyLock, Deserializable},
};

// Initialize the Basic Wallet library only once.
static BASIC_WALLET_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes =
include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/basic_wallet.masl"));
Library::read_from_bytes(bytes).expect("Shipped Basic Wallet library is well-formed")
});

// Initialize the Rpo Falcon 512 library only once.
static RPO_FALCON_512_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes =
include_bytes!(concat!(env!("OUT_DIR"), "/assets/account_components/rpo_falcon_512.masl"));
Library::read_from_bytes(bytes).expect("Shipped Rpo Falcon 512 library is well-formed")
});

// Initialize the Basic Fungible Faucet library only once.
static BASIC_FUNGIBLE_FAUCET_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes = include_bytes!(concat!(
env!("OUT_DIR"),
"/assets/account_components/basic_fungible_faucet.masl"
));
Library::read_from_bytes(bytes).expect("Shipped Basic Fungible Faucet library is well-formed")
});

/// Returns the Basic Wallet Library.
pub fn basic_wallet_library() -> Library {
BASIC_WALLET_LIBRARY.clone()
}

/// Returns the Rpo Falcon 512 Library.
pub fn rpo_falcon_512_library() -> Library {
RPO_FALCON_512_LIBRARY.clone()
}

/// Returns the Basic Fungible Faucet Library.
pub fn basic_fungible_faucet_library() -> Library {
BASIC_FUNGIBLE_FAUCET_LIBRARY.clone()
}
Loading
Loading