1use crate::prelude::*;
3use crate::types::H256;
4
5#[derive(Debug, derive_more::Display, PartialEq, Clone)]
7pub enum SigningError {
8 #[display(fmt = "Message has to be a non-zero 32-bytes slice.")]
10 InvalidMessage,
11}
12#[cfg(feature = "std")]
13impl std::error::Error for SigningError {}
14
15#[derive(Debug, derive_more::Display, PartialEq, Clone)]
17pub enum RecoveryError {
18 #[display(fmt = "Message has to be a non-zero 32-bytes slice.")]
20 InvalidMessage,
21 #[display(fmt = "Signature is invalid (check recovery id).")]
23 InvalidSignature,
24}
25#[cfg(feature = "std")]
26impl std::error::Error for RecoveryError {}
27
28#[cfg(feature = "signing")]
29pub use feature_gated::*;
30
31#[cfg(feature = "signing")]
32mod feature_gated {
33 use super::*;
34 use crate::types::Address;
35 pub trait Key {
49 fn sign(&self, message: &[u8; 32], chain_id: Option<u64>) -> Result<Signature, SigningError>;
55
56 fn sign_message(&self, message: &[u8; 32]) -> Result<Signature, SigningError>;
59
60 fn address(&self) -> Address;
62 }
63}
64
65pub struct Signature {
67 pub v: u64,
69 pub r: H256,
71 pub s: H256,
73}
74
75pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
77 use tiny_keccak::{Hasher, Keccak};
78 let mut output = [0u8; 32];
79 let mut hasher = Keccak::v256();
80 hasher.update(bytes);
81 hasher.finalize(&mut output);
82 output
83}
84
85pub type NameHash = [u8; 32];
87
88pub fn namehash(name: &str) -> NameHash {
92 let mut node = [0u8; 32];
93
94 if name.is_empty() {
95 return node;
96 }
97
98 let mut labels: Vec<&str> = name.split('.').collect();
99
100 labels.reverse();
101
102 for label in labels.iter() {
103 let label_hash = keccak256(label.as_bytes());
104
105 node = keccak256(&[node, label_hash].concat());
106 }
107
108 node
109}
110
111pub fn hash_message<S>(message: S) -> H256
117where
118 S: AsRef<[u8]>,
119{
120 let message = message.as_ref();
121
122 let mut eth_message = format!("\x19Ethereum Signed Message:\n{}", message.len()).into_bytes();
123 eth_message.extend_from_slice(message);
124
125 keccak256(ð_message).into()
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
135 fn name_hash_empty() {
136 let input = "";
137
138 let result = namehash(input);
139
140 let expected = [0u8; 32];
141
142 assert_eq!(expected, result);
143 }
144
145 #[test]
146 fn name_hash_eth() {
147 let input = "eth";
148
149 let result = namehash(input);
150
151 let expected = [
152 0x93, 0xcd, 0xeb, 0x70, 0x8b, 0x75, 0x45, 0xdc, 0x66, 0x8e, 0xb9, 0x28, 0x01, 0x76, 0x16, 0x9d, 0x1c, 0x33,
153 0xcf, 0xd8, 0xed, 0x6f, 0x04, 0x69, 0x0a, 0x0b, 0xcc, 0x88, 0xa9, 0x3f, 0xc4, 0xae,
154 ];
155
156 assert_eq!(expected, result);
157 }
158
159 #[test]
160 fn name_hash_foo_eth() {
161 let input = "foo.eth";
162
163 let result = namehash(input);
164
165 let expected = [
166 0xde, 0x9b, 0x09, 0xfd, 0x7c, 0x5f, 0x90, 0x1e, 0x23, 0xa3, 0xf1, 0x9f, 0xec, 0xc5, 0x48, 0x28, 0xe9, 0xc8,
167 0x48, 0x53, 0x98, 0x01, 0xe8, 0x65, 0x91, 0xbd, 0x98, 0x01, 0xb0, 0x19, 0xf8, 0x4f,
168 ];
169
170 assert_eq!(expected, result);
171 }
172}