From cff457ff15af74ca87a2922054413f7fa21afc69 Mon Sep 17 00:00:00 2001 From: Francisco Gindre Date: Tue, 22 Dec 2020 11:10:13 -0300 Subject: [PATCH 0001/2028] PoC Auto-Shielding Add retrieval of transparent UTXOs to WalletRead Co-authored-by: Kris Nuttycombe Co-authored-by: Kevin Gorham --- zcash_client_backend/Cargo.toml | 4 + zcash_client_backend/src/data_api.rs | 34 +++++-- zcash_client_backend/src/data_api/error.rs | 2 + zcash_client_backend/src/data_api/wallet.rs | 100 +++++++++++++++++- zcash_client_backend/src/encoding.rs | 57 +++++++++++ zcash_client_backend/src/keys.rs | 20 +++- zcash_client_backend/src/wallet.rs | 15 ++- zcash_client_sqlite/src/error.rs | 6 +- zcash_client_sqlite/src/lib.rs | 95 +++++++++++++----- zcash_client_sqlite/src/wallet.rs | 106 ++++++++++++++++++-- zcash_client_sqlite/src/wallet/init.rs | 15 +++ zcash_client_sqlite/src/wallet/transact.rs | 4 +- 12 files changed, 415 insertions(+), 43 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index da0518aa7b..1d7f8e9f22 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -26,6 +26,9 @@ percent-encoding = "2.1.0" proptest = { version = "0.10.1", optional = true } protobuf = "2.20" rand_core = "0.5.1" +ripemd160 = { version = "0.9.1", optional = true } +secp256k1 = { version = "0.20", optional = true } +sha2 = "0.9" subtle = "2.2.3" time = "0.2" zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" } @@ -43,6 +46,7 @@ zcash_client_sqlite = { version = "0.3", path = "../zcash_client_sqlite" } zcash_proofs = { version = "0.5", path = "../zcash_proofs" } [features] +transparent-inputs = ["ripemd160", "secp256k1"] test-dependencies = ["proptest", "zcash_primitives/test-dependencies"] [badges] diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 166fd80cba..2d8a33eb15 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -7,10 +7,14 @@ use std::fmt::Debug; use zcash_primitives::{ block::BlockHash, consensus::BlockHeight, + legacy::TransparentAddress, memo::{Memo, MemoBytes}, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, - transaction::{components::Amount, Transaction, TxId}, + transaction::{ + components::{Amount, OutPoint}, + Transaction, TxId, + }, zip32::ExtendedFullViewingKey, }; @@ -19,7 +23,7 @@ use crate::{ data_api::wallet::ANCHOR_OFFSET, decrypt::DecryptedOutput, proto::compact_formats::CompactBlock, - wallet::{AccountId, SpendableNote, WalletTx}, + wallet::{AccountId, SpendableNote, WalletTransparentOutput, WalletTx}, }; pub mod chain; @@ -160,7 +164,7 @@ pub trait WalletRead { fn get_nullifiers(&self) -> Result, Self::Error>; /// Return all spendable notes. - fn get_spendable_notes( + fn get_spendable_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, @@ -168,12 +172,18 @@ pub trait WalletRead { /// Returns a list of spendable notes sufficient to cover the specified /// target value, if possible. - fn select_spendable_notes( + fn select_spendable_sapling_notes( &self, account: AccountId, target_value: Amount, anchor_height: BlockHeight, ) -> Result, Self::Error>; + + fn get_spendable_transparent_utxos( + &self, + address: &TransparentAddress, + anchor_height: BlockHeight, + ) -> Result, Self::Error>; } /// The subset of information that is relevant to this wallet that has been @@ -215,6 +225,7 @@ pub struct SentTransaction<'a> { pub recipient_address: &'a RecipientAddress, pub value: Amount, pub memo: Option, + pub utxos_spent: Vec, } /// This trait encapsulates the write capabilities required to update stored @@ -274,6 +285,7 @@ pub mod testing { use zcash_primitives::{ block::BlockHash, consensus::BlockHeight, + legacy::TransparentAddress, memo::Memo, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, @@ -283,7 +295,7 @@ pub mod testing { use crate::{ proto::compact_formats::CompactBlock, - wallet::{AccountId, SpendableNote}, + wallet::{AccountId, SpendableNote, WalletTransparentOutput}, }; use super::{ @@ -380,7 +392,7 @@ pub mod testing { Ok(Vec::new()) } - fn get_spendable_notes( + fn get_spendable_sapling_notes( &self, _account: AccountId, _anchor_height: BlockHeight, @@ -388,7 +400,7 @@ pub mod testing { Ok(Vec::new()) } - fn select_spendable_notes( + fn select_spendable_sapling_notes( &self, _account: AccountId, _target_value: Amount, @@ -396,6 +408,14 @@ pub mod testing { ) -> Result, Self::Error> { Ok(Vec::new()) } + + fn get_spendable_transparent_utxos( + &self, + _address: &TransparentAddress, + _anchor_height: BlockHeight, + ) -> Result, Self::Error> { + Ok(Vec::new()) + } } impl WalletWrite for MockWalletDb { diff --git a/zcash_client_backend/src/data_api/error.rs b/zcash_client_backend/src/data_api/error.rs index 1968af8f94..086daee405 100644 --- a/zcash_client_backend/src/data_api/error.rs +++ b/zcash_client_backend/src/data_api/error.rs @@ -25,6 +25,8 @@ pub enum ChainInvalid { #[derive(Debug)] pub enum Error { /// Unable to create a new spend because the wallet balance is not sufficient. + /// The first argument is the amount available, the second is the amount needed + /// to construct a valid transaction. InsufficientBalance(Amount, Amount), /// Chain validation detected an error in the block at the specified block height. diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 82a9a9a9b5..be57606141 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -20,6 +20,12 @@ use crate::{ wallet::{AccountId, OvkPolicy}, }; +#[cfg(feature = "transparent-inputs")] +use zcash_primitives::{legacy::Script, transaction::components::TxOut}; + +#[cfg(feature = "transparent-inputs")] +use crate::keys::derive_transparent_address_from_secret_key; + pub const ANCHOR_OFFSET: u32 = 10; /// Scans a [`Transaction`] for any information that can be decrypted by the accounts in @@ -184,7 +190,8 @@ where .and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?; let target_value = value + DEFAULT_FEE; - let spendable_notes = wallet_db.select_spendable_notes(account, target_value, anchor_height)?; + let spendable_notes = + wallet_db.select_spendable_sapling_notes(account, target_value, anchor_height)?; // Confirm we were able to select sufficient value let selected_value = spendable_notes.iter().map(|n| n.note_value).sum(); @@ -254,5 +261,96 @@ where recipient_address: to, value, memo, + utxos_spent: vec![], + }) +} + +#[cfg(feature = "transparent-inputs")] +pub fn shield_funds( + wallet_db: &mut D, + params: &P, + prover: impl TxProver, + account: AccountId, + sk: &secp256k1::SecretKey, + extsk: &ExtendedSpendingKey, + memo: &MemoBytes, +) -> Result +where + E: From>, + P: consensus::Parameters, + R: Copy + Debug, + D: WalletWrite, +{ + let (latest_scanned_height, latest_anchor) = wallet_db + .get_target_and_anchor_heights() + .and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?; + + // derive the corresponding t-address + let taddr = derive_transparent_address_from_secret_key(*sk); + + // derive own shielded address from the provided extended spending key + let z_address = extsk.default_address().unwrap().1; + + let exfvk = ExtendedFullViewingKey::from(extsk); + + let ovk = exfvk.fvk.ovk; + + // get UTXOs from DB + let utxos = wallet_db.get_spendable_transparent_utxos(&taddr, latest_anchor)?; + let total_amount = utxos.iter().map(|utxo| utxo.value).sum::(); + + let fee = DEFAULT_FEE; + if fee >= total_amount { + return Err(E::from(Error::InsufficientBalance(total_amount, fee))); + } + + let amount_to_shield = total_amount - fee; + + let mut builder = Builder::new(params.clone(), latest_scanned_height); + + for utxo in &utxos { + let coin = TxOut { + value: utxo.value, + script_pubkey: Script { + 0: utxo.script.clone(), + }, + }; + + builder + .add_transparent_input(*sk, utxo.outpoint.clone(), coin) + .map_err(Error::Builder)?; + } + + // there are no sapling notes so we set the change manually + builder.send_change_to(ovk, z_address.clone()); + + // add the sapling output to shield the funds + builder + .add_sapling_output( + Some(ovk), + z_address.clone(), + amount_to_shield, + Some(memo.clone()), + ) + .map_err(Error::Builder)?; + + let consensus_branch_id = BranchId::for_height(params, latest_anchor); + + let (tx, tx_metadata) = builder + .build(consensus_branch_id, &prover) + .map_err(Error::Builder)?; + let output_index = tx_metadata.output_index(0).expect( + "No sapling note was created in autoshielding transaction. This is a programming error.", + ); + + wallet_db.store_sent_tx(&SentTransaction { + tx: &tx, + created: time::OffsetDateTime::now_utc(), + output_index, + account, + recipient_address: &RecipientAddress::Shielded(z_address), + value: amount_to_shield, + memo: Some(memo.clone()), + utxos_spent: utxos.iter().map(|utxo| utxo.outpoint.clone()).collect(), }) } diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 827b99bf0a..18cda72922 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -8,8 +8,10 @@ use bech32::{self, Error, FromBase32, ToBase32, Variant}; use bs58::{self, decode::Error as Bs58Error}; use std::convert::TryInto; +use std::fmt; use std::io::{self, Write}; use zcash_primitives::{ + consensus, legacy::TransparentAddress, sapling::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, @@ -36,6 +38,61 @@ where } } +pub trait AddressCodec

+where + Self: std::marker::Sized, +{ + type Error; + + fn encode(&self, params: &P) -> String; + fn decode(params: &P, address: &str) -> Result; +} + +#[derive(Debug)] +pub enum TransparentCodecError { + UnsupportedAddressType(String), + Base58(Bs58Error), +} + +impl fmt::Display for TransparentCodecError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self { + TransparentCodecError::UnsupportedAddressType(s) => write!( + f, + "Could not recognize {} as a supported p2sh or p2pkh address.", + s + ), + TransparentCodecError::Base58(e) => write!(f, "{}", e), + } + } +} + +impl std::error::Error for TransparentCodecError {} + +impl AddressCodec

for TransparentAddress { + type Error = TransparentCodecError; + + fn encode(&self, params: &P) -> String { + encode_transparent_address( + ¶ms.b58_pubkey_address_prefix(), + ¶ms.b58_script_address_prefix(), + self, + ) + } + + fn decode(params: &P, address: &str) -> Result { + decode_transparent_address( + ¶ms.b58_pubkey_address_prefix(), + ¶ms.b58_script_address_prefix(), + address, + ) + .map_err(TransparentCodecError::Base58) + .and_then(|opt| { + opt.ok_or_else(|| TransparentCodecError::UnsupportedAddressType(address.to_string())) + }) + } +} + /// Writes an [`ExtendedSpendingKey`] as a Bech32-encoded string. /// /// # Examples diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index c2fd72b348..c0de036936 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -1,6 +1,14 @@ //! Helper functions for managing light client key material. +#![cfg(feature = "transparent-inputs")] -use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; +use zcash_primitives::{ + legacy::TransparentAddress, + zip32::{ChildIndex, ExtendedSpendingKey}, +}; + +use secp256k1::{key::PublicKey, Secp256k1}; + +use sha2::{Digest, Sha256}; /// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the /// given seed. @@ -33,6 +41,16 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi ) } +pub fn derive_transparent_address_from_secret_key( + secret_key: secp256k1::key::SecretKey, +) -> TransparentAddress { + let secp = Secp256k1::new(); + let pk = PublicKey::from_secret_key(&secp, &secret_key); + let mut hash160 = ripemd160::Ripemd160::new(); + hash160.update(Sha256::digest(&pk.serialize()[..].to_vec())); + TransparentAddress::PublicKey(*hash160.finalize().as_ref()) +} + #[cfg(test)] mod tests { use super::spending_key; diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index 220aaf6e3d..39e3d9d2c2 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -4,11 +4,16 @@ use subtle::{Choice, ConditionallySelectable}; use zcash_primitives::{ + consensus::BlockHeight, + legacy::TransparentAddress, merkle_tree::IncrementalWitness, sapling::{ keys::OutgoingViewingKey, Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed, }, - transaction::{components::Amount, TxId}, + transaction::{ + components::{Amount, OutPoint}, + TxId, + }, }; /// A type-safe wrapper for account identifiers. @@ -39,6 +44,14 @@ pub struct WalletTx { pub shielded_outputs: Vec>, } +pub struct WalletTransparentOutput { + pub address: TransparentAddress, + pub outpoint: OutPoint, + pub script: Vec, + pub value: Amount, + pub height: BlockHeight, +} + /// A subset of a [`SpendDescription`] relevant to wallets and light clients. /// /// [`SpendDescription`]: zcash_primitives::transaction::components::SpendDescription diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index e83c20e5dc..6c285fb4c1 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -3,7 +3,7 @@ use std::error; use std::fmt; -use zcash_client_backend::data_api; +use zcash_client_backend::{data_api, encoding::TransparentCodecError}; use crate::NoteId; @@ -32,6 +32,9 @@ pub enum SqliteClientError { /// Base58 decoding error Base58(bs58::decode::Error), + /// Base58 decoding error + TransparentAddress(TransparentCodecError), + /// Wrapper for rusqlite errors. DbError(rusqlite::Error), @@ -68,6 +71,7 @@ impl fmt::Display for SqliteClientError { SqliteClientError::InvalidNoteId => write!(f, "The note ID associated with an inserted witness must correspond to a received note."), SqliteClientError::Bech32(e) => write!(f, "{}", e), SqliteClientError::Base58(e) => write!(f, "{}", e), + SqliteClientError::TransparentAddress(e) => write!(f, "{}", e), SqliteClientError::TableNotEmpty => write!(f, "Table is not empty"), SqliteClientError::DbError(e) => write!(f, "{}", e), SqliteClientError::Io(e) => write!(f, "{}", e), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 0d9047574b..c9ba7cad80 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -41,6 +41,7 @@ use rusqlite::{Connection, Statement, NO_PARAMS}; use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight}, + legacy::TransparentAddress, memo::Memo, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, @@ -54,7 +55,7 @@ use zcash_client_backend::{ }, encoding::encode_payment_address, proto::compact_formats::CompactBlock, - wallet::{AccountId, SpendableNote}, + wallet::{AccountId, SpendableNote, WalletTransparentOutput}, }; use crate::error::SqliteClientError; @@ -80,6 +81,11 @@ impl fmt::Display for NoteId { } } +/// A newtype wrapper for sqlite primary key values for the utxos +/// table. +#[derive(Debug, Copy, Clone)] +pub struct UtxoId(i64); + /// A wrapper for the SQLite connection to the wallet database. pub struct WalletDb

{ conn: Connection, @@ -91,7 +97,9 @@ impl WalletDb

{ pub fn for_path>(path: F, params: P) -> Result { Connection::open(path).map(move |conn| WalletDb { conn, params }) } +} +impl WalletDb

{ /// Given a wallet database connection, obtain a handle for the write operations /// for that database. This operation may eagerly initialize and cache sqlite /// prepared statements that are used in write operations. @@ -122,9 +130,18 @@ impl WalletDb

{ stmt_select_tx_ref: self.conn.prepare( "SELECT id_tx FROM transactions WHERE txid = ?", )?, - stmt_mark_recived_note_spent: self.conn.prepare( + stmt_mark_sapling_note_spent: self.conn.prepare( "UPDATE received_notes SET spent = ? WHERE nf = ?" )?, + stmt_mark_transparent_utxo_spent: self.conn.prepare( + "UPDATE utxos SET spent_in_tx = :spent_in_tx + WHERE prevout_txid = :prevout_txid + AND prevout_idx = :prevout_idx" + )?, + stmt_insert_received_transparent_utxo: self.conn.prepare( + "INSERT INTO utxos (address, prevout_txid, prevout_idx, script, value_zat, height) + VALUES (:address, :prevout_txid, :prevout_idx, :script, :value_zat, :height)" + )?, stmt_insert_received_note: self.conn.prepare( "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, memo, nf, is_change) VALUES (:tx, :output_index, :account, :diversifier, :value, :rcm, :memo, :nf, :is_change)", @@ -176,25 +193,25 @@ impl WalletRead for WalletDb

{ type TxRef = i64; fn block_height_extrema(&self) -> Result, Self::Error> { - wallet::block_height_extrema(self).map_err(SqliteClientError::from) + wallet::block_height_extrema(&self).map_err(SqliteClientError::from) } fn get_block_hash(&self, block_height: BlockHeight) -> Result, Self::Error> { - wallet::get_block_hash(self, block_height).map_err(SqliteClientError::from) + wallet::get_block_hash(&self, block_height).map_err(SqliteClientError::from) } fn get_tx_height(&self, txid: TxId) -> Result, Self::Error> { - wallet::get_tx_height(self, txid).map_err(SqliteClientError::from) + wallet::get_tx_height(&self, txid).map_err(SqliteClientError::from) } fn get_extended_full_viewing_keys( &self, ) -> Result, Self::Error> { - wallet::get_extended_full_viewing_keys(self) + wallet::get_extended_full_viewing_keys(&self) } fn get_address(&self, account: AccountId) -> Result, Self::Error> { - wallet::get_address(self, account) + wallet::get_address(&self, account) } fn is_valid_account_extfvk( @@ -202,7 +219,7 @@ impl WalletRead for WalletDb

{ account: AccountId, extfvk: &ExtendedFullViewingKey, ) -> Result { - wallet::is_valid_account_extfvk(self, account, extfvk) + wallet::is_valid_account_extfvk(&self, account, extfvk) } fn get_balance_at( @@ -210,7 +227,7 @@ impl WalletRead for WalletDb

{ account: AccountId, anchor_height: BlockHeight, ) -> Result { - wallet::get_balance_at(self, account, anchor_height) + wallet::get_balance_at(&self, account, anchor_height) } fn get_memo(&self, id_note: Self::NoteRef) -> Result { @@ -224,7 +241,7 @@ impl WalletRead for WalletDb

{ &self, block_height: BlockHeight, ) -> Result>, Self::Error> { - wallet::get_commitment_tree(self, block_height) + wallet::get_commitment_tree(&self, block_height) } #[allow(clippy::type_complexity)] @@ -232,28 +249,41 @@ impl WalletRead for WalletDb

{ &self, block_height: BlockHeight, ) -> Result)>, Self::Error> { - wallet::get_witnesses(self, block_height) + wallet::get_witnesses(&self, block_height) } fn get_nullifiers(&self) -> Result, Self::Error> { - wallet::get_nullifiers(self) + wallet::get_nullifiers(&self) } - fn get_spendable_notes( + fn get_spendable_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, ) -> Result, Self::Error> { - wallet::transact::get_spendable_notes(self, account, anchor_height) + wallet::transact::get_spendable_sapling_notes(&self, account, anchor_height) } - fn select_spendable_notes( + fn select_spendable_sapling_notes( &self, account: AccountId, target_value: Amount, anchor_height: BlockHeight, ) -> Result, Self::Error> { - wallet::transact::select_spendable_notes(self, account, target_value, anchor_height) + wallet::transact::select_spendable_sapling_notes( + &self, + account, + target_value, + anchor_height, + ) + } + + fn get_spendable_transparent_utxos( + &self, + address: &TransparentAddress, + anchor_height: BlockHeight, + ) -> Result, Self::Error> { + wallet::get_spendable_transparent_utxos(&self, address, anchor_height) } } @@ -275,8 +305,10 @@ pub struct DataConnStmtCache<'a, P> { stmt_update_tx_data: Statement<'a>, stmt_select_tx_ref: Statement<'a>, - stmt_mark_recived_note_spent: Statement<'a>, + stmt_mark_sapling_note_spent: Statement<'a>, + stmt_mark_transparent_utxo_spent: Statement<'a>, + stmt_insert_received_transparent_utxo: Statement<'a>, stmt_insert_received_note: Statement<'a>, stmt_update_received_note: Statement<'a>, stmt_select_received_note: Statement<'a>, @@ -355,22 +387,32 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { self.wallet_db.get_nullifiers() } - fn get_spendable_notes( + fn get_spendable_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, ) -> Result, Self::Error> { - self.wallet_db.get_spendable_notes(account, anchor_height) + self.wallet_db + .get_spendable_sapling_notes(account, anchor_height) } - fn select_spendable_notes( + fn select_spendable_sapling_notes( &self, account: AccountId, target_value: Amount, anchor_height: BlockHeight, ) -> Result, Self::Error> { self.wallet_db - .select_spendable_notes(account, target_value, anchor_height) + .select_spendable_sapling_notes(account, target_value, anchor_height) + } + + fn get_spendable_transparent_utxos( + &self, + address: &TransparentAddress, + anchor_height: BlockHeight, + ) -> Result, Self::Error> { + self.wallet_db + .get_spendable_transparent_utxos(address, anchor_height) } } @@ -426,9 +468,12 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // Mark notes as spent and remove them from the scanning cache for spend in &tx.shielded_spends { - wallet::mark_spent(up, tx_row, &spend.nf)?; + wallet::mark_sapling_note_spent(up, tx_row, &spend.nf)?; } + //TODO + //wallet::mark_transparent_utxo_spent(up, tx_ref, &utxo.outpoint)?; + for output in &tx.shielded_outputs { let received_note_id = wallet::put_received_note(up, output, tx_row)?; @@ -490,7 +535,11 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // Assumes that create_spend_to_address() will never be called in parallel, which is a // reasonable assumption for a light client such as a mobile phone. for spend in &sent_tx.tx.shielded_spends { - wallet::mark_spent(up, tx_ref, &spend.nullifier)?; + wallet::mark_sapling_note_spent(up, tx_ref, &spend.nullifier)?; + } + + for utxo_outpoint in &sent_tx.utxos_spent { + wallet::mark_transparent_utxo_spent(up, tx_ref, &utxo_outpoint)?; } wallet::insert_sent_note( diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 9fbc41185f..483f80e7ff 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -15,10 +15,14 @@ use std::convert::TryFrom; use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight, NetworkUpgrade}, + legacy::TransparentAddress, memo::{Memo, MemoBytes}, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Note, Nullifier, PaymentAddress}, - transaction::{components::Amount, Transaction, TxId}, + transaction::{ + components::{Amount, OutPoint}, + Transaction, TxId, + }, zip32::ExtendedFullViewingKey, }; @@ -27,13 +31,13 @@ use zcash_client_backend::{ data_api::error::Error, encoding::{ decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key, - encode_payment_address, + encode_payment_address, AddressCodec, }, - wallet::{AccountId, WalletShieldedOutput, WalletTx}, + wallet::{AccountId, WalletShieldedOutput, WalletTransparentOutput, WalletTx}, DecryptedOutput, }; -use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb}; +use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, UtxoId, WalletDb}; pub mod init; pub mod transact; @@ -588,6 +592,57 @@ pub fn get_nullifiers

( Ok(res) } +pub fn get_spendable_transparent_utxos( + wdb: &WalletDb

, + address: &TransparentAddress, + anchor_height: BlockHeight, +) -> Result, SqliteClientError> { + let mut stmt_blocks = wdb.conn.prepare( + "SELECT address, prevout_txid, prevout_idx, script, value_zat, height + FROM utxos + WHERE address = ? + AND height <= ? + AND spent_in_tx IS NULL", + )?; + + let addr_str = address.encode(&wdb.params); + + let rows = stmt_blocks.query_map(params![addr_str, u32::from(anchor_height)], |row| { + let addr: String = row.get(0)?; + let address = TransparentAddress::decode(&wdb.params, &addr).map_err(|e| { + rusqlite::Error::FromSqlConversionFailure( + addr.len(), + rusqlite::types::Type::Text, + Box::new(e), + ) + })?; + + let id: Vec = row.get(1)?; + + let mut txid_bytes = [0u8; 32]; + txid_bytes.copy_from_slice(&id); + let index: i32 = row.get(2)?; + let script: Vec = row.get(3)?; + let value: i64 = row.get(4)?; + let height: u32 = row.get(5)?; + + Ok(WalletTransparentOutput { + address, + outpoint: OutPoint::new(txid_bytes, index as u32), + script, + value: Amount::from_i64(value).unwrap(), + height: BlockHeight::from(height), + }) + })?; + + let mut utxos = Vec::::new(); + + for utxo in rows { + utxos.push(utxo.unwrap()) + } + Ok(utxos) +} + /// Inserts information about a scanned block into the database. pub fn insert_block<'a, P>( stmts: &mut DataConnStmtCache<'a, P>, @@ -676,18 +731,56 @@ pub fn put_tx_data<'a, P>( /// /// Marking a note spent in this fashion does NOT imply that the /// spending transaction has been mined. -pub fn mark_spent<'a, P>( +pub fn mark_sapling_note_spent<'a, P>( stmts: &mut DataConnStmtCache<'a, P>, tx_ref: i64, nf: &Nullifier, ) -> Result<(), SqliteClientError> { stmts - .stmt_mark_recived_note_spent + .stmt_mark_sapling_note_spent .execute(&[tx_ref.to_sql()?, nf.0.to_sql()?])?; Ok(()) } /// Records the specified shielded output as having been received. +pub fn mark_transparent_utxo_spent<'a, P>( + stmts: &mut DataConnStmtCache<'a, P>, + tx_ref: i64, + outpoint: &OutPoint, +) -> Result<(), SqliteClientError> { + let sql_args: &[(&str, &dyn ToSql)] = &[ + (&":spent_in_tx", &tx_ref), + (&":prevout_txid", &outpoint.hash().to_vec()), + (&":prevout_idx", &outpoint.n()), + ]; + + stmts + .stmt_mark_transparent_utxo_spent + .execute_named(&sql_args)?; + + Ok(()) +} + +pub fn put_received_transparent_utxo<'a, P: consensus::Parameters>( + stmts: &mut DataConnStmtCache<'a, P>, + output: &WalletTransparentOutput, +) -> Result { + let sql_args: &[(&str, &dyn ToSql)] = &[ + (&":address", &output.address.encode(&stmts.wallet_db.params)), + (&":prevout_txid", &output.outpoint.hash().to_vec()), + (&":prevout_idx", &output.outpoint.n()), + (&":script", &output.script), + (&":value_zat", &i64::from(output.value)), + (&":height", &u32::from(output.height)), + ]; + + stmts + .stmt_insert_received_transparent_utxo + .execute_named(&sql_args)?; + + Ok(UtxoId(stmts.wallet_db.conn.last_insert_rowid())) +} + // Assumptions: // - A transaction will not contain more than 2^63 shielded outputs. // - A note value will never exceed 2^63 zatoshis. @@ -847,7 +940,6 @@ pub fn insert_sent_note<'a, P: consensus::Parameters>( Ok(()) } - #[cfg(test)] mod tests { use tempfile::NamedTempFile; diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index f21f53182c..572e54b2bd 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -106,6 +106,21 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { )", NO_PARAMS, )?; + wdb.conn.execute( + "CREATE TABLE IF NOT EXISTS utxos ( + id_utxo INTEGER PRIMARY KEY, + address TEXT NOT NULL, + prevout_txid BLOB NOT NULL, + prevout_idx INTEGER NOT NULL, + script BLOB NOT NULL, + value_zat INTEGER NOT NULL, + height INTEGER NOT NULL, + spent_in_tx INTEGER, + FOREIGN KEY (spent_in_tx) REFERENCES transactions(id_tx), + CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx) + )", + NO_PARAMS, + )?; Ok(()) } diff --git a/zcash_client_sqlite/src/wallet/transact.rs b/zcash_client_sqlite/src/wallet/transact.rs index 5fccb430f3..35ed92f0c4 100644 --- a/zcash_client_sqlite/src/wallet/transact.rs +++ b/zcash_client_sqlite/src/wallet/transact.rs @@ -59,7 +59,7 @@ fn to_spendable_note(row: &Row) -> Result { }) } -pub fn get_spendable_notes

( +pub fn get_spendable_sapling_notes

( wdb: &WalletDb

, account: AccountId, anchor_height: BlockHeight, @@ -87,7 +87,7 @@ pub fn get_spendable_notes

( notes.collect::>() } -pub fn select_spendable_notes

( +pub fn select_spendable_sapling_notes

( wdb: &WalletDb

, account: AccountId, target_value: Amount, From 862e221a9bf46098436e7ca895d8171b4cfb7b26 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 12 Feb 2021 14:08:31 -0700 Subject: [PATCH 0002/2028] Put transparent dependencies behind a feature flag. --- zcash_client_backend/Cargo.toml | 3 +- zcash_client_backend/src/data_api.rs | 7 ++- zcash_client_backend/src/keys.rs | 70 ++++++++++++++++++++++++---- zcash_client_backend/src/wallet.rs | 13 +++--- zcash_client_sqlite/Cargo.toml | 1 + zcash_client_sqlite/src/lib.rs | 16 +++++-- zcash_client_sqlite/src/wallet.rs | 16 +++++-- zcash_primitives/Cargo.toml | 2 +- 8 files changed, 103 insertions(+), 25 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 1d7f8e9f22..916c995357 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -20,6 +20,7 @@ base64 = "0.13" ff = "0.8" group = "0.8" hex = "0.4" +hdwallet = { version = "0.3.0", optional = true } jubjub = "0.5.1" nom = "6.1" percent-encoding = "2.1.0" @@ -27,7 +28,7 @@ proptest = { version = "0.10.1", optional = true } protobuf = "2.20" rand_core = "0.5.1" ripemd160 = { version = "0.9.1", optional = true } -secp256k1 = { version = "0.20", optional = true } +secp256k1 = { version = "0.19", optional = true } sha2 = "0.9" subtle = "2.2.3" time = "0.2" diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 2d8a33eb15..645691cf6e 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -7,7 +7,6 @@ use std::fmt::Debug; use zcash_primitives::{ block::BlockHash, consensus::BlockHeight, - legacy::TransparentAddress, memo::{Memo, MemoBytes}, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, @@ -23,9 +22,12 @@ use crate::{ data_api::wallet::ANCHOR_OFFSET, decrypt::DecryptedOutput, proto::compact_formats::CompactBlock, - wallet::{AccountId, SpendableNote, WalletTransparentOutput, WalletTx}, + wallet::{AccountId, SpendableNote, WalletTx}, }; +#[cfg(feature = "transparent-inputs")] +use {crate::wallet::WalletTransparentOutput, zcash_primitives::legacy::TransparentAddress}; + pub mod chain; pub mod error; pub mod wallet; @@ -179,6 +181,7 @@ pub trait WalletRead { anchor_height: BlockHeight, ) -> Result, Self::Error>; + #[cfg(feature = "transparent-inputs")] fn get_spendable_transparent_utxos( &self, address: &TransparentAddress, diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index c0de036936..ba32975f19 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -1,14 +1,17 @@ //! Helper functions for managing light client key material. -#![cfg(feature = "transparent-inputs")] -use zcash_primitives::{ - legacy::TransparentAddress, - zip32::{ChildIndex, ExtendedSpendingKey}, -}; - -use secp256k1::{key::PublicKey, Secp256k1}; +use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; -use sha2::{Digest, Sha256}; +#[cfg(feature = "transparent-inputs")] +use { + crate::wallet::AccountId, + bs58::decode::Error as Bs58Error, + hdwallet::{ExtendedPrivKey, KeyIndex}, + secp256k1::{key::PublicKey, Secp256k1, SecretKey}, + sha2::{Digest, Sha256}, + std::convert::TryInto, + zcash_primitives::{consensus, legacy::TransparentAddress}, +}; /// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the /// given seed. @@ -41,6 +44,7 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi ) } +#[cfg(feature = "transparent-inputs")] pub fn derive_transparent_address_from_secret_key( secret_key: secp256k1::key::SecretKey, ) -> TransparentAddress { @@ -51,6 +55,56 @@ pub fn derive_transparent_address_from_secret_key( TransparentAddress::PublicKey(*hash160.finalize().as_ref()) } +#[cfg(feature = "transparent-inputs")] +pub fn derive_secret_key_from_seed( + params: &P, + seed: &[u8], + account: AccountId, + index: u32, +) -> Result { + let ext_t_key = ExtendedPrivKey::with_seed(&seed)?; + let private_key = ext_t_key + .derive_private_key(KeyIndex::hardened_from_normalize_index(44)?)? + .derive_private_key(KeyIndex::hardened_from_normalize_index(params.coin_type())?)? + .derive_private_key(KeyIndex::hardened_from_normalize_index(account.0)?)? + .derive_private_key(KeyIndex::Normal(0))? + .derive_private_key(KeyIndex::Normal(index))? + .private_key; + + Ok(private_key) +} + +#[cfg(feature = "transparent-inputs")] +pub struct Wif(pub String); + +#[cfg(feature = "transparent-inputs")] +impl Wif { + pub fn from_secret_key(sk: &SecretKey, compressed: bool) -> Self { + let secret_key = sk.as_ref(); + let mut wif = [0u8; 34]; + wif[0] = 0x80; + wif[1..33].copy_from_slice(secret_key); + if compressed { + wif[33] = 0x01; + Wif(bs58::encode(&wif[..]).with_check().into_string()) + } else { + Wif(bs58::encode(&wif[..]).with_check().into_string()) + } + } +} + +#[cfg(feature = "transparent-inputs")] +impl TryInto for Wif { + type Error = Bs58Error; + + fn try_into(self) -> Result { + bs58::decode(&self.0) + .with_check(None) + .into_vec() + .map(|decoded| SecretKey::from_slice(&decoded[1..33]).expect("wrong size key")) + } +} + #[cfg(test)] mod tests { use super::spending_key; diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index 39e3d9d2c2..a2f5971b40 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -4,16 +4,16 @@ use subtle::{Choice, ConditionallySelectable}; use zcash_primitives::{ - consensus::BlockHeight, - legacy::TransparentAddress, merkle_tree::IncrementalWitness, sapling::{ keys::OutgoingViewingKey, Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed, }, - transaction::{ - components::{Amount, OutPoint}, - TxId, - }, + transaction::{components::Amount, TxId}, +}; + +#[cfg(feature = "transparent-inputs")] +use zcash_primitives::{ + consensus::BlockHeight, legacy::TransparentAddress, transaction::components::OutPoint, }; /// A type-safe wrapper for account identifiers. @@ -44,6 +44,7 @@ pub struct WalletTx { pub shielded_outputs: Vec>, } +#[cfg(feature = "transparent-inputs")] pub struct WalletTransparentOutput { pub address: TransparentAddress, pub outpoint: OutPoint, diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 5e1efe0f63..ef7fb0853a 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -32,4 +32,5 @@ zcash_proofs = { version = "0.5", path = "../zcash_proofs" } [features] mainnet = [] +transparent-inputs = [] test-dependencies = ["zcash_client_backend/test-dependencies"] diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index c9ba7cad80..daddcba8e5 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -41,7 +41,6 @@ use rusqlite::{Connection, Statement, NO_PARAMS}; use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight}, - legacy::TransparentAddress, memo::Memo, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, @@ -55,11 +54,17 @@ use zcash_client_backend::{ }, encoding::encode_payment_address, proto::compact_formats::CompactBlock, - wallet::{AccountId, SpendableNote, WalletTransparentOutput}, + wallet::{AccountId, SpendableNote}, }; use crate::error::SqliteClientError; +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::WalletTransparentOutput, + zcash_primitives::legacy::TransparentAddress, +}; + pub mod chain; pub mod error; pub mod wallet; @@ -83,8 +88,9 @@ impl fmt::Display for NoteId { /// A newtype wrapper for sqlite primary key values for the utxos /// table. +#[cfg(feature = "transparent-inputs")] #[derive(Debug, Copy, Clone)] -pub struct UtxoId(i64); +pub struct UtxoId(pub i64); /// A wrapper for the SQLite connection to the wallet database. pub struct WalletDb

{ @@ -138,6 +144,7 @@ impl WalletDb

{ WHERE prevout_txid = :prevout_txid AND prevout_idx = :prevout_idx" )?, + #[cfg(feature = "transparent-inputs")] stmt_insert_received_transparent_utxo: self.conn.prepare( "INSERT INTO utxos (address, prevout_txid, prevout_idx, script, value_zat, height) VALUES (:address, :prevout_txid, :prevout_idx, :script, :value_zat, :height)" @@ -278,6 +285,7 @@ impl WalletRead for WalletDb

{ ) } + #[cfg(feature = "transparent-inputs")] fn get_spendable_transparent_utxos( &self, address: &TransparentAddress, @@ -308,6 +316,7 @@ pub struct DataConnStmtCache<'a, P> { stmt_mark_sapling_note_spent: Statement<'a>, stmt_mark_transparent_utxo_spent: Statement<'a>, + #[cfg(feature = "transparent-inputs")] stmt_insert_received_transparent_utxo: Statement<'a>, stmt_insert_received_note: Statement<'a>, stmt_update_received_note: Statement<'a>, @@ -406,6 +415,7 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { .select_spendable_sapling_notes(account, target_value, anchor_height) } + #[cfg(feature = "transparent-inputs")] fn get_spendable_transparent_utxos( &self, address: &TransparentAddress, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 483f80e7ff..f95c6d4267 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -15,7 +15,6 @@ use std::convert::TryFrom; use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight, NetworkUpgrade}, - legacy::TransparentAddress, memo::{Memo, MemoBytes}, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Note, Nullifier, PaymentAddress}, @@ -31,13 +30,20 @@ use zcash_client_backend::{ data_api::error::Error, encoding::{ decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key, - encode_payment_address, AddressCodec, + encode_payment_address, }, - wallet::{AccountId, WalletShieldedOutput, WalletTransparentOutput, WalletTx}, + wallet::{AccountId, WalletShieldedOutput, WalletTx}, DecryptedOutput, }; -use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, UtxoId, WalletDb}; +use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb}; + +#[cfg(feature = "transparent-inputs")] +use { + crate::UtxoId, + zcash_client_backend::{encoding::AddressCodec, wallet::WalletTransparentOutput}, + zcash_primitives::legacy::TransparentAddress, +}; pub mod init; pub mod transact; @@ -592,6 +598,7 @@ pub fn get_nullifiers

( Ok(res) } +#[cfg(feature = "transparent-inputs")] pub fn get_spendable_transparent_utxos( wdb: &WalletDb

, address: &TransparentAddress, @@ -761,6 +768,7 @@ pub fn mark_transparent_utxo_spent<'a, P>( Ok(()) } +#[cfg(feature = "transparent-inputs")] pub fn put_received_transparent_utxo<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, output: &WalletTransparentOutput, diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index a950331cff..a651df4ec3 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -35,7 +35,7 @@ proptest = { version = "0.10.1", optional = true } rand = "0.7" rand_core = "0.5.1" ripemd160 = { version = "0.9", optional = true } -secp256k1 = { version = "0.20", optional = true } +secp256k1 = { version = "0.19", optional = true } sha2 = "0.9" subtle = "2.2.3" zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" } From ca3e3a45950f4c9dad2fadb04efb294f182ec94e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 12 Feb 2021 15:54:59 -0700 Subject: [PATCH 0003/2028] Add WIF derivation for transparent keys. With @gmale --- zcash_client_backend/Cargo.toml | 2 +- zcash_client_backend/src/data_api.rs | 1 + zcash_client_backend/src/data_api/wallet.rs | 1 + zcash_client_backend/src/keys.rs | 50 ++++++++++++++++++++- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 916c995357..e90e5e8144 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -48,7 +48,7 @@ zcash_proofs = { version = "0.5", path = "../zcash_proofs" } [features] transparent-inputs = ["ripemd160", "secp256k1"] -test-dependencies = ["proptest", "zcash_primitives/test-dependencies"] +test-dependencies = ["proptest", "zcash_primitives/test-dependencies", "hdwallet"] [badges] maintenance = { status = "actively-developed" } diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 645691cf6e..361a95d327 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -412,6 +412,7 @@ pub mod testing { Ok(Vec::new()) } + #[cfg(feature = "transparent-inputs")] fn get_spendable_transparent_utxos( &self, _address: &TransparentAddress, diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index be57606141..3632f336cf 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -308,6 +308,7 @@ where let mut builder = Builder::new(params.clone(), latest_scanned_height); + #[cfg(feature = "transparent-inputs")] for utxo in &utxos { let coin = TxOut { value: utxo.value, diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index ba32975f19..5028019456 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -5,9 +5,9 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; #[cfg(feature = "transparent-inputs")] use { crate::wallet::AccountId, - bs58::decode::Error as Bs58Error, + bs58::{self, decode::Error as Bs58Error}, hdwallet::{ExtendedPrivKey, KeyIndex}, - secp256k1::{key::PublicKey, Secp256k1, SecretKey}, + secp256k1::{key::PublicKey, key::SecretKey, Secp256k1}, sha2::{Digest, Sha256}, std::convert::TryInto, zcash_primitives::{consensus, legacy::TransparentAddress}, @@ -109,9 +109,55 @@ impl TryInto for Wif { mod tests { use super::spending_key; + #[cfg(feature = "transparent-inputs")] + use { + super::{derive_secret_key_from_seed, derive_transparent_address_from_secret_key, Wif}, + crate::{encoding::AddressCodec, wallet::AccountId}, + secp256k1::key::SecretKey, + std::convert::TryInto, + zcash_primitives::consensus::MAIN_NETWORK, + }; + #[test] #[should_panic] fn spending_key_panics_on_short_seed() { let _ = spending_key(&[0; 31][..], 0, 0); } + + #[cfg(feature = "transparent-inputs")] + #[test] + fn sk_to_wif() { + let seed_hex = "6ef5f84def6f4b9d38f466586a8380a38593bd47c8cda77f091856176da47f26b5bd1c8d097486e5635df5a66e820d28e1d73346f499801c86228d43f390304f"; + let seed = hex::decode(&seed_hex).unwrap(); + let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed, AccountId(0), 0).unwrap(); + assert_eq!( + Wif::from_secret_key(&sk, true).0, + "L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string() + ); + } + + #[cfg(feature = "transparent-inputs")] + #[test] + fn sk_to_taddr() { + let seed_hex = "6ef5f84def6f4b9d38f466586a8380a38593bd47c8cda77f091856176da47f26b5bd1c8d097486e5635df5a66e820d28e1d73346f499801c86228d43f390304f"; + let seed = hex::decode(&seed_hex).unwrap(); + let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed, AccountId(0), 0).unwrap(); + let taddr = derive_transparent_address_from_secret_key(sk); + assert_eq!( + taddr.encode(&MAIN_NETWORK), + "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string() + ); + } + + #[cfg(feature = "transparent-inputs")] + #[test] + fn sk_wif_to_taddr() { + let sk_wif = Wif("L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()); + let sk: SecretKey = sk_wif.try_into().expect("invalid wif"); + let taddr = derive_transparent_address_from_secret_key(sk); + assert_eq!( + taddr.encode(&MAIN_NETWORK), + "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string() + ); + } } From a3bc1e3e63e1baa1b188f69c01605bc39ab49e64 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 12 Feb 2021 16:57:44 -0700 Subject: [PATCH 0004/2028] Rename get_spendable -> get_unspent --- zcash_client_backend/src/data_api.rs | 16 ++++++------ zcash_client_backend/src/data_api/wallet.rs | 4 +-- zcash_client_backend/src/keys.rs | 5 ++-- zcash_client_sqlite/src/lib.rs | 29 +++++++++------------ zcash_client_sqlite/src/wallet.rs | 2 +- zcash_client_sqlite/src/wallet/transact.rs | 4 +-- 6 files changed, 28 insertions(+), 32 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 361a95d327..4f2e2efa95 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -165,16 +165,16 @@ pub trait WalletRead { /// with which they are associated. fn get_nullifiers(&self) -> Result, Self::Error>; - /// Return all spendable notes. - fn get_spendable_sapling_notes( + /// Return all unspent notes. + fn get_unspent_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, ) -> Result, Self::Error>; - /// Returns a list of spendable notes sufficient to cover the specified + /// Returns a list of unspent notes sufficient to cover the specified /// target value, if possible. - fn select_spendable_sapling_notes( + fn select_unspent_sapling_notes( &self, account: AccountId, target_value: Amount, @@ -182,7 +182,7 @@ pub trait WalletRead { ) -> Result, Self::Error>; #[cfg(feature = "transparent-inputs")] - fn get_spendable_transparent_utxos( + fn get_unspent_transparent_utxos( &self, address: &TransparentAddress, anchor_height: BlockHeight, @@ -395,7 +395,7 @@ pub mod testing { Ok(Vec::new()) } - fn get_spendable_sapling_notes( + fn get_unspent_sapling_notes( &self, _account: AccountId, _anchor_height: BlockHeight, @@ -403,7 +403,7 @@ pub mod testing { Ok(Vec::new()) } - fn select_spendable_sapling_notes( + fn select_unspent_sapling_notes( &self, _account: AccountId, _target_value: Amount, @@ -413,7 +413,7 @@ pub mod testing { } #[cfg(feature = "transparent-inputs")] - fn get_spendable_transparent_utxos( + fn get_unspent_transparent_utxos( &self, _address: &TransparentAddress, _anchor_height: BlockHeight, diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 3632f336cf..226887cc0b 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -191,7 +191,7 @@ where let target_value = value + DEFAULT_FEE; let spendable_notes = - wallet_db.select_spendable_sapling_notes(account, target_value, anchor_height)?; + wallet_db.select_unspent_sapling_notes(account, target_value, anchor_height)?; // Confirm we were able to select sufficient value let selected_value = spendable_notes.iter().map(|n| n.note_value).sum(); @@ -296,7 +296,7 @@ where let ovk = exfvk.fvk.ovk; // get UTXOs from DB - let utxos = wallet_db.get_spendable_transparent_utxos(&taddr, latest_anchor)?; + let utxos = wallet_db.get_unspent_transparent_utxos(&taddr, latest_anchor)?; let total_amount = utxos.iter().map(|utxo| utxo.value).sum::(); let fee = DEFAULT_FEE; diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index 5028019456..d6def40701 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -75,6 +75,7 @@ pub fn derive_secret_key_from_seed( } #[cfg(feature = "transparent-inputs")] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Wif(pub String); #[cfg(feature = "transparent-inputs")] @@ -94,7 +95,7 @@ impl Wif { } #[cfg(feature = "transparent-inputs")] -impl TryInto for Wif { +impl<'a> TryInto for &'a Wif { type Error = Bs58Error; fn try_into(self) -> Result { @@ -153,7 +154,7 @@ mod tests { #[test] fn sk_wif_to_taddr() { let sk_wif = Wif("L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()); - let sk: SecretKey = sk_wif.try_into().expect("invalid wif"); + let sk: SecretKey = (&sk_wif).try_into().expect("invalid wif"); let taddr = derive_transparent_address_from_secret_key(sk); assert_eq!( taddr.encode(&MAIN_NETWORK), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index daddcba8e5..961953a8f1 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -263,35 +263,30 @@ impl WalletRead for WalletDb

{ wallet::get_nullifiers(&self) } - fn get_spendable_sapling_notes( + fn get_unspent_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, ) -> Result, Self::Error> { - wallet::transact::get_spendable_sapling_notes(&self, account, anchor_height) + wallet::transact::get_unspent_sapling_notes(&self, account, anchor_height) } - fn select_spendable_sapling_notes( + fn select_unspent_sapling_notes( &self, account: AccountId, target_value: Amount, anchor_height: BlockHeight, ) -> Result, Self::Error> { - wallet::transact::select_spendable_sapling_notes( - &self, - account, - target_value, - anchor_height, - ) + wallet::transact::select_unspent_sapling_notes(&self, account, target_value, anchor_height) } #[cfg(feature = "transparent-inputs")] - fn get_spendable_transparent_utxos( + fn get_unspent_transparent_utxos( &self, address: &TransparentAddress, anchor_height: BlockHeight, ) -> Result, Self::Error> { - wallet::get_spendable_transparent_utxos(&self, address, anchor_height) + wallet::get_unspent_transparent_utxos(&self, address, anchor_height) } } @@ -396,33 +391,33 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { self.wallet_db.get_nullifiers() } - fn get_spendable_sapling_notes( + fn get_unspent_sapling_notes( &self, account: AccountId, anchor_height: BlockHeight, ) -> Result, Self::Error> { self.wallet_db - .get_spendable_sapling_notes(account, anchor_height) + .get_unspent_sapling_notes(account, anchor_height) } - fn select_spendable_sapling_notes( + fn select_unspent_sapling_notes( &self, account: AccountId, target_value: Amount, anchor_height: BlockHeight, ) -> Result, Self::Error> { self.wallet_db - .select_spendable_sapling_notes(account, target_value, anchor_height) + .select_unspent_sapling_notes(account, target_value, anchor_height) } #[cfg(feature = "transparent-inputs")] - fn get_spendable_transparent_utxos( + fn get_unspent_transparent_utxos( &self, address: &TransparentAddress, anchor_height: BlockHeight, ) -> Result, Self::Error> { self.wallet_db - .get_spendable_transparent_utxos(address, anchor_height) + .get_unspent_transparent_utxos(address, anchor_height) } } diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index f95c6d4267..0234f267f4 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -599,7 +599,7 @@ pub fn get_nullifiers

( } #[cfg(feature = "transparent-inputs")] -pub fn get_spendable_transparent_utxos( +pub fn get_unspent_transparent_utxos( wdb: &WalletDb

, address: &TransparentAddress, anchor_height: BlockHeight, diff --git a/zcash_client_sqlite/src/wallet/transact.rs b/zcash_client_sqlite/src/wallet/transact.rs index 35ed92f0c4..db313c73f3 100644 --- a/zcash_client_sqlite/src/wallet/transact.rs +++ b/zcash_client_sqlite/src/wallet/transact.rs @@ -59,7 +59,7 @@ fn to_spendable_note(row: &Row) -> Result { }) } -pub fn get_spendable_sapling_notes

( +pub fn get_unspent_sapling_notes

( wdb: &WalletDb

, account: AccountId, anchor_height: BlockHeight, @@ -87,7 +87,7 @@ pub fn get_spendable_sapling_notes

( notes.collect::>() } -pub fn select_spendable_sapling_notes

( +pub fn select_unspent_sapling_notes

( wdb: &WalletDb

, account: AccountId, target_value: Amount, From b88ee47e36f20cc9d19e143877beb73f54275348 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 12 Feb 2021 19:09:54 -0700 Subject: [PATCH 0005/2028] Add confirmation depth to shield_funds --- zcash_client_backend/src/data_api/wallet.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 226887cc0b..7d69c72031 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -266,6 +266,7 @@ where } #[cfg(feature = "transparent-inputs")] +#[allow(clippy::too_many_arguments)] pub fn shield_funds( wallet_db: &mut D, params: &P, @@ -274,6 +275,7 @@ pub fn shield_funds( sk: &secp256k1::SecretKey, extsk: &ExtendedSpendingKey, memo: &MemoBytes, + confirmations: u32, ) -> Result where E: From>, @@ -296,7 +298,7 @@ where let ovk = exfvk.fvk.ovk; // get UTXOs from DB - let utxos = wallet_db.get_unspent_transparent_utxos(&taddr, latest_anchor)?; + let utxos = wallet_db.get_unspent_transparent_utxos(&taddr, latest_anchor - confirmations)?; let total_amount = utxos.iter().map(|utxo| utxo.value).sum::(); let fee = DEFAULT_FEE; From 8828276361e02155f49da31d2cf9515231f4d26b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 26 Mar 2021 18:39:43 -0600 Subject: [PATCH 0006/2028] Query for unspent utxos checks to ensure that spending tx is mined. Also make it an error to try to send a memo to a transparent address. --- zcash_client_backend/Cargo.toml | 7 +-- zcash_client_backend/src/data_api/error.rs | 8 +++ zcash_client_backend/src/data_api/wallet.rs | 56 ++++++++++++++------- zcash_client_backend/src/lib.rs | 3 ++ zcash_client_sqlite/src/lib.rs | 6 +++ zcash_client_sqlite/src/wallet.rs | 28 +++++++++-- zcash_primitives/src/transaction/builder.rs | 44 ++++++++++++---- 7 files changed, 118 insertions(+), 34 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index e90e5e8144..bc456e6a0e 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -22,6 +22,7 @@ group = "0.8" hex = "0.4" hdwallet = { version = "0.3.0", optional = true } jubjub = "0.5.1" +log = "0.4" nom = "6.1" percent-encoding = "2.1.0" proptest = { version = "0.10.1", optional = true } @@ -29,7 +30,7 @@ protobuf = "2.20" rand_core = "0.5.1" ripemd160 = { version = "0.9.1", optional = true } secp256k1 = { version = "0.19", optional = true } -sha2 = "0.9" +sha2 = { version = "0.9", optional = true } subtle = "2.2.3" time = "0.2" zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" } @@ -47,8 +48,8 @@ zcash_client_sqlite = { version = "0.3", path = "../zcash_client_sqlite" } zcash_proofs = { version = "0.5", path = "../zcash_proofs" } [features] -transparent-inputs = ["ripemd160", "secp256k1"] -test-dependencies = ["proptest", "zcash_primitives/test-dependencies", "hdwallet"] +transparent-inputs = ["ripemd160", "hdwallet", "sha2", "secp256k1"] +test-dependencies = ["proptest", "zcash_primitives/test-dependencies", "hdwallet", "sha2"] [badges] maintenance = { status = "actively-developed" } diff --git a/zcash_client_backend/src/data_api/error.rs b/zcash_client_backend/src/data_api/error.rs index 086daee405..e679eba7c6 100644 --- a/zcash_client_backend/src/data_api/error.rs +++ b/zcash_client_backend/src/data_api/error.rs @@ -59,6 +59,12 @@ pub enum Error { /// The wallet attempted a sapling-only operation at a block /// height when Sapling was not yet active. SaplingNotActive, + + /// A memo is required when constructing a Sapling output + MemoRequired, + + /// It is forbidden to provide a memo when constructing a transparent output. + MemoForbidden, } impl ChainInvalid { @@ -99,6 +105,8 @@ impl fmt::Display for Error { Error::Builder(e) => write!(f, "{:?}", e), Error::Protobuf(e) => write!(f, "{}", e), Error::SaplingNotActive => write!(f, "Could not determine Sapling upgrade activation height."), + Error::MemoRequired => write!(f, "A memo is required when sending to a Sapling address."), + Error::MemoForbidden => write!(f, "It is not possible to send a memo to a transparent address."), } } } diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 7d69c72031..bcd31227e7 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -1,6 +1,5 @@ -//! Functions for scanning the chain and extracting relevant information. +use std::convert::TryFrom; use std::fmt::Debug; - use zcash_primitives::{ consensus::{self, BranchId, NetworkUpgrade}, memo::MemoBytes, @@ -40,6 +39,8 @@ where P: consensus::Parameters, D: WalletWrite, { + debug!("decrypt_and_store: {:?}", tx); + // Fetch the ExtendedFullViewingKeys we are tracking let extfvks = data.get_extended_full_viewing_keys()?; @@ -54,16 +55,32 @@ where .ok_or(Error::SaplingNotActive)?; let outputs = decrypt_transaction(params, height, tx, &extfvks); - if outputs.is_empty() { - Ok(()) - } else { + if !outputs.is_empty() { data.store_received_tx(&ReceivedTransaction { tx, outputs: &outputs, })?; + } - Ok(()) + // store z->t transactions in the same way the would be stored by create_spend_to_address + if !tx.vout.is_empty() { + // TODO: clarify with Kris the simplest way to determine account and iterate over outputs + // i.e. there are probably edge cases where we need to combine vouts into one "sent" transaction for the total value + data.store_sent_tx(&SentTransaction { + tx: &tx, + created: time::OffsetDateTime::now_utc(), + output_index: usize::try_from(0).unwrap(), + account: AccountId(0), + recipient_address: &RecipientAddress::Transparent( + tx.vout[0].script_pubkey.address().unwrap(), + ), + value: tx.vout[0].value, + memo: None, + utxos_spent: vec![], + })?; } + + Ok(()) } #[allow(clippy::needless_doctest_main)] @@ -224,12 +241,22 @@ where match to { RecipientAddress::Shielded(to) => { - builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) + memo.clone().ok_or(Error::MemoRequired).and_then(|memo| { + builder + .add_sapling_output(ovk, to.clone(), value, memo) + .map_err(Error::Builder) + }) } - - RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), - } - .map_err(Error::Builder)?; + RecipientAddress::Transparent(to) => { + if memo.is_some() { + Err(Error::MemoForbidden) + } else { + builder + .add_transparent_output(&to, value) + .map_err(Error::Builder) + } + } + }?; let consensus_branch_id = BranchId::for_height(params, height); let (tx, tx_metadata) = builder @@ -329,12 +356,7 @@ where // add the sapling output to shield the funds builder - .add_sapling_output( - Some(ovk), - z_address.clone(), - amount_to_shield, - Some(memo.clone()), - ) + .add_sapling_output(Some(ovk), z_address.clone(), amount_to_shield, memo.clone()) .map_err(Error::Builder)?; let consensus_branch_id = BranchId::for_height(params, latest_anchor); diff --git a/zcash_client_backend/src/lib.rs b/zcash_client_backend/src/lib.rs index 085070c134..92fab32ec6 100644 --- a/zcash_client_backend/src/lib.rs +++ b/zcash_client_backend/src/lib.rs @@ -8,6 +8,9 @@ // Temporary until we have addressed all Result cases. #![allow(clippy::result_unit_err)] +#[macro_use] +extern crate log; + pub mod address; pub mod data_api; mod decrypt; diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 961953a8f1..f39f2e6883 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -149,6 +149,10 @@ impl WalletDb

{ "INSERT INTO utxos (address, prevout_txid, prevout_idx, script, value_zat, height) VALUES (:address, :prevout_txid, :prevout_idx, :script, :value_zat, :height)" )?, + #[cfg(feature = "transparent-inputs")] + stmt_delete_utxos: self.conn.prepare( + "DELETE FROM utxos WHERE address = :address AND height > :above_height" + )?, stmt_insert_received_note: self.conn.prepare( "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, memo, nf, is_change) VALUES (:tx, :output_index, :account, :diversifier, :value, :rcm, :memo, :nf, :is_change)", @@ -313,6 +317,8 @@ pub struct DataConnStmtCache<'a, P> { #[cfg(feature = "transparent-inputs")] stmt_insert_received_transparent_utxo: Statement<'a>, + #[cfg(feature = "transparent-inputs")] + stmt_delete_utxos: Statement<'a>, stmt_insert_received_note: Statement<'a>, stmt_update_received_note: Statement<'a>, stmt_select_received_note: Statement<'a>, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 0234f267f4..70fd7abf01 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -605,11 +605,13 @@ pub fn get_unspent_transparent_utxos( anchor_height: BlockHeight, ) -> Result, SqliteClientError> { let mut stmt_blocks = wdb.conn.prepare( - "SELECT address, prevout_txid, prevout_idx, script, value_zat, height - FROM utxos - WHERE address = ? - AND height <= ? - AND spent_in_tx IS NULL", + "SELECT u.address, u.prevout_txid, u.prevout_idx, u.script, u.value_zat, u.height, tx.block as block + FROM utxos u + LEFT OUTER JOIN transactions tx + ON tx.id_tx = u.spent_in_tx + WHERE u.address = ? + AND u.height <= ? + AND block IS NULL", )?; let addr_str = address.encode(&wdb.params); @@ -789,6 +791,22 @@ pub fn put_received_transparent_utxo<'a, P: consensus::Parameters>( Ok(UtxoId(stmts.wallet_db.conn.last_insert_rowid())) } +#[cfg(feature = "transparent-inputs")] +pub fn delete_utxos_above<'a, P: consensus::Parameters>( + stmts: &mut DataConnStmtCache<'a, P>, + taddr: &TransparentAddress, + height: BlockHeight, +) -> Result { + let sql_args: &[(&str, &dyn ToSql)] = &[ + (&":address", &taddr.encode(&stmts.wallet_db.params)), + (&":above_height", &u32::from(height)), + ]; + + let rows = stmts.stmt_delete_utxos.execute_named(&sql_args)?; + + Ok(rows) +} + // Assumptions: // - A transaction will not contain more than 2^63 shielded outputs. // - A note value will never exceed 2^63 zatoshis. diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index db58da4ed3..c2a9fc0a3b 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -110,7 +110,7 @@ impl SaplingOutput

{ ovk: Option, to: PaymentAddress, value: Amount, - memo: Option, + memo: MemoBytes, ) -> Result { Self::new_internal(params, height, rng, ovk, to, value, memo) } @@ -122,7 +122,7 @@ impl SaplingOutput

{ ovk: Option, to: PaymentAddress, value: Amount, - memo: Option, + memo: MemoBytes, ) -> Result { let g_d = to.g_d().ok_or(Error::InvalidAddress)?; if value.is_negative() { @@ -142,7 +142,7 @@ impl SaplingOutput

{ ovk, to, note, - memo: memo.unwrap_or_else(MemoBytes::empty), + memo, _params: PhantomData::default(), }) } @@ -521,7 +521,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { ovk: Option, to: PaymentAddress, value: Amount, - memo: Option, + memo: MemoBytes, ) -> Result<(), Error> { let output = SaplingOutput::new_internal( &self.params, @@ -645,7 +645,12 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { return Err(Error::NoChangeAddress); }; - self.add_sapling_output(Some(change_address.0), change_address.1, change, None)?; + self.add_sapling_output( + Some(change_address.0), + change_address.1, + change, + MemoBytes::empty(), + )?; } // @@ -965,6 +970,7 @@ mod tests { use crate::{ consensus::{self, Parameters, H0, TEST_NETWORK}, legacy::TransparentAddress, + memo::MemoBytes, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{prover::mock::MockTxProver, Node, Rseed}, transaction::components::{amount::Amount, amount::DEFAULT_FEE}, @@ -985,7 +991,12 @@ mod tests { let mut builder = Builder::new(TEST_NETWORK, H0); assert_eq!( - builder.add_sapling_output(Some(ovk), to, Amount::from_i64(-1).unwrap(), None), + builder.add_sapling_output( + Some(ovk), + to, + Amount::from_i64(-1).unwrap(), + MemoBytes::empty() + ), Err(Error::InvalidAmount) ); } @@ -1104,7 +1115,12 @@ mod tests { { let mut builder = Builder::new(TEST_NETWORK, H0); builder - .add_sapling_output(ovk, to.clone(), Amount::from_u64(50000).unwrap(), None) + .add_sapling_output( + ovk, + to.clone(), + Amount::from_u64(50000).unwrap(), + MemoBytes::empty(), + ) .unwrap(); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), @@ -1153,7 +1169,12 @@ mod tests { ) .unwrap(); builder - .add_sapling_output(ovk, to.clone(), Amount::from_u64(30000).unwrap(), None) + .add_sapling_output( + ovk, + to.clone(), + Amount::from_u64(30000).unwrap(), + MemoBytes::empty(), + ) .unwrap(); builder .add_transparent_output( @@ -1194,7 +1215,12 @@ mod tests { .add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap()) .unwrap(); builder - .add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None) + .add_sapling_output( + ovk, + to, + Amount::from_u64(30000).unwrap(), + MemoBytes::empty(), + ) .unwrap(); builder .add_transparent_output( From 13cd7498b7c7013389254f3c252f97bf34d24f7b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Mar 2021 14:35:18 -0600 Subject: [PATCH 0007/2028] Store vout as part of store_decrypted_tx --- zcash_client_backend/src/data_api.rs | 15 ++++---- zcash_client_backend/src/data_api/wallet.rs | 31 ++++----------- zcash_client_sqlite/src/lib.rs | 42 ++++++++++++++++++--- zcash_client_sqlite/src/wallet.rs | 37 ++++++------------ 4 files changed, 63 insertions(+), 62 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 4f2e2efa95..9df7ada77f 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -204,9 +204,10 @@ pub struct PrunedBlock<'a> { /// /// The purpose of this struct is to permit atomic updates of the /// wallet database when transactions are successfully decrypted. -pub struct ReceivedTransaction<'a> { +pub struct DecryptedTransaction<'a> { pub tx: &'a Transaction, - pub outputs: &'a Vec, + pub account_id: AccountId, + pub sapling_outputs: &'a Vec, } /// A transaction that was constructed and sent by the wallet. @@ -241,9 +242,9 @@ pub trait WalletWrite: WalletRead { updated_witnesses: &[(Self::NoteRef, IncrementalWitness)], ) -> Result)>, Self::Error>; - fn store_received_tx( + fn store_decrypted_tx( &mut self, - received_tx: &ReceivedTransaction, + received_tx: &DecryptedTransaction, ) -> Result; fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result; @@ -302,7 +303,7 @@ pub mod testing { }; use super::{ - error::Error, BlockSource, PrunedBlock, ReceivedTransaction, SentTransaction, WalletRead, + error::Error, BlockSource, DecryptedTransaction, PrunedBlock, SentTransaction, WalletRead, WalletWrite, }; @@ -432,9 +433,9 @@ pub mod testing { Ok(vec![]) } - fn store_received_tx( + fn store_decrypted_tx( &mut self, - _received_tx: &ReceivedTransaction, + _received_tx: &DecryptedTransaction, ) -> Result { Ok(TxId([0u8; 32])) } diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index bcd31227e7..be92caecac 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -1,4 +1,3 @@ -use std::convert::TryFrom; use std::fmt::Debug; use zcash_primitives::{ consensus::{self, BranchId, NetworkUpgrade}, @@ -14,7 +13,7 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, - data_api::{error::Error, ReceivedTransaction, SentTransaction, WalletWrite}, + data_api::{error::Error, DecryptedTransaction, SentTransaction, WalletWrite}, decrypt_transaction, wallet::{AccountId, OvkPolicy}, }; @@ -54,29 +53,13 @@ where .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) .ok_or(Error::SaplingNotActive)?; - let outputs = decrypt_transaction(params, height, tx, &extfvks); - if !outputs.is_empty() { - data.store_received_tx(&ReceivedTransaction { - tx, - outputs: &outputs, - })?; - } + let sapling_outputs = decrypt_transaction(params, height, tx, &extfvks); - // store z->t transactions in the same way the would be stored by create_spend_to_address - if !tx.vout.is_empty() { - // TODO: clarify with Kris the simplest way to determine account and iterate over outputs - // i.e. there are probably edge cases where we need to combine vouts into one "sent" transaction for the total value - data.store_sent_tx(&SentTransaction { - tx: &tx, - created: time::OffsetDateTime::now_utc(), - output_index: usize::try_from(0).unwrap(), - account: AccountId(0), - recipient_address: &RecipientAddress::Transparent( - tx.vout[0].script_pubkey.address().unwrap(), - ), - value: tx.vout[0].value, - memo: None, - utxos_spent: vec![], + if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { + data.store_decrypted_tx(&DecryptedTransaction { + tx, + account_id: AccountId(0), //FIXME + sapling_outputs: &sapling_outputs, })?; } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index f39f2e6883..a004ee3034 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -49,8 +49,9 @@ use zcash_primitives::{ }; use zcash_client_backend::{ + address::RecipientAddress, data_api::{ - BlockSource, PrunedBlock, ReceivedTransaction, SentTransaction, WalletRead, WalletWrite, + BlockSource, DecryptedTransaction, PrunedBlock, SentTransaction, WalletRead, WalletWrite, }, encoding::encode_payment_address, proto::compact_formats::CompactBlock, @@ -513,21 +514,50 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { }) } - fn store_received_tx( + fn store_decrypted_tx( &mut self, - received_tx: &ReceivedTransaction, + d_tx: &DecryptedTransaction, ) -> Result { self.transactionally(|up| { - let tx_ref = wallet::put_tx_data(up, received_tx.tx, None)?; + let tx_ref = wallet::put_tx_data(up, d_tx.tx, None)?; - for output in received_tx.outputs { + for output in d_tx.sapling_outputs { if output.outgoing { - wallet::put_sent_note(up, output, tx_ref)?; + wallet::put_sent_note( + up, + tx_ref, + output.index, + output.account, + RecipientAddress::Shielded(output.to.clone()), + Amount::from_u64(output.note.value).map_err(|_| { + SqliteClientError::CorruptedData("Note value invalid.".to_string()) + })?, + Some(&output.memo), + )?; } else { wallet::put_received_note(up, output, tx_ref)?; } } + // store received z->t transactions in the same way they would be stored by + // create_spend_to_address If there are any of our shielded inputs, we interpret this + // as our z->t tx and store the vouts as our sent notes. + // FIXME this is a weird heuristic that is bound to trip us up somewhere. + if d_tx.sapling_outputs.iter().any(|output| !output.outgoing) { + for (i, txout) in d_tx.tx.vout.iter().enumerate() { + // FIXME: We shouldn't be confusing notes and transparent outputs. + wallet::insert_sent_note( + up, + tx_ref, + i, + d_tx.account_id, + &RecipientAddress::Transparent(txout.script_pubkey.address().unwrap()), + txout.value, + None, + )?; + } + } + Ok(tx_ref) }) } diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 70fd7abf01..468689f451 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -30,7 +30,6 @@ use zcash_client_backend::{ data_api::error::Error, encoding::{ decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key, - encode_payment_address, }, wallet::{AccountId, WalletShieldedOutput, WalletTx}, DecryptedOutput, @@ -899,38 +898,26 @@ pub fn update_expired_notes

( /// Records information about a note that your wallet created. pub fn put_sent_note<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, - output: &DecryptedOutput, tx_ref: i64, + output_index: usize, + account: AccountId, + to: RecipientAddress, + value: Amount, + memo: Option<&MemoBytes>, ) -> Result<(), SqliteClientError> { - let output_index = output.index as i64; - let account = output.account.0 as i64; - let value = output.note.value as i64; - let to_str = encode_payment_address( - stmts.wallet_db.params.hrp_sapling_payment_address(), - &output.to, - ); - + let ivalue: i64 = value.into(); // Try updating an existing sent note. if stmts.stmt_update_sent_note.execute(params![ - account, - to_str, - value, - &output.memo.as_slice(), + account.0 as i64, + to.encode(&stmts.wallet_db.params), + ivalue, + &memo.map(|m| m.as_slice()), tx_ref, - output_index + output_index as i64 ])? == 0 { // It isn't there, so insert. - insert_sent_note( - stmts, - tx_ref, - output.index, - output.account, - &RecipientAddress::Shielded(output.to.clone()), - Amount::from_u64(output.note.value) - .map_err(|_| SqliteClientError::CorruptedData("Note value invalid.".to_string()))?, - Some(&output.memo), - )? + insert_sent_note(stmts, tx_ref, output_index, account, &to, value, memo)? } Ok(()) From 665c4c7aff3743fce38e7527b553197c51ffa65e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 29 Mar 2021 14:55:15 -0600 Subject: [PATCH 0008/2028] Figure out the account ID for z->t spends. --- zcash_client_backend/src/data_api.rs | 1 - zcash_client_backend/src/data_api/wallet.rs | 1 - zcash_client_sqlite/src/lib.rs | 40 +++++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 9df7ada77f..b2c3c02174 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -206,7 +206,6 @@ pub struct PrunedBlock<'a> { /// wallet database when transactions are successfully decrypted. pub struct DecryptedTransaction<'a> { pub tx: &'a Transaction, - pub account_id: AccountId, pub sapling_outputs: &'a Vec, } diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index be92caecac..9862225a45 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -58,7 +58,6 @@ where if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { data.store_decrypted_tx(&DecryptedTransaction { tx, - account_id: AccountId(0), //FIXME sapling_outputs: &sapling_outputs, })?; } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index a004ee3034..cc71b03ec7 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -521,6 +521,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { self.transactionally(|up| { let tx_ref = wallet::put_tx_data(up, d_tx.tx, None)?; + let mut spending_account_id: Option = None; for output in d_tx.sapling_outputs { if output.outgoing { wallet::put_sent_note( @@ -529,12 +530,21 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { output.index, output.account, RecipientAddress::Shielded(output.to.clone()), - Amount::from_u64(output.note.value).map_err(|_| { - SqliteClientError::CorruptedData("Note value invalid.".to_string()) - })?, + Amount::from_u64(output.note.value) + .map_err(|_| SqliteClientError::CorruptedData("Note value invalid.".to_string()))?, Some(&output.memo), )?; } else { + match spending_account_id { + Some(id) => + if id != output.account { + panic!("Unable to determine a unique account identifier for z->t spend."); + } + None => { + spending_account_id = Some(output.account); + } + } + wallet::put_received_note(up, output, tx_ref)?; } } @@ -544,17 +554,19 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // as our z->t tx and store the vouts as our sent notes. // FIXME this is a weird heuristic that is bound to trip us up somewhere. if d_tx.sapling_outputs.iter().any(|output| !output.outgoing) { - for (i, txout) in d_tx.tx.vout.iter().enumerate() { - // FIXME: We shouldn't be confusing notes and transparent outputs. - wallet::insert_sent_note( - up, - tx_ref, - i, - d_tx.account_id, - &RecipientAddress::Transparent(txout.script_pubkey.address().unwrap()), - txout.value, - None, - )?; + if let Some(account_id) = spending_account_id { + for (i, txout) in d_tx.tx.vout.iter().enumerate() { + // FIXME: We shouldn't be confusing notes and transparent outputs. + wallet::put_sent_note( + up, + tx_ref, + i, + account_id, + RecipientAddress::Transparent(txout.script_pubkey.address().unwrap()), + txout.value, + None, + )?; + } } } From 5595ca225c7551bb87e37e346aded24745c30a74 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Wed, 31 Mar 2021 21:04:38 -0400 Subject: [PATCH 0009/2028] Add public key derivations and related tests. --- zcash_client_backend/src/keys.rs | 94 ++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index d6def40701..09cf7eff7b 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -6,7 +6,7 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey}; use { crate::wallet::AccountId, bs58::{self, decode::Error as Bs58Error}, - hdwallet::{ExtendedPrivKey, KeyIndex}, + hdwallet::{ExtendedPrivKey, ExtendedPubKey, KeyIndex}, secp256k1::{key::PublicKey, key::SecretKey, Secp256k1}, sha2::{Digest, Sha256}, std::convert::TryInto, @@ -50,8 +50,15 @@ pub fn derive_transparent_address_from_secret_key( ) -> TransparentAddress { let secp = Secp256k1::new(); let pk = PublicKey::from_secret_key(&secp, &secret_key); + derive_transparent_address_from_public_key(pk) +} + +#[cfg(feature = "transparent-inputs")] +pub fn derive_transparent_address_from_public_key( + public_key: secp256k1::key::PublicKey, +) -> TransparentAddress { let mut hash160 = ripemd160::Ripemd160::new(); - hash160.update(Sha256::digest(&pk.serialize()[..].to_vec())); + hash160.update(Sha256::digest(&public_key.serialize()[..].to_vec())); TransparentAddress::PublicKey(*hash160.finalize().as_ref()) } @@ -62,15 +69,37 @@ pub fn derive_secret_key_from_seed( account: AccountId, index: u32, ) -> Result { - let ext_t_key = ExtendedPrivKey::with_seed(&seed)?; - let private_key = ext_t_key + let private_key = + derive_extended_private_key_from_seed(params, seed, account, index)?.private_key; + Ok(private_key) +} + +#[cfg(feature = "transparent-inputs")] +pub fn derive_public_key_from_seed( + params: &P, + seed: &[u8], + account: AccountId, + index: u32, +) -> Result { + let private_key = derive_extended_private_key_from_seed(params, seed, account, index)?; + let pub_key = ExtendedPubKey::from_private_key(&private_key); + Ok(pub_key.public_key) +} + +#[cfg(feature = "transparent-inputs")] +pub fn derive_extended_private_key_from_seed( + params: &P, + seed: &[u8], + account: AccountId, + index: u32, +) -> Result { + let pk = ExtendedPrivKey::with_seed(&seed)?; + let private_key = pk .derive_private_key(KeyIndex::hardened_from_normalize_index(44)?)? .derive_private_key(KeyIndex::hardened_from_normalize_index(params.coin_type())?)? .derive_private_key(KeyIndex::hardened_from_normalize_index(account.0)?)? .derive_private_key(KeyIndex::Normal(0))? - .derive_private_key(KeyIndex::Normal(index))? - .private_key; - + .derive_private_key(KeyIndex::Normal(index))?; Ok(private_key) } @@ -112,13 +141,22 @@ mod tests { #[cfg(feature = "transparent-inputs")] use { - super::{derive_secret_key_from_seed, derive_transparent_address_from_secret_key, Wif}, + super::{ + derive_public_key_from_seed, derive_secret_key_from_seed, + derive_transparent_address_from_public_key, derive_transparent_address_from_secret_key, + Wif, + }, crate::{encoding::AddressCodec, wallet::AccountId}, secp256k1::key::SecretKey, std::convert::TryInto, zcash_primitives::consensus::MAIN_NETWORK, }; + fn seed() -> Vec { + let seed_hex = "6ef5f84def6f4b9d38f466586a8380a38593bd47c8cda77f091856176da47f26b5bd1c8d097486e5635df5a66e820d28e1d73346f499801c86228d43f390304f"; + hex::decode(&seed_hex).unwrap() + } + #[test] #[should_panic] fn spending_key_panics_on_short_seed() { @@ -128,11 +166,10 @@ mod tests { #[cfg(feature = "transparent-inputs")] #[test] fn sk_to_wif() { - let seed_hex = "6ef5f84def6f4b9d38f466586a8380a38593bd47c8cda77f091856176da47f26b5bd1c8d097486e5635df5a66e820d28e1d73346f499801c86228d43f390304f"; - let seed = hex::decode(&seed_hex).unwrap(); - let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed, AccountId(0), 0).unwrap(); + let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); + let wif = Wif::from_secret_key(&sk, true).0; assert_eq!( - Wif::from_secret_key(&sk, true).0, + wif, "L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string() ); } @@ -140,14 +177,9 @@ mod tests { #[cfg(feature = "transparent-inputs")] #[test] fn sk_to_taddr() { - let seed_hex = "6ef5f84def6f4b9d38f466586a8380a38593bd47c8cda77f091856176da47f26b5bd1c8d097486e5635df5a66e820d28e1d73346f499801c86228d43f390304f"; - let seed = hex::decode(&seed_hex).unwrap(); - let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed, AccountId(0), 0).unwrap(); - let taddr = derive_transparent_address_from_secret_key(sk); - assert_eq!( - taddr.encode(&MAIN_NETWORK), - "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string() - ); + let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); + let taddr = derive_transparent_address_from_secret_key(sk).encode(&MAIN_NETWORK); + assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); } #[cfg(feature = "transparent-inputs")] @@ -155,10 +187,26 @@ mod tests { fn sk_wif_to_taddr() { let sk_wif = Wif("L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()); let sk: SecretKey = (&sk_wif).try_into().expect("invalid wif"); - let taddr = derive_transparent_address_from_secret_key(sk); + let taddr = derive_transparent_address_from_secret_key(sk).encode(&MAIN_NETWORK); + assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); + } + + #[cfg(feature = "transparent-inputs")] + #[test] + fn pk_from_seed() { + let pk = derive_public_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); + let hex_value = hex::encode(&pk.serialize()); assert_eq!( - taddr.encode(&MAIN_NETWORK), - "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string() + hex_value, + "03b1d7fb28d17c125b504d06b1530097e0a3c76ada184237e3bc0925041230a5af".to_string() ); } + + #[cfg(feature = "transparent-inputs")] + #[test] + fn pk_to_taddr() { + let pk = derive_public_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); + let taddr = derive_transparent_address_from_public_key(pk).encode(&MAIN_NETWORK); + assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); + } } From bdf56925e9633370bfaabf223b231f40d75548f7 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Thu, 1 Apr 2021 01:54:46 -0400 Subject: [PATCH 0010/2028] Insert into accounts table with taddrs. --- zcash_client_sqlite/src/wallet/init.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 572e54b2bd..a55f8c609f 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -2,13 +2,9 @@ use rusqlite::{types::ToSql, NO_PARAMS}; -use zcash_primitives::{ - block::BlockHash, - consensus::{self, BlockHeight}, - zip32::ExtendedFullViewingKey, -}; +use zcash_primitives::{block::BlockHash, consensus::{self, BlockHeight}, legacy::TransparentAddress, zip32::ExtendedFullViewingKey}; -use zcash_client_backend::encoding::encode_extended_full_viewing_key; +use zcash_client_backend::encoding::{encode_extended_full_viewing_key, AddressCodec}; use crate::{address_from_extfvk, error::SqliteClientError, WalletDb}; @@ -161,6 +157,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { pub fn init_accounts_table( wdb: &WalletDb

, extfvks: &[ExtendedFullViewingKey], + taddrs: &Vec, ) -> Result<(), SqliteClientError> { let mut empty_check = wdb.conn.prepare("SELECT * FROM accounts LIMIT 1")?; if empty_check.exists(NO_PARAMS)? { @@ -169,21 +166,23 @@ pub fn init_accounts_table( // Insert accounts atomically wdb.conn.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - for (account, extfvk) in extfvks.iter().enumerate() { + for (account, (extfvk, taddr)) in extfvks.iter().zip(taddrs.iter()).enumerate() { let extfvk_str = encode_extended_full_viewing_key( wdb.params.hrp_sapling_extended_full_viewing_key(), extfvk, ); let address_str = address_from_extfvk(&wdb.params, extfvk); + let taddress_str: String = taddr.encode(&wdb.params); wdb.conn.execute( - "INSERT INTO accounts (account, extfvk, address) - VALUES (?, ?, ?)", + "INSERT INTO accounts (account, extfvk, address, transparent_address) + VALUES (?, ?, ?, ?)", &[ (account as u32).to_sql()?, extfvk_str.to_sql()?, address_str.to_sql()?, + taddress_str.to_sql()?, ], )?; } From 8e16d93f942f63bdb9e6d414fd16077f004544c2 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Wed, 31 Mar 2021 17:59:36 -0400 Subject: [PATCH 0011/2028] Update accounts table create statement. This PR makes the opinionated change that T-addrs are required to be supported when using the zcash_client_sqlite backend. --- zcash_client_backend/src/data_api/wallet.rs | 4 +- zcash_client_backend/src/encoding.rs | 6 +- zcash_client_backend/src/keys.rs | 29 +++++----- zcash_client_sqlite/Cargo.toml | 6 +- zcash_client_sqlite/src/chain.rs | 42 ++++---------- zcash_client_sqlite/src/lib.rs | 40 ++++++++++---- zcash_client_sqlite/src/wallet.rs | 19 +------ zcash_client_sqlite/src/wallet/init.rs | 61 ++++++++++++++------- zcash_client_sqlite/src/wallet/transact.rs | 50 +++++++++++------ 9 files changed, 140 insertions(+), 117 deletions(-) diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 9862225a45..e0a4a37ff0 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -128,7 +128,7 @@ where /// }; /// /// let account = AccountId(0); -/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account.0); +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); /// let to = extsk.default_address().unwrap().1.into(); /// /// let data_file = NamedTempFile::new().unwrap(); @@ -297,7 +297,7 @@ where .and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?; // derive the corresponding t-address - let taddr = derive_transparent_address_from_secret_key(*sk); + let taddr = derive_transparent_address_from_secret_key(sk); // derive own shielded address from the provided extended spending key let z_address = extsk.default_address().unwrap().1; diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 18cda72922..520e3e0719 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -104,9 +104,10 @@ impl AddressCodec

for TransparentAddress { /// use zcash_client_backend::{ /// encoding::encode_extended_spending_key, /// keys::spending_key, +/// wallet::AccountId, /// }; /// -/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0); +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, AccountId(0)); /// let encoded = encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk); /// ``` /// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey @@ -135,10 +136,11 @@ pub fn decode_extended_spending_key( /// use zcash_client_backend::{ /// encoding::encode_extended_full_viewing_key, /// keys::spending_key, +/// wallet::AccountId, /// }; /// use zcash_primitives::zip32::ExtendedFullViewingKey; /// -/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0); +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, AccountId(0)); /// let extfvk = ExtendedFullViewingKey::from(&extsk); /// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk); /// ``` diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index 09cf7eff7b..152c12b0a3 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -24,12 +24,15 @@ use { /// /// ``` /// use zcash_primitives::{constants::testnet::COIN_TYPE}; -/// use zcash_client_backend::{keys::spending_key}; +/// use zcash_client_backend::{ +/// keys::spending_key, +/// wallet::AccountId, +/// }; /// -/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0); +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, AccountId(0)); /// ``` /// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey -pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey { +pub fn spending_key(seed: &[u8], coin_type: u32, account: AccountId) -> ExtendedSpendingKey { if seed.len() < 32 { panic!("ZIP 32 seeds MUST be at least 32 bytes"); } @@ -39,26 +42,26 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi &[ ChildIndex::Hardened(32), ChildIndex::Hardened(coin_type), - ChildIndex::Hardened(account), + ChildIndex::Hardened(account.0), ], ) } #[cfg(feature = "transparent-inputs")] pub fn derive_transparent_address_from_secret_key( - secret_key: secp256k1::key::SecretKey, + secret_key: &secp256k1::key::SecretKey, ) -> TransparentAddress { let secp = Secp256k1::new(); - let pk = PublicKey::from_secret_key(&secp, &secret_key); - derive_transparent_address_from_public_key(pk) + let pk = PublicKey::from_secret_key(&secp, secret_key); + derive_transparent_address_from_public_key(&pk) } #[cfg(feature = "transparent-inputs")] pub fn derive_transparent_address_from_public_key( - public_key: secp256k1::key::PublicKey, + public_key: &secp256k1::key::PublicKey, ) -> TransparentAddress { let mut hash160 = ripemd160::Ripemd160::new(); - hash160.update(Sha256::digest(&public_key.serialize()[..].to_vec())); + hash160.update(Sha256::digest(&public_key.serialize())); TransparentAddress::PublicKey(*hash160.finalize().as_ref()) } @@ -160,7 +163,7 @@ mod tests { #[test] #[should_panic] fn spending_key_panics_on_short_seed() { - let _ = spending_key(&[0; 31][..], 0, 0); + let _ = spending_key(&[0; 31][..], 0, AccountId(0)); } #[cfg(feature = "transparent-inputs")] @@ -178,7 +181,7 @@ mod tests { #[test] fn sk_to_taddr() { let sk = derive_secret_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); - let taddr = derive_transparent_address_from_secret_key(sk).encode(&MAIN_NETWORK); + let taddr = derive_transparent_address_from_secret_key(&sk).encode(&MAIN_NETWORK); assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); } @@ -187,7 +190,7 @@ mod tests { fn sk_wif_to_taddr() { let sk_wif = Wif("L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2".to_string()); let sk: SecretKey = (&sk_wif).try_into().expect("invalid wif"); - let taddr = derive_transparent_address_from_secret_key(sk).encode(&MAIN_NETWORK); + let taddr = derive_transparent_address_from_secret_key(&sk).encode(&MAIN_NETWORK); assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); } @@ -206,7 +209,7 @@ mod tests { #[test] fn pk_to_taddr() { let pk = derive_public_key_from_seed(&MAIN_NETWORK, &seed(), AccountId(0), 0).unwrap(); - let taddr = derive_transparent_address_from_public_key(pk).encode(&MAIN_NETWORK); + let taddr = derive_transparent_address_from_public_key(&pk).encode(&MAIN_NETWORK); assert_eq!(taddr, "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4".to_string()); } } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index ef7fb0853a..720ff385a4 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -21,9 +21,10 @@ jubjub = "0.5.1" protobuf = "2.20" rand_core = "0.5.1" rusqlite = { version = "0.24", features = ["bundled", "time"] } +secp256k1 = { version = "0.19" } time = "0.2" -zcash_client_backend = { version = "0.5", path = "../zcash_client_backend" } -zcash_primitives = { version = "0.5", path = "../zcash_primitives" } +zcash_client_backend = { version = "0.5", path = "../zcash_client_backend", features = ["transparent-inputs"] } +zcash_primitives = { version = "0.5", path = "../zcash_primitives", features = ["transparent-inputs"] } [dev-dependencies] rand_core = "0.5.1" @@ -32,5 +33,4 @@ zcash_proofs = { version = "0.5", path = "../zcash_proofs" } [features] mainnet = [] -transparent-inputs = [] test-dependencies = ["zcash_client_backend/test-dependencies"] diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index b416ebe889..90bc8fde90 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -69,9 +69,7 @@ mod tests { use tempfile::NamedTempFile; use zcash_primitives::{ - block::BlockHash, - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + block::BlockHash, transaction::components::Amount, zip32::ExtendedSpendingKey, }; use zcash_client_backend::data_api::WalletRead; @@ -84,14 +82,10 @@ mod tests { chain::init::init_cache_database, error::SqliteClientError, tests::{ - self, fake_compact_block, fake_compact_block_spending, insert_into_cache, - sapling_activation_height, - }, - wallet::{ - get_balance, - init::{init_accounts_table, init_wallet_db}, - rewind_to_height, + self, fake_compact_block, fake_compact_block_spending, init_test_accounts_table, + insert_into_cache, sapling_activation_height, }, + wallet::{get_balance, init::init_wallet_db, rewind_to_height}, AccountId, BlockDb, NoteId, WalletDb, }; @@ -106,9 +100,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Empty chain should be valid validate_chain( @@ -187,9 +179,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Create some fake CompactBlocks let (cb, _) = fake_compact_block( @@ -259,9 +249,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Create some fake CompactBlocks let (cb, _) = fake_compact_block( @@ -331,9 +319,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Account balance should be zero assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), Amount::zero()); @@ -390,9 +376,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Create a block with height SAPLING_ACTIVATION_HEIGHT let value = Amount::from_u64(50000).unwrap(); @@ -451,9 +435,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Account balance should be zero assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), Amount::zero()); @@ -499,9 +481,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let (extfvk, _taddr) = init_test_accounts_table(&db_data); // Account balance should be zero assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), Amount::zero()); diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index cc71b03ec7..f902ea2fe5 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -60,7 +60,6 @@ use zcash_client_backend::{ use crate::error::SqliteClientError; -#[cfg(feature = "transparent-inputs")] use { zcash_client_backend::wallet::WalletTransparentOutput, zcash_primitives::legacy::TransparentAddress, @@ -89,7 +88,6 @@ impl fmt::Display for NoteId { /// A newtype wrapper for sqlite primary key values for the utxos /// table. -#[cfg(feature = "transparent-inputs")] #[derive(Debug, Copy, Clone)] pub struct UtxoId(pub i64); @@ -145,12 +143,10 @@ impl WalletDb

{ WHERE prevout_txid = :prevout_txid AND prevout_idx = :prevout_idx" )?, - #[cfg(feature = "transparent-inputs")] stmt_insert_received_transparent_utxo: self.conn.prepare( "INSERT INTO utxos (address, prevout_txid, prevout_idx, script, value_zat, height) VALUES (:address, :prevout_txid, :prevout_idx, :script, :value_zat, :height)" )?, - #[cfg(feature = "transparent-inputs")] stmt_delete_utxos: self.conn.prepare( "DELETE FROM utxos WHERE address = :address AND height > :above_height" )?, @@ -285,7 +281,6 @@ impl WalletRead for WalletDb

{ wallet::transact::select_unspent_sapling_notes(&self, account, target_value, anchor_height) } - #[cfg(feature = "transparent-inputs")] fn get_unspent_transparent_utxos( &self, address: &TransparentAddress, @@ -316,9 +311,7 @@ pub struct DataConnStmtCache<'a, P> { stmt_mark_sapling_note_spent: Statement<'a>, stmt_mark_transparent_utxo_spent: Statement<'a>, - #[cfg(feature = "transparent-inputs")] stmt_insert_received_transparent_utxo: Statement<'a>, - #[cfg(feature = "transparent-inputs")] stmt_delete_utxos: Statement<'a>, stmt_insert_received_note: Statement<'a>, stmt_update_received_note: Statement<'a>, @@ -417,7 +410,6 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { .select_unspent_sapling_notes(account, target_value, anchor_height) } - #[cfg(feature = "transparent-inputs")] fn get_unspent_transparent_utxos( &self, address: &TransparentAddress, @@ -656,23 +648,30 @@ mod tests { use protobuf::Message; use rand_core::{OsRng, RngCore}; use rusqlite::params; + use secp256k1::key::SecretKey; - use zcash_client_backend::proto::compact_formats::{ - CompactBlock, CompactOutput, CompactSpend, CompactTx, + use zcash_client_backend::{ + keys::{ + derive_secret_key_from_seed, derive_transparent_address_from_secret_key, spending_key, + }, + proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx}, }; use zcash_primitives::{ block::BlockHash, consensus::{BlockHeight, Network, NetworkUpgrade, Parameters}, + legacy::TransparentAddress, memo::MemoBytes, sapling::{ note_encryption::sapling_note_encryption, util::generate_random_rseed, Note, Nullifier, PaymentAddress, }, transaction::components::Amount, - zip32::ExtendedFullViewingKey, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; + use crate::{wallet::init::init_accounts_table, AccountId, WalletDb}; + use super::BlockDb; #[cfg(feature = "mainnet")] @@ -699,6 +698,25 @@ mod tests { .unwrap() } + pub(crate) fn derive_test_keys_from_seed( + seed: &[u8], + account: AccountId, + ) -> (ExtendedSpendingKey, SecretKey) { + let extsk = spending_key(seed, network().coin_type(), account); + let tsk = derive_secret_key_from_seed(&network(), seed, account, 0).unwrap(); + (extsk, tsk) + } + + pub(crate) fn init_test_accounts_table( + db_data: &WalletDb, + ) -> (ExtendedFullViewingKey, TransparentAddress) { + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(db_data, &[extfvk.clone()], &[taddr.clone()]).unwrap(); + (extfvk, taddr) + } + /// Create a fake CompactBlock at the given height, containing a single output paying /// the given address. Returns the CompactBlock and the nullifier for the new note. pub(crate) fn fake_compact_block( diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 468689f451..44996372ad 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -37,7 +37,6 @@ use zcash_client_backend::{ use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb}; -#[cfg(feature = "transparent-inputs")] use { crate::UtxoId, zcash_client_backend::{encoding::AddressCodec, wallet::WalletTransparentOutput}, @@ -597,7 +596,6 @@ pub fn get_nullifiers

( Ok(res) } -#[cfg(feature = "transparent-inputs")] pub fn get_unspent_transparent_utxos( wdb: &WalletDb

, address: &TransparentAddress, @@ -769,7 +767,6 @@ pub fn mark_transparent_utxo_spent<'a, P>( Ok(()) } -#[cfg(feature = "transparent-inputs")] pub fn put_received_transparent_utxo<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, output: &WalletTransparentOutput, @@ -790,7 +787,6 @@ pub fn put_received_transparent_utxo<'a, P: consensus::Parameters>( Ok(UtxoId(stmts.wallet_db.conn.last_insert_rowid())) } -#[cfg(feature = "transparent-inputs")] pub fn delete_utxos_above<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, taddr: &TransparentAddress, @@ -957,18 +953,11 @@ pub fn insert_sent_note<'a, P: consensus::Parameters>( mod tests { use tempfile::NamedTempFile; - use zcash_primitives::{ - transaction::components::Amount, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - }; + use zcash_primitives::transaction::components::Amount; use zcash_client_backend::data_api::WalletRead; - use crate::{ - tests, - wallet::init::{init_accounts_table, init_wallet_db}, - AccountId, WalletDb, - }; + use crate::{tests, wallet::init::init_wallet_db, AccountId, WalletDb}; use super::{get_address, get_balance}; @@ -979,9 +968,7 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + tests::init_test_accounts_table(&db_data); // The account should be empty assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), Amount::zero()); diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index a55f8c609f..ac77d02868 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -2,7 +2,12 @@ use rusqlite::{types::ToSql, NO_PARAMS}; -use zcash_primitives::{block::BlockHash, consensus::{self, BlockHeight}, legacy::TransparentAddress, zip32::ExtendedFullViewingKey}; +use zcash_primitives::{ + block::BlockHash, + consensus::{self, BlockHeight}, + legacy::TransparentAddress, + zip32::ExtendedFullViewingKey, +}; use zcash_client_backend::encoding::{encode_extended_full_viewing_key, AddressCodec}; @@ -29,7 +34,8 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { "CREATE TABLE IF NOT EXISTS accounts ( account INTEGER PRIMARY KEY, extfvk TEXT NOT NULL, - address TEXT NOT NULL + address TEXT NOT NULL, + transparent_address TEXT NOT NULL )", NO_PARAMS, )?; @@ -133,10 +139,15 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// use tempfile::NamedTempFile; /// /// use zcash_primitives::{ -/// consensus::Network, +/// consensus::{Network, Parameters}, /// zip32::{ExtendedFullViewingKey, ExtendedSpendingKey} /// }; /// +/// use zcash_client_backend::{ +/// keys::{spending_key, derive_transparent_address_from_secret_key, derive_secret_key_from_seed}, +/// wallet::AccountId, +/// }; +/// /// use zcash_client_sqlite::{ /// WalletDb, /// wallet::init::{init_accounts_table, init_wallet_db} @@ -146,9 +157,13 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// let db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap(); /// init_wallet_db(&db_data).unwrap(); /// -/// let extsk = ExtendedSpendingKey::master(&[]); -/// let extfvks = [ExtendedFullViewingKey::from(&extsk)]; -/// init_accounts_table(&db_data, &extfvks).unwrap(); +/// let seed = [0u8; 32]; +/// let account = AccountId(0); +/// let extsk = spending_key(&seed, Network::TestNetwork.coin_type(), account); +/// let tsk = derive_secret_key_from_seed(&Network::TestNetwork, &seed, account, 0).unwrap(); +/// let extfvk = ExtendedFullViewingKey::from(&extsk); +/// let taddr = derive_transparent_address_from_secret_key(&tsk); +/// init_accounts_table(&db_data, &[&extfvk], &[&taddr]).unwrap(); /// ``` /// /// [`get_address`]: crate::wallet::get_address @@ -157,8 +172,11 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { pub fn init_accounts_table( wdb: &WalletDb

, extfvks: &[ExtendedFullViewingKey], - taddrs: &Vec, + taddrs: &[TransparentAddress], ) -> Result<(), SqliteClientError> { + //TODO: make this a proper error? + assert!(extfvks.len() == taddrs.len()); + let mut empty_check = wdb.conn.prepare("SELECT * FROM accounts LIMIT 1")?; if empty_check.exists(NO_PARAMS)? { return Err(SqliteClientError::TableNotEmpty); @@ -253,10 +271,10 @@ pub fn init_blocks_table

( mod tests { use tempfile::NamedTempFile; + use zcash_client_backend::keys::derive_transparent_address_from_secret_key; + use zcash_primitives::{ - block::BlockHash, - consensus::BlockHeight, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + block::BlockHash, consensus::BlockHeight, zip32::ExtendedFullViewingKey, }; use crate::{tests, wallet::get_address, AccountId, WalletDb}; @@ -270,18 +288,18 @@ mod tests { init_wallet_db(&db_data).unwrap(); // We can call the function as many times as we want with no data - init_accounts_table(&db_data, &[]).unwrap(); - init_accounts_table(&db_data, &[]).unwrap(); + init_accounts_table(&db_data, &[], &[]).unwrap(); + init_accounts_table(&db_data, &[], &[]).unwrap(); // First call with data should initialise the accounts table - let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master( - &[], - ))]; - init_accounts_table(&db_data, &extfvks).unwrap(); + let (extsk, tsk) = tests::derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk.clone()], &[taddr.clone()]).unwrap(); // Subsequent calls should return an error - init_accounts_table(&db_data, &[]).unwrap_err(); - init_accounts_table(&db_data, &extfvks).unwrap_err(); + init_accounts_table(&db_data, &[], &[]).unwrap_err(); + init_accounts_table(&db_data, &[extfvk], &[taddr]).unwrap_err(); } #[test] @@ -318,9 +336,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + let (extsk, tsk) = tests::derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk], &[taddr]).unwrap(); // The account's address should be in the data DB let pa = get_address(&db_data, AccountId(0)).unwrap(); diff --git a/zcash_client_sqlite/src/wallet/transact.rs b/zcash_client_sqlite/src/wallet/transact.rs index db313c73f3..c897f0dd15 100644 --- a/zcash_client_sqlite/src/wallet/transact.rs +++ b/zcash_client_sqlite/src/wallet/transact.rs @@ -164,12 +164,16 @@ mod tests { use zcash_client_backend::{ data_api::{chain::scan_cached_blocks, wallet::create_spend_to_address, WalletRead}, + keys::derive_transparent_address_from_secret_key, wallet::OvkPolicy, }; use crate::{ chain::init::init_cache_database, - tests::{self, fake_compact_block, insert_into_cache, sapling_activation_height}, + tests::{ + self, derive_test_keys_from_seed, fake_compact_block, insert_into_cache, + sapling_activation_height, + }, wallet::{ get_balance, get_balance_at, init::{init_accounts_table, init_blocks_table, init_wallet_db}, @@ -193,13 +197,17 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add two accounts to the wallet - let extsk0 = ExtendedSpendingKey::master(&[]); - let extsk1 = ExtendedSpendingKey::master(&[0]); + let (extsk0, tsk0) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let (extsk1, tsk1) = derive_test_keys_from_seed(&[1u8; 32], AccountId(1)); let extfvks = [ ExtendedFullViewingKey::from(&extsk0), ExtendedFullViewingKey::from(&extsk1), ]; - init_accounts_table(&db_data, &extfvks).unwrap(); + let taddrs = [ + derive_transparent_address_from_secret_key(&tsk0), + derive_transparent_address_from_secret_key(&tsk1), + ]; + init_accounts_table(&db_data, &extfvks, &taddrs).unwrap(); let to = extsk0.default_address().unwrap().1.into(); // Invalid extsk for the given account should cause an error @@ -242,9 +250,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk], &[taddr]).unwrap(); let to = extsk.default_address().unwrap().1.into(); // We cannot do anything if we aren't synchronised @@ -280,9 +289,10 @@ mod tests { .unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); - let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk], &[taddr]).unwrap(); let to = extsk.default_address().unwrap().1.into(); // Account balance should be zero @@ -320,9 +330,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk.clone()], &[taddr]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -447,9 +458,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk.clone()], &[taddr]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -568,9 +580,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk.clone()], &[taddr]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -673,9 +686,10 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = ExtendedSpendingKey::master(&[]); + let (extsk, tsk) = derive_test_keys_from_seed(&[0u8; 32], AccountId(0)); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + let taddr = derive_transparent_address_from_secret_key(&tsk); + init_accounts_table(&db_data, &[extfvk.clone()], &[taddr]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(51000).unwrap(); From 9b3025de4dc70237af60e2c9fe2fc9da9f12814b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 9 Apr 2021 14:40:35 -0600 Subject: [PATCH 0012/2028] Add ZIP-321 request based sends to zcash_client_backend. --- zcash_client_backend/src/data_api.rs | 8 +- zcash_client_backend/src/data_api/wallet.rs | 129 +++++++++++++------- zcash_client_backend/src/zip321.rs | 2 +- zcash_client_sqlite/src/lib.rs | 20 +-- zcash_client_sqlite/src/wallet/init.rs | 2 +- 5 files changed, 104 insertions(+), 57 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index b2c3c02174..2a3bd7b2be 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -217,6 +217,12 @@ pub struct DecryptedTransaction<'a> { pub struct SentTransaction<'a> { pub tx: &'a Transaction, pub created: time::OffsetDateTime, + pub account: AccountId, + pub outputs: Vec>, + pub utxos_spent: Vec, +} + +pub struct SentTransactionOutput<'a> { /// The index within the transaction that contains the recipient output. /// /// - If `recipient_address` is a Sapling address, this is an index into the Sapling @@ -224,11 +230,9 @@ pub struct SentTransaction<'a> { /// - If `recipient_address` is a transparent address, this is an index into the /// transparent outputs of the transaction. pub output_index: usize, - pub account: AccountId, pub recipient_address: &'a RecipientAddress, pub value: Amount, pub memo: Option, - pub utxos_spent: Vec, } /// This trait encapsulates the write capabilities required to update stored diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index e0a4a37ff0..38d85b8c5f 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -13,9 +13,12 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, - data_api::{error::Error, DecryptedTransaction, SentTransaction, WalletWrite}, + data_api::{ + error::Error, DecryptedTransaction, SentTransaction, SentTransactionOutput, WalletWrite, + }, decrypt_transaction, wallet::{AccountId, OvkPolicy}, + zip321::{Payment, TransactionRequest}, }; #[cfg(feature = "transparent-inputs")] @@ -159,10 +162,39 @@ pub fn create_spend_to_address( account: AccountId, extsk: &ExtendedSpendingKey, to: &RecipientAddress, - value: Amount, + amount: Amount, memo: Option, ovk_policy: OvkPolicy, ) -> Result +where + E: From>, + P: consensus::Parameters + Clone, + R: Copy + Debug, + D: WalletWrite, +{ + let req = TransactionRequest { + payments: vec![Payment { + recipient_address: to.clone(), + amount, + memo, + label: None, + message: None, + other_params: vec![], + }], + }; + + spend(wallet_db, params, prover, account, extsk, &req, ovk_policy) +} + +pub fn spend( + wallet_db: &mut D, + params: &P, + prover: impl TxProver, + account: AccountId, + extsk: &ExtendedSpendingKey, + request: &TransactionRequest, + ovk_policy: OvkPolicy, +) -> Result where E: From>, P: consensus::Parameters + Clone, @@ -188,7 +220,7 @@ where .get_target_and_anchor_heights() .and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?; - let target_value = value + DEFAULT_FEE; + let target_value = request.payments.iter().map(|p| p.amount).sum::() + DEFAULT_FEE; let spendable_notes = wallet_db.select_unspent_sapling_notes(account, target_value, anchor_height)?; @@ -202,6 +234,7 @@ where } // Create the transaction + let consensus_branch_id = BranchId::for_height(params, height); let mut builder = Builder::new(params.clone(), height); for selected in spendable_notes { let from = extfvk @@ -221,55 +254,61 @@ where .map_err(Error::Builder)?; } - match to { - RecipientAddress::Shielded(to) => { - memo.clone().ok_or(Error::MemoRequired).and_then(|memo| { - builder - .add_sapling_output(ovk, to.clone(), value, memo) - .map_err(Error::Builder) - }) - } - RecipientAddress::Transparent(to) => { - if memo.is_some() { - Err(Error::MemoForbidden) - } else { - builder - .add_transparent_output(&to, value) - .map_err(Error::Builder) + for payment in &request.payments { + match &payment.recipient_address { + RecipientAddress::Shielded(to) => builder + .add_sapling_output( + ovk, + to.clone(), + payment.amount, + payment.memo.clone().unwrap_or_else(MemoBytes::empty), + ) + .map_err(Error::Builder), + RecipientAddress::Transparent(to) => { + if payment.memo.is_some() { + Err(Error::MemoForbidden) + } else { + builder + .add_transparent_output(&to, payment.amount) + .map_err(Error::Builder) + } } - } - }?; + }? + } - let consensus_branch_id = BranchId::for_height(params, height); let (tx, tx_metadata) = builder .build(consensus_branch_id, &prover) .map_err(Error::Builder)?; - let output_index = match to { - // Sapling outputs are shuffled, so we need to look up where the output ended up. - RecipientAddress::Shielded(_) => match tx_metadata.output_index(0) { - Some(idx) => idx, - None => panic!("Output 0 should exist in the transaction"), - }, - RecipientAddress::Transparent(addr) => { - let script = addr.script(); - tx.vout - .iter() - .enumerate() - .find(|(_, tx_out)| tx_out.script_pubkey == script) - .map(|(index, _)| index) - .expect("we sent to this address") + let sent_outputs = request.payments.iter().enumerate().map(|(i, payment)| { + let idx = match &payment.recipient_address { + // Sapling outputs are shuffled, so we need to look up where the output ended up. + RecipientAddress::Shielded(_) => + tx_metadata.output_index(i).expect("An output should exist in the transaction for each shielded payment."), + RecipientAddress::Transparent(addr) => { + let script = addr.script(); + tx.vout + .iter() + .enumerate() + .find(|(_, tx_out)| tx_out.script_pubkey == script) + .map(|(index, _)| index) + .expect("An output should exist in the transaction for each transparent payment.") + } + }; + + SentTransactionOutput { + output_index: idx, + recipient_address: &payment.recipient_address, + value: payment.amount, + memo: payment.memo.clone() } - }; + }).collect(); wallet_db.store_sent_tx(&SentTransaction { tx: &tx, created: time::OffsetDateTime::now_utc(), - output_index, + outputs: sent_outputs, account, - recipient_address: to, - value, - memo, utxos_spent: vec![], }) } @@ -353,11 +392,13 @@ where wallet_db.store_sent_tx(&SentTransaction { tx: &tx, created: time::OffsetDateTime::now_utc(), - output_index, account, - recipient_address: &RecipientAddress::Shielded(z_address), - value: amount_to_shield, - memo: Some(memo.clone()), + outputs: vec![SentTransactionOutput { + output_index, + recipient_address: &RecipientAddress::Shielded(z_address), + value: amount_to_shield, + memo: Some(memo.clone()), + }], utxos_spent: utxos.iter().map(|utxo| utxo.outpoint.clone()).collect(), }) } diff --git a/zcash_client_backend/src/zip321.rs b/zcash_client_backend/src/zip321.rs index bc2d8300b0..7abbcc15ea 100644 --- a/zcash_client_backend/src/zip321.rs +++ b/zcash_client_backend/src/zip321.rs @@ -106,7 +106,7 @@ impl Payment { /// payment value in the request. #[derive(Debug, PartialEq)] pub struct TransactionRequest { - payments: Vec, + pub payments: Vec, } impl TransactionRequest { diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index f902ea2fe5..98c7efcdd6 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -587,15 +587,17 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { wallet::mark_transparent_utxo_spent(up, tx_ref, &utxo_outpoint)?; } - wallet::insert_sent_note( - up, - tx_ref, - sent_tx.output_index, - sent_tx.account, - sent_tx.recipient_address, - sent_tx.value, - sent_tx.memo.as_ref(), - )?; + for output in &sent_tx.outputs { + wallet::insert_sent_note( + up, + tx_ref, + output.output_index, + sent_tx.account, + output.recipient_address, + output.value, + output.memo.as_ref(), + )?; + } // Return the row number of the transaction, so the caller can fetch it for sending. Ok(tx_ref) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index ac77d02868..96e4363008 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -163,7 +163,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// let tsk = derive_secret_key_from_seed(&Network::TestNetwork, &seed, account, 0).unwrap(); /// let extfvk = ExtendedFullViewingKey::from(&extsk); /// let taddr = derive_transparent_address_from_secret_key(&tsk); -/// init_accounts_table(&db_data, &[&extfvk], &[&taddr]).unwrap(); +/// init_accounts_table(&db_data, &[extfvk], &[taddr]).unwrap(); /// ``` /// /// [`get_address`]: crate::wallet::get_address From bb68744df123895272c36107d25127163d18ab1d Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Tue, 13 Apr 2021 13:02:35 -0400 Subject: [PATCH 0013/2028] Ensure that rewinds go far enough to properly restore incremental witness state. --- zcash_client_backend/src/address.rs | 14 +- zcash_client_backend/src/data_api.rs | 14 +- zcash_client_backend/src/data_api/wallet.rs | 12 +- zcash_client_backend/src/encoding.rs | 18 +++ zcash_client_sqlite/src/error.rs | 13 +- zcash_client_sqlite/src/lib.rs | 70 ++++++---- zcash_client_sqlite/src/wallet.rs | 142 ++++++++++++++++---- zcash_client_sqlite/src/wallet/init.rs | 2 +- 8 files changed, 213 insertions(+), 72 deletions(-) diff --git a/zcash_client_backend/src/address.rs b/zcash_client_backend/src/address.rs index 8b144a3b71..472a00aada 100644 --- a/zcash_client_backend/src/address.rs +++ b/zcash_client_backend/src/address.rs @@ -3,8 +3,8 @@ use zcash_primitives::{consensus, legacy::TransparentAddress, sapling::PaymentAddress}; use crate::encoding::{ - decode_payment_address, decode_transparent_address, encode_payment_address, - encode_transparent_address, + decode_payment_address, decode_transparent_address, encode_payment_address_p, + encode_transparent_address_p, }; /// An address that funds can be sent to. @@ -43,14 +43,8 @@ impl RecipientAddress { pub fn encode(&self, params: &P) -> String { match self { - RecipientAddress::Shielded(pa) => { - encode_payment_address(params.hrp_sapling_payment_address(), pa) - } - RecipientAddress::Transparent(addr) => encode_transparent_address( - ¶ms.b58_pubkey_address_prefix(), - ¶ms.b58_script_address_prefix(), - addr, - ), + RecipientAddress::Shielded(pa) => encode_payment_address_p(params, pa), + RecipientAddress::Transparent(addr) => encode_transparent_address_p(params, addr), } } } diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 2a3bd7b2be..e115cf299e 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -143,11 +143,11 @@ pub trait WalletRead { ) -> Result; /// Returns the memo for a note. - /// - /// Implementations of this method must return an error if the note identifier - /// does not appear in the backing data store. fn get_memo(&self, id_note: Self::NoteRef) -> Result; + /// Returns a transaction. + fn get_transaction(&self, id_tx: Self::TxRef) -> Result; + /// Returns the note commitment tree at the specified block height. fn get_commitment_tree( &self, @@ -248,6 +248,7 @@ pub trait WalletWrite: WalletRead { fn store_decrypted_tx( &mut self, received_tx: &DecryptedTransaction, + nullifiers: &[(AccountId, Nullifier)], ) -> Result; fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result; @@ -296,7 +297,7 @@ pub mod testing { memo::Memo, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, - transaction::{components::Amount, TxId}, + transaction::{components::Amount, Transaction, TxId}, zip32::ExtendedFullViewingKey, }; @@ -380,6 +381,10 @@ pub mod testing { Ok(Memo::Empty) } + fn get_transaction(&self, _id_tx: Self::TxRef) -> Result { + Err(Error::ScanRequired) // wrong error but we'll fix it later. + } + fn get_commitment_tree( &self, _block_height: BlockHeight, @@ -439,6 +444,7 @@ pub mod testing { fn store_decrypted_tx( &mut self, _received_tx: &DecryptedTransaction, + _nullifiers: &[(AccountId, Nullifier)], ) -> Result { Ok(TxId([0u8; 32])) } diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 38d85b8c5f..1fa0d3ae6f 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -57,12 +57,16 @@ where .ok_or(Error::SaplingNotActive)?; let sapling_outputs = decrypt_transaction(params, height, tx, &extfvks); + let nullifiers = data.get_nullifiers()?; if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { - data.store_decrypted_tx(&DecryptedTransaction { - tx, - sapling_outputs: &sapling_outputs, - })?; + data.store_decrypted_tx( + &DecryptedTransaction { + tx, + sapling_outputs: &sapling_outputs, + }, + &nullifiers, + )?; } Ok(()) diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 520e3e0719..f9d9221443 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -197,6 +197,13 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { bech32_encode(hrp, |w| w.write_all(&addr.to_bytes())) } +pub fn encode_payment_address_p( + params: &P, + addr: &PaymentAddress, +) -> String { + encode_payment_address(params.hrp_sapling_payment_address(), addr) +} + /// Decodes a [`PaymentAddress`] from a Bech32-encoded string. /// /// # Examples @@ -300,6 +307,17 @@ pub fn encode_transparent_address( bs58::encode(decoded).with_check().into_string() } +pub fn encode_transparent_address_p( + params: &P, + addr: &TransparentAddress, +) -> String { + encode_transparent_address( + ¶ms.b58_pubkey_address_prefix(), + ¶ms.b58_script_address_prefix(), + addr, + ) +} + /// Decodes a [`TransparentAddress`] from a Base58Check-encoded string. /// /// # Examples diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index 6c285fb4c1..f487df7b20 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -4,8 +4,9 @@ use std::error; use std::fmt; use zcash_client_backend::{data_api, encoding::TransparentCodecError}; +use zcash_primitives::consensus::BlockHeight; -use crate::NoteId; +use crate::{NoteId, PRUNING_HEIGHT}; /// The primary error type for the SQLite wallet backend. #[derive(Debug)] @@ -44,6 +45,11 @@ pub enum SqliteClientError { /// A received memo cannot be interpreted as a UTF-8 string. InvalidMemo(zcash_primitives::memo::Error), + /// A requested rewind would violate invariants of the + /// storage layer. The payload returned with this error is + /// the safe rewind height. + RequestedRewindInvalid(BlockHeight), + /// Wrapper for errors from zcash_client_backend BackendError(data_api::error::Error), } @@ -68,7 +74,10 @@ impl fmt::Display for SqliteClientError { } SqliteClientError::IncorrectHrpExtFvk => write!(f, "Incorrect HRP for extfvk"), SqliteClientError::InvalidNote => write!(f, "Invalid note"), - SqliteClientError::InvalidNoteId => write!(f, "The note ID associated with an inserted witness must correspond to a received note."), + SqliteClientError::InvalidNoteId => + write!(f, "The note ID associated with an inserted witness must correspond to a received note."), + SqliteClientError::RequestedRewindInvalid(h) => + write!(f, "A rewind must be either of less than {} blocks, or at least back to block {} for your wallet.", PRUNING_HEIGHT, h), SqliteClientError::Bech32(e) => write!(f, "{}", e), SqliteClientError::Base58(e) => write!(f, "{}", e), SqliteClientError::TransparentAddress(e) => write!(f, "{}", e), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 98c7efcdd6..9a70dea96d 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -44,7 +44,7 @@ use zcash_primitives::{ memo::Memo, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::{Node, Nullifier, PaymentAddress}, - transaction::{components::Amount, TxId}, + transaction::{components::Amount, Transaction, TxId}, zip32::ExtendedFullViewingKey, }; @@ -69,6 +69,8 @@ pub mod chain; pub mod error; pub mod wallet; +pub const PRUNING_HEIGHT: u32 = 100; + /// A newtype wrapper for sqlite primary key values for the notes /// table. #[derive(Debug, Copy, Clone)] @@ -238,6 +240,10 @@ impl WalletRead for WalletDb

{ wallet::get_balance_at(&self, account, anchor_height) } + fn get_transaction(&self, id_tx: i64) -> Result { + wallet::get_transaction(&self, id_tx) + } + fn get_memo(&self, id_note: Self::NoteRef) -> Result { match id_note { NoteId::SentNoteId(id_note) => wallet::get_sent_memo(self, id_note), @@ -368,6 +374,10 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { self.wallet_db.get_balance_at(account, anchor_height) } + fn get_transaction(&self, id_tx: i64) -> Result { + self.wallet_db.get_transaction(id_tx) + } + fn get_memo(&self, id_note: Self::NoteRef) -> Result { self.wallet_db.get_memo(id_note) } @@ -497,7 +507,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { } // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). - wallet::prune_witnesses(up, block.block_height - 100)?; + wallet::prune_witnesses(up, block.block_height - PRUNING_HEIGHT)?; // Update now-expired transactions that didn't get mined. wallet::update_expired_notes(up, block.block_height)?; @@ -509,6 +519,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { fn store_decrypted_tx( &mut self, d_tx: &DecryptedTransaction, + nullifiers: &[(AccountId, Nullifier)], ) -> Result { self.transactionally(|up| { let tx_ref = wallet::put_tx_data(up, d_tx.tx, None)?; @@ -521,7 +532,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { tx_ref, output.index, output.account, - RecipientAddress::Shielded(output.to.clone()), + &output.to, Amount::from_u64(output.note.value) .map_err(|_| SqliteClientError::CorruptedData("Note value invalid.".to_string()))?, Some(&output.memo), @@ -541,22 +552,22 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { } } - // store received z->t transactions in the same way they would be stored by - // create_spend_to_address If there are any of our shielded inputs, we interpret this - // as our z->t tx and store the vouts as our sent notes. - // FIXME this is a weird heuristic that is bound to trip us up somewhere. - if d_tx.sapling_outputs.iter().any(|output| !output.outgoing) { - if let Some(account_id) = spending_account_id { - for (i, txout) in d_tx.tx.vout.iter().enumerate() { - // FIXME: We shouldn't be confusing notes and transparent outputs. - wallet::put_sent_note( + // if we have some transparent outputs yet no shielded outputs, then this is t2t and we + // can safely ignore it otherwise, this is z2t and it might have originated from our + // wallet + if !d_tx.tx.vout.is_empty() { + // store received z->t transactions in the same way they would be stored by + // create_spend_to_address If there are any of our shielded inputs, we interpret this + // as our z->t tx and store the vouts as our sent notes. + // FIXME this is a weird heuristic that is bound to trip us up somewhere. + if d_tx.tx.shielded_spends.iter().any(|input| nullifiers.iter().any(|(_, n)| *n == input.nullifier)) { + for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { + wallet::put_sent_utxo( up, tx_ref, - i, - account_id, - RecipientAddress::Transparent(txout.script_pubkey.address().unwrap()), + output_index, + &txout.script_pubkey.address().unwrap(), txout.value, - None, )?; } } @@ -588,15 +599,24 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { } for output in &sent_tx.outputs { - wallet::insert_sent_note( - up, - tx_ref, - output.output_index, - sent_tx.account, - output.recipient_address, - output.value, - output.memo.as_ref(), - )?; + match output.recipient_address { + RecipientAddress::Shielded(addr) => wallet::insert_sent_note( + up, + tx_ref, + output.output_index, + sent_tx.account, + &addr, + output.value, + output.memo.as_ref(), + )?, + RecipientAddress::Transparent(addr) => wallet::insert_sent_utxo( + up, + tx_ref, + output.output_index, + &addr, + output.value, + )?, + } } // Return the row number of the transaction, so the caller can fetch it for sending. diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 44996372ad..ebfd402812 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -26,16 +26,16 @@ use zcash_primitives::{ }; use zcash_client_backend::{ - address::RecipientAddress, data_api::error::Error, encoding::{ decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key, + encode_payment_address_p, encode_transparent_address_p, }, wallet::{AccountId, WalletShieldedOutput, WalletTx}, DecryptedOutput, }; -use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb}; +use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb, PRUNING_HEIGHT}; use { crate::UtxoId, @@ -311,6 +311,17 @@ pub fn get_received_memo

(wdb: &WalletDb

, id_note: i64) -> Result(wdb: &WalletDb

, id_tx: i64) -> Result { + let tx_bytes: Vec<_> = wdb.conn.query_row( + "SELECT raw FROM transactions + WHERE id_tx = ?", + &[id_tx], + |row| row.get(0), + )?; + + Transaction::read(&tx_bytes[..]).map_err(SqliteClientError::from) +} + /// Returns the memo for a sent note. /// /// The note is identified by its row index in the `sent_notes` table within the wdb @@ -445,6 +456,22 @@ pub fn get_block_hash

( .optional() } +/// Gets the height to which the database must be rewound if any rewind greater than the pruning +/// height is attempted. +pub fn get_rewind_height

(wdb: &WalletDb

) -> Result, SqliteClientError> { + wdb.conn + .query_row( + "SELECT MIN(tx.block) + FROM received_notes n + JOIN transactions tx ON tx.id_tx = n.tx + WHERE n.spent IS NULL", + NO_PARAMS, + |row| row.get(0).map(|h: u32| h.into()), + ) + .optional() + .map_err(SqliteClientError::from) +} + /// Rewinds the database to the given height. /// /// If the requested height is greater than or equal to the height of the last scanned @@ -469,30 +496,46 @@ pub fn rewind_to_height( .or(Ok(sapling_activation_height - 1)) })?; - // nothing to do if we're deleting back down to the max height - if block_height >= last_scanned_height { - Ok(()) + let rewind_height = if block_height >= (last_scanned_height - PRUNING_HEIGHT) { + Some(block_height) } else { - // Decrement witnesses. - wdb.conn.execute( - "DELETE FROM sapling_witnesses WHERE block > ?", - &[u32::from(block_height)], - )?; + match get_rewind_height(&wdb)? { + Some(h) => { + if block_height > h { + return Err(SqliteClientError::RequestedRewindInvalid(h)); + } else { + Some(block_height) + } + } + None => Some(block_height), + } + }; - // Un-mine transactions. - wdb.conn.execute( - "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", - &[u32::from(block_height)], - )?; - - // Now that they aren't depended on, delete scanned blocks. - wdb.conn.execute( - "DELETE FROM blocks WHERE height > ?", - &[u32::from(block_height)], - )?; + // nothing to do if we're deleting back down to the max height - Ok(()) + if let Some(block_height) = rewind_height { + if block_height < last_scanned_height { + // Decrement witnesses. + wdb.conn.execute( + "DELETE FROM sapling_witnesses WHERE block > ?", + &[u32::from(block_height)], + )?; + + // Un-mine transactions. + wdb.conn.execute( + "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", + &[u32::from(block_height)], + )?; + + // Now that they aren't depended on, delete scanned blocks. + wdb.conn.execute( + "DELETE FROM blocks WHERE height > ?", + &[u32::from(block_height)], + )?; + } } + + Ok(()) } /// Returns the commitment tree for the block at the specified height, @@ -897,7 +940,7 @@ pub fn put_sent_note<'a, P: consensus::Parameters>( tx_ref: i64, output_index: usize, account: AccountId, - to: RecipientAddress, + to: &PaymentAddress, value: Amount, memo: Option<&MemoBytes>, ) -> Result<(), SqliteClientError> { @@ -905,7 +948,7 @@ pub fn put_sent_note<'a, P: consensus::Parameters>( // Try updating an existing sent note. if stmts.stmt_update_sent_note.execute(params![ account.0 as i64, - to.encode(&stmts.wallet_db.params), + encode_payment_address_p(&stmts.wallet_db.params, &to), ivalue, &memo.map(|m| m.as_slice()), tx_ref, @@ -919,6 +962,31 @@ pub fn put_sent_note<'a, P: consensus::Parameters>( Ok(()) } +pub fn put_sent_utxo<'a, P: consensus::Parameters>( + stmts: &mut DataConnStmtCache<'a, P>, + tx_ref: i64, + output_index: usize, + to: &TransparentAddress, + value: Amount, +) -> Result<(), SqliteClientError> { + let ivalue: i64 = value.into(); + // Try updating an existing sent note. + if stmts.stmt_update_sent_note.execute(params![ + (None::), + encode_transparent_address_p(&stmts.wallet_db.params, &to), + ivalue, + (None::<&[u8]>), + tx_ref, + output_index as i64 + ])? == 0 + { + // It isn't there, so insert. + insert_sent_utxo(stmts, tx_ref, output_index, &to, value)? + } + + Ok(()) +} + /// Inserts a sent note into the wallet database. /// /// `output_index` is the index within the transaction that contains the recipient output: @@ -932,11 +1000,11 @@ pub fn insert_sent_note<'a, P: consensus::Parameters>( tx_ref: i64, output_index: usize, account: AccountId, - to: &RecipientAddress, + to: &PaymentAddress, value: Amount, memo: Option<&MemoBytes>, ) -> Result<(), SqliteClientError> { - let to_str = to.encode(&stmts.wallet_db.params); + let to_str = encode_payment_address_p(&stmts.wallet_db.params, to); let ivalue: i64 = value.into(); stmts.stmt_insert_sent_note.execute(params![ tx_ref, @@ -949,6 +1017,28 @@ pub fn insert_sent_note<'a, P: consensus::Parameters>( Ok(()) } + +pub fn insert_sent_utxo<'a, P: consensus::Parameters>( + stmts: &mut DataConnStmtCache<'a, P>, + tx_ref: i64, + output_index: usize, + to: &TransparentAddress, + value: Amount, +) -> Result<(), SqliteClientError> { + let to_str = encode_transparent_address_p(&stmts.wallet_db.params, to); + let ivalue: i64 = value.into(); + stmts.stmt_insert_sent_note.execute(params![ + tx_ref, + (output_index as i64), + (None::), + to_str, + ivalue, + (None::<&[u8]>) + ])?; + + Ok(()) +} + #[cfg(test)] mod tests { use tempfile::NamedTempFile; diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 96e4363008..82388dc0de 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -98,7 +98,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { id_note INTEGER PRIMARY KEY, tx INTEGER NOT NULL, output_index INTEGER NOT NULL, - from_account INTEGER NOT NULL, + from_account INTEGER, address TEXT NOT NULL, value INTEGER NOT NULL, memo BLOB, From c1bc06964fd5c3b765859a5aca2524a8fb78595a Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Wed, 14 Apr 2021 13:20:56 -0400 Subject: [PATCH 0014/2028] Add get_all_nullifiers. --- zcash_client_backend/src/data_api.rs | 2 ++ zcash_client_backend/src/data_api/wallet.rs | 4 ++-- zcash_client_sqlite/src/lib.rs | 11 +++++++++-- zcash_client_sqlite/src/wallet.rs | 20 +++++++++++++++++++- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index e115cf299e..245076623e 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -165,6 +165,8 @@ pub trait WalletRead { /// with which they are associated. fn get_nullifiers(&self) -> Result, Self::Error>; + fn get_all_nullifiers(&self) -> Result, Self::Error>; + /// Return all unspent notes. fn get_unspent_sapling_notes( &self, diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 1fa0d3ae6f..22a421705d 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -57,9 +57,9 @@ where .ok_or(Error::SaplingNotActive)?; let sapling_outputs = decrypt_transaction(params, height, tx, &extfvks); - let nullifiers = data.get_nullifiers()?; - + if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { + let nullifiers = data.get_all_nullifiers()?; data.store_decrypted_tx( &DecryptedTransaction { tx, diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 9a70dea96d..4dd1d20a0a 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -270,6 +270,10 @@ impl WalletRead for WalletDb

{ wallet::get_nullifiers(&self) } + fn get_all_nullifiers(&self) -> Result, Self::Error> { + wallet::get_all_nullifiers(&self) + } + fn get_unspent_sapling_notes( &self, account: AccountId, @@ -401,6 +405,10 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> { self.wallet_db.get_nullifiers() } + fn get_all_nullifiers(&self) -> Result, Self::Error> { + self.wallet_db.get_all_nullifiers() + } + fn get_unspent_sapling_notes( &self, account: AccountId, @@ -560,7 +568,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // create_spend_to_address If there are any of our shielded inputs, we interpret this // as our z->t tx and store the vouts as our sent notes. // FIXME this is a weird heuristic that is bound to trip us up somewhere. - if d_tx.tx.shielded_spends.iter().any(|input| nullifiers.iter().any(|(_, n)| *n == input.nullifier)) { + if d_tx.tx.shielded_spends.iter().any(|input| nullifiers.iter().any(|(_, n)| { *n == input.nullifier } )) { for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { wallet::put_sent_utxo( up, @@ -572,7 +580,6 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { } } } - Ok(tx_ref) }) } diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index ebfd402812..f134ef29eb 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -639,6 +639,24 @@ pub fn get_nullifiers

( Ok(res) } +pub fn get_all_nullifiers

( + wdb: &WalletDb

, +) -> Result, SqliteClientError> { + // Get the nullifiers for the notes we are tracking + let mut stmt_fetch_nullifiers = wdb.conn.prepare( + "SELECT rn.id_note, rn.account, rn.nf + FROM received_notes rn", + )?; + let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| { + let account = AccountId(row.get(1)?); + let nf_bytes: Vec = row.get(2)?; + Ok((account, Nullifier::from_slice(&nf_bytes).unwrap())) + })?; + + let res: Vec<_> = nullifiers.collect::>()?; + Ok(res) +} + pub fn get_unspent_transparent_utxos( wdb: &WalletDb

, address: &TransparentAddress, @@ -972,7 +990,7 @@ pub fn put_sent_utxo<'a, P: consensus::Parameters>( let ivalue: i64 = value.into(); // Try updating an existing sent note. if stmts.stmt_update_sent_note.execute(params![ - (None::), + 0, encode_transparent_address_p(&stmts.wallet_db.params, &to), ivalue, (None::<&[u8]>), From 7fc2d9725c2ff0be6f5a63d0ec1cfeb72e2f1b65 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Tue, 20 Apr 2021 10:04:56 -0400 Subject: [PATCH 0015/2028] Placeholder commit: just get things working. Clean up later but for now don't allow nullable accountIds and also delete more data, during a rewind. --- zcash_client_sqlite/src/lib.rs | 27 +++++++++++------ zcash_client_sqlite/src/wallet.rs | 40 ++++++++++++++++++++++++-- zcash_client_sqlite/src/wallet/init.rs | 2 +- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 4dd1d20a0a..e8c4086177 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -568,15 +568,23 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // create_spend_to_address If there are any of our shielded inputs, we interpret this // as our z->t tx and store the vouts as our sent notes. // FIXME this is a weird heuristic that is bound to trip us up somewhere. - if d_tx.tx.shielded_spends.iter().any(|input| nullifiers.iter().any(|(_, n)| { *n == input.nullifier } )) { - for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { - wallet::put_sent_utxo( - up, - tx_ref, - output_index, - &txout.script_pubkey.address().unwrap(), - txout.value, - )?; + + // d_tx.tx.shielded_spends.iter().find(|input| nullifiers.iter().any(|(_, n)| { *n == input.nullifier } )) { + + match nullifiers.iter().find(|(_, n)| d_tx.tx.shielded_spends.iter().any(|input| *n == input.nullifier )) { + Some(tx_input) => { + for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { + wallet::put_sent_utxo( + up, + tx_ref, + output_index, + tx_input.0, + &txout.script_pubkey.address().unwrap(), + txout.value, + )?; + } + }, + None => { } } } @@ -620,6 +628,7 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { up, tx_ref, output.output_index, + sent_tx.account, &addr, output.value, )?, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index f134ef29eb..488486d6d1 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -532,6 +532,38 @@ pub fn rewind_to_height( "DELETE FROM blocks WHERE height > ?", &[u32::from(block_height)], )?; + + // Rewind received notes + wdb.conn.execute( + "DELETE FROM received_notes + WHERE id_note IN ( + SELECT rn.id_note + FROM received_notes rn + LEFT OUTER JOIN transactions tx + ON tx.id_tx = rn.tx + WHERE tx.block > ? + );", + &[u32::from(block_height)], + )?; + + // Rewind sent notes + wdb.conn.execute( + "DELETE FROM sent_notes + WHERE id_note IN ( + SELECT sn.id_note + FROM sent_notes sn + LEFT OUTER JOIN transactions tx + ON tx.id_tx = sn.tx + WHERE tx.block > ? + );", + &[u32::from(block_height)], + )?; + + // Rewind utxos + wdb.conn.execute( + "DELETE FROM utxos WHERE height > ?", + &[u32::from(block_height)], + )?; } } @@ -984,13 +1016,14 @@ pub fn put_sent_utxo<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, tx_ref: i64, output_index: usize, + account: AccountId, to: &TransparentAddress, value: Amount, ) -> Result<(), SqliteClientError> { let ivalue: i64 = value.into(); // Try updating an existing sent note. if stmts.stmt_update_sent_note.execute(params![ - 0, + account.0 as i64, encode_transparent_address_p(&stmts.wallet_db.params, &to), ivalue, (None::<&[u8]>), @@ -999,7 +1032,7 @@ pub fn put_sent_utxo<'a, P: consensus::Parameters>( ])? == 0 { // It isn't there, so insert. - insert_sent_utxo(stmts, tx_ref, output_index, &to, value)? + insert_sent_utxo(stmts, tx_ref, output_index, account, &to, value)? } Ok(()) @@ -1040,6 +1073,7 @@ pub fn insert_sent_utxo<'a, P: consensus::Parameters>( stmts: &mut DataConnStmtCache<'a, P>, tx_ref: i64, output_index: usize, + account: AccountId, to: &TransparentAddress, value: Amount, ) -> Result<(), SqliteClientError> { @@ -1048,7 +1082,7 @@ pub fn insert_sent_utxo<'a, P: consensus::Parameters>( stmts.stmt_insert_sent_note.execute(params![ tx_ref, (output_index as i64), - (None::), + account.0, to_str, ivalue, (None::<&[u8]>) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 82388dc0de..96e4363008 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -98,7 +98,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { id_note INTEGER PRIMARY KEY, tx INTEGER NOT NULL, output_index INTEGER NOT NULL, - from_account INTEGER, + from_account INTEGER NOT NULL, address TEXT NOT NULL, value INTEGER NOT NULL, memo BLOB, From 74434f370cf474156b73bfb685cb62e8cd5bcd42 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Thu, 22 Apr 2021 16:26:57 -0400 Subject: [PATCH 0016/2028] Fix: get rewind height when there are no unspent notes. Previously, this function was not properly returning an optional. --- zcash_client_sqlite/src/wallet.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 488486d6d1..0c8ab365a3 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -466,9 +466,11 @@ pub fn get_rewind_height

(wdb: &WalletDb

) -> Result, Sq JOIN transactions tx ON tx.id_tx = n.tx WHERE n.spent IS NULL", NO_PARAMS, - |row| row.get(0).map(|h: u32| h.into()), + |row| { + row.get(0) + .map(|maybe_height: Option| maybe_height.map(|height| height.into())) + }, ) - .optional() .map_err(SqliteClientError::from) } @@ -557,13 +559,13 @@ pub fn rewind_to_height( WHERE tx.block > ? );", &[u32::from(block_height)], - )?; - + )?; + // Rewind utxos wdb.conn.execute( "DELETE FROM utxos WHERE height > ?", &[u32::from(block_height)], - )?; + )?; } } From 08a5cfa80bcb0bbaa9b03f8ed5acff6ad7db2b4b Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Fri, 30 Apr 2021 21:55:18 -0400 Subject: [PATCH 0017/2028] Add clear function for the accounts table. --- zcash_client_sqlite/src/wallet/init.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 96e4363008..f724a9c9f0 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -209,6 +209,17 @@ pub fn init_accounts_table( Ok(()) } +pub fn clear_accounts_table( + wdb: &WalletDb

, +) -> Result<(), SqliteClientError> { + wdb.conn.execute("PRAGMA foreign_keys = OFF;", NO_PARAMS)?; + wdb.conn.execute( "DELETE FROM accounts;", NO_PARAMS, )?; + wdb.conn.execute("PRAGMA foreign_keys = ON;", NO_PARAMS)?; + + Ok(()) +} + + /// Initialises the data database with the given block. /// /// This enables a newly-created database to be immediately-usable, without needing to From 5a5100395a4d3580e817f09d94293c00edc5b24d Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Fri, 30 Apr 2021 23:06:28 -0400 Subject: [PATCH 0018/2028] Drop accounts table instead. We need to recreate the table each time it is cleared to handle any migrations. This is mostly a stop-gap measure until the migrations and table creations are handled by the same code. --- zcash_client_sqlite/src/wallet/init.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index f724a9c9f0..0e7c81df97 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -209,12 +209,10 @@ pub fn init_accounts_table( Ok(()) } -pub fn clear_accounts_table( +pub fn drop_accounts_table( wdb: &WalletDb

, ) -> Result<(), SqliteClientError> { - wdb.conn.execute("PRAGMA foreign_keys = OFF;", NO_PARAMS)?; - wdb.conn.execute( "DELETE FROM accounts;", NO_PARAMS, )?; - wdb.conn.execute("PRAGMA foreign_keys = ON;", NO_PARAMS)?; + wdb.conn.execute( "DROP TABLE accounts;", NO_PARAMS, )?; Ok(()) } From 3828a814b5d2f8d6ec9111cea8c6c91caf18d26d Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Tue, 4 May 2021 17:37:17 -0400 Subject: [PATCH 0019/2028] Relax fk constraints. --- zcash_client_sqlite/src/wallet/init.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 0e7c81df97..211dfedc52 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -212,7 +212,9 @@ pub fn init_accounts_table( pub fn drop_accounts_table( wdb: &WalletDb

, ) -> Result<(), SqliteClientError> { - wdb.conn.execute( "DROP TABLE accounts;", NO_PARAMS, )?; + wdb.conn.execute_batch( + "PRAGMA foreign_keys = OFF; DROP TABLE accounts; PRAGMA foreign_keys = ON;" + )?; Ok(()) } From bd65b01eb3131f24baab54a7d8e0c05affbc621e Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Fri, 7 May 2021 04:18:45 -0400 Subject: [PATCH 0020/2028] Remove drop accounts function. --- zcash_client_sqlite/src/wallet/init.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 211dfedc52..96e4363008 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -209,17 +209,6 @@ pub fn init_accounts_table( Ok(()) } -pub fn drop_accounts_table( - wdb: &WalletDb

, -) -> Result<(), SqliteClientError> { - wdb.conn.execute_batch( - "PRAGMA foreign_keys = OFF; DROP TABLE accounts; PRAGMA foreign_keys = ON;" - )?; - - Ok(()) -} - - /// Initialises the data database with the given block. /// /// This enables a newly-created database to be immediately-usable, without needing to From 8e3e7de50c3c35bd7c18988bb629dd1baeb1a3d1 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 14 May 2021 14:16:10 -0600 Subject: [PATCH 0021/2028] Minor code cleanup. --- zcash_client_backend/src/data_api/wallet.rs | 2 +- zcash_client_sqlite/src/lib.rs | 28 +++++++++------------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 22a421705d..bc5e9c5a07 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -57,7 +57,7 @@ where .ok_or(Error::SaplingNotActive)?; let sapling_outputs = decrypt_transaction(params, height, tx, &extfvks); - + if !(sapling_outputs.is_empty() && tx.vout.is_empty()) { let nullifiers = data.get_all_nullifiers()?; data.store_decrypted_tx( diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index e8c4086177..8cfcd2d9b8 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -569,22 +569,18 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> { // as our z->t tx and store the vouts as our sent notes. // FIXME this is a weird heuristic that is bound to trip us up somewhere. - // d_tx.tx.shielded_spends.iter().find(|input| nullifiers.iter().any(|(_, n)| { *n == input.nullifier } )) { - - match nullifiers.iter().find(|(_, n)| d_tx.tx.shielded_spends.iter().any(|input| *n == input.nullifier )) { - Some(tx_input) => { - for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { - wallet::put_sent_utxo( - up, - tx_ref, - output_index, - tx_input.0, - &txout.script_pubkey.address().unwrap(), - txout.value, - )?; - } - }, - None => { + if let Some((account_id, _)) = nullifiers.iter().find(|(_, nf)| + d_tx.tx.shielded_spends.iter().any(|input| *nf == input.nullifier) + ) { + for (output_index, txout) in d_tx.tx.vout.iter().enumerate() { + wallet::put_sent_utxo( + up, + tx_ref, + output_index, + *account_id, + &txout.script_pubkey.address().unwrap(), + txout.value, + )?; } } } From fd47ee365286f1e24f2940a120fa0974d5865c00 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 14 May 2021 15:09:36 -0600 Subject: [PATCH 0022/2028] Fix missing method in in-memory wallet prototype. --- zcash_client_backend/src/data_api.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 245076623e..95b09f400c 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -406,6 +406,10 @@ pub mod testing { Ok(Vec::new()) } + fn get_all_nullifiers(&self) -> Result, Self::Error> { + Ok(Vec::new()) + } + fn get_unspent_sapling_notes( &self, _account: AccountId, From f3aded9c84e05b41c2e01e4f74c6a8e48c1ce347 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Mon, 26 Apr 2021 10:14:01 -0700 Subject: [PATCH 0023/2028] Send builder progress --- zcash_primitives/src/transaction/builder.rs | 64 +++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index db58da4ed3..17c8b854d8 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -6,6 +6,7 @@ use std::boxed::Box; use std::error; use std::fmt; use std::marker::PhantomData; +use std::sync::mpsc::Sender; use ff::Field; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; @@ -363,6 +364,30 @@ impl TransactionMetadata { } } +/// Reports on the progress made by the builder towards building the transaction. +/// `cur` is the number of steps completed, and `end` is the total number of steps +/// expected. Note that each step may not be of the same complexity/duration +pub struct Progress { + cur: u32, + end: Option, +} + +impl Progress { + fn new(cur: u32, end: Option) -> Self { + Self { cur, end } + } + + /// Returns the amount of progress made so far building the Tx + pub fn cur(&self) -> u32 { + self.cur + } + + /// Returns the total expected number of steps before this Tx is ready + pub fn end(&self) -> Option { + self.end + } +} + /// Generates a [`Transaction`] from its inputs and outputs. pub struct Builder<'a, P: consensus::Parameters, R: RngCore> { params: P, @@ -590,9 +615,32 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { /// this function, and instead will generate a transaction that will be rejected by /// the network. pub fn build( + self, + consensus_branch_id: consensus::BranchId, + prover: &impl TxProver, + ) -> Result<(Transaction, TransactionMetadata), Error> { + self.build_with_progress_notifier(consensus_branch_id, prover, None) + } + + /// Builds a transaction from the configured spends and outputs. + /// + /// Upon success, returns a tuple containing the final transaction, and the + /// [`TransactionMetadata`] generated during the build process. + /// + /// `consensus_branch_id` must be valid for the block height that this transaction is + /// targeting. An invalid `consensus_branch_id` will *not* result in an error from + /// this function, and instead will generate a transaction that will be rejected by + /// the network. + /// + /// `progress_notifier` sets the notifier channel, where progress of building the transaction is sent. + /// It sends an update after every Spend or Output is computed, and the `u32` sent represents + /// the total steps completed so far. It will eventually send number of spends + outputs. + /// If there's an error building the transaction, the channel is closed. + pub fn build_with_progress_notifier( mut self, consensus_branch_id: consensus::BranchId, prover: &impl TxProver, + progress_notifier: Option>, ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); @@ -682,6 +730,10 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { // Record if we'll need a binding signature let binding_sig_needed = !spends.is_empty() || !outputs.is_empty(); + // Keep track of the total number of steps computed + let total_progress = spends.len() as u32 + outputs.len() as u32; + let mut progress = 0u32; + // Create Sapling SpendDescriptions if !spends.is_empty() { let anchor = self.anchor.expect("anchor was set if spends were added"); @@ -716,6 +768,12 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { spend_auth_sig: None, }); + // Update progress and send a notification on the channel + progress += 1; + progress_notifier + .as_ref() + .map(|tx| tx.send(Progress::new(progress, Some(total_progress)))); + // Record the post-randomized spend location tx_metadata.spend_indices[*pos] = i; } @@ -796,6 +854,12 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { } }; + // Update progress and send a notification on the channel + progress += 1; + progress_notifier + .as_ref() + .map(|tx| tx.send(Progress::new(progress, Some(total_progress)))); + self.mtx.shielded_outputs.push(output_desc); } From 1b0f2060dd68cdc3a7ca1a97487ef6fcfffa7b51 Mon Sep 17 00:00:00 2001 From: str4d Date: Tue, 18 May 2021 13:45:48 +0100 Subject: [PATCH 0024/2028] Improve documentation of builder::Progress --- zcash_primitives/src/transaction/builder.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 17c8b854d8..8ba242d2d6 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -364,11 +364,11 @@ impl TransactionMetadata { } } -/// Reports on the progress made by the builder towards building the transaction. -/// `cur` is the number of steps completed, and `end` is the total number of steps -/// expected. Note that each step may not be of the same complexity/duration +/// Reports on the progress made by the builder towards building a transaction. pub struct Progress { + /// The number of steps completed. cur: u32, + /// The expected total number of steps (as of this progress update), if known. end: Option, } @@ -377,12 +377,17 @@ impl Progress { Self { cur, end } } - /// Returns the amount of progress made so far building the Tx + /// Returns the number of steps completed so far while building the transaction. + /// + /// Note that each step may not be of the same complexity/duration. pub fn cur(&self) -> u32 { self.cur } - /// Returns the total expected number of steps before this Tx is ready + /// Returns the total expected number of steps before this transaction will be ready, + /// or `None` if the end is unknown as of this progress update. + /// + /// Note that each step may not be of the same complexity/duration. pub fn end(&self) -> Option { self.end } From 08a3fb9639cc0820990e583bc301cc037e58325a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 17 May 2021 20:17:12 -0600 Subject: [PATCH 0025/2028] Implement F4Jumble --- zcash_primitives/src/address.rs | 2 + zcash_primitives/src/address/f4jumble.rs | 128 +++++++++++++++++++++++ zcash_primitives/src/lib.rs | 1 + 3 files changed, 131 insertions(+) create mode 100644 zcash_primitives/src/address.rs create mode 100644 zcash_primitives/src/address/f4jumble.rs diff --git a/zcash_primitives/src/address.rs b/zcash_primitives/src/address.rs new file mode 100644 index 0000000000..17613516d4 --- /dev/null +++ b/zcash_primitives/src/address.rs @@ -0,0 +1,2 @@ +/// Types and algorithms used in support of Zcash Unified Addresses +pub mod f4jumble; diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs new file mode 100644 index 0000000000..74b57c6c80 --- /dev/null +++ b/zcash_primitives/src/address/f4jumble.rs @@ -0,0 +1,128 @@ +use blake2b_simd::{Params as Blake2bParams, OUTBYTES}; +use std::cmp::min; + +pub const H_PERS_PREFIX: &[u8; 14] = b"UA_F4Jumble_H_"; +pub const G_PERS_PREFIX: &[u8; 14] = b"UA_F4Jumble_G_"; + +struct Hashes { + l_l: usize, + l_r: usize, +} + +impl Hashes { + fn new(message_length: usize) -> Self { + let l_l = min(OUTBYTES, message_length / 2); + let l_r = message_length - l_l; + Hashes { l_l, l_r } + } + + fn h(&self, i: u8, u: &[u8]) -> Vec { + let mut personal = [0u8; 16]; + (&mut personal[..14]).copy_from_slice(H_PERS_PREFIX); + (&mut personal[14..]).copy_from_slice(&[i, 0]); + + Blake2bParams::new() + .hash_length(self.l_l) + .personal(&personal) + .hash(&u) + .as_ref() + .to_vec() + } + + fn g(&self, i: u8, u: &[u8]) -> Vec { + let mut result = Vec::with_capacity(self.l_r); + for j in 0..ceildiv(self.l_r, OUTBYTES) { + let mut personal = [0u8; 16]; + (&mut personal[..14]).copy_from_slice(G_PERS_PREFIX); + (&mut personal[14..]).copy_from_slice(&[i, j as u8]); + + result.extend( + Blake2bParams::new() + .hash_length(OUTBYTES) + .personal(&personal) + .hash(u) + .as_ref(), + ); + } + + result.into_iter().take(self.l_r).collect() + } +} + +fn xor(a: &[u8], b: &[u8]) -> Vec { + a.iter().zip(b.iter()).map(|(a0, b0)| a0 ^ b0).collect() +} + +fn ceildiv(num: usize, den: usize) -> usize { + (num + den - 1) / den +} + +#[allow(clippy::many_single_char_names)] +pub fn f4jumble(mut a: Vec) -> Option> { + if a.len() >= 48 && a.len() <= 16448 { + Some({ + let hashes = Hashes::new(a.len()); + let b = a.split_off(hashes.l_l); + + let x = xor(&b, &hashes.g(0, &a)); + let y = xor(&a, &hashes.h(0, &x)); + let d = xor(&x, &hashes.g(1, &y)); + let mut c = xor(&y, &hashes.h(1, &d)); + + c.extend(d); + c + }) + } else { + None + } +} + +#[allow(clippy::many_single_char_names)] +pub fn f4jumble_inv(mut c: Vec) -> Option> { + if c.len() >= 48 && c.len() <= 16448 { + Some({ + let hashes = Hashes::new(c.len()); + let d = c.split_off(hashes.l_l); + + let y = xor(&c, &hashes.h(1, &d)); + let x = xor(&d, &hashes.g(1, &y)); + let mut a = xor(&y, &hashes.h(0, &x)); + let b = xor(&x, &hashes.g(0, &a)); + + a.extend(b); + a + }) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use proptest::collection::vec; + use proptest::prelude::*; + + use super::{f4jumble, f4jumble_inv}; + + proptest! { + #[test] + fn f4jumble_roundtrip(msg in vec(any::(), 48..16448)) { + let jumbled = f4jumble(msg.clone()).unwrap(); + let jumbled_len = jumbled.len(); + prop_assert_eq!( + msg.len(), jumbled_len, + "Jumbled length {} was not equal to message length {}", + jumbled_len, msg.len() + ); + + let unjumbled = f4jumble_inv(jumbled).unwrap(); + prop_assert_eq!( + jumbled_len, unjumbled.len(), + "Unjumbled length {} was not equal to jumbled length {}", + unjumbled.len(), jumbled_len + ); + + prop_assert_eq!(msg, unjumbled, "Unjumbled message did not match original message."); + } + } +} diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index a2a113d404..8bd5de7010 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -9,6 +9,7 @@ // Temporary until we have addressed all Result cases. #![allow(clippy::result_unit_err)] +pub mod address; pub mod block; pub mod consensus; pub mod constants; From 69ce777dd1d384a333e57697f03491cb5a5b2d3c Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 18 May 2021 17:27:10 -0600 Subject: [PATCH 0026/2028] Use macros for personalization & iterator for g function. Co-authored-by: str4d --- zcash_primitives/src/address/f4jumble.rs | 56 ++++++++++++++---------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs index 74b57c6c80..68caacfe62 100644 --- a/zcash_primitives/src/address/f4jumble.rs +++ b/zcash_primitives/src/address/f4jumble.rs @@ -1,8 +1,17 @@ use blake2b_simd::{Params as Blake2bParams, OUTBYTES}; use std::cmp::min; -pub const H_PERS_PREFIX: &[u8; 14] = b"UA_F4Jumble_H_"; -pub const G_PERS_PREFIX: &[u8; 14] = b"UA_F4Jumble_G_"; +macro_rules! H_PERS { + ( $i:expr ) => { + [85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 72, 95, $i, 0] + } +} + +macro_rules! G_PERS { + ( $i:expr, $j:expr ) => { + [85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 71, 95, $i, $j] + } +} struct Hashes { l_l: usize, @@ -17,35 +26,26 @@ impl Hashes { } fn h(&self, i: u8, u: &[u8]) -> Vec { - let mut personal = [0u8; 16]; - (&mut personal[..14]).copy_from_slice(H_PERS_PREFIX); - (&mut personal[14..]).copy_from_slice(&[i, 0]); - Blake2bParams::new() .hash_length(self.l_l) - .personal(&personal) + .personal(&H_PERS!(i)) .hash(&u) .as_ref() .to_vec() } fn g(&self, i: u8, u: &[u8]) -> Vec { - let mut result = Vec::with_capacity(self.l_r); - for j in 0..ceildiv(self.l_r, OUTBYTES) { - let mut personal = [0u8; 16]; - (&mut personal[..14]).copy_from_slice(G_PERS_PREFIX); - (&mut personal[14..]).copy_from_slice(&[i, j as u8]); - - result.extend( - Blake2bParams::new() - .hash_length(OUTBYTES) - .personal(&personal) - .hash(u) - .as_ref(), - ); - } - - result.into_iter().take(self.l_r).collect() + (0..ceildiv(self.l_r, OUTBYTES)).flat_map(|j| { + Blake2bParams::new() + .hash_length(OUTBYTES) + .personal(&G_PERS!(i, j as u8)) + .hash(u) + .as_ref() + .to_vec() + .into_iter() + }) + .take(self.l_r) + .collect() } } @@ -103,6 +103,16 @@ mod tests { use proptest::prelude::*; use super::{f4jumble, f4jumble_inv}; + + #[test] + fn h_pers() { + assert_eq!(&H_PERS!(7), b"UA_F4Jumble_H_\x07\x00"); + } + + #[test] + fn g_pers() { + assert_eq!(&G_PERS!(7, 13), b"UA_F4Jumble_G_\x07\x0d"); + } proptest! { #[test] From 4346ca9e501a1f49f7c1ee8d8035d05b903e1ee4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 18 May 2021 17:30:37 -0600 Subject: [PATCH 0027/2028] Apply suggestions from code review Co-authored-by: str4d --- zcash_primitives/src/address/f4jumble.rs | 61 ++++++++++++------------ 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs index 68caacfe62..947c4885aa 100644 --- a/zcash_primitives/src/address/f4jumble.rs +++ b/zcash_primitives/src/address/f4jumble.rs @@ -1,5 +1,8 @@ use blake2b_simd::{Params as Blake2bParams, OUTBYTES}; use std::cmp::min; +use std::ops::RangeInclusive; + +const VALID_LENGTH: RangeInclusive = 48..=16448; macro_rules! H_PERS { ( $i:expr ) => { @@ -58,40 +61,36 @@ fn ceildiv(num: usize, den: usize) -> usize { } #[allow(clippy::many_single_char_names)] -pub fn f4jumble(mut a: Vec) -> Option> { - if a.len() >= 48 && a.len() <= 16448 { - Some({ - let hashes = Hashes::new(a.len()); - let b = a.split_off(hashes.l_l); - - let x = xor(&b, &hashes.g(0, &a)); - let y = xor(&a, &hashes.h(0, &x)); - let d = xor(&x, &hashes.g(1, &y)); - let mut c = xor(&y, &hashes.h(1, &d)); - - c.extend(d); - c - }) +pub fn f4jumble(a: &[u8]) -> Option> { + if VALID_LENGTH.contains(&a.len()) { + let hashes = Hashes::new(a.len()); + let (a, b) = a.split_at(hashes.l_l); + + let x = xor(b, &hashes.g(0, a)); + let y = xor(a, &hashes.h(0, &x)); + let d = xor(&x, &hashes.g(1, &y)); + let mut c = xor(&y, &hashes.h(1, &d)); + + c.extend(d); + Some(c) } else { None } } #[allow(clippy::many_single_char_names)] -pub fn f4jumble_inv(mut c: Vec) -> Option> { - if c.len() >= 48 && c.len() <= 16448 { - Some({ - let hashes = Hashes::new(c.len()); - let d = c.split_off(hashes.l_l); - - let y = xor(&c, &hashes.h(1, &d)); - let x = xor(&d, &hashes.g(1, &y)); - let mut a = xor(&y, &hashes.h(0, &x)); - let b = xor(&x, &hashes.g(0, &a)); - - a.extend(b); - a - }) +pub fn f4jumble_inv(c: &[u8]) -> Option> { + if VALID_LENGTH.contains(&c.len()) { + let hashes = Hashes::new(c.len()); + let (c, d) = c.split_at(hashes.l_l); + + let y = xor(c, &hashes.h(1, d)); + let x = xor(d, &hashes.g(1, &y)); + let mut a = xor(&y, &hashes.h(0, &x)); + let b = xor(&x, &hashes.g(0, &a)); + + a.extend(b); + Some(a) } else { None } @@ -116,8 +115,8 @@ mod tests { proptest! { #[test] - fn f4jumble_roundtrip(msg in vec(any::(), 48..16448)) { - let jumbled = f4jumble(msg.clone()).unwrap(); + fn f4jumble_roundtrip(msg in vec(any::(), 48..=16448)) { + let jumbled = f4jumble(&msg).unwrap(); let jumbled_len = jumbled.len(); prop_assert_eq!( msg.len(), jumbled_len, @@ -125,7 +124,7 @@ mod tests { jumbled_len, msg.len() ); - let unjumbled = f4jumble_inv(jumbled).unwrap(); + let unjumbled = f4jumble_inv(&jumbled).unwrap(); prop_assert_eq!( jumbled_len, unjumbled.len(), "Unjumbled length {} was not equal to jumbled length {}", From 774d166fff9eb7810da4a5732be4e15e9573a71f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 18 May 2021 17:36:24 -0600 Subject: [PATCH 0028/2028] Use VALID_LENGTH constant for f4jumble proptest generation. --- zcash_primitives/src/address/f4jumble.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs index 947c4885aa..0050739ce3 100644 --- a/zcash_primitives/src/address/f4jumble.rs +++ b/zcash_primitives/src/address/f4jumble.rs @@ -101,7 +101,7 @@ mod tests { use proptest::collection::vec; use proptest::prelude::*; - use super::{f4jumble, f4jumble_inv}; + use super::{f4jumble, f4jumble_inv, VALID_LENGTH}; #[test] fn h_pers() { @@ -115,7 +115,7 @@ mod tests { proptest! { #[test] - fn f4jumble_roundtrip(msg in vec(any::(), 48..=16448)) { + fn f4jumble_roundtrip(msg in vec(any::(), VALID_LENGTH)) { let jumbled = f4jumble(&msg).unwrap(); let jumbled_len = jumbled.len(); prop_assert_eq!( From 1dcba34167b5d21d2d37568c1487a0d4e5b2c264 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 19 May 2021 08:50:50 -0600 Subject: [PATCH 0029/2028] Add checks against f4jumble test vectors. --- zcash_primitives/src/address/f4jumble.rs | 52 +- .../src/address/f4jumble/test_vectors.rs | 3618 +++++++++++++++++ 2 files changed, 3652 insertions(+), 18 deletions(-) create mode 100644 zcash_primitives/src/address/f4jumble/test_vectors.rs diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs index 0050739ce3..81fe18f2b7 100644 --- a/zcash_primitives/src/address/f4jumble.rs +++ b/zcash_primitives/src/address/f4jumble.rs @@ -2,18 +2,25 @@ use blake2b_simd::{Params as Blake2bParams, OUTBYTES}; use std::cmp::min; use std::ops::RangeInclusive; +#[cfg(test)] +mod test_vectors; + const VALID_LENGTH: RangeInclusive = 48..=16448; macro_rules! H_PERS { ( $i:expr ) => { - [85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 72, 95, $i, 0] - } + [ + 85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 72, 95, $i, 0, + ] + }; } macro_rules! G_PERS { ( $i:expr, $j:expr ) => { - [85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 71, 95, $i, $j] - } + [ + 85, 65, 95, 70, 52, 74, 117, 109, 98, 108, 101, 95, 71, 95, $i, $j, + ] + }; } struct Hashes { @@ -38,17 +45,18 @@ impl Hashes { } fn g(&self, i: u8, u: &[u8]) -> Vec { - (0..ceildiv(self.l_r, OUTBYTES)).flat_map(|j| { - Blake2bParams::new() - .hash_length(OUTBYTES) - .personal(&G_PERS!(i, j as u8)) - .hash(u) - .as_ref() - .to_vec() - .into_iter() - }) - .take(self.l_r) - .collect() + (0..ceildiv(self.l_r, OUTBYTES)) + .flat_map(|j| { + Blake2bParams::new() + .hash_length(OUTBYTES) + .personal(&G_PERS!(i, j as u8)) + .hash(u) + .as_ref() + .to_vec() + .into_iter() + }) + .take(self.l_r) + .collect() } } @@ -101,13 +109,13 @@ mod tests { use proptest::collection::vec; use proptest::prelude::*; - use super::{f4jumble, f4jumble_inv, VALID_LENGTH}; - + use super::{f4jumble, f4jumble_inv, test_vectors::test_vectors, VALID_LENGTH}; + #[test] fn h_pers() { assert_eq!(&H_PERS!(7), b"UA_F4Jumble_H_\x07\x00"); } - + #[test] fn g_pers() { assert_eq!(&G_PERS!(7, 13), b"UA_F4Jumble_G_\x07\x0d"); @@ -134,4 +142,12 @@ mod tests { prop_assert_eq!(msg, unjumbled, "Unjumbled message did not match original message."); } } + + #[test] + fn f4jumble_check_vectors() { + for v in test_vectors() { + let jumbled = f4jumble(&v.normal).unwrap(); + assert_eq!(jumbled, v.jumbled); + } + } } diff --git a/zcash_primitives/src/address/f4jumble/test_vectors.rs b/zcash_primitives/src/address/f4jumble/test_vectors.rs new file mode 100644 index 0000000000..c803183f11 --- /dev/null +++ b/zcash_primitives/src/address/f4jumble/test_vectors.rs @@ -0,0 +1,3618 @@ +pub(crate) struct TestVector { + pub(crate) normal: Vec, + pub(crate) jumbled: Vec, +} + +// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/f4jumble.py +pub(crate) fn test_vectors() -> Vec { + vec![ + TestVector { + normal: vec![ + 0x9a, 0x2d, 0x9e, 0x94, 0x5b, 0x0c, 0xe1, 0x52, 0xa8, 0x04, 0x9e, 0x29, 0x4c, 0x4d, + 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa, 0x2e, 0xf6, 0xee, 0x69, 0x21, 0x48, + 0x1c, 0xdd, 0x86, 0xb3, 0xcc, 0x43, 0x18, 0xd9, 0x61, 0x4f, 0xc8, 0x20, 0x90, 0x5d, + 0x04, 0x2b, 0xb1, 0xef, 0x9c, 0xa3, + ], + jumbled: vec![ + 0x9a, 0x66, 0xb6, 0x09, 0x74, 0x6b, 0x0f, 0x89, 0x37, 0x6f, 0xc8, 0xc8, 0x09, 0xc0, + 0x88, 0xba, 0x30, 0xe7, 0xf0, 0x7e, 0x8d, 0x82, 0xea, 0x13, 0xad, 0x8e, 0x50, 0x1e, + 0x76, 0x23, 0xc1, 0xc0, 0x71, 0x84, 0x5f, 0xd5, 0x8a, 0x7c, 0xa5, 0x58, 0x77, 0x22, + 0x46, 0x36, 0x56, 0x29, 0x86, 0xc6, + ], + }, + TestVector { + normal: vec![ + 0xf2, 0x49, 0x88, 0xc7, 0xb3, 0x53, 0x42, 0x01, 0xcf, 0xb1, 0xcd, 0x8d, 0xbf, 0x69, + 0xb8, 0x25, 0x0c, 0x18, 0xef, 0x41, 0x29, 0x4c, 0xa9, 0x79, 0x93, 0xdb, 0x54, 0x6c, + 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, 0x9d, 0x4e, 0x30, 0xa7, + 0x35, 0x94, 0xbf, 0x50, 0x98, 0x42, 0x1c, 0x69, 0x37, 0x8a, 0xf1, 0xe4, 0x0f, 0x64, + 0xe1, 0x25, 0x94, 0x6f, 0x62, 0xc2, 0xfa, 0x7b, + ], + jumbled: vec![ + 0xc9, 0xcd, 0x10, 0x3b, 0xb1, 0x07, 0xda, 0xf5, 0x11, 0x14, 0xf3, 0x8d, 0xdd, 0x8d, + 0xbf, 0xa7, 0xe6, 0xe7, 0x27, 0x14, 0x0e, 0x33, 0x2d, 0x98, 0x19, 0xd5, 0x2b, 0x3b, + 0xe0, 0xa1, 0x6e, 0xd1, 0x99, 0x30, 0x2e, 0x29, 0x5c, 0x37, 0x77, 0xd4, 0xb7, 0xd7, + 0x16, 0x54, 0x25, 0x92, 0x0c, 0xd4, 0x4b, 0xdc, 0x9a, 0x27, 0xdb, 0x40, 0x7f, 0x4c, + 0x9f, 0xa0, 0x38, 0x0b, 0xb2, 0x86, 0x97, 0x2c, + ], + }, + TestVector { + normal: vec![ + 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69, 0x68, 0x91, 0x2a, 0x63, 0x81, 0xce, 0x3d, 0xc1, + 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5, 0xfd, 0x93, 0x13, + 0xe8, 0xc7, 0x20, 0x3d, 0x99, 0x6a, 0xf7, 0xd4, 0x77, 0x08, 0x37, 0x56, 0xd5, 0x9a, + 0xf8, 0x0d, 0x06, 0xa7, 0x45, 0xf4, 0x4a, 0xb0, 0x23, 0x75, 0x2c, 0xb5, 0xb4, 0x06, + 0xed, 0x89, 0x85, 0xe1, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26, 0x97, 0xb0, 0xe4, 0xe4, + 0xc7, 0x63, 0xcc, 0xb8, 0xf6, 0x76, 0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, + 0xde, 0xfa, 0x3d, 0x5a, 0x57, 0xef, 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, + 0xd5, 0xfb, 0x1a, 0x38, 0xe0, 0x1d, 0x94, 0x90, 0x3d, 0x3c, 0x3e, 0x0a, 0xd3, 0x36, + 0x0c, 0x1d, 0x37, 0x10, 0xac, 0xd2, 0x0b, 0x18, 0x3e, 0x31, 0xd4, 0x9f, 0x25, 0xc9, + 0xa1, 0x38, + ], + jumbled: vec![ + 0x5e, 0x3a, 0x8f, 0x7b, 0x88, 0x5c, 0xc4, 0xc4, 0xc9, 0x75, 0x54, 0xdf, 0xb2, 0xdb, + 0x15, 0x4d, 0x7a, 0x14, 0x36, 0x91, 0x6a, 0x39, 0xbb, 0x6c, 0xe5, 0x39, 0xc7, 0xd5, + 0x15, 0xfe, 0x96, 0x5d, 0x65, 0x5b, 0x8b, 0x5a, 0xf5, 0xc8, 0x55, 0x80, 0x96, 0x8e, + 0x4b, 0x70, 0xe0, 0x81, 0x75, 0xc3, 0xba, 0x45, 0x09, 0x5e, 0x72, 0x4f, 0xbc, 0xf9, + 0x1b, 0xde, 0x45, 0x82, 0xca, 0xd3, 0xaf, 0xe6, 0x29, 0x56, 0x84, 0x93, 0x3c, 0xe5, + 0x9f, 0x56, 0x0d, 0x44, 0x5d, 0xac, 0xe0, 0x75, 0x0a, 0x8c, 0x26, 0x1c, 0x0a, 0x3b, + 0x64, 0x74, 0x2d, 0x4b, 0x37, 0x9d, 0xc6, 0x76, 0x39, 0x0e, 0xb1, 0xab, 0xbf, 0x0c, + 0x57, 0x6f, 0xd6, 0xe9, 0x38, 0xaf, 0x74, 0x17, 0x1a, 0xa5, 0xb7, 0x8e, 0x01, 0xab, + 0xc0, 0xa8, 0xa5, 0xff, 0x06, 0x5b, 0xe5, 0xb6, 0x98, 0x23, 0x86, 0xd3, 0xec, 0x77, + 0xe6, 0x6e, + ], + }, + TestVector { + normal: vec![ + 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, 0xe3, 0x4a, 0x98, 0x51, 0xa7, 0xaf, + 0x9d, 0xb6, 0x99, 0x0e, 0xd8, 0x3d, 0xd6, 0x4a, 0xf3, 0x59, 0x7c, 0x04, 0x32, 0x3e, + 0xa5, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, 0x32, 0x0d, + 0xad, 0xd6, 0x4f, 0x54, 0x31, 0xe6, 0x1d, 0xdf, 0x65, 0x8d, 0x24, 0xae, 0x67, 0xc2, + 0x2c, 0x8d, 0x13, 0x09, 0x13, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76, + 0xd3, 0x8d, 0x47, 0xf1, 0xe1, 0x91, 0xe0, 0x0c, 0x7a, 0x1d, 0x48, 0xaf, 0x04, 0x68, + 0x27, 0x59, 0x1e, 0x97, 0x33, 0xa9, 0x7f, 0xa6, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, + 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, 0xe6, 0x9c, 0xe8, 0xfc, 0x1b, 0xe4, 0xaa, 0xc0, + 0x0f, 0xf2, 0x71, 0x1e, 0xbd, 0x93, 0x1d, 0xe5, 0x18, 0x85, 0x68, 0x78, 0xf7, 0x34, + 0x76, 0xf2, 0x1a, + ], + jumbled: vec![ + 0x91, 0xf3, 0xde, 0xb1, 0x13, 0x9c, 0x55, 0x0d, 0xbe, 0x38, 0xcb, 0x7b, 0x91, 0x5b, + 0x0b, 0x2b, 0x68, 0xb2, 0xe0, 0x01, 0xa2, 0xf3, 0x82, 0x9c, 0xa2, 0x31, 0x02, 0x31, + 0xac, 0x7b, 0x9c, 0x17, 0xfe, 0x54, 0xc0, 0xb1, 0x42, 0x07, 0xdf, 0xc8, 0x47, 0xfb, + 0xb3, 0x2b, 0xe6, 0xc0, 0x37, 0x13, 0xad, 0x29, 0x8c, 0x81, 0x8c, 0x71, 0x65, 0xf9, + 0x30, 0x3e, 0x4b, 0x2a, 0x42, 0xa9, 0x97, 0xf1, 0xdf, 0x41, 0xb2, 0x29, 0x79, 0x61, + 0x43, 0x3f, 0x93, 0xc7, 0x78, 0x05, 0xa4, 0xe9, 0x93, 0x8a, 0x0f, 0x88, 0xb0, 0x79, + 0x4c, 0x53, 0xb1, 0x8c, 0xb7, 0xd8, 0xab, 0x55, 0xd8, 0x17, 0x9d, 0x3b, 0x80, 0x53, + 0x2b, 0xba, 0x5c, 0xf2, 0x3b, 0xa8, 0x19, 0x02, 0xf9, 0x05, 0xa0, 0x78, 0xa3, 0x03, + 0x66, 0x4b, 0x55, 0xea, 0x49, 0x95, 0x1f, 0xeb, 0xfe, 0xdb, 0x38, 0x72, 0xc3, 0x5b, + 0x4c, 0x88, 0xf3, + ], + }, + TestVector { + normal: vec![ + 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, 0xf7, 0x39, 0x3c, 0x94, 0xe2, 0x88, 0x53, + 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, 0x0f, 0xe5, 0x3e, 0x29, + 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, 0xf4, 0x73, 0xf4, 0x68, 0xa0, + 0x08, 0xe7, 0x23, 0x89, 0xfc, 0x03, 0x88, 0x0d, 0x78, 0x0c, 0xb0, 0x7f, 0xcf, 0xaa, + 0xbe, 0x3f, 0x1a, 0x84, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d, 0x2b, + 0x21, 0x03, 0x59, 0x65, 0x55, 0xed, 0x94, 0x94, 0xc6, 0xac, 0x89, 0x3c, 0x49, 0x72, + 0x38, 0x33, 0xec, 0x89, 0x26, 0xc1, 0x03, 0x95, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, + 0x9c, 0x73, 0x1e, 0x98, 0x5d, 0x99, 0x58, 0x9c, 0x8b, 0xb8, 0x38, 0xe8, 0xaa, 0xf7, + 0x45, 0x53, 0x3e, 0xd9, 0xe8, 0xae, 0x3a, 0x1c, 0xd0, 0x74, 0xa5, 0x1a, 0x20, 0xda, + 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, 0xed, 0x42, 0x43, 0x5e, 0x92, + 0x47, 0x69, 0x30, 0xd0, 0x69, 0x89, 0x6c, 0xff, 0x30, 0xeb, 0x41, 0x4f, 0x72, 0x7b, + 0x89, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75, 0xa4, 0xa6, 0xf2, + 0x65, 0x72, 0x50, 0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, + 0x25, 0x96, 0xbc, 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, + ], + jumbled: vec![ + 0x37, 0x44, 0xf5, 0x22, 0xab, 0xef, 0x20, 0x6f, 0x1b, 0x80, 0xcc, 0x04, 0xa8, 0x67, + 0xb9, 0xea, 0x7a, 0xf7, 0xc1, 0xc9, 0x47, 0xa8, 0xb7, 0xbf, 0x99, 0x3c, 0x7d, 0xb9, + 0xa8, 0xb9, 0x93, 0x8b, 0xee, 0x72, 0x20, 0xc2, 0x0c, 0x21, 0x49, 0x8b, 0x1a, 0xb9, + 0xcb, 0x62, 0x88, 0xec, 0xeb, 0x83, 0xca, 0x36, 0x26, 0xc9, 0xeb, 0x92, 0xbb, 0xad, + 0xf2, 0x43, 0xb3, 0xb4, 0x21, 0xef, 0x7f, 0x2d, 0x46, 0x59, 0x8e, 0x5f, 0x0c, 0x29, + 0x8b, 0x93, 0x98, 0x2c, 0xbf, 0x43, 0x4c, 0x5f, 0xde, 0x3b, 0xfb, 0xb0, 0x4c, 0x92, + 0x9a, 0xc7, 0xba, 0x3f, 0x98, 0xbd, 0x10, 0x17, 0xdd, 0x73, 0xec, 0xa9, 0xc7, 0x6a, + 0xd8, 0x50, 0x6a, 0xa6, 0xbe, 0x7d, 0x16, 0x13, 0x90, 0x91, 0x99, 0xfc, 0x9f, 0x29, + 0xea, 0xe8, 0x36, 0xe8, 0x95, 0x28, 0x1f, 0xb4, 0xa3, 0xb3, 0x0f, 0xa7, 0x5e, 0x06, + 0x5f, 0x2b, 0x96, 0x19, 0x18, 0xb4, 0x86, 0x83, 0xd7, 0x85, 0x71, 0x3a, 0x09, 0xac, + 0xee, 0x7d, 0xb4, 0x75, 0xde, 0x34, 0xd1, 0xe9, 0xb1, 0x8b, 0x21, 0x1d, 0x27, 0x1a, + 0x45, 0x4c, 0xca, 0xeb, 0x0d, 0xed, 0xc4, 0xbf, 0xc6, 0x26, 0xf0, 0xd6, 0x6d, 0x35, + 0x0b, 0xd7, 0x6d, 0xde, 0x03, 0x68, 0xe2, 0x07, 0xc8, 0x52, 0x9d, 0x4f, 0xab, 0x4b, + 0x03, 0x4b, 0xd9, 0x41, 0x6a, 0xe8, 0x9e, 0x1e, 0x84, 0x5d, + ], + }, + TestVector { + normal: vec![ + 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, 0x1a, 0x02, 0xaf, 0x11, 0x7d, 0x41, 0x7a, + 0xdb, 0x3d, 0x15, 0xcc, 0x54, 0xdc, 0xb1, 0xfc, 0xe4, 0x67, 0x50, 0x0c, 0x6b, 0x8f, + 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, 0xcc, 0x40, 0xa9, + 0x8d, 0x5f, 0x29, 0x35, 0x39, 0x5e, 0xe4, 0x76, 0x2d, 0xd2, 0x1a, 0xfd, 0xbb, 0x5d, + 0x47, 0xfa, 0x9a, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, 0xb7, + 0xfa, 0xe2, 0xdb, 0x58, 0x71, 0x05, 0x41, 0x5d, 0x46, 0x42, 0x78, 0x9d, 0x38, 0xf5, + 0x0b, 0x8d, 0xbc, 0xc1, 0x29, 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, 0x35, 0x5b, 0xcf, + 0x73, 0xce, 0xcb, 0x8c, 0xb8, 0xa5, 0xda, 0x01, 0x30, 0x71, 0x52, 0xf1, 0x39, 0x36, + 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, 0x26, 0xc6, 0xcb, 0x4c, 0xd4, + 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a, 0x53, 0x41, 0xec, 0x5d, 0xd7, 0x15, 0x40, + 0x6f, 0x2f, 0xdd, 0x2a, 0xfa, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, 0x2a, + 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, 0x79, + 0xf8, 0x80, 0x08, 0xe3, 0x15, 0xdc, 0x7d, 0x83, 0x88, 0xe7, 0x6c, 0x17, 0x82, 0xfd, + 0x27, 0x95, 0xd1, 0x8a, 0x76, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, + ], + jumbled: vec![ + 0xe4, 0x3d, 0x9f, 0x23, 0x2a, 0x32, 0x2b, 0x14, 0xf1, 0x0c, 0x4c, 0x55, 0x89, 0x71, + 0x4c, 0xb4, 0x04, 0xc6, 0x44, 0x82, 0xf8, 0x3b, 0xec, 0x6e, 0x27, 0x94, 0xc1, 0x4b, + 0xe6, 0xf5, 0x9c, 0x60, 0xfc, 0x1d, 0x50, 0x7b, 0xa0, 0x74, 0x10, 0xee, 0x5f, 0xee, + 0xfd, 0x8b, 0x07, 0xff, 0x6d, 0x37, 0x85, 0xe1, 0xe6, 0x0d, 0xd8, 0x0b, 0x2a, 0xb6, + 0xe6, 0x14, 0x5a, 0x37, 0x9d, 0x47, 0x74, 0xe1, 0xba, 0x81, 0x07, 0xe6, 0x53, 0x9b, + 0x4a, 0xd7, 0x85, 0x86, 0xbc, 0xa3, 0x4e, 0x6b, 0x3b, 0xa8, 0x74, 0x6c, 0x6e, 0xc4, + 0xac, 0x85, 0x6f, 0xbb, 0xc9, 0x24, 0xa4, 0xa9, 0x7a, 0x7e, 0x75, 0x8a, 0xe6, 0x7f, + 0x63, 0x18, 0x9d, 0xc3, 0x3e, 0xb0, 0x5b, 0xca, 0xba, 0x10, 0xaf, 0x8e, 0x9d, 0xac, + 0x9a, 0x66, 0x10, 0x05, 0x16, 0xb0, 0xd1, 0xd6, 0x25, 0x0e, 0x99, 0x7a, 0xdc, 0x4e, + 0x3b, 0xdc, 0xe6, 0xb4, 0xfb, 0x99, 0xce, 0x00, 0x5c, 0x26, 0xf3, 0x7b, 0x9c, 0x14, + 0x83, 0xba, 0xf8, 0xc6, 0x88, 0x35, 0x7e, 0x41, 0xcb, 0x2b, 0xbf, 0xdf, 0x99, 0xb1, + 0xd0, 0x51, 0x3d, 0xc8, 0xea, 0xbb, 0x1a, 0x81, 0xf8, 0x6f, 0x1c, 0x23, 0xff, 0x6e, + 0xb5, 0xc8, 0xa7, 0x63, 0xaa, 0xfc, 0xb8, 0x8a, 0xa9, 0x1a, 0x16, 0x8d, 0xbc, 0x98, + 0x91, 0x4b, 0x30, 0x87, 0x1b, 0x9c, 0x5e, 0xd2, 0x0f, 0x1d, 0x36, + ], + }, + TestVector { + normal: vec![ + 0xcc, 0x97, 0x48, 0x9c, 0xe7, 0x57, 0x45, 0x82, 0x4b, 0x77, 0x86, 0x8c, 0x53, 0x23, + 0x9c, 0xfb, 0xdf, 0x73, 0xca, 0xec, 0x65, 0x60, 0x40, 0x37, 0x31, 0x4f, 0xaa, 0xce, + 0xb5, 0x62, 0x18, 0xc6, 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, 0x33, 0x86, 0x79, 0x3f, + 0x21, 0xa9, 0xfb, 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, 0x00, + 0xe1, 0xb1, 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, + 0x49, 0xe9, 0x50, 0x06, 0x05, 0x91, 0x39, 0x48, 0x12, 0x95, 0x1e, 0x1f, 0xe3, 0x89, + 0x5b, 0x8c, 0xc3, 0xd1, 0x4d, 0x2c, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, + 0x3d, 0x9a, 0x69, 0xf5, 0x33, 0x57, 0xd7, 0x76, 0x7f, 0x4f, 0x5c, 0xcb, 0xdb, 0xc5, + 0x96, 0x63, 0x12, 0x77, 0xf8, 0xfe, 0xcd, 0x08, 0xcb, 0x05, 0x6b, 0x95, 0xe3, 0x02, + 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2, 0x44, 0xfc, 0x71, 0x62, 0x69, 0xb9, 0x26, 0xd6, + 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff, 0x9e, 0x68, 0x62, 0x5a, + 0x19, 0x24, 0x40, 0xea, 0x06, 0x82, 0x81, 0x23, 0xd9, 0x78, 0x84, 0x80, 0x6f, 0x15, + 0xfa, 0x08, 0xda, 0x52, 0x75, 0x4a, 0x10, 0x95, 0xe3, 0xff, 0x1a, 0xbd, 0x5c, 0xe4, + 0xfd, 0xdf, 0xcc, 0xfc, 0x3a, 0x61, 0x28, 0xae, 0xf7, 0x84, 0xa6, 0x46, 0x10, 0xa8, + 0x9d, 0x1a, 0x70, 0x99, 0x21, 0x6d, 0x08, 0x14, 0xd3, 0xa2, 0xd4, 0x52, 0x43, 0x1c, + 0x32, 0xd4, 0x11, 0xac, 0x1c, 0xce, 0x82, 0xad, 0x02, 0x29, 0x40, 0x7b, 0xbc, 0x48, + 0x98, 0x56, 0x75, 0xe3, 0xf8, 0x74, 0xa4, 0x53, 0x3f, 0x1d, 0x63, 0xa8, 0x4d, 0xfa, + 0x3e, 0x0f, 0x46, 0x0f, 0xe2, 0xf5, 0x7e, 0x34, 0xfb, 0xc7, 0x54, 0x23, 0xc3, 0x73, + 0x7f, 0x5b, 0x2a, 0x06, 0x15, 0xf5, 0x72, 0x2d, 0xb0, 0x41, 0xa3, 0xef, 0x66, 0xfa, + 0x48, 0x3a, 0xfd, 0x3c, 0x2e, 0x19, 0xe5, 0x94, 0x44, 0xa6, 0x4a, 0xdd, 0x6d, 0xf1, + 0xd9, 0x63, 0xf5, 0xdd, 0x5b, 0x50, 0x10, 0xd3, 0xd0, 0x25, 0xf0, 0x28, 0x7c, 0x4c, + 0xf1, 0x9c, 0x75, 0xf3, 0x3d, 0x51, 0xdd, 0xdd, 0xba, 0x5d, 0x65, 0x7b, 0x43, 0xee, + 0x8d, 0xa6, 0x45, 0x44, 0x38, 0x14, 0xcc, 0x73, 0x29, 0xf3, 0xe9, 0xb4, 0xe5, 0x4c, + 0x23, 0x6c, 0x29, 0xaf, 0x39, 0x23, 0x10, 0x17, 0x56, 0xd9, 0xfa, 0x4b, 0xd0, 0xf7, + 0xd2, 0xdd, 0xaa, 0xcb, 0x6b, 0x0f, 0x86, 0xa2, 0x65, 0x8e, 0x0a, 0x07, 0xa0, 0x5a, + 0xc5, 0xb9, 0x50, 0x05, 0x1c, 0xd2, 0x4c, 0x47, 0xa8, 0x8d, 0x13, 0xd6, 0x59, 0xba, + 0x2a, 0x46, 0xca, 0x18, 0x30, 0x81, 0x6d, 0x09, 0xcd, 0x76, 0x46, 0xf7, 0x6f, 0x71, + 0x6a, 0xbe, 0xc5, 0xde, 0x07, 0xfe, 0x9b, 0x52, 0x34, 0x10, 0x80, 0x6e, 0xa6, 0xf2, + 0x88, 0xf8, 0x73, 0x6c, 0x23, 0x35, 0x7c, 0x85, 0xf4, 0x57, 0x91, 0xe1, 0x70, 0x80, + 0x29, 0xd9, 0x82, 0x4d, 0x90, 0x70, 0x46, 0x07, 0xf3, 0x87, 0xa0, 0x3e, 0x49, 0xbf, + 0x98, 0x36, 0x57, 0x44, 0x31, 0x34, 0x5a, 0x78, 0x77, 0xef, 0xaa, 0x8a, 0x08, 0xe7, + 0x30, 0x81, 0xef, 0x8d, 0x62, 0xcb, 0x78, 0x0a, 0xb6, 0x88, 0x3a, 0x50, 0xa0, 0xd4, + 0x70, 0x19, 0x0d, 0xfb, 0xa1, 0x0a, 0x85, 0x7f, 0x82, 0x84, 0x2d, 0x38, 0x25, 0xb3, + 0xd6, 0xda, 0x05, 0x73, 0xd3, 0x16, 0xeb, 0x16, 0x0d, 0xc0, 0xb7, 0x16, 0xc4, 0x8f, + 0xbd, 0x46, 0x7f, 0x75, 0xb7, 0x80, 0x14, 0x9a, 0xe8, 0x80, 0x8f, 0x4e, 0x68, 0xf5, + 0x0c, 0x05, 0x36, 0xac, 0xdd, 0xf6, 0xf1, 0xae, 0xab, 0x01, 0x6b, 0x6b, 0xc1, 0xec, + 0x14, 0x4b, 0x4e, 0x55, 0x3a, 0xcf, 0xd6, 0x70, 0xf7, 0x7e, 0x75, 0x5f, 0xc8, 0x8e, + 0x06, 0x77, 0xe3, 0x1b, 0xa4, 0x59, 0xb4, 0x4e, 0x30, 0x77, 0x68, 0x95, 0x8f, 0xe3, + 0x78, 0x9d, 0x41, 0xc2, 0xb1, 0xff, 0x43, 0x4c, 0xb3, 0x0e, 0x15, 0x91, 0x4f, 0x01, + 0xbc, 0x6b, 0xc2, 0x30, 0x7b, 0x48, 0x8d, 0x25, 0x56, 0xd7, 0xb7, 0x38, 0x0e, 0xa4, + 0xff, 0xd7, 0x12, 0xf6, 0xb0, 0x2f, 0xe8, 0x06, 0xb9, 0x45, 0x69, 0xcd, 0x40, 0x59, + 0xf3, 0x96, 0xbf, 0x29, 0xb9, 0x9d, 0x0a, 0x40, 0xe5, 0xe1, 0x71, 0x1c, 0xa9, 0x44, + 0xf7, 0x2d, 0x43, 0x6a, 0x10, 0x2f, 0xca, 0x4b, 0x97, 0x69, 0x3d, 0xa0, 0xb0, 0x86, + 0xfe, 0x9d, 0x2e, 0x71, 0x62, 0x47, 0x0d, 0x02, 0xe0, 0xf0, 0x5d, 0x4b, 0xec, 0x95, + 0x12, 0xbf, 0xb3, 0xf3, 0x83, 0x27, 0x29, 0x6e, 0xfa, 0xa7, 0x43, 0x28, 0xb1, 0x18, + 0xc2, 0x74, 0x02, 0xc7, 0x0c, 0x3a, 0x90, 0xb4, 0x9a, 0xd4, 0xbb, 0xc6, 0x8e, 0x37, + 0xc0, 0xaa, 0x7d, 0x9b, 0x3f, 0xe1, 0x77, 0x99, 0xd7, 0x3b, 0x84, 0x1e, 0x75, 0x17, + 0x13, 0xa0, 0x29, 0x43, 0x90, 0x5a, 0xae, 0x08, 0x03, 0xfd, 0x69, 0x44, 0x2e, 0xb7, + 0x68, 0x1e, 0xc2, 0xa0, 0x56, 0x00, 0x05, 0x4e, 0x92, 0xee, 0xd5, 0x55, 0x02, 0x8f, + 0x21, 0xb6, 0xa1, 0x55, 0x26, 0x8a, 0x2d, 0xd6, 0x64, 0x0a, 0x69, 0x30, 0x1a, 0x52, + 0xa3, 0x8d, 0x4d, 0x9f, 0x9f, 0x95, 0x7a, 0xe3, 0x5a, 0xf7, 0x16, 0x71, 0x18, 0x14, + 0x1c, 0xe4, 0xc9, 0xbe, 0x0a, 0x6a, 0x49, 0x2f, 0xe7, 0x9f, 0x15, 0x81, 0xa1, 0x55, + 0xfa, 0x3a, 0x2b, 0x9d, 0xaf, 0xd8, 0x2e, 0x65, 0x0b, 0x38, 0x6a, 0xd3, 0xa0, 0x8c, + 0xb6, 0xb8, 0x31, 0x31, 0xac, 0x30, 0x0b, 0x08, 0x46, 0x35, 0x4a, 0x7e, 0xef, 0x9c, + 0x41, 0x0e, 0x4b, 0x62, 0xc4, 0x7c, 0x54, 0x26, 0x90, 0x7d, 0xfc, 0x66, 0x85, 0xc5, + 0xc9, 0x9b, 0x71, 0x41, 0xac, 0x62, 0x6a, 0xb4, 0x76, 0x1f, 0xd3, 0xf4, 0x1e, 0x72, + 0x8e, 0x1a, 0x28, 0xf8, 0x9d, 0xb8, 0x9f, 0xfd, 0xec, 0xa3, 0x64, 0xdd, 0x2f, 0x0f, + 0x07, 0x39, 0xf0, 0x53, 0x45, 0x56, 0x48, 0x31, 0x99, 0xc7, 0x1f, 0x18, 0x93, 0x41, + 0xac, 0x9b, 0x78, 0xa2, 0x69, 0x16, 0x42, 0x06, 0xa0, 0xea, 0x1c, 0xe7, 0x3b, 0xfb, + 0x2a, 0x94, 0x2e, 0x73, 0x70, 0xb2, 0x47, 0xc0, 0x46, 0xf8, 0xe7, 0x5e, 0xf8, 0xe3, + 0xf8, 0xbd, 0x82, 0x1c, 0xf5, 0x77, 0x49, 0x18, 0x64, 0xe2, 0x0e, 0x6d, 0x08, 0xfd, + 0x2e, 0x32, 0xb5, 0x55, 0xc9, 0x2c, 0x66, 0x1f, 0x19, 0x58, 0x8b, 0x72, 0xa8, 0x95, + 0x99, 0x71, 0x0a, 0x88, 0x06, 0x12, 0x53, 0xca, 0x28, 0x5b, 0x63, 0x04, 0xb3, 0x7d, + 0xa2, 0xb5, 0x29, 0x4f, 0x5c, 0xb3, 0x54, 0xa8, 0x94, 0x32, 0x28, 0x48, 0xcc, 0xbd, + 0xc7, 0xc2, 0x54, 0x5b, 0x7d, 0xa5, 0x68, 0xaf, 0xac, 0x87, 0xff, 0xa0, 0x05, 0xc3, + 0x12, 0x24, 0x1c, 0x2d, 0x57, 0xf4, 0xb4, 0x5d, 0x64, 0x19, 0xf0, 0xd2, 0xe2, 0xc5, + 0xaf, 0x33, 0xae, 0x24, 0x37, 0x85, 0xb3, 0x25, 0xcd, 0xab, 0x95, 0x40, 0x4f, 0xc7, + 0xae, 0xd7, 0x05, 0x25, 0xcd, 0xdb, 0x41, 0x87, 0x2c, 0xfc, 0xc2, 0x14, 0xb1, 0x32, + 0x32, 0xed, 0xc7, 0x86, 0x09, 0x75, 0x3d, 0xbf, 0xf9, 0x30, 0xeb, 0x0d, 0xc1, 0x56, + 0x61, 0x2b, 0x9c, 0xb4, 0x34, 0xbc, 0x4b, 0x69, 0x33, 0x92, 0xde, 0xb8, 0x7c, 0x53, + 0x04, 0x35, 0x31, 0x2e, 0xdc, 0xed, 0xc6, 0xa9, 0x61, 0x13, 0x33, 0x38, 0xd7, 0x86, + 0xc4, 0xa3, 0xe1, 0x03, 0xf6, 0x01, 0x10, 0xa1, 0x6b, 0x13, 0x37, 0x12, 0x97, 0x04, + 0xbf, 0x47, 0x54, 0xff, 0x6b, 0xa9, 0xfb, 0xe6, 0x59, 0x51, 0xe6, 0x10, 0x62, 0x0f, + 0x71, 0xcd, 0xa8, 0xfc, 0x87, 0x76, 0x25, 0xf2, 0xc5, 0xbb, 0x04, 0xcb, 0xe1, 0x22, + 0x8b, 0x1e, 0x88, 0x6f, 0x40, 0x50, 0xaf, 0xd8, 0xfe, 0x94, 0xe9, 0x7d, 0x2e, 0x9e, + 0x85, 0xc6, 0xbb, 0x74, 0x8c, 0x00, 0x42, 0xd3, 0x24, 0x9a, 0xbb, 0x13, 0x42, 0xbb, + 0x0e, 0xeb, 0xf6, 0x20, 0x58, 0xbf, 0x3d, 0xe0, 0x80, 0xd9, 0x46, 0x11, 0xa3, 0x75, + 0x09, 0x15, 0xb5, 0xdc, 0x6c, 0x0b, 0x38, 0x99, 0xd4, 0x12, 0x22, 0xba, 0xce, 0x76, + 0x0e, 0xe9, 0xc8, 0x81, 0x8d, 0xed, 0x59, 0x9e, 0x34, 0xc5, 0x6d, 0x73, 0x72, 0xaf, + 0x1e, 0xb8, 0x68, 0x52, 0xf2, 0xa7, 0x32, 0x10, 0x4b, 0xdb, 0x75, 0x07, 0x39, 0xde, + 0x6c, 0x2c, 0x6e, 0x0f, 0x9e, 0xb7, 0xcb, 0x17, 0xf1, 0x94, 0x2b, 0xfc, 0x9f, 0x4f, + 0xd6, 0xeb, 0xb6, 0xb4, 0xcd, 0xd4, 0xda, 0x2b, 0xca, 0x26, 0xfa, 0xc4, 0x57, 0x8e, + 0x9f, 0x54, 0x34, 0x05, 0xac, 0xc7, 0xd8, 0x6f, 0xf5, 0x91, 0x58, 0xbd, 0x0c, 0xba, + 0x3a, 0xef, 0x6f, 0x4a, 0x84, 0x72, 0xd1, 0x44, 0xd9, 0x9f, 0x8b, 0x8d, 0x1d, 0xed, + 0xaa, 0x90, 0x77, 0xd4, 0xf0, 0x1d, 0x4b, 0xb2, 0x7b, 0xbe, 0x31, 0xd8, 0x8f, 0xbe, + 0xfa, 0xc3, 0xdc, 0xd4, 0x79, 0x75, 0x63, 0xa2, 0x6b, 0x1d, 0x61, 0xfc, 0xd9, 0xa4, + 0x64, 0xab, 0x21, 0xed, 0x55, 0x0f, 0xe6, 0xfa, 0x09, 0x69, 0x5b, 0xa0, 0xb2, 0xf1, + 0x0e, 0xea, 0x64, 0x68, 0xcc, 0x6e, 0x20, 0xa6, 0x6f, 0x82, 0x6e, 0x3d, 0x14, 0xc5, + 0x00, 0x6f, 0x05, 0x63, 0x88, 0x7f, 0x5e, 0x12, 0x89, 0xbe, 0x1b, 0x20, 0x04, 0xca, + 0xca, 0x8d, 0x3f, 0x34, 0xd6, 0xe8, 0x4b, 0xf5, 0x9c, 0x1e, 0x04, 0x61, 0x9a, 0x7c, + 0x23, 0xa9, 0x96, 0x94, 0x1d, 0x88, 0x9e, 0x46, 0x22, 0xa9, 0xb9, 0xb1, 0xd5, 0x9d, + 0x5e, 0x31, 0x90, 0x94, 0x31, 0x8c, 0xd4, 0x05, 0xba, 0x27, 0xb7, 0xe2, 0xc0, 0x84, + 0x76, 0x2d, 0x31, 0x45, 0x3e, 0xc4, 0x54, 0x9a, 0x4d, 0x97, 0x72, 0x9d, 0x03, 0x34, + 0x60, 0xfc, 0xf8, 0x9d, 0x64, 0x94, 0xf2, 0xff, 0xd7, 0x89, 0xe9, 0x80, 0x82, 0xea, + 0x5c, 0xe9, 0x53, 0x4b, 0x3a, 0xcd, 0x60, 0xfe, 0x49, 0xe3, 0x7e, 0x4f, 0x66, 0x69, + 0x31, 0x67, 0x73, 0x19, 0xed, 0x89, 0xf8, 0x55, 0x88, 0x74, 0x1b, 0x31, 0x28, 0x90, + 0x1a, 0x93, 0xbd, 0x78, 0xe4, 0xbe, 0x02, 0x25, 0xa9, 0xe2, 0x69, 0x2c, 0x77, 0xc9, + 0x69, 0xed, 0x01, 0x76, 0xbd, 0xf9, 0x55, 0x59, 0x48, 0xcb, 0xd5, 0xa3, 0x32, 0xd0, + 0x45, 0xde, 0x6b, 0xa6, 0xbf, 0x44, 0x90, 0xad, 0xfe, 0x74, 0x44, 0xcd, 0x46, 0x7a, + 0x09, 0x07, 0x54, 0x17, 0xfc, 0xc0, 0x06, 0x2e, 0x49, 0xf0, 0x08, 0xc5, 0x1a, 0xd4, + 0x22, 0x74, 0x39, 0xc1, 0xb4, 0x47, 0x6c, 0xcd, 0x8e, 0x97, 0x86, 0x2d, 0xab, 0x7b, + 0xe1, 0xe8, 0xd3, 0x99, 0xc0, 0x5e, 0xf2, 0x7c, 0x6e, 0x22, 0xee, 0x27, 0x3e, 0x15, + 0x78, 0x6e, 0x39, 0x4c, 0x8f, 0x1b, 0xe3, 0x16, 0x82, 0xa3, 0x01, 0x47, 0x96, 0x3a, + 0xc8, 0xda, 0x8d, 0x41, 0xd8, 0x04, 0x25, 0x84, 0x26, 0xa3, 0xf7, 0x02, 0x89, 0xb8, + 0xad, 0x19, 0xd8, 0xde, 0x13, 0xbe, 0x4e, 0xeb, 0xe3, 0xbd, 0x4c, 0x8a, 0x6f, 0x55, + 0xd6, 0xe0, 0xc3, 0x73, 0xd4, 0x56, 0x85, 0x18, 0x79, 0xf5, 0xfb, 0xc2, 0x82, 0xdb, + 0x9e, 0x13, 0x48, 0x06, 0xbf, 0xf7, 0x1e, 0x11, 0xbc, 0x33, 0xab, 0x75, 0xdd, 0x6c, + 0xa0, 0x67, 0xfb, 0x73, 0xa0, 0x43, 0xb6, 0x46, 0xa7, 0xcf, 0x39, 0xca, 0xb4, 0x92, + 0x83, 0x86, 0x78, 0x6d, 0x2f, 0x24, 0x14, 0x1e, 0xe1, 0x20, 0xfd, 0xc3, 0x4d, 0x67, + 0x64, 0xea, 0xfc, 0x66, 0x88, 0x0e, 0xe0, 0x20, 0x4f, 0x53, 0xcc, 0x11, 0x67, 0xed, + 0x20, 0xb4, 0x3a, 0x52, 0xde, 0xa3, 0xca, 0x7c, 0xff, 0x8e, 0xf3, 0x5c, 0xd8, 0xe6, + 0xd7, 0xc1, 0x11, 0xa6, 0x8e, 0xf4, 0x4b, 0xcd, 0x0c, 0x15, 0x13, 0xad, 0x47, 0xca, + 0x61, 0xc6, 0x59, 0xcc, 0x5d, 0x32, 0x5b, 0x44, 0x0f, 0x6b, 0x9f, 0x59, 0xaf, 0xf6, + 0x68, 0x79, 0xbb, 0x66, 0x88, 0xfd, 0x28, 0x59, 0x36, 0x2b, 0x18, 0x2f, 0x20, 0x7b, + 0x31, 0x75, 0x96, 0x1f, 0x64, 0x11, 0xa4, 0x93, 0xbf, 0xfd, 0x04, 0x8e, 0x7d, 0x0d, + 0x87, 0xd8, 0x2f, 0xe6, 0xf9, 0x90, 0xa2, 0xb0, 0xa2, 0x5f, 0x5a, 0xa0, 0x11, 0x1a, + 0x6e, 0x68, 0xf3, 0x7b, 0xf6, 0xf3, 0xac, 0x2d, 0x26, 0xb8, 0x46, 0x86, 0xe5, 0x69, + 0xd5, 0x8d, 0x99, 0xc1, 0x38, 0x35, 0x97, 0xfa, 0xd8, 0x11, 0x93, 0xc4, 0xc1, 0xb1, + 0x6e, 0x6a, 0x90, 0xe2, 0xd5, 0x07, 0xcd, 0xfe, 0x6f, 0xbd, 0xaa, 0x86, 0x16, 0x3e, + 0x9c, 0xf5, 0xde, 0x31, 0x00, 0xfb, 0xca, 0x7e, 0x8d, 0xa0, 0x47, 0xb0, 0x90, 0xdb, + 0x9f, 0x37, 0x95, 0x2f, 0xbf, 0xee, 0x76, 0xaf, 0x61, 0x66, 0x81, 0x90, 0xbd, 0x52, + 0xed, 0x49, 0x0e, 0x67, 0x7b, 0x51, 0x5d, 0x01, 0x43, 0x84, 0xaf, 0x07, 0x21, 0x9c, + 0x7c, 0x0e, 0xe7, 0xfc, 0x7b, 0xfc, 0x79, 0xf3, 0x25, 0x64, 0x4e, 0x4d, 0xf4, 0xc0, + 0xd7, 0xdb, 0x08, 0xe9, 0xf0, 0xbd, 0x02, 0x49, 0x43, 0xc7, 0x05, 0xab, 0xff, 0x89, + 0x94, 0xbf, 0xa6, 0x05, 0xcf, 0xbc, 0x7e, 0xd7, 0x46, 0xa7, 0xd3, 0xf7, 0xc3, 0x7d, + 0x9e, 0x8b, 0xdc, 0x43, 0x3b, 0x7d, 0x79, 0xe0, 0x8a, 0x12, 0xf7, 0x38, 0xa8, 0xf0, + 0xdb, 0xdd, 0xfe, 0xf2, 0xf2, 0x65, 0x7e, 0xf3, 0xe4, 0x7d, 0x1b, 0x0f, 0xd1, 0x1e, + 0x6a, 0x13, 0x31, 0x1f, 0xb7, 0x99, 0xc7, 0x9c, 0x64, 0x1d, 0x9d, 0xa4, 0x3b, 0x33, + 0xe7, 0xad, 0x01, 0x2e, 0x28, 0x25, 0x53, 0x98, 0x78, 0x92, 0x62, 0x27, 0x5f, 0x11, + 0x75, 0xbe, 0x84, 0x62, 0xc0, 0x14, 0x91, 0xc4, 0xd8, 0x42, 0x40, 0x6d, 0x0e, 0xc4, + 0x28, 0x2c, 0x95, 0x26, 0x17, 0x4a, 0x09, 0x87, 0x8f, 0xe8, 0xfd, 0xde, 0x33, 0xa2, + 0x96, 0x04, 0xe5, 0xe5, 0xe7, 0xb2, 0xa0, 0x25, 0xd6, 0x65, 0x0b, 0x97, 0xdb, 0xb5, + 0x2b, 0xef, 0xb5, 0x9b, 0x1d, 0x30, 0xa5, 0x74, 0x33, 0xb0, 0xa3, 0x51, 0x47, 0x44, + 0x44, 0x09, 0x9d, 0xaa, 0x37, 0x10, 0x46, 0x61, 0x32, 0x60, 0xcf, 0x33, 0x54, 0xcf, + 0xcd, 0xad, 0xa6, 0x63, 0xec, 0xe8, 0x24, 0xff, 0xd7, 0xe4, 0x43, 0x93, 0x88, 0x6a, + 0x86, 0x16, 0x5d, 0xdd, 0xdf, 0x2b, 0x4c, 0x41, 0x77, 0x35, 0x54, 0xc8, 0x69, 0x95, + 0x26, 0x94, 0x08, 0xb1, 0x1e, 0x67, 0x37, 0xa4, 0xc4, 0x47, 0x58, 0x6f, 0x69, 0x17, + 0x34, 0x46, 0xd8, 0xe4, 0x8b, 0xf8, 0x4c, 0xbc, 0x00, 0x0a, 0x80, 0x78, 0x99, 0x97, + 0x3e, 0xb9, 0x3c, 0x5e, 0x81, 0x9a, 0xad, 0x66, 0x94, 0x13, 0xf8, 0x38, 0x79, 0x33, + 0xad, 0x15, 0x84, 0xaa, 0x35, 0xe4, 0x3f, 0x4e, 0xcd, 0x1e, 0x2d, 0x04, 0x07, 0xc0, + 0xb1, 0xb8, 0x99, 0x20, 0xff, 0xdf, 0xdb, 0x9b, 0xea, 0x51, 0xac, 0x95, 0xb5, 0x57, + 0xaf, 0x71, 0xb8, 0x9f, 0x90, 0x3f, 0x5d, 0x98, 0x48, 0xf1, 0x4f, 0xcb, 0xeb, 0x18, + 0x37, 0x57, 0x0f, 0x54, 0x4d, 0x63, 0x59, 0xeb, 0x23, 0xfa, 0xf3, 0x8a, 0x08, 0x22, + 0xda, 0x36, 0xce, 0x42, 0x6c, 0x4a, 0x2f, 0xbe, 0xff, 0xeb, 0x0a, 0x8a, 0x2e, 0x29, + 0x7a, 0x9d, 0x19, 0xba, 0x15, 0x02, 0x45, 0x90, 0xe3, 0x32, 0x9d, 0x9f, 0xa9, 0x26, + 0x1f, 0x99, 0x38, 0xa4, 0x03, 0x2d, 0xd3, 0x46, 0x06, 0xc9, 0xcf, 0x9f, 0x3d, 0xd3, + 0x3e, 0x57, 0x6f, 0x05, 0xcd, 0x1d, 0xd6, 0x81, 0x1c, 0x62, 0x98, 0x75, 0x7d, 0x77, + 0xd9, 0xe8, 0x10, 0xab, 0xdb, 0x22, 0x6a, 0xfc, 0xaa, 0x43, 0x46, 0xa6, 0x56, 0x0f, + 0x89, 0x32, 0xb3, 0x18, 0x1f, 0xd3, 0x55, 0xd5, 0xd3, 0x91, 0x97, 0x61, 0x83, 0xf8, + 0xd9, 0x93, 0x88, 0x83, 0x96, 0x32, 0xd6, 0x35, 0x4f, 0x66, 0x6d, 0x09, 0xd3, 0xe5, + 0x62, 0x9e, 0xa1, 0x97, 0x37, 0x38, 0x86, 0x13, 0xd3, 0x8a, 0x34, 0xfd, 0x0f, 0x6e, + 0x50, 0xee, 0x5a, 0x0c, 0xc9, 0x67, 0x71, 0x77, 0xf5, 0x00, 0x28, 0xc1, 0x41, 0x37, + 0x81, 0x87, 0xbd, 0x28, 0x19, 0x40, 0x3f, 0xc5, 0x34, 0xf8, 0x00, 0x76, 0xe9, 0x38, + 0x0c, 0xb4, 0x96, 0x4d, 0x3b, 0x6b, 0x45, 0x81, 0x9d, 0x3b, 0x8e, 0x9c, 0xaf, 0x54, + 0xf0, 0x51, 0x85, 0x2d, 0x67, 0x1b, 0xf8, 0xc1, 0xff, 0xde, 0x2d, 0x15, 0x10, 0x75, + 0x64, 0x18, 0xcb, 0x48, 0x10, 0x93, 0x6a, 0xa5, 0x7e, 0x69, 0x65, 0xd6, 0xfb, 0x65, + 0x6a, 0x76, 0x0b, 0x7f, 0x19, 0xad, 0xf9, 0x6c, 0x17, 0x34, 0x88, 0x55, 0x21, 0x93, + 0xb1, 0x47, 0xee, 0x58, 0x85, 0x80, 0x33, 0xda, 0xc7, 0xcd, 0x0e, 0xb2, 0x04, 0xc0, + 0x64, 0x90, 0xbb, 0xde, 0xdf, 0x5f, 0x75, 0x71, 0xac, 0xb2, 0xeb, 0xe7, 0x6a, 0xce, + 0xf3, 0xf2, 0xa0, 0x1e, 0xe9, 0x87, 0x48, 0x6d, 0xfe, 0x6c, 0x3f, 0x0a, 0x5e, 0x23, + 0x4c, 0x12, 0x72, 0x58, 0xf9, 0x7a, 0x28, 0xfb, 0x5d, 0x16, 0x4a, 0x81, 0x76, 0xbe, + 0x94, 0x6b, 0x80, 0x97, 0xd0, 0xe3, 0x17, 0x28, 0x7f, 0x33, 0xbf, 0x9c, 0x16, 0xf9, + 0xa5, 0x45, 0x40, 0x9c, 0xe2, 0x9b, 0x1f, 0x42, 0x73, 0x72, 0x5f, 0xc0, 0xdf, 0x02, + 0xa0, 0x4e, 0xba, 0xe1, 0x78, 0xb3, 0x41, 0x4f, 0xb0, 0xa8, 0x2d, 0x50, 0xde, 0xb0, + 0x9f, 0xcf, 0x4e, 0x6e, 0xe9, 0xd1, 0x80, 0xff, 0x4f, 0x56, 0xff, 0x3b, 0xc1, 0xd3, + 0x60, 0x1f, 0xc2, 0xdc, 0x90, 0xd8, 0x14, 0xc3, 0x25, 0x6f, 0x49, 0x67, 0xd3, 0xa8, + 0xd6, 0x4c, 0x83, 0xfe, 0xa3, 0x39, 0xc5, 0x1f, 0x5a, 0x8e, 0x58, 0x01, 0xfb, 0xb9, + 0x78, 0x35, 0x58, 0x1b, 0x60, 0x24, 0x65, 0xde, 0xe0, 0x4b, 0x59, 0x22, 0xc2, 0x76, + 0x1b, 0x54, 0x24, 0x5b, 0xec, 0x0c, 0x9e, 0xef, 0x2d, 0xb9, 0x7d, 0x22, 0xb2, 0xb3, + 0x55, 0x6c, 0xc9, 0x69, 0xfb, 0xb1, 0x3d, 0x06, 0x50, 0x97, 0x65, 0xa5, 0x2b, 0x3f, + 0xac, 0x54, 0xb9, 0x3f, 0x42, 0x1b, 0xf0, 0x8e, 0x18, 0xd5, 0x2d, 0xdd, 0x52, 0xcc, + 0x1c, 0x8c, 0xa8, 0xad, 0xfa, 0xcc, 0xab, 0x7e, 0x5c, 0xc2, 0xf4, 0x57, 0x3f, 0xbb, + 0xf8, 0x23, 0x9b, 0xb0, 0xb8, 0xae, 0xdb, 0xf8, 0xda, 0xd1, 0x62, 0x82, 0xda, 0x5c, + 0x91, 0x25, 0xdb, 0xa1, 0xc0, 0x59, 0xd0, 0xdf, 0x8a, 0xbf, 0x62, 0x10, 0x78, 0xf0, + 0x2d, 0x6c, 0x4b, 0xc8, 0x6d, 0x40, 0x84, 0x5a, 0xc1, 0xd5, 0x97, 0x10, 0xc4, 0x5f, + 0x07, 0xd5, 0x85, 0xeb, 0x48, 0xb3, 0x2f, 0xc0, 0x16, 0x7b, 0xa2, 0x56, 0xe7, 0x3c, + 0xa3, 0xb9, 0x31, 0x1c, 0x62, 0xd1, 0x09, 0x49, 0x79, 0x57, 0xd8, 0xdb, 0xe1, 0x0a, + 0xa3, 0xe8, 0x66, 0xb4, 0x0c, 0x0b, 0xaa, 0x2b, 0xc4, 0x92, 0xc1, 0x9a, 0xd1, 0xe6, + 0x37, 0x2d, 0x96, 0x22, 0xbf, 0x16, 0x3f, 0xbf, 0xfe, 0xae, 0xee, 0x79, 0x6a, 0x3c, + 0xd9, 0xb6, 0xfb, 0xbf, 0xa4, 0xd7, 0x92, 0xf3, 0x4d, 0x7f, 0xd6, 0xe7, 0x63, 0xcd, + 0x58, 0x59, 0xdd, 0x26, 0x83, 0x3d, 0x21, 0xd9, 0xbc, 0x54, 0x52, 0xbd, 0x19, 0x51, + 0x5d, 0xff, 0x9f, 0x49, 0x95, 0xb3, 0x5b, 0xc0, 0xc1, 0xf8, 0x76, 0xe6, 0xad, 0x11, + 0xf2, 0x45, 0x2d, 0xc9, 0xae, 0x85, 0xae, 0xc0, 0x1f, 0xc5, 0x6f, 0x8c, 0xbf, 0xda, + 0x75, 0xa7, 0x72, 0x7b, 0x75, 0xeb, 0xbd, 0x6b, 0xbf, 0xfb, 0x43, 0xb6, 0x3a, 0x3b, + 0x1b, 0x67, 0x1e, 0x40, 0xfe, 0xb0, 0xdb, 0x00, 0x29, 0x74, 0xa3, 0xc3, 0xb1, 0xa7, + 0x88, 0x56, 0x72, 0x31, 0xbf, 0x63, 0x99, 0xff, 0x89, 0x23, 0x69, 0x81, 0x14, 0x9d, + 0x42, 0x38, 0x02, 0xd2, 0x34, 0x1a, 0x3b, 0xed, 0xb9, 0xdd, 0xcb, 0xac, 0x1f, 0xe7, + 0xb6, 0x43, 0x5e, 0x14, 0x79, 0xc7, 0x2e, 0x70, 0x89, 0xd0, 0x29, 0xe7, 0xfb, 0xba, + 0xf3, 0xcf, 0x37, 0xe9, 0xb9, 0xa6, 0xb7, 0x76, 0x79, 0x1e, 0x4c, 0x5e, 0x6f, 0xda, + 0x57, 0xe8, 0xd5, 0xf1, 0x4c, 0x8c, 0x35, 0xa2, 0xd2, 0x70, 0x84, 0x6b, 0x9d, 0xbe, + 0x00, 0x5c, 0xda, 0x16, 0xaf, 0x44, 0x08, 0xf3, 0xab, 0x06, 0xa9, 0x16, 0xee, 0xeb, + 0x9c, 0x95, 0x94, 0xb7, 0x04, 0x24, 0xa4, 0xc1, 0xd1, 0x71, 0x29, 0x5b, 0x67, 0x63, + 0xb2, 0x2f, 0x47, 0xf8, 0x0b, 0x53, 0xcc, 0xbb, 0x90, 0x4b, 0xd6, 0x8f, 0xd6, 0x5f, + 0xbd, 0x3f, 0xbd, 0xea, 0x10, 0x35, 0xe9, 0x8c, 0x21, 0xa7, 0xdb, 0xc9, 0x1a, 0x9b, + 0x5b, 0xc7, 0x69, 0x0f, 0x05, 0xec, 0x31, 0x7c, 0x97, 0xf8, 0x76, 0x4e, 0xb4, 0x8e, + 0x91, 0x1d, 0x42, 0x8e, 0xc8, 0xd8, 0x61, 0xb7, 0x08, 0xe8, 0x29, 0x8a, 0xcb, 0x62, + 0x15, 0x51, 0x45, 0x15, 0x5a, 0xe9, 0x5f, 0x0a, 0x1d, 0x15, 0x01, 0x03, 0x47, 0x53, + 0x14, 0x6e, 0x22, 0xd0, 0x5f, 0x58, 0x6d, 0x7f, 0x6b, 0x4f, 0xe1, 0x2d, 0xad, 0x9a, + 0x17, 0xf5, 0xdb, 0x70, 0xb1, 0xdb, 0x96, 0xb8, 0xd9, 0xa8, 0x3e, 0xda, 0xdc, 0x96, + 0x6c, 0x8a, 0x54, 0x66, 0xb6, 0x1f, 0xc9, 0x98, 0xc3, 0x1f, 0x10, 0x70, 0xd9, 0xa5, + 0xc9, 0xa6, 0xd2, 0x68, 0xd3, 0x04, 0xfe, 0x6b, 0x8f, 0xd3, 0xb4, 0x01, 0x03, 0x48, + 0x61, 0x1a, 0xbd, 0xcb, 0xd4, 0x9f, 0xe4, 0xf8, 0x5b, 0x62, 0x3c, 0x78, 0x28, 0xc7, + 0x13, 0x82, 0xe1, 0x03, 0x4e, 0xa6, 0x7b, 0xc8, 0xae, 0x97, 0x40, 0x4b, 0x0c, 0x50, + 0xb2, 0xa0, 0x4f, 0x55, 0x9e, 0x49, 0x95, 0x0a, 0xfc, 0xb0, 0xef, 0x46, 0x2a, 0x2a, + 0xe0, 0x24, 0xb0, 0xf0, 0x22, 0x4d, 0xfd, 0x73, 0x68, 0x4b, 0x88, 0xc7, 0xfb, 0xe9, + 0x2d, 0x02, 0xb6, 0x8f, 0x75, 0x9c, 0x47, 0x52, 0x66, 0x3c, 0xd7, 0xb9, 0x7a, 0x14, + 0x94, 0x36, 0x49, 0x30, 0x55, 0x21, 0x32, 0x6b, 0xde, 0x08, 0x56, 0x30, 0x86, 0x46, + 0x29, 0x29, 0x1b, 0xae, 0x25, 0xff, 0x88, 0x22, 0xa1, 0x4c, 0x4b, 0x66, 0x6a, 0x92, + 0x59, 0xad, 0x0d, 0xc4, 0x2a, 0x82, 0x90, 0xac, 0x7b, 0xc7, 0xf5, 0x3a, 0x16, 0xf3, + 0x79, 0xf7, 0x58, 0xe5, 0xde, 0x75, 0x0f, 0x04, 0xfd, 0x7c, 0xad, 0x47, 0x70, 0x1c, + 0x85, 0x97, 0xf9, 0x78, 0x88, 0xbe, 0xa6, 0xfa, 0x0b, 0xf2, 0x99, 0x99, 0x56, 0xfb, + 0xfd, 0x0e, 0xe6, 0x8e, 0xc3, 0x6e, 0x46, 0x88, 0x80, 0x9a, 0xe2, 0x31, 0xeb, 0x8b, + 0xc4, 0x36, 0x9f, 0x5f, 0xe1, 0x57, 0x3f, 0x57, 0xe0, 0x99, 0xd9, 0xc0, 0x99, 0x01, + 0xbf, 0x39, 0xca, 0xac, 0x48, 0xdc, 0x11, 0x95, 0x6a, 0x8a, 0xe9, 0x05, 0xea, 0xd8, + 0x69, 0x54, 0x54, 0x7c, 0x44, 0x8a, 0xe4, 0x3d, 0x31, 0x5e, 0x66, 0x9c, 0x42, 0x42, + 0xda, 0x56, 0x59, 0x38, 0xf4, 0x17, 0xbf, 0x43, 0xce, 0x7b, 0x2b, 0x30, 0xb1, 0xcd, + 0x40, 0x18, 0x38, 0x8e, 0x1a, 0x91, 0x0f, 0x0f, 0xc4, 0x1f, 0xb0, 0x87, 0x7a, 0x59, + 0x25, 0xe4, 0x66, 0x81, 0x9d, 0x37, 0x5b, 0x0a, 0x91, 0x2d, 0x4f, 0xe8, 0x43, 0xb7, + 0x6e, 0xf6, 0xf2, 0x23, 0xf0, 0xf7, 0xc8, 0x94, 0xf3, 0x8f, 0x7a, 0xb7, 0x80, 0xdf, + 0xd7, 0x5f, 0x66, 0x9c, 0x8c, 0x06, 0xcf, 0xfa, 0x43, 0xeb, 0x47, 0x56, 0x5a, 0x50, + 0xe3, 0xb1, 0xfa, 0x45, 0xad, 0x61, 0xce, 0x9a, 0x1c, 0x47, 0x27, 0xb7, 0xaa, 0xa5, + 0x35, 0x62, 0xf5, 0x23, 0xe7, 0x39, 0x52, 0xbb, 0xf3, 0x3d, 0x8a, 0x41, 0x04, 0x07, + 0x8a, 0xde, 0x3e, 0xaa, 0xa4, 0x96, 0x99, 0xa6, 0x9f, 0xdf, 0x1c, 0x5a, 0xc7, 0x73, + 0x21, 0x46, 0xee, 0x5e, 0x1d, 0x6b, 0x6c, 0xa9, 0xb9, 0x18, 0x0f, 0x96, 0x4c, 0xc9, + 0xd0, 0x87, 0x8a, 0xe1, 0x37, 0x35, 0x24, 0xd7, 0xd5, 0x10, 0xe5, 0x82, 0x27, 0xdf, + 0x6d, 0xe9, 0xd3, 0x0d, 0x27, 0x18, 0x67, 0x64, 0x01, 0x77, 0xb0, 0xf1, 0x85, 0x6e, + 0x28, 0xd5, 0xc8, 0xaf, 0xb0, 0x95, 0xef, 0x61, 0x84, 0xfe, 0xd6, 0x51, 0x58, 0x90, + 0x22, 0xee, 0xae, 0xa4, 0xc0, 0xce, 0x1f, 0xa6, 0xf0, 0x85, 0x09, 0x2b, 0x04, 0x97, + 0x94, 0x89, 0x17, 0x2b, 0x3e, 0xf8, 0x19, 0x4a, 0x79, 0x8d, 0xf5, 0x72, 0x4d, 0x6b, + 0x05, 0xf1, 0xae, 0x00, 0x00, 0x13, 0xa0, 0x8d, 0x61, 0x2b, 0xca, 0x8a, 0x8c, 0x31, + 0x44, 0x3c, 0x10, 0x34, 0x6d, 0xbf, 0x61, 0xde, 0x84, 0x75, 0xc0, 0xbb, 0xec, 0x51, + 0x04, 0xb4, 0x75, 0x56, 0xaf, 0x3d, 0x51, 0x44, 0x58, 0xe2, 0x32, 0x1d, 0x14, 0x60, + 0x71, 0x78, 0x9d, 0x23, 0x35, 0x93, 0x4a, 0x68, 0x06, 0x14, 0xe8, 0x35, 0x62, 0xf8, + 0x2d, 0xfd, 0x40, 0x5b, 0x54, 0xa4, 0x5e, 0xb3, 0x2c, 0x16, 0x54, 0x48, 0xd4, 0xd5, + 0xd6, 0x1c, 0xa2, 0x85, 0x95, 0x85, 0x36, 0x9f, 0x53, 0xf1, 0xa1, 0x37, 0xe9, 0xe8, + 0x2b, 0x67, 0xb8, 0xfd, 0xaf, 0x01, 0xbd, 0xa5, 0x4a, 0x31, 0x73, 0x11, 0x89, 0x6a, + 0xe1, 0x02, 0x80, 0xa0, 0x32, 0x44, 0x0c, 0x42, 0x0a, 0x42, 0x1e, 0x94, 0x4d, 0x1e, + 0x95, 0x2b, 0x70, 0xd5, 0x82, 0x6c, 0xd3, 0xb0, 0x8b, 0x7d, 0xb9, 0x63, 0x0f, 0xe4, + 0xfd, 0x5f, 0x22, 0x12, 0x5d, 0xe8, 0x40, 0xfc, 0xc4, 0x0b, 0x98, 0x03, 0x8a, 0xf1, + 0x1d, 0x55, 0xbe, 0x25, 0x43, 0x25, 0x97, 0xb4, 0xb6, 0x5b, 0x9e, 0xc1, 0xc7, 0xa8, + 0xbb, 0xfd, 0x05, 0x2c, 0xbf, 0x7e, 0x1c, 0x17, 0x85, 0x31, 0x49, 0x34, 0xb2, 0x62, + 0xd5, 0x85, 0x37, 0x54, 0xf1, 0xf1, 0x77, 0x71, 0xcf, 0xb7, 0x50, 0x30, 0x72, 0x65, + 0x57, 0x53, 0xfa, 0x3f, 0x54, 0xec, 0xc5, 0x87, 0xe9, 0xf8, 0x3b, 0x58, 0x19, 0x16, + 0x09, 0x2d, 0xf2, 0x6e, 0x63, 0xe1, 0x89, 0x94, 0xcb, 0x0d, 0xb9, 0x1a, 0x0b, 0xbd, + 0xc7, 0xb6, 0x11, 0x9b, 0x32, 0x22, 0x2a, 0xdf, 0x5e, 0x61, 0xd8, 0xd8, 0xae, 0x89, + 0xda, 0xe4, 0x95, 0x4b, 0x54, 0x81, 0x3b, 0xb3, 0x3f, 0x08, 0xd5, 0x62, 0xba, 0x51, + 0x3f, 0xee, 0x1b, 0x09, 0xc0, 0xfc, 0xd5, 0x16, 0x05, 0x54, 0x19, 0x47, 0x4d, 0xd7, + 0xfd, 0xa0, 0x38, 0xa8, 0x9c, 0x84, 0xea, 0x7b, 0x94, 0x68, 0x28, 0x7f, 0x0e, 0xb0, + 0xc1, 0x0c, 0x4b, 0x13, 0x25, 0x20, 0x19, 0x4d, 0x3d, 0x8d, 0x53, 0x51, 0xfc, 0x10, + 0xd0, 0x9c, 0x15, 0xc8, 0xcc, 0x10, 0x1a, 0xa1, 0x66, 0x3b, 0xbf, 0x17, 0xb8, 0x41, + 0x11, 0xf3, 0x8b, 0xb4, 0x39, 0xf0, 0x73, 0x53, 0xbd, 0xea, 0x35, 0x96, 0xd1, 0x5e, + 0x71, 0x3e, 0x1e, 0x2e, 0x7d, 0x3f, 0x1c, 0x38, 0x31, 0x35, 0xb4, 0x7f, 0xa7, 0xf8, + 0x1f, 0x46, 0xdf, 0x7a, 0x90, 0x2a, 0x40, 0x46, 0x99, 0xec, 0x91, 0x2f, 0x56, 0x56, + 0xc3, 0x5b, 0x85, 0x76, 0x3e, 0x4d, 0xe5, 0x83, 0xae, 0xca, 0xa1, 0xdf, 0xd5, 0xd2, + 0x67, 0x7d, 0x9c, 0x8f, 0xfe, 0xe8, 0x77, 0xf6, 0x3f, 0x40, 0xa5, 0xca, 0x0d, 0x67, + 0xf6, 0xe5, 0x54, 0x12, 0x47, 0x39, 0xf8, 0x05, 0xaf, 0x87, 0x6a, 0xee, 0xde, 0x53, + 0xaa, 0x8b, 0x0f, 0x8e, 0x56, 0x04, 0xa7, 0x3c, 0x30, 0xcb, 0xd0, 0x9d, 0xad, 0x96, + 0x3d, 0x6f, 0x8a, 0x5d, 0xcc, 0x40, 0xde, 0xf4, 0x07, 0x97, 0x34, 0x21, 0x13, 0xba, + 0x20, 0x6f, 0xae, 0x8e, 0xbe, 0x4f, 0x3b, 0xc3, 0xca, 0xf6, 0x92, 0x59, 0xe4, 0x62, + 0xef, 0xf9, 0xba, 0x8b, 0x3f, 0x4b, 0xfa, 0xa1, 0x30, 0x0c, 0x26, 0x92, 0x5a, 0x87, + 0x29, 0xcd, 0x32, 0x91, 0x5b, 0xfc, 0x96, 0x60, 0x86, 0xf0, 0xd5, 0x56, 0x0b, 0xbe, + 0x32, 0xa5, 0x98, 0xc2, 0x2a, 0xdf, 0xb4, 0x8c, 0xef, 0x72, 0xba, 0x5d, 0x42, 0x87, + 0xc0, 0xce, 0xfb, 0xac, 0xfd, 0x8c, 0xe1, 0x95, 0xb4, 0x96, 0x3c, 0x34, 0xa9, 0x4b, + 0xba, 0x7a, 0x17, 0x5d, 0xae, 0x4b, 0xbe, 0x3e, 0xf4, 0x86, 0x3d, 0x53, 0x70, 0x89, + 0x15, 0x09, 0x0f, 0x47, 0xa0, 0x68, 0xe2, 0x27, 0x43, 0x3f, 0x9e, 0x49, 0xd3, 0xaa, + 0x09, 0xe3, 0x56, 0xd8, 0xd6, 0x6d, 0x0c, 0x01, 0x21, 0xe9, 0x1a, 0x3c, 0x4a, 0xa3, + 0xf2, 0x7f, 0xa1, 0xb6, 0x33, 0x96, 0xe2, 0xb4, 0x1d, 0xb9, 0x08, 0xfd, 0xab, 0x8b, + 0x18, 0xcc, 0x73, 0x04, 0xe9, 0x4e, 0x97, 0x05, 0x68, 0xf9, 0x42, 0x1c, 0x0d, 0xbb, + 0xba, 0xf8, 0x45, 0x98, 0xd9, 0x72, 0xb0, 0x53, 0x4f, 0x48, 0xa5, 0xe5, 0x26, 0x70, + 0x43, 0x6a, 0xaa, 0x77, 0x6e, 0xd2, 0x48, 0x2a, 0xd7, 0x03, 0x43, 0x02, 0x01, 0xe5, + 0x34, 0x43, 0xc3, 0x6d, 0xcf, 0xd3, 0x4a, 0x0c, 0xb6, 0x63, 0x78, 0x76, 0x10, 0x5e, + 0x79, 0xbf, 0x3b, 0xd5, 0x8e, 0xc1, 0x48, 0xcb, 0x64, 0x97, 0x0e, 0x32, 0x23, 0xa9, + 0x1f, 0x71, 0xdf, 0xcf, 0xd5, 0xa0, 0x4b, 0x66, 0x7f, 0xba, 0xf3, 0xd4, 0xb3, 0xb9, + 0x08, 0xb9, 0x82, 0x88, 0x20, 0xdf, 0xec, 0xdd, 0x75, 0x37, 0x50, 0xb5, 0xf9, 0xd2, + 0x21, 0x6e, 0x56, 0xc6, 0x15, 0x27, 0x2f, 0x85, 0x44, 0x64, 0xc0, 0xca, 0x4b, 0x1e, + 0x85, 0xae, 0xdd, 0x03, 0x82, 0x92, 0xc4, 0xe1, 0xa5, 0x77, 0x44, 0xeb, 0xba, 0x01, + 0x0b, 0x9e, 0xbf, 0xbb, 0x01, 0x1b, 0xd6, 0xf0, 0xb7, 0x88, 0x05, 0x02, 0x5d, 0x27, + 0xf3, 0xc1, 0x77, 0x46, 0xba, 0xe1, 0x16, 0xc1, 0x5d, 0x9f, 0x47, 0x1f, 0x0f, 0x62, + 0x88, 0xa1, 0x50, 0x64, 0x7b, 0x2a, 0xfe, 0x9d, 0xf7, 0xcc, 0xcf, 0x01, 0xf5, 0xcd, + 0xe5, 0xf0, 0x46, 0x80, 0xbb, 0xfe, 0xd8, 0x7f, 0x6c, 0xf4, 0x29, 0xfb, 0x27, 0xad, + 0x6b, 0xab, 0xe7, 0x91, 0x76, 0x66, 0x11, 0xcf, 0x5b, 0xc2, 0x0e, 0x48, 0xbe, 0xf1, + 0x19, 0x25, 0x9b, 0x9b, 0x8a, 0x0e, 0x39, 0xc3, 0xdf, 0x28, 0xcb, 0x95, 0x82, 0xea, + 0x33, 0x86, 0x01, 0xcd, 0xc4, 0x81, 0xb3, 0x2f, 0xb8, 0x2a, 0xde, 0xeb, 0xb3, 0xda, + 0xde, 0x25, 0xd1, 0xa3, 0xdf, 0x20, 0xc3, 0x7e, 0x71, 0x25, 0x06, 0xb5, 0xd9, 0x96, + 0xc4, 0x9a, 0x9f, 0x0f, 0x30, 0xdd, 0xcb, 0x91, 0xfe, 0x90, 0x04, 0xe1, 0xe8, 0x32, + 0x94, 0xa6, 0xc9, 0x20, 0x3d, 0x94, 0xe8, 0xdc, 0x2c, 0xbb, 0x44, 0x9d, 0xe4, 0x15, + 0x50, 0x32, 0x60, 0x4e, 0x47, 0x99, 0x70, 0x16, 0xb3, 0x04, 0xfd, 0x43, 0x7d, 0x82, + 0x35, 0x04, 0x5e, 0x25, 0x5a, 0x19, 0xb7, 0x43, 0xa0, 0xa9, 0xf2, 0xe3, 0x36, 0xb4, + 0x4c, 0xae, 0x30, 0x7b, 0xb3, 0x98, 0x7b, 0xd3, 0xe4, 0xe7, 0x77, 0xfb, 0xb3, 0x4c, + 0x0a, 0xb8, 0xcc, 0x3d, 0x67, 0x46, 0x6c, 0x0a, 0x88, 0xdd, 0x4c, 0xca, 0xd1, 0x8a, + 0x07, 0xa8, 0xd1, 0x06, 0x8d, 0xf5, 0xb6, 0x29, 0xe5, 0x71, 0x8d, 0x0f, 0x6d, 0xf5, + 0xc9, 0x57, 0xcf, 0x71, 0xbb, 0x00, 0xa5, 0x17, 0x8f, 0x17, 0x5c, 0xac, 0xa9, 0x44, + 0xe6, 0x35, 0xc5, 0x15, 0x9f, 0x73, 0x8e, 0x24, 0x02, 0xa2, 0xd2, 0x1a, 0xa0, 0x81, + 0xe1, 0x0e, 0x45, 0x6a, 0xfb, 0x00, 0xb9, 0xf6, 0x24, 0x16, 0xc8, 0xb9, 0xc0, 0xf7, + 0x22, 0x8f, 0x51, 0x07, 0x29, 0xe0, 0xbe, 0x3f, 0x30, 0x53, 0x13, 0xd7, 0x7f, 0x73, + 0x79, 0xdc, 0x2a, 0xf2, 0x48, 0x69, 0xc6, 0xc7, 0x4e, 0xe4, 0x47, 0x14, 0x98, 0x86, + 0x1d, 0x19, 0x2f, 0x0f, 0xf0, 0xf5, 0x08, 0x28, 0x5d, 0xab, 0x6b, 0x6a, 0x36, 0xcc, + 0xf7, 0xd1, 0x22, 0x56, 0xcc, 0x76, 0xb9, 0x55, 0x03, 0x72, 0x0a, 0xc6, 0x72, 0xd0, + 0x82, 0x68, 0xd2, 0xcf, 0x77, 0x73, 0xb6, 0xba, 0x2a, 0x5f, 0x66, 0x48, 0x47, 0xbf, + 0x70, 0x7f, 0x2f, 0xc1, 0x0c, 0x98, 0xf2, 0xf0, 0x06, 0xec, 0x22, 0xcc, 0xb5, 0xa8, + 0xc8, 0xb7, 0xc4, 0x0c, 0x7c, 0x2d, 0x49, 0xa6, 0x63, 0x9b, 0x9f, 0x2c, 0xe3, 0x3c, + 0x25, 0xc0, 0x4b, 0xc4, 0x61, 0xe7, 0x44, 0xdf, 0xa5, 0x36, 0xb0, 0x0d, 0x94, 0xba, + 0xdd, 0xf4, 0xf4, 0xd1, 0x40, 0x44, 0xc6, 0x95, 0xa3, 0x38, 0x81, 0x47, 0x7d, 0xf1, + 0x24, 0xf0, 0xfc, 0xf2, 0x06, 0xa9, 0xfb, 0x2e, 0x65, 0xe3, 0x04, 0xcd, 0xbf, 0x0c, + 0x4d, 0x23, 0x90, 0x17, 0x0c, 0x13, 0x0a, 0xb8, 0x49, 0xc2, 0xf2, 0x2b, 0x5c, 0xdd, + 0x39, 0x21, 0x64, 0x0c, 0x8c, 0xf1, 0x97, 0x6a, 0xe1, 0x01, 0x0b, 0x0d, 0xfd, 0x9c, + 0xb2, 0x54, 0x3e, 0x45, 0xf9, 0x97, 0x49, 0xcc, 0x4d, 0x61, 0xf2, 0xe8, 0xaa, 0xbf, + 0xe9, 0x8b, 0xd9, 0x05, 0xfa, 0x39, 0x95, 0x1b, 0x33, 0xea, 0x76, 0x9c, 0x45, 0xab, + 0x95, 0x31, 0xc5, 0x72, 0x09, 0x86, 0x2a, 0xd1, 0x2f, 0xd7, 0x6b, 0xa4, 0x80, 0x7e, + 0x65, 0x41, 0x7b, 0x6c, 0xd1, 0x2f, 0xa8, 0xec, 0x91, 0x6f, 0x01, 0x3e, 0xbb, 0x87, + 0x06, 0xa9, 0x6e, 0xff, 0xed, 0xa0, 0x6c, 0x4b, 0xe2, 0x4b, 0x04, 0x84, 0x63, 0x92, + 0xe9, 0xd1, 0xe6, 0x93, 0x0e, 0xae, 0x01, 0xfa, 0x21, 0xfb, 0xd7, 0x00, 0x58, 0x3f, + 0xb5, 0x98, 0xb9, 0x2c, 0x8f, 0x4e, 0xb8, 0xa6, 0x1a, 0xa6, 0x23, 0x5d, 0xb6, 0x0f, + 0x28, 0x41, 0xcf, 0x3a, 0x1c, 0x6a, 0xb5, 0x4c, 0x67, 0x06, 0x68, 0x44, 0x71, 0x1d, + 0x09, 0x1e, 0xb9, 0x31, 0xa1, 0xbd, 0x62, 0x81, 0xae, 0xdf, 0x2a, 0x0e, 0x8f, 0xab, + 0x18, 0x81, 0x72, 0x02, 0xa9, 0xbe, 0x06, 0x40, 0x2e, 0xd9, 0xcc, 0x72, 0x0c, 0x16, + 0xbf, 0xe8, 0x81, 0xe4, 0xdf, 0x42, 0x55, 0xe8, 0x7a, 0xfb, 0x7f, 0xc6, 0x2f, 0x38, + 0x11, 0x6b, 0xbe, 0x03, 0xcd, 0x8a, 0x3c, 0xb1, 0x1a, 0x27, 0xd5, 0x68, 0x41, 0x47, + 0x82, 0xf4, 0x7b, 0x1a, 0x44, 0xc9, 0x7c, 0x68, 0x04, 0x67, 0x69, 0x4b, 0xc9, 0x70, + 0x9d, 0x32, 0x91, 0x6c, 0x97, 0xe8, 0x00, 0x6c, 0xbb, 0x07, 0xba, 0x0e, 0x41, 0x80, + 0xa3, 0x73, 0x80, 0x38, 0xc3, 0x74, 0xc4, 0xcc, 0xe8, 0xf3, 0x29, 0x59, 0xaf, 0xb2, + 0x5f, 0x30, 0x3f, 0x58, 0x15, 0xc4, 0x53, 0x31, 0x24, 0xac, 0xf9, 0xd1, 0x89, 0x40, + 0xe7, 0x75, 0x22, 0xac, 0x5d, 0xc4, 0xb9, 0x57, 0x0a, 0xae, 0x8f, 0x47, 0xb7, 0xf5, + 0x7f, 0xd8, 0x76, 0x7b, 0xea, 0x1a, 0x24, 0xae, 0x7b, 0xed, 0x65, 0xb4, 0xaf, 0xdc, + 0x8f, 0x12, 0x78, 0xc3, 0x0e, 0x2d, 0xb9, 0x8f, 0xd1, 0x72, 0x73, 0x0a, 0xc6, 0xbb, + 0xed, 0x4f, 0x11, 0x27, 0xcd, 0x32, 0xb0, 0x4a, 0x95, 0xb2, 0x05, 0x52, 0x6c, 0xfc, + 0xb4, 0xc4, 0xe1, 0xcc, 0x95, 0x51, 0x75, 0xb3, 0xe8, 0xde, 0x1f, 0x5d, 0x81, 0xb1, + 0x86, 0x69, 0x69, 0x23, 0x50, 0xaa, 0xa1, 0xa1, 0xd7, 0x97, 0x61, 0x75, 0x82, 0xe5, + 0x4d, 0x7a, 0x5b, 0x57, 0xa6, 0x83, 0xb3, 0x2f, 0xb1, 0x09, 0x80, 0x62, 0xda, 0xd7, + 0xb0, 0xc2, 0xeb, 0x51, 0x8f, 0x68, 0x62, 0xe8, 0x3d, 0xb2, 0x5e, 0x3d, 0xba, 0xf7, + 0xae, 0xd5, 0x04, 0xde, 0x93, 0x2a, 0xcb, 0x99, 0xd7, 0x35, 0x99, 0x2c, 0xe6, 0x2b, + 0xae, 0x9e, 0xf8, 0x93, 0xff, 0x6a, 0xcc, 0x0f, 0xfc, 0xf8, 0xe3, 0x48, 0x3e, 0x14, + 0x6b, 0x9d, 0x49, 0xdd, 0x8c, 0x78, 0x35, 0xf4, 0x3a, 0x37, 0xdc, 0xa0, 0x78, 0x7e, + 0x3e, 0xc9, 0xf6, 0x60, 0x52, 0x23, 0xd5, 0xba, 0x7a, 0xe0, 0xab, 0x90, 0x25, 0xb7, + 0x3b, 0xc0, 0x3f, 0x7f, 0xac, 0x36, 0xc0, 0x09, 0xa5, 0x6d, 0x4d, 0x95, 0xd1, 0xe8, + 0x1d, 0x3b, 0x3e, 0xbc, 0xa7, 0xe5, 0x4c, 0xc1, 0xa1, 0x2d, 0x12, 0x7b, 0x57, 0xc8, + 0x13, 0x89, 0x76, 0xe7, 0x91, 0x01, 0x3b, 0x01, 0x5f, 0x06, 0xa6, 0x24, 0xf5, 0x21, + 0xb6, 0xee, 0x04, 0xec, 0x98, 0x08, 0x93, 0xc7, 0xe5, 0xe0, 0x1a, 0x33, 0x62, 0x03, + 0x59, 0x40, 0x94, 0xf8, 0x28, 0x33, 0xd7, 0x44, 0x5f, 0xe2, 0xd0, 0x91, 0x30, 0xf6, + 0x35, 0x11, 0xda, 0x54, 0x83, 0x2d, 0xe9, 0x13, 0x6b, 0x39, 0xf4, 0x59, 0x9f, 0x5a, + 0xa5, 0xdf, 0xbb, 0x45, 0xda, 0x60, 0xcd, 0xce, 0xab, 0x7e, 0xef, 0xde, 0x89, 0xbe, + 0x63, 0xf3, 0xf7, 0xc0, 0xd2, 0x32, 0x48, 0x47, 0xcc, 0xe1, 0x40, 0x5d, 0xef, 0x7c, + 0x46, 0x9b, 0x0e, 0x27, 0x24, 0x94, 0xe5, 0xdf, 0x54, 0xf5, 0x68, 0x65, 0x6c, 0xb9, + 0xc8, 0x81, 0x8d, 0x92, 0xb7, 0x2b, 0x8b, 0xc3, 0x4d, 0xb7, 0xbb, 0x31, 0x12, 0x48, + 0x7e, 0x74, 0x6e, 0xef, 0xe4, 0xe8, 0x08, 0xbb, 0xb2, 0x87, 0xd9, 0x9b, 0xf0, 0x7d, + 0x00, 0xda, 0xbe, 0xde, 0xdc, 0x5e, 0x5f, 0x07, 0x4f, 0xfe, 0xae, 0x0c, 0xba, 0x7d, + 0xa3, 0xa5, 0x16, 0xc1, 0x73, 0xbe, 0x1c, 0x51, 0x33, 0x23, 0xe1, 0x19, 0xf6, 0x35, + 0xe8, 0x20, 0x9a, 0x07, 0x4b, 0x21, 0x6b, 0x70, 0x23, 0xfa, 0xdc, 0x2d, 0x25, 0x94, + 0x9c, 0x90, 0x03, 0x7e, 0x71, 0xe3, 0xe5, 0x50, 0x72, 0x6d, 0x21, 0x0a, 0x2c, 0x68, + 0x83, 0x42, 0xe5, 0x24, 0x40, 0x63, 0x5e, 0x9c, 0xc1, 0x4a, 0xfe, 0x10, 0x10, 0x26, + 0x21, 0xa9, 0xc9, 0xac, 0xcb, 0x78, 0x2e, 0x9e, 0x4a, 0x5f, 0xa8, 0x7f, 0x0a, 0x95, + 0x6f, 0x5b, 0x85, 0x50, 0x99, 0x60, 0x28, 0x5c, 0x22, 0x62, 0x7c, 0x59, 0x48, 0x3a, + 0x5a, 0x4c, 0x28, 0xcc, 0xe4, 0xb1, 0x56, 0xe5, 0x51, 0x40, 0x6a, 0x7e, 0xe8, 0x35, + 0x56, 0x56, 0xa2, 0x1e, 0x43, 0xe3, 0x8c, 0xe1, 0x29, 0xfd, 0xad, 0xb7, 0x59, 0xed, + 0xdf, 0xa0, 0x8f, 0x00, 0xfc, 0x8e, 0x56, 0x7c, 0xef, 0x93, 0xc6, 0x79, 0x2d, 0x01, + 0xdf, 0x05, 0xe6, 0xd5, 0x80, 0xf4, 0xd5, 0xd4, 0x8d, 0xf0, 0x42, 0x45, 0x1a, 0x33, + 0x59, 0x0d, 0x3e, 0x8c, 0xf4, 0x9b, 0x26, 0x27, 0x21, 0x8f, 0x0c, 0x29, 0x2f, 0xa6, + 0x6a, 0xda, 0x94, 0x5f, 0xa5, 0x5b, 0xb2, 0x35, 0x48, 0xe3, 0x3a, 0x83, 0xa5, 0x62, + 0x95, 0x7a, 0x31, 0x49, 0xa9, 0x93, 0xcc, 0x47, 0x23, 0x62, 0x29, 0x87, 0x36, 0xa8, + 0xb7, 0x78, 0xd9, 0x7c, 0xe4, 0x23, 0x01, 0x3d, 0x64, 0xb3, 0x2c, 0xd1, 0x72, 0xef, + 0xa5, 0x51, 0xbf, 0x7f, 0x36, 0x8f, 0x04, 0xbd, 0xae, 0xc6, 0x09, 0x1a, 0x30, 0x04, + 0xa7, 0x57, 0x59, 0x8b, 0x80, 0x1d, 0xcf, 0x67, 0x5c, 0xb8, 0x3e, 0x43, 0xa5, 0x3a, + 0xe8, 0xb2, 0x54, 0xd3, 0x33, 0xbc, 0xda, 0x20, 0xd4, 0x81, 0x7d, 0x34, 0x77, 0xab, + 0xfb, 0xa2, 0x5b, 0xb8, 0x3d, 0xf5, 0x94, 0x9c, 0x12, 0x6f, 0x14, 0x9b, 0x1d, 0x99, + 0x34, 0x1e, 0x4e, 0x6f, 0x91, 0x20, 0xf4, 0xd4, 0x1e, 0x62, 0x91, 0x85, 0x00, 0x2c, + 0x72, 0xc0, 0x12, 0xc4, 0x14, 0xd2, 0x38, 0x2a, 0x6d, 0x47, 0xc7, 0xb3, 0xde, 0xab, + 0xa7, 0x70, 0xc4, 0x00, 0xca, 0x96, 0xb2, 0x81, 0x4f, 0x6b, 0x26, 0xc3, 0xef, 0x17, + 0x42, 0x9f, 0x1a, 0x98, 0xc8, 0x5d, 0x83, 0xdb, 0x20, 0xef, 0xad, 0x48, 0xbe, 0x89, + 0x96, 0xfb, 0x1b, 0xff, 0x59, 0x1e, 0xff, 0xf3, 0x60, 0xfe, 0x11, 0x99, 0x05, 0x6c, + 0x56, 0xe5, 0xfe, 0xec, 0x61, 0xa7, 0xb8, 0xb9, 0xf6, 0x99, 0xd6, 0x01, 0x2c, 0x28, + 0x49, 0x23, 0x2f, 0x32, 0x9f, 0xef, 0x95, 0xc7, 0xaf, 0x37, 0x00, 0x98, 0xff, 0xe4, + 0x91, 0x8e, 0x0c, 0xa1, 0xdf, 0x47, 0xf2, 0x75, 0x86, 0x7b, 0x73, 0x9e, 0x0a, 0x51, + 0x4d, 0x32, 0x09, 0x32, 0x5e, 0x21, 0x70, 0x45, 0x92, 0x7b, 0x47, 0x9c, 0x1c, 0xe2, + 0xe5, 0xd5, 0x4f, 0x25, 0x48, 0x8c, 0xad, 0x15, 0x13, 0xe3, 0xf4, 0x4a, 0x21, 0x26, + 0x6c, 0xfd, 0x84, 0x16, 0x33, 0x32, 0x7d, 0xee, 0x6c, 0xf8, 0x10, 0xfb, 0xf7, 0x39, + 0x3e, 0x31, 0x7d, 0x9e, 0x53, 0xd1, 0xbe, 0x1d, 0x5a, 0xe7, 0x83, 0x9b, 0x66, 0xb9, + 0x43, 0xb9, 0xed, 0x18, 0xf2, 0xc5, 0x30, 0xe9, 0x75, 0x42, 0x23, 0x32, 0xc3, 0x43, + 0x9c, 0xce, 0x49, 0xa2, 0x9f, 0x2a, 0x33, 0x6a, 0x48, 0x51, 0x26, 0x3c, 0x5e, 0x9b, + 0xd1, 0x3d, 0x73, 0x11, 0x09, 0xe8, 0x44, 0xb7, 0xf8, 0xc3, 0x92, 0xa5, 0xc1, 0xdc, + 0xaa, 0x2a, 0xe5, 0xf5, 0x0f, 0xf6, 0x3f, 0xab, 0x97, 0x65, 0xe0, 0x16, 0x70, 0x2c, + 0x35, 0xa6, 0x7c, 0xd7, 0x36, 0x4d, 0x3f, 0xab, 0x55, 0x2f, 0xb3, 0x49, 0xe3, 0x5c, + 0x15, 0xc5, 0x02, 0x50, 0x45, 0x3f, 0xd1, 0x8f, 0x7b, 0x85, 0x59, 0x92, 0x63, 0x2e, + 0x2c, 0x76, 0xc0, 0xfb, 0xf1, 0xef, 0x96, 0x3e, 0xa8, 0x0e, 0x32, 0x23, 0xde, 0x32, + 0x77, 0xbc, 0x55, 0x92, 0x51, 0x72, 0x58, 0x29, 0xec, 0x03, 0xf2, 0x13, 0xba, 0x89, + 0x55, 0xca, 0xb2, 0x82, 0x2f, 0xf2, 0x1a, 0x9b, 0x0a, 0x49, 0x04, 0xd6, 0x68, 0xfc, + 0xd7, 0x72, 0x24, 0xbd, 0xe3, 0xdd, 0x01, 0xf6, 0xff, 0xc4, 0x82, 0x8f, 0x6b, 0x64, + 0x23, 0x0b, 0x35, 0xc6, 0xa0, 0x49, 0x87, 0x34, 0x94, 0x27, 0x6e, 0xa1, 0xd7, 0xed, + 0x5e, 0x92, 0xcb, 0x4f, 0x90, 0xba, 0x83, 0xa9, 0xe4, 0x96, 0x01, 0xb1, 0x94, 0x04, + 0x2f, 0x29, 0x00, 0xd9, 0x9d, 0x31, 0x2d, 0x7b, 0x70, 0x50, 0x8c, 0xf1, 0x76, 0x06, + 0x6d, 0x15, 0x4d, 0xbe, 0x96, 0xef, 0x9d, 0x43, 0x67, 0xe4, 0xc8, 0x40, 0xe4, 0xa1, + 0x7b, 0x5e, 0x51, 0x22, 0xe8, 0xeb, 0xe2, 0x15, 0x8a, 0x3c, 0x5f, 0x4c, 0xba, 0xe2, + 0x1e, 0xa3, 0xfa, 0x1a, 0xe6, 0xc2, 0x5a, 0x94, 0x62, 0xeb, 0xcb, 0xb0, 0xfd, 0x5f, + 0x14, 0x55, 0x4b, 0xc9, 0x77, 0x47, 0xc3, 0x3e, 0x34, 0xda, 0x90, 0xc8, 0x16, 0xd8, + 0xd0, 0xd5, 0x0b, 0xfe, 0x37, 0x61, 0x8c, 0x58, 0x12, 0x89, 0x14, 0x84, 0xfa, 0x25, + 0x93, 0x22, 0xc1, 0x50, 0x92, 0xd4, 0x15, 0x5d, 0x86, 0x96, 0xd6, 0xf1, 0x2f, 0x24, + 0xfd, 0x36, 0x44, 0x96, 0xb3, 0xbe, 0x08, 0x71, 0xca, 0x3d, 0xd9, 0x62, 0x53, 0x48, + 0xa6, 0x14, 0xb5, 0x9b, 0xde, 0x45, 0x88, 0x56, 0x49, 0xba, 0xe3, 0x6d, 0xe3, 0x4d, + 0xef, 0x8f, 0xce, 0xc8, 0x53, 0x43, 0x47, 0x5d, 0x97, 0x6a, 0xe1, 0xe9, 0xb2, 0x78, + 0x29, 0xce, 0x2a, 0xc5, 0xef, 0xd0, 0xb3, 0x99, 0xa8, 0xb4, 0x48, 0xbe, 0x65, 0x04, + 0x29, 0x4e, 0xe6, 0xb3, 0xc1, 0xc6, 0xa5, 0x34, 0x2d, 0x7c, 0x01, 0xae, 0x9d, 0x8a, + 0xd3, 0x07, 0x0c, 0x2b, 0x1a, 0x91, 0x57, 0x3a, 0xf5, 0xe0, 0xc5, 0xe4, 0xcb, 0xbf, + 0x4a, 0xcd, 0xc6, 0xb5, 0x4c, 0x92, 0x72, 0x20, 0x0d, 0x99, 0x70, 0x25, 0x0c, 0x17, + 0xc1, 0x03, 0x6f, 0x06, 0x08, 0x5c, 0x41, 0x85, 0x8e, 0xd3, 0xa0, 0xc4, 0x81, 0x50, + 0xbc, 0x69, 0x7e, 0x4a, 0x69, 0x5f, 0xef, 0x33, 0x5f, 0x7a, 0xd0, 0x7e, 0x1a, 0x46, + 0xdc, 0x76, 0x7f, 0xf8, 0x22, 0xdb, 0x70, 0xe6, 0x66, 0x90, 0x80, 0xb9, 0x81, 0x6b, + 0x22, 0x32, 0xc8, 0x1a, 0x4c, 0x66, 0xcc, 0x58, 0x6a, 0xbf, 0xe1, 0xea, 0xa8, 0xca, + 0x6c, 0xf4, 0x1f, 0xc3, 0xc3, 0xe6, 0xc7, 0xb8, 0x86, 0xfb, 0x6d, 0xac, 0x9f, 0x48, + 0x22, 0xb4, 0xfc, 0x6f, 0xff, 0x9d, 0x05, 0x13, 0xd6, 0x1a, 0x21, 0xc8, 0x0a, 0x37, + 0x76, 0x71, 0xd1, 0x35, 0xa6, 0x68, 0xa0, 0xae, 0x2b, 0xb9, 0x34, 0xc8, 0x2c, 0x41, + 0x42, 0xda, 0x69, 0xd1, 0x2c, 0xa7, 0xde, 0x9a, 0x7d, 0xf7, 0x06, 0x40, 0x0e, 0xc7, + 0x98, 0x78, 0xd8, 0x68, 0xe1, 0x7e, 0x8f, 0x71, 0xea, 0x31, 0x49, 0x5a, 0xf8, 0x19, + 0xa0, 0x16, 0xcc, 0x41, 0x9e, 0x07, 0xc5, 0x01, 0xaa, 0x83, 0x09, 0xb2, 0xe6, 0xc8, + 0x5b, 0x79, 0xb2, 0x76, 0x37, 0x33, 0xa3, 0x7b, 0xbc, 0x04, 0x20, 0xd4, 0x25, 0x37, + 0xb8, 0x71, 0xb4, 0x29, 0x4a, 0x65, 0xd3, 0xe0, 0x55, 0xff, 0x71, 0x8d, 0xd9, 0xdc, + 0x8c, 0x75, 0xe7, 0xe5, 0xb2, 0xef, 0xe4, 0x42, 0x63, 0x73, 0x71, 0xb7, 0xc4, 0x8f, + 0x6e, 0xe9, 0x9e, 0x3e, 0xa3, 0x8a, 0x4b, 0x0f, 0x2f, 0x67, 0xfc, 0x2b, 0x90, 0x8c, + 0xda, 0x65, 0x7e, 0xae, 0x75, 0x4e, 0x03, 0x7e, 0x26, 0x2e, 0x9a, 0x9f, 0x9b, 0xd7, + 0xec, 0x42, 0x67, 0xed, 0x8e, 0x96, 0x93, 0x0e, 0x10, 0x84, 0x78, 0x3c, 0x37, 0xd6, + 0xf9, 0xdd, 0x15, 0xfd, 0x29, 0xf4, 0xcc, 0x47, 0x7e, 0x66, 0xf1, 0x30, 0xd6, 0x30, + 0x43, 0x0d, 0xcc, 0x01, 0x04, 0x89, 0x9b, 0x4f, 0x9f, 0x46, 0xeb, 0x09, 0x0e, 0xf7, + 0xfc, 0x90, 0xb4, 0x79, 0xab, 0xf6, 0x1f, 0x93, 0x95, 0x5e, 0xe0, 0x0e, 0x6a, 0x18, + 0x48, 0xf1, 0xab, 0x14, 0xad, 0x33, 0x4f, 0x2b, 0x68, 0x03, 0x58, 0x08, 0xcd, 0xf1, + 0xbb, 0x9e, 0x9d, 0x9a, 0x81, 0x6b, 0xaf, 0x72, 0x8a, 0x95, 0x5b, 0x96, 0x0b, 0x77, + 0x01, 0xfa, 0x62, 0x66, 0x87, 0xdc, 0x3c, 0x9c, 0xba, 0x64, 0x63, 0x37, 0xb5, 0x3e, + 0x29, 0x81, 0x6e, 0x94, 0x82, 0xdd, 0xf5, 0x57, 0x8a, 0x87, 0x68, 0xaa, 0xe4, 0x77, + 0xfc, 0xe4, 0x10, 0xac, 0x2d, 0x5d, 0xe6, 0x09, 0x58, 0x61, 0xc1, 0x11, 0xd7, 0xfe, + 0xb3, 0xe6, 0xbb, 0x4f, 0xbb, 0x5a, 0x54, 0x95, 0x54, 0x95, 0x97, 0x27, 0x98, 0x35, + 0x0a, 0x25, 0x3f, 0x05, 0xf6, 0x6c, 0x2e, 0xcf, 0xcb, 0xc0, 0xed, 0x43, 0xf5, 0xec, + 0x2e, 0x6d, 0x8d, 0xba, 0x15, 0xa5, 0x12, 0x54, 0xd9, 0x7b, 0x18, 0x21, 0x10, 0x7c, + 0x07, 0xdd, 0x9a, 0x16, 0xef, 0x84, 0x06, 0xf9, 0x43, 0xe2, 0x82, 0xb9, 0x5d, 0x4b, + 0x36, 0x25, 0x30, 0xc9, 0x13, 0xd6, 0xba, 0x42, 0x1d, 0xf6, 0x02, 0x7d, 0xe5, 0xaf, + 0x1e, 0x47, 0x45, 0xd5, 0x86, 0x81, 0x06, 0x95, 0x4b, 0xe6, 0xc1, 0x96, 0x27, 0x80, + 0xa2, 0x94, 0x10, 0x72, 0xe9, 0x51, 0x31, 0xb1, 0x67, 0x9d, 0xf0, 0x63, 0x76, 0x25, + 0x04, 0x2c, 0x37, 0xd4, 0x8f, 0xfb, 0x15, 0x2e, 0x5e, 0xbc, 0x18, 0x5c, 0x8a, 0x2b, + 0x7d, 0x43, 0x85, 0xf1, 0xc9, 0x5a, 0xf9, 0x37, 0xdf, 0x78, 0xdf, 0xd8, 0x75, 0x7f, + 0xab, 0x43, 0x49, 0x68, 0xb0, 0xb5, 0x7c, 0x66, 0x57, 0x44, 0x68, 0xf1, 0x60, 0xb4, + 0x47, 0xac, 0x82, 0x21, 0xe5, 0x06, 0x06, 0x76, 0xa8, 0x42, 0xa1, 0xc6, 0xb7, 0x17, + 0x2d, 0xd3, 0x34, 0x0f, 0x76, 0x40, 0x70, 0xab, 0x1f, 0xe0, 0x91, 0xc5, 0xc7, 0x4c, + 0x95, 0xa5, 0xdc, 0x04, 0x33, 0x90, 0x72, 0x3a, 0x4c, 0x12, 0x7d, 0xa1, 0x4c, 0xdd, + 0xe1, 0xdc, 0x26, 0x75, 0xa6, 0x23, 0x40, 0xb3, 0xe6, 0xaf, 0xd0, 0x52, 0x2a, 0x31, + 0xde, 0x26, 0xe7, 0xd1, 0xec, 0x3a, 0x9c, 0x8a, 0x09, 0x1f, 0xfd, 0xc7, 0x5b, 0x7e, + 0xcf, 0xdc, 0x7c, 0x12, 0x99, 0x5a, 0x5e, 0x37, 0xce, 0x34, 0x88, 0xbd, 0x29, 0xf8, + 0x62, 0x9d, 0x68, 0xf6, 0x96, 0x49, 0x24, 0x48, 0xdd, 0x52, 0x66, 0x97, 0x47, 0x6d, + 0xc0, 0x61, 0x34, 0x6e, 0xbe, 0x3f, 0x67, 0x72, 0x17, 0xff, 0x9c, 0x60, 0xef, 0xce, + 0x94, 0x3a, 0xf2, 0x8d, 0xfd, 0x3f, 0x9e, 0x59, 0x69, 0x25, 0x98, 0xa6, 0x04, 0x7c, + 0x23, 0xc4, 0xc0, 0x14, 0x00, 0xf1, 0xab, 0x57, 0x30, 0xea, 0xc0, 0xae, 0x8d, 0x58, + 0x43, 0xd5, 0x05, 0x1c, 0x37, 0x62, 0x40, 0x17, 0x2a, 0xf2, 0x18, 0xd7, 0xa1, 0xec, + 0xfe, 0x65, 0xb4, 0xf7, 0x51, 0x00, 0x63, 0x89, 0x83, 0xc1, 0x4d, 0xe4, 0x97, 0x47, + 0x55, 0xda, 0xde, 0x80, 0x18, 0xc9, 0xb8, 0xf4, 0x54, 0x3f, 0xb0, 0x95, 0x96, 0x15, + 0x13, 0xe6, 0x7c, 0x61, 0xdb, 0xc5, 0x9c, 0x60, 0x7f, 0x9b, 0x51, 0xf8, 0xd0, 0x9b, + 0xdc, 0xad, 0x28, 0xbc, 0xfb, 0x9e, 0x5d, 0x27, 0x44, 0xea, 0x88, 0x48, 0xb2, 0x62, + 0x3a, 0xc0, 0x7f, 0x8e, 0xf6, 0x1a, 0x81, 0xa3, 0x59, 0x10, 0xb8, 0xa1, 0xba, 0xf3, + 0x9a, 0x91, 0x9a, 0x7b, 0x60, 0xbc, 0x60, 0x4d, 0x63, 0x18, 0x5f, 0x75, 0x92, 0x21, + 0xd8, 0x47, 0xcc, 0x54, 0xa2, 0x27, 0x65, 0xa4, 0xc3, 0x34, 0x75, 0xb5, 0x79, 0x1e, + 0x9a, 0xf3, 0x27, 0x1f, 0xc8, 0xd9, 0x35, 0x06, 0x67, 0x09, 0x0d, 0x81, 0x84, 0xec, + 0x50, 0x52, 0x2d, 0x80, 0x4f, 0x23, 0xc4, 0xfb, 0x44, 0xff, 0xa4, 0x81, 0xbc, 0x92, + 0xae, 0x40, 0x8d, 0x1b, 0x9f, 0x2b, 0x13, 0x19, 0x04, 0xf9, 0x70, 0x5c, 0x59, 0xe2, + 0xf4, 0xbd, 0xe7, 0xa3, 0xb2, 0xc0, 0x85, 0xd9, 0x3f, 0xd2, 0xab, 0xc5, 0xe1, 0x4d, + 0x16, 0x30, 0x01, 0xa1, 0x2f, 0x51, 0x93, 0x8d, 0x02, 0x1a, 0xfa, 0x92, 0x23, 0x9b, + 0x87, 0x3d, 0xc6, 0xc3, 0x57, 0xea, 0xa8, 0xaf, 0x4e, 0xe6, 0xd0, 0x05, 0x40, 0x65, + 0x7f, 0xe3, 0x29, 0x14, 0x10, 0x3b, 0x5d, 0x98, 0xf6, 0x8b, 0xd3, 0xe2, 0xb5, 0x35, + 0x9f, 0x08, 0xcc, 0xd8, 0x8d, 0x0c, 0x81, 0x1e, 0x4c, 0x31, 0xfb, 0xb4, 0x9f, 0x3a, + 0x90, 0xbb, 0xd0, 0x5d, 0xce, 0x62, 0xf3, 0x44, 0xe7, 0x07, 0x75, 0x93, 0x15, 0x9a, + 0xe3, 0x50, 0x50, 0xb0, 0x4c, 0x9e, 0x6b, 0x86, 0xbc, 0x43, 0x2d, 0xc8, 0xb0, 0x48, + 0xc7, 0x3c, 0x00, 0x18, 0xca, 0x5b, 0x69, 0x41, 0x12, 0x97, 0x73, 0x2a, 0x4e, 0x1a, + 0xa9, 0x9a, 0x92, 0x8c, 0x71, 0xe7, 0xa2, 0x4f, 0xd2, 0x77, 0x85, 0x6a, 0xa4, 0x25, + 0x01, 0xe5, 0x1b, 0x01, 0x2a, 0xea, 0x94, 0x46, 0xa2, 0x10, 0x4e, 0x93, 0xf8, 0x15, + 0xa0, 0xb3, 0xa2, 0x9b, 0x45, 0x83, 0x14, 0xf3, 0xd8, 0xbe, 0x2b, 0x98, 0x23, 0xd3, + 0x42, 0xf4, 0x62, 0x13, 0xe9, 0x42, 0xa7, 0xe1, 0x9a, 0x46, 0xe9, 0x70, 0xb5, 0xc5, + 0x06, 0x70, 0x84, 0x30, 0x31, 0x7b, 0x1b, 0xb3, 0xb3, 0x5d, 0xf6, 0x8a, 0xe3, 0x3a, + 0x49, 0x26, 0xa0, 0x3e, 0x6b, 0xfe, 0xb5, 0x51, 0x04, 0x16, 0xfc, 0xbb, 0x05, 0x24, + 0xc9, 0xca, 0x50, 0x74, 0x15, 0x6c, 0xc5, 0xa5, 0xd6, 0xfe, 0x1c, 0x99, 0x5e, 0xdc, + 0x60, 0xa2, 0xf5, 0x50, 0x41, 0x1a, 0xa4, 0x1e, 0x3d, 0xa3, 0xbd, 0xcf, 0x64, 0xbc, + 0xf0, 0x4a, 0x05, 0x10, 0x57, 0x1b, 0x93, 0x6d, 0x47, 0xe5, 0x5c, 0xec, 0x03, 0x30, + 0xee, 0x8d, 0xfe, 0x73, 0x56, 0x34, 0x04, 0xf0, 0x47, 0xd7, 0xf3, 0xa8, 0xa3, 0xd7, + 0x74, 0x3b, 0xc5, 0x54, 0x95, 0x52, 0x10, 0xf1, 0xeb, 0x0d, 0x08, 0x59, 0x9e, 0xa7, + 0x7d, 0x5f, 0x97, 0x4d, 0x87, 0x17, 0x6d, 0x37, 0xd9, 0x8b, 0x9c, 0x0a, 0xd4, 0x40, + 0x40, 0x72, 0x09, 0xed, 0x6a, 0x9f, 0x08, 0x46, 0x4d, 0x56, 0x55, 0x93, 0xe1, 0xa6, + 0x3b, 0x93, 0x85, 0x36, 0xb4, 0x92, 0x44, 0xe9, 0x7d, 0x88, 0x01, 0x73, 0xb6, 0x40, + 0xf2, 0xdd, 0xb7, 0x4d, 0x06, 0x8e, 0xcb, 0x46, 0xcf, 0x28, 0x9b, 0x7d, 0x89, 0x13, + 0x07, 0xbb, 0xa3, 0x70, 0x54, 0xcf, 0x91, 0xb3, 0x1f, 0xc8, 0x2f, 0x74, 0xd5, 0xfc, + 0xc0, 0x00, 0x94, 0x2e, 0xde, 0x91, 0x18, 0x25, 0xf5, 0x3f, 0xe6, 0x09, 0x68, 0x6f, + 0x46, 0x32, 0x23, 0xb1, 0xe9, 0xbc, 0x03, 0xbd, 0xe8, 0x95, 0xd1, 0x23, 0x8f, 0xad, + 0x04, 0xa3, 0xbf, 0xce, 0x68, 0xa0, 0x75, 0xe8, 0xa3, 0x7c, 0x0e, 0x87, 0xbf, 0x46, + 0xdd, 0x01, 0x55, 0x45, 0xf9, 0xb4, 0xfb, 0x0e, 0xec, 0x64, 0x5f, 0xfc, 0xbb, 0xe0, + 0xca, 0x5f, 0x8c, 0x56, 0x1b, 0x25, 0x7d, 0x52, 0xd6, 0x02, 0xd8, 0xc9, 0x4c, 0x50, + 0x28, 0x73, 0xa0, 0x1d, 0x92, 0x51, 0xd8, 0xc8, 0x60, 0xc0, 0x41, 0x52, 0x5b, 0x3b, + 0xf4, 0xe3, 0xa2, 0xeb, 0x92, 0x72, 0x81, 0x5c, 0x75, 0x86, 0x76, 0x84, 0x28, 0xb4, + 0xc2, 0xb2, 0x5e, 0x37, 0x45, 0xf0, 0x09, 0xc5, 0xdc, 0xe2, 0x0b, 0x69, 0xd5, 0xd7, + 0xc4, 0x3c, 0xeb, 0x73, 0x6b, 0x68, 0x31, 0xe8, 0xc1, 0x10, 0xf1, 0x6c, 0xfd, 0xb3, + 0xa4, 0x67, 0xe9, 0x41, 0x4c, 0x00, 0xec, 0xf1, 0x37, 0x31, 0x50, 0x08, 0x94, 0x55, + 0x56, 0x78, 0xc4, 0x97, 0xfa, 0xba, 0x9a, 0x95, 0xd0, 0x1c, 0xc4, 0x64, 0x39, 0x0f, + 0xc4, 0xa7, 0x6b, 0xfa, 0x8b, 0x0e, 0x1c, 0x68, 0xa5, 0x25, 0xd7, 0x06, 0xd6, 0x60, + 0x4b, 0x23, 0x30, 0xb6, 0xb3, 0x48, 0x52, 0x15, 0xf6, 0x06, 0xf1, 0x88, 0x3a, 0x75, + 0x15, 0x88, 0xc7, 0xef, 0xa5, 0x06, 0xc3, 0xe8, 0xd0, 0xc6, 0x01, 0x92, 0xe8, 0x47, + 0x6b, 0xd1, 0x17, 0x5d, 0x95, 0x62, 0x08, 0x7b, 0xdb, 0x81, 0x8e, 0x66, 0x21, 0x62, + 0x86, 0xba, 0xfe, 0x47, 0xff, 0x4d, 0xbc, 0xce, 0xd5, 0x14, 0x44, 0x48, 0x0a, 0x9a, + 0x56, 0x73, 0xec, 0xe7, 0xfa, 0xc7, 0x3a, 0x0e, 0xd4, 0x1a, 0xb0, 0x05, 0x17, 0x53, + 0xa7, 0xca, 0xa8, 0x9b, 0xe3, 0x13, 0x9a, 0xfd, 0x97, 0x93, 0xb3, 0xe0, 0x2f, 0x27, + 0xf0, 0x40, 0x04, 0x65, 0x95, 0xac, 0xd4, 0x7b, 0xf1, 0x3f, 0xd0, 0xda, 0x27, 0xf0, + 0x9e, 0xda, 0x48, 0x03, 0x6d, 0x3e, 0xe4, 0x37, 0xf2, 0xee, 0x8f, 0x86, 0x06, 0xea, + 0x97, 0x34, 0x3c, 0x33, 0x58, 0x46, 0x57, 0xf4, 0x6d, 0xba, 0x99, 0xdb, 0x5c, 0xfe, + 0x6c, 0xa1, 0x76, 0xfa, 0xb7, 0xb0, 0xf3, 0xbf, 0xa0, 0xab, 0x61, 0xe3, 0x40, 0xc3, + 0x4e, 0xb9, 0xf1, 0x7c, 0x7e, 0xc2, 0xbe, 0x03, 0xb1, 0x80, 0xf0, 0xbb, 0x6f, 0x43, + 0x4c, 0x2a, 0x65, 0x42, 0xe0, 0x0e, 0x84, 0x37, 0x3f, 0x4f, 0x46, 0x49, 0xcd, 0xa3, + 0x2b, 0xf6, 0x86, 0x66, 0x61, 0x43, 0xf6, 0x22, 0xaa, 0x48, 0x04, 0x60, 0xb5, 0xaf, + 0xac, 0x51, 0x86, 0x07, 0xcd, 0x9a, 0xf8, 0xbc, 0xd6, 0xb5, 0x8c, 0x30, 0x12, 0x73, + 0x16, 0xb2, 0x5d, 0x5e, 0xa7, 0xbf, 0x6b, 0x0c, 0xab, 0x85, 0x42, 0xff, 0x69, 0xd9, + 0xb2, 0xf1, 0x80, 0xbe, 0x12, 0xed, 0x75, 0x34, 0x4a, 0x39, 0x5a, 0xa1, 0x0f, 0x85, + 0x2f, 0x08, 0x3a, 0xd6, 0x4e, 0xf4, 0x0e, 0x9c, 0x03, 0x09, 0xe9, 0xbb, 0xa5, 0x4b, + 0x8c, 0xb3, 0x3c, 0x95, 0x49, 0x8a, 0x69, 0x53, 0x8d, 0x3a, 0xe5, 0xb2, 0x5e, 0x24, + 0x70, 0x98, 0x30, 0x6f, 0xa8, 0xc7, 0x4a, 0x8e, 0xe5, 0xbc, 0xa9, 0x41, 0x53, 0x1d, + 0x61, 0xaa, 0xc2, 0x7a, 0xab, 0x3d, 0xc5, 0x61, 0x7d, 0x56, 0x06, 0xc9, 0x57, 0x7a, + 0x2a, 0x83, 0x46, 0xe8, 0xd8, 0x5b, 0x32, 0xb8, 0x50, 0x57, 0x75, 0x10, 0x8d, 0xc8, + 0x5e, 0x2a, 0xde, 0x2e, 0xac, 0x1e, 0x63, 0x6e, 0x1a, 0xf4, 0x05, 0x4c, 0x8b, 0x6f, + 0x57, 0x63, 0x2d, 0xf2, 0x69, 0xc3, 0x72, 0x3b, 0x32, 0x08, 0x72, 0xe4, 0xc5, 0x7b, + 0x21, 0x83, 0x58, 0xdc, 0x7e, 0x99, 0x05, 0xbb, 0x04, 0xed, 0xf9, 0x2e, 0xdf, 0x0d, + 0xf6, 0x35, 0xf3, 0xbf, 0x36, 0x1e, 0x57, 0xa1, 0x32, 0x96, 0xe1, 0x44, 0x7a, 0xf5, + 0x08, 0x78, 0x72, 0xd6, 0x36, 0xe2, 0x75, 0x18, 0xa9, 0x87, 0x6e, 0x15, 0xeb, 0x01, + 0xf5, 0xe8, 0xde, 0xd8, 0x18, 0x92, 0x51, 0x1c, 0xc2, 0x85, 0x1b, 0x00, 0xb8, 0x32, + 0x71, 0x2a, 0x6d, 0x3b, 0xa5, 0x66, 0x65, 0x17, 0xbc, 0xd3, 0x56, 0x76, 0x21, 0xa7, + 0xcf, 0x84, 0x45, 0x58, 0x96, 0x53, 0x26, 0x20, 0x20, 0xc3, 0x3b, 0xf7, 0x80, 0x31, + 0xb8, 0xee, 0x07, 0x07, 0xde, 0x07, 0x20, 0x68, 0xc1, 0x70, 0x57, 0x03, 0x27, 0xe6, + 0xd9, 0xf5, 0xc6, 0xdd, 0xc3, 0x35, 0x40, 0x2e, 0xfc, 0x54, 0x88, 0x62, 0xf5, 0xa0, + 0x70, 0x94, 0xfd, 0x42, 0x8a, 0x7b, 0xbc, 0x15, 0xd7, 0xb3, 0x8d, 0x05, 0x36, 0x2c, + 0x9c, 0xa9, 0x85, 0xf5, 0x8a, 0x76, 0x64, 0x7d, 0x2b, 0xe4, 0xc2, 0xcd, 0x6b, 0x3d, + 0x17, 0xd6, 0x87, 0x09, 0x71, 0xd7, 0xa0, 0x98, 0xba, 0xf7, 0x2c, 0x6f, 0x6f, 0x12, + 0x14, 0xcf, 0x1f, 0xaa, 0xe4, 0x88, 0xbd, 0x7d, 0xe2, 0x59, 0xd3, 0x41, 0x5c, 0x2f, + 0x0d, 0xde, 0xc7, 0x45, 0x70, 0x04, 0xf3, 0x57, 0x08, 0xd1, 0xec, 0xcc, 0xcc, 0x0d, + 0xf6, 0x5a, 0x04, 0x94, 0x3a, 0xd5, 0xcb, 0xc1, 0x3f, 0x29, 0x5f, 0x00, 0x0f, 0xe0, + 0x56, 0xc4, 0x0b, 0x2d, 0x88, 0xf2, 0x7d, 0xc3, 0x4c, 0xfe, 0xb8, 0x03, 0xbe, 0x34, + 0x83, 0xa9, 0xeb, 0xf9, 0xb5, 0xa9, 0x02, 0x60, 0x57, 0x72, 0x5d, 0x63, 0xea, 0xd2, + 0xc0, 0xc0, 0xff, 0x1f, 0xe2, 0x6a, 0xc1, 0xe7, 0xbd, 0xfc, 0xd6, 0xfa, 0xd8, 0x75, + 0x84, 0x2d, 0x19, 0x4f, 0x33, 0x17, 0x50, 0x46, 0x2c, 0x06, 0xb8, 0xd7, 0x98, 0x2d, + 0x67, 0x99, 0x5e, 0xd5, 0xd3, 0xae, 0x96, 0xa0, 0x5a, 0xe0, 0x06, 0x7f, 0x4e, 0xb1, + 0xc7, 0xc9, 0x32, 0x31, 0xbd, 0x39, 0x77, 0x3c, 0xbe, 0x0a, 0x9d, 0x66, 0xb0, 0xc9, + 0xaa, 0x8c, 0xff, 0x6a, 0x37, 0x6e, 0x1f, 0x37, 0x2e, 0xac, 0x6a, 0xc4, 0xe4, 0x6c, + 0xc0, 0x94, 0x22, 0x45, 0xd4, 0xc2, 0xdc, 0xf0, 0x2d, 0x76, 0x40, 0xff, 0xcc, 0x5a, + 0x6a, 0xc3, 0xa8, 0x7f, 0x5c, 0x41, 0x15, 0x51, 0xbc, 0xc2, 0xf2, 0x6c, 0xb9, 0x49, + 0x61, 0xd5, 0x3f, 0x95, 0xdd, 0xb1, 0x9a, 0xe9, 0x30, 0xc8, 0xd7, 0x0f, 0x03, 0x1b, + 0x29, 0xa5, 0xdf, 0x99, 0xff, 0x36, 0x69, 0x5e, 0x80, 0x2c, 0xbc, 0xb6, 0xb5, 0x8c, + 0x1b, 0xa7, 0xed, 0x5e, 0xac, 0xfa, 0x76, 0x41, 0x4a, 0x41, 0xad, 0x4a, 0x44, 0xf7, + 0x1f, 0x1b, 0x58, 0x0d, 0x34, 0xc3, 0xa9, 0x52, 0x92, 0x0b, 0x25, 0x4a, 0x14, 0x5f, + 0xea, 0x51, 0x7f, 0x5b, 0x42, 0xb2, 0xf6, 0x5e, 0xcd, 0x0f, 0x82, 0x59, 0x54, 0x78, + 0xd8, 0x0a, 0xe5, 0xc8, 0xce, 0xea, 0x12, 0xa1, 0x61, 0xcc, 0xbb, 0x5e, 0xac, 0x09, + 0x99, 0x0f, 0xc6, 0x19, 0xa4, 0x60, 0x80, 0x43, 0x6d, 0xbd, 0x08, 0xd7, 0x47, 0x84, + 0xaf, 0x00, 0x2d, 0x58, 0xe0, 0x6f, 0xaf, 0x7f, 0x3c, 0xea, 0xe7, 0xd3, 0x41, 0x9b, + 0x1f, 0xca, 0x26, + ], + jumbled: vec![ + 0x55, 0x29, 0x26, 0x0b, 0x66, 0xf7, 0xbf, 0xdd, 0x40, 0x22, 0x06, 0x2c, 0xb8, 0x33, + 0x70, 0xd2, 0x9a, 0x21, 0xe8, 0x25, 0xa7, 0xba, 0x1c, 0x66, 0xfd, 0x9d, 0xd4, 0x40, + 0x8a, 0x6c, 0x2b, 0x8b, 0x8a, 0x8c, 0x49, 0xd5, 0xa3, 0x8b, 0xe5, 0x5b, 0x78, 0x3c, + 0xae, 0x3f, 0xdb, 0xe1, 0x4e, 0x70, 0xd0, 0xeb, 0xef, 0xc0, 0x49, 0x2a, 0x84, 0x2c, + 0x2e, 0x32, 0x91, 0x96, 0x14, 0x50, 0xa9, 0xf8, 0x51, 0x8b, 0x9f, 0x2e, 0xa9, 0xd2, + 0x37, 0x7e, 0x91, 0x52, 0xbf, 0xb1, 0xf3, 0xcf, 0x9c, 0x59, 0xc0, 0x12, 0x45, 0x13, + 0x33, 0x3c, 0x67, 0x80, 0x5f, 0x87, 0x50, 0xa6, 0xde, 0x32, 0xb8, 0x9b, 0x95, 0xa6, + 0x66, 0x50, 0x32, 0x11, 0x07, 0xa7, 0xdf, 0x97, 0x5e, 0xcb, 0x5e, 0x26, 0x67, 0x72, + 0x9a, 0xad, 0x24, 0x75, 0x30, 0xc0, 0x1b, 0x91, 0xc2, 0xbd, 0xc7, 0x6a, 0x9a, 0x32, + 0x2b, 0x32, 0x8d, 0x90, 0x71, 0x55, 0xf2, 0x1f, 0x2f, 0xd7, 0x8a, 0x33, 0x80, 0xc2, + 0xd9, 0x6e, 0xde, 0x49, 0x30, 0xcc, 0x9c, 0xa0, 0x76, 0xcc, 0xb1, 0xbf, 0x9c, 0x4c, + 0x41, 0x3a, 0x29, 0xe1, 0x9b, 0x63, 0xba, 0x52, 0x86, 0x5a, 0xf5, 0xd8, 0x01, 0xfd, + 0x3e, 0x22, 0x54, 0x21, 0xc0, 0xb5, 0xe4, 0x98, 0x44, 0xbc, 0x40, 0x49, 0x24, 0x6d, + 0x01, 0xc1, 0x89, 0x26, 0xf5, 0x0f, 0x1f, 0xd5, 0x9b, 0xf4, 0xdf, 0x76, 0xb4, 0x1b, + 0xfe, 0x48, 0x64, 0x21, 0xb3, 0x3d, 0xf3, 0xa6, 0xbb, 0x8f, 0xa0, 0xa2, 0x09, 0xe9, + 0x6b, 0xf5, 0xcb, 0x5b, 0x55, 0xcc, 0x72, 0x65, 0xe7, 0xeb, 0xe9, 0xd2, 0x2b, 0xa6, + 0x46, 0x44, 0xbd, 0xcc, 0xea, 0xa1, 0x32, 0x40, 0xcf, 0xeb, 0xe1, 0xed, 0x6c, 0x6e, + 0xf7, 0x2a, 0xc8, 0xa5, 0x6f, 0x85, 0x4f, 0x0b, 0xba, 0x8c, 0x5d, 0xeb, 0x79, 0x47, + 0x26, 0xcb, 0x28, 0x8d, 0x3c, 0xf3, 0xd2, 0xc6, 0x66, 0xfb, 0x29, 0xda, 0x8f, 0x74, + 0x86, 0xcf, 0xec, 0xc5, 0x9a, 0x58, 0xb4, 0x93, 0x80, 0x5a, 0x0c, 0x1d, 0x3b, 0xc5, + 0x38, 0xc8, 0x11, 0x1b, 0xbc, 0x4a, 0x9e, 0x10, 0x3b, 0xc4, 0xb4, 0x3e, 0x1f, 0x08, + 0xc6, 0x38, 0x70, 0x66, 0xab, 0x1f, 0x89, 0x06, 0x4e, 0xd1, 0xef, 0x7d, 0x69, 0xb7, + 0x80, 0x49, 0x52, 0x91, 0x58, 0xd9, 0x28, 0x35, 0x4b, 0xea, 0xc1, 0x08, 0xa0, 0x9d, + 0x82, 0x62, 0x5a, 0xb8, 0xe7, 0x25, 0xe1, 0x12, 0x1d, 0x26, 0x13, 0x1d, 0x3a, 0x37, + 0x1c, 0xb2, 0x88, 0x1a, 0x78, 0x93, 0xd5, 0x2c, 0x3e, 0x6a, 0xb1, 0xed, 0xa7, 0xbf, + 0x95, 0xb3, 0xa4, 0x4f, 0xf4, 0x96, 0xa5, 0x0d, 0xaf, 0xc3, 0x4b, 0x88, 0x59, 0xc8, + 0x73, 0xc4, 0xa4, 0x3a, 0xb7, 0x47, 0x24, 0x3b, 0x74, 0x7f, 0x30, 0x9c, 0x9a, 0x70, + 0x93, 0x26, 0xc3, 0x29, 0x3f, 0xfa, 0xbe, 0x5a, 0x50, 0x16, 0xe1, 0x4c, 0x3f, 0x02, + 0x82, 0x3c, 0x0d, 0x3f, 0x05, 0xa7, 0xdf, 0xf3, 0x45, 0xa3, 0x4e, 0xc7, 0x6a, 0x32, + 0x40, 0x01, 0x4c, 0x65, 0x53, 0x9d, 0x93, 0x93, 0x44, 0x27, 0x7e, 0x77, 0x7f, 0x8f, + 0xc5, 0xa9, 0x41, 0xde, 0x93, 0x35, 0xfe, 0x67, 0x3f, 0x08, 0xdc, 0x3c, 0xc7, 0xf8, + 0x8f, 0x45, 0x0d, 0xd5, 0xa5, 0xad, 0x08, 0x11, 0x35, 0x74, 0x60, 0xf3, 0xec, 0xfc, + 0xfa, 0x17, 0x8d, 0x42, 0x8c, 0xc7, 0x60, 0x15, 0xf1, 0xb1, 0xaf, 0xb8, 0xe5, 0xd4, + 0x95, 0x99, 0xf9, 0x18, 0x21, 0xc8, 0x82, 0x12, 0x9c, 0x3e, 0x10, 0xf3, 0x82, 0xa7, + 0x79, 0x5a, 0x10, 0xac, 0x61, 0xf1, 0x5f, 0xe0, 0x08, 0xe5, 0x08, 0x74, 0x5b, 0xba, + 0xa8, 0xce, 0x81, 0x0a, 0x5c, 0xa8, 0xb4, 0xbc, 0x39, 0x53, 0x10, 0x1e, 0x33, 0xc6, + 0xff, 0x98, 0x1d, 0x58, 0x62, 0xfa, 0x10, 0x6c, 0x89, 0xd9, 0xe1, 0x9d, 0x97, 0xd8, + 0x42, 0x01, 0x45, 0x42, 0x01, 0x2c, 0xdd, 0xa3, 0x3b, 0x22, 0x74, 0x9e, 0x07, 0x8e, + 0x46, 0xa2, 0xe0, 0xb9, 0xfc, 0xf1, 0xa1, 0x71, 0x84, 0xb8, 0x92, 0xe8, 0x4b, 0x5c, + 0x49, 0xef, 0x76, 0xd5, 0x1a, 0xfd, 0x5b, 0xa5, 0x54, 0x4a, 0x7a, 0x81, 0x27, 0x87, + 0x67, 0x5a, 0x7c, 0x5b, 0xf3, 0xb7, 0x9a, 0x94, 0xb3, 0x8d, 0x11, 0x10, 0xfa, 0x5a, + 0x97, 0x53, 0x23, 0x3d, 0x8c, 0x74, 0x18, 0x45, 0x44, 0xf9, 0xb3, 0xf0, 0xbd, 0x45, + 0x28, 0x6c, 0xa5, 0x64, 0xd8, 0x35, 0xd7, 0x0c, 0x70, 0xa6, 0xae, 0x5c, 0x22, 0x58, + 0x3b, 0x27, 0x82, 0x95, 0xc9, 0xb2, 0xbe, 0xd2, 0x4e, 0xed, 0x3c, 0xba, 0xd7, 0x2c, + 0xbf, 0xee, 0x9e, 0xc1, 0xed, 0x3f, 0xcf, 0xff, 0x3e, 0x88, 0x9b, 0x37, 0x2c, 0xae, + 0x7c, 0xfa, 0x18, 0xe2, 0x93, 0x85, 0x79, 0xea, 0xe5, 0x52, 0x16, 0x2b, 0x19, 0xea, + 0x35, 0xdc, 0xf7, 0xfe, 0x43, 0xcd, 0xf8, 0xa4, 0xf3, 0x00, 0xed, 0x79, 0x72, 0xfc, + 0x46, 0x70, 0x38, 0x09, 0x7d, 0x76, 0xae, 0xe0, 0x8d, 0xf6, 0x51, 0xd2, 0x71, 0x91, + 0x4b, 0xde, 0x0b, 0xc3, 0x52, 0x2f, 0x9f, 0xb7, 0xd4, 0x87, 0x98, 0x33, 0xe7, 0xb8, + 0x02, 0xf4, 0x9c, 0x18, 0x2e, 0xa3, 0x3c, 0x1e, 0xb7, 0x4b, 0xc3, 0xba, 0x1f, 0x3b, + 0x76, 0xe9, 0x2a, 0x30, 0xff, 0x8d, 0xfe, 0x1a, 0xe0, 0x93, 0xd8, 0x69, 0xa8, 0x38, + 0xc4, 0xc7, 0xfd, 0x40, 0xd0, 0x84, 0x21, 0xe3, 0x87, 0x15, 0x24, 0xc0, 0x0c, 0x5f, + 0xac, 0x34, 0xd0, 0x51, 0x80, 0xb5, 0xd0, 0xca, 0x7e, 0xcd, 0xe5, 0xd7, 0xf6, 0xc8, + 0xdb, 0x86, 0xcc, 0xf4, 0x1a, 0x56, 0xcd, 0xc5, 0x9f, 0x0e, 0xb8, 0xfb, 0x58, 0xf8, + 0xc5, 0x9c, 0xfa, 0x9d, 0xcd, 0xf1, 0xbe, 0x14, 0xb7, 0x4a, 0xba, 0xe1, 0x2c, 0xc1, + 0x91, 0xc6, 0x79, 0xb9, 0x49, 0x5c, 0x85, 0x20, 0xbc, 0xc6, 0x9e, 0x25, 0x74, 0xc8, + 0xf3, 0x3b, 0xc4, 0x16, 0x87, 0xf8, 0xba, 0x19, 0xc4, 0x54, 0xc5, 0xbc, 0xe3, 0xdd, + 0xfe, 0xd0, 0x4a, 0xa1, 0x54, 0x2e, 0xd8, 0x9c, 0x78, 0x52, 0xa2, 0xa6, 0x8f, 0xe2, + 0x28, 0x4a, 0x7e, 0xd3, 0x5c, 0x8c, 0xe0, 0x9e, 0xa3, 0xcf, 0xd1, 0x4b, 0x6c, 0x35, + 0x53, 0xc1, 0xfc, 0xdb, 0xaf, 0x1d, 0x93, 0x6c, 0x1f, 0x0c, 0x93, 0x99, 0xf9, 0x4b, + 0x9e, 0x38, 0x47, 0x75, 0xd3, 0x6d, 0xbc, 0xba, 0xd3, 0xd6, 0x26, 0xb0, 0x62, 0xd1, + 0xea, 0xe8, 0x1b, 0x88, 0x3d, 0x44, 0x47, 0x72, 0xdd, 0xa9, 0x5a, 0xa2, 0x8c, 0x22, + 0xfd, 0x4a, 0xd0, 0xe8, 0x8d, 0x8d, 0xd6, 0x4c, 0x15, 0x65, 0x50, 0x6b, 0x63, 0xf8, + 0x56, 0x22, 0xcb, 0x8c, 0x5f, 0x8c, 0xff, 0xe6, 0x4e, 0x8b, 0xb3, 0x84, 0x92, 0x46, + 0x76, 0x0f, 0x7c, 0x29, 0x10, 0xcd, 0x80, 0x9a, 0x9d, 0xa7, 0x0e, 0x14, 0xfa, 0x3b, + 0x97, 0xf2, 0x83, 0x05, 0x75, 0x9d, 0x3a, 0xd1, 0x10, 0xca, 0xa9, 0xa0, 0xce, 0x64, + 0x70, 0xa3, 0xb5, 0x22, 0x37, 0x3f, 0x6e, 0x05, 0xde, 0x4e, 0x95, 0x49, 0x63, 0xb9, + 0xb1, 0x27, 0xd1, 0x29, 0xf8, 0x6e, 0x4e, 0x26, 0xde, 0x4d, 0x98, 0x96, 0xa8, 0x65, + 0x76, 0xb0, 0x47, 0x46, 0x53, 0x5e, 0x66, 0x84, 0x27, 0x71, 0x31, 0x8f, 0x09, 0x46, + 0x6d, 0xc5, 0x53, 0xee, 0xe9, 0xff, 0x62, 0xb9, 0xd2, 0x54, 0x4b, 0x41, 0xa4, 0xde, + 0xa0, 0xa2, 0x02, 0xb7, 0x57, 0xc3, 0x48, 0x41, 0x75, 0x82, 0xb0, 0x61, 0x49, 0xb4, + 0xfd, 0x21, 0x84, 0x9b, 0xa9, 0x3b, 0xdb, 0x4c, 0x26, 0x3d, 0xac, 0x94, 0x51, 0x8a, + 0x65, 0xdf, 0xa3, 0x71, 0x3b, 0x62, 0x92, 0xe1, 0x29, 0x6f, 0x04, 0xa1, 0x22, 0x2f, + 0x18, 0x83, 0x03, 0x58, 0x23, 0xa2, 0x57, 0xb8, 0x3f, 0x48, 0x06, 0x1c, 0xb1, 0x0c, + 0xfc, 0x19, 0xb6, 0x9d, 0x7b, 0x16, 0xce, 0xae, 0x95, 0x04, 0xfc, 0x87, 0x73, 0x19, + 0xfa, 0x5d, 0x0d, 0x4e, 0x4f, 0xbc, 0xdd, 0x5a, 0xa2, 0xd0, 0xc0, 0x3e, 0x39, 0xc1, + 0x49, 0xff, 0xea, 0x6c, 0x35, 0x80, 0xf1, 0x6a, 0x19, 0x14, 0x5d, 0xa8, 0xff, 0x4c, + 0x79, 0x44, 0x4b, 0x29, 0x8a, 0x93, 0x44, 0x6f, 0xc6, 0xa7, 0x4b, 0x3f, 0x32, 0x33, + 0xad, 0x11, 0x63, 0xee, 0x43, 0xe1, 0x94, 0x1f, 0x10, 0xb4, 0x86, 0xd9, 0xf8, 0x1f, + 0x16, 0x0b, 0x53, 0x0e, 0x6b, 0x76, 0xbd, 0x05, 0xb2, 0xe5, 0xcb, 0x8d, 0xe8, 0x97, + 0xc2, 0xa7, 0xbe, 0xea, 0x58, 0xcb, 0x85, 0x39, 0xfd, 0x67, 0x8f, 0xd0, 0x0b, 0x81, + 0xb3, 0x2e, 0x39, 0x39, 0xe1, 0x0e, 0x4e, 0xed, 0x4f, 0x6d, 0x3a, 0x21, 0x56, 0xcf, + 0x07, 0x77, 0x5c, 0x34, 0x38, 0xea, 0x67, 0x69, 0x19, 0x38, 0x1f, 0x6c, 0x7b, 0x7b, + 0xb1, 0xfc, 0x0f, 0x67, 0x05, 0x67, 0x5f, 0x1f, 0x41, 0xc1, 0xc5, 0xce, 0x75, 0x4d, + 0x44, 0x11, 0xda, 0x85, 0x95, 0x97, 0xd2, 0xe1, 0x88, 0x89, 0xa5, 0x3e, 0x7d, 0x18, + 0x9a, 0x4f, 0xbd, 0x9d, 0x0a, 0x4c, 0xff, 0xf7, 0x8f, 0xa3, 0x70, 0x5d, 0xb6, 0xf9, + 0xdb, 0xaf, 0xbe, 0x7c, 0x8b, 0xf4, 0x6b, 0x79, 0x7c, 0xb5, 0x41, 0x53, 0x60, 0x2e, + 0x51, 0x72, 0x72, 0xd2, 0x80, 0x30, 0xea, 0xb5, 0x17, 0xcc, 0x3f, 0x94, 0x31, 0x57, + 0xfb, 0x9f, 0xc2, 0x96, 0x7e, 0x91, 0x75, 0xb7, 0xc0, 0x99, 0xfb, 0x65, 0x6a, 0x20, + 0x22, 0xce, 0x94, 0x81, 0x3c, 0x10, 0x64, 0x07, 0x2f, 0x2e, 0x40, 0x7b, 0x64, 0xea, + 0xe2, 0x15, 0x78, 0xd2, 0x0f, 0x4c, 0x57, 0x03, 0xd5, 0x8d, 0x00, 0x03, 0x40, 0x5e, + 0x5e, 0xe5, 0x3a, 0xfa, 0x18, 0xdf, 0xa3, 0xec, 0x9a, 0x4b, 0x35, 0xe1, 0x0b, 0x7d, + 0xaa, 0x83, 0x7d, 0x72, 0x7f, 0xeb, 0x3a, 0xfc, 0xe2, 0x62, 0x08, 0x06, 0x5e, 0x5a, + 0xe4, 0x29, 0x8c, 0x5c, 0x8f, 0x6c, 0xd0, 0xe3, 0x4a, 0x53, 0x20, 0x83, 0xca, 0xec, + 0xd5, 0x66, 0xf3, 0xc2, 0xe2, 0x85, 0x5c, 0x5a, 0x85, 0xe7, 0x67, 0xb5, 0x9e, 0x4f, + 0xc8, 0x2b, 0x7f, 0xbd, 0x2a, 0xfe, 0xb9, 0x79, 0x98, 0x32, 0xc6, 0x73, 0xa3, 0x8a, + 0xf1, 0x5c, 0xe8, 0xdc, 0xb4, 0x9e, 0xb7, 0xab, 0x66, 0xe9, 0x5a, 0xcc, 0xf3, 0x7a, + 0xf7, 0x29, 0xe4, 0x4c, 0x71, 0xff, 0xb4, 0xf2, 0x4a, 0xd3, 0x96, 0xd3, 0x0f, 0xe3, + 0x4d, 0x52, 0x6f, 0x07, 0x9f, 0xfc, 0x03, 0x61, 0x06, 0x8c, 0x64, 0x91, 0xf0, 0xcc, + 0xac, 0x69, 0xa5, 0x6a, 0x05, 0x62, 0x41, 0x91, 0x9d, 0xb6, 0x14, 0x2b, 0x19, 0xf6, + 0xb8, 0x54, 0x52, 0x02, 0x44, 0x66, 0x4c, 0x1a, 0x9a, 0x52, 0x66, 0x41, 0x8e, 0x6c, + 0x0a, 0x45, 0x31, 0xad, 0x21, 0x5a, 0x1a, 0x5f, 0x1d, 0x47, 0x18, 0xc6, 0xcd, 0xe1, + 0x2c, 0x57, 0x3d, 0x3c, 0xb6, 0x68, 0xf9, 0xcc, 0xb9, 0x13, 0x30, 0x1c, 0x73, 0x1e, + 0x09, 0xe2, 0xe4, 0xe5, 0xcc, 0x93, 0x00, 0xb5, 0xf4, 0xe7, 0x23, 0x40, 0x0c, 0x7c, + 0xb8, 0x50, 0xf1, 0x9d, 0x85, 0x53, 0xc7, 0xe1, 0xea, 0x7c, 0x07, 0xce, 0x95, 0x30, + 0xc1, 0xc9, 0x99, 0x4a, 0x59, 0xc5, 0xac, 0x9f, 0xa1, 0xe4, 0xbd, 0xa1, 0x65, 0x42, + 0x51, 0xc9, 0x1d, 0x3f, 0xb8, 0x8c, 0x5d, 0x74, 0x81, 0x06, 0xb3, 0x94, 0xa7, 0xe1, + 0xe4, 0xb5, 0xcf, 0xf8, 0x06, 0xb7, 0x3f, 0x84, 0x30, 0x29, 0x0d, 0xca, 0x8e, 0x8e, + 0x3d, 0xb5, 0x8a, 0x33, 0x22, 0xc6, 0xbe, 0x1f, 0x2e, 0xf8, 0x16, 0x76, 0xfe, 0x4e, + 0xe5, 0x7f, 0x42, 0x24, 0x3e, 0x3c, 0xdb, 0x73, 0x46, 0xa5, 0xeb, 0xc4, 0xf8, 0xb7, + 0x0f, 0x51, 0x48, 0xca, 0x94, 0xaa, 0xe3, 0x72, 0xbf, 0x7b, 0x4f, 0xf6, 0xc9, 0x87, + 0xf4, 0xf3, 0x4d, 0xd9, 0xd2, 0xa6, 0x1f, 0x19, 0xcf, 0x60, 0x92, 0x52, 0xbd, 0x07, + 0xb0, 0x96, 0x32, 0x27, 0x88, 0x40, 0x15, 0xca, 0x56, 0x10, 0xdd, 0xd1, 0x9c, 0x8a, + 0xea, 0x1d, 0x98, 0x97, 0x0d, 0xae, 0x84, 0xfc, 0x46, 0x09, 0x4a, 0x67, 0xbd, 0x40, + 0xd9, 0x0a, 0xb3, 0x0b, 0x38, 0x80, 0xc0, 0x6b, 0xc0, 0x94, 0x9c, 0xfd, 0x52, 0xc6, + 0x26, 0x00, 0x4d, 0xa9, 0xea, 0xc3, 0xa4, 0x81, 0x44, 0x56, 0x93, 0x76, 0x3a, 0xbd, + 0xa5, 0x7a, 0xb3, 0xf8, 0x49, 0x7d, 0xfe, 0x69, 0x22, 0x43, 0x0c, 0x54, 0x6d, 0xc0, + 0x76, 0xd8, 0xd5, 0x85, 0x87, 0xba, 0xec, 0x7c, 0x9e, 0xa7, 0xff, 0xf4, 0x33, 0xd8, + 0x8f, 0x1e, 0x27, 0xe4, 0xf1, 0x80, 0x01, 0xa1, 0xa0, 0x7e, 0x1b, 0x84, 0xde, 0x1a, + 0x1b, 0x5b, 0xda, 0xad, 0x70, 0x6f, 0x61, 0x8f, 0xd3, 0x84, 0x60, 0x7b, 0x91, 0x9a, + 0xd8, 0xea, 0xcf, 0x70, 0xad, 0x60, 0xe5, 0x4d, 0x5a, 0xf4, 0xbe, 0x54, 0x1c, 0x84, + 0xe4, 0xd7, 0x25, 0xb2, 0xe1, 0x90, 0xee, 0xac, 0x26, 0x97, 0x09, 0x66, 0x31, 0x02, + 0xcd, 0x89, 0x88, 0x3a, 0x53, 0x92, 0xee, 0xc6, 0xb3, 0x28, 0xdf, 0xc4, 0x29, 0xdb, + 0xc3, 0x0a, 0x1a, 0xf4, 0xac, 0x86, 0x8b, 0xf8, 0x7c, 0x11, 0x3e, 0xd0, 0x50, 0x5d, + 0x6a, 0xa7, 0xa5, 0xf0, 0x2b, 0xed, 0xaf, 0xd0, 0xee, 0xb4, 0x78, 0x71, 0xef, 0xca, + 0x6b, 0xb6, 0x46, 0x96, 0x23, 0x2d, 0xc9, 0x93, 0x67, 0x92, 0xf7, 0xb8, 0xed, 0x7d, + 0xe2, 0x8b, 0x49, 0x91, 0x1e, 0xdf, 0xed, 0x4e, 0xd6, 0xf9, 0x31, 0x5c, 0xb7, 0xb1, + 0x49, 0x1a, 0x24, 0x61, 0xda, 0xb9, 0x26, 0x02, 0x64, 0xed, 0x10, 0xda, 0x9b, 0xf1, + 0xd1, 0x91, 0x8a, 0xe8, 0x74, 0x31, 0xa9, 0x4c, 0x6c, 0x9e, 0x31, 0x60, 0xc3, 0xd0, + 0x54, 0x68, 0x31, 0x1f, 0xea, 0xa8, 0xb6, 0x5b, 0x62, 0x00, 0x8b, 0x25, 0x7d, 0x23, + 0x13, 0x66, 0xce, 0xe3, 0x05, 0x01, 0x4e, 0xb4, 0xa3, 0x5d, 0x02, 0x3a, 0xf3, 0x7d, + 0x05, 0xef, 0x50, 0xff, 0x14, 0xec, 0xb6, 0xc5, 0xf0, 0x20, 0xcd, 0xd9, 0x9a, 0xd8, + 0xd7, 0xd5, 0xcb, 0xde, 0xcb, 0x8f, 0x95, 0xa7, 0xb6, 0xe9, 0x2b, 0xaa, 0xe9, 0x45, + 0xdd, 0xe4, 0x4c, 0x3d, 0x04, 0xd5, 0x0d, 0xb8, 0xa7, 0xcb, 0x5f, 0x99, 0xfb, 0xf6, + 0xa4, 0x3e, 0xf0, 0x6a, 0x39, 0xbb, 0x45, 0x2e, 0xec, 0xa8, 0x03, 0x0d, 0x07, 0xdf, + 0xcd, 0x6f, 0xb9, 0x62, 0x22, 0x70, 0xad, 0x31, 0xad, 0x80, 0x5c, 0x9b, 0x93, 0xc0, + 0x49, 0xf6, 0x0e, 0xcb, 0xae, 0x54, 0x3a, 0x3a, 0xb2, 0x0c, 0xff, 0x01, 0x51, 0x0e, + 0x7f, 0x75, 0x76, 0x27, 0x0e, 0xf5, 0xf6, 0x5c, 0xd1, 0xce, 0xed, 0x00, 0x9e, 0x36, + 0x74, 0x04, 0x8e, 0x91, 0xa1, 0xef, 0x3e, 0x80, 0x4e, 0x73, 0x66, 0x23, 0x12, 0x95, + 0xc5, 0xe2, 0x48, 0x85, 0x1d, 0x50, 0x49, 0x91, 0x9c, 0x51, 0x1a, 0xae, 0x33, 0xfc, + 0x71, 0xee, 0x5b, 0x26, 0xdc, 0xe8, 0x69, 0x16, 0xb9, 0xd6, 0xe7, 0x79, 0xce, 0xcb, + 0xbb, 0xd2, 0xe6, 0x84, 0x86, 0xe4, 0x6d, 0x93, 0x9e, 0x10, 0x62, 0xda, 0x8d, 0xbc, + 0xf1, 0x7d, 0x9d, 0xfb, 0xc0, 0x92, 0x25, 0xfc, 0x94, 0x89, 0x13, 0x39, 0xbb, 0xac, + 0xe9, 0xc0, 0x84, 0x9c, 0x48, 0x76, 0x66, 0x04, 0xf7, 0xe3, 0xef, 0xd6, 0x8b, 0xbf, + 0xb6, 0x10, 0x73, 0x0b, 0xbb, 0x36, 0x75, 0x9f, 0x87, 0x1f, 0x52, 0xc9, 0xb4, 0x6f, + 0xc5, 0x43, 0x53, 0x9e, 0x6a, 0xb4, 0x09, 0x04, 0x91, 0x98, 0x3f, 0xa4, 0x0a, 0x12, + 0xdb, 0xe7, 0x75, 0x5d, 0x06, 0x96, 0x7d, 0x1d, 0x92, 0x39, 0x3a, 0x13, 0xc7, 0x00, + 0x4c, 0x99, 0x60, 0x52, 0xd7, 0xbb, 0x80, 0xfc, 0x9f, 0x2c, 0x4d, 0xa3, 0x36, 0x91, + 0x48, 0x18, 0x17, 0x03, 0xe2, 0x44, 0x3f, 0x9e, 0x10, 0xda, 0x28, 0x94, 0xc0, 0x94, + 0x2e, 0x47, 0x51, 0x51, 0xab, 0x7b, 0x04, 0x83, 0xc2, 0x00, 0x67, 0x49, 0x65, 0xf5, + 0x61, 0x6a, 0x42, 0x8a, 0x5a, 0xcf, 0xa0, 0xcb, 0x32, 0x86, 0x10, 0x92, 0x30, 0x37, + 0x24, 0x25, 0x51, 0xf6, 0xe2, 0x67, 0xbc, 0x3b, 0x4b, 0x9e, 0x09, 0xd0, 0x5c, 0xc8, + 0xe6, 0x0a, 0xac, 0xef, 0xa6, 0x0e, 0xe7, 0xbc, 0x4d, 0x0f, 0xe3, 0xc8, 0xb1, 0x39, + 0xf5, 0x4d, 0x04, 0xca, 0x04, 0xe0, 0xaa, 0xc7, 0x0d, 0x39, 0x13, 0x3c, 0x8c, 0x29, + 0x19, 0xd6, 0xea, 0x3a, 0x80, 0xf6, 0x65, 0x40, 0xa9, 0x8c, 0xe4, 0x98, 0xf0, 0x26, + 0x79, 0x3e, 0x86, 0xd3, 0xb9, 0x0b, 0x7c, 0x08, 0x4e, 0xc2, 0x9e, 0x17, 0x28, 0xe5, + 0x1e, 0x17, 0xb4, 0x41, 0xe7, 0x7d, 0xf0, 0xb5, 0x7f, 0x4d, 0x7b, 0x22, 0x3e, 0xc2, + 0x2c, 0x3c, 0x4f, 0x87, 0xc7, 0x06, 0x2b, 0xd5, 0x7c, 0x32, 0x4e, 0xd2, 0x08, 0x5e, + 0xbc, 0x3d, 0xb6, 0x70, 0x46, 0xde, 0x87, 0xad, 0x5c, 0x25, 0x7e, 0x74, 0x76, 0x8f, + 0x12, 0x67, 0xa7, 0xb6, 0x7e, 0x9f, 0xe7, 0xc9, 0x6a, 0xe9, 0x7b, 0x92, 0xec, 0xd3, + 0x16, 0xff, 0x5f, 0x99, 0xa4, 0xa3, 0xf9, 0xc4, 0x93, 0x26, 0xcf, 0x3d, 0xe2, 0x1b, + 0xe8, 0x4f, 0xd8, 0x7b, 0xcd, 0x01, 0xd3, 0xe0, 0xe8, 0xd0, 0x64, 0x0f, 0xb9, 0x1a, + 0xc2, 0x4e, 0x28, 0x60, 0xfe, 0xbd, 0x1f, 0x6d, 0x06, 0x7d, 0x16, 0xb1, 0x41, 0xaa, + 0x57, 0x48, 0xbd, 0x34, 0x41, 0xde, 0xc8, 0x6c, 0x90, 0xbf, 0xcd, 0x9d, 0x7e, 0x56, + 0xed, 0xfb, 0x4d, 0x71, 0x8f, 0x7b, 0x47, 0x95, 0x95, 0x91, 0x67, 0xe9, 0x96, 0x8c, + 0xae, 0xc7, 0x58, 0x8c, 0x8d, 0xf7, 0x10, 0x95, 0xf5, 0x32, 0x33, 0xd1, 0xaf, 0xec, + 0x6e, 0x52, 0x24, 0x07, 0x0b, 0xd0, 0xd6, 0x9b, 0x5f, 0x00, 0x0f, 0x8b, 0x54, 0x1b, + 0xdb, 0x50, 0xd1, 0x74, 0xd4, 0x79, 0x4f, 0x4c, 0x13, 0xdd, 0xab, 0x1d, 0xdf, 0xe7, + 0x79, 0xa2, 0x7b, 0x73, 0x4b, 0x06, 0x82, 0x09, 0x0a, 0x0e, 0x54, 0xf0, 0xdc, 0xfc, + 0xdc, 0xee, 0x70, 0xbb, 0x7c, 0x16, 0xe8, 0x32, 0x21, 0xc0, 0x2e, 0xdd, 0x6f, 0x8b, + 0x4b, 0x2f, 0xbe, 0x7b, 0x4b, 0x27, 0x3a, 0xf1, 0x6d, 0xa7, 0xf5, 0xc4, 0x10, 0x75, + 0x5c, 0xfa, 0x13, 0x3c, 0x24, 0x36, 0xeb, 0x35, 0x11, 0xd3, 0x95, 0x7f, 0x38, 0x77, + 0xa3, 0x74, 0x53, 0x74, 0x1e, 0x7f, 0x24, 0x1d, 0x1d, 0xf5, 0x57, 0x59, 0x5c, 0x8a, + 0x0b, 0xa7, 0x32, 0x63, 0x37, 0xce, 0x46, 0x4a, 0xc3, 0x86, 0x87, 0xd2, 0xf8, 0x55, + 0xb4, 0xfa, 0x4a, 0x2f, 0x9b, 0xda, 0xe9, 0xfd, 0x9d, 0x05, 0x2a, 0x27, 0xf2, 0x9b, + 0x95, 0x0c, 0xe4, 0x0a, 0xf4, 0xe2, 0xd2, 0x3a, 0x35, 0xc7, 0x94, 0x15, 0x36, 0x4e, + 0xa2, 0x7f, 0x34, 0xe5, 0x8a, 0x10, 0x58, 0x49, 0x50, 0x68, 0x18, 0x2b, 0x65, 0xc2, + 0x0a, 0xb9, 0x3c, 0xef, 0x21, 0x4a, 0x53, 0xab, 0x01, 0x8a, 0x81, 0x58, 0xbf, 0xaf, + 0xb6, 0x96, 0x80, 0x59, 0xf0, 0x2c, 0xe0, 0xab, 0xb6, 0xb1, 0x38, 0xd5, 0x01, 0x72, + 0xcf, 0xce, 0x95, 0x96, 0x00, 0xdf, 0x71, 0xc4, 0x5a, 0x25, 0x08, 0x52, 0x63, 0xa1, + 0x61, 0x50, 0x93, 0x1d, 0x1f, 0x7c, 0x68, 0x12, 0x0a, 0xf7, 0x78, 0x7d, 0xb9, 0x49, + 0xce, 0xc6, 0xce, 0x77, 0x1e, 0x09, 0x81, 0xf9, 0x26, 0xa4, 0x45, 0x4f, 0x86, 0xd8, + 0x4c, 0x5b, 0x52, 0x9e, 0x85, 0x87, 0xb8, 0xf7, 0x14, 0x4f, 0x4c, 0x1b, 0xc6, 0xcf, + 0x10, 0x32, 0xc0, 0x0b, 0x97, 0x84, 0x0f, 0x5f, 0xf9, 0x5d, 0x2b, 0xce, 0x8a, 0xad, + 0x06, 0x89, 0x52, 0x8d, 0x8b, 0xf9, 0x20, 0x05, 0x9a, 0x63, 0x6f, 0x1c, 0x59, 0x60, + 0x8e, 0x10, 0x01, 0xe9, 0xc0, 0xff, 0x16, 0xa6, 0x29, 0xbe, 0x8a, 0x12, 0x30, 0xa1, + 0x69, 0x17, 0xd1, 0x73, 0xd0, 0x17, 0x23, 0x1a, 0x5b, 0x34, 0xd2, 0xf5, 0x9b, 0x64, + 0x33, 0x27, 0x84, 0x5a, 0x10, 0xd7, 0x7f, 0xf1, 0x2f, 0x2b, 0x68, 0x0e, 0x6d, 0xee, + 0x0c, 0x16, 0xf9, 0xdc, 0x85, 0x35, 0x69, 0x6e, 0xa8, 0xe6, 0x05, 0x6d, 0x37, 0xf5, + 0xeb, 0x11, 0x91, 0xa4, 0x2a, 0x66, 0xbe, 0xfe, 0x24, 0xc9, 0xc6, 0xc9, 0x96, 0x9f, + 0x01, 0x68, 0xa3, 0x1a, 0xa4, 0x1f, 0x92, 0xbf, 0xf7, 0x17, 0xbb, 0x85, 0x3f, 0x1f, + 0x52, 0x69, 0x60, 0xfd, 0xc6, 0xa4, 0xe5, 0x5c, 0xee, 0x4e, 0x57, 0x04, 0x90, 0x1c, + 0xfd, 0x3c, 0x7d, 0xdb, 0x92, 0x5b, 0xf5, 0x3d, 0x70, 0xad, 0xa9, 0x44, 0x79, 0x87, + 0xf9, 0x01, 0x3e, 0xac, 0xa0, 0xcd, 0x6b, 0x07, 0xd8, 0x2f, 0x19, 0x42, 0x06, 0xc1, + 0x1d, 0xcf, 0xea, 0x0b, 0x34, 0x74, 0x4d, 0x74, 0x00, 0xfb, 0x82, 0x96, 0x4e, 0x83, + 0xd1, 0x47, 0x25, 0x1f, 0xb1, 0x15, 0x19, 0x1b, 0x5b, 0xfc, 0x98, 0xf7, 0x42, 0x09, + 0xf5, 0x85, 0x87, 0x01, 0x4d, 0xea, 0xa1, 0x12, 0xac, 0x9e, 0x77, 0xf6, 0x2e, 0x62, + 0xbf, 0x79, 0x82, 0x7c, 0x09, 0x4f, 0x03, 0x1c, 0x84, 0xeb, 0xbd, 0x78, 0x65, 0x98, + 0x6c, 0x8c, 0x36, 0xd2, 0xe5, 0x8c, 0x04, 0xa2, 0x79, 0x44, 0x01, 0x65, 0x38, 0xec, + 0xf0, 0x6c, 0x39, 0x60, 0xf6, 0xc6, 0x47, 0x48, 0x52, 0x4a, 0xc9, 0x6d, 0x6f, 0xfc, + 0x6e, 0xa9, 0x13, 0xbe, 0x1d, 0x87, 0x74, 0xc3, 0xfb, 0xaa, 0x31, 0x82, 0x56, 0x15, + 0x30, 0x5d, 0xdd, 0x8e, 0x52, 0xe1, 0xa9, 0xd7, 0xd6, 0xdd, 0xa3, 0x0b, 0x62, 0xe0, + 0xb2, 0xd8, 0x0a, 0x53, 0x91, 0x39, 0x03, 0x5f, 0xa4, 0x76, 0x8d, 0xe7, 0xf5, 0xac, + 0xdd, 0xe9, 0x47, 0x82, 0x38, 0xd4, 0x65, 0x5e, 0x8c, 0x48, 0x39, 0xac, 0x7b, 0x21, + 0x0d, 0xa6, 0x7d, 0x00, 0x41, 0xb7, 0xbd, 0x09, 0x8e, 0xf0, 0xf1, 0xfc, 0x28, 0xc1, + 0x27, 0x60, 0xa3, 0x0a, 0xfc, 0x0e, 0xe7, 0x0e, 0xbe, 0x80, 0x0b, 0xa5, 0x8d, 0xfa, + 0xb7, 0x9f, 0x48, 0xc2, 0xad, 0x38, 0x6e, 0xb7, 0x42, 0x71, 0xc4, 0x35, 0xf4, 0x3c, + 0xa9, 0xcd, 0xba, 0x77, 0xa7, 0xb3, 0x7a, 0x4c, 0x5a, 0x91, 0xdf, 0xbe, 0x2a, 0x34, + 0x63, 0x3c, 0x59, 0x12, 0x1f, 0xa9, 0x63, 0x7d, 0x4e, 0xae, 0x77, 0x07, 0x2f, 0x1d, + 0x16, 0xee, 0xbd, 0x0f, 0x36, 0x5e, 0xfb, 0x54, 0xa8, 0x21, 0x6a, 0x68, 0x74, 0xca, + 0x47, 0x41, 0x63, 0x9a, 0xfc, 0x36, 0x6a, 0xfd, 0x3f, 0x38, 0xba, 0xc5, 0x14, 0xfe, + 0x1a, 0x2d, 0x1c, 0x0b, 0xf2, 0x20, 0x98, 0x28, 0x0e, 0x5c, 0x41, 0xa3, 0x31, 0xbe, + 0xfa, 0xc2, 0x88, 0x11, 0xd3, 0xb7, 0xd1, 0x0e, 0x5b, 0xc7, 0x46, 0x7d, 0x02, 0x2a, + 0xc1, 0x6a, 0x4f, 0x30, 0x32, 0x64, 0x70, 0x29, 0x85, 0xc1, 0xab, 0xcf, 0xf2, 0x9c, + 0x26, 0x6f, 0xdb, 0x1f, 0xd7, 0xfb, 0x28, 0x8e, 0x5a, 0xdf, 0x3c, 0xbe, 0x5e, 0x4c, + 0x2c, 0x74, 0xd0, 0xfc, 0x5a, 0x97, 0xd8, 0x13, 0x0f, 0xfc, 0x8a, 0x1c, 0x07, 0x4a, + 0xda, 0x4c, 0xce, 0xfa, 0x2d, 0xf1, 0x7f, 0xae, 0x73, 0x52, 0x93, 0xc0, 0x7f, 0x6e, + 0x91, 0x52, 0x2e, 0xbc, 0xc6, 0x94, 0xde, 0x98, 0xb3, 0xb5, 0x06, 0xfd, 0x65, 0xea, + 0xb5, 0xab, 0x9e, 0x95, 0x05, 0x8c, 0x5b, 0x7e, 0x0a, 0xb8, 0x47, 0xbb, 0xbd, 0xb4, + 0x4d, 0x94, 0x4c, 0x99, 0xb0, 0x3f, 0x8c, 0x72, 0x34, 0x61, 0x3a, 0xe1, 0x9b, 0xb2, + 0x78, 0x83, 0x87, 0x65, 0xcd, 0x8a, 0x52, 0xf9, 0x09, 0x87, 0x76, 0x6e, 0x76, 0xad, + 0xa8, 0x8f, 0xce, 0xcf, 0x9c, 0xaa, 0x6a, 0x5c, 0x34, 0xbc, 0x3f, 0x44, 0xf2, 0x7e, + 0x2a, 0xd3, 0x13, 0x6d, 0x81, 0x01, 0xdd, 0xdf, 0xea, 0x92, 0x5b, 0x4e, 0xff, 0xa1, + 0xf1, 0xda, 0x71, 0x4d, 0xab, 0x40, 0x0a, 0x17, 0xdc, 0xcc, 0x0b, 0x1a, 0xa9, 0xb4, + 0x7a, 0x7a, 0xa1, 0xad, 0x8f, 0xc8, 0x5b, 0xcf, 0x23, 0x0f, 0x9b, 0x92, 0x6f, 0x33, + 0x09, 0xac, 0x97, 0x2b, 0x1f, 0xbb, 0xbf, 0xd8, 0x82, 0x24, 0x6a, 0xda, 0xc8, 0xe5, + 0x70, 0x12, 0xd2, 0xb2, 0xbd, 0xe7, 0x86, 0xb1, 0xc4, 0x25, 0xb4, 0x5d, 0x96, 0x11, + 0xc4, 0xe1, 0x90, 0x3b, 0x07, 0x47, 0x8c, 0x93, 0x19, 0xdd, 0x43, 0x88, 0xc7, 0x98, + 0xed, 0x6f, 0x92, 0xe1, 0x2b, 0x6e, 0xb7, 0x45, 0xc6, 0x29, 0xde, 0x3a, 0xa2, 0x56, + 0xa8, 0xae, 0x6d, 0xb4, 0xb9, 0xa9, 0x21, 0x99, 0x6b, 0x4e, 0x59, 0x96, 0xc7, 0x75, + 0xaa, 0x39, 0x15, 0x5b, 0x73, 0xd3, 0x1b, 0x75, 0x8c, 0xeb, 0xc5, 0x57, 0x3f, 0x7d, + 0xd0, 0x73, 0x0f, 0xbf, 0xd0, 0x48, 0x6a, 0x1d, 0xf4, 0x6f, 0xc7, 0x58, 0x06, 0xc6, + 0x7b, 0x5d, 0xe1, 0x9e, 0xb5, 0x5d, 0x88, 0x89, 0x51, 0x7b, 0x73, 0x3b, 0x90, 0xf2, + 0x5d, 0x68, 0x56, 0x5b, 0x81, 0xa9, 0xd3, 0x1a, 0xd4, 0x5e, 0x1e, 0x05, 0x03, 0xda, + 0x60, 0xb9, 0xfc, 0xd7, 0xf8, 0x2a, 0x65, 0x03, 0x63, 0x8e, 0x99, 0x68, 0x7d, 0xc2, + 0x2a, 0x46, 0xaf, 0xb3, 0x2c, 0x37, 0x0d, 0x28, 0x18, 0x03, 0x46, 0x3b, 0x62, 0x68, + 0x82, 0x63, 0x6b, 0xff, 0xf7, 0x8f, 0x9d, 0x63, 0x4e, 0x7f, 0x81, 0xf6, 0xff, 0x77, + 0x21, 0x42, 0x93, 0x5a, 0x4a, 0xc2, 0xc5, 0x68, 0xf0, 0x99, 0xc2, 0xc6, 0xb1, 0x0c, + 0xd4, 0x02, 0x5d, 0x12, 0x24, 0x82, 0x6f, 0x33, 0xd9, 0x12, 0xd6, 0xfa, 0x52, 0x0f, + 0xd8, 0xd3, 0xa3, 0x39, 0xee, 0xa0, 0x6a, 0x44, 0x61, 0xae, 0xfa, 0xd8, 0x50, 0xf6, + 0xbe, 0x40, 0x37, 0xdb, 0x5c, 0x2a, 0xd8, 0xf2, 0x17, 0x6f, 0xe7, 0x35, 0x97, 0x2e, + 0xb0, 0x35, 0x0a, 0xf1, 0xab, 0x51, 0xed, 0x63, 0x86, 0x6c, 0x34, 0xb8, 0x0b, 0xd3, + 0x7f, 0xfc, 0xbf, 0xd9, 0x0c, 0xae, 0xa1, 0xbc, 0x46, 0xfa, 0xf8, 0xd6, 0x2e, 0x6b, + 0x36, 0xf1, 0x73, 0x47, 0xb4, 0xf0, 0x16, 0x74, 0x69, 0x87, 0x8c, 0x34, 0x06, 0x89, + 0x23, 0x93, 0x48, 0x74, 0x14, 0xbf, 0xd3, 0xe4, 0xc8, 0x21, 0x78, 0xd8, 0x22, 0x70, + 0x0e, 0x32, 0xf0, 0xfa, 0x69, 0x31, 0xe9, 0xae, 0x65, 0xfd, 0xf2, 0xbc, 0x2d, 0xc1, + 0xa6, 0x1d, 0x87, 0xec, 0xcf, 0x37, 0xb2, 0x4c, 0x4b, 0xf0, 0xe6, 0x7e, 0x75, 0x56, + 0x60, 0xb0, 0xd0, 0xcd, 0x30, 0x27, 0xdf, 0x08, 0xc4, 0x85, 0x4a, 0x0b, 0xcc, 0xc6, + 0x5b, 0x92, 0xf5, 0x23, 0x9c, 0x2a, 0x02, 0x01, 0x58, 0x0c, 0xae, 0x56, 0x90, 0x86, + 0x8d, 0xc8, 0x1c, 0xa1, 0x7e, 0xd4, 0x13, 0xfd, 0x49, 0x8c, 0xf6, 0x21, 0x3c, 0x4d, + 0x2f, 0x53, 0x3b, 0x97, 0xd6, 0x28, 0x5d, 0xfe, 0xb2, 0x71, 0xfb, 0x00, 0xd8, 0xdd, + 0x1d, 0xa1, 0xe8, 0xd7, 0xf9, 0x69, 0xcc, 0x15, 0xba, 0x18, 0x3d, 0xda, 0xc6, 0xc2, + 0x40, 0x06, 0x61, 0x9e, 0x4f, 0xbd, 0x20, 0x09, 0xbf, 0x9b, 0x6e, 0xdb, 0xd1, 0xc2, + 0x1b, 0xae, 0x39, 0xf5, 0x4e, 0x39, 0x48, 0xfa, 0xc0, 0x32, 0x4a, 0x83, 0x7e, 0xeb, + 0x58, 0x77, 0x5b, 0x54, 0x26, 0x0e, 0xe4, 0x42, 0xbd, 0xe0, 0x88, 0xf0, 0xc8, 0x93, + 0xcf, 0x4f, 0xf9, 0x94, 0xe1, 0x42, 0x2f, 0x02, 0xff, 0x28, 0x88, 0xb1, 0xe2, 0xb2, + 0x92, 0x9e, 0xf2, 0x3b, 0x5e, 0x39, 0x89, 0x8f, 0xf2, 0x04, 0x37, 0x87, 0x9a, 0xd7, + 0xb5, 0x8d, 0x52, 0xfc, 0x0b, 0xdb, 0xf6, 0x62, 0x27, 0xaa, 0x5f, 0x8c, 0x4a, 0x8d, + 0x9e, 0xa4, 0x1c, 0xe8, 0x4c, 0x64, 0xbc, 0xf1, 0xb8, 0xee, 0x9d, 0xad, 0x6d, 0x7c, + 0x7a, 0x7f, 0x9c, 0xe0, 0x47, 0x99, 0x2b, 0x76, 0x88, 0x31, 0xc9, 0x56, 0x7a, 0x8d, + 0xa8, 0x0e, 0x60, 0xfb, 0x9f, 0x9c, 0x68, 0xb1, 0x22, 0x4a, 0xf7, 0x66, 0x77, 0xda, + 0x8e, 0xcc, 0x33, 0x79, 0x87, 0xca, 0xc4, 0x4d, 0x57, 0xc7, 0x3a, 0x25, 0x3a, 0xfb, + 0x8e, 0x5e, 0x66, 0xef, 0xf0, 0xe3, 0x92, 0x3d, 0x05, 0xaf, 0xd3, 0x16, 0x2f, 0x57, + 0x92, 0x75, 0xef, 0x48, 0xb4, 0x0d, 0x15, 0x26, 0x2b, 0x9d, 0x09, 0x33, 0x97, 0x42, + 0x14, 0xc8, 0x04, 0x6c, 0x1f, 0x28, 0x76, 0xad, 0x2a, 0x4c, 0xaf, 0x1e, 0xb4, 0xb7, + 0x44, 0x61, 0x13, 0x35, 0x1c, 0x17, 0xce, 0xea, 0xfc, 0x8b, 0xdd, 0x15, 0xe1, 0x29, + 0x2b, 0xc4, 0x7c, 0x2e, 0xab, 0xd1, 0x9c, 0xa6, 0x8c, 0x4e, 0x26, 0x94, 0x27, 0x07, + 0xa8, 0x5b, 0x80, 0x66, 0xed, 0xb8, 0x85, 0x43, 0x50, 0x3d, 0x19, 0xeb, 0x24, 0x09, + 0xca, 0x42, 0x61, 0xcb, 0xb5, 0x25, 0x38, 0xe4, 0x0a, 0x64, 0x0a, 0x89, 0x8e, 0xcc, + 0x93, 0x20, 0x32, 0xd7, 0xae, 0x53, 0x16, 0x59, 0x79, 0x61, 0x03, 0x66, 0xc8, 0xa0, + 0xfb, 0xf6, 0x48, 0x77, 0xe0, 0xb6, 0xfe, 0x70, 0x31, 0x5c, 0x54, 0x77, 0xe0, 0x97, + 0x1c, 0xfb, 0xb0, 0x54, 0x52, 0x8a, 0xa1, 0x0a, 0xfd, 0xbe, 0xfb, 0x54, 0xdc, 0x43, + 0x60, 0xdb, 0x30, 0xa4, 0xd0, 0x66, 0x64, 0x17, 0x3e, 0x81, 0xa1, 0xa9, 0xa2, 0x63, + 0x6f, 0xde, 0xe6, 0x05, 0x2b, 0x44, 0xfb, 0x44, 0x95, 0x09, 0xb6, 0xf9, 0x43, 0x3e, + 0x76, 0xc2, 0x27, 0x54, 0x66, 0xfa, 0xaa, 0x92, 0x0f, 0xbd, 0x00, 0x89, 0x6c, 0x87, + 0xd6, 0x6f, 0x21, 0xe5, 0xb3, 0xe7, 0x2e, 0xd5, 0x4d, 0xde, 0xad, 0x9a, 0x0e, 0xa9, + 0x97, 0xad, 0x8b, 0x92, 0xbc, 0x61, 0x76, 0xe7, 0xac, 0x52, 0x39, 0xfb, 0x65, 0xb5, + 0x60, 0x5f, 0x2b, 0x03, 0x45, 0x33, 0xdd, 0xe5, 0xbb, 0x90, 0x47, 0xf8, 0x38, 0x7e, + 0x55, 0x50, 0x2e, 0x81, 0x83, 0xd9, 0x97, 0x77, 0x44, 0x3d, 0xec, 0xef, 0x48, 0x3e, + 0xcd, 0x01, 0xe2, 0x8b, 0x47, 0xe9, 0x87, 0x6e, 0x19, 0x09, 0x7a, 0xbc, 0xb2, 0x69, + 0xfd, 0x84, 0xdd, 0x4a, 0x49, 0x07, 0x0a, 0xb6, 0x36, 0x9c, 0x38, 0xd2, 0x91, 0x69, + 0x7c, 0xa2, 0x2b, 0xb7, 0x91, 0xf0, 0x91, 0x05, 0x98, 0x9e, 0x44, 0xcb, 0x06, 0x13, + 0x21, 0x78, 0xb9, 0x99, 0x8a, 0x05, 0x91, 0xf4, 0xe4, 0x10, 0xea, 0x14, 0x04, 0x86, + 0x57, 0x53, 0xcb, 0x3f, 0x80, 0xe0, 0xf4, 0x95, 0x5c, 0x5e, 0x58, 0x3c, 0x79, 0xe5, + 0x3b, 0xac, 0x1e, 0x5f, 0x17, 0x32, 0xa6, 0xc3, 0x3f, 0x58, 0xbd, 0xcf, 0x68, 0x46, + 0x4e, 0xc5, 0xf6, 0xae, 0xc1, 0x0e, 0x00, 0xb1, 0xa6, 0x85, 0x27, 0x48, 0xb2, 0x0a, + 0x05, 0xa5, 0x7b, 0x23, 0xdd, 0x2b, 0xee, 0x2f, 0x99, 0xc2, 0x2d, 0x9e, 0xff, 0x74, + 0x65, 0xd6, 0xd0, 0x56, 0x98, 0x9a, 0x81, 0xb9, 0xf2, 0x9f, 0x55, 0x87, 0xcd, 0x37, + 0xbd, 0x09, 0x54, 0x0c, 0x44, 0xf1, 0x30, 0xf1, 0x72, 0x0f, 0x55, 0xaa, 0xe4, 0x17, + 0x71, 0xf0, 0xb2, 0x73, 0xf0, 0x02, 0x64, 0xf9, 0xe9, 0x3e, 0xf3, 0xee, 0x05, 0xae, + 0x63, 0x37, 0x8f, 0x8b, 0xfc, 0x04, 0xaa, 0x9a, 0x2a, 0xd2, 0x17, 0x2d, 0xc6, 0xf7, + 0xce, 0x82, 0xf4, 0x62, 0x20, 0x41, 0x3e, 0x57, 0x47, 0x6a, 0x4d, 0x3a, 0xd2, 0xed, + 0x45, 0x78, 0xf0, 0xf9, 0x71, 0x99, 0x70, 0x40, 0xff, 0xc1, 0x12, 0xee, 0x2b, 0x83, + 0x9d, 0xac, 0x86, 0xc6, 0xc5, 0x29, 0x63, 0x18, 0x3b, 0xed, 0xc0, 0x31, 0x80, 0xb5, + 0xa7, 0xa0, 0xd2, 0xd4, 0x02, 0xb5, 0xf5, 0x65, 0x7a, 0xfa, 0x61, 0xe2, 0x3e, 0x23, + 0x0b, 0xbb, 0x5c, 0xaa, 0x1e, 0xb4, 0x24, 0x2c, 0x04, 0x79, 0x90, 0x8f, 0x00, 0xc8, + 0x14, 0x5a, 0xf5, 0xe0, 0x86, 0x84, 0x21, 0x45, 0xdd, 0xa7, 0xbb, 0x24, 0x19, 0x38, + 0x13, 0x73, 0x01, 0xac, 0x05, 0x9a, 0x89, 0x87, 0xdf, 0x7c, 0x8c, 0xb8, 0xdf, 0x6e, + 0x19, 0x52, 0xa7, 0x0c, 0x68, 0x7b, 0x9b, 0x18, 0x45, 0xc3, 0x61, 0x89, 0xce, 0x92, + 0x80, 0x26, 0xbc, 0xa2, 0x00, 0x97, 0xbf, 0x01, 0x63, 0xf2, 0xde, 0xd7, 0x91, 0xd9, + 0xdc, 0xf3, 0x2e, 0xe6, 0xa6, 0xbc, 0x19, 0x99, 0xc0, 0x7a, 0x3e, 0x92, 0x12, 0x6d, + 0x38, 0xdf, 0x0d, 0x56, 0x3e, 0xd7, 0xef, 0x9f, 0xd1, 0x9f, 0x0c, 0xdc, 0xda, 0x03, + 0x80, 0x58, 0x70, 0x98, 0x20, 0xbe, 0x13, 0xc7, 0x8d, 0xec, 0xec, 0xe5, 0x7f, 0x00, + 0x74, 0x5c, 0x16, 0x05, 0x6a, 0x28, 0x60, 0xfd, 0x75, 0x05, 0xec, 0xa7, 0xc3, 0xb4, + 0x5f, 0x9b, 0x69, 0xaf, 0x01, 0x0e, 0xaa, 0xe5, 0xe3, 0xee, 0x02, 0xbd, 0xaa, 0xcb, + 0x65, 0x0d, 0xd2, 0x99, 0x7a, 0x72, 0x65, 0x64, 0x6b, 0x8d, 0x0a, 0x2e, 0x3e, 0x04, + 0x1c, 0x58, 0xd0, 0x35, 0x34, 0x74, 0xba, 0x59, 0x3b, 0x89, 0x1a, 0xa1, 0x91, 0x8d, + 0x0e, 0x84, 0x7a, 0x04, 0xee, 0xad, 0x90, 0x99, 0xc2, 0xc5, 0x72, 0x5b, 0x89, 0x77, + 0xd9, 0x5f, 0x62, 0x99, 0x29, 0xf7, 0x87, 0x8e, 0x2c, 0x27, 0x80, 0x04, 0x1e, 0x3e, + 0x33, 0x44, 0x3a, 0xd6, 0x6f, 0xb4, 0x37, 0xa4, 0x1d, 0xfb, 0xb5, 0xa9, 0x24, 0xcf, + 0xcd, 0x62, 0x01, 0xfd, 0x68, 0x34, 0x58, 0xe7, 0x16, 0x94, 0x4c, 0x0f, 0x37, 0xcc, + 0x70, 0xa7, 0x50, 0x49, 0xff, 0x01, 0xdd, 0xbe, 0x9b, 0x27, 0x93, 0x9b, 0x0d, 0x5f, + 0xab, 0x06, 0xcd, 0xda, 0xac, 0xfc, 0x23, 0x5a, 0x30, 0x74, 0xaf, 0xf7, 0x67, 0x2d, + 0x19, 0x00, 0x73, 0x9e, 0xd6, 0xd5, 0xb6, 0x49, 0x09, 0x16, 0x0a, 0x26, 0xa6, 0xf2, + 0xe4, 0x19, 0xe0, 0xf7, 0xa4, 0x35, 0x82, 0x24, 0x8d, 0x2e, 0xb7, 0x18, 0xf7, 0xc6, + 0x1d, 0xb4, 0x88, 0xf0, 0xb3, 0xef, 0xa9, 0x7e, 0x18, 0x72, 0x9e, 0x94, 0xc5, 0x80, + 0xfc, 0xe5, 0x89, 0x85, 0x19, 0xa3, 0xd2, 0x43, 0x0b, 0x60, 0x0c, 0x45, 0xb1, 0x76, + 0x67, 0x2f, 0xe4, 0xde, 0x70, 0x01, 0x5d, 0xc2, 0xac, 0x27, 0xbb, 0x89, 0x7c, 0xe9, + 0x8b, 0xc7, 0x16, 0x7f, 0x18, 0x47, 0x85, 0xbe, 0xcd, 0xe7, 0x92, 0x4e, 0xd9, 0x10, + 0xf1, 0xb0, 0x85, 0x58, 0x45, 0xbe, 0x7d, 0x00, 0x3b, 0x80, 0xb6, 0x9f, 0x99, 0x14, + 0x54, 0x2a, 0x2c, 0x08, 0x67, 0xdc, 0xff, 0x4c, 0x2a, 0x4a, 0xa8, 0x0e, 0xfd, 0x2a, + 0x14, 0x04, 0xd4, 0xb5, 0x78, 0x8d, 0xa3, 0x47, 0x3d, 0x9e, 0xa3, 0xb6, 0x7a, 0xbe, + 0x14, 0x60, 0x54, 0xc5, 0xc8, 0x0b, 0xa6, 0x68, 0xdc, 0x24, 0x7b, 0xe5, 0xb9, 0xc6, + 0x77, 0xe9, 0xa4, 0x70, 0xa6, 0xbe, 0xe5, 0xbb, 0xa8, 0x01, 0x57, 0xfa, 0x87, 0x06, + 0x5b, 0xf8, 0x92, 0xc5, 0xc4, 0x4f, 0xff, 0xc2, 0x8e, 0x61, 0x71, 0xb1, 0x71, 0x49, + 0xb8, 0xac, 0xe8, 0x2d, 0x00, 0xf8, 0x61, 0x41, 0xd1, 0x03, 0x71, 0xf4, 0x87, 0xe6, + 0xfe, 0x2d, 0xa9, 0x55, 0xae, 0x31, 0x8e, 0xfd, 0xd2, 0xa4, 0xb6, 0xd4, 0x5b, 0x4a, + 0x47, 0xb0, 0x65, 0x34, 0x92, 0xef, 0x36, 0xff, 0x89, 0xe2, 0x05, 0xa9, 0xd2, 0x3d, + 0x4e, 0x80, 0x4f, 0x5a, 0x9b, 0x9f, 0xdb, 0x05, 0x71, 0xfe, 0x8f, 0x75, 0xa3, 0x4f, + 0x1a, 0xf4, 0xd6, 0x1b, 0xbc, 0x6a, 0xb6, 0xf1, 0x71, 0x6b, 0x7d, 0xfa, 0xd7, 0x7f, + 0x3f, 0x03, 0x5a, 0x76, 0x8b, 0x09, 0xc9, 0x87, 0x6e, 0xd3, 0x70, 0xe5, 0xef, 0x4e, + 0xfe, 0x82, 0xa3, 0xc9, 0xa3, 0x8b, 0xff, 0xe8, 0xfd, 0xa7, 0x65, 0x9d, 0x70, 0x35, + 0x08, 0x70, 0x80, 0x67, 0xee, 0x3a, 0x89, 0x4a, 0x56, 0xb6, 0x93, 0x1a, 0x5c, 0xa4, + 0xe9, 0x44, 0x60, 0x45, 0xd9, 0x0c, 0x31, 0x64, 0x02, 0xbd, 0xda, 0x77, 0x7f, 0x8d, + 0xb4, 0x6a, 0x83, 0xc3, 0xf3, 0xdf, 0xbf, 0xb8, 0x8a, 0x88, 0x00, 0xc8, 0x5e, 0x6f, + 0x23, 0x37, 0x1d, 0x0f, 0x3c, 0x10, 0x67, 0x2d, 0x38, 0x8f, 0x4b, 0xee, 0x56, 0xea, + 0x38, 0x4c, 0x4a, 0xe9, 0x27, 0x9d, 0x54, 0x9c, 0x4b, 0xe4, 0x14, 0x34, 0x5b, 0xbf, + 0xaf, 0x32, 0x08, 0x23, 0x78, 0x9d, 0x7b, 0x50, 0x55, 0xb4, 0xf6, 0x38, 0xa9, 0x5e, + 0x56, 0x8c, 0xf6, 0x3a, 0x3f, 0x2e, 0x63, 0x5d, 0x5f, 0xbd, 0x0d, 0x2c, 0x96, 0x92, + 0x69, 0x68, 0x0f, 0x82, 0xae, 0x1a, 0x1b, 0x93, 0xdb, 0x4b, 0x5e, 0xf4, 0xa4, 0x24, + 0x44, 0x00, 0x0d, 0xcc, 0x08, 0x60, 0x13, 0x0d, 0x0b, 0xc4, 0x6e, 0xfb, 0xe4, 0xbc, + 0x12, 0xfd, 0x4d, 0x02, 0x59, 0xd8, 0xf7, 0x73, 0xcc, 0xf9, 0xee, 0x3d, 0x7c, 0xc7, + 0xe8, 0x90, 0x58, 0xfb, 0x81, 0x7f, 0xbc, 0x51, 0xcd, 0xcc, 0x19, 0xfb, 0x90, 0x44, + 0x53, 0xac, 0xd5, 0x74, 0xfb, 0xe5, 0x21, 0x97, 0x66, 0x1b, 0xf5, 0x20, 0xd6, 0x22, + 0xef, 0x01, 0xd6, 0xbb, 0xff, 0xf4, 0xcb, 0x42, 0x65, 0x13, 0xde, 0xf4, 0xd0, 0x4d, + 0xbb, 0x00, 0x61, 0xfe, 0x46, 0x5d, 0xb1, 0x2c, 0xfc, 0xd3, 0xfe, 0xaa, 0xe3, 0x66, + 0x58, 0x8c, 0x17, 0x2e, 0x1c, 0xd9, 0x9e, 0x18, 0xb2, 0xaa, 0x5d, 0x33, 0x06, 0x29, + 0x64, 0x7f, 0xd7, 0xf9, 0x92, 0x7e, 0x44, 0x07, 0x69, 0x83, 0xdb, 0xb2, 0xc9, 0xe1, + 0x61, 0xa0, 0xef, 0xa5, 0x43, 0xa6, 0x98, 0xbc, 0x27, 0xee, 0x8a, 0xad, 0xe9, 0xfd, + 0xee, 0x78, 0x0b, 0xeb, 0x75, 0xcc, 0xf3, 0x7d, 0x74, 0x43, 0x6f, 0xdd, 0x88, 0x1b, + 0x9f, 0xf8, 0x1e, 0x86, 0x2e, 0x0a, 0xd9, 0xa6, 0x1e, 0x94, 0xc6, 0x6c, 0xa0, 0xa0, + 0xb7, 0x54, 0x7b, 0x04, 0xe7, 0x6c, 0x09, 0xe6, 0x88, 0x61, 0x77, 0x23, 0x30, 0xb5, + 0x33, 0xa5, 0x47, 0x99, 0xc5, 0x8d, 0x52, 0xeb, 0xd1, 0xa1, 0x36, 0x03, 0x5d, 0xe1, + 0xc6, 0x7e, 0x6a, 0xf9, 0x2c, 0xce, 0x10, 0xa3, 0x8c, 0x15, 0x9e, 0x70, 0x8b, 0xec, + 0x51, 0xf7, 0xc9, 0xa0, 0x98, 0x8c, 0xaa, 0xbf, 0xc4, 0x03, 0xf2, 0x59, 0xb4, 0x91, + 0x77, 0xe1, 0x6a, 0xd0, 0x91, 0x17, 0xb9, 0xe2, 0xa3, 0x8f, 0xea, 0x54, 0x86, 0xab, + 0x7e, 0xa4, 0x43, 0x3c, 0x59, 0xe8, 0x74, 0x33, 0x9a, 0x7e, 0x99, 0x22, 0x9f, 0x90, + 0xf8, 0xd2, 0xbc, 0xb3, 0x1d, 0x57, 0x5a, 0xbf, 0xf3, 0xb6, 0x3f, 0xe4, 0x6c, 0xae, + 0xc6, 0x85, 0x76, 0x72, 0x72, 0xcf, 0x75, 0x29, 0x5c, 0xe3, 0xc7, 0xe7, 0x74, 0x4c, + 0x55, 0x7d, 0xd3, 0x9d, 0xfd, 0x28, 0xc8, 0x01, 0x32, 0xdc, 0x7e, 0x16, 0x52, 0x2d, + 0x72, 0x61, 0xdd, 0x8c, 0x41, 0x95, 0xfa, 0x2e, 0x71, 0x89, 0x1b, 0x1c, 0xe3, 0x80, + 0xe3, 0xed, 0xde, 0x3f, 0xa2, 0x6a, 0x05, 0x81, 0x3f, 0xd3, 0x2d, 0x6c, 0xfd, 0x42, + 0xff, 0x70, 0x1e, 0x6d, 0x0c, 0xd8, 0x21, 0x53, 0xb0, 0x6c, 0x96, 0xb3, 0xf8, 0x4c, + 0x0a, 0xe1, 0xa6, 0xbb, 0x57, 0xbd, 0x41, 0x73, 0x78, 0xaf, 0x70, 0x27, 0x8f, 0xba, + 0x34, 0x70, 0xfc, 0xbb, 0x6b, 0x0c, 0x23, 0xb9, 0xaf, 0x0a, 0xcf, 0x7f, 0x1c, 0x44, + 0x9f, 0x24, 0x2f, 0x56, 0xf9, 0x3d, 0x7b, 0x2a, 0x76, 0x08, 0xa8, 0xfb, 0xe7, 0x9c, + 0xc4, 0x6f, 0x3e, 0xf4, 0x91, 0x59, 0xda, 0xa3, 0xe1, 0x2f, 0x78, 0x27, 0x49, 0x9f, + 0x99, 0xf8, 0xe6, 0x28, 0x2c, 0x81, 0xf4, 0x47, 0x5f, 0x75, 0xcc, 0xa3, 0xa1, 0xb5, + 0xff, 0xb8, 0x25, 0xde, 0xe5, 0xbe, 0x23, 0x48, 0xb5, 0x26, 0x5b, 0x23, 0x90, 0x4e, + 0x9c, 0x64, 0xae, 0x70, 0x2a, 0x4c, 0xa1, 0xcf, 0x48, 0xca, 0x99, 0x37, 0x28, 0x63, + 0x77, 0x7d, 0x0f, 0x37, 0x11, 0xc5, 0x4e, 0xfd, 0x2b, 0x9f, 0x93, 0xca, 0xbc, 0x74, + 0x6b, 0x8a, 0x18, 0x27, 0xda, 0x1e, 0x5d, 0x7a, 0x29, 0x0e, 0x79, 0x9e, 0xb6, 0x4b, + 0x60, 0x92, 0xfe, 0x88, 0xe5, 0x8b, 0xf4, 0x49, 0xa9, 0xb3, 0x8e, 0x74, 0x16, 0x31, + 0xfe, 0xe5, 0x8c, 0x64, 0xf2, 0xe8, 0xab, 0x07, 0xd1, 0x0b, 0x9a, 0x6d, 0xf9, 0x91, + 0xfd, 0x13, 0xc3, 0xfe, 0x77, 0xf2, 0x17, 0xcb, 0x19, 0xb6, 0x59, 0x4c, 0x2b, 0x69, + 0xab, 0xe3, 0xcb, 0xb1, 0xf5, 0xf7, 0x42, 0x88, 0x9a, 0xe8, 0x57, 0x6e, 0x79, 0x30, + 0x7f, 0x1d, 0xa3, 0x19, 0xac, 0xb3, 0x47, 0x44, 0x0b, 0x7a, 0x0b, 0xdd, 0xb7, 0xaf, + 0x17, 0x63, 0x54, 0xe4, 0x4c, 0x8f, 0x20, 0x15, 0x8e, 0x74, 0x64, 0xca, 0x2a, 0xd7, + 0xc4, 0x94, 0xdb, 0xde, 0x96, 0xd3, 0xc0, 0x0c, 0x0c, 0x99, 0xd0, 0x47, 0x35, 0x97, + 0xc8, 0x77, 0xef, 0xdb, 0x3d, 0x16, 0x1a, 0xa2, 0x62, 0x72, 0xe1, 0x94, 0x38, 0x41, + 0x69, 0x83, 0xc2, 0x43, 0x70, 0x56, 0x2d, 0x45, 0x29, 0x04, 0xc0, 0x56, 0x32, 0x47, + 0x21, 0xd6, 0x92, 0x27, 0xfb, 0x30, 0x0f, 0xde, 0x35, 0xed, 0xba, 0x9e, 0x48, 0x8f, + 0x02, 0xaf, 0xb5, 0xb4, 0x71, 0xbc, 0x72, 0x00, 0x59, 0x8a, 0x08, 0xcf, 0x14, 0x1a, + 0x9d, 0x79, 0x41, 0xad, 0x69, 0xe3, 0xf8, 0x45, 0x61, 0x0e, 0xc7, 0xe5, 0x0b, 0x70, + 0xaf, 0xc9, 0x44, 0xc2, 0xb8, 0x69, 0x57, 0x5d, 0xf5, 0x8b, 0x28, 0xcd, 0x92, 0x18, + 0x30, 0xa6, 0x3c, 0x50, 0x2b, 0x54, 0xac, 0x2f, 0xd9, 0x6d, 0xb6, 0xf9, 0x78, 0x39, + 0xda, 0x37, 0x36, 0xa0, 0x7f, 0x25, 0x8f, 0xbf, 0xe6, 0x4a, 0x89, 0x7e, 0x5c, 0xac, + 0xd4, 0xdb, 0xa3, 0xfe, 0x80, 0xd9, 0xe1, 0xb0, 0x49, 0x4d, 0xb1, 0x77, 0xbc, 0x28, + 0x65, 0x5e, 0x7a, 0x66, 0xb5, 0x64, 0x98, 0x1a, 0x73, 0x29, 0x60, 0x4e, 0x87, 0x81, + 0xf7, 0x34, 0xff, 0xf1, 0x8e, 0x9c, 0x41, 0xc7, 0x95, 0x6a, 0x23, 0x4c, 0xda, 0x1d, + 0x44, 0x2a, 0xda, 0x2c, 0x1a, 0x5d, 0x97, 0x22, 0x3b, 0xc8, 0xc2, 0xfd, 0x35, 0x2b, + 0xe0, 0xa9, 0x50, 0x5d, 0x42, 0xcb, 0xd9, 0xf5, 0xfc, 0x22, 0x6f, 0x38, 0x0a, 0x6b, + 0xa9, 0xfa, 0x2b, 0x2e, 0x29, 0x58, 0xc3, 0x06, 0x03, 0xab, 0xf1, 0x2f, 0x9e, 0xe8, + 0xe2, 0x64, 0x18, 0xba, 0x63, 0xd6, 0x30, 0x73, 0xae, 0x36, 0xd3, 0x03, 0x8d, 0xfe, + 0x4b, 0xab, 0xcb, 0xf2, 0x23, 0x1b, 0xc7, 0xa4, 0x0d, 0x56, 0xc4, 0x26, 0x46, 0x6a, + 0x28, 0xcd, 0x46, 0x06, 0x84, 0x38, 0x02, 0xf5, 0xf9, 0xa0, 0xe4, 0x04, 0x95, 0xd4, + 0x56, 0x2c, 0x56, 0x0d, 0x18, 0x1a, 0x9a, 0x94, 0x7e, 0x8d, 0x5b, 0x8c, 0xb8, 0x82, + 0xed, 0x1b, 0xd4, 0x30, 0x2d, 0x20, 0xe9, 0x91, 0xc9, 0x21, 0x67, 0x92, 0x0a, 0xc6, + 0x54, 0x34, 0x03, 0x94, 0x15, 0x81, 0x45, 0xc1, 0x32, 0x38, 0x00, 0x1e, 0x9e, 0x69, + 0x5b, 0xc8, 0x68, 0x9a, 0x85, 0x31, 0x81, 0xf5, 0x2e, 0xb8, 0xc3, 0xae, 0x57, 0x4c, + 0xcb, 0xae, 0xa4, 0xdf, 0x35, 0xdc, 0x41, 0xd8, 0x66, 0x5e, 0x7b, 0x83, 0x96, 0x93, + 0xec, 0x4d, 0xe8, 0xd6, 0x71, 0xc7, 0x63, 0x8e, 0x8a, 0x17, 0x89, 0x31, 0xe7, 0xae, + 0x32, 0xd9, 0x48, 0xde, 0x92, 0x79, 0xd5, 0x08, 0x99, 0xda, 0x8e, 0x8c, 0x34, 0x6e, + 0x47, 0xfc, 0x4b, 0xbf, 0xc1, 0x78, 0x7a, 0xf6, 0x1d, 0x22, 0x8b, 0x5f, 0x4b, 0x2e, + 0x75, 0x88, 0x25, 0x63, 0x7f, 0x65, 0x6c, 0x03, 0x57, 0x4d, 0xeb, 0x00, 0xf6, 0x2b, + 0x70, 0xcd, 0x08, 0x56, 0x82, 0x63, 0xb8, 0xf6, 0x63, 0xb6, 0x11, 0xa4, 0x74, 0xde, + 0x9b, 0x8e, 0xe9, 0xda, 0x49, 0x4b, 0x74, 0x96, 0x83, 0x1a, 0xce, 0x49, 0xcf, 0x53, + 0xdb, 0x0b, 0xfa, 0x87, 0xbe, 0x2d, 0x2f, 0x5c, 0xc3, 0x43, 0x91, 0x83, 0xe3, 0xff, + 0xb4, 0xa3, 0x3c, 0x28, 0xfc, 0x67, 0xa2, 0x80, 0x73, 0xb3, 0xd0, 0x0f, 0x58, 0xa9, + 0xc2, 0xd5, 0x0f, 0xca, 0xd3, 0x27, 0x04, 0x7f, 0x16, 0xa9, 0xb9, 0xc7, 0xe2, 0xc7, + 0x02, 0x6c, 0x97, 0x2f, 0x81, 0xcf, 0xf3, 0xb9, 0x88, 0x73, 0xa0, 0x58, 0xe4, 0x04, + 0xaa, 0x13, 0xea, 0x4b, 0xc3, 0xdf, 0x0c, 0x8d, 0xac, 0x6d, 0x65, 0xc5, 0x33, 0xb0, + 0x24, 0x08, 0xbb, 0x4f, 0xd8, 0x42, 0x07, 0x33, 0xc8, 0xbf, 0x6f, 0xcd, 0xcf, 0xd7, + 0x05, 0x1b, 0x2b, 0xbd, 0x71, 0x37, 0xe0, 0xdd, 0x07, 0x73, 0xa7, 0x7b, 0x7d, 0x29, + 0x12, 0xdf, 0x9f, 0x8a, 0xb4, 0xc8, 0x77, 0x27, 0xfc, 0xb3, 0xc0, 0x9c, 0xe9, 0x65, + 0x5e, 0xaa, 0x4a, 0x15, 0x3c, 0xe2, 0x52, 0xbd, 0x59, 0xa9, 0x84, 0xc2, 0x2c, 0xd2, + 0x59, 0xc3, 0xaf, 0x13, 0xe5, 0x88, 0x0d, 0x70, 0x66, 0xd2, 0xe7, 0x82, 0x94, 0x60, + 0xaa, 0xcb, 0x11, 0x59, 0x31, 0xc6, 0x98, 0xfd, 0x50, 0x64, 0xcb, 0x5d, 0xd3, 0x7d, + 0x7f, 0x5c, 0x18, 0x9a, 0x9a, 0x29, 0x0a, 0x00, 0xbe, 0x23, 0xd9, 0xb6, 0x0d, 0x5d, + 0x78, 0x0e, 0x8a, 0x4d, 0xf0, 0xe2, 0x6d, 0xad, 0xb0, 0xc5, 0xbf, 0x6b, 0x7f, 0xc1, + 0xf2, 0x86, 0x63, 0xcf, 0xa8, 0x88, 0xb7, 0xf5, 0xa5, 0x1f, 0x67, 0x6d, 0x2c, 0x25, + 0x2c, 0xcd, 0xe5, 0x8e, 0x1c, 0x54, 0xb5, 0x6e, 0xbf, 0xe0, 0xdf, 0x0f, 0x92, 0xb3, + 0x29, 0x12, 0x39, 0xe6, 0x56, 0x10, 0x91, 0xa9, 0x89, 0xf4, 0x1b, 0x13, 0x32, 0xf2, + 0x80, 0x29, 0x6f, 0xbe, 0x87, 0xd3, 0xf0, 0xad, 0x74, 0xf0, 0xf3, 0xd1, 0xf8, 0xfc, + 0x85, 0xfd, 0xd1, 0x06, 0xb8, 0x8f, 0x46, 0x30, 0x2c, 0xb5, 0x15, 0x4d, 0xcc, 0x25, + 0x29, 0xd1, 0xbf, 0x8f, 0x90, 0x44, 0xb7, 0xff, 0xf0, 0x32, 0xd0, 0x37, 0xb8, 0x34, + 0x77, 0xd6, 0x2f, 0xfe, 0x90, 0xcd, 0xb5, 0x69, 0x68, 0x49, 0x66, 0x24, 0x32, 0x40, + 0x55, 0x0b, 0xaa, 0x5f, 0x01, 0xf6, 0x78, 0xf2, 0x7f, 0xe4, 0x33, 0x32, 0xc6, 0xb6, + 0xba, 0x80, 0x6c, 0x4f, 0x06, 0x8e, 0x47, 0x9a, 0x03, 0x1c, 0xba, 0x96, 0x82, 0xa0, + 0xff, 0x5f, 0x68, 0x36, 0xe5, 0x9e, 0x50, 0x67, 0xd5, 0x78, 0xdd, 0xf6, 0x16, 0x38, + 0x98, 0x16, 0xf5, 0xca, 0x72, 0x80, 0xbb, 0x3f, 0x0b, 0x79, 0x39, 0x18, 0xa9, 0x87, + 0xb4, 0x50, 0xb1, 0x6f, 0x25, 0x19, 0xab, 0x03, 0xec, 0x9f, 0x82, 0xfa, 0x46, 0xd7, + 0xda, 0xab, 0xd1, 0x91, 0xb1, 0x25, 0xbe, 0x9a, 0xb4, 0x84, 0xc8, 0x6d, 0x91, 0x5c, + 0x72, 0x82, 0x9e, 0x8d, 0x8e, 0xf7, 0x50, 0x3c, 0x15, 0x2b, 0x54, 0x95, 0x38, 0x02, + 0x99, 0x58, 0xee, 0xb5, 0x6d, 0xe8, 0xf0, 0x96, 0x51, 0xaa, 0xe0, 0x99, 0xbd, 0x97, + 0x5d, 0x1b, 0x37, 0x70, 0x62, 0x17, 0xf6, 0x0a, 0x74, 0x96, 0x20, 0x6c, 0xdb, 0x7c, + 0x96, 0xbc, 0x2e, 0x30, 0x5b, 0x43, 0x73, 0xef, 0x4f, 0x00, 0xa8, 0x7e, 0x91, 0x80, + 0xfe, 0x1e, 0x16, 0x59, 0xee, 0x10, 0xc7, 0x37, 0x1c, 0x11, 0x97, 0x65, 0xae, 0xea, + 0xd4, 0x41, 0x8b, 0xac, 0x83, 0xe4, 0x4e, 0x27, 0x6c, 0x69, 0x18, 0xa4, 0xb0, 0x77, + 0x8d, 0x13, 0xa1, 0xb1, 0x3a, 0x8b, 0x17, 0x09, 0x02, 0xcf, 0x07, 0x9e, 0x6d, 0x10, + 0xae, 0xf8, 0x73, 0x4d, 0xb4, 0x85, 0xe3, 0x04, 0xfe, 0x9e, 0x74, 0xf2, 0x63, 0x55, + 0xc7, 0x35, 0xfa, 0x48, 0x29, 0xf9, 0x64, 0x72, 0x7f, 0xc0, 0x7c, 0x8a, 0x6a, 0x17, + 0xc5, 0x41, 0x05, 0x5b, 0x24, 0xf6, 0xb6, 0x5b, 0x01, 0x8a, 0x26, 0x9a, 0x79, 0xfa, + 0xb1, 0xab, 0xcb, 0xd0, 0x4b, 0xc8, 0xe8, 0x22, 0x75, 0x27, 0xc4, 0xe5, 0x47, 0x06, + 0x62, 0xb7, 0x13, 0xac, 0xe3, 0x7c, 0xbd, 0xeb, 0x49, 0xf9, 0x5e, 0xb1, 0x35, 0x6d, + 0xe6, 0x18, 0x0f, 0xe8, 0x25, 0x49, 0x7f, 0x18, 0x66, 0x30, 0xf2, 0x11, 0xa6, 0x66, + 0xd7, 0x00, 0x81, 0x77, 0xb9, 0xd8, 0x31, 0x30, 0x8e, 0x98, 0xf0, 0xd9, 0x43, 0xe6, + 0x12, 0xcc, 0xbb, 0xd2, 0x9c, 0xf1, 0x30, 0x70, 0x09, 0xfa, 0xb1, 0x4c, 0xf5, 0xf7, + 0x95, 0x54, 0x07, 0x39, 0x65, 0x59, 0x2f, 0xac, 0xa3, 0x7e, 0x9d, 0x71, 0x6c, 0xce, + 0xab, 0x87, 0x3a, 0xdd, 0xd8, 0x7c, 0x8a, 0xc2, 0xd8, 0x8f, 0x42, 0x49, 0xa6, 0xab, + 0xd5, 0x74, 0x07, 0x48, 0x4f, 0x94, 0x79, 0xa9, 0xc9, 0xaf, 0x47, 0x41, 0x86, 0xab, + 0x6e, 0x6b, 0x5e, 0x48, 0x47, 0xc5, 0xe6, 0xc1, 0x3f, 0xbe, 0x1c, 0xe0, 0xea, 0x31, + 0x69, 0x47, 0xe9, 0xf9, 0x2d, 0xc1, 0xdb, 0x73, 0xb1, 0xb7, 0x0d, 0xa0, 0x89, 0x79, + 0x19, 0x89, 0xfd, 0x52, 0x9f, 0x30, 0xd0, 0xce, 0xfb, 0x61, 0x82, 0x64, 0x07, 0x7e, + 0x71, 0x42, 0xec, 0x1d, 0xec, 0xfd, 0xb7, 0xc9, 0x52, 0xa6, 0xfc, 0xab, 0x39, 0xfa, + 0x57, 0x97, 0xa7, 0x8c, 0xe7, 0x84, 0xf1, 0x94, 0xa3, 0x10, 0xa9, 0x7e, 0x94, 0x71, + 0x91, 0x56, 0xc7, 0x98, 0x6d, 0x7f, 0x5f, 0x27, 0xfe, 0x94, 0x30, 0xb4, 0x3d, 0xaa, + 0xa1, 0x5a, 0xd5, 0x61, 0xf6, 0x82, 0xdc, 0xb0, 0xfe, 0x8e, 0xcc, 0x1e, 0x71, 0x2e, + 0xf0, 0x02, 0xd7, 0x99, 0xfd, 0x5b, 0xcb, 0x2b, 0x31, 0xa5, 0x1e, 0xa6, 0xec, 0x7f, + 0x14, 0x83, 0x82, 0x98, 0x17, 0x82, 0x3e, 0x78, 0x9f, 0x41, 0x54, 0x49, 0x64, 0xae, + 0x17, 0x1a, 0xd6, 0xba, 0x5b, 0x0d, 0x3a, 0x4e, 0xfb, 0x03, 0xbf, 0x90, 0xe9, 0x29, + 0x6c, 0x19, 0xd6, 0xc4, 0xda, 0x3c, 0xb4, 0x7d, 0x52, 0xbd, 0xe2, 0x3d, 0xee, 0x8c, + 0x86, 0xc2, 0xe5, 0x3c, 0xc9, 0xac, 0xa3, 0xd9, 0xf8, 0xce, 0xce, 0x0a, 0x1e, 0x7e, + 0xb7, 0xd2, 0x5a, 0xb5, 0x36, 0x6a, 0x15, 0x19, 0x4f, 0x3f, 0x73, 0x11, 0x68, 0x07, + 0xea, 0x0e, 0xce, 0x33, 0x8c, 0x9a, 0x68, 0x63, 0xc8, 0xcb, 0x69, 0x07, 0x3e, 0x04, + 0xe9, 0x78, 0x88, 0x35, 0x7c, 0xd0, 0x27, 0x4b, 0xb6, 0xd0, 0xc0, 0xb5, 0x04, 0x7c, + 0xae, 0x74, 0xf9, 0xbf, 0x89, 0x95, 0x1a, 0x3d, 0x2a, 0xaa, 0x1e, 0x4e, 0x9d, 0xba, + 0x31, 0x94, 0xb6, 0x6f, 0x71, 0x27, 0x22, 0x58, 0x77, 0xd5, 0x19, 0x1d, 0x07, 0x1a, + 0xbb, 0x2d, 0x0b, 0x39, 0xa2, 0xb6, 0xea, 0xb6, 0xfc, 0x31, 0xa4, 0x03, 0xa9, 0x0c, + 0xaa, 0xe7, 0xbd, 0x17, 0xeb, 0x61, 0x50, 0x78, 0xec, 0x93, 0xf6, 0x6b, 0x1a, 0xe8, + 0x9b, 0x8e, 0x83, 0xed, 0xa7, 0x84, 0xf3, 0x85, 0x9a, 0xef, 0x99, 0x4b, 0xd5, 0x76, + 0x9d, 0x91, 0x3c, 0xc1, 0xc1, 0x3d, 0x2d, 0x46, 0x11, 0x18, 0x06, 0x79, 0x93, 0x5a, + 0x0a, 0x07, 0x44, 0x31, 0x78, 0x1c, 0xd1, 0x3f, 0x63, 0x5e, 0x7e, 0x18, 0x35, 0xc9, + 0xcf, 0xc2, 0x13, 0x75, 0x12, 0x41, 0x3a, 0x6d, 0xd2, 0x96, 0x3a, 0xa0, 0x01, 0x04, + 0x2d, 0x31, 0x6d, 0xfc, 0xa4, 0x6b, 0x54, 0xa7, 0xa5, 0xb1, 0x0f, 0xc3, 0x8d, 0xa0, + 0xc8, 0x53, 0x24, 0x52, 0x1f, 0xa3, 0x78, 0xb1, 0xba, 0x4a, 0x52, 0x06, 0x29, 0x27, + 0xac, 0xbb, 0x84, 0x75, 0xf9, 0x3d, 0x70, 0xca, 0xea, 0xa2, 0xcd, 0xc1, 0xb2, 0xe8, + 0x51, 0x63, 0xa2, 0xc8, 0x41, 0x99, 0x90, 0x5e, 0x51, 0x1d, 0xa5, 0x05, 0x7b, 0x96, + 0x3e, 0xe0, 0xa7, 0x03, 0x41, 0xd1, 0x7f, 0xbf, 0x63, 0xb6, 0x72, 0xf1, 0xbb, 0x60, + 0xca, 0x4b, 0x6b, 0xc0, 0xbc, 0xd0, 0x74, 0x75, 0x3e, 0x5e, 0x78, 0x44, 0xd2, 0xf6, + 0x31, 0xef, 0x2e, 0x83, 0x8c, 0x46, 0xf8, 0xe9, 0xf0, 0x07, 0xfd, 0x0b, 0x17, 0x6f, + 0x18, 0x49, 0x79, 0xe3, 0x07, 0x3d, 0xbd, 0x2f, 0xa0, 0xa3, 0xd8, 0xce, 0x7e, 0x53, + 0x3f, 0x66, 0x26, 0xa0, 0x33, 0x90, 0x1c, 0xab, 0x44, 0x09, 0xdb, 0x93, 0x7c, 0x33, + 0xae, 0xca, 0x8a, 0x84, 0x80, 0x58, 0xf9, 0x22, 0x6b, 0x3c, 0x40, 0x4e, 0x8b, 0x6f, + 0x8a, 0x22, 0xc3, 0x1c, 0xbd, 0xe1, 0x23, 0xd5, 0x71, 0xb5, 0xd4, 0x69, 0x96, 0x3a, + 0xe9, 0x95, 0xfd, 0x72, 0xe7, 0x0c, 0xd0, 0xf3, 0x67, 0x25, 0xd7, 0x70, 0x08, 0x58, + 0x23, 0x59, 0xde, 0xa0, 0xc2, 0xd6, 0x35, 0x55, 0x3b, 0x34, 0x8f, 0x11, 0x13, 0x8e, + 0x52, 0x5b, 0x43, 0xeb, 0x9f, 0x7c, 0xcd, 0x1a, 0x6e, 0x37, 0x17, 0xe9, 0xed, 0xeb, + 0x76, 0x08, 0xd1, 0x6a, 0x07, 0x1e, 0x67, 0x6c, 0x45, 0x90, 0x22, 0x53, 0xc2, 0xbf, + 0x50, 0xb4, 0x88, 0xb3, 0x92, 0x6d, 0x89, 0x5b, 0x7c, 0x3f, 0x13, 0x6a, 0xfa, 0xaa, + 0x15, 0xa4, 0xb1, 0x1a, 0x22, 0xd5, 0x1b, 0x13, 0x67, 0x5e, 0x34, 0x01, 0xa6, 0xaa, + 0x82, 0x3b, 0x73, 0xe2, 0x19, 0x63, 0xe6, 0x4a, 0x7f, 0x1a, 0x5a, 0xed, 0x56, 0x99, + 0x37, 0xbf, 0x3d, 0xf8, 0x18, 0x75, 0x13, 0xdc, 0x67, 0x06, 0x70, 0x9e, 0x1c, 0xa3, + 0x43, 0x94, 0x77, 0xe8, 0xac, 0x43, 0x03, 0x8e, 0x65, 0xc9, 0xb3, 0x48, 0x86, 0x69, + 0x73, 0x87, 0xf2, 0xe0, 0x45, 0xa9, 0x0a, 0xea, 0xbe, 0x2c, 0xcb, 0xe8, 0xa8, 0x21, + 0x86, 0xb5, 0x38, 0x34, 0xc2, 0xdc, 0x70, 0x3d, 0x21, 0xf6, 0xac, 0x47, 0x1f, 0x70, + 0xeb, 0x16, 0x23, 0x5a, 0x53, 0x6a, 0x32, 0x23, 0x38, 0xda, 0x70, 0xa1, 0x44, 0x3f, + 0x34, 0x6e, 0x7f, 0x36, 0xf0, 0x1b, 0x1e, 0x70, 0x6b, 0x54, 0x3a, 0x73, 0x89, 0x95, + 0xd7, 0x23, 0x1e, 0xcc, 0x10, 0xbe, 0xdb, 0x01, 0x65, 0x43, 0x50, 0x3a, 0xe7, 0x5c, + 0x92, 0x6a, 0x61, 0x84, 0xfd, 0xe0, 0xac, 0x86, 0x25, 0x5c, 0x36, 0x9e, 0xae, 0x54, + 0x89, 0x01, 0x94, 0x36, 0xed, 0x9a, 0xd8, 0xe4, 0x8c, 0xb1, 0x35, 0x3f, 0x40, 0x90, + 0x03, 0x48, 0x8d, 0xa8, 0xc7, 0x46, 0x89, 0xca, 0x5d, 0x9c, 0x93, 0x69, 0xd2, 0x4a, + 0x9e, 0x36, 0xce, 0xdc, 0x6e, 0x10, 0xee, 0x7c, 0xec, 0x1e, 0x03, 0xd2, 0x42, 0x35, + 0xf3, 0x75, 0x71, 0x6b, 0x52, 0x05, 0x20, 0xaf, 0xd6, 0x28, 0xab, 0xae, 0xcd, 0xc1, + 0x06, 0x37, 0x9c, 0x9e, 0xe4, 0x15, 0xeb, 0xf4, 0x91, 0x6d, 0x8d, 0x38, 0x32, 0x7c, + 0x06, 0x7e, 0x83, 0x07, 0x2f, 0x2f, 0x64, 0xb1, 0x72, 0x1f, 0x77, 0x69, 0x74, 0x5f, + 0xf3, 0xb9, 0x2e, 0xfe, 0x09, 0x8b, 0x8d, 0xe5, 0x52, 0xf6, 0x3f, 0x26, 0x25, 0xaf, + 0x68, 0x74, 0xf2, 0xdc, 0x42, 0x11, 0xd5, 0xfa, 0x29, 0x1f, 0x3d, 0x01, 0x71, 0xba, + 0x35, 0xfc, 0x1a, 0x19, 0x2f, 0x37, 0x21, 0x72, 0x2c, 0x49, 0x3c, 0xf7, 0xfa, 0xd4, + 0xc3, 0x24, 0xce, 0x87, 0x7c, 0x0d, 0x38, 0xd7, 0x06, 0xa4, 0xec, 0x73, 0xcf, 0x34, + 0x6c, 0xfb, 0xa5, 0x96, 0x2d, 0x33, 0x41, 0x22, 0xb0, 0x8a, 0xb0, 0x35, 0x10, 0xf4, + 0xf9, 0x22, 0xd7, 0x2c, 0xdd, 0x43, 0x48, 0x8c, 0x7b, 0x31, 0x03, 0x2e, 0xf8, 0x50, + 0x4e, 0xb9, 0xf7, 0xed, 0xa8, 0xe4, 0xd2, 0xf7, 0xd0, 0x43, 0x9f, 0x38, 0x24, 0x44, + 0x5c, 0x3d, 0xc9, 0xd3, 0x0b, 0x8b, 0x9b, 0x06, 0x2c, 0xc9, 0x5c, 0xbe, 0x41, 0xeb, + 0x87, 0x4c, 0x5c, 0xb5, 0xe7, 0x7c, 0xc2, 0xf8, 0x5f, 0x5b, 0xdc, 0xcf, 0x9f, 0xef, + 0xc8, 0x6a, 0x7e, 0x72, 0x89, 0x8e, 0xb6, 0xa2, 0xa7, 0x7d, 0xb6, 0x3e, 0x96, 0xe9, + 0xfc, 0x7f, 0x59, 0x97, 0x76, 0xbb, 0xe9, 0x10, 0x20, 0x64, 0x0f, 0x14, 0xf9, 0x47, + 0x8d, 0xca, 0x56, 0xd9, 0x44, 0x9f, 0x3d, 0x37, 0x47, 0x1e, 0x71, 0x42, 0x26, 0x14, + 0xbd, 0x9b, 0x39, 0x26, 0x2e, 0x09, 0x62, 0xba, 0xb0, 0xe8, 0x58, 0xe4, 0x90, 0x6e, + 0x95, 0x80, 0x5b, 0xca, 0x0c, 0x24, 0x15, 0xea, 0xac, 0xb5, 0x0b, 0xc5, 0x1a, 0x50, + 0xe1, 0xc9, 0x4f, 0x23, 0x1d, 0x72, 0x4a, 0x37, 0x09, 0x74, 0xa3, 0x9a, 0x9c, 0xb0, + 0x1b, 0x4b, 0xf8, 0x33, 0x93, 0x39, 0x42, 0xce, 0x8a, 0xdb, 0x76, 0xcb, 0x12, 0x17, + 0xbe, 0x2e, 0xb5, 0x2c, 0x9d, 0x8d, 0x2a, 0x34, 0xf2, 0x1c, 0x78, 0x17, 0x24, 0xa0, + 0xe2, 0x0f, 0xd2, 0x3d, 0xdc, 0x38, 0x0b, 0xab, 0x34, 0xe0, 0xbb, 0x12, 0x8c, 0x6d, + 0xf6, 0x6c, 0xf3, 0x55, 0x66, 0x6f, 0x84, 0x99, 0x8a, 0x18, 0x25, 0x3e, 0x18, 0x72, + 0x3d, 0x4d, 0x0e, 0x3b, 0xb5, 0x25, 0x60, 0x75, 0x9d, 0x68, 0x12, 0x79, 0x68, 0x95, + 0x08, 0xfb, 0x71, 0x9f, 0xcf, 0xf3, 0xb7, 0xc5, 0xf4, 0x01, 0xbc, 0x97, 0x0f, 0x52, + 0x4f, 0xa9, 0x9b, 0x22, 0x70, 0xb2, 0x36, 0xc9, 0x2a, 0x4d, 0xb3, 0x6f, 0x77, 0x1e, + 0x4a, 0xec, 0xa1, 0xb7, 0xd1, 0xf8, 0x90, 0x27, 0x6a, 0xf0, 0xfe, 0x6c, 0x03, 0x4a, + 0xd7, 0xff, 0x2d, 0x7b, 0xaf, 0x87, 0xce, 0x1c, 0x17, 0xc0, 0x1b, 0x5e, 0x4b, 0x5f, + 0xbf, 0x1f, 0x8d, 0x43, 0xf8, 0x47, 0x70, 0x81, 0x03, 0x23, 0x8f, 0xbf, 0x78, 0x5e, + 0x27, 0xdb, 0x31, 0x95, 0x60, 0xac, 0x3a, 0xe3, 0x81, 0xfe, 0x1d, 0xc3, 0x5f, 0x3f, + 0xaa, 0x14, 0xcf, 0x0d, 0xcb, 0x6e, 0x83, 0x8c, 0xcb, 0x5d, 0x10, 0x32, 0xcc, 0x31, + 0xc5, 0x76, 0x17, 0x42, 0x12, 0x1f, 0x7a, 0xc1, 0x44, 0x31, 0xbd, 0x54, 0x90, 0x8b, + 0x69, 0x25, 0xf8, 0x6a, 0xd0, 0x2b, 0xbe, 0xad, 0x61, 0x42, 0x61, 0xc2, 0x6a, 0x59, + 0x98, 0xad, 0x89, 0xb4, 0xdc, 0xc7, 0xf8, 0x8c, 0xdd, 0x27, 0x4e, 0xb3, 0x4d, 0x19, + 0x2e, 0x9c, 0xa6, 0xf5, 0xe8, 0x20, 0xc8, 0x41, 0x8e, 0x5e, 0x4a, 0x56, 0x9c, 0xa1, + 0x6e, 0x5b, 0xd6, 0xfa, 0x64, 0x40, 0xdf, 0xe3, 0xf9, 0x52, 0x4d, 0xe0, 0x77, 0xcc, + 0xc9, 0x71, 0x07, 0x17, 0xde, 0xaf, 0xa2, 0x03, 0xe8, 0xac, 0x32, 0xf3, 0xd3, 0xe0, + 0xfa, 0xe3, 0x3c, 0xb6, 0xa5, 0x1e, 0xf3, 0x89, 0xa6, 0x40, 0x9d, 0xa3, 0x46, 0x1c, + 0xca, 0x5e, 0xa4, 0x83, 0x1c, 0x18, 0xea, 0xb0, 0xf5, 0x75, 0xd3, 0x79, 0x0a, 0xa0, + 0xa3, 0x6d, 0x16, 0xd0, 0x0c, 0xe4, 0xaa, 0x20, 0x98, 0x1c, 0x21, 0xd7, 0x19, 0x50, + 0x52, 0xcd, 0x9b, 0x55, 0xc6, 0x6b, 0x90, 0x9a, 0x83, 0x9d, 0xe7, 0x6d, 0xfd, 0x52, + 0x05, 0x18, 0xf0, 0xdd, 0xb4, 0x62, 0xa8, 0x99, 0x3b, 0x92, 0x49, 0xa3, 0x51, 0x14, + 0x47, 0x54, 0x57, 0x9d, 0x7e, 0xc9, 0x3f, 0x71, 0x3a, 0xce, 0xa6, 0x3a, 0x27, 0x38, + 0x16, 0xa9, 0x25, 0x81, 0xc0, 0x23, 0x9a, 0x71, 0xdc, 0xbd, 0xf0, 0x3c, 0x05, 0x8e, + 0xd4, 0xa1, 0x6f, 0x44, 0xd7, 0x44, 0x32, 0x0c, 0x3b, 0xcf, 0x25, 0x06, 0x6a, 0x93, + 0x5c, 0xe5, 0x30, + ], + }, + TestVector { + normal: vec![ + 0x5a, 0x55, 0x59, 0xcf, 0x9e, 0x2d, 0x3b, 0x60, 0x97, 0x8d, 0x81, 0xa6, 0x78, 0xb9, + 0xed, 0x8e, 0x44, 0x86, 0xb4, 0xd1, 0x46, 0x09, 0xd6, 0xc1, 0x27, 0xc0, 0xc2, 0xfb, + 0xff, 0xe3, 0x0a, 0x60, 0xf7, 0xbf, 0xf1, 0xd9, 0xfb, 0x83, 0x00, 0xed, 0x00, 0x92, + 0x53, 0xba, 0x9b, 0x99, 0x6f, 0xa0, 0x52, 0x41, 0xb1, 0x0f, 0x5a, 0xc9, 0xa8, 0x40, + 0x8e, 0x92, 0x5b, 0x62, 0x6b, 0xb2, 0x1a, 0x47, 0x1f, 0xe3, 0xbe, 0xde, 0x52, 0xbb, + 0xa0, 0x97, 0xb2, 0xa9, 0x9a, 0x9b, 0xa5, 0xa8, 0x66, 0x58, 0xc3, 0xfd, 0x9e, 0xc5, + 0x5b, 0xfa, 0x9b, 0x32, 0x85, 0x67, 0x25, 0x4a, 0xb3, 0x6d, 0x2c, 0x7f, 0x44, 0xd2, + 0xc7, 0xe1, 0x3e, 0xb5, 0x4b, 0xeb, 0x70, 0xea, 0x8f, 0xa9, 0x4b, 0x6c, 0x6e, 0x01, + 0x2d, 0x79, 0xe3, 0xf5, 0x36, 0x89, 0xc2, 0xb1, 0xa1, 0x8e, 0xaf, 0x2d, 0x47, 0x1d, + 0x13, 0xc1, 0xab, 0x39, 0xd9, 0x19, 0x4a, 0xe8, 0x43, 0xab, 0x1d, 0x28, 0xff, 0xa8, + 0xf6, 0x9d, 0xc7, 0xe1, 0x5c, 0xc3, 0x8b, 0x12, 0xe8, 0xfc, 0xd7, 0x92, 0x55, 0xb7, + 0x21, 0x60, 0x56, 0xd9, 0xed, 0xb7, 0x48, 0x2f, 0xb9, 0x8a, 0xa0, 0x33, 0xb6, 0x5e, + 0x51, 0xc1, 0xa0, 0x8b, 0x8a, 0x11, 0xd8, 0x4d, 0x04, 0x09, 0xb7, 0x34, 0xf4, 0x52, + 0xaa, 0xf0, 0xd6, 0xb1, 0x8f, 0x50, 0x25, 0x86, 0x83, 0xd3, 0xf9, 0xa7, 0x6d, 0x39, + 0x9f, 0xd0, 0x47, 0xee, 0xe2, 0x88, 0xbb, 0x45, 0x85, 0x85, 0x1d, 0xc9, 0x3e, 0xcc, + 0xc6, 0x23, 0x22, 0x92, 0x4c, 0xd1, 0x3b, 0x5d, 0xd4, 0xee, 0xd6, 0x6e, 0xd8, 0xd9, + 0x97, 0x2d, 0x77, 0x26, 0x29, 0xea, 0x64, 0x74, 0x2e, 0x54, 0x73, 0x39, 0x81, 0xb0, + 0x06, 0xc0, 0x62, 0x46, 0x8e, 0x4b, 0xd8, 0xf7, 0xdd, 0x9a, 0xf6, 0x98, 0xf5, 0x2a, + 0xe8, 0x14, 0x63, 0x4e, 0x81, 0xd7, 0xf3, 0xe0, 0xc4, 0x20, 0x31, 0x7c, 0xac, 0xa9, + 0xae, 0x48, 0x11, 0xc6, 0xaf, 0x06, 0xfe, 0x80, 0xa8, 0xc0, 0x2a, 0xb7, 0xa0, 0x0e, + 0x18, 0xe4, 0xa6, 0xaa, 0x1e, 0xa1, 0xb7, 0x69, 0x45, 0xd2, 0x61, 0x5d, 0x43, 0xac, + 0x11, 0x8b, 0x56, 0xc2, 0xf2, 0x96, 0x0f, 0xe9, 0x3a, 0x02, 0x5f, 0x13, 0xec, 0x91, + 0xff, 0xc6, 0xd2, 0xc3, 0x53, 0x69, 0x9a, 0xbb, 0x09, 0x2d, 0xed, 0xc0, 0x65, 0xdb, + 0x8f, 0xa2, 0x14, 0xdb, 0xc4, 0x64, 0x66, 0xf8, 0x97, 0xb8, 0x8c, 0x58, 0xb3, 0x01, + 0x52, 0x13, 0x3a, 0xa3, 0x83, 0x1a, 0xf3, 0x7c, 0x74, 0xd9, 0x9e, 0x9e, 0x36, 0xff, + 0x70, 0x11, 0xd3, 0x23, 0x83, 0x05, 0x69, 0x15, 0x08, 0xa2, 0xc3, 0xa4, 0x3e, 0x75, + 0x5d, 0xc0, 0x81, 0xb5, 0x11, 0xd6, 0x48, 0x2a, 0x7d, 0xb6, 0x5f, 0xa9, 0x69, 0x9e, + 0xa8, 0x7f, 0xf4, 0x70, 0x99, 0xed, 0x36, 0x37, 0xdb, 0xb0, 0xa3, 0xd0, 0xef, 0x79, + 0x79, 0x6a, 0x8e, 0xf1, 0xe4, 0xd9, 0x4d, 0x42, 0xb4, 0xbc, 0x2b, 0x4a, 0x03, 0x8a, + 0xe6, 0xe4, 0x6b, 0x24, 0xcf, 0xc8, 0x41, 0x53, 0xd3, 0x1e, 0xaf, 0x89, 0x50, 0x63, + 0xa5, 0xca, 0x95, 0x9b, 0xe6, 0x3f, 0x37, 0xf2, 0xba, 0x0d, 0x43, 0x23, 0x66, 0x73, + 0x6d, 0x86, 0x32, 0xfc, 0xe0, 0x72, 0xb6, 0xae, 0x5b, 0x6f, 0x3f, 0xd5, 0x9d, 0x3f, + 0xaf, 0xf6, 0x38, 0x27, 0x5a, 0x99, 0x2f, 0xef, 0xc8, 0x7e, 0x60, 0xd4, 0x4c, 0x2c, + 0xad, 0xc2, 0xb5, 0xc4, 0x94, 0xe3, 0xe7, 0x2e, 0xb4, 0x59, 0x7c, 0x96, 0xb4, 0x01, + 0x67, 0x79, 0x9a, 0x90, 0x01, 0xa2, 0xed, 0x36, 0x76, 0xa8, 0xb4, 0x03, 0xae, 0x25, + 0xff, 0xd7, 0x72, 0xf7, 0x08, 0x1e, 0x9a, 0x32, 0xbc, 0xc1, 0xc5, 0xe2, 0xed, 0xd4, + 0xe2, 0xa6, 0x57, 0x6b, 0x78, 0x3c, 0xce, 0x3a, 0xae, 0x11, 0xfa, 0x43, 0x22, 0x62, + 0x54, 0x88, 0x56, 0x18, 0x3e, 0xe6, 0x82, 0xd5, 0xdc, 0x31, 0xbe, 0xb3, 0x8f, 0x06, + 0x1c, 0xbd, 0xec, 0xa7, 0x02, 0x1a, 0x44, 0x4e, 0x2d, 0xd4, 0x17, 0xdf, 0x26, 0xdc, + 0xd2, 0x20, 0xf2, 0xb7, 0x31, 0x77, 0x2b, 0x43, 0x9e, 0x96, 0xd6, 0x14, 0xe1, 0xfa, + 0xcb, 0x48, 0x6c, 0x7a, 0x7d, 0x51, 0x71, 0xb1, 0xde, 0x35, 0x9f, 0x6a, 0xd3, 0xa9, + 0x6f, 0x64, 0x9c, 0x96, 0x91, 0x02, 0xa1, 0x96, 0x4f, 0xb4, 0xb4, 0xa1, 0xa4, 0x27, + 0x9c, 0x68, 0xe6, 0xc3, 0x72, 0xe4, 0x21, 0x87, 0xd7, 0x54, 0xe8, 0x04, 0xa6, 0x16, + 0x53, 0x09, 0x20, 0x69, 0xfb, 0x9b, 0x6d, 0x25, 0x26, 0x68, 0x90, 0x80, 0x8b, 0x01, + 0x5d, 0xf2, 0x8c, 0x80, 0x10, 0x65, 0xda, 0x6f, 0xeb, 0xdc, 0x1a, 0x56, 0xbf, 0xd0, + 0x02, 0x62, 0x5a, 0xcf, 0xaa, 0x53, 0x73, 0xfd, 0xe1, 0x49, 0xc1, 0xcf, 0xc3, 0x64, + 0x9b, 0x48, 0x69, 0x69, 0x6d, 0x44, 0xec, 0xb1, 0x24, 0x79, 0xc5, 0xeb, 0xef, 0x99, + 0x5f, 0x10, 0x02, 0x9f, 0x8b, 0x53, 0x0e, 0xeb, 0x3f, 0xdc, 0x2e, 0x50, 0xe8, 0x75, + 0x7f, 0xc0, 0xbb, 0x9e, 0x26, 0x30, 0x23, 0xdb, 0x82, 0xf8, 0x78, 0xd9, 0xac, 0x7f, + 0xfb, 0x0b, 0xd4, 0x39, 0x1d, 0xf1, 0xd8, 0x79, 0x89, 0x9a, 0x3e, 0xf5, 0x7b, 0xfd, + 0x0d, 0x1f, 0x77, 0x55, 0x64, 0x8e, 0xdd, 0x85, 0xbb, 0x05, 0x2a, 0x6e, 0xdf, 0x71, + 0xcd, 0x26, 0x28, 0xc9, 0x87, 0x42, 0x9f, 0x36, 0xdc, 0x50, 0x5c, 0xcc, 0x43, 0xf3, + 0x0e, 0x7a, 0x86, 0x9c, 0x9e, 0x25, 0x5e, 0x2a, 0xf9, 0xfc, 0xf3, 0x0c, 0x12, 0x17, + 0x96, 0xd1, 0x90, 0x00, 0x09, 0x60, 0xcb, 0x6f, 0xe2, 0xf1, 0xbf, 0x24, 0x61, 0x18, + 0xb4, 0x98, 0xf3, 0x24, 0x7f, 0x9d, 0x48, 0x4c, 0x73, 0xcf, 0x09, 0x39, 0x30, 0x39, + 0xe4, 0x53, 0x26, 0xb8, 0xff, 0xff, 0xb3, 0xe7, 0xe6, 0x15, 0x9c, 0x46, 0x69, 0x9f, + 0x10, 0x07, 0x92, 0xd4, 0x67, 0x29, 0x50, 0x34, 0x8a, 0x90, 0x55, 0x2e, 0x45, 0x94, + 0x3b, 0xee, 0xac, 0xf0, 0x3f, 0x32, 0x16, 0xf9, 0x4e, 0x27, 0x4d, 0x63, 0xd6, 0x37, + 0xd9, 0xf1, 0x90, 0xe8, 0xa2, 0x66, 0xcd, 0xee, 0xf1, 0x53, 0x53, 0x0b, 0xee, 0x5c, + 0xb8, 0x35, 0x52, 0x60, 0x50, 0x5c, 0x2c, 0x2e, 0x5d, 0x99, 0x0f, 0xff, 0xdc, 0x34, + 0xec, 0x0f, 0xf7, 0xf1, 0xaf, 0x81, 0xb2, 0x4c, 0xed, 0x0e, 0xfa, 0x62, 0x13, 0xda, + 0x6c, 0x7c, 0x60, 0xc4, 0x87, 0xf5, 0xf7, 0xb0, 0x3f, 0x81, 0x60, 0xa0, 0x57, 0xf4, + 0x6d, 0x05, 0xbf, 0x82, 0x18, 0xb3, 0xad, 0xd9, 0xc0, 0x68, 0x93, 0xbd, 0x02, 0xdb, + 0x9b, 0x61, 0x19, 0x1d, 0xfb, 0x13, 0x3b, 0xfa, 0xbe, 0x48, 0x58, 0xe4, 0x7a, 0x4c, + 0xc3, 0x2e, 0x41, 0x6e, 0xc0, 0x8b, 0x8a, 0xc7, 0x91, 0x5a, 0x43, 0x73, 0x3f, 0x44, + 0x06, 0xe9, 0xd9, 0x67, 0xc5, 0x60, 0xf3, 0x44, 0xd7, 0xe9, 0x04, 0xa2, 0x80, 0x45, + 0xd9, 0x9f, 0x3a, 0xf8, 0xc8, 0x2e, 0x97, 0xe1, 0xb9, 0xc1, 0xb2, 0x05, 0xe5, 0x85, + 0xfb, 0xeb, 0xb4, 0x8f, 0xaf, 0x58, 0xf1, 0xb6, 0x5d, 0xca, 0x24, 0x97, 0xe0, 0x9a, + 0x70, 0xaa, 0xd4, 0x86, 0x5f, 0x85, 0x71, 0x5a, 0x28, 0x0e, 0x18, 0x6f, 0x3f, 0xc1, + 0x74, 0x0d, 0x81, 0x84, 0xd3, 0x3e, 0x83, 0x22, 0x16, 0x95, 0x21, 0xcd, 0xc1, 0x32, + 0x21, 0x29, 0x39, 0xc8, 0x4a, 0x10, 0x89, 0x64, 0xe2, 0xde, 0x74, 0xb6, 0xea, 0x55, + 0xb4, 0xcb, 0x8f, 0x6f, 0x9b, 0xee, 0x98, 0xb1, 0x0d, 0x41, 0x51, 0x09, 0x45, 0x5f, + 0x48, 0xb7, 0x76, 0x08, 0x2d, 0xc3, 0x0b, 0x4b, 0xc7, 0x34, 0x77, 0x07, 0x55, 0x11, + 0x70, 0x03, 0x08, 0x15, 0x8c, 0xe2, 0xf2, 0xf9, 0xbf, 0x0f, 0x69, 0x1b, 0x2c, 0xe5, + 0x3e, 0x61, 0x14, 0x2c, 0xb7, 0x40, 0xc1, 0x5b, 0x7b, 0x62, 0x3c, 0xf4, 0x8b, 0x3f, + 0x7b, 0xfe, 0xfa, 0x31, 0xbc, 0xdc, 0x66, 0x5c, 0x6d, 0x71, 0x23, 0xe9, 0x53, 0x50, + 0x81, 0x13, 0x75, 0x94, 0x7b, 0x05, 0x5a, 0x43, 0xdb, 0x07, 0xe0, 0x3f, 0x33, 0x62, + 0x7d, 0xf5, 0xc6, 0x38, 0xbf, 0xad, 0x95, 0x6d, 0xdc, 0x1e, 0xa7, 0xd7, 0x62, 0x0a, + 0x20, 0xf2, 0x79, 0x2f, 0x63, 0x81, 0x7a, 0x1c, 0xf3, 0x25, 0x80, 0xd0, 0x42, 0x74, + 0x23, 0x4a, 0xf2, 0xa5, 0x1b, 0x56, 0xbb, 0x68, 0xa2, 0x9e, 0x43, 0xa9, 0x54, 0x14, + 0x2b, 0xa4, 0xca, 0x68, 0x23, 0xbd, 0xe9, 0x05, 0x3d, 0x72, 0xfd, 0xad, 0xbc, 0x61, + 0xad, 0x59, 0x36, 0xc5, 0x3f, 0xdd, 0x75, 0x79, 0x44, 0x6d, 0x11, 0xc4, 0x46, 0x07, + 0xf4, 0x16, 0x30, 0xe4, 0xc0, 0x89, 0x15, 0xe6, 0x31, 0x77, 0x15, 0x50, 0xe9, 0xce, + 0x1f, 0xca, 0x2c, 0x63, 0xfe, 0x06, 0xb7, 0x98, 0x9d, 0x58, 0x4f, 0xa7, 0xd7, 0x82, + 0xa8, 0x8c, 0x1e, 0x7d, 0x64, 0xb6, 0xfb, 0xf5, 0x5e, 0x35, 0x96, 0xaf, 0x9b, 0xcb, + 0x75, 0x85, 0xf8, 0xc7, 0xd3, 0xaa, 0x5c, 0x20, 0x82, 0xb2, 0x65, 0x24, 0x9d, 0xf0, + 0x57, 0x01, 0xda, 0xb0, 0x31, 0xc4, 0xba, 0xc1, 0xea, 0x26, 0x7a, 0x29, 0x96, 0xa2, + 0x02, 0x8d, 0x1e, 0x6a, 0x0f, 0x80, 0xa3, 0x84, 0x7c, 0x53, 0x1d, 0xba, 0x96, 0xee, + 0x65, 0xa2, 0x41, 0x89, 0xbd, 0x27, 0x12, 0xe4, 0x0e, 0x95, 0x96, 0x64, 0x98, 0x1e, + 0x58, 0xb2, 0xa4, 0xf9, 0x51, 0xef, 0x8f, 0x49, 0x7d, 0xff, 0xf2, 0xf2, 0xf2, 0x71, + 0xea, 0xb8, 0x9c, 0x62, 0x8e, 0x18, 0xb5, 0xfc, 0xb4, 0x38, 0x82, 0x53, 0x7e, 0xaf, + 0x6a, 0xd2, 0xa6, 0xb1, 0x75, 0x46, 0x33, 0xca, 0xa8, 0x6b, 0xf2, 0xc7, 0x6f, 0x39, + 0x93, 0x15, 0x4f, 0xc7, 0x3e, 0x6f, 0xbb, 0xa2, 0x21, 0x0c, 0x27, 0x43, 0xf5, 0x30, + 0xa4, 0x27, 0x84, 0x9a, 0x30, 0x1e, 0x00, 0xe0, 0x11, 0x29, 0xf0, 0x3a, 0x46, 0x07, + 0xf8, 0x7c, 0xbe, 0x07, 0x62, 0xc0, 0xb1, 0xc6, 0x58, 0x55, 0xde, 0xba, 0x84, 0x22, + 0xca, 0x4b, 0x88, 0xab, 0xee, 0xa6, 0xa4, 0x38, 0x2c, 0xf1, 0x6c, 0xcd, 0x6d, 0xc7, + 0xc3, 0x7c, 0x44, 0xe5, 0x49, 0xc4, 0x53, 0x48, 0x19, 0xac, 0xd8, 0xbb, 0x0a, 0x02, + 0xa5, 0xfa, 0x7a, 0x1c, 0x1d, 0x38, 0x06, 0xfb, 0xc3, 0x40, 0x7f, 0xd7, 0xda, 0x93, + 0xfd, 0x0d, 0xe6, 0x40, 0x0d, 0x3a, 0xb8, 0x97, 0x74, 0x85, 0xcd, 0xdf, 0xbe, 0xd5, + 0x93, 0x2f, 0x50, 0x7b, 0x79, 0x94, 0x7a, 0xdb, 0x2f, 0xad, 0x37, 0x61, 0x5a, 0xa7, + 0x17, 0xdb, 0x5f, 0x29, 0x80, 0x99, 0xf2, 0x0f, 0x26, 0x3b, 0x35, 0x9a, 0x11, 0x51, + 0xa6, 0xb7, 0x5c, 0x01, 0x36, 0x5e, 0xb1, 0x54, 0xae, 0x42, 0x14, 0x0d, 0x6e, 0x10, + 0x34, 0x2f, 0x14, 0xf3, 0x4d, 0xc3, 0x3e, 0x07, 0xff, 0x0e, 0x4d, 0x1a, 0x6b, 0xe3, + 0x75, 0xb3, 0x2f, 0x84, 0xb9, 0x2e, 0x5d, 0x81, 0xeb, 0xb6, 0x39, 0xc4, 0xf2, 0x7e, + 0x71, 0x5a, 0xa4, 0x2c, 0xc7, 0x57, 0x07, 0xd4, 0xeb, 0xd1, 0xbb, 0xfb, 0xe8, 0xf9, + 0x0f, 0xc7, 0xc9, 0x53, 0xe7, 0xa9, 0x71, 0x5e, 0x65, 0xaf, 0x82, 0x67, 0x37, 0x3d, + 0x34, 0x51, 0x67, 0x4f, 0xf0, 0x84, 0xef, 0xd9, 0x2c, 0xcf, 0x3b, 0xcc, 0x7a, 0xca, + 0x14, 0x67, 0xb6, 0x32, 0x7e, 0x4f, 0x95, 0x22, 0xb2, 0xcc, 0x57, 0x9a, 0x7a, 0x8f, + 0xff, 0x7c, 0xa7, 0xcf, 0x14, 0x5d, 0xfc, 0x13, 0xea, 0xfc, 0x34, 0x15, 0x3b, 0x2c, + 0x3e, 0x8a, 0xfb, 0xe5, 0x34, 0x44, 0xd0, 0xc7, 0x3b, 0x3b, 0xd5, 0xbc, 0x87, 0x0b, + 0x01, 0xcd, 0x45, 0x79, 0x11, 0xe3, 0x56, 0x31, 0x3f, 0xd1, 0xda, 0xfb, 0x4c, 0x81, + 0x51, 0x63, 0x4a, 0x01, 0xaf, 0xf7, 0xcf, 0x11, 0x6d, 0x43, 0x3c, 0x3d, 0x2b, 0x3a, + 0xdd, 0xa9, 0xce, 0xbe, 0x18, 0xf7, 0xd1, 0x72, 0x44, 0x3e, 0x5e, 0x7b, 0x5a, 0xc9, + 0xab, 0xe8, 0xdb, 0x22, 0x56, 0xd7, 0xeb, 0xe2, 0xff, 0x28, 0x02, 0x09, 0x39, 0x50, + 0x38, 0x70, 0x59, 0x7b, 0x9a, 0x95, 0x58, 0x92, 0xc7, 0x38, 0x96, 0x50, 0xa2, 0xd4, + 0x2e, 0xc9, 0x2b, 0xe7, 0x23, 0xfe, 0xdf, 0x2f, 0x2e, 0xde, 0x5a, 0x47, 0x2a, 0xa1, + 0xe7, 0x4f, 0x33, 0xad, 0x41, 0x90, 0x15, 0x44, 0xed, 0xbb, 0xe3, 0xac, 0x46, 0x4c, + 0xf4, 0x39, 0x19, 0x60, 0x15, 0xf4, 0xf2, 0x2a, 0xc2, 0xb8, 0xfc, 0x01, 0x49, 0x6b, + 0xea, 0xb4, 0xd4, 0x59, 0x07, 0xf4, 0x79, 0x81, 0x2a, 0x25, 0x94, 0x31, 0xa2, 0xcb, + 0xc9, 0x3d, 0x4f, 0x3b, 0x84, 0xe4, 0xdd, 0x36, 0x60, 0x20, 0x27, 0x3a, 0x67, 0x52, + 0xe5, 0x01, 0xaf, 0x6f, 0xf1, 0xb7, 0x8d, 0xdc, 0x81, 0x7e, 0x6e, 0xa3, 0x51, 0xd6, + 0x00, 0x6b, 0xec, 0xf8, 0xd2, 0xff, 0xb0, 0x39, 0x90, 0xf6, 0x77, 0x74, 0xa8, 0x1e, + 0x05, 0xb7, 0xf4, 0xbb, 0xad, 0x85, 0x77, 0xfa, 0x27, 0xc9, 0xde, 0x64, 0xe1, 0xb1, + 0x1d, 0xcf, 0x38, 0x4f, 0x59, 0x56, 0x44, 0x37, 0x48, 0x75, 0x5a, 0x9f, 0xc6, 0xf2, + 0xa0, 0x0b, 0x10, 0xc3, 0x65, 0x7e, 0xba, 0xc0, 0x3b, 0xfc, 0x0b, 0x58, 0x7b, 0xef, + 0x2f, 0x45, 0xec, 0x8a, 0xcd, 0xaa, 0x51, 0xc1, 0x43, 0xb0, 0xcb, 0x25, 0xb9, 0x14, + 0x2c, 0x61, 0xbd, 0x79, 0x0a, 0x80, 0xd7, 0xc2, 0x3f, 0x90, 0xcc, 0x03, 0x49, 0x5b, + 0x51, 0xe4, 0xd2, 0x84, 0x3e, 0x55, 0x7f, 0x9e, 0x25, 0x45, 0x10, 0x8c, 0x6c, 0x6f, + 0xae, 0x35, 0x9f, 0x64, 0x5c, 0x27, 0x68, 0x91, 0xc0, 0xdc, 0xab, 0x3f, 0xaf, 0x18, + 0x77, 0x00, 0xc0, 0x82, 0xdc, 0x47, 0x77, 0x40, 0xfb, 0x3f, 0x2c, 0xd7, 0xbb, 0x59, + 0xfb, 0x35, 0x85, 0x54, 0xe9, 0x4c, 0x7e, 0x67, 0x8c, 0xe0, 0x1a, 0xeb, 0xf9, 0x4e, + 0x51, 0x5e, 0x49, 0x72, 0x29, 0x67, 0x99, 0x5a, 0xea, 0x85, 0x8d, 0x64, 0xe7, 0x78, + 0x9f, 0xf3, 0x06, 0x36, 0x95, 0x77, 0x22, 0x81, 0x80, 0x32, 0x6a, 0x5b, 0x0a, 0xf4, + 0x75, 0xe2, 0x7a, 0x54, 0xb2, 0x07, 0xb4, 0x1f, 0x92, 0xe3, 0x76, 0x17, 0x0e, 0x3f, + 0xb0, 0x05, 0x02, 0x82, 0x61, 0xc9, 0x9c, 0x2d, 0xbd, 0x0e, 0xed, 0xee, 0x87, 0x1c, + 0x1c, 0x0f, 0x48, 0xb8, 0xe9, 0xb8, 0xe4, 0xbe, 0x77, 0xd1, 0xb7, 0x37, 0xfe, 0x21, + 0xf0, 0xfa, 0x5a, 0x18, 0xeb, 0xb5, 0x27, 0x55, 0xb5, 0xa6, 0xcf, 0x61, 0x30, 0xfb, + 0x56, 0x94, 0x4c, 0xfa, 0xb8, 0x75, 0x27, 0xc2, 0x50, 0xd1, 0x13, 0xb2, 0x9b, 0xca, + 0xc9, 0xaa, 0xa1, 0x0c, 0x2e, 0x7d, 0xe4, 0x15, 0xed, 0xb0, 0x80, 0x6c, 0x6d, 0xa0, + 0x30, 0x20, 0xa1, 0x34, 0xca, 0x7e, 0xcd, 0xc8, 0xda, 0x1b, 0xd5, 0x7a, 0x37, 0xf5, + 0x5a, 0x46, 0x94, 0x0b, 0x45, 0xb2, 0x41, 0xb1, 0xc1, 0x6e, 0xe1, 0x00, 0x92, 0x7d, + 0x1b, 0xd8, 0x60, 0xd4, 0x45, 0xa9, 0xde, 0x50, 0xd4, 0xc3, 0x84, 0xd6, 0xe1, 0xd0, + 0x01, 0x08, 0x02, 0x6c, 0x0e, 0xa5, 0xeb, 0xbf, 0x0b, 0x72, 0xfb, 0xf5, 0xc3, 0x70, + 0xbc, 0xe1, 0x8d, 0x3a, 0xcb, 0xc4, 0x65, 0x99, 0x09, 0x9b, 0xaa, 0xe1, 0xd8, 0x02, + 0xf7, 0x73, 0x33, 0x49, 0x4a, 0x7a, 0xe1, 0x30, 0xfe, 0x86, 0xe8, 0xf8, 0x18, 0xf9, + 0x26, 0x1a, 0x2d, 0xad, 0xb4, 0x12, 0x52, 0x29, 0xba, 0x0f, 0xfc, 0x0e, 0x70, 0x90, + 0x32, 0x44, 0x30, 0xb5, 0x21, 0xa9, 0x0d, 0x22, 0x4a, 0xb7, 0xa1, 0x02, 0x4e, 0x1d, + 0x89, 0x3e, 0x74, 0x04, 0xfe, 0xdb, 0x34, 0x8e, 0x4d, 0x5e, 0x22, 0x35, 0xc5, 0x9a, + 0x78, 0x76, 0xa0, 0xfc, 0x60, 0x14, 0x5c, 0x6a, 0x00, 0x96, 0x87, 0x68, 0x44, 0x60, + 0x27, 0x1e, 0xe1, 0x33, 0xa4, 0x37, 0xfe, 0x52, 0xfb, 0x6c, 0xfb, 0xa9, 0x7f, 0xce, + 0xc1, 0x61, 0xdf, 0x51, 0x5d, 0xde, 0x90, 0x5a, 0x24, 0xda, 0x6d, 0x37, 0xbd, 0xc3, + 0x40, 0x44, 0xa9, 0x55, 0xe6, 0x82, 0xb4, 0x74, 0x71, 0xca, 0x1e, 0x8c, 0x78, 0xc5, + 0x1e, 0xd3, 0x77, 0xcd, 0x4a, 0xfa, 0x89, 0x4b, 0xd9, 0xbd, 0x12, 0xe7, 0x07, 0x15, + 0x6d, 0xa0, 0x72, 0x6f, 0x7c, 0xf5, 0x72, 0x9f, 0xab, 0xe3, 0x72, 0x16, 0x04, 0x63, + 0xfe, 0x04, 0x29, 0x24, 0x4d, 0x06, 0x74, 0x89, 0xba, 0x5d, 0x09, 0x47, 0x2e, 0xcd, + 0x9b, 0xcd, 0xc4, 0xd5, 0xe4, 0xdf, 0x10, 0x1e, 0x18, 0x9d, 0xb8, 0x46, 0x3e, 0xb5, + 0x38, 0x30, 0x7b, 0x58, 0x7d, 0xef, 0xf7, 0x8d, 0xe9, 0xc7, 0x3a, 0xf2, 0x80, 0x80, + 0xb2, 0xfd, 0x05, 0x00, 0x3e, 0x11, 0xd3, 0xe1, 0xb3, 0x29, 0x9d, 0xc9, 0x52, 0x1f, + 0x8b, 0x51, 0x3b, 0xad, 0xb0, 0x10, 0xe9, 0x1b, 0xfe, 0xb9, 0x1b, 0x0b, 0x2a, 0x6c, + 0xb1, 0x29, 0xc2, 0xe8, 0x25, 0xa5, 0x97, 0xb8, 0xfb, 0x75, 0xbc, 0x56, 0x2d, 0x65, + 0x4d, 0x62, 0x10, 0x46, 0x40, 0xdd, 0x74, 0xe5, 0x6c, 0xd1, 0x4b, 0xaa, 0xba, 0x56, + 0x5b, 0x84, 0xb8, 0x45, 0xe1, 0x63, 0xd1, 0xca, 0xef, 0x25, 0x33, 0xc3, 0x98, 0x16, + 0x37, 0x20, 0x4f, 0x96, 0xa5, 0x9c, 0x8e, 0x80, 0x24, 0xd9, 0x04, 0x1b, 0x20, 0x29, + 0xe9, 0x4c, 0x15, 0x24, 0x5f, 0x1a, 0x95, 0x88, 0x40, 0xba, 0x3f, 0x38, 0x0a, 0x4d, + 0x20, 0xf1, 0x18, 0x4e, 0x77, 0x82, 0x7d, 0xe3, 0xff, 0x8f, 0x3d, 0x73, 0x45, 0x9a, + 0xfe, 0x24, 0x1f, 0x72, 0x3c, 0x08, 0x48, 0x23, 0x23, 0x0e, 0x00, 0x3d, 0x3d, 0x21, + 0xe5, 0x35, 0x01, 0xec, 0x04, 0x99, 0xb0, 0x83, 0xa7, 0xda, 0xd6, 0x85, 0xc5, 0x71, + 0x27, 0xf4, 0xde, 0x64, 0x73, 0x3a, 0x88, 0x0c, 0x2d, 0xb2, 0x8f, 0xda, 0xab, 0xf1, + 0xb5, 0x42, 0xd2, 0x05, 0xf6, 0x64, 0xa3, 0x51, 0x35, 0x71, 0x27, 0x11, 0xdc, 0xcc, + 0xd9, 0x31, 0xa5, 0x0b, 0x9c, 0x56, 0x61, 0x88, 0x23, 0x60, 0xd4, 0xca, 0xc0, 0x04, + 0x76, 0x81, 0xbc, 0x2e, 0x2b, 0x3b, 0xf6, 0xc9, 0x97, 0x60, 0xd7, 0xcf, 0xb4, 0xfa, + 0x21, 0x39, 0x43, 0x77, 0xa4, 0x55, 0x1c, 0x76, 0xd1, 0xf7, 0x5a, 0xc0, 0x3c, 0x26, + 0x20, 0x54, 0xdf, 0xfd, 0x79, 0xa9, 0xde, 0xd0, 0x5e, 0x88, 0x89, 0x58, 0x19, 0x9e, + 0xea, 0x45, 0x01, 0xe2, 0x99, 0x0a, 0x53, 0xa5, 0xcd, 0x2a, 0x46, 0xa4, 0x01, 0x57, + 0x65, 0x88, 0xfd, 0x7d, 0x05, 0x8a, 0x26, 0xf2, 0x84, 0x38, 0xe5, 0x78, 0x2f, 0x45, + 0xac, 0x1d, 0x07, 0xf6, 0xf6, 0xf5, 0xed, 0x73, 0x74, 0x1d, 0x57, 0x85, 0x83, 0x7a, + 0x6b, 0x84, 0x4b, 0x47, 0x47, 0x75, 0x71, 0x8c, 0x29, 0xdd, 0x99, 0x08, 0x4e, 0x9f, + 0x88, 0xef, 0x15, 0x3a, 0x83, 0x29, 0xf5, 0x32, 0xa6, 0x90, 0x17, 0xdc, 0x3a, 0x97, + 0xed, 0x75, 0x43, 0x67, 0x72, 0x30, 0x98, 0xe5, 0x76, 0x58, 0x40, 0xb0, 0x22, 0x89, + 0x72, 0x44, 0x74, 0x5f, 0xbb, 0xbb, 0x30, 0xa7, 0xcb, 0x54, 0xfa, 0x05, 0x11, 0x16, + 0x6e, 0x95, 0x44, 0x12, 0x20, 0x00, 0x61, 0x0b, 0xd2, 0xaa, 0xcb, 0xd8, 0x23, 0x25, + 0xa5, 0x9b, 0x95, 0x15, 0x4e, 0xcd, 0x82, 0xc8, 0x8d, 0x23, 0xab, 0xd1, 0xe2, 0x07, + 0x70, 0xff, 0xb8, 0xaa, 0xbf, 0x83, 0xfc, 0x07, 0x34, 0x96, 0x4c, 0xcd, 0x41, 0x1d, + 0x1c, 0x93, 0x57, 0x14, 0xe2, 0x4a, 0xab, 0x56, 0x6f, 0x4f, 0x08, 0x42, 0x40, 0x14, + 0xc4, 0xec, 0xa9, 0x1b, 0x59, 0x0f, 0x08, 0x2b, 0x47, 0x3f, 0x36, 0x1c, 0x87, 0x41, + 0x5d, 0x37, 0xbd, 0x20, 0xd7, 0x0f, 0xd0, 0xb5, 0x2b, 0x6d, 0xdf, 0x18, 0x65, 0xf7, + 0x66, 0x70, 0x2e, 0x32, 0xb0, 0x5b, 0x3c, 0xf1, 0x63, 0x0e, 0xe8, 0x59, 0x7a, 0xae, + 0x19, 0x63, 0x3f, 0x35, 0x16, 0xa8, 0x55, 0x5a, 0xc5, 0xbe, 0x32, 0xc6, 0x75, 0xbe, + 0x18, 0x17, 0xef, 0xbf, 0xfd, 0x93, 0x69, 0x04, 0x1a, 0x08, 0x9c, 0x28, 0x3f, 0x19, + 0x64, 0x99, 0x68, 0xc2, 0x49, 0x8c, 0xde, 0x56, 0xf5, 0x00, 0x43, 0x4f, 0x28, 0x0d, + 0x77, 0xa9, 0xc6, 0x2e, 0x43, 0xcb, 0xd3, 0xf1, 0x36, 0xa4, 0xc6, 0xa0, 0x0a, 0x43, + 0xe6, 0xed, 0x53, 0x0c, 0xb2, 0xe8, 0xae, 0x83, 0x88, 0x60, 0xad, 0xc8, 0x8a, 0xac, + 0xc7, 0xbd, 0x6a, 0x00, 0xae, 0x0c, 0x19, 0xff, 0x45, 0x33, 0xa4, 0x85, 0xef, 0xde, + 0x08, 0x2b, 0x5f, 0x4d, 0x1f, 0x7a, 0x8e, 0xbe, 0x7e, 0xd8, 0x2b, 0x7b, 0x05, 0xa8, + 0xcf, 0xe1, 0xe3, 0x73, 0x45, 0x9f, 0x1b, 0xdc, 0xbf, 0x95, 0x25, 0x74, 0x7e, 0x8c, + 0x95, 0x08, 0xa5, 0x55, 0xfa, 0xcb, 0x79, 0x87, 0x40, 0xe0, 0xbd, 0xf9, 0x94, 0xd9, + 0x73, 0x9b, 0xbe, 0x55, 0x38, 0xa0, 0xae, 0x0f, 0x07, 0x6c, 0x58, 0x2c, 0x0f, 0x5b, + 0xa8, 0x78, 0xb9, 0x9b, 0x82, 0x49, 0xdb, 0x1d, 0x7e, 0x95, 0x05, 0x6c, 0x98, 0xaf, + 0x08, 0x3d, 0x98, 0xcb, 0x0e, 0xd9, 0xe3, 0xf7, 0x43, 0x6e, 0x1c, 0x76, 0x43, 0x76, + 0x6f, 0x96, 0x6b, 0x83, 0xe9, 0x99, 0x20, 0x6e, 0xbd, 0x13, 0x93, 0xb9, 0xb2, 0xa7, + 0xf4, 0x14, 0x48, 0x0f, 0xa0, 0x17, 0x48, 0x00, 0x69, 0xf8, 0x5c, 0x77, 0x49, 0xc4, + 0x35, 0xae, 0x2f, 0xba, 0x2d, 0xdc, 0x10, 0x38, 0xd5, 0x47, 0xd8, 0x48, 0x54, 0x81, + 0x7e, 0xf3, 0x96, 0x35, 0xc2, 0x98, 0x27, 0xaa, 0xd8, 0x67, 0x26, 0xc9, 0xad, 0xe3, + 0xb2, 0x65, 0xb9, 0x08, 0x6c, 0x8b, 0x5b, 0x75, 0xef, 0x56, 0xfe, 0x4b, 0xd8, 0xb4, + 0xd6, 0x28, 0x93, 0x89, 0x5b, 0x3f, 0xd2, 0x73, 0x4f, 0xda, 0xc4, 0x64, 0x15, 0x6d, + 0x7e, 0x5e, 0xbc, 0x7e, 0xcf, 0x1d, 0x83, 0xb8, 0x6f, 0x65, 0x96, 0x37, 0xe3, 0xb1, + 0x42, 0xc1, 0x64, 0x96, 0x3b, 0x8c, 0xdc, 0xf4, 0xba, 0x4f, 0x40, 0x35, 0xdf, 0xfc, + 0x5a, 0x78, 0x94, 0x58, 0x84, 0x77, 0x81, 0x91, 0x8a, 0xc7, 0x2f, 0xc1, 0x8b, 0xbb, + 0xf5, 0x11, 0x00, 0x32, 0xe6, 0x6d, 0x75, 0xb3, 0x17, 0x1e, 0xf4, 0xb5, 0x13, 0x29, + 0x01, 0x64, 0xa7, 0x7b, 0x42, 0xb0, 0xa4, 0xcf, 0xb8, 0x96, 0x39, 0xab, 0x23, 0x84, + 0x5e, 0x1a, 0xa2, 0xa4, 0x52, 0xf3, 0x73, 0x1c, 0x8c, 0xb6, 0x50, 0x82, 0xa6, 0x22, + 0xa7, 0xc2, 0xe0, 0x01, 0x3e, 0xa4, 0x7d, 0x0b, 0xdd, 0x42, 0xd6, 0x99, 0x04, 0x66, + 0x64, 0x9a, 0x90, 0x5c, 0x68, 0x4c, 0x32, 0x51, 0x71, 0x6d, 0x61, 0xf7, 0x60, 0xd5, + 0x3d, 0xe6, 0xe3, 0xf7, 0x90, 0xfb, 0xa7, 0xf5, 0xf1, 0xf4, 0xde, 0x26, 0x71, 0x13, + 0xbd, 0xfc, 0xd7, 0x42, 0x28, 0x22, 0x33, 0x0b, 0x32, 0xd5, 0x8e, 0x67, 0x77, 0x76, + 0x5f, 0x22, 0xa4, 0x11, 0x63, 0x44, 0xee, 0xb6, 0x5b, 0x2e, 0xc5, 0x16, 0x39, 0x3a, + 0xb3, 0x75, 0x1b, 0x53, 0x56, 0xd2, 0xb0, 0xc9, 0x50, 0x0c, 0x0f, 0x3e, 0x46, 0x91, + 0x81, 0x03, 0x5b, 0xc3, 0x66, 0x0f, 0x0b, 0x8f, 0x9f, 0xbe, 0x6e, 0x40, 0xb5, 0xe8, + 0x9c, 0xb7, 0x9b, 0x06, 0x37, 0x14, 0xca, 0x75, 0xe7, 0x2e, 0x2e, 0x10, 0x0a, 0x10, + 0xd6, 0x3b, 0xf7, 0x84, 0xdf, 0x08, 0x20, 0xef, 0x25, 0xf8, 0xef, 0x40, 0xfe, 0x5f, + 0x05, 0xfb, 0x95, 0x68, 0x3f, 0x91, 0x05, 0xff, 0x3c, 0xb2, 0xd2, 0x19, 0xab, 0x76, + 0x60, 0x5a, 0x06, 0x4f, 0x69, 0x21, 0x9f, 0x1d, 0xc0, 0xd0, 0x0b, 0x3b, 0x48, 0x64, + 0x2f, 0x97, 0x0d, 0xc0, 0x0c, 0xca, 0x4b, 0x8b, 0x43, 0x30, 0x8b, 0xe1, 0x82, 0x86, + 0xec, 0x5a, 0x42, 0x88, 0xd6, 0x00, 0xa3, 0x78, 0x5c, 0xb6, 0x22, 0xd4, 0x68, 0xa4, + 0xc6, 0x96, 0x9b, 0x37, 0x92, 0xf2, 0x48, 0x50, 0x27, 0xd0, 0xad, 0x9a, 0xa4, 0xa9, + 0xc2, 0xcc, 0x97, 0x2f, 0x9e, 0xe5, 0x19, 0x0a, 0x95, 0xb1, 0xeb, 0x05, 0x8d, 0xdd, + 0xd8, 0xc0, 0x8e, 0x7d, 0x75, 0x3f, 0x5e, 0x01, 0x1b, 0x2b, 0xcf, 0xee, 0x1d, 0x52, + 0xc1, 0xc4, 0xf2, 0xca, 0xcd, 0xa3, 0x0b, 0xdb, 0x69, 0x30, 0x65, 0x3c, 0x0c, 0xc4, + 0x48, 0x6e, 0x60, 0xe8, 0x9f, 0xa8, 0x49, 0xb3, 0x20, 0x83, 0xba, 0x9d, 0xb4, 0x53, + 0xfb, 0x8d, 0xf6, 0x83, 0xcd, 0x68, 0x75, 0x4c, 0x87, 0xda, 0xa7, 0x31, 0xf5, 0x70, + 0xa7, 0xa4, 0x06, 0x0a, 0xf0, 0xce, 0x70, 0x0d, 0x31, 0xbc, 0xa7, 0xe7, 0x4b, 0x3e, + 0x3b, 0xa3, 0xd0, 0xe8, 0xa6, 0x39, 0x2a, 0x06, 0x2b, 0x8e, 0x86, 0xd9, 0xd7, 0xd0, + 0x0b, 0x21, 0x70, 0x1e, 0x7b, 0x06, 0x2e, 0x06, 0xb1, 0xbc, 0xd8, 0x2a, 0x01, 0xd3, + 0x75, 0x62, 0x6f, 0xbf, 0x87, 0x2d, 0x27, 0xfa, 0x45, 0x11, 0xf5, 0xf8, 0xcf, 0x8c, + 0x9a, 0xbc, 0xef, 0x2a, 0x99, 0x01, 0x76, 0xae, 0x33, 0x93, 0x25, 0xd5, 0xa5, 0x88, + 0xda, 0x57, 0x96, 0xfa, 0xae, 0x5b, 0xab, 0x7c, 0x82, 0x97, 0x7c, 0x0f, 0xf7, 0x97, + 0x09, 0x3e, 0x2c, 0x1f, 0x3a, 0xe8, 0x55, 0xf6, 0x5a, 0xea, 0x91, 0xe1, 0x31, 0x2f, + 0xc6, 0xb8, 0xa4, 0x35, 0x1a, 0x2e, 0xc0, 0x3e, 0x02, 0xe5, 0xd0, 0x2f, 0x53, 0x35, + 0x4b, 0x05, 0x2f, 0xd3, 0xda, 0x0d, 0xff, 0x82, 0xcd, 0x1f, 0x55, 0xeb, 0xca, 0x57, + 0xb6, 0x33, 0x7c, 0x85, 0x93, 0x8a, 0x79, 0x81, 0x3d, 0x20, 0x21, 0xd6, 0x09, 0x4c, + 0x68, 0xb3, 0x75, 0xe9, 0x84, 0xf6, 0x83, 0x93, 0x30, 0x08, 0x71, 0xe3, 0x48, 0xfc, + 0x52, 0x36, 0xcc, 0xa6, 0x33, 0x05, 0x44, 0xe5, 0x46, 0x39, 0xb5, 0x41, 0x87, 0x01, + 0xff, 0x4c, 0xc4, 0x5a, 0x31, 0xf6, 0x2e, 0xdd, 0x84, 0x3d, 0xbb, 0xdc, 0x5a, 0xa7, + 0x27, 0xab, 0x79, 0xb4, 0x42, 0x68, 0x3c, 0x49, 0x56, 0xbb, 0xb1, 0x95, 0xa4, 0xfa, + 0x66, 0xdc, 0x9c, 0xd5, 0x42, 0xc7, 0x6b, 0x91, 0x50, 0xc8, 0x4b, 0xf8, 0x90, 0x78, + 0x99, 0x42, 0xf5, 0x5c, 0x20, 0x0b, 0x77, 0x3e, 0xcd, 0xd7, 0x99, 0x2c, 0xff, 0x3e, + 0xca, 0x24, 0xde, 0x3e, 0x09, 0x84, 0xe1, 0x0e, 0x68, 0xae, 0x38, 0x75, 0x34, 0xb9, + 0x6c, 0xde, 0x37, 0x92, 0xf1, 0x35, 0xbf, 0x5f, 0x68, 0x78, 0x7d, 0x37, 0x0c, 0xa8, + 0xc4, 0xc4, 0x07, 0x4d, 0xc5, 0xd6, 0x01, 0xae, 0x90, 0x49, 0x54, 0x37, 0xc3, 0xc2, + 0xd4, 0x8a, 0x3d, 0x96, 0x66, 0x83, 0xac, 0x05, 0x16, 0x0b, 0x7a, 0x84, 0xea, 0xa7, + 0xaa, 0xb7, 0x40, 0x09, 0xe5, 0x7a, 0x85, 0xf7, 0xbf, 0x68, 0xa2, 0xe4, 0x82, 0x00, + 0x0f, 0x82, 0x9c, 0x54, 0x50, 0x73, 0xa1, 0x5d, 0x5c, 0xd0, 0xfc, 0xc5, 0x74, 0x39, + 0xa4, 0x35, 0x0e, 0xaf, 0x09, 0x8d, 0xfb, 0x82, 0xa0, 0x85, 0xea, 0x8a, 0x4a, 0xf6, + 0xfa, 0x83, 0x81, 0xf0, 0x65, 0x88, 0x19, 0xea, 0xb4, 0x83, 0xf6, 0x5b, 0x32, 0x5d, + 0x5a, 0xed, 0xa1, 0x52, 0x32, 0xcf, 0xad, 0xec, 0x75, 0xab, 0x18, 0x66, 0xe4, 0xc0, + 0x15, 0x5a, 0x9c, 0x74, 0xa7, 0xa5, 0x7c, 0xcf, 0x34, 0xc4, 0x83, 0xac, 0x7d, 0xa1, + 0x58, 0x8a, 0x1b, 0x6b, 0x99, 0x41, 0xf1, 0x10, 0x40, 0xf9, 0x4c, 0xf7, 0x8f, 0xad, + 0x89, 0xbf, 0x11, 0xfe, 0xd6, 0x9a, 0xa0, 0xd8, 0x31, 0x05, 0xad, 0xac, 0xdd, 0x4e, + 0x5f, 0x04, 0xa6, 0x24, 0x24, 0x02, 0x3c, 0x9b, 0x9e, 0x33, 0xc4, 0xfb, 0x7f, 0x12, + 0xbd, 0xf2, 0x1f, 0x07, 0xf2, 0x65, 0xc5, 0x37, 0xd5, 0x1c, 0x65, 0x51, 0xf4, 0x61, + 0x7b, 0x91, 0x5d, 0x21, 0x99, 0x18, 0x39, 0xc3, 0xd0, 0xd3, 0x63, 0x93, 0xd6, 0x46, + 0xe0, 0xa8, 0xa4, 0x15, 0x09, 0x21, 0x7d, 0x0e, 0x7d, 0x2c, 0xa1, 0xa0, 0xa0, 0xd6, + 0x77, 0xa3, 0xea, 0xca, 0x23, 0xed, 0xeb, 0x07, 0xb7, 0x4e, 0x65, 0x2a, 0x0b, 0xc5, + 0x0c, 0x6c, 0x08, 0x3a, 0x55, 0xd6, 0xc7, 0x30, 0x6e, 0x74, 0x08, 0x6f, 0x47, 0x68, + 0x93, 0x3a, 0xa2, 0x48, 0x73, 0x68, 0x18, 0x67, 0xa7, 0x89, 0x3d, 0x77, 0xcb, 0x7f, + 0x29, 0xb8, 0xc8, 0x47, 0xc5, 0x83, 0xf2, 0xd0, 0x71, 0xa6, 0x86, 0x61, 0x6e, 0x20, + 0x67, 0x19, 0xf7, 0x61, 0xae, 0x39, 0xc1, 0x10, 0x44, 0x2e, 0x06, 0x16, 0x3d, 0x2b, + 0x84, 0x59, 0x03, 0x60, 0x69, 0x5d, 0x4e, 0x19, 0x84, 0x9e, 0x63, 0x4f, 0x24, 0xd9, + 0xad, 0x39, 0x6c, 0x19, 0xff, 0x83, 0xce, 0x74, 0xf4, 0x6e, 0x64, 0x5f, 0x93, 0x2e, + 0x14, 0x1a, 0x41, 0x19, 0x59, 0x36, 0xc8, 0x5d, 0x51, 0x44, 0x14, 0xf1, 0x12, 0xe6, + 0x0b, 0x1a, 0x25, 0x37, 0xc3, 0x8d, 0x6d, 0xc6, 0xc4, 0x63, 0x83, 0x05, 0xc9, 0xbd, + 0x6c, 0x62, 0xe3, 0x66, 0xbc, 0x63, 0x12, 0x3e, 0x3e, 0x6d, 0xd3, 0x6e, 0xed, 0xd3, + 0x13, 0x6f, 0xce, 0x8d, 0xee, 0xca, 0x2a, 0xa0, 0x9a, 0x32, 0x98, 0xa3, 0x9d, 0x83, + 0x85, 0x9e, 0xfc, 0x9b, 0x2b, 0x69, 0xcf, 0x9a, 0x7d, 0xee, 0x08, 0xa9, 0x8e, 0x4b, + 0xe5, 0x58, 0xac, 0x79, 0x12, 0xfd, 0xcb, 0x42, 0x20, 0x90, 0x75, 0x42, 0x02, 0x60, + 0xf7, 0xca, 0xd0, 0xf2, 0xc0, 0x1f, 0x2a, 0xfe, 0x33, 0x07, 0x3f, 0x26, 0x24, 0x9d, + 0x94, 0x4f, 0x7a, 0x50, 0xdd, 0x84, 0x83, 0x9b, 0xc3, 0xea, 0x7f, 0xde, 0xe4, 0xed, + 0x71, 0x44, 0x9c, 0xf0, 0x75, 0x33, 0xd2, 0x6e, 0x1e, 0x27, 0xa3, 0xef, 0xb0, 0x32, + 0xc3, 0xa3, 0xb3, 0x4b, 0xd3, 0x09, 0x26, 0x22, 0xd2, 0x06, 0x2a, 0xe5, 0x36, 0xef, + 0x51, 0x49, 0xc4, 0x9b, 0x5b, 0xc9, 0x47, 0x5e, 0xaf, 0xab, 0x6e, 0x67, 0x57, 0x61, + 0x00, 0x8b, 0x0d, 0xad, 0xde, 0xec, 0xaa, 0x60, 0x44, 0x70, 0xbb, 0xe0, 0xfa, 0xda, + 0x25, 0x5d, 0x29, 0x0e, 0x92, 0xb1, 0x90, 0xc2, 0xc2, 0xd8, 0xc2, 0xde, 0xe5, 0x45, + 0x5d, 0x1f, 0xa9, 0xa9, 0xf3, 0xdb, 0x77, 0x79, 0xb5, 0x84, 0x64, 0x34, 0x64, 0xaa, + 0x80, 0x14, 0xba, 0x66, 0x99, 0x4d, 0xe2, 0x55, 0x17, 0xf8, 0x39, 0x80, 0xe6, 0x6e, + 0xe4, 0xf6, 0x23, 0x14, 0xae, 0x6d, 0xbe, 0xf4, 0x52, 0xd5, 0xd3, 0x8b, 0x0a, 0x16, + 0xf3, 0x99, 0x1f, 0x36, 0xd8, 0xa8, 0xb3, 0x9d, 0xdc, 0x0d, 0x55, 0x95, 0xee, 0xd9, + 0x87, 0x62, 0x87, 0x8c, 0xdf, 0x3f, 0x4a, 0x2e, 0xdc, 0x5c, 0xda, 0x77, 0xd5, 0xfe, + 0x4f, 0xaf, 0x63, 0xa1, 0x5f, 0x56, 0x8a, 0x54, 0x0d, 0xa5, 0x7d, 0xd9, 0xbe, 0xb6, + 0xfb, 0x1a, 0x97, 0x7c, 0xcb, 0x91, 0xb4, 0xd7, 0x9c, 0xb3, 0x9b, 0x28, 0x91, 0x1a, + 0x29, 0xe7, 0xbf, 0x02, 0x8a, 0xc6, 0x10, 0x37, 0x96, 0xdf, 0xb6, 0xb2, 0x09, 0x67, + 0x23, 0x9a, 0xd3, 0x73, 0xc3, 0x8c, 0x53, 0xf6, 0xdf, 0x18, 0x23, 0xd4, 0x95, 0x0a, + 0x02, 0x83, 0xe9, 0x9b, 0x9c, 0x06, 0xab, 0x29, 0x66, 0x66, 0x7c, 0x9d, 0xf6, 0x77, + 0x71, 0x6b, 0x0c, 0xad, 0xed, 0x81, 0x8d, 0xf9, 0xe4, 0x49, 0xc0, 0x72, 0xe2, 0x2f, + 0x9d, 0x98, 0xbb, 0x0f, 0x9b, 0x03, 0xbd, 0x5f, 0xd0, 0x13, 0xfc, 0xef, 0x3e, 0xd6, + 0xa4, 0x9a, 0xeb, 0x98, 0x72, 0x02, 0x54, 0x08, 0x7e, 0xf7, 0x28, 0xe3, 0x19, 0x47, + 0xff, 0xe8, 0xf7, 0x66, 0xe6, 0x3e, 0xe4, 0x6f, 0xf2, 0x08, 0x16, 0xd5, 0xfa, 0x8f, + 0xf5, 0x5a, 0x26, 0x39, 0x89, 0x61, 0x49, 0x0a, 0xb9, 0xae, 0x36, 0x6f, 0xc5, 0xa2, + 0xd1, 0x99, 0x6e, 0xd6, 0x93, 0xcc, 0xca, 0x82, 0x35, 0x6f, 0x60, 0x0a, 0xb0, 0x99, + 0xf6, 0xec, 0xa8, 0xbf, 0xe6, 0x45, 0x27, 0x0d, 0x3f, 0x95, 0xed, 0xba, 0x5b, 0x0d, + 0xe7, 0xa3, 0x28, 0x19, 0x23, 0x3b, 0xcc, 0x75, 0x4a, 0x5c, 0xe2, 0xe5, 0xea, 0x07, + 0x84, 0x2e, 0x5f, 0xf2, 0xce, 0xbe, 0x62, 0xad, 0x76, 0xe8, 0xef, 0xf8, 0xd1, 0x5e, + 0xa4, 0xc2, 0x4a, 0x5f, 0x20, 0x78, 0x68, 0x31, 0x9a, 0x5a, 0xf6, 0xb0, 0x35, 0xbe, + 0x3f, 0x44, 0xf4, 0x34, 0x09, 0x4f, 0x6e, 0x52, 0x5b, 0xe6, 0x14, 0xda, 0xc9, 0x20, + 0xa3, 0x30, 0xbd, 0xfb, 0x26, 0xd7, 0x5f, 0xe7, 0xb4, 0xb3, 0x65, 0xd0, 0x94, 0x45, + 0x92, 0x50, 0xaa, 0xa5, 0x54, 0x44, 0x89, 0xfb, 0x1d, 0x99, 0x25, 0x81, 0x80, 0x0a, + 0x77, 0xb8, 0x91, 0x21, 0x57, 0xfc, 0x97, 0x13, 0xaa, 0xac, 0x25, 0xb4, 0xc2, 0x6e, + 0xb0, 0x3f, 0x71, 0x66, 0x46, 0x61, 0x9a, 0xf0, 0x24, 0x56, 0xae, 0x69, 0x59, 0x62, + 0xfe, 0x5e, 0x93, 0x1a, 0x63, 0xb5, 0xc7, 0x90, 0x52, 0xec, 0xd3, 0x33, 0xe1, 0x84, + 0x12, 0xdb, 0x91, 0xe1, 0x5f, 0x7c, 0xbc, 0x70, 0xb4, 0xcd, 0x7e, 0x8e, 0x3c, 0x95, + 0x1f, 0x35, 0x85, 0x72, 0xe3, 0x77, 0x67, 0xe7, 0xd5, 0x27, 0x04, 0xa6, 0x72, 0x1b, + 0x30, 0xef, 0xc4, 0x10, 0x17, 0xae, 0x4d, 0x23, 0x15, 0x58, 0xc5, 0xc8, 0x2c, 0xc7, + 0xdd, 0x7e, 0x33, 0x56, 0xc0, 0x9d, 0xc2, 0x49, 0x06, 0xf0, 0x43, 0x8d, 0xfc, 0xc3, + 0x00, 0x85, 0x6a, 0xc2, 0xce, 0xd8, 0xf7, 0x7f, 0xa8, 0x01, 0x57, 0x36, 0xc6, 0x61, + 0xe8, 0x02, 0x48, 0xae, 0xeb, 0x77, 0x48, 0x74, 0xaa, 0x79, 0xd2, 0x90, 0xb8, 0xf5, + 0x02, 0x7a, 0x0a, 0x50, 0x95, 0x37, 0xfc, 0x7c, 0x68, 0x9b, 0x7a, 0xd8, 0x61, 0x16, + 0xcf, 0xec, 0x26, 0x47, 0xcc, 0xaa, 0xe1, 0xc7, 0x4b, 0x41, 0x6f, 0x3e, 0x6a, 0xe8, + 0xf7, 0xcc, 0x60, 0xea, 0xaf, 0x7b, 0x6a, 0x59, 0x0d, 0x51, 0x54, 0x41, 0x38, 0xe1, + 0x73, 0x29, 0x45, 0x60, 0x3a, 0x53, 0x46, 0x2c, 0x60, 0xe1, 0xf6, 0xcb, 0x0c, 0x9c, + 0xa0, 0x39, 0x0c, 0x48, 0x82, 0x24, 0xc3, 0x13, 0x26, 0x9f, 0xcd, 0x59, 0xfc, 0xb6, + 0x11, 0xfb, 0x2d, 0x9b, 0x4c, 0x8f, 0xa6, 0x01, 0xbb, 0x1c, 0xb8, 0xd0, 0x7d, 0x79, + 0x7b, 0xf5, 0xde, 0x52, 0xbc, 0xee, 0xb0, 0x23, 0x01, 0xc8, 0x96, 0x2a, 0xc1, 0xfc, + 0x04, 0x91, 0xdc, 0x81, 0xaf, 0xfd, 0x6c, 0x1e, 0xbf, 0x89, 0xa1, 0x3d, 0x6f, 0x29, + 0x0e, 0xda, 0x5d, 0x5c, 0xef, 0x38, 0x22, 0x15, 0xc5, 0xe9, 0x51, 0xd7, 0x13, 0x05, + 0xef, 0x33, 0xd9, 0x73, 0x71, 0x26, 0xd0, 0xe6, 0x62, 0x90, 0x5f, 0x12, 0x50, 0x92, + 0x6f, 0x6a, 0x22, 0x99, 0x90, 0xe3, 0x8f, 0x69, 0xad, 0x9a, 0x91, 0x92, 0xb3, 0x02, + 0xf2, 0x6b, 0xdd, 0xa4, 0x65, 0xd9, 0x0b, 0x94, 0xb1, 0x2c, 0x57, 0xfa, 0x3f, 0xd6, + 0x93, 0x00, 0x83, 0xf1, 0x84, 0x43, 0x8d, 0x8a, 0x88, 0x9d, 0x3f, 0x5e, 0xce, 0xa2, + 0xc6, 0xd2, 0x3d, 0x67, 0x36, 0xf2, 0xa0, 0xf1, 0x8e, 0x26, 0xf4, 0xfa, 0x45, 0xd1, + 0xbe, 0x8f, 0x3d, 0xc4, 0xa7, 0x07, 0x13, 0x7e, 0x95, 0xd2, 0xad, 0x59, 0x4f, 0x6c, + 0x03, 0xd2, 0x49, 0x23, 0x06, 0x7a, 0xe4, 0x7f, 0xd6, 0x42, 0x5e, 0xfb, 0x9c, 0x1d, + 0x50, 0x4e, 0x6f, 0xd5, 0x57, 0x53, 0x40, 0x94, 0x56, 0x01, 0xfe, 0x80, 0x6f, 0x57, + 0x56, 0xac, 0xb5, 0x62, 0xf1, 0x3c, 0x0c, 0xa1, 0xd8, 0x03, 0xa1, 0x95, 0xc2, 0xeb, + 0xb2, 0xef, 0x02, 0xac, 0x33, 0xe6, 0xa8, 0x8d, 0xea, 0x07, 0x5b, 0xa9, 0x96, 0xd3, + 0xc3, 0x36, 0x64, 0x8e, 0x86, 0x94, 0xd3, 0xa1, 0x9d, 0x3d, 0xca, 0x53, 0x1b, 0xeb, + 0x50, 0xd4, 0x32, 0x7c, 0x5c, 0x0c, 0x23, 0xcb, 0x7c, 0xfd, 0xb0, 0x8c, 0xa7, 0xcf, + 0x2c, 0xac, 0x6b, 0xc1, 0x39, 0xd0, 0x74, 0x14, 0x73, 0xd3, 0x76, 0x02, 0x9c, 0xb4, + 0xab, 0x6b, 0xf0, 0x54, 0x55, 0x7c, 0xe2, 0x94, 0xc7, 0x28, 0xa4, 0x68, 0x7d, 0x57, + 0xec, 0x89, 0x09, 0xff, 0x51, 0xa4, 0xd0, 0x2f, 0x9d, 0xcd, 0x11, 0x19, 0x3d, 0x7d, + 0x1c, 0x9f, 0xda, 0xe6, 0xa1, 0x73, 0x96, 0xa1, 0xbf, 0x57, 0xa9, 0x94, 0x93, 0x4f, + 0x5e, 0x7a, 0x59, 0xf0, 0x45, 0xde, 0xbe, 0xaf, 0xf6, 0x2e, 0xf3, 0x26, 0xb9, 0x47, + 0xf2, 0xa8, 0xb4, 0x95, 0x55, 0xe4, 0xd9, 0x9b, 0x3b, 0xf5, 0xc8, 0x1f, 0xf9, 0xfe, + 0x31, 0x4e, 0x04, 0x7a, 0xf1, 0x52, 0x50, 0x8f, 0x57, 0x01, 0x5c, 0xa4, 0x02, 0xc6, + 0x7d, 0x92, 0x5c, 0x99, 0xac, 0xea, 0x3e, 0xe8, 0xcc, 0x4b, 0x00, 0x8c, 0x5c, 0xb4, + 0x39, 0x66, 0xe7, 0x14, 0xef, 0x48, 0x0f, 0xd0, 0x5e, 0x07, 0xc7, 0xb2, 0xdd, 0xa9, + 0xaa, 0x39, 0x66, 0x11, 0x3e, 0xaa, 0x29, 0x3d, 0x3f, 0x62, 0x2b, 0x30, 0x9d, 0x64, + 0x80, 0x3c, 0xe1, 0xe6, 0x37, 0x8b, 0x6a, 0xac, 0x4f, 0xab, 0x52, 0x7c, 0x43, 0xcd, + 0x45, 0xed, 0x0a, 0x3c, 0x1a, 0x4b, 0x9f, 0xb1, 0x8d, 0xcc, 0xcf, 0xcd, 0xb6, 0xac, + 0x0c, 0x24, 0x21, 0x63, 0x9c, 0xda, 0x00, 0x75, 0xa2, 0x0d, 0xc5, 0x11, 0x1b, 0x8d, + 0x3d, 0x31, 0x99, 0x49, 0x5b, 0xd9, 0x13, 0x3d, 0xba, 0xb9, 0x45, 0x41, 0x41, 0x0e, + 0x4f, 0xba, 0x92, 0xc7, 0xb6, 0x06, 0xa5, 0xcb, 0x12, 0x2f, 0x14, 0x0c, 0xf1, 0xa3, + 0x59, 0x6f, 0x27, 0x88, 0xf3, 0xc8, 0xb9, 0x26, 0x60, 0xf1, 0x4c, 0xb6, 0x5a, 0xf5, + 0xdd, 0x23, 0xdf, 0xdb, 0xac, 0x13, 0x71, 0xec, 0xf4, 0xb3, 0x37, 0x12, 0xfe, 0xd2, + 0x29, 0x2c, 0x44, 0xf7, 0x08, 0x34, 0xcf, 0x96, 0xc0, 0x5d, 0x58, 0x82, 0x7e, 0x69, + 0xbf, 0xc2, 0xe6, 0x96, 0xfa, 0x08, 0x74, 0x86, 0x9c, 0x02, 0xf3, 0xdc, 0xa1, 0x1c, + 0x3b, 0x90, 0xcb, 0x21, 0x4e, 0x68, 0xbc, 0x1c, 0xae, 0x03, 0x9d, 0x7a, 0x14, 0x6c, + 0xdc, 0x1d, 0x60, 0x9d, 0x7a, 0x6b, 0x3f, 0xd5, 0xd4, 0x61, 0xb0, 0x95, 0x1c, 0x82, + 0xcf, 0xb3, 0xe7, 0x63, 0xfa, 0xd2, 0xd1, 0xbc, 0x76, 0x78, 0xcd, 0xf8, 0x27, 0x79, + 0xf8, 0xfd, 0x5a, 0x1c, 0xe2, 0x2a, 0x8d, 0x3c, 0x45, 0x47, 0xab, 0xd9, 0x59, 0x83, + 0x8a, 0x46, 0xfb, 0x80, 0xaf, 0xe0, 0x1f, 0x8e, 0xcc, 0x99, 0x31, 0x51, 0x3b, 0x19, + 0x62, 0xec, 0x54, 0x08, 0x56, 0xcb, 0x18, 0x93, 0x87, 0xcf, 0xbf, 0xcc, 0x0f, 0x7c, + 0x68, 0x22, 0x3c, 0xba, 0x47, 0xfb, 0x0c, 0x9b, 0x48, 0x6e, 0x4d, 0x99, 0x17, 0x19, + 0x41, 0xf7, 0x67, 0x5a, 0x8b, 0x46, 0x32, 0x8a, 0x3b, 0xc1, 0x09, 0xbf, 0x07, 0xc6, + 0x6d, 0x5e, 0xde, 0x77, 0x1c, 0xc4, 0xc7, 0x4c, 0xe8, 0x03, 0x33, 0x82, 0x91, 0x91, + 0xee, 0xdc, 0x49, 0x35, 0x08, 0xa6, 0x44, 0x53, 0x0a, 0x61, 0x44, 0xf2, 0x2d, 0xcf, + 0x97, 0x52, 0x5a, 0x4c, 0xdc, 0xa1, 0xad, 0x71, 0x07, 0x3b, 0x08, 0x0b, 0x73, 0xea, + 0x45, 0x49, 0xf5, 0x40, 0x1b, 0xff, 0x43, 0x18, 0x26, 0x8e, 0x6a, 0xd6, 0x37, 0x36, + 0x31, 0x57, 0xa1, 0x9a, 0x53, 0xf1, 0x23, 0xa0, 0xb0, 0xe1, 0x6d, 0x0b, 0x77, 0xf0, + 0x20, 0x28, 0xda, 0x46, 0x41, 0x00, 0xfd, 0xe7, 0x6d, 0x83, 0xdd, 0x0b, 0xb2, 0x24, + 0xf7, 0xb5, 0x7a, 0x00, 0xc0, 0x2f, 0x68, 0xae, 0x64, 0x8f, 0xdc, 0x52, 0x99, 0x57, + 0xa1, 0x04, 0x90, 0xdc, 0xe1, 0xfd, 0xdb, 0xb0, 0x90, 0x4f, 0x0d, 0x51, 0x8b, 0xb3, + 0x87, 0x54, 0x40, 0x19, 0x98, 0x3b, 0x61, 0x69, 0x75, 0xa7, 0x8e, 0x74, 0xd8, 0x54, + 0xfd, 0xdc, 0x49, 0xb2, 0x55, 0x16, 0x7b, 0x55, 0xef, 0x4b, 0xee, 0x46, 0x56, 0x68, + 0xb2, 0x0e, 0xa4, 0x11, 0x8c, 0xa5, 0x69, 0xae, 0x48, 0x0e, 0x0f, 0x6e, 0x5e, 0x04, + 0x3a, 0x35, 0x7b, 0x36, 0xd3, 0xab, 0x36, 0xc8, 0x61, 0xf2, 0x27, 0x83, 0x01, 0xdc, + 0xe5, 0x76, 0x74, 0xd5, 0x07, 0x3b, 0x3a, 0x6f, 0x51, 0x03, 0xa0, 0x79, 0x3a, 0xf1, + 0xb7, 0xd4, 0x6f, 0x95, 0x7e, 0x22, 0xd8, 0xd2, 0x58, 0x3b, 0xf1, 0x81, 0x83, 0x6c, + 0x3b, 0xe9, 0x93, 0x0b, 0xac, 0x8f, 0xa4, 0x60, 0xe9, 0x68, 0xaa, 0x71, 0x09, 0x87, + 0x0b, 0xbe, 0xd1, 0x7d, 0xf5, 0xf8, 0x88, 0xc8, 0xca, 0x14, 0x67, 0xae, 0x17, 0xdb, + 0xbc, 0xde, 0x31, 0xc1, 0x10, 0x5c, 0xb5, 0xbd, 0xa8, 0x8a, 0xc6, 0xc6, 0x27, 0x00, + 0x2c, 0xe2, 0x1c, 0x02, 0x14, 0x0f, 0xfe, 0x81, 0xec, 0x58, 0xbf, 0x1e, 0x6d, 0x1b, + 0xb7, 0xaa, 0xad, 0xa4, 0x1f, 0xba, 0x0b, 0xb5, 0x88, 0x77, 0x8a, 0x7f, 0x65, 0x20, + 0x2a, 0xd8, 0x11, 0xea, 0x73, 0xd2, 0x6c, 0x74, 0x55, 0x03, 0x95, 0xaf, 0xf7, 0x53, + 0x25, 0x10, 0x7c, 0x9b, 0x3f, 0x9a, 0xe9, 0xdc, 0xdc, 0xd8, 0x6e, 0xd0, 0x81, 0xa2, + 0xe7, 0x42, 0x47, 0x19, 0xa3, 0xd1, 0x85, 0xb7, 0xe0, 0xa4, 0x3a, 0x47, 0x2e, 0x29, + 0x8a, 0xc0, 0xaf, 0xdc, 0x52, 0x87, 0xd7, 0xad, 0x12, 0x4c, 0xd9, 0x40, 0x5a, 0x62, + 0xcd, 0x1c, 0xa0, 0x8b, 0x28, 0x2e, 0xfe, 0xf7, 0xf9, 0x28, 0xdf, 0x76, 0xe2, 0x82, + 0x1a, 0x41, 0x84, 0x13, 0xeb, 0x7c, 0xea, 0xa5, 0xff, 0x12, 0x90, 0xb0, 0x3e, 0xc9, + 0x1c, 0xe6, 0xdd, 0x28, 0x13, 0x0c, 0x3a, 0xb0, 0xb2, 0x3b, 0x60, 0x2b, 0xd5, 0xbe, + 0x5d, 0xc2, 0x60, 0x03, 0xaa, 0xe0, 0x4b, 0x33, 0xd7, 0xbd, 0x25, 0x90, 0xe9, 0x0c, + 0x8c, 0x38, 0x8e, 0xa7, 0x95, 0x51, 0x22, 0xdb, 0xac, 0xa6, 0x7b, 0x30, 0x39, 0x5a, + 0x92, 0x8b, 0x57, 0xb8, 0x57, 0x51, 0x23, 0x20, 0x5a, 0xe1, 0x91, 0x52, 0xe4, 0x1e, + 0x00, 0x29, 0x31, 0xb4, 0x57, 0x46, 0x19, 0x8e, 0x5d, 0xd9, 0x57, 0x1a, 0x56, 0xa7, + 0xe0, 0xd4, 0x23, 0xff, 0x27, 0x98, 0x9d, 0x3e, 0xb4, 0x17, 0xec, 0xd3, 0xc3, 0x09, + 0x3f, 0xb8, 0x2c, 0x56, 0x58, 0xe2, 0x96, 0x24, 0xc5, 0x32, 0x19, 0xa6, 0x0c, 0xd0, + 0xa8, 0xc4, 0xda, 0x36, 0x7e, 0x29, 0xa7, 0x17, 0x79, 0xa7, 0x30, 0x32, 0x98, 0x5a, + 0x3d, 0x1f, 0xd0, 0x3d, 0xd4, 0xd0, 0x6e, 0x05, 0x56, 0x6f, 0x3b, 0x84, 0x36, 0x7c, + 0xf0, 0xfa, 0xee, 0x9b, 0xc3, 0xbd, 0x7a, 0x3a, 0x60, 0x6a, 0x9f, 0xdb, 0x84, 0x9c, + 0x5d, 0x82, 0xd0, 0xa6, 0x19, 0x23, 0xc2, 0xe5, 0xd8, 0xaa, 0x63, 0xa8, 0xa5, 0x0c, + 0x38, 0xbd, 0x03, 0x87, 0x72, 0xc4, 0x14, 0x3d, 0x8b, 0x7a, 0xcf, 0xd7, 0x4e, 0x72, + 0xc0, 0x4d, 0x89, 0x24, 0x8d, 0xff, 0x20, 0xfe, 0x8d, 0xc5, 0xec, 0x21, 0x49, 0x05, + 0x4e, 0xa2, 0x41, 0x64, 0xe8, 0x5f, 0x67, 0x44, 0xad, 0x0c, 0xac, 0xf1, 0xa8, 0xb7, + 0x01, 0x26, 0xf4, 0x82, 0xc0, 0x92, 0xed, 0x9f, 0x61, 0x27, 0xd2, 0x05, 0x0d, 0x12, + 0xe8, 0x78, 0xa7, 0x96, 0x53, 0xa1, 0xe8, 0x4d, 0xae, 0xc3, 0xeb, 0xe6, 0x2d, 0x5f, + 0x6c, 0x4a, 0xbe, 0x5c, 0xe9, 0x0a, 0x7f, 0xe2, 0xe5, 0x2a, 0x8d, 0x78, 0x46, 0xe8, + 0xed, 0xf2, 0xf2, 0xbc, 0xe0, 0x5a, 0x03, 0x7c, 0x82, 0x6f, 0x22, 0xca, 0xad, 0x12, + 0x61, 0x46, 0x7d, 0xcf, 0xb7, 0xd6, 0xb6, 0x13, 0x3d, 0xc2, 0x1e, 0x80, 0x96, 0xc7, + 0xe9, 0xf8, 0xe9, 0xe1, 0x0c, 0x1e, 0x3f, 0xac, 0x40, 0x58, 0xb6, 0x82, 0xc6, 0x8e, + 0x54, 0xfa, 0xca, 0xe0, 0xf9, 0xc2, 0xdd, 0x4d, 0x64, 0xd9, 0x04, 0x61, 0x52, 0xb4, + 0x76, 0x23, 0x32, 0x93, 0x9f, 0x17, 0xe6, 0xaa, 0xf7, 0xd8, 0xb9, 0xd3, 0x58, 0xe2, + 0x21, 0x8d, 0x4e, 0x0d, 0x69, 0xa4, 0xf1, 0x19, 0xe1, 0xc6, 0x4e, 0xec, 0x4c, 0x8b, + 0x53, 0x28, 0x09, 0x70, 0x71, 0x31, 0xf0, 0x1f, 0x55, 0xc7, 0xad, 0x04, 0xcf, 0xb6, + 0x3f, 0x7c, 0x4a, 0x3d, 0x0a, 0x2b, 0x0f, 0xfb, 0x0b, 0x05, 0xa6, 0xbe, 0x05, 0x5b, + 0x8c, 0x94, 0xca, 0x80, 0xbb, 0x0a, 0x1d, 0x13, 0xcd, 0x4c, 0xd6, 0x9a, 0xb9, 0x83, + 0x04, 0xae, 0x25, 0x15, 0xd5, 0xf7, 0x69, 0x9d, 0x4a, 0xbe, 0xe5, 0xc2, 0x0b, 0xe6, + 0x09, 0xd8, 0x73, 0x51, 0x10, 0x12, 0xf2, 0x34, 0xbd, 0x85, 0xa7, 0xef, 0xf5, 0xfb, + 0x63, 0x4c, 0xff, 0x26, 0x58, 0xba, 0x65, 0x16, 0x04, 0x85, 0x63, 0x09, 0x5e, 0xce, + 0xfb, 0x30, 0x15, 0xee, 0x3f, 0x03, 0xca, 0x52, 0xa1, 0x77, 0xf2, 0x61, 0xec, 0xdc, + 0x26, 0xbc, 0x08, 0x9d, 0x34, 0xc6, 0x40, 0x48, 0x46, 0xe9, 0xc6, 0x47, 0xfc, 0xfe, + 0x98, 0xcc, 0x6a, 0xcd, 0xbb, 0x46, 0x4f, 0x64, 0x27, 0x8a, 0xd8, 0xce, 0x9d, 0x1a, + 0xe0, 0xd4, 0x15, 0xbc, 0x0c, 0x05, 0x24, 0x5f, 0xdd, 0xaf, 0x4e, 0xbc, 0x8d, 0xc7, + 0x03, 0xa8, 0x5c, 0xb2, 0x70, 0xf7, 0x96, 0xad, 0x2d, 0x93, 0x7e, 0x2a, 0xc0, 0xd5, + 0xe0, 0xa3, 0x48, 0x21, 0x75, 0x80, 0x00, 0xaa, 0x59, 0xc9, 0xd4, 0x65, 0x24, 0x85, + 0x29, 0x4e, 0xe0, 0xab, 0x29, 0x69, 0x6b, 0x21, 0x43, 0x0f, 0xa5, 0x4d, 0xcf, 0xbf, + 0x2b, 0x9c, 0x49, 0xd1, 0x42, 0x06, 0x42, 0x09, 0xee, 0xee, 0xd4, 0xd4, 0x71, 0xff, + 0xc0, 0x17, 0xd4, 0xe2, 0x0a, 0x79, 0x6b, 0x09, 0x27, 0x80, 0x4c, 0x06, 0x1b, 0x9f, + 0x4a, 0x70, 0x91, 0xfe, 0x01, 0x5a, 0xda, 0x68, 0xfd, 0x84, 0x42, 0xe0, 0x18, 0x25, + 0xc8, 0x8d, 0xfe, 0x55, 0xcf, 0x5d, 0xe3, 0x89, 0x36, 0xf7, 0xce, 0x25, 0x31, 0x1b, + 0x90, 0x2b, 0xa9, 0x7a, 0x3c, 0x12, 0xa9, 0x5c, 0xfa, 0x1c, 0x3a, 0x59, 0x1b, 0x81, + 0x8f, 0x60, 0x83, 0x27, 0x09, 0xd9, 0xe4, 0x83, 0x9e, 0x41, 0x0f, 0xb3, 0x6b, 0x84, + 0xf3, 0xac, 0x4f, 0x07, 0x0f, 0xc3, 0x5e, 0x16, 0x19, 0x78, 0x25, 0x9e, 0x5b, 0x8e, + 0xdc, 0x74, 0x4d, 0x90, 0x91, 0x9a, 0xa7, 0x70, 0xbb, 0x36, 0x21, 0x51, 0x28, 0xe5, + 0x82, 0xb5, 0x96, 0x41, 0xe2, 0x38, 0x52, 0xe9, 0x58, 0xeb, 0x8f, 0xc3, 0xc0, 0xaa, + 0x96, 0x15, 0x2b, 0xa4, 0xf7, 0x7f, 0x13, 0x8d, 0x6a, 0x67, 0x12, 0xa3, 0xae, 0x32, + 0x26, 0x01, 0x58, 0x83, 0xf8, 0x1d, 0xb2, 0x3e, 0x58, 0x3c, 0x86, 0x9c, 0x4c, 0x71, + 0x14, 0x3a, 0x6f, 0xff, 0xd6, 0x5e, 0x8d, 0xfd, 0xc5, 0x0c, 0x99, 0xa2, 0xf1, 0xf3, + 0x14, 0xcd, 0xcc, 0x71, 0x35, 0x9e, 0x23, 0x5f, 0x1d, 0x7d, 0xc2, 0xb5, 0xf3, 0x8e, + 0xf7, 0xb9, 0x70, 0x84, 0x31, 0x63, 0xc0, 0x3f, 0x9d, 0xd4, 0x0a, 0x80, 0x15, 0xef, + 0xdc, 0x87, 0x91, 0x95, 0x6a, 0x3f, 0x3c, 0xed, 0xd9, 0xea, 0x64, 0xf8, 0xef, 0xa7, + 0xa0, 0x81, 0x5a, 0x70, 0x38, 0x1d, 0x71, 0x46, 0x78, 0x17, 0xbd, 0x04, 0xca, 0x52, + 0x9a, 0xed, 0xe0, 0x7f, 0xf6, 0x0d, 0x17, 0x6a, 0xed, 0x0f, 0x85, 0x5a, 0x2e, 0xae, + 0xa8, 0x9e, 0xae, 0xac, 0xa8, 0x93, 0x58, 0xc0, 0x81, 0x82, 0x6a, 0x08, 0x12, 0xa5, + 0xbc, 0xa2, 0x8b, 0xe1, 0x37, 0x3f, 0x08, 0x6d, 0xbd, 0xba, 0x7e, 0x43, 0xe2, 0x03, + 0x21, 0x2c, 0x9f, 0xed, 0x21, 0x47, 0x4b, 0xa1, 0x9a, 0x05, 0x5f, 0xfc, 0xc1, 0x79, + 0x41, 0x2e, 0x89, 0x3a, 0x74, 0x48, 0x32, 0x29, 0x8c, 0x5f, 0xe2, 0x4c, 0xc6, 0xb1, + 0x86, 0x67, 0xf4, 0x9b, 0x34, 0xdf, 0xb1, 0x23, 0x79, 0x26, 0x74, 0x19, 0xa9, 0xcb, + 0x94, 0x03, 0xd8, 0x16, 0x7d, 0x8d, 0x1e, 0x91, 0xd2, 0x81, 0x1a, 0x04, 0x3b, 0x29, + 0x24, 0x3b, 0x06, 0x9b, 0x37, 0x58, 0x78, 0x47, 0xdc, 0x6f, 0xcd, 0xdb, 0x18, 0x31, + 0xbd, 0x1c, 0xc2, 0x56, 0x7c, 0xa0, 0x33, 0xac, 0x40, 0xf7, 0x4a, 0xb6, 0x95, 0x5f, + 0x68, 0x3b, 0x12, 0xe4, 0xe8, 0x25, 0x4e, 0x4e, 0xa7, 0x60, 0xd3, 0x8b, 0x3f, 0x46, + 0x79, 0x1c, 0x5c, 0x4c, 0xb1, 0x2b, 0xc7, 0xcc, 0xb0, 0xed, 0x18, 0x65, 0xf2, 0x5d, + 0x60, 0x1c, 0x30, 0x3f, 0x81, 0xfb, 0x1f, 0xa1, 0xdb, 0x48, 0x53, 0x3d, 0x3d, 0x6b, + 0x28, 0x8e, 0x4d, 0x9a, 0x4d, 0xff, 0x8e, 0xc2, 0x1c, 0x96, 0xf5, 0x78, 0x39, 0x97, + 0x10, 0xc8, 0x25, 0xfe, 0x7e, 0x32, 0xf9, 0x3a, 0x8c, 0x07, 0x43, 0xf9, 0xeb, 0xd5, + 0x4c, 0xc1, 0x51, 0xc7, 0x61, 0x03, 0x37, 0xae, 0xbf, 0x7e, 0x9b, 0x91, 0x57, 0x20, + 0xa5, 0x43, 0x51, 0xd4, 0x9a, 0xb8, 0xc2, 0x2f, 0xa3, 0x49, 0x98, 0xdc, 0xf5, 0x83, + 0xd4, 0x38, 0x73, 0x61, 0xef, 0x3f, 0xf8, 0x6f, 0x50, 0xec, 0x53, 0xf4, 0x92, 0x49, + 0xe4, 0xad, 0x34, 0x96, 0x03, 0x06, 0x6f, 0xc9, 0xc6, 0x61, 0xd6, 0x9f, 0x91, 0x1d, + 0xfa, 0x72, 0x41, 0xc8, 0xd5, 0x79, 0x2d, 0x43, 0xc4, 0x57, 0xd5, 0xde, 0x96, 0x52, + 0x3a, 0x53, 0xd6, 0x67, 0xec, 0x5c, 0x4e, 0xf9, 0xd5, 0x02, 0xa1, 0x6f, 0x15, 0x22, + 0x47, 0x58, 0x96, 0xd7, 0x9b, 0xc5, 0x78, 0x33, 0xe9, 0x77, 0x17, 0x1c, 0x32, 0x4d, + 0xce, 0x2a, 0x1e, 0xa1, 0xe4, 0x30, 0x4f, 0x49, 0xe4, 0x3a, 0xe0, 0x65, 0xe3, 0xfb, + 0x19, 0x6f, 0x76, 0xd9, 0xb8, 0x79, 0xc7, 0x20, 0x08, 0x62, 0xea, 0xd1, 0x8d, 0xea, + 0x5f, 0xb6, 0xa1, 0x7a, 0xce, 0xa3, 0x33, 0x86, 0xeb, 0x4c, 0xa1, 0xb5, 0x14, 0x86, + 0xa9, 0x14, 0x8f, 0xbd, 0xf9, 0xa9, 0x53, 0x32, 0xaa, 0x60, 0x5c, 0x5d, 0x54, 0x83, + 0xce, 0x4b, 0xa8, 0xec, 0xe0, 0x1a, 0x8f, 0xf2, 0xb7, 0xef, 0x82, 0xd0, 0x5c, 0x0b, + 0x6e, 0x86, 0x1b, 0x91, 0x5f, 0x13, 0xca, 0x0e, 0xb3, 0xea, 0x13, 0xd5, 0x07, 0x08, + 0x07, 0xa2, 0xcb, 0x66, 0x80, 0xa2, 0x49, 0xea, 0x9c, 0x72, 0x24, 0x39, 0x2c, 0xbc, + 0x8a, 0xb8, 0x25, 0x01, 0xb2, 0x6f, 0x11, 0x2a, 0xc7, 0x89, 0xa1, 0x2a, 0x31, 0xad, + 0x13, 0x14, 0xe2, 0xed, 0xe0, 0x8f, 0xad, 0x31, 0x43, 0xaf, 0x30, 0xc2, 0x7f, 0x40, + 0x3b, 0xc8, 0x66, 0xc7, 0x55, 0x17, 0x78, 0x52, 0xaf, 0xd0, 0xab, 0xb9, 0x0a, 0xde, + 0x1d, 0x68, 0x27, 0x26, 0xf4, 0x20, 0x08, 0xb4, 0x6a, 0xd7, 0xf8, 0xab, 0xdb, 0x18, + 0x11, 0x7f, 0x72, 0x64, 0x13, 0x90, 0xf0, 0x86, 0xb6, 0xe1, 0x49, 0x8b, 0xe6, 0x95, + 0x48, 0x52, 0x7e, 0x6a, 0xda, 0x2b, 0x38, 0xb9, 0xfe, 0x12, 0x1e, 0xf6, 0x70, 0xaf, + 0x74, 0x37, 0xd3, 0x25, 0x36, 0xd5, 0xcf, 0x5c, 0x4a, 0xb1, 0x9d, 0xd9, 0x97, 0x71, + 0x58, 0x2d, 0x03, 0x81, 0x04, 0xb7, 0xe0, 0x39, 0xa3, 0x76, 0xf7, 0xac, 0xbb, 0xea, + 0xdb, 0x34, 0xf9, 0x45, 0xbe, 0xb9, 0xd7, 0xca, 0x0e, 0x4e, 0x3d, 0x5c, 0x5e, 0x4e, + 0xb1, 0xd8, 0x52, 0x6e, 0xbd, 0x13, 0xda, 0xcb, 0x1b, 0xa3, 0x57, 0x35, 0xc6, 0xd0, + 0x4a, 0x45, 0x55, 0xac, 0xf4, 0xbf, 0x11, 0x76, 0x26, 0x50, 0x0d, 0x77, 0xb3, 0x81, + 0x89, 0xdd, 0x48, 0x88, 0x04, 0x12, 0x25, 0xac, 0xbe, 0x38, 0x74, 0xa4, 0xc0, 0xf6, + 0x07, 0xfe, 0x67, 0x45, 0xf9, 0x35, 0x5b, 0x3f, 0xa1, 0x88, 0xf1, 0xd6, 0x5c, 0x09, + 0xf3, 0x89, 0xaf, 0x1b, 0x9d, 0x62, 0x32, 0xaa, 0x79, 0x44, 0x79, 0x19, 0xc5, 0x50, + 0xf6, 0xf3, 0x1f, 0xec, 0x35, 0x48, 0x1c, 0xb9, 0x22, 0xde, 0x2d, 0xb5, 0xb4, 0xda, + 0x2f, 0x81, 0x94, 0x86, 0x17, 0x02, 0x8e, 0x32, 0x17, 0x06, 0xa3, 0xa7, 0x78, 0xc1, + 0x93, 0x8c, 0x44, 0x3b, 0xb0, 0x0e, 0x5b, 0x0f, 0xf0, 0x6a, 0xd8, 0xab, 0x9b, 0x1a, + 0xb0, 0xc1, 0x14, 0x77, 0x67, 0x3f, 0x85, 0xdf, 0x95, 0x61, 0xdb, 0xea, 0x45, 0xd5, + 0xf9, 0x78, 0x1e, 0xbe, 0x31, 0x7a, 0x07, 0x10, 0xae, 0x54, 0x61, 0xe3, 0x4f, 0xe6, + 0xf1, 0xb1, 0xaa, 0x9b, 0x4e, 0x67, 0xb1, 0x49, 0x10, 0x98, 0x48, 0x02, 0xc2, 0xa7, + 0xe3, 0x81, 0x93, 0xbc, 0x7b, 0xdc, 0x8b, 0xa3, 0xe4, 0xe3, 0xd1, 0xd9, 0x33, 0xbf, + 0xb5, 0x80, 0xf5, 0xb3, 0xe8, 0x7a, 0x2a, 0x06, 0x51, 0x70, 0x51, 0x41, 0x0f, 0xe1, + 0xb4, 0xff, 0x1e, 0xa0, 0xad, 0xe8, 0x24, 0xf3, 0x38, 0x51, 0x54, 0x56, 0xa5, 0x7c, + 0x7a, 0x91, 0x6a, 0x74, 0x38, 0x8e, 0xe8, 0xf1, 0x28, 0x1f, 0x9a, 0xde, 0x0a, 0xe2, + 0xa2, 0x61, 0x3a, 0x06, 0x12, 0xc4, 0x69, 0xdf, 0x79, 0x2b, 0x8d, 0xf4, 0xca, 0xe4, + 0xfc, 0x25, 0xc1, 0xca, 0xdb, 0xa9, 0x5a, 0x80, 0x7c, 0xe6, 0x1e, 0x5a, 0x53, 0x03, + 0xfa, 0xaf, 0x9e, 0x14, 0x65, 0x39, 0x96, 0xb5, 0xa8, 0xad, 0xc3, 0x4f, 0xd4, 0x75, + 0xef, 0x14, 0x99, 0x09, 0x4b, 0xab, 0xaf, 0x1f, 0x3f, 0x07, 0xda, 0x9a, 0x39, 0x0b, + 0x1d, 0x9f, 0xc9, 0xa0, 0x83, 0x27, 0x98, 0x7a, 0xdf, 0xe9, 0x56, 0x48, 0x63, 0xfb, + 0xdf, 0xa8, 0xf6, 0xb4, 0x6a, 0x88, 0x41, 0x58, 0x30, 0x99, 0xaf, 0xb7, 0x87, 0x01, + 0x18, 0xfa, 0xce, 0x76, 0x34, 0x7e, 0x40, 0xb6, 0xfd, 0x8c, 0xd1, 0x55, 0x82, 0xae, + 0x8e, 0x23, 0xbe, 0x9a, 0x02, 0x19, 0xbc, 0x3e, 0x4e, 0x45, 0x46, 0xa3, 0x0d, 0x3b, + 0xbb, 0xbd, 0x16, 0x86, 0x08, 0x68, 0x76, 0xbe, 0x0e, 0x4c, 0x85, 0x9b, 0xe7, 0x1f, + 0xb5, 0x8f, 0x4f, 0xab, 0x3d, 0x28, 0xc0, 0xb4, 0xf7, 0xe7, 0x5a, 0xd1, 0xed, 0xb7, + 0xf8, 0x89, 0x46, 0xfb, 0x40, 0xcf, 0xa5, 0x78, 0x6a, 0x0f, 0xcb, 0xa1, 0x30, 0x3c, + 0x83, 0x47, 0xec, 0xee, 0x93, 0xd4, 0x6d, 0x14, 0x0b, 0xb5, 0xf6, 0x95, 0x31, 0xd6, + 0x66, 0x54, 0x8b, 0x10, 0x9c, 0xe7, 0x64, 0xbe, 0xad, 0x7c, 0x87, 0xbd, 0x4c, 0x87, + 0x64, 0x94, 0xde, 0x82, 0xdb, 0x6e, 0x50, 0x73, 0xa6, 0xc9, 0x4f, 0x7c, 0x09, 0x9a, + 0x40, 0xd7, 0xa3, 0x1c, 0x4a, 0x04, 0xb6, 0x9c, 0x9f, 0xcc, 0xf3, 0xc7, 0xdd, 0x56, + 0xf5, 0x54, 0x47, 0x76, 0xc5, 0x3b, 0x4d, 0xf7, 0x95, 0x39, 0x81, 0xd5, 0x5a, 0x96, + 0xa6, 0xdc, 0xff, 0x99, 0x04, 0xa9, 0x08, 0x42, 0xe5, 0xba, 0xfe, 0xc8, 0x84, 0x0c, + 0x2d, 0x25, 0x5b, 0xf5, 0xad, 0x61, 0xc4, 0x60, 0xf9, 0x8f, 0xeb, 0x82, 0xa1, 0x0f, + 0xa1, 0xc0, 0x99, 0xf6, 0x27, 0x76, 0x79, 0x82, 0x36, 0xc5, 0xca, 0x7f, 0x1e, 0x46, + 0xeb, 0xdb, 0x2b, 0x14, 0x4d, 0x87, 0x13, 0xe5, 0x6c, 0x77, 0x2f, 0x2c, 0x3b, 0x86, + 0x0e, 0xa5, 0xb0, 0x3a, 0x88, 0x54, 0xbc, 0x6e, 0x65, 0x90, 0xd6, 0x3c, 0xc0, 0xea, + 0x54, 0xf1, 0x0b, 0x73, 0xba, 0x24, 0x1b, 0xf7, 0x4b, 0x63, 0x55, 0x51, 0xa2, 0xaa, + 0xca, 0x96, 0x87, 0xac, 0x52, 0x69, 0xfd, 0x36, 0x8b, 0x26, 0xd7, 0x0a, 0x73, 0x7f, + 0x26, 0x76, 0x85, 0x99, 0x8a, 0x3f, 0x7d, 0x26, 0x37, 0x91, 0x49, 0x09, 0xc7, 0x46, + 0x49, 0x5d, 0x24, 0xc4, 0x98, 0x63, 0x5e, 0xf9, 0x7a, 0xc6, 0x6a, 0x40, 0x08, 0x94, + 0xc0, 0x9f, 0x73, 0x48, 0x8e, 0xb7, 0xcf, 0x33, 0xf6, 0xda, 0xd1, 0x66, 0x6a, 0x05, + 0xf9, 0x1a, 0xd7, 0x75, 0x79, 0x65, 0xc2, 0x99, 0x36, 0xe7, 0xfa, 0x48, 0xd7, 0x7e, + 0x89, 0xee, 0x09, 0x62, 0xf5, 0x8c, 0x05, 0x1d, 0x11, 0xd0, 0x55, 0xfc, 0xe2, 0x04, + 0xa5, 0x62, 0xde, 0x68, 0x08, 0x8a, 0x1b, 0x26, 0x48, 0xb8, 0x17, 0x4c, 0xbc, 0xfc, + 0x8b, 0x5b, 0x5c, 0xd0, 0x77, 0x11, 0x5a, 0xfd, 0xe1, 0x84, 0x05, 0x05, 0x4e, 0x5d, + 0xa9, 0xa0, 0x43, 0x10, 0x34, 0x2c, 0x5d, 0x3b, 0x52, 0x6e, 0x0b, 0x02, 0xc5, 0xca, + 0x17, 0x22, 0xba, 0xde, 0xee, 0x23, 0xd1, 0x45, 0xe8, 0xeb, 0x22, 0x13, 0xfc, 0x4a, + 0xf1, 0xe4, 0x50, 0xe4, 0xd5, 0x21, 0x7c, 0x66, 0x17, 0x00, 0x8c, 0x78, 0xf4, 0xfb, + 0x11, 0x12, 0xf4, 0x02, 0x8a, 0x70, 0x4f, 0xc5, 0xa9, 0x38, 0x2c, 0x6b, 0x03, 0xe7, + 0xd8, 0x08, 0x5e, 0x90, 0x6c, 0xf8, 0x4c, 0xa2, 0xc1, 0x20, 0x7c, 0x87, 0xa2, 0xbc, + 0xe2, 0x08, 0x0a, 0x98, 0x91, 0x66, 0x8d, 0x69, 0xb0, 0x44, 0xbe, 0xce, 0xd6, 0xcd, + 0xa3, 0x2c, 0x22, 0x9c, 0x91, 0x17, 0x91, 0x7a, 0xa0, 0x7d, 0xdf, 0xfc, 0xd3, 0x77, + 0x39, 0x5c, 0xba, 0x61, 0x6d, 0x63, 0xc0, 0xb6, 0x9c, 0x01, 0xfc, 0xc4, 0x53, 0x91, + 0xfd, 0x5b, 0x87, 0x63, 0xfb, 0x96, 0xd7, 0xca, 0x33, 0x3a, 0x12, 0xde, 0x3c, 0xef, + 0xa9, 0x1c, 0x6c, 0x98, 0xf9, 0x47, 0x3b, 0x8e, 0x10, 0x4a, 0x71, 0x29, 0x3e, 0x46, + 0x37, 0x47, 0x05, 0xba, 0xf6, 0x5f, 0xa4, 0x13, 0x84, 0xba, 0x5c, 0x8e, 0x0c, 0x88, + 0xa3, 0xeb, 0x07, 0xe0, 0xbe, 0x34, 0xda, 0xdd, 0xfa, 0xbb, 0x7b, 0x65, 0x54, 0x3b, + 0x5f, 0x39, 0xcb, 0x20, 0x23, 0xd4, 0x67, 0x89, 0xeb, 0x7d, 0x98, 0x9a, 0xf7, 0x79, + 0xe5, 0xb8, 0xd2, 0x83, 0x85, 0xa8, 0x5b, 0x0d, 0xa2, 0xab, 0xe0, 0x7f, 0x0c, 0x2b, + 0xb4, 0x25, 0x5f, 0xce, 0xa0, 0x31, 0x88, 0x52, 0x7a, 0x30, 0x7d, 0x40, 0x91, 0x59, + 0xe9, 0x01, 0x66, 0xfa, 0xc6, 0xa0, 0x70, 0xba, 0x05, 0xb3, 0xe4, 0xdb, 0xfd, 0x3a, + 0x2b, 0xfc, 0xc9, 0xee, 0x6e, 0xd0, 0x16, 0xc0, 0xf6, 0x65, 0xbe, 0x81, 0x33, 0xb7, + 0xdc, 0x1d, 0x86, 0x04, 0x4d, 0xb0, 0xf9, 0xdb, 0x40, 0xfb, 0x0e, 0x9f, 0x8b, 0xc2, + 0xe4, 0xdb, 0x53, 0x82, 0xa8, 0xb4, 0xf8, 0x15, 0xb4, 0xe8, 0x43, 0x4a, 0xd0, 0xdf, + 0xbc, 0x51, 0xa5, 0xe9, 0xb1, 0x45, 0xe1, 0x59, 0x6c, 0xbf, 0x46, 0x70, 0xb7, 0xe0, + 0x5d, 0xfd, 0xaf, 0xbb, 0x0c, 0xf3, 0xdd, 0xee, 0x28, 0xd7, 0x6a, 0x82, 0x42, 0x8e, + 0x8a, 0xba, 0x43, 0x64, 0xe8, 0x4b, 0xac, 0x37, 0x92, 0x98, 0xdf, 0x29, 0x32, 0xe6, + 0x9b, 0xb5, 0xd0, 0x45, 0x51, 0x6e, 0xfc, 0x33, 0xae, 0x6c, 0xc3, 0x94, 0x7c, 0xeb, + 0x09, 0xed, 0x37, 0x16, 0x67, 0x21, 0x2a, 0x83, 0x1b, 0x54, 0x85, 0xea, 0xfc, 0xe8, + 0x48, 0x81, 0x88, 0xea, 0x4e, 0x27, 0xd0, 0xcd, 0xf7, 0xdd, 0xd3, 0x48, 0xab, 0xff, + 0x77, 0x7f, 0x4a, 0x13, 0xbb, 0xc7, 0x16, 0xb6, 0xa5, 0x94, 0x4e, 0xe7, 0x27, 0x96, + 0x56, 0x90, 0xe2, 0x09, 0xb4, 0x9e, 0xb9, 0x62, 0xc0, 0x39, 0x97, 0x5f, 0x93, 0x9e, + 0xd5, 0xc6, 0xe4, 0xc4, 0x00, 0xd8, 0x87, 0x75, 0x94, 0x33, 0xd3, 0xad, 0x71, 0x6d, + 0xa0, 0xcb, 0x44, 0x61, 0x13, 0xc7, 0x72, 0x7a, 0x64, 0xb5, 0x8c, 0x3f, 0x8a, 0x0f, + 0x81, 0x18, 0x9f, 0x98, 0x00, 0x52, 0x33, 0xa8, 0x13, 0x66, 0xae, 0xe7, 0x3c, 0xec, + 0x85, 0x22, 0x8e, 0xbc, 0xfd, 0x5e, 0xe3, 0xc3, 0xfb, 0x44, 0xdb, 0x76, 0xba, 0x24, + 0x3f, 0x28, 0x42, 0xb7, 0xb5, 0xfc, 0x74, 0x6a, 0xe5, 0x1b, 0x0b, 0xc4, 0xbd, 0x4f, + 0xc9, 0xfd, 0x83, 0x35, 0x65, 0xea, 0x85, 0x2b, 0x92, 0xb2, 0x24, 0xf6, 0x99, 0x03, + 0x18, 0xad, 0x8c, 0x7d, 0x94, 0x37, 0xe2, 0x0e, 0x2a, 0x1f, 0x20, 0xe8, 0x18, 0xf9, + 0x05, 0x7c, 0x5a, 0xba, 0xaa, 0x2e, 0x5c, 0x15, 0xb9, 0x49, 0x45, 0xcd, 0x42, 0x4c, + 0x28, 0xa5, 0xfa, 0x38, 0x5d, 0xad, 0xfe, 0x49, 0x07, 0xb2, 0x74, 0xd8, 0x42, 0x70, + 0x7d, 0xb3, 0x69, 0x7a, 0x5a, 0xe6, 0xc8, 0xf5, 0x42, 0xe5, 0xec, 0xc0, 0x7f, 0xe4, + 0x73, 0x50, 0xd1, 0x01, 0x46, 0x70, 0x21, 0x2e, 0xfe, 0x81, 0xfb, 0x7c, 0x73, 0xe8, + 0x45, 0x0d, 0xf8, 0x14, 0xef, 0x62, 0x32, 0xf7, 0x49, 0x0f, 0x63, 0xcc, 0xf0, 0x74, + 0x80, 0xf8, 0x84, 0xa6, 0x6e, 0xaf, 0xfc, 0x28, 0xfe, 0xa4, 0x48, 0xd7, 0xb4, 0x01, + 0xcd, 0xae, 0x10, 0xe7, 0xc0, 0xc7, 0xf9, 0xa7, 0xb1, 0x53, 0x31, 0x96, 0x9f, 0xc8, + 0xcb, 0x36, 0x39, 0x67, 0x73, 0xde, 0x19, 0x19, 0x31, 0xc7, 0x50, 0xf6, 0xce, 0x5c, + 0xaa, 0xf2, 0x97, 0x68, 0xeb, 0xb2, 0x7d, 0xac, 0xc7, 0x38, 0x05, 0x6a, 0x81, 0x25, + 0xb4, 0x77, 0x2b, 0xf8, 0x7a, 0xe1, 0x0a, 0x8a, 0x30, 0x9b, 0x9b, 0xd6, 0x55, 0x04, + 0x3c, 0xfc, 0x31, 0x59, 0x49, 0x43, 0x68, 0xc5, 0xab, 0x8c, 0xad, 0xb7, 0xf6, 0x71, + 0xe9, 0x62, 0x6b, 0xd2, 0x63, 0xe3, 0x11, 0x81, 0xa6, 0x04, 0xb5, 0x06, 0xa0, 0x3b, + 0x43, 0x9a, 0x7f, 0xfe, 0x43, 0x55, 0x89, 0x24, 0x77, 0xe2, 0xbd, 0xf3, 0x38, 0xc6, + 0x2c, 0x39, 0x22, 0xf7, 0xd3, 0xc9, 0xa5, 0x6c, 0x71, 0x03, 0xd9, 0x11, 0x94, 0x8a, + 0x84, 0xb5, 0xae, 0x2d, 0xbb, 0x16, 0xa3, 0x76, 0x1a, 0xdd, 0x05, 0x3a, 0x0f, 0x96, + 0x7e, 0x6b, 0x5b, 0xc9, 0x42, 0x11, 0xb6, 0x54, 0x71, 0x53, 0x26, 0x7c, 0x6e, 0xe1, + 0xca, 0xd0, 0xd9, 0x74, 0xa7, 0x10, 0x88, 0x58, 0x37, 0x35, 0xe4, 0xf6, 0x3d, 0x33, + 0x15, 0x6d, 0xad, 0xd5, 0x4c, 0x2f, 0xaf, 0x89, 0x11, 0x4a, 0x12, 0x7b, 0x97, 0xb9, + 0x4c, 0xc2, 0xa2, 0x2e, 0xf3, 0x03, 0xf4, 0x59, 0xd0, 0x4f, 0xc0, 0xb5, 0x3a, 0xce, + 0x59, 0x18, 0xd4, 0x7f, 0xf3, 0x3a, 0x55, 0x8b, 0xd7, 0x1a, 0x75, 0xf3, 0x55, 0xfb, + 0xd0, 0x6b, 0xbc, 0xcf, 0x4e, 0x02, 0xc3, 0xc0, 0xa4, 0xb6, 0x3d, 0x0c, 0xc9, 0x49, + 0x80, 0x1d, 0x63, 0xa6, 0x4c, 0xb2, 0xd3, 0x23, 0x73, 0xb2, 0xc7, 0xb2, 0x74, 0xab, + 0x2d, 0xb4, 0x68, 0x21, 0x42, 0xc8, 0xb2, 0x1d, 0x84, 0xc4, 0x81, 0xf5, 0xef, 0x21, + 0xe4, 0xb5, 0xe3, 0x60, 0x34, 0x51, 0xbf, 0x94, 0x77, 0x4d, 0x0e, 0xf4, 0x7f, 0x63, + 0xfa, 0x6a, 0xbb, 0x78, 0xd2, 0x1c, 0x19, 0x3c, 0xbe, 0x65, 0xb6, 0x95, 0xfe, 0x67, + 0x42, 0x3c, 0x1e, 0x2d, 0x31, 0x2e, 0x27, 0x76, 0xfa, 0x24, 0xec, 0xe8, 0x46, 0x83, + 0xe7, 0x48, 0x76, 0xc5, 0x5e, 0xa0, 0x36, 0x9e, 0x4e, 0xa0, 0xe8, 0x64, 0x94, 0xe0, + 0x0d, 0xde, 0x23, 0x6a, 0x16, 0x89, 0x73, 0x1f, 0x0a, 0x5d, 0x82, 0x03, 0xaf, 0xde, + 0x5c, 0x42, 0x36, 0x40, 0xb8, 0x1e, 0x4f, 0x63, 0x1c, 0x98, 0x1c, 0x11, 0xa2, 0xe1, + 0xd1, 0x84, 0xc6, 0x7c, 0x52, 0x8d, 0xf9, 0x2d, 0x53, 0xae, 0xc4, 0x4a, 0x40, 0xa4, + 0xea, 0x2a, 0x13, 0x1b, 0x47, 0x33, 0xcf, 0xe4, 0x5c, 0x6b, 0x00, 0x12, 0xc3, 0xe9, + 0xe2, 0x09, 0x75, 0xba, 0xae, 0xcb, 0x02, 0x32, 0xdf, 0x88, 0x0b, 0xd7, 0xd1, 0xde, + 0x13, 0xe1, 0x34, 0x94, 0x62, 0xec, 0x8d, 0x5d, 0xf3, 0xe7, 0x80, 0xff, 0xa7, 0x2e, + 0xba, 0x8a, 0x8d, 0xf7, 0xfc, 0xf3, 0x98, 0xec, 0x23, 0x05, 0x13, 0xca, 0x9d, 0x61, + 0x23, 0xf8, 0xb9, 0xd8, 0x17, 0x85, 0x60, 0xda, 0xf9, 0x75, 0x11, 0x19, 0x55, 0xa2, + 0xbc, 0xa3, 0x42, 0x3e, 0xee, 0xfc, 0x52, 0x7b, 0xe3, 0xa8, 0x54, 0x3e, 0xb9, 0x0a, + 0x5e, 0xc0, 0x2f, 0x35, 0xa7, 0xc6, 0x4b, 0x7d, 0xd5, 0x9a, 0x72, 0xda, 0x00, 0x74, + 0x63, 0x4e, 0x01, 0xd2, 0xab, 0xf3, 0x63, 0x7a, 0xdd, 0x77, 0xc7, 0x35, 0x0f, 0x12, + 0xb0, 0x11, 0xb2, 0x94, 0x16, 0x8e, 0xc7, 0x55, 0x76, 0xe4, 0x7d, 0x16, 0x9e, 0x39, + 0x38, 0xbf, 0x6a, 0xe2, 0xaa, 0x8f, 0xf7, 0xcf, 0xba, 0x7c, 0xac, 0xb1, 0xf9, 0x2b, + 0x6e, 0x4c, 0x24, 0x97, 0xbf, 0xfa, 0x9f, 0x17, 0xca, 0xd2, 0x42, 0xfa, 0x9c, 0x31, + 0x79, 0xc1, 0xa3, 0xaa, 0x81, 0xf7, 0x36, 0x16, 0x49, 0x57, 0x2c, 0x71, 0x5c, 0x25, + 0xa1, 0xf6, 0xcd, 0x5a, 0xce, 0x82, 0xc0, 0x0a, 0xb2, 0x34, 0x2b, 0x9c, 0x3c, 0xb4, + 0xff, 0xfd, 0xda, 0x16, 0x0c, 0xa5, 0xab, 0x9e, 0x9b, 0xaf, 0x21, 0x39, 0xef, 0x9a, + 0xfb, 0xe1, 0xb1, 0xf3, 0x09, 0x46, 0x2a, 0xfc, 0xe4, 0x62, 0xa7, 0x9b, 0xb9, 0x69, + 0x8e, 0x22, 0xc9, 0x57, 0xc5, 0x90, 0xa7, 0x53, 0xa7, 0x6b, 0x87, 0xe0, 0x09, 0x12, + 0x1e, 0x06, 0xf6, 0xa1, 0xbf, 0x62, 0xa0, 0x8b, 0xf4, 0x35, 0xd9, 0x2e, 0x2f, 0xff, + 0xe8, 0x6e, 0x2a, 0x9c, 0xbb, 0xa9, 0x13, 0x3a, 0x68, 0xe4, 0xae, 0xbf, 0x33, 0xc3, + 0x84, 0x36, 0xf2, 0x54, 0x5f, 0xc2, 0xd5, 0x28, 0x32, 0xd1, 0x65, 0xaf, 0x41, 0x5b, + 0x24, 0x4a, 0xdc, 0x5f, 0x57, 0x37, 0x7d, 0xee, 0xdf, 0x46, 0x0a, 0xa3, 0xbe, 0xb4, + 0x34, 0x19, 0xc6, 0xb0, 0x82, 0xe8, 0x35, 0xce, 0x84, 0xca, 0x13, 0xb6, 0x90, 0x8a, + 0x88, 0x13, 0xc0, 0x21, 0xde, 0x9f, 0xa9, 0xa4, 0x4e, 0x4c, 0x18, 0xdc, 0xb3, 0xd2, + 0x1f, 0xaa, 0xbd, 0xb4, 0x19, 0x31, 0xb2, 0xfd, 0x49, 0x76, 0x44, 0xdc, 0x3a, 0x15, + 0x07, 0xfa, 0x5a, 0xc7, 0xc7, 0x6b, 0xee, 0xbb, 0xdb, 0xd1, 0xd4, 0x92, 0x99, 0xa5, + 0x5b, 0xd4, 0x99, 0x27, 0xe9, 0xd7, 0xf4, 0x88, 0x4e, 0x6e, 0xd3, 0xfd, 0x5e, 0x4b, + 0x7c, 0xb8, 0x35, 0xb8, 0x33, 0x08, 0x96, 0x4e, 0x3c, 0x46, 0x87, 0x3f, 0xd6, 0x13, + 0x31, 0x7b, 0x91, 0xd2, 0x92, 0x36, 0xea, 0x90, 0xe3, 0x65, 0xd1, 0x62, 0xcc, 0x05, + 0x1c, 0x84, 0x6d, 0x24, 0x21, 0x76, 0xda, 0xf6, 0xd2, 0x86, 0x18, 0xae, 0x31, 0xfb, + 0xaa, 0xe9, 0x99, 0xa9, 0x3f, 0x17, 0x5c, 0x69, 0x38, 0xe6, 0x31, 0xa0, 0x81, 0xf2, + 0xc1, 0xf3, 0xfd, 0x78, 0x25, 0x49, 0xd3, 0xf3, 0x24, 0x57, 0x59, 0x60, 0x6d, 0x9f, + 0x92, 0xd5, 0x54, 0x8a, 0xcf, 0xea, 0xdb, 0xaf, 0x9c, 0xaa, 0x6b, 0x93, 0xdc, 0x08, + 0x82, 0x8d, 0x74, 0xf6, 0xd5, 0xfd, 0xd8, 0x33, 0x31, 0xf0, 0x96, 0x91, 0x45, 0x95, + 0x52, 0x97, 0xe6, 0x9f, 0x00, 0xfd, 0x29, 0x87, 0xf2, 0xda, 0x2b, 0x94, 0xb9, 0x95, + 0xfe, 0xcb, 0xe6, 0x22, 0xa7, 0x35, 0xef, 0x7f, 0x12, 0x07, 0xf6, 0x71, 0x62, 0x94, + 0x89, 0x20, 0x2b, 0xea, 0x0b, 0x47, 0x5e, 0x51, 0x68, 0x1a, 0xa1, 0x67, 0x78, 0xb3, + 0x9b, 0xd9, 0x23, 0xc9, 0x8d, 0xc6, 0xff, 0x83, 0x73, 0xc7, 0x9b, 0xb1, 0x70, 0x30, + 0x41, 0x7b, 0xc2, 0x00, 0xc8, 0xf0, 0xb8, 0x55, 0xac, 0xfe, 0xc1, 0x79, 0xf7, 0x67, + 0x4c, 0xec, 0x27, 0x21, 0xa1, 0x0f, 0xca, 0x69, 0x3d, 0x83, 0xcf, 0xe5, 0xb8, 0xcd, + 0xcc, 0x18, 0xf8, 0x1a, 0xd6, 0x17, 0xfa, 0x26, 0xf0, 0xdf, 0xb8, 0x36, 0x55, 0xb8, + 0xa2, 0x9a, 0x7f, 0x83, 0x42, 0x32, 0x42, 0x5e, 0x8c, 0x47, 0x45, 0x88, 0xf1, 0x8d, + 0xd3, 0x26, 0xaa, 0x39, 0x6c, 0x3e, 0x47, 0x75, 0xe0, 0x02, 0x05, 0xfc, 0x9e, 0x45, + 0xf7, 0xb7, 0xd2, 0xe6, 0xd5, 0x5d, 0xcb, 0x90, 0xe2, 0x3f, 0xf6, 0xb5, 0x08, 0x45, + 0x9a, 0xa6, 0x99, 0xbf, 0xcb, 0xd5, 0x6f, 0x10, 0x99, 0x77, 0x64, 0xd0, 0x87, 0x40, + 0x89, 0x86, 0xe7, 0x3d, 0x6e, 0x28, 0x4f, 0xea, 0x9a, 0x23, 0xc3, 0x93, 0x11, 0x78, + 0x2f, 0x86, 0xca, 0xbf, 0xf9, 0x45, 0x5e, 0x4c, 0xf6, 0x99, 0xe5, 0xf5, 0xd4, 0xbc, + 0x0b, 0x39, 0x05, 0xa4, 0xe3, 0xbd, 0x01, 0xc5, 0x4d, 0xf8, 0x64, 0x34, 0x43, 0xbe, + 0x0f, 0x88, 0x90, 0x32, 0xea, 0x32, 0x5b, 0xf0, 0x71, 0x07, 0xfd, 0x41, 0xd6, 0x73, + 0xee, 0xba, 0xe6, 0xfa, 0x63, 0x7b, 0x70, 0xcc, 0x0e, 0xd3, 0xf0, 0x09, 0x58, 0xdf, + 0xb8, 0xdc, 0xf0, 0x0e, 0x85, 0xa1, 0xd0, 0xa6, 0xa8, 0x90, 0x81, 0x40, 0xc2, 0xf4, + 0x34, 0xc2, 0xe2, 0x60, 0xef, 0xb0, 0xbc, 0xa2, 0x00, 0x35, 0x04, 0xc9, 0x99, 0x93, + 0xa9, 0xe1, 0xc0, 0xff, 0x9c, 0xef, 0xe6, 0xa6, 0x65, 0xd7, 0x91, 0x42, 0x86, 0x90, + 0xe4, 0x7e, 0xf8, 0xc1, 0x31, 0xa8, 0xe9, 0xbf, 0xb4, 0xc3, 0x08, 0x02, 0x35, 0x03, + 0x2d, 0x73, 0x1b, 0x0d, 0x38, 0x41, 0x22, 0x5f, 0x1c, 0x11, 0xe2, 0xc2, 0x8e, 0xe8, + 0x4d, 0x35, 0xf9, 0x22, 0x61, 0x00, 0x56, 0x59, 0x72, 0xeb, 0x26, 0x9d, 0x27, 0x8e, + 0xf6, 0x49, 0x79, 0xbf, 0x65, 0x15, 0xed, 0x4a, 0x68, 0x40, 0xb0, 0x88, 0x3a, 0x9e, + 0x6e, 0xf6, 0x4a, 0x0e, 0xfc, 0xae, 0x1c, 0xf2, 0x1d, 0xfe, 0x74, 0x85, 0x4e, 0x84, + 0xc2, 0x74, 0x9f, 0xac, 0x03, 0x82, 0x52, 0x75, 0xc9, 0xb6, 0x30, 0x21, 0x84, 0xc7, + 0x2d, 0xf4, 0xc4, 0xbb, 0x28, 0x62, 0xe4, 0xe8, 0xa7, 0xd9, 0xa4, 0xa2, 0x82, 0x86, + 0x6f, 0x9a, 0x7b, 0x2c, 0xfc, 0x9a, 0x56, 0x31, 0x3d, 0xa0, 0xc4, 0x7a, 0x34, 0xb7, + 0xb9, 0xcd, 0xa3, 0xac, 0xe8, 0x18, 0x5f, 0x07, 0xdf, 0x36, 0xe4, 0x48, 0xa7, 0x6a, + 0xa4, 0x77, 0xf2, 0x24, 0xd8, 0x7a, 0x07, 0x4f, 0x43, 0xaf, 0x5d, 0x5f, 0x79, 0xb3, + 0xab, 0x11, 0x28, 0xf0, 0x81, 0x91, 0x44, 0x7f, 0xa6, 0x46, 0xbf, 0xdd, 0xe5, 0xb5, + 0x1e, 0x23, 0x3c, 0xa6, 0x15, 0x5d, 0x10, 0x15, 0x85, 0xbc, 0x2c, 0x40, 0x15, 0x8a, + 0xc2, 0x10, 0x6e, 0x66, 0xa2, 0x6e, 0x46, 0x42, 0x33, 0x70, 0x63, 0x68, 0x76, 0xb4, + 0x34, 0xa7, 0x4f, 0x8c, 0xe8, 0x06, 0x00, 0x50, 0xb0, 0x82, 0xa7, 0x9b, 0x61, 0xbb, + 0x5d, 0x34, 0x4e, 0xb5, 0xa1, 0x15, 0x83, 0x26, 0xce, 0xd9, 0xa9, 0xd9, 0xf5, 0x4f, + 0xb2, 0xfe, 0x8f, 0x9f, 0x05, 0xcd, 0x11, 0x1e, 0xe4, 0x6c, 0x47, 0x10, 0xf6, 0xf6, + 0x3a, 0x62, 0x69, 0x45, 0x57, 0xef, 0x1b, 0x12, 0xc8, 0x80, 0x06, 0xb6, 0x78, 0x72, + 0x50, 0x5f, 0x4e, 0x88, 0x3b, 0x58, 0x59, 0x07, 0x92, 0x9a, 0x2f, 0x3f, 0xdb, 0x0d, + 0x8f, 0x79, 0x14, 0xc4, 0x2d, 0xde, 0x2d, 0x20, 0x00, 0xf5, 0xae, 0x02, 0xd4, 0x18, + 0x21, 0xc8, 0xe1, 0xee, 0x01, 0x38, 0xeb, 0xcb, 0x72, 0x8d, 0x7c, 0x6c, 0x3c, 0x80, + 0x02, 0x7e, 0x43, 0x75, 0x94, 0xc6, 0x70, 0xfd, 0x6f, 0x39, 0x08, 0x22, 0x2e, 0xe7, + 0xa1, 0xb9, 0x17, 0xf8, 0x27, 0x1a, 0xbe, 0x66, 0x0e, 0x39, 0xe0, 0x51, 0xaa, 0xa6, + 0xfc, 0xa1, 0x86, 0x22, 0x76, 0xe2, 0xba, 0xa0, 0xfe, 0x0b, 0x16, 0x2a, 0xeb, 0xcf, + 0xe3, 0xd9, 0x34, 0x9c, 0x8d, 0x15, 0x4b, 0xb7, 0xee, 0x28, 0x21, 0x2c, 0x1b, 0xaa, + 0x70, 0x5d, 0x82, 0x07, 0x0d, 0x70, 0x32, 0xf2, 0x69, 0x5d, 0x17, 0x96, 0x80, 0x9f, + 0xab, 0x41, 0x24, 0x69, 0x26, 0xaf, 0x99, 0x2b, 0x6e, 0xee, 0x95, 0xa9, 0xa0, 0x6b, + 0xc4, 0x56, 0x2c, 0x5f, 0x2f, 0x1b, 0x19, 0x54, 0x95, 0x00, 0x37, 0x2e, 0x7a, 0xd5, + 0x79, 0xa6, 0xd6, 0xd7, 0x8b, 0x33, 0x15, 0x31, 0x30, 0xfb, 0x44, 0x8f, 0xb7, 0x9e, + 0x8a, 0x66, 0x9d, 0xb8, 0xa0, 0xf3, 0x5c, 0xdf, 0x9a, 0xe5, 0xd3, 0x2d, 0x73, 0x2f, + 0xc7, 0x94, 0x18, 0xe2, 0x3b, 0x45, 0x1d, 0xdc, 0x95, 0xa2, 0x2a, 0xba, 0xbb, 0x05, + 0x6e, 0xc6, 0xb5, 0xe8, 0xba, 0x4f, 0x52, 0x4d, 0xfa, 0xfe, 0x87, 0x52, 0x62, 0xdd, + 0x7b, 0xe4, 0x1c, 0xbb, 0xc6, 0x24, 0x20, 0xd4, 0xad, 0x6d, 0xf5, 0xc9, 0xb7, 0x13, + 0x60, 0x4f, 0x65, 0x60, 0x88, 0xa4, 0x48, 0x5e, 0x93, 0xbe, 0x19, 0x07, 0xd2, 0x7a, + 0xc6, 0xec, 0x3c, 0x57, 0x25, 0x9b, 0xd6, 0x98, 0x1d, 0x42, 0xc1, 0xb7, 0x8a, 0x29, + 0xad, 0x96, 0x85, 0xe6, 0x3c, 0x49, 0x4d, 0x41, 0x29, 0x62, 0x3e, 0xa1, 0xa7, 0xff, + 0xec, 0x85, 0xfa, 0x29, 0x41, 0x10, 0x73, 0xed, 0xb2, 0x97, 0x8e, 0xf4, 0xe4, 0x69, + 0xdd, 0xd5, 0xcd, 0xa9, 0x86, 0x18, 0x99, 0x95, 0xf8, 0x8d, 0x6a, 0xb3, 0x66, 0xdb, + 0x01, 0x90, 0x01, 0xf5, 0xb2, 0x52, 0x88, 0xcf, 0x86, 0x0f, 0xd9, 0x98, 0xee, 0x57, + 0x3c, 0x8c, 0xc4, 0x8a, 0xa9, 0xef, 0xcf, 0x9b, 0x61, 0x7e, 0x04, 0x3c, 0x32, 0x9c, + 0xd1, 0xaa, 0x1a, 0x0e, 0xd3, 0xa4, 0x02, 0xfb, 0x96, 0xe3, 0x36, 0xc7, 0x19, 0xe6, + 0x25, 0x3c, 0xb6, 0x91, 0xaa, 0x0d, 0xb5, 0x27, 0x36, 0x62, 0x6e, 0xd1, 0x97, 0x88, + 0x75, 0x88, 0x8e, 0xc7, 0x6c, 0x84, 0x6b, 0xc2, 0x27, 0x27, 0x2a, 0x58, 0x53, 0x17, + 0xdf, 0xf0, 0xb1, 0x14, 0x8d, 0x92, 0xd6, 0xf5, 0xfb, 0x7d, 0x95, 0x33, 0x67, 0x70, + 0xa7, 0xd1, 0x6f, 0xac, 0x1a, 0xdd, 0x86, 0x07, 0x76, 0xcb, 0x48, 0x02, 0x21, 0xf8, + 0xfb, 0x33, 0xd7, 0xe4, 0xe9, 0xb0, 0x79, 0x02, 0xd2, 0xff, 0x86, 0xfd, 0xac, 0x72, + 0x09, 0x62, 0x34, 0xae, 0xd4, 0x8d, 0xe8, 0x92, 0xff, 0x73, 0x55, 0x07, 0x3b, 0xbf, + 0x06, 0x15, 0xf6, 0x7b, 0x11, 0x00, 0xcc, 0x2e, 0xa3, 0xba, 0x3d, 0x6c, 0x1a, 0x1a, + 0x90, 0x87, 0xb1, 0x19, 0xba, 0xee, 0xbf, 0xa6, 0x2b, 0xc9, 0xf0, 0xec, 0x47, 0x9d, + 0x99, 0xc1, 0xa3, 0xb1, 0x58, 0xb5, 0x14, 0xd1, 0x62, 0x9d, 0xb3, 0x99, 0x3f, 0x11, + 0x67, 0x2a, 0x26, 0x70, 0x8e, 0x5a, 0xd8, 0x16, 0xb5, 0x47, 0xab, 0x7e, 0x82, 0x7d, + 0x07, 0x1b, 0xa7, 0x84, 0x2b, 0x3e, 0x90, 0x30, 0x53, 0x83, 0x89, 0x6e, 0xc4, 0x90, + 0x5f, 0x70, 0xc7, 0x8b, 0x69, 0x4e, 0x6a, 0x5a, 0x3e, 0x43, 0x12, 0xcd, 0x82, 0x08, + 0x13, 0x2b, 0x84, 0x0f, 0x05, 0xc7, 0x14, 0x52, 0x3c, 0xa8, 0x19, 0x72, 0x0a, 0xe2, + 0x27, 0xfd, 0x1a, 0xcb, 0xa7, 0x14, 0xfa, 0x4f, 0xc4, 0x5f, 0xc5, 0x39, 0x88, 0x57, + 0xb4, 0x0d, 0xc1, 0x48, 0x79, 0x85, 0x6f, 0x35, 0x4b, 0xa4, 0xd2, 0x58, 0x1d, 0x0c, + 0xda, 0x54, 0xb6, 0x38, 0xba, 0x9d, 0x76, 0xf9, 0xb5, 0x2d, 0x17, 0xc8, 0xf8, 0x8e, + 0xe6, 0x3f, 0x58, 0x45, 0xb5, 0xdc, 0xef, 0xa4, 0xc3, 0x47, 0x9b, 0xce, 0x9a, 0xca, + 0xd1, 0x8b, 0x4a, 0xea, 0xe0, 0x3c, 0x0e, 0xae, 0x22, 0x5d, 0x42, 0x84, 0x8b, 0xde, + 0xaa, 0x53, 0x6d, 0x7d, 0x8d, 0xd3, 0xbc, 0x97, 0x9f, 0x06, 0x58, 0x66, 0x73, 0xbc, + 0x6f, 0xf1, 0xc5, 0xd3, 0xb3, 0x20, 0xf3, 0x49, 0xa5, 0xb3, 0xa8, 0xb3, 0x55, 0x59, + 0x22, 0x96, 0xaa, 0xf6, 0x1c, 0x5b, 0x72, 0x52, 0xf7, 0x3e, 0xc0, 0xa9, 0x46, 0x6a, + 0x1b, 0x85, 0x76, 0x4f, 0xb0, 0x83, 0x1b, 0x4a, 0x1a, 0x36, 0x89, 0x0e, 0x22, 0x4c, + 0x01, 0xac, 0xfc, 0xe4, 0x8e, 0xe3, 0xed, 0x93, 0x87, 0x73, 0x98, 0xe0, 0x72, 0x6d, + 0x02, 0x93, 0x6d, 0x0d, 0x03, 0x2e, 0x18, 0xe3, 0x28, 0x8b, 0x26, 0x70, 0xe1, 0x36, + 0x2c, 0x32, 0xd6, 0xe4, 0x73, 0x3b, 0x9d, 0xd2, 0xd5, 0xf2, 0x6e, 0x1f, 0xe3, 0x06, + 0xf7, 0x3c, 0x00, 0x7f, 0xdd, 0xca, 0xe9, 0xd9, 0xc0, 0xaa, 0xf1, 0x87, 0xd7, 0x42, + 0x8b, 0x1e, 0x9d, 0x47, 0x9c, 0x18, 0x23, 0x7b, 0x98, 0x28, 0xbc, 0xa8, 0xb9, 0x8c, + 0x9d, 0x9b, 0xec, 0x7d, 0x82, 0x70, 0xb5, 0xd8, 0xee, 0xc3, 0xcc, 0x4f, 0x43, 0xfa, + 0x01, 0x88, 0x52, 0x1b, 0xc6, 0x1b, 0x21, 0xdd, 0x04, 0xe3, 0x7a, 0x83, 0xec, 0xe6, + 0x8c, 0xa7, 0xa2, 0xfa, 0x6c, 0x8f, 0x9e, 0x34, 0xa6, 0x29, 0x03, 0x35, 0xaa, 0x1f, + 0xbd, 0x83, 0xd5, 0x4a, 0xaf, 0x44, 0x1e, 0x31, 0x9e, 0xa4, 0x7a, 0x86, 0x2a, 0xd0, + 0x29, 0x3c, 0xed, 0xf5, 0xdd, 0x9e, 0xda, 0xde, 0xee, 0x33, 0xcb, 0x52, 0x2c, 0xd0, + 0x11, 0x8b, 0xbd, 0x81, 0x1a, 0xce, 0x9a, 0x23, 0xbd, 0xa3, 0x9a, 0xba, 0x72, 0xf1, + 0x56, 0x6f, 0xc1, 0x68, 0x84, 0x97, 0xd2, 0xa7, 0x92, 0x8c, 0x36, 0x70, 0x15, 0x25, + 0x67, 0x8b, 0xc9, 0x72, 0x14, 0xb3, 0x1b, 0x37, 0xba, 0xb4, 0x6b, 0x88, 0xf2, 0x7f, + 0x04, 0x48, 0xde, 0xcb, 0x31, 0x62, 0x2d, 0x0f, 0x0f, 0x87, 0xa8, 0x55, 0xba, 0x54, + 0x00, 0x03, 0x32, 0x03, 0x1f, 0x73, 0xab, 0xff, 0xd4, 0x65, 0x91, 0xda, 0x0b, 0x88, + 0x72, 0x35, 0x04, 0xed, 0xb2, 0x33, 0x72, 0x30, 0xda, 0xd2, 0xac, 0xc0, 0xd8, 0xbb, + 0x68, 0xbc, 0x83, 0x7a, 0x2f, 0xf9, 0x30, 0xbf, 0xf0, 0x6f, 0xde, 0x74, 0xeb, 0x90, + 0xaa, 0xe4, 0xf6, 0x0d, 0xbb, 0x6e, 0xb8, 0x27, 0xea, 0x99, 0x88, 0x4a, 0xcd, 0x62, + 0x85, 0xa9, 0x88, 0x92, 0x80, 0x2c, 0xf5, 0x9d, 0x5d, 0x60, 0xd0, 0x16, 0x63, 0x38, + 0x7b, 0x3e, 0xd2, 0x72, 0x3b, 0xd6, 0x48, 0x9e, 0x9c, 0x2c, 0x10, 0x6d, 0x4a, 0xa2, + 0xde, 0x23, 0xce, 0xd1, 0x6c, 0x72, 0x04, 0x29, 0xc7, 0x75, 0x3a, 0x77, 0x38, 0xec, + 0x7d, 0x9d, 0xb8, 0x62, 0x42, 0x29, 0xed, 0xd2, 0x17, 0xb8, 0x0d, 0x74, 0x87, 0x5a, + 0x14, 0xca, 0xe4, 0x86, 0x3f, 0x13, 0x9e, 0x9c, 0x0b, 0x13, 0x1b, 0x2a, 0x4c, 0x28, + 0x07, 0x1a, 0x38, 0xec, 0x61, 0xf6, 0x68, 0x01, 0xaa, 0x59, 0x56, 0xfc, 0xb2, 0xa4, + 0x6b, 0x95, 0x87, 0x66, 0x5b, 0x75, 0x71, 0xaa, 0x03, 0x48, 0x1f, 0xd8, 0xd9, 0xd5, + 0x69, 0x8f, 0x83, 0x6f, 0xc8, 0x63, 0x5e, 0x69, 0xe3, 0xbd, 0xe4, 0x2f, 0x4a, 0xc0, + 0x71, 0x32, 0x8b, 0x54, 0x09, 0xf6, 0xe4, 0x2d, 0x79, 0x0a, 0xed, 0xd7, 0x3b, 0xc1, + 0xa2, 0x35, 0x47, 0x23, 0xb3, 0xb8, 0x19, 0xd0, 0x63, 0x7a, 0x6f, 0xa4, 0x66, 0x39, + 0x46, 0xa3, 0x0a, 0xc5, 0xaf, 0xdd, 0x30, 0xce, 0x83, 0x0f, 0x67, 0x91, 0xb4, 0x57, + 0x52, 0x70, 0xa1, 0x72, 0x0f, 0x91, 0x86, 0x6e, 0x2b, 0x86, 0xf4, 0x78, 0x88, 0x94, + 0xc8, 0xda, 0x62, 0xd8, 0xb9, 0x1f, 0xaf, 0x52, 0x0e, 0x3b, 0xed, 0xbc, 0x12, 0x06, + 0xa5, 0xa5, 0xe6, 0xef, 0xd3, 0xdf, 0xde, 0x08, 0x43, 0xc3, 0xb0, 0x67, 0x57, 0x64, + 0x3f, 0xc0, 0x06, 0x00, 0x88, 0x38, 0xca, 0x47, 0x30, 0x87, 0xf8, 0x97, 0x79, 0x18, + 0xcc, 0x1b, 0x81, 0xc9, 0xe6, 0x8e, 0x3b, 0x88, 0x8f, 0xe6, 0xf7, 0xc6, 0x30, 0xf1, + 0xbc, 0x7a, 0xe1, 0x88, 0xf5, 0x12, 0x84, 0x20, 0x41, 0xca, 0xda, 0x1e, 0x05, 0xf8, + 0x66, 0xd2, 0x56, 0x2d, 0xbe, 0x09, 0xc4, 0xb4, 0x30, 0x68, 0xf7, 0x54, 0xda, 0xd3, + 0x4d, 0xf0, 0xfc, 0xfc, 0x18, 0x1f, 0x31, 0x80, 0x1a, 0x79, 0x92, 0xd2, 0xf1, 0x6b, + 0xe0, 0x21, 0x1b, 0x4a, 0x22, 0xf6, 0x2a, 0xab, 0x64, 0x70, 0x1b, 0xf4, 0xa4, 0xe6, + 0xd6, 0x66, 0xfc, 0x30, 0x4a, 0x5c, 0x79, 0xc6, 0x09, 0xac, 0xc4, 0x3b, 0x00, 0xb4, + 0x86, 0x48, 0x93, 0xd3, 0x7d, 0x50, 0x07, 0xf0, 0xc3, 0x29, 0xa4, 0x75, 0x50, 0x52, + 0x57, 0x75, 0x70, 0xdd, 0x38, 0xfa, 0xc0, 0x43, 0xcd, 0x91, 0xc1, 0x2e, 0xe3, 0x4e, + 0x9c, 0xfa, 0xe3, 0x92, 0xa7, 0x8b, 0xda, 0xbd, 0x4e, 0xe3, 0x1d, 0xc0, 0xde, 0xb0, + 0x2f, 0xe7, 0xb1, 0xd8, 0xb0, 0x17, 0x8a, 0xc9, 0x51, 0x31, 0x05, 0xfc, 0xc7, 0xe3, + 0x0b, 0xa8, 0xe0, 0x16, 0xaa, 0x36, 0xa6, 0xb5, 0xdf, 0x5e, 0x5a, 0x19, 0x09, 0xf6, + 0x3a, 0xba, 0x09, 0x5d, 0x98, 0x77, 0xa8, 0xf2, 0xdc, 0x53, 0xf4, 0x6f, 0x6c, 0x9b, + 0x07, 0xad, 0xdf, 0x14, 0x6f, 0x4f, 0xfa, 0x50, 0x1f, 0x9d, 0xd3, 0xcf, 0xf9, 0x24, + 0xe3, 0x01, 0x0f, 0xaf, 0x50, 0x4e, 0x2b, 0x8a, 0xca, 0x73, 0x57, 0xac, 0xbf, 0xfe, + 0xc7, 0x3a, 0xc3, 0x4c, 0x1a, 0x73, 0x16, 0x0f, 0x2c, 0xea, 0x1e, 0x05, 0x10, 0xf8, + 0x4d, 0x2f, 0xe2, 0xf7, 0x3b, 0x6e, 0x92, 0x19, 0x07, 0xa1, 0xb7, 0xb3, 0x75, 0x12, + 0x13, 0x24, 0x1b, 0x2c, 0xfa, 0xa5, 0x5a, 0x5e, 0xa4, 0xdd, 0x51, 0x7e, 0x7b, 0x49, + 0xd2, 0xde, 0x8c, 0x09, 0x08, 0x43, 0x73, 0x0d, 0x24, 0x08, 0xa2, 0xa3, 0x04, 0xaa, + 0x1e, 0x2e, 0x13, 0x70, 0xa6, 0xbf, 0x6c, 0x2b, 0xc7, 0x3f, 0xf0, 0x0d, 0x89, 0x3b, + 0xc1, 0x28, 0x5e, 0xfc, 0xa8, 0x25, 0x99, 0xd1, 0x81, 0xf1, 0x23, 0x51, 0xf9, 0x39, + 0xa9, 0x4e, 0xa8, 0xb9, 0x75, 0xc0, 0x65, 0xa9, 0x1f, 0xf2, 0x57, 0xca, 0xc7, 0xa9, + 0x23, 0x85, 0xfc, 0x8f, 0xa9, 0x21, 0xb1, 0x06, 0xba, 0x86, 0x60, 0xc6, 0x0a, 0xc8, + 0xba, 0x5e, 0xce, 0x45, 0x60, 0x6f, 0x04, 0xf3, 0x6a, 0x3a, 0x90, 0xbb, 0x38, 0x38, + 0xc4, 0x2a, 0xbf, 0x62, 0xdd, 0x2d, 0x84, 0xba, 0xbe, 0xf3, 0xe1, 0x88, 0xe9, 0x17, + 0x1a, 0xff, 0x9b, 0xc1, 0x16, 0x66, 0x90, 0x09, 0xd8, 0x87, 0x13, 0x0a, 0xc9, 0xf7, + 0x39, 0x6a, 0x62, 0x7a, 0x84, 0x74, 0xc1, 0x81, 0x1b, 0x69, 0x6f, 0x99, 0x55, 0x2b, + 0x14, 0xc4, 0x84, 0xdf, 0xe4, 0x2c, 0x24, 0xd5, 0x7c, 0x3a, 0x9c, 0x3f, 0xea, 0x13, + 0x76, 0xcd, 0xcb, 0x63, 0x42, 0x1c, 0x31, 0x4a, 0x62, 0x2a, 0x9a, 0xef, 0x0b, 0xc0, + 0x57, 0xcb, 0x11, 0xbc, 0x5e, 0x30, 0x66, 0xe3, 0x3a, 0x3b, 0x9b, 0x31, 0xdf, 0x25, + 0x75, 0xcd, 0x51, 0x85, 0xa4, 0xf3, 0xfc, 0x4e, 0x4c, 0x3d, 0x40, 0x2e, 0xd4, 0x20, + 0x46, 0xf8, 0x1f, 0x97, 0x48, 0x16, 0xd2, 0x79, 0xb1, 0x51, 0x3a, 0xb8, 0x1d, 0x3f, + 0x0a, 0x3c, 0x7f, 0x7f, 0xcf, 0x2f, 0xbb, 0x4e, 0x26, 0x32, 0x19, 0x93, 0xa5, 0x13, + 0xad, 0x3d, 0x7f, 0x4a, 0xfe, 0x6c, 0x1b, 0xbd, 0xc6, 0x57, 0x58, 0x50, 0x80, 0xbb, + 0x5a, 0x0f, 0x25, 0x97, 0x3d, 0x63, 0xeb, 0x20, 0xad, 0xa0, 0x16, 0x6b, 0xbd, 0x8a, + 0x39, 0xff, 0x93, 0x24, 0x6f, 0x27, 0x89, 0x73, 0x2a, 0xd0, 0x55, 0x87, 0xf8, 0xdb, + 0x7b, 0xc8, 0x7c, 0x24, 0x2c, 0xfd, 0x36, 0xce, 0x68, 0x5a, 0x4b, 0x65, 0x69, 0x86, + 0xc3, 0x9f, 0xd7, 0xfc, 0xb2, 0x3c, 0x91, 0x91, 0x3e, 0x46, 0x11, 0x19, 0x1e, 0xdc, + 0xc8, 0x8b, 0x78, 0xf1, 0x45, 0xea, 0x29, 0xd2, 0x71, 0xb9, 0x40, 0xc6, 0x99, 0x41, + 0xe4, 0xc3, 0xfd, 0x2d, 0x71, 0xf3, 0xb1, 0x90, 0x69, 0x0e, 0xe1, 0x6f, 0x5d, 0x14, + 0xac, 0x22, 0x24, 0xe6, 0xfc, 0x89, 0x59, 0x76, 0x54, 0x52, 0x7d, 0xab, 0xe7, 0x2e, + 0x75, 0xd2, 0xd2, 0xa1, 0x3a, 0x9f, 0xba, 0xa6, 0x37, 0x8e, 0x8a, 0x26, 0x43, 0x21, + 0x08, 0x7a, 0x19, 0x00, 0xef, 0xe3, 0xca, 0xd1, 0x4a, 0x57, 0x96, 0x86, 0xaa, 0x36, + 0x36, 0xbd, 0x37, 0x5b, 0xd3, 0x13, 0x6b, 0xee, 0x0b, 0xda, 0xab, 0xcf, 0xac, 0x88, + 0x1b, 0xc7, 0x01, 0x81, 0x27, 0x21, 0xe6, 0xfb, 0x75, 0xaa, 0x07, 0x2d, 0x2d, 0x18, + 0x7e, 0x62, 0x25, 0x8d, 0x65, 0xa1, 0x92, 0x15, 0x7c, 0xdf, 0x2e, 0xc3, 0x21, 0x40, + 0x7f, 0x68, 0x2f, 0x5e, 0xec, 0x6a, 0x32, 0x97, 0xab, 0x20, 0xb7, 0x06, 0x1c, 0x62, + 0x24, 0x57, 0x16, 0xa4, 0x4f, 0x71, 0xfb, 0xfc, 0x34, 0xc7, 0x9b, 0x44, 0xe0, 0x9e, + 0x42, 0x12, 0xac, 0x26, 0x53, 0xf6, 0xc4, 0x03, 0x64, 0x3e, 0x1c, 0x5b, 0x9a, 0xd1, + 0x34, 0xd8, 0x9c, 0x68, 0x0b, 0x70, 0x72, 0x83, 0xaf, 0x54, 0x32, 0x6f, 0xc4, 0xf8, + 0x4d, 0x6a, 0x58, 0x29, 0xa0, 0xad, 0x48, 0x30, 0x80, 0x6c, 0x05, 0x75, 0x84, 0x92, + 0xcd, 0x6a, 0xc4, 0x6b, 0xa0, 0x1a, 0x2b, 0x37, 0x22, 0xb5, 0xe4, 0xcd, 0xaf, 0xbb, + 0x3f, 0x36, 0x78, 0x5f, 0x42, 0x4a, 0xf0, 0x44, 0xda, 0xc5, 0xdb, 0x5f, 0x7d, 0xf8, + 0x39, 0xeb, 0x63, 0xc0, 0xc1, 0x7d, 0x8b, 0x0c, 0x79, 0xdb, 0x86, 0x30, 0x94, 0x20, + 0x15, 0xbe, 0x13, 0xf7, 0x9a, 0xf6, 0xf4, 0x3e, 0x5a, 0xb0, 0x77, 0x81, 0x14, 0x79, + 0x8f, 0x44, 0x22, 0x58, 0xee, 0xdc, 0x43, 0x6f, 0xcc, 0x38, 0x6b, 0x36, 0xb5, 0x7e, + 0x19, 0x17, 0xd7, 0x20, 0x17, 0x73, 0x66, 0xf4, 0x24, 0xb0, 0xa5, 0x4b, 0x0b, 0x60, + 0xf4, 0xfb, 0x13, 0x58, 0xc2, 0x0a, 0xa4, 0x1d, 0xc5, 0x02, 0xe1, 0xdd, 0x8a, 0x16, + 0x33, 0xf3, 0xd8, 0xe3, 0x27, 0x6b, 0x59, 0xe7, 0xd2, 0xc4, 0xe6, 0x24, 0xa6, 0xf5, + 0x36, 0x95, 0xbc, 0xaf, 0x24, 0x7e, 0x36, 0x48, 0x3f, 0x13, 0xb2, 0x04, 0x42, 0x22, + 0x37, 0xfc, 0x6a, 0xb3, 0xeb, 0xa0, 0x2f, 0xc4, 0x14, 0x2b, 0x42, 0x97, 0xeb, 0xb5, + 0x68, 0x3d, 0xb8, 0xd2, 0x43, 0x19, 0x70, 0x6a, 0xd2, 0x6a, 0xaf, 0xd8, 0x1c, 0x53, + 0xb7, 0x40, 0xf3, 0x45, 0x43, 0xa6, 0xb3, 0xe9, 0xf5, 0xbb, 0x7d, 0x5c, 0x49, 0xe8, + 0xc3, 0x7f, 0x61, 0x49, 0x21, 0x25, 0x4f, 0x32, 0x12, 0x39, 0x4c, 0x79, 0x7d, 0x1c, + 0xee, 0x78, 0x99, 0xb7, 0xb4, 0xb6, 0x5b, 0x59, 0xb7, 0x34, 0x2f, 0x92, 0x53, 0x1c, + 0x1d, 0x59, 0xe1, 0x79, 0x70, 0xb7, 0x31, 0x74, 0x14, 0x43, 0x8c, 0xd8, 0x0b, 0xd0, + 0xf9, 0xa6, 0x7c, 0x9b, 0x9e, 0x55, 0x2f, 0x01, 0x3c, 0x11, 0x5a, 0x95, 0x4f, 0x35, + 0xe0, 0x61, 0x6c, 0x68, 0xd4, 0x31, 0x63, 0xd3, 0x34, 0xda, 0xc3, 0x82, 0x70, 0x33, + 0xe5, 0xad, 0x84, 0x88, 0xbf, 0xd9, 0xc4, 0xbb, 0xbe, 0x8f, 0x59, 0x35, 0xc6, 0xc5, + 0xea, 0x04, 0xc3, 0xad, 0x49, 0xc7, 0x47, 0xa9, 0xe7, 0x23, 0x1b, 0xcd, 0x7d, 0x16, + 0x21, 0x5e, 0x6e, 0x80, 0x73, 0x7d, 0x6b, 0x54, 0xfe, 0xc8, 0xb8, 0x84, 0x02, 0xf0, + 0x47, 0x52, 0x45, 0xe1, 0x74, 0xa7, 0x45, 0xb8, 0x31, 0xf8, 0xfe, 0x03, 0xa7, 0x6f, + 0xb9, 0xce, 0xca, 0x4d, 0x22, 0xb7, 0x83, 0xc3, 0x28, 0xc6, 0x91, 0x5c, 0x43, 0x40, + 0x50, 0x64, 0xae, 0x56, 0xbc, 0x89, 0xe6, 0x4d, 0x15, 0x78, 0xe4, 0xd3, 0xa3, 0x4b, + 0xb9, 0x55, 0x91, 0xea, 0xf1, 0xd3, 0xda, 0x02, 0xa4, 0x54, 0x9f, 0xa8, 0x0d, 0xb0, + 0xff, 0x7c, 0xb0, 0x39, 0x93, 0xb6, 0x8a, 0xe1, 0x5a, 0x30, 0xe8, 0x79, 0x49, 0xaa, + 0x08, 0x0e, 0x94, 0xab, 0xde, 0x68, 0x89, 0x8c, 0x33, 0x92, 0xa2, 0x17, 0xd6, 0x49, + 0x61, 0x6b, 0xbe, 0x73, 0x9b, 0x13, 0xd1, 0x4d, 0xf0, 0x3f, 0xf2, 0x76, 0x71, 0x48, + 0x9b, 0xe0, 0xb4, 0xbe, 0xba, 0xaf, 0xa7, 0xd1, 0xe6, 0x39, 0xd5, 0xb3, 0xe9, 0x94, + 0xff, 0xb6, 0xb7, 0xa2, 0x09, 0xf6, 0xad, 0xfe, 0x8d, 0x1e, 0x5c, 0xcf, 0x01, 0x0c, + 0x19, 0x16, 0x8a, 0xeb, 0x18, 0xaa, 0x9d, 0x68, 0x7e, 0x24, 0xad, 0xc0, 0xb1, 0x13, + 0x5c, 0x70, 0xc9, 0x70, 0xe0, 0x90, 0x3a, 0xf6, 0xe1, 0x70, 0x81, 0xd5, 0x81, 0x8e, + 0x88, 0xb1, 0x4e, 0x4f, 0x60, 0x1b, 0x8c, 0x06, 0x3e, 0x3f, 0x43, 0x87, 0xff, 0xa2, + 0x32, 0x2a, 0x51, 0x81, 0x90, 0x9f, 0x09, 0x80, 0xd6, 0x89, 0xde, 0x7f, 0x8e, 0x6a, + 0x5c, 0x62, 0xa7, 0x77, 0xd1, 0x75, 0x00, 0x2a, 0x13, 0x7d, 0xe8, 0x5b, 0x88, 0x88, + 0x92, 0x91, 0x98, 0x11, 0x7a, 0xa5, 0xd6, 0x19, 0x93, 0xe1, 0xdc, 0xf7, 0x58, 0x76, + 0xdc, 0xa6, 0x09, 0xf9, 0xd2, 0x84, 0x71, 0xf9, 0x97, 0xfa, 0x11, 0xf9, 0x9d, 0x42, + 0x3f, 0x9c, 0xf1, 0x73, 0x4b, 0xe8, 0xa5, 0xff, 0x99, 0x7d, 0x45, 0x1e, 0xb3, 0xcf, + 0x4b, 0x3d, 0xfd, 0xd9, 0xd4, 0x54, 0x5c, 0x35, 0xb2, 0xb5, 0xa7, 0xdc, 0x17, 0xa8, + 0x36, 0xb1, 0x2b, 0x43, 0xbe, 0xfc, 0x0b, 0xe0, 0xa1, 0xbd, 0x36, 0x97, 0x72, 0x33, + 0x80, 0x78, 0xb4, 0xff, 0x7d, 0x8e, 0x2d, 0x97, 0x9a, 0x34, 0x41, 0xe1, 0xc8, 0xf5, + 0xaf, 0xe4, 0x7b, 0x1e, 0x7d, 0xa5, 0x6c, 0xf0, 0x06, 0x02, 0xd0, 0x1b, 0x11, 0x0c, + 0x05, 0xcf, 0x48, 0xfd, 0xa3, 0xe6, 0xcc, 0xe3, 0x2a, 0x04, 0x40, 0x00, 0xf4, 0x5c, + 0x6d, 0x1e, 0x69, 0x6d, 0x24, 0x5c, 0xbd, 0x31, 0x2b, 0xdc, 0x3a, 0x3a, 0x21, 0xc9, + 0x92, 0xd0, 0xeb, 0xc8, 0xcc, 0x8f, 0xa6, 0x30, 0x6d, 0x7e, 0x13, 0x0a, 0x2b, 0xa4, + 0x20, 0x18, 0xfe, 0x59, 0x69, 0x49, 0xfd, 0x82, 0x26, 0x7b, 0xcc, 0x59, 0xdd, 0x46, + 0x26, 0xef, 0xc3, 0xea, 0x74, 0x38, 0xd0, 0x5c, 0x91, 0xb0, 0xf8, 0xe0, 0x92, 0x55, + 0x0d, 0x2d, 0x39, 0xa0, 0x1e, 0xb4, 0x5e, 0xe8, 0xf7, 0xd0, 0x9b, 0x03, 0x8d, 0x83, + 0x83, 0xe1, 0x9b, 0xc3, 0x0e, 0x64, 0x03, 0x82, 0x8c, 0xdb, 0x65, 0x2a, 0x55, 0x6b, + 0x12, 0x04, 0x09, 0x31, 0x40, 0x2a, 0xa6, 0xac, 0x34, 0xfc, 0x19, 0xfd, 0xc0, 0x6e, + 0x2e, 0x77, 0x87, 0xf5, 0xb7, 0x7b, 0x04, 0x5f, 0xd0, 0x98, 0xc0, 0x31, 0xbd, 0xbd, + 0x46, 0x27, 0x76, 0x09, 0xd8, 0x42, 0xf4, 0x84, 0x24, 0xed, 0xa3, 0x1e, 0x3c, 0xf2, + 0xcd, 0xd6, 0x43, 0x85, 0xba, 0xd3, 0x11, 0x88, 0x58, 0xd1, 0x42, 0xd9, 0x06, 0xea, + 0xdb, 0x75, 0x90, 0xc9, 0x41, 0x36, 0xda, 0x6a, 0x06, 0x35, 0x14, 0xd6, 0xa2, 0x5f, + 0x7b, 0x37, 0xd7, 0x66, 0x4f, 0x9b, 0x97, 0x09, 0x43, 0x3e, 0x6e, 0x70, 0x21, 0x18, + 0xa4, 0xab, 0x9e, 0x7a, 0x7a, 0x3e, 0x62, 0x59, 0x12, 0x99, 0x37, 0xd2, 0x9d, 0x0d, + 0xb2, 0x60, 0x70, 0x52, 0x3e, 0x8b, 0x06, 0x43, 0x13, 0x0a, 0xbe, 0xfe, 0x94, 0x3b, + 0x40, 0x12, 0x98, 0xae, 0x01, 0xa3, 0xab, 0x00, 0xab, 0xbc, 0x60, 0xd7, 0xdb, 0x93, + 0x3c, 0x7f, 0x07, 0xa8, 0xbf, 0x0f, 0x7c, 0xe1, 0x66, 0x0b, 0xcc, 0xb4, 0x5e, 0x04, + 0x2b, 0x45, 0x1b, 0x93, 0x50, 0x02, 0xce, 0xce, 0x27, 0xf3, 0x6a, 0xba, 0x56, 0x47, + 0xac, 0x28, 0xd8, 0x18, 0x6c, 0xdd, 0x1f, 0xb9, 0x5d, 0xc1, 0x35, 0xd4, 0x89, 0x92, + 0xf6, 0x8d, 0xa1, 0x2a, 0xd6, 0x1a, 0xc7, 0x56, 0x68, 0x0d, 0xd7, 0xf8, 0xd0, 0x77, + 0x4a, 0xbd, 0x6c, 0xfd, 0xa2, 0xf0, 0x32, 0xaf, 0x3b, 0xe1, 0x39, 0xa6, 0x33, 0xd6, + 0x73, 0x3c, 0x75, 0xd1, 0xab, 0xa8, 0x90, 0x18, 0xc8, 0x57, 0x2b, 0x99, 0xcd, 0x30, + 0xc5, 0x37, 0x06, 0x79, 0x41, 0xdf, 0x1c, 0x4b, 0xc1, 0xfd, 0x57, 0x0f, 0x7b, 0x4d, + 0xdc, 0x97, 0x51, 0x86, 0x23, 0xe3, 0xae, 0x4a, 0x87, 0xbd, 0xb9, 0x66, 0xc9, 0x4d, + 0x86, 0x1e, 0x80, 0xde, 0x88, 0xc2, 0x92, 0xae, 0xe9, 0x38, 0x71, 0x94, 0xe2, 0x56, + 0xc6, 0x70, 0x07, 0x52, 0x30, 0x1c, 0x73, 0xfc, 0x95, 0x65, 0xa4, 0x04, 0x80, 0xd8, + 0x12, 0x6e, 0x9d, 0x08, 0x58, 0x79, 0xe2, 0x4b, 0x16, 0xe9, 0xc4, 0x85, 0xd8, 0xf0, + 0xd6, 0x18, 0xca, 0x0d, 0xd1, 0x21, 0xb5, 0x1a, 0x7c, 0xab, 0x23, 0x0c, 0x5b, 0x45, + 0x67, 0x2b, 0xdb, 0x8e, 0xa3, 0xa0, 0x40, 0xf7, 0xaa, 0xa0, 0x98, 0xba, 0x26, 0x02, + 0x5d, 0x2e, 0xab, 0x79, 0x48, 0x69, 0x3d, 0xd5, 0xf6, 0xd3, 0x09, 0x65, 0x01, 0xe9, + 0xe0, 0x71, 0x25, 0xd7, 0xeb, 0x29, 0x3b, 0x3a, 0xba, 0xd5, 0x7f, 0xd5, 0xf0, 0x11, + 0x64, 0x70, 0x2d, 0xae, 0x64, 0xbd, 0xba, 0x8c, 0x92, 0x4f, 0xb0, 0x79, 0x96, 0x79, + 0xd7, 0x7f, 0x98, 0xd3, 0x03, 0x91, 0x9f, 0xb4, 0xa7, 0xff, 0x26, 0xa9, 0x6f, 0x13, + 0x7a, 0x5e, 0x5c, 0xb9, 0x5b, 0xc4, 0xc6, 0xff, 0x99, 0x93, 0x52, 0x6b, 0xda, 0x15, + 0x03, 0x16, 0x8a, 0xb4, 0x8c, 0xbd, 0x45, 0x15, 0x39, 0x27, 0xd3, 0x04, 0x30, 0x42, + 0x3d, 0xbd, 0xf0, 0x66, 0x05, 0xf5, 0xb5, 0x4b, 0x80, 0x8f, 0xeb, 0x22, 0xb2, 0x08, + 0xb0, 0x64, 0x58, 0x18, 0x47, 0xb2, 0xf6, 0x4c, 0xa6, 0x48, 0x37, 0x00, 0x72, 0x16, + 0xde, 0x6e, 0xca, 0xff, 0xeb, 0x4b, 0x69, 0xe6, 0x33, 0x47, 0xf8, 0x4a, 0xbc, 0xad, + 0x8f, 0x2e, 0x75, 0x7d, 0x58, 0x61, 0xce, 0x77, 0xee, 0x46, 0x51, 0x3d, 0xa7, 0x41, + 0x68, 0x37, 0xdc, 0xb2, 0x3d, 0x33, 0xea, 0x72, 0xaf, 0x23, 0xd0, 0xad, 0x8c, 0x93, + 0x07, 0xd0, 0xb5, 0x85, 0x8d, 0xa9, 0x5b, 0x77, 0xff, 0xf9, 0x02, 0x7b, 0x88, 0x59, + 0xe1, 0x1d, 0xcb, 0xd5, 0x98, 0x35, 0x0e, 0xee, 0x50, 0x93, 0x94, 0x81, 0x70, 0x8e, + 0xa7, 0x08, 0xeb, 0x9f, 0x66, 0x43, 0x88, 0xb9, 0xc6, 0x4d, 0x6a, 0xf0, 0xf9, 0x66, + 0x90, 0x34, 0x24, 0x00, 0x34, 0x8e, 0x92, 0x9e, 0x07, 0x46, 0x02, 0x53, 0xf3, 0x83, + 0x90, 0xf8, 0x7b, 0xd6, 0xc0, 0x53, 0x08, 0xc3, 0xbd, 0xe2, 0x52, 0x28, 0xe0, 0xfa, + 0x08, 0x80, 0xb0, 0x8e, 0xf3, 0x4a, 0x5a, 0x9c, 0xc0, 0xea, 0x0a, 0x67, 0xca, 0x65, + 0xb6, 0xff, 0xd0, 0x05, 0x57, 0x29, 0x09, 0xf1, 0xc4, 0x2d, 0xd7, 0x45, 0xee, 0xee, + 0x9d, 0xd6, 0xb4, 0x43, 0x9c, 0x9f, 0x3f, 0x98, 0xa1, 0x18, 0xfe, 0x16, 0x69, 0x8e, + 0x9c, 0xef, 0xf5, 0x58, 0xf1, 0x60, 0x66, 0x97, 0x5f, 0xe3, 0x95, 0x83, 0xe9, 0xb5, + 0x85, 0x3b, 0x13, 0x11, 0x39, 0x15, 0x80, 0x01, 0x9f, 0xe5, 0x5d, 0x59, 0xd1, 0xc8, + 0x28, 0xd3, 0xfe, 0xb6, 0xa3, 0xb9, 0xce, 0x92, 0xd0, 0x89, 0xae, 0x4b, 0x40, 0x8e, + 0x23, 0xd6, 0xa4, 0x37, 0xd4, 0x98, 0x9b, 0x51, 0x9b, 0x7a, 0x9e, 0xb0, 0x8a, 0xe6, + 0xd4, 0x48, 0xa7, 0xa1, 0x6e, 0x8a, 0xed, 0x26, 0xa2, 0xec, 0xd0, 0xca, 0xd8, 0x08, + 0x44, 0xfd, 0x06, 0x50, 0xd8, 0xc4, 0xe4, 0xd2, 0xaf, 0x90, 0x65, 0x67, 0x48, 0xd8, + 0x09, 0x9a, 0x0c, 0x75, 0x6f, 0xc1, 0x6c, 0xca, 0x06, 0xa3, 0x34, 0x43, 0x07, 0x02, + 0xae, 0x19, 0x61, 0x66, 0x5b, 0x48, 0x45, 0xac, 0xd1, 0xa8, 0xe3, 0x41, 0x01, 0xe6, + 0x8b, 0xb6, 0x44, 0xac, 0x03, 0x4d, 0xc6, 0x3e, 0x6e, 0x34, 0x4c, 0x3d, 0x63, 0x76, + 0x2a, 0x7a, 0x5b, 0xf5, 0x9f, 0x13, 0x09, 0x54, 0x10, 0x98, 0x1d, 0x6b, 0x6b, 0x16, + 0xbc, 0xd4, 0xc9, 0xfa, 0x68, 0xaf, 0x6e, 0x53, 0x65, 0xe9, 0x4e, 0xcb, 0xe7, 0xab, + 0x8b, 0x80, 0x43, 0xdf, 0xba, 0xcb, 0x23, 0xc8, 0x4d, 0x71, 0xa8, 0xfe, 0x5d, 0x9a, + 0xc5, 0x50, 0x2c, 0xe9, 0xf7, 0x3f, 0x40, 0x8e, 0x14, 0x37, 0x6d, 0xb8, 0x6e, 0xf5, + 0x7c, 0xc3, 0x7d, 0x09, 0x89, 0x6f, 0xa9, 0x06, 0x97, 0x2e, 0x55, 0x71, 0x80, 0xa4, + 0xab, 0x5a, 0xd0, 0x9d, 0x88, 0x46, 0xdd, 0x6d, 0xa7, 0x48, 0x76, 0x54, 0x36, 0xe0, + 0x16, 0x02, 0x40, 0xf8, 0xd4, 0x1c, 0x0a, 0xc7, 0x83, 0xf9, 0x39, 0xf2, 0xd0, 0xed, + 0x26, 0x2c, 0xe8, 0x59, 0xc1, 0x31, 0xeb, 0xc9, 0x3f, 0xf2, 0xe6, 0xe4, 0x07, 0xd4, + 0xe2, 0x43, 0xe1, 0xe9, 0x31, 0xd5, 0x3a, 0x45, 0x43, 0xb6, 0xe2, 0x6d, 0x82, 0x59, + 0x6f, 0xc5, 0x3b, 0x52, 0x31, 0x2c, 0x77, 0x6d, 0x12, 0xeb, 0x2b, 0x65, 0x9b, 0x4f, + 0xb0, 0x98, 0xdf, 0x87, 0xd6, 0x83, 0xcf, 0x9e, 0x54, 0x12, 0xee, 0x56, 0xc3, 0xfe, + 0x98, 0x41, 0xd7, 0x3f, 0xd0, 0x70, 0xdf, 0xa5, 0x1f, 0x5b, 0xaf, 0xed, 0xf2, 0x06, + 0xf1, 0x3c, 0x52, 0x4e, 0x5c, 0x50, 0xca, 0xc9, 0x90, 0x6e, 0xfa, 0x39, 0x32, 0x90, + 0x04, 0x2e, 0x3b, 0xc5, 0x9f, 0x96, 0x0b, 0x7d, 0x24, 0x0a, 0xe4, 0x43, 0xfc, 0x49, + 0x26, 0x9c, 0xe0, 0x00, 0x61, 0xe6, 0x5c, 0x6d, 0x74, 0x81, 0x2a, 0x30, 0xdd, 0x5f, + 0x5f, 0xe7, 0x4e, 0xff, 0x61, 0xe0, 0xcb, 0xab, 0x3c, 0xec, 0x75, 0xd0, 0xae, 0xf9, + 0x50, 0x83, 0x18, 0x94, 0x52, 0xdd, 0x3d, 0x9e, 0xdf, 0x44, 0x87, 0xbc, 0x73, 0x4c, + 0x8b, 0x24, 0xf2, 0x12, 0x96, 0xe4, 0xe9, 0xef, 0x11, 0x7d, 0x7f, 0xb9, 0x77, 0xe3, + 0xb0, 0xe6, 0x40, 0x6e, 0x63, 0x08, 0x59, 0x06, 0x33, 0x1a, 0x93, 0x03, 0x3d, 0x1c, + 0xb8, 0x36, 0x0f, 0xe6, 0xfe, 0xa6, 0x1a, 0x68, 0x26, 0xdf, 0x36, 0x25, 0x57, 0x89, + 0xf9, 0x2e, 0x40, 0xba, 0xfc, 0xb2, 0xeb, 0xcb, 0x9e, 0x55, 0x6f, 0x6c, 0x0c, 0xca, + 0xdc, 0x6a, 0xf0, 0x8e, 0x31, 0xec, 0x4a, 0xd5, 0x28, 0x80, 0x34, 0xe1, 0x6d, 0x15, + 0x5c, 0xfd, 0xca, 0xda, 0x7b, 0xab, 0x59, 0x9c, 0x2f, 0xa4, 0xad, 0x2e, 0x62, 0x93, + 0xf9, 0xfe, 0x09, 0x71, 0x69, 0x14, 0x82, 0x76, 0xb6, 0xa9, 0xea, 0xa7, 0x2f, 0x14, + 0x8b, 0x0c, 0x95, 0x65, 0xc3, 0xc2, 0xdd, 0x63, 0x12, 0x5e, 0x0f, 0xa5, 0x30, 0x86, + 0x1a, 0x71, 0x0d, 0xf8, 0xe4, 0x81, 0xf2, 0x71, 0x29, 0x20, 0xf8, 0x78, 0x7e, 0x0a, + 0xed, 0xfe, 0x61, 0x8a, 0xff, 0x50, 0xa3, 0xb5, 0x62, 0x13, 0x88, 0x4d, 0x62, 0x62, + 0xc1, 0x1d, 0xeb, 0xf2, 0xba, 0x7e, 0x8a, 0xd6, 0x69, 0x2c, 0xb1, 0x70, 0x78, 0x33, + 0x14, 0x18, 0xda, 0x4b, 0xe0, 0x64, 0xff, 0x52, 0x70, 0x07, 0x39, 0x34, 0xab, 0xcd, + 0x2a, 0xb0, 0x46, 0x9e, 0xca, 0xf7, 0x27, 0x5b, 0x4b, 0xd7, 0x2b, 0xc6, 0xed, 0x34, + 0x47, 0x8e, 0xa4, 0x08, 0x9b, 0x73, 0x6a, 0x16, 0xdd, 0x90, 0x6d, 0x49, 0xf2, 0x5c, + 0x33, 0x82, 0x7c, 0x57, 0x1c, 0xe0, 0xb5, 0xd7, 0x21, 0x77, 0xaa, 0x35, 0x08, 0x80, + 0x4b, 0xc0, 0xf8, 0xfa, 0xa9, 0x47, 0x12, 0x22, 0x31, 0x40, 0x2d, 0x2f, 0x5c, 0xc9, + 0xa0, 0xeb, 0x0e, 0x09, 0xd4, 0x27, 0xb4, 0x27, 0x28, 0x8d, 0x93, 0x7d, 0x9d, 0x72, + 0xb7, 0x74, 0x56, 0xf8, 0x86, 0x59, 0x4c, 0xd8, 0xc6, 0xa4, 0x62, 0xf7, 0x7f, 0xd8, + 0x30, 0x76, 0x46, 0x9c, 0xc0, 0xec, 0xba, 0x3c, 0xc4, 0x0c, 0xad, 0x69, 0xe5, 0xb5, + 0x41, 0x12, 0xea, 0xb3, 0x33, 0x96, 0xae, 0xcf, 0xbc, 0x21, 0x1f, 0x1f, 0x79, 0xcf, + 0x33, 0x10, 0x8e, 0x93, 0xd9, 0x53, 0x78, 0xba, 0xe6, 0x95, 0x82, 0x74, 0xb3, 0x10, + 0x88, 0xfb, 0xd8, 0xb3, 0xa3, 0xa0, 0xd1, 0x54, 0xa7, 0x89, 0x73, 0x5b, 0x03, 0x49, + 0xc4, 0xd5, 0x1c, 0x88, 0x9d, 0x08, 0x95, 0x2d, 0xdd, 0x54, 0x88, 0xbe, 0x95, 0x56, + 0x05, 0x94, 0xe6, 0x73, 0xfa, 0x05, 0x1b, 0xf9, 0xb6, 0x14, 0xa1, 0x5e, 0x10, 0x0b, + 0x60, 0xa0, 0xfe, 0x9a, 0x7e, 0x12, 0xa9, 0xb2, 0x56, 0xdf, 0x58, 0x9b, 0x3e, 0x48, + 0xe5, 0xb8, 0x0f, 0xb8, 0xcf, 0xf0, 0x3e, 0x86, 0xf6, 0x0c, 0xc0, 0x70, 0xfb, 0x23, + 0xc9, 0x7d, 0x4c, 0x14, 0xfa, 0x3a, 0x73, 0x46, 0xff, 0x55, 0x6b, 0xc6, 0x85, 0x5a, + 0x5f, 0x83, 0xe3, 0xdc, 0xd9, 0xf6, 0xea, 0xb3, 0xda, 0xbc, 0xd4, 0x77, 0x50, 0xe3, + 0x4e, 0x7c, 0x09, 0x38, 0xf6, 0x4d, 0x45, 0x1e, 0x39, 0x50, 0x9e, 0x90, 0x27, 0x47, + 0xa7, 0x07, 0x55, 0x12, 0x20, 0x95, 0x08, 0x2a, 0xb7, 0x98, 0x59, 0x19, 0x07, 0x31, + 0x41, 0xb6, 0xd3, 0x70, 0x20, 0x91, 0xab, 0x71, 0x72, 0x80, 0xbd, 0xc5, 0x5e, 0x79, + 0x9c, 0x01, 0xad, 0x86, 0x41, 0x90, 0x4e, 0x3b, 0x1d, 0xd2, 0x9e, 0x1a, 0x96, 0x4c, + 0x73, 0x7d, 0x3c, 0x15, 0x5a, 0xfb, 0x30, 0x7b, 0x74, 0x8e, 0x41, 0x12, 0xb4, 0x8b, + 0x77, 0xd5, 0xed, 0x57, 0x00, 0xe6, 0x00, 0x2b, 0x18, 0xb0, 0xfe, 0xd2, 0xcf, 0xfd, + 0xf6, 0x1f, 0xd9, 0x93, 0x4b, 0x60, 0x73, 0x2f, 0x4d, 0x37, 0x81, 0x0a, 0x91, 0xac, + 0xef, 0x1e, 0x03, 0x8b, 0x81, 0xd7, 0x36, 0xd9, 0x8e, 0xad, 0xa9, 0xcd, 0x7e, 0x0c, + 0x2b, 0xe2, 0x7a, 0xb8, 0x50, 0x32, 0x06, 0x60, 0x91, 0x22, 0x4e, 0xdf, 0x87, 0x2f, + 0x79, 0x63, 0x7d, 0xda, 0x39, 0x16, 0x79, 0x6a, 0x5c, 0x62, 0xf5, 0x7f, 0x1d, 0xe3, + 0x76, 0x78, 0xb6, 0xde, 0xa0, 0x08, 0x69, 0x93, 0x36, 0x74, 0xf8, 0x8e, 0x41, 0xa9, + 0x18, 0x08, 0x07, 0x3b, 0x0f, 0x43, 0x6e, 0xbe, 0x25, 0xa5, 0xf4, 0x4a, 0x60, 0x10, + 0x33, 0xe2, 0x18, 0x4b, 0x88, 0xdb, 0x79, 0xe9, 0x68, 0xca, 0x6d, 0x89, 0xb7, 0x49, + 0x01, 0xbe, 0x6c, 0x6d, 0xb3, 0x63, 0x65, 0x80, 0x18, 0x2e, 0x65, 0x8d, 0xfc, 0x68, + 0x67, 0x67, 0xd6, 0xd8, 0x19, 0xfa, 0x92, 0x3e, 0x0c, 0xdf, 0x3e, 0xa3, 0x65, 0x76, + 0xf8, 0x52, 0xbc, 0xd4, 0xe1, 0x96, 0xa7, 0x1a, 0x13, 0x29, 0xf6, 0xc3, 0xff, 0x8e, + 0x42, 0xe3, 0x09, 0x5a, 0xbd, 0x8e, 0xc1, 0x97, 0x99, 0x07, 0x13, 0xee, 0x89, 0x39, + 0x4c, 0x57, 0x19, 0xb2, 0x76, 0xde, 0x8f, 0x81, 0x8a, 0x34, 0xa7, 0xbe, 0xc1, 0xf2, + 0x68, 0x68, 0x2e, 0x91, 0x42, 0xc7, 0xd3, 0x87, 0x89, 0xf6, 0x76, 0xcc, 0x12, 0xb7, + 0x1a, 0xb6, 0x66, 0x35, 0xc5, 0x02, 0xe6, 0x9d, 0x05, 0xb9, 0xc7, 0xef, 0x01, 0x52, + 0x97, 0x75, 0xc6, 0x23, 0xa4, 0x8e, 0x4c, 0xc5, 0xc4, 0x15, 0xc9, 0xfd, 0x56, 0x53, + 0x65, 0xa4, 0x16, 0x37, 0x68, 0x78, 0x51, 0x53, 0x88, 0x7f, 0xb5, 0xf9, 0x63, 0xe7, + 0xac, 0xc1, 0x62, 0xf2, 0x80, 0x5f, 0x45, 0xf4, 0x44, 0x87, 0xf8, 0x5e, 0x19, 0x9c, + 0x1d, 0xf4, 0xa0, 0xfc, 0xa4, 0xd4, 0x4b, 0xaa, 0x62, 0xda, 0x7a, 0xf5, 0xed, 0x69, + 0x68, 0x41, 0x12, 0xd3, 0x5f, 0x36, 0x73, 0x73, 0x2f, 0x5a, 0x1a, 0xc3, 0xe4, 0xf0, + 0x21, 0xba, 0x5c, 0x2c, 0x32, 0xf0, 0x6e, 0x6b, 0x90, 0xfa, 0xe2, 0xd2, 0x54, 0xcf, + 0x09, 0xe7, 0x69, 0x0c, 0xf4, 0xe3, 0xaa, 0x70, 0x30, 0x98, 0x74, 0x48, 0xe1, 0x47, + 0xf9, 0x43, 0xba, 0xb5, 0xca, 0xb5, 0x58, 0x02, 0x9a, 0x36, 0x02, 0x4d, 0x2e, 0x79, + 0x0f, 0xc6, 0xfd, 0x66, 0x7f, 0x17, 0x6e, 0x0a, 0xa9, 0x9d, 0xd1, 0xd7, 0x2b, 0x57, + 0x36, 0x8f, 0x01, 0xb6, 0x6c, 0x4a, 0x96, 0xc1, 0x56, 0xf3, 0xf2, 0x85, 0x41, 0xab, + 0x4c, 0xa4, 0x96, 0x69, 0x60, 0x21, 0x82, 0x08, 0x46, 0x69, 0x61, 0x12, 0x94, 0x90, + 0xa7, 0xd8, 0xb6, 0x5c, 0x14, 0x70, 0xba, 0xd8, 0xdb, 0x08, 0x28, 0xef, 0x06, 0xc1, + 0xcb, 0x55, 0x70, 0x0e, 0x85, 0xe2, 0x4f, 0xde, 0xa9, 0x4e, 0xa2, 0xb0, 0x6e, 0x8d, + 0x8a, 0x89, 0xfc, 0x91, 0x87, 0x1f, 0x88, 0xfb, 0x1a, 0xbd, 0xcd, 0x72, 0x1e, 0xff, + 0xf1, 0x2e, 0xf9, 0xd4, 0xf5, 0xb0, 0x45, 0x85, 0x19, 0x7c, 0x3b, 0x3c, 0xc8, 0xe8, + 0x57, 0xd8, 0x1f, 0x21, 0xef, 0x88, 0x1f, 0xed, 0x53, 0x3c, 0x92, 0xcf, 0x4c, 0xb0, + 0xe1, 0x8f, 0xe7, 0xd3, 0x4e, 0x99, 0x7c, 0x64, 0x92, 0x88, 0x4f, 0xe5, 0x6a, 0x8b, + 0x91, 0x08, 0x98, 0x0d, 0x45, 0x3c, 0xb8, 0xa6, 0x6e, 0xa0, 0xa0, 0x15, 0x35, 0x50, + 0x06, 0x0a, 0xcb, 0x04, 0x3a, 0x40, 0xed, 0x6f, 0x92, 0x9d, 0x3e, 0x0d, 0xa1, 0x64, + 0xb2, 0x36, 0x19, 0xaf, 0x1d, 0xe4, 0x56, 0xfd, 0xd0, 0x37, 0xbf, 0x1e, 0xa7, 0xfa, + 0xb2, 0x9a, 0x67, 0x61, 0xef, 0x4d, 0xed, 0xc8, 0x6c, 0x2f, 0x17, 0x62, 0xad, 0x64, + 0x48, 0x4c, 0x08, 0xff, 0xea, 0x77, 0x5a, 0x90, 0x4d, 0xec, 0x82, 0x7f, 0xd8, 0x7a, + 0x18, 0x86, 0x0d, 0x6e, 0x8a, 0x4a, 0x52, 0xb5, 0xcf, 0x44, 0xbe, 0x28, 0xa6, 0x2d, + 0x41, 0x59, 0x02, 0x09, 0x3a, 0x0c, 0x36, 0x5d, 0x29, 0x9e, 0xde, 0xba, 0x53, 0x13, + 0x6c, 0x62, 0x6e, 0x16, 0x0a, 0xcb, 0x00, 0x44, 0xce, 0x6f, 0x2b, 0xb8, 0xdd, 0xe1, + 0xfd, 0xda, 0x5b, 0x47, 0x4d, 0x5b, 0x35, 0x07, 0x47, 0x4e, 0x3d, 0x52, 0x77, 0x24, + 0x12, 0x01, 0xb8, 0x26, 0x1a, 0x49, 0xd4, 0x91, 0xaf, 0x04, 0x9b, 0x39, 0xe2, 0x6d, + 0x13, 0x57, 0xc3, 0x06, 0x92, 0x64, 0x16, 0x77, 0x6d, 0x7d, 0x13, 0xf8, 0x40, 0xbd, + 0x82, 0xac, 0xa0, 0x1c, 0x83, 0x1c, 0x98, 0x3f, 0x19, 0x85, 0xee, 0x0a, 0xda, 0xe8, + 0xdb, 0x84, 0x47, 0xc0, 0xe5, 0x1c, 0x09, 0xdf, 0xe3, 0xde, 0xe3, 0x88, 0x0a, 0x97, + 0x13, 0xce, 0xb7, 0x45, 0xab, 0xfd, 0xd9, 0xf1, 0xc7, 0xea, 0xd7, 0x63, 0x08, 0xcd, + 0xee, 0xa2, 0x1c, 0x8b, 0x09, 0x57, 0x02, 0x7c, 0x5d, 0x00, 0xe5, 0x0a, 0x43, 0x88, + 0xc7, 0xaf, 0x2b, 0xd6, 0x43, 0xcb, 0x5e, 0xae, 0x49, 0x27, 0x4d, 0x12, 0x30, 0xa4, + 0xcd, 0x49, 0x23, 0x7a, 0xe3, 0x7b, 0x38, 0x10, 0xc2, 0xc3, 0x95, 0x8a, 0x7d, 0xee, + 0x02, 0x34, 0x30, 0x1b, 0x89, 0xa2, 0xdf, 0x2a, 0x78, 0xef, 0x0b, 0xfb, 0x4b, 0xf6, + 0xb3, 0x87, 0xdf, 0x2c, 0x6c, 0x86, 0xe6, 0x1c, 0xd1, 0x0c, 0xa1, 0x1f, 0x81, 0x13, + 0x01, 0x26, 0x07, 0xf1, 0x5b, 0x28, 0x56, 0x24, 0x0f, 0xdc, 0x52, 0x06, 0x5a, 0x10, + 0x28, 0xc8, 0xa2, 0xdd, 0xfd, 0xd1, 0x5c, 0xf5, 0x26, 0x5f, 0x87, 0x38, 0x8a, 0xb9, + 0xbf, 0x21, 0xc9, 0xa7, 0x8c, 0x59, 0x03, 0x8a, 0x98, 0xab, 0x64, 0xfd, 0x67, 0x10, + 0x77, 0xd4, 0x72, 0xc2, 0x09, 0xdd, 0x72, 0x9b, 0xd7, 0xf8, 0x48, 0x09, 0x45, 0xfb, + 0xa7, 0x52, 0x09, 0x8a, 0x94, 0xcc, 0xb2, 0x4c, 0xf3, 0xbc, 0x09, 0x2d, 0x42, 0x36, + 0x46, 0x11, 0xa2, 0x93, 0xaf, 0xf3, 0xc5, 0x79, 0x37, 0x2c, 0x12, 0xe1, 0x50, 0x90, + 0xaa, 0x27, 0x23, 0x20, 0x57, 0xf2, 0xed, 0xde, 0x4e, 0x1d, 0xb2, 0x92, 0xf7, 0xb1, + 0x86, 0x47, 0x22, 0x67, 0x35, 0x17, 0x6d, 0x90, 0xf1, 0x26, 0x5b, 0x37, 0x98, 0xcc, + 0xab, 0xac, 0x0b, 0x8d, 0x79, 0xb1, 0x77, 0x20, 0xb2, 0xba, 0x71, 0xd7, 0x85, 0x0c, + 0xc2, 0xa0, 0x87, 0x2b, 0xf0, 0xf4, 0xb8, 0x14, 0x36, 0x78, 0x59, 0xf8, 0x99, 0x48, + 0xf0, 0xa1, 0xa3, 0x83, 0x60, 0x4b, 0x9e, 0xf0, 0x7e, 0xa9, 0x3d, 0xbb, 0x98, 0x71, + 0xc0, 0x09, 0xaa, 0x6a, 0x31, 0xd8, 0xea, 0xf1, 0x43, 0x0b, 0x7b, 0xc0, 0xac, 0x26, + 0x4e, 0x2f, 0x97, 0x6a, 0xd3, 0x97, 0xf2, 0x7f, 0x48, 0x37, 0x8f, 0x8a, 0x4e, 0xd9, + 0x02, 0xc6, 0x6e, 0x49, 0x18, 0xfa, 0xee, 0x8d, 0xc0, 0x06, 0x72, 0x46, 0x96, 0x0d, + 0xb1, 0xf8, 0xcd, 0x07, 0xbf, 0x90, 0xd7, 0x53, 0x7c, 0xc2, 0x7b, 0xbb, 0x8c, 0x9d, + 0x5b, 0x29, 0x62, 0xc4, 0x7e, 0xd1, 0x82, 0xa2, 0xfc, 0xe0, 0x5f, 0x8e, 0x03, 0xc4, + 0xe2, 0x5e, 0x49, 0x6d, 0xd5, 0x7d, 0x6a, 0xb3, 0x45, 0x8f, 0xac, 0xbd, 0x91, 0xea, + 0x22, 0x72, 0xff, 0xda, 0x47, 0xb0, 0x73, 0x59, 0x5e, 0x78, 0xdd, 0x84, 0xb7, 0x1f, + 0xf8, 0x8b, 0x74, 0x21, 0x02, 0x88, 0xf0, 0xea, 0xf8, 0xe7, 0x1a, 0xeb, 0xa4, 0x4c, + 0x5e, 0xc3, 0x82, 0xe3, 0x59, 0x33, 0xe1, 0x7b, 0xa7, 0xef, 0xd6, 0x64, 0x90, 0xf6, + 0x72, 0x03, 0x2d, 0x4e, 0xbc, 0xf7, 0xcd, 0x55, 0x7a, 0xe0, 0xdb, 0xb7, 0x25, 0x00, + 0x4e, 0xcb, 0x05, 0x7a, 0x5a, 0x2b, 0x15, 0x7a, 0x1a, 0xbf, 0xb9, 0x83, 0x87, 0x08, + 0xba, 0x28, 0xe7, 0xea, 0xa2, 0x12, 0xa9, 0x04, 0x22, 0xc1, 0x27, 0x17, 0x53, 0xb9, + 0xf3, 0x0f, 0x8f, 0xf8, 0xe5, 0x33, 0xa9, 0x93, 0xf0, 0x69, 0xbd, 0x82, 0x2b, 0xf7, + 0x24, 0xd1, 0xb7, 0x38, 0xc7, 0x3d, 0x4b, 0x46, 0xe9, 0x90, 0x28, 0xde, 0x1e, 0xaa, + 0xdf, 0x9a, 0xb0, 0x89, 0xdd, 0x46, 0x6c, 0xa1, 0x85, 0xa8, 0x0a, 0xfc, 0xfd, 0x44, + 0x68, 0x5c, 0xf8, 0xec, 0xe5, 0x58, 0xd7, 0xbf, 0xd0, 0x17, 0x39, 0x20, 0xd7, 0x17, + 0x51, 0x30, 0xf0, 0xe4, 0xd0, 0x93, 0x74, 0x41, 0xbc, 0xe9, 0x8c, 0xfa, 0x5b, 0x33, + 0x3b, 0x66, 0x19, 0x0f, 0x2b, 0x44, 0x71, 0x38, 0xe8, 0xc2, 0x6d, 0x84, 0x12, 0xca, + 0xc8, 0x20, 0x86, 0xd6, 0x1b, 0x5d, 0x2c, 0x8c, 0xf0, 0xbb, 0xeb, 0xac, 0x5b, 0x89, + 0xbf, 0xe8, 0x2b, 0x58, 0x91, 0x76, 0x64, 0xba, 0xb9, 0x1c, 0xe2, 0xec, 0xe2, 0x90, + 0xb2, 0x7b, 0x60, 0x52, 0xd4, 0xbf, 0x99, 0x1a, 0x33, 0xf4, 0x58, 0x1a, 0x63, 0x36, + 0x25, 0x78, 0x79, 0x58, 0x89, 0x7f, 0xca, 0x4b, 0x98, 0xb7, 0xe7, 0x27, 0x7c, 0x5e, + 0x6a, 0x1d, 0x88, 0x59, 0x48, 0xc9, 0xd4, 0x84, 0xdd, 0x0c, 0xef, 0xef, 0x85, 0x4e, + 0x81, 0x76, 0xc3, 0x97, 0xdc, 0xfa, 0x77, 0x2e, 0x71, 0x14, 0x72, 0xe7, 0x90, 0xba, + 0x8d, 0x39, 0x35, 0xd5, 0x7c, 0xa3, 0x13, 0x49, 0x37, 0x9e, 0x62, 0x83, 0xa6, 0xaa, + 0x8f, 0xc9, 0x91, 0xef, 0xc7, 0xd3, 0xb7, 0xef, 0x66, 0xb9, 0x2f, 0xe0, 0x9d, 0x35, + 0x16, 0x27, 0x0a, 0xe1, 0x9a, 0x99, 0x92, 0x16, 0xee, 0xae, 0x16, 0x21, 0x44, 0xac, + 0xea, 0x56, 0x0d, 0x17, 0x72, 0x05, 0xf2, 0x6c, 0x97, 0x03, 0xb5, 0x4e, 0x80, 0xaf, + 0x1a, 0x87, 0x94, 0xd6, 0xd3, 0xf1, 0xc5, 0xee, 0xad, 0x22, 0x0b, 0x11, 0x9f, 0x06, + 0xb2, 0x00, 0x98, 0x6c, 0x91, 0x21, 0x32, 0xcb, 0x08, 0xa9, 0x8e, 0x0f, 0xee, 0x35, + 0xe7, 0xf7, 0x7f, 0xc8, 0x52, 0x1d, 0x38, 0x77, 0x3e, 0x61, 0x4e, 0xee, 0xb8, 0xa3, + 0xea, 0xd8, 0x6a, 0x02, 0x48, 0x32, 0xe6, 0x4a, 0x4c, 0x75, 0x72, 0x0c, 0xdc, 0xdd, + 0xf9, 0xd0, 0x77, 0x09, 0xa1, 0x68, 0xd0, 0x10, 0x12, 0xc2, 0xe4, 0xf3, 0x34, 0x30, + 0xf2, 0x99, 0x70, 0xc6, 0x0b, 0xe8, 0xc5, 0xe2, 0xc8, 0xcc, 0x8a, 0x86, 0xed, 0xcd, + 0x51, 0x2d, 0xa7, 0x0d, 0xd7, 0xbb, 0x40, 0xe2, 0x7b, 0x32, 0xdf, 0x3d, 0x77, 0x6a, + 0x4a, 0x7b, 0x00, 0xe3, 0xbd, 0x8f, 0x69, 0x7f, 0x1f, 0x4e, 0x5c, 0x9f, 0xbe, 0xbe, + 0xb4, 0x46, 0xb0, 0x25, 0xfd, 0x80, 0x65, 0xb1, 0x86, 0xae, 0xdc, 0x75, 0xf5, 0x68, + 0x87, 0x2c, 0x16, 0xfa, 0xf5, 0xe5, 0xa3, 0x47, 0x4d, 0x8a, 0x9d, 0x45, 0x54, 0x8f, + 0xac, 0xb7, 0x46, 0x9a, 0xcb, 0x2d, 0xa1, 0x0b, 0x70, 0x78, 0x25, 0x9c, 0x50, 0x7c, + 0x4d, 0xeb, 0xe4, 0x50, 0x8e, 0x0c, 0xee, 0x4f, 0xbc, 0xb0, 0xd1, 0x3b, 0xf6, 0x24, + 0x37, 0xdc, 0xf0, 0x5a, 0x63, 0x13, 0x45, 0xef, 0xbe, 0x0d, 0x7b, 0xb9, 0x01, 0x61, + 0x66, 0x55, 0x4f, 0xf3, 0x8a, 0x1d, 0x77, 0xf2, 0xfd, 0xa4, 0xe7, 0xeb, 0xa7, 0xa7, + 0x8a, 0xb3, 0x1f, 0x38, 0x29, 0x42, 0x52, 0xa2, 0xb1, 0x0f, 0xd2, 0x86, 0x5b, 0x57, + 0x05, 0x05, 0x5d, 0xfe, 0x9b, 0x3e, 0x9e, 0x8f, 0x7a, 0xd5, 0xf4, 0x00, 0x7d, 0xbe, + 0x42, 0x2b, 0x3a, 0xa0, 0xbe, 0xb9, 0xd1, 0xc8, 0x9d, 0x37, 0x46, 0x08, 0x54, 0xff, + 0x6e, 0x5f, 0x03, 0xe5, 0xff, 0x3d, 0x4f, 0x18, 0x48, 0xf4, 0xcc, 0x64, 0x21, 0x8a, + 0x01, 0xf2, 0x47, 0x2b, 0xb0, 0x55, 0x80, 0x2f, 0x97, 0xf3, 0x20, 0x41, 0xa7, 0x92, + 0x79, 0x0b, 0x7c, 0x22, 0x6b, 0x04, 0xa6, 0xea, 0xe8, 0x5f, 0x1b, 0x71, 0xca, 0x19, + 0xa1, 0x71, 0x89, 0x02, 0xb4, 0xc3, 0xa3, 0xb5, 0x06, 0xd8, 0xc1, 0xb7, 0xae, 0x72, + 0x8c, 0x9b, 0x6c, 0xc3, 0x17, 0xe5, 0xe0, 0xde, 0xe5, 0x33, 0xe2, 0xe9, 0x99, 0x73, + 0xd8, 0x83, 0xa4, 0x0c, 0x6e, 0x68, 0xf2, 0x31, 0xd2, 0xcb, 0x01, 0x2f, 0x60, 0xc1, + 0x43, 0xcc, 0xab, 0xdd, 0x40, 0x45, 0x59, 0x0d, 0x9e, 0x43, 0xfb, 0xa3, 0x6f, 0xe4, + 0xcf, 0xd9, 0x7b, 0x4b, 0xdd, 0x0c, 0x4d, 0x2c, 0x93, 0xc5, 0x72, 0x8b, 0x12, 0x87, + 0xfd, 0x25, 0x41, 0x72, 0x2c, 0x69, 0x9b, 0xc1, 0xa0, 0x05, 0x83, 0xdb, 0xc9, 0x48, + 0xd5, 0x32, 0x4a, 0xc5, 0xbd, 0x7a, 0x68, 0x09, 0x64, 0x67, 0x3e, 0xdf, 0x2c, 0x6d, + 0xeb, 0xb1, 0xc8, 0xe1, 0xd0, 0x24, 0x16, 0xe6, 0xbd, 0xb2, 0xa7, 0x68, 0x1b, 0xf4, + 0x29, 0x92, 0x25, 0xc2, 0x1b, 0x5d, 0xb6, 0xa8, 0x45, 0xad, 0x10, 0x4d, 0x34, 0x29, + 0xcd, 0xc5, 0x9e, 0x3b, 0xca, 0xcf, 0x6d, 0xbc, 0x88, 0xaf, 0x0f, 0x67, 0xdc, 0xbd, + 0xf3, 0xa0, 0x72, 0x3e, 0x4d, 0x4b, 0xce, 0x32, 0x85, 0x1b, 0xb5, 0x19, 0x7a, 0x8f, + 0x43, 0x30, 0xb2, 0x72, 0x27, 0xf0, 0xb7, 0x71, 0xd0, 0xaf, 0x17, 0x5e, 0x9c, 0x3f, + 0x6e, 0x1f, 0x68, 0x46, 0x2e, 0xe7, 0xfe, 0x17, 0x97, 0xd9, 0x28, 0x40, 0x6f, 0x92, + 0x38, 0xa3, 0xf3, 0xfd, 0x83, 0x6a, 0x27, 0x56, 0xdd, 0x0a, 0x11, 0xe1, 0xab, 0x94, + 0x9d, 0x5e, 0x30, 0x89, 0x4f, 0x56, 0x29, 0x95, 0x25, 0xe6, 0x5d, 0x95, 0x0f, 0x2e, + 0xb5, 0x0b, 0x3a, 0x8e, 0xa7, 0xac, 0xad, 0xbc, 0x3c, 0x77, 0xeb, 0x53, 0xe7, 0xde, + 0x9b, 0xa8, 0x2f, 0x7d, 0xd5, 0xf6, 0x13, 0xcd, 0xa6, 0x29, 0xfc, 0xd2, 0xf6, 0x36, + 0x6b, 0x2e, 0x1e, 0xc2, 0x40, 0xd4, 0x82, 0xc3, 0xa6, 0xf9, 0xd9, 0x8d, 0xab, 0x1c, + 0x86, 0x4c, 0x00, 0xb8, 0xfd, 0x36, 0x46, 0xf0, 0xd5, 0x96, 0xfe, 0x18, 0x0f, 0x70, + 0xb1, 0x94, 0x84, 0x25, 0x63, 0xe9, 0xf3, 0xf4, 0xdc, 0xf5, 0x2b, 0x89, 0x3a, 0x70, + 0x9e, 0x1d, 0xd4, 0xa7, 0xca, 0x1c, 0x49, 0xec, 0x81, 0x4e, 0x8f, 0xe6, 0xe0, 0xe0, + 0xde, 0x54, 0x6a, 0x4f, 0xbe, 0x7d, 0x25, 0x67, 0x0b, 0x2f, 0xc6, 0x8a, 0x8f, 0xb2, + 0xc4, 0xa6, 0x3d, 0xef, 0xec, 0x6f, 0xe0, 0x1d, 0x8c, 0xe0, 0xf5, 0x1d, 0x3c, 0x65, + 0xa4, 0x28, 0x90, 0x97, 0x5f, 0xa1, 0xed, 0xed, 0x70, 0x56, 0x20, 0xdf, 0xcd, 0x1d, + 0x0c, 0xde, 0xad, 0x2a, 0xbf, 0xa6, 0xdf, 0xe2, 0x6d, 0x79, 0xc9, 0x0c, 0x63, 0xff, + 0x96, 0xe5, 0x40, 0xb7, 0x61, 0x5d, 0x43, 0xa6, 0x26, 0x1d, 0x57, 0x73, 0x03, 0x06, + 0xb6, 0x63, 0x2c, 0x8e, 0xe6, 0x1b, 0xaa, 0x4a, 0xb4, 0xd3, 0x08, 0x4d, 0x65, 0x9c, + 0xab, 0xcf, 0xc4, 0x06, 0x4c, 0x09, 0xd2, 0x42, 0x69, 0xb3, 0x03, 0x17, 0x10, 0xb6, + 0x7d, 0x3b, 0x0b, 0x73, 0x6f, 0xac, 0xbc, 0x18, 0x1e, 0xb1, 0xdc, 0x8c, 0x49, 0x3f, + 0x10, 0xdb, 0xe6, 0xfe, 0x45, 0xfd, 0xd4, 0xab, 0x60, 0x22, 0xfa, 0xbd, 0xd3, 0x4c, + 0x09, 0xf7, 0x51, 0x04, 0xc3, 0x85, 0xc9, 0x26, 0x83, 0x41, 0xc1, 0x6e, 0xbe, 0x80, + 0xf8, 0xc8, 0x0e, 0x8e, 0x06, 0x23, 0x06, 0x03, 0x99, 0x5a, 0xde, 0x55, 0x61, 0xfe, + 0xd4, 0x5c, 0xf8, 0xd1, 0x14, 0xd4, 0xcf, 0x02, 0x42, 0x0c, 0x4b, 0x96, 0x2d, 0xc2, + 0x02, 0xf8, 0xa5, 0x07, 0xf3, 0xd8, 0xe8, 0xa3, 0x44, 0xfb, 0xa1, 0x0a, 0x32, 0x7f, + 0xf2, 0x22, 0x54, 0xf6, 0xc3, 0xac, 0x8f, 0x3c, 0xf9, 0x70, 0x0b, 0x1f, 0xd2, 0xec, + 0xbe, 0x9f, 0x4e, 0x91, 0xe4, 0x3a, 0x65, 0x4f, 0xff, 0x02, 0x7c, 0xd9, 0x17, 0x4b, + 0x63, 0x8e, 0x6e, 0xfe, 0xc4, 0xab, 0xfb, 0xa1, 0x87, 0xf8, 0xf3, 0xdb, 0xa0, 0x45, + 0x9d, 0xa6, 0xc3, 0xf8, 0x00, 0xcb, 0x6b, 0x61, 0x33, 0xa8, 0xb4, 0xac, 0x1e, 0xf6, + 0x58, 0xd1, 0x11, 0xc0, 0x3f, 0x07, 0x22, 0x08, 0xdc, 0xc2, 0x07, 0xa2, 0x22, 0x3a, + 0x70, 0x22, 0x92, 0x43, 0x2e, 0x83, 0x06, 0xfc, 0x03, 0x04, 0x63, 0xe7, 0x54, 0xff, + 0x0f, 0x15, 0x3d, 0x97, 0xbc, 0x9c, 0xe9, 0x6d, 0xff, 0x4b, 0xed, 0x2f, 0x1e, 0xa5, + 0xb8, 0xea, 0x87, 0x6d, 0x2e, 0xe4, 0xe4, 0xf6, 0xe4, 0x9a, 0x4a, 0x85, 0xa9, 0xcf, + 0x4a, 0x33, 0xdc, 0xd9, 0x36, 0x60, 0xa4, 0x25, 0x43, 0xe5, 0x34, 0x22, 0x39, 0x0d, + 0x66, 0x5b, 0xdd, 0x30, 0x24, 0x78, 0xb3, 0x3c, 0x8d, 0x57, 0x47, 0x92, 0x41, 0x4c, + 0x5f, 0xe5, 0xb7, 0x4f, 0xe1, 0xd1, 0x69, 0x52, 0x5c, 0x99, 0x30, 0x1a, 0x3a, 0x68, + 0xa0, 0xc8, 0x5f, 0x99, 0x08, 0xed, 0x24, 0x25, 0x51, 0x5d, 0x45, 0xca, 0xe5, 0xca, + 0xe7, 0xce, 0x0e, 0x98, 0xb5, 0x82, 0x9e, 0xd6, 0x96, 0xbe, 0x2c, 0x3d, 0xb4, 0x59, + 0xe0, 0xad, 0x5b, 0x5d, 0xf7, 0x4a, 0xa1, 0x7b, 0x43, 0x44, 0x65, 0x42, 0xaf, 0x17, + 0x84, 0x40, 0x1e, 0xfe, 0xc9, 0xf1, 0x25, 0x6d, 0xaf, 0x71, 0x91, 0x59, 0xd8, 0xa1, + 0x83, 0x3f, 0xc0, 0x5c, 0xdb, 0x01, 0xf6, 0x88, 0xef, 0x49, 0x81, 0xc7, 0x4a, 0x7f, + 0xf4, 0x3d, 0xe3, 0x55, 0xc3, 0xc4, 0x66, 0x1c, 0x36, 0xfa, 0x24, 0xec, 0x10, 0x99, + 0xa8, 0xad, 0xf4, 0xe3, 0x11, 0x48, 0x78, 0x20, 0xb5, 0xa7, 0x76, 0xea, 0x06, 0x42, + 0xef, 0x8e, 0xf1, 0xe2, 0x87, 0x82, 0x76, 0x7d, 0x9d, 0xe5, 0x7d, 0xea, 0xde, 0xad, + 0xcb, 0x4a, 0xf5, 0x19, 0x3e, 0x09, 0xc9, 0xbb, 0x74, 0x73, 0x77, 0x3a, 0x8c, 0xa5, + 0x6d, 0x76, 0x51, 0x1d, 0x65, 0x99, 0x20, 0xdb, 0x99, 0x64, 0xd3, 0x2b, 0xad, 0xb6, + 0x1f, 0x4c, 0xf6, 0xb0, 0x22, 0xd7, 0xc1, 0x53, 0x93, 0x18, 0x49, 0x64, 0x3e, 0x8b, + 0x99, 0xea, 0xe0, 0x28, 0x4f, 0x8b, 0x01, 0x15, 0xb4, 0x23, 0x7a, 0x7c, 0x5d, 0x81, + 0x97, 0x0f, 0xe8, 0x7c, 0x6f, 0x84, 0xb6, 0x68, 0x6c, 0x46, 0x25, 0xdb, 0xdd, 0x9d, + 0x79, 0xd2, 0xc5, 0x55, 0xdd, 0x4f, 0xce, 0xed, 0x2c, 0x5e, 0x5e, 0x89, 0x6f, 0x63, + 0x1a, 0xe4, 0x59, 0x7e, 0x9c, 0xc0, 0xbe, 0xe7, 0xb3, 0x02, 0x5f, 0x95, 0x56, 0x10, + 0x6a, 0x84, 0x3a, 0x18, 0x22, 0x7f, 0x5a, 0xb9, 0x61, 0x7d, 0x7b, 0xcb, 0x1a, 0xf5, + 0x28, 0xfa, 0xa7, 0xa0, 0x52, 0xea, 0x4f, 0x52, 0xca, 0x59, 0x45, 0x57, 0xfd, 0xad, + 0x33, 0x05, 0x2b, 0xc8, 0x2b, 0x39, 0xc6, 0xa6, 0x09, 0xa0, 0x70, 0x75, 0x3d, 0x78, + 0x8b, 0x2c, 0x4a, 0x2c, 0xae, 0xbb, 0xe7, 0x9f, 0xf0, 0x12, 0x07, 0x1c, 0x07, 0x08, + 0x10, 0x94, 0xad, 0x60, 0x59, 0xc2, 0x8f, 0x48, 0xe5, 0x56, 0xc4, 0xe8, 0xd8, 0xc5, + 0x37, 0x8b, 0xc2, 0x93, 0x07, 0x6b, 0xb4, 0x97, 0x07, 0x5f, 0x9c, 0xa0, 0xba, 0x13, + 0x11, 0x55, 0x0f, 0xa2, 0x17, 0x3d, 0x0e, 0xb1, 0xf0, 0xbd, 0xdd, 0xf3, 0xb3, 0xd5, + 0xc2, 0x43, 0xff, 0xea, 0xbe, 0xe8, 0x23, 0xcd, 0x63, 0xb4, 0x39, 0x39, 0xce, 0x95, + 0x46, 0xed, 0x4c, 0x41, 0xe6, 0x0c, 0xcc, 0x7e, 0x1c, 0x54, 0x3c, 0xb3, 0xe2, 0xd3, + 0x50, 0xe2, 0xe2, 0xe9, 0x74, 0x21, 0x5c, 0xf7, 0xaa, 0x96, 0x9b, 0x66, 0x81, 0x14, + 0xac, 0xdb, 0x29, 0xf4, 0xcd, 0xcf, 0xdc, 0xec, 0x2a, 0x8c, 0xe4, 0xf5, 0x95, 0xf4, + 0xff, 0x5f, 0x70, 0x7e, 0x7f, 0xa4, 0xde, 0xe8, 0xbf, 0x8f, 0x39, 0x52, 0xae, 0x32, + 0xe7, 0x7f, 0x34, 0xf8, 0xb3, 0xab, 0xaa, 0xe9, 0x69, 0x28, 0xba, 0x4a, 0x6c, 0x0f, + 0xbf, 0x5b, 0x29, 0x19, 0x2d, 0xae, 0x80, 0x0d, 0xfa, 0x79, 0x57, 0x0c, 0xaf, 0x0b, + 0xb8, 0x33, 0xbd, 0x37, 0xa3, 0xd4, 0xbe, 0xaf, 0x09, 0x1f, 0x6b, 0x3e, 0x55, 0xaa, + 0xe5, 0x25, 0xf4, 0x13, 0xac, 0x80, 0x4c, 0x34, 0x7d, 0x54, 0x1d, 0x2c, 0x09, 0xec, + 0x6e, 0x54, 0x03, 0x5d, 0xf1, 0xd8, 0x30, 0x28, 0x4d, 0x9b, 0x46, 0xff, 0xd2, 0xb2, + 0xeb, 0x04, 0x0b, 0x61, 0x77, 0xd0, 0xa0, 0x9c, 0x16, 0x60, 0x34, 0xa9, 0x57, 0xb1, + 0x8f, 0xf6, 0x2e, 0x43, 0x4a, 0x3e, 0xc7, 0x32, 0x62, 0xe4, 0xb2, 0x3f, 0xec, 0x9d, + 0x29, 0x0a, 0x81, 0xc5, 0xb1, 0xf7, 0x3c, 0xb4, 0xcd, 0x1c, 0x47, 0x2b, 0x86, 0xe5, + 0x34, 0xab, 0x9e, 0x65, 0x53, 0x29, 0x5d, 0xb0, 0xcf, 0x34, 0xe1, 0x39, 0x2a, 0xad, + 0x5a, 0xbc, 0xf3, 0x98, 0x64, 0x16, 0xa7, 0x0a, 0x9d, 0xbe, 0x59, 0xbb, 0x95, 0x8e, + 0xbc, 0x71, 0x1c, 0x3a, 0xe0, 0x8c, 0xaf, 0x52, 0xec, 0xa9, 0xcb, 0x54, 0xc4, 0x58, + 0xbe, 0x7f, 0x5e, 0x62, 0x14, 0xec, 0xa0, 0xf0, 0xa3, 0x81, 0x52, 0x62, 0x20, 0x01, + 0x32, 0xe6, 0x14, 0x54, 0x37, 0xec, 0xd2, 0x1f, 0xc8, 0x03, 0x6c, 0xb0, 0x0a, 0x49, + 0x13, 0x84, 0xc3, 0x41, 0xd8, 0x72, 0xdc, 0xda, 0x31, 0xb1, 0x42, 0x96, 0x73, 0xd9, + 0xc4, 0xf5, 0x7b, 0x81, 0xa0, 0x23, 0x6d, 0xa5, 0xec, 0x55, 0x02, 0xee, 0x29, 0x63, + 0x15, 0x0a, 0x00, 0x26, 0xbd, 0x63, 0xef, 0x67, 0x9e, 0x8c, 0x25, 0xb8, 0xec, 0xee, + 0x06, 0x56, 0x4a, 0xf3, 0xb0, 0x2d, 0xea, 0xb1, 0x06, 0x97, 0xa2, 0x4d, 0xe6, 0x7d, + 0x4f, 0x65, 0x04, 0xae, 0x27, 0x37, 0xb8, 0xe1, 0x73, 0x25, 0xc2, 0xff, 0x15, 0x0c, + 0x62, 0xe3, 0x79, 0x83, 0x44, 0xa1, 0xad, 0x3c, 0xbb, 0x75, 0xb7, 0xf2, 0xa1, 0x57, + 0x38, 0xf6, 0x01, 0xcf, 0x00, 0xf7, 0xe8, 0xbc, 0x08, 0xb6, 0x89, 0x56, 0x7e, 0x4c, + 0x7c, 0x01, 0x05, 0x8b, 0xee, 0xc2, 0x90, 0x3c, 0x5c, 0xa6, 0xb4, 0xc4, 0xa5, 0x71, + 0xf4, 0x60, 0xd6, 0x05, 0x87, 0x36, 0x29, 0x96, 0xc6, 0xe1, 0x25, 0x54, 0xe8, 0xe3, + 0x4e, 0x68, 0x3a, 0x27, 0xf8, 0xa5, 0xff, 0x97, 0x1d, 0x5a, 0x0d, 0xc2, 0xf3, 0xef, + 0xd3, 0x88, 0x99, 0x87, 0xc1, 0xcc, 0x39, 0xce, 0x5d, 0x4b, 0x6b, 0x54, 0x4c, 0xe0, + 0x4c, 0x71, 0xee, 0x4b, 0xfa, 0xe5, 0x04, 0x0d, 0x61, 0xf0, 0x57, 0xe4, 0xf7, 0x70, + 0x17, 0x28, 0xf1, 0x20, 0x04, 0xa7, 0xf7, 0xed, 0xeb, 0x3a, 0xb2, 0x26, 0x09, 0xed, + 0x33, 0xb0, 0xab, 0x5d, 0x69, 0xb1, 0x2d, 0x45, 0x76, 0x57, 0x77, 0x14, 0xdf, 0xc6, + 0xdd, 0xa7, 0x1f, 0xf6, 0x01, 0x7b, 0x55, 0xb3, 0x35, 0x4d, 0x11, 0xe9, 0x21, 0x67, + 0x92, 0xe5, 0x60, 0x9f, 0xc0, 0x67, 0x88, 0xec, 0x66, 0x8e, 0xef, 0x64, 0x5e, 0x63, + 0xb3, 0x7e, 0x2d, 0x0c, 0xd2, 0x63, 0x04, 0x08, 0x00, 0xbc, 0x8a, 0xa2, 0x80, 0x15, + 0x6a, 0x79, 0x4f, 0x62, 0xa5, 0xf6, 0x93, 0xeb, 0xd9, 0x07, 0x4b, 0x5d, 0x35, 0x4a, + 0x71, 0xc8, 0xe3, 0x36, 0xde, 0x04, 0x08, 0xac, 0x70, 0x80, 0xa2, 0xae, 0xee, 0x36, + 0x6c, 0x58, 0x14, 0x6f, 0x32, 0xe3, 0x49, 0xa9, 0xbc, 0x65, 0x7e, 0xc9, 0xe5, 0x7a, + 0x89, 0xa0, 0x4c, 0xce, 0xee, 0x21, 0xbd, 0xf3, 0x79, 0x3e, 0x49, 0xa5, 0xcf, 0x71, + 0x3a, 0x42, 0xd0, 0x29, 0xdd, 0xdb, 0x3d, 0xb4, 0x95, 0x09, 0x2c, 0x37, 0xce, 0x81, + 0x4b, 0xe7, 0x3e, 0xf4, 0xec, 0x8d, 0x70, 0xe8, 0x69, 0xbd, 0x2b, 0x78, 0x8f, 0x15, + 0x00, 0xfe, 0x5e, 0xe5, 0x6c, 0x0c, 0xe7, 0x04, 0xeb, 0xa2, 0xc1, 0xa3, 0xa3, 0x29, + 0x0d, 0xe6, 0xec, 0x68, 0xcc, 0xb5, 0xef, 0x7c, 0xd0, 0x21, 0x2a, 0x3f, 0x09, 0x96, + 0x92, 0xcf, 0x00, 0x04, 0x8d, 0xe5, 0x01, 0x26, 0x19, 0xe7, 0x41, 0x69, 0x2b, 0xfc, + 0x74, 0x05, 0xba, 0x3e, 0x87, 0x5e, 0x98, 0xb7, 0xca, 0x31, 0xe9, 0x65, 0xa1, 0x6f, + 0xdd, 0xb5, 0xb0, 0xb7, 0x72, 0xa3, 0xf5, 0xd0, 0x50, 0xd8, 0xad, 0x7f, 0x60, 0x7f, + 0x71, 0xc5, 0x36, 0x3f, 0x7b, 0x7d, 0x2c, 0x34, 0x38, 0xab, 0xe6, 0xb8, 0xcd, 0x3b, + 0xb4, 0x21, 0x8b, 0x4d, 0x7f, 0x55, 0x65, 0x0b, 0x80, 0x13, 0x80, 0xc7, 0xb5, 0xc6, + 0x10, 0x07, 0x9e, 0x51, 0x37, 0x16, 0xc4, 0x6f, 0xaf, 0xcf, 0x3c, 0x8c, 0x27, 0x15, + 0x38, 0x27, 0x83, 0xae, 0xe6, 0x69, 0xa9, 0xdf, 0x47, 0x17, 0x70, 0x71, 0xb5, 0x43, + 0x98, 0xce, 0xcf, 0xd6, 0x86, 0xa0, 0xbc, 0x9a, 0xd3, 0x7f, 0x44, 0xb5, 0x38, 0x87, + 0x75, 0x87, 0x51, 0x66, 0x00, 0x6d, 0x25, 0xdf, 0x4b, 0x5e, 0xd1, 0xc4, 0x1f, 0x12, + 0x1b, 0x9e, 0x16, 0xfc, 0xa6, 0xe0, 0x15, 0xa9, 0x01, 0xe1, 0xe7, 0xe2, 0xc0, 0x99, + 0x4e, 0x42, 0x7b, 0xeb, 0xd3, 0x56, 0xe4, 0x17, 0x6d, 0xec, 0x83, 0xe6, 0xfe, 0x80, + 0x02, 0x9c, 0xfc, 0x47, 0x8b, 0x88, 0xb6, 0xfd, 0x38, 0xc0, 0x39, 0xe0, 0x8b, 0x6f, + 0xd9, 0x5d, 0xab, 0xcf, 0xb2, 0x5f, 0x23, 0x8b, 0x26, 0x62, 0x06, 0xb0, 0xa2, 0xf9, + 0xa2, 0xee, 0xa1, 0xc0, 0x83, 0xfa, 0xc8, 0x08, 0xaa, 0xfa, 0x03, 0x65, 0x66, 0xcc, + 0xd2, 0x02, 0xbc, 0xfa, 0x41, 0x4e, 0x71, 0xc8, 0xb4, 0x89, 0x33, 0xc8, 0xed, 0x45, + 0x28, 0x7e, 0x1b, 0x43, 0x9b, 0x61, 0x06, 0xa5, 0x50, 0x94, 0x73, 0xf5, 0x7b, 0x87, + 0x88, 0xaf, 0x52, 0x7c, 0xf9, 0xa7, 0xab, 0xa5, 0x93, 0xdc, 0x9f, 0x5e, 0x5a, 0xca, + 0x1a, 0x64, 0x8e, 0xe4, 0x88, 0xf3, 0x6d, 0xeb, 0x4a, 0x3f, 0xdb, 0x0f, 0xf6, 0xf5, + 0xa3, 0x04, 0x4a, 0x63, 0xe1, 0x7f, 0x70, 0xa4, 0x30, 0x38, 0x24, 0x60, 0x3a, 0xb5, + 0x0e, 0x9b, 0xf7, 0x5b, 0xae, 0xb5, 0x7b, 0xfd, 0xc8, 0x9b, 0xfd, 0xbc, 0x27, 0x27, + 0x9d, 0x10, 0x73, 0xbf, 0x7f, 0x95, 0x05, 0xfb, 0x31, 0x68, 0xd2, 0x06, 0xe2, 0xbf, + 0x41, 0x02, 0xbf, 0x15, 0x9c, 0xff, 0x61, 0xe6, 0xd6, 0x6c, 0x80, 0x37, 0x50, 0xda, + 0x25, 0x4c, 0xd6, 0xb8, 0x1a, 0xed, 0x42, 0x09, 0x97, 0x94, 0xb8, 0x4e, 0xce, 0x90, + 0x42, 0x18, 0xe6, 0xf6, 0x6e, 0xc6, 0x34, 0xe9, 0x2e, 0xef, 0xf4, 0x5f, 0x52, 0xe0, + 0x4b, 0x4b, 0x79, 0x5a, 0x15, 0x25, 0xaa, 0xf9, 0xc5, 0x1d, 0x62, 0x60, 0xfb, 0xd6, + 0x4e, 0x8d, 0x8a, 0xc2, 0x66, 0xdc, 0x6e, 0x7d, 0xf6, 0x15, 0x3a, 0xd9, + ], + jumbled: vec![ + 0xe2, 0x0a, 0xd4, 0xe5, 0x55, 0x5e, 0xe2, 0xa3, 0xc3, 0x36, 0x03, 0xf3, 0x35, 0xfa, + 0xca, 0x77, 0x05, 0x57, 0x46, 0xa1, 0xd8, 0x07, 0x32, 0xaa, 0x51, 0xa6, 0x6e, 0x7f, + 0x1c, 0xc1, 0xb9, 0xe0, 0x91, 0xd4, 0x28, 0x22, 0x18, 0xe7, 0xb2, 0x2f, 0xbb, 0xcf, + 0xcd, 0xac, 0xab, 0x95, 0xdf, 0x40, 0x36, 0xa5, 0x89, 0xb3, 0xf5, 0x92, 0xfa, 0xc7, + 0xde, 0xb2, 0xed, 0xf3, 0xdc, 0x61, 0xa2, 0x1b, 0x09, 0xe7, 0x4b, 0xcd, 0xd3, 0xa3, + 0xbb, 0xe0, 0xce, 0xb6, 0x0f, 0x74, 0x81, 0x0a, 0xc8, 0x07, 0x47, 0xd6, 0x68, 0x95, + 0xcf, 0x20, 0xb9, 0x7e, 0xbd, 0xf2, 0xa7, 0x19, 0x72, 0x59, 0x13, 0x41, 0xfc, 0x84, + 0x02, 0x19, 0x3d, 0xc9, 0xe6, 0xce, 0xd6, 0x92, 0x3d, 0x60, 0xf8, 0xa0, 0x34, 0xe8, + 0x8e, 0x79, 0x9c, 0xa5, 0x41, 0xb9, 0x29, 0x56, 0x55, 0x7b, 0x2e, 0xd8, 0x74, 0xf0, + 0x4b, 0x55, 0x45, 0xfe, 0x64, 0x7f, 0x88, 0xdc, 0x79, 0x8a, 0x4c, 0xc8, 0x66, 0xef, + 0x6c, 0x63, 0x61, 0x82, 0x5c, 0x1c, 0xcc, 0xe0, 0x64, 0xb6, 0x87, 0x5e, 0x64, 0x22, + 0x46, 0x6c, 0x35, 0x9c, 0x77, 0xe2, 0x8c, 0x82, 0xaf, 0xcf, 0xa9, 0xa8, 0x8d, 0x08, + 0xcd, 0xc0, 0xaa, 0xc7, 0xb5, 0xc9, 0x56, 0x6b, 0x5d, 0x5d, 0x14, 0x92, 0xad, 0xfb, + 0x1b, 0xb3, 0xe2, 0x20, 0x3b, 0x56, 0xe6, 0x96, 0xdd, 0xf6, 0x29, 0x5a, 0x34, 0x94, + 0x16, 0xc4, 0x55, 0x7b, 0xac, 0xa3, 0x47, 0xd6, 0x00, 0x94, 0x0a, 0xf5, 0x6b, 0x6f, + 0x69, 0xd5, 0x8b, 0x58, 0x33, 0x5c, 0xed, 0xf2, 0xc4, 0x72, 0xa7, 0x87, 0xd1, 0x8b, + 0xd3, 0xba, 0x20, 0x3f, 0xea, 0x26, 0x06, 0x22, 0x89, 0xe4, 0xc1, 0x07, 0x9e, 0x1a, + 0x6a, 0x1b, 0x57, 0x35, 0x7b, 0x15, 0xaa, 0x98, 0x78, 0x2b, 0x93, 0xf7, 0xda, 0x65, + 0xf2, 0x28, 0x1c, 0xb8, 0x54, 0xca, 0x2f, 0x1e, 0xff, 0x35, 0x84, 0xa0, 0xb0, 0x23, + 0xb5, 0xc6, 0xb5, 0x06, 0x3d, 0x55, 0x53, 0x39, 0xd2, 0x23, 0x4e, 0x59, 0x45, 0x10, + 0x6a, 0x5a, 0xfd, 0xcf, 0xc9, 0xfa, 0xa9, 0x55, 0x8d, 0x39, 0x8f, 0xae, 0x2e, 0x01, + 0x22, 0x88, 0xb7, 0xa6, 0xd9, 0x39, 0x58, 0x72, 0xd4, 0xa4, 0xe9, 0xde, 0x0c, 0x89, + 0xed, 0xbf, 0xd4, 0x11, 0x9c, 0x66, 0x11, 0x2f, 0xec, 0x93, 0x1a, 0x23, 0x5f, 0x27, + 0xf9, 0xf6, 0x7e, 0xb8, 0x97, 0x08, 0x7e, 0xfa, 0x42, 0x2d, 0x19, 0x86, 0x5d, 0x9f, + 0x73, 0xf8, 0xb7, 0x0a, 0x13, 0x9e, 0xf6, 0xc4, 0x41, 0xb6, 0xa5, 0xaa, 0xe5, 0x69, + 0x57, 0x7c, 0x6e, 0x83, 0x3d, 0xf8, 0x9f, 0x54, 0x27, 0xa9, 0xe7, 0x06, 0x38, 0xed, + 0x52, 0xd8, 0xb8, 0xf6, 0x30, 0x71, 0x1b, 0x8f, 0x6f, 0x09, 0xee, 0x7d, 0x94, 0xe7, + 0x72, 0x05, 0x2b, 0xad, 0x58, 0xb0, 0xaa, 0xdf, 0xe4, 0xff, 0x70, 0x12, 0x00, 0xdb, + 0x59, 0x7f, 0xbd, 0x89, 0xf9, 0x8f, 0x93, 0xef, 0x24, 0xde, 0xc5, 0x83, 0xe9, 0x49, + 0x1c, 0xd8, 0xe3, 0x8c, 0xef, 0xa8, 0x03, 0x93, 0x18, 0xe2, 0x5e, 0xd3, 0x5f, 0xa0, + 0x53, 0x5f, 0x24, 0xa5, 0x59, 0x2d, 0xdf, 0x00, 0x49, 0xf5, 0xb1, 0x96, 0x71, 0x10, + 0x98, 0x08, 0xa6, 0xa8, 0x42, 0x06, 0xa7, 0x28, 0x0e, 0x06, 0x6a, 0x1f, 0xc0, 0x62, + 0x93, 0x28, 0xd5, 0xb0, 0x12, 0xbb, 0x67, 0xc9, 0x68, 0x42, 0xbe, 0x48, 0xae, 0x60, + 0x0e, 0xe0, 0x24, 0x76, 0xfc, 0x8e, 0xa7, 0x29, 0x7f, 0x2c, 0x95, 0x22, 0xb1, 0xb0, + 0x01, 0xa3, 0xac, 0xda, 0xea, 0x73, 0x22, 0x63, 0xe9, 0xce, 0xc8, 0xd9, 0xb1, 0x2b, + 0xde, 0x16, 0xc6, 0x4d, 0xdf, 0xeb, 0x62, 0xd3, 0x4d, 0x61, 0x01, 0x21, 0xee, 0xf9, + 0x8e, 0x3a, 0x7d, 0xed, 0xae, 0xd1, 0x92, 0x96, 0xa7, 0xae, 0xdf, 0x8b, 0xbd, 0xeb, + 0x21, 0x4e, 0xab, 0x98, 0xaa, 0x09, 0x7e, 0x0e, 0xb2, 0xf2, 0x66, 0xad, 0x0f, 0xab, + 0x69, 0xca, 0x39, 0x5d, 0x8b, 0xbb, 0xc1, 0xb3, 0x75, 0x18, 0x3b, 0x3e, 0x04, 0x71, + 0xd8, 0x95, 0xae, 0x28, 0x87, 0xcb, 0xb9, 0xcb, 0x71, 0xac, 0xa2, 0x41, 0xfc, 0x2d, + 0x9a, 0xb2, 0x85, 0xf7, 0xda, 0xd4, 0xbc, 0x80, 0x82, 0x86, 0x80, 0x5d, 0x0e, 0xd0, + 0x28, 0x55, 0x36, 0xc7, 0xdf, 0xb0, 0x3c, 0x75, 0xb1, 0xa3, 0x00, 0x92, 0xca, 0x2c, + 0x27, 0x72, 0xe2, 0x07, 0x16, 0x12, 0x9e, 0xaa, 0x09, 0xd9, 0x39, 0x54, 0x6c, 0x55, + 0xca, 0xf3, 0x57, 0xf1, 0xba, 0x36, 0xe5, 0xa2, 0x19, 0xf8, 0x8c, 0x8e, 0x50, 0x2b, + 0x4b, 0xd4, 0xa0, 0x6b, 0xd9, 0x4a, 0x4a, 0x54, 0xcf, 0xb8, 0x5c, 0xc0, 0x80, 0x3d, + 0xdc, 0xa3, 0x68, 0xf9, 0x6b, 0x6f, 0xc9, 0x62, 0x67, 0x85, 0xc8, 0x2d, 0xe5, 0xbd, + 0x71, 0x45, 0x94, 0x0e, 0xb2, 0xd6, 0xcf, 0xac, 0x18, 0x7d, 0xb0, 0x0f, 0x1d, 0x14, + 0x82, 0x53, 0x2f, 0x0d, 0xfe, 0xed, 0x12, 0x66, 0x94, 0x4a, 0xb1, 0x96, 0x98, 0xd1, + 0x12, 0xc9, 0x0b, 0xd4, 0x57, 0xae, 0x50, 0xe6, 0x6d, 0xc9, 0xeb, 0x00, 0x02, 0x85, + 0x92, 0x30, 0x95, 0x28, 0x82, 0x91, 0x1d, 0x1c, 0x86, 0x15, 0xef, 0x13, 0x11, 0xee, + 0x76, 0xca, 0xf9, 0xea, 0x58, 0x40, 0x24, 0x9f, 0x37, 0x65, 0x88, 0x12, 0xc5, 0x70, + 0x61, 0x4b, 0x9d, 0x6a, 0x7b, 0x8e, 0xc1, 0x21, 0x47, 0xb9, 0xec, 0x25, 0x9a, 0xde, + 0x05, 0x01, 0x78, 0x3f, 0xc0, 0x03, 0x60, 0x65, 0x1a, 0x10, 0x2e, 0x7d, 0x38, 0x31, + 0xda, 0xdd, 0x43, 0x86, 0x4a, 0x9e, 0x69, 0x42, 0xf8, 0xc0, 0xeb, 0x49, 0x86, 0xa5, + 0x8f, 0xc2, 0xb3, 0xac, 0x9a, 0x52, 0x98, 0x24, 0x25, 0xc2, 0x04, 0xa3, 0x5b, 0x1b, + 0x21, 0xe1, 0x5f, 0xea, 0x1d, 0x7a, 0x25, 0xd2, 0x3a, 0x78, 0x5d, 0x43, 0x9d, 0xd4, + 0x2d, 0xb3, 0xe0, 0x6e, 0x6b, 0xdd, 0x04, 0x78, 0x68, 0x8f, 0xb3, 0x61, 0x70, 0xf1, + 0x71, 0xc3, 0xa2, 0xef, 0xf9, 0x1a, 0x93, 0x34, 0xaf, 0xa6, 0xaf, 0x6b, 0x13, 0x03, + 0x0d, 0xf9, 0xb8, 0xf3, 0xf1, 0xce, 0x9d, 0x68, 0x18, 0x92, 0x9d, 0xbf, 0x1c, 0x74, + 0x6a, 0xe4, 0x4d, 0x50, 0x70, 0x6b, 0x95, 0x5e, 0xbc, 0xcb, 0xa4, 0x30, 0xf4, 0xc2, + 0x0e, 0xa0, 0x4c, 0x75, 0xe6, 0x2b, 0x1c, 0xb7, 0x6a, 0x73, 0xf4, 0x57, 0x54, 0x86, + 0x38, 0x66, 0x5c, 0xd7, 0x98, 0x33, 0xbd, 0x93, 0xb1, 0x45, 0x57, 0xb5, 0xf2, 0xf0, + 0x5b, 0x17, 0x51, 0x30, 0xd0, 0xc2, 0x03, 0xdf, 0xd5, 0xae, 0x65, 0x17, 0xec, 0xfa, + 0x37, 0x9a, 0xc8, 0xf2, 0xb7, 0x95, 0x04, 0x70, 0x08, 0x7b, 0xc8, 0x04, 0x6c, 0x0f, + 0xd7, 0x5b, 0xe0, 0xec, 0x6b, 0x8d, 0x4a, 0x49, 0xd7, 0xa3, 0xec, 0xf4, 0x70, 0x84, + 0x56, 0xce, 0xcb, 0x05, 0x71, 0x3a, 0x88, 0x9d, 0x59, 0x72, 0x16, 0x6b, 0x83, 0x71, + 0x15, 0x2a, 0xd5, 0x70, 0x2e, 0xc9, 0xf2, 0x29, 0x2a, 0x69, 0x67, 0xad, 0x40, 0x15, + 0xcb, 0xc9, 0x27, 0x5c, 0xcd, 0xa0, 0x21, 0x7b, 0x19, 0x97, 0x7e, 0x86, 0xb5, 0xcd, + 0x2d, 0x70, 0x1b, 0x18, 0xf5, 0xd9, 0x8a, 0x04, 0xf6, 0xe3, 0xb8, 0xf6, 0x47, 0xd0, + 0xe4, 0x16, 0xc2, 0x3a, 0xca, 0x94, 0xe3, 0x0c, 0x09, 0xae, 0xa5, 0xeb, 0xb5, 0xdc, + 0xf8, 0xd2, 0x37, 0x1c, 0xb9, 0x28, 0xc3, 0x0c, 0x35, 0x2f, 0x26, 0x96, 0x52, 0xde, + 0xc3, 0x82, 0xa7, 0xcd, 0xdd, 0x77, 0x32, 0x38, 0x47, 0x9b, 0xcf, 0x4b, 0x87, 0x55, + 0x24, 0x34, 0x63, 0x67, 0x7a, 0xb3, 0x35, 0xbe, 0xdd, 0x70, 0x71, 0x75, 0x18, 0x91, + 0x79, 0xde, 0xa8, 0x5a, 0x83, 0x2c, 0x32, 0xb9, 0x40, 0x35, 0x39, 0x39, 0xae, 0x7e, + 0x57, 0xb0, 0x9f, 0x65, 0xfc, 0x79, 0xa3, 0x89, 0x89, 0xc1, 0x0a, 0x8f, 0x8c, 0xc1, + 0xc2, 0x27, 0xcb, 0x89, 0x07, 0x73, 0xd4, 0xf2, 0x81, 0x37, 0xee, 0xbd, 0x85, 0x27, + 0x82, 0xd6, 0xdb, 0x30, 0x39, 0xf0, 0x96, 0x08, 0xb2, 0x9d, 0x20, 0xeb, 0x37, 0x34, + 0x06, 0x99, 0xe8, 0x8f, 0xd1, 0x40, 0x66, 0xdf, 0x35, 0xa5, 0xbd, 0xe4, 0x52, 0xdc, + 0x61, 0x2d, 0xa6, 0xed, 0xc8, 0xfb, 0x15, 0xd3, 0xe6, 0xb8, 0x6f, 0x63, 0xe6, 0x40, + 0x49, 0xd3, 0xcd, 0x94, 0xc4, 0xfd, 0xc7, 0x9a, 0x82, 0x43, 0x3a, 0xa2, 0xd1, 0xdb, + 0x3b, 0xd2, 0x65, 0x76, 0x03, 0xe1, 0x6e, 0xf9, 0x89, 0x1e, 0x34, 0xc4, 0x0c, 0x3e, + 0x25, 0x67, 0xc8, 0x83, 0xa2, 0xb8, 0x08, 0xd0, 0xfa, 0x3c, 0x3a, 0xb8, 0xc4, 0xff, + 0xeb, 0xbe, 0xb7, 0xf9, 0x7d, 0xa1, 0x0b, 0xc9, 0x5e, 0x5b, 0x1e, 0xba, 0xdb, 0x2d, + 0x0e, 0x7f, 0x7f, 0x27, 0x63, 0xb8, 0xc7, 0xef, 0xcb, 0x3e, 0x48, 0x62, 0xc8, 0xb6, + 0x52, 0x2d, 0xb2, 0x09, 0x6b, 0xbb, 0xe9, 0xd7, 0x82, 0x5b, 0x29, 0x1a, 0xf4, 0x8e, + 0xc0, 0xa8, 0x64, 0xe4, 0x9d, 0x0e, 0x0c, 0x66, 0x88, 0xc7, 0x65, 0xa2, 0x7a, 0xf2, + 0xbf, 0xae, 0x25, 0xad, 0xfe, 0xa4, 0x6b, 0x27, 0x1c, 0x81, 0xee, 0xaa, 0x57, 0x79, + 0x60, 0x07, 0x5b, 0x93, 0x68, 0x06, 0x5d, 0x4b, 0x49, 0x8b, 0x3f, 0x8c, 0xc8, 0x13, + 0x3f, 0x48, 0x12, 0x3c, 0xbb, 0xc9, 0xb6, 0xd3, 0x85, 0x19, 0x94, 0x07, 0x94, 0x61, + 0x4d, 0xb5, 0xc1, 0xc5, 0xba, 0x09, 0xe2, 0x3f, 0x2b, 0x80, 0xc9, 0x88, 0x20, 0x1b, + 0x27, 0x0e, 0x2a, 0x74, 0x72, 0x50, 0x17, 0x0e, 0x46, 0x75, 0x81, 0x53, 0x84, 0x40, + 0x91, 0xf7, 0x03, 0x76, 0xbe, 0x7a, 0xfe, 0x80, 0x99, 0x43, 0xcb, 0x10, 0xce, 0x24, + 0x6e, 0x15, 0x92, 0x63, 0xf3, 0x91, 0x9c, 0xb1, 0xc7, 0xc7, 0x94, 0x05, 0x4c, 0xc4, + 0x01, 0x66, 0x43, 0x5e, 0x2a, 0xb1, 0x01, 0x30, 0xcc, 0x59, 0x4b, 0x09, 0xfa, 0x10, + 0xe8, 0x57, 0x7c, 0xf3, 0xbd, 0xbd, 0xa0, 0x7b, 0x9c, 0x2a, 0x26, 0x09, 0xb5, 0x84, + 0xdd, 0x86, 0xee, 0x4e, 0xa4, 0x5e, 0xb3, 0x92, 0xfb, 0xe1, 0x90, 0xda, 0xca, 0xc4, + 0x08, 0xd7, 0x4d, 0xd7, 0xf1, 0x65, 0xa3, 0xd7, 0x6d, 0xbd, 0x75, 0x0d, 0xbc, 0x6b, + 0x5a, 0xf3, 0x81, 0xb0, 0xab, 0xb2, 0xa5, 0x79, 0x04, 0xb8, 0xaf, 0x88, 0x71, 0x6c, + 0x81, 0x4a, 0x38, 0xb5, 0x44, 0xc1, 0x3e, 0xfb, 0x81, 0xc1, 0x31, 0xbb, 0xbe, 0x24, + 0x3c, 0x6b, 0x48, 0xf1, 0x0c, 0xa4, 0x12, 0x55, 0x1e, 0x41, 0xab, 0x38, 0x00, 0xf9, + 0x10, 0x40, 0x61, 0x25, 0xab, 0x06, 0xd9, 0xf2, 0x4a, 0x5f, 0x8d, 0xd9, 0x39, 0xb2, + 0xc4, 0x6d, 0x3f, 0x6b, 0x22, 0xc6, 0x48, 0x20, 0x94, 0xbd, 0xa9, 0x50, 0xfa, 0x58, + 0xc9, 0x34, 0xb8, 0x38, 0x4c, 0x7a, 0xcd, 0x43, 0x35, 0x0e, 0xa3, 0x4c, 0x67, 0x0c, + 0xc4, 0xc2, 0x8c, 0x30, 0xd2, 0xc1, 0xc6, 0x1c, 0x24, 0x29, 0x91, 0xbd, 0x17, 0x4b, + 0x4e, 0x88, 0x1c, 0xa3, 0xa0, 0xf1, 0x22, 0x19, 0xfe, 0x68, 0xc4, 0x8b, 0xaf, 0x35, + 0x44, 0xc4, 0x8a, 0xe1, 0x75, 0xc0, 0xc0, 0xc1, 0x9d, 0x7d, 0x73, 0x53, 0x30, 0x45, + 0xd8, 0xe8, 0x71, 0xbd, 0xc0, 0x2b, 0x96, 0x8b, 0x42, 0x66, 0x94, 0x62, 0xfd, 0xd2, + 0xd2, 0x42, 0x1d, 0x88, 0x04, 0xe6, 0x47, 0xf9, 0xf4, 0x2c, 0x32, 0xea, 0x68, 0x3b, + 0x53, 0x81, 0x85, 0x0a, 0x1c, 0x6a, 0xc5, 0x7f, 0x5e, 0x90, 0x99, 0xd5, 0xd0, 0x29, + 0x44, 0x62, 0x65, 0xff, 0xca, 0x53, 0xde, 0x7b, 0xff, 0xf9, 0x66, 0xed, 0xa9, 0x2d, + 0x2b, 0x91, 0x40, 0xc2, 0x8b, 0xbb, 0xb8, 0x3a, 0x84, 0xfe, 0x72, 0x3e, 0xf3, 0xa0, + 0x87, 0x9c, 0x74, 0xff, 0x9c, 0x32, 0x7c, 0x48, 0x74, 0x34, 0xcd, 0x2d, 0x79, 0x51, + 0x77, 0x0c, 0xaf, 0xbe, 0x20, 0x00, 0xcc, 0x92, 0xc3, 0x52, 0x74, 0xdb, 0xdf, 0x56, + 0x78, 0x72, 0x80, 0x6c, 0x5e, 0x62, 0x3b, 0xc7, 0xf7, 0x2e, 0x20, 0x43, 0x44, 0x5e, + 0x76, 0xab, 0x41, 0x94, 0x8d, 0x66, 0xca, 0x0b, 0x0d, 0x23, 0x2e, 0x2d, 0xf7, 0x36, + 0xc7, 0xf5, 0x62, 0x48, 0x30, 0x15, 0xd4, 0x53, 0xe1, 0xa9, 0x57, 0x77, 0xb9, 0x1a, + 0x8a, 0x8c, 0xc8, 0x1a, 0x9d, 0xd9, 0x9e, 0x8c, 0x4c, 0x65, 0x6f, 0x7c, 0x5d, 0xfd, + 0x97, 0xba, 0xd6, 0xf8, 0xa7, 0xb0, 0xb3, 0x89, 0x99, 0xcd, 0xef, 0x05, 0x22, 0x71, + 0xd8, 0xbf, 0x69, 0xe1, 0x0e, 0x3f, 0x20, 0x34, 0xf3, 0xcb, 0x64, 0x1c, 0x8e, 0x3f, + 0x5a, 0xf7, 0x8b, 0xa5, 0x47, 0x43, 0xa0, 0xb6, 0xcf, 0x1c, 0xb0, 0xb1, 0xcb, 0xa9, + 0xaf, 0x27, 0xa7, 0xc1, 0x95, 0x52, 0x04, 0x91, 0x09, 0x3a, 0xf3, 0x6e, 0x55, 0xcd, + 0xd5, 0x0e, 0xe9, 0xaa, 0x3e, 0xfe, 0xc2, 0xb4, 0x53, 0x4c, 0x4e, 0x82, 0x8f, 0x53, + 0x11, 0x34, 0x0a, 0xae, 0x91, 0xaf, 0xcf, 0x2d, 0xec, 0x63, 0x99, 0x4c, 0x14, 0xe4, + 0x39, 0x07, 0x1a, 0xf7, 0xf4, 0xb0, 0x4e, 0x78, 0xe2, 0x7c, 0x97, 0xc4, 0x47, 0xee, + 0x29, 0xba, 0x13, 0x00, 0x25, 0xd5, 0x6e, 0x83, 0x69, 0x4c, 0x8d, 0xf7, 0x1c, 0x2b, + 0xe5, 0x5d, 0xed, 0xf0, 0xf9, 0x7b, 0xb3, 0x7c, 0xc4, 0x2f, 0xdb, 0x5d, 0x41, 0xb2, + 0x40, 0xd3, 0x0c, 0x62, 0xac, 0xd5, 0x83, 0xdc, 0xb2, 0x92, 0xc3, 0x6f, 0x5f, 0xff, + 0x2a, 0xcf, 0x3e, 0xbf, 0x17, 0x9e, 0x1d, 0xe4, 0x55, 0xad, 0xc4, 0x34, 0x69, 0x12, + 0x5c, 0x16, 0xef, 0x35, 0x2d, 0xa3, 0xce, 0x86, 0x27, 0x52, 0x32, 0x16, 0x5c, 0xc4, + 0x77, 0x86, 0x01, 0xad, 0xdd, 0x50, 0x06, 0x1e, 0x6e, 0xec, 0x3f, 0x47, 0x5d, 0x9c, + 0x95, 0xbf, 0xd8, 0x1b, 0x00, 0xd6, 0xe8, 0xcf, 0xac, 0xf9, 0x69, 0x32, 0xab, 0x9b, + 0xbf, 0xbc, 0xd7, 0x2b, 0x5d, 0x49, 0x26, 0xe6, 0x00, 0xae, 0xc0, 0x1e, 0x68, 0x77, + 0x0d, 0x21, 0x70, 0xf7, 0x34, 0xa3, 0x1c, 0xe8, 0xfa, 0xf4, 0xd9, 0xea, 0x47, 0xd6, + 0xa4, 0xfe, 0x11, 0x8a, 0x0c, 0x1d, 0x45, 0xd5, 0x3b, 0x76, 0x51, 0xc1, 0x55, 0xea, + 0x0c, 0x15, 0xe6, 0xe1, 0x7c, 0x96, 0x7d, 0x08, 0x46, 0x4c, 0x8f, 0x8f, 0x59, 0xbc, + 0x97, 0x3f, 0x24, 0x8d, 0xe5, 0x03, 0xbb, 0x15, 0x86, 0xa0, 0x9e, 0x58, 0xcf, 0x9a, + 0xc6, 0x07, 0x67, 0xc7, 0xc2, 0xe0, 0xb7, 0xcf, 0x9a, 0x77, 0x0f, 0x31, 0x8a, 0x53, + 0x10, 0x40, 0x7d, 0x0c, 0x56, 0xad, 0xc9, 0xd7, 0xf7, 0x84, 0x3f, 0xd4, 0x33, 0x3f, + 0x3d, 0xed, 0x99, 0x0b, 0x30, 0x85, 0x6a, 0xe0, 0x16, 0xd7, 0xf0, 0xd6, 0xd5, 0x3d, + 0x41, 0x74, 0xd9, 0xa3, 0xf1, 0x23, 0x51, 0x7d, 0x5b, 0x86, 0x92, 0x8d, 0x12, 0xa3, + 0x92, 0x0e, 0xea, 0xe3, 0xa8, 0x99, 0xd5, 0x4e, 0x53, 0x5a, 0x1c, 0x6c, 0x6c, 0x51, + 0x35, 0x6f, 0x9d, 0x8e, 0x59, 0x79, 0x3f, 0x0a, 0x20, 0x50, 0xd4, 0xf6, 0xb9, 0xc5, + 0x03, 0xc6, 0x3f, 0xff, 0x3f, 0xcc, 0x0c, 0x1c, 0xae, 0xad, 0xf9, 0x08, 0x05, 0x27, + 0x41, 0xdb, 0x71, 0xf6, 0xc8, 0x57, 0xdb, 0x76, 0xb3, 0xa3, 0x3f, 0x14, 0xe5, 0x20, + 0x6b, 0x27, 0x1b, 0xdc, 0x09, 0x93, 0x08, 0x6c, 0x8c, 0x55, 0xaf, 0x5f, 0xbd, 0x84, + 0x4f, 0x41, 0xde, 0x94, 0xb9, 0x21, 0xdb, 0x1e, 0xc6, 0x92, 0x14, 0xc1, 0xe7, 0xc8, + 0xff, 0x07, 0x6c, 0x77, 0x67, 0x3d, 0x9f, 0x06, 0x51, 0x80, 0xef, 0x99, 0xc3, 0xd5, + 0xf9, 0xa6, 0x8c, 0xbc, 0x3f, 0x2a, 0x72, 0x4d, 0x80, 0xe3, 0xdf, 0x03, 0x69, 0xb1, + 0x0a, 0x04, 0x47, 0xfd, 0x8e, 0x10, 0x01, 0xbc, 0x3a, 0x1e, 0xd4, 0xae, 0xa2, 0x0a, + 0xf8, 0x5d, 0x41, 0x5d, 0x73, 0x81, 0xdf, 0xab, 0x91, 0x81, 0x70, 0x15, 0x17, 0x00, + 0xd0, 0xaf, 0x17, 0x97, 0x28, 0xcd, 0xa5, 0xaf, 0x77, 0xf4, 0x05, 0x17, 0x9c, 0x79, + 0xaf, 0x1e, 0xa4, 0xbe, 0xda, 0x30, 0x07, 0xe7, 0x48, 0xfe, 0xcd, 0x53, 0x93, 0x76, + 0x88, 0x80, 0x54, 0x87, 0x9f, 0x93, 0x94, 0xe8, 0x4a, 0xd7, 0x13, 0xee, 0x64, 0x24, + 0x5c, 0xaf, 0x13, 0x54, 0x2e, 0xbe, 0xd7, 0xda, 0xfd, 0xd8, 0x0b, 0xba, 0x2c, 0xd2, + 0x07, 0x07, 0xf2, 0x0a, 0xee, 0x05, 0x4a, 0x2b, 0x0d, 0x3b, 0x28, 0x17, 0x85, 0x8c, + 0xc1, 0x93, 0xed, 0xf8, 0x55, 0x25, 0xe9, 0xb9, 0x87, 0x8e, 0xd9, 0xd3, 0x4d, 0x95, + 0x47, 0x8a, 0xe0, 0x4c, 0x3e, 0x91, 0x4c, 0xbf, 0x18, 0x2c, 0x03, 0x8e, 0x25, 0x80, + 0xbd, 0x1e, 0x2f, 0xf0, 0x80, 0xd9, 0xff, 0xc1, 0x9f, 0x4e, 0x1d, 0xe4, 0x75, 0x13, + 0xf3, 0x7a, 0x61, 0xfe, 0xc7, 0x08, 0x54, 0xd9, 0xfc, 0xa0, 0x07, 0x7b, 0x77, 0x0b, + 0x02, 0x32, 0x9d, 0x6b, 0x08, 0xaa, 0xcc, 0x5e, 0xf2, 0xb8, 0xac, 0xb4, 0xe6, 0x48, + 0xfa, 0x99, 0xa0, 0xdd, 0xb3, 0x43, 0xc8, 0xaa, 0xf6, 0x60, 0x66, 0xac, 0x92, 0xdc, + 0xf4, 0x9a, 0x64, 0xd1, 0x24, 0x5d, 0x61, 0x47, 0xb8, 0xd2, 0x86, 0x3b, 0xad, 0x57, + 0x3e, 0x54, 0x80, 0xdc, 0x55, 0x64, 0x52, 0xf4, 0x7c, 0xea, 0x6a, 0xe6, 0x01, 0x0e, + 0xad, 0xef, 0x59, 0x5a, 0xe4, 0x32, 0x1d, 0x69, 0x69, 0xb7, 0x3e, 0x1f, 0x33, 0x36, + 0x15, 0xa3, 0xc9, 0x6f, 0x59, 0x14, 0x13, 0x5c, 0x46, 0xdc, 0xd3, 0xca, 0xb8, 0x02, + 0x0c, 0x57, 0xef, 0x70, 0xdf, 0x2e, 0x62, 0x89, 0x64, 0x28, 0x00, 0x00, 0xf4, 0xcf, + 0xb6, 0x47, 0x89, 0x14, 0xc3, 0xe4, 0xfe, 0xbb, 0x1e, 0x26, 0x16, 0x92, 0x6c, 0x54, + 0x51, 0xd3, 0xe8, 0x59, 0xc4, 0xb3, 0x0a, 0x8f, 0x07, 0x70, 0x8c, 0xcd, 0x73, 0x6b, + 0xc9, 0xfd, 0xd4, 0xff, 0x00, 0x8d, 0xee, 0x0a, 0xe3, 0x5a, 0x81, 0xa8, 0x86, 0x9f, + 0x11, 0x2d, 0x4f, 0x73, 0x22, 0x25, 0xda, 0x84, 0xf5, 0x2a, 0x8d, 0xb5, 0xdf, 0x6f, + 0x5d, 0xd5, 0xa6, 0x2e, 0xdb, 0xc6, 0xad, 0x29, 0xc2, 0xb7, 0x60, 0x56, 0xd8, 0xb6, + 0x0f, 0x0e, 0x2b, 0xf8, 0x29, 0x90, 0xad, 0x88, 0x8a, 0x4c, 0xbf, 0xa0, 0x09, 0xbb, + 0x39, 0x60, 0x26, 0x2a, 0x3e, 0x99, 0xbe, 0x56, 0x38, 0x0c, 0x75, 0xc6, 0x11, 0xfa, + 0x85, 0x52, 0x78, 0x27, 0x29, 0x9f, 0xa8, 0xa1, 0xa7, 0xfd, 0x9d, 0xac, 0x32, 0xca, + 0x59, 0xb8, 0xb1, 0x4c, 0x30, 0xd7, 0x11, 0x2c, 0xdb, 0x24, 0xce, 0x81, 0x66, 0x8c, + 0x54, 0x29, 0x68, 0x92, 0x0c, 0x54, 0x9c, 0x8a, 0x5b, 0xfb, 0x53, 0x72, 0x24, 0x3d, + 0x8d, 0xad, 0x7d, 0x69, 0xcb, 0x62, 0x14, 0xa1, 0x8e, 0xb3, 0x6c, 0xb7, 0x38, 0x73, + 0x7c, 0x0c, 0x8a, 0x91, 0xdb, 0x48, 0xd9, 0xf7, 0x71, 0xed, 0x14, 0x3b, 0x41, 0x40, + 0x35, 0x6c, 0xe5, 0x71, 0xb4, 0xe0, 0x8a, 0xc6, 0xda, 0x81, 0x32, 0x1a, 0x1d, 0x91, + 0xa3, 0x6c, 0xb5, 0xa1, 0x54, 0x4a, 0x57, 0xf3, 0x1f, 0xbe, 0xa3, 0xa3, 0x3f, 0x08, + 0x67, 0xc4, 0xef, 0xb0, 0xa5, 0x1b, 0x35, 0x1c, 0xd3, 0x1d, 0x40, 0x4c, 0x80, 0xf4, + 0xc8, 0x42, 0xac, 0x03, 0x9e, 0xfd, 0x6b, 0xc2, 0x1a, 0x22, 0xb0, 0xf7, 0x2d, 0x1f, + 0x57, 0x73, 0xad, 0xb3, 0x4a, 0x47, 0x03, 0x9c, 0xef, 0x10, 0x06, 0x2e, 0xa3, 0x60, + 0x21, 0xd9, 0x17, 0x0f, 0x6a, 0x6f, 0x25, 0x27, 0x0d, 0xff, 0xfa, 0x5b, 0x96, 0xe3, + 0x2b, 0x04, 0x85, 0x5c, 0x83, 0x2c, 0x1b, 0xb6, 0xf2, 0x1a, 0x62, 0x5b, 0xe5, 0x8f, + 0x2e, 0xe4, 0xc9, 0x0c, 0x61, 0xce, 0x8d, 0xb0, 0x8f, 0x8f, 0x2f, 0xb0, 0x43, 0x88, + 0x63, 0xbe, 0x54, 0x81, 0x65, 0xa2, 0xc1, 0xf8, 0xd6, 0xad, 0xad, 0x78, 0xe7, 0xf8, + 0x18, 0xea, 0x8c, 0x02, 0x6f, 0xf8, 0x23, 0x54, 0x29, 0x50, 0xa1, 0x1d, 0xe6, 0x49, + 0x0e, 0x1e, 0x6f, 0x30, 0x4d, 0xf4, 0x4d, 0x5b, 0x25, 0x57, 0x47, 0x56, 0x47, 0x7f, + 0x49, 0xfc, 0x94, 0xcb, 0xca, 0xd8, 0x43, 0xc8, 0xfa, 0x9e, 0xf7, 0x02, 0xdf, 0x23, + 0x7f, 0x54, 0x03, 0x9b, 0x5d, 0xbc, 0xcc, 0xb3, 0xe7, 0xdb, 0x31, 0x3b, 0x63, 0x90, + 0xe3, 0xad, 0x53, 0x68, 0x1d, 0xc5, 0xe4, 0x90, 0x17, 0x22, 0x1a, 0xc1, 0x69, 0xac, + 0x47, 0x2d, 0xb0, 0xb1, 0x32, 0x8e, 0x4c, 0xa0, 0xa1, 0x17, 0x27, 0x70, 0xd5, 0x8e, + 0x86, 0xad, 0x96, 0x2e, 0x0a, 0xff, 0xd5, 0xbf, 0xb0, 0x98, 0x99, 0x42, 0xbc, 0x97, + 0x03, 0xba, 0x37, 0x03, 0x26, 0x77, 0xba, 0xde, 0xd6, 0x23, 0xfd, 0xb5, 0xad, 0xd5, + 0xe8, 0x04, 0x33, 0xf7, 0xa9, 0x79, 0xe9, 0x3b, 0x0c, 0x3d, 0x8e, 0xdb, 0x1e, 0x28, + 0xbc, 0x5e, 0xee, 0xea, 0x65, 0xd6, 0xbe, 0x24, 0x95, 0x13, 0x16, 0xf6, 0x5a, 0x48, + 0xdc, 0x99, 0xee, 0xf0, 0x12, 0xed, 0x1f, 0xcb, 0x26, 0x71, 0xa9, 0xf3, 0x67, 0x19, + 0x42, 0x7f, 0x7a, 0xb1, 0x11, 0xac, 0x2d, 0xae, 0x77, 0x21, 0x6b, 0xc6, 0xc9, 0x94, + 0x86, 0x05, 0xfb, 0x5e, 0xf0, 0x76, 0xe2, 0xdb, 0xf5, 0xa0, 0x54, 0x12, 0xed, 0x28, + 0x38, 0x90, 0xa6, 0xf8, 0xfa, 0x34, 0x07, 0xe1, 0xd0, 0xd5, 0x36, 0x44, 0x12, 0x6c, + 0xf5, 0x01, 0xbc, 0x2e, 0xde, 0x0d, 0x82, 0x65, 0x69, 0xac, 0x18, 0x80, 0x58, 0x20, + 0xc5, 0xdd, 0xd0, 0xc0, 0xa4, 0x5b, 0x7f, 0x0d, 0x2f, 0xa1, 0x47, 0xfc, 0x42, 0x7b, + 0x93, 0x54, 0x99, 0x63, 0x25, 0x69, 0x5b, 0x36, 0x6d, 0xff, 0xc5, 0x4d, 0x89, 0xf6, + 0xa6, 0xf9, 0x84, 0xde, 0xa0, 0xd0, 0x3f, 0xc5, 0x07, 0xf8, 0xb1, 0xd4, 0x66, 0x1a, + 0x47, 0x80, 0x44, 0xce, 0xcf, 0x74, 0x0e, 0x5c, 0xd5, 0x3f, 0x7f, 0xda, 0xc5, 0xa7, + 0x1f, 0xa3, 0x8f, 0xb4, 0x3d, 0xbe, 0xa2, 0x7e, 0x76, 0xc7, 0xe4, 0xb1, 0xc1, 0xf1, + 0x74, 0x6e, 0xca, 0xf3, 0xbc, 0xbd, 0xd9, 0x52, 0x48, 0xf0, 0x22, 0x33, 0x20, 0x72, + 0xd1, 0x59, 0xb0, 0xb6, 0xed, 0x90, 0x85, 0x50, 0x13, 0x68, 0x0d, 0xae, 0x7b, 0x69, + 0xd0, 0xe0, 0x51, 0x49, 0x44, 0x19, 0xd2, 0xbd, 0x91, 0x8d, 0xce, 0xb1, 0xc4, 0x36, + 0x1c, 0x64, 0xe5, 0x1b, 0xd2, 0x84, 0xcf, 0x64, 0xbc, 0x14, 0x56, 0xa2, 0x6f, 0x99, + 0xaa, 0xd4, 0x48, 0xa0, 0x04, 0xeb, 0x9f, 0xc6, 0xd3, 0xb8, 0x2c, 0xbe, 0x0c, 0xe7, + 0xb8, 0x07, 0xc3, 0xa7, 0x33, 0xf1, 0x36, 0xea, 0xca, 0x6c, 0xc4, 0x56, 0x50, 0x30, + 0x04, 0xe0, 0x42, 0x81, 0xd5, 0x23, 0x61, 0x30, 0xfb, 0x38, 0x88, 0xc9, 0x0c, 0x25, + 0x41, 0xcd, 0x20, 0x70, 0xe5, 0x83, 0x5e, 0x96, 0x58, 0x84, 0x39, 0x3b, 0xe4, 0xab, + 0x1a, 0x8d, 0x95, 0x96, 0x77, 0xce, 0xb0, 0x83, 0x35, 0x1f, 0xa8, 0xfa, 0x84, 0x03, + 0x2d, 0x3a, 0x25, 0x78, 0xde, 0x80, 0x1b, 0x1f, 0x0b, 0x2b, 0x3b, 0xca, 0x73, 0x2e, + 0x12, 0x81, 0x0b, 0x45, 0xc2, 0xe4, 0xee, 0xb3, 0xe6, 0xeb, 0x98, 0x19, 0xb3, 0xec, + 0x7b, 0xef, 0x0a, 0x6e, 0xff, 0x40, 0x4f, 0x6c, 0x4b, 0x64, 0x28, 0x93, 0xbc, 0xaf, + 0xab, 0x6c, 0xc7, 0x39, 0x83, 0x54, 0x8c, 0x1d, 0x5b, 0x3d, 0xde, 0xcc, 0x5d, 0x74, + 0x4a, 0x84, 0xd9, 0x34, 0xb8, 0xb2, 0x45, 0x3b, 0xe0, 0x18, 0xf8, 0x5a, 0x2c, 0xed, + 0x33, 0x27, 0xca, 0xba, 0xf8, 0xdb, 0x8a, 0x6a, 0xdd, 0x3e, 0xd8, 0x1d, 0x58, 0xa4, + 0x76, 0x41, 0xea, 0x28, 0x59, 0x13, 0x0b, 0x2e, 0x1a, 0xfe, 0xa8, 0xc8, 0xf2, 0xcd, + 0xd2, 0x00, 0x8c, 0x10, 0x98, 0x1e, 0x90, 0xa1, 0x86, 0x13, 0x22, 0x69, 0xbb, 0x87, + 0x2b, 0xce, 0xd0, 0x50, 0x17, 0x9b, 0xe7, 0xbc, 0x23, 0x31, 0x8f, 0x7d, 0x28, 0x65, + 0x8e, 0x08, 0x69, 0x3f, 0x37, 0xdc, 0xc8, 0xbf, 0xf2, 0xce, 0x38, 0xf2, 0xcf, 0xd3, + 0x6b, 0x10, 0xc6, 0x36, 0xd2, 0x95, 0x01, 0x43, 0x1b, 0xc0, 0xb5, 0x5c, 0xdd, 0x28, + 0x5e, 0xc6, 0x1b, 0xf9, 0xd1, 0x26, 0xc8, 0x2e, 0x72, 0x2e, 0x96, 0x20, 0x16, 0x4b, + 0xe5, 0xae, 0x4f, 0xd3, 0x61, 0x74, 0x71, 0x85, 0xf2, 0xda, 0x10, 0x83, 0x5d, 0x28, + 0x01, 0x70, 0x2b, 0xed, 0x5c, 0x5d, 0x2b, 0x8a, 0xc5, 0x3b, 0x55, 0xba, 0x19, 0x5a, + 0x0b, 0xa9, 0x16, 0x56, 0xcc, 0x84, 0x1a, 0x94, 0xab, 0xbb, 0xff, 0x84, 0xa6, 0x19, + 0xfa, 0x87, 0xbd, 0x4d, 0x98, 0x0c, 0x2b, 0xcd, 0x8b, 0x77, 0xc5, 0x66, 0x65, 0x56, + 0xc9, 0x61, 0xf8, 0x72, 0x9e, 0x73, 0xd7, 0x73, 0xb9, 0x54, 0x3a, 0xa4, 0xf7, 0xcf, + 0xba, 0x25, 0xc9, 0x66, 0xab, 0x09, 0x8e, 0xdb, 0x7f, 0xc4, 0xcc, 0x82, 0x74, 0x30, + 0xf6, 0x41, 0x37, 0x52, 0x36, 0xd2, 0x9b, 0x9c, 0x41, 0x21, 0x94, 0xbd, 0x15, 0xf1, + 0x17, 0xdb, 0x45, 0x3e, 0x1d, 0xfe, 0x69, 0x9c, 0x8d, 0x54, 0xae, 0x28, 0xeb, 0x20, + 0x04, 0x85, 0xea, 0xf9, 0x08, 0x64, 0x79, 0x65, 0xb1, 0x6a, 0x1b, 0xa3, 0x96, 0x3c, + 0x14, 0xb2, 0x8f, 0xc2, 0xc5, 0x38, 0xba, 0x8f, 0x2e, 0x78, 0xe0, 0xa4, 0xc2, 0x4b, + 0x84, 0x5d, 0x91, 0x7c, 0x4a, 0x8c, 0x8e, 0xa7, 0x00, 0xdd, 0xcf, 0x4d, 0x03, 0x9c, + 0x00, 0xd9, 0x9f, 0x76, 0x57, 0xde, 0xf8, 0xbd, 0x2e, 0xad, 0x0e, 0xb4, 0x21, 0x58, + 0x17, 0x95, 0x03, 0xab, 0x43, 0xba, 0x47, 0xbc, 0xe1, 0x21, 0xa6, 0xaa, 0x7d, 0x07, + 0x3e, 0x12, 0xde, 0xe8, 0xe8, 0x7d, 0x2c, 0x28, 0xc1, 0x57, 0x6d, 0x12, 0x01, 0xb8, + 0x25, 0x42, 0x6c, 0x78, 0xbc, 0x61, 0x6d, 0x7f, 0x79, 0x87, 0x4b, 0xeb, 0x51, 0x48, + 0xa0, 0xf7, 0x88, 0x1d, 0xed, 0x54, 0x67, 0xac, 0x4b, 0xe4, 0xca, 0x2f, 0xe4, 0x78, + 0x1a, 0x8d, 0x66, 0xdf, 0xe0, 0xa4, 0x70, 0x33, 0x16, 0xde, 0x1a, 0x3f, 0xab, 0xc2, + 0x7c, 0x61, 0x40, 0xe3, 0x1b, 0x39, 0xe1, 0xe0, 0x8e, 0x81, 0x51, 0xf9, 0x21, 0x3e, + 0x52, 0x38, 0x6d, 0x6e, 0x76, 0x7f, 0x60, 0x82, 0xd3, 0xe5, 0x4a, 0xb2, 0x3a, 0xf5, + 0x52, 0xe7, 0xc9, 0xc2, 0x87, 0xd1, 0x77, 0xca, 0xe2, 0xf7, 0x69, 0xb8, 0x54, 0x09, + 0x9d, 0x00, 0x0c, 0xcb, 0x33, 0x87, 0x27, 0x7b, 0x3a, 0xa3, 0x0e, 0x93, 0xe3, 0x20, + 0x87, 0x08, 0x0c, 0x82, 0x25, 0x46, 0xb9, 0x2a, 0xe3, 0x98, 0xb0, 0x3d, 0x5d, 0xcc, + 0x01, 0x35, 0x00, 0x70, 0xc6, 0x60, 0xf6, 0x46, 0x13, 0xbf, 0xdf, 0xac, 0x99, 0x8c, + 0x24, 0x40, 0x10, 0xdf, 0xae, 0xe7, 0x1c, 0x82, 0xf0, 0xe5, 0x9d, 0x6d, 0x0d, 0xd6, + 0x10, 0x60, 0x52, 0x8b, 0x81, 0x54, 0x28, 0xb9, 0x1e, 0x97, 0xf2, 0x3d, 0x42, 0x7e, + 0x11, 0xb4, 0x48, 0x80, 0xfb, 0xd4, 0x84, 0xaa, 0xcc, 0x92, 0x22, 0x93, 0xd1, 0x0d, + 0xa1, 0x12, 0xcd, 0x4c, 0xd8, 0xbb, 0xa6, 0xd0, 0xf2, 0x2e, 0x1e, 0x8b, 0x3f, 0xc9, + 0x1d, 0xb5, 0x7b, 0x2e, 0xe8, 0xf0, 0x4e, 0x5b, 0x3f, 0xf4, 0x44, 0xd7, 0x27, 0x15, + 0xb5, 0x4a, 0x87, 0x57, 0x88, 0x69, 0x21, 0x8c, 0x4e, 0xf4, 0xdf, 0x65, 0x3b, 0xa8, + 0xfc, 0xec, 0xe0, 0xe8, 0xdc, 0x6b, 0x6d, 0xf9, 0xbc, 0x01, 0x39, 0xa5, 0x98, 0xda, + 0x8c, 0x21, 0xcc, 0x62, 0x68, 0x18, 0x16, 0x24, 0xc0, 0x33, 0x4d, 0x3d, 0x5e, 0x85, + 0x9b, 0x95, 0x7e, 0xb7, 0xad, 0x10, 0xb3, 0x49, 0x2b, 0x8b, 0xa4, 0x8c, 0xbb, 0x5a, + 0x25, 0xca, 0x80, 0x1a, 0x38, 0xc2, 0x72, 0x3a, 0x0d, 0x8b, 0xdb, 0x16, 0x8b, 0xf0, + 0xe3, 0x3f, 0xda, 0xbc, 0x5d, 0x66, 0x59, 0xbe, 0xed, 0xfd, 0x52, 0x9c, 0x19, 0x7f, + 0x00, 0x35, 0x29, 0x9f, 0xb9, 0xaa, 0x17, 0x6b, 0x6c, 0x0b, 0xcd, 0xce, 0x7c, 0x01, + 0xa4, 0xe0, 0x9c, 0x77, 0x4b, 0x97, 0x56, 0xb3, 0xb6, 0x4a, 0xdf, 0x55, 0x6d, 0xb6, + 0xf7, 0xa3, 0x65, 0x9f, 0xab, 0xaa, 0x66, 0x10, 0x5d, 0x88, 0xc7, 0x57, 0xa8, 0xdc, + 0xac, 0xc0, 0xc2, 0x4c, 0x7c, 0x70, 0x17, 0x7a, 0xf9, 0x4e, 0x7c, 0xc8, 0x56, 0xbe, + 0xfe, 0xbe, 0x59, 0x46, 0x91, 0x95, 0x05, 0x6a, 0xbe, 0xb1, 0x47, 0x07, 0xe1, 0x16, + 0xec, 0x88, 0xbe, 0x71, 0xf7, 0x31, 0x1f, 0xda, 0xff, 0xda, 0x99, 0x16, 0xf6, 0xe4, + 0xf7, 0x7f, 0xdb, 0xde, 0x7c, 0x65, 0x3e, 0xf6, 0xe9, 0xa5, 0xe9, 0x76, 0x9a, 0xfc, + 0x27, 0x86, 0x98, 0x9a, 0x89, 0x2e, 0x13, 0xf3, 0x8a, 0x80, 0xee, 0x5f, 0x50, 0xe0, + 0x26, 0x75, 0x66, 0x37, 0x54, 0xbb, 0xec, 0xea, 0x98, 0x70, 0xea, 0xd1, 0xd2, 0x7b, + 0x04, 0xf2, 0xa5, 0xa6, 0x9f, 0x3f, 0x9e, 0x3b, 0xef, 0x7e, 0x07, 0x4f, 0x0a, 0x0b, + 0xde, 0x2d, 0x54, 0x6e, 0x89, 0xde, 0x2f, 0x31, 0x30, 0x08, 0x1b, 0xce, 0xc6, 0x10, + 0x1e, 0x4e, 0xcd, 0xb6, 0x28, 0x2c, 0x91, 0xec, 0x01, 0x36, 0x3a, 0xca, 0x5c, 0xb6, + 0x1d, 0x85, 0x3e, 0x0b, 0x48, 0xb5, 0xc1, 0xf6, 0xb4, 0xe6, 0xf6, 0x71, 0x33, 0x7b, + 0x7c, 0xdb, 0x64, 0x68, 0x84, 0xae, 0x84, 0x15, 0x85, 0x27, 0x17, 0x0d, 0x46, 0xa4, + 0x1e, 0x6f, 0xb6, 0xdd, 0x9a, 0x84, 0x78, 0xbe, 0x76, 0x9e, 0x35, 0x64, 0x4e, 0x6a, + 0xd3, 0x05, 0x75, 0x41, 0x37, 0x71, 0x83, 0x2f, 0x9a, 0x28, 0x88, 0x55, 0xc8, 0x41, + 0x56, 0xd6, 0x10, 0x34, 0x66, 0x8f, 0xe8, 0x74, 0x4c, 0xdc, 0xce, 0xf6, 0xa2, 0x9c, + 0x1b, 0x0c, 0x8d, 0x9b, 0x3e, 0x53, 0xe1, 0x36, 0x4c, 0xf6, 0x7d, 0x28, 0xf7, 0x03, + 0xdb, 0x05, 0x69, 0x8c, 0x94, 0x4d, 0x28, 0xf0, 0x3b, 0xa3, 0x08, 0xde, 0xf1, 0x96, + 0xc1, 0x10, 0x01, 0x60, 0xed, 0xa0, 0xb3, 0x89, 0xe1, 0x4b, 0x9c, 0x43, 0xb2, 0xb8, + 0x6f, 0x65, 0x1a, 0x43, 0x59, 0xc5, 0x62, 0xa3, 0x5e, 0x5f, 0x8f, 0x6b, 0x9a, 0xbe, + 0xcd, 0xe0, 0xb9, 0x76, 0x5d, 0x0e, 0xdd, 0xf6, 0x83, 0x77, 0x12, 0x5a, 0xf6, 0x07, + 0x7a, 0x90, 0x0f, 0x84, 0x79, 0x6d, 0x36, 0xc7, 0xc1, 0xc3, 0x9e, 0xc9, 0xd9, 0x71, + 0x3a, 0xba, 0xb1, 0x63, 0x84, 0x68, 0xcf, 0x31, 0x23, 0x48, 0x8a, 0x80, 0xd0, 0xc2, + 0xf1, 0x5c, 0xfa, 0x4e, 0x0b, 0xfa, 0x02, 0xd2, 0xce, 0xc5, 0x8a, 0xbc, 0xa8, 0xf4, + 0x16, 0x49, 0xde, 0x0a, 0x18, 0xc2, 0xca, 0x37, 0xf3, 0x9d, 0x14, 0xdb, 0xe8, 0x9b, + 0x43, 0x5b, 0xd8, 0x19, 0xd6, 0x9a, 0xd6, 0x32, 0xe3, 0x6b, 0xb9, 0xf8, 0xfe, 0x0b, + 0xb6, 0xf5, 0x8b, 0x82, 0xa7, 0xa5, 0x8a, 0x50, 0x7d, 0xf0, 0x65, 0x42, 0xdf, 0x10, + 0x08, 0x54, 0x21, 0xae, 0x2d, 0xdc, 0x43, 0x34, 0x83, 0x16, 0xfc, 0xea, 0xb9, 0xe0, + 0x2e, 0xe3, 0x49, 0x7f, 0x72, 0x20, 0xc6, 0xe5, 0xc5, 0xc4, 0x25, 0x1c, 0x5f, 0xea, + 0x07, 0x18, 0x1e, 0x0a, 0xbe, 0xd0, 0x06, 0xa1, 0xdd, 0x91, 0x6b, 0x5f, 0x2b, 0xa9, + 0xfe, 0xba, 0x5c, 0x53, 0x9b, 0xac, 0xb0, 0x9d, 0x24, 0x33, 0xf5, 0x1a, 0x8f, 0x80, + 0xd2, 0x51, 0x22, 0xfe, 0xde, 0x7d, 0xb8, 0x0d, 0x17, 0xde, 0x03, 0x4c, 0x10, 0x35, + 0x23, 0xbd, 0x4c, 0x90, 0x1b, 0x5d, 0xb3, 0x46, 0xca, 0x3e, 0x15, 0xbe, 0x98, 0xc7, + 0xc1, 0xcb, 0x61, 0xf1, 0xa8, 0x9d, 0xb3, 0x95, 0xc2, 0x75, 0x6a, 0x68, 0x72, 0x73, + 0x42, 0x0b, 0xf4, 0xb2, 0x87, 0xfe, 0x34, 0x19, 0x4d, 0xfb, 0x60, 0x67, 0x0c, 0xff, + 0xc5, 0x38, 0xd3, 0xaf, 0xe9, 0x0b, 0x03, 0x92, 0x17, 0x11, 0xea, 0xd0, 0x95, 0x08, + 0x9e, 0x77, 0x7d, 0xcf, 0xe1, 0x51, 0x2d, 0x37, 0xbf, 0x5d, 0xb4, 0xfd, 0xae, 0x05, + 0x99, 0x35, 0x99, 0x30, 0x6f, 0xfc, 0xd3, 0xdb, 0xfc, 0xdc, 0x20, 0x51, 0x95, 0x24, + 0x67, 0x5c, 0x16, 0x15, 0x14, 0xc2, 0xf4, 0x99, 0x25, 0x73, 0xfc, 0x56, 0x50, 0x97, + 0x0c, 0xac, 0x00, 0xea, 0x5c, 0xb7, 0x1d, 0x10, 0xab, 0x30, 0xa0, 0x65, 0x3a, 0x13, + 0x7e, 0x50, 0xec, 0xb5, 0xde, 0x0b, 0x4a, 0xef, 0x07, 0xdc, 0x44, 0xfd, 0x47, 0xdb, + 0x58, 0xc2, 0x9f, 0x8f, 0x4e, 0x2d, 0x1b, 0x02, 0x11, 0x36, 0x59, 0xd6, 0x22, 0x63, + 0xa2, 0x47, 0x19, 0x6a, 0xcb, 0xb6, 0xa3, 0x86, 0xab, 0x0c, 0xea, 0x99, 0x57, 0xed, + 0x2c, 0x21, 0x2b, 0x8f, 0x0f, 0x54, 0x9a, 0xc0, 0x12, 0x5a, 0x86, 0xba, 0x15, 0xe5, + 0x54, 0xac, 0xb6, 0x84, 0x86, 0xd8, 0x1d, 0x2a, 0x2d, 0x87, 0xb4, 0xcb, 0xb5, 0x55, + 0x9a, 0xda, 0x38, 0x37, 0x12, 0xdb, 0x85, 0xaa, 0x6f, 0x42, 0x38, 0x5d, 0xcf, 0x29, + 0x09, 0x0d, 0x9e, 0xa6, 0xa6, 0xc4, 0xbe, 0x92, 0x47, 0x9e, 0x78, 0x99, 0xa6, 0x93, + 0x02, 0xc4, 0x83, 0x7f, 0x2d, 0x67, 0xe9, 0x63, 0xb6, 0x06, 0x4b, 0x13, 0xe5, 0x76, + 0x8b, 0x85, 0x46, 0x82, 0xe5, 0xd9, 0xf0, 0x37, 0xa8, 0x07, 0xc9, 0x97, 0x12, 0x48, + 0xc0, 0xed, 0x51, 0x71, 0xd8, 0x71, 0x69, 0x7f, 0xf5, 0xe3, 0xcf, 0xd6, 0x07, 0xad, + 0x7f, 0x9d, 0x47, 0x8f, 0x09, 0x5d, 0x39, 0xf8, 0x6d, 0x93, 0x09, 0xfa, 0x37, 0x27, + 0x74, 0x56, 0x81, 0xd2, 0x90, 0xe9, 0xd0, 0xed, 0x54, 0xf2, 0xaa, 0x75, 0xdf, 0xca, + 0x77, 0xdf, 0x70, 0x3d, 0xf5, 0x3a, 0xec, 0x78, 0x4f, 0x52, 0xae, 0xe3, 0xde, 0xfe, + 0x4e, 0x12, 0xb7, 0x66, 0x56, 0x78, 0xb8, 0xc6, 0xdd, 0x74, 0x98, 0x0a, 0xe3, 0x53, + 0xef, 0x46, 0xd7, 0x0e, 0x09, 0xdb, 0xd8, 0x1f, 0x8b, 0xca, 0x1c, 0x10, 0x65, 0x0f, + 0x8d, 0x95, 0xfe, 0xc5, 0x59, 0x75, 0x36, 0xf3, 0x97, 0xc1, 0x00, 0x99, 0xbf, 0xe7, + 0x70, 0x90, 0x38, 0x3f, 0xd7, 0x56, 0x0f, 0xfb, 0x65, 0x06, 0x9b, 0xcc, 0x7b, 0xf5, + 0x45, 0x86, 0xfa, 0x9d, 0x7d, 0xab, 0xfa, 0xa4, 0x16, 0xef, 0xc5, 0x4f, 0x92, 0x49, + 0x66, 0xa5, 0xf5, 0x70, 0xb6, 0x2f, 0xbb, 0xd4, 0x08, 0x1d, 0x35, 0xd8, 0x8c, 0x4a, + 0x99, 0x6d, 0x8d, 0x17, 0xbc, 0x61, 0xe0, 0x10, 0x9f, 0x28, 0x84, 0x5f, 0xd5, 0xe8, + 0x84, 0x71, 0xed, 0x58, 0x8d, 0xb9, 0xfa, 0xfc, 0xf9, 0xc2, 0xdb, 0x46, 0x82, 0x20, + 0x96, 0x2e, 0xb1, 0x3b, 0xe7, 0x77, 0xb9, 0x7e, 0x14, 0x13, 0xec, 0x3d, 0x58, 0x55, + 0x56, 0xa2, 0x3b, 0xf2, 0xd8, 0x8c, 0x6e, 0x25, 0x7f, 0x41, 0x38, 0x96, 0x9b, 0x57, + 0xcc, 0x4d, 0x7a, 0x84, 0xa3, 0xb3, 0xcb, 0xfa, 0xda, 0x15, 0x92, 0x63, 0x65, 0xc2, + 0x2c, 0x89, 0xec, 0xfb, 0xb0, 0xd9, 0x54, 0xee, 0x62, 0x5c, 0xc5, 0xfb, 0xe2, 0xbb, + 0x19, 0x09, 0x9d, 0x33, 0x60, 0xff, 0xaa, 0x11, 0xd7, 0xaf, 0xb3, 0x82, 0x04, 0xce, + 0xc1, 0xfc, 0xd6, 0x57, 0x17, 0x5f, 0xcb, 0xab, 0xcf, 0x18, 0xff, 0x5c, 0x87, 0x38, + 0x1c, 0x10, 0xf2, 0x7a, 0x2d, 0x2a, 0x77, 0x83, 0x90, 0x34, 0x55, 0xeb, 0x44, 0xee, + 0x3d, 0x84, 0x12, 0x1d, 0x74, 0xf6, 0xa9, 0x28, 0x63, 0x0d, 0x7a, 0x62, 0xcb, 0x46, + 0xcd, 0xcb, 0x5f, 0x90, 0x8c, 0x6e, 0x46, 0x61, 0x3a, 0xd3, 0x84, 0xc9, 0xec, 0x44, + 0x67, 0x97, 0xc9, 0xd9, 0x4f, 0xc0, 0x82, 0x21, 0x18, 0x44, 0x51, 0x3b, 0x9e, 0x52, + 0x76, 0xd4, 0x67, 0x71, 0x55, 0xa8, 0x1e, 0xff, 0x8c, 0xf3, 0x8f, 0x31, 0x8f, 0x73, + 0xca, 0xbf, 0x48, 0xd5, 0xe2, 0x3f, 0x11, 0x9f, 0x57, 0xfe, 0xc2, 0xdc, 0xad, 0x14, + 0x62, 0x8d, 0x60, 0x9f, 0xde, 0xcd, 0x00, 0x65, 0xf7, 0x56, 0x23, 0x69, 0x63, 0x9b, + 0x21, 0x28, 0x00, 0x89, 0x21, 0x8c, 0xc5, 0x24, 0x81, 0x11, 0x7e, 0x73, 0x07, 0xe0, + 0x12, 0x35, 0xf4, 0x7d, 0x49, 0x71, 0x90, 0x7d, 0x35, 0x9f, 0x16, 0x75, 0x4c, 0x7d, + 0x88, 0xdd, 0xc7, 0xcd, 0x38, 0x6f, 0x27, 0xcf, 0x18, 0xa6, 0xa6, 0xa3, 0xa5, 0xb5, + 0xe8, 0xbd, 0xdf, 0xbd, 0xad, 0xd5, 0x23, 0x5d, 0xe4, 0x02, 0xfd, 0xd1, 0xfd, 0x0c, + 0xcf, 0xdb, 0x3a, 0x6f, 0x3c, 0x21, 0xb7, 0xa0, 0xaa, 0xcd, 0x33, 0xda, 0xdf, 0x01, + 0xc8, 0xd6, 0x99, 0x62, 0x0f, 0xda, 0xad, 0xa3, 0x09, 0x3e, 0x1b, 0x9f, 0x7b, 0xf7, + 0x20, 0x1d, 0xe5, 0xab, 0x7f, 0xec, 0x3c, 0x25, 0x00, 0x2e, 0x8c, 0x60, 0xa4, 0x17, + 0xc0, 0x39, 0xac, 0x37, 0xc9, 0xba, 0x0d, 0x56, 0x3b, 0x76, 0x3a, 0x2e, 0x7e, 0xce, + 0xdd, 0x9d, 0x35, 0x89, 0xe6, 0x6b, 0xd2, 0x12, 0xe4, 0x7b, 0x22, 0x6a, 0x1d, 0x49, + 0x0a, 0x1a, 0xd8, 0x1f, 0x1a, 0xa5, 0xa8, 0xd4, 0xea, 0x06, 0xe1, 0xa3, 0xa3, 0xfa, + 0x77, 0x99, 0xb5, 0x27, 0x2f, 0x96, 0x2f, 0xd8, 0xcc, 0xa2, 0x7c, 0x5d, 0xc6, 0xdb, + 0xd2, 0xd1, 0x9d, 0x29, 0xc6, 0x43, 0x9f, 0xe9, 0x16, 0xb3, 0x21, 0x6e, 0xc3, 0x1a, + 0x28, 0x7e, 0xf4, 0x62, 0x23, 0x00, 0x66, 0xff, 0xa4, 0x98, 0xe6, 0xe3, 0xd9, 0x39, + 0xae, 0x07, 0xea, 0x16, 0xc8, 0x42, 0x8a, 0xcf, 0x90, 0x42, 0x09, 0x75, 0x45, 0x75, + 0x87, 0xda, 0xd0, 0x18, 0x38, 0xc8, 0x2e, 0x12, 0x8d, 0x93, 0xf8, 0xe8, 0x9c, 0xea, + 0x37, 0x0b, 0xe4, 0x26, 0xf9, 0x93, 0xbe, 0xfe, 0x1a, 0xe5, 0x93, 0x98, 0xce, 0xae, + 0x2c, 0x5a, 0xa4, 0xe8, 0xc5, 0xdb, 0x4e, 0xe4, 0x5e, 0xf5, 0xe7, 0x51, 0xe5, 0xdc, + 0x09, 0x53, 0x93, 0xc5, 0x68, 0x4a, 0x20, 0x2e, 0xdf, 0x2d, 0xd2, 0x74, 0x51, 0x3b, + 0xba, 0x95, 0x73, 0x8c, 0x84, 0x50, 0xcf, 0xe0, 0x65, 0xd8, 0xe2, 0x19, 0x71, 0x48, + 0x97, 0xcf, 0xdf, 0x1f, 0xd4, 0x22, 0x73, 0x38, 0xdc, 0xf5, 0xd7, 0xdf, 0x83, 0x38, + 0x51, 0x60, 0x28, 0xde, 0x2f, 0xc3, 0x0c, 0x21, 0x74, 0x20, 0x2b, 0x74, 0x1d, 0x3d, + 0xc2, 0xbf, 0x44, 0x7a, 0x0f, 0xc4, 0x3f, 0x68, 0xba, 0xdd, 0x2d, 0x80, 0x84, 0x1b, + 0x0c, 0x57, 0xf2, 0xe2, 0x3b, 0x81, 0x75, 0x6f, 0x18, 0xbb, 0x5a, 0xa4, 0x6c, 0x80, + 0x43, 0xac, 0xa8, 0x66, 0xf8, 0x01, 0x15, 0xb3, 0x1e, 0x35, 0x17, 0xda, 0x79, 0xb0, + 0x44, 0xee, 0xdc, 0x01, 0x2d, 0x63, 0xfe, 0x89, 0x68, 0x3b, 0xb4, 0xf6, 0x60, 0x85, + 0xb4, 0xd1, 0x27, 0x34, 0x13, 0x86, 0xd4, 0x34, 0x1c, 0xaf, 0x18, 0x58, 0xb6, 0x2f, + 0xdc, 0x97, 0xb0, 0x35, 0x7e, 0x8b, 0xa6, 0x89, 0x4c, 0x6e, 0xf5, 0xf1, 0xe7, 0xf1, + 0x52, 0x8e, 0xc0, 0xad, 0xf9, 0x7b, 0x56, 0x84, 0x1b, 0xd3, 0x75, 0xf7, 0x2c, 0x8c, + 0xb0, 0x99, 0xb8, 0x02, 0x4d, 0x1f, 0x70, 0x25, 0x01, 0xc0, 0xea, 0x3d, 0x93, 0xe8, + 0x9f, 0xeb, 0xea, 0x85, 0x4f, 0x93, 0x43, 0xf7, 0x64, 0x25, 0x06, 0x7d, 0x94, 0x01, + 0x89, 0x2f, 0xee, 0x7d, 0xe2, 0x7e, 0x19, 0xf1, 0x13, 0x50, 0x57, 0xf6, 0x14, 0x27, + 0x83, 0x96, 0x05, 0xa5, 0xe6, 0x96, 0xc5, 0x66, 0xcd, 0xb8, 0xc4, 0x4c, 0xb7, 0x8e, + 0x28, 0xb5, 0xa1, 0x57, 0x4d, 0xf7, 0x5a, 0x92, 0x01, 0xa8, 0xd2, 0x9e, 0xb3, 0x2a, + 0x3d, 0x35, 0xd5, 0x68, 0x62, 0x0a, 0x47, 0x62, 0xe8, 0x4e, 0xa7, 0x72, 0x84, 0x64, + 0x0d, 0xf6, 0x2e, 0x9a, 0xaf, 0xbf, 0x2d, 0x6a, 0xe5, 0x0f, 0x9d, 0x3f, 0xcd, 0x13, + 0x68, 0xda, 0xdf, 0xa5, 0xcc, 0x15, 0x7f, 0xc1, 0x79, 0xe9, 0x19, 0xde, 0x16, 0xac, + 0xcd, 0xaa, 0x06, 0x2c, 0x53, 0x16, 0xc8, 0xc7, 0x4d, 0x16, 0xcf, 0xb2, 0x5e, 0x0f, + 0xea, 0xa1, 0xef, 0x80, 0xd3, 0x00, 0xba, 0x59, 0x2e, 0x95, 0x41, 0xd8, 0x30, 0xb3, + 0xfa, 0xf9, 0xea, 0xef, 0xd6, 0x63, 0x75, 0x4c, 0x2a, 0xb1, 0x66, 0xb8, 0x2f, 0x45, + 0xaa, 0xac, 0xe8, 0xbb, 0x67, 0x61, 0x7b, 0xb4, 0x87, 0xa4, 0x0d, 0xa8, 0xa0, 0xe6, + 0x16, 0xbc, 0x47, 0xc0, 0x76, 0x20, 0x80, 0x2a, 0x89, 0x85, 0x7b, 0x11, 0xd2, 0x8d, + 0x64, 0x93, 0xe1, 0xe0, 0xdb, 0xeb, 0x4a, 0x21, 0xc3, 0xb6, 0xd7, 0x36, 0x49, 0xf7, + 0xf1, 0x90, 0x05, 0x00, 0x41, 0x94, 0x32, 0x07, 0x57, 0xf3, 0xd7, 0x3a, 0xbb, 0xbb, + 0x4e, 0xed, 0xd2, 0x6c, 0x80, 0xfb, 0x55, 0xf0, 0x81, 0x4c, 0xc1, 0xd6, 0x28, 0x4f, + 0xcc, 0xe9, 0xe7, 0x4b, 0x8d, 0x17, 0x34, 0x64, 0xd8, 0x7f, 0xef, 0x0e, 0xfa, 0x0c, + 0x65, 0x6d, 0xc2, 0xf5, 0xfa, 0xc0, 0xb5, 0x79, 0x9f, 0x22, 0x64, 0xb1, 0x6b, 0xf1, + 0x1d, 0xfb, 0xfe, 0x12, 0x88, 0xcd, 0x9e, 0xb1, 0x00, 0x3c, 0x69, 0x85, 0x15, 0xe0, + 0x91, 0x68, 0xbc, 0x7b, 0xfd, 0xd2, 0x75, 0x6f, 0x3b, 0xee, 0x73, 0xf7, 0x29, 0xdb, + 0x94, 0x86, 0x56, 0xd9, 0xe8, 0x21, 0x65, 0x32, 0xe9, 0x62, 0xf3, 0x3e, 0x5a, 0xde, + 0xd0, 0x48, 0xa7, 0x19, 0xf1, 0xa9, 0xfe, 0xfc, 0xb1, 0xf1, 0x4e, 0xe2, 0x65, 0x9b, + 0x72, 0x5d, 0x4b, 0xe3, 0x31, 0x0b, 0xca, 0xc0, 0x35, 0x54, 0x0a, 0x65, 0x54, 0x20, + 0xd6, 0xc4, 0xae, 0x56, 0x58, 0xcc, 0xc2, 0x33, 0x88, 0x8e, 0xd7, 0x45, 0x00, 0x67, + 0x16, 0x48, 0x95, 0xa1, 0x4d, 0x40, 0x3e, 0x3d, 0xd5, 0x0f, 0x45, 0x4a, 0xd8, 0x40, + 0xe5, 0x28, 0x07, 0xc1, 0xf6, 0xd6, 0x9a, 0x4a, 0x77, 0xe1, 0x7a, 0x65, 0xd0, 0x57, + 0x1c, 0x42, 0x94, 0x9e, 0xd9, 0xcd, 0x44, 0x86, 0xa6, 0xf2, 0x99, 0x06, 0xd7, 0xa5, + 0x26, 0x03, 0x75, 0x81, 0x43, 0xd4, 0xca, 0xca, 0xc6, 0x0f, 0x26, 0xb9, 0x8f, 0xf7, + 0x90, 0x8c, 0x64, 0x35, 0x15, 0xb0, 0x55, 0x18, 0x91, 0x01, 0x62, 0xf7, 0x34, 0x2f, + 0xb9, 0xa1, 0x5c, 0x20, 0xb8, 0x44, 0xaf, 0x0d, 0x06, 0xf6, 0x1f, 0xbf, 0x2a, 0x5d, + 0x40, 0x26, 0xbc, 0x7d, 0x4d, 0x5e, 0xc9, 0x89, 0x5f, 0x1a, 0x78, 0x35, 0xac, 0x2f, + 0x81, 0x8f, 0x39, 0x9e, 0x95, 0xbf, 0x67, 0x44, 0xa6, 0xc2, 0xdf, 0x11, 0x7d, 0x18, + 0xb3, 0xb0, 0x78, 0x46, 0xb6, 0x02, 0x72, 0x9a, 0x26, 0x3c, 0x2d, 0xfe, 0xa4, 0x10, + 0x78, 0x6f, 0xd2, 0x4e, 0x86, 0xc4, 0xb7, 0x6c, 0x03, 0x85, 0xdb, 0x39, 0x01, 0x63, + 0x07, 0x5d, 0x5b, 0x6d, 0xb7, 0x5a, 0xe6, 0x7b, 0x3c, 0x70, 0x39, 0xb0, 0x3c, 0xc1, + 0xd7, 0xe6, 0x08, 0xbf, 0x75, 0xbf, 0x43, 0x7d, 0xea, 0xf2, 0x26, 0x9f, 0x9d, 0xc1, + 0x08, 0xcc, 0xb9, 0x1d, 0xab, 0x97, 0x39, 0x1a, 0x19, 0x5b, 0xcf, 0x20, 0x30, 0x83, + 0x03, 0xf7, 0xd7, 0xf7, 0x03, 0x93, 0x21, 0xed, 0x0f, 0x6f, 0xd6, 0x1a, 0x64, 0x9f, + 0xf7, 0xda, 0x50, 0x8e, 0x46, 0x03, 0x0b, 0xba, 0x87, 0x49, 0xb6, 0xdd, 0xa7, 0xf7, + 0x30, 0x0c, 0x45, 0x8b, 0x0b, 0x93, 0xf0, 0x79, 0x15, 0x6a, 0x1d, 0x3a, 0x6d, 0xc5, + 0xf4, 0x2a, 0x7f, 0xbc, 0xdf, 0xc9, 0x30, 0x54, 0x85, 0x9c, 0x30, 0x51, 0x83, 0xe8, + 0x8b, 0xa3, 0xe5, 0x90, 0x68, 0x4c, 0xc3, 0x9e, 0xbd, 0x90, 0x28, 0xbf, 0x51, 0x36, + 0x51, 0xdc, 0x1c, 0x87, 0x1f, 0x0a, 0xcb, 0x6e, 0xec, 0x38, 0xc2, 0x57, 0x50, 0x07, + 0xa2, 0xfa, 0x14, 0xab, 0x8e, 0x0c, 0x84, 0x42, 0xd1, 0x0f, 0x82, 0x40, 0xfb, 0xe9, + 0x70, 0x4a, 0x72, 0xb8, 0x9f, 0x8b, 0x6b, 0xae, 0x62, 0xe3, 0x9f, 0xb4, 0xf0, 0x9c, + 0x22, 0x0b, 0x9b, 0x9e, 0xb9, 0xe6, 0x0c, 0xc5, 0x1b, 0xa5, 0x0b, 0xf3, 0x08, 0x1a, + 0x4e, 0x5a, 0x71, 0x22, 0xb9, 0x8e, 0xbd, 0x26, 0x15, 0x9e, 0x00, 0x12, 0x63, 0x1d, + 0x43, 0x4f, 0x04, 0x39, 0xc8, 0x27, 0x56, 0x48, 0x95, 0xb4, 0xaa, 0x62, 0x01, 0x74, + 0x25, 0xf5, 0xf6, 0xc5, 0xe3, 0xb3, 0x22, 0x3a, 0x98, 0xcd, 0xd2, 0x5b, 0xa6, 0x0d, + 0xa2, 0x9f, 0xc8, 0xfa, 0x49, 0xf7, 0x58, 0x4f, 0x0d, 0x0e, 0xd4, 0x71, 0x2e, 0x56, + 0x0b, 0xbf, 0x46, 0xd8, 0xc2, 0x0d, 0xe0, 0xaf, 0xcb, 0x53, 0x8b, 0xba, 0x3a, 0xc1, + 0x86, 0xf5, 0x7c, 0xa6, 0x7c, 0xc1, 0x42, 0x02, 0xd4, 0x46, 0x1b, 0x67, 0xc6, 0x92, + 0xee, 0x22, 0xbe, 0xba, 0x92, 0x19, 0xa4, 0x44, 0x9a, 0xbc, 0x21, 0x0d, 0x93, 0x05, + 0x57, 0x9b, 0x31, 0xdf, 0x32, 0x86, 0x9a, 0x97, 0x9e, 0xc7, 0xdb, 0x13, 0x58, 0x0a, + 0xfb, 0xf1, 0xf8, 0x07, 0x83, 0x3a, 0x4b, 0xc4, 0x08, 0xa5, 0xad, 0x27, 0x86, 0xc5, + 0x70, 0xaa, 0x20, 0xc7, 0x93, 0xe7, 0x35, 0xc5, 0x7b, 0xd0, 0x4c, 0xf3, 0xa5, 0x3f, + 0xc0, 0x5f, 0x84, 0x39, 0xd8, 0x02, 0x13, 0xce, 0xb0, 0xc1, 0xe1, 0x91, 0x7a, 0x59, + 0xfe, 0x57, 0xcd, 0x32, 0xf3, 0x08, 0xa1, 0x92, 0xeb, 0x6c, 0x69, 0xc2, 0x00, 0x6e, + 0x59, 0x72, 0xd2, 0x31, 0x3b, 0x53, 0x03, 0xe6, 0xf5, 0x66, 0x44, 0x4d, 0x75, 0xb7, + 0x59, 0x45, 0x82, 0x7d, 0x38, 0xab, 0x0f, 0x4a, 0xe8, 0x2a, 0x73, 0x8f, 0x9d, 0x92, + 0x81, 0x4d, 0x73, 0x69, 0x99, 0x6f, 0xa7, 0x95, 0xbe, 0xdf, 0x0d, 0x35, 0x03, 0x93, + 0xc7, 0x6d, 0xf9, 0xdf, 0xf1, 0x0b, 0x48, 0x4d, 0x75, 0xd3, 0x31, 0xd3, 0xca, 0x96, + 0xde, 0xb4, 0x93, 0x3b, 0x8f, 0x9a, 0x63, 0x14, 0xa5, 0x5d, 0x14, 0x08, 0x2a, 0xc6, + 0x17, 0xf0, 0xc6, 0xf0, 0xb0, 0x26, 0x7f, 0x9e, 0x01, 0x6e, 0x98, 0x61, 0x6c, 0x73, + 0xa1, 0x00, 0x51, 0x05, 0x82, 0xdc, 0x55, 0x1a, 0x7f, 0x4f, 0x56, 0xaa, 0xa5, 0xc4, + 0xf3, 0xec, 0x2e, 0xfc, 0xe7, 0x2d, 0x34, 0xc3, 0x8b, 0xc8, 0xbe, 0xdd, 0x5f, 0x9d, + 0x5b, 0x25, 0x6a, 0x6b, 0xae, 0x22, 0x9e, 0xf5, 0x8e, 0x6f, 0xc9, 0xef, 0x1b, 0xcf, + 0xfb, 0xfe, 0x24, 0x0c, 0x1e, 0x3e, 0x2d, 0xb7, 0x2d, 0xdf, 0xd5, 0xd9, 0x5b, 0x32, + 0xad, 0xe8, 0x54, 0x25, 0x2b, 0x6a, 0xd0, 0x77, 0x76, 0x70, 0x92, 0xe3, 0xb9, 0xc6, + 0x6d, 0x85, 0x9b, 0xe2, 0x53, 0x93, 0x28, 0x99, 0xba, 0xf4, 0xdb, 0xa3, 0x95, 0xda, + 0x49, 0xb3, 0xce, 0xf7, 0x7c, 0xf7, 0xba, 0xb9, 0x7f, 0x97, 0x08, 0x13, 0xb1, 0xd2, + 0xc8, 0xe8, 0x6d, 0x26, 0x62, 0x71, 0x95, 0x7e, 0x61, 0x2f, 0xfd, 0xb6, 0x65, 0x2f, + 0x82, 0x60, 0x68, 0x42, 0x32, 0x8b, 0x1c, 0x9a, 0x2d, 0x87, 0xc7, 0x09, 0xfc, 0x15, + 0xd4, 0x5f, 0x3f, 0x9e, 0x63, 0x55, 0xd1, 0xe0, 0xe1, 0x1f, 0x56, 0xd3, 0xa4, 0xea, + 0x49, 0x04, 0x11, 0x88, 0x98, 0x5b, 0xa3, 0xb7, 0x3c, 0x45, 0xc6, 0x48, 0x87, 0xa9, + 0x43, 0x02, 0x09, 0xce, 0x5f, 0xfe, 0x42, 0x3d, 0xfd, 0x19, 0xae, 0x2d, 0x51, 0x68, + 0x7b, 0xea, 0xbc, 0xbe, 0x5a, 0x07, 0x8b, 0xda, 0x6f, 0xe6, 0x7a, 0x4c, 0xfb, 0x2b, + 0xae, 0x82, 0x44, 0x4c, 0x22, 0xb7, 0xf2, 0x92, 0xf8, 0x2f, 0x95, 0x7f, 0x4a, 0x6c, + 0x27, 0xa1, 0x23, 0x22, 0xbb, 0xd4, 0xde, 0x49, 0x8f, 0x99, 0x02, 0xe1, 0xba, 0x01, + 0x2e, 0x61, 0x1e, 0xfc, 0x5b, 0xf3, 0xb4, 0xac, 0x48, 0x7d, 0xc6, 0xf6, 0xca, 0xec, + 0xe4, 0x2a, 0x43, 0x3b, 0x6d, 0xa6, 0x2f, 0x09, 0x4b, 0xc9, 0x87, 0xd7, 0x78, 0xb2, + 0x0b, 0xc1, 0x33, 0x1b, 0xa0, 0x84, 0xe5, 0xf7, 0x7c, 0xc7, 0x04, 0x1f, 0xaf, 0x4d, + 0x89, 0xfd, 0x23, 0x15, 0xb6, 0x58, 0xed, 0x40, 0x5e, 0xa8, 0x24, 0x86, 0xfa, 0x75, + 0x70, 0x03, 0xb2, 0x6d, 0xec, 0x64, 0xed, 0x9b, 0x25, 0x2e, 0x3d, 0xe4, 0xdf, 0xff, + 0x74, 0xef, 0x59, 0x1f, 0xb1, 0x2e, 0x99, 0xb5, 0x24, 0x87, 0x4a, 0x3c, 0xb5, 0xa3, + 0x1c, 0x1a, 0xd4, 0xac, 0xa1, 0xeb, 0x81, 0xd1, 0x97, 0x64, 0x62, 0x7a, 0x7d, 0x54, + 0xca, 0xe1, 0x99, 0xf3, 0x49, 0xd4, 0xeb, 0x68, 0xe9, 0xe2, 0x6a, 0xdb, 0xb8, 0xae, + 0xbd, 0x5c, 0xfb, 0x62, 0x7f, 0x4f, 0x5c, 0x34, 0xd6, 0xeb, 0xc8, 0x0f, 0xda, 0xd0, + 0x92, 0x3e, 0xac, 0xa5, 0x3b, 0xff, 0xf2, 0x8a, 0x54, 0xb6, 0xd7, 0x12, 0xbb, 0xcb, + 0xff, 0xaa, 0x59, 0x96, 0x30, 0x5c, 0x44, 0xd3, 0xe3, 0xdf, 0x5d, 0x3c, 0x2d, 0x2d, + 0xa8, 0xe9, 0x3d, 0x76, 0xeb, 0x1b, 0x9d, 0x66, 0x0c, 0x65, 0x93, 0x21, 0xb1, 0xfb, + 0x4a, 0x6f, 0xaa, 0xca, 0x3e, 0xd6, 0x6b, 0x90, 0x23, 0xa9, 0x26, 0xb0, 0x5a, 0x40, + 0xcc, 0xc0, 0xd3, 0x60, 0xfd, 0xbe, 0xfb, 0xc9, 0x06, 0x0d, 0x84, 0xe2, 0x1b, 0x6e, + 0xbc, 0xbf, 0xfc, 0x7d, 0x45, 0x9a, 0xb9, 0x18, 0x05, 0x21, 0x4d, 0x5c, 0xed, 0x6e, + 0x79, 0xb5, 0xf0, 0x25, 0x9a, 0x95, 0x31, 0x41, 0x2c, 0xa4, 0xf5, 0x76, 0x44, 0x2b, + 0x1b, 0xbd, 0x8c, 0x42, 0x6b, 0xd0, 0x11, 0x6a, 0xc7, 0x34, 0x9d, 0xc8, 0xb0, 0xaf, + 0x46, 0x66, 0x1b, 0x32, 0xa4, 0x34, 0xce, 0x07, 0x42, 0x6e, 0x1b, 0x05, 0x22, 0xff, + 0x91, 0x28, 0x14, 0xa5, 0x8e, 0x91, 0xc6, 0xef, 0x52, 0x14, 0x2b, 0xda, 0xb3, 0x0b, + 0xd8, 0x86, 0x3c, 0x17, 0xd9, 0x10, 0x67, 0xe1, 0xef, 0xf2, 0x51, 0xc7, 0xd5, 0xd1, + 0xf9, 0x2a, 0x25, 0x1b, 0x49, 0x8a, 0x62, 0xbd, 0xcb, 0x2c, 0x7f, 0x60, 0xd7, 0xc9, + 0xf3, 0xd9, 0x3a, 0xa1, 0xbd, 0x4e, 0xc7, 0xa2, 0x4e, 0x08, 0xe6, 0xac, 0x62, 0xf0, + 0xad, 0xec, 0x2b, 0xbe, 0x9a, 0x39, 0x3b, 0x31, 0x1b, 0x54, 0xb9, 0x27, 0xfb, 0x67, + 0x82, 0x93, 0xc8, 0x13, 0xc4, 0x9a, 0xbf, 0x23, 0xcb, 0x7f, 0x3e, 0x64, 0xd4, 0x24, + 0xfc, 0x92, 0x0c, 0x93, 0x40, 0x3c, 0x3e, 0x48, 0xa3, 0xce, 0x61, 0xe2, 0x6d, 0x27, + 0x32, 0xab, 0x5b, 0xf7, 0x32, 0x54, 0xda, 0x8c, 0xce, 0x6d, 0xf8, 0xb5, 0x92, 0x21, + 0x28, 0xb4, 0x75, 0x77, 0x40, 0x6e, 0xbe, 0xb3, 0x99, 0x8a, 0x9f, 0x23, 0x96, 0xb6, + 0x8b, 0x1d, 0x04, 0xbd, 0xeb, 0xc6, 0x53, 0x3b, 0x4e, 0x7d, 0x88, 0xd2, 0xdb, 0x51, + 0xf5, 0x6d, 0x92, 0xb9, 0xf7, 0x5c, 0x8d, 0x99, 0x06, 0xe0, 0x5f, 0x40, 0xbf, 0xee, + 0x38, 0x4e, 0x2c, 0xa4, 0x4b, 0xed, 0x8d, 0xf4, 0xe7, 0x16, 0x99, 0xb9, 0x6a, 0x54, + 0xf2, 0x2d, 0x52, 0x36, 0xf7, 0x6a, 0xa3, 0xff, 0x86, 0xd4, 0x9a, 0x54, 0x98, 0xb7, + 0x9b, 0x40, 0x5c, 0x23, 0x8a, 0x6a, 0xe3, 0xcd, 0x68, 0x77, 0x5f, 0xbf, 0xed, 0xd7, + 0xf2, 0xa1, 0xa7, 0x33, 0x38, 0x70, 0x6b, 0xdb, 0x65, 0xa8, 0x8c, 0x8a, 0xe7, 0x5d, + 0x94, 0x89, 0xac, 0xdb, 0x9c, 0x38, 0xba, 0x01, 0x4e, 0x36, 0x55, 0x5e, 0x58, 0x93, + 0x19, 0x73, 0x6e, 0x44, 0x58, 0xe3, 0x2e, 0xdd, 0x87, 0x6f, 0xb9, 0x6a, 0x39, 0xf5, + 0x18, 0x4d, 0x7d, 0x2e, 0x56, 0x9d, 0xda, 0x6a, 0xf1, 0x78, 0x41, 0xef, 0xa5, 0x0c, + 0x7c, 0x37, 0x80, 0x1f, 0x2e, 0x63, 0x92, 0xc2, 0xf9, 0x9e, 0xf1, 0x10, 0x21, 0x77, + 0xd5, 0xff, 0xf6, 0x5e, 0xd4, 0x50, 0x65, 0x93, 0xf9, 0xb9, 0xd0, 0xf5, 0x23, 0x23, + 0x4d, 0x6a, 0xa6, 0x8f, 0xa2, 0x4f, 0x91, 0x17, 0x92, 0x14, 0xdf, 0x66, 0xa9, 0xbb, + 0x67, 0x68, 0xfe, 0xa1, 0xb6, 0x46, 0xed, 0x68, 0x43, 0x8a, 0x2b, 0x81, 0xe0, 0x92, + 0x90, 0x89, 0xeb, 0x6b, 0x42, 0x07, 0xfd, 0x83, 0xc5, 0x53, 0xd6, 0xa3, 0x0f, 0xd0, + 0xaf, 0xd4, 0xa4, 0xf6, 0xf6, 0xc0, 0xb4, 0x2a, 0x8a, 0x54, 0x12, 0x48, 0xaf, 0xb7, + 0x66, 0xc5, 0x16, 0x52, 0xe0, 0x51, 0xcf, 0x81, 0x05, 0xeb, 0xc9, 0x15, 0x89, 0xee, + 0x98, 0x94, 0x82, 0xc9, 0x99, 0x28, 0x59, 0x63, 0xfb, 0x27, 0x85, 0x2e, 0x1d, 0x02, + 0x97, 0x61, 0xb5, 0x3e, 0x56, 0xcb, 0xbf, 0xe3, 0xe7, 0xcf, 0x1e, 0x3d, 0xdc, 0x17, + 0x8c, 0xd4, 0xa4, 0x7d, 0xf8, 0xec, 0x2a, 0x84, 0xe3, 0x4c, 0x61, 0xc2, 0xf5, 0x52, + 0x16, 0x70, 0x86, 0xf0, 0xab, 0x80, 0x4d, 0xc5, 0x6a, 0x1d, 0x28, 0x9d, 0x4c, 0x11, + 0x16, 0x70, 0x19, 0x64, 0x2a, 0x9d, 0x89, 0xee, 0x95, 0x2c, 0x6c, 0x66, 0x55, 0xde, + 0x59, 0x91, 0x06, 0x79, 0x7c, 0x79, 0x9e, 0x30, 0xd7, 0xfe, 0x9a, 0x34, 0x13, 0xad, + 0x53, 0x74, 0x66, 0x21, 0xd9, 0xef, 0xe9, 0xc3, 0x61, 0x0b, 0xac, 0x6a, 0xd3, 0x42, + 0xc6, 0x51, 0x3e, 0x12, 0xb2, 0x23, 0xeb, 0xf8, 0x20, 0x33, 0xf2, 0x92, 0x4c, 0x9a, + 0x5a, 0x4f, 0xea, 0xfe, 0x14, 0x56, 0x90, 0xc9, 0x51, 0x49, 0x6d, 0xe8, 0xf6, 0x58, + 0xde, 0xb2, 0x6b, 0x0f, 0x80, 0xea, 0x4d, 0x4f, 0xf1, 0x97, 0xc9, 0xca, 0x38, 0xfd, + 0xc8, 0x3d, 0x31, 0x4c, 0x78, 0xc5, 0x97, 0x9e, 0xe3, 0xee, 0x85, 0xd5, 0xfd, 0x97, + 0xf7, 0x3d, 0x3a, 0xc3, 0x42, 0xe2, 0xc3, 0x80, 0xd3, 0xa6, 0xf8, 0x2a, 0x22, 0x7b, + 0x11, 0x9f, 0xa9, 0xd0, 0xec, 0xf9, 0xf6, 0xbf, 0x66, 0xbc, 0x52, 0xb9, 0x4c, 0xfc, + 0x8b, 0xf4, 0xc5, 0x21, 0xd6, 0xc7, 0x45, 0x34, 0x53, 0x61, 0xb7, 0x01, 0xb7, 0xd9, + 0x75, 0xe6, 0x51, 0xf0, 0x5c, 0xaf, 0xa0, 0xe2, 0xe3, 0xb1, 0x57, 0xb3, 0xd2, 0xe3, + 0x41, 0x76, 0x82, 0x86, 0x3a, 0x6e, 0x92, 0xc7, 0xa3, 0x9b, 0xa7, 0x45, 0x9d, 0x9a, + 0x37, 0x0c, 0xc3, 0xb1, 0x56, 0x49, 0x20, 0x91, 0xba, 0xc4, 0xf9, 0xaf, 0x03, 0x66, + 0xd6, 0xa1, 0x86, 0x1b, 0xb3, 0x22, 0x9e, 0x01, 0xc4, 0xfe, 0x8c, 0xdd, 0xa2, 0xdc, + 0x5e, 0x1b, 0xcd, 0xe9, 0xe6, 0x41, 0x98, 0x4b, 0x2d, 0xea, 0x8f, 0xcb, 0xa5, 0xb7, + 0xc3, 0x42, 0x0f, 0x03, 0xcc, 0x23, 0x9d, 0x75, 0x9e, 0x4f, 0x02, 0xab, 0x7a, 0xc3, + 0xcf, 0x5f, 0x06, 0x86, 0x9c, 0x2b, 0x52, 0x69, 0x92, 0x56, 0xb5, 0x64, 0x7c, 0x87, + 0x28, 0xc3, 0x61, 0xc4, 0x63, 0x54, 0xb9, 0x8f, 0x03, 0x1e, 0xa4, 0xe2, 0x2e, 0xef, + 0x68, 0x48, 0x40, 0x0e, 0xd2, 0x37, 0xf3, 0x0e, 0x70, 0x92, 0xbf, 0xdc, 0xc4, 0xee, + 0x36, 0x63, 0x81, 0x44, 0x79, 0xb2, 0xc3, 0x13, 0x4f, 0x99, 0x82, 0x21, 0x23, 0x73, + 0x21, 0x6a, 0x35, 0x63, 0xab, 0x47, 0x7f, 0xa7, 0x31, 0xca, 0x34, 0x1c, 0x01, 0x23, + 0xfa, 0x9a, 0x8e, 0x1d, 0x15, 0x24, 0x71, 0xe1, 0x8d, 0x35, 0x97, 0x37, 0x47, 0x94, + 0xe7, 0x50, 0xc9, 0x8a, 0x25, 0x2b, 0xf7, 0xe9, 0x20, 0x0e, 0xf1, 0x14, 0xc8, 0xee, + 0x5b, 0x2b, 0xca, 0xf9, 0xbe, 0xc0, 0xc4, 0xee, 0x72, 0xa4, 0x1a, 0x5a, 0xb7, 0xc9, + 0x1d, 0x6b, 0x01, 0xd4, 0x35, 0x63, 0x74, 0xeb, 0x63, 0x41, 0x73, 0x8c, 0x63, 0xfd, + 0x29, 0xe0, 0xe7, 0xf3, 0xe9, 0x5c, 0x9d, 0x47, 0xa7, 0x77, 0x25, 0xf4, 0xe2, 0x0a, + 0xd4, 0x6e, 0x44, 0x56, 0xd4, 0x6f, 0x6a, 0xd4, 0x9e, 0x76, 0x82, 0x23, 0x58, 0xb4, + 0x7b, 0x74, 0x25, 0x78, 0xd0, 0xcc, 0x8f, 0xf0, 0x31, 0xce, 0xdf, 0x5e, 0x87, 0xfe, + 0x4a, 0x71, 0x18, 0xa0, 0x81, 0xa9, 0x4e, 0x99, 0x79, 0x97, 0x1f, 0xca, 0x23, 0x0c, + 0xce, 0x04, 0xc7, 0x7b, 0x5f, 0x44, 0x74, 0xad, 0x47, 0x58, 0xb9, 0x75, 0x3b, 0xf6, + 0x36, 0x18, 0x6a, 0xb2, 0xb9, 0x73, 0x5f, 0xc8, 0xd8, 0x2e, 0x6c, 0x27, 0xf5, 0xfb, + 0x3a, 0x4b, 0xb4, 0x25, 0xb9, 0x59, 0x95, 0x34, 0x0b, 0xe0, 0x0f, 0x12, 0xf9, 0x61, + 0xbc, 0x3a, 0x78, 0x8a, 0x42, 0x8d, 0x95, 0x22, 0x56, 0x2a, 0xfd, 0x8d, 0xee, 0x11, + 0x58, 0x2c, 0xfe, 0x43, 0x32, 0x7d, 0xe9, 0xfd, 0x86, 0x4c, 0x49, 0x27, 0xea, 0x8e, + 0x8e, 0xab, 0x3f, 0xf9, 0xfd, 0x05, 0x0b, 0x35, 0x24, 0x89, 0xda, 0x69, 0xc7, 0x97, + 0x7b, 0x35, 0x25, 0x4c, 0x42, 0x5a, 0xcb, 0x1a, 0xca, 0x37, 0x77, 0x7f, 0x27, 0xf4, + 0xa2, 0x23, 0x7d, 0xf4, 0x56, 0x8d, 0x63, 0x98, 0xd3, 0x2b, 0xbf, 0x28, 0x58, 0xe2, + 0xdc, 0x22, 0x37, 0x83, 0xda, 0x27, 0xbe, 0x6b, 0xb9, 0xe2, 0xa9, 0x36, 0xa0, 0x49, + 0x17, 0x42, 0x9f, 0xaa, 0xa6, 0xf2, 0x92, 0xe9, 0xc6, 0x4e, 0x2e, 0x27, 0x7b, 0x55, + 0xa4, 0x70, 0x75, 0x72, 0x65, 0x7f, 0xb8, 0x41, 0x99, 0x03, 0x98, 0x54, 0x66, 0x08, + 0xf3, 0x1f, 0xd2, 0xa7, 0x75, 0x1e, 0xeb, 0x26, 0x35, 0x0a, 0x3a, 0x9f, 0x0b, 0xd0, + 0xf5, 0xe3, 0xd5, 0x05, 0x5c, 0x3d, 0x94, 0x73, 0x7c, 0x56, 0x22, 0x00, 0x6c, 0x65, + 0x16, 0xf5, 0x32, 0x82, 0x8b, 0x9e, 0x1a, 0x5b, 0xb9, 0xfb, 0x36, 0x9b, 0x94, 0x70, + 0x95, 0x36, 0x32, 0x1a, 0x11, 0x93, 0x1f, 0xec, 0x0f, 0xf9, 0xe7, 0x4e, 0x42, 0x7e, + 0xc7, 0x22, 0x0c, 0x23, 0x17, 0x4c, 0x5d, 0x12, 0xc5, 0xd1, 0x2a, 0x0b, 0xba, 0x0a, + 0x1b, 0x6d, 0xc2, 0xb3, 0x5c, 0x2d, 0xaf, 0xc2, 0x13, 0x65, 0x82, 0xc3, 0x49, 0xfc, + 0xb8, 0x3d, 0x20, 0xcc, 0x0e, 0x4d, 0xf9, 0xd6, 0x24, 0xcf, 0x33, 0x03, 0xfd, 0x7c, + 0x35, 0x61, 0xb1, 0x96, 0x21, 0xf8, 0xf6, 0x1a, 0xb8, 0x43, 0x9f, 0xba, 0x49, 0xb6, + 0xa2, 0x10, 0xba, 0xa3, 0x3d, 0x8e, 0x41, 0x9d, 0x0e, 0x03, 0x2e, 0x67, 0x3b, 0x3d, + 0x10, 0x61, 0xdf, 0xc4, 0x28, 0x48, 0x7d, 0x1b, 0xa5, 0x66, 0xd5, 0x34, 0x9c, 0x38, + 0x0c, 0xbc, 0x73, 0x00, 0xc2, 0xcb, 0x54, 0x58, 0x53, 0xb2, 0xb2, 0x6b, 0xce, 0x4e, + 0xd6, 0x51, 0xfb, 0xa0, 0xe2, 0xbe, 0xbc, 0xf4, 0x61, 0xa2, 0x6a, 0xe5, 0xe6, 0xd2, + 0x4d, 0xce, 0xdd, 0x0f, 0x33, 0x91, 0xc4, 0x67, 0x77, 0x88, 0xfc, 0xdf, 0x98, 0x4a, + 0xe3, 0x07, 0xdc, 0x50, 0xb7, 0x48, 0xc3, 0x7e, 0xce, 0x98, 0x3b, 0x4d, 0x27, 0xd8, + 0xd6, 0xaa, 0x97, 0x8f, 0x84, 0x82, 0xb8, 0x9c, 0x09, 0x74, 0x13, 0xc5, 0xae, 0xe3, + 0x16, 0x3d, 0xd7, 0x61, 0x1f, 0xef, 0xff, 0xa2, 0x4c, 0x1f, 0x54, 0xfa, 0xfd, 0xce, + 0x2d, 0xc0, 0x90, 0x51, 0x9b, 0x52, 0x69, 0x9e, 0x32, 0x6f, 0x29, 0xfe, 0xd1, 0x64, + 0x35, 0x7c, 0x72, 0xa3, 0xb1, 0xb0, 0x4b, 0x78, 0xd0, 0xb6, 0x7a, 0x3b, 0xd5, 0x2e, + 0x31, 0x2c, 0x26, 0xc8, 0x85, 0xb6, 0xbc, 0x6f, 0xd6, 0x1b, 0xbf, 0x82, 0xfa, 0xa9, + 0x55, 0xd2, 0xc9, 0xe7, 0xcf, 0x2c, 0x93, 0x72, 0xe8, 0x21, 0xaa, 0x26, 0x43, 0xc8, + 0x23, 0xdc, 0x9f, 0xa0, 0xdd, 0x48, 0x53, 0xbb, 0x55, 0xdc, 0x3b, 0x33, 0x20, 0x86, + 0x91, 0x27, 0xde, 0x15, 0xfd, 0x75, 0x94, 0x0e, 0x0b, 0xda, 0xd8, 0x19, 0x0f, 0x3d, + 0x18, 0x82, 0x03, 0x5c, 0x34, 0x00, 0xcb, 0xe3, 0x0c, 0xf4, 0x43, 0x3a, 0x9d, 0x84, + 0x9c, 0x57, 0x16, 0x11, 0x8a, 0x2c, 0xc4, 0x21, 0x6a, 0x0b, 0xb5, 0x34, 0x3b, 0x41, + 0xe7, 0x83, 0x87, 0xc7, 0x8c, 0x8d, 0x62, 0x28, 0x71, 0x00, 0xb4, 0x26, 0x78, 0x6b, + 0x91, 0xc5, 0xa6, 0x3f, 0xbe, 0x50, 0x55, 0x5f, 0xa1, 0xfc, 0x9e, 0xeb, 0xfb, 0x82, + 0x52, 0x91, 0xbf, 0x93, 0x79, 0x18, 0x1d, 0xf0, 0x21, 0x05, 0x98, 0x14, 0x77, 0xf5, + 0xe0, 0x8f, 0x77, 0xc8, 0x3c, 0x02, 0x2a, 0x9b, 0x2d, 0x3a, 0xfa, 0xbe, 0xd5, 0xc9, + 0x14, 0xd3, 0x12, 0x4d, 0xd3, 0xed, 0xd8, 0x11, 0xec, 0xbc, 0x93, 0x33, 0xe8, 0xec, + 0x6c, 0x41, 0xf4, 0xca, 0x1a, 0x36, 0x3c, 0x8d, 0xb9, 0x61, 0x4a, 0x76, 0x64, 0x0b, + 0xe4, 0xbd, 0x77, 0x4d, 0x8d, 0x49, 0xc5, 0xb5, 0xfc, 0x01, 0xc8, 0x57, 0x9c, 0x38, + 0xd5, 0x57, 0x86, 0xb6, 0xdf, 0x88, 0xd6, 0xfa, 0xa3, 0xa3, 0x90, 0xca, 0x07, 0xe9, + 0xf1, 0x93, 0x7b, 0xac, 0x88, 0x69, 0xd1, 0x2d, 0xa0, 0x62, 0x88, 0xd3, 0x50, 0x48, + 0x79, 0xce, 0x8a, 0x2b, 0xda, 0xf4, 0x9b, 0x91, 0xa6, 0x85, 0xcd, 0xc4, 0xa4, 0x01, + 0x98, 0x9f, 0x5e, 0x62, 0x0f, 0xed, 0xdf, 0x91, 0x18, 0xa3, 0x98, 0x90, 0x57, 0x97, + 0xb4, 0x72, 0x3c, 0xd4, 0xc5, 0x7b, 0x47, 0x84, 0x7c, 0xca, 0x74, 0x90, 0x5a, 0x94, + 0x61, 0x0d, 0x9a, 0x5b, 0xff, 0x3c, 0x40, 0xe8, 0x67, 0x4d, 0x2b, 0x5a, 0xdd, 0xde, + 0xeb, 0x11, 0xb2, 0xc5, 0xf7, 0xd0, 0x38, 0xeb, 0x8a, 0x68, 0x53, 0x3e, 0x02, 0x19, + 0x28, 0xbb, 0x87, 0x03, 0x91, 0x41, 0x70, 0x8e, 0x3d, 0x08, 0x36, 0xf4, 0x21, 0xed, + 0xb7, 0x3b, 0x9d, 0xd7, 0x82, 0x16, 0xd4, 0x8d, 0x41, 0xc2, 0x58, 0x6b, 0xdf, 0x0c, + 0x09, 0x9e, 0x4d, 0x9f, 0x09, 0x24, 0xd1, 0xe0, 0xac, 0x48, 0x39, 0x9a, 0x57, 0xb4, + 0xbf, 0x53, 0x4b, 0x1e, 0x91, 0x6e, 0xaf, 0x48, 0x61, 0x54, 0x34, 0x80, 0x6d, 0xa3, + 0xb4, 0x72, 0x7e, 0x0a, 0x6a, 0x3d, 0x89, 0xee, 0x67, 0x84, 0xab, 0xd1, 0xda, 0xd9, + 0xc6, 0x6b, 0x53, 0x8a, 0x93, 0xf0, 0x97, 0x8e, 0x71, 0x13, 0xdf, 0xed, 0x03, 0x06, + 0xf5, 0xf7, 0xe5, 0x33, 0xfe, 0xf3, 0x14, 0x2d, 0x49, 0x36, 0x06, 0xcc, 0x5b, 0xaa, + 0x3a, 0xeb, 0x76, 0x7b, 0x23, 0xf1, 0xbc, 0x5a, 0x2a, 0x2b, 0xa9, 0x2d, 0x43, 0x14, + 0xd9, 0x73, 0x54, 0xd7, 0xcf, 0x58, 0xdb, 0x73, 0xdb, 0xba, 0xc3, 0x35, 0xe7, 0x82, + 0x5e, 0x18, 0x08, 0x36, 0xe6, 0x90, 0x33, 0xbe, 0xd5, 0x2f, 0xfa, 0xf2, 0x04, 0xb2, + 0x7e, 0x28, 0x0d, 0x1c, 0x78, 0x0d, 0xc8, 0xb7, 0x25, 0x8a, 0xb5, 0x30, 0x28, 0x82, + 0xe3, 0xcb, 0x1f, 0xab, 0xce, 0x48, 0xa4, 0x6f, 0x00, 0xac, 0xa4, 0x17, 0x83, 0x18, + 0x82, 0xd7, 0x4c, 0xf6, 0x9b, 0xda, 0x56, 0xe8, 0xb4, 0x84, 0x11, 0xf0, 0x7b, 0x9b, + 0xfc, 0xba, 0xc7, 0x2e, 0xd5, 0x8b, 0x44, 0xfc, 0x11, 0x6f, 0xcd, 0xd7, 0xde, 0xfa, + 0x96, 0x71, 0x13, 0x9a, 0xb3, 0xee, 0x68, 0xff, 0xeb, 0xc4, 0x22, 0x64, 0x41, 0x7a, + 0x34, 0xb7, 0x7e, 0x77, 0x1e, 0xdc, 0x4e, 0xc6, 0x5e, 0x0f, 0x42, 0xb1, 0x53, 0xf1, + 0xd6, 0x13, 0x41, 0x71, 0x36, 0x1e, 0x30, 0x24, 0x1d, 0x50, 0xf6, 0xfb, 0x83, 0x80, + 0x8d, 0xc4, 0xdf, 0xa9, 0x9c, 0x8f, 0x2e, 0xbe, 0x82, 0xfa, 0x8c, 0xd8, 0xbe, 0x1b, + 0x59, 0xc9, 0x34, 0xd8, 0xce, 0xdd, 0x96, 0x74, 0x76, 0xc6, 0x3b, 0x31, 0x4a, 0x63, + 0x18, 0x83, 0xe4, 0x10, 0x14, 0x79, 0x0c, 0x93, 0x7d, 0x7c, 0xe2, 0xf1, 0xc1, 0x84, + 0xfe, 0xf0, 0xaf, 0xe4, 0xc2, 0x43, 0xa6, 0x48, 0x89, 0xba, 0x82, 0x9c, 0xa5, 0x4e, + 0xc7, 0x98, 0xb1, 0xa1, 0x2e, 0xc5, 0x04, 0x6a, 0x4d, 0xa0, 0x6d, 0x70, 0xee, 0x79, + 0xeb, 0x9e, 0x5d, 0x5a, 0x59, 0x05, 0x45, 0xad, 0x01, 0x32, 0x92, 0xcb, 0xf3, 0xfc, + 0xc7, 0x2b, 0xfa, 0xfa, 0x6e, 0x55, 0xec, 0xd7, 0x8f, 0x9a, 0xb4, 0xb1, 0xe9, 0xa4, + 0xc1, 0x16, 0xae, 0xd9, 0x6b, 0xb8, 0xe9, 0xbd, 0x5a, 0x2b, 0x43, 0x06, 0x69, 0xfc, + 0x36, 0x41, 0xab, 0x76, 0xe6, 0x13, 0xdf, 0x9c, 0x0e, 0xcc, 0x1b, 0x0a, 0x19, 0xd3, + 0xb2, 0x90, 0x68, 0x88, 0x15, 0x78, 0x68, 0xe2, 0xa5, 0xd8, 0x33, 0xa7, 0xba, 0xd5, + 0xaa, 0xa5, 0x3a, 0x13, 0xa9, 0x23, 0xa9, 0x6b, 0x35, 0xc3, 0x5b, 0xc1, 0x6c, 0xe6, + 0xc3, 0x58, 0x2f, 0x47, 0xfa, 0x01, 0x0b, 0x81, 0x06, 0x26, 0x0d, 0xd2, 0xff, 0x46, + 0x69, 0x56, 0x8c, 0x59, 0x01, 0xd4, 0xd0, 0x9a, 0x08, 0xff, 0xd9, 0xed, 0x7b, 0x1a, + 0xb2, 0x90, 0x90, 0xcb, 0xd5, 0xfc, 0xb0, 0xae, 0x27, 0x2d, 0x5d, 0x7b, 0xd2, 0xb2, + 0x42, 0x4e, 0xc4, 0xcf, 0x80, 0xce, 0xac, 0x0a, 0x6d, 0x98, 0x04, 0x10, 0xec, 0xba, + 0x54, 0xee, 0xd3, 0xd0, 0x8e, 0x28, 0x16, 0xd3, 0x17, 0x30, 0x09, 0x62, 0x13, 0xdc, + 0x23, 0x8e, 0xd9, 0xec, 0x0e, 0xec, 0x4c, 0xaf, 0xe9, 0x6f, 0xdd, 0xc1, 0x58, 0xf7, + 0xf3, 0xe4, 0x72, 0x06, 0x10, 0x33, 0x93, 0x66, 0x62, 0x51, 0x9b, 0x6a, 0x20, 0x88, + 0xee, 0x70, 0xfb, 0x5a, 0x86, 0x8a, 0xb1, 0xb3, 0xdb, 0x2f, 0x3b, 0x77, 0xb4, 0x6f, + 0x77, 0x1d, 0x5d, 0x34, 0x23, 0xc2, 0xcf, 0xd8, 0x75, 0xc4, 0x11, 0xcd, 0xcd, 0x18, + 0x7c, 0x00, 0xdb, 0x50, 0x43, 0xc0, 0x3f, 0xa5, 0x44, 0x47, 0x2c, 0xca, 0x63, 0xa6, + 0x64, 0x29, 0xdc, 0x7b, 0xe3, 0x0f, 0x4e, 0xc4, 0xc9, 0xbf, 0x15, 0xda, 0x29, 0x1e, + 0xf3, 0xaf, 0xe4, 0x8e, 0x66, 0x65, 0x36, 0x82, 0x8f, 0x93, 0xe8, 0x70, 0xc5, 0x15, + 0x57, 0xab, 0xe7, 0xb5, 0xaf, 0xb7, 0x25, 0x5a, 0xcc, 0x05, 0x9b, 0x8a, 0x1f, 0x66, + 0xc3, 0x19, 0xa7, 0x6e, 0x34, 0xbb, 0xd7, 0x75, 0xbe, 0x79, 0x94, 0x9d, 0x59, 0x3f, + 0xb8, 0x97, 0x56, 0xf7, 0x08, 0xa5, 0xef, 0x3f, 0xc9, 0x15, 0x67, 0xdf, 0xff, 0x0d, + 0x8c, 0xf0, 0xe9, 0xe9, 0x36, 0x29, 0x7b, 0xea, 0x77, 0xd7, 0x4f, 0x97, 0x05, 0xd8, + 0xde, 0x3a, 0x8a, 0x1d, 0xf8, 0x21, 0x64, 0x1e, 0x34, 0x7b, 0x39, 0xeb, 0x2b, 0xf2, + 0xbd, 0x26, 0x26, 0x6c, 0xef, 0x3d, 0x63, 0xfb, 0xd7, 0xa4, 0x74, 0xcf, 0xce, 0x7d, + 0x52, 0x01, 0xb6, 0xd6, 0x94, 0x5d, 0x78, 0xed, 0x50, 0x7b, 0xb6, 0x38, 0x3a, 0x5c, + 0x4d, 0xfb, 0x1b, 0xb5, 0x0e, 0x11, 0x2c, 0x8d, 0xda, 0x19, 0x4b, 0x82, 0x48, 0x98, + 0x89, 0x11, 0xd7, 0x15, 0x96, 0xce, 0xcb, 0xf6, 0xb8, 0x8e, 0x20, 0xcf, 0xf6, 0x6e, + 0x84, 0x31, 0xb9, 0x4b, 0x08, 0x59, 0xc8, 0x45, 0x30, 0xde, 0x71, 0xa1, 0xf7, 0x3f, + 0x05, 0x68, 0x61, 0xd5, 0xd3, 0x3e, 0x7f, 0x03, 0x18, 0x23, 0xc9, 0x37, 0xb4, 0x73, + 0x20, 0x7b, 0x9b, 0x08, 0x3f, 0x62, 0x48, 0x2b, 0xbf, 0x89, 0x86, 0x18, 0x25, 0x78, + 0x3b, 0xa5, 0x20, 0x93, 0xca, 0x75, 0xe5, 0xef, 0x76, 0xe2, 0xca, 0xbf, 0xd1, 0x01, + 0x4c, 0xc4, 0x5f, 0x9f, 0x45, 0x6f, 0x08, 0x65, 0x53, 0x5d, 0xd9, 0xfb, 0x5f, 0x08, + 0x6b, 0xdb, 0x53, 0x39, 0x75, 0xd7, 0xdc, 0x72, 0xb2, 0x80, 0x5d, 0x3f, 0x41, 0x2c, + 0x65, 0x06, 0xb6, 0xb2, 0xcd, 0xe4, 0xd2, 0x5b, 0x24, 0x12, 0x45, 0x69, 0x2a, 0x70, + 0x24, 0xfb, 0xc9, 0xc3, 0x72, 0x8a, 0xc2, 0xf7, 0x18, 0x8e, 0xfe, 0x9a, 0x6d, 0xd1, + 0x4a, 0x79, 0x8a, 0xb2, 0xf2, 0x87, 0x4d, 0x77, 0xb7, 0x24, 0x53, 0xea, 0x1d, 0x6a, + 0x56, 0x9c, 0xd4, 0xdd, 0x27, 0x2e, 0x12, 0x8e, 0xdb, 0x7e, 0x40, 0x4a, 0xbe, 0xd1, + 0x87, 0x4d, 0xfa, 0xe5, 0xc1, 0x84, 0x92, 0x53, 0x4a, 0x15, 0xc2, 0xa0, 0x08, 0xb6, + 0x40, 0x74, 0x82, 0xe7, 0x71, 0x10, 0xa3, 0x3c, 0x85, 0x77, 0x5e, 0x98, 0x0a, 0xe5, + 0x06, 0xd2, 0xc6, 0x1f, 0x31, 0xa0, 0xf0, 0x86, 0x44, 0x7a, 0x3f, 0x09, 0x78, 0xf0, + 0x3e, 0x9e, 0x96, 0xae, 0x2a, 0x69, 0x34, 0xa8, 0xe9, 0xa9, 0x2e, 0x45, 0xf5, 0xc7, + 0xea, 0x4b, 0xd0, 0x95, 0xc5, 0xb1, 0x43, 0x47, 0x99, 0x63, 0x4e, 0x67, 0x3f, 0x96, + 0x7c, 0xc8, 0xec, 0x81, 0xfb, 0x3c, 0xf5, 0x97, 0x04, 0x94, 0xd3, 0x19, 0x61, 0x2d, + 0x96, 0x01, 0xba, 0x12, 0x7b, 0x42, 0xc1, 0xd7, 0xfb, 0x69, 0xca, 0x96, 0x62, 0xa0, + 0x9a, 0xbd, 0xab, 0x46, 0x85, 0xb2, 0x72, 0xdc, 0xb6, 0x32, 0xb2, 0x86, 0x37, 0xe4, + 0x67, 0x7f, 0x73, 0xaf, 0xaf, 0xe2, 0x26, 0x47, 0x5d, 0xbf, 0xb0, 0xe2, 0x37, 0x22, + 0x26, 0x1c, 0x82, 0x0b, 0x96, 0x41, 0xdb, 0xb4, 0xd2, 0x88, 0x41, 0x3c, 0xe6, 0x9b, + 0xe7, 0x89, 0x0d, 0x93, 0xaf, 0xb6, 0x6c, 0xce, 0x1f, 0xc1, 0x0a, 0xda, 0x26, 0xcf, + 0x31, 0x1f, 0xb8, 0x8b, 0x93, 0x6c, 0x2a, 0x54, 0x68, 0x26, 0x19, 0x6d, 0xbb, 0xa7, + 0x77, 0x2b, 0xec, 0xc2, 0xa9, 0xed, 0x24, 0x98, 0xcc, 0x1a, 0xef, 0xdb, 0x9a, 0xf9, + 0xd0, 0xac, 0x07, 0xff, 0x4c, 0xb4, 0xdd, 0xbc, 0x62, 0x72, 0xf7, 0xa3, 0x4e, 0xe6, + 0xd3, 0xad, 0xc1, 0x57, 0x42, 0xfe, 0xe0, 0xb0, 0x2f, 0x80, 0x54, 0x10, 0x38, 0xfc, + 0x9e, 0x2f, 0x3b, 0x24, 0xba, 0x39, 0x2b, 0x30, 0x48, 0x14, 0x00, 0x80, 0x08, 0x43, + 0xba, 0xd9, 0xec, 0xd6, 0xf1, 0xe2, 0xcc, 0xc8, 0xca, 0x04, 0x39, 0xc6, 0x97, 0x47, + 0xfc, 0x76, 0x15, 0x3c, 0x26, 0xfb, 0x3e, 0xcd, 0xc0, 0x59, 0xae, 0x3c, 0x08, 0xe6, + 0x19, 0x8c, 0x65, 0x0d, 0x05, 0x17, 0xe6, 0xc8, 0x03, 0xba, 0xd0, 0x36, 0x27, 0xd6, + 0xfc, 0x7b, 0xa4, 0x64, 0x24, 0xd7, 0x10, 0xc5, 0x8b, 0xc8, 0x93, 0xaf, 0x37, 0x96, + 0x9b, 0xd8, 0x88, 0xd5, 0x7a, 0xc5, 0xe4, 0xaa, 0xcb, 0x82, 0x4b, 0x13, 0x69, 0xb7, + 0x4d, 0xe0, 0xd4, 0xa9, 0x67, 0x12, 0x75, 0x1f, 0x8a, 0xaf, 0xdb, 0x67, 0x9d, 0x62, + 0x9b, 0x13, 0x38, 0x5c, 0x5a, 0x7a, 0x61, 0x2c, 0xce, 0xac, 0xb9, 0xab, 0x89, 0x22, + 0xba, 0x8a, 0xdb, 0xd1, 0x0a, 0xd9, 0x4a, 0x3b, 0xff, 0xb1, 0xc6, 0xda, 0x50, 0xb5, + 0xfc, 0x16, 0xb3, 0xac, 0xfd, 0x97, 0xbc, 0x2f, 0xd9, 0x79, 0x62, 0xfe, 0x66, 0xde, + 0x30, 0x0b, 0x9c, 0x82, 0x85, 0x4b, 0x9a, 0x55, 0xa4, 0x44, 0xdf, 0x35, 0xd1, 0x98, + 0xe6, 0xb3, 0xcd, 0x2f, 0x7d, 0x87, 0x41, 0x87, 0xe9, 0x40, 0x6c, 0x0c, 0xa3, 0x2e, + 0xba, 0x8f, 0xbb, 0xce, 0x4b, 0xbe, 0xfe, 0x5d, 0x5f, 0xd2, 0x7c, 0x53, 0xee, 0x38, + 0xbd, 0x77, 0x92, 0x3d, 0xda, 0x1e, 0x3d, 0x20, 0x15, 0x82, 0xc4, 0x69, 0xac, 0x36, + 0x6d, 0xa5, 0x52, 0x29, 0xda, 0xd4, 0x7b, 0x01, 0xa7, 0x0d, 0x82, 0x59, 0xd1, 0xc4, + 0x3e, 0x5c, 0xe4, 0xf4, 0x25, 0x9d, 0x22, 0x37, 0x99, 0x9e, 0x35, 0x08, 0x89, 0xab, + 0x7e, 0xff, 0xc2, 0x59, 0x17, 0xca, 0xf0, 0x8d, 0x4a, 0x5d, 0x22, 0x74, 0xa2, 0xc0, + 0x00, 0xa8, 0x43, 0x2c, 0xb5, 0x9e, 0x57, 0x53, 0xca, 0x3a, 0xe2, 0x04, 0x11, 0x57, + 0x93, 0x2a, 0x57, 0x70, 0x7b, 0x0d, 0x0c, 0x41, 0x13, 0x4c, 0x2c, 0x03, 0x95, 0x59, + 0xec, 0x57, 0x2b, 0xfe, 0xbb, 0xa5, 0x8b, 0xac, 0x9e, 0xb4, 0xeb, 0x11, 0x5f, 0x42, + 0x8b, 0x6a, 0xe2, 0x07, 0xa1, 0x45, 0x36, 0xb1, 0xad, 0xa6, 0x7d, 0x2c, 0x2e, 0x56, + 0x5e, 0x79, 0x82, 0x91, 0xdc, 0x44, 0xfd, 0x97, 0xe3, 0x1e, 0x30, 0xe9, 0x21, 0x0a, + 0x1e, 0x06, 0xa7, 0x99, 0x1c, 0xfb, 0x2b, 0xf6, 0x29, 0x04, 0xd7, 0xe0, 0x50, 0x92, + 0x66, 0x0a, 0x76, 0xde, 0x6b, 0x51, 0x03, 0x95, 0x77, 0xe9, 0x71, 0x3a, 0xbc, 0xa4, + 0x2d, 0x58, 0x9f, 0x0f, 0x35, 0x2e, 0xdc, 0x3b, 0x82, 0x83, 0x78, 0x53, 0x42, 0x6a, + 0xfd, 0x03, 0x6c, 0xa9, 0x5e, 0x95, 0xd5, 0x7b, 0x73, 0x82, 0x2a, 0x76, 0x33, 0x1f, + 0xb0, 0x3d, 0x35, 0xe5, 0x73, 0x43, 0x0f, 0x9d, 0x0c, 0x08, 0x0d, 0xfa, 0x7f, 0xdf, + 0x7d, 0x5f, 0x33, 0xd9, 0x8b, 0xff, 0xdf, 0x7a, 0x84, 0xa8, 0x4e, 0x8c, 0xd8, 0x34, + 0x9f, 0x99, 0x68, 0xf5, 0x1f, 0x8d, 0xe0, 0x41, 0xc6, 0xf0, 0xa0, 0x04, 0xf4, 0x7b, + 0x05, 0xf6, 0x12, 0xf6, 0x14, 0xec, 0xfa, 0xcc, 0x0c, 0xf7, 0x19, 0x67, 0x04, 0x7b, + 0xc8, 0xc2, 0x1d, 0x6b, 0xd3, 0xd8, 0x5f, 0xa6, 0x9b, 0x99, 0xd0, 0x75, 0x57, 0x31, + 0xd8, 0xcb, 0x38, 0x3e, 0xee, 0x1a, 0xad, 0xe6, 0xf1, 0xf5, 0xaa, 0x65, 0x3c, 0xeb, + 0x2f, 0xbe, 0x91, 0xe2, 0x1d, 0x6d, 0x69, 0x2a, 0xa7, 0xf9, 0x09, 0x72, 0x23, 0x14, + 0x70, 0x3b, 0xcc, 0xee, 0xf5, 0x97, 0x4c, 0x31, 0x6a, 0xef, 0x0e, 0x12, 0x39, 0xa0, + 0xe0, 0x9e, 0xad, 0x1b, 0xfb, 0xfa, 0xe6, 0xea, 0x02, 0x55, 0x25, 0x50, 0x95, 0xb8, + 0x28, 0x59, 0x65, 0xf5, 0x8f, 0xcd, 0x5a, 0xe8, 0xd4, 0x38, 0x8b, 0x9e, 0x8a, 0xa9, + 0xfb, 0x08, 0xa9, 0x97, 0x8c, 0x0d, 0x3f, 0xf1, 0x9a, 0xb9, 0xe6, 0x4d, 0x8b, 0x63, + 0xf5, 0x82, 0xeb, 0xbc, 0xc1, 0xf5, 0x3b, 0x57, 0x5e, 0x9e, 0xfb, 0xe9, 0x70, 0x51, + 0xc6, 0xa9, 0x00, 0x9e, 0xc6, 0x8f, 0x5d, 0x40, 0x13, 0x18, 0xf8, 0x93, 0xb0, 0x9a, + 0x22, 0x60, 0x61, 0x5f, 0x9d, 0xc4, 0x64, 0x2c, 0x59, 0xe4, 0x35, 0x96, 0xfe, 0xdf, + 0x71, 0xac, 0xc0, 0xd1, 0xd3, 0xfa, 0xf6, 0x18, 0x45, 0xc8, 0x11, 0xe6, 0xb4, 0x67, + 0x98, 0x26, 0x75, 0x32, 0x9b, 0x79, 0xb8, 0x81, 0xca, 0x7f, 0x25, 0x02, 0xb0, 0x5a, + 0xfe, 0x29, 0xf2, 0x5e, 0x77, 0x5e, 0xfd, 0x77, 0x6d, 0x9d, 0xb2, 0x05, 0x2e, 0x3b, + 0x94, 0xa0, 0xd1, 0xbe, 0x1e, 0xaf, 0x42, 0xf5, 0xf3, 0xf9, 0x88, 0x89, 0xf9, 0x0b, + 0x54, 0x68, 0x4e, 0x27, 0xd0, 0xe2, 0xa4, 0xc8, 0x91, 0x3a, 0x51, 0xd1, 0xa9, 0x3c, + 0xd0, 0x29, 0x39, 0xd9, 0xf5, 0x88, 0x90, 0x0e, 0x7b, 0xef, 0x44, 0xb4, 0x4d, 0xa7, + 0xf2, 0xda, 0x4c, 0xe9, 0x23, 0xa0, 0x22, 0x5d, 0x44, 0xfa, 0x36, 0x03, 0x06, 0x93, + 0xc5, 0xbc, 0xdb, 0x9d, 0x1c, 0x22, 0x40, 0x31, 0x8e, 0x42, 0xf5, 0xd4, 0x95, 0xe0, + 0x91, 0x24, 0xae, 0x4e, 0xd3, 0x70, 0x92, 0x33, 0xd5, 0x54, 0x80, 0xfe, 0xff, 0xcc, + 0x17, 0x3a, 0x13, 0x72, 0x9a, 0x90, 0xda, 0xf9, 0x73, 0x31, 0xdc, 0x52, 0x0e, 0x11, + 0xe8, 0x5c, 0x33, 0x7b, 0xa3, 0x9b, 0x7b, 0x58, 0xd9, 0x4f, 0xf7, 0xe9, 0x6e, 0x3d, + 0x0b, 0x4b, 0x66, 0xc3, 0x94, 0x7e, 0x7e, 0x1c, 0x1a, 0x22, 0x95, 0xc4, 0xd8, 0x34, + 0x75, 0x40, 0x92, 0x0d, 0x6d, 0x7b, 0x9b, 0xff, 0xd7, 0x52, 0xb6, 0x03, 0xc5, 0x2f, + 0xdf, 0x9d, 0x8d, 0xb8, 0x6c, 0xce, 0xa9, 0x22, 0xf0, 0x4a, 0x04, 0xc1, 0xc3, 0x31, + 0x7b, 0x71, 0xdc, 0x48, 0x0a, 0x26, 0x48, 0x67, 0x1a, 0xba, 0x2c, 0xa9, 0xdb, 0x66, + 0xe8, 0x2e, 0x73, 0xb7, 0xbb, 0xfe, 0xba, 0x24, 0x81, 0x53, 0x8a, 0x87, 0xe4, 0x39, + 0xed, 0x20, 0x65, 0xcc, 0x65, 0xf7, 0xa2, 0x6e, 0x0c, 0x83, 0x60, 0x02, 0xff, 0xac, + 0x67, 0xe6, 0x90, 0x7f, 0xad, 0x14, 0x42, 0x18, 0xed, 0x6e, 0x36, 0x3b, 0xfe, 0xc9, + 0x88, 0x73, 0x89, 0x86, 0x86, 0xd8, 0x45, 0xc0, 0xa4, 0xfb, 0x6d, 0xf4, 0xe3, 0x0e, + 0xc9, 0x16, 0x09, 0x37, 0x12, 0x31, 0xa9, 0xb7, 0xf7, 0xa2, 0x0c, 0xce, 0x3d, 0x38, + 0xf9, 0xa3, 0xd4, 0x43, 0x67, 0xb2, 0x05, 0xd0, 0x72, 0x47, 0x1f, 0xcf, 0x67, 0xda, + 0xfe, 0x45, 0x5d, 0x6c, 0xf9, 0x5e, 0x3f, 0x56, 0x2a, 0x08, 0x93, 0x77, 0xc4, 0x84, + 0x36, 0x96, 0x8d, 0xb9, 0x3f, 0x82, 0xa5, 0x1a, 0xc3, 0xea, 0xeb, 0x89, 0x3c, 0x61, + 0x4d, 0x54, 0xed, 0x69, 0xd9, 0xbc, 0xea, 0x9d, 0x3e, 0x9e, 0x20, 0x5f, 0xa9, 0xb2, + 0xa3, 0x4e, 0xa7, 0xdb, 0x76, 0x77, 0x9b, 0xdf, 0x71, 0xf3, 0xf9, 0x23, 0xb6, 0x8f, + 0xc6, 0x74, 0xa2, 0xc9, 0xfb, 0x0d, 0x1b, 0x67, 0x66, 0xed, 0x41, 0xb5, 0x16, 0x6e, + 0x09, 0xca, 0xb7, 0x40, 0x14, 0xba, 0x7a, 0x84, 0x2f, 0x83, 0x19, 0x78, 0xf7, 0x50, + 0x14, 0xc2, 0xbc, 0xac, 0xca, 0x85, 0x9c, 0x8f, 0xb0, 0x43, 0xb9, 0xb5, 0xcf, 0xf4, + 0x9d, 0x62, 0x9a, 0x29, 0x87, 0x98, 0xde, 0x55, 0x11, 0xe0, 0x39, 0x37, 0x40, 0xda, + 0x58, 0x34, 0x2b, 0x48, 0xac, 0x82, 0x74, 0xa9, 0x2a, 0x8c, 0x4c, 0x02, 0x01, 0xbd, + 0x86, 0xee, 0x16, 0x6e, 0x54, 0x0f, 0x53, 0xd1, 0xb7, 0x97, 0xf4, 0xae, 0x59, 0xe1, + 0xab, 0xfa, 0x7b, 0xae, 0x5d, 0xca, 0xb6, 0x59, 0x0a, 0x54, 0x3f, 0x74, 0x2b, 0x1a, + 0x61, 0x26, 0x4d, 0xad, 0xa9, 0x4b, 0x48, 0x1d, 0xa9, 0x15, 0x05, 0x67, 0x7c, 0xd0, + 0x52, 0x7f, 0xbb, 0x95, 0xcc, 0xd6, 0x31, 0xc7, 0x64, 0xdf, 0x36, 0x5b, 0xde, 0xe1, + 0xa2, 0x22, 0xdd, 0x25, 0x29, 0x51, 0x6a, 0xa1, 0x3d, 0x95, 0x59, 0xaa, 0x6c, 0xf6, + 0xfe, 0xbb, 0xff, 0x61, 0xa8, 0xcf, 0x6c, 0x20, 0xb7, 0x35, 0x28, 0x15, 0x9c, 0x1f, + 0xcf, 0x37, 0x86, 0x13, 0x1c, 0x00, 0x79, 0x09, 0xf3, 0xea, 0x68, 0xdb, 0xc8, 0x82, + 0xdc, 0xff, 0x28, 0x0d, 0x2b, 0xa6, 0x73, 0x7a, 0x97, 0x3e, 0xd5, 0x8a, 0xa6, 0x95, + 0xfb, 0x6f, 0x26, 0x34, 0xd7, 0xd5, 0xc7, 0x50, 0xfe, 0x72, 0x2c, 0x21, 0x31, 0xe0, + 0x4f, 0xdf, 0x1e, 0x50, 0x8b, 0x5e, 0xb1, 0x9a, 0x00, 0xbf, 0xd3, 0x18, 0xf2, 0xb4, + 0xca, 0xb0, 0xe0, 0x0d, 0x61, 0xeb, 0x23, 0xff, 0x8c, 0x68, 0x92, 0xf5, 0x36, 0x14, + 0xdd, 0xae, 0x13, 0xc6, 0x90, 0xd1, 0xff, 0x04, 0xf5, 0x00, 0xd6, 0xb7, 0xf5, 0xe9, + 0x1d, 0x61, 0xa5, 0x93, 0xe7, 0xd4, 0xc7, 0x00, 0xa2, 0x5d, 0x5a, 0x38, 0xae, 0xbe, + 0xb9, 0x63, 0xce, 0x16, 0x97, 0xa5, 0xbb, 0x1d, 0x90, 0xa1, 0x00, 0xc2, 0xc2, 0xd4, + 0x36, 0x63, 0x84, 0x95, 0xcd, 0x84, 0xdf, 0xc4, 0x3c, 0x21, 0x20, 0x6f, 0xa6, 0x4e, + 0xff, 0x5b, 0x86, 0xa7, 0x23, 0x33, 0xe6, 0xce, 0xd9, 0x79, 0x4b, 0xbd, 0x43, 0x20, + 0xad, 0xdd, 0x45, 0x34, 0x8a, 0xc2, 0x9a, 0xae, 0x33, 0x00, 0x37, 0x11, 0x51, 0xbc, + 0x61, 0xa4, 0x3d, 0xc4, 0xa7, 0x39, 0x4e, 0x4a, 0x37, 0x2d, 0x78, 0xd7, 0x5f, 0x12, + 0x1a, 0x5a, 0x51, 0xfd, 0x5b, 0xdc, 0x4a, 0x03, 0x8b, 0x21, 0x75, 0x21, 0x87, 0xba, + 0x80, 0x48, 0xb1, 0x37, 0x45, 0x45, 0x6f, 0xc7, 0x81, 0x3e, 0x82, 0x9d, 0xc6, 0x07, + 0xf1, 0x58, 0xd3, 0xe3, 0x3a, 0x2f, 0x61, 0x43, 0x86, 0xe4, 0xee, 0x45, 0xfb, 0xb6, + 0x98, 0xea, 0x45, 0x86, 0x98, 0x98, 0x70, 0x1a, 0x26, 0x77, 0x3f, 0xf1, 0x36, 0xe8, + 0xd6, 0x8c, 0x1a, 0x46, 0xac, 0xcf, 0x42, 0xfd, 0xec, 0xa3, 0x3e, 0xfd, 0x05, 0x4e, + 0x5a, 0x7b, 0x2b, 0x0a, 0xa4, 0x68, 0x37, 0x7d, 0x87, 0x8d, 0x1a, 0xe2, 0x35, 0xf4, + 0xee, 0x7a, 0xd7, 0x8f, 0x83, 0x71, 0xbb, 0x21, 0x1e, 0x4d, 0x28, 0x0c, 0xbd, 0x9c, + 0x89, 0x67, 0x60, 0xeb, 0xd8, 0xf6, 0x0e, 0xb7, 0x1e, 0x63, 0xe9, 0x8c, 0xfd, 0x89, + 0xa4, 0x45, 0xa8, 0xe7, 0x19, 0x37, 0xde, 0xa1, 0x37, 0xff, 0xe4, 0x96, 0x16, 0xb4, + 0x81, 0x52, 0x47, 0xba, 0xff, 0x56, 0x3e, 0x87, 0x3c, 0xeb, 0xd3, 0x96, 0x6c, 0x79, + 0xb9, 0xb4, 0x10, 0x82, 0x00, 0x2f, 0x8f, 0x73, 0x11, 0x7d, 0xb1, 0xdc, 0x71, 0x92, + 0xfa, 0x12, 0xdf, 0x15, 0xb0, 0x7f, 0x3a, 0x57, 0x43, 0x56, 0xa9, 0x21, 0x1f, 0x7c, + 0x44, 0x26, 0x49, 0xd5, 0x69, 0x4a, 0x4f, 0x46, 0xe2, 0x34, 0x3d, 0x51, 0x06, 0xf8, + 0x22, 0x35, 0xc5, 0x86, 0xcf, 0xc8, 0x17, 0x43, 0xc9, 0x67, 0xb7, 0xa4, 0x94, 0x6f, + 0x82, 0x9d, 0x7c, 0x3d, 0x60, 0x80, 0x01, 0x05, 0x8e, 0xe1, 0x43, 0x3f, 0x74, 0xca, + 0x72, 0x84, 0xa1, 0x31, 0xe2, 0x98, 0x78, 0xb3, 0x4f, 0xa4, 0x48, 0xcb, 0x58, 0xe3, + 0xa1, 0x6d, 0x47, 0x28, 0x8e, 0x2e, 0x8a, 0x0a, 0x95, 0x90, 0x63, 0xad, 0x95, 0x45, + 0x4e, 0x2d, 0xe3, 0xb4, 0x57, 0xc2, 0x82, 0xb3, 0xfb, 0x89, 0x32, 0xa0, 0xc4, 0x8b, + 0x69, 0x78, 0xd7, 0x14, 0xf4, 0x6f, 0xbe, 0x53, 0x62, 0x05, 0xe0, 0x3f, 0x4a, 0x15, + 0xf2, 0xd5, 0x79, 0x29, 0x47, 0xd7, 0xfa, 0xdc, 0x19, 0x04, 0x74, 0xae, 0x47, 0xe6, + 0x6f, 0x82, 0x66, 0xef, 0xe8, 0x10, 0x39, 0x2b, 0x1d, 0x90, 0x33, 0xae, 0xbf, 0x8e, + 0x5b, 0xd4, 0x70, 0x9f, 0x10, 0x76, 0xa3, 0x45, 0x4b, 0xe9, 0x7b, 0xcb, 0x27, 0x3f, + 0x90, 0xa9, 0x87, 0xc2, 0x98, 0x30, 0x22, 0x97, 0xfb, 0x65, 0x2b, 0x0d, 0xea, 0xec, + 0xbd, 0x3c, 0x32, 0xea, 0x58, 0x14, 0x3c, 0x7c, 0x6b, 0x4b, 0x80, 0xd9, 0xfe, 0xfe, + 0x81, 0x0f, 0x74, 0x07, 0x54, 0x5d, 0x50, 0x25, 0x27, 0xcd, 0x7e, 0xd9, 0x71, 0xc1, + 0xae, 0xf4, 0xf8, 0x37, 0xfe, 0x8d, 0x60, 0xac, 0x7f, 0x5d, 0x36, 0x16, 0x0c, 0x75, + 0x6d, 0x79, 0x45, 0x8a, 0xbf, 0x90, 0x4b, 0x0c, 0x37, 0x2d, 0x46, 0x54, 0xd1, 0x24, + 0x04, 0x03, 0x2d, 0xf3, 0xaa, 0x15, 0x53, 0x20, 0xc3, 0x93, 0xbf, 0xd1, 0xcf, 0xd4, + 0x65, 0x3f, 0xe2, 0x24, 0xd8, 0x15, 0xe4, 0x82, 0x09, 0x77, 0xdd, 0x34, 0xbb, 0x5c, + 0x5f, 0x43, 0x12, 0xec, 0x86, 0x8a, 0xf7, 0xf5, 0x75, 0xcf, 0xe7, 0x74, 0xd4, 0xdf, + 0x2b, 0x0c, 0x0e, 0xdf, 0xb4, 0x11, 0xe5, 0x98, 0x4f, 0x7d, 0x9e, 0x60, 0xdd, 0xf4, + 0x4a, 0x12, 0x5b, 0xc0, 0x63, 0x21, 0x81, 0x72, 0xa2, 0x65, 0x1f, 0x73, 0x56, 0xcd, + 0x30, 0x16, 0x4f, 0x57, 0xa7, 0x23, 0x85, 0x82, 0xf6, 0xb1, 0x1b, 0xb4, 0x13, 0x46, + 0x71, 0xaf, 0x5b, 0x78, 0x9d, 0xd8, 0xcb, 0x7b, 0x25, 0x0d, 0x50, 0x20, 0x28, 0x26, + 0x85, 0xc8, 0x4d, 0x4e, 0xce, 0xc9, 0xb3, 0x3f, 0x9e, 0x50, 0x1f, 0x5d, 0x30, 0x7c, + 0x53, 0x87, 0xe6, 0xa2, 0xa5, 0x28, 0xfa, 0x71, 0x28, 0x58, 0xce, 0x65, 0xdc, 0x4d, + 0x8c, 0x0d, 0x29, 0x04, 0xac, 0x3d, 0x39, 0x55, 0x6c, 0x0c, 0x8d, 0x3e, 0x94, 0x31, + 0xb9, 0x44, 0x92, 0x95, 0x58, 0x57, 0xcd, 0x09, 0xec, 0x63, 0xd5, 0xae, 0x0c, 0xf1, + 0xfa, 0x28, 0x6c, 0xbb, 0x7c, 0xa8, 0x49, 0xed, 0x8c, 0x8a, 0x0e, 0xdb, 0xa1, 0xf6, + 0x1d, 0x67, 0xa0, 0x8f, 0xc7, 0xc8, 0x16, 0xfd, 0x68, 0xaf, 0xaf, 0x9f, 0xda, 0xeb, + 0x3b, 0xf5, 0x25, 0xce, 0xce, 0x7e, 0x98, 0x42, 0xe8, 0x1b, 0x4a, 0xc8, 0xef, 0xbc, + 0x63, 0x9f, 0xcf, 0xf5, 0x50, 0x79, 0x3d, 0xfd, 0xdf, 0xa9, 0x33, 0xb8, 0x8a, 0x8f, + 0xa7, 0x29, 0x49, 0x46, 0x43, 0x2f, 0x98, 0xde, 0x73, 0x2d, 0x8f, 0xd5, 0x1c, 0x93, + 0xb7, 0x3a, 0x83, 0xd1, 0x61, 0x56, 0x8e, 0x19, 0x66, 0x9d, 0xc3, 0xc0, 0xc1, 0x92, + 0xfc, 0x22, 0xcb, 0xc1, 0xd2, 0xfd, 0xeb, 0x44, 0xfa, 0xc2, 0x98, 0x44, 0x33, 0xb7, + 0x67, 0x26, 0x1d, 0x51, 0xa3, 0x02, 0x0d, 0x04, 0xa1, 0xca, 0xd9, 0x8e, 0xb4, 0xba, + 0x12, 0xbe, 0xc1, 0x2d, 0x5a, 0x19, 0x27, 0xbc, 0x88, 0xd5, 0x22, 0xac, 0xa1, 0x3f, + 0x81, 0xb6, 0x9b, 0x06, 0x38, 0xd6, 0x42, 0x46, 0xb9, 0x52, 0xbf, 0x07, 0x45, 0x61, + 0x43, 0x48, 0x3b, 0x20, 0xe9, 0x8d, 0xea, 0xfc, 0xe8, 0x08, 0xfd, 0x59, 0xe3, 0x82, + 0x1c, 0x45, 0x24, 0xe3, 0x33, 0x43, 0xb3, 0x3a, 0xa2, 0xa1, 0x03, 0x05, 0x35, 0xd1, + 0x31, 0xfe, 0x18, 0xd9, 0x00, 0xad, 0xa8, 0xe6, 0xe2, 0x01, 0xb5, 0x92, 0x9a, 0x13, + 0xa8, 0xc1, 0x2a, 0xae, 0x9e, 0xd9, 0x6e, 0x52, 0x1d, 0xce, 0x3e, 0xc7, 0x33, 0x59, + 0x0f, 0xd8, 0x5c, 0x18, 0xcb, 0xd7, 0x4a, 0x98, 0x02, 0xc5, 0x54, 0x5f, 0xf9, 0xae, + 0x28, 0x57, 0xa5, 0x5a, 0x32, 0xd8, 0x43, 0xb5, 0x40, 0xf1, 0x61, 0x97, 0xa0, 0x3f, + 0x0c, 0xce, 0xe1, 0x07, 0x7f, 0xb5, 0x74, 0x6c, 0x5b, 0x57, 0x34, 0xd3, 0x1b, 0x1d, + 0xd4, 0x8c, 0x53, 0xc7, 0x5b, 0xaa, 0xd4, 0x20, 0xa5, 0x84, 0x3e, 0x22, 0x0c, 0xc7, + 0x48, 0xa2, 0xb4, 0xbb, 0xf0, 0x26, 0xd2, 0xe3, 0xf4, 0xed, 0x7b, 0xb9, 0x6b, 0x3b, + 0x78, 0x58, 0x4f, 0xeb, 0xc9, 0xfa, 0xc3, 0xed, 0x94, 0x9d, 0x2c, 0x67, 0x16, 0x2d, + 0x71, 0x16, 0x7b, 0xcb, 0x7a, 0x0b, 0xee, 0xb0, 0xa2, 0x4c, 0xa4, 0x50, 0x1c, 0x11, + 0x64, 0x10, 0x9e, 0x34, 0xfc, 0x3e, 0x05, 0xf8, 0x31, 0x51, 0x16, 0x49, 0x13, 0xcd, + 0xcd, 0xc9, 0x14, 0x97, 0x4e, 0x2c, 0x6a, 0x90, 0xc5, 0xd2, 0xa8, 0xdc, 0x76, 0x1e, + 0xe0, 0xca, 0xd9, 0x56, 0x5c, 0xda, 0x48, 0x5d, 0xe9, 0xf4, 0x73, 0xb5, 0xed, 0x1d, + 0xc5, 0x89, 0x12, 0x48, 0x37, 0x4b, 0x5e, 0x81, 0x61, 0x63, 0xcd, 0x9c, 0x84, 0xd0, + 0x3a, 0x51, 0xef, 0x2b, 0x99, 0xd8, 0x11, 0x96, 0x8b, 0xf7, 0x93, 0x8c, 0x9f, 0x95, + 0x3f, 0x17, 0xcb, 0xcc, 0x67, 0xd6, 0x98, 0x03, 0x6a, 0x9b, 0x0c, 0x12, 0xba, 0xbe, + 0xda, 0x64, 0x7f, 0x7a, 0x46, 0x9d, 0xa4, 0x86, 0x69, 0xd5, 0x8a, 0x8e, 0x75, 0x72, + 0x8c, 0x12, 0xde, 0xe0, 0x6e, 0x62, 0x33, 0xf5, 0xd3, 0xbd, 0x6c, 0xfa, 0x2a, 0xd1, + 0x78, 0x50, 0xe9, 0xe8, 0x40, 0xd3, 0x72, 0x08, 0x45, 0x47, 0x08, 0x58, 0xd8, 0x45, + 0x80, 0xb1, 0x3f, 0xd7, 0x0a, 0x98, 0x08, 0x62, 0x51, 0x93, 0x4a, 0xa1, 0xb7, 0x51, + 0x5a, 0x8b, 0xde, 0x3f, 0xb2, 0xf0, 0xa7, 0xaf, 0xa9, 0xcf, 0x4d, 0x77, 0x83, 0x54, + 0x07, 0xcb, 0x35, 0x38, 0xfa, 0xee, 0x99, 0xb5, 0xb2, 0xbf, 0xfd, 0x11, 0xea, 0xf5, + 0x92, 0x61, 0x82, 0x9a, 0xfd, 0xb3, 0xd8, 0x4c, 0x50, 0xfb, 0x38, 0x83, 0x1f, 0xa1, + 0xdb, 0x2a, 0x08, 0x8a, 0x98, 0xe6, 0x0f, 0x45, 0xfc, 0x52, 0x05, 0x04, 0x50, 0x2a, + 0x5a, 0x4d, 0x06, 0x40, 0xd1, 0x99, 0xad, 0x2f, 0x72, 0xa7, 0x8f, 0x76, 0x6f, 0xbf, + 0x58, 0xa4, 0x4d, 0xe4, 0x04, 0x43, 0x5f, 0x7e, 0x9d, 0x18, 0xfb, 0x1b, 0xe4, 0x28, + 0x40, 0x9a, 0x4a, 0x92, 0x68, 0x23, 0xd3, 0xc5, 0xba, 0x90, 0x59, 0x26, 0xb4, 0xd8, + 0x73, 0x2e, 0x12, 0x25, 0x70, 0x44, 0x30, 0x7d, 0x92, 0x6a, 0x01, 0x95, 0xee, 0x18, + 0xb7, 0x8c, 0xb6, 0xf7, 0x55, 0x9c, 0x61, 0xfc, 0x9f, 0x93, 0x49, 0xdb, 0x35, 0x8c, + 0x95, 0x95, 0xf9, 0xdd, 0x20, 0xa2, 0x99, 0xff, 0xc7, 0xfb, 0x74, 0x0d, 0x03, 0xa1, + 0x9b, 0x74, 0x66, 0xdd, 0x0e, 0x7a, 0x9c, 0x1d, 0x0e, 0x8e, 0xc8, 0x5d, 0x3c, 0xea, + 0xc9, 0x2d, 0x5e, 0xa0, 0x41, 0xa7, 0xa6, 0x3d, 0x17, 0x30, 0x01, 0x43, 0x3e, 0x4f, + 0xb7, 0xad, 0x42, 0x96, 0xce, 0x87, 0x92, 0x7b, 0xf0, 0xb7, 0xf1, 0xc9, 0x83, 0x02, + 0x14, 0xfd, 0xe1, 0x30, 0x1d, 0xcc, 0xf4, 0x20, 0xbd, 0x9b, 0x5b, 0x7d, 0x11, 0x25, + 0x66, 0xb1, 0x9c, 0x0f, 0xeb, 0xc0, 0x95, 0x34, 0x49, 0x2a, 0xb8, 0x85, 0x49, 0x25, + 0x37, 0xcd, 0x77, 0xf5, 0xb5, 0xd9, 0xab, 0x7c, 0x70, 0x02, 0xa9, 0x68, 0xdb, 0xb8, + 0x4f, 0x30, 0x57, 0x04, 0xdd, 0xfd, 0x6a, 0x8a, 0x2b, 0xb3, 0x81, 0xfb, 0xde, 0x0c, + 0xec, 0x8f, 0xc5, 0xf3, 0x2c, 0x12, 0x1b, 0x5c, 0x56, 0x7e, 0x8b, 0x24, 0x37, 0x88, + 0xd1, 0xab, 0x7d, 0xfc, 0x0d, 0x99, 0x6f, 0xc7, 0x54, 0x5d, 0xdf, 0x9c, 0x00, 0x16, + 0x36, 0x72, 0xff, 0x7d, 0x59, 0xc8, 0x02, 0xf0, 0x4c, 0xc1, 0xf3, 0xbf, 0x82, 0x1e, + 0xd5, 0x1c, 0x76, 0x4f, 0x79, 0xb2, 0xb4, 0x86, 0x51, 0xab, 0x16, 0xab, 0xcb, 0xe6, + 0x0d, 0xa6, 0x28, 0x98, 0xba, 0xa2, 0x5c, 0xe7, 0xeb, 0xde, 0x13, 0x78, 0x3e, 0xa1, + 0xf8, 0x8c, 0xad, 0xa0, 0x44, 0xe9, 0x71, 0xaf, 0x9b, 0x8e, 0x64, 0x4a, 0xbb, 0x4c, + 0xa2, 0x22, 0x4b, 0x8b, 0x4a, 0x80, 0x05, 0x08, 0xed, 0xf1, 0x7f, 0xbc, 0x79, 0xf5, + 0x09, 0xa2, 0xac, 0xa9, 0xf7, 0x03, 0xb4, 0xbc, 0x57, 0xa1, 0xc4, 0x80, 0x1f, 0xb8, + 0x56, 0xa2, 0xfb, 0x67, 0x45, 0x17, 0xfd, 0xc9, 0x0c, 0x53, 0xa1, 0x20, 0xd5, 0x07, + 0xec, 0xe2, 0xec, 0x4a, 0xe9, 0x65, 0x3e, 0x4f, 0x2e, 0x7a, 0x83, 0x8f, 0x44, 0xc1, + 0x7f, 0x7f, 0x8b, 0xf4, 0x65, 0x50, 0xe1, 0x74, 0x8a, 0x3a, 0x7f, 0xea, 0xf7, 0xcd, + 0x48, 0x2f, 0x34, 0x67, 0x7b, 0xd6, 0x56, 0x0b, 0xa5, 0xc5, 0x17, 0x8a, 0x72, 0x0f, + 0x47, 0xbe, 0x2c, 0xf2, 0x16, 0x77, 0x04, 0x9c, 0xc9, 0xd9, 0x40, 0x0a, 0x39, 0x11, + 0x91, 0x8b, 0xad, 0x83, 0x46, 0x99, 0x40, 0x69, 0xe2, 0x45, 0x7d, 0xef, 0xd9, 0xed, + 0xda, 0x3c, 0xb9, 0x46, 0x2b, 0x8b, 0x80, 0xe4, 0xd3, 0x2e, 0xb6, 0xd3, 0xe9, 0x61, + 0x40, 0xb6, 0xd5, 0x2f, 0x39, 0x15, 0x4b, 0x1b, 0xc2, 0x24, 0x20, 0xa4, 0x48, 0x5b, + 0x79, 0x58, 0x8a, 0x4f, 0x29, 0x12, 0xf9, 0x1f, 0xd9, 0x36, 0x88, 0x36, 0x46, 0xd1, + 0x74, 0xa7, 0x5f, 0x16, 0x9c, 0xf9, 0x33, 0x12, 0x07, 0xd9, 0xcf, 0x06, 0x8f, 0x5f, + 0x65, 0xfc, 0xd1, 0xbd, 0xc8, 0x07, 0xc4, 0x58, 0xb9, 0x3f, 0xf7, 0x34, 0x23, 0x34, + 0x04, 0x7b, 0xbc, 0xcb, 0x03, 0x24, 0x7b, 0x6d, 0xec, 0x28, 0xec, 0xa6, 0x32, 0xd8, + 0xe5, 0x30, 0xb9, 0xe2, 0x53, 0x31, 0x31, 0xef, 0xbe, 0x56, 0xad, 0x92, 0x98, 0xdb, + 0x36, 0xd0, 0xde, 0xa8, 0x39, 0x56, 0xbf, 0xdd, 0x44, 0xf7, 0x17, 0x8e, 0x98, 0xc5, + 0x1d, 0x11, 0xcf, 0xd3, 0x4f, 0x48, 0x2a, 0xb8, 0xb7, 0x49, 0x4f, 0x96, 0x41, 0x10, + 0xae, 0x40, 0x86, 0x99, 0x2f, 0x70, 0xb6, 0x14, 0x8c, 0xc8, 0x9d, 0x61, 0x32, 0x6f, + 0xb2, 0x66, 0xd6, 0xf0, 0xb6, 0xe6, 0x97, 0x1e, 0xb6, 0x0d, 0x66, 0x4a, 0xa9, 0x25, + 0x64, 0xa5, 0x0f, 0x7f, 0xf6, 0x39, 0x51, 0x1d, 0x2a, 0xb2, 0x71, 0xe5, 0x07, 0x45, + 0x8c, 0xd0, 0x6d, 0xd2, 0x04, 0x7d, 0xc7, 0x37, 0x1b, 0xdb, 0x1c, 0xfc, 0xaa, 0x54, + 0x09, 0xaa, 0xab, 0xb2, 0xcc, 0xea, 0x6c, 0x8f, 0xd1, 0xd4, 0x76, 0x33, 0x5c, 0xa2, + 0xd5, 0x82, 0x53, 0x17, 0x9d, 0x05, 0x6a, 0xb4, 0x5c, 0x0c, 0xca, 0xaa, 0x12, 0xf7, + 0x02, 0xfe, 0xec, 0xb5, 0x94, 0x61, 0xbd, 0x76, 0xe9, 0xa7, 0xbd, 0xa6, 0x72, 0xac, + 0x29, 0x10, 0xcd, 0x07, 0xce, 0x23, 0xba, 0x37, 0x29, 0x3c, 0xfc, 0xde, 0xae, 0xea, + 0xda, 0x7a, 0xa1, 0x37, 0x9c, 0xaf, 0x1c, 0xe0, 0xcf, 0x5f, 0x8e, 0x5b, 0x88, 0xdb, + 0xf5, 0x15, 0xb0, 0x62, 0x46, 0x0e, 0xba, 0x38, 0x03, 0x20, 0xe3, 0x2a, 0xe2, 0xcb, + 0xbf, 0x7c, 0xa2, 0x8e, 0x88, 0x9e, 0x45, 0xae, 0x9a, 0x66, 0x1f, 0x67, 0x81, 0x08, + 0x27, 0x6a, 0x18, 0x4b, 0x6e, 0xc7, 0x80, 0xff, 0xec, 0xeb, 0x4a, 0x75, 0x95, 0xb4, + 0x10, 0xd4, 0xc6, 0xe5, 0x1b, 0x2b, 0x62, 0x9f, 0x71, 0xd6, 0x33, 0xd6, 0x29, 0x35, + 0x48, 0x4a, 0xcd, 0x1c, 0x0e, 0x20, 0xbe, 0xfd, 0x86, 0x9c, 0x7d, 0x12, 0xe7, 0xd7, + 0x06, 0x71, 0xfc, 0x3d, 0x0c, 0x88, 0xc8, 0x06, 0xc3, 0xfe, 0xf0, 0xec, 0xf8, 0x50, + 0xb4, 0x69, 0xe1, 0x2e, 0x88, 0xbb, 0x61, 0x40, 0x54, 0xf4, 0x14, 0xa5, 0xa2, 0x69, + 0xe4, 0x89, 0x5a, 0x1e, 0xb6, 0xb3, 0xbb, 0xdd, 0xc5, 0x27, 0x00, 0xc9, 0x93, 0x68, + 0x29, 0xdc, 0xb9, 0xec, 0xb7, 0x2f, 0xf7, 0x97, 0xe0, 0x97, 0xd8, 0x55, 0x2c, 0xb0, + 0x89, 0x1a, 0x01, 0xd1, 0x82, 0xd2, 0xbd, 0xd8, 0x1b, 0x4e, 0xc4, 0x8c, 0x29, 0xe4, + 0xd3, 0x3b, 0xb1, 0x23, 0xff, 0x4a, 0x1c, 0x28, 0x79, 0x95, 0xf9, 0xb0, 0x93, 0x71, + 0x1c, 0x20, 0x5e, 0x7a, 0x4a, 0x5e, 0x08, 0x5b, 0xb9, 0xfe, 0xf3, 0x0f, 0x58, 0x13, + 0xce, 0x76, 0xda, 0xba, 0xc8, 0x4a, 0xd9, 0xf0, 0xdd, 0xd1, 0x4f, 0xa2, 0x76, 0x51, + 0x3a, 0xf4, 0x1b, 0x7b, 0xd9, 0xea, 0xfc, 0x44, 0xc4, 0x1b, 0x95, 0x97, 0x94, 0x8d, + 0x14, 0x17, 0xe2, 0xe0, 0xea, 0x80, 0x04, 0x6f, 0x5b, 0xd3, 0xcc, 0x16, 0x04, 0x28, + 0xb8, 0x9d, 0x75, 0xc9, 0x48, 0xb5, 0x38, 0xe8, 0x1c, 0xfc, 0x8c, 0x52, 0xf6, 0xae, + 0x81, 0x39, 0x5a, 0x4a, 0xb4, 0xb2, 0x5f, 0xb1, 0x3d, 0x1c, 0xcd, 0x38, 0xeb, 0x7d, + 0x62, 0x03, 0x03, 0x8c, 0xeb, 0x45, 0xba, 0xf1, 0x53, 0x22, 0x9e, 0x13, 0x4f, 0x98, + 0x34, 0x97, 0xba, 0x58, 0xf2, 0x19, 0x3f, 0x16, 0xbd, 0x08, 0xc3, 0xbc, 0xf1, 0x0e, + 0x35, 0x99, 0x6d, 0xc3, 0x79, 0x0f, 0x2f, 0x99, 0x42, 0xe4, 0x48, 0x6e, 0x39, 0x10, + 0x57, 0xfd, 0x72, 0x5e, 0x6a, 0x20, 0xda, 0x6d, 0x15, 0x07, 0x19, 0xad, 0x61, 0x86, + 0xc5, 0xe8, 0x67, 0x8a, 0x08, 0x8f, 0xd9, 0xad, 0xb6, 0xe9, 0x38, 0x2d, 0x16, 0xf3, + 0x7e, 0x2c, 0xa6, 0xca, 0xb1, 0x1e, 0x79, 0xb4, 0x7a, 0xf1, 0x1e, 0xb8, 0xd3, 0x2d, + 0x09, 0x06, 0xe1, 0x02, 0x4f, 0x45, 0xe8, 0x7d, 0xff, 0x75, 0xce, 0x53, 0x50, 0xf7, + 0xe0, 0xf0, 0x5d, 0x5d, 0x87, 0x91, 0x70, 0x4b, 0x8b, 0x70, 0x90, 0xee, 0x31, 0x2f, + 0x09, 0x97, 0x8f, 0xe7, 0x65, 0x3d, 0x2d, 0xbb, 0x08, 0x6c, 0x5e, 0x11, 0x74, 0xf7, + 0x25, 0x52, 0xde, 0x8b, 0xfd, 0xa3, 0xe7, 0xc7, 0x07, 0xe3, 0x02, 0x93, 0xa1, 0x6b, + 0xc3, 0xab, 0x5a, 0x88, 0xd5, 0x7c, 0x16, 0xe5, 0x2b, 0x30, 0x5e, 0x1d, 0x7a, 0xcb, + 0x28, 0xe8, 0x45, 0x7c, 0x57, 0x13, 0x14, 0x84, 0x0e, 0xce, 0x19, 0x71, 0xad, 0x31, + 0x43, 0xe5, 0x4c, 0x45, 0xfb, 0x82, 0x11, 0xab, 0xb5, 0x22, 0x9a, 0x22, 0x9b, 0x01, + 0xcc, 0xb8, 0x40, 0xfc, 0x7a, 0x98, 0x43, 0x2f, 0x2a, 0x86, 0x3d, 0xa9, 0xef, 0xda, + 0x3d, 0xce, 0xad, 0x4d, 0x50, 0x4b, 0x66, 0x9e, 0xbd, 0xd7, 0x98, 0x5e, 0x40, 0x33, + 0xde, 0x6a, 0xbf, 0x66, 0x78, 0xb2, 0xf2, 0x0f, 0x40, 0x89, 0x01, 0xd8, 0xf8, 0xd9, + 0x95, 0x41, 0x7f, 0x41, 0x6d, 0x00, 0x01, 0x25, 0x41, 0x69, 0x43, 0x78, 0xd6, 0xfd, + 0x64, 0x23, 0xba, 0x3d, 0x05, 0x67, 0x1a, 0x6a, 0x6d, 0xaa, 0x9f, 0xed, 0xf0, 0xe7, + 0x1a, 0x53, 0xd7, 0x78, 0xb5, 0xc2, 0xe9, 0x08, 0x6b, 0x98, 0x90, 0x45, 0x95, 0xde, + 0xb6, 0xb8, 0xe0, 0xec, 0x56, 0x63, 0x9f, 0xd3, 0xce, 0xda, 0x3d, 0x2e, 0x22, 0x10, + 0xa4, 0x5e, 0x68, 0xbb, 0x5f, 0x35, 0x12, 0xf1, 0x3e, 0xe8, 0x40, 0xa5, 0xfe, 0xab, + 0xe6, 0xe0, 0xd7, 0x70, 0x58, 0x86, 0xa8, 0xd5, 0x7d, 0x79, 0xba, 0xb0, 0xdb, 0xd2, + 0xed, 0xcc, 0xf0, 0x8f, 0x38, 0xd7, 0x72, 0xa7, 0x3e, 0xc3, 0xce, 0xef, 0x6b, 0xb9, + 0x23, 0xb2, 0xae, 0x3b, 0xbd, 0x0c, 0x0b, 0xb4, 0x68, 0x81, 0xfc, 0xc3, 0xed, 0x33, + 0x78, 0xca, 0x32, 0x29, 0x0b, 0x39, 0x76, 0xda, 0x26, 0xd8, 0xa1, 0x46, 0x68, 0xce, + 0x1a, 0x3f, 0x80, 0x6a, 0xe7, 0xc5, 0x04, 0x04, 0xd4, 0x32, 0xd7, 0x49, 0xb6, 0xcd, + 0x16, 0xb5, 0xdd, 0x19, 0x11, 0x90, 0xc2, 0xe9, 0x55, 0x32, 0x22, 0x29, 0xe5, 0xd1, + 0xec, 0x0f, 0x4d, 0xbd, 0x3e, 0x4f, 0x2f, 0xaa, 0x42, 0x44, 0xc5, 0xf8, 0x3c, 0x66, + 0x95, 0x05, 0x18, 0xd6, 0x57, 0xb9, 0x03, 0xd1, 0x5a, 0xf6, 0xf2, 0x34, 0x3e, 0x66, + 0xde, 0xf4, 0x0c, 0x4e, 0x31, 0xda, 0x2c, 0x2c, 0xef, 0x3e, 0xd7, 0xb7, 0x8f, 0xe2, + 0x85, 0x34, 0xac, 0xed, 0x07, 0xde, 0x2c, 0xa1, 0x6d, 0x98, 0xe8, 0x17, 0x62, 0x26, + 0x4e, 0x3b, 0x84, 0xf3, 0xfc, 0x1a, 0xa4, 0x35, 0xbd, 0x8b, 0xd7, 0x8a, 0x8d, 0x19, + 0x97, 0x8c, 0x23, 0x7a, 0x2a, 0xd2, 0x99, 0xd9, 0x59, 0xb6, 0x00, 0x05, 0xf7, 0xdb, + 0x6f, 0x10, 0x81, 0xee, 0x73, 0x65, 0x60, 0xcd, 0xe3, 0x5f, 0xa6, 0x7d, 0x67, 0xde, + 0xbe, 0x7b, 0xda, 0xd3, 0x13, 0x50, 0x2d, 0x40, 0x49, 0x23, 0xc9, 0x31, 0x51, 0xf9, + 0x7e, 0xa5, 0xed, 0xa1, 0x40, 0x30, 0x3e, 0x08, 0x3d, 0xf4, 0x2c, 0xea, 0x9e, 0xaf, + 0xba, 0xe9, 0x41, 0x88, 0x10, 0x4c, 0x77, 0x80, 0x9c, 0xa0, 0x4b, 0x6e, 0xda, 0x1f, + 0x05, 0x87, 0x4f, 0x41, 0xf0, 0x6d, 0x8c, 0x67, 0x3b, 0xdf, 0xb7, 0x25, 0x26, 0x2b, + 0x70, 0x67, 0xff, 0xc1, 0xd2, 0xbf, 0xa3, 0xf2, 0x74, 0x24, 0x0d, 0xb1, 0xe0, 0xb7, + 0x57, 0x59, 0x0d, 0xa5, 0xd4, 0x24, 0x7f, 0x5b, 0x9f, 0x91, 0xae, 0x18, 0x7e, 0x72, + 0xb0, 0x91, 0xb8, 0x2b, 0x18, 0x38, 0xbf, 0x4d, 0xe0, 0x80, 0xf6, 0x33, 0x18, 0x7b, + 0x55, 0xcc, 0x6b, 0x9d, 0xea, 0x59, 0x48, 0x06, 0xcd, 0x8a, 0xc5, 0x1f, 0x7a, 0xb3, + 0x91, 0xfe, 0x02, 0x91, 0x7f, 0x4e, 0x54, 0xdd, 0xb6, 0x58, 0x5f, 0x3c, 0x4e, 0xc6, + 0xf8, 0xa9, 0x84, 0x2f, 0x13, 0x36, 0x12, 0x92, 0xdd, 0xc6, 0x83, 0x6f, 0xdd, 0x23, + 0x27, 0x8f, 0x3a, 0x84, 0x17, 0x09, 0xb6, 0x6d, 0xe2, 0xee, 0x4b, 0xe4, 0x27, 0xb3, + 0x8d, 0xe1, 0x0d, 0x92, 0xa6, 0xd4, 0x9f, 0xd2, 0x9a, 0xc1, 0x06, 0x4e, 0xe8, 0x9a, + 0x35, 0xc3, 0x56, 0xdb, 0x93, 0x27, 0x8e, 0x56, 0xcd, 0x12, 0x6e, 0x9e, 0xf9, 0x72, + 0x3d, 0x50, 0x81, 0xec, 0x74, 0xf9, 0x3f, 0x59, 0x92, 0xad, 0x8f, 0x2f, 0x0d, 0xec, + 0x53, 0xa0, 0x27, 0xb3, 0x94, 0x25, 0x4f, 0x20, 0x66, 0xaf, 0x97, 0xe3, 0xbe, 0x47, + 0xbf, 0x47, 0xe6, 0x3d, 0x0f, 0x41, 0xfe, 0x9d, 0x54, 0x14, 0xf4, 0xc0, 0xe3, 0xdd, + 0xc9, 0xeb, 0xf0, 0xfa, 0xaf, 0x9d, 0xc0, 0x36, 0x61, 0xb9, 0x79, 0x28, 0xb2, 0xa6, + 0xdd, 0x7d, 0x17, 0x6e, 0x43, 0xb2, 0x14, 0xb0, 0xff, 0xee, 0x17, 0x66, 0x23, 0xb5, + 0x65, 0x42, 0xf4, 0x1d, 0x58, 0x54, 0xc6, 0x40, 0xc1, 0x40, 0x53, 0xd5, 0x9c, 0x0d, + 0x04, 0xd1, 0x75, 0x07, 0xe1, 0x73, 0xb8, 0x39, 0xae, 0x83, 0xfb, 0x4e, 0xcb, 0xdc, + 0x13, 0xa9, 0xbd, 0xd9, 0x69, 0x4a, 0x95, 0xb0, 0x7e, 0x7f, 0x30, 0xab, 0xcc, 0x2c, + 0x96, 0xae, 0x6f, 0x0e, 0x8b, 0xe5, 0x6d, 0x4c, 0x13, 0x01, 0xa5, 0x90, 0xbb, 0x52, + 0x10, 0xe6, 0x4f, 0x06, 0xf5, 0x75, 0xb7, 0x85, 0x4c, 0xec, 0x13, 0x1c, 0xe1, 0xef, + 0x6f, 0xe5, 0x81, 0x43, 0xd3, 0xc7, 0x1e, 0x7b, 0x1a, 0xd1, 0x59, 0x55, 0x60, 0xaf, + 0x5f, 0xcd, 0xe8, 0xc8, 0xdf, 0x5a, 0x7b, 0x34, 0x57, 0x48, 0xcf, 0x7c, 0x9a, 0x56, + 0xac, 0x93, 0x58, 0xdb, 0xa4, 0xb2, 0x95, 0x15, 0x91, 0x3f, 0xcf, 0x5b, 0x3a, 0x5c, + 0xbf, 0x47, 0xd5, 0xe7, 0x8b, 0xc7, 0x6a, 0x86, 0x26, 0xdb, 0xa1, 0x31, 0x6a, 0x6b, + 0x98, 0x5c, 0x2a, 0x3e, 0x7a, 0x77, 0x54, 0x01, 0xce, 0xb3, 0xa9, 0xeb, 0x62, 0xe9, + 0xa6, 0x8c, 0x3c, 0xa4, 0x86, 0x0b, 0x6c, 0xb2, 0x37, 0x96, 0xff, 0xc7, 0xbb, 0x6a, + 0xae, 0x96, 0x0a, 0xc5, 0x2a, 0xe5, 0x20, 0xf6, 0x0e, 0xd8, 0x11, 0x16, 0xcd, 0x87, + 0x39, 0x0b, 0x9f, 0x08, 0x89, 0x0a, 0xf8, 0x2c, 0xd3, 0xea, 0x8a, 0x51, 0xe5, 0x4b, + 0x19, 0xee, 0x60, 0xba, 0xa4, 0xc5, 0xa5, 0x91, 0x18, 0x76, 0x92, 0xfe, 0xab, 0x99, + 0x55, 0x0a, 0x94, 0xfd, 0x3a, 0xaa, 0xff, 0x9a, 0xda, 0x1d, 0xc3, 0x8c, 0xda, 0xe6, + 0x73, 0xc8, 0x09, 0x1a, 0xa7, 0x74, 0x19, 0x3e, 0x17, 0x94, 0x15, 0xd5, 0x50, 0x7a, + 0x1a, 0x7c, 0x95, 0xd8, 0x67, 0x49, 0xd1, 0x49, 0x82, 0xae, 0x7b, 0x20, 0xea, 0xe7, + 0xce, 0xa0, 0x77, 0x2f, 0xbb, 0x0f, 0x6b, 0xe6, 0x67, 0xb6, 0xed, 0xce, 0x5d, 0xac, + 0xaa, 0x69, 0xe9, 0xcb, 0x7a, 0x29, 0x10, 0x86, 0x4e, 0x75, 0x75, 0x51, 0x46, 0xad, + 0xe5, 0xa2, 0xb9, 0x6e, 0xae, 0xdb, 0x93, 0x5b, 0x5e, 0xe1, 0x4a, 0xd8, 0x3d, 0xd8, + 0x3c, 0x69, 0x2b, 0xd3, 0xb7, 0xb1, 0x9b, 0xbd, 0xfb, 0xc1, 0xca, 0x73, 0x46, 0x59, + 0x52, 0xd0, 0x51, 0x80, 0x06, 0x9a, 0xe8, 0x55, 0x84, 0x3a, 0x3f, 0x50, 0xef, 0xe2, + 0xc2, 0xee, 0xf8, 0x2c, 0xa2, 0x8f, 0x81, 0xb5, 0xf9, 0xab, 0xb6, 0xd9, 0xc9, 0xd3, + 0xe5, 0xe9, 0x65, 0x08, 0xbc, 0xb5, 0x0e, 0x08, 0xcf, 0x0a, 0xfb, 0xc3, 0xa2, 0x2f, + 0xbc, 0x0f, 0x97, 0x0d, 0x67, 0x44, 0xbc, 0xb4, 0xb3, 0x71, 0x82, 0x35, 0x46, 0x2b, + 0xe0, 0xb5, 0x44, 0x65, 0x7f, 0x07, 0x13, 0xc5, 0x08, 0xbf, 0x0f, 0x1f, 0x9f, 0xb6, + 0x88, 0x96, 0x31, 0xfa, 0x6f, 0xda, 0x90, 0xda, 0x8c, 0x2b, 0x83, 0x8c, 0x00, 0x7f, + 0xc4, 0xf7, 0x79, 0xa0, 0xd9, 0xaa, 0xf7, 0xd9, 0xf7, 0x27, 0xc5, 0xbb, 0xeb, 0x2f, + 0x71, 0x90, 0x22, 0xb8, 0x9c, 0x67, 0x4b, 0xd9, 0x84, 0x0c, 0xb3, 0x06, 0xdf, 0xf6, + 0x0a, 0x3c, 0xeb, 0x8b, 0x85, 0xef, 0x77, 0x65, 0x53, 0xcf, 0xbd, 0xc7, 0xa5, 0x79, + 0x6e, 0xc5, 0xa5, 0x17, 0x6a, 0xc2, 0x1b, 0x20, 0x23, 0x45, 0x5c, 0xf5, 0x32, 0x47, + 0x47, 0xf1, 0x4d, 0x65, 0xd4, 0xf6, 0xc4, 0x10, 0xc2, 0x69, 0xaf, 0x72, 0xa4, 0xc5, + 0x95, 0x91, 0xcf, 0xcd, 0x1c, 0xe7, 0x22, 0x2a, 0x32, 0x06, 0xe2, 0x4d, 0x48, 0x29, + 0x31, 0xec, 0x91, 0xee, 0x03, 0x65, 0xd4, 0x43, 0xd2, 0xe6, 0x82, 0x39, 0x0b, 0x6d, + 0x6c, 0xa8, 0x8b, 0x21, 0x7f, 0x17, 0x38, 0x1a, 0xbe, 0x2d, 0xd2, 0xec, 0x08, 0x46, + 0x63, 0xf5, 0x69, 0x87, 0xea, 0x43, 0x5f, 0x5e, 0x09, 0xf7, 0x8c, 0x61, 0x9b, 0x0b, + 0x57, 0x33, 0x7a, 0x96, 0x81, 0x5e, 0xd7, 0x47, 0xa1, 0x2c, 0x9f, 0x0a, 0x72, 0xfd, + 0xa7, 0x43, 0x94, 0x81, 0xa9, 0x6f, 0xa8, 0xb0, 0x96, 0x83, 0x67, 0x40, 0xe8, 0x10, + 0x31, 0xb7, 0xea, 0xb2, 0xdb, 0xb5, 0x79, 0x00, 0x1a, 0xb6, 0x37, 0xb6, 0xe6, 0x1b, + 0x58, 0xd1, 0xfe, 0xec, 0x01, 0x3e, 0x24, 0x2b, 0x8a, 0x1a, 0x39, 0xfb, 0x3e, 0xb3, + 0x3c, 0x7f, 0x4b, 0xd3, 0x85, 0xa9, 0x34, 0x9b, 0x3b, 0x86, 0x24, 0x05, 0x0b, 0x55, + 0x87, 0x92, 0xeb, 0x82, 0x28, 0xbb, 0x2b, 0x23, 0x79, 0x79, 0x4b, 0x85, 0xcb, 0x9e, + 0x96, 0x1b, 0x2f, 0x00, 0xd3, 0x2a, 0x92, 0x55, 0x2c, 0xbc, 0xba, 0xdd, 0x29, 0xec, + 0x41, 0xa3, 0xad, 0x54, 0xb2, 0xf5, 0xd4, 0x9e, 0x8c, 0xcb, 0x2c, 0x14, 0xe1, 0x92, + 0xd7, 0x71, 0x26, 0x50, 0xb7, 0x62, 0x97, 0x40, 0x9c, 0x62, 0x2f, 0x52, 0xa3, 0xa2, + 0x15, 0xb2, 0x70, 0xf1, 0x07, 0x96, 0xf0, 0xfd, 0x9b, 0x2b, 0x25, 0x55, 0x07, 0xb8, + 0x54, 0xc3, 0x66, 0x35, 0xd3, 0x4d, 0xef, 0x8f, 0xfd, 0xda, 0x8d, 0xfa, 0x3a, 0xae, + 0x59, 0x66, 0x42, 0x32, 0xe2, 0x65, 0xcb, 0x37, 0x0b, 0x99, 0xc3, 0x61, 0xed, 0xaa, + 0x76, 0xd7, 0x10, 0x79, 0xeb, 0xaf, 0x46, 0xe8, 0x83, 0xb4, 0x79, 0xa3, 0x05, 0xe0, + 0xc1, 0xfb, 0xf4, 0x51, 0xa2, 0x35, 0x76, 0x41, 0xbe, 0x8c, 0xaf, 0x2b, 0xcd, 0xdd, + 0xea, 0xcf, 0xbf, 0x32, 0xf2, 0x7a, 0x35, 0x3e, 0xdf, 0x1d, 0x15, 0xe5, 0xb3, 0xce, + 0xa7, 0xd1, 0xdd, 0x34, 0x27, 0x95, 0xba, 0x3d, 0x1b, 0x92, 0xe9, 0xf9, 0x17, 0xc1, + 0xc1, 0x57, 0xf5, 0x1a, 0x18, 0xe1, 0x12, 0xb3, 0x3d, 0x08, 0x0f, 0xf7, 0x14, 0xd8, + 0xf8, 0xf9, 0x2e, 0x6a, 0xff, 0x0c, 0x49, 0xa5, 0xc8, 0xad, 0x32, 0x1f, 0x4c, 0xfb, + 0x34, 0x48, 0xeb, 0x23, 0xf6, 0x1d, 0x18, 0xf2, 0x47, 0x10, 0x9e, 0xbd, 0x0e, 0x7a, + 0x25, 0xc0, 0x3d, 0x4e, 0x79, 0x1e, 0xcd, 0x66, 0xa1, 0xca, 0x1f, 0xd1, 0xbf, 0xfe, + 0xd6, 0xb9, 0x46, 0x77, 0x7a, 0x7e, 0xdb, 0xb8, 0xe0, 0x09, 0x43, 0xd8, 0x67, 0x19, + 0x39, 0xe4, 0xfa, 0x35, 0x39, 0x50, 0x2e, 0xcc, 0x62, 0x22, 0x8e, 0x92, 0xf1, 0xf2, + 0xd6, 0xce, 0x72, 0x63, 0x14, 0x46, 0xa1, 0x45, 0x73, 0xd0, 0xe8, 0xfa, 0x7a, 0xa0, + 0x31, 0x0f, 0xeb, 0x62, 0x7d, 0x61, 0xc1, 0xf2, 0x82, 0x25, 0x9d, 0xbd, 0xc9, 0xbd, + 0xa4, 0x87, 0x23, 0x34, 0x43, 0x9c, 0x5a, 0xea, 0xe6, 0xd5, 0x4c, 0x38, 0x66, 0xaf, + 0x75, 0x80, 0xe7, 0x29, 0x98, 0x22, 0x44, 0x96, 0x50, 0x9c, 0x46, 0x64, 0x9d, 0x29, + 0x45, 0xd3, 0xef, 0x19, 0x03, 0xc4, 0x03, 0x2f, 0x31, 0xbd, 0x00, 0x41, 0x9f, 0xfd, + 0xc0, 0xdf, 0x7c, 0xfc, 0x71, 0x6c, 0x9a, 0x68, 0x43, 0x9b, 0x5d, 0x39, 0x39, 0x90, + 0x07, 0x72, 0xf0, 0x3c, 0x78, 0xab, 0x91, 0xf0, 0xd6, 0xf6, 0x29, 0xf3, 0xaf, 0x9f, + 0x54, 0xeb, 0x41, 0x8d, 0x02, 0xf8, 0xfd, 0x7c, 0x0b, 0x77, 0x1a, 0xcc, 0x68, 0xd9, + 0x08, 0x5a, 0x85, 0xef, 0x0e, 0x61, 0x2f, 0x6c, 0xb3, 0xf9, 0xd0, 0xf5, 0xf0, 0x48, + 0xcf, 0x87, 0x1d, 0x10, 0x3b, 0x43, 0x35, 0x06, 0xac, 0xd8, 0x28, 0xc8, 0x93, 0xd8, + 0xa4, 0xaa, 0x26, 0x31, 0xc5, 0x03, 0xf6, 0x2c, 0xd1, 0xfb, 0x2c, 0x71, 0x5c, 0x91, + 0x73, 0x7c, 0xd2, 0xe6, 0x48, 0x2b, 0xb9, 0x89, 0xbf, 0x22, 0xd5, 0x70, 0x6e, 0x45, + 0xc2, 0xfc, 0xf6, 0x16, 0x95, 0xdd, 0x98, 0xcb, 0x2f, 0xb4, 0xd8, 0x9e, 0x47, 0x90, + 0xfd, 0x69, 0xbf, 0x13, 0xac, 0xd4, 0x2a, 0x1c, 0x00, 0x9c, 0x04, 0x57, 0x9c, 0x0b, + 0x3a, 0x0e, 0x8b, 0x3e, 0xfe, 0x8e, 0x93, 0x84, 0x7d, 0xc6, 0x96, 0xfd, 0xb1, 0x0b, + 0x7b, 0x2f, 0x4f, 0xde, 0xe9, 0xc9, 0x97, 0xe1, 0xaa, 0x7b, 0xf8, 0x6b, 0x6b, 0x49, + 0xd1, 0x59, 0x84, 0x10, 0x80, 0x08, 0xc7, 0x5d, 0x70, 0xb6, 0xc3, 0x2c, 0x84, 0xbb, + 0x7f, 0x4a, 0x37, 0x69, 0x2d, 0x23, 0x72, 0xfd, 0x07, 0x9b, 0x38, 0x0d, 0x74, 0x59, + 0x5f, 0x9e, 0x2b, 0x48, 0xb8, 0x94, 0x75, 0x55, 0x62, 0x34, 0xe9, 0x59, 0xc9, 0xd3, + 0xe4, 0x35, 0x41, 0x07, 0xed, 0x46, 0xfd, 0x04, 0xf4, 0xdb, 0x45, 0xed, 0x31, 0x69, + 0x90, 0x8a, 0x99, 0x64, 0x76, 0x4a, 0x0d, 0x0e, 0x3d, 0x90, 0x42, 0x93, 0x40, 0x7c, + 0x45, 0x15, 0xb5, 0x3e, 0x8e, 0x5e, 0x0a, 0x17, 0x20, 0x67, 0xb1, 0xd3, 0xbf, 0x8b, + 0x53, 0x87, 0x13, 0x83, 0xc3, 0x42, 0x7d, 0xf5, 0x07, 0xe3, 0x51, 0xfa, 0x46, 0xc3, + 0x6c, 0x86, 0x38, 0x18, 0x01, 0x25, 0x3f, 0xc3, 0x8d, 0xd2, 0xd0, 0x09, 0x66, 0x7c, + 0x82, 0x36, 0xe0, 0x32, 0x37, 0x5d, 0x25, 0x49, 0x13, 0x78, 0x76, 0xa9, 0xe7, 0xbc, + 0xd9, 0xc2, 0x4e, 0x02, 0xfe, 0x56, 0x0c, 0xe1, 0x04, 0x57, 0x07, 0xb9, 0xa6, 0xd1, + 0x9f, 0x69, 0xcf, 0x8a, 0xb5, 0x49, 0xf5, 0x70, 0x0c, 0xa7, 0xc5, 0x52, 0x25, 0x23, + 0xf5, 0xf5, 0x23, 0xd6, 0xd8, 0x5f, 0x7d, 0x28, 0xfb, 0xe8, 0xc1, 0x0a, 0x44, 0x57, + 0x00, 0x23, 0xd3, 0xea, 0x20, 0xe5, 0xff, 0x4e, 0xe7, 0xf8, 0x43, 0xc2, 0x48, 0xa8, + 0x27, 0x70, 0xd9, 0x8d, 0xb8, 0x96, 0x78, 0xfd, 0x64, 0x97, 0x7a, 0xa6, 0x64, 0x5e, + 0x21, 0x98, 0x2e, 0x20, 0x76, 0x38, 0x12, 0x97, 0xa2, 0x50, 0xc0, 0x93, 0x9f, 0xb7, + 0x51, 0x0d, 0xea, 0xb0, 0x64, 0x2c, 0x09, 0x92, 0x34, 0x32, 0xa9, 0x6b, 0xb6, 0x61, + 0xeb, 0xd6, 0xc2, 0x48, 0x8a, 0xf5, 0x24, 0x42, 0x35, 0xf6, 0xf9, 0x38, 0x4d, 0x02, + 0x9c, 0x2b, 0xa5, 0x3c, 0x5b, 0x2e, 0x15, 0x7f, 0x0d, 0x0c, 0x2c, 0x90, 0xd4, 0x74, + 0x4e, 0x8a, 0x7d, 0x64, 0x31, 0x55, 0xa0, 0x46, 0xa3, 0x88, 0xe7, 0xa9, 0x4d, 0x2d, + 0x70, 0x63, 0x37, 0x56, 0x9d, 0xf9, 0x2f, 0xef, 0xf6, 0xab, 0x54, 0xb2, 0x80, 0xf0, + 0x16, 0xb7, 0xf2, 0xb7, 0x5e, 0x47, 0x20, 0x7f, 0x0b, 0x3e, 0xf5, 0x33, 0x88, 0x6d, + 0x09, 0xc6, 0x26, 0x22, 0xdd, 0x46, 0x3c, 0x7c, 0xb4, 0xf9, 0x54, 0xae, 0xe6, 0x28, + 0x90, 0xa3, 0xcd, 0xef, 0xfc, 0x0a, 0xd7, 0x8c, 0xad, 0x99, 0xe1, 0xb0, 0xcd, 0x95, + 0xf5, 0x0c, 0x87, 0x77, 0x21, 0xeb, 0x49, 0x5b, 0x3e, 0xb9, 0x3e, 0xf0, 0x4f, 0x43, + 0xbe, 0x7e, 0x5b, 0xb2, 0x03, 0xce, 0xb6, 0x2a, 0x6b, 0x46, 0xcb, 0x94, 0xa0, 0xa1, + 0xd8, 0xb5, 0x0f, 0xf6, 0x1f, 0x40, 0x84, 0x0a, 0x6e, 0x58, 0x6a, 0xd4, 0xce, 0x58, + 0x30, 0x14, 0xfa, 0x06, 0x5b, 0xb6, 0xb5, 0xc1, 0x60, 0x93, 0x37, 0xd8, 0xd1, 0x6f, + 0x89, 0x6b, 0x2d, 0x8a, 0x94, 0xc6, 0xf9, 0x8e, 0xc5, 0x7e, 0x04, 0x72, 0x10, 0xc1, + 0x20, 0x64, 0x71, 0x75, 0x85, 0xde, 0x4d, 0x73, 0x02, 0xa4, 0xe5, 0x69, 0x37, 0x12, + 0x24, 0x69, 0xfb, 0x8f, 0x6d, 0xb0, 0x72, 0xfd, 0x51, 0x1e, 0x2d, 0x05, 0xc3, 0x9c, + 0x6d, 0x94, 0x78, 0x80, 0xb4, 0xc1, 0x00, 0x43, 0xfa, 0x19, 0xdd, 0x2e, 0x29, 0x8d, + 0x5d, 0xb0, 0x78, 0x90, 0xde, 0x5b, 0x8a, 0x8f, 0xd6, 0x3d, 0xd1, 0x02, 0x10, 0xd5, + 0x5e, 0x69, 0xd4, 0x5d, 0xd2, 0x0f, 0xbf, 0x60, 0x3a, 0x0f, 0x08, 0xe2, 0xe1, 0x63, + 0x55, 0x10, 0xa8, 0xfb, 0xed, 0x7c, 0x35, 0xeb, 0xaa, 0x81, 0x99, 0x31, 0xc5, 0x62, + 0x16, 0x18, 0x38, 0x19, 0x63, 0xcf, 0x96, 0x7a, 0xd0, 0x50, 0x2a, 0xc3, 0x7b, 0x0e, + 0x50, 0x1f, 0x9a, 0x5d, 0xda, 0x81, 0xd0, 0x7b, 0x62, 0x3c, 0x01, 0x52, 0x31, 0x95, + 0xd5, 0xeb, 0x8e, 0x85, 0x20, 0x1e, 0x61, 0x35, 0x03, 0x6a, 0xd4, 0x7c, 0xcd, 0xa0, + 0xc6, 0x62, 0x04, 0x8a, 0xcc, 0x4e, 0xe6, 0x35, 0xbd, 0x2b, 0x42, 0xbd, 0xa6, 0x82, + 0xf9, 0xd8, 0x20, 0x29, 0x6b, 0x92, 0x6d, 0xeb, 0x65, 0xee, 0x79, 0x13, 0x93, 0xc2, + 0x77, 0x2b, 0xf7, 0xf9, 0x6e, 0x4a, 0xf9, 0x8c, 0x81, 0xc7, 0x6c, 0x9f, 0xd1, 0x2d, + 0x63, 0xb4, 0x03, 0x1c, 0xdc, 0x5d, 0x58, 0xc1, 0xfc, 0x58, 0x32, 0x10, 0xeb, 0x3f, + 0xf6, 0xa7, 0xd4, 0x8f, 0x85, 0x5a, 0x77, 0xb8, 0x28, 0x75, 0xe2, 0x64, 0x64, 0x41, + 0x71, 0xd9, 0xf6, 0x07, 0xcf, 0x2c, 0x81, 0x58, 0x6f, 0x34, 0x9c, 0x12, 0x10, 0x39, + 0x7f, 0xf0, 0xec, 0x0c, 0x0f, 0x55, 0xd2, 0x2a, 0x6b, 0x85, 0x15, 0xb0, 0xce, 0xba, + 0xba, 0x6d, 0x5e, 0x8f, 0xd2, 0xcc, 0xa5, 0xbf, 0x2f, 0xa2, 0xb0, 0xc8, 0xa4, 0x73, + 0x88, 0x70, 0x8d, 0xec, 0xec, 0xc6, 0x25, 0x53, 0x82, 0x8f, 0xf1, 0x46, 0xee, 0x3a, + 0x83, 0x66, 0x4b, 0x66, 0x0f, 0xe8, 0x83, 0x87, 0x3d, 0x3e, 0xcb, 0xc7, 0xf9, 0x07, + 0x9b, 0x84, 0xb7, 0xf6, 0xb0, 0xe9, 0x53, 0x85, 0x5d, 0x4a, 0xb4, 0xe6, 0x2c, 0x6c, + 0x51, 0xb5, 0x68, 0x9f, 0xc4, 0x5f, 0x41, 0x7b, 0xb2, 0x92, 0xf5, 0xb7, 0x08, 0x84, + 0x2b, 0x8a, 0xda, 0x48, 0x88, 0xc1, 0x91, 0xcd, 0x50, 0xf5, 0xee, 0x94, 0xf5, 0x3e, + 0xc2, 0x0b, 0x3e, 0x59, 0x73, 0x9c, 0xd3, 0x4d, 0x46, 0xdd, 0xfb, 0xae, 0xd9, 0x2a, + 0xdb, 0x5b, 0xfc, 0x16, 0x39, 0x9b, 0x38, 0x87, 0x1b, 0xed, 0x33, 0x92, 0xa8, 0x6b, + 0x89, 0xb1, 0x42, 0xc0, 0x65, 0x9e, 0xe3, 0xc0, 0x4a, 0xff, 0x8f, 0x6a, 0x00, 0xe4, + 0x7e, 0x76, 0x65, 0x2f, 0x22, 0x70, 0xb4, 0xb0, 0x06, 0x89, 0x56, 0x5a, 0x9f, 0xfc, + 0xfa, 0x79, 0x04, 0x4d, 0x6d, 0x9b, 0x72, 0x11, 0x81, 0x81, 0x7a, 0x82, 0x85, 0x05, + 0x95, 0x4a, 0x40, 0x19, 0x6b, 0x2e, 0x1e, 0x71, 0xc5, 0x47, 0xa1, 0x64, 0x11, 0xf6, + 0xa7, 0xfa, 0xc8, 0xa8, 0x2c, 0x25, 0xc3, 0x81, 0x3f, 0x4e, 0x37, 0xe5, 0x33, 0xc4, + 0xa5, 0xc1, 0xed, 0xba, 0x10, 0xaf, 0x6b, 0x9f, 0x21, 0x0c, 0x65, 0x5e, 0x76, 0x27, + 0xb0, 0x63, 0x41, 0x7d, 0xe3, 0x4c, 0x34, 0x8b, 0x6c, 0x04, 0xf5, 0x81, 0x6b, 0xe1, + 0xe5, 0x6c, 0x37, 0x18, 0x87, 0x7f, 0x3e, 0xf0, 0x25, 0x39, 0xe6, 0xf6, 0x66, 0xa2, + 0xbd, 0x4f, 0xfd, 0xbe, 0x0c, 0xab, 0xf7, 0x72, 0x04, 0xd8, 0xfd, 0xbb, 0x55, 0x7f, + 0xdf, 0x68, 0x49, 0x7f, 0x61, 0x08, 0xfb, 0x7b, 0xf3, 0x3f, 0x8a, 0x1a, 0x23, 0x5f, + 0xb3, 0xdd, 0x1c, 0x04, 0xaa, 0x2c, 0xeb, 0x85, 0x20, 0xd3, 0x1a, 0x3a, 0xe4, 0x36, + 0x27, 0xc0, 0x61, 0x00, 0xf6, 0xab, 0x25, 0xc7, 0x4e, 0x2e, 0x6d, 0x78, 0xb8, 0x69, + 0x32, 0xf3, 0xac, 0x51, 0x17, 0x09, 0x80, 0x90, 0x02, 0x1e, 0x8b, 0x81, 0xe9, 0xda, + 0x2e, 0x52, 0x8a, 0xbd, 0xb6, 0xc3, 0x05, 0xff, 0x0a, 0x54, 0xef, 0x9b, 0x3d, 0xb9, + 0x2a, 0xbc, 0x61, 0x2c, 0x8b, 0xb4, 0x5d, 0x74, 0xb8, 0xda, 0xb5, 0x69, 0x67, 0x24, + 0x2d, 0xd3, 0x50, 0x17, 0x08, 0xf2, 0xe0, 0x03, 0x18, 0xc3, 0x8b, 0x1e, 0x64, 0x22, + 0xfe, 0x87, 0x65, 0x04, 0xb4, 0x5d, 0x54, 0x52, 0xd8, 0xad, 0x2c, 0x86, 0x7a, 0xab, + 0x1e, 0xfc, 0xa1, 0xe6, 0xc7, 0x5c, 0xb4, 0xbd, 0x6f, 0x7d, 0xf1, 0x5b, 0xae, 0x9c, + 0x18, 0x07, 0xd5, 0xa8, 0x20, 0xe5, 0x54, 0xe0, 0x70, 0x9f, 0x00, 0x37, 0x88, 0xb0, + 0x2b, 0x25, 0x5e, 0x7b, 0x6e, 0xcf, 0x22, 0x72, 0x2f, 0xf2, 0x9d, 0x20, 0x5b, 0x09, + 0xf2, 0x28, 0x8a, 0x59, 0xbc, 0x5f, 0x54, 0x34, 0xe7, 0x2b, 0x30, 0x37, 0xcc, 0xdc, + 0x5b, 0x1a, 0x27, 0x8d, 0x2b, 0x0b, 0x11, 0xb7, 0xc0, 0xa8, 0xe5, 0x9b, 0xb1, 0xdc, + 0x3c, 0x37, 0x1f, 0xee, 0x2a, 0x1f, 0x6c, 0x8f, 0x44, 0xfd, 0xb5, 0x78, 0xfa, 0x2b, + 0x7d, 0x9d, 0xb2, 0xc0, 0x1e, 0xab, 0xed, 0x6b, 0xa2, 0x10, 0x7e, 0xed, 0x5a, 0xc3, + 0xeb, 0x68, 0xcd, 0xa1, 0x87, 0xf8, 0x00, 0x66, 0x13, 0x94, 0x0e, 0x6a, 0xcf, 0x90, + 0x16, 0x4a, 0x7f, 0xca, 0xaa, 0xa2, 0xa6, 0x07, 0xd9, 0x86, 0x9e, 0xf8, 0x83, 0x59, + 0xad, 0x07, 0xed, 0x84, 0xe1, 0xba, 0xb7, 0xbf, 0x1e, 0x4d, 0x31, 0x16, 0xeb, 0x15, + 0x57, 0xd2, 0x2f, 0xb5, 0x50, 0xbd, 0x57, 0xcf, 0xe3, 0x52, 0xa5, 0x16, 0x7e, 0x13, + 0xe9, 0xb6, 0xe0, 0xf5, 0x43, 0x93, 0x3f, 0xf0, 0x5f, 0x3e, 0x0d, 0x89, 0x6b, 0xaf, + 0x33, 0x0c, 0xa2, 0x4d, 0x95, 0xf3, 0xcc, 0x55, 0xf6, 0x04, 0x5d, 0x50, 0x04, 0xbf, + 0x8a, 0x3c, 0xa7, 0x64, 0xd1, 0xa7, 0x46, 0x53, 0xe7, 0x10, 0x76, 0xaf, 0xb8, 0xf8, + 0xb1, 0x93, 0x83, 0x6d, 0x58, 0x66, 0x7f, 0xf8, 0x79, 0xd3, 0xfb, 0x44, 0xb5, 0xb1, + 0xeb, 0x62, 0x51, 0x17, 0x4a, 0x22, 0xf0, 0xa4, 0x6b, 0xf3, 0xa9, 0x5d, 0x61, 0xab, + 0xd9, 0x6d, 0xa1, 0x4d, 0xa8, 0x15, 0x1c, 0xff, 0x03, 0x58, 0x04, 0x70, 0x8b, 0x15, + 0x78, 0x60, 0xa1, 0x50, 0x3f, 0x75, 0x29, 0xfc, 0x13, 0xe9, 0x7d, 0xb6, 0xc8, 0x67, + 0x3f, 0xde, 0x23, 0x0a, 0x44, 0x02, 0xfa, 0x53, 0x8f, 0xb9, 0x23, 0x95, 0x6a, 0x0a, + 0x87, 0xe5, 0x2f, 0x3a, 0x6a, 0xf6, 0xd8, 0xb4, 0xcf, 0x3a, 0xa7, 0x02, 0xa1, 0x07, + 0x88, 0xea, 0x15, 0x41, 0xe4, 0xc4, 0x78, 0x11, 0xdf, 0x79, 0x58, 0x0a, 0x8e, 0x93, + 0xdf, 0x84, 0x07, 0x23, 0xe6, 0x41, 0x72, 0xb0, 0xe8, 0x41, 0xd0, 0x50, 0x98, 0x4d, + 0x11, 0xaa, 0x11, 0xd8, 0x19, 0xa8, 0x4b, 0x14, 0xaf, 0xd8, 0x36, 0x1e, 0xa8, 0x48, + 0x19, 0xbb, 0xde, 0xf6, 0xf5, 0x6f, 0xf5, 0x88, 0xde, 0x94, 0x45, 0x14, 0x18, 0x2c, + 0x05, 0x1e, 0xe9, 0x26, 0x66, 0x93, 0x02, 0xfa, 0x91, 0x5c, 0xa9, 0x8d, 0xb9, 0x0d, + 0x30, 0xe8, 0xde, 0x40, 0x2a, 0x52, 0x91, 0x74, 0x7a, 0x35, 0x77, 0xae, 0x51, 0x84, + 0x68, 0xe1, 0xa8, 0x8c, 0x07, 0x74, 0x97, 0xa9, 0xc5, 0xf8, 0x71, 0xc6, 0xe0, 0xb0, + 0xe6, 0x6b, 0x87, 0xa6, 0xd4, 0x20, 0x48, 0x93, 0xcf, 0x2a, 0x4d, 0xde, 0x4a, 0xd7, + 0x91, 0x51, 0x6f, 0xb5, 0x1f, 0x74, 0x9f, 0xd8, 0xbe, 0x31, 0x3c, 0x3b, 0x4d, 0x3b, + 0xe8, 0x86, 0xb2, 0xcd, 0xb2, 0x11, 0x4c, 0x65, 0x84, 0x10, 0x21, 0x70, 0x59, 0xff, + 0xf4, 0xed, 0x34, 0x0e, 0x9a, 0xa6, 0x15, 0xf7, 0xe9, 0xde, 0xb8, 0x2b, 0x80, 0x3c, + 0xac, 0x41, 0x4c, 0x97, 0xda, 0x84, 0x50, 0x51, 0x79, 0xaf, 0x96, 0x78, 0xed, 0x70, + 0xc9, 0xd9, 0xd8, 0x81, 0xa8, 0x84, 0xf2, 0x76, 0x1b, 0x90, 0xa7, 0xc3, 0x1e, 0x1c, + 0x45, 0x26, 0x5a, 0xbe, 0x95, 0x3b, 0x4a, 0xc5, 0xb5, 0xbc, 0x45, 0x13, 0x21, 0x6c, + 0x39, 0x6a, 0xfe, 0xae, 0x2f, 0x14, 0x02, 0x50, 0x5e, 0x1f, 0xbf, 0x84, 0xde, 0x6a, + 0xfb, 0x8d, 0x9a, 0x3d, 0x83, 0x01, 0x47, 0xc0, 0xc1, 0x18, 0x07, 0xce, 0xe5, 0x84, + 0x7d, 0xf5, 0x9f, 0x25, 0x95, 0xb5, 0x4a, 0x7a, 0x51, 0x63, 0xdb, 0x25, 0x7f, 0xae, + 0x16, 0x89, 0x33, 0x14, 0x71, 0x02, 0x97, 0xfc, 0xa3, 0xbf, 0x1a, 0x50, 0x6d, 0x37, + 0x4a, 0xa7, 0xca, 0x19, 0x48, 0x70, 0xe5, 0x33, 0xa5, 0x33, 0xa9, 0xde, 0xb8, 0xa2, + 0x91, 0xf2, 0x30, 0xd3, 0xde, 0x4e, 0xe0, 0x3d, 0x09, 0xac, 0x50, 0x6f, 0xb7, 0x57, + 0x81, 0x7e, 0x52, 0xa5, 0x48, 0x5b, 0x3f, 0xdb, 0x33, 0x90, 0xcf, 0xe4, 0xdc, 0xc3, + 0xbd, 0x0e, 0xb7, 0x44, 0x02, 0x3f, 0xc4, 0xbf, 0xc7, 0x1d, 0xf6, 0xd5, 0x2b, 0xa9, + 0x39, 0xd6, 0x4c, 0x10, 0x31, 0x14, 0xb9, 0x51, 0x75, 0x4f, 0x75, 0xdb, 0xfb, 0x5b, + 0x2d, 0x6f, 0xbb, 0x06, 0x19, 0xd0, 0xac, 0x82, 0xc2, 0x4e, 0x2c, 0xeb, 0xe3, 0x47, + 0x18, 0x43, 0x6b, 0x28, 0x03, 0x23, 0x7d, 0x8d, 0x4b, 0x91, 0xb0, 0x26, 0xb3, 0x0b, + 0x40, 0x30, 0xb9, 0xc5, 0x34, 0xdc, 0xed, 0x7d, 0xea, 0x64, 0x93, 0x69, 0xb2, 0x0a, + 0xa6, 0x7f, 0x1f, 0x45, 0xd6, 0x0b, 0x45, 0x7b, 0x84, 0xc2, 0xf5, 0xf9, 0x7f, 0x17, + 0xc6, 0x3f, 0xff, 0xfe, 0x0c, 0xd0, 0xeb, 0xff, 0xb9, 0x3b, 0x06, 0xce, 0x3c, 0xe8, + 0xa8, 0x4f, 0x6b, 0xd7, 0xdc, 0x53, 0x35, 0xbf, 0xe6, 0x2f, 0xf0, 0xb3, 0x56, 0x91, + 0x93, 0x1e, 0x70, 0x88, 0xcb, 0x35, 0x36, 0xec, 0x81, 0xc8, 0xd4, 0x4d, 0xe7, 0xa8, + 0xf6, 0x1d, 0x3b, 0xf9, 0x7b, 0xa3, 0x5b, 0x43, 0xdb, 0xa7, 0x48, 0xf7, 0x39, 0x7d, + 0x0c, 0xe8, 0x16, 0x0c, 0xb1, 0x81, 0x31, 0xe2, 0xd0, 0xaa, 0x72, 0x34, 0x96, 0x67, + 0x8a, 0xe4, 0x6a, 0x97, 0x4d, 0xca, 0xc5, 0xcb, 0x89, 0x12, 0x6c, 0x42, 0x23, 0x12, + 0xc3, 0x81, 0xbb, 0xb4, 0xa0, 0x0d, 0xed, 0x95, 0x3e, 0x1c, 0x64, 0x69, 0xc4, 0x7f, + 0xec, 0x44, 0xf0, 0x36, 0xd6, 0x89, 0x9c, 0xd8, 0x6d, 0x0a, 0x7e, 0xfc, 0x35, 0xc8, + 0xa4, 0xbc, 0x3c, 0x13, 0x17, 0x13, 0xe4, 0xe0, 0x1e, 0x40, 0x2b, 0x27, 0xe3, 0x50, + 0x3b, 0xe0, 0xf1, 0xf3, 0x7b, 0x07, 0x07, 0xd7, 0xe2, 0x72, 0xcf, 0x89, 0xa3, 0xe3, + 0xb7, 0xda, 0x24, 0x13, 0x51, 0x2e, 0xd8, 0x9e, 0xa0, 0xe6, 0x64, 0x5f, 0x55, 0xf4, + 0x0a, 0xf9, 0x13, 0x66, 0xb8, 0x39, 0x2c, 0x52, 0xa8, 0x4f, 0xa9, 0x1b, 0xbe, 0x20, + 0xc5, 0x6d, 0x94, 0x1a, 0x9a, 0x7d, 0xb5, 0x64, 0x41, 0xa4, 0xc9, 0x88, 0xc6, 0xa0, + 0x91, 0x4b, 0xab, 0xe3, 0x81, 0x7c, 0x46, 0xea, 0xa6, 0x22, 0x05, 0x32, 0xa9, 0x17, + 0x85, 0xe2, 0xd5, 0xcc, 0x7b, 0x73, 0xce, 0x26, 0x22, 0x08, 0xbd, 0x7e, 0x6d, 0x76, + 0xfa, 0x9a, 0x26, 0x2b, 0x7b, 0xc3, 0x81, 0xd3, 0x9a, 0x36, 0x9c, 0xf4, 0xb7, 0xfd, + 0x01, 0x7f, 0xf6, 0x78, 0x49, 0x8b, 0xad, 0xe1, 0x2a, 0x8c, 0xe6, 0x2f, 0x18, 0xc1, + 0xd9, 0x8b, 0xf0, 0x08, 0xfa, 0x2d, 0xe4, 0xd4, 0x13, 0xe7, 0xa3, 0x95, 0x92, 0x35, + 0xaa, 0xc8, 0xd7, 0x99, 0x19, 0xeb, 0x83, 0x8b, 0xd6, 0x3f, 0x58, 0xb9, 0x9b, 0x4e, + 0xae, 0x1f, 0x85, 0x6e, 0x8c, 0x08, 0x4c, 0x3c, 0x2b, 0x6e, 0xf3, 0x65, 0xa7, 0x44, + 0x52, 0xde, 0x5a, 0xdd, 0x01, 0x25, 0x88, 0x61, 0x75, 0x79, 0x39, 0xfd, 0x60, 0x9a, + 0xa9, 0xdf, 0x6c, 0x8f, 0xd3, 0xcb, 0xe8, 0x8d, 0x0f, 0x3c, 0x08, 0x58, 0xf9, 0xd7, + 0x33, 0x52, 0x7e, 0x4c, 0x50, 0xf7, 0x1d, 0x75, 0x5f, 0xa5, 0xc3, 0x39, 0xa4, 0xfe, + 0xce, 0xf9, 0x9c, 0xa7, 0x47, 0x8a, 0x5b, 0xd5, 0xb5, 0xb1, 0xc8, 0xdd, 0xb4, 0x46, + 0xe0, 0xc7, 0x6d, 0x3b, 0x83, 0x49, 0x59, 0x10, 0x8f, 0x93, 0xb2, 0x52, 0xc5, 0x4a, + 0xfb, 0x35, 0x8d, 0xaa, 0xbc, 0xef, 0xe0, 0x72, 0x56, 0x89, 0x37, 0x1e, 0x9e, 0xf8, + 0x27, 0x53, 0x4c, 0xbb, 0xc7, 0xb1, 0xb7, 0xaa, 0xf1, 0xa4, 0x48, 0x87, 0x49, 0xe1, + 0x1a, 0xd4, 0x53, 0xc7, 0xa1, 0xc5, 0x15, 0xc0, 0x17, 0x8c, 0xc2, 0xeb, 0xef, 0xe0, + 0x73, 0xe7, 0x8e, 0x48, 0x83, 0x6e, 0x8d, 0xc4, 0x7d, 0xb2, 0x09, 0x43, 0xf4, 0xc3, + 0xd9, 0xc5, 0x0f, 0xdb, 0xfe, 0xdc, 0xb0, 0x94, 0xe2, 0xc3, 0xde, 0xe1, 0xc6, 0x07, + 0x45, 0xf5, 0x45, 0x7c, 0x47, 0x5f, 0x8e, 0x8f, 0x3b, 0xd4, 0x04, 0x72, 0x6e, 0xf6, + 0x6f, 0x00, 0x10, 0x8f, 0xce, 0x52, 0x30, 0x41, 0x57, 0x26, 0xc7, 0xa1, 0x25, 0x78, + 0x86, 0x4c, 0x41, 0x0f, 0x3f, 0x70, 0x26, 0xc0, 0x6a, 0xbb, 0x35, 0xb8, 0x4b, 0xeb, + 0x2f, 0x44, 0x30, 0x4e, 0xb1, 0x15, 0xee, 0xf2, 0xc1, 0xf9, 0x57, 0x78, 0xe4, 0xd9, + 0x60, 0x61, 0x87, 0xd6, 0x63, 0x94, 0xdc, 0x7c, 0x7e, 0xc1, 0x46, 0xb7, 0x1a, 0x0d, + 0x13, 0xc8, 0x43, 0x3e, 0x94, 0x19, 0xf3, 0xb1, 0x3e, 0x7f, 0xcf, 0xd3, 0x2b, 0x3e, + 0xf6, 0xc9, 0xc6, 0x39, 0xd1, 0x65, 0x85, 0x0b, 0xe7, 0x4d, 0xe4, 0x8a, 0x0d, 0x61, + 0x0e, 0x88, 0xc7, 0x3c, 0xfe, 0xf8, 0x53, 0x0a, 0x52, 0x27, 0x51, 0xba, 0x67, 0xec, + 0xba, 0x2a, 0xc0, 0x53, 0xdf, 0xe8, 0xd7, 0x5f, 0x81, 0x72, 0xbb, 0x25, 0x2f, 0x47, + 0xb4, 0x97, 0x37, 0x6c, 0xca, 0x04, 0x2f, 0x7b, 0x1d, 0xa1, 0x7b, 0x2c, 0xcd, 0x2f, + 0xc8, 0x80, 0xf4, 0x1d, 0x2c, 0xbd, 0x1a, 0x30, 0x71, 0x31, 0x53, 0xb8, 0xad, 0x56, + 0xf4, 0x3f, 0xb5, 0xb6, 0x9a, 0x18, 0x2c, 0xbd, 0x7b, 0x36, 0x84, 0xf7, 0xc5, 0xd9, + 0x9b, 0x41, 0xb7, 0x2d, 0x51, 0x79, 0x84, 0xd2, 0xa6, 0xe2, 0x58, 0x2b, 0xe9, 0x76, + 0xdc, 0xdb, 0x45, 0x2e, 0x57, 0xc2, 0xfd, 0x5f, 0x4b, 0x5d, 0x6f, 0x84, 0x8f, 0xd3, + 0xf4, 0x84, 0x00, 0xa6, 0xc7, 0xae, 0xed, 0x6d, 0xc5, 0x4c, 0x5e, 0x3d, 0xc7, 0xf4, + 0xaf, 0x15, 0x1b, 0xca, 0x48, 0x86, 0xb9, 0x5d, 0x6a, 0x56, 0xe1, 0x9d, 0x20, 0x80, + 0x54, 0x43, 0x59, 0x35, 0x6b, 0x76, 0xa9, 0x27, 0x59, 0x27, 0x47, 0x30, 0x45, 0x45, + 0xc5, 0x62, 0x64, 0x41, 0xa1, 0x73, 0xe2, 0xfa, 0x0f, 0x6b, 0x87, 0x8e, 0x09, 0xac, + 0x80, 0x9e, 0xf4, 0x55, 0xae, 0xd1, 0x4a, 0xb6, 0x05, 0x4d, 0xaf, 0xab, 0x94, 0x45, + 0x95, 0xfe, 0x13, 0x27, 0x03, 0x79, 0x23, 0x40, 0x97, 0x25, 0x34, 0x41, 0xc0, 0x1c, + 0x25, 0x78, 0x56, 0xa2, 0x35, 0x5e, 0x90, 0xb1, 0x6f, 0x27, 0x76, 0xeb, 0xc7, 0x42, + 0xe7, 0x90, 0x4d, 0x00, 0x9d, 0xf5, 0xe6, 0xa0, 0x38, 0x6d, 0xac, 0x3a, 0xd7, 0xc0, + 0x3d, 0x2a, 0x51, 0x1d, 0xa1, 0x93, 0x41, 0xdd, 0x6c, 0x43, 0x3d, 0x67, 0x92, 0xf7, + 0x5b, 0x3f, 0x55, 0x09, 0x8c, 0x3a, 0x49, 0xc5, 0x93, 0x1e, 0xd9, 0xf5, 0xc5, 0x5b, + 0xf6, 0x47, 0x01, 0x9a, 0x4b, 0x33, 0xc0, 0x5f, 0x2c, 0xc2, 0x21, 0xde, 0xb8, 0x3a, + 0x70, 0xa3, 0x8a, 0x12, 0x20, 0x56, 0xe1, 0x14, 0x65, 0xf8, 0xb1, 0x12, 0x57, 0x65, + 0x22, 0xf1, 0xda, 0x6a, 0x6f, 0x0a, 0xda, 0xfd, 0xb7, 0x2b, 0x2d, 0xfc, 0x9a, 0x5d, + 0x36, 0x6a, 0x88, 0xcf, 0x15, 0xf2, 0x5f, 0x28, 0x7b, 0x44, 0x12, 0xab, 0x74, 0xd1, + 0x2d, 0x87, 0xc9, 0x15, 0x41, 0xb7, 0xf7, 0x5c, 0xb2, 0x92, 0xff, 0x5b, 0x22, 0x78, + 0x3d, 0xfa, 0xac, 0x15, 0x82, 0x69, 0xd2, 0xa9, 0x45, 0xf3, 0xc2, 0x64, 0x63, 0x57, + 0x28, 0x35, 0x0a, 0x82, 0xc6, 0x6d, 0x1a, 0xea, 0x36, 0x6a, 0xbd, 0xbf, 0xa1, 0xda, + 0x77, 0x98, 0x99, 0x23, 0x99, 0x63, 0x4c, 0xf6, 0xad, 0x17, 0x2a, 0xd7, 0x94, 0x91, + 0x99, 0xc1, 0xc2, 0x56, 0x19, 0x84, 0x67, 0xd2, 0xb8, 0x24, 0x58, 0x57, 0x44, 0x64, + 0x45, 0xbd, 0x2a, 0x47, 0xd3, 0xf6, 0x1f, 0xf2, 0x0f, 0x35, 0x6f, 0x62, 0xc1, 0xe0, + 0x86, 0x3a, 0xda, 0xfa, 0x32, 0x2d, 0x38, 0xc4, 0x14, 0x9c, 0x66, 0xdf, 0x6b, 0x9b, + 0x53, 0x2b, 0x09, 0x97, 0x64, 0x30, 0x96, 0xa9, 0x94, 0x08, 0x5f, 0xfe, 0x16, 0xe6, + 0x71, 0x17, 0xc6, 0xaf, 0x3a, 0xab, 0x65, 0x4a, 0x0e, 0x87, 0x98, 0x31, + ], + }, + ] +} From 5623e02a7c1d8ccf5693be724bec6ee2a14bfe2b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 26 Jan 2021 22:48:11 +0000 Subject: [PATCH 0030/2028] Migrate to ff 0.9 et al. --- components/zcash_note_encryption/Cargo.toml | 8 ++++---- zcash_client_backend/Cargo.toml | 13 ++++++------- zcash_client_sqlite/Cargo.toml | 9 ++++----- zcash_extensions/Cargo.toml | 6 +++--- zcash_primitives/Cargo.toml | 14 +++++++------- zcash_proofs/Cargo.toml | 14 +++++++------- zcash_proofs/src/circuit/ecc.rs | 8 ++++---- zcash_proofs/src/circuit/sapling.rs | 20 ++++++++++++++------ 8 files changed, 49 insertions(+), 43 deletions(-) diff --git a/components/zcash_note_encryption/Cargo.toml b/components/zcash_note_encryption/Cargo.toml index bed67dafee..5ddfac7c3c 100644 --- a/components/zcash_note_encryption/Cargo.toml +++ b/components/zcash_note_encryption/Cargo.toml @@ -15,11 +15,11 @@ edition = "2018" blake2b_simd = "0.5" byteorder = "1" crypto_api_chachapoly = "0.4" -ff = "0.8" -group = "0.8" -rand_core = "0.5.1" +ff = "0.9" +group = "0.9" +rand_core = "0.6" subtle = "2.2.3" [dev-dependencies] zcash_primitives = { version = "0.5", path = "../../zcash_primitives" } -jubjub = "0.5.1" +jubjub = "0.6" diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index da0518aa7b..ef1e450a1f 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -14,18 +14,18 @@ edition = "2018" [dependencies] bech32 = "0.8" -bls12_381 = "0.3.1" +bls12_381 = "0.4" bs58 = { version = "0.4", features = ["check"] } base64 = "0.13" -ff = "0.8" -group = "0.8" +ff = "0.9" +group = "0.9" hex = "0.4" -jubjub = "0.5.1" +jubjub = "0.6" nom = "6.1" percent-encoding = "2.1.0" proptest = { version = "0.10.1", optional = true } protobuf = "2.20" -rand_core = "0.5.1" +rand_core = "0.6" subtle = "2.2.3" time = "0.2" zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" } @@ -36,8 +36,7 @@ protobuf-codegen-pure = "2.20" [dev-dependencies] gumdrop = "0.8" -rand_core = "0.5.1" -rand_xorshift = "0.2" +rand_xorshift = "0.3" tempfile = "3.1.0" zcash_client_sqlite = { version = "0.3", path = "../zcash_client_sqlite" } zcash_proofs = { version = "0.5", path = "../zcash_proofs" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 5e1efe0f63..9aabe07a5b 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -15,18 +15,17 @@ edition = "2018" [dependencies] bech32 = "0.8" bs58 = { version = "0.4", features = ["check"] } -ff = "0.8" -group = "0.8" -jubjub = "0.5.1" +ff = "0.9" +group = "0.9" +jubjub = "0.6" protobuf = "2.20" -rand_core = "0.5.1" +rand_core = "0.6" rusqlite = { version = "0.24", features = ["bundled", "time"] } time = "0.2" zcash_client_backend = { version = "0.5", path = "../zcash_client_backend" } zcash_primitives = { version = "0.5", path = "../zcash_primitives" } [dev-dependencies] -rand_core = "0.5.1" tempfile = "3" zcash_proofs = { version = "0.5", path = "../zcash_proofs" } diff --git a/zcash_extensions/Cargo.toml b/zcash_extensions/Cargo.toml index c15fb51d66..30790f09ad 100644 --- a/zcash_extensions/Cargo.toml +++ b/zcash_extensions/Cargo.toml @@ -13,7 +13,7 @@ blake2b_simd = "0.5" zcash_primitives = { version = "0.5", path = "../zcash_primitives", features = ["zfuture"] } [dev-dependencies] -ff = "0.8" -jubjub = "0.5.1" -rand_core = "0.5.1" +ff = "0.9" +jubjub = "0.6" +rand_core = "0.6" zcash_proofs = { version = "0.5", path = "../zcash_proofs" } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index a950331cff..69da0155c4 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -20,20 +20,20 @@ aes = "0.6" bitvec = "0.18" blake2b_simd = "0.5" blake2s_simd = "0.5" -bls12_381 = "0.3.1" +bls12_381 = "0.4" byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } -ff = "0.8" +ff = "0.9" fpe = "0.4" -group = "0.8" +group = "0.9" hex = "0.4" -jubjub = "0.5.1" +jubjub = "0.6" lazy_static = "1" log = "0.4" proptest = { version = "0.10.1", optional = true } -rand = "0.7" -rand_core = "0.5.1" +rand = "0.8" +rand_core = "0.6" ripemd160 = { version = "0.9", optional = true } secp256k1 = { version = "0.20", optional = true } sha2 = "0.9" @@ -47,7 +47,7 @@ funty = "=1.1.0" criterion = "0.3" hex-literal = "0.3" proptest = "0.10.1" -rand_xorshift = "0.2" +rand_xorshift = "0.3" [features] transparent-inputs = ["ripemd160", "secp256k1"] diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 63e8f7c14e..83c179de63 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -15,23 +15,23 @@ edition = "2018" all-features = true [dependencies] -bellman = { version = "0.8", default-features = false, features = ["groth16"] } +bellman = { version = "0.9", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" -bls12_381 = "0.3.1" +bls12_381 = "0.4" byteorder = "1" directories = { version = "3", optional = true } -ff = "0.8" -group = "0.8" -jubjub = "0.5.1" +ff = "0.9" +group = "0.9" +jubjub = "0.6" lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } -rand_core = "0.5.1" +rand_core = "0.6" wagyu-zcash-parameters = { version = "0.2", optional = true } zcash_primitives = { version = "0.5", path = "../zcash_primitives" } [dev-dependencies] criterion = "0.3" -rand_xorshift = "0.2" +rand_xorshift = "0.3" [features] default = ["local-prover", "multicore"] diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 5167fa5de0..be7960e63b 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -737,9 +737,9 @@ mod test { let s_bits = s .to_le_bits() - .into_iter() + .iter() + .by_val() .take(jubjub::Fr::NUM_BITS as usize) - .cloned() .enumerate() .map(|(i, b)| { AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", i)), Some(b)) @@ -788,9 +788,9 @@ mod test { let s_bits = s .to_le_bits() - .into_iter() + .iter() + .by_val() .take(jubjub::Fr::NUM_BITS as usize) - .cloned() .enumerate() .map(|(i, b)| { AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", i)), Some(b)) diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index fb00131b83..1c5fe766d5 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -591,10 +591,14 @@ fn test_input_circuit_with_bls12_381() { cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), - lhs.into_iter() + lhs.iter() + .by_val() .take(bls12_381::Scalar::NUM_BITS as usize) - .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)) - .cloned(), + .chain( + rhs.iter() + .by_val() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), )) .to_affine() .get_u(); @@ -765,10 +769,14 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), - lhs.into_iter() + lhs.iter() + .by_val() .take(bls12_381::Scalar::NUM_BITS as usize) - .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)) - .cloned(), + .chain( + rhs.iter() + .by_val() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), )) .to_affine() .get_u(); From 9a80ae5cd1af105cdf24832b5f136f9b2f7b63be Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 26 Jan 2021 22:50:24 +0000 Subject: [PATCH 0031/2028] bitvec 0.20 --- zcash_primitives/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 69da0155c4..e62ec0cbcd 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -17,7 +17,7 @@ all-features = true [dependencies] aes = "0.6" -bitvec = "0.18" +bitvec = "0.20" blake2b_simd = "0.5" blake2s_simd = "0.5" bls12_381 = "0.4" From 49d946fb5a9ace5faff149e057bfd883d415312a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 27 Jan 2021 21:10:55 +0000 Subject: [PATCH 0032/2028] Remove wasm32-unknown-unknown from build checks By default `getrandom` 0.2 does not compile on unsupported targets; it is necessary to enable its `js` feature flag specifically when targeting `wasm32-unknown-unknown`. Since we don't expose that flag ourselves (instead relying on the downstream user to do this) we can't directly test this ourselves. `wasm32-wasi` is fully-supported, so we continue to test builds against that target to ensure we retain WASM compatibility. --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ad90d53f3..34739c5ebe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,6 @@ jobs: strategy: matrix: target: - - wasm32-unknown-unknown - wasm32-wasi steps: From 35dadc7f6c3d996f935a53814a45540a4be774af Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 20 May 2021 14:16:39 +0100 Subject: [PATCH 0033/2028] f4jumble: Test both directions against test vectors --- zcash_primitives/src/address/f4jumble.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zcash_primitives/src/address/f4jumble.rs b/zcash_primitives/src/address/f4jumble.rs index 81fe18f2b7..9db6a77c6a 100644 --- a/zcash_primitives/src/address/f4jumble.rs +++ b/zcash_primitives/src/address/f4jumble.rs @@ -148,6 +148,8 @@ mod tests { for v in test_vectors() { let jumbled = f4jumble(&v.normal).unwrap(); assert_eq!(jumbled, v.jumbled); + let unjumbled = f4jumble_inv(&v.jumbled).unwrap(); + assert_eq!(unjumbled, v.normal); } } } From c754363c0798867c550827b326a5571b5d1a1226 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 7 Mar 2021 18:18:16 +0000 Subject: [PATCH 0034/2028] zcash_address: Initial empty library crate --- Cargo.toml | 1 + components/zcash_address/Cargo.toml | 14 ++ components/zcash_address/LICENSE-APACHE | 202 ++++++++++++++++++++++++ components/zcash_address/LICENSE-MIT | 21 +++ components/zcash_address/README.md | 20 +++ components/zcash_address/src/lib.rs | 7 + 6 files changed, 265 insertions(+) create mode 100644 components/zcash_address/Cargo.toml create mode 100644 components/zcash_address/LICENSE-APACHE create mode 100644 components/zcash_address/LICENSE-MIT create mode 100644 components/zcash_address/README.md create mode 100644 components/zcash_address/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 846f8c7a7b..21f33296ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "components/equihash", + "components/zcash_address", "components/zcash_note_encryption", "zcash_client_backend", "zcash_client_sqlite", diff --git a/components/zcash_address/Cargo.toml b/components/zcash_address/Cargo.toml new file mode 100644 index 0000000000..e967c69ccf --- /dev/null +++ b/components/zcash_address/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "zcash_address" +description = "Zcash address parsing and serialization" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] diff --git a/components/zcash_address/LICENSE-APACHE b/components/zcash_address/LICENSE-APACHE new file mode 100644 index 0000000000..1e5006dc14 --- /dev/null +++ b/components/zcash_address/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/components/zcash_address/LICENSE-MIT b/components/zcash_address/LICENSE-MIT new file mode 100644 index 0000000000..9500c140cc --- /dev/null +++ b/components/zcash_address/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Electric Coin Company + +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. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/components/zcash_address/README.md b/components/zcash_address/README.md new file mode 100644 index 0000000000..46867e3e91 --- /dev/null +++ b/components/zcash_address/README.md @@ -0,0 +1,20 @@ +# zcash_address + +TBD + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs new file mode 100644 index 0000000000..31e1bb209f --- /dev/null +++ b/components/zcash_address/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From ec77175d2b05763190e1d5fb1d9788f714e7a29b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 8 Mar 2021 03:25:59 +0000 Subject: [PATCH 0035/2028] zcash_address: Add address-encoding support This provides round-trip encoding for Zcash addresses. --- components/zcash_address/Cargo.toml | 2 + components/zcash_address/src/encoding.rs | 314 +++++++++++++++++++ components/zcash_address/src/kind.rs | 6 + components/zcash_address/src/kind/orchard.rs | 18 ++ components/zcash_address/src/kind/p2pkh.rs | 7 + components/zcash_address/src/kind/p2sh.rs | 7 + components/zcash_address/src/kind/sapling.rs | 22 ++ components/zcash_address/src/kind/sprout.rs | 15 + components/zcash_address/src/lib.rs | 40 ++- 9 files changed, 425 insertions(+), 6 deletions(-) create mode 100644 components/zcash_address/src/encoding.rs create mode 100644 components/zcash_address/src/kind.rs create mode 100644 components/zcash_address/src/kind/orchard.rs create mode 100644 components/zcash_address/src/kind/p2pkh.rs create mode 100644 components/zcash_address/src/kind/p2sh.rs create mode 100644 components/zcash_address/src/kind/sapling.rs create mode 100644 components/zcash_address/src/kind/sprout.rs diff --git a/components/zcash_address/Cargo.toml b/components/zcash_address/Cargo.toml index e967c69ccf..bac4703acc 100644 --- a/components/zcash_address/Cargo.toml +++ b/components/zcash_address/Cargo.toml @@ -12,3 +12,5 @@ license = "MIT OR Apache-2.0" edition = "2018" [dependencies] +bech32 = "0.8" +bs58 = { version = "0.4", features = ["check"] } diff --git a/components/zcash_address/src/encoding.rs b/components/zcash_address/src/encoding.rs new file mode 100644 index 0000000000..5e57c3e2ce --- /dev/null +++ b/components/zcash_address/src/encoding.rs @@ -0,0 +1,314 @@ +use std::{convert::TryInto, error::Error, fmt, str::FromStr}; + +use bech32::{self, FromBase32, ToBase32, Variant}; + +use crate::{kind::*, AddressKind, Network, ZcashAddress}; + +/// An error while attempting to parse a string as a Zcash address. +#[derive(Debug, PartialEq)] +pub enum ParseError { + /// The string is an invalid encoding. + InvalidEncoding, + /// The string might be an unknown Zcash address from the future. + MaybeZcash, + /// The string is not a Zcash address. + NotZcash, +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ParseError::InvalidEncoding => write!(f, "Invalid encoding"), + ParseError::MaybeZcash => write!( + f, + "This might be a Zcash address from the future that we don't know about" + ), + ParseError::NotZcash => write!(f, "Not a Zcash address"), + } + } +} + +impl Error for ParseError {} + +impl FromStr for ZcashAddress { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + // Remove leading and trailing whitespace, to handle copy-paste errors. + let s = s.trim(); + + // Most Zcash addresses use Bech32, so try that first. + match bech32::decode(s) { + // Zcash addresses only use the original Bech32 variant, since the data + // corresponding to a particular HRP always has a fixed length. + Ok((_, _, Variant::Bech32m)) => return Err(ParseError::NotZcash), + Ok((hrp, data, Variant::Bech32)) => { + // If we reached this point, the encoding is supposed to be valid Bech32. + let data = + Vec::::from_base32(&data).map_err(|_| ParseError::InvalidEncoding)?; + + let net = match hrp.as_str() { + sapling::MAINNET | orchard::MAINNET => Network::Main, + sapling::TESTNET | orchard::TESTNET => Network::Test, + sapling::REGTEST | orchard::REGTEST => Network::Regtest, + _ => { + // Use some heuristics to try and guess whether this might be a Zcash + // address from the future: + // - Zcash HRPs always start with a 'z'. + // - Zcash shielded addresses with diversification have data of + // length 43, but if we added the simple form of detection keys + // the data would have length 75. + return Err( + if hrp.starts_with('z') && (data.len() == 43 || data.len() == 75) { + ParseError::MaybeZcash + } else { + ParseError::NotZcash + }, + ); + } + }; + + return match hrp.as_str() { + sapling::MAINNET | sapling::TESTNET | sapling::REGTEST => { + data[..].try_into().map(AddressKind::Sapling) + } + orchard::MAINNET | orchard::TESTNET | orchard::REGTEST => { + data[..].try_into().map(AddressKind::Orchard) + } + _ => unreachable!(), + } + .map_err(|_| ParseError::InvalidEncoding) + .map(|kind| ZcashAddress { net, kind }); + } + Err(_) => (), + } + + // The rest use Base58Check. + if let Ok(decoded) = bs58::decode(s).with_check(None).into_vec() { + let net = match decoded[..2].try_into().unwrap() { + sprout::MAINNET | p2pkh::MAINNET | p2sh::MAINNET => Network::Main, + sprout::TESTNET | p2pkh::TESTNET | p2sh::TESTNET => Network::Test, + // We will not define new Base58Check address encodings. + _ => return Err(ParseError::NotZcash), + }; + + return match decoded[..2].try_into().unwrap() { + sprout::MAINNET | sprout::TESTNET => { + decoded[2..].try_into().map(AddressKind::Sprout) + } + p2pkh::MAINNET | p2pkh::TESTNET => decoded[2..].try_into().map(AddressKind::P2pkh), + p2sh::MAINNET | p2sh::TESTNET => decoded[2..].try_into().map(AddressKind::P2sh), + _ => unreachable!(), + } + .map_err(|_| ParseError::InvalidEncoding) + .map(|kind| ZcashAddress { kind, net }); + }; + + // If it's not valid Bech32 or Base58Check, it's not a Zcash address. + Err(ParseError::NotZcash) + } +} + +fn encode_bech32(hrp: &str, data: &[u8]) -> String { + bech32::encode(hrp, data.to_base32(), Variant::Bech32).expect("hrp is invalid") +} + +fn encode_b58(prefix: [u8; 2], data: &[u8]) -> String { + let mut decoded = Vec::with_capacity(2 + data.len()); + decoded.extend_from_slice(&prefix); + decoded.extend_from_slice(data); + bs58::encode(decoded).with_check().into_string() +} + +impl fmt::Display for ZcashAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let encoded = match self.kind { + AddressKind::Sprout(data) => encode_b58( + if let Network::Main = self.net { + sprout::MAINNET + } else { + sprout::TESTNET + }, + &data, + ), + AddressKind::Sapling(data) => encode_bech32( + match self.net { + Network::Main => sapling::MAINNET, + Network::Test => sapling::TESTNET, + Network::Regtest => sapling::REGTEST, + }, + &data, + ), + AddressKind::Orchard(data) => encode_bech32( + match self.net { + Network::Main => orchard::MAINNET, + Network::Test => orchard::TESTNET, + Network::Regtest => orchard::REGTEST, + }, + &data, + ), + AddressKind::P2pkh(data) => encode_b58( + if let Network::Main = self.net { + p2pkh::MAINNET + } else { + p2pkh::TESTNET + }, + &data, + ), + AddressKind::P2sh(data) => encode_b58( + if let Network::Main = self.net { + p2sh::MAINNET + } else { + p2sh::TESTNET + }, + &data, + ), + }; + write!(f, "{}", encoded) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn encoding(encoded: &str, decoded: ZcashAddress) { + assert_eq!(decoded.to_string(), encoded); + assert_eq!(encoded.parse(), Ok(decoded)); + } + + #[test] + fn sprout() { + encoding( + "zc8E5gYid86n4bo2Usdq1cpr7PpfoJGzttwBHEEgGhGkLUg7SPPVFNB2AkRFXZ7usfphup5426dt1buMmY3fkYeRrQGLa8y", + ZcashAddress { net: Network::Main, kind: AddressKind::Sprout([0; 64]) }, + ); + encoding( + "ztJ1EWLKcGwF2S4NA17pAJVdco8Sdkz4AQPxt1cLTEfNuyNswJJc2BbBqYrsRZsp31xbVZwhF7c7a2L9jsF3p3ZwRWpqqyS", + ZcashAddress { net: Network::Test, kind: AddressKind::Sprout([0; 64]) }, + ); + } + + #[test] + fn sapling() { + encoding( + "zs1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpq6d8g", + ZcashAddress { + net: Network::Main, + kind: AddressKind::Sapling([0; 43]), + }, + ); + encoding( + "ztestsapling1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfhgwqu", + ZcashAddress { + net: Network::Test, + kind: AddressKind::Sapling([0; 43]), + }, + ); + encoding( + "zregtestsapling1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqknpr3m", + ZcashAddress { + net: Network::Regtest, + kind: AddressKind::Sapling([0; 43]), + }, + ); + } + + #[test] + fn orchard() { + encoding( + "zo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq58lk79", + ZcashAddress { + net: Network::Main, + kind: AddressKind::Orchard([0; 43]), + }, + ); + encoding( + "ztestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcrmt3p", + ZcashAddress { + net: Network::Test, + kind: AddressKind::Orchard([0; 43]), + }, + ); + encoding( + "zregtestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq88jxqx", + ZcashAddress { + net: Network::Regtest, + kind: AddressKind::Orchard([0; 43]), + }, + ); + } + + #[test] + fn maybe_zcash() { + assert_eq!( + "zmaybe1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqql7xs38" + .parse::(), + Err(ParseError::MaybeZcash), + ); + assert_eq!( + "zpossibly1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq559klt" + .parse::(), + Err(ParseError::MaybeZcash), + ); + assert_eq!( + "nope1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg8f5j9" + .parse::(), + Err(ParseError::NotZcash), + ); + } + + #[test] + fn transparent() { + encoding( + "t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs", + ZcashAddress { + net: Network::Main, + kind: AddressKind::P2pkh([0; 20]), + }, + ); + encoding( + "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", + ZcashAddress { + net: Network::Test, + kind: AddressKind::P2pkh([0; 20]), + }, + ); + encoding( + "t3JZcvsuaXE6ygokL4XUiZSTrQBUoPYFnXJ", + ZcashAddress { + net: Network::Main, + kind: AddressKind::P2sh([0; 20]), + }, + ); + encoding( + "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", + ZcashAddress { + net: Network::Test, + kind: AddressKind::P2sh([0; 20]), + }, + ); + } + + #[test] + fn whitespace() { + assert_eq!( + " t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs".parse(), + Ok(ZcashAddress { + net: Network::Main, + kind: AddressKind::P2pkh([0; 20]) + }), + ); + assert_eq!( + "t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs ".parse(), + Ok(ZcashAddress { + net: Network::Main, + kind: AddressKind::P2pkh([0; 20]) + }), + ); + assert_eq!( + "something t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs".parse::(), + Err(ParseError::NotZcash), + ); + } +} diff --git a/components/zcash_address/src/kind.rs b/components/zcash_address/src/kind.rs new file mode 100644 index 0000000000..647ceec594 --- /dev/null +++ b/components/zcash_address/src/kind.rs @@ -0,0 +1,6 @@ +pub(crate) mod orchard; +pub(crate) mod sapling; +pub(crate) mod sprout; + +pub(crate) mod p2pkh; +pub(crate) mod p2sh; diff --git a/components/zcash_address/src/kind/orchard.rs b/components/zcash_address/src/kind/orchard.rs new file mode 100644 index 0000000000..3641fe17ae --- /dev/null +++ b/components/zcash_address/src/kind/orchard.rs @@ -0,0 +1,18 @@ +/// The HRP for a Bech32-encoded mainnet Orchard address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.4.1][orchardpaymentaddrencoding]. +/// +/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding +pub(crate) const MAINNET: &str = "zo"; + +/// The HRP for a Bech32-encoded testnet Orchard address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.4.1][orchardpaymentaddrencoding]. +/// +/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding +pub(crate) const TESTNET: &str = "ztestorchard"; + +/// The HRP for a Bech32-encoded regtest Orchard address. +pub(crate) const REGTEST: &str = "zregtestorchard"; + +pub(crate) type Data = [u8; 43]; diff --git a/components/zcash_address/src/kind/p2pkh.rs b/components/zcash_address/src/kind/p2pkh.rs new file mode 100644 index 0000000000..0120e2c39f --- /dev/null +++ b/components/zcash_address/src/kind/p2pkh.rs @@ -0,0 +1,7 @@ +/// The prefix for a Base58Check-encoded mainnet transparent P2PKH address. +pub(crate) const MAINNET: [u8; 2] = [0x1c, 0xb8]; + +/// The prefix for a Base58Check-encoded testnet transparent P2PKH address. +pub(crate) const TESTNET: [u8; 2] = [0x1d, 0x25]; + +pub(crate) type Data = [u8; 20]; diff --git a/components/zcash_address/src/kind/p2sh.rs b/components/zcash_address/src/kind/p2sh.rs new file mode 100644 index 0000000000..5059513182 --- /dev/null +++ b/components/zcash_address/src/kind/p2sh.rs @@ -0,0 +1,7 @@ +/// The prefix for a Base58Check-encoded mainnet transparent P2SH address. +pub(crate) const MAINNET: [u8; 2] = [0x1c, 0xbd]; + +/// The prefix for a Base58Check-encoded testnet transparent P2SH address. +pub(crate) const TESTNET: [u8; 2] = [0x1c, 0xba]; + +pub(crate) type Data = [u8; 20]; diff --git a/components/zcash_address/src/kind/sapling.rs b/components/zcash_address/src/kind/sapling.rs new file mode 100644 index 0000000000..2cbf914d61 --- /dev/null +++ b/components/zcash_address/src/kind/sapling.rs @@ -0,0 +1,22 @@ +/// The HRP for a Bech32-encoded mainnet Sapling address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.4][saplingpaymentaddrencoding]. +/// +/// [saplingpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#saplingpaymentaddrencoding +pub(crate) const MAINNET: &str = "zs"; + +/// The HRP for a Bech32-encoded testnet Sapling address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.4][saplingpaymentaddrencoding]. +/// +/// [saplingpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#saplingpaymentaddrencoding +pub(crate) const TESTNET: &str = "ztestsapling"; + +/// The HRP for a Bech32-encoded regtest Sapling address. +/// +/// It is defined in [the `zcashd` codebase]. +/// +/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L493 +pub(crate) const REGTEST: &str = "zregtestsapling"; + +pub(crate) type Data = [u8; 43]; diff --git a/components/zcash_address/src/kind/sprout.rs b/components/zcash_address/src/kind/sprout.rs new file mode 100644 index 0000000000..fedb79b416 --- /dev/null +++ b/components/zcash_address/src/kind/sprout.rs @@ -0,0 +1,15 @@ +/// The prefix for a Base58Check-encoded mainnet Sprout address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.3][sproutpaymentaddrencoding]. +/// +/// [sproutpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#sproutpaymentaddrencoding +pub(crate) const MAINNET: [u8; 2] = [0x16, 0x9a]; + +/// The prefix for a Base58Check-encoded testnet Sprout address. +/// +/// Defined in the [Zcash Protocol Specification section 5.6.3][]. +/// +/// []: https://zips.z.cash/protocol/protocol.pdf#sproutpaymentaddrencoding +pub(crate) const TESTNET: [u8; 2] = [0x16, 0xb6]; + +pub(crate) type Data = [u8; 64]; diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 31e1bb209f..3a01029e72 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -1,7 +1,35 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } +mod encoding; +mod kind; + +pub use encoding::ParseError; + +/// A Zcash address. +#[derive(Debug, PartialEq)] +pub struct ZcashAddress { + net: Network, + kind: AddressKind, +} + +/// The Zcash network for which an address is encoded. +#[derive(Debug, PartialEq)] +enum Network { + /// Zcash Mainnet. + Main, + /// Zcash Testnet. + Test, + /// Private integration / regression testing, used in `zcashd`. + /// + /// For some address types there is no distinction between test and regtest encodings; + /// those will always be parsed as `Network::Test`. + Regtest, +} + +/// Known kinds of Zcash addresses. +#[derive(Debug, PartialEq)] +enum AddressKind { + Sprout(kind::sprout::Data), + Sapling(kind::sapling::Data), + Orchard(kind::orchard::Data), + P2pkh(kind::p2pkh::Data), + P2sh(kind::p2sh::Data), } From a366460157f600c81814eba0c184bd4cc6bce764 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 8 Mar 2021 04:46:19 +0000 Subject: [PATCH 0036/2028] zcash_address: ZcashAddress::convert() -> T: FromAddress This enables easy conversion of an encoded Zcash address to a target type, with automatic handling of Zcash address types that are not supported by the target. --- components/zcash_address/src/convert.rs | 86 +++++++++++++++++++++++++ components/zcash_address/src/lib.rs | 4 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 components/zcash_address/src/convert.rs diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs new file mode 100644 index 0000000000..50a500ad4a --- /dev/null +++ b/components/zcash_address/src/convert.rs @@ -0,0 +1,86 @@ +use std::{error::Error, fmt}; + +use crate::{kind::*, AddressKind, Network, ZcashAddress}; + +/// An address type is not supported for conversion. +#[derive(Debug)] +pub struct UnsupportedAddress(&'static str); + +impl fmt::Display for UnsupportedAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Zcash {} addresses are not supported", self.0) + } +} + +impl Error for UnsupportedAddress {} + +impl ZcashAddress { + pub fn convert(self) -> Result { + match self.kind { + AddressKind::Sprout(data) => T::from_sprout(self.net, data), + AddressKind::Sapling(data) => T::from_sapling(self.net, data), + AddressKind::Orchard(data) => T::from_orchard(self.net, data), + AddressKind::P2pkh(data) => T::from_transparent_p2pkh(self.net, data), + AddressKind::P2sh(data) => T::from_transparent_p2sh(self.net, data), + } + } +} + +/// A helper trait for converting a [`ZcashAddress`] into another type. +/// +/// # Examples +/// +/// ``` +/// use zcash_address::{FromAddress, Network, UnsupportedAddress, ZcashAddress}; +/// +/// #[derive(Debug)] +/// struct MySapling([u8; 43]); +/// +/// // Implement the FromAddress trait, overriding whichever conversion methods match your +/// // requirements for the resulting type. +/// impl FromAddress for MySapling { +/// fn from_sapling(net: Network, data: [u8; 43]) -> Result { +/// Ok(MySapling(data)) +/// } +/// } +/// +/// // For a supported address type, the conversion works. +/// let addr: ZcashAddress = +/// "zs1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpq6d8g" +/// .parse() +/// .unwrap(); +/// assert!(addr.convert::().is_ok()); +/// +/// // For an unsupported address type, we get an error. +/// let addr: ZcashAddress = "t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs".parse().unwrap(); +/// assert_eq!( +/// addr.convert::().unwrap_err().to_string(), +/// "Zcash transparent P2PKH addresses are not supported", +/// ); +/// ``` +pub trait FromAddress: Sized { + fn from_sprout(net: Network, data: sprout::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Sprout")) + } + + fn from_sapling(net: Network, data: sapling::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Sapling")) + } + + fn from_orchard(net: Network, data: orchard::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Orchard")) + } + + fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("transparent P2PKH")) + } + + fn from_transparent_p2sh(net: Network, data: p2sh::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("transparent P2SH")) + } +} diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 3a01029e72..76139aca12 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -1,6 +1,8 @@ +mod convert; mod encoding; mod kind; +pub use convert::{FromAddress, UnsupportedAddress}; pub use encoding::ParseError; /// A Zcash address. @@ -12,7 +14,7 @@ pub struct ZcashAddress { /// The Zcash network for which an address is encoded. #[derive(Debug, PartialEq)] -enum Network { +pub enum Network { /// Zcash Mainnet. Main, /// Zcash Testnet. From f7b105817157d56731c41f4838c8cea0f6c03c16 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 9 Mar 2021 13:51:59 +1300 Subject: [PATCH 0037/2028] zcash_address: Extend MaybeZcash heuristics to 64-byte addresses --- components/zcash_address/src/encoding.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/zcash_address/src/encoding.rs b/components/zcash_address/src/encoding.rs index 5e57c3e2ce..d266b1ced3 100644 --- a/components/zcash_address/src/encoding.rs +++ b/components/zcash_address/src/encoding.rs @@ -57,9 +57,12 @@ impl FromStr for ZcashAddress { // - Zcash HRPs always start with a 'z'. // - Zcash shielded addresses with diversification have data of // length 43, but if we added the simple form of detection keys - // the data would have length 75. + // the data would have length 75. Alternatively if we switch from a + // 11-byte diversifier to two field elements, that would be 64 bytes. return Err( - if hrp.starts_with('z') && (data.len() == 43 || data.len() == 75) { + if hrp.starts_with('z') + && (data.len() == 43 || data.len() == 64 || data.len() == 75) + { ParseError::MaybeZcash } else { ParseError::NotZcash From c7fcee27a229ea55ce20738c606c719628d791bf Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 13 Mar 2021 09:11:03 +1300 Subject: [PATCH 0038/2028] zcash_address: Add ZcashAddress::try_from_encoded method This places parsing documentation front and centre, while also making it clear that `str::parse` is the anticipated main entry point. --- components/zcash_address/src/encoding.rs | 1 + components/zcash_address/src/lib.rs | 36 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/components/zcash_address/src/encoding.rs b/components/zcash_address/src/encoding.rs index d266b1ced3..2a898d335b 100644 --- a/components/zcash_address/src/encoding.rs +++ b/components/zcash_address/src/encoding.rs @@ -33,6 +33,7 @@ impl Error for ParseError {} impl FromStr for ZcashAddress { type Err = ParseError; + /// Attempts to parse the given string as a Zcash address. fn from_str(s: &str) -> Result { // Remove leading and trailing whitespace, to handle copy-paste errors. let s = s.trim(); diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 76139aca12..ea52e678cc 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -35,3 +35,39 @@ enum AddressKind { P2pkh(kind::p2pkh::Data), P2sh(kind::p2sh::Data), } + +impl ZcashAddress { + /// Attempts to parse the given string as a Zcash address. + /// + /// This simply calls [`s.parse()`], leveraging the [`FromStr` implementation]. + /// + /// [`s.parse()`]: std::primitive::str::parse + /// [`FromStr` implementation]: ZcashAddress#impl-FromStr + /// + /// # Errors + /// + /// In most cases, [`ParseError::NotZcash`] will be returned on failure. The two + /// exceptions are: + /// + /// - If the parser can detect that the string _must_ contain an address encoding used + /// by Zcash, [`ParseError::InvalidEncoding`] will be returned if any subsequent + /// part of that encoding is invalid. + /// + /// - [`ParseError::MaybeZcash`] will be returned if the string is Bech32-encoded data + /// that satisfies some heuristics for probable future Zcash address formats (such + /// as beginning with a `z`). This can either be treated as an indication that this + /// library dependency should be updated, or mapped to [`ParseError::NotZcash`]. + /// + /// # Examples + /// + /// ``` + /// use zcash_address::ZcashAddress; + /// + /// let encoded = "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9sly"; + /// let addr = ZcashAddress::try_from_encoded(&encoded); + /// assert_eq!(encoded.parse(), addr); + /// ``` + pub fn try_from_encoded(s: &str) -> Result { + s.parse() + } +} From b9f704955ab210241b7338669c3859f024a35de2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 13 Mar 2021 09:12:52 +1300 Subject: [PATCH 0039/2028] zcash_address: Move ZcashAddress::convert into root Using two separate `impl ZcashAddress` blocks resulted in separate blocks in the documentation, which is unnecessary. --- components/zcash_address/src/convert.rs | 16 +++------------- components/zcash_address/src/lib.rs | 10 ++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs index 50a500ad4a..ec6685a183 100644 --- a/components/zcash_address/src/convert.rs +++ b/components/zcash_address/src/convert.rs @@ -1,6 +1,6 @@ use std::{error::Error, fmt}; -use crate::{kind::*, AddressKind, Network, ZcashAddress}; +use crate::{kind::*, Network}; /// An address type is not supported for conversion. #[derive(Debug)] @@ -14,20 +14,10 @@ impl fmt::Display for UnsupportedAddress { impl Error for UnsupportedAddress {} -impl ZcashAddress { - pub fn convert(self) -> Result { - match self.kind { - AddressKind::Sprout(data) => T::from_sprout(self.net, data), - AddressKind::Sapling(data) => T::from_sapling(self.net, data), - AddressKind::Orchard(data) => T::from_orchard(self.net, data), - AddressKind::P2pkh(data) => T::from_transparent_p2pkh(self.net, data), - AddressKind::P2sh(data) => T::from_transparent_p2sh(self.net, data), - } - } -} - /// A helper trait for converting a [`ZcashAddress`] into another type. /// +/// [`ZcashAddress`]: crate::ZcashAddress +/// /// # Examples /// /// ``` diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index ea52e678cc..eca9832471 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -70,4 +70,14 @@ impl ZcashAddress { pub fn try_from_encoded(s: &str) -> Result { s.parse() } + + pub fn convert(self) -> Result { + match self.kind { + AddressKind::Sprout(data) => T::from_sprout(self.net, data), + AddressKind::Sapling(data) => T::from_sapling(self.net, data), + AddressKind::Orchard(data) => T::from_orchard(self.net, data), + AddressKind::P2pkh(data) => T::from_transparent_p2pkh(self.net, data), + AddressKind::P2sh(data) => T::from_transparent_p2sh(self.net, data), + } + } } From ff07eeaabb68a2a270e7e502e6e4ffbf07d08a03 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 13 Mar 2021 10:42:56 +1300 Subject: [PATCH 0040/2028] zcash_address: Document ZcashAddress::convert --- components/zcash_address/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index eca9832471..94881207a0 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -71,6 +71,18 @@ impl ZcashAddress { s.parse() } + /// Converts this address into another type. + /// + /// `convert` can convert into any type that implements the [`FromAddress`] trait. + /// This enables `ZcashAddress` to be used as a common parsing and serialization + /// interface for Zcash addresses, while delegating operations on those addresses + /// (such as constructing transactions) to downstream crates. + /// + /// If you want to get the encoded string for this address, use the [`Display`] + /// implementation instead via [`address.to_string()`]. + /// + /// [`Display`]: std::fmt::Display + /// [`address.to_string()`]: std::string::ToString pub fn convert(self) -> Result { match self.kind { AddressKind::Sprout(data) => T::from_sprout(self.net, data), From ce8797e4b1dd1039709cdf7be998d4e708f83231 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 13 Mar 2021 10:43:20 +1300 Subject: [PATCH 0041/2028] zcash_address: impl {Clone, Copy, Eq, Hash} for ZcashAddress --- components/zcash_address/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 94881207a0..af5dea0683 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -6,14 +6,14 @@ pub use convert::{FromAddress, UnsupportedAddress}; pub use encoding::ParseError; /// A Zcash address. -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ZcashAddress { net: Network, kind: AddressKind, } /// The Zcash network for which an address is encoded. -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Network { /// Zcash Mainnet. Main, @@ -27,7 +27,7 @@ pub enum Network { } /// Known kinds of Zcash addresses. -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] enum AddressKind { Sprout(kind::sprout::Data), Sapling(kind::sapling::Data), From 9f7398cd051ed32f8d40ae57bfd290d1c984704f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 13 Mar 2021 11:24:03 +1300 Subject: [PATCH 0042/2028] zcash_address: Add convert::ToAddress helper trait --- components/zcash_address/src/convert.rs | 101 +++++++++++++++++++++++- components/zcash_address/src/lib.rs | 2 +- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs index ec6685a183..66e4664638 100644 --- a/components/zcash_address/src/convert.rs +++ b/components/zcash_address/src/convert.rs @@ -1,6 +1,6 @@ use std::{error::Error, fmt}; -use crate::{kind::*, Network}; +use crate::{kind::*, AddressKind, Network, ZcashAddress}; /// An address type is not supported for conversion. #[derive(Debug)] @@ -74,3 +74,102 @@ pub trait FromAddress: Sized { Err(UnsupportedAddress("transparent P2SH")) } } + +/// A helper trait for converting another type into a [`ZcashAddress`]. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. Its +/// purpose is to move these conversion functions out of the main `ZcashAddress` API +/// documentation, as they are only required when creating addresses (rather than when +/// parsing addresses, which is a more common occurrence). +/// +/// [`ZcashAddress`]: crate::ZcashAddress +/// +/// # Examples +/// +/// ``` +/// use zcash_address::{ToAddress, Network, ZcashAddress}; +/// +/// #[derive(Debug)] +/// struct MySapling([u8; 43]); +/// +/// impl MySapling { +/// /// Encodes this Sapling address for the given network. +/// fn encode(&self, net: Network) -> ZcashAddress { +/// ZcashAddress::from_sapling(net, self.0) +/// } +/// } +/// +/// let addr = MySapling([0; 43]); +/// let encoded = addr.encode(Network::Main); +/// assert_eq!( +/// encoded.to_string(), +/// "zs1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpq6d8g", +/// ); +/// ``` +pub trait ToAddress: private::Sealed { + fn from_sprout(net: Network, data: sprout::Data) -> Self; + + fn from_sapling(net: Network, data: sapling::Data) -> Self; + + fn from_orchard(net: Network, data: orchard::Data) -> Self; + + fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Self; + + fn from_transparent_p2sh(net: Network, data: p2sh::Data) -> Self; +} + +impl ToAddress for ZcashAddress { + fn from_sprout(net: Network, data: sprout::Data) -> Self { + ZcashAddress { + net: if let Network::Regtest = net { + Network::Test + } else { + net + }, + kind: AddressKind::Sprout(data), + } + } + + fn from_sapling(net: Network, data: sapling::Data) -> Self { + ZcashAddress { + net, + kind: AddressKind::Sapling(data), + } + } + + fn from_orchard(net: Network, data: orchard::Data) -> Self { + ZcashAddress { + net, + kind: AddressKind::Orchard(data), + } + } + + fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Self { + ZcashAddress { + net: if let Network::Regtest = net { + Network::Test + } else { + net + }, + kind: AddressKind::P2pkh(data), + } + } + + fn from_transparent_p2sh(net: Network, data: p2sh::Data) -> Self { + ZcashAddress { + net: if let Network::Regtest = net { + Network::Test + } else { + net + }, + kind: AddressKind::P2sh(data), + } + } +} + +mod private { + use crate::ZcashAddress; + + pub trait Sealed {} + impl Sealed for ZcashAddress {} +} diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index af5dea0683..cd239779de 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -2,7 +2,7 @@ mod convert; mod encoding; mod kind; -pub use convert::{FromAddress, UnsupportedAddress}; +pub use convert::{FromAddress, ToAddress, UnsupportedAddress}; pub use encoding::ParseError; /// A Zcash address. From ae2b8bfd6d3d8b2dd096bd5a4052df38e1a7cf01 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 20 May 2021 15:12:33 +0100 Subject: [PATCH 0043/2028] zcash_address: Replace Orchard address encodings with Unified Addresses This commit removes the now-undefined Orchard encoding logic, and adds the general Bech32m encoding/decoding logic for Unified Addresses. The internal data format of Unified Addresses is not correct in this commit. --- components/zcash_address/src/convert.rs | 10 +- components/zcash_address/src/encoding.rs | 109 ++++++++----------- components/zcash_address/src/kind.rs | 3 +- components/zcash_address/src/kind/orchard.rs | 18 --- components/zcash_address/src/kind/unified.rs | 19 ++++ components/zcash_address/src/lib.rs | 12 +- 6 files changed, 72 insertions(+), 99 deletions(-) delete mode 100644 components/zcash_address/src/kind/orchard.rs create mode 100644 components/zcash_address/src/kind/unified.rs diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs index 66e4664638..476140f6d6 100644 --- a/components/zcash_address/src/convert.rs +++ b/components/zcash_address/src/convert.rs @@ -59,9 +59,9 @@ pub trait FromAddress: Sized { Err(UnsupportedAddress("Sapling")) } - fn from_orchard(net: Network, data: orchard::Data) -> Result { + fn from_unified(net: Network, data: unified::Data) -> Result { let _ = (net, data); - Err(UnsupportedAddress("Orchard")) + Err(UnsupportedAddress("Unified")) } fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Result { @@ -111,7 +111,7 @@ pub trait ToAddress: private::Sealed { fn from_sapling(net: Network, data: sapling::Data) -> Self; - fn from_orchard(net: Network, data: orchard::Data) -> Self; + fn from_unified(net: Network, data: unified::Data) -> Self; fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Self; @@ -137,10 +137,10 @@ impl ToAddress for ZcashAddress { } } - fn from_orchard(net: Network, data: orchard::Data) -> Self { + fn from_unified(net: Network, data: unified::Data) -> Self { ZcashAddress { net, - kind: AddressKind::Orchard(data), + kind: AddressKind::Unified(data), } } diff --git a/components/zcash_address/src/encoding.rs b/components/zcash_address/src/encoding.rs index 2a898d335b..8195f8daaa 100644 --- a/components/zcash_address/src/encoding.rs +++ b/components/zcash_address/src/encoding.rs @@ -9,8 +9,6 @@ use crate::{kind::*, AddressKind, Network, ZcashAddress}; pub enum ParseError { /// The string is an invalid encoding. InvalidEncoding, - /// The string might be an unknown Zcash address from the future. - MaybeZcash, /// The string is not a Zcash address. NotZcash, } @@ -19,10 +17,6 @@ impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseError::InvalidEncoding => write!(f, "Invalid encoding"), - ParseError::MaybeZcash => write!( - f, - "This might be a Zcash address from the future that we don't know about" - ), ParseError::NotZcash => write!(f, "Not a Zcash address"), } } @@ -40,49 +34,47 @@ impl FromStr for ZcashAddress { // Most Zcash addresses use Bech32, so try that first. match bech32::decode(s) { - // Zcash addresses only use the original Bech32 variant, since the data - // corresponding to a particular HRP always has a fixed length. - Ok((_, _, Variant::Bech32m)) => return Err(ParseError::NotZcash), + Ok((hrp, data, Variant::Bech32m)) => { + // If we reached this point, the encoding is supposed to be valid Bech32m. + let data = + Vec::::from_base32(&data).map_err(|_| ParseError::InvalidEncoding)?; + + let net = match hrp.as_str() { + unified::MAINNET => Network::Main, + unified::TESTNET => Network::Test, + unified::REGTEST => Network::Regtest, + // We will not define new Bech32m address encodings. + _ => { + return Err(ParseError::NotZcash); + } + }; + + return data[..] + .try_into() + .map(AddressKind::Unified) + .map_err(|_| ParseError::InvalidEncoding) + .map(|kind| ZcashAddress { net, kind }); + } Ok((hrp, data, Variant::Bech32)) => { // If we reached this point, the encoding is supposed to be valid Bech32. let data = Vec::::from_base32(&data).map_err(|_| ParseError::InvalidEncoding)?; let net = match hrp.as_str() { - sapling::MAINNET | orchard::MAINNET => Network::Main, - sapling::TESTNET | orchard::TESTNET => Network::Test, - sapling::REGTEST | orchard::REGTEST => Network::Regtest, + sapling::MAINNET => Network::Main, + sapling::TESTNET => Network::Test, + sapling::REGTEST => Network::Regtest, + // We will not define new Bech32 address encodings. _ => { - // Use some heuristics to try and guess whether this might be a Zcash - // address from the future: - // - Zcash HRPs always start with a 'z'. - // - Zcash shielded addresses with diversification have data of - // length 43, but if we added the simple form of detection keys - // the data would have length 75. Alternatively if we switch from a - // 11-byte diversifier to two field elements, that would be 64 bytes. - return Err( - if hrp.starts_with('z') - && (data.len() == 43 || data.len() == 64 || data.len() == 75) - { - ParseError::MaybeZcash - } else { - ParseError::NotZcash - }, - ); + return Err(ParseError::NotZcash); } }; - return match hrp.as_str() { - sapling::MAINNET | sapling::TESTNET | sapling::REGTEST => { - data[..].try_into().map(AddressKind::Sapling) - } - orchard::MAINNET | orchard::TESTNET | orchard::REGTEST => { - data[..].try_into().map(AddressKind::Orchard) - } - _ => unreachable!(), - } - .map_err(|_| ParseError::InvalidEncoding) - .map(|kind| ZcashAddress { net, kind }); + return data[..] + .try_into() + .map(AddressKind::Sapling) + .map_err(|_| ParseError::InvalidEncoding) + .map(|kind| ZcashAddress { net, kind }); } Err(_) => (), } @@ -113,6 +105,10 @@ impl FromStr for ZcashAddress { } } +fn encode_bech32m(hrp: &str, data: &[u8]) -> String { + bech32::encode(hrp, data.to_base32(), Variant::Bech32m).expect("hrp is invalid") +} + fn encode_bech32(hrp: &str, data: &[u8]) -> String { bech32::encode(hrp, data.to_base32(), Variant::Bech32).expect("hrp is invalid") } @@ -143,11 +139,11 @@ impl fmt::Display for ZcashAddress { }, &data, ), - AddressKind::Orchard(data) => encode_bech32( + AddressKind::Unified(data) => encode_bech32m( match self.net { - Network::Main => orchard::MAINNET, - Network::Test => orchard::TESTNET, - Network::Regtest => orchard::REGTEST, + Network::Main => unified::MAINNET, + Network::Test => unified::TESTNET, + Network::Regtest => unified::REGTEST, }, &data, ), @@ -219,49 +215,30 @@ mod tests { } #[test] - fn orchard() { + fn unified() { encoding( "zo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq58lk79", ZcashAddress { net: Network::Main, - kind: AddressKind::Orchard([0; 43]), + kind: AddressKind::Unified([0; 43]), }, ); encoding( "ztestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcrmt3p", ZcashAddress { net: Network::Test, - kind: AddressKind::Orchard([0; 43]), + kind: AddressKind::Unified([0; 43]), }, ); encoding( "zregtestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq88jxqx", ZcashAddress { net: Network::Regtest, - kind: AddressKind::Orchard([0; 43]), + kind: AddressKind::Unified([0; 43]), }, ); } - #[test] - fn maybe_zcash() { - assert_eq!( - "zmaybe1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqql7xs38" - .parse::(), - Err(ParseError::MaybeZcash), - ); - assert_eq!( - "zpossibly1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq559klt" - .parse::(), - Err(ParseError::MaybeZcash), - ); - assert_eq!( - "nope1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg8f5j9" - .parse::(), - Err(ParseError::NotZcash), - ); - } - #[test] fn transparent() { encoding( diff --git a/components/zcash_address/src/kind.rs b/components/zcash_address/src/kind.rs index 647ceec594..3999fad39a 100644 --- a/components/zcash_address/src/kind.rs +++ b/components/zcash_address/src/kind.rs @@ -1,4 +1,5 @@ -pub(crate) mod orchard; +pub(crate) mod unified; + pub(crate) mod sapling; pub(crate) mod sprout; diff --git a/components/zcash_address/src/kind/orchard.rs b/components/zcash_address/src/kind/orchard.rs deleted file mode 100644 index 3641fe17ae..0000000000 --- a/components/zcash_address/src/kind/orchard.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// The HRP for a Bech32-encoded mainnet Orchard address. -/// -/// Defined in the [Zcash Protocol Specification section 5.6.4.1][orchardpaymentaddrencoding]. -/// -/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding -pub(crate) const MAINNET: &str = "zo"; - -/// The HRP for a Bech32-encoded testnet Orchard address. -/// -/// Defined in the [Zcash Protocol Specification section 5.6.4.1][orchardpaymentaddrencoding]. -/// -/// [orchardpaymentaddrencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardpaymentaddrencoding -pub(crate) const TESTNET: &str = "ztestorchard"; - -/// The HRP for a Bech32-encoded regtest Orchard address. -pub(crate) const REGTEST: &str = "zregtestorchard"; - -pub(crate) type Data = [u8; 43]; diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs new file mode 100644 index 0000000000..35310099d3 --- /dev/null +++ b/components/zcash_address/src/kind/unified.rs @@ -0,0 +1,19 @@ +/// The HRP for a Bech32m-encoded mainnet Unified Address. +/// +/// Defined in [ZIP 316][zip-0316]. +/// +/// [zip-0316]: https://zips.z.cash/zip-0316 +pub(crate) const MAINNET: &str = "u"; + +/// The HRP for a Bech32m-encoded testnet Unified Address. +/// +/// Defined in [ZIP 316][zip-0316]. +/// +/// [zip-0316]: https://zips.z.cash/zip-0316 +pub(crate) const TESTNET: &str = "utest"; + +/// The HRP for a Bech32m-encoded regtest Unified Address. +pub(crate) const REGTEST: &str = "uregtest"; + +/// TODO +pub(crate) type Data = [u8; 43]; diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index cd239779de..8f8841aa1e 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -31,7 +31,7 @@ pub enum Network { enum AddressKind { Sprout(kind::sprout::Data), Sapling(kind::sapling::Data), - Orchard(kind::orchard::Data), + Unified(kind::unified::Data), P2pkh(kind::p2pkh::Data), P2sh(kind::p2sh::Data), } @@ -46,17 +46,11 @@ impl ZcashAddress { /// /// # Errors /// - /// In most cases, [`ParseError::NotZcash`] will be returned on failure. The two - /// exceptions are: - /// /// - If the parser can detect that the string _must_ contain an address encoding used /// by Zcash, [`ParseError::InvalidEncoding`] will be returned if any subsequent /// part of that encoding is invalid. /// - /// - [`ParseError::MaybeZcash`] will be returned if the string is Bech32-encoded data - /// that satisfies some heuristics for probable future Zcash address formats (such - /// as beginning with a `z`). This can either be treated as an indication that this - /// library dependency should be updated, or mapped to [`ParseError::NotZcash`]. + /// - In all other cases, [`ParseError::NotZcash`] will be returned on failure. /// /// # Examples /// @@ -87,7 +81,7 @@ impl ZcashAddress { match self.kind { AddressKind::Sprout(data) => T::from_sprout(self.net, data), AddressKind::Sapling(data) => T::from_sapling(self.net, data), - AddressKind::Orchard(data) => T::from_orchard(self.net, data), + AddressKind::Unified(data) => T::from_unified(self.net, data), AddressKind::P2pkh(data) => T::from_transparent_p2pkh(self.net, data), AddressKind::P2sh(data) => T::from_transparent_p2sh(self.net, data), } From 6717cd821c6e61ced2b02c18c115a29893cf45ac Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 20 May 2021 17:11:12 +0100 Subject: [PATCH 0044/2028] Move F4Jumble implementation into zcash_address --- components/zcash_address/Cargo.toml | 4 ++++ components/zcash_address/src/kind/unified.rs | 2 ++ .../zcash_address/src/kind/unified}/f4jumble.rs | 0 .../zcash_address/src/kind/unified}/f4jumble/test_vectors.rs | 0 zcash_primitives/src/address.rs | 2 -- zcash_primitives/src/lib.rs | 1 - 6 files changed, 6 insertions(+), 3 deletions(-) rename {zcash_primitives/src/address => components/zcash_address/src/kind/unified}/f4jumble.rs (100%) rename {zcash_primitives/src/address => components/zcash_address/src/kind/unified}/f4jumble/test_vectors.rs (100%) delete mode 100644 zcash_primitives/src/address.rs diff --git a/components/zcash_address/Cargo.toml b/components/zcash_address/Cargo.toml index bac4703acc..a02fc1feb6 100644 --- a/components/zcash_address/Cargo.toml +++ b/components/zcash_address/Cargo.toml @@ -13,4 +13,8 @@ edition = "2018" [dependencies] bech32 = "0.8" +blake2b_simd = "0.5" bs58 = { version = "0.4", features = ["check"] } + +[dev-dependencies] +proptest = "0.10.1" diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs index 35310099d3..bcc8668c3f 100644 --- a/components/zcash_address/src/kind/unified.rs +++ b/components/zcash_address/src/kind/unified.rs @@ -1,3 +1,5 @@ +mod f4jumble; + /// The HRP for a Bech32m-encoded mainnet Unified Address. /// /// Defined in [ZIP 316][zip-0316]. diff --git a/zcash_primitives/src/address/f4jumble.rs b/components/zcash_address/src/kind/unified/f4jumble.rs similarity index 100% rename from zcash_primitives/src/address/f4jumble.rs rename to components/zcash_address/src/kind/unified/f4jumble.rs diff --git a/zcash_primitives/src/address/f4jumble/test_vectors.rs b/components/zcash_address/src/kind/unified/f4jumble/test_vectors.rs similarity index 100% rename from zcash_primitives/src/address/f4jumble/test_vectors.rs rename to components/zcash_address/src/kind/unified/f4jumble/test_vectors.rs diff --git a/zcash_primitives/src/address.rs b/zcash_primitives/src/address.rs deleted file mode 100644 index 17613516d4..0000000000 --- a/zcash_primitives/src/address.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Types and algorithms used in support of Zcash Unified Addresses -pub mod f4jumble; diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 8bd5de7010..a2a113d404 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -9,7 +9,6 @@ // Temporary until we have addressed all Result cases. #![allow(clippy::result_unit_err)] -pub mod address; pub mod block; pub mod consensus; pub mod constants; From e982d7211f1c081ff65dd34ea6a87c7fc48f7f9b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 20 May 2021 17:28:35 +0100 Subject: [PATCH 0045/2028] zcash_address: Implement Unified Address encoding / decoding --- components/zcash_address/src/convert.rs | 6 +- components/zcash_address/src/encoding.rs | 27 ++-- components/zcash_address/src/kind.rs | 2 +- components/zcash_address/src/kind/unified.rs | 124 ++++++++++++++++++- components/zcash_address/src/lib.rs | 7 +- 5 files changed, 145 insertions(+), 21 deletions(-) diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs index 476140f6d6..ce29a0c3cc 100644 --- a/components/zcash_address/src/convert.rs +++ b/components/zcash_address/src/convert.rs @@ -59,7 +59,7 @@ pub trait FromAddress: Sized { Err(UnsupportedAddress("Sapling")) } - fn from_unified(net: Network, data: unified::Data) -> Result { + fn from_unified(net: Network, data: unified::Address) -> Result { let _ = (net, data); Err(UnsupportedAddress("Unified")) } @@ -111,7 +111,7 @@ pub trait ToAddress: private::Sealed { fn from_sapling(net: Network, data: sapling::Data) -> Self; - fn from_unified(net: Network, data: unified::Data) -> Self; + fn from_unified(net: Network, data: unified::Address) -> Self; fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Self; @@ -137,7 +137,7 @@ impl ToAddress for ZcashAddress { } } - fn from_unified(net: Network, data: unified::Data) -> Self { + fn from_unified(net: Network, data: unified::Address) -> Self { ZcashAddress { net, kind: AddressKind::Unified(data), diff --git a/components/zcash_address/src/encoding.rs b/components/zcash_address/src/encoding.rs index 8195f8daaa..56adbd0396 100644 --- a/components/zcash_address/src/encoding.rs +++ b/components/zcash_address/src/encoding.rs @@ -122,14 +122,14 @@ fn encode_b58(prefix: [u8; 2], data: &[u8]) -> String { impl fmt::Display for ZcashAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let encoded = match self.kind { + let encoded = match &self.kind { AddressKind::Sprout(data) => encode_b58( if let Network::Main = self.net { sprout::MAINNET } else { sprout::TESTNET }, - &data, + data, ), AddressKind::Sapling(data) => encode_bech32( match self.net { @@ -137,7 +137,7 @@ impl fmt::Display for ZcashAddress { Network::Test => sapling::TESTNET, Network::Regtest => sapling::REGTEST, }, - &data, + data, ), AddressKind::Unified(data) => encode_bech32m( match self.net { @@ -145,7 +145,7 @@ impl fmt::Display for ZcashAddress { Network::Test => unified::TESTNET, Network::Regtest => unified::REGTEST, }, - &data, + &data.to_bytes(), ), AddressKind::P2pkh(data) => encode_b58( if let Network::Main = self.net { @@ -153,7 +153,7 @@ impl fmt::Display for ZcashAddress { } else { p2pkh::TESTNET }, - &data, + data, ), AddressKind::P2sh(data) => encode_b58( if let Network::Main = self.net { @@ -161,7 +161,7 @@ impl fmt::Display for ZcashAddress { } else { p2sh::TESTNET }, - &data, + data, ), }; write!(f, "{}", encoded) @@ -171,6 +171,7 @@ impl fmt::Display for ZcashAddress { #[cfg(test)] mod tests { use super::*; + use crate::kind::unified; fn encoding(encoded: &str, decoded: ZcashAddress) { assert_eq!(decoded.to_string(), encoded); @@ -217,24 +218,26 @@ mod tests { #[test] fn unified() { encoding( - "zo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq58lk79", + "u1cd8yzk5mdn4n9r8c24tp8j8e9ethw3rr7ker5zhew3kycyyxggdzfkcq5f9yf2jv8m5ar8krncsntlfpx3p4azvwrkp8z74t3vu4kqq2", ZcashAddress { net: Network::Main, - kind: AddressKind::Unified([0; 43]), + kind: AddressKind::Unified(unified::Address(vec![unified::Receiver::Sapling( + [0; 43], + )])), }, ); encoding( - "ztestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcrmt3p", + "utest1cd8yzk5mdn4n9r8c24tp8j8e9ethw3rr7ker5zhew3kycyyxggdzfkcq5f9yf2jv8m5ar8krncsntlfpx3p4azvwrkp8z74t3vptphj8", ZcashAddress { net: Network::Test, - kind: AddressKind::Unified([0; 43]), + kind: AddressKind::Unified(unified::Address(vec![unified::Receiver::Sapling([0; 43])])), }, ); encoding( - "zregtestorchard1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq88jxqx", + "uregtest1cd8yzk5mdn4n9r8c24tp8j8e9ethw3rr7ker5zhew3kycyyxggdzfkcq5f9yf2jv8m5ar8krncsntlfpx3p4azvwrkp8z74t3vsnt5j0", ZcashAddress { net: Network::Regtest, - kind: AddressKind::Unified([0; 43]), + kind: AddressKind::Unified(unified::Address(vec![unified::Receiver::Sapling([0; 43])])), }, ); } diff --git a/components/zcash_address/src/kind.rs b/components/zcash_address/src/kind.rs index 3999fad39a..5397c027f8 100644 --- a/components/zcash_address/src/kind.rs +++ b/components/zcash_address/src/kind.rs @@ -1,4 +1,4 @@ -pub(crate) mod unified; +pub mod unified; pub(crate) mod sapling; pub(crate) mod sprout; diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs index bcc8668c3f..3e028b34f2 100644 --- a/components/zcash_address/src/kind/unified.rs +++ b/components/zcash_address/src/kind/unified.rs @@ -1,3 +1,8 @@ +use std::convert::{TryFrom, TryInto}; +use std::iter; + +use crate::{kind, ParseError}; + mod f4jumble; /// The HRP for a Bech32m-encoded mainnet Unified Address. @@ -17,5 +22,120 @@ pub(crate) const TESTNET: &str = "utest"; /// The HRP for a Bech32m-encoded regtest Unified Address. pub(crate) const REGTEST: &str = "uregtest"; -/// TODO -pub(crate) type Data = [u8; 43]; +const PADDING_LEN: usize = 16; + +/// The set of known Receivers for Unified Addresses. +/// +/// This enum is an internal-only type, and is maintained in preference order, so that the +/// derived [`PartialOrd`] will sort receivers correctly. From its documentation: +/// +/// > When derived on enums, variants are ordered by their top-to-bottom discriminant +/// > order. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Receiver { + Orchard([u8; 43]), + Sapling(kind::sapling::Data), + P2pkh(kind::p2pkh::Data), + P2sh(kind::p2sh::Data), + Unknown { typecode: u8, data: Vec }, +} + +impl TryFrom<(u8, &[u8])> for Receiver { + type Error = ParseError; + + fn try_from((typecode, addr): (u8, &[u8])) -> Result { + match typecode { + 0x00 => addr.try_into().map(Receiver::P2pkh), + 0x01 => addr.try_into().map(Receiver::P2sh), + 0x02 => addr.try_into().map(Receiver::Sapling), + 0x03 => addr.try_into().map(Receiver::Orchard), + _ => Ok(Receiver::Unknown { + typecode, + data: addr.to_vec(), + }), + } + .map_err(|_| ParseError::InvalidEncoding) + } +} + +impl Receiver { + fn typecode(&self) -> u8 { + match self { + Receiver::P2pkh(_) => 0x00, + Receiver::P2sh(_) => 0x01, + Receiver::Sapling(_) => 0x02, + Receiver::Orchard(_) => 0x03, + Receiver::Unknown { typecode, .. } => *typecode, + } + } + + fn addr(&self) -> &[u8] { + match self { + Receiver::P2pkh(data) => data, + Receiver::P2sh(data) => data, + Receiver::Sapling(data) => data, + Receiver::Orchard(data) => data, + Receiver::Unknown { data, .. } => data, + } + } +} + +/// A Unified Address. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Address(pub(crate) Vec); + +impl TryFrom<&[u8]> for Address { + type Error = ParseError; + + fn try_from(buf: &[u8]) -> Result { + let encoded = f4jumble::f4jumble_inv(buf).ok_or(ParseError::InvalidEncoding)?; + + // Validate and strip trailing zero bytes. + let encoded = match encoded.split_at(encoded.len() - PADDING_LEN) { + (encoded, tail) if tail == &[0; PADDING_LEN][..] => Ok(encoded), + _ => Err(ParseError::InvalidEncoding), + }?; + + iter::repeat(()) + .scan(encoded, |encoded, _| match encoded { + // Base case: we've parsed the full encoding. + [] => None, + // The raw encoding of a Unified Address is a concatenation of: + // - typecode: byte + // - length: byte + // - addr: byte[length] + [typecode, length, data @ ..] if data.len() >= *length as usize => { + let (addr, rest) = data.split_at(*length as usize); + *encoded = rest; + Some((*typecode, addr).try_into()) + } + // The encoding is truncated. + _ => Some(Err(ParseError::InvalidEncoding)), + }) + .collect::>() + .map(Address) + } +} + +impl Address { + /// Returns the raw encoding of this Unified Address. + pub(crate) fn to_bytes(&self) -> Vec { + self.0 + .iter() + .flat_map(|receiver| { + let addr = receiver.addr(); + // Holds by construction. + assert!(addr.len() < 256); + + let encoded: Vec<_> = iter::empty() + .chain(Some(receiver.typecode())) + .chain(Some(addr.len() as u8)) + .chain(addr.into_iter().cloned()) + .chain(iter::repeat(0).take(PADDING_LEN)) + .collect(); + + f4jumble::f4jumble(&encoded).unwrap() + }) + .collect() + } +} diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 8f8841aa1e..f461d2e856 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -4,9 +4,10 @@ mod kind; pub use convert::{FromAddress, ToAddress, UnsupportedAddress}; pub use encoding::ParseError; +pub use kind::unified; /// A Zcash address. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ZcashAddress { net: Network, kind: AddressKind, @@ -27,11 +28,11 @@ pub enum Network { } /// Known kinds of Zcash addresses. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Sprout(kind::sprout::Data), Sapling(kind::sapling::Data), - Unified(kind::unified::Data), + Unified(unified::Address), P2pkh(kind::p2pkh::Data), P2sh(kind::p2sh::Data), } From c2b57048bf3f0bdf889e5a52084bfb75c9bce942 Mon Sep 17 00:00:00 2001 From: str4d Date: Tue, 25 May 2021 16:37:25 +0100 Subject: [PATCH 0046/2028] Update components/zcash_address/README.md Co-authored-by: Daira Hopwood --- components/zcash_address/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/zcash_address/README.md b/components/zcash_address/README.md index 46867e3e91..ead489de4d 100644 --- a/components/zcash_address/README.md +++ b/components/zcash_address/README.md @@ -1,6 +1,7 @@ # zcash_address -TBD +Zcash address parsing and serialization. This library allows its users to easily +recognize and give good error messages for new Zcash address types. ## License From ff8695de03f03ec7aed24fdb21bb78200f0a326b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 25 May 2021 20:03:25 +0100 Subject: [PATCH 0047/2028] zcash_address: Add failing test showing unified::Address encoding bug --- .../proptest-regressions/kind/unified.txt | 7 +++ components/zcash_address/src/kind/unified.rs | 55 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 components/zcash_address/proptest-regressions/kind/unified.txt diff --git a/components/zcash_address/proptest-regressions/kind/unified.txt b/components/zcash_address/proptest-regressions/kind/unified.txt new file mode 100644 index 0000000000..f70dff62c0 --- /dev/null +++ b/components/zcash_address/proptest-regressions/kind/unified.txt @@ -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 e08469bc301313ef868b97a5c37d9a9746d9720c915a9127c89db25c3be778fd # shrinks to ua = Address([Sapling([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]), P2pkh([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])]) diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs index 3e028b34f2..560208802d 100644 --- a/components/zcash_address/src/kind/unified.rs +++ b/components/zcash_address/src/kind/unified.rs @@ -139,3 +139,58 @@ impl Address { .collect() } } + +#[cfg(test)] +mod tests { + use std::convert::TryFrom; + + use proptest::{ + array::{uniform11, uniform20, uniform32}, + prelude::*, + }; + + use super::{Address, Receiver}; + + prop_compose! { + fn uniform43()(a in uniform11(0u8..), b in uniform32(0u8..)) -> [u8; 43] { + let mut c = [0; 43]; + c[..11].copy_from_slice(&a); + c[11..].copy_from_slice(&b); + c + } + } + + fn arb_shielded_receiver() -> BoxedStrategy { + prop_oneof![ + uniform43().prop_map(Receiver::Sapling), + uniform43().prop_map(Receiver::Orchard), + ] + .boxed() + } + + fn arb_transparent_receiver() -> BoxedStrategy { + prop_oneof![ + uniform20(0u8..).prop_map(Receiver::P2pkh), + uniform20(0u8..).prop_map(Receiver::P2sh), + ] + .boxed() + } + + prop_compose! { + fn arb_unified_address()( + shielded in prop::collection::hash_set(arb_shielded_receiver(), 1..2), + transparent in prop::option::of(arb_transparent_receiver()), + ) -> Address { + Address(shielded.into_iter().chain(transparent).collect()) + } + } + + proptest! { + #[test] + fn ua_roundtrip(ua in arb_unified_address()) { + let bytes = ua.to_bytes(); + let decoded = Address::try_from(&bytes[..]); + prop_assert_eq!(decoded, Ok(ua)); + } + } +} From ff94f66d8e2b6e6efd89bfd2388d5a2c4c5887f2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 25 May 2021 21:23:15 +0100 Subject: [PATCH 0048/2028] zcash_address: Fix padding and F4Jumble positions in Address::to_bytes These need to be applied to the entire UA encoding, not to the encoding of each individual receiver. --- components/zcash_address/src/kind/unified.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs index 560208802d..5c7b5e6a89 100644 --- a/components/zcash_address/src/kind/unified.rs +++ b/components/zcash_address/src/kind/unified.rs @@ -120,23 +120,23 @@ impl TryFrom<&[u8]> for Address { impl Address { /// Returns the raw encoding of this Unified Address. pub(crate) fn to_bytes(&self) -> Vec { - self.0 + let encoded: Vec<_> = self + .0 .iter() .flat_map(|receiver| { let addr = receiver.addr(); // Holds by construction. assert!(addr.len() < 256); - let encoded: Vec<_> = iter::empty() + iter::empty() .chain(Some(receiver.typecode())) .chain(Some(addr.len() as u8)) .chain(addr.into_iter().cloned()) - .chain(iter::repeat(0).take(PADDING_LEN)) - .collect(); - - f4jumble::f4jumble(&encoded).unwrap() }) - .collect() + .chain(iter::repeat(0).take(PADDING_LEN)) + .collect(); + + f4jumble::f4jumble(&encoded).unwrap() } } From d031dabcfc760a5665e23fd7f0fdf3720ecf1f2e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 26 May 2021 21:58:08 +0100 Subject: [PATCH 0049/2028] Builder: Move progress notifier configuration to a builder method This is what builder methods are for :) and it helps to limit the growth of alternate `build` methods. --- zcash_primitives/src/transaction/builder.rs | 36 ++++++++------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 8ba242d2d6..280dffbd57 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -407,6 +407,7 @@ pub struct Builder<'a, P: consensus::Parameters, R: RngCore> { #[cfg(feature = "zfuture")] tze_inputs: TzeInputs<'a, TransactionData>, change_address: Option<(OutgoingViewingKey, PaymentAddress)>, + progress_notifier: Option>, _phantom: &'a PhantomData

, } @@ -504,6 +505,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { #[cfg(feature = "zfuture")] tze_inputs: TzeInputs::default(), change_address: None, + progress_notifier: None, _phantom: &PhantomData, } } @@ -610,21 +612,14 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { self.change_address = Some((ovk, to)); } - /// Builds a transaction from the configured spends and outputs. - /// - /// Upon success, returns a tuple containing the final transaction, and the - /// [`TransactionMetadata`] generated during the build process. + /// Sets the notifier channel, where progress of building the transaction is sent. /// - /// `consensus_branch_id` must be valid for the block height that this transaction is - /// targeting. An invalid `consensus_branch_id` will *not* result in an error from - /// this function, and instead will generate a transaction that will be rejected by - /// the network. - pub fn build( - self, - consensus_branch_id: consensus::BranchId, - prover: &impl TxProver, - ) -> Result<(Transaction, TransactionMetadata), Error> { - self.build_with_progress_notifier(consensus_branch_id, prover, None) + /// An update is sent after every Spend or Output is computed, and the `u32` sent + /// represents the total steps completed so far. It will eventually send number of + /// spends + outputs. If there's an error building the transaction, the channel is + /// closed. + pub fn with_progress_notifier(&mut self, progress_notifier: Sender) { + self.progress_notifier = Some(progress_notifier); } /// Builds a transaction from the configured spends and outputs. @@ -636,16 +631,10 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { /// targeting. An invalid `consensus_branch_id` will *not* result in an error from /// this function, and instead will generate a transaction that will be rejected by /// the network. - /// - /// `progress_notifier` sets the notifier channel, where progress of building the transaction is sent. - /// It sends an update after every Spend or Output is computed, and the `u32` sent represents - /// the total steps completed so far. It will eventually send number of spends + outputs. - /// If there's an error building the transaction, the channel is closed. - pub fn build_with_progress_notifier( + pub fn build( mut self, consensus_branch_id: consensus::BranchId, prover: &impl TxProver, - progress_notifier: Option>, ) -> Result<(Transaction, TransactionMetadata), Error> { let mut tx_metadata = TransactionMetadata::new(); @@ -775,7 +764,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { // Update progress and send a notification on the channel progress += 1; - progress_notifier + self.progress_notifier .as_ref() .map(|tx| tx.send(Progress::new(progress, Some(total_progress)))); @@ -861,7 +850,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { // Update progress and send a notification on the channel progress += 1; - progress_notifier + self.progress_notifier .as_ref() .map(|tx| tx.send(Progress::new(progress, Some(total_progress)))); @@ -1085,6 +1074,7 @@ mod tests { #[cfg(feature = "zfuture")] tze_inputs: TzeInputs::default(), change_address: None, + progress_notifier: None, _phantom: &PhantomData, }; From 6f0e7c86a34a602a8d06534c999c1032e9aba8ba Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 26 May 2021 22:02:01 +0100 Subject: [PATCH 0050/2028] zcash_primitives: Add Builder::with_progress_notifier to changelog --- zcash_primitives/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index f87f53d1fd..3576f25ae9 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -6,6 +6,11 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `zcash_primitives::transaction::Builder::with_progress_notifier`, for setting + a notification channel on which transaction build progress updates will be + sent. + ### Changed - MSRV is now 1.51.0. - The following modules and helpers have been moved into From 16627b4569be155f9f9e45561119bdec75fe2bd0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 May 2021 22:33:00 +0100 Subject: [PATCH 0051/2028] zcash_note_encryption: Enforce ZIP 212 check on esk from outPlaintext It needs to equal the esk derived from the note (for v2 note plaintexts). --- components/zcash_note_encryption/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 481ee1e81e..95c725b209 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -64,7 +64,7 @@ pub enum NoteValidity { } pub trait Domain { - type EphemeralSecretKey; + type EphemeralSecretKey: ConstantTimeEq; type EphemeralPublicKey; type SharedSecret; type SymmetricKey: AsRef<[u8]>; @@ -490,6 +490,14 @@ pub fn try_output_recovery_with_ock>( domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, output.epk(), &plaintext)?; let memo = domain.extract_memo(&plaintext); + // ZIP 212: Check that the esk provided to this function is consistent with the esk we + // can derive from the note. + if let Some(derived_esk) = D::derive_esk(¬e) { + if (!derived_esk.ct_eq(&esk)).into() { + return None; + } + } + if let NoteValidity::Valid = check_note_validity::(¬e, output.epk(), &output.cmstar_bytes()) { From ee2b96c82d812cc9c36968d876b2f5a923637ac0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 May 2021 22:57:48 +0100 Subject: [PATCH 0052/2028] zcash_note_encryption: s/TryFrom/From on ExtractedCommitmentBytes bound This was left over from an earlier refactor where we could call a domain API to extract cmstar from a note commitment (which could fail for Orchard). This part of extraction was subsequently refactored into the domain logic (and is rejected earlier for Orchard). The resulting bound is wrong because it's always possible to serialize a scalar. --- components/zcash_note_encryption/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 95c725b209..7855349e99 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -5,7 +5,6 @@ use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use rand_core::RngCore; -use std::convert::TryFrom; use subtle::{Choice, ConstantTimeEq}; pub const COMPACT_NOTE_SIZE: usize = 1 + // version @@ -75,7 +74,7 @@ pub trait Domain { type OutgoingViewingKey; type ValueCommitment; type ExtractedCommitment; - type ExtractedCommitmentBytes: Eq + TryFrom; + type ExtractedCommitmentBytes: Eq + for<'a> From<&'a Self::ExtractedCommitment>; type Memo; fn derive_esk(note: &Self::Note) -> Option; @@ -384,9 +383,7 @@ fn check_note_validity( epk: &D::EphemeralPublicKey, cmstar_bytes: &D::ExtractedCommitmentBytes, ) -> NoteValidity { - if D::ExtractedCommitmentBytes::try_from(D::cmstar(¬e)) - .map_or(false, |cs| &cs == cmstar_bytes) - { + if &D::ExtractedCommitmentBytes::from(&D::cmstar(¬e)) == cmstar_bytes { let epk_bytes = D::epk_bytes(epk); D::check_epk_bytes(¬e, |derived_esk| { if D::epk_bytes(&D::ka_derive_public(¬e, &derived_esk)) From ae43e6c074dae82e9a2c8a578b57ef61091c9b1d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 May 2021 23:14:48 +0100 Subject: [PATCH 0053/2028] zcash_note_encryption: Pass cmstar_bytes to Domain::derive_ock PRF^ock in the spec takes cm* as a byte array. --- components/zcash_note_encryption/src/lib.rs | 4 ++-- zcash_primitives/src/sapling/note_encryption.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 7855349e99..eba324f772 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -110,7 +110,7 @@ pub trait Domain { fn derive_ock( ovk: &Self::OutgoingViewingKey, cv: &Self::ValueCommitment, - cmstar: &Self::ExtractedCommitment, + cmstar_bytes: &Self::ExtractedCommitmentBytes, ephemeral_key: &EphemeralKeyBytes, ) -> OutgoingCipherKey; @@ -291,7 +291,7 @@ impl NoteEncryption { rng: &mut R, ) -> [u8; OUT_CIPHERTEXT_SIZE] { let (ock, input) = if let Some(ovk) = &self.ovk { - let ock = D::derive_ock(ovk, &cv, &cmstar, &D::epk_bytes(&self.epk)); + let ock = D::derive_ock(ovk, &cv, &cmstar.into(), &D::epk_bytes(&self.epk)); let input = D::outgoing_plaintext_bytes(&self.note, &self.esk); (ock, input) diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index f50e2416d8..8ca78bacd3 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -54,7 +54,7 @@ fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, ephemeral_key: &EphemeralKeyByte pub fn prf_ock( ovk: &OutgoingViewingKey, cv: &jubjub::ExtendedPoint, - cmu: &bls12_381::Scalar, + cmu_bytes: &[u8; 32], ephemeral_key: &EphemeralKeyBytes, ) -> OutgoingCipherKey { OutgoingCipherKey( @@ -64,7 +64,7 @@ pub fn prf_ock( .to_state() .update(&ovk.0) .update(&cv.to_bytes()) - .update(&cmu.to_repr()) + .update(cmu_bytes) .update(ephemeral_key.as_ref()) .finalize() .as_bytes() @@ -209,10 +209,10 @@ impl Domain for SaplingDomain

{ fn derive_ock( ovk: &Self::OutgoingViewingKey, cv: &Self::ValueCommitment, - cmu: &Self::ExtractedCommitment, + cmu_bytes: &Self::ExtractedCommitmentBytes, epk: &EphemeralKeyBytes, ) -> OutgoingCipherKey { - prf_ock(ovk, cv, cmu, epk) + prf_ock(ovk, cv, cmu_bytes, epk) } fn outgoing_plaintext_bytes( @@ -413,7 +413,7 @@ pub fn try_sapling_output_recovery( &prf_ock( &ovk, &output.cv, - &output.cmu, + &output.cmu.to_repr(), &epk_bytes(&output.ephemeral_key), ), output, @@ -524,7 +524,7 @@ mod tests { &mut rng, ); let epk = *ne.epk(); - let ock = prf_ock(&ovk, &cv, &cmu, &epk_bytes(&epk)); + let ock = prf_ock(&ovk, &cv, &cmu.to_repr(), &epk_bytes(&epk)); let output = OutputDescription { cv, @@ -547,7 +547,7 @@ mod tests { out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE], modify_plaintext: impl Fn(&mut [u8; NOTE_PLAINTEXT_SIZE]), ) { - let ock = prf_ock(&ovk, &cv, &cmu, &epk_bytes(epk)); + let ock = prf_ock(&ovk, &cv, &cmu.to_repr(), &epk_bytes(epk)); let mut op = [0; OUT_CIPHERTEXT_SIZE]; assert_eq!( @@ -1279,7 +1279,7 @@ mod tests { assert_eq!(k_enc.as_bytes(), tv.k_enc); let ovk = OutgoingViewingKey(tv.ovk); - let ock = prf_ock(&ovk, &cv, &cmu, &epk_bytes(&epk)); + let ock = prf_ock(&ovk, &cv, &cmu.to_repr(), &epk_bytes(&epk)); assert_eq!(ock.as_ref(), tv.ock); let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); From f6705f23c3cafb03c5c93e5c3c88c35cf8803c17 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 May 2021 23:45:23 +0100 Subject: [PATCH 0054/2028] zcash_note_encryption: Add ovk recovery API --- components/zcash_note_encryption/src/lib.rs | 25 +++++++++++++++++++ .../src/sapling/note_encryption.rs | 23 +++++++---------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index eba324f772..215b7119fd 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -434,6 +434,31 @@ pub fn try_compact_note_decryption>( ) } +/// Recovery of the full note plaintext by the sender. +/// +/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. +/// If successful, the corresponding note and memo are returned, along with the address to +/// which the note was sent. +/// +/// Implements [Zcash Protocol Specification section 4.19.3][decryptovk]. +/// +/// [decryptovk]: https://zips.z.cash/protocol/nu5.pdf#decryptovk +pub fn try_output_recovery_with_ovk>( + domain: &D, + ovk: &D::OutgoingViewingKey, + output: &Output, + cv: &D::ValueCommitment, + out_ciphertext: &[u8], +) -> Option<(D::Note, D::Recipient, D::Memo)> { + let ock = D::derive_ock( + ovk, + &cv, + &output.cmstar_bytes(), + &D::epk_bytes(&output.epk()), + ); + try_output_recovery_with_ock(domain, &ock, output, out_ciphertext) +} + /// Recovery of the full note plaintext by the sender. /// /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ock`. diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index 8ca78bacd3..c6de6a9978 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -7,10 +7,10 @@ use rand_core::RngCore; use std::convert::TryInto; use zcash_note_encryption::{ - try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ock, Domain, - EphemeralKeyBytes, NoteEncryption, NotePlaintextBytes, NoteValidity, OutPlaintextBytes, - OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, - OUT_PLAINTEXT_SIZE, + try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ock, + try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, NoteEncryption, NotePlaintextBytes, + NoteValidity, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, + NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; use crate::{ @@ -407,17 +407,12 @@ pub fn try_sapling_output_recovery( ovk: &OutgoingViewingKey, output: &OutputDescription, ) -> Option<(Note, PaymentAddress, MemoBytes)> { - try_sapling_output_recovery_with_ock( - params, + let domain = SaplingDomain { + params: params.clone(), height, - &prf_ock( - &ovk, - &output.cv, - &output.cmu.to_repr(), - &epk_bytes(&output.ephemeral_key), - ), - output, - ) + }; + + try_output_recovery_with_ovk(&domain, ovk, output, &output.cv, &output.out_ciphertext) } #[cfg(test)] From 362838c3fabc8aaee5e86cf6975e3fececff10be Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 May 2021 23:48:03 +0100 Subject: [PATCH 0055/2028] zcash_note_encryption: Fix array size in Domain::extract_{esk, pk_d} Decrypted output size is `OUT_PLAINTEXT_BYTES`, which the decryptor can always provide (either by decrypting into the correct size array as now, or truncating the buffer before passing it to the domain). --- components/zcash_note_encryption/src/lib.rs | 6 +++--- zcash_primitives/src/sapling/note_encryption.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 215b7119fd..b67afcfa29 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -148,10 +148,10 @@ pub trait Domain { fn extract_memo(&self, plaintext: &[u8]) -> Self::Memo; fn extract_pk_d( - out_plaintext: &[u8; OUT_CIPHERTEXT_SIZE], + out_plaintext: &[u8; OUT_PLAINTEXT_SIZE], ) -> Option; - fn extract_esk(out_plaintext: &[u8; OUT_CIPHERTEXT_SIZE]) -> Option; + fn extract_esk(out_plaintext: &[u8; OUT_PLAINTEXT_SIZE]) -> Option; } pub trait ShieldedOutput { @@ -477,7 +477,7 @@ pub fn try_output_recovery_with_ock>( assert_eq!(output.enc_ciphertext().len(), ENC_CIPHERTEXT_SIZE); assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE); - let mut op = [0; OUT_CIPHERTEXT_SIZE]; + let mut op = [0; OUT_PLAINTEXT_SIZE]; assert_eq!( ChachaPolyIetf::aead_cipher() .open_to(&mut op, &out_ciphertext, &[], ock.as_ref(), &[0u8; 12]) diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index c6de6a9978..c2d9f85d8b 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -10,7 +10,7 @@ use zcash_note_encryption::{ try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ock, try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, NoteEncryption, NotePlaintextBytes, NoteValidity, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, - NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, + NOTE_PLAINTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; use crate::{ @@ -272,7 +272,7 @@ impl Domain for SaplingDomain

{ note.cmu() } - fn extract_pk_d(op: &[u8; OUT_CIPHERTEXT_SIZE]) -> Option { + fn extract_pk_d(op: &[u8; OUT_PLAINTEXT_SIZE]) -> Option { let pk_d = jubjub::SubgroupPoint::from_bytes( op[0..32].try_into().expect("slice is the correct length"), ); @@ -284,7 +284,7 @@ impl Domain for SaplingDomain

{ } } - fn extract_esk(op: &[u8; OUT_CIPHERTEXT_SIZE]) -> Option { + fn extract_esk(op: &[u8; OUT_PLAINTEXT_SIZE]) -> Option { jubjub::Fr::from_repr( op[32..OUT_PLAINTEXT_SIZE] .try_into() From eba6f417fec632d744bbd541710b4d74f44446a6 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 29 May 2021 00:15:25 +0100 Subject: [PATCH 0056/2028] zcash_note_encryption: Doc fixes --- components/zcash_note_encryption/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index b67afcfa29..5c4ccbb796 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -321,11 +321,11 @@ impl NoteEncryption { /// Trial decryption of the full note plaintext by the recipient. /// /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ivk`. -/// If successful, the corresponding Sapling note and memo are returned, along with the -/// `PaymentAddress` to which the note was sent. +/// If successful, the corresponding note and memo are returned, along with the address to +/// which the note was sent. /// /// Implements section 4.19.2 of the -/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptivk) +/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptivk). pub fn try_note_decryption>( domain: &D, ivk: &D::IncomingViewingKey, @@ -404,8 +404,8 @@ fn check_note_validity( /// Trial decryption of the compact note plaintext by the recipient for light clients. /// /// Attempts to decrypt and validate the first 52 bytes of `enc_ciphertext` using the -/// given `ivk`. If successful, the corresponding Sapling note is returned, along with the -/// `PaymentAddress` to which the note was sent. +/// given `ivk`. If successful, the corresponding note is returned, along with the address +/// to which the note was sent. /// /// Implements the procedure specified in [`ZIP 307`]. /// @@ -462,12 +462,12 @@ pub fn try_output_recovery_with_ovk>( /// Recovery of the full note plaintext by the sender. /// /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ock`. -/// If successful, the corresponding Sapling note and memo are returned, along with the -/// `PaymentAddress` to which the note was sent. +/// If successful, the corresponding note and memo are returned, along with the address to +/// which the note was sent. /// /// Implements part of section 4.19.3 of the -/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptovk) -/// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. +/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptovk). +/// For decryption using a Full Viewing Key see [`try_output_recovery_with_ovk`]. pub fn try_output_recovery_with_ock>( domain: &D, ock: &OutgoingCipherKey, From 4efb21d1c7639a309fb43dc52dab921a685d3a94 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 11 May 2021 09:57:28 -0600 Subject: [PATCH 0057/2028] Make amount addition and subtraction traits use checked operations. --- zcash_client_backend/Cargo.toml | 2 +- zcash_client_backend/src/data_api/error.rs | 7 ++ zcash_client_backend/src/data_api/wallet.rs | 8 ++- zcash_client_sqlite/src/chain.rs | 25 +++++-- zcash_client_sqlite/src/lib.rs | 2 +- zcash_client_sqlite/src/wallet/transact.rs | 5 +- zcash_extensions/src/transparent/demo.rs | 4 +- zcash_primitives/Cargo.toml | 11 ++- zcash_primitives/src/transaction/builder.rs | 36 +++++++--- .../src/transaction/components/amount.rs | 69 ++++++++++++++----- 10 files changed, 128 insertions(+), 41 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index ef1e450a1f..1c5e21f823 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -23,7 +23,7 @@ hex = "0.4" jubjub = "0.6" nom = "6.1" percent-encoding = "2.1.0" -proptest = { version = "0.10.1", optional = true } +proptest = { version = "1.0.0", optional = true } protobuf = "2.20" rand_core = "0.6" subtle = "2.2.3" diff --git a/zcash_client_backend/src/data_api/error.rs b/zcash_client_backend/src/data_api/error.rs index 1968af8f94..bd4241d91e 100644 --- a/zcash_client_backend/src/data_api/error.rs +++ b/zcash_client_backend/src/data_api/error.rs @@ -24,6 +24,9 @@ pub enum ChainInvalid { #[derive(Debug)] pub enum Error { + /// The amount specified exceeds the allowed range. + InvalidAmount, + /// Unable to create a new spend because the wallet balance is not sufficient. InsufficientBalance(Amount, Amount), @@ -72,6 +75,10 @@ impl ChainInvalid { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self { + Error::InvalidAmount => write!( + f, + "The value lies outside the valid range of Zcash amounts." + ), Error::InsufficientBalance(have, need) => write!( f, "Insufficient balance (have {}, need {} including fee)", diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 82a9a9a9b5..c8b5e5acbd 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -183,11 +183,15 @@ where .get_target_and_anchor_heights() .and_then(|x| x.ok_or_else(|| Error::ScanRequired.into()))?; - let target_value = value + DEFAULT_FEE; + let target_value = (value + DEFAULT_FEE).ok_or_else(|| E::from(Error::InvalidAmount))?; let spendable_notes = wallet_db.select_spendable_notes(account, target_value, anchor_height)?; // Confirm we were able to select sufficient value - let selected_value = spendable_notes.iter().map(|n| n.note_value).sum(); + let selected_value = spendable_notes + .iter() + .map(|n| n.note_value) + .sum::>() + .ok_or_else(|| E::from(Error::InvalidAmount))?; if selected_value < target_value { return Err(E::from(Error::InsufficientBalance( selected_value, diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index b416ebe889..1b3692239c 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -358,13 +358,19 @@ mod tests { scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); // Account balance should reflect both received notes - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value + value2); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value + value2).unwrap() + ); // "Rewind" to height of last scanned block rewind_to_height(&db_data, sapling_activation_height() + 1).unwrap(); // Account balance should be unaltered - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value + value2); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value + value2).unwrap() + ); // Rewind so that one block is dropped rewind_to_height(&db_data, sapling_activation_height()).unwrap(); @@ -376,7 +382,10 @@ mod tests { scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); // Account balance should again reflect both received notes - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value + value2); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value + value2).unwrap() + ); } #[test] @@ -485,7 +494,10 @@ mod tests { scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); // Account balance should reflect both received notes - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value + value2); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value + value2).unwrap() + ); } #[test] @@ -543,6 +555,9 @@ mod tests { scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); // Account balance should equal the change - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value - value2); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value - value2).unwrap() + ); } } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 0d9047574b..b9629e1cfc 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -701,7 +701,7 @@ mod tests { let note = Note { g_d: change_addr.diversifier().g_d().unwrap(), pk_d: *change_addr.pk_d(), - value: (in_value - value).into(), + value: (in_value - value).unwrap().into(), rseed, }; let encryptor = sapling_note_encryption::<_, Network>( diff --git a/zcash_client_sqlite/src/wallet/transact.rs b/zcash_client_sqlite/src/wallet/transact.rs index 5fccb430f3..129ff1fea7 100644 --- a/zcash_client_sqlite/src/wallet/transact.rs +++ b/zcash_client_sqlite/src/wallet/transact.rs @@ -356,7 +356,10 @@ mod tests { // Verified balance does not include the second note let (_, anchor_height2) = (&db_data).get_target_and_anchor_heights().unwrap().unwrap(); - assert_eq!(get_balance(&db_data, AccountId(0)).unwrap(), value + value); + assert_eq!( + get_balance(&db_data, AccountId(0)).unwrap(), + (value + value).unwrap() + ); assert_eq!( get_balance_at(&db_data, AccountId(0), anchor_height2).unwrap(), value diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 6109ec8585..741f79cd97 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -740,7 +740,7 @@ mod tests { TzeOutPoint::new(tx_a.txid().0, 0), tx_a.tze_outputs[0].clone(), ); - let value_xfr = value - DEFAULT_FEE; + let value_xfr = (value - DEFAULT_FEE).unwrap(); db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, h2) .map_err(|e| format!("transfer failure: {:?}", e)) .unwrap(); @@ -769,7 +769,7 @@ mod tests { builder_c .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), - value_xfr - DEFAULT_FEE, + (value_xfr - DEFAULT_FEE).unwrap(), ) .unwrap(); diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index e62ec0cbcd..d5b910bbed 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -31,7 +31,9 @@ hex = "0.4" jubjub = "0.6" lazy_static = "1" log = "0.4" -proptest = { version = "0.10.1", optional = true } +nonempty = "0.6" +orchard = { git = "https://github.com/zcash/orchard", branch = "main" } +proptest = { version = "1.0.0", optional = true } rand = "0.8" rand_core = "0.6" ripemd160 = { version = "0.9", optional = true } @@ -43,11 +45,16 @@ zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encr # Temporary workaround for https://github.com/myrrlyn/funty/issues/3 funty = "=1.1.0" +[dependencies.pasta_curves] +git = "https://github.com/zcash/pasta_curves.git" +rev = "b55a6960dfafd7f767e2820ddf1adaa499322f98" + [dev-dependencies] criterion = "0.3" hex-literal = "0.3" -proptest = "0.10.1" +proptest = "1.0.0" rand_xorshift = "0.3" +orchard = { git = "https://github.com/zcash/orchard", branch = "main", features = ["test-dependencies"] } [features] transparent-inputs = ["ripemd160", "secp256k1"] diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 280dffbd57..cc6b260d8e 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -251,18 +251,18 @@ impl TransparentInputs { Ok(()) } - fn value_sum(&self) -> Amount { + fn value_sum(&self) -> Option { #[cfg(feature = "transparent-inputs")] { self.inputs .iter() .map(|input| input.coin.value) - .sum::() + .sum::>() } #[cfg(not(feature = "transparent-inputs"))] { - Amount::zero() + Some(Amount::zero()) } } @@ -643,8 +643,18 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { // // Valid change - let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() - - self.mtx.vout.iter().map(|vo| vo.value).sum::(); + let change = self.mtx.value_balance - self.fee + + self + .transparent_inputs + .value_sum() + .ok_or(Error::InvalidAmount)? + - self + .mtx + .vout + .iter() + .map(|vo| vo.value) + .sum::>() + .ok_or(Error::InvalidAmount)?; #[cfg(feature = "zfuture")] let change = change @@ -653,13 +663,17 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { .builders .iter() .map(|ein| ein.prevout.value) - .sum::() + .sum::>() + .ok_or(Error::InvalidAmount)? - self .mtx .tze_outputs .iter() .map(|tzo| tzo.value) - .sum::(); + .sum::>() + .ok_or(Error::InvalidAmount)?; + + let change = change.ok_or(Error::InvalidAmount)?; if change.is_negative() { return Err(Error::ChangeIsNegative(change)); @@ -1150,7 +1164,9 @@ mod tests { let builder = Builder::new(TEST_NETWORK, H0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), - Err(Error::ChangeIsNegative(Amount::zero() - DEFAULT_FEE)) + Err(Error::ChangeIsNegative( + (Amount::zero() - DEFAULT_FEE).unwrap() + )) ); } @@ -1168,7 +1184,7 @@ mod tests { assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative( - Amount::from_i64(-50000).unwrap() - DEFAULT_FEE + (Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap() )) ); } @@ -1186,7 +1202,7 @@ mod tests { assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative( - Amount::from_i64(-50000).unwrap() - DEFAULT_FEE + (Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap() )) ); } diff --git a/zcash_primitives/src/transaction/components/amount.rs b/zcash_primitives/src/transaction/components/amount.rs index f47b485f74..1fa376f622 100644 --- a/zcash_primitives/src/transaction/components/amount.rs +++ b/zcash_primitives/src/transaction/components/amount.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::iter::Sum; use std::ops::{Add, AddAssign, Sub, SubAssign}; @@ -101,6 +102,14 @@ impl Amount { } } +impl TryFrom for Amount { + type Error = (); + + fn try_from(value: i64) -> Result { + Amount::from_i64(value) + } +} + impl From for i64 { fn from(amount: Amount) -> i64 { amount.0 @@ -114,36 +123,52 @@ impl From for u64 { } impl Add for Amount { - type Output = Amount; + type Output = Option; + + fn add(self, rhs: Amount) -> Option { + Amount::from_i64(self.0 + rhs.0).ok() + } +} + +impl Add for Option { + type Output = Self; - fn add(self, rhs: Amount) -> Amount { - Amount::from_i64(self.0 + rhs.0).expect("addition should remain in range") + fn add(self, rhs: Amount) -> Option { + self.and_then(|lhs| lhs + rhs) } } impl AddAssign for Amount { fn add_assign(&mut self, rhs: Amount) { - *self = *self + rhs + *self = (*self + rhs).expect("Addition must produce a valid amount value.") } } impl Sub for Amount { - type Output = Amount; + type Output = Option; + + fn sub(self, rhs: Amount) -> Option { + Amount::from_i64(self.0 - rhs.0).ok() + } +} + +impl Sub for Option { + type Output = Self; - fn sub(self, rhs: Amount) -> Amount { - Amount::from_i64(self.0 - rhs.0).expect("subtraction should remain in range") + fn sub(self, rhs: Amount) -> Option { + self.and_then(|lhs| lhs - rhs) } } impl SubAssign for Amount { fn sub_assign(&mut self, rhs: Amount) { - *self = *self - rhs + *self = (*self - rhs).expect("Subtraction must produce a valid amount value.") } } -impl Sum for Amount { - fn sum>(iter: I) -> Amount { - iter.fold(Amount::zero(), Add::add) +impl Sum for Option { + fn sum>(iter: I) -> Self { + iter.fold(Some(Amount::zero()), |acc, a| acc? + a) } } @@ -153,11 +178,23 @@ pub mod testing { use super::{Amount, MAX_MONEY}; + prop_compose! { + pub fn arb_amount()(amt in -MAX_MONEY..MAX_MONEY) -> Amount { + Amount::from_i64(amt).unwrap() + } + } + prop_compose! { pub fn arb_nonnegative_amount()(amt in 0i64..MAX_MONEY) -> Amount { Amount::from_i64(amt).unwrap() } } + + prop_compose! { + pub fn arb_positive_amount()(amt in 1i64..MAX_MONEY) -> Amount { + Amount::from_i64(amt).unwrap() + } + } } #[cfg(test)] @@ -213,10 +250,9 @@ mod tests { } #[test] - #[should_panic] - fn add_panics_on_overflow() { + fn add_overflow() { let v = Amount(MAX_MONEY); - let _sum = v + Amount(1); + assert_eq!(v + Amount(1), None) } #[test] @@ -227,10 +263,9 @@ mod tests { } #[test] - #[should_panic] - fn sub_panics_on_underflow() { + fn sub_underflow() { let v = Amount(-MAX_MONEY); - let _diff = v - Amount(1); + assert_eq!(v - Amount(1), None) } #[test] From 3dc05a69eb691eaaf401813c7a35032b1e4317e9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 11 May 2021 10:10:21 -0600 Subject: [PATCH 0058/2028] Add Nu5 NetworkUpgrade variant. --- components/zcash_note_encryption/src/lib.rs | 1 - zcash_primitives/src/consensus.rs | 107 +++++++++++++++++++- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 481ee1e81e..3bece34f22 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -445,7 +445,6 @@ pub fn try_compact_note_decryption>( /// /// Implements part of section 4.19.3 of the /// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptovk) -/// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. pub fn try_output_recovery_with_ock>( domain: &D, ock: &OutgoingCipherKey, diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 4e5c49dd4b..02e3913f73 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -3,7 +3,7 @@ use std::cmp::{Ord, Ordering}; use std::convert::TryFrom; use std::fmt; -use std::ops::{Add, Sub}; +use std::ops::{Add, Bound, RangeBounds, Sub}; use crate::constants; @@ -195,6 +195,7 @@ impl Parameters for MainNetwork { NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), + NetworkUpgrade::Nu5 => None, #[cfg(feature = "zfuture")] NetworkUpgrade::ZFuture => None, } @@ -239,6 +240,7 @@ impl Parameters for TestNetwork { NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), + NetworkUpgrade::Nu5 => None, #[cfg(feature = "zfuture")] NetworkUpgrade::ZFuture => None, } @@ -352,6 +354,10 @@ pub enum NetworkUpgrade { /// /// [Canopy]: https://z.cash/upgrade/canopy/ Canopy, + /// The [Nu5] network upgrade. + /// + /// [Nu5]: https://z.cash/upgrade/nu5/ + Nu5, /// The ZFUTURE network upgrade. /// /// This upgrade is expected never to activate on mainnet; @@ -369,6 +375,7 @@ impl fmt::Display for NetworkUpgrade { NetworkUpgrade::Blossom => write!(f, "Blossom"), NetworkUpgrade::Heartwood => write!(f, "Heartwood"), NetworkUpgrade::Canopy => write!(f, "Canopy"), + NetworkUpgrade::Nu5 => write!(f, "Nu5"), #[cfg(feature = "zfuture")] NetworkUpgrade::ZFuture => write!(f, "ZFUTURE"), } @@ -383,6 +390,7 @@ impl NetworkUpgrade { NetworkUpgrade::Blossom => BranchId::Blossom, NetworkUpgrade::Heartwood => BranchId::Heartwood, NetworkUpgrade::Canopy => BranchId::Canopy, + NetworkUpgrade::Nu5 => BranchId::Nu5, #[cfg(feature = "zfuture")] NetworkUpgrade::ZFuture => BranchId::ZFuture, } @@ -399,6 +407,7 @@ const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ NetworkUpgrade::Blossom, NetworkUpgrade::Heartwood, NetworkUpgrade::Canopy, + NetworkUpgrade::Nu5, ]; pub const ZIP212_GRACE_PERIOD: u32 = 32256; @@ -430,6 +439,8 @@ pub enum BranchId { Heartwood, /// The consensus rules deployed by [`NetworkUpgrade::Canopy`]. Canopy, + /// The consensus rules deployed by [`NetworkUpgrade::Nu5`]. + Nu5, /// Candidates for future consensus rules; this branch will never /// activate on mainnet. #[cfg(feature = "zfuture")] @@ -447,6 +458,7 @@ impl TryFrom for BranchId { 0x2bb4_0e60 => Ok(BranchId::Blossom), 0xf5b9_230b => Ok(BranchId::Heartwood), 0xe9ff_75a6 => Ok(BranchId::Canopy), + 0xf919_a198 => Ok(BranchId::Nu5), #[cfg(feature = "zfuture")] 0xffff_ffff => Ok(BranchId::ZFuture), _ => Err("Unknown consensus branch ID"), @@ -463,6 +475,7 @@ impl From for u32 { BranchId::Blossom => 0x2bb4_0e60, BranchId::Heartwood => 0xf5b9_230b, BranchId::Canopy => 0xe9ff_75a6, + BranchId::Nu5 => 0xf919_a198, #[cfg(feature = "zfuture")] BranchId::ZFuture => 0xffff_ffff, } @@ -484,6 +497,94 @@ impl BranchId { // Sprout rules apply before any network upgrade BranchId::Sprout } + + /// Returns the range of heights for the consensus epoch associated with this branch id. + /// + /// The resulting tuple implements the [`RangeBounds`] trait. + pub fn height_range(&self, params: &P) -> Option> { + self.height_bounds(params).map(|(lower, upper)| { + ( + Bound::Included(lower), + upper.map_or(Bound::Unbounded, Bound::Excluded), + ) + }) + } + + /// Returns the range of heights for the consensus epoch associated with this branch id. + /// + /// The return type of this value is slightly more precise than [`Self::height_range`]. + pub fn height_bounds( + &self, + params: &P, + ) -> Option<(BlockHeight, Option)> { + match self { + BranchId::Sprout => params + .activation_height(NetworkUpgrade::Overwinter) + .map(|upper| (BlockHeight(0), Some(upper))), + BranchId::Overwinter => params + .activation_height(NetworkUpgrade::Overwinter) + .map(|lower| (lower, params.activation_height(NetworkUpgrade::Sapling))), + BranchId::Sapling => params + .activation_height(NetworkUpgrade::Sapling) + .map(|lower| (lower, params.activation_height(NetworkUpgrade::Blossom))), + BranchId::Blossom => params + .activation_height(NetworkUpgrade::Blossom) + .map(|lower| (lower, params.activation_height(NetworkUpgrade::Heartwood))), + BranchId::Heartwood => params + .activation_height(NetworkUpgrade::Heartwood) + .map(|lower| (lower, params.activation_height(NetworkUpgrade::Canopy))), + BranchId::Canopy => params + .activation_height(NetworkUpgrade::Canopy) + .map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu5))), + BranchId::Nu5 => params.activation_height(NetworkUpgrade::Nu5).map(|lower| { + #[cfg(feature = "zfuture")] + let upper = params.activation_height(NetworkUpgrade::ZFuture); + #[cfg(not(feature = "zfuture"))] + let upper = None; + (lower, upper) + }), + #[cfg(feature = "zfuture")] + BranchId::ZFuture => params + .activation_height(NetworkUpgrade::ZFuture) + .map(|lower| (lower, None)), + } + } +} + +#[cfg(any(test, feature = "test-dependencies"))] +pub mod testing { + use proptest::sample::select; + use proptest::strategy::{Just, Strategy}; + + use super::{BlockHeight, BranchId, Parameters}; + + pub fn arb_branch_id() -> impl Strategy { + select(vec![ + BranchId::Sprout, + BranchId::Overwinter, + BranchId::Sapling, + BranchId::Blossom, + BranchId::Heartwood, + BranchId::Canopy, + BranchId::Nu5, + #[cfg(feature = "zfuture")] + BranchId::ZFuture, + ]) + } + + pub fn arb_height( + branch_id: BranchId, + params: &P, + ) -> impl Strategy> { + branch_id + .height_bounds(params) + .map_or(Strategy::boxed(Just(None)), |(lower, upper)| { + Strategy::boxed( + (lower.0..upper.map_or(std::u32::MAX, |u| u.0)) + .prop_map(|h| Some(BlockHeight(h))), + ) + }) + } } #[cfg(test)] @@ -503,7 +604,9 @@ mod tests { MAIN_NETWORK.activation_height(nu_a), MAIN_NETWORK.activation_height(nu_b), ) { - (a, b) if a < b => (), + (Some(a), Some(b)) if a < b => (), + (Some(_), None) => (), + (None, None) => (), _ => panic!( "{} should not be before {} in UPGRADES_IN_ORDER", nu_a, nu_b From 936b552de2b78ea2512b4e50587d6c7ddf2fce45 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 11 May 2021 10:16:09 -0600 Subject: [PATCH 0059/2028] Add NoteValue newtype, Nullifier::as_ref and proptest generation. --- zcash_primitives/src/sapling.rs | 84 ++++++++++++++++++++++++++++++++- zcash_primitives/src/zip32.rs | 13 +++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index f99f8efe14..12c36bde94 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -16,13 +16,14 @@ use group::{Curve, Group, GroupEncoding}; use lazy_static::lazy_static; use rand_core::{CryptoRng, RngCore}; use std::array::TryFromSliceError; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::io::{self, Read, Write}; use subtle::{Choice, ConstantTimeEq}; use crate::{ constants::{self, SPENDING_KEY_GENERATOR}, merkle_tree::Hashable, + transaction::components::amount::MAX_MONEY, }; use self::{ @@ -360,6 +361,11 @@ impl Nullifier { self.0.to_vec() } } +impl AsRef<[u8]> for Nullifier { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} impl ConstantTimeEq for Nullifier { fn ct_eq(&self, other: &Self) -> Choice { @@ -367,6 +373,27 @@ impl ConstantTimeEq for Nullifier { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct NoteValue(u64); + +impl TryFrom for NoteValue { + type Error = (); + + fn try_from(value: u64) -> Result { + if value <= MAX_MONEY as u64 { + Ok(NoteValue(value)) + } else { + Err(()) + } + } +} + +impl From for u64 { + fn from(value: NoteValue) -> u64 { + value.0 + } +} + #[derive(Clone, Debug)] pub struct Note { /// The value of the note @@ -485,3 +512,58 @@ impl Note { } } } + +#[cfg(any(test, feature = "test-dependencies"))] +pub mod testing { + use proptest::prelude::*; + use std::cmp::min; + use std::convert::TryFrom; + + use crate::{ + transaction::components::amount::MAX_MONEY, zip32::testing::arb_extended_spending_key, + }; + + use super::{Node, Note, NoteValue, PaymentAddress, Rseed}; + + prop_compose! { + pub fn arb_note_value()(value in 0u64..MAX_MONEY as u64) -> NoteValue { + NoteValue::try_from(value).unwrap() + } + } + + prop_compose! { + /// The + pub fn arb_positive_note_value(bound: u64)( + value in 1u64..(min(bound, MAX_MONEY as u64)) + ) -> NoteValue { + NoteValue::try_from(value).unwrap() + } + } + + pub fn arb_payment_address() -> impl Strategy { + arb_extended_spending_key() + .prop_map(|sk| sk.default_address().map(|(_, a)| a)) + .prop_filter("A valid payment address is required.", |r| r.is_ok()) + .prop_map(|r| r.unwrap()) + } + + prop_compose! { + pub fn arb_node()(value in prop::array::uniform32(prop::num::u8::ANY)) -> Node { + Node::new(value) + } + } + + prop_compose! { + pub fn arb_note(value: NoteValue)( + addr in arb_payment_address(), + rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212) + ) -> Note { + Note { + value: value.into(), + g_d: addr.g_d().unwrap(), // this unwrap is safe because arb_payment_address always generates an address witha valid g_d + pk_d: *addr.pk_d(), + rseed + } + } + } +} diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 8fb1ee4028..a39936d5b3 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -1071,3 +1071,16 @@ mod tests { } } } + +#[cfg(any(test, feature = "test-dependencies"))] +pub mod testing { + use proptest::prelude::*; + + use super::ExtendedSpendingKey; + + prop_compose! { + pub fn arb_extended_spending_key()(seed in prop::array::uniform32(prop::num::u8::ANY)) -> ExtendedSpendingKey { + ExtendedSpendingKey::master(&seed) + } + } +} From 76999eb5c7b83c095e95e731ae77e47f24bbe05d Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 11 May 2021 10:54:39 -0600 Subject: [PATCH 0060/2028] Make txid contents private & use txid for TzeOutPoint --- zcash_client_backend/src/data_api.rs | 4 +-- zcash_client_backend/src/proto.rs | 14 +++++++- zcash_client_backend/src/welding_rig.rs | 16 +++++---- zcash_client_sqlite/src/wallet.rs | 8 ++--- zcash_extensions/src/transparent/demo.rs | 8 ++--- .../src/transaction/components/tze.rs | 18 +++++----- zcash_primitives/src/transaction/mod.rs | 35 ++++++++++++++++--- 7 files changed, 72 insertions(+), 31 deletions(-) diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 166fd80cba..3d823eaacf 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -412,14 +412,14 @@ pub mod testing { &mut self, _received_tx: &ReceivedTransaction, ) -> Result { - Ok(TxId([0u8; 32])) + Ok(TxId::from_bytes([0u8; 32])) } fn store_sent_tx( &mut self, _sent_tx: &SentTransaction, ) -> Result { - Ok(TxId([0u8; 32])) + Ok(TxId::from_bytes([0u8; 32])) } fn rewind_to_height(&mut self, _block_height: BlockHeight) -> Result<(), Self::Error> { diff --git a/zcash_client_backend/src/proto.rs b/zcash_client_backend/src/proto.rs index 9493c4c269..49d6ebf13e 100644 --- a/zcash_client_backend/src/proto.rs +++ b/zcash_client_backend/src/proto.rs @@ -8,7 +8,10 @@ use zcash_primitives::{ block::{BlockHash, BlockHeader}, consensus::BlockHeight, sapling::Nullifier, - transaction::components::sapling::{CompactOutputDescription, OutputDescription}, + transaction::{ + components::sapling::{CompactOutputDescription, OutputDescription}, + TxId, + }, }; use zcash_note_encryption::COMPACT_NOTE_SIZE; @@ -74,6 +77,15 @@ impl compact_formats::CompactBlock { } } +impl compact_formats::CompactTx { + /// Returns the transaction Id + pub fn txid(&self) -> TxId { + let mut hash = [0u8; 32]; + hash.copy_from_slice(&self.hash); + TxId::from_bytes(hash) + } +} + impl compact_formats::CompactOutput { /// Returns the note commitment for this output. /// diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index b48a4eec9d..55d354bd6e 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -12,7 +12,7 @@ use zcash_primitives::{ note_encryption::{try_sapling_compact_note_decryption, SaplingDomain}, Node, Note, Nullifier, PaymentAddress, SaplingIvk, }, - transaction::{components::sapling::CompactOutputDescription, TxId}, + transaction::components::sapling::CompactOutputDescription, zip32::ExtendedFullViewingKey, }; @@ -32,7 +32,8 @@ use crate::wallet::{AccountId, WalletShieldedOutput, WalletShieldedSpend, Wallet fn scan_output( params: &P, height: BlockHeight, - (index, output): (usize, CompactOutput), + index: usize, + output: CompactOutput, vks: &[(&AccountId, &K)], spent_from_accounts: &HashSet, tree: &mut CommitmentTree, @@ -198,6 +199,8 @@ pub fn scan_block( let block_height = block.height(); for tx in block.vtx.into_iter() { + let txid = tx.txid(); + let index = tx.index as usize; let num_spends = tx.spends.len(); let num_outputs = tx.outputs.len(); @@ -249,7 +252,7 @@ pub fn scan_block( }) .collect(); - for to_scan in tx.outputs.into_iter().enumerate() { + for (idx, c_out) in tx.outputs.into_iter().enumerate() { // Grab mutable references to new witnesses from previous outputs // in this transaction so that we can update them. Scoped so we // don't hold mutable references to shielded_outputs for too long. @@ -261,7 +264,8 @@ pub fn scan_block( if let Some(output) = scan_output( params, block_height, - to_scan, + idx, + c_out, vks, &spent_from_accounts, tree, @@ -275,11 +279,9 @@ pub fn scan_block( } if !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { - let mut txid = TxId([0u8; 32]); - txid.0.copy_from_slice(&tx.hash); wtxs.push(WalletTx { txid, - index: tx.index as usize, + index, num_spends, num_outputs, shielded_spends, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 9fbc41185f..73e7ffd236 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -389,7 +389,7 @@ pub fn block_height_extrema

( /// /// let data_file = NamedTempFile::new().unwrap(); /// let db = WalletDb::for_path(data_file, Network::TestNetwork).unwrap(); -/// let height = get_tx_height(&db, TxId([0u8; 32])); +/// let height = get_tx_height(&db, TxId::from_bytes([0u8; 32])); /// ``` pub fn get_tx_height

( wdb: &WalletDb

, @@ -398,7 +398,7 @@ pub fn get_tx_height

( wdb.conn .query_row( "SELECT block FROM transactions WHERE txid = ?", - &[txid.0.to_vec()], + &[txid.as_ref().to_vec()], |row| row.get(0).map(u32::into), ) .optional() @@ -616,7 +616,7 @@ pub fn put_tx_meta<'a, P, N>( tx: &WalletTx, height: BlockHeight, ) -> Result { - let txid = tx.txid.0.to_vec(); + let txid = tx.txid.as_ref().to_vec(); if stmts .stmt_update_tx_meta .execute(params![u32::from(height), (tx.index as i64), txid])? @@ -643,7 +643,7 @@ pub fn put_tx_data<'a, P>( tx: &Transaction, created_at: Option, ) -> Result { - let txid = tx.txid().0.to_vec(); + let txid = tx.txid().as_ref().to_vec(); let mut raw_tx = vec![]; tx.write(&mut raw_tx)?; diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 741f79cd97..520d4a96bb 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -625,7 +625,7 @@ mod tests { // let in_b = TzeIn { - prevout: TzeOutPoint::new(tx_a.txid().0, 0), + prevout: TzeOutPoint::new(tx_a.txid(), 0), witness: tze::Witness::from(0, &Witness::open(preimage_1)), }; let out_b = TzeOut { @@ -642,7 +642,7 @@ mod tests { // let in_c = TzeIn { - prevout: TzeOutPoint::new(tx_b.txid().0, 0), + prevout: TzeOutPoint::new(tx_b.txid(), 0), witness: tze::Witness::from(0, &Witness::close(preimage_2)), }; @@ -737,7 +737,7 @@ mod tests { extension_id: 0, }; let prevout_a = ( - TzeOutPoint::new(tx_a.txid().0, 0), + TzeOutPoint::new(tx_a.txid(), 0), tx_a.tze_outputs[0].clone(), ); let value_xfr = (value - DEFAULT_FEE).unwrap(); @@ -759,7 +759,7 @@ mod tests { extension_id: 0, }; let prevout_b = ( - TzeOutPoint::new(tx_a.txid().0, 0), + TzeOutPoint::new(tx_a.txid(), 0), tx_b.tze_outputs[0].clone(), ); db_c.demo_close(prevout_b, preimage_2) diff --git a/zcash_primitives/src/transaction/components/tze.rs b/zcash_primitives/src/transaction/components/tze.rs index d26485825d..1744b43c5d 100644 --- a/zcash_primitives/src/transaction/components/tze.rs +++ b/zcash_primitives/src/transaction/components/tze.rs @@ -10,6 +10,7 @@ use std::convert::TryFrom; use crate::{ extensions::transparent as tze, serialize::{CompactSize, Vector}, + transaction::TxId, }; use super::amount::Amount; @@ -20,24 +21,23 @@ fn to_io_error(_: std::num::TryFromIntError) -> io::Error { #[derive(Clone, Debug, PartialEq)] pub struct TzeOutPoint { - hash: [u8; 32], + txid: TxId, n: u32, } impl TzeOutPoint { - pub fn new(hash: [u8; 32], n: u32) -> Self { - TzeOutPoint { hash, n } + pub fn new(txid: TxId, n: u32) -> Self { + TzeOutPoint { txid, n } } pub fn read(mut reader: R) -> io::Result { - let mut hash = [0u8; 32]; - reader.read_exact(&mut hash)?; + let txid = TxId::read(&mut reader)?; let n = reader.read_u32::()?; - Ok(TzeOutPoint { hash, n }) + Ok(TzeOutPoint { txid, n }) } pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(&self.hash)?; + self.txid.write(&mut writer)?; writer.write_u32::(self.n) } @@ -45,8 +45,8 @@ impl TzeOutPoint { self.n } - pub fn hash(&self) -> &[u8; 32] { - &self.hash + pub fn txid(&self) -> &TxId { + &self.txid } } diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 78bdf9546b..3d359dd3af 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -41,7 +41,7 @@ const ZFUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF; const ZFUTURE_TX_VERSION: u32 = 0x0000FFFF; #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct TxId(pub [u8; 32]); +pub struct TxId([u8; 32]); impl fmt::Display for TxId { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -51,6 +51,29 @@ impl fmt::Display for TxId { } } +impl AsRef<[u8; 32]> for TxId { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl TxId { + pub fn from_bytes(bytes: [u8; 32]) -> Self { + TxId(bytes) + } + + pub fn read(mut reader: R) -> io::Result { + let mut hash = [0u8; 32]; + reader.read_exact(&mut hash)?; + Ok(TxId::from_bytes(hash)) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.0)?; + Ok(()) + } +} + /// The set of defined transaction format versions. /// /// This is serialized in the first four or eight bytes of the transaction format, and @@ -502,7 +525,7 @@ pub mod testing { use super::{ components::{amount::MAX_MONEY, Amount, OutPoint, TxIn, TxOut}, - Transaction, TransactionData, TxVersion, + Transaction, TransactionData, TxId, TxVersion, }; #[cfg(feature = "zfuture")] @@ -519,6 +542,10 @@ pub mod testing { 0x6a, // OP_RETURN, ]; + pub fn arb_txid() -> impl Strategy { + prop::array::uniform32(any::()).prop_map(TxId::from_bytes) + } + prop_compose! { pub fn arb_outpoint()(hash in prop::array::uniform32(1u8..), n in 1..100u32) -> OutPoint { OutPoint::new(hash, n) @@ -551,8 +578,8 @@ pub mod testing { #[cfg(feature = "zfuture")] prop_compose! { - pub fn arb_tzeoutpoint()(hash in prop::array::uniform32(1u8..), n in 1..100u32) -> TzeOutPoint { - TzeOutPoint::new(hash, n) + pub fn arb_tzeoutpoint()(txid in arb_txid(), n in 1..100u32) -> TzeOutPoint { + TzeOutPoint::new(txid, n) } } From 62bd06f14e5af8343df94ce3ad61809233faacc4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 27 May 2021 16:33:46 -0600 Subject: [PATCH 0061/2028] Apply suggestions from code review Co-authored-by: str4d --- zcash_primitives/src/consensus.rs | 7 ++++++- zcash_primitives/src/sapling.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 02e3913f73..07f12f4b36 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -512,7 +512,12 @@ impl BranchId { /// Returns the range of heights for the consensus epoch associated with this branch id. /// - /// The return type of this value is slightly more precise than [`Self::height_range`]. + /// The return type of this value is slightly more precise than [`Self::height_range`]: + /// - `Some((x, Some(y)))` means that the consensus rules corresponding to this branch id + /// are in effect for the range `x..y` + /// - `Some((x, None))` means that the consensus rules corresponding to this branch id are + /// in effect for the range `x..` + /// - `None` means that the consensus rules corresponding to this branch id are never in effect. pub fn height_bounds( &self, params: &P, diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 12c36bde94..8b0207ec5b 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -526,7 +526,7 @@ pub mod testing { use super::{Node, Note, NoteValue, PaymentAddress, Rseed}; prop_compose! { - pub fn arb_note_value()(value in 0u64..MAX_MONEY as u64) -> NoteValue { + pub fn arb_note_value()(value in 0u64..=MAX_MONEY as u64) -> NoteValue { NoteValue::try_from(value).unwrap() } } @@ -534,7 +534,7 @@ pub mod testing { prop_compose! { /// The pub fn arb_positive_note_value(bound: u64)( - value in 1u64..(min(bound, MAX_MONEY as u64)) + value in 1u64..=(min(bound, MAX_MONEY as u64)) ) -> NoteValue { NoteValue::try_from(value).unwrap() } From 168314cec654b9928c9ce6ec127c4e59287e5adb Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 31 May 2021 11:08:45 -0600 Subject: [PATCH 0062/2028] Update changelog. --- zcash_primitives/CHANGELOG.md | 14 +++++++++++++- zcash_primitives/src/consensus.rs | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 3576f25ae9..ad5eb67ad4 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -10,7 +10,12 @@ and this library adheres to Rust's notion of - `zcash_primitives::transaction::Builder::with_progress_notifier`, for setting a notification channel on which transaction build progress updates will be sent. - +- `zcash_primitives::transaction::Txid::{read, write, from_bytes}` +- `zcash_primitives::sapling::NoteValue` a typesafe wrapper for Sapling note values. +- `zcash_primitives::consensus::BranchId::{height_range, height_bounds}` functions + to provide range values for branch active heights. +- `zcash_primitives::consensus::NetworkUpgrade::Nu5` value representing the Nu5 upgrade. +- `zcash_primitives::consensus::BranchId::Nu5` value representing the Nu5 consensus branch. ### Changed - MSRV is now 1.51.0. - The following modules and helpers have been moved into @@ -24,6 +29,13 @@ and this library adheres to Rust's notion of - `zcash_primitives::util::{hash_to_scalar, generate_random_rseed}` - Renamed `zcash_primitives::transaction::components::JSDescription` to `JsDescription` (matching Rust naming conventions). +- `zcash_primitives::transaction::TxId` contents is now private. +- Renamed `zcash_primitives::transaction::components::tze::hash` to + `zcash_primitives::transaction::components::tze::txid` +- `zcash_primitives::transaction::components::tze::TzeOutPoint` constructor + now taxes a TxId rather than a raw byte array. +- `zcash_primitives::transaction::components::Amount` addition, subtraction, + and summation now return `Option` rather than panicing on overflow. ## [0.5.0] - 2021-03-26 ### Added diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 07f12f4b36..437677b34f 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -513,9 +513,9 @@ impl BranchId { /// Returns the range of heights for the consensus epoch associated with this branch id. /// /// The return type of this value is slightly more precise than [`Self::height_range`]: - /// - `Some((x, Some(y)))` means that the consensus rules corresponding to this branch id + /// - `Some((x, Some(y)))` means that the consensus rules corresponding to this branch id /// are in effect for the range `x..y` - /// - `Some((x, None))` means that the consensus rules corresponding to this branch id are + /// - `Some((x, None))` means that the consensus rules corresponding to this branch id are /// in effect for the range `x..` /// - `None` means that the consensus rules corresponding to this branch id are never in effect. pub fn height_bounds( From 8267d06846ea69b9bb793eae1e8eaca4adb6ad3f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 1 Apr 2021 14:08:17 -0600 Subject: [PATCH 0063/2028] Refactor transaction builder to create separate builders for each section. --- zcash_client_backend/src/data_api/wallet.rs | 4 +- zcash_extensions/src/transparent/demo.rs | 14 +- zcash_primitives/src/transaction/builder.rs | 1139 +++++++++++-------- 3 files changed, 661 insertions(+), 496 deletions(-) diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index c8b5e5acbd..02ab16c677 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -8,7 +8,7 @@ use zcash_primitives::{ transaction::{ builder::Builder, components::{amount::DEFAULT_FEE, Amount}, - Transaction, + Transaction, TxVersion, }, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; @@ -230,7 +230,7 @@ where let consensus_branch_id = BranchId::for_height(params, height); let (tx, tx_metadata) = builder - .build(consensus_branch_id, &prover) + .build(TxVersion::Sapling, consensus_branch_id, &prover) .map_err(Error::Builder)?; let output_index = match to { diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index 520d4a96bb..ec9e4d9a8a 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -479,7 +479,7 @@ mod tests { amount::{Amount, DEFAULT_FEE}, TzeIn, TzeOut, TzeOutPoint, }, - Transaction, TransactionData, + Transaction, TransactionData, TxVersion, }, zip32::ExtendedSpendingKey, }; @@ -693,7 +693,7 @@ mod tests { // let mut rng = OsRng; - let mut builder_a = Builder::new_with_rng_zfuture(TEST_NETWORK, H0, rng); + let mut builder_a = Builder::new(TEST_NETWORK, H0); // create some inputs to spend let extsk = ExtendedSpendingKey::master(&[]); @@ -723,7 +723,7 @@ mod tests { .map_err(|e| format!("open failure: {:?}", e)) .unwrap(); let (tx_a, _) = builder_a - .build(BranchId::Canopy, &prover) + .build(TxVersion::ZFuture, BranchId::ZFuture, &prover) .map_err(|e| format!("build failure: {:?}", e)) .unwrap(); @@ -731,7 +731,7 @@ mod tests { // Transfer // - let mut builder_b = Builder::new_with_rng_zfuture(TEST_NETWORK, H0, rng); + let mut builder_b = Builder::new(TEST_NETWORK, H0); let mut db_b = DemoBuilder { txn_builder: &mut builder_b, extension_id: 0, @@ -745,7 +745,7 @@ mod tests { .map_err(|e| format!("transfer failure: {:?}", e)) .unwrap(); let (tx_b, _) = builder_b - .build(BranchId::Canopy, &prover) + .build(TxVersion::ZFuture, BranchId::ZFuture, &prover) .map_err(|e| format!("build failure: {:?}", e)) .unwrap(); @@ -753,7 +753,7 @@ mod tests { // Closing transaction // - let mut builder_c = Builder::new_with_rng_zfuture(TEST_NETWORK, H0, rng); + let mut builder_c = Builder::new(TEST_NETWORK, H0); let mut db_c = DemoBuilder { txn_builder: &mut builder_c, extension_id: 0, @@ -774,7 +774,7 @@ mod tests { .unwrap(); let (tx_c, _) = builder_c - .build(BranchId::Canopy, &prover) + .build(TxVersion::ZFuture, BranchId::ZFuture, &prover) .map_err(|e| format!("build failure: {:?}", e)) .unwrap(); diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index cc6b260d8e..7c0a82a4cd 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -17,25 +17,26 @@ use crate::{ memo::MemoBytes, merkle_tree::MerklePath, sapling::{ - keys::OutgoingViewingKey, note_encryption::sapling_note_encryption, prover::TxProver, - redjubjub::PrivateKey, spend_sig_internal, util::generate_random_rseed_internal, + keys::OutgoingViewingKey, + note_encryption::sapling_note_encryption, + prover::TxProver, + redjubjub::{PrivateKey, Signature}, + spend_sig_internal, + util::generate_random_rseed_internal, Diversifier, Node, Note, PaymentAddress, }, transaction::{ components::{ amount::{Amount, DEFAULT_FEE}, - OutputDescription, SpendDescription, TxOut, + OutputDescription, SpendDescription, TxIn, TxOut, }, - signature_hash_data, SignableInput, Transaction, TransactionData, SIGHASH_ALL, + signature_hash_data, SignableInput, Transaction, TransactionData, TxVersion, SIGHASH_ALL, }, zip32::ExtendedSpendingKey, }; #[cfg(feature = "transparent-inputs")] -use crate::{ - legacy::Script, - transaction::components::{OutPoint, TxIn}, -}; +use crate::{legacy::Script, transaction::components::OutPoint}; #[cfg(feature = "zfuture")] use crate::{ @@ -94,6 +95,7 @@ struct SpendDescriptionInfo { merkle_path: MerklePath, } +#[derive(Clone)] pub struct SaplingOutput { /// `None` represents the `ovk = ⊥` case. ovk: Option, @@ -106,19 +108,19 @@ pub struct SaplingOutput { impl SaplingOutput

{ pub fn new( params: &P, - height: BlockHeight, + target_height: BlockHeight, rng: &mut R, ovk: Option, to: PaymentAddress, value: Amount, memo: Option, ) -> Result { - Self::new_internal(params, height, rng, ovk, to, value, memo) + Self::new_internal(params, target_height, rng, ovk, to, value, memo) } fn new_internal( params: &P, - height: BlockHeight, + target_height: BlockHeight, rng: &mut R, ovk: Option, to: PaymentAddress, @@ -130,7 +132,7 @@ impl SaplingOutput

{ return Err(Error::InvalidAmount); } - let rseed = generate_random_rseed_internal(params, height, rng); + let rseed = generate_random_rseed_internal(params, target_height, rng); let note = Note { g_d, @@ -200,32 +202,36 @@ impl SaplingOutput

{ struct TransparentInputInfo { sk: secp256k1::SecretKey, pubkey: [u8; secp256k1::constants::PUBLIC_KEY_SIZE], + utxo: OutPoint, coin: TxOut, } -#[cfg(feature = "transparent-inputs")] -struct TransparentInputs { +struct TransparentBuilder { + #[cfg(feature = "transparent-inputs")] secp: secp256k1::Secp256k1, + #[cfg(feature = "transparent-inputs")] inputs: Vec, + vout: Vec, } -#[cfg(feature = "transparent-inputs")] -impl Default for TransparentInputs { - fn default() -> Self { - TransparentInputs { +impl TransparentBuilder { + fn new() -> Self { + TransparentBuilder { + #[cfg(feature = "transparent-inputs")] secp: secp256k1::Secp256k1::gen_new(), - inputs: Default::default(), + #[cfg(feature = "transparent-inputs")] + inputs: vec![], + vout: vec![], } } -} - -#[cfg(not(feature = "transparent-inputs"))] -#[derive(Default)] -struct TransparentInputs; -impl TransparentInputs { #[cfg(feature = "transparent-inputs")] - fn push(&mut self, sk: secp256k1::SecretKey, coin: TxOut) -> Result<(), Error> { + fn add_input( + &mut self, + sk: secp256k1::SecretKey, + utxo: OutPoint, + coin: TxOut, + ) -> Result<(), Error> { if coin.value.is_negative() { return Err(Error::InvalidAmount); } @@ -246,96 +252,209 @@ impl TransparentInputs { _ => return Err(Error::InvalidAddress), } - self.inputs.push(TransparentInputInfo { sk, pubkey, coin }); + self.inputs.push(TransparentInputInfo { + sk, + pubkey, + utxo, + coin, + }); Ok(()) } - fn value_sum(&self) -> Option { - #[cfg(feature = "transparent-inputs")] - { - self.inputs - .iter() - .map(|input| input.coin.value) - .sum::>() + fn add_output(&mut self, to: &TransparentAddress, value: Amount) -> Result<(), Error> { + if value.is_negative() { + return Err(Error::InvalidAmount); } - #[cfg(not(feature = "transparent-inputs"))] - { - Some(Amount::zero()) - } + self.vout.push(TxOut { + value, + script_pubkey: to.script(), + }); + + Ok(()) } - #[cfg(feature = "transparent-inputs")] - fn apply_signatures( - &self, - mtx: &mut TransactionData, - consensus_branch_id: consensus::BranchId, - ) { - let mut sighash = [0u8; 32]; - for (i, info) in self.inputs.iter().enumerate() { - sighash.copy_from_slice(&signature_hash_data( - mtx, - consensus_branch_id, - SIGHASH_ALL, - SignableInput::transparent(i, &info.coin.script_pubkey, info.coin.value), - )); + fn value_balance(&self) -> Option { + #[cfg(feature = "transparent-inputs")] + let input_sum = self + .inputs + .iter() + .map(|input| input.coin.value) + .sum::>()?; + + #[cfg(not(feature = "transparent-inputs"))] + let input_sum = Amount::zero(); + + input_sum + - self + .vout + .iter() + .map(|vo| vo.value) + .sum::>()? + } - let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); - let sig = self.secp.sign(&msg, &info.sk); + fn build(&self) -> (Vec, Vec) { + #[cfg(feature = "transparent-inputs")] + let vin = self + .inputs + .iter() + .map(|i| TxIn::new(i.utxo.clone())) + .collect(); - // Signature has to have "SIGHASH_ALL" appended to it - let mut sig_bytes: Vec = sig.serialize_der()[..].to_vec(); - sig_bytes.extend(&[SIGHASH_ALL as u8]); + #[cfg(not(feature = "transparent-inputs"))] + let vin = vec![]; - // P2PKH scriptSig - mtx.vin[i].script_sig = Script::default() << &sig_bytes[..] << &info.pubkey[..]; - } + (vin, self.vout.clone()) } - #[cfg(not(feature = "transparent-inputs"))] - fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {} + #[cfg(feature = "transparent-inputs")] + fn create_signatures( + self, + mtx: &TransactionData, + consensus_branch_id: consensus::BranchId, + ) -> Vec