1use 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#[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 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#[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 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#[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 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 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 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 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 if credential_sigs.sigs.len() != public_keys.len() {
169 return Err(SignatureError::MismatchMapIndices);
170 }
171
172 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 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
205pub fn calculate_message_hash(message: impl AsRef<[u8]>, signer: AccountAddress) -> [u8; 32] {
207 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
222fn 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 for (outer_key, inner_map) in &signatures.sigs {
234 let on_chain_cred = on_chain_credentials
236 .get(&CredentialIndex { index: *outer_key })
237 .ok_or(SignatureError::MissingIndicesOnChain {
238 credential_index: *outer_key,
239 key_index: 0u8,
241 })?;
242
243 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
268pub 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 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 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 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 valid_signatures_count += 1;
330 } else {
331 return Ok(false);
333 }
334 }
335
336 if valid_signatures_count >= signatures_threshold.into() {
339 valid_credential_signatures_count += 1;
340 }
341 }
342
343 Ok(valid_credential_signatures_count >= credential_signatures_threshold.into())
346}
347
348pub 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
367pub 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
397pub 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
412pub 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 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 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 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 let signature = signing_key.sign(&message_hash);
485
486 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 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
510pub 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 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 Ok(signature.sigs.get_mut(&0).unwrap().sigs.remove(&0).unwrap())
529}
530
531pub 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 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
570pub 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 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 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 let keypairs = AccountKeys::singleton(rng);
603 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 let signature = Signature::from(ed25519_dalek::Signature::from_bytes(&[0u8; 64]));
610
611 let account_signatures_verification_data =
613 AccountSignaturesVerificationData::singleton(signature, public_key);
614
615 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 let ed25519_signature = ed25519_dalek::Signature::from_bytes(&[0u8; 64]);
627 let account_signatures = AccountSignatures::singleton(Signature::from(ed25519_signature));
628
629 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 #[test]
640 fn test_sign_and_verify_text_message_unchecked() {
641 let message: &str = "test";
643 let text_message = message.as_bytes();
644
645 let rng = &mut rand::thread_rng();
646
647 let keypairs = AccountKeys::singleton(rng);
649
650 let account_address = AccountAddress([0u8; 32]);
652
653 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 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 #[test]
677 fn test_sign_and_verify_text_message_unchecked_single() {
678 let message: &str = "test";
680 let text_message = message.as_bytes();
681
682 let rng = &mut rand::thread_rng();
683
684 let keypairs = AccountKeys::singleton(rng);
686 let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
687
688 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 let account_address = AccountAddress([0u8; 32]);
695
696 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 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 #[tokio::test]
718 async fn test_sign_and_verify_text_message_checked() {
719 let message: &str = "test";
721 let text_message = message.as_bytes();
722
723 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 let keypairs = AccountKeys::from(InitialAccountData {
736 keys: [(KeyIndex(0), keypair)].into(),
737 threshold: SignatureThreshold::ONE,
738 });
739
740 let account_address =
743 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
744 .expect("Expect generating account address successfully");
745
746 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 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 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 #[tokio::test]
782 async fn test_sign_and_verify_text_message_checked_single() {
783 let message: &str = "test";
785 let text_message = message.as_bytes();
786
787 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 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 let account_address =
808 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
809 .expect("Expect generating account address successfully");
810
811 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 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 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 #[test]
845 fn test_sign_and_verify_binary_message_unchecked() {
846 let binary_message: &[u8] = b"test";
848
849 let rng = &mut rand::thread_rng();
850
851 let keypairs = AccountKeys::singleton(rng);
853
854 let account_address = AccountAddress([0u8; 32]);
856
857 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 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 #[test]
882 fn test_sign_and_verify_binary_message_unchecked_single() {
883 let binary_message: &[u8] = b"test";
885
886 let rng = &mut rand::thread_rng();
887
888 let keypairs = AccountKeys::singleton(rng);
890 let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
891
892 let account_address = AccountAddress([0u8; 32]);
894
895 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 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 #[tokio::test]
917 async fn test_sign_and_verify_binary_message_checked() {
918 let binary_message: &[u8] = b"test";
920
921 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 let keypairs = AccountKeys::from(InitialAccountData {
934 keys: [(KeyIndex(0), keypair)].into(),
935 threshold: SignatureThreshold::ONE,
936 });
937
938 let account_address =
941 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
942 .expect("Expect generating account address successfully");
943
944 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 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 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 #[tokio::test]
980 async fn test_sign_and_verify_binary_message_checked_single() {
981 let binary_message: &[u8] = b"test";
983
984 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 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 let account_address =
1005 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
1006 .expect("Expect generating account address successfully");
1007
1008 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 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 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 #[test]
1042 fn test_sign_and_verify_text_message_unchecked_multi_sig_account() {
1043 let message: &str = "test";
1045 let text_message = message.as_bytes();
1046
1047 let rng = &mut rand::thread_rng();
1048
1049 let single_keypairs = AccountKeys::singleton(rng);
1051
1052 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 let account_address = AccountAddress([0u8; 32]);
1102
1103 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 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 #[tokio::test]
1135 async fn test_sign_and_verify_binary_message_multi_sig_account() {
1136 let binary_message: &[u8] = b"test";
1138
1139 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 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 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 let account_address =
1174 AccountAddress::from_str("4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix")
1175 .expect("Expect generating account address successfully");
1176
1177 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 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 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}