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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Rust Monero Library
// Written in 2019-2023 by
//   Monero Rust Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//

//! Block and block header structures.
//!
//! This module defines structures for manipulating Monero blocks. A block is composed of an
//! [`Header`](BlockHeader), the miner [`Transaction`], and a list of transactions'
//! [`Hash`](hash::Hash) included in the block.
//!

use crate::blockdata::transaction::Transaction;
use crate::consensus::encode::VarInt;
use crate::cryptonote::hash;
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use std::fmt;

/// A block header containing the version, the mining timestamp, the previous block hash and the
/// nonce.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct BlockHeader {
    /// Major version, defines the consensus rules.
    pub major_version: VarInt,
    /// Minor version, also used to vote.
    pub minor_version: VarInt,
    /// Block mining timestamp.
    pub timestamp: VarInt,
    /// Previous block hash.
    pub prev_id: hash::Hash,
    /// The nonce used for the proof of work.
    pub nonce: u32,
}

impl fmt::Display for BlockHeader {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        writeln!(fmt, "Major version: {}", self.major_version,)?;
        writeln!(fmt, "Minor version: {}", self.minor_version,)?;
        writeln!(fmt, "Timestamp: {}", self.timestamp,)?;
        writeln!(fmt, "Previous id: {}", self.prev_id,)?;
        writeln!(fmt, "Nonce: {}", self.nonce,)
    }
}

impl_consensus_encoding!(
    BlockHeader,
    major_version,
    minor_version,
    timestamp,
    prev_id,
    nonce
);

/// A full block with the mining transaction and the commitments (hash) to all included
/// transaction.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Block {
    /// The block header.
    pub header: BlockHeader,
    /// The coinbase transaction (mining transaction).
    pub miner_tx: Transaction,
    /// List of included transactions within this block, only hashes are store.
    pub tx_hashes: Vec<hash::Hash>,
}

impl fmt::Display for Block {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        writeln!(fmt, "Block header: {}", self.header,)?;
        writeln!(fmt, "Miner tx: {}", self.miner_tx)?;
        for tx in &self.tx_hashes {
            writeln!(fmt, "tx: {}", tx,)?;
        }
        Ok(())
    }
}

impl_consensus_encoding!(Block, header, miner_tx, tx_hashes);

const CORRECT_BLOCK_ID_202612: crate::Hash = crate::Hash(hex_literal::hex!(
    "426d16cff04c71f8b16340b722dc4010a2dd3831c22041431f772547ba6e331a"
));
const EXISTING_BLOCK_ID_202612: crate::Hash = crate::Hash(hex_literal::hex!(
    "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"
));

impl Block {
    /// Compute block tx root
    pub fn tx_root(&self) -> crate::Hash {
        crate::cryptonote::hash::tree_hash(
            crate::cryptonote::hash::Hashable::hash(&self.miner_tx),
            &self.tx_hashes,
        )
    }

    /// Internal function that serializes the block header and other necessary data
    /// in order to get the block ID or to calculate the proof of work.
    fn serialize_header_and_root(&self) -> Vec<u8> {
        let mut blob = crate::consensus::serialize(&self.header);
        blob.extend_from_slice(&self.tx_root()[..]);
        blob.append(&mut crate::consensus::serialize(&VarInt(
            1 + self.tx_hashes.len() as u64,
        )));

        blob
    }

    /// Serializes the block into the format required to calculate the proof of work.
    pub fn serialize_hashable(&self) -> Vec<u8> {
        self.serialize_header_and_root()
    }

