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