1use crate::rpc::{TempoHeaderResponse, TempoTransactionRequest};
2use alloy_consensus::{EthereumTxEnvelope, TxEip4844, error::ValueError};
3use alloy_network::{TransactionBuilder, TxSigner};
4use alloy_primitives::{B256, Bytes, Signature};
5use reth_evm::EvmEnv;
6use reth_primitives_traits::SealedHeader;
7use reth_rpc_convert::{
8 SignTxRequestError, SignableTxRequest, TryIntoSimTx, TryIntoTxEnv,
9 transaction::FromConsensusHeader,
10};
11use reth_rpc_eth_types::EthApiError;
12use tempo_evm::TempoBlockEnv;
13use tempo_primitives::{
14 SignatureType, TempoHeader, TempoSignature, TempoTxEnvelope, TempoTxType,
15 transaction::{Call, RecoveredTempoAuthorization},
16};
17use tempo_revm::{TempoBatchCallEnv, TempoTxEnv};
18
19impl TryIntoSimTx<TempoTxEnvelope> for TempoTransactionRequest {
20 fn try_into_sim_tx(self) -> Result<TempoTxEnvelope, ValueError<Self>> {
21 match self.output_tx_type() {
22 TempoTxType::AA => {
23 let tx = self.build_aa()?;
24
25 let signature = TempoSignature::default();
27
28 Ok(tx.into_signed(signature).into())
29 }
30 TempoTxType::FeeToken => {
31 let tx = self.build_fee_token()?;
32
33 let signature = Signature::new(Default::default(), Default::default(), false);
35
36 Ok(tx.into_signed(signature).into())
37 }
38 TempoTxType::Legacy
39 | TempoTxType::Eip2930
40 | TempoTxType::Eip1559
41 | TempoTxType::Eip7702 => {
42 let Self {
43 inner,
44 fee_token,
45 nonce_key,
46 calls,
47 key_type,
48 key_data,
49 tempo_authorization_list,
50 } = self;
51 let envelope = match TryIntoSimTx::<EthereumTxEnvelope<TxEip4844>>::try_into_sim_tx(
52 inner.clone(),
53 ) {
54 Ok(inner) => inner,
55 Err(e) => {
56 return Err(e.map(|inner| Self {
57 inner,
58 fee_token,
59 nonce_key,
60 calls,
61 key_type,
62 key_data,
63 tempo_authorization_list,
64 }));
65 }
66 };
67
68 Ok(envelope.try_into().map_err(
69 |e: ValueError<EthereumTxEnvelope<TxEip4844>>| {
70 e.map(|_inner| Self {
71 inner,
72 fee_token,
73 nonce_key,
74 calls,
75 key_type,
76 key_data,
77 tempo_authorization_list,
78 })
79 },
80 )?)
81 }
82 }
83 }
84}
85
86impl TryIntoTxEnv<TempoTxEnv, TempoBlockEnv> for TempoTransactionRequest {
87 type Err = EthApiError;
88
89 fn try_into_tx_env<Spec>(
90 self,
91 evm_env: &EvmEnv<Spec, TempoBlockEnv>,
92 ) -> Result<TempoTxEnv, Self::Err> {
93 let Self {
94 inner,
95 fee_token,
96 calls,
97 key_type,
98 key_data,
99 tempo_authorization_list,
100 nonce_key,
101 } = self;
102 Ok(TempoTxEnv {
103 fee_token,
104 is_system_tx: false,
105 fee_payer: None,
106 tempo_tx_env: if !calls.is_empty()
107 || !tempo_authorization_list.is_empty()
108 || nonce_key.is_some()
109 {
110 let mock_signature = key_type
113 .as_ref()
114 .map(|kt| create_mock_tempo_signature(kt, key_data.as_ref()))
115 .unwrap_or_else(|| {
116 create_mock_tempo_signature(&SignatureType::Secp256k1, None)
117 });
118
119 let calls = if !calls.is_empty() {
120 calls
121 } else if let Some(to) = &inner.to {
122 vec![Call {
123 to: *to,
124 value: inner.value.unwrap_or_default(),
125 input: inner.input.clone().into_input().unwrap_or_default(),
126 }]
127 } else {
128 return Err(EthApiError::InvalidParams("empty calls list".to_string()));
129 };
130
131 Some(Box::new(TempoBatchCallEnv {
132 aa_calls: calls,
133 signature: mock_signature,
134 tempo_authorization_list: tempo_authorization_list
135 .into_iter()
136 .map(RecoveredTempoAuthorization::new)
137 .collect(),
138 nonce_key: nonce_key.unwrap_or_default(),
139 key_authorization: None,
140 signature_hash: B256::ZERO,
141 valid_before: None,
142 valid_after: None,
143 subblock_transaction: false,
144 }))
145 } else {
146 None
147 },
148 inner: inner.try_into_tx_env(evm_env)?,
149 })
150 }
151}
152
153fn create_mock_tempo_signature(
155 key_type: &SignatureType,
156 key_data: Option<&Bytes>,
157) -> TempoSignature {
158 use tempo_primitives::transaction::tt_signature::{
159 P256SignatureWithPreHash, PrimitiveSignature, TempoSignature, WebAuthnSignature,
160 };
161
162 match key_type {
163 SignatureType::Secp256k1 => {
164 TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new(
166 alloy_primitives::U256::ZERO,
167 alloy_primitives::U256::ZERO,
168 false,
169 )))
170 }
171 SignatureType::P256 => {
172 TempoSignature::Primitive(PrimitiveSignature::P256(P256SignatureWithPreHash {
174 r: alloy_primitives::B256::ZERO,
175 s: alloy_primitives::B256::ZERO,
176 pub_key_x: alloy_primitives::B256::ZERO,
177 pub_key_y: alloy_primitives::B256::ZERO,
178 pre_hash: false,
179 }))
180 }
181 SignatureType::WebAuthn => {
182 const BASE_CLIENT_JSON: &str = r#"{"type":"webauthn.get","challenge":"","origin":""}"#;
190 const AUTH_DATA_SIZE: usize = 37;
191 const MIN_WEBAUTHN_SIZE: usize = AUTH_DATA_SIZE + BASE_CLIENT_JSON.len(); const DEFAULT_WEBAUTHN_SIZE: usize = 800; let size = if let Some(data) = key_data {
196 match data.len() {
197 1 => data[0] as usize,
198 2 => u16::from_be_bytes([data[0], data[1]]) as usize,
199 4 => u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize,
200 _ => DEFAULT_WEBAUTHN_SIZE, }
202 } else {
203 DEFAULT_WEBAUTHN_SIZE };
205
206 let size = size.max(MIN_WEBAUTHN_SIZE);
208
209 let mut webauthn_data = vec![0u8; AUTH_DATA_SIZE];
211 webauthn_data[32] = 0x01; let additional_bytes = size - MIN_WEBAUTHN_SIZE;
215 let client_json = if additional_bytes > 0 {
216 let padding = "x".repeat(additional_bytes);
219 format!(r#"{{"type":"webauthn.get","challenge":"","origin":"{padding}"}}"#,)
220 } else {
221 BASE_CLIENT_JSON.to_string()
222 };
223
224 webauthn_data.extend_from_slice(client_json.as_bytes());
225 let webauthn_data = Bytes::from(webauthn_data);
226
227 TempoSignature::Primitive(PrimitiveSignature::WebAuthn(WebAuthnSignature {
228 webauthn_data,
229 r: alloy_primitives::B256::ZERO,
230 s: alloy_primitives::B256::ZERO,
231 pub_key_x: alloy_primitives::B256::ZERO,
232 pub_key_y: alloy_primitives::B256::ZERO,
233 }))
234 }
235 }
236}
237
238impl SignableTxRequest<TempoTxEnvelope> for TempoTransactionRequest {
239 async fn try_build_and_sign(
240 self,
241 signer: impl TxSigner<Signature> + Send,
242 ) -> Result<TempoTxEnvelope, SignTxRequestError> {
243 SignableTxRequest::<TempoTxEnvelope>::try_build_and_sign(self.inner, signer).await
244 }
245}
246
247impl FromConsensusHeader<TempoHeader> for TempoHeaderResponse {
248 fn from_consensus_header(header: SealedHeader<TempoHeader>, block_size: usize) -> Self {
249 Self {
250 timestamp_millis: header.timestamp_millis(),
251 inner: FromConsensusHeader::from_consensus_header(header, block_size),
252 }
253 }
254}