1use 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#[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 expected_public_key: Box<VerifyingKey>,
52 actual_public_key: Box<VerifyingKey>,
53 },
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: [(0, AccountSignaturesVerificationEntry {
128 signature,
129 public_key,
130 })]
131 .into(),
132 };
133
134 AccountSignaturesVerificationData {
135 data: [(0, credential_map)].into(),
136 }
137 }
138
139 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 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 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 if credential_sigs.sigs.len() != public_keys.len() {
166 return Err(SignatureError::MismatchMapIndices);
167 }
168
169 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 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
198pub fn calculate_message_hash(message: impl AsRef<[u8]>, signer: AccountAddress) -> [u8; 32] {
200 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
215fn 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 for (outer_key, inner_map) in &signatures.sigs {
227 let on_chain_cred = on_chain_credentials
229 .get(&CredentialIndex { index: *outer_key })
230 .ok_or(SignatureError::MissingIndicesOnChain {
231 credential_index: *outer_key,
232 key_index: 0u8,
234 })?;
235
236 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
255pub 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 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 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 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 valid_signatures_count += 1;
313 } else {
314 return Ok(false);
316 }
317 }
318
319 if valid_signatures_count >= signatures_threshold.into() {
322 valid_credential_signatures_count += 1;
323 }
324 }
325
326 Ok(valid_credential_signatures_count >= credential_signatures_threshold.into())
329}
330
331pub 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
350pub 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
380pub 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
395pub 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 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 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 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 let signature = signing_key.sign(&message_hash);
464
465 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 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
489pub 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 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 Ok(signature.sigs.get_mut(&0).unwrap().sigs.remove(&0).unwrap())
508}
509
510pub 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 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
549pub 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 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 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 let keypairs = AccountKeys::singleton(rng);
582 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 let signature = Signature::from(ed25519_dalek::Signature::from_bytes(&[0u8; 64]));
589
590 let account_signatures_verification_data =
592 AccountSignaturesVerificationData::singleton(signature, public_key);
593
594 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 let ed25519_signature = ed25519_dalek::Signature::from_bytes(&[0u8; 64]);
606 let account_signatures = AccountSignatures::singleton(Signature::from(ed25519_signature));
607
608 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 #[test]
619 fn test_sign_and_verify_text_message_unchecked() {
620 let message: &str = "test";
622 let text_message = message.as_bytes();
623
624 let rng = &mut rand::thread_rng();
625
626 let keypairs = AccountKeys::singleton(rng);
628
629 let account_address = AccountAddress([0u8; 32]);
631
632 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 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 #[test]
656 fn test_sign_and_verify_text_message_unchecked_single() {
657 let message: &str = "test";
659 let text_message = message.as_bytes();
660
661 let rng = &mut rand::thread_rng();
662
663 let keypairs = AccountKeys::singleton(rng);
665 let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
666
667 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 let account_address = AccountAddress([0u8; 32]);
674
675 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 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 #[tokio::test]
697 async fn test_sign_and_verify_text_message_checked() {
698 let message: &str = "test";
700 let text_message = message.as_bytes();
701
702 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 let keypairs = AccountKeys::from(InitialAccountData {
715 keys: [(KeyIndex(0), keypair)].into(),
716 threshold: SignatureThreshold::ONE,
717 });
718
719 let account_address =
722 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
723 .expect("Expect generating account address successfully");
724
725 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 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 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 #[tokio::test]
761 async fn test_sign_and_verify_text_message_checked_single() {
762 let message: &str = "test";
764 let text_message = message.as_bytes();
765
766 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 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 let account_address =
787 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
788 .expect("Expect generating account address successfully");
789
790 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 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 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 #[test]
824 fn test_sign_and_verify_binary_message_unchecked() {
825 let binary_message: &[u8] = b"test";
827
828 let rng = &mut rand::thread_rng();
829
830 let keypairs = AccountKeys::singleton(rng);
832
833 let account_address = AccountAddress([0u8; 32]);
835
836 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 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 #[test]
861 fn test_sign_and_verify_binary_message_unchecked_single() {
862 let binary_message: &[u8] = b"test";
864
865 let rng = &mut rand::thread_rng();
866
867 let keypairs = AccountKeys::singleton(rng);
869 let single_key = keypairs.keys[&CredentialIndex { index: 0 }].keys[&KeyIndex(0)].clone();
870
871 let account_address = AccountAddress([0u8; 32]);
873
874 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 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 #[tokio::test]
896 async fn test_sign_and_verify_binary_message_checked() {
897 let binary_message: &[u8] = b"test";
899
900 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 let keypairs = AccountKeys::from(InitialAccountData {
913 keys: [(KeyIndex(0), keypair)].into(),
914 threshold: SignatureThreshold::ONE,
915 });
916
917 let account_address =
920 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
921 .expect("Expect generating account address successfully");
922
923 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 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 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 #[tokio::test]
959 async fn test_sign_and_verify_binary_message_checked_single() {
960 let binary_message: &[u8] = b"test";
962
963 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 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 let account_address =
984 AccountAddress::from_str("47b6Qe2XtZANHetanWKP1PbApLKtS3AyiCtcXaqLMbypKjCaRw")
985 .expect("Expect generating account address successfully");
986
987 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 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 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 #[test]
1021 fn test_sign_and_verify_text_message_unchecked_multi_sig_account() {
1022 let message: &str = "test";
1024 let text_message = message.as_bytes();
1025
1026 let rng = &mut rand::thread_rng();
1027
1028 let single_keypairs = AccountKeys::singleton(rng);
1030
1031 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 let account_address = AccountAddress([0u8; 32]);
1075
1076 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 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 #[tokio::test]
1108 async fn test_sign_and_verify_binary_message_multi_sig_account() {
1109 let binary_message: &[u8] = b"test";
1111
1112 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 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 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 let account_address =
1147 AccountAddress::from_str("4jxvYasaPncfmCFCLZCvuL5cZuvR5HAQezCHZH7ZA7AGsRYpix")
1148 .expect("Expect generating account address successfully");
1149
1150 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 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 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}