pink_web3/types/
recovery.rs

1use crate::prelude::*;
2use crate::types::{SignedData, SignedTransaction, H256};
3use core::fmt::{Display, Formatter, Result as FmtResult};
4
5/// Data for recovering the public address of signed data.
6///
7/// Note that the signature data is in 'Electrum' notation and may have chain
8/// replay protection applied. That means that `v` is expected to be `27`, `28`,
9/// or `35 + chain_id * 2` or `36 + chain_id * 2`.
10#[derive(Clone, Debug, PartialEq)]
11pub struct Recovery {
12    /// The message to recover
13    pub message: RecoveryMessage,
14    /// V value.
15    pub v: u64,
16    /// R value.
17    pub r: H256,
18    /// S value.
19    pub s: H256,
20}
21
22impl Recovery {
23    /// Creates new recovery data from its parts.
24    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    /// Creates new recovery data from a raw signature.
37    ///
38    /// This parses a raw signature which is expected to be 65 bytes long where
39    /// the first 32 bytes is the `r` value, the second 32 bytes the `s` value
40    /// and the final byte is the `v` value in 'Electrum' notation.
41    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    /// Retrieve the Recovery Id ("Standard V")
60    ///
61    /// Returns `None` if `v` value is invalid
62    /// (equivalent of returning `4` in some implementaions).
63    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    /// Retrieves the recovery id & compact signature in it's raw form.
73    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/// Recovery message data.
99///
100/// The message data can either be a binary message that is first hashed
101/// according to EIP-191 and then recovered based on the signature or a
102/// precomputed hash.
103#[derive(Clone, Debug, PartialEq)]
104pub enum RecoveryMessage {
105    /// Message bytes
106    Data(Vec<u8>),
107    /// Message hash
108    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/// An error parsing a raw signature.
148#[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}