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