1use std::collections::HashMap;
4
5use crate::EthApi;
6use alloy_dyn_abi::TypedData;
7use alloy_eips::eip2718::Decodable2718;
8use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder};
9use alloy_primitives::{eip191_hash_message, Address, PrimitiveSignature as Signature, B256};
10use alloy_rpc_types_eth::TransactionRequest;
11use alloy_signer::SignerSync;
12use alloy_signer_local::PrivateKeySigner;
13use reth_provider::BlockReader;
14use reth_rpc_eth_api::helpers::{signer::Result, AddDevSigners, EthSigner};
15use reth_rpc_eth_types::SignError;
16
17impl<Provider, Pool, Network, EvmConfig> AddDevSigners
18 for EthApi<Provider, Pool, Network, EvmConfig>
19where
20 Provider: BlockReader,
21{
22 fn with_dev_accounts(&self) {
23 *self.inner.signers().write() = DevSigner::random_signers(20)
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct DevSigner {
30 addresses: Vec<Address>,
31 accounts: HashMap<Address, PrivateKeySigner>,
32}
33
34#[allow(dead_code)]
35impl DevSigner {
36 pub fn random<T: Decodable2718>() -> Box<dyn EthSigner<T>> {
38 let mut signers = Self::random_signers(1);
39 signers.pop().expect("expect to generate at least one signer")
40 }
41
42 pub fn random_signers<T: Decodable2718>(num: u32) -> Vec<Box<dyn EthSigner<T> + 'static>> {
45 let mut signers = Vec::with_capacity(num as usize);
46 for _ in 0..num {
47 let sk = PrivateKeySigner::random_with(&mut rand::thread_rng());
48
49 let address = sk.address();
50 let addresses = vec![address];
51
52 let accounts = HashMap::from([(address, sk)]);
53 signers.push(Box::new(Self { addresses, accounts }) as Box<dyn EthSigner<T>>);
54 }
55 signers
56 }
57
58 fn get_key(&self, account: Address) -> Result<&PrivateKeySigner> {
59 self.accounts.get(&account).ok_or(SignError::NoAccount)
60 }
61
62 fn sign_hash(&self, hash: B256, account: Address) -> Result<Signature> {
63 let signature = self.get_key(account)?.sign_hash_sync(&hash);
64 signature.map_err(|_| SignError::CouldNotSign)
65 }
66}
67
68#[async_trait::async_trait]
69impl<T: Decodable2718> EthSigner<T> for DevSigner {
70 fn accounts(&self) -> Vec<Address> {
71 self.addresses.clone()
72 }
73
74 fn is_signer_for(&self, addr: &Address) -> bool {
75 self.accounts.contains_key(addr)
76 }
77
78 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature> {
79 let hash = eip191_hash_message(message);
82 self.sign_hash(hash, address)
83 }
84
85 async fn sign_transaction(&self, request: TransactionRequest, address: &Address) -> Result<T> {
86 let signer = self.accounts.get(address).ok_or(SignError::NoAccount)?.clone();
88 let wallet = EthereumWallet::from(signer);
89
90 let txn_envelope =
92 request.build(&wallet).await.map_err(|_| SignError::InvalidTransactionRequest)?;
93
94 let encoded = txn_envelope.encoded_2718();
96 let txn_signed = T::decode_2718(&mut encoded.as_ref())
97 .map_err(|_| SignError::InvalidTransactionRequest)?;
98
99 Ok(txn_signed)
100 }
101
102 fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature> {
103 let encoded = payload.eip712_signing_hash().map_err(|_| SignError::InvalidTypedData)?;
104 self.sign_hash(encoded, address)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use alloy_consensus::Transaction;
112 use alloy_primitives::{Bytes, U256};
113 use alloy_rpc_types_eth::TransactionInput;
114 use reth_ethereum_primitives::TransactionSigned;
115 use revm_primitives::TxKind;
116
117 fn build_signer() -> DevSigner {
118 let signer: PrivateKeySigner =
119 "4646464646464646464646464646464646464646464646464646464646464646".parse().unwrap();
120 let address = signer.address();
121 let accounts = HashMap::from([(address, signer)]);
122 let addresses = vec![address];
123 DevSigner { addresses, accounts }
124 }
125
126 #[tokio::test]
127 async fn test_sign_type_data() {
128 let eip_712_example = r#"{
129 "types": {
130 "EIP712Domain": [
131 {
132 "name": "name",
133 "type": "string"
134 },
135 {
136 "name": "version",
137 "type": "string"
138 },
139 {
140 "name": "chainId",
141 "type": "uint256"
142 },
143 {
144 "name": "verifyingContract",
145 "type": "address"
146 }
147 ],
148 "Person": [
149 {
150 "name": "name",
151 "type": "string"
152 },
153 {
154 "name": "wallet",
155 "type": "address"
156 }
157 ],
158 "Mail": [
159 {
160 "name": "from",
161 "type": "Person"
162 },
163 {
164 "name": "to",
165 "type": "Person"
166 },
167 {
168 "name": "contents",
169 "type": "string"
170 }
171 ]
172 },
173 "primaryType": "Mail",
174 "domain": {
175 "name": "Ether Mail",
176 "version": "1",
177 "chainId": 1,
178 "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
179 },
180 "message": {
181 "from": {
182 "name": "Cow",
183 "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
184 },
185 "to": {
186 "name": "Bob",
187 "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
188 },
189 "contents": "Hello, Bob!"
190 }
191 }"#;
192 let data: TypedData = serde_json::from_str(eip_712_example).unwrap();
193 let signer = build_signer();
194 let from = *signer.addresses.first().unwrap();
195 let sig = EthSigner::<reth_ethereum_primitives::TransactionSigned>::sign_typed_data(
196 &signer, from, &data,
197 )
198 .unwrap();
199 let expected = Signature::new(
200 U256::from_str_radix(
201 "5318aee9942b84885761bb20e768372b76e7ee454fc4d39b59ce07338d15a06c",
202 16,
203 )
204 .unwrap(),
205 U256::from_str_radix(
206 "5e585a2f4882ec3228a9303244798b47a9102e4be72f48159d890c73e4511d79",
207 16,
208 )
209 .unwrap(),
210 false,
211 );
212 assert_eq!(sig, expected)
213 }
214
215 #[tokio::test]
216 async fn test_signer() {
217 let message = b"Test message";
218 let signer = build_signer();
219 let from = *signer.addresses.first().unwrap();
220 let sig =
221 EthSigner::<reth_ethereum_primitives::TransactionSigned>::sign(&signer, from, message)
222 .await
223 .unwrap();
224 let expected = Signature::new(
225 U256::from_str_radix(
226 "54313da7432e4058b8d22491b2e7dbb19c7186c35c24155bec0820a8a2bfe0c1",
227 16,
228 )
229 .unwrap(),
230 U256::from_str_radix(
231 "687250f11a3d4435004c04a4cb60e846bc27997271d67f21c6c8170f17a25e10",
232 16,
233 )
234 .unwrap(),
235 true,
236 );
237 assert_eq!(sig, expected)
238 }
239
240 #[tokio::test]
241 async fn test_sign_transaction() {
242 let message = b"Test message";
243 let signer = build_signer();
244 let from = *signer.addresses.first().unwrap();
245 let request = TransactionRequest {
246 chain_id: Some(1u64),
247 from: Some(from),
248 to: Some(TxKind::Create),
249 gas: Some(1000),
250 gas_price: Some(1000u128),
251 value: Some(U256::from(1000)),
252 input: TransactionInput {
253 data: Some(Bytes::from(message.to_vec())),
254 input: Some(Bytes::from(message.to_vec())),
255 },
256 nonce: Some(0u64),
257 ..Default::default()
258 };
259 let txn_signed: std::result::Result<TransactionSigned, SignError> =
260 signer.sign_transaction(request, &from).await;
261 assert!(txn_signed.is_ok());
262
263 assert_eq!(Bytes::from(message.to_vec()), txn_signed.unwrap().input().0);
264 }
265}