use alloc::string::String;
use miden_protocol::account::component::{AccountComponentCode, AccountComponentMetadata};
use miden_protocol::account::{
Account,
AccountBuilder,
AccountComponent,
AccountComponentName,
AccountProcedureRoot,
AccountType,
};
use miden_protocol::errors::AccountError;
use thiserror::Error;
use super::AuthMethod;
use crate::account::account_component_code;
use crate::account::auth::{AuthMultisig, AuthMultisigConfig, AuthSingleSig};
use crate::procedure_root;
account_component_code!(BASIC_WALLET_CODE, "wallets/basic_wallet.masl");
procedure_root!(
BASIC_WALLET_RECEIVE_ASSET,
BasicWallet::NAME,
BasicWallet::RECEIVE_ASSET_PROC_NAME,
BasicWallet::code()
);
procedure_root!(
BASIC_WALLET_MOVE_ASSET_TO_NOTE,
BasicWallet::NAME,
BasicWallet::MOVE_ASSET_TO_NOTE_PROC_NAME,
BasicWallet::code()
);
pub struct BasicWallet;
impl BasicWallet {
pub const NAME: &'static str = "miden::standards::components::wallets::basic_wallet";
const RECEIVE_ASSET_PROC_NAME: &str = "receive_asset";
const MOVE_ASSET_TO_NOTE_PROC_NAME: &str = "move_asset_to_note";
pub const fn name() -> AccountComponentName {
AccountComponentName::from_static_str(Self::NAME)
}
pub fn code() -> &'static AccountComponentCode {
&BASIC_WALLET_CODE
}
pub fn receive_asset_root() -> AccountProcedureRoot {
*BASIC_WALLET_RECEIVE_ASSET
}
pub fn move_asset_to_note_root() -> AccountProcedureRoot {
*BASIC_WALLET_MOVE_ASSET_TO_NOTE
}
pub fn component_metadata() -> AccountComponentMetadata {
AccountComponentMetadata::new(Self::NAME)
.with_description("Basic wallet component for receiving and sending assets")
}
}
impl From<BasicWallet> for AccountComponent {
fn from(_: BasicWallet) -> Self {
let metadata = BasicWallet::component_metadata();
AccountComponent::new(BasicWallet::code().clone(), vec![], metadata).expect(
"basic wallet component should satisfy the requirements of a valid account component",
)
}
}
#[derive(Debug, Error)]
pub enum BasicWalletError {
#[error("unsupported authentication method: {0}")]
UnsupportedAuthMethod(String),
#[error("account creation failed")]
AccountError(#[source] AccountError),
}
pub fn create_basic_wallet(
init_seed: [u8; 32],
auth_method: AuthMethod,
account_storage_mode: AccountType,
) -> Result<Account, BasicWalletError> {
let auth_component: AccountComponent = match auth_method {
AuthMethod::SingleSig { approver: (pub_key, auth_scheme) } => {
AuthSingleSig::new(pub_key, auth_scheme).into()
},
AuthMethod::Multisig { threshold, approvers } => {
let config = AuthMultisigConfig::new(approvers, threshold)
.and_then(|cfg| {
cfg.with_proc_thresholds(vec![(BasicWallet::receive_asset_root(), 1)])
})
.map_err(BasicWalletError::AccountError)?;
AuthMultisig::new(config).map_err(BasicWalletError::AccountError)?.into()
},
AuthMethod::NoAuth => {
return Err(BasicWalletError::UnsupportedAuthMethod(
"basic wallets cannot be created with NoAuth authentication method".into(),
));
},
AuthMethod::NetworkAccount { .. } => {
return Err(BasicWalletError::UnsupportedAuthMethod(
"basic wallets cannot be created with NetworkAccount authentication method".into(),
));
},
AuthMethod::Unknown => {
return Err(BasicWalletError::UnsupportedAuthMethod(
"basic wallets cannot be created with Unknown authentication method".into(),
));
},
};
let account = AccountBuilder::new(init_seed)
.account_type(account_storage_mode)
.with_auth_component(auth_component)
.with_component(BasicWallet)
.build()
.map_err(BasicWalletError::AccountError)?;
Ok(account)
}
#[cfg(test)]
mod tests {
use miden_protocol::account::auth::{self, PublicKeyCommitment};
use miden_protocol::utils::serde::{Deserializable, Serializable};
use miden_protocol::{ONE, Word};
use super::{Account, AccountType, AuthMethod, create_basic_wallet};
use crate::account::wallets::BasicWallet;
#[test]
fn test_create_basic_wallet() {
let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
let auth_scheme = auth::AuthScheme::Falcon512Poseidon2;
let wallet = create_basic_wallet(
[1; 32],
AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
AccountType::Public,
);
wallet.unwrap_or_else(|err| {
panic!("{}", err);
});
}
#[test]
fn test_serialize_basic_wallet() {
let pub_key = PublicKeyCommitment::from(Word::from([ONE; 4]));
let auth_scheme = auth::AuthScheme::EcdsaK256Keccak;
let wallet = create_basic_wallet(
[1; 32],
AuthMethod::SingleSig { approver: (pub_key, auth_scheme) },
AccountType::Public,
)
.unwrap();
let bytes = wallet.to_bytes();
let deserialized_wallet = Account::read_from_bytes(&bytes).unwrap();
assert_eq!(wallet, deserialized_wallet);
}
#[test]
fn get_faucet_procedures() {
let _receive_asset_root = BasicWallet::receive_asset_root();
let _move_asset_to_note_root = BasicWallet::move_asset_to_note_root();
}
}