Multisig contract
This contract is used by prover contracts during proof construction to start a signing session and collect signatures from participants.
flowchart TD subgraph Axelar b[Prover] m[Multisig] end s[Verifier] b--StartSigningSession-->m s--SubmitSignature-->m b--Multisig (query)-->m
- StartSigningSession: The multisig contract receives a binary message from the prover. It uses the current active
verifier set to back a new signing session and then emits an event to notify signers that a message is pending
signature. The prover may optionally pass a
sig_verifieraddress that overrides the default per-signature check. - SubmitSignature: Each signer signs the message using their own private key and submits the signature. The contract validates that the signer is a participant in the verifier set associated with the session.
- Multisig (query): The prover queries this to retrieve the current state of the session, the collected signatures so far, and the verifier set with participant information. The final proof is assembled by the prover once the multisig session is completed.
Signing Sequence Diagram
sequenceDiagram participant Relayer box LightYellow Axelar participant Prover participant Multisig end actor Signers Relayer->>+Prover: ExecuteMsg::ConstructProof Prover->>+Multisig: ExecuteMsg::StartSigningSession Multisig-->>Signers: emit SigningStarted event Multisig->>-Prover: reply with session ID deactivate Prover loop Collect signatures Signers->>+Multisig: ExecuteMsg::SubmitSignature Multisig-->>Relayer: emit SignatureSubmitted event end Multisig-->>-Relayer: emit SigningCompleted event Relayer->>+Prover: QueryMsg::Proof Prover->>+Multisig: QueryMsg::Multisig Multisig-->>-Prover: reply with status, current signatures vector, and verifier set Prover-->>-Relayer: returns data and proof
Custom signature verification
If the multisig contract does not natively support the required signature verification, the sig_verifier parameter
in ExecuteMsg::StartSigningSession can be set by the prover to specify a custom signature verification contract. The
custom contract must implement the following interface defined in packages/signature-verifier-api:
pub enum QueryMsg {
#[returns(bool)]
VerifySignature {
signature: HexBinary,
message: HexBinary,
public_key: HexBinary,
signer_address: String,
session_id: Uint64,
},
}
When a custom verification contract is specified, on each SubmitSignature call the multisig contract will query the
custom contract's VerifySignature to validate the signature. The custom contract returns true if the signature is
valid or false otherwise.
sequenceDiagram actor Signers box LightYellow Axelar participant Multisig participant SignatureVerifier end Signers->>+Multisig: ExecuteMsg::SubmitSignature Multisig->>SignatureVerifier: QueryMsg::VerifySignature SignatureVerifier->>Multisig: returns true/false deactivate Multisig
Authorization
Prior to calling StartSigningSession, the prover contract must be authorized. To authorize one or more contracts,
governance calls AuthorizeCallers with a map of contract address to chain name. Authorization can be revoked by
either governance or the contract admin via UnauthorizeCallers. Each authorization is scoped to a single chain name:
a contract authorized for chain X can only start signing sessions for chain X.
Governance or the admin can also globally pause and resume signing via DisableSigning / EnableSigning.
Interface
pub enum ExecuteMsg {
// Open a new signing session. Permission: Specific(authorized) — only contracts that
// were previously approved via AuthorizeCallers can call this.
StartSigningSession {
verifier_set_id: String,
msg: HexBinary,
chain_name: ChainName,
// Optional address of a contract that overrides the default signature check.
// See "Custom signature verification" above.
sig_verifier: Option<String>,
},
// Submit a signature from a participant in the verifier set for the session.
// Permission: Any (the verifier set check is part of execution).
SubmitSignature {
session_id: Uint64,
signature: HexBinary,
},
// Register a new verifier set so future signing sessions can reference it by id.
// Permission: Any (callable by provers as part of their rotation flow).
RegisterVerifierSet { verifier_set: VerifierSet },
// Register a public key for the sender. To prevent registering someone else's key, the
// sender must sign their own address using the private key being registered.
// Permission: Any.
RegisterPublicKey {
public_key: PublicKey,
signed_sender_address: HexBinary,
},
// Authorize a set of contracts to call StartSigningSession. Each entry is scoped to a
// specific chain name. Permission: Governance.
AuthorizeCallers {
contracts: HashMap<String, ChainName>,
},
// Revoke authorization for a set of contracts. Permission: Elevated (admin or governance).
UnauthorizeCallers {
contracts: HashMap<String, ChainName>,
},
// Emergency command to stop all signing. Permission: Elevated.
DisableSigning,
// Resumes signing after an emergency shutdown. Permission: Elevated.
EnableSigning,
}
#[derive(QueryResponses)]
pub enum QueryMsg {
// Returns the Multisig session state, collected signatures, and verifier set.
#[returns(Multisig)]
Multisig { session_id: Uint64 },
// Returns a previously registered VerifierSet by its id.
#[returns(VerifierSet)]
VerifierSet { verifier_set_id: String },
// Returns a registered public key for a verifier and key type.
#[returns(PublicKey)]
PublicKey {
verifier_address: String,
key_type: KeyType,
},
// Returns whether the given contract is currently authorized to start signing sessions
// for the given chain.
#[returns(bool)]
IsCallerAuthorized {
contract_address: String,
chain_name: ChainName,
},
}
pub struct Multisig {
pub state: MultisigState,
pub verifier_set: VerifierSet,
pub signatures: HashMap<String, Signature>,
}
pub enum MultisigState {
Pending,
Completed { completed_at: u64 },
}
Events
pub enum Event {
// Emitted when a new signing session is open.
SigningStarted {
session_id: Uint64,
verifier_set_id: String,
pub_keys: HashMap<String, PublicKey>,
msg: MsgToSign,
chain_name: ChainName,
expires_at: u64,
},
// Emitted when a participant submits a signature.
SignatureSubmitted {
session_id: Uint64,
participant: Addr,
signature: Signature,
},
// Emitted when a signing session was completed.
SigningCompleted {
session_id: Uint64,
completed_at: u64,
chain_name: ChainName,
},
// Emitted when a PublicKey is registered.
PublicKeyRegistered {
verifier: Addr,
public_key: PublicKey,
},
// Emitted when a contract is authorized to create signing sessions for a chain.
CallerAuthorized {
contract_address: Addr,
chain_name: ChainName,
},
// Emitted when a contract is unauthorized.
CallerUnauthorized {
contract_address: Addr,
chain_name: ChainName,
},
// Emitted when signing is globally enabled.
SigningEnabled,
// Emitted when signing is globally disabled.
SigningDisabled,
}