@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/Cargo.toml +26 -23
- package/index.d.ts +145 -76
- package/index.js +31 -3
- package/package.json +16 -14
- package/src/account.rs +41 -11
- package/src/block.rs +1 -1
- package/src/call_override.rs +1 -1
- package/src/cast.rs +54 -2
- package/src/chains/generic.rs +51 -0
- package/src/chains/l1.rs +260 -0
- package/src/chains/op.rs +368 -0
- package/src/chains.rs +7 -0
- package/src/config.rs +393 -67
- package/src/context.rs +135 -17
- package/src/lib.rs +28 -14
- package/src/log.rs +2 -2
- package/src/logger.rs +54 -1152
- package/src/provider/factory.rs +22 -0
- package/src/provider/response.rs +70 -0
- package/src/provider.rs +55 -322
- package/src/result.rs +44 -44
- package/src/scenarios.rs +12 -18
- package/src/subscription.rs +32 -0
- package/src/trace/exit.rs +8 -9
- package/src/trace/return_data.rs +1 -1
- package/src/trace/solidity_stack_trace.rs +5 -4
- package/src/trace.rs +9 -7
- package/src/withdrawal.rs +1 -1
- package/src/provider/config.rs +0 -291
- package/src/subscribe.rs +0 -63
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
|
|
7
|
-
use
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
623
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
}
|