Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some unit tests and proptests #212

Merged
merged 26 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8d3ec7b
Little test to exercise sha256dWriter::flush()
dconnolly Jan 30, 2020
9dead77
s/GetData/NotFound/ in read_notfound
dconnolly Jan 31, 2020
5b16670
Move transaction test vectors around
dconnolly Jan 31, 2020
1c3f481
Make MerkleTreeRootHash wrapped array public
dconnolly Feb 1, 2020
8b9c1a2
Impl Zcash(De)Serialize for BlockHeader
dconnolly Feb 1, 2020
a9aec65
Add test for BlockHeaderHash::from(BlockHeader)
dconnolly Feb 1, 2020
5289e7c
Add BlockHeaderHash (de)serialization roundtrip proptest
dconnolly Feb 1, 2020
2e3a0b3
Implement a bunch of traits for EquihashSolution
dconnolly Feb 1, 2020
b19b808
Add a roundtrip (de)serialization proptest for EquihashSolution
dconnolly Feb 1, 2020
705c5cf
derive(Arbitrary) on MerkleTreeRootHash
dconnolly Feb 1, 2020
ac95921
derive(Copy) on SaplingNoteTreeRootHash
dconnolly Feb 1, 2020
c2c695f
Use updated EquihashSolution in BlockHeader
dconnolly Feb 1, 2020
a8df66d
Impl Zcash(De)Serialization for Block
dconnolly Feb 1, 2020
93e1bb9
Extend blockheaderhash unit test to check a single (de)serialization …
dconnolly Feb 1, 2020
54424fd
Update .gitignore
dconnolly Feb 1, 2020
5c40688
Remove derive(Arbitrary) on BlockHeader
dconnolly Feb 1, 2020
393f675
Move block type tests and proptest support impls to block::tests
dconnolly Feb 1, 2020
2a27a58
Add proptests-regressions/block/tests.txt
dconnolly Feb 1, 2020
2e206fa
Cleanup
dconnolly Feb 1, 2020
ebfee99
Remove debugging variable assignments
dconnolly Feb 3, 2020
836329b
Use Result::expect() in test (de)serializations
dconnolly Feb 4, 2020
5a4c52a
Add a simple test to check that our mainnet blockheader test vector d…
dconnolly Feb 4, 2020
fefa057
Read and write the block header version, which is fixed at 4
dconnolly Feb 4, 2020
1c3c875
Add comment around sha256d_flush test
dconnolly Feb 4, 2020
ecd5c29
Write and read the equihash solution compactsize on (de)serialize
dconnolly Feb 4, 2020
f659e60
Update test block header hash
dconnolly Feb 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
/target/
# Firebase caches (?)
.firebase/
# Emacs detritus
*~
7 changes: 7 additions & 0 deletions zebra-chain/proptest-regressions/block/tests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 1fcf1e08caccdafad9c9f51c46cd24b992cf7207f55cae605a54830868527baa # shrinks to header = BlockHeader { previous_block_hash: BlockHeaderHash("0000000000000000000000000000000000000000000000000000000000000000"), merkle_root_hash: MerkleTreeRootHash([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]), final_sapling_root_hash: SaplingNoteTreeRootHash([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]), time: 1969-12-31T23:59:59Z, bits: 0, nonce: [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], solution: EquihashSolution("000000000000000000000128fd6ffe028bf9b8e789cf2bf69845f52fddda4066064dd798670fd27ca39ab1a3a363ccd3aab31a6e3aaf168095acafbf6a95f01a651d0c1b799a8c3134838b78de78c7128b4280136f544792764a739e3f152e92a79e849d87db179f8aa6d565b5c9c5cb60f92acd669254c0a454ecc0d74d05e21135f5f9445dd18755bc2bd2af041ba3f903e22ceccf729825b382e95b0d90b6a8657645395caa9041a93e904505c762f57ed042cb8e3b69a7fbee5bb8d174fabd95fbae8e7c01e8ecfe69d32d3954c8bdf75f8a292b08358a74d67a63599c9c97a2af237cfe0a14fbe21d73676a097706836f31bfac1e269e050e8cd95ffa04f24553206df40b559ba59078053a19ca20e51fde2ed9bf3a6f2d64e50985abdb59843738296438fd1e0223f1bac8525f42ccbb8ca128436fd5fb936bf5819b2469334d2082120bcdd62e6b800bb64124dd7472e1a8fc14d8e022a4195ff7be3b93deb24f5cf2c54015fd3ba008d035f57c991dae92eb6ad4ddae0e115ab4bd1f36350688d1bbe44574dcf53f158ab3d2747764b5bbf962d66570ef2b25d9cca5dcca7c692c33d0999f916ec0c4f87816797fdb7d38152110008470755ccd74552d498962297c7f99e4a59aface18dfb626bd106043932d70ccb97aa17152cf2da8d30f252cdb42883376ab048f94b376b9f844a412900a41bb63e11af1ea6cb0387646665cb067ada754335c66f499008bf836a3ee08330e4cef1c8218c0af52cadb7caf11e970737f2b684409921969e5be75a3b3328a98528f1d5af263ba33555a9df47f055f5698ce5f02827e08ec56e17bca426e16ea75474be0c68a02c22a1a095821019e293d79d2cb39baea9d190b9c07b9c2961a1b87a20ca3b6bc078cd7f928062d37b4f39b43057196079a71148ea1c5ce60c78711d314e021dc378c00a66eb4190b19b1804241b01a2bb9577ea4532f31ed3b76772e91313fd2639d1ba0e238035fc1a1dfe3f83caa7b82a9fddbbf15cbf6266ab824bcbfbdd7d8684a1002aaa96d234f5bacc124b7df180fd743d9108848f2cd38fdd96493353b31cac694fba0f502fe8410659fdf77a3774959a3579e66eebe8a190361b7abbd5f761b8b5b3fc78f4f146b3ffb08560589d7d2e12d279f7720e0b4bf72dd396d8628e1a335c2260af81198b32345da6cae3f93f734f7faa8d7a025130443260c8dc0a3640256315839570a9745f3dd4c75a3dcac9ff7b0dd081e072dd2337e3461078ba09c51c477e4e0f64bdf3cf898df0e8add9f94618993242a147e3aa7a879fe8ced8b0e6421c700376757c32c5df309d45b986ea28733a5519f3b1b501951bbad2c4d4dc823b05233a6c957bdd336f74bc803d5cb6b001681eac90e4ce1ee1766419b2f2a1024d81b03c21699d61b0faeeba6325d429c5f338de675a9c0ebd2ef75f4ccd6fd0b46232368c8b59d574be2cf1c7ea01a5fd3f8f30f268ba858a0910bee8ce4345028a6e471fce6f28b98ae64b9609c8235adebd8cca87462cae60349bc4b61a8a86071025d0f6f314a8a00cd98bd2680e19441bdc124722ddc64833d29b26fb5c34819d6315fc05e4c362dd13e3221f78e251869abdf26d728ba0896897348a07407d5822928e724987998101a6d00e39c125b7b0d22f43aed8b7f15080b4d0fa056454392466bcd27e56d7d49dd35a86e0337c0bfb5efb14e3ebf353ee57d6631437881e42ed3da531737d6266550ea4a6ced3f9b53523e6233e95a8b5fa730d321b1618cb67488f82c6fe2b17b6483910e25cc7b6df22560694a185bea249d29ac0699edba45038713077589a3b3058e1a4bf4579f4b224dbe01e1e017c268dafde88a89e9cb29f999b8870ce704275e24861613188555") }
94 changes: 53 additions & 41 deletions zebra-chain/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
//! Definitions of block datastructures.

