@nomicfoundation/edr 0.11.3 → 0.12.0-next.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 +61 -27
- package/LICENSE +5 -1
- package/index.d.ts +875 -137
- package/index.js +61 -3
- package/package.json +20 -16
- package/src/account.rs +109 -32
- package/src/block.rs +2 -103
- package/src/call_override.rs +7 -7
- package/src/cast.rs +47 -17
- package/src/chains/generic.rs +51 -0
- package/src/chains/l1.rs +262 -0
- package/src/chains/op.rs +425 -0
- package/src/chains.rs +7 -0
- package/src/config.rs +537 -67
- package/src/context.rs +374 -17
- package/src/debug_trace.rs +2 -2
- package/src/instrument.rs +109 -0
- package/src/lib.rs +38 -14
- package/src/log.rs +12 -14
- package/src/logger.rs +77 -1177
- package/src/mock.rs +68 -0
- package/src/precompile.rs +50 -0
- package/src/provider/factory.rs +22 -0
- package/src/provider/response.rs +73 -0
- package/src/provider.rs +64 -325
- package/src/result.rs +60 -69
- package/src/scenarios.rs +11 -17
- package/src/serde.rs +57 -0
- package/src/solidity_tests/artifact.rs +184 -0
- package/src/solidity_tests/config.rs +725 -0
- package/src/solidity_tests/factory.rs +22 -0
- package/src/solidity_tests/l1.rs +68 -0
- package/src/solidity_tests/op.rs +69 -0
- package/src/solidity_tests/runner.rs +51 -0
- package/src/solidity_tests/test_results.rs +668 -0
- package/src/solidity_tests.rs +56 -0
- package/src/subscription.rs +32 -0
- package/src/trace/debug.rs +1 -1
- package/src/trace/exit.rs +12 -13
- package/src/trace/library_utils.rs +1 -1
- package/src/trace/return_data.rs +11 -11
- package/src/trace/solidity_stack_trace.rs +11 -8
- package/src/trace.rs +37 -44
- package/src/withdrawal.rs +4 -4
- package/src/provider/config.rs +0 -291
- package/src/subscribe.rs +0 -63
package/src/logger.rs
CHANGED
|
@@ -1,300 +1,76 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
fmt::Display,
|
|
3
|
-
sync::{mpsc::channel, Arc},
|
|
4
|
-
};
|
|
1
|
+
use std::sync::{mpsc::channel, Arc};
|
|
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::{
|
|
23
6
|
threadsafe_function::{
|
|
24
7
|
ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
25
8
|
},
|
|
26
|
-
|
|
9
|
+
JsFunction, Status,
|
|
27
10
|
};
|
|
28
11
|
use napi_derive::napi;
|
|
29
12
|
|
|
30
|
-
use crate::cast::TryCast;
|
|
31
|
-
|
|
32
|
-
#[napi(object)]
|
|
33
|
-
pub struct ContractAndFunctionName {
|
|
34
|
-
/// The contract name.
|
|
35
|
-
pub contract_name: String,
|
|
36
|
-
/// The function name. Only present for calls.
|
|
37
|
-
pub function_name: Option<String>,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
|
|
41
|
-
type Error = napi::Error;
|
|
42
|
-
|
|
43
|
-
fn try_cast(self) -> std::result::Result<(String, Option<String>), Self::Error> {
|
|
44
|
-
Ok((self.contract_name, self.function_name))
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
13
|
#[napi(object)]
|
|
49
14
|
pub struct LoggerConfig {
|
|
50
15
|
/// Whether to enable the logger.
|
|
51
16
|
pub enable: bool,
|
|
52
|
-
#[napi(ts_type = "(inputs:
|
|
17
|
+
#[napi(ts_type = "(inputs: ArrayBuffer[]) => string[]")]
|
|
53
18
|
pub decode_console_log_inputs_callback: JsFunction,
|
|
54
19
|
#[napi(ts_type = "(message: string, replace: boolean) => void")]
|
|
55
20
|
pub print_line_callback: JsFunction,
|
|
56
21
|
}
|
|
57
22
|
|
|
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| {
|
|
23
|
+
impl LoggerConfig {
|
|
24
|
+
/// Resolves the logger config, converting it to a
|
|
25
|
+
/// `edr_napi_core::logger::Config`.
|
|
26
|
+
pub fn resolve(self, env: &napi::Env) -> napi::Result<edr_napi_core::logger::Config> {
|
|
27
|
+
let mut decode_console_log_inputs_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> =
|
|
28
|
+
self.decode_console_log_inputs_callback
|
|
29
|
+
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
|
|
30
|
+
let inputs = ctx.env.create_array_with_length(ctx.value.len()).and_then(
|
|
31
|
+
|mut inputs| {
|
|
279
32
|
for (idx, input) in ctx.value.into_iter().enumerate() {
|
|
280
|
-
ctx.env
|
|
281
|
-
|
|
282
|
-
|
|
33
|
+
ctx.env
|
|
34
|
+
.create_arraybuffer_with_data(input.to_vec())
|
|
35
|
+
.and_then(|input| {
|
|
36
|
+
inputs.set_element(idx as u32, input.into_raw())
|
|
37
|
+
})?;
|
|
283
38
|
}
|
|
284
39
|
|
|
285
40
|
Ok(inputs)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
41
|
+
},
|
|
42
|
+
)?;
|
|
43
|
+
|
|
44
|
+
Ok(vec![inputs])
|
|
45
|
+
})?;
|
|
46
|
+
|
|
47
|
+
// Maintain a weak reference to the function to avoid blocking the event loop
|
|
48
|
+
// from exiting.
|
|
49
|
+
decode_console_log_inputs_callback.unref(env)?;
|
|
50
|
+
|
|
51
|
+
let decode_console_log_inputs_fn = Arc::new(move |console_log_inputs| {
|
|
52
|
+
let (sender, receiver) = channel();
|
|
53
|
+
|
|
54
|
+
let status = decode_console_log_inputs_callback.call_with_return_value(
|
|
55
|
+
console_log_inputs,
|
|
56
|
+
ThreadsafeFunctionCallMode::Blocking,
|
|
57
|
+
move |decoded_inputs: Vec<String>| {
|
|
58
|
+
sender.send(decoded_inputs).map_err(|_error| {
|
|
59
|
+
napi::Error::new(
|
|
60
|
+
Status::GenericFailure,
|
|
61
|
+
"Failed to send result from decode_console_log_inputs",
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
assert_eq!(status, Status::Ok);
|
|
290
67
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
decode_console_log_inputs_fn.unref(env)?;
|
|
68
|
+
receiver.recv().unwrap()
|
|
69
|
+
});
|
|
294
70
|
|
|
295
|
-
let mut
|
|
296
|
-
|
|
297
|
-
|ctx: ThreadSafeCallContext<(String, bool)>| {
|
|
71
|
+
let mut print_line_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> = self
|
|
72
|
+
.print_line_callback
|
|
73
|
+
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<(String, bool)>| {
|
|
298
74
|
// String
|
|
299
75
|
let message = ctx.env.create_string_from_std(ctx.value.0)?;
|
|
300
76
|
|
|
@@ -302,917 +78,41 @@ impl LogCollector {
|
|
|
302
78
|
let replace = ctx.env.get_boolean(ctx.value.1)?;
|
|
303
79
|
|
|
304
80
|
Ok(vec![message.into_unknown(), replace.into_unknown()])
|
|
305
|
-
}
|
|
306
|
-
)?;
|
|
307
|
-
|
|
308
|
-
// Maintain a weak reference to the function to avoid the event loop from
|
|
309
|
-
// 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;
|
|
81
|
+
})?;
|
|
488
82
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
83
|
+
// Maintain a weak reference to the function to avoid blocking the event loop
|
|
84
|
+
// from exiting.
|
|
85
|
+
print_line_callback.unref(env)?;
|
|
86
|
+
|
|
87
|
+
let print_line_fn = Arc::new(move |message, replace| {
|
|
88
|
+
let (sender, receiver) = channel();
|
|
89
|
+
|
|
90
|
+
let status = print_line_callback.call_with_return_value(
|
|
91
|
+
(message, replace),
|
|
92
|
+
ThreadsafeFunctionCallMode::Blocking,
|
|
93
|
+
move |()| {
|
|
94
|
+
sender.send(()).map_err(|_error| {
|
|
95
|
+
napi::Error::new(
|
|
96
|
+
Status::GenericFailure,
|
|
97
|
+
"Failed to send result from decode_console_log_inputs",
|
|
98
|
+
)
|
|
499
99
|
})
|
|
500
|
-
|
|
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
|
-
}
|
|
608
|
-
|
|
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;
|
|
621
|
-
|
|
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
|
-
),
|
|
100
|
+
},
|
|
714
101
|
);
|
|
715
102
|
|
|
716
|
-
|
|
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:");
|
|
103
|
+
let () = receiver.recv().unwrap();
|
|
755
104
|
|
|
756
|
-
|
|
757
|
-
|
|
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
|
-
));
|
|
105
|
+
if status == napi::Status::Ok {
|
|
106
|
+
Ok(())
|
|
891
107
|
} else {
|
|
892
|
-
|
|
108
|
+
Err(LoggerError::PrintLine)
|
|
893
109
|
}
|
|
894
110
|
});
|
|
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
111
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
logger.log_empty_block(block);
|
|
966
|
-
} 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
|
-
});
|
|
994
|
-
}
|
|
995
|
-
});
|
|
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
|
-
|
|
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
|
|
112
|
+
Ok(edr_napi_core::logger::Config {
|
|
113
|
+
enable: self.enable,
|
|
114
|
+
decode_console_log_inputs_fn,
|
|
115
|
+
print_line_fn,
|
|
116
|
+
})
|
|
1202
117
|
}
|
|
1203
118
|
}
|
|
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
|
-
}
|