-
Notifications
You must be signed in to change notification settings - Fork 143
/
Copy pathdefault.rs
251 lines (216 loc) · 8.24 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
use std::ops::RangeInclusive;
use anyhow::{Context as _, anyhow};
use cid::Cid;
use fvm_ipld_blockstore::{Blockstore, Buffered};
use fvm_ipld_encoding::CborStore;
use fvm_shared::ActorID;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ErrorNumber;
use fvm_shared::version::NetworkVersion;
use log::debug;
use super::{Engine, Machine, MachineContext};
use crate::blockstore::BufferedBlockstore;
use crate::externs::Externs;
use crate::kernel::{ClassifyResult, Context as _, Result};
use crate::machine::Manifest;
use crate::state_tree::{ActorState, StateTree};
use crate::syscall_error;
use crate::system_actor::State as SystemActorState;
pub struct DefaultMachine<B, E> {
/// The initial execution context for this epoch.
context: MachineContext,
/// The WASM engine is created on construction of the DefaultMachine, and
/// is dropped when the DefaultMachine is dropped.
engine: Engine,
/// Boundary A calls are handled through externs. These are calls from the
/// FVM to the Filecoin client.
externs: E,
/// The state tree. It is updated with the results from every message
/// execution as the call stack for every message concludes.
///
/// Owned.
state_tree: StateTree<BufferedBlockstore<B>>,
/// Mapping of CIDs to builtin actor types.
builtin_actors: Manifest,
/// Somewhat unique ID of the machine consisting of (epoch, randomness)
/// randomness is generated with `initial_state_root`
id: String,
}
impl<B, E> DefaultMachine<B, E>
where
B: Blockstore + 'static,
E: Externs + 'static,
{
/// Create a new [`DefaultMachine`].
///
/// # Arguments
///
/// * `engine`: The global wasm [`Engine`] (engine, pooled resources, caches).
/// * `context`: Machine execution [context][`MachineContext`] (system params, epoch, network
/// version, etc.).
/// * `blockstore`: The underlying [blockstore][`Blockstore`] for reading/writing state.
/// * `externs`: Client-provided ["external"][`Externs`] methods for accessing chain state.
pub fn new(
engine: &Engine,
context: &MachineContext,
blockstore: B,
externs: E,
) -> anyhow::Result<Self> {
const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
NetworkVersion::V15..=NetworkVersion::V17;
debug!(
"initializing a new machine, epoch={}, base_fee={}, nv={:?}, root={}",
context.epoch, &context.base_fee, context.network_version, context.initial_state_root
);
if !SUPPORTED_VERSIONS.contains(&context.network_version) {
return Err(anyhow!(
"unsupported network version: {}",
context.network_version
));
}
// Sanity check that the blockstore contains the supplied state root.
if !blockstore
.has(&context.initial_state_root)
.context("failed to load initial state-root")?
{
return Err(anyhow!(
"blockstore doesn't have the initial state-root {}",
&context.initial_state_root
));
}
// Create a new state tree from the supplied root.
let state_tree = {
let bstore = BufferedBlockstore::new(blockstore);
StateTree::new_from_root(bstore, &context.initial_state_root)?
};
// Load the built-in actors manifest.
let (builtin_actors_cid, manifest_version) = match context.builtin_actors_override {
Some(manifest_cid) => {
let (version, cid): (u32, Cid) = state_tree
.store()
.get_cbor(&manifest_cid)?
.context("failed to load actor manifest")?;
(cid, version)
}
None => {
let (state, _) = SystemActorState::load(&state_tree)?;
(state.builtin_actors, 1)
}
};
let builtin_actors =
Manifest::load(state_tree.store(), &builtin_actors_cid, manifest_version)?;
// Preload any uncached modules.
// This interface works for now because we know all actor CIDs
// ahead of time, but with user-supplied code, we won't have that
// guarantee.
// Skip preloading all builtin actors when testing. This results in JIT
// bytecode to machine code compilation, and leads to faster tests.
#[cfg(not(any(test, feature = "testing")))]
engine.preload(state_tree.store(), builtin_actors.builtin_actor_codes())?;
// 16 bytes is random _enough_
let randomness: [u8; 16] = rand::random();
Ok(DefaultMachine {
context: context.clone(),
engine: engine.clone(),
externs,
state_tree,
builtin_actors,
id: format!(
"{}-{}",
context.epoch,
cid::multibase::encode(cid::multibase::Base::Base32Lower, randomness)
),
})
}
}
impl<B, E> Machine for DefaultMachine<B, E>
where
B: Blockstore + 'static,
E: Externs + 'static,
{
type Blockstore = BufferedBlockstore<B>;
type Externs = E;
fn engine(&self) -> &Engine {
&self.engine
}
fn blockstore(&self) -> &Self::Blockstore {
self.state_tree.store()
}
fn context(&self) -> &MachineContext {
&self.context
}
fn externs(&self) -> &Self::Externs {
&self.externs
}
fn builtin_actors(&self) -> &Manifest {
&self.builtin_actors
}
fn state_tree(&self) -> &StateTree<Self::Blockstore> {
&self.state_tree
}
fn state_tree_mut(&mut self) -> &mut StateTree<Self::Blockstore> {
&mut self.state_tree
}
/// Flushes the state-tree and returns the new root CID.
///
/// This method also flushes all new blocks (reachable from this new root CID) from the write
/// buffer into the underlying blockstore (the blockstore with which the machine was
/// constructed).
fn flush(&mut self) -> Result<Cid> {
let root = self.state_tree_mut().flush()?;
self.blockstore().flush(&root).or_fatal()?;
Ok(root)
}
/// Creates an uninitialized actor.
fn create_actor(&mut self, addr: &Address, act: ActorState) -> Result<ActorID> {
let state_tree = self.state_tree_mut();
let addr_id = state_tree
.register_new_address(addr)
.context("failed to register new address")
.or_fatal()?;
state_tree
.set_actor(&Address::new_id(addr_id), act)
.context("failed to set actor")
.or_fatal()?;
Ok(addr_id)
}
fn transfer(&mut self, from: ActorID, to: ActorID, value: &TokenAmount) -> Result<()> {
if value.is_negative() {
return Err(syscall_error!(IllegalArgument;
"attempted to transfer negative transfer value {}", value)
.into());
}
// If the from actor doesn't exist, we return "insufficient funds" to distinguish between
// that and the case where the _receiving_ actor doesn't exist.
let mut from_actor = self
.state_tree
.get_actor_id(from)?
.context("cannot transfer from non-existent sender")
.or_error(ErrorNumber::InsufficientFunds)?;
if &from_actor.balance < value {
return Err(syscall_error!(InsufficientFunds; "sender does not have funds to transfer (balance {}, transfer {})", &from_actor.balance, value).into());
}
if from == to {
debug!("attempting to self-transfer: noop (from/to: {})", from);
return Ok(());
}
let mut to_actor = self
.state_tree
.get_actor_id(to)?
.context("cannot transfer to non-existent receiver")
.or_error(ErrorNumber::NotFound)?;
from_actor.deduct_funds(value)?;
to_actor.deposit_funds(value);
self.state_tree.set_actor_id(from, from_actor)?;
self.state_tree.set_actor_id(to, to_actor)?;
log::trace!("transferred {} from {} to {}", value, from, to);
Ok(())
}
fn into_store(self) -> Self::Blockstore {
self.state_tree.into_store()
}
fn machine_id(&self) -> &str {
&self.id
}
}