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}