tronz_primitives/
signature.rs1use core::{fmt, str::FromStr};
4
5use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
6
7use crate::{Address, B256, error::SignatureError};
8
9pub const SIGNATURE_LEN: usize = 65;
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
18pub struct RecoverableSignature {
19 r: [u8; 32],
20 s: [u8; 32],
21 v: u8,
22}
23
24impl RecoverableSignature {
25 pub fn from_signature(sig: &Signature, recovery_id: RecoveryId) -> Self {
27 let bytes = sig.to_bytes();
28 let mut r = [0u8; 32];
29 let mut s = [0u8; 32];
30 r.copy_from_slice(&bytes[..32]);
31 s.copy_from_slice(&bytes[32..]);
32 Self { r, s, v: recovery_id.to_byte() }
33 }
34
35 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError> {
40 if bytes.len() != SIGNATURE_LEN {
41 return Err(SignatureError::BadLength(bytes.len()));
42 }
43 let v = match bytes[64] {
44 v @ (0 | 1) => v,
45 27 => 0,
46 28 => 1,
47 other => return Err(SignatureError::BadRecoveryId(other)),
48 };
49 let mut r = [0u8; 32];
50 let mut s = [0u8; 32];
51 r.copy_from_slice(&bytes[..32]);
52 s.copy_from_slice(&bytes[32..64]);
53 Ok(Self { r, s, v })
54 }
55
56 pub fn r(&self) -> &[u8; 32] {
58 &self.r
59 }
60
61 pub fn s(&self) -> &[u8; 32] {
63 &self.s
64 }
65
66 pub fn v(&self) -> u8 {
68 self.v
69 }
70
71 pub fn to_bytes(&self) -> [u8; SIGNATURE_LEN] {
76 let mut out = [0u8; SIGNATURE_LEN];
77 out[..32].copy_from_slice(&self.r);
78 out[32..64].copy_from_slice(&self.s);
79 out[64] = self.v;
80 out
81 }
82
83 pub fn to_legacy_bytes(&self) -> [u8; SIGNATURE_LEN] {
88 let mut out = self.to_bytes();
89 out[64] += 27;
90 out
91 }
92
93 pub fn recover_address_from_prehash(&self, prehash: B256) -> Result<Address, SignatureError> {
95 let (sig, recid) = self.split()?;
96 let vk = VerifyingKey::recover_from_prehash(prehash.as_slice(), &sig, recid)?;
97 Ok(Address::from_public_key(&vk))
98 }
99
100 pub fn split(&self) -> Result<(Signature, RecoveryId), SignatureError> {
102 let mut rs = [0u8; 64];
103 rs[..32].copy_from_slice(&self.r);
104 rs[32..].copy_from_slice(&self.s);
105 let sig = Signature::from_slice(&rs)?;
106 let recid = RecoveryId::from_byte(self.v).ok_or(SignatureError::BadRecoveryId(self.v))?;
107 Ok((sig, recid))
108 }
109}
110
111impl TryFrom<&[u8]> for RecoverableSignature {
112 type Error = SignatureError;
113
114 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
115 Self::from_bytes(bytes)
116 }
117}
118
119impl From<RecoverableSignature> for [u8; SIGNATURE_LEN] {
120 fn from(signature: RecoverableSignature) -> Self {
121 signature.to_bytes()
122 }
123}
124
125impl From<&RecoverableSignature> for [u8; SIGNATURE_LEN] {
126 fn from(signature: &RecoverableSignature) -> Self {
127 signature.to_bytes()
128 }
129}
130
131impl FromStr for RecoverableSignature {
132 type Err = SignatureError;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 let bytes = hex::decode(s.strip_prefix("0x").unwrap_or(s))?;
136 Self::from_bytes(&bytes)
137 }
138}
139
140impl From<(Signature, RecoveryId)> for RecoverableSignature {
141 fn from((signature, recovery_id): (Signature, RecoveryId)) -> Self {
142 Self::from_signature(&signature, recovery_id)
143 }
144}
145
146impl TryFrom<RecoverableSignature> for (Signature, RecoveryId) {
147 type Error = SignatureError;
148
149 fn try_from(signature: RecoverableSignature) -> Result<Self, Self::Error> {
150 signature.split()
151 }
152}
153
154impl fmt::Debug for RecoverableSignature {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "RecoverableSignature(0x{})", hex::encode(self.to_bytes()))
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use k256::ecdsa::{SigningKey, signature::hazmat::PrehashSigner};
163
164 use super::*;
165
166 #[test]
167 fn bytes_roundtrip() {
168 let mut bytes = [7u8; SIGNATURE_LEN];
169 bytes[64] = 1;
170 let sig = RecoverableSignature::from_bytes(&bytes).unwrap();
171 assert_eq!(sig.to_bytes(), bytes);
172 assert_eq!(sig.v(), 1);
173 }
174
175 #[test]
176 fn normalises_eth_v() {
177 let mut bytes = [3u8; SIGNATURE_LEN];
178 bytes[64] = 28;
179 let sig = RecoverableSignature::from_bytes(&bytes).unwrap();
180 assert_eq!(sig.v(), 1);
181 }
182
183 #[test]
184 fn bad_length_and_recid() {
185 assert!(matches!(
186 RecoverableSignature::from_bytes(&[0u8; 10]),
187 Err(SignatureError::BadLength(10))
188 ));
189 let mut bytes = [0u8; SIGNATURE_LEN];
190 bytes[64] = 5;
191 assert!(matches!(
192 RecoverableSignature::from_bytes(&bytes),
193 Err(SignatureError::BadRecoveryId(5))
194 ));
195 }
196
197 #[test]
198 fn from_signature_and_split() {
199 let signing = SigningKey::from_bytes(&[1u8; 32].into()).unwrap();
200 let (sig, recid): (Signature, RecoveryId) = signing.sign_prehash(&[9u8; 32]).unwrap();
201 let rec = RecoverableSignature::from_signature(&sig, recid);
202 let (sig2, recid2) = rec.split().unwrap();
203 assert_eq!(sig, sig2);
204 assert_eq!(recid.to_byte(), recid2.to_byte());
205 }
206
207 #[test]
208 fn recover_address_round_trips() {
209 use k256::ecdsa::signature::hazmat::PrehashSigner;
210
211 let signing = SigningKey::from_bytes(&[1u8; 32].into()).unwrap();
212 let expected = crate::Address::from_public_key(signing.verifying_key());
213 let prehash = crate::B256::repeat_byte(0x42);
214 let (sig, recid): (Signature, RecoveryId) =
215 signing.sign_prehash(prehash.as_slice()).unwrap();
216 let rec = RecoverableSignature::from_signature(&sig, recid);
217 assert_eq!(rec.recover_address_from_prehash(prehash).unwrap(), expected);
218 }
219
220 #[test]
221 fn to_bytes_stays_0_1_and_legacy_is_27_28() {
222 for v in [0u8, 1] {
224 let mut bytes = [3u8; SIGNATURE_LEN];
225 bytes[64] = v;
226 let sig = RecoverableSignature::from_bytes(&bytes).unwrap();
227 assert!(matches!(sig.to_bytes()[64], 0 | 1));
228 assert!(matches!(sig.to_legacy_bytes()[64], 27 | 28));
229 assert_eq!(sig.to_bytes()[..64], sig.to_legacy_bytes()[..64]);
230 assert_eq!(RecoverableSignature::from_bytes(&sig.to_legacy_bytes()).unwrap(), sig);
231 }
232 }
233
234 #[test]
235 fn standard_byte_and_string_conversions() {
236 let mut bytes = [3u8; SIGNATURE_LEN];
237 bytes[64] = 1;
238 let signature = RecoverableSignature::try_from(bytes.as_slice()).unwrap();
239
240 assert_eq!(<[u8; SIGNATURE_LEN]>::from(signature), bytes);
241 assert_eq!(<[u8; SIGNATURE_LEN]>::from(&signature), bytes);
242 assert_eq!(hex::encode(bytes).parse::<RecoverableSignature>().unwrap(), signature);
243 assert_eq!(
244 format!("0x{}", hex::encode(bytes)).parse::<RecoverableSignature>().unwrap(),
245 signature
246 );
247 }
248
249 #[test]
250 fn k256_tuple_conversions() {
251 let signing = SigningKey::from_bytes(&[1u8; 32].into()).unwrap();
252 let tuple: (Signature, RecoveryId) = signing.sign_prehash(&[9u8; 32]).unwrap();
253 let recoverable = RecoverableSignature::from(tuple);
254 let roundtrip: (Signature, RecoveryId) = recoverable.try_into().unwrap();
255
256 assert_eq!(roundtrip.0, tuple.0);
257 assert_eq!(roundtrip.1, tuple.1);
258 }
259}