pink_web3/types/
recovery.rs1use crate::prelude::*;
2use crate::types::{SignedData, SignedTransaction, H256};
3use core::fmt::{Display, Formatter, Result as FmtResult};
4
5#[derive(Clone, Debug, PartialEq)]
11pub struct Recovery {
12 pub message: RecoveryMessage,
14 pub v: u64,
16 pub r: H256,
18 pub s: H256,
20}
21
22impl Recovery {
23 pub fn new<M>(message: M, v: u64, r: H256, s: H256) -> Recovery
25 where
26 M: Into<RecoveryMessage>,
27 {
28 Recovery {
29 message: message.into(),
30 v,
31 r,
32 s,
33 }
34 }
35
36 pub fn from_raw_signature<M, B>(message: M, raw_signature: B) -> Result<Recovery, ParseSignatureError>
42 where
43 M: Into<RecoveryMessage>,
44 B: AsRef<[u8]>,
45 {
46 let bytes = raw_signature.as_ref();
47
48 if bytes.len() != 65 {
49 return Err(ParseSignatureError);
50 }
51
52 let v = bytes[64];
53 let r = H256::from_slice(&bytes[0..32]);
54 let s = H256::from_slice(&bytes[32..64]);
55
56 Ok(Recovery::new(message, v as _, r, s))
57 }
58
59 pub fn recovery_id(&self) -> Option<i32> {
64 match self.v {
65 27 => Some(0),
66 28 => Some(1),
67 v if v >= 35 => Some(((v - 1) % 2) as _),
68 _ => None,
69 }
70 }
71
72 pub fn as_signature(&self) -> Option<([u8; 64], i32)> {
74 let recovery_id = self.recovery_id()?;
75 let signature = {
76 let mut sig = [0u8; 64];
77 sig[..32].copy_from_slice(self.r.as_bytes());
78 sig[32..].copy_from_slice(self.s.as_bytes());
79 sig
80 };
81
82 Some((signature, recovery_id))
83 }
84}
85
86impl<'a> From<&'a SignedData> for Recovery {
87 fn from(signed: &'a SignedData) -> Self {
88 Recovery::new(signed.message_hash, signed.v as _, signed.r, signed.s)
89 }
90}
91
92impl<'a> From<&'a SignedTransaction> for Recovery {
93 fn from(tx: &'a SignedTransaction) -> Self {
94 Recovery::new(tx.message_hash, tx.v, tx.r, tx.s)
95 }
96}
97
98#[derive(Clone, Debug, PartialEq)]
104pub enum RecoveryMessage {
105 Data(Vec<u8>),
107 Hash(H256),
109}
110
111impl From<&[u8]> for RecoveryMessage {
112 fn from(s: &[u8]) -> Self {
113 s.to_owned().into()
114 }
115}
116
117impl From<Vec<u8>> for RecoveryMessage {
118 fn from(s: Vec<u8>) -> Self {
119 RecoveryMessage::Data(s)
120 }
121}
122
123impl From<&str> for RecoveryMessage {
124 fn from(s: &str) -> Self {
125 s.as_bytes().to_owned().into()
126 }
127}
128
129impl From<String> for RecoveryMessage {
130 fn from(s: String) -> Self {
131 RecoveryMessage::Data(s.into_bytes())
132 }
133}
134
135impl From<[u8; 32]> for RecoveryMessage {
136 fn from(hash: [u8; 32]) -> Self {
137 H256(hash).into()
138 }
139}
140
141impl From<H256> for RecoveryMessage {
142 fn from(hash: H256) -> Self {
143 RecoveryMessage::Hash(hash)
144 }
145}
146
147#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
149pub struct ParseSignatureError;
150
151impl Display for ParseSignatureError {
152 fn fmt(&self, f: &mut Formatter) -> FmtResult {
153 write!(f, "error parsing raw signature: wrong number of bytes, expected 65")
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use hex_literal::hex;
161
162 #[test]
163 fn recovery_signature() {
164 let message = "Some data";
165 let v = 0x1cu8;
166 let r = hex!("b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd").into();
167 let s = hex!("6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a029").into();
168
169 let signed = SignedData {
170 message: message.as_bytes().to_owned(),
171 message_hash: hex!("1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655").into(),
172 v,
173 r,
174 s,
175 signature: hex!("b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c").into(),
176 };
177 let expected_signature = (
178 hex!("b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a029").into(), 1
179 );
180 let (sig, id) = Recovery::from(&signed).as_signature().unwrap();
181 assert_eq!((sig.to_vec(), id), expected_signature);
182 let (sig, id) = Recovery::new(message, v as _, r, s).as_signature().unwrap();
183 assert_eq!((sig.to_vec(), id), expected_signature);
184 let (sig, id) = Recovery::from_raw_signature(message, &signed.signature.0)
185 .unwrap()
186 .as_signature()
187 .unwrap();
188 assert_eq!((sig.to_vec(), id), expected_signature);
189 }
190}