use miden_protocol::Word;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProcedureName {
UpdateSigners,
UpdateProcedureThreshold,
AuthTx,
UpdateGuardian,
SendAsset,
ReceiveAsset,
}
impl ProcedureName {
pub fn root(&self) -> Word {
match self {
ProcedureName::UpdateSigners => procedure_root_word(
"0x0f664cdaae422fe43bb45d959c7e469b0c855ad1fc4e79fddd730ec3cb983c6e",
),
ProcedureName::UpdateProcedureThreshold => procedure_root_word(
"0xa32cd13808fd8fb91adb3dceaed4d8faebc3bd80fa49c2903be4b8d7bf89ba77",
),
ProcedureName::AuthTx => procedure_root_word(
"0x71ba7380c6138d5e80e911094a9767ed0fa5c8ffefcf6a776754bfc75bc163b9",
),
ProcedureName::UpdateGuardian => procedure_root_word(
"0x93dedb135fd5bb7112c07aacf4a5680ddc76e45ecf043bfc42ea735b6d971911",
),
ProcedureName::SendAsset => procedure_root_word(
"0xf261e7bdd1faee5db3b0abe4bb67b153fbca6ece3e456b83eff98697f21f6a97",
),
ProcedureName::ReceiveAsset => procedure_root_word(
"0xd7416b798a70aabbca510c3cd0f48ba35473b5d76dc302375157c6f563fffc15",
),
}
}
pub fn all() -> &'static [ProcedureName] {
&[
ProcedureName::UpdateSigners,
ProcedureName::UpdateProcedureThreshold,
ProcedureName::AuthTx,
ProcedureName::UpdateGuardian,
ProcedureName::SendAsset,
ProcedureName::ReceiveAsset,
]
}
}
#[derive(Debug, Clone, Copy)]
pub struct ProcedureThreshold {
pub procedure: ProcedureName,
pub threshold: u32,
}
impl ProcedureThreshold {
pub fn new(procedure: ProcedureName, threshold: u32) -> Self {
Self {
procedure,
threshold,
}
}
pub fn procedure_root(&self) -> Word {
self.procedure.root()
}
}
impl std::fmt::Display for ProcedureName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProcedureName::UpdateSigners => write!(f, "update_signers"),
ProcedureName::UpdateProcedureThreshold => write!(f, "update_procedure_threshold"),
ProcedureName::AuthTx => write!(f, "auth_tx"),
ProcedureName::UpdateGuardian => write!(f, "update_guardian"),
ProcedureName::SendAsset => write!(f, "send_asset"),
ProcedureName::ReceiveAsset => write!(f, "receive_asset"),
}
}
}
impl std::str::FromStr for ProcedureName {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"update_signers" => Ok(ProcedureName::UpdateSigners),
"update_procedure_threshold" => Ok(ProcedureName::UpdateProcedureThreshold),
"auth_tx" => Ok(ProcedureName::AuthTx),
"update_guardian" => Ok(ProcedureName::UpdateGuardian),
"send_asset" => Ok(ProcedureName::SendAsset),
"receive_asset" => Ok(ProcedureName::ReceiveAsset),
_ => Err(format!("unknown procedure name: {}", s)),
}
}
}
fn procedure_root_word(hex_str: &str) -> Word {
Word::parse(hex_str).expect("valid procedure root constant")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn procedure_threshold_new_creates_correctly() {
let threshold = ProcedureThreshold::new(ProcedureName::ReceiveAsset, 1);
assert_eq!(threshold.procedure, ProcedureName::ReceiveAsset);
assert_eq!(threshold.threshold, 1);
}
#[test]
fn procedure_threshold_procedure_root_returns_correct_root() {
let threshold = ProcedureThreshold::new(ProcedureName::SendAsset, 2);
assert_eq!(threshold.procedure_root(), ProcedureName::SendAsset.root());
}
#[test]
fn procedure_name_round_trip() {
for name in ProcedureName::all() {
let s = name.to_string();
let parsed: ProcedureName = s.parse().unwrap();
assert_eq!(*name, parsed);
}
}
#[test]
fn procedure_roots_are_valid() {
for name in ProcedureName::all() {
let _root = name.root();
}
}
fn auth_root_in(code: &miden_protocol::account::AccountComponentCode, masm_name: &str) -> Word {
let export = code
.exports()
.find(|e| e.path.to_string().rsplit("::").next() == Some(masm_name))
.unwrap_or_else(|| panic!("procedure `{masm_name}` not found"));
code.get_procedure_root_by_path(&*export.path)
.expect("root by path")
.into()
}
fn upstream_auth_code() -> &'static miden_protocol::account::AccountComponentCode {
miden_standards::account::auth::AuthGuardedMultisig::code()
}
#[test]
fn procedure_roots_match_upstream_component() {
use miden_standards::account::wallets::BasicWallet;
let auth_code = upstream_auth_code();
assert_eq!(
ProcedureName::UpdateSigners.root(),
auth_root_in(auth_code, "update_signers_and_threshold")
);
assert_eq!(
ProcedureName::AuthTx.root(),
auth_root_in(auth_code, "auth_tx_guarded_multisig")
);
assert_eq!(
ProcedureName::UpdateProcedureThreshold.root(),
auth_root_in(auth_code, "set_procedure_threshold")
);
assert_eq!(
ProcedureName::UpdateGuardian.root(),
auth_root_in(auth_code, "update_guardian_public_key")
);
assert_eq!(
ProcedureName::SendAsset.root(),
Word::from(BasicWallet::move_asset_to_note_root())
);
assert_eq!(
ProcedureName::ReceiveAsset.root(),
Word::from(BasicWallet::receive_asset_root())
);
}
#[test]
fn rust_built_accounts_carry_the_pinned_auth_tx_root() {
use miden_confidential_contracts::multisig_guardian::{
MultisigGuardianBuilder, MultisigGuardianConfig,
};
let config = MultisigGuardianConfig::new(
1,
vec![Word::from([1u32, 0, 0, 0])],
Word::from([9u32, 0, 0, 0]),
);
let account = MultisigGuardianBuilder::new(config)
.with_seed([7u8; 32])
.build_existing()
.expect("account builds");
assert!(
account.code().has_procedure(ProcedureName::AuthTx.root()),
"the Rust builder must produce the same auth_tx root the browser builder does"
);
}
#[test]
fn rust_built_accounts_classify_as_guarded_multisig() {
use miden_confidential_contracts::multisig_guardian::{
MultisigGuardianBuilder, MultisigGuardianConfig,
};
use miden_standards::account::interface::{
AccountComponentInterface, AccountComponentInterfaceExt,
};
let config = MultisigGuardianConfig::new(
1,
vec![Word::from([1u32, 0, 0, 0])],
Word::from([9u32, 0, 0, 0]),
);
let account = MultisigGuardianBuilder::new(config)
.with_seed([7u8; 32])
.build_existing()
.expect("account builds");
let procedures: Vec<_> = account.code().procedures().to_vec();
let components = AccountComponentInterface::from_procedures(&procedures);
assert!(
components.iter().any(|component| matches!(
component,
AccountComponentInterface::AuthGuardedMultisig
)),
"miden-client must classify the built account as AuthGuardedMultisig, got {components:?}"
);
}
#[test]
fn parse_unknown_returns_error() {
let result: Result<ProcedureName, _> = "unknown_proc".parse();
assert!(result.is_err());
}
}