-
Notifications
You must be signed in to change notification settings - Fork 6
feat!: introduces multisignature abstractions in utils #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e54cab1
80f8517
439e267
aa934dc
6c70c01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||||||||||||||||||
use algokit_transact::{Address, MultisigSubsignature}; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
#[derive(Debug, thiserror::Error)] | ||||||||||||||||||||||||
pub enum MultisigError { | ||||||||||||||||||||||||
#[error("Invalid multisig account: {0}")] | ||||||||||||||||||||||||
InvalidMultisigSignature(#[from] algokit_transact::AlgoKitTransactError), | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
#[derive(Clone)] | ||||||||||||||||||||||||
pub struct MultisigAccount { | ||||||||||||||||||||||||
pub version: u8, | ||||||||||||||||||||||||
pub threshold: u8, | ||||||||||||||||||||||||
pub participants: Vec<Address>, | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
impl From<algokit_transact::MultisigSignature> for MultisigAccount { | ||||||||||||||||||||||||
fn from(multisig: algokit_transact::MultisigSignature) -> Self { | ||||||||||||||||||||||||
Self { | ||||||||||||||||||||||||
version: multisig.version, | ||||||||||||||||||||||||
threshold: multisig.threshold, | ||||||||||||||||||||||||
participants: multisig.participants(), | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
impl TryFrom<MultisigAccount> for algokit_transact::MultisigSignature { | ||||||||||||||||||||||||
type Error = MultisigError; | ||||||||||||||||||||||||
fn try_from(account: MultisigAccount) -> Result<Self, Self::Error> { | ||||||||||||||||||||||||
Ok(algokit_transact::MultisigSignature::from_participants( | ||||||||||||||||||||||||
account.version, | ||||||||||||||||||||||||
account.threshold, | ||||||||||||||||||||||||
account.participants, | ||||||||||||||||||||||||
)?) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
impl TryFrom<MultisigAccount> for Address { | ||||||||||||||||||||||||
type Error = MultisigError; | ||||||||||||||||||||||||
fn try_from(account: MultisigAccount) -> Result<Self, Self::Error> { | ||||||||||||||||||||||||
let msig_signature: algokit_transact::MultisigSignature = account.try_into()?; | ||||||||||||||||||||||||
Ok(msig_signature.into()) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||
pub struct MultisigSignature { | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed or can we leverage the existing algokit_transact MultisigSignature? |
||||||||||||||||||||||||
pub subsignatures: Vec<Option<MultisigSubsignature>>, | ||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||
use algokit_transact::{Address, SignedTransaction, Transaction}; | ||||||||||||||||||||||||||
use crate::multisig::MultisigAccount; | ||||||||||||||||||||||||||
use algokit_transact::{ALGORAND_SIGNATURE_BYTE_LENGTH, Address, SignedTransaction, Transaction}; | ||||||||||||||||||||||||||
use async_trait::async_trait; | ||||||||||||||||||||||||||
use derive_more::Debug; | ||||||||||||||||||||||||||
use std::sync::Arc; | ||||||||||||||||||||||||||
|
@@ -15,8 +16,8 @@ pub trait TransactionSigner: Send + Sync { | |||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||
transaction: &Transaction, | ||||||||||||||||||||||||||
) -> Result<SignedTransaction, String> { | ||||||||||||||||||||||||||
let result = self.sign_transactions(&[transaction.clone()], &[0]).await?; | ||||||||||||||||||||||||||
Ok(result[0].clone()) | ||||||||||||||||||||||||||
let mut result = self.sign_transactions(&[transaction.clone()], &[0]).await?; | ||||||||||||||||||||||||||
Ok(result.remove(0)) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -25,10 +26,10 @@ pub trait TransactionSignerGetter { | |||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
#[derive(Clone)] | ||||||||||||||||||||||||||
pub struct EmptySigner {} | ||||||||||||||||||||||||||
pub struct EmptyKeyPairSigner {} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
#[async_trait] | ||||||||||||||||||||||||||
impl TransactionSigner for EmptySigner { | ||||||||||||||||||||||||||
impl TransactionSigner for EmptyKeyPairSigner { | ||||||||||||||||||||||||||
async fn sign_transactions( | ||||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||
txns: &[Transaction], | ||||||||||||||||||||||||||
|
@@ -40,7 +41,7 @@ impl TransactionSigner for EmptySigner { | |||||||||||||||||||||||||
if idx < txns.len() { | ||||||||||||||||||||||||||
Ok(SignedTransaction { | ||||||||||||||||||||||||||
transaction: txns[idx].clone(), | ||||||||||||||||||||||||||
signature: Some([0; 64]), | ||||||||||||||||||||||||||
signature: Some([0; ALGORAND_SIGNATURE_BYTE_LENGTH]), | ||||||||||||||||||||||||||
auth_address: None, | ||||||||||||||||||||||||||
multisignature: None, | ||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||
|
@@ -52,7 +53,47 @@ impl TransactionSigner for EmptySigner { | |||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
impl TransactionSignerGetter for EmptySigner { | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||
#[derive(Clone)] | ||||||||||||||||||||||||||
pub struct EmptyMultisigSigner { | ||||||||||||||||||||||||||
multisig: MultisigAccount, | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
#[async_trait] | ||||||||||||||||||||||||||
impl TransactionSigner for EmptyMultisigSigner { | ||||||||||||||||||||||||||
async fn sign_transactions( | ||||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||
txns: &[Transaction], | ||||||||||||||||||||||||||
indices: &[usize], | ||||||||||||||||||||||||||
) -> Result<Vec<SignedTransaction>, String> { | ||||||||||||||||||||||||||
let mut multisig: algokit_transact::MultisigSignature = self | ||||||||||||||||||||||||||
.multisig | ||||||||||||||||||||||||||
.clone() | ||||||||||||||||||||||||||
.try_into() | ||||||||||||||||||||||||||
.map_err(|e| format!("Failed to convert multisig: {}", e))?; | ||||||||||||||||||||||||||
multisig | ||||||||||||||||||||||||||
.subsignatures | ||||||||||||||||||||||||||
.iter_mut() | ||||||||||||||||||||||||||
.for_each(|subsig| subsig.signature = Some([0; ALGORAND_SIGNATURE_BYTE_LENGTH])); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Comment on lines
+73
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting all subsignatures to zero bytes creates an invalid multisig signature. This could be misleading for testing purposes. Consider adding a comment explaining that this creates dummy signatures for testing, or provide a way to create valid empty signatures.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||
indices | ||||||||||||||||||||||||||
.iter() | ||||||||||||||||||||||||||
.map(|&idx| { | ||||||||||||||||||||||||||
if idx < txns.len() { | ||||||||||||||||||||||||||
Ok(SignedTransaction { | ||||||||||||||||||||||||||
transaction: txns[idx].clone(), | ||||||||||||||||||||||||||
signature: None, | ||||||||||||||||||||||||||
auth_address: None, | ||||||||||||||||||||||||||
multisignature: Some(multisig.clone()), | ||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||
Err(format!("Index {} out of bounds for transactions", idx)) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||
.collect() | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
impl TransactionSignerGetter for EmptyKeyPairSigner { | ||||||||||||||||||||||||||
fn get_signer(&self, _address: Address) -> Option<Arc<dyn TransactionSigner>> { | ||||||||||||||||||||||||||
Some(Arc::new(self.clone())) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not completely clear how we intend this to be used.
The
MultisigAccount
from utils-ts for example requires either accounts with private keys or signer accounts on construction, giving the account the required info to be able to also sign the transaction. https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/types/account.ts#L20