af_keys/multisig.rs
1//! Additional helpers for multisig signing.
2
3use anyhow::{Context, Error};
4use serde::{Deserialize, Serialize};
5use sui_sdk_types::{Address, MultisigCommittee, Transaction, UserSignature};
6
7use crate::Keystore;
8
9/// Data needed for signing as a multisig.
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub struct MultisigIntent {
12 pub committee: MultisigCommittee,
13 /// The indexes of the public keys in `committee` to sign for.
14 pub signers: Vec<usize>,
15}
16
17/// Sign the transaction data with the private key(s) for an address.
18///
19/// If the address is a native Sui multisig, `multisig_intent` should be specified to tell the
20/// function which constituents to sign for.
21pub fn sign_for_address(
22 transaction: &Transaction,
23 address: Address,
24 multisig_intent: Option<MultisigIntent>,
25 keystore: &Keystore,
26) -> Result<UserSignature, Error> {
27 let signature = if let Some(intent) = multisig_intent {
28 let msig_address = intent.committee.derive_address();
29 anyhow::ensure!(
30 msig_address == address,
31 "multisig address {msig_address} doesn't match target address {address}"
32 );
33 UserSignature::Multisig(keystore.multisign_tx(
34 transaction,
35 intent.committee,
36 &intent.signers,
37 )?)
38 } else {
39 UserSignature::Simple(keystore.sign_tx(transaction, address)?)
40 };
41 Ok(signature)
42}
43
44/// Computes the required signatures for a transaction's data.
45///
46/// [`Transaction`] has a sender and a sponsor (which may be equal to sender), which are
47/// [`Address`]es. This function then gets that information and knows who it has to sign for.
48///
49/// For simple cases, it just uses those `Address`es and signs for them using the [`Keystore`].
50///
51/// However, there's no way to know if `Address` corresponds to a multisig. So the function has
52/// two optional arguments: `multisig_sender` and `multisig_sponsor`. They exist so that the caller
53/// can tell the function if the sender and/or sponsor are multisigs. Their value encodes all the
54/// public keys that compose the multisig, their weights, and the threshold (public information).
55///
56/// The function can then sign for the simple addresses that compose the multisig (assuming
57/// [`Keystore`] has the private keys for each) and combine the simple signatures into a generic
58/// signature.
59///
60/// The [`MultisigIntent`] message declares what public keys the [`Keystore`] has to sign for. It's
61/// not required to sign for all of them, only a subset that has enough weight.
62pub fn signatures(
63 transaction: &Transaction,
64 multisig_sender: Option<MultisigIntent>,
65 multisig_sponsor: Option<MultisigIntent>,
66 keystore: &Keystore,
67) -> Result<Vec<UserSignature>, Error> {
68 let sender_signature =
69 sign_for_address(transaction, transaction.sender, multisig_sender, keystore)
70 .context("Signing for sender")?;
71 let mut signatures = vec![sender_signature];
72 if transaction.sender == transaction.gas_payment.owner {
73 if multisig_sponsor.is_some() {
74 log::warn!("Ignoring multisig_sponsor since sender owns the gas inputs");
75 };
76 } else {
77 signatures.push(
78 sign_for_address(
79 transaction,
80 transaction.gas_payment.owner,
81 multisig_sponsor,
82 keystore,
83 )
84 .context("Signing for sponsor")?,
85 );
86 };
87 Ok(signatures)
88}