@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/context.rs
CHANGED
|
@@ -1,39 +1,335 @@
|
|
|
1
|
-
use std::
|
|
1
|
+
use std::sync::Arc;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
use
|
|
3
|
+
use edr_eth::HashMap;
|
|
4
|
+
use edr_napi_core::{
|
|
5
|
+
provider::{self, SyncProviderFactory},
|
|
6
|
+
solidity,
|
|
7
|
+
};
|
|
8
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
9
|
+
use edr_solidity_tests::{
|
|
10
|
+
decode::RevertDecoder,
|
|
11
|
+
multi_runner::{SuiteResultAndArtifactId, TestContract, TestContracts},
|
|
12
|
+
TestFilterConfig,
|
|
13
|
+
};
|
|
14
|
+
use napi::{
|
|
15
|
+
threadsafe_function::{
|
|
16
|
+
ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
17
|
+
},
|
|
18
|
+
tokio::{runtime, sync::Mutex as AsyncMutex},
|
|
19
|
+
Env, JsFunction, JsObject,
|
|
20
|
+
};
|
|
5
21
|
use napi_derive::napi;
|
|
6
22
|
use tracing_subscriber::{prelude::*, EnvFilter, Registry};
|
|
7
23
|
|
|
24
|
+
use crate::{
|
|
25
|
+
config::{ProviderConfig, TracingConfigWithBuffers},
|
|
26
|
+
logger::LoggerConfig,
|
|
27
|
+
provider::{Provider, ProviderFactory},
|
|
28
|
+
solidity_tests::{
|
|
29
|
+
artifact::{Artifact, ArtifactId},
|
|
30
|
+
config::SolidityTestRunnerConfigArgs,
|
|
31
|
+
factory::SolidityTestRunnerFactory,
|
|
32
|
+
test_results::SuiteResult,
|
|
33
|
+
LinkingOutput,
|
|
34
|
+
},
|
|
35
|
+
subscription::SubscriptionConfig,
|
|
36
|
+
};
|
|
37
|
+
|
|
8
38
|
#[napi]
|
|
9
|
-
#[derive(Debug)]
|
|
10
39
|
pub struct EdrContext {
|
|
11
|
-
inner: Arc<Context
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
impl Deref for EdrContext {
|
|
15
|
-
type Target = Arc<Context>;
|
|
16
|
-
|
|
17
|
-
fn deref(&self) -> &Self::Target {
|
|
18
|
-
&self.inner
|
|
19
|
-
}
|
|
40
|
+
inner: Arc<AsyncMutex<Context>>,
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
#[napi]
|
|
23
44
|
impl EdrContext {
|
|
24
45
|
#[doc = "Creates a new [`EdrContext`] instance. Should only be called once!"]
|
|
25
|
-
#[napi(constructor)]
|
|
46
|
+
#[napi(catch_unwind, constructor)]
|
|
26
47
|
pub fn new() -> napi::Result<Self> {
|
|
27
48
|
let context = Context::new()?;
|
|
28
49
|
|
|
29
50
|
Ok(Self {
|
|
30
|
-
inner: Arc::new(context),
|
|
51
|
+
inner: Arc::new(AsyncMutex::new(context)),
|
|
31
52
|
})
|
|
32
53
|
}
|
|
54
|
+
|
|
55
|
+
#[doc = "Constructs a new provider with the provided configuration."]
|
|
56
|
+
#[napi(catch_unwind, ts_return_type = "Promise<Provider>")]
|
|
57
|
+
pub fn create_provider(
|
|
58
|
+
&self,
|
|
59
|
+
env: Env,
|
|
60
|
+
chain_type: String,
|
|
61
|
+
provider_config: ProviderConfig,
|
|
62
|
+
logger_config: LoggerConfig,
|
|
63
|
+
subscription_config: SubscriptionConfig,
|
|
64
|
+
tracing_config: TracingConfigWithBuffers,
|
|
65
|
+
) -> napi::Result<JsObject> {
|
|
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));
|
|
85
|
+
|
|
86
|
+
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
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
|
+
);
|
|
94
|
+
|
|
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
|
+
));
|
|
100
|
+
|
|
101
|
+
#[cfg(feature = "scenarios")]
|
|
102
|
+
let scenario_file =
|
|
103
|
+
try_or_reject_promise!(runtime.clone().block_on(crate::scenarios::scenario_file(
|
|
104
|
+
chain_type.clone(),
|
|
105
|
+
provider_config.clone(),
|
|
106
|
+
logger_config.enable,
|
|
107
|
+
)));
|
|
108
|
+
|
|
109
|
+
let builder = {
|
|
110
|
+
// TODO: https://github.com/NomicFoundation/edr/issues/760
|
|
111
|
+
// TODO: Don't block the JS event loop
|
|
112
|
+
let context = runtime.block_on(async { self.inner.lock().await });
|
|
113
|
+
|
|
114
|
+
try_or_reject_promise!(context.create_provider_builder(
|
|
115
|
+
&env,
|
|
116
|
+
&chain_type,
|
|
117
|
+
provider_config,
|
|
118
|
+
logger_config,
|
|
119
|
+
subscription_config.into(),
|
|
120
|
+
&contract_decoder,
|
|
121
|
+
))
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
runtime.clone().spawn_blocking(move || {
|
|
125
|
+
let result = builder.build(runtime.clone()).map(|provider| {
|
|
126
|
+
Provider::new(
|
|
127
|
+
provider,
|
|
128
|
+
runtime,
|
|
129
|
+
contract_decoder,
|
|
130
|
+
#[cfg(feature = "scenarios")]
|
|
131
|
+
scenario_file,
|
|
132
|
+
)
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
deferred.resolve(|_env| result);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
Ok(promise)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#[doc = "Registers a new provider factory for the provided chain type."]
|
|
142
|
+
#[napi(catch_unwind)]
|
|
143
|
+
pub async fn register_provider_factory(
|
|
144
|
+
&self,
|
|
145
|
+
chain_type: String,
|
|
146
|
+
factory: &ProviderFactory,
|
|
147
|
+
) -> napi::Result<()> {
|
|
148
|
+
let mut context = self.inner.lock().await;
|
|
149
|
+
context.register_provider_factory(chain_type, factory.as_inner().clone());
|
|
150
|
+
Ok(())
|
|
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
|
+
}
|
|
33
328
|
}
|
|
34
329
|
|
|
35
|
-
#[derive(Debug)]
|
|
36
330
|
pub struct Context {
|
|
331
|
+
provider_factories: HashMap<String, Arc<dyn SyncProviderFactory>>,
|
|
332
|
+
solidity_test_runner_factories: HashMap<String, Arc<dyn solidity::SyncTestRunnerFactory>>,
|
|
37
333
|
#[cfg(feature = "tracing")]
|
|
38
334
|
_tracing_write_guard: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
|
|
39
335
|
}
|
|
@@ -56,7 +352,7 @@ impl Context {
|
|
|
56
352
|
let (flame_layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
|
|
57
353
|
.map_err(|err| {
|
|
58
354
|
napi::Error::new(
|
|
59
|
-
Status::GenericFailure,
|
|
355
|
+
napi::Status::GenericFailure,
|
|
60
356
|
format!("Failed to create tracing.folded file with error: {err:?}"),
|
|
61
357
|
)
|
|
62
358
|
})?;
|
|
@@ -76,8 +372,69 @@ impl Context {
|
|
|
76
372
|
}
|
|
77
373
|
|
|
78
374
|
Ok(Self {
|
|
375
|
+
provider_factories: HashMap::new(),
|
|
376
|
+
solidity_test_runner_factories: HashMap::new(),
|
|
79
377
|
#[cfg(feature = "tracing")]
|
|
80
378
|
_tracing_write_guard: guard,
|
|
81
379
|
})
|
|
82
380
|
}
|
|
381
|
+
|
|
382
|
+
/// Registers a new provider factory for the provided chain type.
|
|
383
|
+
pub fn register_provider_factory(
|
|
384
|
+
&mut self,
|
|
385
|
+
chain_type: String,
|
|
386
|
+
factory: Arc<dyn SyncProviderFactory>,
|
|
387
|
+
) {
|
|
388
|
+
self.provider_factories.insert(chain_type, factory);
|
|
389
|
+
}
|
|
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
|
+
|
|
400
|
+
/// Tries to create a new provider for the provided chain type and
|
|
401
|
+
/// configuration.
|
|
402
|
+
pub fn create_provider_builder(
|
|
403
|
+
&self,
|
|
404
|
+
env: &napi::Env,
|
|
405
|
+
chain_type: &str,
|
|
406
|
+
provider_config: edr_napi_core::provider::Config,
|
|
407
|
+
logger_config: edr_napi_core::logger::Config,
|
|
408
|
+
subscription_config: edr_napi_core::subscription::Config,
|
|
409
|
+
contract_decoder: &Arc<ContractDecoder>,
|
|
410
|
+
) -> napi::Result<Box<dyn provider::Builder>> {
|
|
411
|
+
if let Some(factory) = self.provider_factories.get(chain_type) {
|
|
412
|
+
factory.create_provider_builder(
|
|
413
|
+
env,
|
|
414
|
+
provider_config,
|
|
415
|
+
logger_config,
|
|
416
|
+
subscription_config,
|
|
417
|
+
contract_decoder.clone(),
|
|
418
|
+
)
|
|
419
|
+
} else {
|
|
420
|
+
Err(napi::Error::new(
|
|
421
|
+
napi::Status::GenericFailure,
|
|
422
|
+
"Provider for provided chain type does not exist",
|
|
423
|
+
))
|
|
424
|
+
}
|
|
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
|
+
}
|
|
83
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
|
@@ -1,23 +1,47 @@
|
|
|
1
|
-
#![warn(missing_docs)]
|
|
1
|
+
// #![warn(missing_docs)]
|
|
2
2
|
|
|
3
|
-
//! NAPI bindings for
|
|
3
|
+
//! NAPI bindings for EDR's core types.
|
|
4
4
|
|
|
5
5
|
#[global_allocator]
|
|
6
6
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|
7
7
|
|
|
8
8
|
mod account;
|
|
9
9
|
mod block;
|
|
10
|
-
|
|
11
|
-
mod
|
|
12
|
-
|
|
13
|
-
mod
|
|
10
|
+
/// Types for overriding a call.
|
|
11
|
+
pub mod call_override;
|
|
12
|
+
/// Types for casting N-API types to Rust types.
|
|
13
|
+
pub mod cast;
|
|
14
|
+
/// Supported chain types.
|
|
15
|
+
pub mod chains;
|
|
16
|
+
/// Types for configuration.
|
|
17
|
+
pub mod config;
|
|
18
|
+
/// Types related to an EDR N-API context.
|
|
19
|
+
pub mod context;
|
|
14
20
|
mod debug_trace;
|
|
15
|
-
|
|
16
|
-
mod
|
|
17
|
-
|
|
18
|
-
mod
|
|
21
|
+
/// Types and functions related to code coverage instrumentation.
|
|
22
|
+
pub mod instrument;
|
|
23
|
+
/// Types for EVM execution logs.
|
|
24
|
+
pub mod log;
|
|
25
|
+
/// Types for an RPC request logger.
|
|
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;
|
|
32
|
+
/// Types for Ethereum RPC providers.
|
|
33
|
+
pub mod provider;
|
|
34
|
+
/// Types for EVM execution results.
|
|
35
|
+
pub mod result;
|
|
36
|
+
/// Types relating to benchmark scenarios.
|
|
19
37
|
#[cfg(feature = "scenarios")]
|
|
20
|
-
mod scenarios;
|
|
21
|
-
mod
|
|
22
|
-
|
|
23
|
-
mod
|
|
38
|
+
pub mod scenarios;
|
|
39
|
+
mod serde;
|
|
40
|
+
/// Solidity test runner.
|
|
41
|
+
pub mod solidity_tests;
|
|
42
|
+
/// Types for subscribing to events.
|
|
43
|
+
pub mod subscription;
|
|
44
|
+
/// Types for EVM traces.
|
|
45
|
+
pub mod trace;
|
|
46
|
+
/// Types related to Ethereum withdrawals.
|
|
47
|
+
pub mod withdrawal;
|
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
|
}
|