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