concordium_rust_sdk/
signatures.rs

1//! Functionality for generating, and verifying account signatures.
2use crate::v2::{self, AccountIdentifier, BlockIdentifier, QueryError};
3use concordium_base::{
4    common::{
5        types::{CredentialIndex, KeyIndex, KeyPair, Signature},
6        Versioned,
7    },
8    contracts_common::{AccountAddress, SignatureThreshold},
9    curve_arithmetic::Curve,
10    id::types::{
11        AccountCredentialWithoutProofs, AccountKeys, Attribute, InitialAccountData,
12        PublicCredentialData, VerifyKey,
13    },
14};
15use ed25519_dalek::{SigningKey, VerifyingKey};
16use sha2::Digest;
17use std::collections::BTreeMap;
18
19/// Errors that can occur during generating or verifying account signatures.
20#[derive(thiserror::Error, Debug)]
21pub enum SignatureError {
22    #[error("Network error: {0}")]
23    QueryError(#[from] QueryError),
24    #[error(
25        "Indices do not exist on chain for credential index `{credential_index}` and key index \
26         `{key_index}`"
27    )]
28    MissingIndicesOnChain {
29        credential_index: u8,
30        key_index:        u8,
31    },
32    #[error("The indices in the maps do not match")]
33    MismatchMapIndices,
34    #[error(
35        "The public key and the private key in the `account_keys` map do not match for credential \
36         index `{credential_index}` and key index `{key_index}`"
37    )]
38    MismatchPublicPrivateKeys {
39        credential_index: u8,
40        key_index:        u8,
41    },
42    #[error(
43        "The public key on chain `{expected_public_key:?}` and the public key \
44         `{actual_public_key:?}` in the signature map do not match for credential index \
45         `{credential_index}` and key index `{key_index}`"
46    )]
47    MismatchPublicKeyOnChain {
48        credential_index:    u8,
49        key_index:           u8,
50        // Because of a clippy warning that this enum variant is quite large, `Box` is used.
51        expected_public_key: Box<VerifyingKey>,
52        actual_public_key:   Box<VerifyingKey>,
53    },
54}
55
56/// Account signatures are constructed similarly to transaction signatures. The
57/// only difference is that account signatures use as account nonce the value 0
58/// when signing. This is to ensure that the user does not accidentally sign a
59/// valid transaction. As such account signatures can be used to sign messages
60/// in wallets that can then be verified in a smart contract or by a backend.
61///
62/// Account signatures should be thought of as a nested map, indexed on the
63/// outer layer by credential indices, and the inner map maps key indices to
64/// [`Signature`]s.
65#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
66#[repr(transparent)]
67pub struct AccountSignatures {
68    pub sigs: BTreeMap<u8, CredentialSignatures>,
69}
70
71#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
72#[repr(transparent)]
73pub struct CredentialSignatures {
74    pub sigs: BTreeMap<u8, Signature>,
75}
76
77impl AccountSignatures {
78    /// Create the account signature type from a single signature at credential
79    /// and key indices 0. This is a helper function to create the type when
80    /// dealing with a single-signer account (in contrast to a multi-sig
81    /// account). The naming `singleton` is used in Concordium's code base
82    /// to indicate that there is only one element in the maps.
83    pub fn singleton(signature: Signature) -> Self {
84        let credential_map = CredentialSignatures {
85            sigs: [(0, signature)].into(),
86        };
87
88        AccountSignatures {
89            sigs: [(0, credential_map)].into(),
90        }
91    }
92}
93
94/// Account signatures verification data includes the [`Signature`]s and
95/// associated [`VerifyKey`]s for some credentials and key indices.
96///
97/// Account signatures verification data should be thought of as a nested map,
98/// indexed on the outer layer by credential indices, and the inner map maps key
99/// indices to [`Signature`]s and [`VerifyKey`]s.
100#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
101#[repr(transparent)]
102pub struct AccountSignaturesVerificationData {
103    pub data: BTreeMap<u8, CredentialSignaturesVerificationData>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
107#[repr(transparent)]
108pub struct CredentialSignaturesVerificationData {
109    pub data: BTreeMap<u8, AccountSignaturesVerificationEntry>,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
113pub struct AccountSignaturesVerificationEntry {
114    pub signature:  Signature,
115    pub public_key: VerifyKey,
116}
117
118impl AccountSignaturesVerificationData {
119    /// Create the account signatures verification data type from a single
120    /// signature and public key at credential and key indices 0. This is a
121    /// helper function to create the type when dealing with a single-signer
122    /// account (in contrast to a multi-sig account). The naming `singleton`
123    /// is used in Concordium's code base to indicate that there is only one
124    /// element in the maps.
125    pub fn singleton(signature: Signature, public_key: VerifyKey) -> Self {
126        let credential_map = CredentialSignaturesVerificationData {
127            data: [(0, AccountSignaturesVerificationEntry {
128                signature,
129                public_key,
130            })]
131            .into(),
132        };
133
134        AccountSignaturesVerificationData {
135            data: [(0, credential_map)].into(),
136        }
137    }
138
139    /// Zip an [`AccountSignatures`] and an [`AccountKeys`] into
140    /// an [`AccountSignaturesVerificationData`] type. The function returns an
141    /// error if the indices in the maps do not match.
142    pub fn zip_signatures_and_keys(
143        account_signatures: AccountSignatures,
144        account_keys: AccountKeys,
145    ) -> Result<Self, SignatureError> {
146        let mut outer_map = BTreeMap::new();
147
148        // Check that the length of the outer map matches.
149        if account_signatures.sigs.len() != account_keys.keys.len() {
150            return Err(SignatureError::MismatchMapIndices);
151        }
152
153        for (outer_key, credential_sigs) in account_signatures.sigs {
154            // Check if corresponding account key exists.
155
156            let Some(account_key_pair) =
157                account_keys.keys.get(&CredentialIndex { index: outer_key })
158            else {
159                return Err(SignatureError::MismatchMapIndices);
160            };
161
162            let public_keys = account_key_pair.get_public_keys();
163
164            // Check that the length of the inner map matches.
165            if credential_sigs.sigs.len() != public_keys.len() {
166                return Err(SignatureError::MismatchMapIndices);
167            }
168
169            // Create the zipped inner map.
170            let inner_map: Result<
171                BTreeMap<u8, AccountSignaturesVerificationEntry>,
172                SignatureError,
173            > = credential_sigs
174                .sigs
175                .into_iter()
176                .zip(public_keys.into_iter())
177                .map(|((inner_key, signature), (key_index, public_key))| {
178                    // Ensure that inner_key and key_index matches.
179                    if inner_key != key_index.0 {
180                        return Err(SignatureError::MismatchMapIndices);
181                    }
182                    Ok((inner_key, AccountSignaturesVerificationEntry {
183                        signature,
184                        public_key,
185                    }))
186                })
187                .collect();
188
189            outer_map.insert(outer_key, CredentialSignaturesVerificationData {
190                data: inner_map?,
191            });
192        }
193
194        Ok(AccountSignaturesVerificationData { data: outer_map })
195    }
196}
197
198/// Calculate the message hash that is signed in Concordium wallets.
199pub fn calculate_message_hash(message: impl AsRef<[u8]>, signer: AccountAddress) -> [u8; 32] {
200    // A message signed in a Concordium wallet is prepended with the
201    // `account` address (signer) and 8 zero bytes. Accounts in a Concordium
202    // wallet can either sign a regular transaction (in that case the
203    // prepend is `account` address and the nonce of the account which is by
204    // design >= 1) or sign a message (in that case the prepend is `account`
205    // address and 8 zero bytes). Hence, the 8 zero bytes ensure that the user
206    // does not accidentally sign a transaction. The account nonce is of type
207    // u64 (8 bytes).
208    let mut hasher = sha2::Sha256::new();
209    hasher.update(signer);
210    hasher.update([0u8; 8]);
211    hasher.update(message);
212    hasher.finalize().into()
213}
214
215/// Check that all key indices in the signatures map exist on chain but not vice
216/// versa.
217fn check_signature_map_key_indices_on_chain<C: Curve, AttributeType: Attribute<C::Scalar>>(
218    signatures: &AccountSignatures,
219    on_chain_credentials: &BTreeMap<
220        CredentialIndex,
221        Versioned<AccountCredentialWithoutProofs<C, AttributeType>>,
222    >,
223) -> Result<(), SignatureError> {
224    // Ensure all outer-level keys in the signatures map exist in the
225    // on_chain_credentials map.
226    for (outer_key, inner_map) in &signatures.sigs {
227        // Check if the outer_key exists in the on_chain_credentials map.
228        let on_chain_cred = on_chain_credentials
229            .get(&CredentialIndex { index: *outer_key })
230            .ok_or(SignatureError::MissingIndicesOnChain {
231                credential_index: *outer_key,
232                // The key_index does not exist in this context, use the default value.
233                key_index:        0u8,
234            })?;
235
236        // Ensure that the inner-level keys in the signatures map exist in the
237        // on_chain_credentials map.
238        for inner_key in inner_map.sigs.keys() {
239            let map = match &on_chain_cred.value {
240                AccountCredentialWithoutProofs::Initial { icdv } => &icdv.cred_account.keys,
241                AccountCredentialWithoutProofs::Normal { cdv, .. } => &cdv.cred_key_info.keys,
242            };
243
244            if !map.contains_key(&KeyIndex(*inner_key)) {
245                return Err(SignatureError::MissingIndicesOnChain {
246                    credential_index: *outer_key,
247                    key_index:        *inner_key,
248                });
249            }
250        }
251    }
252    Ok(())
253}
254
255/// Verify a given message was signed by the given account.
256/// Concretely this means:
257/// - enough credential holders signed the message.
258/// - each of the credential signatures has the required number of signatures.
259/// - all of the signatures are valid, that is, it is not sufficient that a
260///   threshold number are valid, and some extra signatures included are
261///   invalid.
262pub async fn verify_account_signature(
263    mut client: v2::Client,
264    signer: AccountAddress,
265    signatures: &AccountSignatures,
266    message: impl AsRef<[u8]>,
267    bi: BlockIdentifier,
268) -> Result<bool, SignatureError> {
269    let message_hash = calculate_message_hash(message, signer);
270
271    let signer_account_info = client
272        .get_account_info(&AccountIdentifier::Address(signer), bi)
273        .await?;
274
275    let signer_account_credentials = signer_account_info.response.account_credentials;
276    let credential_signatures_threshold = signer_account_info.response.account_threshold;
277
278    // Ensure that all key indices in the signatures map exist on chain but not vice
279    // versa. This allows us to iterate through the `signer_account_credentials` map
280    // to get the public keys and thresholds from the on-chain information
281    // further down while still knowing that each signature in the signatures map
282    // has a corresponding entry on chain.
283    check_signature_map_key_indices_on_chain(signatures, &signer_account_credentials)?;
284
285    let mut valid_credential_signatures_count = 0u8;
286    for (credential_index, credential) in signer_account_credentials {
287        // Retrieve the public key and threshold from the on-chain information.
288        let (keys, signatures_threshold) = match credential.value {
289            AccountCredentialWithoutProofs::Initial { icdv } => {
290                (icdv.cred_account.keys, icdv.cred_account.threshold)
291            }
292            AccountCredentialWithoutProofs::Normal { cdv, .. } => {
293                (cdv.cred_key_info.keys, cdv.cred_key_info.threshold)
294            }
295        };
296
297        let mut valid_signatures_count = 0u8;
298
299        for (key_index, public_key) in keys {
300            // If a signature exists for the given credential and key index, verify it and
301            // increase the `valid_signatures_count`.
302            let Some(cred_sigs) = signatures.sigs.get(&credential_index.index) else {
303                continue;
304            };
305
306            let Some(signature) = cred_sigs.sigs.get(&key_index.0) else {
307                continue;
308            };
309
310            if public_key.verify(message_hash, signature) {
311                // If the signature is valid, increase the `valid_signatures_count`.
312                valid_signatures_count += 1;
313            } else {
314                // If any signature is invalid, return `false`.
315                return Ok(false);
316            }
317        }
318
319        // Check if the number of valid signatures meets the required threshold
320        // so that this credential counts as having a valid credential signature.
321        if valid_signatures_count >= signatures_threshold.into() {
322            valid_credential_signatures_count += 1;
323        }
324    }
325
326    // Check if the total number of valid credential signatures meets the required
327    // threshold.
328    Ok(valid_credential_signatures_count >= credential_signatures_threshold.into())
329}
330
331/// Wrapper around the [`verify_account_signature`] function to simplify the
332/// verification for single-signer accounts.
333pub async fn verify_single_account_signature(
334    client: v2::Client,
335    signer: AccountAddress,
336    signature: Signature,
337    message: impl AsRef<[u8]>,
338    bi: BlockIdentifier,
339) -> Result<bool, SignatureError> {
340    verify_account_signature(
341        client,
342        signer,
343        &AccountSignatures::singleton(signature),
344        message,
345        bi,
346    )
347    .await
348}
349
350/// Verifies account signatures similarly to the [`verify_account_signature`]
351/// function but does not perform any checks against the on-chain data such as:
352/// - if the signer account exists on chain.
353/// - if the credential or key indices from the `signature_data` map exist on
354///   chain.
355/// - if the thresholds on chain are followed.
356/// - if the public keys in the `signature_data` map match the public keys on
357///   chain.
358pub fn verify_account_signature_unchecked(
359    signer: AccountAddress,
360    signature_data: AccountSignaturesVerificationData,
361    message: impl AsRef<[u8]>,
362) -> Result<bool, SignatureError> {
363    let message_hash = calculate_message_hash(message, signer);
364
365    for credential in signature_data.data.values() {
366        for AccountSignaturesVerificationEntry {
367            signature,
368            public_key,
369        } in credential.data.values()
370        {
371            if !public_key.verify(message_hash, signature) {
372                return Ok(false);
373            }
374        }
375    }
376
377    Ok(true)
378}
379
380/// Wrapper around the [`verify_account_signature_unchecked`] function to
381/// simplify the verification for single-signer accounts.
382pub fn verify_single_account_signature_unchecked(
383    signer: AccountAddress,
384    signature: Signature,
385    public_key: VerifyKey,
386    message: impl AsRef<[u8]>,
387) -> Result<bool, SignatureError> {
388    verify_account_signature_unchecked(
389        signer,
390        AccountSignaturesVerificationData::singleton(signature, public_key),
391        message,
392    )
393}
394
395/// Generate [`AccountSignatures`] for a signer account with the given
396/// `account_keys`. The function does perform checks against the on-chain data
397/// such as:
398/// - if the signer account exists on chain.
399/// - if the credential or key indices from the `account_keys` map exist on
400///   chain.
401/// - if the public keys in the `account_keys` map match the public keys on
402///   chain.
403/// - if the public keys in the `account_keys` map match the private keys in the
404///   `account_keys` map.
405pub async fn sign_as_account(
406    mut client: v2::Client,
407    signer: AccountAddress,
408    account_keys: AccountKeys,
409    message: impl AsRef<[u8]>,
410    bi: BlockIdentifier,
411) -> Result<AccountSignatures, SignatureError> {
412    let message_hash = calculate_message_hash(message, signer);
413
414    // Generate the return type.
415    let mut account_signatures = AccountSignatures {
416        sigs: BTreeMap::new(),
417    };
418
419    let signer_account_info = client
420        .get_account_info(&AccountIdentifier::Address(signer), bi)
421        .await?;
422
423    let signer_account_credentials = signer_account_info.response.account_credentials;
424
425    // Iterate through the `account_keys` map.
426    for (credential_index, credential) in account_keys.keys {
427        for (key_index, signing_key) in credential.keys {
428            let on_chain_credential = &signer_account_credentials
429                .get(&credential_index)
430                .ok_or(SignatureError::MissingIndicesOnChain {
431                    credential_index: credential_index.index,
432                    key_index:        key_index.0,
433                })?
434                .value;
435
436            let on_chain_keys = match on_chain_credential {
437                AccountCredentialWithoutProofs::Initial { icdv } => &icdv.cred_account.keys,
438                AccountCredentialWithoutProofs::Normal { cdv, .. } => &cdv.cred_key_info.keys,
439            };
440
441            let on_chain_public_key =
442                on_chain_keys
443                    .get(&key_index)
444                    .ok_or(SignatureError::MissingIndicesOnChain {
445                        credential_index: credential_index.index,
446                        key_index:        key_index.0,
447                    })?;
448
449            let VerifyKey::Ed25519VerifyKey(public_key) = *on_chain_public_key;
450
451            // Check that the public key in the `account_keys` map matches the public key on
452            // chain.
453            if signing_key.public() != public_key {
454                return Err(SignatureError::MismatchPublicKeyOnChain {
455                    credential_index:    credential_index.index,
456                    key_index:           key_index.0,
457                    expected_public_key: Box::new(public_key),
458                    actual_public_key:   Box::new(signing_key.public()),
459                });
460            };
461
462            // Generate the signature.
463            let signature = signing_key.sign(&message_hash);
464
465            // Check that the signature is valid to ensure
466            // that the public and private keys in the `account_keys` map match.
467            if !VerifyKey::from(public_key).verify(message_hash, &Signature::from(signature)) {
468                return Err(SignatureError::MismatchPublicPrivateKeys {
469                    credential_index: credential_index.index,
470                    key_index:        key_index.0,
471                });
472            }
473
474            // Insert the generated signature into the return map.
475            account_signatures
476                .sigs
477                .entry(credential_index.index)
478                .or_insert_with(|| CredentialSignatures {
479                    sigs: BTreeMap::new(),
480                })
481                .sigs
482                .insert(key_index.0, signature.into());
483        }
484    }
485
486    Ok(account_signatures)
487}
488
489/// Wrapper around the [`sign_as_account`] function to simplify the signature
490/// generation for single-signer accounts.
491pub async fn sign_as_single_signer_account(
492    client: v2::Client,
493    signer: AccountAddress,
494    signing_key: SigningKey,
495    message: impl AsRef<[u8]>,
496    bi: BlockIdentifier,
497) -> Result<Signature, SignatureError> {
498    let keypair: KeyPair = KeyPair::from(signing_key);
499    // Generate account keys that have one keypair at index 0 in both maps.
500    let keypairs = AccountKeys::from(InitialAccountData {
501        keys:      [(KeyIndex(0), keypair)].into(),
502        threshold: SignatureThreshold::ONE,
503    });
504    let mut signature = sign_as_account(client, signer, keypairs, message, bi).await?;
505    // Accessing the maps at indices 0 and unwrapping is safe because we generated
506    // an AccountSignature with a keypair at index 0 at both maps.
507    Ok(signature.sigs.get_mut(&0).unwrap().sigs.remove(&0).unwrap())
508}
509
510/// Generates account signatures similarly to the [`sign_as_account`] function
511/// but does not perform any checks against the on-chain data such as:
512/// - if the signer account exists on chain.
513/// - if the credential or key indices from the `account_keys` map exist on
514///   chain.
515/// - if the public keys in the `account_keys` map match the public keys on
516///   chain.
517/// - if the public keys in the `account_keys` map match the private keys in the
518///   `account_keys` map.
519pub fn sign_as_account_unchecked(
520    signer: AccountAddress,
521    account_keys: &AccountKeys,
522    message: impl AsRef<[u8]>,
523) -> AccountSignatures {
524    let message_hash = calculate_message_hash(message, signer);
525
526    // Generate the return type.
527    let mut account_signatures = AccountSignatures {
528        sigs: BTreeMap::new(),
529    };
530
531    for (credential_index, credential) in &account_keys.keys {
532        for (key_index, signing_keys) in &credential.keys {
533            let signature = signing_keys.sign(&message_hash);
534
535            account_signatures
536                .sigs
537                .entry(credential_index.index)
538                .or_insert_with(|| CredentialSignatures {
539                    sigs: BTreeMap::new(),
540                })
541                .sigs
542                .insert(key_index.0, signature.into());
543        }
544    }
545
546    account_signatures
547}
548
549/// Wrapper around the [`sign_as_account_unchecked`] function to simplify the
550/// signature generation for single-signer accounts.
551pub fn sign_as_single_signer_account_unchecked(
552    signer: AccountAddress,
553    signing_key: SigningKey,
554    message: impl AsRef<[u8]>,
555) -> Result<Signature, SignatureError> {
556    let keypair: KeyPair = KeyPair::from(signing_key);
557    // Generate account keys that have one keypair at index 0 in both maps.
558    let keypairs = AccountKeys::from(InitialAccountData {
559        keys:      [(KeyIndex(0), keypair)].into(),
560        threshold: SignatureThreshold::ONE,
561    });
562    let signature = sign_as_account_unchecked(signer, &keypairs, message);
563    // Accessing the maps at indices 0 is safe because we generated an
564    // AccountSignature with a keypair at index 0 at both maps.
565    Ok(signature.sigs[&0].sigs[&0].clone())
566}
567
568#[cfg(test)]
569mod tests {
570    use super::*;
571    use concordium_base::{base::AccountThreshold, id::types::CredentialData};
572    use std::str::FromStr;
573
574    const NODE_URL: &str = "http://node.testnet.concordium.com:20000";
575
576    #[test]
577    fn test_serde_account_signatures_verification_data() {
578        let rng = &mut rand::thread_rng();
579
580        // Generate account keys that have one keypair at index 0 in both maps.
581        let keypairs = AccountKeys::singleton(rng);
582        // Get the public key from the map.
583        let credential_keys = &keypairs.keys[&CredentialIndex { index: 0 }];
584        let key_pair = &credential_keys.keys[&KeyIndex(0)];
585        let public_key = key_pair.public().into();
586
587        // Generate a signature.
588        let signature = Signature::from(ed25519_dalek::Signature::from_bytes(&[0u8; 64]));
589
590        // Create an AccountSignaturesVerificationData type.
591        let account_signatures_verification_data =
592            AccountSignaturesVerificationData::singleton(signature, public_key);
593
594        // Check that the serialization and deserialization work.
595        let serialized = serde_json::to_string(&account_signatures_verification_data)
596            .expect("Failed to serialize account_signatures_verification_data");
597        let deserialized: AccountSignaturesVerificationData = serde_json::from_str(&serialized)
598            .expect("Failed to deserialize account_signatures_verification_data");
599        assert_eq!(account_signatures_verification_data, deserialized);
600    }
601
602    #[test]
603    fn test_serde_account_signatures() {
604        // Create an AccountSignatures type.
605        let ed25519_signature = ed25519_dalek::Signature::from_bytes(&[0u8; 64]);
606        let account_signatures = AccountSignatures::singleton(Signature::from(ed25519_signature));
607
608        // Check that the serialization and deserialization work.
609        let serialized = serde_json::to_string(&account_signatures)
610            .expect("Failed to serialize account_signatures");
611        let deserialized: AccountSignatures =
612            serde_json::from_str(&serialized).expect("Failed to deserialize account_signatures");
613        assert_eq!(account_signatures, deserialized);
614    }
615
616    // We test signing and verifying a text message here. We use the `unchecked`
617    // version of the functions.
618    #[test]
619    fn test_sign_and_verify_text_message_unchecked() {
620        // Create a message to sign.
621        let message: &str = "test";
622        let text_message = message.as_bytes();
623
624        let rng = &mut rand::thread_rng();
625
626        // Generate account keys that have one keypair at index 0 in both maps.
627        let keypairs = AccountKeys::singleton(rng);
628
629        // Create an account address.
630        let account_address = AccountAddress([0u8; 32]);
631
632        // Generate a signature.
633        let account_signature = sign_as_account_unchecked(account_address, &keypairs, text_message);
634
635        assert_eq!(account_signature.sigs.len(), 1);
636
637        let account_signatures_verification_data =
638            AccountSignaturesVerificationData::zip_signatures_and_keys(account_signature, keypairs)
639                .expect("Expect zipping of maps to succeed");
640
641        assert_eq!(account_signatures_verification_data.data.len(), 1);
642
643        // Check that the signature is valid.
644        let is_valid = verify_account_signature_unchecked(
645            account_address,
646            account_signatures_verification_data,
647            text_message,
648        )
649        .expect("Expect verification to succeed");
650        assert!(is_valid);
651    }
652
653    // We test signing and verifying a text message here. We use the `unchecked`
654    // and `single` versions of the functions.
655    #[test]
656    fn test_sign_and_verify_text_message_unchecked_single() {
657        // Create a message to sign.
658        let message: &str = "test";
659        let text_message = message.as_bytes();
660
661        let rng = &mut rand::thread_rng();
662
663        // Generate account keys that have one keypair at index 0 in both maps.
664        let keypairs = AccountKeys::singleton(rng);
665        let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
666
667        // Get the public key from the map.
668        let credential_keys = &keypairs.keys[&CredentialIndex { index: 0 }];
669        let key_pair = &credential_keys.keys[&KeyIndex(0)];
670        let public_key = key_pair.public();
671
672        // Create an account address.
673        let account_address = AccountAddress([0u8; 32]);
674
675        // Generate a signature.
676        let single_account_signature = sign_as_single_signer_account_unchecked(
677            account_address,
678            single_key.into(),
679            text_message,
680        )
681        .expect("Expect signing to succeed");
682
683        // Check that the signature is valid.
684        let is_valid = verify_single_account_signature_unchecked(
685            account_address,
686            single_account_signature,
687            public_key.into(),
688            text_message,
689        )
690        .expect("Expect verification to succeed");
691        assert!(is_valid);
692    }
693
694    // We test signing and verifying a text message here. We use the `checked`
695    // version of the functions.
696    #[tokio::test]
697    async fn test_sign_and_verify_text_message_checked() {
698        // Create a message to sign.
699        let message: &str = "test";
700        let text_message = message.as_bytes();
701
702        // Create a keypair from a private key.
703        let private_key = "f74e3188e4766841600f6fd0095a0ac1c30e4c2e97b9797d7e05a28a48f5c37c";
704        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
705        let signing_key = SigningKey::from_bytes(
706            bytes
707                .as_slice()
708                .try_into()
709                .expect("Invalid private key size"),
710        );
711        let keypair: KeyPair = KeyPair::from(signing_key);
712
713        // Generate account keys that have one keypair at index 0 in both maps.
714        let keypairs = AccountKeys::from(InitialAccountData {
715            keys:      [(KeyIndex(0), keypair)].into(),
716            threshold: SignatureThreshold::ONE,
717        });
718
719        // Add the corresponding account address from testnet associated with the above
720        // private key.
721        let account_address =
722            AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
723                .expect("Expect generating account address successfully");
724
725        // Establish a connection to the node client.
726        let client = v2::Client::new(
727            v2::Endpoint::new(NODE_URL).expect("Expect generating endpoint successfully"),
728        )
729        .await
730        .expect("Expect generating node client successfully");
731
732        // Generate a signature.
733        let account_signature = sign_as_account(
734            client.clone(),
735            account_address,
736            keypairs,
737            text_message,
738            BlockIdentifier::Best,
739        )
740        .await
741        .expect("Expect signing to succeed");
742
743        assert_eq!(account_signature.sigs.len(), 1);
744
745        // Check that the signature is valid.
746        let is_valid = verify_account_signature(
747            client.clone(),
748            account_address,
749            &account_signature,
750            text_message,
751            BlockIdentifier::Best,
752        )
753        .await
754        .expect("Expect verification to succeed");
755        assert!(is_valid);
756    }
757
758    // We test signing and verifying a text message here. We use the `checked`
759    // and `single` versions of the functions.
760    #[tokio::test]
761    async fn test_sign_and_verify_text_message_checked_single() {
762        // Create a message to sign.
763        let message: &str = "test";
764        let text_message = message.as_bytes();
765
766        // Create a keypair from a private key.
767        let private_key = "f74e3188e4766841600f6fd0095a0ac1c30e4c2e97b9797d7e05a28a48f5c37c";
768        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
769        let signing_key = SigningKey::from_bytes(
770            bytes
771                .as_slice()
772                .try_into()
773                .expect("Invalid private key size"),
774        );
775        let keypair: KeyPair = KeyPair::from(signing_key);
776
777        // Generate account keys that have one keypair at index 0 in both maps.
778        let keypairs = AccountKeys::from(InitialAccountData {
779            keys:      [(KeyIndex(0), keypair)].into(),
780            threshold: SignatureThreshold::ONE,
781        });
782        let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
783
784        // Add the corresponding account address from testnet associated with the above
785        // private key.
786        let account_address =
787            AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
788                .expect("Expect generating account address successfully");
789
790        // Establish a connection to the node client.
791        let client = v2::Client::new(
792            v2::Endpoint::new(NODE_URL).expect("Expect generating endpoint successfully"),
793        )
794        .await
795        .expect("Expect generating node client successfully");
796
797        // Generate a signature.
798        let single_account_signature = sign_as_single_signer_account(
799            client.clone(),
800            account_address,
801            single_key.into(),
802            text_message,
803            BlockIdentifier::Best,
804        )
805        .await
806        .expect("Expect signing to succeed");
807
808        // Check that the signature is valid.
809        let is_valid = verify_single_account_signature(
810            client,
811            account_address,
812            single_account_signature,
813            text_message,
814            BlockIdentifier::Best,
815        )
816        .await
817        .expect("Expect verification to succeed");
818        assert!(is_valid);
819    }
820
821    // We test signing and verifying a binary message here. We use the
822    // `unchecked` version of the functions.
823    #[test]
824    fn test_sign_and_verify_binary_message_unchecked() {
825        // Create a message to sign.
826        let binary_message: &[u8] = b"test";
827
828        let rng = &mut rand::thread_rng();
829
830        // Generate account keys that have one keypair at index 0 in both maps.
831        let keypairs = AccountKeys::singleton(rng);
832
833        // Create an account address.
834        let account_address = AccountAddress([0u8; 32]);
835
836        // Generate a signature.
837        let account_signature =
838            sign_as_account_unchecked(account_address, &keypairs, binary_message);
839
840        assert_eq!(account_signature.sigs.len(), 1);
841
842        let account_signatures_verification_data =
843            AccountSignaturesVerificationData::zip_signatures_and_keys(account_signature, keypairs)
844                .expect("Expect zipping of maps to succeed");
845
846        assert_eq!(account_signatures_verification_data.data.len(), 1);
847
848        // Check that the signature is valid.
849        let is_valid = verify_account_signature_unchecked(
850            account_address,
851            account_signatures_verification_data,
852            binary_message,
853        )
854        .expect("Expect verification to succeed");
855        assert!(is_valid);
856    }
857
858    // We test signing and verifying a binary message here. We use the
859    // `unchecked` and `single` versions of the functions.
860    #[test]
861    fn test_sign_and_verify_binary_message_unchecked_single() {
862        // Create a message to sign.
863        let binary_message: &[u8] = b"test";
864
865        let rng = &mut rand::thread_rng();
866
867        // Generate account keys that have one keypair at index 0 in both maps.
868        let keypairs = AccountKeys::singleton(rng);
869        let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
870
871        // Create an account address.
872        let account_address = AccountAddress([0u8; 32]);
873
874        // Generate a signature.
875        let single_account_signature = sign_as_single_signer_account_unchecked(
876            account_address,
877            single_key.clone().into(),
878            binary_message,
879        )
880        .expect("Expect signing to succeed");
881
882        // Check that the signature is valid.
883        let is_valid = verify_single_account_signature_unchecked(
884            account_address,
885            single_account_signature,
886            single_key.public().into(),
887            binary_message,
888        )
889        .expect("Expect verification to succeed");
890        assert!(is_valid);
891    }
892
893    // We test signing and verifying a binary message here. We use the `checked`
894    // version of the functions.
895    #[tokio::test]
896    async fn test_sign_and_verify_binary_message_checked() {
897        // Create a message to sign.
898        let binary_message: &[u8] = b"test";
899
900        // Create a keypair from a private key.
901        let private_key = "f74e3188e4766841600f6fd0095a0ac1c30e4c2e97b9797d7e05a28a48f5c37c";
902        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
903        let signing_key = SigningKey::from_bytes(
904            bytes
905                .as_slice()
906                .try_into()
907                .expect("Invalid private key size"),
908        );
909        let keypair: KeyPair = KeyPair::from(signing_key);
910
911        // Generate account keys that have one keypair at index 0 in both maps.
912        let keypairs = AccountKeys::from(InitialAccountData {
913            keys:      [(KeyIndex(0), keypair)].into(),
914            threshold: SignatureThreshold::ONE,
915        });
916
917        // Add the corresponding account address from testnet associated with the above
918        // private key.
919        let account_address =
920            AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
921                .expect("Expect generating account address successfully");
922
923        // Establish a connection to the node client.
924        let client = v2::Client::new(
925            v2::Endpoint::new(NODE_URL).expect("Expect generating endpoint successfully"),
926        )
927        .await
928        .expect("Expect generating node client successfully");
929
930        // Generate a signature.
931        let account_signature = sign_as_account(
932            client.clone(),
933            account_address,
934            keypairs,
935            binary_message,
936            BlockIdentifier::Best,
937        )
938        .await
939        .expect("Expect signing to succeed");
940
941        assert_eq!(account_signature.sigs.len(), 1);
942
943        // Check that the signature is valid.
944        let is_valid = verify_account_signature(
945            client,
946            account_address,
947            &account_signature,
948            binary_message,
949            BlockIdentifier::Best,
950        )
951        .await
952        .expect("Expect verification to succeed");
953        assert!(is_valid);
954    }
955
956    // We test signing and verifying a binary message here. We use the `checked`
957    // and `single` versions of the functions.
958    #[tokio::test]
959    async fn test_sign_and_verify_binary_message_checked_single() {
960        // Create a message to sign.
961        let binary_message: &[u8] = b"test";
962
963        // Create a keypair from a private key.
964        let private_key = "f74e3188e4766841600f6fd0095a0ac1c30e4c2e97b9797d7e05a28a48f5c37c";
965        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
966        let signing_key = SigningKey::from_bytes(
967            bytes
968                .as_slice()
969                .try_into()
970                .expect("Invalid private key size"),
971        );
972        let keypair: KeyPair = KeyPair::from(signing_key);
973
974        // Generate account keys that have one keypair at index 0 in both maps.
975        let keypairs = AccountKeys::from(InitialAccountData {
976            keys:      [(KeyIndex(0), keypair)].into(),
977            threshold: SignatureThreshold::ONE,
978        });
979        let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
980
981        // Add the corresponding account address from testnet associated with the above
982        // private key.
983        let account_address =
984            AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
985                .expect("Expect generating account address successfully");
986
987        // Establish a connection to the node client.
988        let client = v2::Client::new(
989            v2::Endpoint::new(NODE_URL).expect("Expect generating endpoint successfully"),
990        )
991        .await
992        .expect("Expect generating node client successfully");
993
994        // Generate a signature.
995        let single_account_signature = sign_as_single_signer_account(
996            client.clone(),
997            account_address,
998            single_key.clone().into(),
999            binary_message,
1000            BlockIdentifier::Best,
1001        )
1002        .await
1003        .expect("Expect signing to succeed");
1004
1005        // Check that the signature is valid.
1006        let is_valid = verify_single_account_signature(
1007            client,
1008            account_address,
1009            single_account_signature,
1010            binary_message,
1011            BlockIdentifier::Best,
1012        )
1013        .await
1014        .expect("Expect verification to succeed");
1015        assert!(is_valid);
1016    }
1017
1018    // We test signing and verifying a text message here with a multi-sig
1019    // account. We use the `unchecked` version of the functions.
1020    #[test]
1021    fn test_sign_and_verify_text_message_unchecked_multi_sig_account() {
1022        // Create a message to sign.
1023        let message: &str = "test";
1024        let text_message = message.as_bytes();
1025
1026        let rng = &mut rand::thread_rng();
1027
1028        // Generate account keys that have one keypair at index 0 in both maps.
1029        let single_keypairs = AccountKeys::singleton(rng);
1030
1031        // Create a multi-sig account from the above keypairs.
1032        let credential_map = [
1033            (CredentialIndex { index: 0 }, CredentialData {
1034                keys:      [
1035                    (
1036                        KeyIndex(0u8),
1037                        single_keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)]
1038                            .clone(),
1039                    ),
1040                    (
1041                        KeyIndex(1u8),
1042                        single_keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)]
1043                            .clone(),
1044                    ),
1045                ]
1046                .into(),
1047                threshold: SignatureThreshold::TWO,
1048            }),
1049            (CredentialIndex { index: 1 }, CredentialData {
1050                keys:      [
1051                    (
1052                        KeyIndex(0u8),
1053                        single_keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)]
1054                            .clone(),
1055                    ),
1056                    (
1057                        KeyIndex(1u8),
1058                        single_keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)]
1059                            .clone(),
1060                    ),
1061                ]
1062                .into(),
1063                threshold: SignatureThreshold::TWO,
1064            }),
1065        ]
1066        .into();
1067
1068        let keypairs_multi_sig_account = AccountKeys {
1069            keys:      credential_map,
1070            threshold: AccountThreshold::TWO,
1071        };
1072
1073        // Create an account address.
1074        let account_address = AccountAddress([0u8; 32]);
1075
1076        // Generate signatures.
1077        let account_signatures =
1078            sign_as_account_unchecked(account_address, &keypairs_multi_sig_account, text_message);
1079
1080        assert_eq!(account_signatures.sigs.len(), 2);
1081        assert_eq!(account_signatures.sigs[&0].sigs.len(), 2);
1082        assert_eq!(account_signatures.sigs[&1].sigs.len(), 2);
1083
1084        let account_signatures_verification_data =
1085            AccountSignaturesVerificationData::zip_signatures_and_keys(
1086                account_signatures,
1087                keypairs_multi_sig_account,
1088            )
1089            .expect("Expect zipping of maps to succeed");
1090
1091        assert_eq!(account_signatures_verification_data.data.len(), 2);
1092        assert_eq!(account_signatures_verification_data.data[&0].data.len(), 2);
1093        assert_eq!(account_signatures_verification_data.data[&1].data.len(), 2);
1094
1095        // Check that the signatures are valid.
1096        let is_valid = verify_account_signature_unchecked(
1097            account_address,
1098            account_signatures_verification_data,
1099            text_message,
1100        )
1101        .expect("Expect verification to succeed");
1102        assert!(is_valid);
1103    }
1104
1105    // We test signing and verifying a binary message here with a multi-sig
1106    // account. We use the `checked` version of the functions.
1107    #[tokio::test]
1108    async fn test_sign_and_verify_binary_message_multi_sig_account() {
1109        // Create a message to sign.
1110        let binary_message: &[u8] = b"test";
1111
1112        // Create first keypair from a private key.
1113        let private_key = "bbabcd59e5ca2edb2073e1936f4df84fbe352fb03c4e55061957f5099b94f562";
1114        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
1115        let signing_key = SigningKey::from_bytes(
1116            bytes
1117                .as_slice()
1118                .try_into()
1119                .expect("Invalid private key size"),
1120        );
1121        let keypair1: KeyPair = KeyPair::from(signing_key);
1122
1123        // Create second keypair from a private key.
1124        let private_key = "751c9c11a7e9f4729779d8795c21aa02973bb2e7276700444ead643c255a38ae";
1125        let bytes = hex::decode(private_key).expect("Invalid hex string for private key.");
1126        let signing_key = SigningKey::from_bytes(
1127            bytes
1128                .as_slice()
1129                .try_into()
1130                .expect("Invalid private key size"),
1131        );
1132        let keypair2: KeyPair = KeyPair::from(signing_key);
1133
1134        // Generate account keys of multi-sig account.
1135        let keypairs = AccountKeys::from(InitialAccountData {
1136            keys:      [
1137                (KeyIndex(0), keypair1.clone()),
1138                (KeyIndex(1), keypair2.clone()),
1139            ]
1140            .into(),
1141            threshold: SignatureThreshold::TWO,
1142        });
1143
1144        // Add the corresponding account address from testnet associated with the above
1145        // private key.
1146        let account_address =
1147            AccountAddress::from_str("4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix")
1148                .expect("Expect generating account address successfully");
1149
1150        // Establish a connection to the node client.
1151        let client = v2::Client::new(
1152            v2::Endpoint::new(NODE_URL).expect("Expect generating endpoint successfully"),
1153        )
1154        .await
1155        .expect("Expect generating node client successfully");
1156
1157        // Generate signature.
1158        let account_signatures = sign_as_account(
1159            client.clone(),
1160            account_address,
1161            keypairs,
1162            binary_message,
1163            BlockIdentifier::Best,
1164        )
1165        .await
1166        .expect("Expect signing to succeed");
1167
1168        assert_eq!(account_signatures.sigs.len(), 1);
1169        assert_eq!(account_signatures.sigs[&0].sigs.len(), 2);
1170
1171        // Check that the signatures are valid.
1172        let is_valid = verify_account_signature(
1173            client,
1174            account_address,
1175            &account_signatures,
1176            binary_message,
1177            BlockIdentifier::Best,
1178        )
1179        .await
1180        .expect("Expect verification to succeed");
1181        assert!(is_valid);
1182    }
1183}