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
use alloc::vec::Vec;
use alloy_primitives::Bytes;
use alloy_rlp::Encodable;
use serde::{Deserialize, Serialize};
/// Represents the execution witness of a block. Contains lists of required preimages and
/// headers used during execution and verification.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionWitness {
/// List of all hashed trie nodes preimages that were required during the execution of
/// the block, including during state root recomputation.
pub state: Vec<Bytes>,
/// List of all contract codes (created / accessed) preimages that were required during
/// the execution of the block, including during state root recomputation.
pub codes: Vec<Bytes>,
/// List of all hashed account and storage keys (addresses and slots) preimages
/// (unhashed account addresses and storage slots, respectively) that were required during
/// the execution of the block.
pub keys: Vec<Bytes>,
/// RLP-encoded block headers required for proving correctness of stateless execution.
///
/// This collection stores block headers needed to verify:
/// - State reads are correct (i.e. the code and accounts are correct wrt the pre-state root)
/// - BLOCKHASH opcode execution results are correct
///
/// ## Why this field will be empty in the future
///
/// This field is expected to be empty in the future because:
/// - EIP-2935 (Prague) will include block hashes directly in the state
/// - Verkle/Delayed execution will change the block structure to contain the pre-state root
/// instead of the post-state root.
///
/// Once both of these upgrades have been implemented, this field will be empty
/// moving forward because the data that this was proving will either be in the
/// current block or in the state.
///
/// ## State Read Verification
///
/// To verify state reads are correct, we need the pre-state root of the current block,
/// which is (currently) equal to the post-state root of the previous block. We therefore
/// need the previous block's header in order to prove that the state reads are correct.
///
/// Note: While the pre-state root is located in the previous block, this field
/// will always have one or more items.
///
/// ## BLOCKHASH Opcode Verification
///
/// The BLOCKHASH opcode returns the block hash for a given block number, but it
/// only works for the 256 most recent blocks, not including the current block.
/// To verify that a block hash is indeed correct wrt the BLOCKHASH opcode
/// and not an arbitrary set of block hashes, we need a contiguous set of
/// block headers starting from the current block.
///
/// ### Example
///
/// Consider a blockchain at block 200, and inside of block 200, a transaction
/// calls BLOCKHASH(100):
/// - This is valid because block 100 is within the 256-block lookback window
/// - To verify this, we need all of the headers from block 100 through block 200
/// - These headers form a chain proving the correctness of block 100's hash.
///
/// The naive way to construct the headers would be to unconditionally include the last
/// 256 block headers. However note, we may not need all 256, like in the example above.
pub headers: Vec<Bytes>,
}
impl ExecutionWitness {
/// Sets the `headers` field from already RLP-encoded headers.
pub fn with_rlp_headers(mut self, headers: Vec<Bytes>) -> Self {
self.headers = headers;
self
}
/// Sets the `headers` field by RLP-encoding each item.
pub fn with_headers<H: Encodable>(mut self, headers: impl IntoIterator<Item = H>) -> Self {
self.headers = headers
.into_iter()
.map(|header| {
let mut buf = Vec::new();
header.encode(&mut buf);
buf.into()
})
.collect();
self
}
}