1use chrono::{DateTime, ParseError, SecondsFormat, TimeZone, Utc};
2use hex::encode;
3use lazy_static::lazy_static;
4use regex::Regex;
5use secp256k1::Secp256k1;
6use serde::{Deserialize, Serialize};
7use std::{
8 fmt::{Debug, Display, LowerHex, UpperHex},
9 ops::Deref,
10};
11
12use thiserror::Error;
13use web3::{
14 signing::{hash_message, keccak256, recover, RecoveryError},
15 types::{H160, H256},
16};
17
18#[derive(Debug, PartialEq, Error)]
20pub enum DecodeHexError {
21 #[error("hexadecimal value must be prefixed with 0x")]
22 MissingPrefix,
23
24 #[error("odd number of digits")]
25 OddLength,
26
27 #[error("invalid string length")]
28 InvalidLength,
29
30 #[error("invalid character {c:?} at position {index}")]
31 InvalidHexCharacter { c: char, index: usize },
32}
33
34impl From<hex::FromHexError> for DecodeHexError {
36 fn from(err: hex::FromHexError) -> Self {
37 match err {
38 hex::FromHexError::OddLength => DecodeHexError::OddLength,
39 hex::FromHexError::InvalidStringLength => DecodeHexError::InvalidLength,
40 hex::FromHexError::InvalidHexCharacter { c, index } => {
41 DecodeHexError::InvalidHexCharacter {
42 c,
43 index: index + 2,
44 }
45 }
46 }
47 }
48}
49
50impl From<secp256k1::Error> for DecodeHexError {
51 fn from(_value: secp256k1::Error) -> Self {
52 DecodeHexError::InvalidLength
53 }
54}
55
56pub fn decode(value: &str) -> Result<Vec<u8>, DecodeHexError> {
76 if !value.starts_with("0x") {
77 return Err(DecodeHexError::MissingPrefix);
78 }
79
80 if value.len() % 2 != 0 {
81 return Err(DecodeHexError::OddLength);
82 }
83
84 Ok(hex::decode(&value[2..])?)
85}
86
87pub fn decode_to_slice(value: &str, bits: &mut [u8]) -> Result<(), DecodeHexError> {
102 if !value.starts_with("0x") {
103 return Err(DecodeHexError::MissingPrefix);
104 }
105
106 if value.len() != ((bits.len() * 2) + 2) {
107 return Err(DecodeHexError::InvalidLength);
108 }
109
110 Ok(hex::decode_to_slice(&value[2..], bits)?)
111}
112
113#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Copy)]
114#[serde(try_from = "String", into = "String")]
115pub struct Address(H160);
116
117impl Deref for Address {
118 type Target = H160;
119 fn deref(&self) -> &Self::Target {
120 &self.0
121 }
122}
123
124impl From<[u8; 20]> for Address {
125 fn from(value: [u8; 20]) -> Self {
134 Self(H160(value))
135 }
136}
137
138impl From<H160> for Address {
139 fn from(value: H160) -> Self {
149 Self(value)
150 }
151}
152
153impl std::cmp::PartialEq<H160> for Address {
154 fn eq(&self, other: &H160) -> bool {
155 self.0 == *other
156 }
157}
158
159impl std::cmp::PartialEq<H160> for &Address {
160 fn eq(&self, other: &H160) -> bool {
161 self.0 == *other
162 }
163}
164
165impl From<Address> for String {
166 fn from(value: Address) -> Self {
168 value.checksum()
169 }
170}
171
172impl TryFrom<&str> for Address {
173 type Error = DecodeHexError;
174
175 fn try_from(value: &str) -> Result<Self, Self::Error> {
218 let mut bits: [u8; 20] = [0; 20];
219 match decode_to_slice(value, &mut bits) {
220 Ok(_) => Ok(Self::from(bits)),
221 Err(err) => Err(err),
222 }
223 }
224}
225
226impl TryFrom<String> for Address {
227 type Error = DecodeHexError;
228 fn try_from(value: String) -> Result<Self, Self::Error> {
229 Self::try_from(value.as_str())
230 }
231}
232
233impl UpperHex for Address {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 std::fmt::UpperHex::fmt(&**self, f)
249 }
250}
251
252impl LowerHex for Address {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 std::fmt::LowerHex::fmt(&**self, f)
268 }
269}
270
271impl Display for Address {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 write!(f, "{self:#x}")
281 }
282}
283
284impl Address {
285 pub fn zero() -> Self {
292 Self::from([0; 20])
293 }
294
295 pub fn checksum(&self) -> String {
306 let hash = keccak256(format!("{self:x}").as_bytes());
307 let checksum = self
308 .as_bytes()
309 .iter()
310 .enumerate()
311 .map(|(i, b)| {
312 let h1 = (hash[i] & 0b1111_0000) >> 4;
314 let h2 = hash[i] & 0b0000_1111;
315 let hex = format!("{b:02x}");
316 let b1 = hex.get(0..=0).unwrap_or("0");
317 let b2 = hex.get(1..=1).unwrap_or("0");
318 format!(
320 "{}{}",
321 if h1 >= 8 {
322 b1.to_uppercase()
323 } else {
324 b1.to_lowercase()
325 },
326 if h2 >= 8 {
327 b2.to_uppercase()
328 } else {
329 b2.to_lowercase()
330 },
331 )
332 })
333 .collect::<String>();
334
335 format!("0x{checksum}")
336 }
337}
338
339pub const PERSONAL_SIGNATURE_SIZE: usize = 65;
340
341#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
342#[serde(try_from = "String", into = "String")]
343pub struct PersonalSignature([u8; PERSONAL_SIGNATURE_SIZE]);
344
345impl Deref for PersonalSignature {
346 type Target = [u8; PERSONAL_SIGNATURE_SIZE];
347 fn deref(&self) -> &Self::Target {
348 &self.0
349 }
350}
351
352impl Default for PersonalSignature {
353 fn default() -> Self {
354 PersonalSignature([0; PERSONAL_SIGNATURE_SIZE])
355 }
356}
357
358impl From<[u8; PERSONAL_SIGNATURE_SIZE]> for PersonalSignature {
359 fn from(value: [u8; PERSONAL_SIGNATURE_SIZE]) -> Self {
360 Self(value)
361 }
362}
363
364impl From<web3::signing::Signature> for PersonalSignature {
365 fn from(value: web3::signing::Signature) -> Self {
366 let mut bits = [0u8; PERSONAL_SIGNATURE_SIZE];
367 bits[..32].copy_from_slice(&value.r.0);
368 bits[32..64].copy_from_slice(&value.s.0);
369 bits[64] = (value.v * 0b1111) as u8;
370 Self(bits)
371 }
372}
373
374impl From<secp256k1::ecdsa::RecoverableSignature> for PersonalSignature {
375 fn from(value: secp256k1::ecdsa::RecoverableSignature) -> Self {
376 let mut bits = [0u8; PERSONAL_SIGNATURE_SIZE];
377 let (recovery_id, signature) = value.serialize_compact();
378 bits[..64].copy_from_slice(&signature);
379 bits[64] = 27 + recovery_id.to_i32() as u8;
380 Self(bits)
381 }
382}
383impl TryFrom<&str> for PersonalSignature {
384 type Error = DecodeHexError;
385
386 fn try_from(value: &str) -> Result<Self, Self::Error> {
387 if value.len() != 132 {
388 return Err(DecodeHexError::InvalidLength);
389 }
390
391 let mut bits = [0u8; PERSONAL_SIGNATURE_SIZE];
392 match decode_to_slice(value, &mut bits) {
393 Ok(_) => Ok(Self::from(bits)),
394 Err(err) => Err(err),
395 }
396 }
397}
398
399impl TryFrom<String> for PersonalSignature {
400 type Error = DecodeHexError;
401
402 fn try_from(value: String) -> Result<Self, Self::Error> {
403 Self::try_from(value.as_str())
404 }
405}
406
407impl From<PersonalSignature> for String {
408 fn from(value: PersonalSignature) -> Self {
409 value.to_string()
410 }
411}
412
413impl From<PersonalSignature> for web3::signing::Signature {
414 fn from(value: PersonalSignature) -> Self {
415 let mut r: [u8; 32] = [0; 32];
416 r.copy_from_slice(&value[..32]);
417
418 let mut s: [u8; 32] = [0; 32];
419 s.copy_from_slice(&value[32..64]);
420
421 let v: u64 = value.0[64] as u64;
422
423 Self {
424 v,
425 r: H256(r),
426 s: H256(s),
427 }
428 }
429}
430
431impl From<PersonalSignature> for Vec<u8> {
432 fn from(value: PersonalSignature) -> Self {
433 value.to_vec()
434 }
435}
436
437impl Display for PersonalSignature {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 write!(f, "0x{}", hex::encode(self.0))
446 }
447}
448
449impl PersonalSignature {
450 pub fn try_recover_from_message(&self, message: &str) -> Result<Address, RecoveryError> {
463 let result = recover(
464 hash_message(message).as_bytes(),
465 &self.0[..=63],
466 (self.0[64] as i32) - 27,
467 );
468
469 match result {
470 Ok(h160) => Ok(Address::from(h160)),
471 Err(err) => Err(err),
472 }
473 }
474
475 pub fn is_valid_signature(&self, message: &str, signer: &Address) -> bool {
476 self.try_recover_from_message(message)
477 .map(|address| address == *signer)
478 .unwrap_or(false)
479 }
480}
481
482#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
484#[serde(try_from = "String", into = "String")]
485pub struct EIP1271Signature(Vec<u8>);
486
487impl Deref for EIP1271Signature {
488 type Target = Vec<u8>;
489 fn deref(&self) -> &Self::Target {
490 &self.0
491 }
492}
493
494impl Display for EIP1271Signature {
495 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496 write!(f, "0x{}", hex::encode(&self.0))
497 }
498}
499
500impl TryFrom<&str> for EIP1271Signature {
501 type Error = DecodeHexError;
502
503 fn try_from(value: &str) -> Result<Self, Self::Error> {
504 let data = decode(value)?;
505 Ok(Self(data))
506 }
507}
508
509impl TryFrom<String> for EIP1271Signature {
510 type Error = DecodeHexError;
511
512 fn try_from(value: String) -> Result<Self, Self::Error> {
513 Self::try_from(value.as_str())
514 }
515}
516
517impl From<EIP1271Signature> for Vec<u8> {
518 fn from(value: EIP1271Signature) -> Self {
519 value.to_vec()
520 }
521}
522
523impl From<EIP1271Signature> for String {
524 fn from(value: EIP1271Signature) -> Self {
525 format!("0x{}", hex::encode(value.0))
526 }
527}
528
529#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, PartialOrd, Copy)]
530#[serde(try_from = "String", into = "String")]
531pub struct Expiration(DateTime<Utc>);
532
533impl Deref for Expiration {
534 type Target = DateTime<Utc>;
535 fn deref(&self) -> &Self::Target {
536 &self.0
537 }
538}
539
540impl<T: chrono::TimeZone> From<DateTime<T>> for Expiration {
541 fn from(value: DateTime<T>) -> Self {
542 Expiration(value.with_timezone(&Utc))
543 }
544}
545
546impl TryFrom<&str> for Expiration {
547 type Error = ParseError;
548
549 fn try_from(value: &str) -> Result<Self, Self::Error> {
550 let expiration = DateTime::parse_from_rfc3339(value)?;
551 Ok(Self(expiration.with_timezone(&Utc)))
552 }
553}
554
555impl TryFrom<String> for Expiration {
556 type Error = ParseError;
557
558 fn try_from(value: String) -> Result<Self, Self::Error> {
559 Self::try_from(value.as_str())
560 }
561}
562
563impl From<Expiration> for String {
564 fn from(value: Expiration) -> Self {
565 value.to_string()
566 }
567}
568
569impl Display for Expiration {
570 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571 write!(f, "{}", self.0.to_rfc3339_opts(SecondsFormat::Millis, true))
572 }
573}
574
575impl From<Expiration> for DateTime<Utc> {
576 fn from(value: Expiration) -> Self {
577 value.0
578 }
579}
580
581#[derive(PartialEq, Debug, Error)]
582pub enum EphemeralPayloadError {
583 #[error("invalid payload content")]
584 InvalidPayload,
585
586 #[error("missing title line on payload")]
587 MissingTitle,
588
589 #[error("missing address line on payload")]
590 MissingAddress,
591
592 #[error("invalid address: {err} (address: {value})")]
593 InvalidAddress { err: DecodeHexError, value: String },
594
595 #[error("missing expiration line on payload")]
596 MissingExpiration,
597
598 #[error("invalid expiration: {err} (expiration: {value})")]
599 InvalidExpiration { err: ParseError, value: String },
600}
601
602pub type EIP1654Signature = EIP1271Signature;
606
607static DEFAULT_EPHEMERAL_PAYLOAD_TITLE: &str = "Memetaverse Login";
608
609#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
623#[serde(try_from = "String", into = "String")]
624pub struct EphemeralPayload {
625 pub title: String,
626 pub address: Address,
627 pub expiration: Expiration,
628}
629
630static RE_TITLE_CAPTURE: &str = "title";
631static RE_ADDRESS_CAPTURE: &str = "address";
632static RE_EXPIRATION_CAPTURE: &str = "expiration";
633
634impl TryFrom<&str> for EphemeralPayload {
635 type Error = EphemeralPayloadError;
636
637 fn try_from(value: &str) -> Result<Self, Self::Error> {
638 lazy_static! {
639 static ref EPHEMERAL_PAYLOAD_REGEX: Regex = Regex::new(&format!(
640 r"^(?P<{}>[^\r\n]*)\r?\nEphemeral address: (?P<{}>[^\r\n]*)\r?\nExpiration: (?P<{}>.*)$",
641 RE_TITLE_CAPTURE, RE_ADDRESS_CAPTURE, RE_EXPIRATION_CAPTURE
642 ))
643 .unwrap();
644 }
645
646 let captures = match EPHEMERAL_PAYLOAD_REGEX.captures(value) {
647 None => return Err(EphemeralPayloadError::InvalidPayload),
648 Some(captures) => captures,
649 };
650
651 let title = match captures.name(RE_TITLE_CAPTURE) {
652 None => return Err(EphemeralPayloadError::MissingTitle),
653 Some(title) => title.as_str().to_string(),
654 };
655
656 let address = match captures.name(RE_ADDRESS_CAPTURE) {
657 None => return Err(EphemeralPayloadError::MissingAddress),
658 Some(address) => {
659 let value = address.as_str();
660 Address::try_from(value).map_err(|err| EphemeralPayloadError::InvalidAddress {
661 value: value.to_string(),
662 err,
663 })?
664 }
665 };
666
667 let expiration = match captures.name(RE_EXPIRATION_CAPTURE) {
668 None => return Err(EphemeralPayloadError::MissingExpiration),
669 Some(expiration) => {
670 let value = expiration.as_str();
671 Expiration::try_from(value).map_err(|err| {
672 EphemeralPayloadError::InvalidExpiration {
673 value: value.to_string(),
674 err,
675 }
676 })?
677 }
678 };
679
680 Ok(Self {
681 title,
682 address,
683 expiration,
684 })
685 }
686}
687
688impl TryFrom<String> for EphemeralPayload {
689 type Error = EphemeralPayloadError;
690
691 fn try_from(value: String) -> Result<Self, Self::Error> {
692 Self::try_from(value.as_str())
693 }
694}
695
696impl Display for EphemeralPayload {
697 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
698 write!(
699 f,
700 "{}\nEphemeral address: {}\nExpiration: {}",
701 self.title,
702 self.address.checksum(),
703 self.expiration
704 )
705 }
706}
707
708impl From<EphemeralPayload> for String {
709 fn from(payload: EphemeralPayload) -> Self {
710 format!("{}", payload)
711 }
712}
713
714impl EphemeralPayload {
715 pub fn new(address: Address, expiration: Expiration) -> Self {
716 Self::new_with_title(
717 String::from(DEFAULT_EPHEMERAL_PAYLOAD_TITLE),
718 address,
719 expiration,
720 )
721 }
722
723 pub fn new_with_title(title: String, address: Address, expiration: Expiration) -> Self {
724 Self {
725 title,
726 address,
727 expiration,
728 }
729 }
730
731 pub fn is_expired(&self) -> bool {
732 *self.expiration < Utc::now()
733 }
734
735 pub fn is_expired_at<Z: TimeZone>(&self, time: &DateTime<Z>) -> bool {
736 *self.expiration < *time
737 }
738}
739
740struct Hash(H256);
742
743impl secp256k1::ThirtyTwoByteHash for Hash {
744 fn into_32(self) -> [u8; 32] {
745 self.0 .0
746 }
747}
748
749fn to_public_key(secret: &secp256k1::SecretKey) -> secp256k1::PublicKey {
751 lazy_static! {
752 static ref SECP256K1: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
753 }
754
755 secret.public_key(&SECP256K1)
756}
757
758#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
760#[serde(rename_all = "camelCase")]
761struct EphemeralAccountRepresentation {
762 pub address: String,
763 pub public_key: String,
764 pub private_key: String,
765}
766
767impl TryFrom<EphemeralAccountRepresentation> for Account {
768 type Error = DecodeHexError;
769
770 fn try_from(value: EphemeralAccountRepresentation) -> Result<Self, Self::Error> {
771 Account::try_from(value.private_key)
772 }
773}
774
775
776#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
778#[serde(
779 try_from = "EphemeralAccountRepresentation",
780 into = "EphemeralAccountRepresentation"
781)]
782pub struct Account(secp256k1::SecretKey);
783
784impl TryFrom<&str> for Account {
785 type Error = DecodeHexError;
786
787 fn try_from(value: &str) -> Result<Self, Self::Error> {
794 let mut bytes = [0u8; 32];
795 decode_to_slice(value, &mut bytes)?;
796 let key = secp256k1::SecretKey::from_slice(&bytes)?;
797 Ok(Self(key))
798 }
799}
800
801impl TryFrom<String> for Account {
803 type Error = DecodeHexError;
804
805 fn try_from(value: String) -> Result<Self, Self::Error> {
806 Self::try_from(value.as_str())
807 }
808}
809
810impl From<Account> for String {
812 fn from(account: Account) -> Self {
813 format!("0x{}", encode(account.0.secret_bytes()))
814 }
815}
816
817impl From<Account> for EphemeralAccountRepresentation {
819 fn from(account: Account) -> Self {
820 let public = to_public_key(&account.0).serialize_uncompressed();
821 Self {
822 address: account.address().checksum(),
823 public_key: format!("0x{}", hex::encode(public)),
824 private_key: account.into(),
825 }
826 }
827}
828
829impl Account {
830
831 pub fn random() -> Self {
833 Self::from_rng(&mut rand::thread_rng())
834 }
835
836 pub fn from_rng<R: rand::Rng + ?Sized>(r: &mut R) -> Self {
838 Self(secp256k1::SecretKey::new(r))
839 }
840}
841
842pub trait Signer {
844
845 fn address(&self) -> Address;
847
848 fn sign<M: AsRef<[u8]>>(&self, message: M) -> PersonalSignature;
850}
851
852impl Signer for Account {
853 fn address(&self) -> Address {
862 let public = to_public_key(&self.0).serialize_uncompressed();
863 let hash = keccak256(&public[1..]);
864 let mut bytes = [0u8; 20];
865 bytes.copy_from_slice(&hash[12..]);
866 Address::from(bytes)
867 }
868
869 fn sign<M: AsRef<[u8]>>(&self, message: M) -> PersonalSignature {
880 let hash = Hash(hash_message(message.as_ref()));
881 let message = secp256k1::Message::from(hash);
882 let secp = Secp256k1::new();
883 secp.sign_ecdsa_recoverable(&message, &self.0).into()
884 }
885}