concordium_rust_sdk/
signatures.rs

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