oasis_core_runtime/consensus/roothash/
block.rs

1//! Roothash block and header.
2//!
3//! # Note
4//!
5//! This **MUST** be kept in sync with go/roothash/api/block.
6//!
7use crate::common::{crypto::hash::Hash, namespace::Namespace};
8
9/// Runtime block.
10///
11/// # Note
12///
13/// This should be kept in sync with go/roothash/api/block/block.go.
14#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
15pub struct Block {
16    /// Header.
17    pub header: Header,
18}
19
20impl Block {
21    /// Creates a new empty genesis block given a runtime id and POSIX timestamp.
22    pub fn new_genesis_block(id: Namespace, timestamp: u64) -> Block {
23        Block {
24            header: Header {
25                version: 0,
26                round: 0,
27                timestamp,
28                header_type: HeaderType::Normal,
29                namespace: id,
30                previous_hash: Hash::empty_hash(),
31                io_root: Hash::empty_hash(),
32                state_root: Hash::empty_hash(),
33                messages_hash: Hash::empty_hash(),
34                in_msgs_hash: Hash::empty_hash(),
35            },
36        }
37    }
38
39    /// Creates a new empty block with a specific type.
40    pub fn new_empty_block(child: &Block, timestamp: u64, header_type: HeaderType) -> Block {
41        Block {
42            header: Header {
43                version: child.header.version,
44                namespace: child.header.namespace,
45                round: child.header.round + 1,
46                timestamp,
47                header_type,
48                previous_hash: child.header.encoded_hash(),
49                io_root: Hash::empty_hash(),
50                // State root is unchanged.
51                state_root: child.header.state_root,
52                messages_hash: Hash::empty_hash(),
53                in_msgs_hash: Hash::empty_hash(),
54            },
55        }
56    }
57}
58
59/// Header type.
60///
61/// # Note
62///
63/// This should be kept in sync with go/roothash/api/block/header.go.
64#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
65#[repr(u8)]
66pub enum HeaderType {
67    #[default]
68    Invalid = 0,
69    Normal = 1,
70    RoundFailed = 2,
71    EpochTransition = 3,
72    Suspended = 4,
73}
74
75/// Block header.
76///
77/// # Note
78///
79/// This should be kept in sync with go/roothash/api/block/header.go.
80#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
81pub struct Header {
82    /// Protocol version number.
83    pub version: u16,
84    /// Chain namespace.
85    pub namespace: Namespace,
86    /// Round number.
87    pub round: u64,
88    /// Timestamp (POSIX time).
89    pub timestamp: u64,
90    /// Header type.
91    pub header_type: HeaderType,
92    /// Previous block hash.
93    pub previous_hash: Hash,
94    /// I/O merkle root.
95    pub io_root: Hash,
96    /// State merkle root.
97    pub state_root: Hash,
98    /// Messages hash.
99    pub messages_hash: Hash,
100    /// Hash of processed incoming messages.
101    pub in_msgs_hash: Hash,
102}
103
104impl Header {
105    /// Returns a hash of an encoded header.
106    pub fn encoded_hash(&self) -> Hash {
107        Hash::digest_bytes(&cbor::to_vec(self.clone()))
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_consistent_hash_header() {
117        // NOTE: These hashes MUST be synced with go/roothash/api/block/header_test.go.
118        let empty = Header::default();
119        assert_eq!(
120            empty.encoded_hash(),
121            Hash::from("677ad1a6b9f5e99ed94e5d598b6f92a4641a5f952f2d753b2a6122b6dceeb792")
122        );
123
124        let populated = Header {
125            version: 42,
126            namespace: Namespace::from(Hash::empty_hash().as_ref()),
127            round: 1000,
128            timestamp: 1560257841,
129            header_type: HeaderType::RoundFailed,
130            previous_hash: empty.encoded_hash(),
131            io_root: Hash::empty_hash(),
132            state_root: Hash::empty_hash(),
133            messages_hash: Hash::empty_hash(),
134            in_msgs_hash: Hash::empty_hash(),
135        };
136        assert_eq!(
137            populated.encoded_hash(),
138            Hash::from("b17374d9b36796752a787d0726ef44826bfdb3ece52545e126c8e7592663544d")
139        );
140    }
141}