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
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use std::ops::RangeInclusive;

use anyhow::{anyhow, Context as _};
use cid::Cid;
use fvm_ipld_blockstore::{Block, Blockstore, Buffered};
use fvm_ipld_encoding::{CborStore, DAG_CBOR};
use fvm_shared::version::NetworkVersion;
use log::debug;
use multihash::Code::Blake2b256;

use super::{Machine, MachineContext};
use crate::blockstore::BufferedBlockstore;
use crate::externs::Externs;
use crate::kernel::{ClassifyResult, Result};
use crate::machine::limiter::DefaultMemoryLimiter;
use crate::machine::Manifest;
use crate::state_tree::StateTree;
use crate::system_actor::State as SystemActorState;

pub struct DefaultMachine<B, E> {
    /// The initial execution context for this epoch.
    context: MachineContext,
    /// 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
    ///
    /// * `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(context: &MachineContext, blockstore: B, externs: E) -> anyhow::Result<Self> {
        #[cfg(not(feature = "nv24-dev"))]
        const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
            NetworkVersion::V21..=NetworkVersion::V23;

        #[cfg(feature = "nv24-dev")]
        const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
            NetworkVersion::V21..=NetworkVersion::V24;

        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
            ));
        }

        put_empty_blocks(&blockstore)?;

        // 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)?;

        // 16 bytes is random _enough_
        let randomness: [u8; 16] = rand::random();

        Ok(DefaultMachine {
            context: context.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;
    type Limiter = DefaultMemoryLimiter;

    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)
    }

    fn into_store(self) -> Self::Blockstore {
        self.state_tree.into_store()
    }

    fn machine_id(&self) -> &str {
        &self.id
    }

    fn new_limiter(&self) -> Self::Limiter {
        DefaultMemoryLimiter::for_network(&self.context().network)
    }
}

/// DagCBOR-encoded empty array. This is the default state object, so it always has to exist.
const EMPTY_ARRAY_BLOCK: Block<&'static [u8]> = Block::new(DAG_CBOR, &[128]);

#[test]
fn test_empty_array_block() {
    let expected = Block::new(DAG_CBOR, fvm_ipld_encoding::to_vec::<[(); 0]>(&[]).unwrap());
    assert_eq!(expected, EMPTY_ARRAY_BLOCK);
}

// Helper method that puts certain "empty" types in the blockstore.
// These types are privileged by some parts of the system (eg. as the default actor state).
fn put_empty_blocks(blockstore: impl Blockstore) -> anyhow::Result<()> {
    use fvm_shared::EMPTY_ARR_CID;
    let empty_arr_cid = blockstore.put(Blake2b256, &EMPTY_ARRAY_BLOCK)?;

    debug_assert!(
        empty_arr_cid == EMPTY_ARR_CID,
        "empty CID sanity check failed",
    );

    Ok(())
}