From ca9b49674cd633000010acb8fcea89cfbaebe92b Mon Sep 17 00:00:00 2001 From: alfred micheal Date: Fri, 28 Aug 2026 00:26:18 +0100 Subject: [PATCH 1/4] docs: add doc comments to multisig-account's Signature struct and its fields (#177) --- contracts/multisig-account/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/multisig-account/src/lib.rs b/contracts/multisig-account/src/lib.rs index 50b0a48..31ed57a 100644 --- a/contracts/multisig-account/src/lib.rs +++ b/contracts/multisig-account/src/lib.rs @@ -15,10 +15,13 @@ enum DataKey { SignerCount, } +/// A single ed25519 signature from one signer in the multisig set. #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct Signature { + /// The public key of the signer who created this signature. pub public_key: BytesN<32>, + /// The ed25519 signature bytes. pub signature: BytesN<64>, } From 25211b6c58c13b27e31f6a9a6ce0679f13dee791 Mon Sep 17 00:00:00 2001 From: alfred micheal Date: Fri, 28 Aug 2026 00:26:34 +0100 Subject: [PATCH 2/4] docs: add doc comments to multisig-account Error variants (#178) --- contracts/multisig-account/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/multisig-account/src/lib.rs b/contracts/multisig-account/src/lib.rs index 31ed57a..3bf2262 100644 --- a/contracts/multisig-account/src/lib.rs +++ b/contracts/multisig-account/src/lib.rs @@ -25,16 +25,24 @@ pub struct Signature { pub signature: BytesN<64>, } +/// Errors returned by the multisig-account contract's public entry points. #[contracterror] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[repr(u32)] pub enum Error { + /// The configured threshold is zero or exceeds the signer count. InvalidThreshold = 1, + /// The signer configuration contains duplicate public keys. DuplicateSigner = 2, + /// The supplied signature count is below the configured threshold. NotEnoughSigners = 3, + /// Signatures are not strictly ordered by ascending public key. BadSignatureOrder = 4, + /// A signature corresponds to a public key that is not a configured signer. UnknownSigner = 5, + /// The contract has not been initialized; threshold or signer count is unavailable. NotInitialized = 6, + /// The supplied signature count exceeds the configured signer count. TooManySigners = 7, } From 6da7940cd8ca70d4ba3fe4c0dafb7ba0f67b0c25 Mon Sep 17 00:00:00 2001 From: alfred micheal Date: Fri, 28 Aug 2026 00:26:59 +0100 Subject: [PATCH 3/4] docs: add doc comments to multisig-account's __constructor and __check_auth, cross-reference ADR (#179) --- contracts/multisig-account/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/contracts/multisig-account/src/lib.rs b/contracts/multisig-account/src/lib.rs index 3bf2262..1e0f19d 100644 --- a/contracts/multisig-account/src/lib.rs +++ b/contracts/multisig-account/src/lib.rs @@ -57,6 +57,11 @@ pub struct MultisigAccount; #[contractimpl] impl MultisigAccount { + /// Initialize the multisig account with a set of authorized signers and a signature threshold. + /// + /// # Arguments + /// * `signers` — A vector of ed25519 public keys (32 bytes each) authorized to sign transactions. + /// * `threshold` — The minimum number of signatures required to authorize a transaction; must be > 0 and ≤ the signer count. pub fn __constructor(env: Env, signers: Vec>, threshold: u32) { if threshold == 0 || threshold > signers.len() { panic_with_error!(&env, Error::InvalidThreshold); @@ -84,6 +89,15 @@ impl CustomAccountInterface for MultisigAccount { type Signature = Vec; type Error = Error; + /// Verify the authorization of a transaction by checking N-of-M ed25519 signatures. + /// + /// Verifies that the supplied signatures meet the configured threshold and each belongs to + /// an authorized signer, with signatures ordered in ascending public-key order. + /// + /// # Arguments + /// * `signature_payload` — A 32-byte hash of the transaction to authorize. + /// * `signatures` — A vector of ed25519 signatures, each with a public key and signature bytes, ordered by ascending public key. + /// * `_auth_contexts` — Intentionally unused; see [ADR-0007](../adr/0007-unscoped-multisig-authorization.md) for why this account does not scope authorization to specific contracts or functions during pre-alpha. fn __check_auth( env: Env, signature_payload: Hash<32>, From 61b911e1e793a7ac9ad0d6bebc7982cd7c07c9a3 Mon Sep 17 00:00:00 2001 From: alfred micheal Date: Fri, 28 Aug 2026 00:27:19 +0100 Subject: [PATCH 4/4] test: add 3-of-5 multisig authorization test generalizing beyond 2-of-3 (#180) --- contracts/multisig-account/src/test.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/contracts/multisig-account/src/test.rs b/contracts/multisig-account/src/test.rs index 638e820..e4a26ee 100644 --- a/contracts/multisig-account/src/test.rs +++ b/contracts/multisig-account/src/test.rs @@ -179,3 +179,22 @@ fn too_many_signatures_is_rejected() { Err(Ok(Error::TooManySigners)) ); } + +#[test] +fn three_of_five_signers_authorize() { + let env = Env::default(); + let mut keys = std::vec![ + SigningKey::from_bytes(&[1; 32]), + SigningKey::from_bytes(&[2; 32]), + SigningKey::from_bytes(&[3; 32]), + SigningKey::from_bytes(&[4; 32]), + SigningKey::from_bytes(&[5; 32]), + ]; + keys.sort_by_key(|key| key.verifying_key().to_bytes()); + let account = register_account(&env, &keys, 3); + let payload = BytesN::from_array(&env, &[7; 32]); + // Supply exactly 3 signatures from the 5 signers in correct order + let signatures = signatures_for(&env, &keys[..3], &payload.to_array()); + + assert_eq!(check_auth(&env, &account, &payload, signatures), Ok(())); +}