use guardian_shared::SignatureScheme;
use miden_client::assembly::CodeBuilder;
use miden_client::transaction::{TransactionRequest, TransactionRequestBuilder, TransactionScript};
use miden_protocol::assembly::Package;
use miden_protocol::{Felt, Hasher, Word};
use miden_standards::StandardsLib;
use miden_standards::account::auth::MultisigAuthArgs;
use crate::error::{MultisigError, Result};
use crate::procedures::ProcedureName;
use crate::transaction::TransactionRequestBuilderExt;
pub fn build_multisig_config_advice(
threshold: u64,
signer_commitments: &[Word],
scheme: SignatureScheme,
) -> (Word, Vec<Felt>) {
let num_approvers = signer_commitments.len() as u64;
let scheme_id = Felt::new_unchecked(scheme.auth_scheme_id());
let mut payload = Vec::with_capacity(4 + signer_commitments.len() * 8);
payload.extend_from_slice(&[
Felt::new_unchecked(threshold),
Felt::new_unchecked(num_approvers),
Felt::new_unchecked(0),
Felt::new_unchecked(0),
]);
for commitment in signer_commitments.iter().rev() {
payload.extend_from_slice(commitment.as_elements());
payload.extend_from_slice(&[
scheme_id,
Felt::new_unchecked(0),
Felt::new_unchecked(0),
Felt::new_unchecked(0),
]);
}
let digest = Hasher::hash_elements(&payload);
let config_hash: Word = digest;
(config_hash, payload)
}
pub fn build_update_signers_script() -> Result<TransactionScript> {
let standards_lib: Package = StandardsLib::default().into();
let tx_script_code = "
use miden::standards::auth::multisig
@transaction_script
pub proc main
call.multisig::update_signers_and_threshold
end
";
let tx_script = CodeBuilder::new()
.with_dynamically_linked_package(standards_lib)
.map_err(|e| MultisigError::TransactionExecution(format!("failed to link library: {}", e)))?
.compile_tx_script(tx_script_code)
.map_err(|e| {
MultisigError::TransactionExecution(format!("failed to compile script: {}", e))
})?;
Ok(tx_script)
}
pub fn build_update_signers_transaction_request<I>(
threshold: u64,
signer_commitments: &[Word],
auth_args: &MultisigAuthArgs,
extra_advice: I,
scheme: SignatureScheme,
) -> Result<(TransactionRequest, Word)>
where
I: IntoIterator<Item = (Word, Vec<Felt>)>,
{
let (config_hash, config_values) =
build_multisig_config_advice(threshold, signer_commitments, scheme);
let script = build_update_signers_script()?;
let request = TransactionRequestBuilder::new()
.custom_script(script)
.script_arg(config_hash)
.extend_advice_map([(config_hash, config_values)])
.extend_advice_map(extra_advice)
.multisig_auth_args(auth_args)
.build()?;
Ok((request, config_hash))
}
pub fn build_update_procedure_threshold_script(
procedure: ProcedureName,
threshold: u32,
) -> Result<TransactionScript> {
let standards_lib: Package = StandardsLib::default().into();
let procedure_root = procedure.root();
let tx_script_code = format!(
r#"
use miden::standards::auth::multisig
@transaction_script
pub proc main
push.{procedure_root}
push.{threshold}
call.multisig::set_procedure_threshold
dropw
drop
end
"#
);
let tx_script = CodeBuilder::new()
.with_dynamically_linked_package(standards_lib)
.map_err(|e| MultisigError::TransactionExecution(format!("failed to link library: {}", e)))?
.compile_tx_script(&tx_script_code)
.map_err(|e| {
MultisigError::TransactionExecution(format!("failed to compile script: {}", e))
})?;
Ok(tx_script)
}
pub fn build_update_procedure_threshold_transaction_request<I>(
procedure: ProcedureName,
threshold: u32,
auth_args: &MultisigAuthArgs,
extra_advice: I,
) -> Result<TransactionRequest>
where
I: IntoIterator<Item = (Word, Vec<Felt>)>,
{
let script = build_update_procedure_threshold_script(procedure, threshold)?;
let request = TransactionRequestBuilder::new()
.custom_script(script)
.extend_advice_map(extra_advice)
.multisig_auth_args(auth_args)
.build()?;
Ok(request)
}