@nomicfoundation/edr 0.12.0-alpha.0 → 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 +47 -16
- package/LICENSE +5 -1
- package/index.d.ts +773 -104
- package/index.js +31 -1
- package/package.json +20 -17
- package/src/account.rs +102 -55
- package/src/block.rs +2 -103
- package/src/call_override.rs +8 -8
- package/src/cast.rs +7 -29
- package/src/chains/generic.rs +1 -1
- package/src/chains/l1.rs +20 -18
- package/src/chains/op.rs +95 -38
- package/src/config.rs +305 -161
- package/src/context.rs +260 -21
- package/src/debug_trace.rs +2 -2
- package/src/instrument.rs +109 -0
- package/src/lib.rs +10 -0
- package/src/log.rs +12 -14
- package/src/logger.rs +28 -30
- package/src/mock.rs +68 -0
- package/src/precompile.rs +50 -0
- package/src/provider/response.rs +8 -5
- package/src/provider.rs +14 -8
- package/src/result.rs +18 -27
- package/src/scenarios.rs +2 -2
- 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 +1 -1
- package/src/trace/debug.rs +1 -1
- package/src/trace/exit.rs +4 -4
- package/src/trace/library_utils.rs +7 -2
- package/src/trace/return_data.rs +10 -10
- package/src/trace/solidity_stack_trace.rs +7 -5
- package/src/trace.rs +29 -38
- package/src/withdrawal.rs +3 -3
package/src/context.rs
CHANGED
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
3
|
use edr_eth::HashMap;
|
|
4
|
-
use edr_napi_core::
|
|
4
|
+
use edr_napi_core::{
|
|
5
|
+
provider::{self, SyncProviderFactory},
|
|
6
|
+
solidity,
|
|
7
|
+
};
|
|
5
8
|
use edr_solidity::contract_decoder::ContractDecoder;
|
|
9
|
+
use edr_solidity_tests::{
|
|
10
|
+
decode::RevertDecoder,
|
|
11
|
+
multi_runner::{SuiteResultAndArtifactId, TestContract, TestContracts},
|
|
12
|
+
TestFilterConfig,
|
|
13
|
+
};
|
|
6
14
|
use napi::{
|
|
7
|
-
|
|
15
|
+
threadsafe_function::{
|
|
16
|
+
ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
17
|
+
},
|
|
8
18
|
tokio::{runtime, sync::Mutex as AsyncMutex},
|
|
19
|
+
Env, JsFunction, JsObject,
|
|
9
20
|
};
|
|
10
21
|
use napi_derive::napi;
|
|
11
|
-
use tracing_subscriber::{EnvFilter, Registry
|
|
22
|
+
use tracing_subscriber::{prelude::*, EnvFilter, Registry};
|
|
12
23
|
|
|
13
24
|
use crate::{
|
|
14
25
|
config::{ProviderConfig, TracingConfigWithBuffers},
|
|
15
26
|
logger::LoggerConfig,
|
|
16
27
|
provider::{Provider, ProviderFactory},
|
|
28
|
+
solidity_tests::{
|
|
29
|
+
artifact::{Artifact, ArtifactId},
|
|
30
|
+
config::SolidityTestRunnerConfigArgs,
|
|
31
|
+
factory::SolidityTestRunnerFactory,
|
|
32
|
+
test_results::SuiteResult,
|
|
33
|
+
LinkingOutput,
|
|
34
|
+
},
|
|
17
35
|
subscription::SubscriptionConfig,
|
|
18
36
|
};
|
|
19
37
|
|
|
@@ -25,7 +43,7 @@ pub struct EdrContext {
|
|
|
25
43
|
#[napi]
|
|
26
44
|
impl EdrContext {
|
|
27
45
|
#[doc = "Creates a new [`EdrContext`] instance. Should only be called once!"]
|
|
28
|
-
#[napi(constructor)]
|
|
46
|
+
#[napi(catch_unwind, constructor)]
|
|
29
47
|
pub fn new() -> napi::Result<Self> {
|
|
30
48
|
let context = Context::new()?;
|
|
31
49
|
|
|
@@ -35,7 +53,7 @@ impl EdrContext {
|
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
#[doc = "Constructs a new provider with the provided configuration."]
|
|
38
|
-
#[napi(ts_return_type = "Promise<Provider>")]
|
|
56
|
+
#[napi(catch_unwind, ts_return_type = "Promise<Provider>")]
|
|
39
57
|
pub fn create_provider(
|
|
40
58
|
&self,
|
|
41
59
|
env: Env,
|
|
@@ -45,44 +63,64 @@ impl EdrContext {
|
|
|
45
63
|
subscription_config: SubscriptionConfig,
|
|
46
64
|
tracing_config: TracingConfigWithBuffers,
|
|
47
65
|
) -> napi::Result<JsObject> {
|
|
48
|
-
let
|
|
49
|
-
|
|
66
|
+
let (deferred, promise) = env.create_deferred()?;
|
|
67
|
+
|
|
68
|
+
macro_rules! try_or_reject_promise {
|
|
69
|
+
($expr:expr) => {
|
|
70
|
+
match $expr {
|
|
71
|
+
Ok(value) => value,
|
|
72
|
+
Err(error) => {
|
|
73
|
+
deferred.reject(error);
|
|
74
|
+
return Ok(promise);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let runtime = runtime::Handle::current();
|
|
81
|
+
let provider_config =
|
|
82
|
+
try_or_reject_promise!(provider_config.resolve(&env, runtime.clone()));
|
|
83
|
+
|
|
84
|
+
let logger_config = try_or_reject_promise!(logger_config.resolve(&env));
|
|
50
85
|
|
|
51
86
|
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
52
|
-
let build_info_config =
|
|
53
|
-
edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers(
|
|
54
|
-
|
|
87
|
+
let build_info_config = try_or_reject_promise!(
|
|
88
|
+
edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers(
|
|
89
|
+
(&edr_napi_core::solidity::config::TracingConfigWithBuffers::from(tracing_config))
|
|
90
|
+
.into(),
|
|
91
|
+
)
|
|
92
|
+
.map_err(|error| napi::Error::from_reason(error.to_string()))
|
|
93
|
+
);
|
|
55
94
|
|
|
56
|
-
let contract_decoder = ContractDecoder::new(&build_info_config)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
95
|
+
let contract_decoder = try_or_reject_promise!(ContractDecoder::new(&build_info_config)
|
|
96
|
+
.map_or_else(
|
|
97
|
+
|error| Err(napi::Error::from_reason(error.to_string())),
|
|
98
|
+
|contract_decoder| Ok(Arc::new(contract_decoder))
|
|
99
|
+
));
|
|
60
100
|
|
|
61
101
|
#[cfg(feature = "scenarios")]
|
|
62
102
|
let scenario_file =
|
|
63
|
-
runtime
|
|
103
|
+
try_or_reject_promise!(runtime.clone().block_on(crate::scenarios::scenario_file(
|
|
64
104
|
chain_type.clone(),
|
|
65
105
|
provider_config.clone(),
|
|
66
106
|
logger_config.enable,
|
|
67
|
-
))
|
|
107
|
+
)));
|
|
68
108
|
|
|
69
|
-
let runtime = runtime::Handle::current();
|
|
70
109
|
let builder = {
|
|
71
110
|
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
72
111
|
// TODO: Don't block the JS event loop
|
|
73
112
|
let context = runtime.block_on(async { self.inner.lock().await });
|
|
74
113
|
|
|
75
|
-
context.create_provider_builder(
|
|
114
|
+
try_or_reject_promise!(context.create_provider_builder(
|
|
76
115
|
&env,
|
|
77
116
|
&chain_type,
|
|
78
117
|
provider_config,
|
|
79
118
|
logger_config,
|
|
80
119
|
subscription_config.into(),
|
|
81
120
|
&contract_decoder,
|
|
82
|
-
)
|
|
121
|
+
))
|
|
83
122
|
};
|
|
84
123
|
|
|
85
|
-
let (deferred, promise) = env.create_deferred()?;
|
|
86
124
|
runtime.clone().spawn_blocking(move || {
|
|
87
125
|
let result = builder.build(runtime.clone()).map(|provider| {
|
|
88
126
|
Provider::new(
|
|
@@ -101,7 +139,7 @@ impl EdrContext {
|
|
|
101
139
|
}
|
|
102
140
|
|
|
103
141
|
#[doc = "Registers a new provider factory for the provided chain type."]
|
|
104
|
-
#[napi]
|
|
142
|
+
#[napi(catch_unwind)]
|
|
105
143
|
pub async fn register_provider_factory(
|
|
106
144
|
&self,
|
|
107
145
|
chain_type: String,
|
|
@@ -111,10 +149,187 @@ impl EdrContext {
|
|
|
111
149
|
context.register_provider_factory(chain_type, factory.as_inner().clone());
|
|
112
150
|
Ok(())
|
|
113
151
|
}
|
|
152
|
+
|
|
153
|
+
#[napi(catch_unwind)]
|
|
154
|
+
pub async fn register_solidity_test_runner_factory(
|
|
155
|
+
&self,
|
|
156
|
+
chain_type: String,
|
|
157
|
+
factory: &SolidityTestRunnerFactory,
|
|
158
|
+
) -> napi::Result<()> {
|
|
159
|
+
let mut context = self.inner.lock().await;
|
|
160
|
+
context.register_solidity_test_runner(chain_type, factory.as_inner().clone());
|
|
161
|
+
Ok(())
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#[doc = "Executes Solidity tests."]
|
|
165
|
+
#[doc = ""]
|
|
166
|
+
#[doc = "The function will return as soon as test execution is started."]
|
|
167
|
+
#[doc = "The progress callback will be called with the results of each test"]
|
|
168
|
+
#[doc = "suite. It is up to the caller to track how many times the callback"]
|
|
169
|
+
#[doc = "is called to know when all tests are done."]
|
|
170
|
+
#[allow(clippy::too_many_arguments)]
|
|
171
|
+
#[napi(catch_unwind, ts_return_type = "Promise<void>")]
|
|
172
|
+
pub fn run_solidity_tests(
|
|
173
|
+
&self,
|
|
174
|
+
env: Env,
|
|
175
|
+
chain_type: String,
|
|
176
|
+
artifacts: Vec<Artifact>,
|
|
177
|
+
test_suites: Vec<ArtifactId>,
|
|
178
|
+
config_args: SolidityTestRunnerConfigArgs,
|
|
179
|
+
tracing_config: TracingConfigWithBuffers,
|
|
180
|
+
#[napi(ts_arg_type = "(result: SuiteResult) => void")]
|
|
181
|
+
on_test_suite_completed_callback: JsFunction,
|
|
182
|
+
) -> napi::Result<JsObject> {
|
|
183
|
+
let (deferred, promise) = env.create_deferred()?;
|
|
184
|
+
|
|
185
|
+
let on_test_suite_completed_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> =
|
|
186
|
+
match on_test_suite_completed_callback.create_threadsafe_function(
|
|
187
|
+
// Unbounded queue size
|
|
188
|
+
0,
|
|
189
|
+
|ctx: ThreadSafeCallContext<SuiteResult>| Ok(vec![ctx.value]),
|
|
190
|
+
) {
|
|
191
|
+
Ok(value) => value,
|
|
192
|
+
Err(error) => {
|
|
193
|
+
deferred.reject(error);
|
|
194
|
+
return Ok(promise);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let test_filter: Arc<TestFilterConfig> =
|
|
199
|
+
Arc::new(match config_args.try_get_test_filter() {
|
|
200
|
+
Ok(test_filter) => test_filter,
|
|
201
|
+
Err(error) => {
|
|
202
|
+
deferred.reject(error);
|
|
203
|
+
return Ok(promise);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
let runtime = runtime::Handle::current();
|
|
208
|
+
let config = match config_args.resolve(&env, runtime.clone()) {
|
|
209
|
+
Ok(config) => config,
|
|
210
|
+
Err(error) => {
|
|
211
|
+
deferred.reject(error);
|
|
212
|
+
return Ok(promise);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
let context = self.inner.clone();
|
|
217
|
+
runtime.clone().spawn(async move {
|
|
218
|
+
macro_rules! try_or_reject_deferred {
|
|
219
|
+
($expr:expr) => {
|
|
220
|
+
match $expr {
|
|
221
|
+
Ok(value) => value,
|
|
222
|
+
Err(error) => {
|
|
223
|
+
deferred.reject(error);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
let factory = {
|
|
230
|
+
let context = context.lock().await;
|
|
231
|
+
try_or_reject_deferred!(context.solidity_test_runner_factory(&chain_type).await)
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
let linking_output =
|
|
235
|
+
try_or_reject_deferred!(LinkingOutput::link(&config.project_root, artifacts));
|
|
236
|
+
|
|
237
|
+
// Build revert decoder from ABIs of all artifacts.
|
|
238
|
+
let abis = linking_output
|
|
239
|
+
.known_contracts
|
|
240
|
+
.iter()
|
|
241
|
+
.map(|(_, contract)| &contract.abi);
|
|
242
|
+
|
|
243
|
+
let revert_decoder = RevertDecoder::new().with_abis(abis);
|
|
244
|
+
|
|
245
|
+
let test_suites = try_or_reject_deferred!(test_suites
|
|
246
|
+
.into_iter()
|
|
247
|
+
.map(edr_solidity::artifacts::ArtifactId::try_from)
|
|
248
|
+
.collect::<Result<Vec<_>, _>>());
|
|
249
|
+
|
|
250
|
+
let contracts = try_or_reject_deferred!(test_suites
|
|
251
|
+
.iter()
|
|
252
|
+
.map(|artifact_id| {
|
|
253
|
+
let contract_data = linking_output
|
|
254
|
+
.known_contracts
|
|
255
|
+
.get(artifact_id)
|
|
256
|
+
.ok_or_else(|| {
|
|
257
|
+
napi::Error::new(
|
|
258
|
+
napi::Status::GenericFailure,
|
|
259
|
+
format!("Unknown contract: {}", artifact_id.identifier()),
|
|
260
|
+
)
|
|
261
|
+
})?;
|
|
262
|
+
|
|
263
|
+
let bytecode = contract_data.bytecode.clone().ok_or_else(|| {
|
|
264
|
+
napi::Error::new(
|
|
265
|
+
napi::Status::GenericFailure,
|
|
266
|
+
format!(
|
|
267
|
+
"No bytecode for test suite contract: {}",
|
|
268
|
+
artifact_id.identifier()
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
})?;
|
|
272
|
+
|
|
273
|
+
let test_contract = TestContract {
|
|
274
|
+
abi: contract_data.abi.clone(),
|
|
275
|
+
bytecode,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
Ok((artifact_id.clone(), test_contract))
|
|
279
|
+
})
|
|
280
|
+
.collect::<napi::Result<TestContracts>>());
|
|
281
|
+
|
|
282
|
+
let include_traces = config.include_traces.into();
|
|
283
|
+
|
|
284
|
+
let test_runner = try_or_reject_deferred!(runtime
|
|
285
|
+
.clone()
|
|
286
|
+
.spawn_blocking(move || {
|
|
287
|
+
factory.create_test_runner(
|
|
288
|
+
runtime,
|
|
289
|
+
config,
|
|
290
|
+
contracts,
|
|
291
|
+
linking_output.known_contracts,
|
|
292
|
+
linking_output.libs_to_deploy,
|
|
293
|
+
revert_decoder,
|
|
294
|
+
tracing_config.into(),
|
|
295
|
+
)
|
|
296
|
+
})
|
|
297
|
+
.await
|
|
298
|
+
.expect("Failed to join test runner factory thread"));
|
|
299
|
+
|
|
300
|
+
let () = try_or_reject_deferred!(test_runner.run_tests(
|
|
301
|
+
test_filter,
|
|
302
|
+
Arc::new(
|
|
303
|
+
move |SuiteResultAndArtifactId {
|
|
304
|
+
artifact_id,
|
|
305
|
+
result,
|
|
306
|
+
}| {
|
|
307
|
+
let suite_result = SuiteResult::new(artifact_id, result, include_traces);
|
|
308
|
+
|
|
309
|
+
let status = on_test_suite_completed_callback
|
|
310
|
+
.call(suite_result, ThreadsafeFunctionCallMode::Blocking);
|
|
311
|
+
|
|
312
|
+
// This should always succeed since we're using an unbounded queue. We add
|
|
313
|
+
// an assertion for completeness.
|
|
314
|
+
assert_eq!(
|
|
315
|
+
status,
|
|
316
|
+
napi::Status::Ok,
|
|
317
|
+
"Failed to call on_test_suite_completed_callback with status: {status}"
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
));
|
|
322
|
+
|
|
323
|
+
deferred.resolve(move |_env| Ok(()));
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
Ok(promise)
|
|
327
|
+
}
|
|
114
328
|
}
|
|
115
329
|
|
|
116
330
|
pub struct Context {
|
|
117
331
|
provider_factories: HashMap<String, Arc<dyn SyncProviderFactory>>,
|
|
332
|
+
solidity_test_runner_factories: HashMap<String, Arc<dyn solidity::SyncTestRunnerFactory>>,
|
|
118
333
|
#[cfg(feature = "tracing")]
|
|
119
334
|
_tracing_write_guard: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
|
|
120
335
|
}
|
|
@@ -158,6 +373,7 @@ impl Context {
|
|
|
158
373
|
|
|
159
374
|
Ok(Self {
|
|
160
375
|
provider_factories: HashMap::new(),
|
|
376
|
+
solidity_test_runner_factories: HashMap::new(),
|
|
161
377
|
#[cfg(feature = "tracing")]
|
|
162
378
|
_tracing_write_guard: guard,
|
|
163
379
|
})
|
|
@@ -172,6 +388,15 @@ impl Context {
|
|
|
172
388
|
self.provider_factories.insert(chain_type, factory);
|
|
173
389
|
}
|
|
174
390
|
|
|
391
|
+
pub fn register_solidity_test_runner(
|
|
392
|
+
&mut self,
|
|
393
|
+
chain_type: String,
|
|
394
|
+
factory: Arc<dyn solidity::SyncTestRunnerFactory>,
|
|
395
|
+
) {
|
|
396
|
+
self.solidity_test_runner_factories
|
|
397
|
+
.insert(chain_type, factory);
|
|
398
|
+
}
|
|
399
|
+
|
|
175
400
|
/// Tries to create a new provider for the provided chain type and
|
|
176
401
|
/// configuration.
|
|
177
402
|
pub fn create_provider_builder(
|
|
@@ -198,4 +423,18 @@ impl Context {
|
|
|
198
423
|
))
|
|
199
424
|
}
|
|
200
425
|
}
|
|
426
|
+
|
|
427
|
+
pub async fn solidity_test_runner_factory(
|
|
428
|
+
&self,
|
|
429
|
+
chain_type: &str,
|
|
430
|
+
) -> napi::Result<Arc<dyn solidity::SyncTestRunnerFactory>> {
|
|
431
|
+
if let Some(factory) = self.solidity_test_runner_factories.get(chain_type) {
|
|
432
|
+
Ok(Arc::clone(factory))
|
|
433
|
+
} else {
|
|
434
|
+
Err(napi::Error::new(
|
|
435
|
+
napi::Status::GenericFailure,
|
|
436
|
+
"Solidity test runner for provided chain type does not exist",
|
|
437
|
+
))
|
|
438
|
+
}
|
|
439
|
+
}
|
|
201
440
|
}
|
package/src/debug_trace.rs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
-
use napi::bindgen_prelude::{BigInt,
|
|
3
|
+
use napi::bindgen_prelude::{BigInt, Uint8Array};
|
|
4
4
|
use napi_derive::napi;
|
|
5
5
|
|
|
6
6
|
#[napi(object)]
|
|
7
7
|
pub struct DebugTraceResult {
|
|
8
8
|
pub pass: bool,
|
|
9
9
|
pub gas_used: BigInt,
|
|
10
|
-
pub output: Option<
|
|
10
|
+
pub output: Option<Uint8Array>,
|
|
11
11
|
pub struct_logs: Vec<DebugTraceLogItem>,
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
use edr_instrument::coverage::{self, Version};
|
|
2
|
+
use napi::bindgen_prelude::Uint8Array;
|
|
3
|
+
use napi_derive::napi;
|
|
4
|
+
|
|
5
|
+
#[napi(object)]
|
|
6
|
+
pub struct InstrumentationResult {
|
|
7
|
+
/// The generated source code with coverage instrumentation.
|
|
8
|
+
#[napi(readonly)]
|
|
9
|
+
pub source: String,
|
|
10
|
+
/// The metadata for each instrumented code segment.
|
|
11
|
+
#[napi(readonly)]
|
|
12
|
+
pub metadata: Vec<InstrumentationMetadata>,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
impl TryFrom<edr_instrument::coverage::InstrumentationResult> for InstrumentationResult {
|
|
16
|
+
type Error = usize;
|
|
17
|
+
|
|
18
|
+
fn try_from(
|
|
19
|
+
value: edr_instrument::coverage::InstrumentationResult,
|
|
20
|
+
) -> Result<Self, Self::Error> {
|
|
21
|
+
let metadata = value
|
|
22
|
+
.metadata
|
|
23
|
+
.into_iter()
|
|
24
|
+
.map(InstrumentationMetadata::try_from)
|
|
25
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
26
|
+
|
|
27
|
+
Ok(InstrumentationResult {
|
|
28
|
+
source: value.source,
|
|
29
|
+
metadata,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[napi(object)]
|
|
35
|
+
pub struct InstrumentationMetadata {
|
|
36
|
+
/// The tag that identifies the instrumented code. Tags are
|
|
37
|
+
/// deterministically generated from the source code, source id, and
|
|
38
|
+
/// Solidity version.
|
|
39
|
+
#[napi(readonly)]
|
|
40
|
+
pub tag: Uint8Array,
|
|
41
|
+
/// The kind of instrumented code. Currently, the only supported kind
|
|
42
|
+
/// is "statement".
|
|
43
|
+
#[napi(readonly)]
|
|
44
|
+
pub kind: String,
|
|
45
|
+
/// The starting position of the instrumented code - including trivia such
|
|
46
|
+
/// as whitespace - in the source code, in UTF-16 code units.
|
|
47
|
+
#[napi(readonly)]
|
|
48
|
+
pub start_utf16: i64,
|
|
49
|
+
/// The ending position of the instrumented code - including trivia such as
|
|
50
|
+
/// whitespace - in the source code, in UTF-16 code units.
|
|
51
|
+
#[napi(readonly)]
|
|
52
|
+
pub end_utf16: i64,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
impl TryFrom<edr_instrument::coverage::InstrumentationMetadata> for InstrumentationMetadata {
|
|
56
|
+
type Error = usize;
|
|
57
|
+
|
|
58
|
+
fn try_from(
|
|
59
|
+
value: edr_instrument::coverage::InstrumentationMetadata,
|
|
60
|
+
) -> Result<Self, Self::Error> {
|
|
61
|
+
let start_utf16 = value
|
|
62
|
+
.start_utf16
|
|
63
|
+
.try_into()
|
|
64
|
+
.map_err(|_error| value.start_utf16)?;
|
|
65
|
+
let end_utf16 = value
|
|
66
|
+
.end_utf16
|
|
67
|
+
.try_into()
|
|
68
|
+
.map_err(|_error| value.end_utf16)?;
|
|
69
|
+
|
|
70
|
+
Ok(InstrumentationMetadata {
|
|
71
|
+
tag: Uint8Array::with_data_copied(value.tag),
|
|
72
|
+
kind: value.kind.to_owned(),
|
|
73
|
+
start_utf16,
|
|
74
|
+
end_utf16,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Adds per-statement coverage instrumentation to the given Solidity source
|
|
80
|
+
/// code.
|
|
81
|
+
#[napi(catch_unwind)]
|
|
82
|
+
pub fn add_statement_coverage_instrumentation(
|
|
83
|
+
source_code: String,
|
|
84
|
+
source_id: String,
|
|
85
|
+
solidity_version: String,
|
|
86
|
+
coverage_library_path: String,
|
|
87
|
+
) -> napi::Result<InstrumentationResult> {
|
|
88
|
+
let solidity_version = Version::parse(&solidity_version).map_err(|error| {
|
|
89
|
+
napi::Error::new(
|
|
90
|
+
napi::Status::InvalidArg,
|
|
91
|
+
format!("Invalid Solidity version: {error}"),
|
|
92
|
+
)
|
|
93
|
+
})?;
|
|
94
|
+
|
|
95
|
+
let instrumented = coverage::instrument_code(
|
|
96
|
+
&source_code,
|
|
97
|
+
&source_id,
|
|
98
|
+
solidity_version,
|
|
99
|
+
&coverage_library_path,
|
|
100
|
+
)
|
|
101
|
+
.map_err(|error| napi::Error::new(napi::Status::GenericFailure, error))?;
|
|
102
|
+
|
|
103
|
+
instrumented.try_into().map_err(|location| {
|
|
104
|
+
napi::Error::new(
|
|
105
|
+
napi::Status::GenericFailure,
|
|
106
|
+
format!("Cannot represent source locations in JavaScript: {location}."),
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
}
|
package/src/lib.rs
CHANGED
|
@@ -18,10 +18,17 @@ pub mod config;
|
|
|
18
18
|
/// Types related to an EDR N-API context.
|
|
19
19
|
pub mod context;
|
|
20
20
|
mod debug_trace;
|
|
21
|
+
/// Types and functions related to code coverage instrumentation.
|
|
22
|
+
pub mod instrument;
|
|
21
23
|
/// Types for EVM execution logs.
|
|
22
24
|
pub mod log;
|
|
23
25
|
/// Types for an RPC request logger.
|
|
24
26
|
pub mod logger;
|
|
27
|
+
/// Types for mocking provider behavior.
|
|
28
|
+
#[cfg(feature = "test-mock")]
|
|
29
|
+
mod mock;
|
|
30
|
+
/// Types for precompiles.
|
|
31
|
+
pub mod precompile;
|
|
25
32
|
/// Types for Ethereum RPC providers.
|
|
26
33
|
pub mod provider;
|
|
27
34
|
/// Types for EVM execution results.
|
|
@@ -29,6 +36,9 @@ pub mod result;
|
|
|
29
36
|
/// Types relating to benchmark scenarios.
|
|
30
37
|
#[cfg(feature = "scenarios")]
|
|
31
38
|
pub mod scenarios;
|
|
39
|
+
mod serde;
|
|
40
|
+
/// Solidity test runner.
|
|
41
|
+
pub mod solidity_tests;
|
|
32
42
|
/// Types for subscribing to events.
|
|
33
43
|
pub mod subscription;
|
|
34
44
|
/// Types for EVM traces.
|
package/src/log.rs
CHANGED
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
use napi::
|
|
1
|
+
use napi::bindgen_prelude::Uint8Array;
|
|
2
2
|
use napi_derive::napi;
|
|
3
3
|
|
|
4
4
|
/// Ethereum execution log.
|
|
5
5
|
#[napi(object)]
|
|
6
6
|
pub struct ExecutionLog {
|
|
7
|
-
pub address:
|
|
8
|
-
pub topics: Vec<
|
|
9
|
-
pub data:
|
|
7
|
+
pub address: Uint8Array,
|
|
8
|
+
pub topics: Vec<Uint8Array>,
|
|
9
|
+
pub data: Uint8Array,
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
impl ExecutionLog {
|
|
13
|
-
|
|
14
|
-
let topics =
|
|
12
|
+
impl From<&edr_eth::log::ExecutionLog> for ExecutionLog {
|
|
13
|
+
fn from(value: &edr_eth::log::ExecutionLog) -> Self {
|
|
14
|
+
let topics = value
|
|
15
15
|
.topics()
|
|
16
16
|
.iter()
|
|
17
|
-
.map(
|
|
17
|
+
.map(Uint8Array::with_data_copied)
|
|
18
18
|
.collect();
|
|
19
19
|
|
|
20
|
-
let data =
|
|
21
|
-
.create_buffer_with_data(log.data.data.to_vec())
|
|
22
|
-
.map(JsBufferValue::into_raw)?;
|
|
20
|
+
let data = Uint8Array::with_data_copied(&value.data.data);
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
address:
|
|
22
|
+
Self {
|
|
23
|
+
address: Uint8Array::with_data_copied(value.address),
|
|
26
24
|
topics,
|
|
27
25
|
data,
|
|
28
|
-
}
|
|
26
|
+
}
|
|
29
27
|
}
|
|
30
28
|
}
|
package/src/logger.rs
CHANGED
|
@@ -1,38 +1,20 @@
|
|
|
1
|
-
use std::sync::{
|
|
1
|
+
use std::sync::{mpsc::channel, Arc};
|
|
2
2
|
|
|
3
3
|
use edr_eth::Bytes;
|
|
4
4
|
use edr_napi_core::logger::LoggerError;
|
|
5
5
|
use napi::{
|
|
6
|
-
JsFunction, Status,
|
|
7
6
|
threadsafe_function::{
|
|
8
7
|
ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
9
8
|
},
|
|
9
|
+
JsFunction, Status,
|
|
10
10
|
};
|
|
11
11
|
use napi_derive::napi;
|
|
12
12
|
|
|
13
|
-
use crate::cast::TryCast;
|
|
14
|
-
|
|
15
|
-
#[napi(object)]
|
|
16
|
-
pub struct ContractAndFunctionName {
|
|
17
|
-
/// The contract name.
|
|
18
|
-
pub contract_name: String,
|
|
19
|
-
/// The function name. Only present for calls.
|
|
20
|
-
pub function_name: Option<String>,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
|
|
24
|
-
type Error = napi::Error;
|
|
25
|
-
|
|
26
|
-
fn try_cast(self) -> std::result::Result<(String, Option<String>), Self::Error> {
|
|
27
|
-
Ok((self.contract_name, self.function_name))
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
13
|
#[napi(object)]
|
|
32
14
|
pub struct LoggerConfig {
|
|
33
15
|
/// Whether to enable the logger.
|
|
34
16
|
pub enable: bool,
|
|
35
|
-
#[napi(ts_type = "(inputs:
|
|
17
|
+
#[napi(ts_type = "(inputs: ArrayBuffer[]) => string[]")]
|
|
36
18
|
pub decode_console_log_inputs_callback: JsFunction,
|
|
37
19
|
#[napi(ts_type = "(message: string, replace: boolean) => void")]
|
|
38
20
|
pub print_line_callback: JsFunction,
|
|
@@ -48,9 +30,11 @@ impl LoggerConfig {
|
|
|
48
30
|
let inputs = ctx.env.create_array_with_length(ctx.value.len()).and_then(
|
|
49
31
|
|mut inputs| {
|
|
50
32
|
for (idx, input) in ctx.value.into_iter().enumerate() {
|
|
51
|
-
ctx.env
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
})?;
|
|
54
38
|
}
|
|
55
39
|
|
|
56
40
|
Ok(inputs)
|
|
@@ -60,8 +44,8 @@ impl LoggerConfig {
|
|
|
60
44
|
Ok(vec![inputs])
|
|
61
45
|
})?;
|
|
62
46
|
|
|
63
|
-
// Maintain a weak reference to the function to avoid the event loop
|
|
64
|
-
// exiting.
|
|
47
|
+
// Maintain a weak reference to the function to avoid blocking the event loop
|
|
48
|
+
// from exiting.
|
|
65
49
|
decode_console_log_inputs_callback.unref(env)?;
|
|
66
50
|
|
|
67
51
|
let decode_console_log_inputs_fn = Arc::new(move |console_log_inputs| {
|
|
@@ -96,13 +80,27 @@ impl LoggerConfig {
|
|
|
96
80
|
Ok(vec![message.into_unknown(), replace.into_unknown()])
|
|
97
81
|
})?;
|
|
98
82
|
|
|
99
|
-
// Maintain a weak reference to the function to avoid the event loop
|
|
100
|
-
// exiting.
|
|
83
|
+
// Maintain a weak reference to the function to avoid blocking the event loop
|
|
84
|
+
// from exiting.
|
|
101
85
|
print_line_callback.unref(env)?;
|
|
102
86
|
|
|
103
87
|
let print_line_fn = Arc::new(move |message, replace| {
|
|
104
|
-
let
|
|
105
|
-
|
|
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
|
+
)
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
let () = receiver.recv().unwrap();
|
|
106
104
|
|
|
107
105
|
if status == napi::Status::Ok {
|
|
108
106
|
Ok(())
|