fuel_core_compression/
lib.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
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::cast_possible_truncation)]
#![deny(unused_crate_dependencies)]
#![deny(warnings)]

pub mod compress;
pub mod config;
pub mod decompress;
mod eviction_policy;
pub mod ports;
mod registry;

pub use config::Config;
pub use registry::RegistryKeyspace;

use fuel_core_types::{
    blockchain::header::PartialBlockHeader,
    fuel_tx::CompressedTransaction,
};
use registry::RegistrationsPerTable;

/// Compressed block, without the preceding version byte.
#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CompressedBlockPayloadV0 {
    /// Temporal registry insertions
    pub registrations: RegistrationsPerTable,
    /// Compressed block header
    pub header: PartialBlockHeader,
    /// Compressed transactions
    pub transactions: Vec<CompressedTransaction>,
}

/// Versioned compressed block.
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum VersionedCompressedBlock {
    V0(CompressedBlockPayloadV0),
}

impl Default for VersionedCompressedBlock {
    fn default() -> Self {
        Self::V0(Default::default())
    }
}

#[cfg(test)]
mod tests {
    use fuel_core_compression as _;
    use fuel_core_types::{
        blockchain::{
            header::{
                ApplicationHeader,
                ConsensusHeader,
            },
            primitives::Empty,
        },
        fuel_compression::RegistryKey,
        tai64::Tai64,
    };
    use proptest::prelude::*;

    use super::*;

    fn keyspace() -> impl Strategy<Value = RegistryKeyspace> {
        prop_oneof![
            Just(RegistryKeyspace::Address),
            Just(RegistryKeyspace::AssetId),
            Just(RegistryKeyspace::ContractId),
            Just(RegistryKeyspace::ScriptCode),
            Just(RegistryKeyspace::PredicateCode),
        ]
    }

    proptest! {
        /// Serialization for compressed transactions is already tested in fuel-vm,
        /// but the rest of the block de/serialization is tested here.
        #[test]
        fn postcard_roundtrip(
            da_height in 0..=u64::MAX,
            prev_root in prop::array::uniform32(0..=u8::MAX),
            height in 0..=u32::MAX,
            consensus_parameters_version in 0..=u32::MAX,
            state_transition_bytecode_version in 0..=u32::MAX,
            registration_inputs in prop::collection::vec(
                (keyspace(), prop::num::u16::ANY, prop::array::uniform32(0..=u8::MAX)).prop_map(|(ks, rk, arr)| {
                    let k = RegistryKey::try_from(rk as u32).unwrap();
                    (ks, k, arr)
                }),
                0..123
            ),
        ) {
            let mut registrations: RegistrationsPerTable = Default::default();

            for (ks, key, arr) in registration_inputs {
                let value_len_limit = (key.as_u32() % 32) as usize;
                match ks {
                    RegistryKeyspace::Address => {
                        registrations.address.push((key, arr.into()));
                    }
                    RegistryKeyspace::AssetId => {
                        registrations.asset_id.push((key, arr.into()));
                    }
                    RegistryKeyspace::ContractId => {
                        registrations.contract_id.push((key, arr.into()));
                    }
                    RegistryKeyspace::ScriptCode => {
                        registrations.script_code.push((key, arr[..value_len_limit].to_vec().into()));
                    }
                    RegistryKeyspace::PredicateCode => {
                        registrations.predicate_code.push((key, arr[..value_len_limit].to_vec().into()));
                    }
                }
            }

            let header = PartialBlockHeader {
                application: ApplicationHeader {
                    da_height: da_height.into(),
                    consensus_parameters_version,
                    state_transition_bytecode_version,
                    generated: Empty,
                },
                consensus: ConsensusHeader {
                    prev_root: prev_root.into(),
                    height: height.into(),
                    time: Tai64::UNIX_EPOCH,
                    generated: Empty
                }
            };
            let original = CompressedBlockPayloadV0 {
                registrations,
                header,
                transactions: vec![],
            };

            let compressed = postcard::to_allocvec(&original).unwrap();
            let decompressed: CompressedBlockPayloadV0 =
                postcard::from_bytes(&compressed).unwrap();

            let CompressedBlockPayloadV0 {
                registrations,
                header,
                transactions,
            } = decompressed;

            assert_eq!(registrations, original.registrations);

            assert_eq!(header.da_height, da_height.into());
            assert_eq!(*header.prev_root(), prev_root.into());
            assert_eq!(*header.height(), height.into());
            assert_eq!(header.consensus_parameters_version, consensus_parameters_version);
            assert_eq!(header.state_transition_bytecode_version, state_transition_bytecode_version);

            assert!(transactions.is_empty());
        }
    }
}