Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/executor/runtime_call/tests.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2023  Pierre Krieger
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
#![cfg(test)]
19
20
//! The test in this module reads various JSON files containing test fixtures and executes them.
21
//!
22
//! Each test fixture contains a block (header and body), plus the storage of its parent. The
23
//! test consists in executing the block, to make sure that the state trie root matches the one
24
//! calculated by smoldot.
25
26
use core::{iter, ops};
27
28
use super::{run, Config, RuntimeCall, StorageProofSizeBehavior};
29
use crate::{executor::host, trie};
30
use alloc::collections::BTreeMap;
31
32
#[test]
33
1
fn execute_blocks() {
34
    // Tests ordered alphabetically.
35
4
    for (test_num, test_json) in [
36
1
        include_str!("./child-trie-create-multiple.json"),
37
1
        include_str!("./child-trie-create-one.json"),
38
1
        include_str!("./child-trie-destroy.json"),
39
1
        include_str!("./child-trie-read-basic.json"),
40
1
        // TODO: more tests?
41
1
    ]
42
1
    .into_iter()
43
1
    .enumerate()
44
    {
45
        // Decode the test JSON.
46
4
        let test_data = serde_json::from_str::<Test>(test_json).unwrap();
47
48
        // Turn the nice-looking data into something with better access times.
49
4
        let storage = {
50
4
            let mut storage = test_data
51
4
                .parent_storage
52
4
                .main_trie
53
4
                .iter()
54
217
                .map(|(key, value)| ((None, key.0.clone()), value.0.clone()))
55
4
                .collect::<BTreeMap<_, _>>();
56
11
            for (
child_trie, child_trie_data7
) in &test_data.parent_storage.child_tries {
57
14
                for (
key, value7
) in child_trie_data {
58
7
                    storage.insert((Some(child_trie.0.clone()), key.0.clone()), value.0.clone());
59
7
                }
60
            }
61
4
            storage
62
4
        };
63
4
64
4
        // Build the runtime.
65
4
        let virtual_machine = {
66
4
            let code = storage
67
4
                .get(&(None, b":code".to_vec()))
68
4
                .expect("no runtime code found");
69
4
            let heap_pages = crate::executor::storage_heap_pages_to_value(
70
4
                storage.get(&(None, b":heappages".to_vec())).map(|v| 
&v[..]0
),
71
4
            )
72
4
            .unwrap();
73
4
74
4
            host::HostVmPrototype::new(host::Config {
75
4
                module: code,
76
4
                heap_pages,
77
4
                exec_hint: crate::executor::vm::ExecHint::ExecuteOnceWithNonDeterministicValidation,
78
4
                allow_unresolved_imports: false,
79
4
            })
80
4
            .unwrap()
81
4
        };
82
4
83
4
        // The runtime indicates the version of the trie items of the parent storage.
84
4
        // While in principle each storage item could have a different version, in practice we
85
4
        // just assume they're all the same.
86
4
        let state_version = virtual_machine
87
4
            .runtime_version()
88
4
            .decode()
89
4
            .state_version
90
4
            .unwrap_or(host::TrieEntryVersion::V0);
91
4
92
4
        // Start executing `Core_execute_block`. This runtime call will verify at the end whether
93
4
        // the trie root hash of the block matches the one calculated by smoldot.
94
4
        let mut execution = run(Config {
95
4
            virtual_machine,
96
4
            function_to_call: "Core_execute_block",
97
4
            max_log_level: 3,
98
4
            storage_proof_size_behavior: StorageProofSizeBehavior::Unimplemented,
99
4
            storage_main_trie_changes: Default::default(),
100
4
            calculate_trie_changes: false,
101
4
            parameter: {
102
4
                // Block header + number of extrinsics + extrinsics
103
4
                let encoded_body_len =
104
4
                    crate::util::encode_scale_compact_usize(test_data.block.body.len());
105
4
                iter::once(either::Right(either::Left(&test_data.block.header.0)))
106
4
                    .chain(iter::once(either::Right(either::Right(encoded_body_len))))
107
18
                    .chain(test_data.block.body.iter().map(|b| either::Left(&b.0)))
108
4
            },
109
4
        })
110
4
        .unwrap();
111
112
        loop {
113
4
            match execution {
114
4
                RuntimeCall::Finished(Ok(_)) => break, // Test successful!
115
0
                RuntimeCall::Finished(Err(err)) => {
116
0
                    panic!("Error during test #{}: {:?}", test_num, err)
117
                }
118
5
                RuntimeCall::SignatureVerification(sig) => execution = sig.verify_and_resume(),
119
4.90k
                RuntimeCall::ClosestDescendantMerkleValue(req) => execution = req.resume_unknown(),
120
449
                RuntimeCall::StorageGet(get) => {
121
449
                    let value = storage
122
449
                        .get(&(
123
449
                            get.child_trie().map(|c| 
c.as_ref().to_owned()11
),
124
449
                            get.key().as_ref().to_owned(),
125
449
                        ))
126
449
                        .map(|v| 
(iter::once(&v[..]), state_version)289
);
127
449
                    execution = get.inject_value(value);
128
449
                }
129
6.05k
                RuntimeCall::NextKey(req) => {
130
6.05k
                    // Because `NextKey` might ask for branch nodes, and that we don't build the
131
6.05k
                    // trie in its entirety, we have to use an algorithm that finds the branch
132
6.05k
                    // nodes for us.
133
6.05k
                    let next_key = {
134
6.05k
                        let mut search = trie::branch_search::BranchSearch::NextKey(
135
6.05k
                            trie::branch_search::start_branch_search(trie::branch_search::Config {
136
6.05k
                                key_before: req.key().collect::<Vec<_>>().into_iter(),
137
6.05k
                                or_equal: req.or_equal(),
138
6.05k
                                prefix: req.prefix().collect::<Vec<_>>().into_iter(),
139
6.05k
                                no_branch_search: !req.branch_nodes(),
140
6.05k
                            }),
141
6.05k
                        );
142
143
12.5k
                        loop {
144
12.5k
                            match search {
145
                                trie::branch_search::BranchSearch::Found {
146
6.05k
                                    branch_trie_node_key,
147
6.05k
                                } => break branch_trie_node_key,
148
6.52k
                                trie::branch_search::BranchSearch::NextKey(bs_req) => {
149
6.52k
                                    let result = storage
150
6.52k
                                        .range((
151
6.52k
                                            if bs_req.or_equal() {
152
6.51k
                                                ops::Bound::Included((
153
6.51k
                                                    req.child_trie().map(|c| 
c.as_ref().to_owned()88
),
154
6.51k
                                                    bs_req.key_before().collect::<Vec<_>>(),
155
6.51k
                                                ))
156
                                            } else {
157
10
                                                ops::Bound::Excluded((
158
10
                                                    req.child_trie().map(|c| 
c.as_ref().to_owned()1
),
159
10
                                                    bs_req.key_before().collect::<Vec<_>>(),
160
10
                                                ))
161
                                            },
162
6.52k
                                            ops::Bound::Unbounded,
163
6.52k
                                        ))
164
6.52k
                                        .next()
165
6.52k
                                        .filter(|((trie, key), _)| {
166
6.44k
                                            *trie == req.child_trie().map(|c| 
c.as_ref().to_owned()55
)
167
6.32k
                                                && key.starts_with(
168
6.32k
                                                    &bs_req.prefix().collect::<Vec<_>>(),
169
6.32k
                                                )
170
6.52k
                                        
}6.44k
)
171
6.52k
                                        .map(|((_, k), _)| 
k985
);
172
6.52k
173
6.52k
                                    search = bs_req.inject(result.map(|k| 
k.iter().copied()985
));
174
                                }
175
                            }
176
                        }
177
                    };
178
179
6.05k
                    execution = req.inject_key(next_key.map(|nk| 
nk.into_iter()343
));
180
                }
181
0
                RuntimeCall::LogEmit(log) => execution = log.resume(),
182
                RuntimeCall::OffchainStorageSet(_) | RuntimeCall::Offchain(_) => {
183
0
                    unimplemented!()
184
                }
185
            }
186
        }
187
    }
188
1
}
189
190
// Serde structs used to decode the test fixtures.
191
192
20
#[derive(
s12
erde::Deserialize)]
_RINvXs_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtBa_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_7___FieldB1h_11deserializeINtNtCscu7pqq74Vb8_10serde_json2de6MapKeyNtNtB2I_4read7StrReadEEBg_
Line
Count
Source
192
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtBb_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1k_7Visitor9visit_seqINtNtCscu7pqq74Vb8_10serde_json2de9SeqAccessNtNtB2S_4read7StrReadEEBh_
_RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtBb_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1k_7Visitor9visit_mapINtNtCscu7pqq74Vb8_10serde_json2de9MapAccessNtNtB2S_4read7StrReadEEBh_
Line
Count
Source
192
12
#[derive(
s4
erde::Deserialize)]
_RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtB8_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1h_7Visitor9visit_strNtNtCscu7pqq74Vb8_10serde_json5error5ErrorEBe_
Line
Count
Source
192
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RNvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtBa_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_9___VisitorNtB1j_7Visitor9expecting
Unexecuted instantiation: _RNvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtB7_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB2_14___FieldVisitorNtB1g_7Visitor9expecting
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtB8_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1h_7Visitor9visit_u64pEBe_
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5tests1__NtB8_4TestNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1h_7Visitor11visit_bytespEBe_
193
struct Test {
194
    block: Block,
195
    #[serde(rename = "parentStorage")]
196
    parent_storage: Storage,
197
}
198
199
20
#[derive(
s12
erde::Deserialize)]
_RINvXs_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtBa_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_7___FieldB1k_11deserializeINtNtCscu7pqq74Vb8_10serde_json2de6MapKeyNtNtB2L_4read7StrReadEEBg_
Line
Count
Source
199
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtBb_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1n_7Visitor9visit_seqINtNtCscu7pqq74Vb8_10serde_json2de9SeqAccessNtNtB2V_4read7StrReadEEBh_
_RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtBb_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1n_7Visitor9visit_mapINtNtCscu7pqq74Vb8_10serde_json2de9MapAccessNtNtB2V_4read7StrReadEEBh_
Line
Count
Source
199
12
#[derive(
s4
erde::Deserialize)]
_RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtB8_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1k_7Visitor9visit_strNtNtCscu7pqq74Vb8_10serde_json5error5ErrorEBe_
Line
Count
Source
199
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RNvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtBa_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_9___VisitorNtB1m_7Visitor9expecting
Unexecuted instantiation: _RNvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtB7_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB2_14___FieldVisitorNtB1j_7Visitor9expecting
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtB8_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1k_7Visitor9visit_u64pEBe_
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss_1__NtB8_5BlockNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1k_7Visitor11visit_bytespEBe_
200
struct Block {
201
    header: HexString,
202
    body: Vec<HexString>,
203
}
204
205
20
#[derive(
s12
erde::Deserialize)]
_RINvXs_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtBa_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_7___FieldB1n_11deserializeINtNtCscu7pqq74Vb8_10serde_json2de6MapKeyNtNtB2O_4read7StrReadEEBg_
Line
Count
Source
205
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtBb_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1q_7Visitor9visit_seqINtNtCscu7pqq74Vb8_10serde_json2de9SeqAccessNtNtB2Y_4read7StrReadEEBh_
_RINvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtBb_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB6_9___VisitorNtB1q_7Visitor9visit_mapINtNtCscu7pqq74Vb8_10serde_json2de9MapAccessNtNtB2Y_4read7StrReadEEBh_
Line
Count
Source
205
12
#[derive(
s4
erde::Deserialize)]
_RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtB8_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1n_7Visitor9visit_strNtNtCscu7pqq74Vb8_10serde_json5error5ErrorEBe_
Line
Count
Source
205
8
#[derive(serde::Deserialize)]
Unexecuted instantiation: _RNvXs0_NvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtBa_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB5_9___VisitorNtB1p_7Visitor9expecting
Unexecuted instantiation: _RNvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtB7_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB2_14___FieldVisitorNtB1m_7Visitor9expecting
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtB8_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1n_7Visitor9visit_u64pEBe_
Unexecuted instantiation: _RINvXNvXNvNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testss0_1__NtB8_7StorageNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeNtB3_14___FieldVisitorNtB1n_7Visitor11visit_bytespEBe_
206
struct Storage {
207
    #[serde(rename = "mainTrie")]
208
    main_trie: hashbrown::HashMap<HexString, HexString>,
209
    #[serde(rename = "childTries")]
210
    child_tries: hashbrown::HashMap<HexString, hashbrown::HashMap<HexString, HexString>>,
211
}
212
213
#[derive(Clone, PartialEq, Eq, Hash)]
214
struct HexString(Vec<u8>);
215
216
impl<'a> serde::Deserialize<'a> for HexString {
217
468
    fn deserialize<D>(deserializer: D) -> Result<HexString, D::Error>
218
468
    where
219
468
        D: serde::Deserializer<'a>,
220
468
    {
221
468
        let string = String::deserialize(deserializer)
?0
;
222
223
468
        if string.is_empty() {
224
0
            return Ok(HexString(Vec::new()));
225
468
        }
226
468
227
468
        if !string.starts_with("0x") {
228
0
            return Err(serde::de::Error::custom(
229
0
                "hexadecimal string doesn't start with 0x",
230
0
            ));
231
468
        }
232
233
468
        let bytes = hex::decode(&string[2..]).map_err(serde::de::Error::custom)
?0
;
234
468
        Ok(HexString(bytes))
235
468
    }
_RINvXNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testsNtB3_9HexStringNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeINtNtCscu7pqq74Vb8_10serde_json2de6MapKeyNtNtB28_4read7StrReadEEB9_
Line
Count
Source
217
231
    fn deserialize<D>(deserializer: D) -> Result<HexString, D::Error>
218
231
    where
219
231
        D: serde::Deserializer<'a>,
220
231
    {
221
231
        let string = String::deserialize(deserializer)
?0
;
222
223
231
        if string.is_empty() {
224
0
            return Ok(HexString(Vec::new()));
225
231
        }
226
231
227
231
        if !string.starts_with("0x") {
228
0
            return Err(serde::de::Error::custom(
229
0
                "hexadecimal string doesn't start with 0x",
230
0
            ));
231
231
        }
232
233
231
        let bytes = hex::decode(&string[2..]).map_err(serde::de::Error::custom)
?0
;
234
231
        Ok(HexString(bytes))
235
231
    }
Unexecuted instantiation: _RINvXNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testsNtB3_9HexStringNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeINtNvNtNtB1g_9___private2de13missing_field24MissingFieldDeserializerNtNtCscu7pqq74Vb8_10serde_json5error5ErrorEEB9_
_RINvXNtNtNtCsN16ciHI6Qf_7smoldot8executor12runtime_call5testsNtB3_9HexStringNtNtCsf0yC2YK6bpM_5serde2de11Deserialize11deserializeQINtNtCscu7pqq74Vb8_10serde_json2de12DeserializerNtNtB29_4read7StrReadEEB9_
Line
Count
Source
217
237
    fn deserialize<D>(deserializer: D) -> Result<HexString, D::Error>
218
237
    where
219
237
        D: serde::Deserializer<'a>,
220
237
    {
221
237
        let string = String::deserialize(deserializer)
?0
;
222
223
237
        if string.is_empty() {
224
0
            return Ok(HexString(Vec::new()));
225
237
        }
226
237
227
237
        if !string.starts_with("0x") {
228
0
            return Err(serde::de::Error::custom(
229
0
                "hexadecimal string doesn't start with 0x",
230
0
            ));
231
237
        }
232
233
237
        let bytes = hex::decode(&string[2..]).map_err(serde::de::Error::custom)
?0
;
234
237
        Ok(HexString(bytes))
235
237
    }
236
}