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