-
Notifications
You must be signed in to change notification settings - Fork 137
/
default.rs
570 lines (494 loc) · 19.4 KB
/
default.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
use anyhow::{anyhow, Context};
use derive_more::{Deref, DerefMut};
use fvm_ipld_encoding::{to_vec, RawBytes, DAG_CBOR};
use fvm_shared::address::{Address, Payload};
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::{ErrorNumber, ExitCode};
use fvm_shared::sys::BlockId;
use fvm_shared::{ActorID, MethodNum, METHOD_SEND};
use num_traits::Zero;
use super::{Backtrace, CallManager, InvocationResult, NO_DATA_BLOCK_ID};
use crate::call_manager::backtrace::Frame;
use crate::call_manager::FinishRet;
use crate::gas::{Gas, GasTracker};
use crate::kernel::{Block, BlockRegistry, ExecutionError, Kernel, Result, SyscallError};
use crate::machine::limiter::ExecMemory;
use crate::machine::{Engine, Machine};
use crate::state_tree::ActorState;
use crate::syscalls::error::Abort;
use crate::syscalls::{charge_for_exec, update_gas_available};
use crate::trace::{ExecutionEvent, ExecutionTrace};
use crate::{account_actor, syscall_error};
/// The default [`CallManager`] implementation.
#[repr(transparent)]
pub struct DefaultCallManager<M: Machine>(Option<Box<InnerDefaultCallManager<M>>>);
#[doc(hidden)]
#[derive(Deref, DerefMut)]
pub struct InnerDefaultCallManager<M: Machine> {
/// The machine this kernel is attached to.
#[deref]
#[deref_mut]
machine: M,
/// The gas tracker.
gas_tracker: GasTracker,
/// The ActorID and the address of the original sender of the chain message that initiated
/// this call stack.
origin: ActorID,
/// The nonce of the chain message that initiated this call stack.
nonce: u64,
/// Number of actors created in this call stack.
num_actors_created: u64,
/// Current call-stack depth.
call_stack_depth: u32,
/// The current chain of errors, if any.
backtrace: Backtrace,
/// The current execution trace.
exec_trace: ExecutionTrace,
/// Number of actors that have been invoked in this message execution.
invocation_count: u64,
/// Limits on memory throughout the execution.
limits: M::Limiter,
}
#[doc(hidden)]
impl<M: Machine> std::ops::Deref for DefaultCallManager<M> {
type Target = InnerDefaultCallManager<M>;
fn deref(&self) -> &Self::Target {
self.0.as_ref().expect("call manager is poisoned")
}
}
#[doc(hidden)]
impl<M: Machine> std::ops::DerefMut for DefaultCallManager<M> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().expect("call manager is poisoned")
}
}
impl<M> CallManager for DefaultCallManager<M>
where
M: Machine,
{
type Machine = M;
fn new(
machine: M,
gas_limit: i64,
origin: ActorID,
nonce: u64,
gas_premium: TokenAmount,
) -> Self {
let limits = machine.new_limiter();
let mut gas_tracker = GasTracker::new(Gas::new(gas_limit), Gas::zero(), gas_premium);
if machine.context().tracing {
gas_tracker.enable_tracing()
}
DefaultCallManager(Some(Box::new(InnerDefaultCallManager {
machine,
gas_tracker,
origin,
nonce,
num_actors_created: 0,
call_stack_depth: 0,
backtrace: Backtrace::default(),
exec_trace: vec![],
invocation_count: 0,
limits,
})))
}
fn limiter_mut(&mut self) -> &mut <Self::Machine as Machine>::Limiter {
&mut self.limits
}
fn send<K>(
&mut self,
from: ActorID,
to: Address,
method: MethodNum,
params: Option<Block>,
value: &TokenAmount,
) -> Result<InvocationResult>
where
K: Kernel<CallManager = Self>,
{
if self.machine.context().tracing {
self.trace(ExecutionEvent::Call {
from,
to,
method,
params: params
.as_ref()
.map(|blk| blk.data().to_owned().into())
.unwrap_or_default(),
value: value.clone(),
});
}
let result =
self.with_stack_frame(|s| s.send_unchecked::<K>(from, to, method, params, value));
if self.machine.context().tracing {
self.trace(match &result {
Ok(InvocationResult::Return(v)) => ExecutionEvent::CallReturn(
v.as_ref()
.map(|blk| RawBytes::from(blk.data().to_vec()))
.unwrap_or_default(),
),
Ok(InvocationResult::Failure(code)) => ExecutionEvent::CallAbort(*code),
Err(ExecutionError::OutOfGas) => ExecutionEvent::CallError(SyscallError::new(
ErrorNumber::Forbidden,
"out of gas",
)),
Err(ExecutionError::Fatal(_)) => {
ExecutionEvent::CallError(SyscallError::new(ErrorNumber::Forbidden, "fatal"))
}
Err(ExecutionError::Syscall(s)) => ExecutionEvent::CallError(s.clone()),
});
}
result
}
fn with_transaction(
&mut self,
f: impl FnOnce(&mut Self) -> Result<InvocationResult>,
) -> Result<InvocationResult> {
self.state_tree_mut().begin_transaction();
let (revert, res) = match f(self) {
Ok(v) => (!v.exit_code().is_success(), Ok(v)),
Err(e) => (true, Err(e)),
};
self.state_tree_mut().end_transaction(revert)?;
res
}
fn finish(mut self) -> (FinishRet, Self::Machine) {
let InnerDefaultCallManager {
machine,
backtrace,
mut gas_tracker,
mut exec_trace,
..
} = *self.0.take().expect("call manager is poisoned");
// TODO: Having to check against zero here is fishy, but this is what lotus does.
let gas_used = gas_tracker.gas_used().max(Gas::zero()).round_up();
// Finalize any trace events, if we're tracing.
if machine.context().tracing {
exec_trace.extend(gas_tracker.drain_trace().map(ExecutionEvent::GasCharge));
}
(
FinishRet {
gas_used,
backtrace,
exec_trace,
},
machine,
)
}
// Accessor methods so the trait can implement some common methods by default.
fn machine(&self) -> &Self::Machine {
&self.machine
}
fn machine_mut(&mut self) -> &mut Self::Machine {
&mut self.machine
}
fn gas_tracker(&self) -> &GasTracker {
&self.gas_tracker
}
fn gas_tracker_mut(&mut self) -> &mut GasTracker {
&mut self.gas_tracker
}
// Other accessor methods
fn origin(&self) -> ActorID {
self.origin
}
fn nonce(&self) -> u64 {
self.nonce
}
// Helper for creating actors. This really doesn't belong on this trait.
fn next_actor_idx(&mut self) -> u64 {
let ret = self.num_actors_created;
self.num_actors_created += 1;
ret
}
fn invocation_count(&self) -> u64 {
self.invocation_count
}
}
impl<M> DefaultCallManager<M>
where
M: Machine,
{
fn trace(&mut self, trace: ExecutionEvent) {
// The price of deref magic is that you sometimes need to tell the compiler: no, this is
// fine.
let s = &mut **self;
s.exec_trace
.extend(s.gas_tracker.drain_trace().map(ExecutionEvent::GasCharge));
s.exec_trace.push(trace);
}
fn create_account_actor<K>(&mut self, addr: &Address) -> Result<ActorID>
where
K: Kernel<CallManager = Self>,
{
self.charge_gas(self.price_list().on_create_actor())?;
if addr.is_bls_zero_address() {
return Err(
syscall_error!(IllegalArgument; "cannot create the bls zero address actor").into(),
);
}
// Create the actor in the state tree.
let id = {
let code_cid = self.builtin_actors().get_account_code();
let state = ActorState::new_empty(*code_cid, Some(*addr));
self.create_actor(addr, state)?
};
// Now invoke the constructor; first create the parameters, then
// instantiate a new kernel to invoke the constructor.
let params = to_vec(&addr).map_err(|e| {
// This shouldn't happen, but we treat it as an illegal argument error and move on.
// It _likely_ means that the inputs were invalid in some unexpected way.
log::error!(
"failed to serialize address when creating actor, ignoring: {}",
e
);
syscall_error!(IllegalArgument; "failed to serialize params: {}", e)
})?;
self.send_resolved::<K>(
account_actor::SYSTEM_ACTOR_ID,
id,
fvm_shared::METHOD_CONSTRUCTOR,
Some(Block::new(DAG_CBOR, params)),
&TokenAmount::zero(),
)?;
Ok(id)
}
fn create_embryo_actor<K>(&mut self, addr: &Address) -> Result<ActorID>
where
K: Kernel<CallManager = Self>,
{
self.charge_gas(self.price_list().on_create_actor())?;
// Create the actor in the state tree, but don't call any constructor.
let code_cid = self.builtin_actors().get_embryo_code();
let state = ActorState::new_empty(*code_cid, Some(*addr));
self.create_actor(addr, state)
}
/// Send without checking the call depth.
fn send_unchecked<K>(
&mut self,
from: ActorID,
to: Address,
method: MethodNum,
params: Option<Block>,
value: &TokenAmount,
) -> Result<InvocationResult>
where
K: Kernel<CallManager = Self>,
{
// Get the receiver; this will resolve the address.
let to = match self.state_tree().lookup_id(&to)? {
Some(addr) => addr,
None => match to.payload() {
Payload::BLS(_) | Payload::Secp256k1(_) => {
// Try to create an account actor if the receiver is a key address.
self.create_account_actor::<K>(&to)?
}
// Validate that there's an actor at the target ID (we don't care what is there,
// just that something is there).
Payload::Delegated(da)
if self.state_tree().get_actor_id(da.namespace())?.is_some() =>
{
self.create_embryo_actor::<K>(&to)?
}
_ => return Err(syscall_error!(NotFound; "actor does not exist: {}", to).into()),
},
};
// Do the actual send.
self.send_resolved::<K>(from, to, method, params, value)
}
/// Send with resolved addresses.
fn send_resolved<K>(
&mut self,
from: ActorID,
to: ActorID,
method: MethodNum,
params: Option<Block>,
value: &TokenAmount,
) -> Result<InvocationResult>
where
K: Kernel<CallManager = Self>,
{
// Lookup the actor.
let state = self
.state_tree()
.get_actor_id(to)?
.ok_or_else(|| syscall_error!(NotFound; "actor does not exist: {}", to))?;
// Charge the method gas. Not sure why this comes second, but it does.
self.charge_gas(self.price_list().on_method_invocation(value, method))?;
// Transfer, if necessary.
if !value.is_zero() {
self.machine.transfer(from, to, value)?;
}
// Abort early if we have a send.
if method == METHOD_SEND {
log::trace!("sent {} -> {}: {}", from, to, &value);
return Ok(InvocationResult::Return(Default::default()));
}
// Store the parametrs, and initialize the block registry for the target actor.
let mut block_registry = BlockRegistry::new();
let params_id = if let Some(blk) = params {
block_registry.put(blk)?
} else {
NO_DATA_BLOCK_ID
};
// Increment invocation count
self.invocation_count += 1;
// This is a cheap operation as it doesn't actually clone the struct,
// it returns a referenced copy.
let engine: Engine = self.engine().clone();
// Ensure that actor's code is loaded and cached in the engine.
// NOTE: this does not cover the EVM smart contract actor, which is a built-in actor, is
// listed the manifest, and therefore preloaded during system initialization.
#[cfg(feature = "m2-native")]
self.engine()
.prepare_actor_code(&state.code, self.blockstore())
.map_err(
|_| syscall_error!(NotFound; "actor code cid does not exist {}", &state.code),
)?;
log::trace!("calling {} -> {}::{}", from, to, method);
self.map_mut(|cm| {
// Make the kernel.
let kernel = K::new(cm, block_registry, from, to, method, value.clone());
// Make a store.
let mut store = engine.new_store(kernel);
// From this point on, there are no more syscall errors, only aborts.
let result: std::result::Result<BlockId, Abort> = (|| {
// Instantiate the module.
let instance = engine
.get_instance(&mut store, &state.code)
.and_then(|i| i.context("actor code not found"))
.map_err(Abort::Fatal)?;
// Resolve and store a reference to the exported memory.
let memory = instance
.get_memory(&mut store, "memory")
.context("actor has no memory export")
.map_err(Abort::Fatal)?;
store.data_mut().memory = memory;
// Lookup the invoke method.
let invoke: wasmtime::TypedFunc<(u32,), u32> = instance
.get_typed_func(&mut store, "invoke")
// All actors will have an invoke method.
.map_err(Abort::Fatal)?;
// Set the available gas.
update_gas_available(&mut store)?;
// Invoke it.
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
invoke.call(&mut store, (params_id,))
}))
.map_err(|panic| Abort::Fatal(anyhow!("panic within actor: {:?}", panic)))?;
// Charge for any remaining uncharged execution gas, returning an error if we run out.
charge_for_exec(&mut store)?;
// If the invocation failed due to running out of exec_units, we have already
// detected it and returned OutOfGas above. Any other invocation failure is returned
// here as an Abort
Ok(res?)
})();
let invocation_data = store.into_data();
let last_error = invocation_data.last_error;
let (mut cm, block_registry) = invocation_data.kernel.into_inner();
// Resolve the return block's ID into an actual block, converting to an abort if it
// doesn't exist.
let result = result.and_then(|ret_id| {
Ok(if ret_id == NO_DATA_BLOCK_ID {
None
} else {
Some(block_registry.get(ret_id).map_err(|_| {
Abort::Exit(
ExitCode::SYS_MISSING_RETURN,
String::from("returned block does not exist"),
)
})?)
})
});
// Process the result, updating the backtrace if necessary.
let ret = match result {
Ok(ret) => Ok(InvocationResult::Return(ret.cloned())),
Err(abort) => {
if let Some(err) = last_error {
cm.backtrace.begin(err);
}
let (code, message, res) = match abort {
Abort::Exit(code, message) => {
(code, message, Ok(InvocationResult::Failure(code)))
}
Abort::OutOfGas => (
ExitCode::SYS_OUT_OF_GAS,
"out of gas".to_owned(),
Err(ExecutionError::OutOfGas),
),
Abort::Fatal(err) => (
ExitCode::SYS_ASSERTION_FAILED,
"fatal error".to_owned(),
Err(ExecutionError::Fatal(err)),
),
};
cm.backtrace.push_frame(Frame {
source: to,
method,
message,
code,
});
res
}
};
// Log the results if tracing is enabled.
if log::log_enabled!(log::Level::Trace) {
match &ret {
Ok(val) => log::trace!(
"returning {}::{} -> {} ({})",
to,
method,
from,
val.exit_code()
),
Err(e) => log::trace!("failing {}::{} -> {} (err:{})", to, method, from, e),
}
}
(ret, cm)
})
}
/// Temporarily replace `self` with a version that contains `None` for the inner part,
/// to be able to hand over ownership of `self` to a new kernel, while the older kernel
/// has a reference to the hollowed out version.
fn map_mut<F, T>(&mut self, f: F) -> T
where
F: FnOnce(Self) -> (T, Self),
{
replace_with::replace_with_and_return(self, || DefaultCallManager(None), f)
}
/// Check that we're not violating the call stack depth, then envelope a call
/// with an increase/decrease of the depth to make sure none of them are missed.
fn with_stack_frame<F, V>(&mut self, f: F) -> Result<V>
where
F: FnOnce(&mut Self) -> Result<V>,
{
// We check _then_ set because we don't count the top call. This effectivly allows a
// call-stack depth of `max_call_depth + 1` (or `max_call_depth` sub-calls). While this is
// likely a bug, this is how NV15 behaves so we mimic that behavior here.
//
// By example:
//
// 1. If the max depth is 0, call_stack_depth will be 1 and the top-level message won't be
// able to make sub-calls (1 > 0).
// 2. If the max depth is 1, the call_stack_depth will be 1 in the top-level message, 2 in
// sub-calls, and said sub-calls will not be able to make further subcalls (2 > 1).
//
// NOTE: Unlike the FVM, Lotus adds _then_ checks. It does this because the
// `call_stack_depth` in lotus is 0 for the top-level call, unlike in the FVM where it's 1.
if self.call_stack_depth > self.machine.context().max_call_depth {
let sys_err = syscall_error!(LimitExceeded, "message execution exceeds call depth");
if self.machine.context().tracing {
self.trace(ExecutionEvent::CallError(sys_err.clone()));
}
return Err(sys_err.into());
}
self.call_stack_depth += 1;
let res = <<<DefaultCallManager<M> as CallManager>::Machine as Machine>::Limiter>::with_stack_frame(
self,
|s| s.limiter_mut(),
f,
);
self.call_stack_depth -= 1;
res
}
}