    /// Compute block id
    pub fn id(&self) -> crate::Hash {
        let mut blob = self.serialize_header_and_root();
        let mut out = crate::consensus::serialize(&VarInt(blob.len().try_into().unwrap()));
        out.append(&mut blob);

        let hash = crate::Hash(crate::cryptonote::hash::keccak_256(&out));

        if hash == CORRECT_BLOCK_ID_202612 {
            EXISTING_BLOCK_ID_202612
        } else {
            hash
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::consensus::encode::deserialize;
    use crate::consensus::encode::serialize;

    #[test]
    fn test_block_ser() {
        // block with only the miner tx and no other transactions
        let hex = "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000";
        // blockhashing blob for above block as accepted by monero
        let hex_blockhash_blob="0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201";
        let bytes = hex::decode(hex).unwrap();
        let block = deserialize::<Block>(&bytes[..]).unwrap();
        let encode2 = block.serialize_hashable();
        assert_eq!(hex::encode(encode2), hex_blockhash_blob);
        let bytes2 = serialize::<Block>(&block);
        assert_eq!(bytes, bytes2);
        let hex2 = hex::encode(bytes2);
        assert_eq!(hex, hex2);
    }

    #[test]
    fn block_202612_id() {
        let blob = hex_literal::hex!("01009ad29fa0055da0a3d004c352a90cc86b00fab676695d76a4d1de16036c41ba4dd188c4d76f4609004001b0af0c01fff4ae0c04cf82e4c88e01027c09e864f1cfa7efda0e9509db7d0c9be8b6dea45193fcd3e18bb459ad3979fd80d0dbc3f4020271788387a5e1ef4e0d0bec6b9ab712b059dca4ae8f0c5d9c4f6d50c162fe064080e0bcefa75702498dfe48dc12cc37c708d5926b2d5e21b49b14ed981357a55e18f0d0979167cf80c0caf384a302025f2ab4d32c3b57199104c19c3adc686d5b6461e0b1e98079657f55b29c6cc9272b01eab699e73c4127f7dc148a0bed5aa01b494c4623b331fc0d09ccd7234dbfb13202080000000014f4c7cd810417ce4c8feeb82a6d6adaa8a89724b32bf4456f6909c7f84c8ce3ee9ebba191636993cde84ff1696474e1437279e3665f4e69598ec7e1100fbf00c2fb0536228bc35e23f40c3f0f424daa94df58788328192a6ad75f8c7d0d4d6388cf560acb1709744c976b1ccdae5333e1a97cb1c9a356fc495bdb4f59855d12492a5784b50e5f56bb52a57da48146efb92055ccad13450effe19f7cea60fc086a44d0094e5da3854e639dfce55146e33abdec99918811858d1027a2fa100ade9977e236b0a27ca27510637674c373e463b45170e603e5e38171a3b022f499ac44ffeea4ede9e508ecd9e62cb639263522b6fbb5eca94d4b9cbb0de8826f6b9f2c04a9d085b4518c028137de922dc80c9ff037839ebe29638b6fbdd26ed00c29e97f6abf6b9af2df7e6a542fdcb67c2fd590842f8404fb2a9ce032acab67a3dcd7afe034e0a5aef286783b744ffa547959125bae87bbe89d74d9c7e82a9ce21f2e9bd1aba31e4d10769784947bedb6f3b551e1563492d706a5179808deb348d7a698cc920c1cd3d31c389a5f74b21810894b8bb05a35564d14cdeeb4198a2a3912f7e22552b549b9a1700b9b7bbf96810725969a8bb391c8dcc6b37e225b5a2cb8ee9839c1ffa0c35787255d844734a0f14b0cf888a208480ab4097930c61fc0f0ea10210111d0e4e8226a2f722768e4ad491f66aab7dc87ef15e17dc47a6b450f9ad8400d4dd7f5eeb93aa72c6e8665168f4a38dd4e8d8fe30b373241666a516753f97f74c71fdc7ea5deffe526e805c7fc55ba0972ac0fdb8e3c28b408dfcaeaa147128df5e470e6ea166381ba2c38b21f25af3e349c8be32311820e21cc9fd741d7fb93aecb65e3dbc36a4c756d742b30bc1877b4df8132ecc899608a4d96f1fae0a34b9696b05f723620a7a72e7da9b0cb43e1ab842dcbc17959247d944c74b9ef380c51a65197a693491d001c540e7d0d4fc873e01d489a3e55ec7afbf07574b6a80bd013516d0d901e6098a302599c60bbaca7d494c9deb90f1c102a3c8ef0787e66303f3c7e9cdfb1ec719e943a58db3f9a3748f6b574c9d362851ee9e93d6b8666ae7480ec78eaec6075dbbc1f9dba9ed6e7719cdd508ae00171fd8bd7cbeb922432e50dad4876ea7314de6bea40fa6eaf63d6ae45b2cac6ef5b821603780435591eeacd94f74b75b8cf6e4256cb790a8e7188580388b2a9d7a9f38f2ab3e7ba7c431afff53b9e0aa6fc2bc912d64fb15ba9574b626ed50c66e7de171d09dbb9b69a3f7997930eeda64ac6db91f2a34873a2248e345c2069c5ff0ccebc1bcca0d838f4471c9d4108c96f4db7bdeb68b27bc2f30293d027a7ffbbb9206a86461b9ef7522c03527dde6024d379f4cef3090e94007f4c966d84900cf86c540b2ae697080d06228c9cd7fc0d51b685f3ac880220fbcb6f20763b206beed5c570be319363295421119b253763fd5c31e312102acba946770125b49b1443cdf2d1f67bec8fcd07eba8a6a47f260084da727ca8e3ee60aca73d84a0d11b9947a16a071aa76960ab34a1eaf45c9a46b397c1cb3900d5738338081a7fe613d39504072e27306890dea5acef11bf404bd55f0f90e674c242a27b80a6d524313c180bb017365e89073b66aea877496d27dd61f9231ae835d66a0c8beddb21fb953caef8b1c6868ad7f5ecdbf16082b0e5464a001e848d89eb7bcbd49a2a7e9c3e7c9f2900cc38b950634c7ba8b6e1fe7b504b99bb69e39f5f940f3c0870e992b03ac3f2523a2a0961c48437efdbdc2a0248e33ddd07da5634f944c43d214cabc6df0287fef3adcbc7aab9f217fdedce123d1d59aec4348bc1082ee1155bfc7e1d855f9a0618fb6b763831fbed0a7299ee0b9c6bf42909d18fd1ce820456be20957dced02b5ccabd14f5f36277bde23897bd24f1a92afe42c117ec9d9113be370950e04b050a66a5b82db1fa094fef7cd453576998f7d39edce77ea31ad3ba846a30a1244d67e26e84cfd8f79b260109021f2c85373173ab8b59bad3f55ddea4bb1514c0c1448536cb6c87380c3606905547ec0e769b7e578ba1340f22358b2bcdf5f60e8309cf527ab5008efd0ee0d869ace47413a08aeaa91015b8297f6deb24b1fbd60238216dbd99fc37cc390bb6cc15ea8a81852c77b412189e3716da93b326e6971070e64d5748615b7b56f3b9d368bcb861e22e92787053dc7e31f158020edd4012f2ba4563ce9d40b49564a840a6286e524195c44c7638d5ab60c808f9fd49ede97aa3c0aa86b0676590262666c078b1ec62258bb2951180debe35f3c7bf04bc0f0511b3f06f35591e399a640249433c306d8e185f07a3e9a7858b1a4dd566a292cc6bad65c6b43f715ae8bd1d108673bbca7642285555ba057093526b507c3ad9c4e30c1d0f80a7059fd4a82815e6ea68ed108f0e54bbd5339a20f3a2eec88f44ab9b9bff676a7aa6847a61cc97ce4856fcf2bd3ece00dd0dcaa2cf6bbbe82acd6ddb3e23ab1ddf853f51f01b404c42a9f602a5013558ce942630b9d8ea9f6109475547bcbbcd2b62a00c15c3724535b4a3d97738a1017fff4d3e2b6609b37396dc9d29f3ff5fc0a18838ff666987788f034bb958fc27637aa30603f12a28e64a87f2bc1cbd66b0ad6ede1124311744750f7ea0a1adf0e4b2ca81ba233c72ddb4d1d5afdde396de5c982d8d2030685cc51f01de75c34091dfa41adeb32bd3783501deb616bf4002e5a5c1d1d21cbb3ec879454108b2710030b898d9f139e09a247853630693e27da04f95eab3c105fc5f9076243d43aec3bedcd83150a5105430d1a8d5655180dcc153ee26173938676fd540dd748921408da110d1962dfc8f9b693540263f2e9373e12c51d0c979c5da1c70f761b8343c82876f1a667e8b5ade37bfbc0d4d48195a8359b3de164093d0db2df51a65b379aba2cdf170e5a05e6cade3d1ba6076371e1920af1e6e14fba0b690b5bd4d5f950dc9f828a55e321243a43b8b268af973df48c1f5c80a9b95c241a6ed27779dd58b0c4e9b8cd50de7ae5d161d3ba879515893f2fefd916ed8bbce571abff0ce53e7e25b82ef2a9628a69af4b39546e02aaf3f5bbc1a97623e4467514135609c5fb051ca0762382ef00ef0202fe282886a263176c04b9f58b28c11d326ed54abddb841c29cd428e2d3afe24d01bd4a0e630f8659ac95ca4fc3a57d9b2d35786a2aa269a6079f32a1f3c80bbc59e041cd6b7f232257001a391ee342d5bed8d2e641e284739d98781527c507eb34fa142203f6c2f153a3448b18b20314a79c3e64425b9c69cc80b899c2d3c143ecdf9a7e72e0a01d5f59dd24cd0f73d91c8023fd3d534abe1f50082c900346d38e69ba1c7e877148e18e7fbf0c900b4987284b0702cc9906066ec350f6b51455b8290a70f599936b97d9682c490d0f74469295912e53de9ba36c3311449df36f800b0e7eb4f3a9b04b04d34cee48dd91aaef830e90a041869d4526041ad3f35d20cc5b22cccd5a355f623dfa976b5c8b01e1b683520914d0065a384c5c693f10a473998a85dd8eceec30e0f9e25286a38fbd866190477d341eddcc2dcc04e1410c578511e3b711bbf65f6c475ac0c03a63094d6650a348db3020c2ce644f568ffc83a054f48698eab23cdd88ea309fe6608d3a94dc5345e7b196723ef8c16e7b8657ca150f68d99eb813b85202a6c09f5884aacee58729afbfc3074ce07517683fba17a8354b1b4521b6cf2033dd56b2594b5ec3f547b97bc7ab27a4c3ee2d7eb88c4f477e0dee388013243b8ba5599c00f60231e3f4f86c6921698f5a7a02de861d231a745ff90ff72bb9ae2eed18ab5746d78aefebe814a780d306dbb7901d471f66f0a8b97add4e8edf8f266d2d90dadad4120f5910992dd5fa49afd346e6794b400a63e35d410f175b25e865a225ae7a85d1c5fe1d9467689361efcd49a955e7998d5ed9f1bc9e1a4338ff7a8d1a92b6b664a8b162308de29a13e2f7ea83d4333dd623cd245186be98edde0a3687381231faaa4012eaba0fa49cd72df73d3290d7ebe6c7cee16d24c9349f0c3b4ccdadae48967266d0001f5115fbe44dc5be0a30b78542bf3f91043983d6e8a13f4c44de48eb3e3d9e4d32f5a8aa8a3bdae19e61890a9ec1e0d6bfc51641814e770d6c168f1c56d117fce5fd1d8154bfcc2ab8570e3f41bec6a959f700cb57136b9f86e468d20fee20b1ae2dc0b408fad342afe13ef4a9274c47f6e0b6330231ecf4caae072ce83b4622bb37cd886f0d61544a10a14d341838b77a3e18cca63810c950e4983613d349e14ce35d2df55c66b8871089a5edbbe5515ce8ad0ca49790e2e59a7585c67b60cffff543e45c8e3a049a2ed482573fcf7a96a1949e9ca2553c9c650e04cb5338c98766919381b1b7cf76f21afb5f402a4366c65311b8134d8ff41a7566d1628fa8c937c0186dd7f03da54a2a0a873b9ff9e837d8755e569be510c6985fae46233ede308ae3f6c2efa5ecacb63e63097590b476e9f290b0a7c96ebbe0309ce919c138024e5b613e5b209219b6768fe878f24460832c6e4bcef1feefbb88c4435a57bc06b8fda5e321be4668dc1a37e3bf6d53412502fdd929157113816ca9c41f6c9b5dd1562eb4aedf47e085dc6f61f0ad9384a615407d39e909fde96ce5b5164fb31f67b17259d150e2401a59222d4cb80fb1cade735c40cd6d45afc35d369421edb7228c417c5920fb9e2263981448f2595575724c9e76356c7e27907ab35d5e69733e45ab1cb4aec2f9bc724c7ce9b56947f12390162dae5ec571ca782d9f59aa56817e8d242abd86bd9322702c7875e57a212027fd453fedeac75495a719afdf5bc0c23e7908181353a5e71139012c0f9765bddce4735491b919640f049243c239dad9aab9edba051eccb6dea2f3caaa0a0acbe7bb7e6d0b26e66ad587f3e36180afe101d3961e1f73d7ac3e617dbc8135bb610139a2bd53f0b3e4ab722c93ec13e6b5274c089ccf4afffca4fe3aa34b1b812d7ce6e03f9410ad299aadda5115a74ca9c96e2aa735b4402f2b8ec0ffe207c6c3c0d02f8f0b73feeb27e84a9c0a6aa7fc571d72bb4221dac24449d9d67d3ca5dccc443a744a5c3b272fa74a42d348eb7809964dd8e9b57dc41159825dff888371ad4cf8a023676fff9468b3b664f78190167293d616ba10e985bf5aa566964722808119fdbcbf0dd12675c2a1bcbf83ee75e54a46509d3254177e3e1d353a4472a358c8980b52bf0c5e937173ad4627155027b6ee18c38c123a5e200658021e3dd3e0cc8b00f866ab62b889cdb3e511cd145710c6b98a8c8984b3311d0c294845acf13b39fb164c725f938cd52ba3fe0ad64021bd6e6e4404bd830e8dc39a90287163c1f594abe1933aef3bb059921667bd8d4dec7912413b7f8ebe362a4b1485e8566740fc030764164db64ecc6d970f1f51978c407aee205cd65bc6533bc132c4c31d277a5de2229cbddd841e642db39dead415b5c04549512177d8989a3175dd503d5d7cda8da1b68cc4624991a4bd083fdaa6d801a9d18a2eb2d0e8c1af378884da6d8bc6ee4b1def4b082e881b7d9cfd6757d9a8313eecfe692b3e935a3b1432d69757a0c8f43783d598fc772d7538d614ff2d1b3752f130bb9a3d7ac04e0526a2db6f24664250b93fd8799e737bf62813b78faac88847153bb9931ceeb6f2864bbd63e8af0b422eafd97c700bd35de7366ed4a09ea799b980f7464f6f13ed095b08e9aecaf471085fc2157510ceb9863522c21c43f1fc94ca840ee219f8e9131e3c1075a9b10d3c16e3f514e88d64c808fb3a885dd15bdb4dc40ceaebdadd0408cdf455a9cc17358b17fde893157ad51d75efaca8c29555a849601534cb25e6f146802523801e0ae4183ab1d3be60b74386b2463e26d046dcd0a1efdb689a756da8d7c28d5ac64193646e591039c8e1ae89f7552c48fef75fe7c355915423e19521133454001eb2a95e8aef4d5a7220d4f0bae7a8f6419b850a38fd8110a8940937cab256ac8d3a1723f278df52c5856afbe397f35ae6a5e2f71bb50b28c1520be29c9868ed88d7eb3537cbe14a92938adac92d01c388cc4e48ff4255214aff6d5e00fb3207788bf7aa8e757268924969d10e8aaac3b0dfaf23c3dfbc3f91f65aa325d5b6d514f0d680ecbad032e25bc3092366dfa041e9ce0be812e1c84b17bc613f4d6a89b242990826d30b7b8012d10e527024a5186d587275e8c55df758ea43e4c4deb8582a12a6975b405c85566e467537a166800fb2ddfbf78be078ba9114a2ef5cf8d01cb0d35b0b95a3fe329286dd8b7d6c745b6ac16726d3fac28aa58d1613ca87f6d3ef052bb9a231a6a708431e94c5712e92e2c6540dfd880eef0b4380f6e74903927f700646401fcfe6f2a11edb949f946366842ce7b2917453bc9ef97f5bc6c761542b5a642447f5e29a8d306473e9031e0bde6a26cac15eb6d0ede8a238853daca575a1bf1f13b28ebd965322b5a07660d7bc266f38d66a9b485123e69467b7abd10c59e9afa29b5fccf79414a5cb5dd7bef16833f26af2bba0ca53338f5940dce4cf9772dcaeb48abb266643076df153042fe83854950578208cc41d37355cee2d8464998e8ce8d9c87701215c58138820199faaa56b540ec0274d2f02080cba82be5c8c00ca088b19e934bf07dc525d8e416cc82e85809a884f87b819693a720aaf6ee1cbaba6eb0e1f9390a76a84a76ad19ebb77f244acd4f5c3dff8abfd2e6ad308bd181a155d4f1f68684c5971ead0aed7220d0e7bbeb55159161de3f37a888a9bbd27c00764159ec086fe305f3d2b604f0ed5dcc3d0fbd80806fc026277ccffd3d4ca79f06f72fdf6dd8418318c7af1001b96edacda0675df665040bf04f701d4fd14531dc1f779f8f1e24aca97db18f0230b92bc581ac454d4c1e2956f27f7875ec40b0b797fbd16bc7c405977b4e19b6ffba8450c8ce21d31c77834cd5622a48dcef2d48710a5d17b893563fda29408e224649fa060188130076acffbcb390cae5e8a1c1aa45997cc3e85bcb7c0ac1b27ea009de1a02e3da71c13dda31b559e7068e93fd17a49d63f8bb8d7b7d53af0dfe91de6f479a54fdfdd2502864bd3d896bfbb3ee8c73a5ba89b51b5b41565046420c2215f8b4d6aae1a5ac3d76d5e6531677fcc1dedb7ee79d808f3f95a39e11ed93a92393617d7bf89c78cb8a87d1731da13d5347f9a48e65dd5117e9face756201429c08700dd0e77a63dfbd4f7c42bea07671f26653f1561426710a7cb3165359b842745ecf7c0b1691330e58f9513008ddb87dc812ba663dfe1a1434b6233f5d95db95342be5e368d6ee1d9af821f84dbf672dcfa3910fe0919c2bfc4cd11ed6a2d74e8ebe6508287a9b116cdb33fb945c92ac2c526c11c6c1dff8d739d19e91b330691c0a7a482b0261a04d6b6fd37dffd3334a98fbee0d45a8373291b9c6fda29cc8440ff5c45f183727c75d390b8149abea607d7eeaa49ad45ceb1eccfbfd534378fc7e9f2c293a6aee429c0c2240ce6f0e0349927b37f79cd9d9c71c8377519bc7a3e89564ec3c12f2afdc8a2af6ef221b383094f1633b137b040e3912395bbdf664ee790467fa6243c9e068b27e879d431a002fbc8463ae3f6514a88a53cdcc1bec4aa439df57e084c5ca5cf507fd720301b0e543108a3f901d53234bb3d5b0b3c0c33998dd580968bd47caf245a30a15e7aa022a5867af095300286bbb3e9c01f8e7a286be7dcdf2ffecf87125c042e8757f6e5d455d6f3dcd055310b63964824c5c7170d313ef8e42bb5cad63b8e393a8c34ed49001b64a981751a3c967ddfd8f3a36f15f5354e840a2a899682e0e47af947ff0bd14537645dac16d0ff066650f8059fc4bb2c6ffdf98afc437b95c657d2d2959930d9a44f955455f0400cecd809fb99fb9884d0103c1918f2ac9b288021c2b6215d7c85c6460946981f21904a38d95b0cb2be73aba05991b643067ad949b95cf65782f4042f73a2cebb7c8e25b155d0115702dd33a7e10099c0541423f0528877cb0cc9401e0a561a0c3d1fb65cd40647dacc2bde15fee821414cd02812a2376cf635e83a434f076823d8d92d1658a3ab25fad0cb8feed1aee52e65ea64c3ac956f6334ab59226e8dbc3ddbd8ed563b7da0ce3f77775739d39d4ff9059966c15eb4816cbb924135cf9258d5e4354dd584019b00ba654aaa378ea5d592d732280d9aaceb72845340ff93fcdd315ac51055473d3a361773b2c508fd1160337139dc1416cdb106116d5eb712117e1da488487520fb12c0aa3d4ecb736da96bda650fa48f7b24f687018a78a540173d69906277bd2a8d2a6647b89f008bf3f66e9da3eb771ca30256fcb6749875fee537fd83a12f6bdfb09001ec68dc7c85feb48f76e33764031737668fd24c1067e4d85577b46b9f2c53bb66828707fcebbacb27ff9bda1998b608311e6a7d1edbf1c83aff291257f03b4e33bbea150a370f29b6b5044a181bf0d645087fea19b5e2f7337d7a1dfb3fd5e2db332ed03f023b7790ff70cd3038b55cf358d38ae2427f93178a7657d8545f004960e1d9376c37890309453ad51d4e9687b9c7c644ec51cd15fd1b6cc66723f7dd4e3827614c976a3f681e37c3f567f0e43c9af0a786d8ada3e526de39e3e1b5ad759d8173a7df158420c4ba80409ce88a9e61a186b06485186809736ad469d2acdffe2d2c2a4aa0c1a47ca99959f87ea403241d4aa6e145b94230cfdc7a91628bb1420d377c55ff182e6e951903b68ece50a747e767907efa2d6163d4e9489af05b9df8668157e4b5832eb35692f132f82ee408ea14f4475ef4d703e9b670aadd45e5681f33282c9d8d5ac27737cb381f86772265f7073a9f05e6c36ee81f3c4d57a531d330f4eeb76421e3690ec2e48ffc6817ef65a32079f5a30e81992a8df74cb8c708f17b3661c4416f21328100ef4c1cb2702d76079d364d4b71129c18c2c413b44bb532af63640dea679e30e3421cf46598d5bc18b0e2eebb5a326716c02987c9ad403799f34952fa352e23dbb95931df1123e32749f4bd50700b99b352b5820c4af578719dec682e740af7b0d8a05019b80efb7d40f2a083a81974d6494d8d7cf0b9922cdc63a55417d89bb07e92e79304232e3536b981e1b8c109d83d3087c99d1195aa717003a756f2d917372c5135cd33e924a32044e12a1440f85ad7590140cd516f6623da309d802c4fb801db02be7d486635c7b9a2577a60b4fe64f749809896af4e0c2007cde745fced03a45d618b7f1522245f4efcacebee8fadca9eb9060eec0032f33f7ff5de39b8a76797a6b9142f2a9e11d8b78dd7a8ac5f0ca089d58f06f27a6f3435147911981dae7d2b806ececa0e572927ce1451682a6bcd8ffa3d54996b125edd2a1b4f91eb97e156b6fd4b23ce2c1db4d9851a5218b26991cff955036bedc25dfd04f3dc056b9ce6f2d87692240c294e049d608929be850bf89ba3604d1af063189becce88cbacb8cc56a9592a20c11ca0b512566c8c9927e86b0ff7ecbe8732b71d34ca4634695077dd27ccdaae2bfc682f0e89610543ecb3dfef42a0ebfb06e029b0bea886920ca35b2673982ac6c8e137f55368923d17884e3d50102352822a7959a6461761482e0789a97675dad23b01ee2612eb4ca47767ad83b868d1cf27b5e0da32914c2207c49a51d12fe14905d86436ed604c6a009abb888b0f442f51e2f49f0b94d32e0571d4b7556b62e5c0d1a91066c94ff7da8500dbc84e96d00a547132d6268a71f15f0b387a8553ca3cb1e7bc60b33fee1285d226266378415f181a37ef2833118304cd0e505c229d8e6d6aecba8c35859659a2c5f4f90ef9f8101072e93736f48582a589f3b83c5c55c55492172904ed7bc87a576a35966134534361dedfe4b5f1083240c49524bb5e82da3e43052d199e6969912107b38ca3870c0f15eb81595286a3d0e8d5ce54b0c5a9eb92d9b378a9f80b931afbac2a7145f7e74e7bfba306da065f92bae91660652a69f46950625ee8ab755dd59d45c21902f98f4ef6a9e597d4444e7f53bc030a96cb8d3cc736a6022ca13518b91e0526e1b0ed4c0e1e78a10f6ffea0cdaffdf6de62d8a57302dfb4af2216a4df96f9fb629d0aea30ac9ba40c3e6ad8f81ab36c2d004a4ff4d8fe9c0ca8eb6a90c5b90ba05723c18da60d20725d54502529f2486761b4afa257c67ec29f627877f11cab3825881ccf74bebdc49a23ebf6759322660332ac8e3b8a29b2e48c210596fa90ec1bd1e235cf84edcc90b29b6b6ae3834649fc8701a72e18f3c25408e55e53ff7a6aa81551eb295a939f72f9f2272120513df7f3942c0761fdf42f2078d8fd1127905cd94ad034b4407383f54301838da88fd7b198dcb59de0cdc46dbbf449252b0b252bc543e71a04fe387aa96c512a8b0dfd8c3114acd1a6166d7657cf1c44ee5474d8d3b3866e571d1ac298194b80ac92fd16dd5fc36528f01f99d0a37a7a1c7273f8277257417b1c4053668e283446aa62bf7f5320a19247eeaa04be8483f795bee5c44a2239d21a67071292d3c9a04750343bc23f7c73583e678a352aa965d5978ca0f5385dbf83255bc52401c1859fed2295b8c2a28b6f31740153db9aefd2a61ba25fdd574e624474474a9593c321393c6bdf11dd6fb10df20a1ff3d742f4f29c06e2dc7b3517cbc9fba2758fc72b3f59ff974072a8ebfa78c86eb166a37eaff4a9729681788e6b3ecfcdac6f67dcc248aa7fa295a08f1334387d269ff647157e818eef40e77e037fa32577fd20cce5876fedc6800c05da5aefa823896a014f10cb2c1a5e70a7d8341e8cec540f3ff9fef55af1dde44f6749da932b62ea700c6eb5e1b32735aa0095e0da1a0db4505ef8e094b40815c86bcd540ef33410937dbf8e6b0f4714a0bf437b296b213bc42a41d249265ec531bccca8d555aa3e5d438a86971846f2d07f21192c604eaf21b25f8b5c695e95386e2cdeeaa6eb0d36a13ee67e390c0417432a89a614bdda122680930401f4c561f649f52e395f6f45f7ffd973d696c631c5880c9d800d102f92f5ba079d162a588f0fce4200a54e36add594cfae9e42d1590e96055ff4ce7d054d1ac8c981871d003793be4a129ccdcd6a55c1c5f5f9269923efc8aeae9fef6691bac92987e27c85bb3c033ac8866050ddabfa464f2c16254b946fddfd1349cb2de3dbc3d03c7a947f3e17c8266a5f37217551ae4f31326c16ddaea0913838bc604b45acc27847566f1417d085b5648385ea7af7dc3e6e0016d6612ee56a6f6d7dd33154f1a1026c4a9e7c031e1f32c3399fece4100ed37c519cff7924b08ec48b6eb506528c724f47b624ce6b2a4825738f3ce23fa9535255f911ab8706d7b4868c1b13190aaae1372ea7f2a12d52e12b9c431f524d1aed43a466a56516ed8e936a904a0fa17c3668365d91caf18a30d648ba3e54151209eda85675adec175a29635c6d876a01f8dfbc2211959cdc2bce9fa65e465b429b02503e37a6ed066a4e639b13f1428a189562311be8352378395405b161ba22d85849507e6e9ea23cb251e879ca8653ca847121d3eaf8a04221f4ad227ab430fae7b7ab73925c1043a59bf7af3f9b7bbc6e5d8857b3c6fcf5d509ea84e806166ecfd7da542029c5f7aa23f59944d5b05f64c030fe06ad5497b115efddfbcd11debd08401c8218c118d60b2d66d1846e64dbb06769cd43774ecc7bbfc08510ec2feeb03984732a79088e6c1e45d12179720ff40fcbc8e2f2a1d8af7cf0d6d0a676cd2e420aa80f0ac6ad2b84ce39cda8d2a47e1153bc5ca0402ab1d274363b2e08f1b322be59d0083b03f21c43db3dd4020a18e5a17f1b5c276066e3f9db04a85948da4009f42a1fb40f8c2cfe3bf55800d539059de53b57385a5e11efcff3da27b61e2eff1e1ba712d5eeb1d783dbd132a9d082d539d39a71cac2903a3ec5c0ba1fe65f7b26309e7aca3f63a58e30d2028790eb706c1d7f2ca439efddaccbcc8dfff38f8ae1bcb08d40a63910191fc5102d8ed8e3aee1d172b98f5d310940509c88e497263712f20fbd827eb5efb543464c4c8cd7c902a45b8da79d6257a885399992198a4f6019373feee605386e933fbb0283d65ec676075a60f8b9a1000144bc60a0a105545dbec201485c8e5a82e5cfc1b5ec1b5459035e73613d8bbefc35bb20f3ec337a278400132852e065f3ca95d1bae8e43fbd7d6db327310fa7fe784195d384690154af6bbc3acc2e6cbe2e5a048dc7088e6cce38ab1d4774da7dc6c85be3d1178d41aa75a11808245dba4a6f3d507a54d9a52265950514c2dad83ec0c9712018fb0102ffbad20c1d952f0893913181df09f976fab16a1881aafdf22d5c6edb9b630f49ff284ee84dbf193058026ac97521be54e2803cc0b2c7bea00604638d4dcce057a48bcb02392f4c5129d6930775132cf24cab421f36bb18e21e9e6b2512723687cede5a92d167d123ed72d4333ec3c7f8ce4ad3ee8ec5e0c532a08ff387be336379863a9bd295bbe122e78a302b2ed13604c84522ef792ac155ce88e4bea934b5c78f7be302360728853e76d8e230694bcae16264d02898bd130c44ae27e110bc2fdb329069c0ecc4e6a4a241028df6b95b3cd8778d6b33b54fffc8da025acee6f934dc9cb6123756367c7a80b605e8965a11c40d6c3943830a583a4235076a8bbb39feb499813cede5ff9183af53fb3b8528617a2e609c2142543e2042ea3e8162e86e7045848523d7ff671f11f7e91ea84924779722b8cdbf567e74e0e2304afc0ff8fee6d951f9dbbf698e14cca664191609b62277346150122c12d6e12cbe334d91882e5c0b2547d4420bb2e0c9269dd90b58755478fe6434711a640080f114ac96993a84393e6ff67eb2698da125ef920b4d7707f6c3bc78665fda43814e41339b7c5f58ac4ddccfb488895c93fc1f0079579c1b0e7af5b27bf84c6cea52f3405059932690de1beac7e3d99c224b8395874f5c18f8a0911317f43584f209eb59e33431e176789e2d0e8fd5a36eec628e66731d3665e7db559dcae0464a02962ec969d8d9487bd9c5f665b1e9584382b9531c8680aeb059103193a94f0dfc2908230bc354c5a623fd32a3a97b0bdc5303e6d0354bdded5c82a6b0b863b811a1c2627a3857cb2397b018818a51d63945389b2cc229a164c977ef794886e4887e544de3fc6f3810816b9ea45a9a0cde1d14c99f48ff92f3e7384e9f480aeb3bb248a4d7f6fe4b08727d96d282184ff6667991b377c1c744e62d281068e8791f011dec4c57697c3da49f2b79ca6ffdd7a349af87331bc80ba0ab1a5e271c7a7aad77132e6cd251413c9ff7d61c381df478b60a13aaf1c7b3652dae3a4ad71772806c080cfc8050b4bb588c1e0382e000f062a7d316135606b34768c93818b36b3b8f0d77e815c02f12eaeea0e7e23c17b6cc4cc8d03a26fa46cfa66131f2aa721e53acc0c05c5c13fdd1be84e25bc3284728a948fdc0c607244b977a147565831095af4abfb4480fd1b7042eb2820e33d263b2346a3fb613a1b6e23f254ee738e8207523baf0c0fc652d308baf362603b3650021bbfc108ff8e8aa6352f7431e18d20e8fb922c8edea4711558c4338919f255d8ce38b7e8704478216bc30fe117e7a8432ec79ec03666367f7bc1d6d9198f9b1c7621c3dbb4adde509e3d022cc7e322ef6b0e2cb95fc2f63cdbcb7eed3502b7acc07b09714e6220f421c66389d7dd289d73874f3ba47495f5e3b4c2acd70565f3d6f246c727db5d6b247d61027c3b63d8ad1c8d7bf696d6ea722a2592d2e132e1c766343d29ec1b7fcfc25a41138b803013043ade7774efd22c71c92eb34700bd72993383ba264d6caddd49c678ff859ab2e05e0bebaf2087bcbf11e1accb179178aac1a01eb8888f219c2b145ef6c4a672e235b50431a0a0c23a054137f364a4de18ce701677abeb0033e785d9ea73d7e6cce7079c99e56c4e04a866989c7f3a2b47de9ec0e34eaefa44f1369bc1bd8703f9872c0eb538ee3d2698bfa5d0c305c8d7035f182bc795cae078c176e1cd03f1fa442df2a3898e8936000906133a18b5ae192a5f1e423523d345103e6b60c03494efe9520675c0c2d8ec2ecdfed299839b62869d1907053a2e191c9812c439999450c562056438893029974f87c742538e234942ecaaad365f177c1952fe47422dbb1333debc933640e6ba5abe580a636bd4508e5bf00e68c85faf5efb9cd586746e953d011e3644e10bff0ec461c6816e8df0af56f1425d4cf6765698f0c51335c5a2990718b03b59d28d3db5d2b58e63258d5b69eeed290a26ae8916693b11fba7a52b19a1a834ae037ddb688f1b368b4c945ea50c88941bfe707f75f91769171f927d6235a5ec86598d3ec285d31f8897b97550db8a3347d20235c9cc3e6d81db8e164de5f4bcb2c0ffd1601bcda6fdd7f8457c51f06f4af2ea2a65844292c5054ddba88b29ea30833edb7a6d30dfedfd09da1d9d2cdccf31a4faf8d6145a239f49f8ec5c75c4a65c7b68adec29aa5f8c3e188e57049b040dcc4f61d076251b78c267ded6c7fccd9cfaa849a7feec0775401c2c8cf5849757aa1de923efa05840cdc057463a0d8fef20ff7ff9d1fd07adef1dcf6be0a03e1e25958c50214056e04157e5953d6502b5244481a039cc0785018c7f296306bc64aa778ffa98d779e58bcbc26bde71ae78f5458e9bbdea0316777250bb993383502833cf679559e1bda68070438bc818770a1c1b8a54ed06705434c66474afa7f5aadf762184b271a842bc7f12e03653d70339ee703fec97603b95c17d84be175bb7c77850222efdda7471101268d4ac032f6546fa1fbf5931061bd7087efa69caca3f75df522f60660dac30b9ac6969d36c7868114def473eebbac7f79a1dd510ae3e41fc51c7b6dbb18c980a4f6b32dd07f8590128411537fff181820d19ef1cb68dd041eed02b378cbad5c329ad6b9203111db87dccda28c521f747b0ddc1464811b7d9a0159efeac81ddc7f3b2b7cf4a7baca33b8f3f0f38da3abda076fed36bd6ad6f2c917febed11981c8ebd721d43b2e9d4e3a9826e58cebf1d9bcf51d1f013f489f9cf140d9ad419e00e206035661b079454f4b2f857c4a322d4a084fb269ce496624fc751f16bbf590130eba70263fa4c1bd431aed75d5b0be9ff87c26ce0818fa211dc0dfd58b8bde7e23b5fddccb508843ee1be53b8b28c783ad2c3b302f0335cbc0a4f136807fb14c4145b4c63ba4ea3914da3f7d66455b51f4e19e983cafd3f6462114c09aa3e859ce281a9738e1a4ec1c1c22abb539b2637d226f33a590b6dccacefa783d58201eafde42a5819759b41dd3787ddb96a6f468ebf9ebac1e9fa997effd95d01814e3c91b4f7b5221fb46bb7d2ceae13be9d38ff714830aa6d3e457b6ff392141f231c10455608c1b51730f90ae46c42936347dd2b4a4f301a08c45862f2141095027802553d235acaadebbc3ee595f53a51df939beadad9e46fc6dbcbaf43b8421071a37d7b0a195159f5ca8604e3d6d52fccd3f564866de915bc3c56a9052d92373e8acc1c7f52a4c9bba4eddef16928da803fe69fd95cf66b041e85e2d728916fa73af14e6e24a4c28ae93da89d9d227323e4da31c66a9fa463187792a58f0e9e139c103eb009aaa7baa4c8913305abc6d436384a6a6722bc0dab6b20a995b8a387b143265c90fe839940ca9907bec4d3bf0cc96ae21adcc8aca9542bf4f3e1d9728b1150d0a528ba54acf9e35d83a9a7579ab4e80494e5273a0ef3d8eab3084f17c19dad187b65670dd1a0f04800819f0afcd7cb733b8f65e299723356f7940345cacdf2ce0de28ef58824d82651676846ef21309551f070d27e11f86fcf7efbbfe6218567e269f86f7aa84e21e7442ef1d1198743430a532675e86d6f79ed1007b9ae52e35006ec19bdd8ac5f6d911ecb484b1e9911bac85923ccaac060a6264d5bb2b6a3a06a721fe155e48c23a0047ad187632dc8107dac20d56891379f3eb346578a6b805fbfd5b90d1e41d324e7d666b50de53299f2ff7e462f2328a662060ae7f051284e3b989a8f686039c9221ce7492d252e8be03b2971475142fe3d22f8998a40e5ea12860d0d86173a6a219c019afa548bfe4b9ce4a403e1d2b28f9bd2204a24cd0a12ed99d3e171edd89afe54c2ab1a4bd9ec00df83c1b58360e0a1129c6378e516f615ba0b4f3ac3ddf8baedb38174fffbfea5c29af9d9bc9047b6c4fef5600adab302213e111533041becb4e9465e951e336b4c0029782a704228ac92373b675479305960eb04ac081a28bf413fb92f3bc8a58d4d21b6b1be0bde83ac16b3aee2dfb222a8c6e347f3f035a1d6e9535243ea7fcfaf1b465426f987f000e75ef43892f31e024e21145c442e40586776bc52980b47c3e65e642fec6d4e623f39afcea7e21997901d52ec340fa9514934d2f0d5b8c74f638ab45b92752bf1a1d6e1cdd67fd9b636d8d45b4fcf152285b7c123cb606f8ec78a97b95ef8bc5be7d9933509b5f47c944d627ea5eabaf91906c725a088ef1b73491d579b2447f48680ca05c72f2fe398e5c9ef064520f6b906b07c08b71676c0fad1a3ca71c75e2812ebd470d29ed7031c3e7b2c4280315fa1a453760e8d0b92d5257c51698be359e6c3cfe916c220e9eb741d49f6d27cc3be47df1f9db488d0a8dd835ed1143c750f14b179e8fb5f0b451d00fafe05723dbbf7e5b931f2948e30bf789c414d06bed34a917cfc6a62dcd335a2b983acc999942f4594148b59f2814d2bdd41a5f6156c14da5eb0c06be76f6afdbea8496217be019444737210cc14eb552f477850fbe45f3a8580f180b9e157c62c7afbff42166bd5c04f43ebd9e8b954665421149c3cbab118eed74c2b29d7ea3e79c59b37c2c295219e2e8a860d9e96e8222cd5f794269852359babc557b2079f379d2ea08ea557a0418bfa6eca354a1a83e1acb3ad2607d0cd443afba8b56619549f94b1f9574526f2a8ffe8cff7791d31ccfb27f942afbbbac65d47ab2e78d12d7e9fafa706c16121f0141e59263b35195d27c9dff9e6021e7a60b5d57ccc700d266e5149254a6b394f8b9481bfa0b806bfc5d16dc6b25199f911ca8b62f9d89a86dbbddd1297b3ef8c4d1b60a67bd552a748572dedf45895ecb46fdf370f7ec5fb3f99a46394c2e6ac8835c1b63117ebcb4645be40761ef025a0cce2e106ba353da6a15840cba73f249ed866f36c9d9ae641e859d76f42512f1eee12e03d2a5e670f0438c3b4eaf94dd86ee1b94884ce95ef736548cc944f9d4a634fb165bf512d7609b624ad77bfc5cc2af701a10bb06075f880cd024d69b3d30dfb80fa6dfebab3b765ff912d4213e1ecfeaf17151699d26b7d80984362281102f30b263521fd7a434794ca9c927ef8dcfd68330815fc6a89971ce060f5046d69ba27afac95a615b01b3f0109439fcb2dc4dd43f54fcafaed31b6a1443c5dff8c20540b86edffb615b42f5c7484c73c73e26ab2d4bbe7f749298cd05a54afd814cc7389c358c92e4ca552849839579e2b2431a62ed77f069e81351b3ddae218352aec1df772823d67f35b99a8fee86e4179f4a8ce18ca992737550f0dceb02b57fb3049036eda2ad8159cac5fa0a2d2792c89114f6c40490abe47e51bd3591b796865b84719033b042fdb2964961dfbdc61da434133aa78f7b38590c0cc64a989b609435444ca125de4e3208359cec94570ca062599746c028b7a3bebbf722de2c5933370f99da0e59934bbef6c874d740655a45c14d535564541f611741dd24ba9186d780dd5488728f382b2423d6a4af1f30e967180e8685ca34773373d345e70f7f91104ff148eb89cb083fa3f7367858895ce27310187ce1c9ff3a587e63a01395aa55f49979580bdbfd3e51a674e1456107cb4d2b189029da51434067a9c7985728462e2a2319b1ac7f029ae79cb93179bdb219f133297e89ddfd570312421bbeefd3eae4123d60c5863a7c50978c35d2220424e6792144b935efae3611fbfac5a9c58e34437991a9ca1fcc4788712e4d9d873960b9ddd1349f04356eda71f0aab37dca520db9778a4a486634e1e672c38e881c1919d0035b8ee0620ca22e43586e18223e51f55a93938c60b5b742b3639e8677e197d29f5e0e45c887994d2710d1614201b68b255180f521c9d6c869b8141fb3b29644a1b6f09d7a46c401429e45dfec38401b20f3ba42be8301074787649e7e905ab135a172ab5d0ecac0383f064364b9c7c84b1b621d7c61b99ac665fe5f9e12b5f9e346ed69584e44b102fe657a996ba49d3479238d39eeb703d1d8a3b13097d9f5c2ccd89e7dd89977bd8ad8139b84f57646fb8bc1299b2db6e2c57df3896825d973bb6118b1055397a5d7a32ef834d6f02472f195bb5ba12b0f166bd3191dfa55da0093277bc8939803f66fa3cc9df3ec01924b81cbb021f8cad659bbb87695eb51b0c1bd80d41adfcba93f4a617af29197700bdff4a0bb1652ac3eb5e5a2749a5c7383a7b30f604f928d08edc336d44748bcffdaf0c0e53e5a81c13c716b5600c265619b9670e44f6e879b8800ba18000fea4cfebbfb66be7b5c5390c5eff7538049f789f910e7b2aa92d7273b845f6ba2ec250d0e896f10acbcb2272f50c54cdc82296bf95712161826ce5184730c52c74ce310931ad732796e7f90f928499060b0512ffedf27cf7d7e142134488685bcd566f5df54b7d694e18ae2c04bf24cabd236ab9d1504b0061d48477e8581aecf4f855eec1db98b2a37c16ca7fdf841545099d2fda4ce869c2c3fbad7def3b9536e4279780121c50960db03ec9cf1fa27c9c79b135eb42f88d97b90878d3c0646b4a1a1ac33207e284ba346dc3f7f6e7aaa984a085cdb36e3030ff830b26d79cce48d4720057f38432459d10174f4c5d22dff800df376d12fe44c46bc0e5d78c7b6baea708d908771d9a6f40e4821ce85e5487072f8ddd1db883a8df565f6b19e52b2b6c15c5a4a93bc1a20c77c872aec71af8ac11c563d5809a3addf8a6766b5f97b6bdda14378375cc95a8293e3659493b26f5008818662ecdec7302e4e4d74a0455621b03ab5d72baff6952a607e0b0148bc42fa97e1a316119581fec6dc597f5b8d47e1a5d6c83235ffe2738424a407e79eefae90d0d13c463f6c6dbeca771f2362256c7f6b9994832df39d860bcf85f3e772f530fab26a5b6bf79e0ce3a53df445b50458a0a8ec37fd483bf04657469e92885c06973a141aa03ec1e907a90affdc3f168dc17d57a786c76890f0dded9d0ac1c54de94fd29183b403855ddf1604d072eba9479e96b713028b7af097b83f34a0edc4b7ffec75e7b7448bc2017815ebce6b145e804da95223566eb4409e45ba25ff6f814f2feb1dc79cdc86fc1068f300ee6042199ebff10aa497ba9b86221e948b4a8680ac44684054080e5e1d6c3642cf5140bf901a9a7a5a44c05f3fd55cf025c0edfcac1dea962443b1507c7d5e81d0c629b37a1bf258135d7a8e8ddbb72da9122e0c56e506b0da2ea547ad286fbcdf5f87fc8186d35af26983576318602dfe253655e3672e81559edd128f3aeb9bfe998f4fdda5e360dd6ed8e8df498aeae53176372c07c019bc834c204a86e9a12470929d49730ea7795360a54a8046f637ea8f956352fe1a7ead454bd05fe9a3c83f0868bc451f1dbc2237ca216fd7621bd83dc60fac80fc485931654f03b1542cc3490b677c55ffcefbb9ab40a9b93eb0792f05941b9c87cfb76c3c55c456a56cf665b8ca5c442a8e22a23620cdb8f3f97b4f21268ae7a135277a579ad65c7867c865626609a576f91a9536de01a78c5c7c8de21dca8703e6a8c82cb15a3a6bec441beb68c0a97c8dc005c1135f884039c1110ba7be696a435aaad1c12d222434757b4181d6a77f8bb97e8d5d2e9db4b781c3f3515b179cee562721b574e51ff76e304b06e99cda9409792b8be55b0458f358bf00e24cedcf8b2805ff86848a07c01f8d0fa885a16b8a719cde738da238b4b7f4c0622a6bb6ce88b09b3204c4577ed79b3aed71d4ded13145ce554a75abe6a11a1d1f95eb85f1a15c9c4b539ce869fe9fcaaf39ecd0502f7a8906131b8e3cafd17f5588a2490cdf339c04328f249b08e5ad12973662e6b3cf71e372b433a9015dbd9b09c8da695a9e5ec581e460970c41e8963aad0087527753471ee08662b84935a26ecfb114f7511ced61e064671d0d0f20e422738dab820ba562ca46cbb0df2e3b725edd029da185c7cf575bc1f8329df41022c0b32c319d90c5a25a8d235e71bb755bdfa189ee7c12eecd3fe1a8d8d45dcaceceb07dc8977118f2dad352ed7d8a57d7fe02ac3454e3afcf853b536d3854c1626889704e9c05022f7ede4aa442b7831896fc50a2f370f682a99ebeace3f1e3056ad58e7964beefce19aa80135780d6b8a17ced8a223c4e7abac1d7d389d67fad90c87031597f4d2f2315c4591961ab88d6bd21f92bf17e84365751dc015a7303d7c5af762793a62f6eb0e373337aff03347b552e8f97ee6698acfd30b3f90089d5620b8f8c3e4e87c51a6d43ec16139f251bfc5c3d85f12a660f07b185fb634d5a67ee084edf58c9417c1051af7a9b37abebe06034db6c30f5c6513a929b210a3353b4047567a596618da2c3da64ba66600e4f6b53a8b4ff0f7ad7ea8d88bbda31e68d5422c0886857ddcff404baabbc50aa33838a7f77309d95e1506d3db36bac94e71e2279f416f5f174c4e0479031c91a2e2d57e8dc2764f96c08c21da0fa4048f91f118c89e10707538c89bde3ce48f7bd0e691e6cea649f7cd47c379862164001b338724f1596e537a6b6c4c5446a1c29897dfbc7d8f9d95c0b2539e3de2d12026763b88c321ff88c0622b8aa0392b4e6da98579cd2f34cb751428e96c2ddb786731d99054fab4e7a3bf042a82c801d6cfda08f8263373c2a876f2699206b5a59c0681d3c0a35f28d1821efbcf08661b5bec25e62d91f522a7196749f75f765544cc83c686c5d1647382af337c547a4f6087f6972fc41dd972b4e84247a243754ddbf0030cfb8549db9c2e03feb2fb70d7b051ae78453cfe99e0a7a72c045351682f3c4bebd5b482e261582f08c1acd197ab8b651315bfc97147661116d0cd2f251417132f09694c63ab029dafb28e7d75d9c85412f4c6e65dffcb9fb17135ca0aa28e30d53b7123a8e57054ef173d7c85dacc59d2e0a0932925b1c25c009e0037f15c7a2b861c33a1300307cc22a995a5349eb380fb2ea474241aa35225a2d73f7313ba09ac4dfb64f76f1bab8547a3aa9f9614836027c068175f56863ed89f74ac1cc8d0179ac0a6a07127486be9a4d4830f69fadfc31fe828b80bf12a3e7a5a724cc69ce5504e37dc3218bbd9404c3e4dbb210bcf874b24abfac04981192f6d545505159ca57cd3a3364dbb028fc668cf6b25571db4a8b112eb28fcec3ca590c46031eb41f5a247319c24df0e579550a52839e99fd1d1f7ea3c395e106f31e409044f731d769bc0d9ae3dca2600ab62c261b5e7be0ec734124327833471c88720ba749d7f7a87ef8822b4fd5718670b58e014985cc5a63700bbbd3e45cc167f67159dded4b805b09805a83176181efae5f8a91f081b91de9ed7c313518334217ebe6d8f7b0dbc696f1e3b7a38d768a12cffb17b29929b253a32ba9b97f6f2b74b545fe8d118f1b42234d4428270cf0ecd193442a5a11fa0f01ab37a04b91ab3d8e7f05fac7a894ee33296a5ee2b5f74a2502a39a2faa565d0d913ed75d0d2a248d4d754ba0aa836d0acd01acab1b8d083d7022b857e60ae653131976f8736d502b288377ce5dc3adfccb94d0a21e799934afc6995f6fa10ff935ebd54e7095bbebb4a5714158ed74ec42b8bc46c6b16981459c6b733f078b23a389b01f3f9f84ff0f86298803a39e62687a851be6369644c386468837c547b38c640c8b2a81de8a36d7284ac81a771c6d4ac0e1901dc574c5c0ec5fda63273f420c7acb3e3f7150d8a3426f78a9a200fcd8f96431dbd2a946cba8ca21427b29bb999e5a2203c8204f133ffb66faa280ced90afd9aac2d2be759c8b077b5008168528a4265da050822d37f39ee37d920a89a04b5820cad71f5125d92d0feff43b3552f8d16f102b930f9eedd87511a619376b8af27b48bf4cb3604d2b2d28a2e3b33e3179e96fc0e792d574490c62aab1c0b690cca9f9c65a411f2ef389eb379db286f82b66a6db40b552bfdc0039fcfc0cb350dc0c5497e735fae4dbdffabf99ba22d1eb488dbe27480c34309f43a1fcce160e2c5a923b89485aad8e68904d9e9533affb4d948dd96ab85ede03f2dd2e5e4ce4a185aceb68b846af50d299333564d56af8762bd7620a314e297ec14ebed4f7cbf6e9c460fac05fd4c460eaa6a9005786ca511d368ca32e9d0a8bed94961ec77ee57f49982c048cc56e93a508a91e50f3b7a3cc2f90e690ddf1ad4a117e12fc2fb8d3b7b4373e4d4aa563b329c37f6009e41c5594b9e9fafdd67aeaee2c6de2a16fbba9a3c0c335800c721804af7e39dc6e84ef2c69b975f11c5431a1372e231c5a8baa4f238c849a5daa479a83d1741997bb71bf0c7c66f35cdc84f40b9b34e0bc1728f1686973e7ff1094881dcaa9fd2f32ef6927e5c2a52c920222fd98c4470a3cf2341bf9c7734188a3b0456bb9f189c9d35497f4a783686202695aa53498a43ecebd383a2e914fb7a678ac92b06e828590d21c578d23d30ce9ee94c15dfaea916cde9e41fae41baa70ed5e253342e6ccc45a8387fb23afde6d7dcb218a584f6408b6ff0c5ef2742c25bcaa49d4930e454a49bb15b6a6be04d57dfa50b920dca5c7bd0a70970522b7dd7038659b2fd5ecaa2ca592918f7e967e7444ff49c4fc5426a4b630115e8aadb6617f3c37dda37c22791c1687bfe07fa1d1d2c03302c3ecbca0fb65c72db84a3aea5fe763e4ea19c2d3d874156001e89eb3bb57e69be71276996fa49d83c6ae81848d6779f1556d4aa365805c719f4cc44860d8733633d251c210af65a9e29b0a51f61cc2bf162d9f82ae22b429670a8a6ebf1a909628c53ae2798b656eb56ec440616fcad9d72ffef8c3092c2d93082181397c2c0fe516ef3b32339fcb681cb64f58084d7c360114cc4b7e0b4f7c75bd95a3ec893242223d570c65a73d2d714c86291781bb86df24404754df7d9811025f659c34d3c67af3634b79da6d59297784bfea414885d710918c1b91bce0568550cd1538311dd3f2c71edf570");

        assert_eq!(
            deserialize::<Block>(&blob[..]).unwrap().id(),
            EXISTING_BLOCK_ID_202612
        );
    }

    #[cfg(feature = "fuzzing")]
    use crate::util::fuzz_utils::fuzz_block_deserialize;

    #[test]
    #[cfg(feature = "fuzzing")]
    fn previous_fuzz_block_deserialize_failures() {
        let data = [];
        fuzz_block_deserialize(&data);

        // panic: memory overflow
        let data = [
            0, 0, 0, 10, 33, 10, 2, 2, 2, 6, 167, 175, 253, 167, 167, 167, 167, 167, 167, 167, 89,
            88, 88, 88, 88, 88, 88, 88, 167, 167, 167, 0, 167, 145, 145, 145, 145, 145, 2, 253,
            253, 126, 0, 1, 255, 2, 2, 2, 2, 2, 2, 2, 2, 2, 63, 140, 29, 29, 233, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3,
            2, 63, 140, 29, 29, 233, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
            102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
            102, 102, 102, 102, 102, 102, 29, 29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3, 3, 3, 3,
            3, 3, 3, 3, 3, 3, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
            102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
            102, 102, 102, 102, 102, 102, 29, 29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3, 3, 3, 3,
            3, 3, 3, 3, 3, 3, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29,
        ];
        fuzz_block_deserialize(&data);

        // panic: memory overflow
        let data = [
            0, 0, 0, 10, 33, 10, 2, 2, 2, 6, 167, 175, 253, 167, 167, 167, 167, 167, 167, 167, 89,
            88, 88, 88, 88, 88, 88, 88, 167, 167, 167, 0, 167, 145, 145, 145, 145, 145, 2, 253,
            253, 126, 0, 1, 255, 2, 2, 2, 2, 40, 2, 2, 2, 2, 2, 63, 140, 29, 29, 233, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3,
            3, 2, 63, 0, 42, 29, 233, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 0, 0, 0, 10, 33, 10, 2, 2, 2,
            0, 0, 0, 0, 6, 253, 175, 2, 2, 253, 253, 126, 0, 0, 255, 0, 58, 255, 255, 29, 29, 29,
            29, 185, 185, 185, 185, 185, 185, 2, 2, 2, 0, 0, 0, 0, 29, 29, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 29, 29, 0, 8, 0, 0, 30, 0, 0, 0, 0, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210,
            0, 0, 0, 210, 210, 210,
        ];
        fuzz_block_deserialize(&data);

        // panic: memory overflow
        let data = [
            0, 0, 0, 10, 33, 10, 2, 2, 2, 6, 167, 175, 253, 167, 167, 167, 167, 167, 167, 167, 89,
            88, 88, 88, 88, 88, 88, 88, 167, 167, 167, 0, 167, 145, 145, 145, 145, 145, 2, 253,
            253, 126, 0, 1, 255, 2, 2, 2, 2, 40, 2, 2, 2, 2, 2, 63, 140, 29, 29, 233, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3,
            3, 2, 63, 0, 42, 29, 233, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
            29, 29, 29, 29, 29, 29, 29, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
            29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 0, 0, 0, 10, 33, 10, 2, 2, 2,
            0, 0, 0, 0, 6, 253, 175, 2, 2, 253, 253, 126, 0, 0, 255, 0, 58, 255, 255, 29, 29, 29,
            29, 185, 185, 185, 185, 185, 185, 2, 2, 2, 0, 0, 0, 0, 29, 29, 29, 29, 29, 0, 8, 0, 0,
            30, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 29, 29, 29, 29, 29, 29, 29, 29, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 210, 210, 210,
        ];
        fuzz_block_deserialize(&data);
    }
}