algorand_rs/
account.rs

1use data_encoding::BASE32_NOPAD;
2use rand::rngs::OsRng;
3use rand::Rng;
4use ring::signature::Ed25519KeyPair as KeyPairType;
5use ring::signature::KeyPair;
6
7use crate::auction::{Bid, SignedBid};
8use crate::crypto::{Address, MultisigAddress, MultisigSignature, MultisigSubsig, Signature};
9use crate::error::{ApiError, Result};
10use crate::models::Ed25519PublicKey;
11use crate::transaction::{SignedTransaction, Transaction};
12
13use sha2::Digest;
14use std::borrow::Borrow;
15
16type ChecksumAlg = sha2::Sha512Trunc256;
17
18pub struct Account {
19    seed: [u8; 32],
20    address: Address,
21    key_pair: KeyPairType,
22}
23
24impl Account {
25    pub fn generate() -> Account {
26        let seed: [u8; 32] = OsRng.gen();
27        Self::from_seed(seed)
28    }
29
30    /// Create account from human readable mnemonic of a 32 byte seed
31    pub fn from_mnemonic(mnemonic: &str) -> Result<Account> {
32        let seed = crate::mnemonic::to_key(mnemonic)?;
33        Ok(Self::from_seed(seed))
34    }
35
36    /// Create account from 32 byte seed
37    pub fn from_seed(seed: [u8; 32]) -> Account {
38        let key_pair = KeyPairType::from_seed_unchecked(&seed).unwrap();
39        let mut pk = [0; 32];
40        pk.copy_from_slice(key_pair.public_key().as_ref());
41        let address = Address::new(pk);
42        Account {
43            seed,
44            address,
45            key_pair,
46        }
47    }
48
49    /// Get the public key address of the account
50    pub fn address(&self) -> Address {
51        self.address
52    }
53
54    /// Get the human readable mnemonic of the 32 byte seed
55    pub fn mnemonic(&self) -> String {
56        crate::mnemonic::from_key(&self.seed).unwrap()
57    }
58
59    /// Get the 32 byte seed
60    pub fn seed(&self) -> [u8; 32] {
61        self.seed
62    }
63
64    fn sign(&self, bytes: &[u8]) -> Signature {
65        let signature = self.key_pair.sign(&bytes);
66        // ring returns a signature with padding at the end to make it 105 bytes, only 64 bytes are actually used
67        let mut stripped_signature = [0; 64];
68        stripped_signature.copy_from_slice(&signature.as_ref()[..64]);
69        Signature(stripped_signature)
70    }
71
72    /// Sign a bid with the account's private key
73    pub fn sign_bid(&self, bid: Bid) -> Result<SignedBid> {
74        let encoded_bid = rmp_serde::to_vec_named(&bid)?;
75        let mut prefix_encoded_bid = b"aB".to_vec();
76        prefix_encoded_bid.extend_from_slice(&encoded_bid);
77        let signature = self.sign(&prefix_encoded_bid);
78        Ok(SignedBid {
79            bid,
80            sig: signature,
81        })
82    }
83
84    /// Sign a transaction with the account's private key
85    pub fn sign_transaction(&self, transaction: &Transaction) -> Result<SignedTransaction> {
86        let encoded_tx = rmp_serde::to_vec_named(transaction)?;
87        let mut prefix_encoded_tx = b"TX".to_vec();
88        prefix_encoded_tx.extend_from_slice(&encoded_tx);
89        let signature = self.sign(&prefix_encoded_tx);
90        let id = BASE32_NOPAD.encode(&ChecksumAlg::digest(&signature.0));
91        Ok(SignedTransaction {
92            transaction: transaction.clone(),
93            sig: Some(signature),
94            multisig: None,
95            transaction_id: id,
96        })
97    }
98
99    /// Sign the transaction and populate the multisig field of the signed transaction with the given multisig address
100    pub fn sign_multisig_transaction(
101        &self,
102        from: MultisigAddress,
103        transaction: &Transaction,
104    ) -> Result<SignedTransaction> {
105        if from.address() != transaction.sender {
106            return Err(ApiError::InvalidSenderInMultisig.into());
107        }
108        let my_public_key = Ed25519PublicKey(self.address.0);
109        if !from.public_keys.contains(&my_public_key) {
110            return Err(ApiError::InvalidSecretKeyInMultisig.into());
111        }
112        let signed_transaction = self.sign_transaction(transaction)?;
113        let subsigs: Vec<MultisigSubsig> = from
114            .public_keys
115            .iter()
116            .map(|key| {
117                if *key == my_public_key {
118                    MultisigSubsig {
119                        key: *key,
120                        sig: signed_transaction.clone().sig,
121                    }
122                } else {
123                    MultisigSubsig {
124                        key: *key,
125                        sig: None,
126                    }
127                }
128            })
129            .collect();
130        let multisig = MultisigSignature {
131            version: from.version,
132            threshold: from.threshold,
133            subsigs,
134        };
135        Ok(SignedTransaction {
136            multisig: Some(multisig),
137            sig: None,
138            transaction: transaction.clone(),
139            transaction_id: signed_transaction.transaction_id,
140        })
141    }
142
143    /// Appends the multisig signature from the given multisig address to the transaction
144    pub fn append_multisig_transaction(
145        &self,
146        from: MultisigAddress,
147        transaction: &SignedTransaction,
148    ) -> Result<SignedTransaction> {
149        let from_transaction = self.sign_multisig_transaction(from, &transaction.transaction)?;
150        Self::merge_multisig_transactions(&[&from_transaction, transaction])
151    }
152
153    /// Returns a signed transaction with the multisig signatures of the passed signed transactions merged
154    pub fn merge_multisig_transactions<T: Borrow<SignedTransaction>>(
155        transactions: &[T],
156    ) -> Result<SignedTransaction> {
157        if transactions.len() < 2 {
158            return Err(ApiError::InsufficientTransactions.into());
159        }
160        let mut merged = transactions[0].borrow().clone();
161        for transaction in transactions {
162            let merged_msig = merged.multisig.as_mut().unwrap();
163            let msig = transaction.borrow().multisig.as_ref().unwrap();
164            if merged_msig.subsigs.len() != msig.subsigs.len() {
165                return Err(ApiError::InvalidNumberOfSubsignatures.into());
166            }
167            assert_eq!(merged_msig.subsigs.len(), msig.subsigs.len());
168            for (merged_subsig, subsig) in merged_msig.subsigs.iter_mut().zip(&msig.subsigs) {
169                if subsig.key != merged_subsig.key {
170                    return Err(ApiError::InvalidPublicKeyInMultisig.into());
171                }
172                if merged_subsig.sig.is_none() {
173                    merged_subsig.sig = subsig.sig
174                } else if merged_subsig.sig != subsig.sig && subsig.sig.is_some() {
175                    return Err(ApiError::MismatchingSignatures.into());
176                }
177            }
178        }
179        Ok(merged)
180    }
181}