@nomicfoundation/edr 0.10.0 → 0.12.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/logger.rs CHANGED
@@ -1,29 +1,12 @@
1
- use std::{
2
- fmt::Display,
3
- sync::{mpsc::channel, Arc},
4
- };
1
+ use std::sync::{Arc, mpsc::channel};
5
2
 
6
- use ansi_term::{Color, Style};
7
- use edr_eth::{
8
- transaction::{self, Transaction},
9
- Bytes, B256, U256,
10
- };
11
- use edr_evm::{
12
- blockchain::BlockchainError,
13
- chain_spec::L1ChainSpec,
14
- precompile::{self, Precompiles},
15
- trace::{AfterMessage, TraceMessage},
16
- transaction::SignedTransaction as _,
17
- ExecutionResult, SyncBlock,
18
- };
19
- use edr_provider::{ProviderError, TransactionFailure};
20
- use edr_solidity::contract_decoder::ContractDecoder;
21
- use itertools::izip;
3
+ use edr_eth::Bytes;
4
+ use edr_napi_core::logger::LoggerError;
22
5
  use napi::{
6
+ JsFunction, Status,
23
7
  threadsafe_function::{
24
8
  ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
25
9
  },
26
- Env, JsFunction, Status,
27
10
  };
28
11
  use napi_derive::napi;
29
12
 
@@ -55,227 +38,15 @@ pub struct LoggerConfig {
55
38
  pub print_line_callback: JsFunction,
56
39
  }
57
40
 