use chrono::{DateTime, Utc};
#[cfg(test)]
pub mod test_vectors;
#[cfg(test)]
mod tests;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use chrono::{DateTime, TimeZone, Utc};
use hex;
use std::{fmt, io};

#[cfg(test)]
use proptest_derive::Arbitrary;

use crate::equihash_solution::EquihashSolution;
use crate::merkle_tree::MerkleTreeRootHash;
use crate::note_commitment_tree::SaplingNoteTreeRootHash;
use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize};
Expand All @@ -23,6 +33,7 @@ use crate::transaction::Transaction;
/// for now I want to call it a `BlockHeaderHash` because that's
/// more explicit.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct BlockHeaderHash(pub [u8; 32]);

impl fmt::Debug for BlockHeaderHash {
Expand Down Expand Up @@ -52,8 +63,7 @@ impl ZcashSerialize for BlockHeaderHash {

impl ZcashDeserialize for BlockHeaderHash {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let bytes = reader.read_32_bytes()?;
Ok(BlockHeaderHash(bytes))
Ok(BlockHeaderHash(reader.read_32_bytes()?))
}
}

Expand All @@ -63,7 +73,7 @@ impl ZcashDeserialize for BlockHeaderHash {
/// backwards reference (previous header hash) present in the block
/// header. Each block points backwards to its parent, all the way
/// back to the genesis block (the first block in the blockchain).
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BlockHeader {
/// A SHA-256d hash in internal byte order of the previous block’s
/// header. This ensures no previous block can be changed without
Expand Down Expand Up @@ -104,22 +114,42 @@ pub struct BlockHeader {
nonce: [u8; 32],

/// The Equihash solution.
// The solution size when serialized should be in bytes ('always
// 1344'). I first tried this as a [u8; 1344] but until const
// generics land we'd have to implement all our common traits
// manually, like in pzec.
solution: Vec<u8>,
solution: EquihashSolution,
}

impl ZcashSerialize for BlockHeader {
fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), SerializationError> {
unimplemented!();
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
// "The current and only defined block version number for Zcash is 4."
writer.write_u32::<LittleEndian>(4)?;
self.previous_block_hash.zcash_serialize(&mut writer)?;
writer.write_all(&self.merkle_root_hash.0[..])?;
writer.write_all(&self.final_sapling_root_hash.0[..])?;
writer.write_u32::<LittleEndian>(self.time.timestamp() as u32)?;
writer.write_u32::<LittleEndian>(self.bits)?;
writer.write_all(&self.nonce[..])?;
self.solution.zcash_serialize(&mut writer)?;
Ok(())
}
}

impl ZcashDeserialize for BlockHeader {
fn zcash_deserialize<R: io::Read>(_reader: R) -> Result<Self, SerializationError> {
unimplemented!();
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
// "The current and only defined block version number for Zcash is 4."
let version = reader.read_u32::<LittleEndian>()?;

if version != 4 {
return Err(SerializationError::Parse("bad block header"));
}

Ok(BlockHeader {
previous_block_hash: BlockHeaderHash::zcash_deserialize(&mut reader)?,
merkle_root_hash: MerkleTreeRootHash(reader.read_32_bytes()?),
final_sapling_root_hash: SaplingNoteTreeRootHash(reader.read_32_bytes()?),
time: Utc.timestamp(reader.read_u32::<LittleEndian>()? as i64, 0),
bits: reader.read_u32::<LittleEndian>()?,
nonce: reader.read_32_bytes()?,
solution: EquihashSolution::zcash_deserialize(reader)?,
})
}
}

Expand All @@ -130,6 +160,7 @@ impl ZcashDeserialize for BlockHeader {
/// Block header: a data structure containing the block's metadata
/// Transactions: an array (vector in Rust) of transactions
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Block {
/// First 80 bytes of the block as defined by the encoding used by
/// "block" messages.
Expand All @@ -140,37 +171,18 @@ pub struct Block {
}

impl ZcashSerialize for Block {
fn zcash_serialize<W: io::Write>(&self, _writer: W) -> Result<(), SerializationError> {
unimplemented!();
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
self.header.zcash_serialize(&mut writer)?;
self.transactions.zcash_serialize(&mut writer)?;
Ok(())
}
}

impl ZcashDeserialize for Block {
fn zcash_deserialize<R: io::Read>(_reader: R) -> Result<Self, SerializationError> {
unimplemented!();
}
}

#[cfg(test)]
mod tests {

use std::io::Write;

use super::BlockHeaderHash;

use crate::sha256d_writer::Sha256dWriter;

#[test]
fn blockheaderhash_debug() {
let preimage = b"foo bar baz";
let mut sha_writer = Sha256dWriter::default();
let _ = sha_writer.write_all(preimage);

let hash = BlockHeaderHash(sha_writer.finish());

assert_eq!(
format!("{:?}", hash),
"BlockHeaderHash(\"bf46b4b5030752fedac6f884976162bbfb29a9398f104a280b3e34d51b416631\")"
);
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Block {
header: BlockHeader::zcash_deserialize(&mut reader)?,
transactions: Vec::zcash_deserialize(&mut reader)?,
})
}
}
98 changes: 98 additions & 0 deletions zebra-chain/src/block/test_vectors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copied from librustzcash
// From mainnet block 415000.
// https://explorer.zcha.in/blocks/415000
pub const HEADER_MAINNET_415000: [u8; 1487] = [
0x04, 0x00, 0x00, 0x00, 0x52, 0x74, 0xb4, 0x3b, 0x9e, 0x4a, 0xd8, 0xf4, 0x3e, 0x93, 0xf7, 0x84,
0x63, 0xd2, 0x4d, 0xcf, 0xe5, 0x31, 0xae, 0xb4, 0x71, 0x98, 0x19, 0xf4, 0xf9, 0x7f, 0x7e, 0x03,
0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x73, 0xbc, 0x4b, 0xfa, 0x95, 0xc9, 0xbe, 0xc3, 0x6a, 0xad,
0x72, 0x68, 0xa5, 0x73, 0x04, 0x97, 0x97, 0xbd, 0xfc, 0x5a, 0xa4, 0xc7, 0x43, 0xfb, 0xe4, 0x82,
0x0a, 0xa3, 0x93, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa8, 0xbe, 0xcc, 0x5b, 0xe1, 0xab, 0x03, 0x1c, 0xc2, 0xfd, 0x60, 0x7c,
0x77, 0x6a, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xb2, 0x18, 0x19, 0xfd, 0x40, 0x05, 0x00,
0x94, 0x9d, 0x55, 0xde, 0x0c, 0xc6, 0x33, 0xe0, 0xcc, 0xe4, 0x1e, 0x46, 0x49, 0xef, 0x4a, 0xa3,
0x34, 0x9f, 0x01, 0x00, 0x29, 0x0f, 0xfe, 0x28, 0x1b, 0x94, 0x7b, 0x3b, 0x53, 0xfb, 0xd2, 0xf3,
0x5b, 0x1c, 0xe2, 0x92, 0x64, 0x9b, 0x96, 0xac, 0x6e, 0x08, 0x83, 0xaf, 0x3a, 0x68, 0x44, 0xb9,
0x55, 0x92, 0xe7, 0x45, 0x56, 0xda, 0x34, 0x4b, 0x47, 0x01, 0x96, 0x1c, 0xd4, 0x13, 0x0c, 0x68,
0x21, 0x9c, 0xfa, 0x13, 0x41, 0xd5, 0xaf, 0xb5, 0x04, 0x9e, 0xb0, 0xe8, 0xbe, 0x4a, 0x2d, 0x92,
0xd6, 0x78, 0xc4, 0x07, 0x85, 0xe3, 0x37, 0x05, 0x54, 0x8b, 0x5f, 0x3a, 0x54, 0xf0, 0xa4, 0xc3,
0x9a, 0x2f, 0x58, 0xee, 0x78, 0x4a, 0x24, 0x16, 0x3c, 0xd8, 0x6f, 0x54, 0x81, 0x23, 0x27, 0xdf,
0x55, 0xe1, 0xd5, 0x5c, 0xa8, 0x4b, 0x6e, 0x7b, 0x88, 0x7a, 0x7c, 0xbf, 0xb9, 0x09, 0x1a, 0x58,
0x5b, 0xdb, 0x8e, 0xa4, 0x75, 0x93, 0x07, 0xc5, 0x6c, 0x1b, 0x3d, 0xaf, 0xc6, 0x69, 0x24, 0x5a,
0x6f, 0x65, 0x4b, 0x6f, 0x73, 0x00, 0x52, 0x26, 0x6a, 0x01, 0xad, 0x4f, 0x9c, 0x0b, 0x59, 0xed,
0x4e, 0x17, 0x71, 0x2b, 0x3e, 0x72, 0xdf, 0x04, 0x98, 0xaa, 0x8d, 0xe4, 0x88, 0x8f, 0x99, 0x35,
0x31, 0xc6, 0x0a, 0xcd, 0xed, 0x1d, 0x4b, 0x66, 0xe8, 0x9d, 0xe0, 0xb6, 0x48, 0x2c, 0xcc, 0xd4,
0xa7, 0x12, 0xf5, 0xcf, 0x9d, 0x4c, 0xa8, 0x3b, 0xe0, 0xf9, 0x22, 0xde, 0x2c, 0x1d, 0xbb, 0x3a,
0x14, 0x07, 0x48, 0x0d, 0xbe, 0x87, 0x95, 0x99, 0x3d, 0x8b, 0xe6, 0x40, 0x98, 0x8a, 0xbf, 0xe7,
0xa8, 0xa1, 0xb3, 0x3a, 0x12, 0x13, 0x1c, 0x45, 0x1e, 0x1a, 0xbc, 0x0d, 0x83, 0xfb, 0x85, 0x18,
0x62, 0xc6, 0x37, 0xce, 0x72, 0x4d, 0x5f, 0xe9, 0x7a, 0xa9, 0xa8, 0x06, 0xcf, 0x34, 0xba, 0xb5,
0x09, 0xf4, 0x55, 0x4b, 0x0c, 0xd1, 0x0a, 0x7d, 0xdf, 0xd5, 0x82, 0x1b, 0x09, 0x1a, 0xd2, 0xc9,
0x0c, 0x1a, 0xa1, 0xd8, 0x1e, 0xb3, 0xd7, 0x2d, 0xb4, 0x19, 0x93, 0xb6, 0x48, 0xf4, 0x1e, 0x21,
0x38, 0xff, 0x95, 0x31, 0xa3, 0x0f, 0xf7, 0x3b, 0x22, 0x14, 0x0e, 0x4e, 0xbd, 0x7b, 0xaa, 0x33,
0x84, 0x8e, 0x51, 0x2d, 0x99, 0x30, 0x0c, 0x5c, 0x13, 0x1c, 0x6e, 0x75, 0xf5, 0x71, 0x4a, 0x5c,
0x6d, 0xcb, 0x17, 0x8b, 0x4a, 0x49, 0x78, 0xda, 0xc8, 0x3a, 0xd4, 0x12, 0xfb, 0xd6, 0x92, 0x01,
0x92, 0x50, 0xc5, 0x53, 0x04, 0x9a, 0xad, 0x45, 0x79, 0x84, 0xbe, 0xdf, 0xc9, 0x6a, 0xe7, 0x01,
0xc6, 0x59, 0xbc, 0x70, 0x07, 0xa9, 0x7d, 0x0a, 0x90, 0x02, 0xb9, 0x45, 0xbd, 0xec, 0x45, 0xa9,
0x45, 0xef, 0x62, 0x85, 0xb2, 0xcd, 0x55, 0x3b, 0x4c, 0x09, 0xd9, 0x07, 0xc6, 0x27, 0x86, 0x3f,
0x03, 0x99, 0xe8, 0x72, 0x5b, 0x4f, 0xf7, 0xfc, 0x59, 0x79, 0xe3, 0xcf, 0xf2, 0x28, 0x14, 0x50,
0x84, 0x48, 0xef, 0x8b, 0x98, 0x31, 0xc2, 0x85, 0x95, 0x93, 0x33, 0x39, 0x6a, 0xa3, 0x62, 0xa5,
0x1c, 0xf2, 0x05, 0x09, 0x7a, 0xfa, 0xbe, 0xc1, 0x5e, 0x41, 0xfb, 0x6e, 0x30, 0xb6, 0x22, 0x37,
0x4b, 0xf5, 0x8b, 0x37, 0xef, 0x9d, 0x1b, 0x24, 0x1e, 0xad, 0x5a, 0x68, 0x2b, 0x98, 0xb6, 0x57,
0x49, 0xa5, 0x75, 0x68, 0xe2, 0x38, 0xd5, 0x0a, 0xfd, 0x41, 0x7e, 0x1e, 0x96, 0x0e, 0x7b, 0x5a,
0x06, 0x4f, 0xd9, 0xf6, 0x94, 0xd7, 0x83, 0xa2, 0xcb, 0xcd, 0x58, 0x55, 0x2d, 0xed, 0xbb, 0x9e,
0x5e, 0x11, 0x23, 0x67, 0x4e, 0xf7, 0x3a, 0x52, 0x41, 0x96, 0xcf, 0x05, 0xd3, 0xe5, 0x24, 0x66,
0x05, 0x49, 0xff, 0xe7, 0xbd, 0x65, 0x68, 0x05, 0x71, 0x35, 0xff, 0xd5, 0xaf, 0xd9, 0x43, 0xf6,
0xda, 0x11, 0xcb, 0xb5, 0x97, 0xe8, 0xcc, 0xec, 0xd7, 0x7e, 0xcb, 0xe9, 0x09, 0xde, 0x06, 0x31,
0xbf, 0xa2, 0x9c, 0xd3, 0xe3, 0xd5, 0x54, 0x46, 0x71, 0xba, 0x80, 0x25, 0x61, 0x53, 0xd6, 0xe9,
0x99, 0x0b, 0x88, 0xad, 0x8e, 0x0c, 0xf4, 0x98, 0x9b, 0xef, 0x4b, 0xe4, 0x57, 0xf9, 0xc7, 0xb0,
0xf1, 0xaa, 0xcd, 0x6e, 0x0e, 0xf3, 0x20, 0x60, 0x5c, 0x29, 0xed, 0x0c, 0xd2, 0xeb, 0x6c, 0xfc,
0xe2, 0x16, 0xc5, 0x2a, 0x31, 0x75, 0x80, 0x20, 0x1c, 0xad, 0x7a, 0x09, 0x43, 0xd2, 0x4b, 0x7b,
0x06, 0xd5, 0xbf, 0x75, 0x87, 0x61, 0xdd, 0x96, 0xe1, 0x19, 0x70, 0xb5, 0xde, 0xd6, 0x97, 0x22,
0x2b, 0x2c, 0x77, 0xe7, 0xf2, 0x56, 0xa6, 0x05, 0xac, 0x75, 0x55, 0x49, 0xc1, 0x65, 0x1f, 0x25,
0xad, 0xfc, 0x9d, 0x53, 0xd9, 0x11, 0x7e, 0x3a, 0x0b, 0xb4, 0x09, 0xee, 0xe4, 0xa6, 0x00, 0x12,
0x04, 0x72, 0x94, 0x9c, 0x7d, 0xda, 0x1c, 0x2e, 0xdb, 0x3c, 0x33, 0x0c, 0x7f, 0x96, 0x17, 0x99,
0x82, 0x91, 0x64, 0x57, 0xd3, 0x31, 0xe9, 0x63, 0x09, 0xdd, 0x24, 0xdf, 0x74, 0xee, 0xdd, 0x00,
0xe7, 0xdb, 0x49, 0x7e, 0xe1, 0x30, 0xf7, 0x7d, 0xe6, 0x66, 0xeb, 0x55, 0x7f, 0xb3, 0x16, 0xe8,
0x7a, 0xda, 0xf1, 0x81, 0x3c, 0xe4, 0x26, 0xa4, 0x58, 0xa6, 0xee, 0xe3, 0xa8, 0x5b, 0x2a, 0xb8,
0x8f, 0x65, 0x53, 0xaa, 0xda, 0xe8, 0xde, 0x65, 0x2e, 0x21, 0x1a, 0x1d, 0x9f, 0x33, 0x4d, 0x59,
0x6b, 0x5e, 0xb6, 0x17, 0x34, 0x07, 0xef, 0xcc, 0x2e, 0x81, 0x54, 0xbb, 0x9c, 0xa1, 0x21, 0x2a,
0xa9, 0xa1, 0xa1, 0x12, 0x1d, 0x2f, 0x5a, 0x77, 0x12, 0xcf, 0x25, 0xcc, 0x81, 0x48, 0xb8, 0x05,
0x2e, 0x0d, 0x2e, 0x09, 0xf2, 0x0e, 0x5b, 0xa2, 0xa9, 0x82, 0x77, 0xe9, 0x75, 0xb0, 0xee, 0xd9,
0xa8, 0x92, 0x06, 0x96, 0x63, 0x37, 0x16, 0x3f, 0x21, 0x5c, 0x9d, 0x04, 0xa6, 0x59, 0x8b, 0x09,
0x58, 0xd3, 0x33, 0xd8, 0x46, 0x77, 0x3c, 0x69, 0xe5, 0xab, 0xfd, 0x0a, 0x04, 0x27, 0xf3, 0x66,
0x06, 0x14, 0xdd, 0x82, 0xb7, 0x9a, 0xdb, 0x85, 0x1a, 0x0d, 0x58, 0xb6, 0x2d, 0xf5, 0xf0, 0xb3,
0xac, 0x83, 0x6e, 0x6e, 0x25, 0xf3, 0xa5, 0x1f, 0x49, 0xa9, 0x9a, 0xde, 0x57, 0x79, 0x6f, 0xe9,
0xfc, 0xc2, 0x6f, 0x0a, 0x1f, 0x94, 0xff, 0x08, 0x19, 0xfe, 0x52, 0xb7, 0x50, 0x87, 0xed, 0xbe,
0xd3, 0xa8, 0x16, 0x26, 0xeb, 0x54, 0x16, 0xc6, 0x65, 0x57, 0xf1, 0x1c, 0x0f, 0xce, 0xdf, 0xf2,
0x23, 0xd6, 0xaa, 0x8c, 0xd5, 0xc3, 0x53, 0x86, 0xe5, 0xb4, 0xb9, 0x5a, 0x0f, 0x03, 0x92, 0xca,
0x30, 0x1a, 0x38, 0xb3, 0x68, 0x7d, 0x09, 0x44, 0x93, 0xb9, 0xe9, 0xd2, 0x64, 0xd0, 0x7a, 0x19,
0x0c, 0xe5, 0x7d, 0x11, 0x68, 0x04, 0x38, 0x2a, 0x3f, 0xab, 0xe1, 0x5a, 0xf4, 0xdf, 0x4f, 0xa0,
0x43, 0xf0, 0x28, 0x7a, 0xa1, 0xed, 0x55, 0x68, 0xd9, 0xef, 0x5d, 0x12, 0x51, 0x0d, 0x01, 0x0c,
0xcd, 0xab, 0x4e, 0xb6, 0x16, 0xf6, 0xdf, 0x13, 0xbb, 0x31, 0x26, 0xef, 0x43, 0xd9, 0xd6, 0x57,
0x35, 0xe4, 0xe4, 0xc0, 0x4b, 0x57, 0x63, 0x48, 0xd0, 0x40, 0xb5, 0x35, 0x05, 0x5a, 0x3d, 0x5a,
0xe1, 0x91, 0xb7, 0x5f, 0x06, 0x12, 0xf3, 0xb2, 0x40, 0x66, 0xa0, 0x52, 0x45, 0xf2, 0x7f, 0xe5,
0x7b, 0xda, 0x66, 0xbd, 0x6d, 0xec, 0x7e, 0x4f, 0xc9, 0xcb, 0x23, 0x68, 0x02, 0x06, 0x2a, 0xdd,
0xe3, 0xcd, 0x0e, 0x31, 0x34, 0x82, 0xc9, 0x2a, 0x0c, 0x72, 0x11, 0x02, 0xb1, 0xf3, 0x8b, 0x01,
0x5a, 0xb8, 0xd0, 0x15, 0x59, 0xcb, 0xcb, 0x40, 0xf6, 0x74, 0xe9, 0xef, 0xad, 0x5e, 0xe9, 0xc2,
0xfe, 0x13, 0x3f, 0xaa, 0x55, 0xca, 0x1d, 0xd0, 0xff, 0x26, 0x71, 0x0f, 0x9d, 0xa8, 0x19, 0xcc,
0x14, 0x59, 0xcb, 0x7e, 0xd2, 0x60, 0xda, 0xd3, 0xdb, 0x05, 0x96, 0x25, 0x8d, 0x47, 0xc7, 0x4c,
0x32, 0xa8, 0xb8, 0x52, 0xb6, 0x71, 0xc5, 0xa0, 0xca, 0xa2, 0x00, 0x16, 0x03, 0xd9, 0x0c, 0x91,
0xa7, 0xdf, 0x2e, 0x2d, 0x4e, 0xe9, 0xae, 0x9b, 0xf1, 0xa6, 0xb1, 0xec, 0x88, 0x15, 0x1c, 0x62,
0x36, 0x0d, 0x03, 0x02, 0x4d, 0x2e, 0x2d, 0x01, 0x14, 0x08, 0x4f, 0x6b, 0x88, 0xc5, 0xbb, 0xa2,
0x4a, 0xa7, 0xce, 0xcf, 0xac, 0x16, 0xe9, 0x1e, 0x0b, 0xaf, 0x3d, 0x86, 0x53, 0xe2, 0x18, 0x09,
0x3e, 0x81, 0xd2, 0xa6, 0x3c, 0x32, 0xef, 0xf1, 0xd9, 0x03, 0x0f, 0x9e, 0x14, 0x14, 0xec, 0xe4,
0x20, 0xda, 0xa2, 0x4e, 0x0d, 0xd5, 0xb8, 0x45, 0xb3, 0x27, 0x4b, 0xb8, 0x39, 0xca, 0x1c, 0x53,
0xbc, 0xc0, 0x19, 0x42, 0x42, 0xd7, 0x4b, 0x26, 0x31, 0xb9, 0x49, 0x5a, 0x65, 0x4f, 0xbb, 0xdc,
0xbf, 0xad, 0x77, 0x9f, 0x73, 0x22, 0xb6, 0x07, 0x36, 0x24, 0x98, 0x80, 0x60, 0x48, 0x21, 0xd9,
0x69, 0x24, 0xe3, 0xfa, 0x39, 0x7f, 0x35, 0x4a, 0x5e, 0xcc, 0xa3, 0x4f, 0x61, 0x4d, 0xa5, 0x45,
0x6f, 0x9b, 0x36, 0x33, 0x8c, 0x37, 0xd8, 0xf6, 0xfb, 0xf6, 0x26, 0xbe, 0x98, 0x34, 0x77, 0x76,
0x60, 0x22, 0x87, 0x27, 0x46, 0xda, 0x10, 0xa1, 0x77, 0x1c, 0xeb, 0x02, 0xdd, 0x8a, 0xac, 0x01,
0xba, 0x18, 0x6b, 0xf1, 0x48, 0x86, 0x30, 0x47, 0x9e, 0x12, 0x84, 0xda, 0x01, 0x90, 0xfc, 0xe8,
0xb5, 0x9a, 0xc6, 0xb0, 0xfd, 0x41, 0x6b, 0xee, 0x56, 0xb7, 0x2f, 0x0a, 0x58, 0x45, 0x15, 0x35,
0x57, 0xff, 0x0f, 0x49, 0x50, 0xa0, 0xdc, 0x5b, 0xe6, 0x5c, 0xe9, 0x42, 0xd2, 0x2e, 0x18, 0x53,
0x4c, 0x4e, 0x0e, 0xfa, 0xbb, 0x2d, 0x15, 0x25, 0xdc, 0x48, 0x58, 0xb9, 0xb0, 0xf7, 0x7d, 0x47,
0x4a, 0x12, 0x5e, 0xbc, 0x25, 0x0e, 0x08, 0xfe, 0xdb, 0xfa, 0xa6, 0x6f, 0x45, 0x3d, 0x90, 0x93,
0x2c, 0xab, 0x3f, 0xf4, 0x52, 0x21, 0x90, 0x99, 0x68, 0xe5, 0x1e, 0x6b, 0xc2, 0x54, 0xd5, 0x09,
0xad, 0xeb, 0x75, 0xcb, 0xa7, 0x6d, 0x48, 0xfe, 0x02, 0x4e, 0x3e, 0x66, 0xd8, 0xdf, 0x5e,
];
Loading