1use std::{collections::HashMap, sync::Arc};
2
3use alloy::{
4 consensus::{
5 error::UnsupportedTransactionType,
6 proofs::{calculate_transaction_root, calculate_withdrawals_root},
7 Receipt, ReceiptWithBloom, TxReceipt, TxType, TypedTransaction,
8 },
9 eips::{BlockId, Encodable2718},
10 network::{
11 BuildResult, Network, NetworkWallet, TransactionBuilder, TransactionBuilderError,
12 UnbuiltTransactionError,
13 },
14 primitives::{Address, Bytes, ChainId, TxKind, U256},
15 rpc::types::{state::StateOverride, AccessList, Log, TransactionRequest},
16};
17use async_trait::async_trait;
18use revm::context::result::{ExecutionResult, HaltReason};
19
20use helios_common::{
21 execution_provider::ExecutionProvider,
22 fork_schedule::ForkSchedule,
23 network_spec::NetworkSpec,
24 types::{Account, EvmError},
25};
26
27use crate::evm::DataNetworkEvm;
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct DataNetwork;
31
32#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
33#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
34impl NetworkSpec for DataNetwork {
35 type HaltReason = HaltReason;
36
37 fn encode_receipt(receipt: &Self::ReceiptResponse) -> Vec<u8> {
38 let tx_type = receipt.transaction_type();
39 let receipt = receipt.inner.as_receipt_with_bloom().unwrap();
40 let logs = receipt
41 .logs()
42 .iter()
43 .map(|l| l.inner.clone())
44 .collect::<Vec<_>>();
45
46 let consensus_receipt = Receipt {
47 cumulative_gas_used: receipt.cumulative_gas_used(),
48 status: receipt.status_or_post_state(),
49 logs,
50 };
51
52 let rwb = ReceiptWithBloom::new(consensus_receipt, receipt.bloom());
53 let encoded = alloy::rlp::encode(rwb);
54
55 match tx_type {
56 TxType::Legacy => encoded,
57 _ => [vec![tx_type as u8], encoded].concat(),
58 }
59 }
60
61 fn encode_transaction(tx: &Self::TransactionResponse) -> Vec<u8> {
62 tx.inner.encoded_2718()
63 }
64
65 fn is_hash_valid(block: &Self::BlockResponse) -> bool {
66 if block.header.hash_slow() != block.header.hash {
67 return false;
68 }
69
70 if let Some(txs) = block.transactions.as_transactions() {
71 let txs_root = calculate_transaction_root(
72 &txs.iter().map(|t| t.clone().inner).collect::<Vec<_>>(),
73 );
74 if txs_root != block.header.transactions_root {
75 return false;
76 }
77 }
78
79 if let Some(withdrawals) = &block.withdrawals {
80 let withdrawals_root =
81 calculate_withdrawals_root(&withdrawals.iter().copied().collect::<Vec<_>>());
82 if Some(withdrawals_root) != block.header.withdrawals_root {
83 return false;
84 }
85 }
86
87 true
88 }
89
90 fn receipt_contains(list: &[Self::ReceiptResponse], elem: &Self::ReceiptResponse) -> bool {
91 for receipt in list {
92 if receipt == elem {
93 return true;
94 }
95 }
96
97 false
98 }
99
100 fn receipt_logs(receipt: &Self::ReceiptResponse) -> Vec<Log> {
101 receipt.inner.logs().to_vec()
102 }
103
104 async fn transact<E: ExecutionProvider<Self>>(
105 tx: &Self::TransactionRequest,
106 validate_tx: bool,
107 execution: Arc<E>,
108 chain_id: u64,
109 fork_schedule: ForkSchedule,
110 block_id: BlockId,
111 state_overrides: Option<StateOverride>,
112 ) -> Result<(ExecutionResult, HashMap<Address, Account>), EvmError> {
113 let mut evm = DataNetworkEvm::new(execution, chain_id, fork_schedule, block_id);
114
115 evm.transact_inner(tx, validate_tx, state_overrides).await
116 }
117}
118
119impl Network for DataNetwork {
120 type TxType = alloy::consensus::TxType;
121 type TxEnvelope = alloy::consensus::TxEnvelope;
122 type UnsignedTx = alloy::consensus::TypedTransaction;
123 type ReceiptEnvelope = alloy::consensus::ReceiptEnvelope;
124 type Header = alloy::consensus::Header;
125 type TransactionRequest = alloy::rpc::types::TransactionRequest;
126 type TransactionResponse = alloy::rpc::types::Transaction;
127 type ReceiptResponse = alloy::rpc::types::TransactionReceipt;
128 type HeaderResponse = alloy::rpc::types::Header;
129 type BlockResponse = alloy::rpc::types::Block<Self::TransactionResponse, Self::HeaderResponse>;
130}
131
132impl TransactionBuilder<DataNetwork> for TransactionRequest {
133 fn chain_id(&self) -> Option<ChainId> {
134 self.chain_id
135 }
136
137 fn set_chain_id(&mut self, chain_id: ChainId) {
138 self.chain_id = Some(chain_id);
139 }
140
141 fn nonce(&self) -> Option<u64> {
142 self.nonce
143 }
144
145 fn set_nonce(&mut self, nonce: u64) {
146 self.nonce = Some(nonce);
147 }
148
149 fn take_nonce(&mut self) -> Option<u64> {
150 self.nonce.take()
151 }
152
153 fn input(&self) -> Option<&Bytes> {
154 self.input.input()
155 }
156
157 fn set_input<T: Into<Bytes>>(&mut self, input: T) {
158 self.input.input = Some(input.into());
159 }
160
161 fn from(&self) -> Option<Address> {
162 self.from
163 }
164
165 fn set_from(&mut self, from: Address) {
166 self.from = Some(from);
167 }
168
169 fn kind(&self) -> Option<TxKind> {
170 self.to
171 }
172
173 fn clear_kind(&mut self) {
174 self.to = None;
175 }
176
177 fn set_kind(&mut self, kind: TxKind) {
178 self.to = Some(kind);
179 }
180
181 fn value(&self) -> Option<U256> {
182 self.value
183 }
184
185 fn set_value(&mut self, value: U256) {
186 self.value = Some(value)
187 }
188
189 fn gas_price(&self) -> Option<u128> {
190 self.gas_price
191 }
192
193 fn set_gas_price(&mut self, gas_price: u128) {
194 self.gas_price = Some(gas_price);
195 }
196
197 fn max_fee_per_gas(&self) -> Option<u128> {
198 self.max_fee_per_gas
199 }
200
201 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
202 self.max_fee_per_gas = Some(max_fee_per_gas);
203 }
204
205 fn max_priority_fee_per_gas(&self) -> Option<u128> {
206 self.max_priority_fee_per_gas
207 }
208
209 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
210 self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
211 }
212
213 fn gas_limit(&self) -> Option<u64> {
214 self.gas
215 }
216
217 fn set_gas_limit(&mut self, gas_limit: u64) {
218 self.gas = Some(gas_limit);
219 }
220
221 fn access_list(&self) -> Option<&AccessList> {
222 self.access_list.as_ref()
223 }
224
225 fn set_access_list(&mut self, access_list: AccessList) {
226 self.access_list = Some(access_list);
227 }
228
229 fn complete_type(&self, ty: TxType) -> Result<(), Vec<&'static str>> {
230 match ty {
231 TxType::Legacy => self.complete_legacy(),
232 TxType::Eip2930 => self.complete_2930(),
233 TxType::Eip1559 => self.complete_1559(),
234 TxType::Eip4844 => self.complete_4844(),
235 TxType::Eip7702 => self.complete_7702(),
236 }
237 }
238
239 fn can_submit(&self) -> bool {
240 self.from.is_some()
244 }
245
246 fn can_build(&self) -> bool {
247 let common = self.gas.is_some() && self.nonce.is_some();
252
253 let legacy = self.gas_price.is_some();
254 let eip2930 = legacy
255 && <TransactionRequest as TransactionBuilder<DataNetwork>>::access_list(self).is_some();
256
257 let eip1559 = self.max_fee_per_gas.is_some() && self.max_priority_fee_per_gas.is_some();
258
259 let eip4844 = eip1559 && self.sidecar.is_some() && self.to.is_some();
260 common && (legacy || eip2930 || eip1559 || eip4844)
261 }
262
263 #[doc(alias = "output_transaction_type")]
264 fn output_tx_type(&self) -> TxType {
265 self.preferred_type()
266 }
267
268 #[doc(alias = "output_transaction_type_checked")]
269 fn output_tx_type_checked(&self) -> Option<TxType> {
270 match self.buildable_type() {
271 Some(TxType::Eip4844) => None,
272 tx_type => tx_type,
273 }
274 }
275
276 fn prep_for_submission(&mut self) {
277 self.transaction_type = Some(self.preferred_type() as u8);
278 self.trim_conflicting_keys();
279 self.populate_blob_hashes();
280 }
281
282 fn build_unsigned(self) -> BuildResult<TypedTransaction, DataNetwork> {
283 if let Err((tx_type, missing)) = self.missing_keys() {
284 return Err(
285 TransactionBuilderError::InvalidTransactionRequest(tx_type, missing)
286 .into_unbuilt(self),
287 );
288 }
289
290 if let Some(TxType::Eip4844) = self.buildable_type() {
291 return Err(UnbuiltTransactionError {
292 request: self,
293 error: TransactionBuilderError::Custom(Box::new(UnsupportedTransactionType::new(
294 TxType::Eip4844,
295 ))),
296 });
297 }
298
299 Ok(self.build_typed_tx().expect("checked by missing_keys"))
300 }
301
302 async fn build<W: NetworkWallet<DataNetwork>>(
303 self,
304 wallet: &W,
305 ) -> Result<<DataNetwork as Network>::TxEnvelope, TransactionBuilderError<DataNetwork>> {
306 Ok(wallet.sign_request(self).await?)
307 }
308}