58
- #[derive(Clone)]
59
- pub enum LoggingState {
60
- CollapsingMethod(CollapsedMethod),
61
- HardhatMinining {
62
- empty_blocks_range_start: Option<u64>,
63
- },
64
- IntervalMining {
65
- empty_blocks_range_start: Option<u64>,
66
- },
67
- Empty,
68
- }
69
-
70
- impl LoggingState {
71
- /// Converts the state into a hardhat mining state.
72
- pub fn into_hardhat_mining(self) -> Option<u64> {
73
- match self {
74
- Self::HardhatMinining {
75
- empty_blocks_range_start,
76
- } => empty_blocks_range_start,
77
- _ => None,
78
- }
79
- }
80
-
81
- /// Converts the state into an interval mining state.
82
- pub fn into_interval_mining(self) -> Option<u64> {
83
- match self {
84
- Self::IntervalMining {
85
- empty_blocks_range_start,
86
- } => empty_blocks_range_start,
87
- _ => None,
88
- }
89
- }
90
- }
91
-
92
- impl Default for LoggingState {
93
- fn default() -> Self {
94
- Self::Empty
95
- }
96
- }
97
-
98
- #[derive(Clone)]
99
- enum LogLine {
100
- Single(String),
101
- WithTitle(String, String),
102
- }
103
-
104
- #[derive(Debug, thiserror::Error)]
105
- pub enum LoggerError {
106
- #[error("Failed to print line")]
107
- PrintLine,
108
- }
109
-
110
- #[derive(Clone)]
111
- pub struct Logger {
112
- collector: LogCollector,
113
- }
114
-
115
- impl Logger {
116
- pub fn new(
117
- env: &Env,
118
- config: LoggerConfig,
119
- contract_decoder: Arc<ContractDecoder>,
120
- ) -> napi::Result<Self> {
121
- Ok(Self {
122
- collector: LogCollector::new(env, config, contract_decoder)?,
123
- })
124
- }
125
- }
126
-
127
- impl edr_provider::Logger for Logger {
128
- type BlockchainError = BlockchainError;
129
-
130
- type LoggerError = LoggerError;
131
-
132
- fn is_enabled(&self) -> bool {
133
- self.collector.is_enabled
134
- }
135
-
136
- fn set_is_enabled(&mut self, is_enabled: bool) {
137
- self.collector.is_enabled = is_enabled;
138
- }
139
-
140
- fn log_call(
141
- &mut self,
142
- spec_id: edr_eth::SpecId,
143
- transaction: &transaction::Signed,
144
- result: &edr_provider::CallResult,
145
- ) -> Result<(), Self::LoggerError> {
146
- self.collector.log_call(spec_id, transaction, result);
147
-
148
- Ok(())
149
- }
150
-
151
- fn log_estimate_gas_failure(
152
- &mut self,
153
- spec_id: edr_eth::SpecId,
154
- transaction: &transaction::Signed,
155
- failure: &edr_provider::EstimateGasFailure,
156
- ) -> Result<(), Self::LoggerError> {
157
- self.collector
158
- .log_estimate_gas(spec_id, transaction, failure);
159
-
160
- Ok(())
161
- }
162
-
163
- fn log_interval_mined(
164
- &mut self,
165
- spec_id: edr_eth::SpecId,
166
- mining_result: &edr_provider::DebugMineBlockResult<Self::BlockchainError>,
167
- ) -> Result<(), Self::LoggerError> {
168
- self.collector.log_interval_mined(spec_id, mining_result)
169
- }
170
-
171
- fn log_mined_block(
172
- &mut self,
173
- spec_id: edr_eth::SpecId,
174
- mining_results: &[edr_provider::DebugMineBlockResult<Self::BlockchainError>],
175
- ) -> Result<(), Self::LoggerError> {
176
- self.collector.log_mined_blocks(spec_id, mining_results);
177
-
178
- Ok(())
179
- }
180
-
181
- fn log_send_transaction(
182
- &mut self,
183
- spec_id: edr_eth::SpecId,
184
- transaction: &edr_evm::transaction::Signed,
185
- mining_results: &[edr_provider::DebugMineBlockResult<Self::BlockchainError>],
186
- ) -> Result<(), Self::LoggerError> {
187
- self.collector
188
- .log_send_transaction(spec_id, transaction, mining_results);
189
-
190
- Ok(())
191
- }
192
-
193
- fn print_method_logs(
194
- &mut self,
195
- method: &str,
196
- error: Option<&ProviderError<LoggerError>>,
197
- ) -> Result<(), Self::LoggerError> {
198
- if let Some(error) = error {
199
- self.collector.state = LoggingState::Empty;
200
-
201
- if matches!(error, ProviderError::UnsupportedMethod { .. }) {
202
- self.collector
203
- .print::<false>(Color::Red.paint(error.to_string()))?;
204
- } else {
205
- self.collector.print::<false>(Color::Red.paint(method))?;
206
- self.collector.print_logs()?;
207
-
208
- if !matches!(error, ProviderError::TransactionFailed(_)) {
209
- self.collector.print_empty_line()?;
210
-
211
- let error_message = error.to_string();
212
- self.collector
213
- .try_indented(|logger| logger.print::<false>(&error_message))?;
214
-
215
- if matches!(error, ProviderError::InvalidEip155TransactionChainId) {
216
- self.collector.try_indented(|logger| {
217
- logger.print::<false>(Color::Yellow.paint(
218
- "If you are using MetaMask, you can learn how to fix this error here: https://hardhat.org/metamask-issue"
219
- ))
220
- })?;
221
- }
222
- }
223
-
224
- self.collector.print_empty_line()?;
225
- }
226
- } else {
227
- self.collector.print_method(method)?;
228
-
229
- let printed = self.collector.print_logs()?;
230
- if printed {
231
- self.collector.print_empty_line()?;
232
- }
233
- }
234
-
235
- Ok(())
236
- }
237
-
238
- fn print_contract_decoding_error(&mut self, error: &str) -> Result<(), Self::LoggerError> {
239
- self.collector.log(
240
- "Contract decoder failed to be updated. Please report this to help us improve Hardhat.",
241
- );
242
- self.collector.print_empty_line()?;
243
- self.collector.log(error);
244
- Ok(())
245
- }
246
- }
247
-
248
- #[derive(Clone)]
249
- pub struct CollapsedMethod {
250
- count: usize,
251
- method: String,
252
- }
253
-
254
- #[derive(Clone)]
255
- struct LogCollector {
256
- contract_decoder: Arc<ContractDecoder>,
257
- decode_console_log_inputs_fn: ThreadsafeFunction<Vec<Bytes>, ErrorStrategy::Fatal>,
258
- indentation: usize,
259
- is_enabled: bool,
260
- logs: Vec<LogLine>,
261
- print_line_fn: ThreadsafeFunction<(String, bool), ErrorStrategy::Fatal>,
262
- state: LoggingState,
263
- title_length: usize,
264
- }
265
-
266
- impl LogCollector {
267
- pub fn new(
268
- env: &Env,
269
- config: LoggerConfig,
270
- contract_decoder: Arc<ContractDecoder>,
271
- ) -> napi::Result<Self> {
272
- let mut decode_console_log_inputs_fn = config
273
- .decode_console_log_inputs_callback
274
- .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
275
- let inputs =
276
- ctx.env
277
- .create_array_with_length(ctx.value.len())
278
- .and_then(|mut inputs| {
41
+ impl LoggerConfig {
42
+ /// Resolves the logger config, converting it to a
43
+ /// `edr_napi_core::logger::Config`.
44
+ pub fn resolve(self, env: &napi::Env) -> napi::Result<edr_napi_core::logger::Config> {
45
+ let mut decode_console_log_inputs_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> =
46
+ self.decode_console_log_inputs_callback
47
+ .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
48
+ let inputs = ctx.env.create_array_with_length(ctx.value.len()).and_then(
49
+ |mut inputs| {
279
50
  for (idx, input) in ctx.value.into_iter().enumerate() {
280
51
  ctx.env.create_buffer_with_data(input.to_vec()).and_then(
281
52
  |input| inputs.set_element(idx as u32, input.into_raw()),
@@ -283,18 +54,39 @@ impl LogCollector {
283
54
  }
284
55
 
285
56
  Ok(inputs)
286
- })?;
57
+ },
58
+ )?;
287
59
 
288
- Ok(vec![inputs])
289
- })?;
60
+ Ok(vec![inputs])
61
+ })?;
290
62
 
291
63
  // Maintain a weak reference to the function to avoid the event loop from
292
64
  // exiting.
293
- decode_console_log_inputs_fn.unref(env)?;
65
+ decode_console_log_inputs_callback.unref(env)?;
66
+
67
+ let decode_console_log_inputs_fn = Arc::new(move |console_log_inputs| {
68
+ let (sender, receiver) = channel();
69
+
70
+ let status = decode_console_log_inputs_callback.call_with_return_value(
71
+ console_log_inputs,
72
+ ThreadsafeFunctionCallMode::Blocking,
73
+ move |decoded_inputs: Vec<String>| {
74
+ sender.send(decoded_inputs).map_err(|_error| {
75
+ napi::Error::new(
76
+ Status::GenericFailure,
77
+ "Failed to send result from decode_console_log_inputs",
78
+ )
79
+ })
80
+ },
81
+ );
82
+ assert_eq!(status, Status::Ok);
294
83
 
295
- let mut print_line_fn = config.print_line_callback.create_threadsafe_function(
296
- 0,
297
- |ctx: ThreadSafeCallContext<(String, bool)>| {
84
+ receiver.recv().unwrap()
85
+ });
86
+
87
+ let mut print_line_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> = self
88
+ .print_line_callback
89
+ .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<(String, bool)>| {
298
90
  // String
299
91
  let message = ctx.env.create_string_from_std(ctx.value.0)?;
300
92
 
@@ -302,917 +94,27 @@ impl LogCollector {
302
94
  let replace = ctx.env.get_boolean(ctx.value.1)?;
303
95
 
304
96
  Ok(vec![message.into_unknown(), replace.into_unknown()])
305
- },
306
- )?;
97
+ })?;
307
98
 
308
99
  // Maintain a weak reference to the function to avoid the event loop from
309
100
  // exiting.
310
- print_line_fn.unref(env)?;
311
-
312
- Ok(Self {
313
- contract_decoder,
314
- decode_console_log_inputs_fn,
315
- indentation: 0,
316
- is_enabled: config.enable,
317
- logs: Vec::new(),
318
- print_line_fn,
319
- state: LoggingState::default(),
320
- title_length: 0,
321
- })
322
- }
323
-
324
- pub fn log_call(
325
- &mut self,
326
- spec_id: edr_eth::SpecId,
327
- transaction: &transaction::Signed,
328
- result: &edr_provider::CallResult,
329
- ) {
330
- let edr_provider::CallResult {
331
- console_log_inputs,
332
- execution_result,
333
- trace,
334
- } = result;
335
-
336
- self.state = LoggingState::Empty;
337
-
338
- self.indented(|logger| {
339
- logger.log_contract_and_function_name::<true>(spec_id, trace);
340
-
341
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
342
- if let Some(to) = transaction.kind().to() {
343
- logger.log_with_title("To", format!("0x{to:x}"));
344
- }
345
- if transaction.value() > U256::ZERO {
346
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
347
- }
348
-
349
- logger.log_console_log_messages(console_log_inputs);
350
-
351
- if let Some(transaction_failure) =
352
- TransactionFailure::from_execution_result(execution_result, None, trace)
353
- {
354
- logger.log_transaction_failure(&transaction_failure);
355
- }
356
- });
357
- }
358
-
359
- pub fn log_estimate_gas(
360
- &mut self,
361
- spec_id: edr_eth::SpecId,
362
- transaction: &transaction::Signed,
363
- result: &edr_provider::EstimateGasFailure,
364
- ) {
365
- let edr_provider::EstimateGasFailure {
366
- console_log_inputs,
367
- transaction_failure,
368
- } = result;
369
-
370
- self.state = LoggingState::Empty;
371
-
372
- self.indented(|logger| {
373
- logger.log_contract_and_function_name::<true>(
374
- spec_id,
375
- &transaction_failure.failure.solidity_trace,
376
- );
377
-
378
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
379
- if let Some(to) = transaction.kind().to() {
380
- logger.log_with_title("To", format!("0x{to:x}"));
381
- }
382
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
383
-
384
- logger.log_console_log_messages(console_log_inputs);
385
-
386
- logger.log_transaction_failure(&transaction_failure.failure);
387
- });
388
- }
389
-
390
- fn log_transaction_failure(&mut self, failure: &edr_provider::TransactionFailure) {
391
- let is_revert_error = matches!(
392
- failure.reason,
393
- edr_provider::TransactionFailureReason::Revert(_)
394
- );
395
-
396
- let error_type = if is_revert_error {
397
- "Error"
398
- } else {
399
- "TransactionExecutionError"
400
- };
401
-
402
- self.log_empty_line();
403
- self.log(format!("{error_type}: {failure}"));
404
- }
405
-
406
- pub fn log_mined_blocks(
407
- &mut self,
408
- spec_id: edr_eth::SpecId,
409
- mining_results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
410
- ) {
411
- let num_results = mining_results.len();
412
- for (idx, mining_result) in mining_results.iter().enumerate() {
413
- let state = std::mem::take(&mut self.state);
414
- let empty_blocks_range_start = state.into_hardhat_mining();
415
-
416
- if mining_result.block.transactions().is_empty() {
417
- self.log_hardhat_mined_empty_block(&mining_result.block, empty_blocks_range_start);
418
-
419
- let block_number = mining_result.block.header().number;
420
- self.state = LoggingState::HardhatMinining {
421
- empty_blocks_range_start: Some(
422
- empty_blocks_range_start.unwrap_or(block_number),
423
- ),
424
- };
425
- } else {
426
- self.log_hardhat_mined_block(spec_id, mining_result);
427
-
428
- if idx < num_results - 1 {
429
- self.log_empty_line();
430
- }
431
- }
432
- }
433
- }
434
-
435
- pub fn log_interval_mined(
436
- &mut self,
437
- spec_id: edr_eth::SpecId,
438
- mining_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
439
- ) -> Result<(), LoggerError> {
440
- let block_header = mining_result.block.header();
441
- let block_number = block_header.number;
442
-
443
- if mining_result.block.transactions().is_empty() {
444
- let state = std::mem::take(&mut self.state);
445
- let empty_blocks_range_start = state.into_interval_mining();
446
-
447
- if let Some(empty_blocks_range_start) = empty_blocks_range_start {
448
- self.print::<true>(format!(
449
- "Mined empty block range #{empty_blocks_range_start} to #{block_number}"
450
- ))?;
451
- } else {
452
- let base_fee = if let Some(base_fee) = block_header.base_fee_per_gas.as_ref() {
453
- format!(" with base fee {base_fee}")
454
- } else {
455
- String::new()
456
- };
457
-
458
- self.print::<false>(format!("Mined empty block #{block_number}{base_fee}"))?;
459
- }
460
-
461
- self.state = LoggingState::IntervalMining {
462
- empty_blocks_range_start: Some(
463
- empty_blocks_range_start.unwrap_or(block_header.number),
464
- ),
465
- };
466
- } else {
467
- self.log_interval_mined_block(spec_id, mining_result);
468
-
469
- self.print::<false>(format!("Mined block #{block_number}"))?;
470
-
471
- let printed = self.print_logs()?;
472
- if printed {
473
- self.print_empty_line()?;
474
- }
475
- }
476
-
477
- Ok(())
478
- }
479
-
480
- pub fn log_send_transaction(
481
- &mut self,
482
- spec_id: edr_eth::SpecId,
483
- transaction: &edr_evm::transaction::Signed,
484
- mining_results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
485
- ) {
486
- if !mining_results.is_empty() {
487
- self.state = LoggingState::Empty;
488
-
489
- let (sent_block_result, sent_transaction_result, sent_trace) = mining_results
490
- .iter()
491
- .find_map(|result| {
492
- izip!(
493
- result.block.transactions(),
494
- result.transaction_results.iter(),
495
- result.transaction_traces.iter()
496
- )
497
- .find(|(block_transaction, _, _)| {
498
- *block_transaction.transaction_hash() == *transaction.transaction_hash()
499
- })
500
- .map(|(_, transaction_result, trace)| (result, transaction_result, trace))
501
- })
502
- .expect("Transaction result not found");
503
-
504
- if mining_results.len() > 1 {
505
- self.log_multiple_blocks_warning();
506
- self.log_auto_mined_block_results(
507
- spec_id,
508
- mining_results,
509
- transaction.transaction_hash(),
510
- );
511
- self.log_currently_sent_transaction(
512
- spec_id,
513
- sent_block_result,
514
- transaction,
515
- sent_transaction_result,
516
- sent_trace,
517
- );
518
- } else if let Some(result) = mining_results.first() {
519
- let transactions = result.block.transactions();
520
- if transactions.len() > 1 {
521
- self.log_multiple_transactions_warning();
522
- self.log_auto_mined_block_results(
523
- spec_id,
524
- mining_results,
525
- transaction.transaction_hash(),
526
- );
527
- self.log_currently_sent_transaction(
528
- spec_id,
529
- sent_block_result,
530
- transaction,
531
- sent_transaction_result,
532
- sent_trace,
533
- );
534
- } else if let Some(transaction) = transactions.first() {
535
- self.log_single_transaction_mining_result(spec_id, result, transaction);
536
- }
537
- }
538
- }
539
- }
540
-
541
- fn contract_and_function_name(
542
- &self,
543
- code: Bytes,
544
- calldata: Option<Bytes>,
545
- ) -> (String, Option<String>) {
546
- let edr_solidity::contract_decoder::ContractAndFunctionName {
547
- contract_name,
548
- function_name,
549
- } = self
550
- .contract_decoder
551
- .get_contract_and_function_names_for_call(&code, calldata.as_ref());
552
- (contract_name, function_name)
553
- }
554
-
555
- fn format(&self, message: impl ToString) -> String {
556
- let message = message.to_string();
557
-
558
- if message.is_empty() {
559
- message
560
- } else {
561
- message
562
- .split('\n')
563
- .map(|line| format!("{:indent$}{line}", "", indent = self.indentation))
564
- .collect::<Vec<_>>()
565
- .join("\n")
566
- }
567
- }
568
-
569
- fn indented(&mut self, display_fn: impl FnOnce(&mut Self)) {
570
- self.indentation += 2;
571
- display_fn(self);
572
- self.indentation -= 2;
573
- }
574
-
575
- fn try_indented(
576
- &mut self,
577
- display_fn: impl FnOnce(&mut Self) -> Result<(), LoggerError>,
578
- ) -> Result<(), LoggerError> {
579
- self.indentation += 2;
580
- let result = display_fn(self);
581
- self.indentation -= 2;
582
-
583
- result
584
- }
585
-
586
- fn log(&mut self, message: impl ToString) {
587
- let formatted = self.format(message);
588
-
589
- self.logs.push(LogLine::Single(formatted));
590
- }
591
-
592
- fn log_auto_mined_block_results(
593
- &mut self,
594
- spec_id: edr_eth::SpecId,
595
- results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
596
- sent_transaction_hash: &B256,
597
- ) {
598
- for result in results {
599
- self.log_block_from_auto_mine(spec_id, result, sent_transaction_hash);
600
- }
601
- }
602
-
603
- fn log_base_fee(&mut self, base_fee: Option<&U256>) {
604
- if let Some(base_fee) = base_fee {
605
- self.log(format!("Base fee: {base_fee}"));
606
- }
607
- }
101
+ print_line_callback.unref(env)?;
608
102
 
609
- fn log_block_from_auto_mine(
610
- &mut self,
611
- spec_id: edr_eth::SpecId,
612
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
613
- transaction_hash_to_highlight: &edr_eth::B256,
614
- ) {
615
- let edr_provider::DebugMineBlockResult {
616
- block,
617
- transaction_results,
618
- transaction_traces,
619
- console_log_inputs,
620
- } = result;
103
+ let print_line_fn = Arc::new(move |message, replace| {
104
+ let status =
105
+ print_line_callback.call((message, replace), ThreadsafeFunctionCallMode::Blocking);
621
106
 
622
- let transactions = block.transactions();
623
- let num_transactions = transactions.len();
624
-
625
- debug_assert_eq!(num_transactions, transaction_results.len());
626
- debug_assert_eq!(num_transactions, transaction_traces.len());
627
-
628
- let block_header = block.header();
629
-
630
- self.indented(|logger| {
631
- logger.log_block_id(block);
632
-
633
- logger.indented(|logger| {
634
- logger.log_base_fee(block_header.base_fee_per_gas.as_ref());
635
-
636
- for (idx, transaction, result, trace) in izip!(
637
- 0..num_transactions,
638
- transactions,
639
- transaction_results,
640
- transaction_traces
641
- ) {
642
- let should_highlight_hash =
643
- *transaction.transaction_hash() == *transaction_hash_to_highlight;
644
- logger.log_block_transaction(
645
- spec_id,
646
- transaction,
647
- result,
648
- trace,
649
- console_log_inputs,
650
- should_highlight_hash,
651
- );
652
-
653
- logger.log_empty_line_between_transactions(idx, num_transactions);
654
- }
655
- });
656
- });
657
-
658
- self.log_empty_line();
659
- }
660
-
661
- fn log_block_hash(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
662
- let block_hash = block.hash();
663
-
664
- self.log(format!("Block: {block_hash}"));
665
- }
666
-
667
- fn log_block_id(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
668
- let block_number = block.header().number;
669
- let block_hash = block.hash();
670
-
671
- self.log(format!("Block #{block_number}: {block_hash}"));
672
- }
673
-
674
- fn log_block_number(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
675
- let block_number = block.header().number;
676
-
677
- self.log(format!("Mined block #{block_number}"));
678
- }
679
-
680
- /// Logs a transaction that's part of a block.
681
- fn log_block_transaction(
682
- &mut self,
683
- spec_id: edr_eth::SpecId,
684
- transaction: &edr_evm::transaction::Signed,
685
- result: &edr_evm::ExecutionResult,
686
- trace: &edr_evm::trace::Trace,
687
- console_log_inputs: &[Bytes],
688
- should_highlight_hash: bool,
689
- ) {
690
- let transaction_hash = transaction.transaction_hash();
691
- if should_highlight_hash {
692
- self.log_with_title(
693
- "Transaction",
694
- Style::new().bold().paint(transaction_hash.to_string()),
695
- );
696
- } else {
697
- self.log_with_title("Transaction", transaction_hash.to_string());
698
- }
699
-
700
- self.indented(|logger| {
701
- logger.log_contract_and_function_name::<false>(spec_id, trace);
702
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
703
- if let Some(to) = transaction.kind().to() {
704
- logger.log_with_title("To", format!("0x{to:x}"));
705
- }
706
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
707
- logger.log_with_title(
708
- "Gas used",
709
- format!(
710
- "{gas_used} of {gas_limit}",
711
- gas_used = result.gas_used(),
712
- gas_limit = transaction.gas_limit()
713
- ),
714
- );
715
-
716
- logger.log_console_log_messages(console_log_inputs);
717
-
718
- let transaction_failure = edr_provider::TransactionFailure::from_execution_result(
719
- result,
720
- Some(transaction_hash),
721
- trace,
722
- );
723
-
724
- if let Some(transaction_failure) = transaction_failure {
725
- logger.log_transaction_failure(&transaction_failure);
726
- }
727
- });
728
- }
729
-
730
- fn log_console_log_messages(&mut self, console_log_inputs: &[Bytes]) {
731
- let (sender, receiver) = channel();
732
-
733
- let status = self.decode_console_log_inputs_fn.call_with_return_value(
734
- console_log_inputs.to_vec(),
735
- ThreadsafeFunctionCallMode::Blocking,
736
- move |decoded_inputs: Vec<String>| {
737
- sender.send(decoded_inputs).map_err(|_error| {
738
- napi::Error::new(
739
- Status::GenericFailure,
740
- "Failed to send result from decode_console_log_inputs",
741
- )
742
- })
743
- },
744
- );
745
- assert_eq!(status, Status::Ok);
746
-
747
- let console_log_inputs = receiver.recv().unwrap();
748
- // This is a special case, as we always want to print the console.log messages.
749
- // The difference is how. If we have a logger, we should use that, so that logs
750
- // are printed in order. If we don't, we just print the messages here.
751
- if self.is_enabled {
752
- if !console_log_inputs.is_empty() {
753
- self.log_empty_line();
754
- self.log("console.log:");
755
-
756
- self.indented(|logger| {
757
- for input in console_log_inputs {
758
- logger.log(input);
759
- }
760
- });
761
- }
762
- } else {
763
- for input in console_log_inputs {
764
- let status = self
765
- .print_line_fn
766
- .call((input, false), ThreadsafeFunctionCallMode::Blocking);
767
-
768
- assert_eq!(status, napi::Status::Ok);
769
- }
770
- }
771
- }
772
-
773
- fn log_contract_and_function_name<const PRINT_INVALID_CONTRACT_WARNING: bool>(
774
- &mut self,
775
- spec_id: edr_eth::SpecId,
776
- trace: &edr_evm::trace::Trace,
777
- ) {
778
- if let Some(TraceMessage::Before(before_message)) = trace.messages.first() {
779
- if let Some(to) = before_message.to {
780
- // Call
781
- let is_precompile = {
782
- let precompiles =
783
- Precompiles::new(precompile::PrecompileSpecId::from_spec_id(spec_id));
784
- precompiles.contains(&to)
785
- };
786
-
787
- if is_precompile {
788
- let precompile = u16::from_be_bytes([to[18], to[19]]);
789
- self.log_with_title(
790
- "Precompile call",
791
- format!("<PrecompileContract {precompile}>"),
792
- );
793
- } else {
794
- let is_code_empty = before_message
795
- .code
796
- .as_ref()
797
- .map_or(true, edr_evm::Bytecode::is_empty);
798
-
799
- if is_code_empty {
800
- if PRINT_INVALID_CONTRACT_WARNING {
801
- self.log("WARNING: Calling an account which is not a contract");
802
- }
803
- } else {
804
- let (contract_name, function_name) = self.contract_and_function_name(
805
- before_message
806
- .code
807
- .as_ref()
808
- .map(edr_evm::Bytecode::original_bytes)
809
- .expect("Call must be defined"),
810
- Some(before_message.data.clone()),
811
- );
812
-
813
- let function_name = function_name.expect("Function name must be defined");
814
- self.log_with_title(
815
- "Contract call",
816
- if function_name.is_empty() {
817
- contract_name
818
- } else {
819
- format!("{contract_name}#{function_name}")
820
- },
821
- );
822
- }
823
- }
824
- } else {
825
- let result = if let Some(TraceMessage::After(AfterMessage {
826
- execution_result,
827
- ..
828
- })) = trace.messages.last()
829
- {
830
- execution_result
831
- } else {
832
- unreachable!("Before messages must have an after message")
833
- };
834
-
835
- // Create
836
- let (contract_name, _) =
837
- self.contract_and_function_name(before_message.data.clone(), None);
838
-
839
- self.log_with_title("Contract deployment", contract_name);
840
-
841
- if let ExecutionResult::Success { output, .. } = result {
842
- if let edr_evm::Output::Create(_, address) = output {
843
- if let Some(deployed_address) = address {
844
- self.log_with_title(
845
- "Contract address",
846
- format!("0x{deployed_address:x}"),
847
- );
848
- }
849
- } else {
850
- unreachable!("Create calls must return a Create output")
851
- }
852
- }
853
- }
854
- }
855
- }
856
-
857
- fn log_empty_block(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
858
- let block_header = block.header();
859
- let block_number = block_header.number;
860
-
861
- let base_fee = if let Some(base_fee) = block_header.base_fee_per_gas.as_ref() {
862
- format!(" with base fee {base_fee}")
863
- } else {
864
- String::new()
865
- };
866
-
867
- self.log(format!("Mined empty block #{block_number}{base_fee}",));
868
- }
869
-
870
- fn log_empty_line(&mut self) {
871
- self.log("");
872
- }
873
-
874
- fn log_empty_line_between_transactions(&mut self, idx: usize, num_transactions: usize) {
875
- if num_transactions > 1 && idx < num_transactions - 1 {
876
- self.log_empty_line();
877
- }
878
- }
879
-
880
- fn log_hardhat_mined_empty_block(
881
- &mut self,
882
- block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>,
883
- empty_blocks_range_start: Option<u64>,
884
- ) {
885
- self.indented(|logger| {
886
- if let Some(empty_blocks_range_start) = empty_blocks_range_start {
887
- logger.replace_last_log_line(format!(
888
- "Mined empty block range #{empty_blocks_range_start} to #{block_number}",
889
- block_number = block.header().number
890
- ));
891
- } else {
892
- logger.log_empty_block(block);
893
- }
894
- });
895
- }
896
-
897
- /// Logs the result of interval mining a block.
898
- fn log_interval_mined_block(
899
- &mut self,
900
- spec_id: edr_eth::SpecId,
901
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
902
- ) {
903
- let edr_provider::DebugMineBlockResult {
904
- block,
905
- transaction_results,
906
- transaction_traces,
907
- console_log_inputs,
908
- } = result;
909
-
910
- let transactions = block.transactions();
911
- let num_transactions = transactions.len();
912
-
913
- debug_assert_eq!(num_transactions, transaction_results.len());
914
- debug_assert_eq!(num_transactions, transaction_traces.len());
915
-
916
- let block_header = block.header();
917
-
918
- self.indented(|logger| {
919
- logger.log_block_hash(block);
920
-
921
- logger.indented(|logger| {
922
- logger.log_base_fee(block_header.base_fee_per_gas.as_ref());
923
-
924
- for (idx, transaction, result, trace) in izip!(
925
- 0..num_transactions,
926
- transactions,
927
- transaction_results,
928
- transaction_traces
929
- ) {
930
- logger.log_block_transaction(
931
- spec_id,
932
- transaction,
933
- result,
934
- trace,
935
- console_log_inputs,
936
- false,
937
- );
938
-
939
- logger.log_empty_line_between_transactions(idx, num_transactions);
940
- }
941
- });
942
- });
943
- }
944
-
945
- fn log_hardhat_mined_block(
946
- &mut self,
947
- spec_id: edr_eth::SpecId,
948
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
949
- ) {
950
- let edr_provider::DebugMineBlockResult {
951
- block,
952
- transaction_results,
953
- transaction_traces,
954
- console_log_inputs,
955
- } = result;
956
-
957
- let transactions = block.transactions();
958
- let num_transactions = transactions.len();
959
-
960
- debug_assert_eq!(num_transactions, transaction_results.len());
961
- debug_assert_eq!(num_transactions, transaction_traces.len());
962
-
963
- self.indented(|logger| {
964
- if transactions.is_empty() {
965
- logger.log_empty_block(block);
107
+ if status == napi::Status::Ok {
108
+ Ok(())
966
109
  } else {
967
- logger.log_block_number(block);
968
-
969
- logger.indented(|logger| {
970
- logger.log_block_hash(block);
971
-
972
- logger.indented(|logger| {
973
- logger.log_base_fee(block.header().base_fee_per_gas.as_ref());
974
-
975
- for (idx, transaction, result, trace) in izip!(
976
- 0..num_transactions,
977
- transactions,
978
- transaction_results,
979
- transaction_traces
980
- ) {
981
- logger.log_block_transaction(
982
- spec_id,
983
- transaction,
984
- result,
985
- trace,
986
- console_log_inputs,
987
- false,
988
- );
989
-
990
- logger.log_empty_line_between_transactions(idx, num_transactions);
991
- }
992
- });
993
- });
110
+ Err(LoggerError::PrintLine)
994
111
  }
995
112
  });
996
- }
997
-
998
- /// Logs a warning about multiple blocks being mined.
999
- fn log_multiple_blocks_warning(&mut self) {
1000
- self.indented(|logger| {
1001
- logger
1002
- .log("There were other pending transactions. More than one block had to be mined:");
1003
- });
1004
- self.log_empty_line();
1005
- }
1006
-
1007
- /// Logs a warning about multiple transactions being mined.
1008
- fn log_multiple_transactions_warning(&mut self) {
1009
- self.indented(|logger| {
1010
- logger.log("There were other pending transactions mined in the same block:");
1011
- });
1012
- self.log_empty_line();
1013
- }
1014
-
1015
- fn log_with_title(&mut self, title: impl Into<String>, message: impl Display) {
1016
- // repeat whitespace self.indentation times and concatenate with title
1017
- let title = format!("{:indent$}{}", "", title.into(), indent = self.indentation);
1018
- if title.len() > self.title_length {
1019
- self.title_length = title.len();
1020
- }
1021
-
1022
- let message = format!("{message}");
1023
- self.logs.push(LogLine::WithTitle(title, message));
1024
- }
1025
-
1026
- fn log_currently_sent_transaction(
1027
- &mut self,
1028
- spec_id: edr_eth::SpecId,
1029
- block_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1030
- transaction: &transaction::Signed,
1031
- transaction_result: &edr_evm::ExecutionResult,
1032
- trace: &edr_evm::trace::Trace,
1033
- ) {
1034
- self.indented(|logger| {
1035
- logger.log("Currently sent transaction:");
1036
- logger.log("");
1037
- });
1038
-
1039
- self.log_transaction(
1040
- spec_id,
1041
- block_result,
1042
- transaction,
1043
- transaction_result,
1044
- trace,
1045
- );
1046
- }
1047
-
1048
- fn log_single_transaction_mining_result(
1049
- &mut self,
1050
- spec_id: edr_eth::SpecId,
1051
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1052
- transaction: &transaction::Signed,
1053
- ) {
1054
- let trace = result
1055
- .transaction_traces
1056
- .first()
1057
- .expect("A transaction exists, so the trace must exist as well.");
1058
-
1059
- let transaction_result = result
1060
- .transaction_results
1061
- .first()
1062
- .expect("A transaction exists, so the result must exist as well.");
1063
-
1064
- self.log_transaction(spec_id, result, transaction, transaction_result, trace);
1065
- }
1066
-
1067
- fn log_transaction(
1068
- &mut self,
1069
- spec_id: edr_eth::SpecId,
1070
- block_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1071
- transaction: &transaction::Signed,
1072
- transaction_result: &edr_evm::ExecutionResult,
1073
- trace: &edr_evm::trace::Trace,
1074
- ) {
1075
- self.indented(|logger| {
1076
- logger.log_contract_and_function_name::<false>(spec_id, trace);
1077
113
 
1078
- let transaction_hash = transaction.transaction_hash();
1079
- logger.log_with_title("Transaction", transaction_hash);
1080
-
1081
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
1082
- if let Some(to) = transaction.kind().to() {
1083
- logger.log_with_title("To", format!("0x{to:x}"));
1084
- }
1085
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
1086
- logger.log_with_title(
1087
- "Gas used",
1088
- format!(
1089
- "{gas_used} of {gas_limit}",
1090
- gas_used = transaction_result.gas_used(),
1091
- gas_limit = transaction.gas_limit()
1092
- ),
1093
- );
1094
-
1095
- let block_number = block_result.block.header().number;
1096
- logger.log_with_title(format!("Block #{block_number}"), block_result.block.hash());
1097
-
1098
- logger.log_console_log_messages(&block_result.console_log_inputs);
1099
-
1100
- let transaction_failure = edr_provider::TransactionFailure::from_execution_result(
1101
- transaction_result,
1102
- Some(transaction_hash),
1103
- trace,
1104
- );
1105
-
1106
- if let Some(transaction_failure) = transaction_failure {
1107
- logger.log_transaction_failure(&transaction_failure);
1108
- }
1109
- });
1110
- }
1111
-
1112
- fn print<const REPLACE: bool>(&mut self, message: impl ToString) -> Result<(), LoggerError> {
1113
- if !self.is_enabled {
1114
- return Ok(());
1115
- }
1116
-
1117
- let formatted = self.format(message);
1118
-
1119
- let status = self
1120
- .print_line_fn
1121
- .call((formatted, REPLACE), ThreadsafeFunctionCallMode::Blocking);
1122
-
1123
- if status == napi::Status::Ok {
1124
- Ok(())
1125
- } else {
1126
- Err(LoggerError::PrintLine)
1127
- }
1128
- }
1129
-
1130
- fn print_empty_line(&mut self) -> Result<(), LoggerError> {
1131
- self.print::<false>("")
1132
- }
1133
-
1134
- fn print_logs(&mut self) -> Result<bool, LoggerError> {
1135
- let logs = std::mem::take(&mut self.logs);
1136
- if logs.is_empty() {
1137
- return Ok(false);
1138
- }
1139
-
1140
- for log in logs {
1141
- let line = match log {
1142
- LogLine::Single(message) => message,
1143
- LogLine::WithTitle(title, message) => {
1144
- let title = format!("{title}:");
1145
- format!("{title:indent$} {message}", indent = self.title_length + 1)
1146
- }
1147
- };
1148
-
1149
- self.print::<false>(line)?;
1150
- }
1151
-
1152
- Ok(true)
1153
- }
1154
-
1155
- fn print_method(&mut self, method: &str) -> Result<(), LoggerError> {
1156
- if let Some(collapsed_method) = self.collapsed_method(method) {
1157
- collapsed_method.count += 1;
1158
-
1159
- let line = format!("{method} ({count})", count = collapsed_method.count);
1160
- self.print::<true>(Color::Green.paint(line))
1161
- } else {
1162
- self.state = LoggingState::CollapsingMethod(CollapsedMethod {
1163
- count: 1,
1164
- method: method.to_string(),
1165
- });
1166
-
1167
- self.print::<false>(Color::Green.paint(method))
1168
- }
1169
- }
1170
-
1171
- /// Retrieves the collapsed method with the provided name, if it exists.
1172
- fn collapsed_method(&mut self, method: &str) -> Option<&mut CollapsedMethod> {
1173
- if let LoggingState::CollapsingMethod(collapsed_method) = &mut self.state {
1174
- if collapsed_method.method == method {
1175
- return Some(collapsed_method);
1176
- }
1177
- }
1178
-
1179
- None
1180
- }
1181
-
1182
- fn replace_last_log_line(&mut self, message: impl ToString) {
1183
- let formatted = self.format(message);
1184
-
1185
- *self.logs.last_mut().expect("There must be a log line") = LogLine::Single(formatted);
1186
- }
1187
- }
1188
-
1189
- fn wei_to_human_readable(wei: U256) -> String {
1190
- if wei == U256::ZERO {
1191
- "0 ETH".to_string()
1192
- } else if wei < U256::from(100_000u64) {
1193
- format!("{wei} wei")
1194
- } else if wei < U256::from(100_000_000_000_000u64) {
1195
- let mut decimal = to_decimal_string(wei, 9);
1196
- decimal.push_str(" gwei");
1197
- decimal
1198
- } else {
1199
- let mut decimal = to_decimal_string(wei, 18);
1200
- decimal.push_str(" ETH");
1201
- decimal
114
+ Ok(edr_napi_core::logger::Config {
115
+ enable: self.enable,
116
+ decode_console_log_inputs_fn,
117
+ print_line_fn,
118
+ })
1202
119
  }
1203
120
  }
1204
-
1205
- /// Converts the provided `value` to a decimal string after dividing it by
1206
- /// `10^exponent`. The returned string will have at most `MAX_DECIMALS`
1207
- /// decimals.
1208
- fn to_decimal_string(value: U256, exponent: u8) -> String {
1209
- const MAX_DECIMALS: u8 = 4;
1210
-
1211
- let (integer, remainder) = value.div_rem(U256::from(10).pow(U256::from(exponent)));
1212
- let decimal = remainder / U256::from(10).pow(U256::from(exponent - MAX_DECIMALS));
1213
-
1214
- // Remove trailing zeros
1215
- let decimal = decimal.to_string().trim_end_matches('0').to_string();
1216
-
1217
- format!("{integer}.{decimal}")
1218
- }