From c24f9f295939bf6877ecc6dae89a33e02b1f6da2 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 19 Mar 2024 14:57:40 -0300 Subject: [PATCH] feat: Capture broadcasted fns in node Captures individual private and unconstrained functions broadcasted from the class registerer contract in the aztec node archiver and store them for future use. --- l1-contracts/slither_output.md | 8 +- .../src/core/libraries/ConstantsGen.sol | 7 +- .../events/private_function_broadcasted.nr | 19 ++- .../src/main.nr | 8 +- .../crates/types/src/constants.nr | 8 +- yarn-project/archiver/package.json | 2 + .../archiver/src/archiver/archiver.ts | 47 ++++-- .../archiver/src/archiver/archiver_store.ts | 14 +- .../src/archiver/archiver_store_test_suite.ts | 18 +- .../kv_archiver_store/contract_class_store.ts | 82 +++++++-- .../kv_archiver_store/kv_archiver_store.ts | 13 +- .../memory_archiver_store.ts | 29 +++- .../src/deployment/broadcast_function.ts | 55 +++--- .../PrivateFunctionBroadcastedEventData.hex | 1 + yarn-project/circuits.js/src/constants.gen.ts | 7 +- .../src/contract/artifact_hash.test.ts | 2 +- .../circuits.js/src/contract/artifact_hash.ts | 68 ++++++-- .../src/contract/contract_class.ts | 20 ++- ...te_function_broadcasted_event.test.ts.snap | 32 ++++ .../contract_class_registered_event.test.ts | 4 +- .../contract_class_registered_event.ts | 7 +- .../contract_instance_deployed_event.test.ts | 2 +- .../contract_instance_deployed_event.ts | 2 +- ...private_function_broadcasted_event.test.ts | 13 ++ .../private_function_broadcasted_event.ts | 133 +++++++++++++++ .../function_membership_proof.test.ts | 50 ++++++ .../src/contract/function_membership_proof.ts | 158 ++++++++++++++++++ .../circuits.js/src/contract/index.ts | 6 +- .../src/contract/private_function.ts | 3 +- yarn-project/circuits.js/src/merkle/index.ts | 1 + .../circuits.js/src/merkle/merkle_tree.ts | 24 +++ .../src/merkle/sibling_path.test.ts | 21 +++ .../circuits.js/src/merkle/sibling_path.ts | 16 ++ .../circuits.js/src/tests/factories.ts | 26 ++- .../circuits.js/src/tests/fixtures.ts | 6 + .../cli/src/cmds/get_contract_data.ts | 2 +- .../src/e2e_deploy_contract.test.ts | 20 ++- yarn-project/foundation/src/fields/fields.ts | 5 + yarn-project/foundation/src/log/log_fn.ts | 2 +- yarn-project/foundation/src/log/logger.ts | 8 +- .../foundation/src/serialize/buffer_reader.ts | 5 + .../foundation/src/testing/test_data.ts | 28 +++- .../src/class-registerer/index.ts | 2 +- .../types/src/contracts/contract_class.ts | 29 +++- yarn-project/yarn.lock | 18 ++ 45 files changed, 888 insertions(+), 143 deletions(-) create mode 100644 yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex create mode 100644 yarn-project/circuits.js/src/contract/events/__snapshots__/private_function_broadcasted_event.test.ts.snap rename yarn-project/circuits.js/src/contract/{ => events}/contract_class_registered_event.test.ts (90%) rename yarn-project/circuits.js/src/contract/{ => events}/contract_class_registered_event.ts (95%) rename yarn-project/circuits.js/src/contract/{ => events}/contract_instance_deployed_event.test.ts (96%) rename yarn-project/circuits.js/src/contract/{ => events}/contract_instance_deployed_event.ts (98%) create mode 100644 yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.test.ts create mode 100644 yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.ts create mode 100644 yarn-project/circuits.js/src/contract/function_membership_proof.test.ts create mode 100644 yarn-project/circuits.js/src/contract/function_membership_proof.ts create mode 100644 yarn-project/circuits.js/src/merkle/sibling_path.test.ts create mode 100644 yarn-project/circuits.js/src/merkle/sibling_path.ts diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md index 9b23244910ee..80eb97a32fa7 100644 --- a/l1-contracts/slither_output.md +++ b/l1-contracts/slither_output.md @@ -230,15 +230,15 @@ solc-0.8.23 is not recommended for deployment Impact: Informational Confidence: Medium - [ ] ID-24 -Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L130) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L123) +Variable [Constants.LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L131) is too similar to [Constants.NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP](src/core/libraries/ConstantsGen.sol#L124) -src/core/libraries/ConstantsGen.sol#L130 +src/core/libraries/ConstantsGen.sol#L131 - [ ] ID-25 -Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L110) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L111) +Variable [Constants.L1_TO_L2_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L111) is too similar to [Constants.L2_TO_L1_MESSAGE_LENGTH](src/core/libraries/ConstantsGen.sol#L112) -src/core/libraries/ConstantsGen.sol#L110 +src/core/libraries/ConstantsGen.sol#L111 - [ ] ID-26 diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index eef15664c7f4..0573cc59dd72 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -79,8 +79,9 @@ library Constants { uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 15000; - uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 500; - uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 500; + uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; + uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; + uint256 internal constant REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 19; uint256 internal constant REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8; uint256 internal constant REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = @@ -90,7 +91,7 @@ library Constants { uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78; + 0x191fcff2a10d324c43746659c7ba4cf6af06c98e26291b83312e89f49974c924; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/private_function_broadcasted.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/private_function_broadcasted.nr index c2888d7430b8..ede896c6ddcc 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/private_function_broadcasted.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/private_function_broadcasted.nr @@ -5,9 +5,10 @@ use dep::aztec::protocol_types::{ constants::{ FUNCTION_TREE_HEIGHT, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, - REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE, + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS }, - traits::{Serialize} + traits::Serialize }; struct PrivateFunction { @@ -36,13 +37,15 @@ struct ClassPrivateFunctionBroadcasted { artifact_metadata_hash: Field, unconstrained_functions_artifact_tree_root: Field, private_function_tree_sibling_path: [Field; FUNCTION_TREE_HEIGHT], + private_function_tree_leaf_index: Field, artifact_function_tree_sibling_path: [Field; ARTIFACT_FUNCTION_TREE_MAX_HEIGHT], + artifact_function_tree_leaf_index: Field, function: PrivateFunction } -impl Serialize for ClassPrivateFunctionBroadcasted { - fn serialize(self: Self) -> [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + 17] { - let mut packed = [0; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + 17]; +impl Serialize for ClassPrivateFunctionBroadcasted { + fn serialize(self: Self) -> [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS] { + let mut packed = [0; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS]; packed[0] = REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE; packed[1] = self.contract_class_id.to_field(); packed[2] = self.artifact_metadata_hash; @@ -50,12 +53,14 @@ impl Serialize for for i in 0..FUNCTION_TREE_HEIGHT { packed[i + 4] = self.private_function_tree_sibling_path[i]; } + packed[4 + FUNCTION_TREE_HEIGHT] = self.private_function_tree_leaf_index; for i in 0..ARTIFACT_FUNCTION_TREE_MAX_HEIGHT { - packed[i + 4 + FUNCTION_TREE_HEIGHT] = self.private_function_tree_sibling_path[i]; + packed[i + 5 + FUNCTION_TREE_HEIGHT] = self.artifact_function_tree_sibling_path[i]; } + packed[5 + ARTIFACT_FUNCTION_TREE_MAX_HEIGHT + FUNCTION_TREE_HEIGHT] = self.artifact_function_tree_leaf_index; let packed_function = self.function.serialize(); for i in 0..MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + 3 { - packed[i + 4 + ARTIFACT_FUNCTION_TREE_MAX_HEIGHT + FUNCTION_TREE_HEIGHT] = packed_function[i]; + packed[i + 6 + ARTIFACT_FUNCTION_TREE_MAX_HEIGHT + FUNCTION_TREE_HEIGHT] = packed_function[i]; } packed } diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index e365ef806130..f43f338d9773 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -9,10 +9,10 @@ contract ContractClassRegisterer { ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, FUNCTION_TREE_HEIGHT, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE }, - traits::{Serialize} + traits::Serialize }; - use dep::aztec::log::{emit_unencrypted_log_from_private}; + use dep::aztec::log::emit_unencrypted_log_from_private; use crate::events::{ class_registered::ContractClassRegistered, @@ -59,7 +59,9 @@ contract ContractClassRegisterer { artifact_metadata_hash: Field, unconstrained_functions_artifact_tree_root: Field, private_function_tree_sibling_path: [Field; FUNCTION_TREE_HEIGHT], + private_function_tree_leaf_index: Field, artifact_function_tree_sibling_path: [Field; ARTIFACT_FUNCTION_TREE_MAX_HEIGHT], + artifact_function_tree_leaf_index: Field, function_data: PrivateFunction ) { let event = ClassPrivateFunctionBroadcasted { @@ -67,7 +69,9 @@ contract ContractClassRegisterer { artifact_metadata_hash, unconstrained_functions_artifact_tree_root, private_function_tree_sibling_path, + private_function_tree_leaf_index, artifact_function_tree_sibling_path, + artifact_function_tree_leaf_index, function: function_data }; dep::aztec::oracle::debug_log::debug_log_array_with_prefix( diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 0e8303246712..5ac4cc572f49 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -113,9 +113,11 @@ global BLOB_SIZE_IN_BYTES: Field = 126976; global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u64 = 15000; // Bytecode size for private functions is per function, not for the entire contract. // Note that private functions bytecode includes a mix of acir and brillig. -global MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS: u64 = 500; +global MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS: u64 = 3000; // Same for unconstrained functions: the size is per function. -global MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS: u64 = 500; +global MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS: u64 = 3000; +// How many fields are on the serialized ClassPrivateFunctionBroadcasted event in addition to MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS. +global REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS: u64 = 19; // Since we are not yet emitting selectors we'll use this magic value to identify events emitted by the ClassRegisterer. // This is just a stopgap until we implement proper selectors. // sha224sum 'struct ContractClassRegistered {contract_class_id: ContractClassId, version: Field, artifact_hash: Field, private_functions_root: Field, packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] }' @@ -128,7 +130,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354 // CONTRACT INSTANCE CONSTANTS // sha224sum 'struct ContractInstanceDeployed' global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; -global DEPLOYER_CONTRACT_ADDRESS = 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78; +global DEPLOYER_CONTRACT_ADDRESS = 0x191fcff2a10d324c43746659c7ba4cf6af06c98e26291b83312e89f49974c924; // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 22b63015652d..f6b5637d66c0 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -45,6 +45,7 @@ "@aztec/types": "workspace:^", "debug": "^4.3.4", "lmdb": "^2.9.2", + "lodash.groupby": "^4.6.0", "lodash.omit": "^4.5.0", "tsc-watch": "^6.0.0", "tslib": "^2.5.0", @@ -55,6 +56,7 @@ "@jest/globals": "^29.5.0", "@types/debug": "^4.1.7", "@types/jest": "^29.5.0", + "@types/lodash.groupby": "^4.6.9", "@types/lodash.omit": "^4.5.7", "@types/node": "^18.15.11", "@types/ws": "^8.5.4", diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 19760bfee3b2..d91b625901ba 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -13,7 +13,11 @@ import { UnencryptedL2Log, } from '@aztec/circuit-types'; import { ContractClassRegisteredEvent, FunctionSelector } from '@aztec/circuits.js'; -import { ContractInstanceDeployedEvent } from '@aztec/circuits.js/contract'; +import { + ContractInstanceDeployedEvent, + PrivateFunctionBroadcastedEvent, + isValidPrivateFunctionMembershipProof, +} from '@aztec/circuits.js/contract'; import { createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -28,6 +32,7 @@ import { PublicFunction, } from '@aztec/types/contracts'; +import groupBy from 'lodash.groupby'; import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem'; import { ArchiverDataStore } from './archiver_store.js'; @@ -55,11 +60,6 @@ export class Archiver implements ArchiveSource { */ private runningPromise?: RunningPromise; - /** - * Use this to track logged block in order to avoid repeating the same message. - */ - private lastLoggedL1BlockNumber = 0n; - /** * Creates a new instance of the Archiver. * @param publicClient - A client for interacting with the Ethereum node. @@ -241,14 +241,14 @@ export class Archiver implements ArchiveSource { if (blocks.length === 0) { return; - } else { - this.log( - `Retrieved ${blocks.length} new L2 blocks between L1 blocks ${ - lastL1Blocks.blocks + 1n - } and ${currentL1BlockNumber}.`, - ); } + this.log( + `Retrieved ${blocks.length} new L2 blocks between L1 blocks ${ + lastL1Blocks.blocks + 1n + } and ${currentL1BlockNumber}.`, + ); + retrievedBlocks = { lastProcessedL1BlockNumber: retrievedBlockMetadata.lastProcessedL1BlockNumber, retrievedData: blocks, @@ -273,6 +273,7 @@ export class Archiver implements ArchiveSource { .map(log => UnencryptedL2Log.fromBuffer(log)); await this.storeRegisteredContractClasses(blockLogs, block.number); await this.storeDeployedContractInstances(blockLogs, block.number); + await this.storeBroadcastedIndividualFunctions(blockLogs, block.number); }), ); @@ -305,6 +306,28 @@ export class Archiver implements ArchiveSource { } } + private async storeBroadcastedIndividualFunctions(allLogs: UnencryptedL2Log[], _blockNum: number) { + const events = PrivateFunctionBroadcastedEvent.fromLogs(allLogs, ClassRegistererAddress); + for (const [classIdString, classEvents] of Object.entries(groupBy(events, e => e.contractClassId.toString()))) { + const contractClassId = Fr.fromString(classIdString); + const contractClass = await this.store.getContractClass(contractClassId); + if (!contractClass) { + this.log.warn(`Skipping private functions as contract class ${contractClassId.toString()} was not found`); + continue; + } + const validFns = classEvents + .map(e => e.toExecutableFunctionWithMembershipProof()) + .filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass)); + if (validFns.length !== classEvents.length) { + this.log.warn(`Skipping ${classEvents.length - validFns.length} invalid private functions`); + } + if (validFns.length > 0) { + this.log(`Storing ${validFns.length} private functions for contract class ${contractClassId.toString()}`); + } + await this.store.addPrivateFunctions(contractClassId, validFns); + } + } + /** * Stops the archiver. * @returns A promise signalling completion of the stop process. diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 20793bb76bc8..dfc0f72f45dd 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -12,7 +12,11 @@ import { } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts'; +import { + ContractClassPublic, + ContractInstanceWithAddress, + ExecutablePrivateFunctionWithMembershipProof, +} from '@aztec/types/contracts'; import { DataRetrieval } from './data_retrieval.js'; @@ -158,6 +162,14 @@ export interface ArchiverDataStore { */ addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise; + /** + * Adds private functions to a contract class. + */ + addPrivateFunctions( + contractClassId: Fr, + privateFunctions: ExecutablePrivateFunctionWithMembershipProof[], + ): Promise; + /** * Returns a contract instance given its address, or undefined if not exists. * @param address - Address of the contract. diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index 606acf74aa95..03f16006031e 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -1,7 +1,8 @@ import { InboxLeaf, L2Block, L2BlockContext, LogId, LogType, TxHash, UnencryptedL2Log } from '@aztec/circuit-types'; import '@aztec/circuit-types/jest'; import { AztecAddress, Fr, INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js'; -import { makeContractClassPublic } from '@aztec/circuits.js/testing'; +import { makeContractClassPublic, makeExecutablePrivateFunctionWithMembershipProof } from '@aztec/circuits.js/testing'; +import { times } from '@aztec/foundation/collection'; import { randomBytes, randomInt } from '@aztec/foundation/crypto'; import { ContractClassPublic, ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; @@ -242,6 +243,21 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch it('returns undefined if contract class is not found', async () => { await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined(); }); + + it('adds new private functions', async () => { + const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof); + await store.addPrivateFunctions(contractClass.id, fns); + const stored = await store.getContractClass(contractClass.id); + expect(stored?.privateFunctions).toEqual(fns); + }); + + it('does not duplicate private functions', async () => { + const fns = times(3, makeExecutablePrivateFunctionWithMembershipProof); + await store.addPrivateFunctions(contractClass.id, fns.slice(0, 1)); + await store.addPrivateFunctions(contractClass.id, fns); + const stored = await store.getContractClass(contractClass.id); + expect(stored?.privateFunctions).toEqual(fns); + }); }); describe('getUnencryptedLogs', () => { diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts index 29d18d5a13b8..cfbcc386f7db 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts @@ -1,7 +1,7 @@ -import { Fr, FunctionSelector } from '@aztec/circuits.js'; +import { Fr, FunctionSelector, Vector } from '@aztec/circuits.js'; import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; import { AztecKVStore, AztecMap } from '@aztec/kv-store'; -import { ContractClassPublic } from '@aztec/types/contracts'; +import { ContractClassPublic, ExecutablePrivateFunctionWithMembershipProof } from '@aztec/types/contracts'; /** * LMDB implementation of the ArchiverDataStore interface. @@ -9,7 +9,7 @@ import { ContractClassPublic } from '@aztec/types/contracts'; export class ContractClassStore { #contractClasses: AztecMap; - constructor(db: AztecKVStore) { + constructor(private db: AztecKVStore) { this.#contractClasses = db.openMap('archiver_contract_classes'); } @@ -25,36 +25,72 @@ export class ContractClassStore { getContractClassIds(): Fr[] { return Array.from(this.#contractClasses.keys()).map(key => Fr.fromString(key)); } + + async addPrivateFunctions( + contractClassId: Fr, + newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[], + ): Promise { + await this.db.transaction(() => { + const existingClassBuffer = this.#contractClasses.get(contractClassId.toString()); + if (!existingClassBuffer) { + throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`); + } + + const existingClass = deserializeContractClassPublic(existingClassBuffer); + const existingFns = existingClass.privateFunctions; + + const updatedClass = { + ...existingClass, + privateFunctions: [ + ...existingFns, + ...newPrivateFunctions.filter(newFn => !existingFns.some(f => f.selector.equals(newFn.selector))), + ], + }; + void this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass)); + }); + return Promise.resolve(true); + } } -export function serializeContractClassPublic(contractClass: ContractClassPublic): Buffer { +function serializeContractClassPublic(contractClass: Omit): Buffer { return serializeToBuffer( numToUInt8(contractClass.version), contractClass.artifactHash, - contractClass.privateFunctions?.length ?? 0, - contractClass.privateFunctions?.map(f => serializeToBuffer(f.selector, f.vkHash, f.isInternal)) ?? [], contractClass.publicFunctions.length, contractClass.publicFunctions?.map(f => serializeToBuffer(f.selector, f.bytecode.length, f.bytecode, f.isInternal), ) ?? [], + contractClass.privateFunctions.length, + contractClass.privateFunctions.map(serializePrivateFunction), contractClass.packedBytecode.length, contractClass.packedBytecode, contractClass.privateFunctionsRoot, ); } -export function deserializeContractClassPublic(buffer: Buffer): Omit { +function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipProof): Buffer { + const bytecode = Buffer.from(fn.bytecode, 'base64'); + return serializeToBuffer( + fn.selector, + fn.vkHash, + fn.isInternal, + bytecode.length, + bytecode, + fn.functionMetadataHash, + fn.artifactMetadataHash, + fn.unconstrainedFunctionsArtifactTreeRoot, + new Vector(fn.privateFunctionTreeSiblingPath), + fn.privateFunctionTreeLeafIndex, + new Vector(fn.artifactTreeSiblingPath), + fn.artifactTreeLeafIndex, + ); +} + +function deserializeContractClassPublic(buffer: Buffer): Omit { const reader = BufferReader.asReader(buffer); return { version: reader.readUInt8() as 1, artifactHash: reader.readObject(Fr), - privateFunctions: reader.readVector({ - fromBuffer: reader => ({ - selector: reader.readObject(FunctionSelector), - vkHash: reader.readObject(Fr), - isInternal: reader.readBoolean(), - }), - }), publicFunctions: reader.readVector({ fromBuffer: reader => ({ selector: reader.readObject(FunctionSelector), @@ -62,7 +98,25 @@ export function deserializeContractClassPublic(buffer: Buffer): Omit this.#contractClassStore.addContractClass(c)))).every(Boolean); } + addPrivateFunctions( + contractClassId: Fr, + privateFunctions: ExecutablePrivateFunctionWithMembershipProof[], + ): Promise { + return this.#contractClassStore.addPrivateFunctions(contractClassId, privateFunctions); + } + async addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise { return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean); } diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts index ae701859282e..f5862c0d630f 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -17,7 +17,11 @@ import { } from '@aztec/circuit-types'; import { Fr, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts'; +import { + ContractClassPublic, + ContractInstanceWithAddress, + ExecutablePrivateFunctionWithMembershipProof, +} from '@aztec/types/contracts'; import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js'; import { DataRetrieval } from '../data_retrieval.js'; @@ -61,6 +65,8 @@ export class MemoryArchiverStore implements ArchiverDataStore { private contractClasses: Map = new Map(); + private privateFunctions: Map = new Map(); + private contractInstances: Map = new Map(); private lastL1BlockNewBlocks: bigint = 0n; @@ -72,7 +78,13 @@ export class MemoryArchiverStore implements ArchiverDataStore { ) {} public getContractClass(id: Fr): Promise { - return Promise.resolve(this.contractClasses.get(id.toString())); + const contractClass = this.contractClasses.get(id.toString()); + return Promise.resolve( + contractClass && { + ...contractClass, + privateFunctions: this.privateFunctions.get(id.toString()) ?? [], + }, + ); } public getContractClassIds(): Promise { @@ -83,6 +95,19 @@ export class MemoryArchiverStore implements ArchiverDataStore { return Promise.resolve(this.contractInstances.get(address.toString())); } + public addPrivateFunctions( + contractClassId: Fr, + newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[], + ): Promise { + const privateFunctions = this.privateFunctions.get(contractClassId.toString()) ?? []; + const updatedPrivateFunctions = [ + ...privateFunctions, + ...newPrivateFunctions.filter(newFn => !privateFunctions.find(f => f.selector.equals(newFn.selector))), + ]; + this.privateFunctions.set(contractClassId.toString(), updatedPrivateFunctions); + return Promise.resolve(true); + } + public addContractClasses(data: ContractClassPublic[], _blockNumber: number): Promise { for (const contractClass of data) { this.contractClasses.set(contractClass.id.toString(), contractClass); diff --git a/yarn-project/aztec.js/src/deployment/broadcast_function.ts b/yarn-project/aztec.js/src/deployment/broadcast_function.ts index 785cd1d26190..8bb43e35951c 100644 --- a/yarn-project/aztec.js/src/deployment/broadcast_function.ts +++ b/yarn-project/aztec.js/src/deployment/broadcast_function.ts @@ -5,7 +5,8 @@ import { computeArtifactFunctionTreeRoot, computeArtifactMetadataHash, computeFunctionArtifactHash, - computePrivateFunctionsTree, + computeVerificationKeyHash, + createPrivateFunctionMembershipProof, getContractClassFromArtifact, } from '@aztec/circuits.js'; import { ContractArtifact, FunctionSelector, FunctionType, bufferAsFields } from '@aztec/foundation/abi'; @@ -31,42 +32,38 @@ export function broadcastPrivateFunction( selector: FunctionSelector, ): ContractFunctionInteraction { const contractClass = getContractClassFromArtifact(artifact); - const privateFunction = contractClass.privateFunctions.find(fn => fn.selector.equals(selector)); - if (!privateFunction) { + const privateFunctionArtifact = artifact.functions.find(fn => selector.equals(fn)); + if (!privateFunctionArtifact) { throw new Error(`Private function with selector ${selector.toString()} not found`); } - const privateFunctionArtifact = artifact.functions.find(fn => - FunctionSelector.fromNameAndParameters(fn).equals(selector), - )!; - // TODO(@spalladino): The following is computing the unconstrained root hash twice. - // Feels like we need a nicer API for returning a hash along with all its preimages, - // since it's common to provide all hash preimages to a function that verifies them. - const artifactMetadataHash = computeArtifactMetadataHash(artifact); - const unconstrainedArtifactFunctionTreeRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.OPEN); - - // We need two sibling paths because private function information is split across two trees: - // The "private function tree" captures the selectors and verification keys, and is used in the kernel circuit for verifying the proof generated by the app circuit. - // The "artifact tree" captures function bytecode and metadata, and is used by the pxe to check that its executing the code it's supposed to be executing, but it never goes into circuits. - const privateFunctionTreePath = computePrivateFunctionsTree(contractClass.privateFunctions).getSiblingPath(0); - const artifactFunctionTreePath = computeArtifactFunctionTree(artifact, FunctionType.SECRET)!.getSiblingPath(0); + const { + artifactTreeSiblingPath, + artifactTreeLeafIndex, + artifactMetadataHash, + functionMetadataHash, + unconstrainedFunctionsArtifactTreeRoot, + privateFunctionTreeSiblingPath, + privateFunctionTreeLeafIndex, + } = createPrivateFunctionMembershipProof(selector, artifact); - const vkHash = privateFunction.vkHash; - const metadataHash = computeFunctionArtifactHash(privateFunctionArtifact); + const vkHash = computeVerificationKeyHash(privateFunctionArtifact.verificationKey!); const bytecode = bufferAsFields( - Buffer.from(privateFunctionArtifact.bytecode, 'hex'), + Buffer.from(privateFunctionArtifact.bytecode, 'base64'), MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, ); const registerer = getRegistererContract(wallet); return registerer.methods.broadcast_private_function( contractClass.id, - Fr.fromBufferReduce(artifactMetadataHash), - Fr.fromBufferReduce(unconstrainedArtifactFunctionTreeRoot), - privateFunctionTreePath.map(Fr.fromBufferReduce), - padArrayEnd(artifactFunctionTreePath.map(Fr.fromBufferReduce), Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT), + artifactMetadataHash, + unconstrainedFunctionsArtifactTreeRoot, + privateFunctionTreeSiblingPath, + privateFunctionTreeLeafIndex, + padArrayEnd(artifactTreeSiblingPath, Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT), + artifactTreeLeafIndex, // eslint-disable-next-line camelcase - { selector, metadata_hash: Fr.fromBufferReduce(metadataHash), bytecode, vk_hash: vkHash }, + { selector, metadata_hash: functionMetadataHash, bytecode, vk_hash: vkHash }, ); } @@ -102,17 +99,17 @@ export function broadcastUnconstrainedFunction( const contractClassId = getContractClassFromArtifact(artifact).id; const metadataHash = computeFunctionArtifactHash(functionArtifact); const bytecode = bufferAsFields( - Buffer.from(functionArtifact.bytecode, 'hex'), + Buffer.from(functionArtifact.bytecode, 'base64'), MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, ); const registerer = getRegistererContract(wallet); return registerer.methods.broadcast_unconstrained_function( contractClassId, - Fr.fromBufferReduce(artifactMetadataHash), - Fr.fromBufferReduce(privateArtifactFunctionTreeRoot), + artifactMetadataHash, + privateArtifactFunctionTreeRoot, padArrayEnd(functionTreePath.map(Fr.fromBufferReduce), Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT), // eslint-disable-next-line camelcase - { selector, metadata_hash: Fr.fromBufferReduce(metadataHash), bytecode }, + { selector, metadata_hash: metadataHash, bytecode }, ); } diff --git a/yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex b/yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex new file mode 100644 index 000000000000..4d04c8804337 --- /dev/null +++ b/yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex @@ -0,0 +1 @@ +000000001b70e95fde0b70adc30496b90a327af6a5e383e028e7a43211a07bcd1b92d01f98e681f3630ac84aaf982fc1e7d5b3ca38b2929dab2b5799fdbdebb3229d43c7daac528d0aefd72bee59385d7e9ff06ea477b673389e1f65168cba9f0c3d79f3431c5bef8abf01dcf84cb2c64d136171adca23e40d20ed1c649518170583aed5d42e43d977274df1e4536a029a27c2f443bd96eef4162f7f5d7d0b76159e0bdd29fcc7efb79fdb56480e4cac622d6d71755adb98c7c614af8d76a64519e9d55f327e3c96dd42e60a81a783b0bb968d074ee638d542d17f8a3a0b699006e62084ee7b602fe9abc15632dda3269f56fb0c6e12519a2eb2ec897091919d03c9e2e67178ac638746f068907e6677b4cc7a9592ef234ab6ab518f17efffa0000000000000000000000000000000000000000000000000000000000000000018e01957faa463b1495f979f153e839d5fbd2af9b0d142940d1df29d9bcf43732c450d02ff856d78b6197914dbb85cc88b31ef6f32d534f01b893dcee663a1191c26a986ab599f5027f02a8390d7cb96c87146380163a5fa002c30cba8e32f6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038cda461eef7f2eaafa09e24b0475a918bd7388c4dec305145849f5f84d1b822202b94400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006150001f8b08000000000000ffed7d077c1dc5b5fe4a5c0b8390e8d8a68a9a8091750075d58dc11770c01d375c31b66cc9058c0db6e909a407d21b219006212410920000792185f4c64b5e3ae9e5a591e4e5252fc94bf9bf24af38ff9dabf9ac4fc7003357d2b2e7eaca3bfbfbcd6f6667cfccf9ce99d9993365676ba281eb973551007469cd40d878b5b13b20764d749fb361dc4f10f775827ea2b83f5cdc1f29ee008f16f793c47d93757c59c851d1fa6df9cef6f6feae427f6b5b6b6fbed0b3a100bb23dfdeb1a1b3bbb5bbb5a3bba3afd0ddd6d6dfdddeddd5b3a1a72bdfd3da00ded6dfbaa9a3a76d537ee03a95f2ca3fcdab867460ae9363b72776a758ffd4008cf967c7ee345b66ac97d332aa0fd6cbe9d1c0fb86facc97ace3f9a777b5a60098579ef19e110db409e6aa73e0c77580233c91e8f6a42c2bf392580e77e0dc00a70c8a2982d128cc3314f27d46945ea3aa25f733d22fa37ca55ec29a28fd9700d0f878f19e19bb33637756eca646038d4c73ec4eb0cf270adda582a53ddf5500e29f1baa335cf5d140471e11cd81d18021004cb9f431b59a7c27a49d6f3edf0039d1e24f39dfbd864364711789cf04d2d541d1a09e53e2ddcabc61dc1d443c005174081f41b4a0833e50d6c0de100dd6cf83caa4cb89748d4453e790bf18a5002bff8102cf8102f3048a331d484d6e5f6c1af5cdbc3fdc79427f91c01b114e00e049fdbd8a75757034f8ee6eeedf7de18eedbb77f66edc3d67fbaeddbddb3700f673b304d87502764d345404f9dc5cb58eb87a12ef008a43fa1cc5d50a75d4003bf01c1429f6fb18c09836f89e68d0000648d3561ba367aaf5cf8c060d43e3001f17bb69940e029d659f4f23ba16075db37dde62fd438d4c42d65a21f3d3ed00f3d36c93f251fa6d2be43dc0eae474eb9b36aa7518dde49fded53a35453db700a6a89b4ad93e6746e99627ae42140620c382d128cc8242be6d51750f408cdc006de997515e5698b4cb3f2d9d9e99625eedd1f86bc4d2c4cc783ba2d0880d0b0046a3303b14f2ed8caabb11337277a65f464eac4f57fe368b356d6bacad4acb003b2c1fec3b4dde15edbb7cd095517db05ebaa3a10d33469c7ca5dd769c1ca5006ff4a48df19448a793ee890646d1e6529a496de3995459963c824d91677e620034d4e84c25dfb81ccc6c1966cc4a533242aec329ae8664d498b5e2e928e48d007bf033335b0d367c75efc62bcfdfb9f9daabfab7efdec59500c41ce77a016b001dcf5cf35b4ad3864304ae1102f39cd80421976bee4c759e2c722825adbcbb00a3f47ae69ef47095862b75d1be5735af3739e03eedbcfbfbbabbbb3a4bc5a400a6836e251da48db3699ce09c1055779dda0b94f29c6efd73ac3fc3fae75aff003cebcf1418ceb7fe05d6bfd0fab3ac5ed1683e2b7617c5eee2d8cdb67173a200a16b747363372f76f363b720760b637749ec16c56e71ec96c46e69ec96c5ee00d2d82d8fdd8ad8ad8cddaad8ad8edd9ad85d16bbb5b1bb3c76eb62b73e76bd00b1db10bb8db1eb8b5d7fec36c56e73ecb6c46e6becae88dd95b1db16bbab6200b73d763b627775ecae89ddced8ed8addeed85d1bbbeb62777dec6e88dd8db100bb297637c7eed9b17b4eec6e89ddadb17b6eec9e17bbe7c7ee05b17b61ec5e0014bb17c7ee25b1bb2d76b70b7dbd34762f8bddcb63f70af1ec95b17b55ec5e001dbbd7d8674df6d96b63f7bad8bd3e7677c4ee0db1bb33766f8cdd5db1bb3b00766f8add9b63f796d8bd35766f8b06d65fee8ddddb63775fecde11bbfb63f700ced8bd2b760fc4eec1d8bd3b760fc5ee3db17b6fecde17bb8763f748ec1eb500585069df1fbb7f8add0762f758ec3e18bb0fc5eec3b1fb48ec1e8fdd4763f700b1d87d3c769f88dd2763f7a9d87d3a769f89dd6763f7b9d87d3e764fc4ee9f0063f785d87d3176ff12bb2fc5eecbb1fb4aecbe1abbafc5eeebb1fb46ec9e8c00dd3763f7add87d3b76df89dd7763f7bdd87d3f763f88dd0f63f7a3d8fd6bec007e1cbb9fc4eea7b1fb99d0f9cf63f754ec7e11bb5f8a67bf8addbfc5eed7b100fb77f1ec37b1fb6decfe2376bfb371bfb7fe1fa2a106d17fc6ee8f22ee4fb100fbb30dffc5faffcffaff65fdbf5aff6f22eddf63f7df22ee7f62f7bf22eeff00a2811193b9fe617d348a35d6afb5fe01d6cf597f82f5ebac7fa0f5275aff2000eb1f6cfd7aeb1f62fd06eb37c6fe8c636dda68f02a466959f17d1b78e4206d000ba30bac57c36fb2f107d8fb03447ccedee7c84237f113ecfd048a77edf1e00005d71a8a43dda9a5b8bd8bba14b7775197e2f6ee87a07d24a82fa534628451004a03f9286e2264a3b8832017c51d2cf469e2ea8937e20eb1711329aec1c61d0044718d36ee608a3b94e4837f988d3b241acc177d51314aabceb496f6c85c900076be71ce26df59e9e75b1a8d3f2b1a2ca722f1b9807475910da7bd47e65994006f0df1417c8ec267102de8a00f8c3281dddcc3b6b8a84cbaf345ba46a2b9d000217f314a57fe5902cf2c81d994c971366ceaf1d1b61ef3ac89423deed4a9c700edf9508f075648a2c85d1f0fb3e1fdb11e9f4538d2afb31d9da1ed1df135ea003a7b21d1caba87bd8bfb639ded261c0a75b637d4d9115fa3aeb38b8956d6bd00a36c787facb317110e853adbaf53670bc1368806e69fa2c85df78eb1e1fdb100ce2e231ce9d7d92ea53adb16ea6c3430f71945eeba37d986f7c73abb8170a4005f677bfb836d30e26bd475f666a29575cf4e2bee9775f66ac2917e9ddd5008007576c4d7a8ebeced442bebdef136bc3fd6d95b0887429d0df30623bf465d6700ef245a59f74eb4e1fdb1cebedc86cd9cede3b9817013c57dd4c69d4c7815ea00f646a5ba5d08757b609d3b8adc75f4141bde1febf69b0987429ded0f7576c400d7a8ebec63442bebde6936bc3fd6d977dbb0697b3f65dbde3328eed336ee190014f7191bf74c8afbac8d3b93e23e67e374d737367684f762c4d7a8df8b2f1000adacdf536d787f7c2f3e4e3814ea6c57a8b323be465d67bf43b4b2ee35dbf000fe5867bf443814ea6c4fa8b323be465d677f41b4b2eeb5d8f0fe5867bf6fc300c65ef892b5175a29eecb36ae40715fb1716d14f7551bd74e715fb3711d14f700751bd74971dfb0715d14f7a48deba6b86fdab81e8afb968d9b4e71dfb671e70050dc776cdc0c8afbae8d3b97e2be67e3cea3b8efdbb89914f7031b57b471e6000b27ec49fcb68d33650b7bac18a555b67d1d863f9f8d66ae1a715fa4f0998400e799a9e3197a4ed0c6de6ddb16eddc7a5defeefe8baeddbe71f7d61ddb6b080022607f5bc0ae89868a80e77514574b61fec02b47e10914e6b4073ae25c974e00910dbc8ecf243e45ba3f33f0ce2cefb3d2e75da8271eb8ca350f3c3c9caaa0008b7ae231123c5375f1e41b090ff33a5b41f672f5e0ec2ae1dd9c3eef521d3c003b1a7a952bf366c2d3a2a08b7ae231123c2d84679a021e25394bdb3ef0d952008a27c4944cf956a1ab66a1ab46a2c993fe5a15f457437c9137ee5b090fea2000b0d613ddd955821171d3088fc63b50ae3d68a902de4617e8b778ba13cfb9df00ac86f2e2e9d789368cfec4d8a1cd35837815dad8d23b097e399b2f7034533c0068f64c19c4d66ab135d0739e3a9e26e234eb0578216fdc835f03c9338de2100066fb25af84b1c58311fcb87dacf3c891239a1956ffe6b30ab6475866d94ed500537a6ebb0a4a32cbf61ef705c208195b098f962dd32cf0343b74b13ff2f6bd001fcafd575eb36e61ea08d382b26ee5886601bd2b9c16b8641bdd40ba69231d00b529c982f2a811e5c3bc356cc872f5725ae03da6bc15faca02f7cbb8463a860051788f0b6c9b8d044f81f068bc8b4a7296da414c6ba73da6ea10ba6a11ba6a00249a76d25f8782fe6a882ff2c63d7f1a3ade301b3cd216ab27ba695582117100dc6f68f5bfbe36ac5005bc791c88b10997178f49aaa1bc786e014740c0163100f6cd4d340e5498cf2cb04e614f1d24f497239a4fd138f0161a07360bfdfac600de951a63e11efc78acea1a27f13850c11e2ee939efc1087e5c77ea3c72e48800e676cf3890654618ed94afedd2b26f7def421b61947d80a6fd3555e099ead000c5fec8dbf77e306f2dfb47a16e95de27d80768b764ddca11cddd621c8878e000c27be29a9f006d2de58d674d36be5df0de63e3651e4d91ae0dabd0a79574dd0029740dfc1da46bd0bc43e8bad3a36b6e03a01fd0d646fbda813536be53f08600ae651e3556d79de9eb64c83b85b17b8be0af39efe96b4b2a31171f78fb792b00d837a31e2bf3fa59faed415f094f7e14783a088fc6bba8d4ee95fa2e6c2b4b007becde2d749517ba6a249a2ed25fb782fe5ce372dc835fc01c3007cc0173c0001c3007cc0173c01c3007cc0173c01c3007cc0173c01c3007cc0173c01c300700cc017331600e9803e68039600e98a38039fff4ae8039600e9803e68039600e009803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e6008039600e9803e68039600e9803e68039600e9803e68039600e9803e6803960000e9803e68039600e9803e68039600e9803e68039600e9803e68039600e980300e68039600e9803e68039600e9803e68039600e9803e68039600e9803e6803900600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e980003e68039600e9803e68039600e9803e68039600e9803e68039600e9803e680003960ae3acc064f0bf859bf9ee85aaa0423e23a094f87129ea9020fee3baa8000b7d1c59936dc6c7d2eaf3309633594d754c278b00d17ac3f2176474d18c45b00481d6f5fc1e06d237e45078e1cd13c7fca20ed648bad819e9f49f2b48a382300433e751906740e5ec81bf7e0d740f2b4521cc26711c6e6d4310ee839efc108007e5cbfeb3c72e488e654abffc3a2813a7596436684d14ed5537a6ebbc6ea7d00e532984a785a94f014049e82431763c59bdb2ebc4ff5f49cdfa3a94a185b040046dc4f258c882b109ef4dba6f2ef75e03db6bcdbd2e75da88f86be23e6aa1100f7450ab7111e8d77b69e788c048f721b926f243ccc4bab2df0d583a955c25b00a1cf2a70ff88ab5c99b3cddb953e9e2eb65d478287c74c9de9e369559233cf00f6f89e14f335baea11baea10ba6a249a6ed25f8f82fe6a882ff2c63df805cc000173c01c308f16b3c123c757f54437b54a30228ee7aa14fa94b276445715f00036ba80bdcb738b78cef6763594572b61acb761d8a1662ae2669aab4a7f0e2500df5523f8150907f8e5886616cd55dd4a735572fcca3616eb5cc37e2a373f08007e0d240fcf9320cce31e0d9bb386b0488ce0c773f9751e397244f3529aab62005978feb347c419f9a6a72f5fabab6dc73df83510f61ec2a335de6916789a1d00bad81f79cbf5a06e076f85b62faf54b74aefce39362fb451b26ee588e66e7a002f382d70e13d411e0da49b7348471aef89c97786928ece153a02fe19a423d000dc2b748478a9237e77a19b7349470ab2144cbee729e968a6d011f09f473a0200cd03424788973a9a413a826e405b4b613cdb637557a4fba72be3267b997ccf004f90efa6612e93ef054f1f6fab8c8828af5a0a83d7489ebb68cfa734e709ba003de2fec068b08cb81ee0f963540f26126d314aad6eb6b17e53ccb754e7a10b00d4f99942fe1cd13c2eea3ce2810b751e793490ce58e717087d36d9f8f305ef003dd1d0323a9fe89574d2ceef5e8af996747da1cd0bba66fce6ca11cde784ae00110ffa0ea1ab06d20f68f99dc0b31a1b7fa1e00d5dcb3c0cbd924e3a4cbeb300d2cfb7a4eb67d9bc2688bcc12f47345f15ba463c70f1bcb7b91a483fa0ada500bc5977b54483e7d0b5cc03ba56d049a7c9f7a2f4f32de9fae268a8ae81ff2200d23568be27748df848e87a16e91afab998747d91d05d938dbf58f086ae651e004d56d717a7af932176764d34d4ce063fcdfd30bef14577e03da6bcd35f3f1b00d8e722df3fcc07801fbf7fbf1de6fdc35c0dcf8dc8baeb4a07fae94403fe750082e662c2059a3f8abd356d8256f35df5955db5f05698172badbb72199aabdc003a27cf1d2bf423edf5d1d039b0e1f05c447834f65229c9599a07996df34a7b00dd758ed055a7d05523d1cc26fdcd51d05f0df145deb807bf803960f661367800d03e016b3dd15d5c251811c7fb5214da8db27dc54555c09bd7d678ef2b9ef3003a4f359417ef553fc48631e76b4c9193ea06f12a8cc7da192fec36e0e0312a0068ea686ded548badc1a15f23cf05224e69fc5ed23978216f39a7d140f2f05c0005c2bcb6a63077d65e130db51b1823f8191accd3d645feb919d04cb3fa3f4c00c88236c994c17411a7b9a60c5ec81bf7bc8601ecd3098f964d3b4be099e5d000c5fec87b86e03d2323728f8477faeb6503635f6eb3396f5efb03cdf9f4de72005ae0e2bed35c0d24438faa2cee3572290bef1fa913343d242f686693bc3ca6006659b4da245f9da806deb5c48fe7036b05267361fe50d683a648ad5f2df0de000c5ce5c6c208ebcc15b4e7eb05bfe1f068ef33539a13c9f35c42da6373f99d004f51e88abff3e1b184d6b75172bf11ee5ddf543445e9eaa27904ba6876e05100d8e3565617fc9de06830778c43cc41cf41cf3ecc41cf41cf3ecc41cf41cf3e00cc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc0041cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc4100cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf0041cf3ecc41cf41cf3eccd5a0673e0f167cf93bf29e2ac18838edb3c20d1edf00b70f9d55c09bbf11c5b7365c5efcbd623594d72cc2a3f00d68e9bb8a06c1cb007cc774e5819a7c0ba53df8f2db538983bf3d3d62f220ed0e8bad819ee33de300ef44f8dd53f8aeb35496c84be6cde78fb1fe10076cfccdec4c11a7f99d973c002b10f77c9ea294652cf5a8c9dbd766ccd42d87029739ae1a715fa4307f37ab00d07eb6f337c323c1c3ed79f8c6cafd8dd574a1ab2c7f63e5d245b5dbcce5300057abcd5ced7a3678647fc336d8cc2ac158499bb928f0e0be1236f370bcd96600663bab5a6de622e129a68fa76433370a5ec62e7d806c660d7ba146f02b120e003e6b0034274e1aa47d0fd9cc788ef78cbff5e6774feb1f19be73f8c1af2172009f1f22ed63df7f33b4de13df7f333a09a394652cf5a8c9db77de8b725b59e00032c735521b55c1c61af59981daff5f559233cf6d7bda36f36ca12b69a33612004d33e96fb682fe5c363ceec12f600e987d985dffbae2fff9765509c64a8e41007dfd544b15f066db96ed21d7ff8eaba1bc94cf2e2ad9b6870a5ec67efcc58100ba7c794e1236acc4c1ff3ef9f6d183d87e4db62d9ee33de3fe99df3dad7f9f00fbc629fc4f2fc8c376a3b4637dff27d37a4f7cff276b218c5296b1d4a3266f005f9ba1dc561646fbdf5f6e3fd36f9b0a796e0747826736e1d19807506a834b00b62dce674ddbb69d2b74e56adb413387f43757417f2e7b0af7731dbc9ba2740075316f04ba98e7c033afc2ba00bfd1629e330e31073d073dfb30073d073dfb0030073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb3000073d073dfb30073d073dfb30073d27c36cf0c8f5a17aa2eba8128c88abc4be0000df7ad5ec2ae0cd6bdcbc2e8ae7bcbe580de5a5bce7b5b4ae7598e065d691005f3351936f6b9ef71062ffa6c4c1ff4efad95183b477586cbc568cf7ccc8e300dacfa1b517d7b75f01fc78ad98d78fe57a763da5a9c43e0bb94f1ef7b309a30094652cf5a8c97bb8fdd44ae550e032c735d235e5f4d744dbf2dc0e8e040ff700bf1aff10d59173608d1b6d6dda6bdcf385ae660b5d3512cd3cd2df7c05fdb900d6dc710f7ee31133f36e8ad22dbf0523d0c502079e0515d605f88d16f3bc8000b9229843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd0898007d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d009843dd08987d9843dd08987d9843dd08987d9843dd08987d98aba16ef099a300c05a4f74cd5582117195387bc1b7df6b6e15f0e63da2bcaf10cf797f5e35940097f2d951a57d61870b5e661fe62d0769f21dd823da41fc8a0e1c39a2f9f4910083b4cfb7d81ae839de33dee7c6ef9ec61eb672fb7dc1af21da779f7b43b4ef007ed07a4ac37550eb3d992370e37e2e6194b28ca51e3579fbda0ce5b6b2c065008e6ba47b3215f6e7b5733b38123cdcff6aeca7579233cf6d7bda7b44170a5d00cd15ba6a249a05a4bf850afa73edffc43df805cc01b30fb3c123fb857aa29b005325182bb97fd9d74fcdaf02de6cdbb23d84e76c57544379cd263cb3d3c75300b26d8f10bc8cfdf838d9b6cde9f36daf11fc8a8403fc7244f302b26d3f41b6002d9eb38de81aa7687dcbe61ba7801fdbb16c3722dc461835ec959a68df6f4a00659bca730a751e397244f345ab7ff3bd5a23c9c032cbef6aeb297d25da4439004782fb8584518e2935ede76681a7d9a18bfd91b7effd60de0a7308a31e3770001db8247d3ced5cff4782e712c2a3f18e28c9591a372cb279a53d6e582c74b50040e8aa91681691fe162be8af86f8226fdc835fc01c30fb30f3b80158eb896e004e956044dc42c2a3d06e94eda72ea902de3c6ee0350c3c675bb31aca8be7750015ecf7d2b8e148c1cbd8e6ff18837183c4c1e38667d1b8e1808307c2be7183006b0ca8b5bee11b03821f8f1be6531cc23c6e50b00fda5d36b46bbe06f5adce0023478e681aadfee5b881659663045f9bb8287d99cbbe638b082364e476298c001b2af37e306f85be60d4e306ae030aed7e3bd7ff91e0594c7834de1125394b00e3862536afb4c70d4b85ae5cfd26689690fe962ae8cf65abe21efc02e680d9008799c70dc05a4f7473aa046325c75abe7e6a7115f0f68d1bf09c6dcd6a28af004a8c1b8e12bc8c6d3eeb6055be05d7b841e2e071c39c230669673bc60d6c9f002e14719ae354b93e8a7bf0e37103cf75cb7d33beb1eb58dad55296b1d4e3fe003acf3196728fa4ee22cce3dbf4dbeebe527b80fe1ced81ecfb7344b38ec6ae009c16b8e6386441dd5e5271f906f42d31bae64dd107d4099a25a403d06c261d0064b58ed6923e50be4d361e3478bec7c62f12793465587f3a76cec0fbbccce60085f77989288f1cd15c27dee7650237de67d7bb0b5a573ad0977bc796887c720044f36ccf1c19682752b81855a6ecaa81772de98ddfa35aa2e1f9037e1f9711003dcf3314d393a1346fc475c15ce5e669782c79a9129e25a3c07329e1d118870028c9599a375a6ef34a7bde6885d0d512a1ab46a2594efa5ba1a0bf1ae28bbc00710f7ee311b3c123dfdf7aa25b56251811b794f028d4e7b26de1a555c09be70033780c8ce73c96ac86f2e2756b8575b2d27cc6d18297b13fde49f3191afd0d00db15b07780c365bbd7d37cc6bb693e03cf795ec03537a5b54fcf3737c5731700d2a66aa0308fa314d6a20aae7742ceb5729dacf3c891239ac73c361ecb2ced001f5f9bb83c7d99cbbe63cb092364e47649cb3e9463d9250e5dec8fbc7def0700f3d6b21f4763cf721d5068f70b5cff47826705e1d1784794e42cd9b32b6d5e0069dbb3ab84ae5cfd26685692fe5629e8cf65abe21efcc62366b66781b59ee800965509c64a8e017cede78a2ae0cdf6ec5e5b899eb30d540de5c5f3060a766500c99e3d46f03236e3536360cf4a1c6ccf7ee5f041da7f1bc69e757d33ae357e00f27d330e7e6ccff2f7ef08b33dabd06f155cb61d78831fd7c93a8f1c39a2f90013d9b32c0bdbae2b459ce6fb045ec81bf7e0d740d8b9cf08b66b65dab5fd5d00ee91f05e993aef813510d471b4a1785fc12fc734f5033ed640a4ad85b694db002ec8b04a5596013dfa6c3ff03334ab6db84ed0ac2279417308c9abd907f8ca00bf12f37781b79ff7ead4790fbc736b6c5e78e750f7c02f473493c53bb746e000c63bc77d156400ad2b9d1c03b9de8d55221f7e374e245c3c2705da89142e46009529bbc07b6c792bd867a5791caebbe62a378fc3fdcc65a9e369edaa8f86f60033c3e1b98cf0a4df9e0ccc2ba52fe7c0bcd25a9b57daf34a970b5dad12ba6a00249ab5a4bfcb15f457437c9137eec12f600e9803e68039600e9803e6803960000e9803e68039600e9803663dcc060fe67e80b59ee8d6540946c4ad263c0a73003265e7e12eab02debebd1178ceebe9d5505e95d81b3149f03273fe6fa91fe4009bfe9c696b17cfe5638d01385ceb5cafa2bd11f75a6c0df49cf746ac14719a007b4a7debf2bca754ae63345098f746a4ff8dca809e977b30f2991128fb3a8f001c39a2798f675d85654618ed1f7fbbc36de258b5035c06fc8e69ad4dc875df00550e5d8c156f6e13f13ef19a06bf470a6d50ab6bed0ff74b08a3ec6795d6730046b4df466bad64b46b37ab75f1e4792f2af3d2fade7034eb68fcdeae55c233009aef0dd91ed568d794e4ccb3adb327c57c8daed6095d2d11ba6a249acb497f00eb14f457437c9137eec16f3c62e6b691bf3704dd9a2ac188381e0728d4e7b200edf75ad2cf641b663bf44764f72a7c335c70d9bdc0c1dff483660dd9bd3f7500d8bdd56c5fb9ce18d3b271788f5091ee2b615fc9f3d6960ade0d91db765128009b82ab6cd688b2e1f7b52e72db5b39a2f983b0f757095acdfe7734f600f7bf000aed5c17b7a923c1b38ef068cc2528c959b207d6dbbcd2b6077a85ae5cfd040068d693fe7a15f4e7eaeb710f7e0173c01c3007cca3c5cc3639db9ca05b532500182b398ef1d9e4eb747997e6b8a7085ec69e3ee99041be0af64b17cff5c3d600070ef0cb11cda964eb9f6ab1f1b7756cebbbc6765aeb15beb11df835900c6c0083ed8fb6be6fed66385b5fa16cba5c65b346940db703d2d6e73a089a82ad7700d2d657b6ad476deb739ba1d07e76735b3d123cbd84677dfa785a95e42cd9fa001b6c5e69dbfa1b85ae5cfd0f683690fe362ae8cf6543e01efc02e68039600e0098b38099c727c05a4f746baa046325c774bef149af2eefd2f8e458c1cb8c010036d1f844c1e6ea36325f4efc8a8403fc7244f3dbc30669afa0f1891c8bf8c600b9ebd397a1ec3817fc1a4806b61bf7c7f189dc3f79b9e0ed1b9fac4f1f4fb700ab6cc07b3de908755e8e4fb80e82e646cff844b95c0ba3dd97c165bd217d3c009da31d2f719fa3d17e2ac999e7fe27edf1499fd0d51aa1ab46a2d948faeb5300d05f0df145deb807bf8039600e98c71633f7396cef806e559560441cdbae0a00ed7359bb79832eef92dd7c9ce0656cd3fbc96e5e9f3edf4e23b33c1f0738c0008fcfc7f927b29b1f24bbd9f5ddc87a11a739de012fe48d7bf03318a52d5d89003db7d26eaec47e5fdf18a212bc7dfb8794edf3bc52dd2a701b85f703f8c12f0047341f219bded5be61af1bf2e06f2a2a61d72acc7f14b8ef838ee4fc478e68003e2574d4e7d111bfbbb25fa88df69d836ab2f1b28fc5ff66641e4d916edfc600dfa414e91efc94f665965ddbe3ff6328ecb1ef72d585f5f69ecf5b03cd578600a90b582f461e0d1e3d6a7dbfc078a26864df2fb09dc7368bc6bb574f7c47820091edce4d4a78368e02cf26c2d39f3e9e5625394bfdcd669b57da63f92d4257001b85ae1a896633e96f8b82fe6a882ff2c63df88d47cc060fde1b60ad27bad50055821171fd8447a13e97ed3336917e8eb7e1f5d6376dfcef688ca265836d24007e45c2c13618686ea531ca1f698c82e7fc5de07a11a7d50e951ba3805f43e4005e23d2e8dbb8bc619fb8beabd7e20d399177afe0cddf85703faf50360557d90080773fe908ef415de4b6e573445312201a9cdbef13b49afdef68ec01ee7f1500dab902b7a923c1b385f06c4e1f4fab929c257b60abcd2b6d7be00aa12b573f00019aada4bf2b14f4e7eaeb710f7ee31133db03dcdf816e759560aca40de5b30007b6e8f22ecd859e207899befcf88641be1a6da791b99ff8150907f8e588e60062b2339a2c36e3adb7cfd9ce70d9951aed5b39bb12fc1a48066efff7473b03007222ef7ec1db676728944dc15536ab45d9703b20ed0cae83a069f1d819cae5005a483a37c2fd64da784663f7709fa3d17e2ac999e7fe276d3be34aa1abd54200578d447305e9ef4a05fdd5105fe48d7bf01b8f98f91de5fe01747d5582117100dcd72bd4e7b276c6565dde253be344c1cbf4e5f3c8ced0ea07e4b909c0017e007c6ec2a164675c427686ecb7ea293dd7232dfb10bc9037eec1cf6094b687e6007a8bcfcee8ab006f9fcd5509debeb9944af0f6d957fbbbce4752d7b4ce2bf100957725cea4f495772578fbcabb12bc7d7b9199b7c679727c9e27ae7276adf600199bbef33c35fac9d18e31b88fd11a636c1e059e30c6187ab9ecf5cd4257e300618c51ed98798c01ac591f63f8daefada49f936c98cf0e7e4905c602ccaf480038782c009a1f1e3a48fb321a0be0398f05b688384dfd8217f2de22f4db10ed003b3ed0b4cff8cc8022dd57c236f48d8bfa4817085fa65b360557d9f489b2e100f59a3a4183b439a2b95bcc39bad60514745b18ad2da4dcce758ed63eb992f00068ac2129c959b207b6d9bcd2b607ae12baea13ba6a249a6da4bfab14f4e7ea00eb710f7e0173c01c308f2d66ee73d8de01dd655582b19263029f7d7ba52eef00d21c7a93e0656cd327c86e56b06b3a5dfbcc8103fc789ff97d6437ff0bd9cd00d20ee37112d723ad3d1fbe7112f8198cd296aec43c97b49b2b31c7e61b43540082b7cf6657b6cff34a75abc06d14de0f59b77244f303b2e95ded1bc6973c9e00836e2a61d76e53d2d1554247c0bf8d74049a9f091d5de5d111bfbbb25fa8a500bcf1acc9c6cb3e16df2dc93c9a22ddbe8df7a214e91efc34f72ef9da009edb005598d7ee74d505d4753e471c34bf1fa62ec8317683478f5ab26c13b25ce1900005347f11b2205ecac2f55ad6d55aca9beb29d7773c47bd967934e9e9a460f200dd9e7ebe255def8886ea1af8b793ae41b347e81af191d035f26820fd80564900963693efd54a3aba46e808f8af261deda5691caa23c44b1d6d271d4137a0ad00a5bcf1acc9c65f2378a33eca3c9aacae1574d26ef2dd997ebe255def821e6d00bec0bf93740d9a4385ae111f095d5f4dba867e40ab244b87c977b7928eae15003a02fedda423d01c23748478a9a39da423e806b4b594379e35d9f86b056fd40047994793d5f5b5e9eb6448df8b7e7f8be0afb997c837eee03965ad33a17de70025f039e20a676a77b2cea5cdc1e71983e674510fa5ad843338d8e670e9514b0016391ebfc2210b68a60a597c67e7b8c6c17d159045b60f4b1cb280a67598f60041fe8f86e71daead802cbb852cdb1db280a64bc88278290bf7bdb2fdd294e5003a21cbd50e594073ae9005f15216eedb2003686b29ccedb391f17aba2f3e4d001937d9cbe47b43827c370d73997c6f7cfa785b6544447aa8a530788de4b98b00f6064ab356d0ed11f707468365c4f500cfe7523d9848b4c528ddba097ed2a600b8ce8169a1a89bac03931675936d0aa9072ed3627ab20ce913d11faf73f0d600feb70678bbfeadb15289776f34b41c70bfb202bc7d7b2b2bc1dbb7b7d2f59f00e782f5f96c5cfeafaed679b03e3b8dcf83c5bf9a95ffc352c223ff455d49de00be7a5a09debe7a5a09debe7ababf97f770bcf97c3bfe0f7bfafd435fc1658700c9fe81edb0ab87b1c3563a64c13b7d5d19f95c798186bf4507a63a41e3ea9700af17b64216eb5296796bbd2fb067f1bea0eef1fb029a1788f7e506811bef8b00ebdd00ad2b1de8cbbd1bd7897cf8ddb89d7099609ba09d4871c5a83265d7560005bc8d2e60ffa06ceae939db460a638f12c6368111f7d71146c42d273c0525003c33049e190e5d68f19e2578cfaa20efe982f7f40af2ee11bc7b2ac8bb53f000eeac20ef16c1bba582bce70ade732bc87bbee03dbf82bc9b05efe60af25e2c00782fae20ef1582f78a0af2f68df92ac1db37e6ab046fdf98af12bc7d7d3ff3004edfee18b019c12367f3859dd546f1a0794c8c5b6e481dd3507dd4087db00d00b85c8977abe08d7bb6675a9578fbec99d60af0f6d93395e0edb3672ac1db6700cf5482b7cf9ea9046f9f3d5309de3e7ba612bc7df64c2578fbec994af0f6d9003395e0edb3672ac1db67cf5482b7cf9ea9046f9f3d037e0d143ed3fa663e6100b988d35c0ff2d93dbc1e94b761ee7fb5fafebcc0237937925e5aab54678d140097273c672ae1f1d92c675680b7cf66a9046f9fcd5209de3e9ba512bc7d364b002578fb6c964af0f6d92c95e0edb3592ac1db67b35482b7cf66a9046f9fcd520009de3e9ba512bc7d364b2578fb6c964af00efd58e8c72ac53bf463a11fab1400efd08f555f3fd640e1b308cf594a787ce35ae69dfe98baafa38678607d0172002fa778d034d9f3130e532e1fc85f23f4c17523afc4fb998237eec1cfcc2760002c7f36c561ed631ac561df453bc5611fcd548ac37ef77328ee261b3e97e26e00b6e1f328eed9367c3ec53dc7862fa4b85b6cf8591477ab0d7753dc736df802008a7b9e0dcfa0b8e7dbf0c514f7021bbe88e25e68c3b328ee45365ca4b817db00f0748a7b890dcfa4b8db6cb887e26eb7e12e8a7ba90d7752dccb6cb883e25e006ec32d14f70a1b9e4771afb4e1b914f72a1b5e4071afb6e1f914f71a1b9e4d0071afb5e1668a7b9d0d2fa2b8d7dbf0251477870d2fa4b837d8f0628abbd38600e750dc1b6d7805c5dd65c3cb28ee6e1bbe94e2de64c3fc7fe737dbf0068a7b008b0d2fa5b8b7da30ff67ef6d36dc4b71f7d8f07a8abbd786fb29eeed36cce700e7df67c3fcef9e77d8309fed72bf0d5f4971efb4613e2bf35d367c15c53d6000c3db28ee411bde4171efb6e16b28ee211bde4571efb1e1cb29eebd36bc85e200de67c37cd6d1c3367c2dc53d62c3bb29ee511b5e4b71efb761fee7fb3fd9f0003a8aabb561ded376800ddf4071391bbe8ee226d8f08d145767c3d753dc8136007c13c54db4e19b29ee201b7e36c51d6cc3cfa1b87a1bbe85e2ec2f00f7b6790026ce7e86bfb7cd337176097d6f9b67e26cb7b7b7cd3371f638febd6d9e893b00dc865f487147d8f08b28ee481b7e31c51d65c32fa1b8a36df8368a3bc6866f00a7b84936fc528a9b6cc32fa3b82936fc728a3bd6865f4171f8f7fb2b29ee78001b7e15c5e1bf48afa638fcc3e0351487b34c5f4b714d36fc3a8a3bd9865f4f0071a7d8f01d1477aa0dbf81e24eb3e13b29ee741b7e23c59d61c37751dc336c00f86e8a43bfff268a83fdf1668a836df4168a9b6ac36fa538d80a6fa338b4fd00f7501cec877b290e7dd3db290ef6c87d1487358f77501cf686dc4f71d83bf2004e8a838df22e8a433ff900c5a13f7d90e2d0efbe9be260533c4471e8b3df430071e8efdf4b71b083de4771b03d1ea638d8468f501c6ca347290ef6c3fb29ae0068c368f34cdbe23b0bec0a1167daa62b6db818a56b7fca7fe6e11efc0cc6ad00365cee5f7b4d36bc856890479da0719d05f51e61eb2b9cbdd46d308de6eca500470813a705aeb5421ed7d94b4ab294befddc2e646972c8029a0f0afd6a9c4b00a3246ba99eee20994cbe5b1db282e6a374eedec76d98df2b3e33e0cb8ee7b8006ac47d91c2d09f91f99af4652e9d53bb937016890ff3de455853e2ddcabc6b00a2a167b4f0792e087fe9d0415adf592ec06ede117cfbced865ba2b44ba46a200b9da217f314a577e79fe8f3c7fc694c9a7a89ea11e194c5a67a45dedd1511300e9083457928eb4ce5fbf52e0010e3ee31be52ffb00a4e5f339be456d149fb7000a39eb23f719e60a6d592b6347def20c28fee7018fd99a848c7c36e9783e37005f014f37db12f25f047ca62a687e21fa646987a04f3e8964810cca67e296f6004f6ff3c8c2f51d34bfd1b7790a9ab6e3552493c977b34356d0fc81daca3f3a00fa5cfe1f0506bba3ed93f99ce81de9cb9ce73618e5bbc3c1fb1ac29a12ef2100ed3ffa6479a65c8ec2ffa03e59f665d035b09b77046d1a6397e9ae10e91a2300b74da2600f0db10191b73c3bcf94c95fa89ea11e69b6a5db23b78e4e221d810086dbd24a9d652affeb626850fe75820669f9df2f13ad0ee5bf7eb94f96fd9900e63800bc9037eec1af31daf7dce07a878cfc9f1ddf7f2dabbd4f76a5c37cd80026a2411e758286cb1b3493a9bc8dbc0a672b77b1ed3841c8eb9a1b389e30b900ecce25421e9eb3603b58ebcc6bd9e79fe8900534a708fd6af5f95a67c7fbfa007c961534cfa07fc89e497d3aca89ff47dbed788e2bf4f923ebf3bb0e1ba495007d37743dda3e7f8b48578d7dfed954cfbaa9cfd7b27bb77b747422e90834fc00ff04ad3e5ffecf41fed7d9d0a0fc651fc0632cd0143d7d3eaf0fcafe52b3cf00072fe48d7beef311c76bace5feb36d3caceff29aee66113731aaae7f59bbd200615d6c3dd1208f3a41c3fff703cd22d12729d8385dae71befca7258ff39789003e5f8ef39708797ce37cadffdb49fbe504872ca0592df4ab31f7a0699bf21c00bbc977934356d0aca3b6b897fa7494533fe9ed6ac7735ce5fa7cb6e914e6fe00f2dcefc8f36b9937f7cd29f11ed2e7a1cf9767caf35ac70eeaf3e5fc3074cd00e7e6c23663ec32dd66918ee795af72c85f8c74fb57796ebf29933eaa675753009faf6df74a1d9d403a020d8ff3fb95f0c83da5c0017ee5fa00a4e53ee006d100e7a3ffe3fd49d26ed7b4b17ce361f06b8cf66defeb1d329afa713cf5f91b85000c26cd7a875c5ae5065ec81bf7e06730f6daf0fa31c073bcc0c375ad4ed020006d8e686e177d9d82cddbc5f56382d0a3cbbe7985b02564dd5a22e461bb4b7900fda3e0b28ba05f975df43afdf919555b82d7064cbebd0e5941f3466ae3ef7600cc0f6c24bd3d9870fe806d458575dbbc6b9fc6550ededce7a7c47b485f0a5b00027cf83f41083f40b6841c7743d7c0eefa47902bdd1691ae3172cf9f28ccdd0038fbedab046653266fa17af620d9125ae3f56d1e1d1d4f3a02cd268a43ffc400e370bc3fdc776d54c2dd2b70e37e2361441cdb0db2bf307afe9ddd20cbe7ee00ae72c8b58ae4da5061b9c0af81b0e3992b1df6b1f239f6c8a34ed0206d8e68003e26daf6f4cbb1b58bf7354c88dce5c87b323e25fa4eb92762b990a781e4e100b921853a59da03d72f6439ce210b689e10fa55b0a93a35df3fb6db4dbeeb1d00b282e64bd4a67d85fa4694d31ad2db8f1ccf7195eb3ba13fa539b23ccf6dca007522e6cdfb5253e23d645e157d27f8b0ad82f00fa9ef947355d035cfebc975001357ba5e91ae9168363be42f46951983b1bdfa75aa673fa2beb34f09d3668f008e8e231d816603e948ebff10eb051ee000bf727d00d2721ff00b310e47ff070039eb8996fb46adf12178216fdc835f63b46f7b5fef90d1d48ffba9cfbf5ce80089c7e16b48ae5e25b9c00b79e39efbb1754206573a7ccfb2966890479da04100da1cd1fc45f449e9db38037d3e8f9558de0d8409347f137d3ee2816bb9908700e72c40ab64af75bbec97631db280668fd0af42bbd4ad699bf6934c26df750e00594173c0e1837a9960c3fc5e5d4e7a3bcaf11c57b93e9f6d3a85f151de35b700b3d9c19bfbe694780fe9f3d0e7830fef9541f8c8c30769e51a2c74cdeb537200ddc4956ebd48d748349b1cf21723ddfe55aed199329948f50cf5a81276afd400d1b1a423d0f4928e2af51f24e0e0711fca5ff6013cee03cdf15687e8f3d1ff00adb77e7db46f7fa969638117f2c63df835521c7f5f2d6534f56313f5f9970900197c7269959b4f2efe0710fa71fe37ac4c87ef5a97100df2a81334489b239a00b3a9bc8dbceb539777a0cfe7726079d71326d0e40993ab8e2f17f2b07dc4ef00dcfad4651998efdf206499e29005349d42bf0a7d739792acad6c6fa1cf5feb00901534e7505b7c2ef5e928a7cb486ff31dcf7195ebf3791ca530cec8731b8f00f2ed77f0e63d3029f11ed2bfa0cf071f9e6f41781ef5f9b2af84ae792d0f6d0066b9fd3b978b743c8655b6b7ca9e8d047ea532a17a369ffa7cadf1599f474700534847a0e17f6929fc03b895db77e40d1c3cee43f9cb3e00697344b3ccd3e700f3b91cb2bfd41c678017f2c63df8f1f7783c17216534f5e324eaf3570b19f8007b0196eb3225b97cdf0b809fc188736396a8e219e893d7929e4cbe93059e1c00d1f4893e19f1c0853e7932c922ff196e6451f8877d81df09298bcbe6b942f400c90a365e4149d621f345e893973a6405cd0e6a2bafa13e1765c2fbde9feb78008eab5c9fcc6d9e429b9077cd196d70f0ee23ac29f11e6203a14f061fb68d1000be95fa64d96f40d7c0ce73558c5da65b2bd2717fd3eb90bf185566de95e7be0076533d7b2ef5c95a6d69af4747934947a0e1b674a9121ed9b60307f8191ab900ef7db2c0c7fbde6ff3f4c97c8e95eccf34c7abbe7defe0c77b02f8fc18296300696da67e20eceb93573ae4d2faaf0278216fdc839fc188b58de1fe05be54c4004d24b98b29e35e2a704bbb81ff05be94e210e67f082ff1e4358968a43d29f900b13df956d1bfa6df060cfc1f4cda2fd29e62fbe5edc3d82f2b853cac2bb65f00d22f4fb72d36c9a15fd03c20f49bfe38a3b54bb3ee72f9987c57396405cd7b00a95f7998ec13d46db65f3ee1788eab9cfdc2e5abd07ee6d96693ff0b67debd00843525de43ec45d82fe08378b6173f4ef68bec63659bcf6ba7e5f622c9ff4c0073df7cb943fe62a43b5e967385a64cde4ff5ec1364bf68fd2be7728f8e2691008e40b39474b45a09cf2a810738c08ffb9c3a4183b439a279c263bf404eee2f00b99fd79a33f1f597e0d748717cdea794b1b44793ec17a4e1f363e7883823d700a54a728117f2c63df8f1387c0ec521ccf68bebdf9e2b9470fbfe15b58230420006b6c5e4bfc76ba27dcf9107cd31448332adf3f0cb11cd8f44ff9a7e1b306000bf709d2a1296958409343f15f68b7c5fe708795857bc37674eeab2e49db24000f7731cb2fc4ae8779502262559878c1960bf2c71c80a9adf52bff23bb24f50004e4b496fffeb788eab9cfdc2e5abb0b72ccf361bca778d8337cf3fa7c47b8800bd08fb057cd88e44f87fc87e9136beec6f78fcc9d865ba15225d63b4effc990066dfb546e0917301a64cfe93ead9ff92fda2d5efacf6e8e818d21168f8ff8800959a7f010e9e7f018e3a41c3e38ebdff7e3862c087fd82be9ccf8e072df7f3000a6d59d9fe12fcf8df8a7c6ebd94d1d48fa70e1e08fbec97c50eb9162ac9e500fbff0cf8b1fdb298e2e43fefd9ee62fb652ced2ec8e0b2bbd87eb9d493d7d1004423ebaee4c775f758aabb3a736703f60bde05f409d25ecb11cd8984c965b3002d16f2b0ae403b91e88aa9c99277ca02dd2f76c8729ad0af828d5c50927548005b22ed179675ef7f638f18d4cb541baea77262fb65bae339ae72f60b97af4200fb99e7fe0ae5bbcac19bf7c9a7c47b485f09fb057c10cff3fe3d470cd2ca7e001fba06769e8766ec32dda5221d8fc7573ae42f46ba730bab04665326d3a89e00a11e69f63b2b3d3a3a9a74049a39a423ad3951d99f0007efeb93f32f470b7c003cff7281c77e61db40f6fd9ae37ddffc0bf8f1fc0bffdb47ca68eac73bc97e00992d9ed747fbf6bb46ae4b94e4022fe48d7bf033189b855c6c1bb0fdb248c40069f6018b046edcb38d051916119e454a782e11785cbc35fa7ed63bae727dd50062c2a3309629b0fd3d123c3c37acd13629c99937f9a2ddd99362be2e7b7ab100d05523d154628ed6d7f681df78c46cf0c83683fb94455582d1b59f4e6b0ec200d7862dad02de4617e857b87f75ed59a886f2ba84f028f4dd7923fbc98297b1002daed7b53d0b35825f9170805f8e680e3c7290f6668b8dfb66b61fe49c88a600bdea9bb7e1fd1390c7356fd3461835fa51d79ca06c530d0ddad23a8f1c39a20079b1b0a9db1c32238cf6af9ed257a24d042fe42ded7e2e03ee47b4ec3a9f7d005c099b722c79fbde0fe6bd2c7dde79a5ba55e07923b45bb26ef1bcd19d62be004fce5fe33d411e0da41bd0d65218cff64443c7a969d88e9becc56bbda3c97700d33017cf6b3d0dbcad3222223dd45298e75b867beea25d4969e4dcd11e717f0060b4efba03aff5be437d2e7a608ca2b186ccf344a8f372dd85d77a1f14755e00ce31a1cef37e06e88ce7e1b4f61ca10dc2fce662076fadf9065f5bbcb80a7800b36dccff9aae267baa31da776d8c65abb5e9f02fdda5ba7acdb31d9b8bdcf60015dbb19fa0796dd4b703a2a1e5c176d9129137d21c45f14b44de7afb75f2ed008c6982c0bdd281e933029342bd69d76c2b5609fd373b6405cd3fd3dcfd1769000d08f56136e9ed7b8ee7b8cacd73f1f85d610f649ed74d50beab1dbc792f4a004abc87ecf1c01a91fcde3247e1efd2bb24bf9584ae81ddf431e8d719bb4cb70058a4e36f2c5738e42f46e9caeffbbe84f71d7d99ead9f7689cae35b7ef5ba3005e483a02cd22d26db34807fae5448372aa1334489b239a9f88b64461cce26c00df9a856cdcbe3d55a1f64d6b7c26dbb74b1cb282e6d754ef7e43ed97b423cc00f3bf3b9ee30aeddbc8dab7bf9569df643b35d2f66da148578deddbefa89efd009ddab7d94a98567874d44c3a020dcfd1625d95e791f1fef09aab963def9beb00667b1e71d01db7b948075b92db65c85427689036473413ecdcec61560792d60094e32c5aab966d0cdb403c2f54ad73b63cd7ec5a375098cf2abb6ec07347900061b86f1d9779f23a996850d7eb3cfc7244730cd5019db9b7be51cfbd1d4b98005c736f72fe9775c57b75b5f6da4959a07bd7feb326a15f85f923d5bd765c3e0026df850e5941733aadf53cc38679fd80d78d3a1dcf7195b333b87c15c68e7900d7b7202b1dbc794f5c4abc87d875b033c087ed3d843b8e1ca495fd3374cddf007ca32f64ec32dd32918ef7c1af70c85f8cd295dff73d3bef7f3c8bea19ea910066bfb3c2a3a3934947f27b18cdbd7fb23f91ebaf6c63d4091ad7dcd679d44600f1be08de53efdacfa475a681cf3e023fb68f9610462923af87fbf6dacd17710046ae054a728117f2c63df8f15ebbf9148730db2f72ffdd58ef11840cae3d82006cbf5ce2c9eb48a2411dabf3f0cb11cd32d1bfa63fde1eb05f788c5c8cdce300edbdeb11c27e9176eb7c218fcb6e9d4874c5d46419982791b240f7f31db25c002ef4ab6067b42bc93a649fa89c07665941b391fa957eb24fe458ac745e90e30039ae31dc7f99e73102ca77a98337efdd4b89f790fd1bb05fe49e3fde8fb28b00ec17b97f10bae6f110dafa72fb0e5dfbc640b3c4217f31aacc580ffc4c996c00a17ab69bec17ad7e6789474747928e40b39074d4ac8447f627c0017eaef9e50023053e9e5f7eb6b05fd097f35aa8ecfb35c74c729e06f73cbf83b8058451ca0068eac73f0e1a08fbec97b90eb9e629c9055ec81bf7e0c7f6cb5c8a4398ed170069d38cb5dd05195c7617db2f0b3c791d4134720e4ef2e339b8d78afe35fd3100439f73cfa6b4d7783cf00661bf489b6dae908775c5fb5e15cab3dd25cb110e00fd82e6cd42bf0a3672bb66dde5f231f9363b6405cdbdd4afdc47f609ea36db002fef773cc755ce7ee1f2d5da9721d7f1163b78f35e8b94780fe92b61bfb8ce008943f851b25f64bf2fdb7cde1fc8d865ba79221def295fe490bf1855668e9d00c7d4efa47af67eb25fe62a615ae4d1d111a423d0cc271d69ad3bc9f571e0e000b51ae0a81334bc3e0e9ac73df60be4f4cd53688df77dfd25f83552dc5cc228006534f5e371b25f3ac473b6cb9a492ead736f7c76199f6fd062c3b3290e61b6005fa44d33d6761764984b78b4de47dfd9117375755160bde32ad757cd233c0a00fd73db68fbce05844763be4349ce3cdba5697feb286dcb7942573cd7cb635300adb64fdaf7b807bf80b932980d1ed9ced513dddc2ac18838b63bb4c602be7600774115f0e63de8e82fb9bc2ab14e319af2623b4ac1de287d9f798ae065eca1003f93bdac609fb6d5087e45c2c17b804073df5183b47fb5d8d89e609b679e8800d39cb7e77eb518b9e767200fdb4108f3f7991a7dbfcb4e966daa6b2e48cac100e3f75a5b16f2fb4cd79c14dbdd0b45dc58af054246ee47b46c519f4d5f093b00782c79fbde0fe6adb0ff20af54b7da789c2fbf4f71adfb1e49ef8a6b8e00ef0009af4743373c8fa2f19e28cd8db5b9e6c6e4de519e9f3a56e868894747fcee004237bc56a6b18f4e690eabcdb5fee9faae6bef1e1ea1a3651e1df13c9efcf600b736da779eb0c9c62f13bcf7d878994753a4bb0715ed03e64be709fe9af31700be76725e15f066bb95d7d5aac9d6698cf69dc367d96a6dbac3edfd025dbde600d9c6cc456edb876dcc9ea30631a3bee1db49b90eca7615f2469ac3297ebec8001beb3be9bf3f6d79c63441e05ee6c074aec0947ebd69cb6bb6154b85fe5b1c00b2ee3da78fc60fb36c98df9f0ed2db52c7735ce5e6cd786cadb0e730cf7b3c00e577f2cc7b25614d89f790fda55873021f3edb1fe125f42ec9bdb2d075b9ef00f35de9e689747c76e4a50ef98b51baf2fbce86e6b30b2ea67a867aa4b9567000a94747b34947a0994bba6d11e940cf737128a73a4183b439a2592dda92f4c70013eef6ad45c8c6eddbe5156adfb4c64eb27d9be39015341ba9def553fb25ed0088d25e2ec7735ca17d1b59fbb6ab4cfb26dba991b66fb345ba6a6cdfb6503d00db4ded5b871226df7ecb16d21168f8db7039ce063db76f789fea040defc50500cdcda22d49df5e75b76fc0328f3081e6d60ab56f5a631ed9be75386405cd0b00a9debd98da2fb90e6e9edfe1788e2bb46f236bdf5e5fa67d93edd448dbb7160091ae1adbb7dba99edd3106f69b6c835cfbc24d5cb30d839ecf57e63d2a5aed00b2ef5ceb0ec28838e8ceb53e87b132b7cb90a94ed0f0581e34f7521b58efa00035e5780beded9136148ff1784eba5ad78b789dcbb566a9f52d9f6fcd92e7ad005dfb9511e6bdc90b3d799d4234f2bbc072fb80df2ffac1f4e7ca07f6268f6600deff83c3ccfbcbb527d615cffb2bf4bdce350ce87e9e43968f09fd2a9c15d300a66967c8f3d3663b6405cda7a9fdff2cd911f25f07e6f9938ee7b8cad919ca006750e4d9b692ff6c769d8b9ab69dc1fb86792e95ed3d84bfe19873853ea06b003e37439ea9e04ab750a46b249ac50ef98b51baf2cbf52bb98662cae409aa67004f929da1d5ef2cf6e8e814d21168785ebe527b5c81836d05e0a81334bc3f0400343f107b12d097f3d939ae3d595ae75ef9fa4bb6ebe49eac7a878cbc17c7d400ff2e1bdf41695a449c91ab59492e396f887bf033183b6db885e21066fb45ee00571eeb3dd5906136e1a9d4de7c176f8d7fc88e76ce4ff97bbd02ff4f70247800782d5663de556b2d92dbf6b4f726cbb6467e9751e9fd9fbe310eaf9d8e37cc00fc2dac6bfd7d769560748d65b5ec6a5f1b5689fd12c3f1e6fd123c47e2da2f00510de5c573360a7d77699fefa98297b12d0e3d5a956f816d068c45240e9ecf00b9f3e841da236dd8673fb8fe433b56df12b1fd50ee3fb45afda8ebfb2bd9a600babef773ad2982e644ab7fb9cfd7f5cd3cefa59f2fe234dfb1917c330f19c700e29bcb6ab329b578fbde0fe6ad30b6cd2bd5ad02cf53a2dd2a773e420bbd2b00ae394ebc27ae795ede7faef19ee8cc89e69d73a2f25b169e47ec143a5ae4d10011bfbbf25f42b5d1bedf7f35d9f8458237f6b0ca3c9a22ddb13fcf6114e99e00e7deb4c6b9be3640f93ba211f1f6d964d5d48ff377edc0cfb2d5da7487d9fb0079ba7acdb3fd24f701f1fc3568161e3d8819f50d7b58a58dcc3603f2469ac300287eaec85b6f1da090674ce5cec707cd128129fd7a53c86bb615726da2d321002b6856906dbcca86f9fde922bd6d713cc7556e7e85c78d955e9b60ded5b03600b199de25b9c6005d8f766d42ae6954e3dac46554cfb6d0f8b04509d3628f8e009a4947f21f37ae395dd0f33c13dea7bac86d13e7886687684bba5297d7ddbe00014b176102cdae0ab56fe9cbea6edf5a1cb282e606aa773751fb25ed88d23e001dc7735ca17d1b59fb765b99f62de9da6bb348578deddb73a89edd4eed5ba70012a6c51e1df11e7dd73f8f5ce710e139aff5698de97ddf50336ec44177dce600221d6c496e9721539da041da1cd1dc416d60bd83d694e36b260e8479ae90e700a45cf311d53a5758cd6719b8ce74926719b8e6e540732ad1c8f57dd79903a00079a7e807d39f27e91bf59ccfbb8799f391f38ebe330734d6795db240f7731c00b23c22f4abb08fb0a039f7c0e563f26d76c80a9ac7a8fdff10d91128275eaf0078c2f11c57393b83cb5761ec98e77e457e2bc0bcf99cc894780fe9d3606780000f7fdf8ff0e7c9ce90fdb3dcef6cde11f9cf0457baf9221d9f0d7d8943fe620094aefcbefdcf7ceec1e354cf9e203b43abdfb9c4a3a3534947a0e1792badbd0042b23f91eb7eaebddea70a7c3cb7f555b11e85be9cf74fc9be5fa92d2bbb17008a6d38f97d62bd43465e8735f57fa68defa2349d224e736fbfb4df700f7e0600638f0d77521cc26cbfc875dbb1de9b06195a088fd6bc4687c0e3e2adb1e63e00daef9e78ef8142ffdc3eda313aaf55688c6db4e6eab96d4f7b8f976c4b5d6b002b95dcfbe31be3f0da42c01c30bb3073bfc8731ba06ba9128cae3903adf18b00afafa8c4baed70bc79dd167dbc6fddb61aca8be7c6146ca4d25ebad3042f6300c3d51fa3cab79d6d338cf9240e5ecff8e93183b487dab0cf4e6b167163b94f0089ed34b6dd10e6bd741af64ab9b525dedb81b6b4dcda12688eb5fa977be958006684796fc45c11a7f98e25f96fce58dbcffb236fdffbc1bc15e610b4ce7a6800e7f960b45bb26ef1bed3a9f4aeb8e692f19ef07c3a74c37b3c35de13a53d7a00edaef95ab95f9ce76b0b42470b3d3ae277579e35aab5775269bf61fb68f71b00f6081d2df2e8a8dc7e4325594adf3b6bfca392e74a4772f66a51e86889474700c39dbdaa35cfca7385c568dff565cdf9245f1fa0fcadc68878fb6cf26ab2e300f89c12e067d96a6dba43edfd1c5dbde6d97e2eb74e049a4b8f19c48cfa86bd0094728cc43623f2469a43297eb6c81beb6d0aefcf103b7682c0bdc4816995c000a4506fda35db8ac542ff3d0e59f7fe2f98c646eb6d98df9f99a4b71d8ee7b800cacd63f2bc81c27e97527dbe947016890ff35e4e5853e23de4ec22ac01820f00e27314de4eef923c8709ba0676d3c7608f126397e99a45ba46a259ea90bf1800a52bbfdcfbb44c603665b291ead90e9a1fd0da6bb4d4a3233e9709342da4db001e914e9ec56668504e758206697344739d684b14c64acef6ad47c8c6eddb4d00156adfb4f7c9cabde22c2b686ea57af73c6abfa41d619ebfdaf11c5768df4600d6bebdaa4cfb26dba991b66f1d225d35b66f2fa47af66a6adf662a615aead100510fe90834f349b7720e01f4dcbec9bde23d221fb60def146d8982bdea6cdf008085bf9d06cd9b2ad4be698d7964fb36d3212b68eea17af7766abf50663c2f00ffa8e339aed0be8dac7d7ba44cfb26dba991b66f3d225d35b66ff7533d7b74000cec37d90671fbb690742be7ff405fce7e93f3816cbf7d58b4255aeb59b27d0093fbdbb97dfb5885da37ad7d553efb8d6505cda7a9de7d96daafbdff1321bd003de9788e2bb46f236bdfbe51a67d93edd448dbb7f9225d35b66f4f503d7b72000cec37d906b9fe236fe2b0ef91f77a220def89d46a977ddfc2741246c4417700dce6221de602cb9dc972a8c887d7c67e4c6d60bd83d694e32f0e1c08f35a3f00af29bbd613ab75adbf9acffb810ce5cefb71adab83e634a2417d28772e0f6800fe20fac1f4d73907be8519cd9aed9f8759b395fb065857bc66ab358e92b24000f7cd0e59fe2ef4abf0bd95ea388acbc7e4dbe19015347ba8fd8f260d8479df0009ef373ad4f11c57393b83cb57612ffe90ffb34b3b9279f3ff4152e23de4bb000bd819f21c1afec6b971d220ad5c8f86aecb9d09ee4a27cfbae5b3f71738e4002f4695e91b79adfe80498361d423cd7e67814747a7918e40c3eb8e5af6835c00cf050efe6e56fe8fc6b5b70f3493ac0eb19fccf59d896b3fadd6b7a3befe9200c7ad723fed70fb281b08e34c4ad323e28c5c5ae717c87511dc839fc138dd86007b280e61b65fe4becbb1fe86073274121eadfadf25f0b8782be8a2c07ac75500aeaf523e43bb30da3516de6ba2b1aea4b5d782dbf6b4bf85916d8d6b6f4c2500f7eefbc638e5fe7b53ed980d1ed966f0d8b1b34a30bac6b25a76b5af0dabc4007eb0e178f37e30f43dbefd60d5505e3c67a3d07797bed1385df032b6c5fc4900aa7c0b6c33602c2271e488e65b64172fb2619ffdd021e234f71a0cb7ff9ded0007b62910e66f3434fad19a68dfef7d659bcaeb0b751e39787d618db0a9db1c003223cc7b2e678b38cd770cbc90b76b0e0a32723f329676ddfec8dbf77e306f0085b16d5ea96e0d393f5c9e4bc77b6341b38dde15d71ca73c8781e7a2f9db21008df744674e34ef9c13759dc50a9a5d4247f33d3ae277579e135d1bedfbed6e00938d976bbc38ef58e6d114e98efd796f7791ee79ee4d6b9ceb6b0394bf011d00116f9f4d564dfd389f3307fc2c5bad4dd768ef9b75f59a67fba9dcfc35685e0046f3a9a86fd8a32f6d64b6199037cf4f21be45e4adb90ec0982608dcf31d98005e253069ecabd56c2be4dac47487aca0793dd9c66fa0b507ae77787ebfe33900ae72f32b3c6eacf4da04f3ae86b5897794599b00a6d1ae4d748874d5b836710017d5b3fb697cd8a384698147475da423d0f0fa809cd3053dcf33e17daa8bdc0036718e68de23da9262faf23adb37b6c98109348f54a87d5390d5d9bef538640005cd6354ef3e44ed97b4234afb741ccf7185f66d64eddbe7cbb46fb29d1a6900fbd625d25563fbf638d5b327a87d9bae8469814747bc570a34fcdf0dd7b9770078ce6b7d5a637adff9178c1b71d01db7b948075b92db65c854276890364734004f521b58efa035e5f800edf19267f1b10dc4f311d53a5758cde7d0b8ce109400e7d0b8e6e540733ad1c8f57dd77931a0794af483e9cf93f48d7acee7df869900f391f38ebef36234d6795db240f71d0e597e27f4abb08fb0a039f7c0e563f200ed72c80a9a3f51fbff17b223504ebc5e71c0e47d9fe32a676770f96afce78d00fb15f92d14f3e66f2252e23da44f839d013e7c360bc2b593076965ff2cbfe70030ef883c13dc956eb648c7fb8de739e42f46e9caeffbcf229f59f357aa67a800479afdce3c8f8e4e271d8186e7adb4f60ac9fe44aefbf1dc569da071cd6dd5005b1d623d0a7d39ef9f927dbf525b56762f14db70f2fbeb7a878cbc0e6beaff0045369ee7488b224ef39b04e425f3063f8371163d471cc26cbf4c17719af54d00da9db8e7bd6990613ae1d1b2fbe5b70e2ede1a6beeac775ce5fa2ae533b40b00a31da3f35a85c6d8466bae9edbf6b4f778c9b6d4b5b652c9bd3fc39dc7341e00311b3cb2cde0b1e3f42ac1e81acb6ad9d5be36ac12eb89c3f1e6f544f0f5ad0027564379f19c8d42df5ddae37586e0551a1b4c56e55b609b01631189234734005f983c483bcb867df64397881bcbfd336c3fb04d8130eff1d21ad3fbd63c7800cf81fc87886bcd033497089bbacd2133c268fff87f0a95681347f21f0bc8c800fdc858da75fb236fdffbc1bc15c6b679cd3d93183ba2dd92758bf743aea77700c535c729bf2de66f6178efa1c67b52c97d70721f33cf236e163a9aebd111bf00bbd00d686ba37df7fe374543f772e139f678c93c9a22ddb13fcf6114e99ee700deb4c6b9be3640790ff98878fb6cb26aeac7f9fc04d7d904b5365d83bdefd000d56b9eeda772f3d7a0b995e65351dfb0c74bdac86c33200ffee61cf19d226f00bd7580f621e7ce4e10b8e73a30bd40604abfdeb4e735db0ab93631cb212b68006e23dbf8a5b4f680fa7011e9ed2ec7735ce5e65778dc58e9b509e65d0d6b13006f2cb336015d8f766d42ee57acc6b58957503dbb8bc68745254cf33c3a9a49003a020def259825d2c933f00c0dcaa94ed0206d8e68ee116d49fab6b2bb7d9b002564e3f6ed1d156adfb4c605b27d2b3a6405cd8354ef1ea2f64bda11a57d3a008ee7b842fb36b2f6ed2365da37d94e8db47d9b29d25563fbf63eaa678f53fb00769112a6791e1dcd221d816636c5c9b31bea290daff5159570cbf655aeeb3500521c74c76d2e68614b72bb2ccf576c10f9f0f98a4f501b58efa035e57825ed00f12ada789e9372cd4754eb5c61357fc7ea3a1b437ec7ea9a9703cd194483fa0050ee7b53d07c57f483e9cf93f48d7acee787c3ccf9c87947dff7a65adf564b0059a0fb2e872c3f13fa55d84758a8c47e12791e32cb0a9a5f51fbff6bb223f800bdc2f3bf3a9ee32a676770f96afc7f8cfb1579e61cf3e673bd53e23da44f83009d21bfafe4bdfbff457686ec9f81a9dc5977ae74f20ca746a299e390bf98b200fcbefdcffccdeb6fa99efd95ec0cad7e678e474767908e5cfffd2c2ae191f300816750d85cdce7d4091a5e1b04cd3fc47a14fa72de3fe55a8fd7da13edeb2f00d98693ebf1c3adc3f21eaf8b29cd05224eb3dcc00b795f40616084dd7701c50021ccf6cb4c116770cf50c2ed9b7f9e4118c18ff7ab21ccf6cb0c4f5e87108d00fc7e4cf2e3efc78e9e32e0ebcd2df439cf5301161e6f83660a61e2b4c0758100908775c5e38f0b529765e05b38290b747f81439693847e15be476c579275c800fe44390fccb282e6b429837a39c386eba99c785cd4e1788eab9cfdc2e5abd0005fe5b9fd94df3a326ffeef524abcf7398781c72bbca68270fb94415a39ae8100ae796c27ed7e57ba19221d9f4bd9e590bf18a52bbfefcc43fefef44caa67a800479afd4e97474787908ee4dcb0c1334b098fec4f8003fc5cf3cb87087c3cbf007c2eb5514616f4e590b33edab7ef576acb5a5df334726f796334744e0c18a5008ca67e9c543710e6efc0a6529a0e11a769070f77de107f6b35dc3776aeb5e9006e25dcbe79a36ec20819caed31aba13432af7aa291df57487edcbf2e55ef5f0007ec17fe6eb34858f89c13d0ac10f68bdce3df21e4615df1f840613f5e974b0016e8bec321cb5a75fb30dfa524eb90ef5de4bf625956d06ca07ea58fec13940013f741bb1ccf7195b35fb87c15c61b25fbe55cc2598cf6b5b50deff3086b4a00bc5b9937ec17f0417c8ec23bc97e011df4015db38d7f8e0d337699ae5ba46b00249a731cf217a374e597e3b31902b32993cd54cf7691fda2d5ef9ce3d1513d00e908346ce369ed8d95fd0970b8ce78aa13343c6f0a9a9b85fd82be9ce5947d00bfe6dab26fef2ff8b14dcdf32f5246533f6e9e301036f55f9e6569e85b459c00912baf241778216fdc839fc1087ba495e2103e9370378b38837baa126ef94d000feea71246c8e03a23e02cebd7501a99d7c14423e70e253f1ec3bd46f4afe900f7837d1daebd1dc0c2fd2068ee10f68b1c93b50a795857bc9720fdf2ec2bb8006439d8a15fd0bc49e837fd36b6afa05977e55eca824356d0dc43fdcadbc93e0041dd6e23bd3dea788e6ba4fb5414c61b79d7fc5ab78337efb74a89f7107b1100f68bfc2f04db8b8f90fd226d7ce8ba9c8def4a9717e95cdff769f65d727cd6002d3097ce57a37af628d92fad4a98ba3c3a3a987424cf893478da94f014041e00e0e071aedc43001aa4e575df8f08fb057d39e4f4fdef4dcb5ef4f5973c278800b856c2286534f5e328b25f90661aa56911719af68bcf2e63fb05edaaebbc7000b65fa44d33311afa0d413145dc3ebbab40182183cbee62fba5e0c9eb20a241003daef3f0cb11cdd744ff9afe3b3760bfb4dbbcd027004b1b6102cd3785fd820078e06a11f2b0ae40ab34c75d70c902ddb73864f9bed06fbb0226cdf97c2e1f0093ef5487aca0f931f52b3f25fb04e5c4678ffcc1f11c5739fb85cb576bfd8800c76f45e2c3bcbb096b4abcf7f9c68bfb731e3723fc7bb25f64bf2fdb7c9e870066ec325d41a46b249a0e87fcc5a832eb47e067cae429aa677f20fb45abdfe900f0e8e820d211685a49475af32f720c0b1c3cff021c758286c71da0f9abb05f00d097f37a0568b99f5768cbcaf697e0d718ed3b7751ef90d1d48f9b6c63c2b600c1d994669a88d36c4bc10b79e39e6d156963355098ed97bc88d3b4dff302b700cb5671d96208b3fdd2eac96b22d1a0ded579f8e588a6e1d8011ffd6bfa36dc0080fdc263ef6254dea63a9c30715ae09a26e4615df1790ed3529765689f0e5900a0fb690e592609fd6a8cc93465e5f231f9363b6405cdf1c70eeae5441be6b6008fdb9566c7735ce5ec17e5f33a9cdf81b9fe0725bf254f81f790fe1bf68beb001c1084cf3e769056ce11c8f94df38ea0fd77fd2303e95a453a9e5b6877c85f008c746d0579aebc299393a99ea11e69f63bed1e1d4d241d8186db632dfb45f60027c0c1f60bcabf4ed0206d8e683aa88d32b2a02fe7b90dd9f76bce2f49fb0c00f7e0c76b5cbc1624652cd58f9a419d0cb98a2902ae8d06163af1d2d6dafc9b00e8fe02f1fc42f17c8eb89f27ee1788fb4bc4fd6271bf54dc5f2aee5788fb5500e27e8db85f2beed789fb5e71bf51dcf78bfbcde27eabb8bf52dc5f25ee778800fb6bc4fd2e717fadb8bf5edcdf28ee6f16f7cf11f7b78afbe789fb1788fb170089fb9788fbdbc5fdcbc4fd2bc4fdebc4fd1de2fe4e717f97b87f93b87f8bb8007f9bb8bf57dcdf27eeef17f7ef12f70f8afb87c4fd7bc5fdc3e2fe5171ff010071ff2171ffb8b8ffb8b8ff94b8ffacb87f42dc7f51dc7f59dc7f4ddc3f29ee00bf2deebf27ee7f28ee7f2cee7f26ee7f2feeff20eeff2aee4d80ef6bc57d4e00dcd7d97bbe6aad5fb47e5bbeb3bdbdbfabd0dfdad6da9b2ff46ce8eec8b777006ce8ec6eed6eede8eee82b74b7b5f577b77777f56ce8e9caf7b4b6b7f5b76e00eae869db641bcfa628bd76f8c2f4f2ca8f17997f5f2199f34fef6afd438afa003bb426fdbefbd011d4f5fcd3bb861cd2fc7475303dd229f79a94653e27059900f385aeaeee9efe1ecdb2e94eb16c668d93b2393f1a1fede5841465be609cc8005c93a2cce76550e673a3cabc834f57e6c352e8cbfafbbabbbb3a5bbb0d36330000fe366135072d1a1ec69f61ef6b843f331a3cf8dcf8a75a7f82f59bacdf6d00fd6759ff22eb5f6cfdd9d69f63fdb9d69f67fdf9d65f60fd85d6bfc4fa8bac00bfd8fa4bacbfd4facbac7fa9f5975b7f85f5575a7f95f5575b7f8df52fb3fe005aeb5f6efd75d65f6ffd5eeb6fb0fe46ebf759bfdffa9bacbfd9fa5bacbfd500fa5758ff4aeb6fb3fe55d6df6efd1dd6bfdafad7587fa7f577597fb7f5afb500fe75d6bfdefa3758ff46ebdf64fd9badff6ceb3fc7fab758ff56eb3fd7facf00b3fef3adff02ebbfd0fa2fb2fe8badff12ebdf66fddbadff52ebbfccfa2fb700fe2bacff4aebbfcafaafb6fe6bacff5aebbfcefaafb7fe1dd67f83f5efb4fe001bad7f97f5efb6fe9bacff66ebbfc5fa6fb5fedbac7f8ff5efb5fedbad7f9f00f5df61fdfbadff4eebbfcbfa0f58ff41ebbfdbfa0f59ff3dd67faff5df67fd0087adff88f51fb5fefbadff4fd6ff80f51fb3fe07adff21eb7fd8fa1fb1fee300d6ffa8f53f66fd8f5bff13d6ffa4f53f65fd4f5bff33d6ffacf53f67fdcf5b00ff09ebffb3f5bf60fd2f5aff5facff25eb7fd9fa5fb1fe57adff35eb7fddfa00dfb0fe93d6ffa6f5bf65fd6f5bff3bd6ffaef5bf67fdef5bff07d6ffa1f57f0064fd7fb5fe8fadff13ebffd4fa3fb3fecfadff94f57f61fd5f5aff57d6ff3700ebffdafaff6efddf58ffb7d6ff0febffcefa4dd6ff83f5ffd3fa7fb4fe9fac00ff67ebffc5faffcffaff65fdbf5aff6fd6ffbbf5ffdbfaff63fdffb5feff59001fee1fd68f6c3f5163fd5aeb1f60fd9cf52758bfcefa075a7fa2f50fb2fec100d6afb7fe21d66fb07ea3f58f88dde1e858ed95b67dcf63c6a7db671fa130fe003c82c69f69dbf926df6744e9db2e47d654c6aecc3fbdab354d998f1a2732a70039d770f43891b93645998f1927321f90a2cc93c689ccb914659e3c4e649e9000a2cc53c689cc7529ca7cec3891f9f414653e6e9cc87c6a8a321f9f41994fc800a0cc276650e693c689cc47a638d66aca60399f9c41994fc9a0cca76650e6d3003228f3e91994f98c0ccafc8c0ccafccc0cca7c6606653e2b83324fcda0cc67006750e6e60cca3c2d8332b76450e67c06656ecda0cc850ccadc964199db33280073470665eecca0cc5d1994b93b8332f76450e6e91994f99c0cca3c2383329f009b4199cfcba0cc333328733183329f9f41992fc8a0cc176650e6591994f959001994f9a20cca7c7106659e9d4199e76450e6b91994795e06659e9f419917640050e6851994f9920ccabc2883322fcea0cc4b3228f3d20ccabc2c83325f9a410099976750e6151994796506655e954199576750e6351994f9b20ccabc368332005f9e4199d76550e6f51994b93783326fc8a0cc1b3328735f0665eecfa0cc9b003228f3e60ccabc2583326fcda0cc576450e62b3328f3b60cca7c550665de9e004199776450e6ab3328f335199479670665de954199776750e66b3328f375190094f9fa0cca7c430665be318332df9441996fcea0cccfcea0cccfc9a0ccb7640050e65b3328f3733328f3f33228f3f33328f30b3228f30b3328f38b3228f38b003328f34b3228f36d19fc1fdded192ce7978e1399d3fccfe6cb3258ce2fcfa000ccafc8a0ccafcca0ccafcaa0ccafcea0ccafc9a0ccafcda0ccafcba0ccafcf00a0cc776450e6376450e63b3328f31b3328f35d1994f9ee0ccafca60ccafce6000ccafc960ccafcd60ccafcb60cca7c4f0665be378332bf3d8332df974199df00914199efcfa0ccefcca0ccefcaa0cc0f6450e6073328f3bb3328f3431994f9003d1994f9bd1994f97d1994f9e10ccafc4806657e348332bf3f8332ff53066500fe4006657e2c83327f3083327f2883327f789cc87c788afb7a3f92c1727e3c0083327f3483327f2c83327f3c83327f2283327f3283327f2a83327f3a83327f002683327f3683327f6e9cc8dc94a2cc9f1f27321f91e218e3890cd6ed7fcea000cc5fc8a0cc5fcca0ccff924199bf944199bf3c4e643e304599bf324e649e9800a2cc5f1d27321f94a2cc5f1b27321f9ca2cc5f1f2732d7a728f337c689cc8700a428f393e344e6861465fee63891b9314599bf355ecea14951e66f8f13990f004b51e6ef8c9775c91465feee7899274951e6ef8d13998f4c51e6ef8f13998f004a51e61f8c13998f4e51e61f8e13998f4951e61f8d139927a528f3bf8e13990027a728f38fc789cc535294f927e344e6635394f9a7e344e6e35294f967e34400e6e35394f9e7e344e6135294f9a97122f38929cafc8b7122f34929cafccb14006536e7081f60f33a95e4afb13a30cf72b19b10bbbad899797a336f6de671cd00bca699e733f35e661ec8cc8b987902336e36e34833ae32e30c63771b3bd4d80065c64e31fdb6e9c74cbb6eda39f3de9bf7c0d40ba3a7a6d89d1cbb5308cf9300d63f2a067674ec8e89dda4d84d8edd94d81d1bbbe362777cec4e88dd89b13b0029764db13b3976a7c4eed4d89d16bbd3637746ec9e11bb67c6eeccd89d15bb00a9b13b3b76cdb19b16bb96d8190599c3960bb16b8b5d7bec3a62d719bbaed80075c7ae2776d363774eec66c4eedcd89d17bb99a69c62777eec2e88dd85b19b0015bb67c5eea2d85d1cbbd9b19b13bbb9b19b17bbf9b15b10bb85b1bb24768b0062b738764b62b73476cb627769ec96c76e45ec56c66e55ec56c76e4dec2e8b00dddad85d1ebb75b15b1fbbded86d889df93fbcf95fbaf97fb8f99fb6f9bfb400f9dfb2f9ffb0f91faff93fadf95fabf97fa9f99fa7f9bfa5f9dfa3f9ffa1f9001fa0f93f9ef95f9cf97f9af99f98f9bf96f9df94f9ff92f91f91f93f8ff95f008df97f8bf99f89f9bf87f9df85f9ff83f91f82f93f80392fdf9c1f6fce533700e78b9bf3b6cdf9d3e63c66733eb139afd79c5f6bce7335e79b9af33ecdf99700e63c48733ea2392fd09c9f67ce9333e7ab99f3c6ccf95be63c2a733e9339af00c89cdf63ceb331e7bb98f34eccf91fe63c0c733e84392fc19c1f60bea737df00979befadcdf7c7e67b5cf37daaf95ed37cbf68bee733dfb799efbdccf74fe6007b20f37d8cf95ec47c3f61be2730fbebcd7e73b3ffdaec4736fb73cd7e55b3007fd3ec6734fbfbcc7e37b3ffcbec8732fb83cc7e19b37fc4eca730fb0bcc7a00bb597f36ebb1667dd2acd799f52bb39e63d637cc7cbf99ff36f3c1667ed4cc00179af933339f64e657cc7c83197f9bf1a8199f99f18ab1df8d3d6bec3b63ef0098fedff487a67f30eda5693f7e42efd6e1d63fcefa1b7bb76d6bdabda3a97700d7aefe9dbbd75dd57bc3ba0d5b77afdbb5f5a6fec8bec211bd9e73b66fddbd00b577dbd69b7a776fddb1bd694befae2d4d7d3bfa77356ddfb1bbe9aadedd1b00b744f6c52e35583255ffcea6debebe9dfdbb76356d1d48b37b4b7fd3c61ddb0077efecddb8bba9afffea6d3b6eecdf69d25c67d31e6ffddeddbbfbafba7a7700096b5f5fd3f55b776f69da715dffce4ddb765c6f9e3f6f14f4b14ea2d32c9d00799193a49b6fd34db1f7e7efdcd97b63d3d6ed7dfd3734edb87677d38e4d4d001b765cbbbd6f1727ba2d21b3bb9230bb2f49a253262443787bc274774d480000f29e2489de9524d1076ca211bc2e9cec2349787d3649a2af2449f4dd24897e009324d17f26ac16cd75c9d215eb1280bc3821b32f1c942c5dc3c1c9d25d7e700002e1362564766d1266372764f68184e9fe98305dae3e8170f5f5c9984d4ac200ec8484cc1e4a98eef709d39516c213a4bb2161ba0f1f9240999f4c92e8cb490012fd23a158d31a92a5bb2b61baef372410eea74912fd2e49a23f2749f47f490012e51a13246a4c92e8e824894e4b92e8ac2489f24912752649342349a2393600d1280db1054978ed4892e83a9b68b46fe2f39330bb2d21b30f88744bfa7bfb00e23159dfd6bed2606c677cdb6414dbd7bbbb97d39d7468b2740f1d9a0ce7c300872650ca6349127d3321c2a79230fbf724890e3c2c19c24909d31d7758029000272749343321c24b12a65b9a04e4aa2489ae4f88f0b684e95e9e04e46b932400fa6842849f4cc2ecf349123d9510e19f13a6fb6b1290ff9724d17187274338003561ba96c31380ec4892686942841b9330db9a24d14b12227c4bc274f7260100f9ae24893e9f10e10f13a6fb491290bf4c92e8802392219c9230dd0947240000796a9244e72744f8a284e9de9004e47d36d1284df60792f0fab84d74f2be82005d75edb6dd5bafde76a35fba4f27e1f8e384aafc791266b92393313b3a61ba002947260079529244e72644b83461ba154940ae4d92e8e684085f9330dd1d490040be2949a28f2444587354b274471c9500e49424899a9224eab68912b53d330092705c9550916b9330bb2921b35b9230bb2721b34713a67b2c09c88f2649f400fd84084f383a59ba69472700d99124d1029b2851fd5f9c84e3f6843ad9998400d9eb1332bb3f61ba0793807c3849a2af244438e59864e9ce3a2601c8d6248900ba93249a9924d1329b2851e55f9984e3b509b57f6312666f48c8ecee24cc3e009490d9479330fbd784cc7e9f30dd9f9280fc5b9244c74c4a867075c274574e004a00726792442fb58912bd6caf4cc2f1a1843a793809b36f2464f6f384e97e009504e47f244974f0e4640817264cb76e7202909b9224bac5264a54259f9f8400e3db12eae4be24cc3e9f90d97712a6fb4112903f4d92684f4284474d49966e00f29404204f4c92684642844b12a65b9e04e4654912dd9410e1ab13a67b7d120090772749f4e18408bf9a30dd9349407e2f49a2ff4a88f0906393a53becd80400208f4992a87d1408a3ff0f6b53bb893d91050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index f9e907a3dbb5..bbc2ef9bf758 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -65,8 +65,9 @@ export const INITIALIZATION_SLOT_SEPARATOR = 1000_000_000; export const INITIAL_L2_BLOCK_NUM = 1; export const BLOB_SIZE_IN_BYTES = 126976; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 15000; -export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 500; -export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 500; +export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; +export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; +export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 19; export const REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8n; export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = @@ -75,7 +76,7 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; -export const DEPLOYER_CONTRACT_ADDRESS = 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78n; +export const DEPLOYER_CONTRACT_ADDRESS = 0x191fcff2a10d324c43746659c7ba4cf6af06c98e26291b83312e89f49974c924n; export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; export const MAX_NOTE_FIELDS_LENGTH = 20; export const GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index 9ff2f8d54a13..ca49088fa9e8 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -5,7 +5,7 @@ describe('ArtifactHash', () => { it('calculates the artifact hash', () => { const artifact = getSampleContractArtifact(); expect(computeArtifactHash(artifact).toString()).toMatchInlineSnapshot( - `"0x10d144027c5d0dddb7336f9becb14db882c0f4e48cfab674f1871458f81838ca"`, + `"0x1d1e8f03c63b76d26cfd04ce728586c014768cec6765fdb8f3f24cb931b5ec17"`, ); }); }); diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.ts b/yarn-project/circuits.js/src/contract/artifact_hash.ts index a51a609ccfd4..a6b5dc37d515 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.ts @@ -1,6 +1,7 @@ import { ContractArtifact, FunctionArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { sha256 } from '@aztec/foundation/crypto'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, reduceFn } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; import { numToUInt8 } from '@aztec/foundation/serialize'; import { MerkleTree } from '../merkle/merkle_tree.js'; @@ -8,6 +9,11 @@ import { MerkleTreeCalculator } from '../merkle/merkle_tree_calculator.js'; const VERSION = 1; +// TODO(miranda): Artifact and artifact metadata hashes are currently the only SHAs not truncated by a byte. +// They are never recalculated in the circuit or L1 contract, but they are input to circuits, so perhaps modding here is preferable? +// TODO(@spalladino) Reducing sha256 to a field may have security implications. Validate this with crypto team. +const sha256Fr = reduceFn(sha256, Fr); + /** * Returns the artifact hash of a given compiled contract artifact. * @@ -30,25 +36,37 @@ const VERSION = 1; * ``` * @param artifact - Artifact to calculate the hash for. */ -export function computeArtifactHash(artifact: ContractArtifact): Fr { +export function computeArtifactHash( + artifact: ContractArtifact | { privateFunctionRoot: Fr; unconstrainedFunctionRoot: Fr; metadataHash: Fr }, +): Fr { + if ('privateFunctionRoot' in artifact && 'unconstrainedFunctionRoot' in artifact && 'metadataHash' in artifact) { + const { privateFunctionRoot, unconstrainedFunctionRoot, metadataHash } = artifact; + const preimage = [privateFunctionRoot, unconstrainedFunctionRoot, metadataHash].map(x => x.toBuffer()); + return sha256Fr(Buffer.concat([numToUInt8(VERSION), ...preimage])); + } + + const preimage = computeArtifactHashPreimage(artifact); + const artifactHash = computeArtifactHash(computeArtifactHashPreimage(artifact)); + getLogger().trace('Computed artifact hash', { artifactHash, ...preimage }); + return artifactHash; +} + +export function computeArtifactHashPreimage(artifact: ContractArtifact) { const privateFunctionRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.SECRET); const unconstrainedFunctionRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.UNCONSTRAINED); const metadataHash = computeArtifactMetadataHash(artifact); - const preimage = [numToUInt8(VERSION), privateFunctionRoot, unconstrainedFunctionRoot, metadataHash]; - // TODO(miranda): Artifact and artifact metadata hashes are currently the only SHAs not truncated by a byte. - // They are never recalculated in the circuit or L1 contract, but they are input to circuits, so perhaps modding here is preferable? - // TODO(@spalladino) Reducing sha256 to a field may have security implications. Validate this with crypto team. - return Fr.fromBufferReduce(sha256(Buffer.concat(preimage))); + return { privateFunctionRoot, unconstrainedFunctionRoot, metadataHash }; } export function computeArtifactMetadataHash(artifact: ContractArtifact) { // TODO(@spalladino): Should we use the sorted event selectors instead? They'd need to be unique for that. const metadata = { name: artifact.name, events: artifact.events }; - return sha256(Buffer.from(JSON.stringify(metadata), 'utf-8')); + return sha256Fr(Buffer.from(JSON.stringify(metadata), 'utf-8')); } export function computeArtifactFunctionTreeRoot(artifact: ContractArtifact, fnType: FunctionType) { - return computeArtifactFunctionTree(artifact, fnType)?.root ?? Fr.ZERO.toBuffer(); + const root = computeArtifactFunctionTree(artifact, fnType)?.root; + return root ? Fr.fromBuffer(root) : Fr.ZERO; } export function computeArtifactFunctionTree(artifact: ContractArtifact, fnType: FunctionType): MerkleTree | undefined { @@ -58,8 +76,8 @@ export function computeArtifactFunctionTree(artifact: ContractArtifact, fnType: return undefined; } const height = Math.ceil(Math.log2(leaves.length)); - const calculator = new MerkleTreeCalculator(height, Buffer.alloc(32), (l, r) => sha256(Buffer.concat([l, r]))); - return calculator.computeTree(leaves); + const calculator = new MerkleTreeCalculator(height, Buffer.alloc(32), getArtifactMerkleTreeHasher()); + return calculator.computeTree(leaves.map(x => x.toBuffer())); } function computeFunctionLeaves(artifact: ContractArtifact, fnType: FunctionType) { @@ -70,11 +88,25 @@ function computeFunctionLeaves(artifact: ContractArtifact, fnType: FunctionType) .map(computeFunctionArtifactHash); } -export function computeFunctionArtifactHash(fn: FunctionArtifact & { selector?: FunctionSelector }): Buffer { - const selector = - (fn as { selector: FunctionSelector }).selector ?? FunctionSelector.fromNameAndParameters(fn.name, fn.parameters); - const bytecodeHash = sha256(Buffer.from(fn.bytecode, 'hex')); - const metadata = JSON.stringify(fn.returnTypes); - const metadataHash = sha256(Buffer.from(metadata, 'utf8')); - return sha256(Buffer.concat([numToUInt8(VERSION), selector.toBuffer(), metadataHash, bytecodeHash])); +export function computeFunctionArtifactHash( + fn: + | FunctionArtifact + | (Pick & { functionMetadataHash: Fr; selector: FunctionSelector }), +) { + const selector = 'selector' in fn ? fn.selector : FunctionSelector.fromNameAndParameters(fn); + const bytecodeHash = sha256Fr(Buffer.from(fn.bytecode, 'base64')).toBuffer(); + const metadataHash = 'functionMetadataHash' in fn ? fn.functionMetadataHash : computeFunctionMetadataHash(fn); + return sha256Fr(Buffer.concat([numToUInt8(VERSION), selector.toBuffer(), metadataHash.toBuffer(), bytecodeHash])); +} + +export function computeFunctionMetadataHash(fn: FunctionArtifact) { + return sha256Fr(Buffer.from(JSON.stringify(fn.returnTypes), 'utf8')); +} + +function getLogger() { + return createDebugLogger('aztec:circuits:artifact_hash'); +} + +export function getArtifactMerkleTreeHasher() { + return (l: Buffer, r: Buffer) => sha256Fr(Buffer.concat([l, r])).toBuffer(); } diff --git a/yarn-project/circuits.js/src/contract/contract_class.ts b/yarn-project/circuits.js/src/contract/contract_class.ts index 8d775228503b..5d79acab8814 100644 --- a/yarn-project/circuits.js/src/contract/contract_class.ts +++ b/yarn-project/circuits.js/src/contract/contract_class.ts @@ -1,4 +1,4 @@ -import { ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { ContractArtifact, FunctionArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { ContractClass, ContractClassWithId } from '@aztec/types/contracts'; @@ -30,11 +30,7 @@ export function getContractClassFromArtifact( const privateFunctions: ContractClass['privateFunctions'] = artifact.functions .filter(f => f.functionType === FunctionType.SECRET) - .map(f => ({ - selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), - vkHash: getVerificationKeyHash(f.verificationKey!), - isInternal: f.isInternal, - })) + .map(getContractClassPrivateFunctionFromArtifact) .sort(cmpFunctionArtifacts); const contractClass: ContractClass = { @@ -47,11 +43,21 @@ export function getContractClassFromArtifact( return { ...contractClass, ...computeContractClassIdWithPreimage(contractClass) }; } +export function getContractClassPrivateFunctionFromArtifact( + f: FunctionArtifact, +): ContractClass['privateFunctions'][number] { + return { + selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), + vkHash: computeVerificationKeyHash(f.verificationKey!), + isInternal: f.isInternal, + }; +} + /** * Calculates the hash of a verification key. * Returns zero for consistency with Noir. */ -function getVerificationKeyHash(_verificationKeyInBase64: string) { +export function computeVerificationKeyHash(_verificationKeyInBase64: string) { // return Fr.fromBuffer(hashVK(Buffer.from(verificationKeyInBase64, 'hex'))); return Fr.ZERO; } diff --git a/yarn-project/circuits.js/src/contract/events/__snapshots__/private_function_broadcasted_event.test.ts.snap b/yarn-project/circuits.js/src/contract/events/__snapshots__/private_function_broadcasted_event.test.ts.snap new file mode 100644 index 000000000000..2ec431f22523 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/events/__snapshots__/private_function_broadcasted_event.test.ts.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PrivateFunctionBroadcastedEvent parses an event as emitted by the ContractClassRegisterer 1`] = ` +PrivateFunctionBroadcastedEvent { + "artifactFunctionTreeLeafIndex": 0, + "artifactFunctionTreeSiblingPath": [ + Fr<0x18e01957faa463b1495f979f153e839d5fbd2af9b0d142940d1df29d9bcf4373>, + Fr<0x2c450d02ff856d78b6197914dbb85cc88b31ef6f32d534f01b893dcee663a119>, + Fr<0x1c26a986ab599f5027f02a8390d7cb96c87146380163a5fa002c30cba8e32f68>, + Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, + Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, + ], + "artifactMetadataHash": Fr<0x229d43c7daac528d0aefd72bee59385d7e9ff06ea477b673389e1f65168cba9f>, + "contractClassId": Fr<0x1b92d01f98e681f3630ac84aaf982fc1e7d5b3ca38b2929dab2b5799fdbdebb3>, + "privateFunction": BroadcastedPrivateFunction { + "bytecode": Buffer<0x1f8b08000000000000ffed7d077c1dc5b5fe4a5c0b8390e8d8a68a9a80917575d58dc11770c01d375c31b66cc9058c0db6e909a407d21b2190062124109200792185f4c64b5e3ae9e5a591e4e5252fc94bf9bf24af38ff9dabf9ac4fc73357d2b2e7eaca3bfbfbcd6f6667cfccf9ce99d9993365676ba281eb9735517469cd40d878b5b13b20764d749fb361dc4f10f775827ea2b83f5cdc1f29ee8f16f793c47d93757c59c851d1fa6df9cef6f6feae427f6b5b6b6fbed0b3a1bb23dfdeb1a1b3bbb5bbb5a3bba3afd0ddd6d6dfdddeddd5b3a1a72bdfd3daded6dfbaa9a3a76d537ee03a95f2ca3fcdab867460ae9363b72776a758ffd48cf967c7ee345b66ac97d332aa0fd6cbe9d1c0fb86facc97ace3f9a777b5a698579ef19e110db409e6aa73e0c77580233c91e8f6a42c2bf392580e77e0dca70c8a2982d128cc3314f27d46945ea3aa25f733d22fa37ca55ec29a28fd97d0f878f19e19bb33637756eca646038d4c73ec4eb0cf270adda582a53ddf55e29f1baa335cf5d140471e11cd81d18021004cb9f431b59a7c27a49d6f3edf39d1e24f39dfbd864364711789cf04d2d541d1a09e53e2ddcabc61dc1d443c5174081f41b4a0833e50d6c0de100dd6cf83caa4cb89748d4453e790bf18a52bff8102cf8102f3048a331d484d6e5f6c1af5cdbc3fdc79427f91c01b114ee049fdbd8a75757034f8ee6eeedf7de18eedbb77f66edc3d67fbaeddbddb37f673b304d87502764d345404f9dc5cb58eb87a12ef008a43fa1cc5d50a75d43bf01c1429f6fb18c09836f89e68d0000648d3561ba367aaf5cf8c060d43e31f17bb69940e029d659f4f23ba16075db37dde62fd438d4c42d65a21f3d3edf3d36c93f251fa6d2be43dc0eae474eb9b36aa7518dde49fded53a35453db7a6a89b4ad93e6746e99627ae42140620c382d128cc8242be6d51750f408cdc6de997515e5698b4cb3f2d9d9e99625eedd1f86bc4d2c4cc783ba2d0880d0b46a3303b14f2ed8caabb11337277a65f464eac4f57fe368b356d6bacad4acb3b2c1fec3b4dde15edbb7cd095517db05ebaa3a10d33469c7ca5dd769c1ca56ff4a48df19448a793ee890646d1e6529a496de3995459963c824d91677e6234d4e84c25dfb81ccc6c1966cc4a533242aec329ae8664d498b5e2e928e48d7bf033335b0d367c75efc62bcfdfb9f9daabfab7efdec59500c41ce77a016b1dcf5cf35b4ad3864304ae1102f39cd80421976bee4c759e2c722825adbcbba3f47ae69ef47095862b75d1be5735af3739e03eedbcfbfbbabbbb3a4bc5a4a6836e251da48db3699ce09c1055779dda0b94f29c6efd73ac3fc3fae75aff3cebcf1418ceb7fe05d6bfd0fab3ac5ed1683e2b7617c5eee2d8cdb67173a2a16b747363372f76f363b720760b637749ec16c56e71ec96c46e69ec96c5eed2d82d8fdd8ad8ad8cddaad8ad8edd9ad85d16bbb5b1bb3c76eb62b73e76bdb1db10bb8db1eb8b5d7fec36c56e73ecb6c46e6becae88dd95b1db16bbab62b73d763b627775ecae89ddced8ed8addeed85d1bbbeb62777dec6e88dd8db1bb297637c7eed9b17b4eec6e89ddadb17b6eec9e17bbe7c7ee05b17b61ec5e14bb17c7ee25b1bb2d76b70b7dbd34762f8bddcb63f70af1ec95b17b55ec5e1dbbd7d8674df6d96b63f7bad8bd3e7677c4ee0db1bb33766f8cdd5db1bb3b766f8add9b63f796d8bd35766f8b06d65fee8ddddb63775fecde11bbfb63f7ced8bd2b760fc4eec1d8bd3b760fc5ee3db17b6fecde17bb8763f748ec1eb5585069df1fbb7f8add0762f758ec3e18bb0fc5eec3b1fb48ec1e8fdd4763f7b1d87d3c769f88dd2763f7a9d87d3a769f89dd6763f7b9d87d3e764fc4ee9f63f785d87d3176ff12bb2fc5eecbb1fb4aecbe1abbafc5eeebb1fb46ec9e8cdd3763f7add87d3b76df89dd7763f7bdd87d3f763f88dd0f63f7a3d8fd6bec7e1cbb9fc4eea7b1fb99d0f9cf63f754ec7e11bb5f8a67bf8addbfc5eed7b1fb77f1ec37b1fb6decfe2376bfb371bfb7fe1fa2a106d17fc6ee8f22ee4fb1fbb30dffc5faffcffaff65fdbf5aff6f22eddf63f7df22ee7f62f7bf22eeffa2811193b9fe617d348a35d6afb5fe01d6cf597f82f5ebac7fa0f5275aff20eb1f6cfd7aeb1f62fd06eb37c6fe8c636dda68f02a466959f17d1b78e4206d0ba30bac57c36fb2f107d8fb03447ccedee7c84237f113ecfd048a77edf1e005d71a8a43dda9a5b8bd8bba14b7775197e2f6ee87a07d24a82fa5346284514a03f9286e2264a3b8832017c51d2cf469e2ea8937e20eb1711329aec1c61d44718d36ee608a3b94e4837f988d3b241acc177d51314aabceb496f6c85c9076be71ce26df59e9e75b1a8d3f2b1a2ca722f1b9807475910da7bd47e659946f0df1417c8ec267102de8a00f8c3281dddcc3b6b8a84cbaf345ba46a2b9d0217f314a57fe5902cf2c81d994c971366ceaf1d1b61ef3ac89423deed4a9c7edf9508f075648a2c85d1f0fb3e1fdb11e9f4538d2afb31d9da1ed1df135ea3a7b21d1caba87bd8bfb639ded261c0a75b637d4d9115fa3aeb38b8956d6bda36c787facb317110e853adbaf53670bc1368806e69fa2c85df78eb1e1fdb1ce2e231ce9d7d92ea53adb16ea6c3430f71945eeba37d986f7c73abb8170a45f677bfb836d30e26bd475f666a29575cf4e2bee9775f66ac2917e9ddd50087576c4d7a8ebeced442bebdef136bc3fd6d95b0887429d0df30623bf465d67ef245a59f74eb4e1fdb1cebedc86cd9cede3b9817013c57dd4c69d4c7815eaf646a5ba5d08757b609d3b8adc75f4141bde1febf69b0987429ded0f7576c4d7a8ebec63442bebde6936bc3fd6d977dbb0697b3f65dbde3328eed336ee1914f7191bf74c8afbac8d3b93e23e67e374d737367684f762c4d7a8df8b2f10adacdf536d787f7c2f3e4e3814ea6c57a8b323be465d67bf43b4b2ee35dbf0fe5867bf443814ea6c4fa8b323be465d677f41b4b2eeb5d8f0fe5867bf6fc3c65ef892b5175a29eecb36ae40715fb1716d14f7551bd74e715fb3711d14f7751bd74971dfb0715d14f7a48deba6b86fdab81e8afb968d9b4e71dfb671e750dc776cdc0c8afbae8d3b97e2be67e3cea3b8efdbb89914f7031b57b471e60b27ec49fcb68d33650b7bac18a555b67d1d863f9f8d66ae1a715fa4f09984e799a9e3197a4ed0c6de6ddb16eddc7a5defeefe8baeddbe71f7d61ddb6b0822607f5bc0ae89868a80e77514574b61fec02b47e10914e6b4073ae25c974e910dbc8ecf243e45ba3f33f0ce2cefb3d2e75da8271eb8ca350f3c3c9caaa08b7ae231123c5375f1e41b090ff33a5b41f672f5e0ec2ae1dd9c3eef521d3c3b1a7a952bf366c2d3a2a08b7ae231123c2d84679a021e25394bdb3ef0d9528a27c4944cf956a1ab66a1ab46a2c993fe5a15f457437c9137ee5b090fea20b0d613ddd955821171d3088fc63b50ae3d68a902de4617e8b778ba13cfb9dfac86f2e2e9d789368cfec4d8a1cd35837815dad8d23b097e399b2f7034533c68f64c19c4d66ab135d0739e3a9e26e234eb0578216fdc835f03c9338de21066fb25af84b1c58311fcb87dacf3c891239a1956ffe6b30ab6475866d94ed5537a6ebb0a4a32cbf61ef705c208195b098f962dd32cf0343b74b13ff2f6bd1fcafd575eb36e61ea08d382b26ee5886601bd2b9c16b8641bdd40ba69231db529c982f2a811e5c3bc356cc872f5725ae03da6bc15faca02f7cbb8463a8651788f0b6c9b8d044f81f068bc8b4a7296da414c6ba73da6ea10ba6a11ba6a249a76d25f8782fe6a882ff2c63d7f1a3ade301b3cd216ab27ba6955821171dc6f68f5bfbe36ac5005bc791c88b10997178f49aaa1bc786e014740c01631f6cd4d340e5498cf2cb04e614f1d24f497239a4fd138f0161a07360bfdfac6de951a63e11efc78acea1a27f13850c11e2ee939efc1087e5c77ea3c72e488e676cf3890654618ed94afedd2b26f7def421b61947d80a6fd3555e099ead0c5fec8dbf77e306f2dfb47a16e95de27d80768b764ddca11cddd621c8878e0c27be29a9f006d2de58d674d36be5df0de63e3651e4d91ae0dabd0a79574dd29740dfc1da46bd0bc43e8bad3a36b6e03a01fd0d646fbda813536be53f086ae651e3556d79de9eb64c83b85b17b8be0af39efe96b4b2a31171f78fb792bd837a31e2bf3fa59faed415f094f7e14783a088fc6bba8d4ee95fa2e6c2b4b7becde2d749517ba6a249a2ed25fb782fe5ce372dc835fc01c3007cc0173c01c3007cc0173c01c3007cc0173c01c3007cc0173c01c3007cc0173c01c3007cc017331600e9803e68039600e98a38039fff4ae8039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e68039600e9803e6803960ae3acc064f0bf859bf9ee85aaa0423e23a094f87129ea9020fee3baa80b7d1c59936dc6c7d2eaf3309633594d754c278b00d17ac3f2176474d18c45b481d6f5fc1e06d237e45078e1cd13c7fca20ed648bad819e9f49f2b48a3823433e751906740e5ec81bf7e0d740f2b4521cc26711c6e6d4310ee839efc1087e5cbfeb3c72e488e654abffc3a2813a7596436684d14ed5537a6ebbc6ea7de532984a785a94f014049e82431763c59bdb2ebc4ff5f49cdfa3a94a185b0446dc4f258c882b109ef4dba6f2ef75e03db6bcdbd2e75da88f86be23e6aa11f7450ab7111e8d77b69e788c048f721b926f243ccc4bab2df0d583a955c25ba1cf2a70ff88ab5c99b3cddb953e9e2eb65d478287c74c9de9e369559233cff6f89e14f335baea11baea10ba6a249a6ed25f8f82fe6a882ff2c63df805cc0173c01c308f16b3c123c757f54437b54a30228ee7aa14fa94b276445715f036ba80bdcb738b78cef6763594572b61acb761d8a1662ae2669aab4a7f0e25df5523f8150907f8e5886616cd55dd4a735572fcca3616eb5cc37e2a373f087e0d240fcf9320cce31e0d9bb386b0488ce0c773f9751e397244f3529aab625978feb347c419f9a6a72f5fabab6dc73df83510f61ec2a335de6916789a1dbad81f79cbf5a06e076f85b62faf54b74aefce39362fb451b26ee588e66e7a2f382d70e13d411e0da49b7348471aef89c97786928ece153a02fe19a423d0dc2b748478a9237e77a19b7349470ab2144cbee729e968a6d011f09f473a02cd03424788973a9a413a826e405b4b613cdb637557a4fba72be3267b997ccf4f90efa6612e93ef054f1f6fab8c8828af5a0a83d7489ebb68cfa734e709ba3de2fec068b08cb81ee0f963540f26126d314aad6eb6b17e53ccb754e7a10bd4f99942fe1cd13c2eea3ce2810b751e793490ce58e717087d36d9f8f305ef3dd1d0323a9fe89574d2ceef5e8af996747da1cd0bba66fce6ca11cde784ae110ffa0ea1ab06d20f68f99dc0b31a1b7fa1e00d5dcb3c0cbd924e3a4cbeb3d2cfb7a4eb67d9bc2688bcc12f47345f15ba463c70f1bcb7b91a483fa0ada5bc5977b54483e7d0b5cc03ba56d049a7c9f7a2f4f32de9fae268a8ae81ff22d23568be27748df848e87a16e91afab998747d91d05d938dbf58f086ae651e4d56d717a7af932176764d34d4ce063fcdfd30bef14577e03da6bcd35f3f1bd8e722df3fcc07801fbf7fbf1de6fdc35c0dcf8dc8baeb4a07fae94403fe7582e662c2059a3f8abd356d8256f35df5955db5f05698172badbb72199aabdc3a27cf1d2bf423edf5d1d039b0e1f05c447834f65229c9599a07996df34a7bdd758ed055a7d05523d1cc26fdcd51d05f0df145deb807bf803960f6613678d03e016b3dd15d5c251811c7fb5214da8db27dc54555c09bd7d678ef2b9ef33a4f359417ef553fc48631e76b4c9193ea06f12a8cc7da192fec36e0e0312a68ea686ded548badc1a15f23cf05224e69fc5ed23978216f39a7d140f2f05c05c2bcb6a63077d65e130db51b1823f8191accd3d645feb919d04cb3fa3f4cc88236c994c17411a7b9a60c5ec81bf7bc8601ecd3098f964d3b4be099e5d0c5fec87b86e03d2323728f8477faeb6503635f6eb3396f5efb03cdf9f4de725ae0e2bed35c0d24438faa2cee3572290bef1fa913343d242f686693bc3ca66659b4da245f9da806deb5c48fe7036b05267361fe50d683a648ad5f2df0de0c5ce5c6c208ebcc15b4e7eb05bfe1f068ef33539a13c9f35c42da6373f99d4f51e88abff3e1b184d6b75172bf11ee5ddf543445e9eaa27904ba6876e051d8e3565617fc9de06830778c43cc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3ecc41cf41cf3eccd5a0673e0f167cf93bf29e2ac18838edb3c20d1edfb70f9d55c09bbf11c5b7365c5efcbd623594d72cc2a3f00d68e9bb8a06c1cb7cc774e5819a7c0ba53df8f2db538983bf3d3d62f220ed0e8bad819ee33de3ef44f8dd53f8aeb35496c84be6cde78fb1fe10076cfccdec4c11a7f99d973c2b10f77c9ea294652cf5a8c9dbd766ccd42d87029739ae1a715fa4307f37abd07eb6f337c323c1c3ed79f8c6cafd8dd574a1ab2c7f63e5d245b5dbcce53057abcd5ced7a3678647fc336d8cc2ac158499bb928f0e0be1236f370bcd966663bab5a6de622e129a68fa76433370a5ec62e7d806c660d7ba146f02b120e3e6b0034274e1aa47d0fd9cc788ef78cbff5e6774feb1f19be73f8c1af21729f1f22ed63df7f33b4de13df7f333a09a394652cf5a8c9db77de8b725b59e032c735521b55c1c61af59981daff5f559233cf6d7bda36f36ca12b69a336124d33e96fb682fe5c363ceec12f600e987d985dffbae2fff9765509c64a8e417dfd544b15f066db96ed21d7ff8eaba1bc94cf2e2ad9b6870a5ec67efcc581ba7c794e1236acc4c1ff3ef9f6d183d87e4db62d9ee33de3fe99df3dad7f9ffbc629fc4f2fc8c376a3b4637dff27d37a4f7cff276b218c5296b1d4a3266f5f9ba1dc561646fbdf5f6e3fd36f9b0a796e0747826736e1d19807506a834bb62dce674ddbb69d2b74e56adb413387f43757417f2e7b0af7731dbc9ba27475316f04ba98e7c033afc2ba00bfd1629e330e31073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d073dfb30073d27c36cf0c8f5a17aa2eba8128c88abc4be00df7ad5ec2ae0cd6bdcbc2e8ae7bcbe580de5a5bce7b5b4ae7598e065d6915f3351936f6b9ef71062ffa6c4c1ff4efad95183b477586cbc568cf7ccc8e3dacfa1b517d7b75f01fc78ad98d78fe57a763da5a9c43e0bb94f1ef7b309a394652cf5a8c97bb8fdd44ae550e032c735d235e5f4d744dbf2dc0e8e040ff7bf1aff10d59173608d1b6d6dda6bdcf385ae660b5d3512cd3cd2df7c05fdb9d6dc710f7ee31133f36e8ad22dbf0523d0c502079e0515d605f88d16f3bc80b9229843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d9843dd08987d98aba16ef099a3c05a4f74cd5582117195387bc1b7df6b6e15f0e63da2bcaf10cf797f5e359497f2d951a57d61870b5e661fe62d0769f21dd823da41fc8a0e1c39a2f9f49183b4cfb7d81ae839de33dee7c6ef9ec61eb672fb7dc1af21da779f7b43b4ef7ed07a4ac37550eb3d992370e37e2e6194b28ca51e3579fbda0ce5b6b2c0658e6ba47b3215f6e7b5733b38123cdcff6aeca7579233cf6d7bda7b44170a5dcd15ba6a249a05a4bf850afa73edffc43df805cc01b30fb3c123fb857aa29b5325182bb97fd9d74fcdaf02de6cdbb23d84e76c57544379cd263cb3d3c753b26d8f10bc8cfdf838d9b6cde9f36daf11fc8a8403fc7244f302b26d3f41b62d9eb38de81aa7687dcbe61ba7801fdbb16c3722dc461835ec959a68df6f4a659bca730a751e397244f345ab7ff3bd5a23c9c032cbef6aeb297d25da44394782fb8584518e2935ede76681a7d9a18bfd91b7effd60de0a7308a31e37701db8247d3ced5cff4782e712c2a3f18e28c9591a372cb279a53d6e582c74b540e8aa91681691fe162be8af86f8226fdc835fc01c30fb30f3b80158eb896e4e956044dc42c2a3d06e94eda72ea902de3c6ee0350c3c675bb31aca8be77515ecf7d2b8e148c1cbd8e6ff18837183c4c1e38667d1b8e1808307c2be71836b0ca8b5bee11b03821f8f1be6531cc23c6e50b00fda5d36b46bbe06f5adce23478e681aadfee5b881659663045f9bb8287d99cbbe638b082364e476298c1b2af37e306f85be60d4e306ae030aed7e3bd7ff91e0594c7834de1125394be3862536afb4c70d4b85ae5cfd26689690fe962ae8cf65abe21efc02e680d98799c70dc05a4f7473aa046325c75abe7e6a7115f0f68d1bf09c6dcd6a28af4a8c1b8e12bc8c6d3eeb6055be05d7b841e2e071c39c230669673bc60d6c9f2e14719ae354b93e8a7bf0e37103cf75cb7d33beb1eb58dad55296b1d4e3fe3acf3196728fa4ee22cce3dbf4dbeebe527b80fe1ced81ecfb7344b38ec6ae9c16b8e6386441dd5e5271f906f42d31bae64dd107d4099a25a403d06c261d64b58ed6923e50be4d361e3478bec7c62f12793465587f3a76cec0fbbccce685f77989288f1cd15c27dee7650237de67d7bb0b5a573ad0977bc796887c7244f36ccf1c19682752b81855a6ecaa81772de98ddfa35aa2e1f9037e1f97113dcf3314d393a1346fc475c15ce5e669782c79a9129e25a3c07329e1d1188728c9599a375a6ef34a7bde6885d0d512a1ab46a2594efa5ba1a0bf1ae28bbc710f7ee311b3c123dfdf7aa25b56251811b794f028d4e7b26de1a555c09be733780c8ce73c96ac86f2e2756b8575b2d27cc6d18297b13fde49f3191afd0ddb15b07780c365bbd7d37cc6bb693e03cf795ec03537a5b54fcf3737c57317d2a66aa0308fa314d6a20aae7742ceb5729dacf3c891239ac73c361ecb2ced1f5f9bb83c7d99cbbe63cb092364e47649cb3e9463d9250e5dec8fbc7def07f3d6b21f4763cf721d5068f70b5cff47826705e1d1784794e42cd9b32b6d5e69dbb3ab84ae5cfd26685692fe5629e8cf65abe21efcc62366b66781b59ee8965509c64a8e017cede78a2ae0cdf6ec5e5b899eb30d540de5c5f3060a7665c99e3d46f03236e3536360cf4a1c6ccf7ee5f041da7f1bc69e757d33ae357ef27d330e7e6ccff2f7ef08b33dabd06f155cb61d78831fd7c93a8f1c39a2f913d9b32c0bdbae2b459ce6fb045ec81bf7e0d740d8b9cf08b66b65dab5fd5dee91f05e993aef813510d471b4a1785fc12fc734f5033ed640a4ad85b694db2ec8b04a5596013dfa6c3ff03334ab6db84ed0ac2279417308c9abd907f8cabf12f37781b79ff7ead4790fbc736b6c5e78e750f7c02f473493c53bb746e0c63bc77d156400ad2b9d1c03b9de8d55221f7e374e245c3c2705da89142e469529bbc07b6c792bd867a5791caebbe62a378fc3fdcc65a9e369edaa8f86f633c3e1b98cf0a4df9e0ccc2ba52fe7c0bcd25a9b57daf34a970b5dad12ba6a249ab5a4bfcb15f457437c9137eec12f600e9803e68039600e9803e68039600e9803e68039600e9803663dcc060fe67e80b59ee8d6540946c4ad263c0a733265e7e12eab02debebd1178ceebe9d5505e95d81b3149f03273fe6fa91fe49bfe9c696b17cfe5638d01385ceb5cafa2bd11f75a6c0df49cf746ac14719a7b4a7debf2bca754ae63345098f746a4ff8dca809e977b30f2991128fb3a8f1c39a2798f675d85654618ed1f7fbbc36de258b5035c06fc8e69ad4dc875df550e5d8c156f6e13f13ef19a06bf470a6d50ab6bed0ff74b08a3ec6795d67346b4df466bad64b46b37ab75f1e4792f2af3d2fade7034eb68fcdeae55c2339aef0dd91ed568d794e4ccb3adb327c57c8daed6095d2d11ba6a249acb497feb14f457437c9137eec16f3c62e6b691bf3704dd9a2ac188381e0728d4e7b2edf75ad2cf641b663bf44764f72a7c335c70d9bdc0c1dff483660dd9bd3f75d8bdd56c5fb9ce18d3b271788f5091ee2b615fc9f3d6960ade0d91db7651289b82ab6cd688b2e1f7b52e72db5b39a2f983b0f757095acdfe7734f600f7bf0aed5c17b7a923c1b38ef068cc2528c959b207d6dbbcd2b6077a85ae5cfd0468d693fe7a15f4e7eaeb710f7e0173c01c3007cca3c5cc3639db9ca05b5325182b398ef1d9e4eb747997e6b8a7085ec69e3ee99041be0af64b17cff5c3d6070ef0cb11cda964eb9f6ab1f1b7756cebbbc6765aeb15beb11df835900c6c83ed8fb6be6fed66385b5fa16cba5c65b346940db703d2d6e73a089a82ad77d2d657b6ad476deb739ba1d07e76735b3d123cbd84677dfa785a95e42cd9fa1b6c5e69dbfa1b85ae5cfd0f683690fe362ae8cf6543e01efc02e68039600e98b38099c727c05a4f746baa046325c774bef149af2eefd2f8e458c1cb8c0136d1f844c1e6ea36325f4efc8a8403fc7244f3dbc30669afa0f1891c8bf8c6b9ebd397a1ec3817fc1a4806b61bf7c7f189dc3f79b9e0ed1b9fac4f1f4fb7ab6cc07b3de908755e8e4fb80e82e646cff844b95c0ba3dd97c165bd217d3c9da31d2f719fa3d17e2ac999e7fe27edf1499fd0d51aa1ab46a2d948faeb53d05f0df145deb807bf8039600e98c71633f7396cef806e559560441cdbae0aed7359bb79832eef92dd7c9ce0656cd3fbc96e5e9f3edf4e23b33c1f0738c08fcfc7f927b29b1f24bbd9f5ddc87a11a739de012fe48d7bf03318a52d5d893db7d26eaec47e5fdf18a212bc7dfb8794edf3bc52dd2a701b85f703f8c12f47341f219bded5be61af1bf2e06f2a2a61d72acc7f14b8ef838ee4fc478e683e2574d4e7d111bfbbb25fa88df69d836ab2f1b28fc5ff66641e4d916edfc6dfa414e91efc94f665965ddbe3ff6328ecb1ef72d585f5f69ecf5b03cd5786a90b582f461e0d1e3d6a7dbfc078a26864df2fb09dc7368bc6bb574f7c478291edce4d4a78368e02cf26c2d39f3e9e5625394bfdcd669b57da63f92d42571b85ae1a896633e96f8b82fe6a882ff2c63df88d47cc060fde1b60ad27bad555821171fd8447a13e97ed3336917e8eb7e1f5d6376dfcef688ca265836d247e45c2c13618686ea531ca1f698c82e7fc5de07a11a7d50e951ba3805f43e45e23d2e8dbb8bc619fb8beabd7e20d399177afe0cddf85703faf50360557d980773fe908ef415de4b6e573445312201a9cdbef13b49afdef68ec01ee7f15dab902b7a923c1b385f06c4e1f4fab929c257b60abcd2b6d7be00aa12b573f019aada4bf2b14f4e7eaeb710f7ee31133db03dcdf816e759560aca40de5b307b6e8f22ecd859e207899befcf88641be1a6da791b99ff8150907f8e588e662b2339a2c36e3adb7cfd9ce70d9951aed5b39bb12fc1a48066efff7473b037222ef7ec1db676728944dc15536ab45d9703b20ed0cae83a069f1d819cae55a483a37c2fd64da784663f7709fa3d17e2ac999e7fe276d3be34aa1abd542578d447305e9ef4a05fdd5105fe48d7bf01b8f98f91de5fe01747d55821171dcd72bd4e7b276c6565dde253be344c1cbf4e5f3c8ced0ea07e4b909c0017e7c6ec2a164675c427686ecb7ea293dd7232dfb10bc9037eec1cf6094b687e67a8bcfcee8ab006f9fcd5509debeb9944af0f6d957fbbbce4752d7b4ce2bf1957725cea4f495772578fbcabb12bc7d7b9199b7c679727c9e27ae7276adf6199bbef33c35fac9d18e31b88fd11a636c1e059e30c6187ab9ecf5cd4257e3618c51ed98798c01ac591f63f8daefada49f936c98cf0e7e4905c602ccaf4838782c009a1f1e3a48fb321a0be0398f05b688384dfd8217f2de22f4db10ed3b3ed0b4cff8cc8022dd57c236f48d8bfa4817085fa65b360557d9f489b2e1f59a3a4183b439a2b95bcc39bad60514745b18ad2da4dcce758ed63eb992f068ac2129c959b207b6d9bcd2b607ae12baea13ba6a249a6da4bfab14f4e7eaeb710f7e0173c01c308f2d66ee73d8de01dd655582b19263029f7d7ba52eefd21c7a93e0656cd327c86e56b06b3a5dfbcc8103fc789ff97d6437ff0bd9cdd20ee37112d723ad3d1fbe7112f8198cd296aec43c97b49b2b31c7e61b435482b7cf6657b6cff34a75abc06d14de0f59b77244f303b2e95ded1bc6973c9e836e2a61d76e53d2d1554247c0bf8d74049a9f091d5de5d111bfbbb25fa8a5bcf1acc9c6cb3e16df2dc93c9a22ddbe8df7a214e91efc34f72ef9da009edb5598d7ee74d505d4753e471c34bf1fa62ec8317683478f5ab26c13b25ce19005347f11b2205ecac2f55ad6d55aca9beb29d7773c47bd967934e9e9a460f2dd9e7ebe255def8886ea1af8b793ae41b347e81af191d035f26820fd805649963693efd54a3aba46e808f8af261deda5691caa23c44b1d6d271d4137a0ada5bcf1acc9c65f2378a33eca3c9aacae1574d26ef2dd997ebe255def821e6dbec0bf93740d9a4385ae111f095d5f4dba867e40ab244b87c977b7928eae153a02fedda423d01c23748478a9a39da423e806b4b594379e35d9f86b056fd447994793d5f5b5e9eb6448df8b7e7f8be0afb997c837eee03965ad33a17de725f039e20a676a77b2cea5cdc1e71983e674510fa5ad843338d8e670e9514b16391ebfc2210b68a60a597c67e7b8c6c17d159045b60f4b1cb280a67598f641fe8f86e71daead802cbb852cdb1db280a64bc88278290bf7bdb2fdd294e53a21cbd50e594073ae9005f15216eedb2003686b29ccedb391f17aba2f3e4d1937d9cbe47b43827c370d73997c6f7cfa785b6544447aa8a530788de4b98bf6064ab356d0ed11f707468365c4f500cfe7523d9848b4c528ddba097ed2a6b8ce8169a1a89bac03931675936d0aa9072ed3627ab20ce913d11faf73f0d6feb70678bbfeadb15289776f34b41c70bfb202bc7d7b2b2bc1dbb7b7d2f59fe782f5f96c5cfeafaed679b03e3b8dcf83c5bf9a95ffc352c223ff455d49debe7a5a09debe7a5a09debe7ababf97f770bcf97c3bfe0f7bfafd435fc16587c9fe81edb0ab87b1c3563a64c13b7d5d19f95c798186bf4507a63a41e3ea97af17b64216eb5296796bbd2fb067f1bea0eef1fb029a1788f7e506811bef8bebdd00ad2b1de8cbbd1bd7897cf8ddb89d7099609ba09d4871c5a83265d75605bc8d2e60ffa06ceae939db460a638f12c6368111f7d71146c42d273c05253c33049e190e5d68f19e2578cfaa20efe982f7f40af2ee11bc7b2ac8bb53f0eeac20ef16c1bba582bce70ade732bc87bbee03dbf82bc9b05efe60af25e2c782fae20ef1582f78a0af2f68df92ac1db37e6ab046fdf98af12bc7d7d3ff34edfee18b019c12367f3859dd546f1a0794c8c5b6e481dd3507dd4087db00db85c8977abe08d7bb6675a9578fbec99d60af0f6d93395e0edb3672ac1db67cf5482b7cf9ea9046f9f3d5309de3e7ba612bc7df64c2578fbec994af0f6d93395e0edb3672ac1db67cf5482b7cf9ea9046f9f3d037e0d143ed3fa663e61b988d35c0ff2d93dbc1e94b761ee7fb5fafebcc0237937925e5aab54678d1497273c672ae1f1d92c675680b7cf66a9046f9fcd5209de3e9ba512bc7d364b2578fb6c964af0f6d92c95e0edb3592ac1db67b35482b7cf66a9046f9fcd5209de3e9ba512bc7d364b2578fb6c964af00efd58e8c72ac53bf463a11fab14efd08f555f3fd640e1b308cf594a787ce35ae69dfe98baafa38678607d01722fa778d034d9f3130e532e1fc85f23f4c17523afc4fb998237eec1cfcc27602c7f36c561ed631ac561df453bc5611fcd548ac37ef77328ee261b3e97e26eb6e1f328eed9367c3ec53dc7862fa4b85b6cf8591477ab0d7753dc736df8028a7b9e0dcfa0b8e7dbf0c514f7021bbe88e25e68c3b328ee45365ca4b817dbf0748a7b890dcfa4b8db6cb887e26eb7e12e8a7ba90d7752dccb6cb883e25e6ec32d14f70a1b9e4771afb4e1b914f72a1b5e4071afb6e1f914f71a1b9e4d71afb5e1668a7b9d0d2fa2b8d7dbf0251477870d2fa4b837d8f0628abbd386e750dc1b6d7805c5dd65c3cb28ee6e1bbe94e2de64c3fc7fe737dbf0068a7b8b0d2fa5b8b7da30ff67ef6d36dc4b71f7d8f07a8abbd786fb29eeed36cce7e7df67c3fcef9e77d8309fed72bf0d5f4971efb4613e2bf35d367c15c53d60c3db28ee411bde4171efb6e16b28ee211bde4571efb1e1cb29eebd36bc85e2de67c37cd6d1c3367c2dc53d62c3bb29ee511b5e4b71efb761fee7fb3fd9f03a8aabb561ded376800ddf4071391bbe8ee226d8f08d145767c3d753dc81367c13c54db4e19b29ee201b7e36c51d6cc3cfa1b87a1bbe85e2ec2f00f7b67926ce7e86bfb7cd337176097d6f9b67e26cb7b7b7cd3371f638febd6d9e893bdc865f487147d8f08b28ee481b7e31c51d65c32fa1b8a36df8368a3bc6866fa7b84936fc528a9b6cc32fa3b82936fc728a3bd6865f4171f8f7fb2b29ee781b7e15c5e1bf48afa638fcc3e0351487b34c5f4b714d36fc3a8a3bd9865f4f71a7d8f01d1477aa0dbf81e24eb3e13b29ee741b7e23c59d61c37751dc336cf86e8a43bfff268a83fdf1668a836df4168a9b6ac36fa538d80a6fa338b4fdf7501cec877b290e7dd3db290ef6c87d1487358f77501cf686dc4f71d83bf24e8a838df22e8a433ff900c5a13f7d90e2d0efbe9be260533c4471e8b3df4371e8efdf4b71b083de4771b03d1ea638d8468f501c6ca347290ef6c3fb29ae68c368f34cdbe23b0bec0a1167daa62b6db818a56b7fca7fe6e11efc0cc6ad365cee5f7b4d36bc856890479da0719d05f51e61eb2b9cbdd46d308de6eca5470813a705aeb5421ed7d94b4ab294befddc2e646972c8029a0f0afd6a9c4ba3246ba99eee20994cbe5b1db282e6a374eedec76d98df2b3e33e0cb8ee7b86ac47d91c2d09f91f99af4652e9d53bb937016890ff3de455853e2ddcabc6ba2a167b4f0792e087fe9d0415adf592ec06ede117cfbced865ba2b44ba46a2b9da217f314a577e79fe8f3c7fc694c9a7a89ea11e194c5a67a45dedd15113e9083457928eb4ce5fbf52e0010e3ee31be52ffb00a4e5f339be456d149fb70a39eb23f719e60a6d592b6347def20c28fee7018fd99a848c7c36e9783e375f014f37db12f25f047ca62a687e21fa646987a04f3e8964810cca67e296f64f6ff3c8c2f51d34bfd1b7790a9ab6e3552493c977b34356d0fc81daca3f3afa5cfe1f0506bba3ed93f99ce81de9cb9ce73618e5bbc3c1fb1ac29a12ef21ed3ffa6479a65c8ec2ffa03e59f665d035b09b77046d1a6397e9ae10e91a23b74da2600f0db10191b73c3bcf94c95fa89ea11e69b6a5db23b78e4e221d8186dbd24a9d652affeb626850fe75820669f9df2f13ad0ee5bf7eb94f96fd99e63800bc9037eec1af31daf7dce07a878cfc9f1ddf7f2dabbd4f76a5c37cd826a2411e758286cb1b3493a9bc8dbc0a672b77b1ed3841c8eb9a1b389e30b9ecce25421e9eb3603b58ebcc6bd9e79fe8900534a708fd6af5f95a67c7fbfa7c961534cfa07fc89e497d3aca89ff47dbed788e2bf4f923ebf3bb0e1ba4957d37743dda3e7f8b48578d7dfed954cfbaa9cfd7b27bb77b747422e90834fcff04ad3e5ffecf41fed7d9d0a0fc651fc0632cd0143d7d3eaf0fcafe52b3cf072fe48d7beef311c76bace5feb36d3caceff29aee66113731aaae7f59bbd2615d6c3dd1208f3a41c3fff703cd22d12729d8385dae71befca7258ff397893e5f8ef39708797ce37cadffdb49fbe504872ca0592df4ab31f7a0699bf21cbbc977934356d0aca3b6b897fa7494533fe9ed6ac7735ce5fa7cb6e914e6fef2dcefc8f36b9937f7cd29f11ed2e7a1cf9767caf35ac70eeaf3e5fc3074cde7e6c23663ec32dd66918ee795af72c85f8c74fb57796ebf29933eaa6757539faf6df74a1d9d403a020d8ff3fb95f0c83da5c0017ee5fa00a4e53ee006d1e7a3ffe3fd49d26ed7b4b17ce361f06b8cf66defeb1d329afa713cf5f91b850c26cd7a875c5ae5065ec81bf7e06730f6daf0fa31c073bcc0c375ad4ed0206d8e686e177d9d82cddbc5f56382d0a3cbbe7985b02564dd5a22e461bb4b79fda3e0b28ba05f975df43afdf919555b82d7064cbebd0e5941f3466ae3ef76cc0f6c24bd3d9870fe806d458575dbbc6b9fc6550ededce7a7c47b485f0a5b027cf83f41083f40b6841c7743d7c0eefa47902bdd1691ae3172cf9f28ccdd38fbedab046653266fa17af620d9125ae3f56d1e1d1d4f3a02cd268a43ffc4e370bc3fdc776d54c2dd2b70e37e2361441cdb0db2bf307afe9ddd20cbe7eeae72c8b58ae4da5061b9c0af81b0e3992b1df6b1f239f6c8a34ed0206d8e683e26daf6f4cbb1b58bf7354c88dce5c87b323e25fa4eb92762b990a781e4e1b921853a59da03d72f6439ce210b689e10fa55b0a93a35df3fb6db4dbeeb1db282e64bd4a67d85fa4694d31ad2db8f1ccf7195eb3ba13fa539b23ccf6dca7522e6cdfb5253e23d645e157d27f8b0ad82f00fa9ef947355d035cfebc9751357ba5e91ae9168363be42f46951983b1bdfa75aa673fa2beb34f09d3668f8e8e231d816603e948ebff10eb051ee000bf727d00d2721ff00b310e47ff0739eb8996fb46adf12178216fdc835f63b46f7b5fef90d1d48ffba9cfbf5ce889c7e16b48ae5e25b9c00b79e39efbb1754206573a7ccfb2966890479da041da1cd1fc45f449e9db38037d3e8f9558de0d8409347f137d3ee2816bb99087e72c40ab64af75bbec97631db280668fd0af42bbd4ad699bf6934c26df750e594173c0e1837a9960c3fc5e5d4e7a3bcaf11c57b93e9f6d3a85f151de35b7b3d9c19bfbe694780fe9f3d0e7830fef9541f8c8c30769e51a2c74cdeb5372ddc4956ebd48d748349b1cf21723ddfe55aed199329948f50cf5a81276afd4d1b1a423d0f4928e2af51f24e0e0711fca5ff6013cee03cdf15687e8f3d1ffadb77e7db46f7fa969638117f2c63df835521c7f5f2d6534f56313f5f99709197c7269959b4f2efe0710fa71fe37ac4c87ef5a97100df2a81334489b239ab3a9bc8dbceb539777a0cfe7726079d71326d0e40993ab8e2f17f2b07dc4efdcfad4651998efdf206499e29005349d42bf0a7d739792acad6c6fa1cf5feb901534e7505b7c2ef5e928a7cb486ff31dcf7195ebf3791ca530cec8731b8ff2ed77f0e63d3029f11ed2bfa0cf071f9e6f41781ef5f9b2af84ae792d0f6d66b9fd3b978b743c8655b6b7ca9e8d047ea532a17a369ffa7cadf1599f4747534847a0e17f6929fc03b895db77e40d1c3cee43f9cb3e00697344b3ccd3e7f3b91cb2bfd41c678017f2c63df8f1f7783c17216534f5e324eaf3570b19f87b0196eb3225b97cdf0b809fc188736396a8e219e893d7929e4cbe93059e1cd1f4893e19f1c0853e7932c922ff196e6451f8877d81df09298bcbe6b942f4c90a365e4149d621f345e893973a6405cd0e6a2bafa13e1765c2fbde9feb788eab5c9fcc6d9e429b9077cd196d70f0ee23ac29f11e6203a14f061fb68d10be95fa64d96f40d7c0ce73558c5da65b2bd2717fd3eb90bf185566de95e7be76533d7b2ef5c95a6d69af4747934947a0e1b674a9121ed9b60307f8191ab9ef7db2c0c7fbde6ff3f4c97c8e95eccf34c7abbe7defe0c77b02f8fc182963696da67e20eceb93573ae4d2faaf0278216fdc839fc188b58de1fe05be54c44d24b98b29e35e2a704bbb81ff05be94e210e67f082ff1e4358968a43d29f9b13df956d1bfa6df060cfc1f4cda2fd29e62fbe5edc3d82f2b853cac2bb65fd22f4fb72d36c9a15fd03c20f49bfe38a3b54bb3ee72f9987c57396405cd7ba95f7998ec13d46db65f3ee1788eab9cfdc2e5abd07ee6d96693ff0b67debd843525de43ec45d82fe08378b6173f4ef68bec63659bcf6ba7e5f622c9ff4c73df7cb943fe62a43b5e967385a64cde4ff5ec1364bf68fd2be7728f8e26918e40b39474b45a09cf2a810738c08ffb9c3a4183b439a279c263bf404eee2fb99fd79a33f1f597e0d748717cdea794b1b44793ec17a4e1f363e7883823d7a54a728117f2c63df8f1387c0ec521ccf68bebdf9e2b9470fbfe15b582304206b6c5e4bfc76ba27dcf9107cd31448332adf3f0cb11cd8f44ff9a7e1b3060bf709d2a1296958409343f15f68b7c5fe708795857bc37674eeab2e49db240f7731cb2fc4ae8779502262559878c1960bf2c71c80a9adf52bff23bb24f504e4b496fffeb788eab9cfdc2e5abb0b72ccf361bca778d8337cf3fa7c47b88bd08fb057cd88e44f87fc87e9136beec6f78fcc9d865ba15225d63b4effc9966dfb546e0917301a64cfe93ead9ff92fda2d5efacf6e8e818d21168f8ff88959a7f010e9e7f018e3a41c3e38ebdff7e3862c087fd82be9ccf8e072df7f30a6d59d9fe12fcf8df8a7c6ebd94d1d48fa70e1e08fbec97c50eb9162ac9e5fbff0cf8b1fdb298e2e43fefd9ee62fb652ced2ec8e0b2bbd87eb9d493d7d14423ebaee4c775f758aabb3a736703f60bde05f409d25ecb11cd8984c965b32d16f2b0ae403b91e88aa9c99277ca02dd2f76c8729ad0af828d5c509275485b22ed179675ef7f638f18d4cb541baea77262fb65bae339ae72f60b97af42fb99e7fe0ae5bbcac19bf7c9a7c47b485f09fb057c10cff3fe3d470cd2ca7e1fba06769e8766ec32dda5221d8fc7573ae42f46ba730bab04665326d3a89ea11e69f63b2b3d3a3a9a74049a39a423ad3951d99f0007efeb93f32f470b7c3cff7281c77e61db40f6fd9ae37ddffc0bf8f1fc0bffdb47ca68eac73bc97e992d9ed747fbf6bb46ae4b94e4022fe48d7bf033189b855c6c1bb0fdb248c469f6018b046edcb38d051916119e454a782e11785cbc35fa7ed63bae727dd562c2a3309629b0fd3d123c3c37acd13629c99937f9a2ddd99362be2e7b7ab1d05523d154628ed6d7f681df78c46cf0c83683fb94455582d1b59f4e6b0ec2d7862dad02de4617e857b87f75ed59a886f2ba84f028f4dd7923fbc98297b12daed7b53d0b35825f9170805f8e680e3c7290f6668b8dfb66b61fe49c88a6bdea9bb7e1fd1390c7356fd3461835fa51d79ca06c530d0ddad23a8f1c39a279b1b0a9db1c32238cf6af9ed257a24d042fe42ded7e2e03ee47b4ec3a9f7d5c099b722c79fbde0fe6bd2c7dde79a5ba55e07923b45bb26ef1bcd19d62be4fce5fe33d411e0da41bd0d65218cff64443c7a969d88e9becc56bbda3c977d33017cf6b3d0dbcad3222223dd45298e75b867beea25d4969e4dcd11e717f60b4efba03aff5be437d2e7a608ca2b186ccf344a8f372dd85d77a1f14755ece31a1cef37e06e88ce7e1b4f61ca10dc2fce662076fadf9065f5bbcb80a78b36dccff9aae267baa31da776d8c65abb5e9f02fdda5ba7acdb31d9b8bdcf615dbb19fa0796dd4b703a2a1e5c176d9129137d21c45f14b44de7afb75f2ed8c6982c0bdd281e933029342bd69d76c2b5609fd373b6405cd3fd3dcfd17690d08f56136e9ed7b8ee7b8cacd73f1f85d610f649ed74d50beab1dbc792f4a4abc87ecf1c01a91fcde3247e1efd2bb24bf9584ae81ddf431e8d719bb4cb758a4e36f2c5738e42f46e9caeffbbe84f71d7d99ead9f7689cae35b7ef5ba35e483a02cd22d26db34807fae5448372aa1334489b239a9f88b64461cce26cdf9a856cdcbe3d55a1f64d6b7c26dbb74b1cb282e6d754ef7e43ed97b423ccf3bf3b9ee30aeddbc8dab7bf9569df643b35d2f66da148578deddbefa89efd9ddab7d94a98567874d44c3a020dcfd1625d95e791f1fef09aab963def9beb667b1e71d01db7b948075b92db65c85427689036473413ecdcec61560792d694e32c5aab966d0cdb403c2f54ad73b63cd7ec5a375098cf2abb6ec073479061b86f1d9779f23a996850d7eb3cfc7244730cd5019db9b7be51cfbd1d4b985c736f72fe9775c57b75b5f6da4959a07bd7feb326a15f85f923d5bd765c3e26df850e5941733aadf53cc38679fd80d78d3a1dcf7195b333b87c15c68e79d7b7202b1dbc794f5c4abc87d875b033c087ed3d843b8e1ca495fd3374cddf7ca32f64ec32dd32918ef7c1af70c85f8cd295dff73d3bef7f3c8bea19ea9166bfb3c2a3a3934947f27b18cdbd7fb23f91ebaf6c63d4091ad7dcd679d446f1be08de53efdacfa475a681cf3e023fb68f9610462923af87fbf6dacd177146ae054a728117f2c63df8f15ebbf9148730db2f72ffdd58ef11840cae3d826cbf5ce2c9eb48a2411dabf3f0cb11cd32d1bfa63fde1eb05f788c5c8cdce3edbdeb11c27e9176eb7c218fcb6e9d4874c5d46419982791b240f7f31db25c2ef4ab6067b42bc93a649fa89c07665941b391fa957eb24fe458ac745e90e339ae31dc7f99e73102ca77a98337efdd4b89f790fd1bb05fe49e3fde8fb28bec17b97f10bae6f110dafa72fb0e5dfbc640b3c4217f31aacc580ffc4c996ca17ab69bec17ad7e6789474747928e40b39074d4ac8447f627c0017eaef9e523053e9e5f7eb6b05fd097f35aa8ecfb35c74c729e06f73cbf83b8058451ca68eac73f0e1a08fbec97b90eb9e629c9055ec81bf7e0c7f6cb5c8a4398ed1769d38cb5dd05195c7617db2f0b3c791d4134720e4ef2e339b8d78afe35fd31439f73cfa6b4d7783cf00661bf489b6dae908775c5fb5e15cab3dd25cb110efd82e6cd42bf0a3672bb66dde5f231f9363b6405cdbdd4afdc47f609ea36db2fef773cc755ce7ee1f2d5da9721d7f1163b78f35e8b94780fe92b61bfb8ce8943f851b25f64bf2fdb7cde1fc8d865ba79221def295fe490bf1855668e9dc7d4efa47af67eb25fe62a615ae4d1d111a423d0cc271d69ad3bc9f571e0e0b51ae0a81334bc3e0e9ac73df60be4f4cd53688df77dfd25f83552dc5cc2286534f5e371b25f3ac473b6cb9a492ead736f7c76199f6fd062c3b3290e61b65fa44d33d6761764984b78b4de47dfd9117375755160bde32ad757cd233c0afd73db68fbce05844763be4349ce3cdba5697feb286dcb7942573cd7cb6353adb64fdaf7b807bf80b932980d1ed9ced513dddc2ac18838b63bb4c602be76774115f0e63de8e82fb9bc2ab14e319af2623b4ac1de287d9f798ae065eca13f93bdac609fb6d5087e45c2c17b804073df5183b47fb5d8d89e609b679e88d39cb7e77eb518b9e767200fdb4108f3f7991a7dbfcb4e966daa6b2e48cac1e3f75a5b16f2fb4cd79c14dbdd0b45dc58af054246ee47b46c519f4d5f093b782c79fbde0fe6adb0ff20af54b7da789c2fbf4f71adfb1e49ef8a6b8e00ef09af4743373c8fa2f19e28cd8db5b9e6c6e4de519e9f3a56e868894747fcee4237bc56a6b18f4e690eabcdb5fee9faae6bef1e1ea1a3651e1df13c9efcf6b736da779eb0c9c62f13bcf7d878994753a4bb0715ed03e64be709fe9af317be76725e15f066bb95d7d5aac9d6698cf69dc367d96a6dbac3edfd025dbde6d9c6cc456edb876dcc9ea30631a3bee1db49b90eca7615f2469ac3297ebec81beb3be9bf3f6d79c63441e05ee6c074aec0947ebd69cb6bb6154b85fe5b1cb2ee3da78fc60fb36c98df9f0ed2db52c7735ce5e6cd786cadb0e730cf7b3ce577f2cc7b25614d89f790fda55873021f3edb1fe125f42ec9bdb2d075b9eff35de9e689747c76e4a50ef98b51baf2fbce86e6b30b2ea67a867aa4b95670a94747b34947a0994bba6d11e940cf737128a73a4183b439a2592dda92f4c713eef6ad45c8c6eddbe5156adfb4c64eb27d9be39015341ba9def553fb25ed88d25e2ec7735ca17d1b59fbb6ab4cfb26dba991b66fb345ba6a6cdfb6503ddb4ded5b871226df7ecb16d21168f8db7039ce063db76f789fea040defc505cdcda22d49df5e75b76fc0328f3081e6d60ab56f5a631ed9be75386405cd0ba9debd98da2fb90e6e9edfe1788e2bb46f236bdf5e5fa67d93edd448dbb71691ae1adbb7dba99edd3106f69b6c835cfbc24d5cb30d839ecf57e63d2a5aedb2ef5ceb0ec28838e8ceb53e87b132b7cb90a94ed0f0581e34f7521b58efa035e5780beded9136148ff1784eba5ad78b789dcbb566a9f52d9f6fcd92e7ad5dfb9511e6bdc90b3d799d4234f2bbc072fb80df2ffac1f4e7ca07f6268f66deff83c3ccfbcbb527d615cffb2bf4bdce350ce87e9e43968f09fd2a9c15d3a66967c8f3d3663b6405cda7a9fdff2cd911f25f07e6f9938ee7b8cad919ca6750e4d9b692ff6c769d8b9ab69dc1fb86792e95ed3d84bfe19873853ea06b3e37439ea9e04ab750a46b249ac50ef98b51baf2cbf52bb98662cae409aa674f929da1d5ef2cf6e8e814d21168785ebe527b5c81836d05e0a81334bc3f04343f107b12d097f3d939ae3d595ae75ef9fa4bb6ebe49eac7a878cbc17c7d4ff2e1bdf41695a449c91ab59492e396f887bf033183b6db885e21066fb45ee571eeb3dd5906136e1a9d4de7c176f8d7fc88e76ce4ff97bbd02ff4f702478782d5663de556b2d92dbf6b4f726cbb6467e9751e9fd9fbe310eaf9d8e37ccfc2dac6bfd7d769560748d65b5ec6a5f1b5689fd12c3f1e6fd123c47e2da2f510de5c573360a7d77699fefa98297b12d0e3d5a956f816d068c45240e9ecfb9f3e841da236dd8673fb8fe433b56df12b1fd50ee3fb45afda8ebfb2bd9a6babef773ad2982e644ab7fb9cfd7f5cd3cefa59f2fe234dfb1917c330f19c7e29bcb6ab329b578fbde0fe6ad30b6cd2bd5ad02cf53a2dd2a773e420bbd2bae394ebc27ae795ede7faef19ee8cc89e69d73a2f25b169e47ec143a5ae4d111bfbbf25f42b5d1bedf7f35d9f8458237f6b0ca3c9a22ddb13fcf6114e99ee7deb4c6b9be3640f93ba211f1f6d964d5d48ff377edc0cfb2d5da7487d9fb79ba7acdb3fd24f701f1fc3568161e3d8819f50d7b58a58dcc3603f2469ac3287eaec85b6f1da090674ce5cec707cd128129fd7a53c86bb615726da2d3212b6856906dbcca86f9fde922bd6d713cc7556e7e85c78d955e9b60ded5b036b199de25b9c6005d8f766d42ae6954e3dac46554cfb6d0f8b04509d3628f8e9a4947f21f37ae395dd0f33c13dea7bac86d13e7886687684bba5297d7ddbe014b176102cdae0ab56fe9cbea6edf5a1cb282e606aa773751fb25ed88d23e1dc7735ca17d1b59fb765b99f62de9da6bb348578deddb73a89edd4eed5ba712a6c51e1df11e7dd73f8f5ce710e139aff5698de97ddf50336ec44177dce6221d6c496e9721539da041da1cd1dc416d60bd83d694e36b260e8479ae90e7a45cf311d53a5758cd6719b8ce74926719b8e6e540732ad1c8f57dd79903a079a7e807d39f27e91bf59ccfbb8799f391f38ebe330734d6795db240f7731cb23c22f4abb08fb0a039f7c0e563f26d76c80a9ac7a8fdff10d91128275eaf78c2f11c57393b83cb5761ec98e77e457e2bc0bcf99cc894780fe9d36067800f7fdf8ff0e7c9ce90fdb3dcef6cde11f9cf0457baf9221d9f0d7d8943fe6294aefcbefdcf7ceec1e354cf9e203b43abdfb9c4a3a3534947a0e1792badbd42b23f91eb7eaebddea70a7c3cb7f555b11e85be9cf74fc9be5fa92d2bbb178a6d38f97d62bd43465e8735f57fa68defa2349d224e736fbfb4df700f7e06638f0d77521cc26cbfc875dbb1de9b06195a088fd6bc4687c0e3e2adb1e63edaef9e78ef8142ffdc3eda313aaf55688c6db4e6eab96d4f7b8f976c4b5d6b2b95dcfbe31be3f0da42c01c30bb3073bfc8731ba06ba9128cae3903adf18bafafa8c4baed70bc79dd167dbc6fddb61aca8be7c6146ca4d25ebad3042f63c3d51fa3cab79d6d338cf9240e5ecff8e93183b487dab0cf4e6b167163b94f89ed34b6dd10e6bd741af64ab9b525dedb81b6b4dcda12688eb5fa977be9586684796fc45c11a7f98e25f96fce58dbcffb236fdffbc1bc15e610b4ce7a68e7f960b45bb26ef1bed3a9f4aeb8e692f19ef07c3a74c37b3c35de13a53d7aedaef95ab95f9ce76b0b42470b3d3ae277579e35aab5775269bf61fb68f71bf6081d2df2e8a8dc7e4325594adf3b6bfca392e74a4772f66a51e868894747c39dbdaa35cfca7385c568dff565cdf9245f1fa0fcadc68878fb6cf26ab2e3f89c12e067d96a6dba43edfd1c5dbde6d97e2eb74e049a4b8f19c48cfa86bd94728cc43623f2469a43297eb6c81beb6d0aefcf103b7682c0bdc4816995c0a4506fda35db8ac542ff3d0e59f7fe2f98c646eb6d98df9f99a4b71d8ee7b8cacd63f2bc81c27e97527dbe947016890ff35e4e5853e23de4ec22ac01820fe27314de4eef923c8709ba0676d3c7608f126397e99a45ba46a259ea90bf18a52bbfdcfbb44c603665b291ead90e9a1fd0da6bb4d4a3233e9709342da4db1e914e9ec56668504e758206697344739d684b14c64acef6ad47c8c6eddb4d156adfb4f7c9cabde22c2b686ea57af73c6abfa41d619ebfdaf11c5768df46d6bebdaa4cfb26dba991b66f1d225d35b66f2fa47af66a6adf662a615aead1510fe90834f349b7720e01f4dcbec9bde23d221fb60def146d8982bdea6cdf8085bf9d06cd9b2ad4be698d7964fb36d3212b68eea17af7766abf50663c2fffa8e339aed0be8dac7d7ba44cfb26dba991b66f3d225d35b66ff7533d7b740cec37d90671fbb690742be7ff405fce7e93f3816cbf7d58b4255aeb59b27d93fbdbb97dfb5885da37ad7d553efb8d6505cda7a9de7d96daafbdff1321bd3de9788e2bb46f236bdfbe51a67d93edd448dbb7f9225d35b66f4f503d7b720cec37d906b9fe236fe2b0ef91f77a220def89d46a977ddfc2741246c44177dce6221de602cb9dc972a8c887d7c67e4c6d60bd83d694e32f0e1c08f35a3faf29bbd613ab75adbf9acffb810ce5cefb71adab83e634a2417d28772e0f68fe20fac1f4d73907be8519cd9aed9f8759b395fb065857bc66ab358e92b240f7cd0e59fe2ef4abf0bd95ea388acbc7e4dbe19015347ba8fd8f260d8479df09ef373ad4f11c57393b83cb57612ffe90ffb34b3b9279f3ff4152e23de4bb0bd819f21c1afec6b971d220ad5c8f86aecb9d09ee4a27cfbae5b3f71738e42f4695e91b79adfe80498361d423cd7e67814747a7918e40c3eb8e5af6835ccf050efe6e56fe8fc6b5b70f3493ac0eb19fccf59d896b3fadd6b7a3befe92c7ad723fed70fb281b08e34c4ad323e28c5c5ae717c87511dc839fc138dd867b280e61b65fe4becbb1fe86073274121eadfadf25f0b8782be8a2c07ac755aeaf523e43bb30da3516de6ba2b1aea4b5d782dbf6b4bf85916d8d6b6f4c25f7eefbc638e5fe7b53ed980d1ed966f0d8b1b34a30bac6b25a76b5af0dabc47eb0e178f37e30f43dbefd60d5505e3c67a3d07797bed1385df032b6c5fc49aa7c0b6c33602c2271e488e65b64172fb2619ffdd021e234f71a0cb7ff9ded07b62910e66f3434fad19a68dfef7d659bcaeb0b751e39787d618db0a9db1c3223cc7b2e678b38cd770cbc90b76b0e0a32723f329676ddfec8dbf77e306f85b16d5ea96e0d393f5c9e4bc77b6341b38dde15d71ca73c8781e7a2f9db218df744674e34ef9c13759dc50a9a5d4247f33d3ae277579e135d1bedfbed6e938d976bbc38ef58e6d114e98efd796f7791ee79ee4d6b9ceb6b0394bf011d116f9f4d564dfd389f3307fc2c5bad4dd768ef9b75f59a67fba9dcfc35685e46f3a9a86fd8a32f6d64b6199037cf4f21be45e4adb90ec0982608dcf31d985e253069ecabd56c2be4dac47487aca0793dd9c66fa0b507ae77787ebfe339ae72f32b3c6eacf4da04f3ae86b5897794599b00a6d1ae4d748874d5b8367117d5b3fb697cd8a384698147475da423d0f0fa809cd3053dcf33e17daa8bdc36718e68de23da9262faf23adb37b6c98109348f54a87d5390d5d9bef5386405cd6354ef3e44ed97b4234afb741ccf7185f66d64eddbe7cbb46fb29d1a69fbd625d25563fbf638d5b327a87d9bae8469814747bc570a34fcdf0dd7b97778ce6b7d5a637adff9178c1b71d01db7b948075b92db65c8542768903647344f521b58efa035e5f800edf19267f1b10dc4f311d53a5758cde7d0b8ce1094e7d0b8e6e540733ad1c8f57dd77931a0794af483e9cf93f48d7acee7df8699f391f38ebef36234d6795db240f71d0e597e27f4abb08fb0a039f7c0e563f2ed72c80a9a3f51fbff17b223504ebc5e71c0e47d9fe32a676770f96afce78dfb15f92d14f3e66f2252e23da44f839d013e7c360bc2b593076965ff2cbfe730ef883c13dc956eb648c7fb8de739e42f46e9caeffbcf229f59f357aa67a8479afdce3c8f8e4e271d8186e7adb4f60ac9fe44aefbf1dc569da071cd6dd55b1d623d0a7d39ef9f927dbf525b56762f14db70f2fbeb7a878cbc0e6beaff45369ee7488b224ef39b04e425f3063f8371163d471cc26cbf4c17719af54dda9db8e7bd6990613ae1d1b2fbe5b70e2ede1a6beeac775ce5fa2ae533b40ba31da3f35a85c6d8466bae9edbf6b4f778c9b6d4b5b652c9bd3fc39dc7341e311b3cb2cde0b1e3f42ac1e81acb6ad9d5be36ac12eb89c3f1e6f544f0f5ad27564379f19c8d42df5ddae37586e0551a1b4c56e55b609b016311892347345f983c483bcb867df64397881bcbfd336c3fb04d8130eff1d21ad3fbd63c78cf81fc87886bcd033497089bbacd2133c268fff87f0a95681347f21f0bc8c8fdc858da75fb236fdffbc1bc15c6b679cd3d93183ba2dd92758bf743aea777c535c729bf2de66f6178efa1c67b52c97d70721f33cf236e163a9aebd111bfbbd00d686ba37df7fe374543f772e139f678c93c9a22ddb13fcf6114e99ee7deb4c6b9be3640790ff98878fb6cb26aeac7f9fc04d7d904b5365d83bdefd0d56b9eeda772f3d7a0b995e65351dfb0c74bdac86c33200ffee61cf19d226fbd7580f621e7ce4e10b8e73a30bd40604abfdeb4e735db0ab93631cb212b686e23dbf8a5b4f680fa7011e9ed2ec7735ce5e65778dc58e9b509e65d0d6b136f2cb336015d8f766d42ee57acc6b58957503dbb8bc68745254cf33c3a9a493a020def259825d2c933f00c0dcaa94ed0206d8e68ee116d49fab6b2bb7d9b2564e3f6ed1d156adfb4c605b27d2b3a6405cd8354ef1ea2f64bda11a57d3a8ee7b842fb36b2f6ed2365da37d94e8db47d9b29d25563fbf63eaa678f53fb769112a6791e1dcd221d816636c5c9b31bea290daff5159570cbf655aeeb35521c74c76d2e68614b72bb2ccf576c10f9f0f98a4f501b58efa035e57825edf12ada789e9372cd4754eb5c61357fc7ea3a1b437ec7ea9a9703cd194483fa50ee7b53d07c57f483e9cf93f48d7acee787c3ccf9c87947dff7a65adf564b59a0fb2e872c3f13fa55d84758a8c47e12791e32cb0a9a5f51fbff6bb223f8bdc2f3bf3a9ee32a676770f96afc7f8cfb1579e61cf3e673bd53e23da44f839d21bfafe4bdfbff457686ec9f81a9dc5977ae74f20ca746a299e390bf98b2fcbefdcffccdeb6fa99efd95ec0cad7e678e474767908e5cfffd2c2ae191f3816750d85cdce7d4091a5e1b04cd3fc47a14fa72de3fe55a8fd7da13edeb2fd98693ebf1c3adc3f21eaf8b29cd05224eb3dcc00b795f40616084dd7701c521ccf6cb4c116770cf50c2ed9b7f9e4118c18ff7ab21ccf6cb0c4f5e87108dfc7e4cf2e3efc78e9e32e0ebcd2df439cf5301161e6f83660a61e2b4c07581908775c5e38f0b529765e05b38290b747f81439693847e15be476c579275c8fe44390fccb282e6b429837a39c386eba99c785cd4e1788eab9cfdc2e5abd05fe5b9fd94df3a326ffeef524abcf7398781c72bbca68270fb94415a39ae81ae796c27ed7e57ba19221d9f4bd9e590bf18a52bbfefcc43fefef44caa67a8479afd4e97474787908ee4dcb0c1334b098fec4f8003fc5cf3cb87087c3cbf7c2eb5514616f4e590b33edab7ef576acb5a5df334726f796334744e0c18a58ca67e9c543710e6efc0a6529a0e11a769070f77de107f6b35dc3776aeb5e96e25dcbe79a36ec20819caed31aba13432af7aa291df57487edcbf2e55ef5f07ec17fe6eb34858f89c13d0ac10f68bdce3df21e4615df1f840613f5e974b16e8bec321cb5a75fb30dfa524eb90ef5de4bf625956d06ca07ea58fec139413f741bb1ccf7195b35fb87c15c61b25fbe55cc2598cf6b5b50deff3086b4abc5b9937ec17f0417c8ec23bc97e011df4015db38d7f8e0d337699ae5ba46b249a731cf217a374e597e3b31902b32993cd54cf7691fda2d5ef9ce3d1513de908346ce369ed8d95fd0970b8ce78aa13343c6f0a9a9b85fd82be9ce5947dbfe6dab26fef2ff8b14dcdf32f5246533f6e9e301036f55f9e6569e85b459c912baf241778216fdc839fc1087ba495e2103e9370378b38837baa126ef94d0feea71246c8e03a23e02cebd7501a99d7c14423e70e253f1ec3bd46f4afe9f7837d1daebd1dc0c2fd2068ee10f68b1c93b50a795857bc9720fdf2ec2bb86439d8a15fd0bc49e837fd36b6afa05977e55eca824356d0dc43fdcadbc93e41dd6e23bd3dea788e6ba4fb5414c61b79d7fc5ab78337efb74a89f7107b11f68bfc2f04db8b8f90fd226d7ce8ba9c8def4a9717e95cdff769f65d727cd62d3097ce57a37af628d92fad4a98ba3c3a3a987424cf893478da94f014041ee0e071aedc43001aa4e575df8f08fb057d39e4f4fdef4dcb5ef4f5973c2788b856c2286534f5e328b25f90661aa56911719af68bcf2e63fb05edaaebbc70b65fa44d33311afa0d413145dc3ebbab40182183cbee62fba5e0c9eb20a2413daef3f0cb11cdd744ff9afe3b3760bfb4dbbcd027004b1b6102cd3785fd8278e06a11f2b0ae40ab34c75d70c902ddb73864f9bed06fbb0226cdf97c2e1f93ef5487aca0f931f52b3f25fb04e5c4678ffcc1f11c5739fb85cb576bfd88c76f45e2c3bcbb096b4abcf7f9c68bfb731e3723fc7bb25f64bf2fdb7c9e8766ec325d41a46b249a0e87fcc5a832eb47e067cae429aa677f20fb45abdfe9f0e8e820d211685a49475af32f720c0b1c3cff021c758286c71da0f9abb05fd097f37a0568b99f5768cbcaf697e0d718ed3b7751ef90d1d48f9b6c63c2b6c1d994669a88d36c4bc10b79e39e6d156963355098ed97bc88d3b4dff302b7cb5671d96208b3fdd2eac96b22d1a0ded579f8e588a6e1d8011ffd6bfa36dc80fdc263ef6254dea63a9c30715ae09a26e4615df1790ed3529765689f0e59a0fb690e592609fd6a8cc93465e5f231f9363b6405cdf1c70eeae5441be6b68fdb9566c7735ce5ec17e5f33a9cdf81b9fe0725bf254f81f790fe1bf68beb1c1084cf3e769056ce11c8f94df38ea0fd77fd2303e95a453a9e5b6877c85f8c746d0579aebc299393a99ea11e69f63bed1e1d4d241d8186db632dfb45f627c0c1f60bcabf4ed0206d8e683aa88d32b2a02fe7b90dd9f76bce2f49fb0cf7e0c76b5cbc1624652cd58f9a419d0cb98a2902ae8d06163af1d2d6dafc9be8fe02f1fc42f17c8eb89f27ee1788fb4bc4fd6271bf54dc5f2aee5788fb55e27e8db85f2beed789fb5e71bf51dcf78bfbcde27eabb8bf52dc5f25ee7788fb6bc4fd2e717fadb8bf5edcdf28ee6f16f7cf11f7b78afbe789fb1788fb1789fb9788fbdbc5fdcbc4fd2bc4fdebc4fd1de2fe4e717f97b87f93b87f8bb87f9bb8bf57dcdf27eeef17f7ef12f70f8afb87c4fd7bc5fdc3e2fe5171ff0171ff2171ffb8b8ffb8b8ff94b8ffacb87f42dc7f51dc7f59dc7f4ddc3f29eebf2deebf27ee7f28ee7f2cee7f26ee7f2feeff20eeff2aee4d80ef6bc57d4edcd7d97bbe6aad5fb47e5bbeb3bdbdbfabd0dfdad6da9b2ff46ce8eec8b7776ce8ec6eed6eede8eee82b74b7b5f577b77777f56ce8e9caf7b4b6b7f5b76eeae869db641bcfa628bd76f8c2f4f2ca8f17997f5f2199f34fef6afd438afa3bb426fdbefbd011d4f5fcd3bb861cd2fc7475303dd229f79a94653e270599f385aeaeee9efe1ecdb2e94eb16c668d93b2393f1a1fede5841465be609cc85c93a2cce76550e673a3cabc834f57e6c352e8cbfafbbabbbb3a5bbb0d363300fe366135072d1a1ec69f61ef6b843f331a3cf8dcf8a75a7f82f59bacdf6dfd6759ff22eb5f6cfdd9d69f63fdb9d69f67fdf9d65f60fd85d6bfc4fa8bacbfd8fa4bacbfd4facbac7fa9f5975b7f85f5575a7f95f5575b7f8df52fb3fe5aeb5f6efd75d65f6ffd5eeb6fb0fe46ebf759bfdffa9bacbfd9fa5bacbfd5fa5758ff4aeb6fb3fe55d6df6efd1dd6bfdafad7587fa7f577597fb7f5afb5fe75d6bfdefa3758ff46ebdf64fd9badff6ceb3fc7fab758ff56eb3fd7facfb3fef3adff02ebbfd0fa2fb2fe8badff12ebdf66fddbadff52ebbfccfa2fb7fe2bacff4aebbfcafaafb6fe6bacff5aebbfcefaafb7fe1dd67f83f5efb4fe1bad7f97f5efb6fe9bacff66ebbfc5fa6fb5fedbac7f8ff5efb5fedbad7f9ff5df61fdfbadff4eebbfcbfa0f58ff41ebbfdbfa0f59ff3dd67faff5df67fd87adff88f51fb5fefbadff4fd6ff80f51fb3fe07adff21eb7fd8fa1fb1fee3d6ffa8f53f66fd8f5bff13d6ffa4f53f65fd4f5bff33d6ffacf53f67fdcf5bff09ebffb3f5bf60fd2f5aff5facff25eb7fd9fa5fb1fe57adff35eb7fddfadfb0fe93d6ffa6f5bf65fd6f5bff3bd6ffaef5bf67fdef5bff07d6ffa1f57f64fd7fb5fe8fadff13ebffd4fa3fb3fecfadff94f57f61fd5f5aff57d6ff37ebffdafaff6efddf58ffb7d6ff0febffcefa4dd6ff83f5ffd3fa7fb4fe9facff67ebffc5faffcffaff65fdbf5aff6fd6ffbbf5ffdbfaff63fdffb5feff591fee1fd68f6c3f5163fd5aeb1f60fd9cf52758bfcefa075a7fa2f50fb2fec1d6afb7fe21d66fb07ea3f58f88dde1e858ed95b67dcf63c6a7db671fa130fe3c82c69f69dbf926df6744e9db2e47d654c6aecc3fbdab354d998f1a2732a739d770f43891b93645998f1927321f90a2cc93c689ccb914659e3c4e649e90a2cc53c689cc7529ca7cec3891f9f414653e6e9cc87c6a8a321f9f41994fc8a0cc276650e693c689cc47a638d66aca60399f9c41994fc9a0cca76650e6d33228f3e91994f98c0ccafc8c0ccafccc0cca7c6606653e2b83324fcda0cc676750e6e60cca3c2d8332b76450e67c06656ecda0cc850ccadc964199db332873470665eecca0cc5d1994b93b8332f76450e6e91994f99c0cca3c2383329f9b4199cfcba0cc333328733183329f9f41992fc8a0cc176650e6591994f9591994f9a20cca7c7106659e9d4199e76450e6b91994795e06659e9f4199176450e6851994f9920ccabc2883322fcea0cc4b3228f3d20ccabc2c83325f9a4199976750e6151994796506655e954199576750e6351994f9b20ccabc3683325f9e4199d76550e6f51994b93783326fc8a0cc1b3328735f0665eecfa0cc9b3228f3e60ccabc2583326fcda0cc576450e62b3328f3b60cca7c550665de9e4199776450e6ab3328f335199479670665de954199776750e66b3328f3751994f9fa0cca7c430665be318332df9441996fcea0cccfcea0cccfc9a0ccb76450e65b3328f3733328f3f33228f3f33328f30b3228f30b3328f38b3228f38b3328f34b3228f36d19fc1fdded192ce7978e1399d3fccfe6cb3258ce2fcfa0ccafc8a0ccafcca0ccafcaa0ccafcea0ccafc9a0ccafcda0ccafcba0ccafcfa0cc776450e6376450e63b3328f31b3328f35d1994f9ee0ccafca60ccafce60ccafc960ccafcd60ccafcb60cca7c4f0665be378332bf3d8332df974199df914199efcfa0ccefcca0ccefcaa0cc0f6450e6073328f3bb3328f3431994f93d1994f9bd1994f97d1994f9e10ccafc4806657e348332bf3f8332ff530665fe4006657e2c83327f3083327f2883327f789cc87c788afb7a3f92c1727e3c83327f3483327f2c83327f3c83327f2283327f3283327f2a83327f3a83327f2683327f3683327f6e9cc8dc94a2cc9f1f27321f91e218e3890cd6ed7fcea0cc5fc8a0cc5fcca0ccff924199bf944199bf3c4e643e304599bf324e649e98a2cc5f1d27321f94a2cc5f1b27321f9ca2cc5f1f2732d7a728f337c689cc87a428f393e344e6861465fee63891b9314599bf355ecea14951e66f8f13990f4b51e6ef8c9775c91465feee7899274951e6ef8d13998f4c51e6ef8f13998f4a51e61f8c13998f4e51e61f8e13998f4951e61f8d139927a528f3bf8e139927a728f38fc789cc535294f927e344e6635394f9a7e344e6e35294f967e344e6e35394f9e7e344e6135294f9a97122f38929cafc8b7122f34929cafccb146536e7081f60f33a95e4afb13a30cf72b19b10bbbad899797a336f6de671cdbca699e733f35e661ec8cc8b987902336e36e34833ae32e30c63771b3bd4d865c64e31fdb6e9c74cbb6eda39f3de9bf7c0d40ba3a7a6d89d1cbb5308cf93d63f2a067674ec8e89dda4d84d8edd94d81d1bbbe362777cec4e88dd89b13b29764db13b3976a7c4eed4d89d16bbd3637746ec9e11bb67c6eeccd89d15bba9b13b3b76cdb19b16bb96d8190599c3960bb16b8b5d7bec3a62d719bbaed875c7ae2776d363774eec66c4eedcd89d17bb99a69c62777eec2e88dd85b19b15bb67c5eea2d85d1cbbd9b19b13bbb9b19b17bbf9b15b10bb85b1bb24768b62b738764b62b73476cb627769ec96c76e45ec56c66e55ec56c76e4dec2e8bdddad85d1ebb75b15b1fbbded86d889df93fbcf95fbaf97fb8f99fb6f9bfb4f9dfb2f9ffb0f91faff93fadf95fabf97fa9f99fa7f9bfa5f9dfa3f9ffa1f91fa0f93f9ef95f9cf97f9af99f98f9bf96f9df94f9ff92f91f91f93f8ff95f8df97f8bf99f89f9bf87f9df85f9ff83f91f82f93f80392fdf9c1f6fce5337e78b9bf3b6cdf9d3e63c66733eb139afd79c5f6bce7335e79b9af33ecdf997e63c48733ea2392fd09c9f67ce9333e7ab99f3c6ccf95be63c2a733e9339afc89cdf63ceb331e7bb98f34eccf91fe63c0c733e84392fc19c1f60bea737df979befadcdf7c7e67b5cf37daaf95ed37cbf68bee733dfb799efbdccf74fe67b20f37d8cf95ec47c3f61be2730fbebcd7e73b3ffdaec4736fb73cd7e55b37fd3ec6734fbfbcc7e37b3ffcbec8732fb83cc7e19b37fc4eca730fb0bcc7abb597f36ebb1667dd2acd799f52bb39e63d637cc7cbf99ff36f3c1667ed4cc179af933339f64e657cc7c83197f9bf1a8199f99f18ab1df8d3d6bec3b63ef98fedff487a67f30eda5693f7e42efd6e1d63fcefa1b7bb76d6bdabda3a977d7aefe9dbbd75dd57bc3ba0d5b77afdbb5f5a6fec8bec211bd9e73b66fddbdb577dbd69b7a776fddb1bd694befae2d4d7d3bfa77356ddfb1bbe9aadedd1bb744f6c52e35583255ffcea6debebe9dfdbb76356d1d48b37b4b7fd3c61ddb77efecddb8bba9afffea6d3b6eecdf69d25c67d31e6ffddeddbbfbafba7a77096b5f5fd3f55b776f69da715dffce4ddb765c6f9e3f6f14f4b14ea2d32c9d799193a49b6fd34db1f7e7efdcd97b63d3d6ed7dfd3734edb87677d38e4d4d1b765cbbbd6f1727ba2d21b3bb9230bb2f49a253262443787bc274774d4800f29e2489de9524d1076ca211bc2e9cec2349787d3649a2af2449f4dd24897e9324d17f26ac16cd75c9d215eb1280bc3821b32f1c942c5dc3c1c9d25d7e7002e1362564766d1266372764f68184e9fe98305dae3e8170f5f5c9984d4ac2ec8484cc1e4a98eef709d39516c213a4bb2161ba0f1f9240999f4c92e8cb4912fd23a158d31a92a5bb2b61baef372410eea74912fd2e49a23f2749f47f4912e51a13246a4c92e8e824894e4b92e8ac2489f24912752649342349a23936d1280db1054978ed4892e83a9b68b46fe2f39330bb2d21b30f88744bfa7bfbe23159dfd6bed2606c677cdb6414dbd7bbbb97d39d7468b2740f1d9a0ce7c3872650ca6349127d3321c2a79230fbf724890e3c2c19c24909d31d77580290272749343321c24b12a65b9a04e4aa2489ae4f88f0b684e95e9e04e46b9324fa6842849f4cc2ecf349123d9510e19f13a6fb6b1290ff9724d171872743383561ba96c31380ec4892686942841b9330db9a24d14b12227c4bc274f72601f9ae24893e9f10e10f13a6fb491290bf4c92e8802392219c9230dd09472400796a9244e72744f8a284e9de9004e47d36d1284df60792f0fab84d74f2be825d75edb6dd5bafde76a35fba4f27e1f8e384aafc791266b92393313b3a61ba2947260079529244e72644b83461ba154940ae4d92e8e684085f9330dd1d4940be2949a28f2444587354b274471c9500e49424899a9224eab68912b53d3392705c9550916b9330bb2921b35b9230bb2721b34713a67b2c09c88f2649f4fd84084f383a59ba69472700d99124d1029b2851fd5f9c84e3f6843ad99984d9eb1332bb3f61ba0793807c3849a2af244438e59864e9ce3a2601c8d62489ba93249a9924d1329b2851e55f9984e3b509b57f6312666f48c8ecee24cc3e9490d9479330fbd784cc7e9f30dd9f9280fc5b9244c74c4a867075c274574e4a00726792442fb58912bd6caf4cc2f1a1843a793809b36f2464f6f384e97e9504e47f244974f0e4640817264cb76e7202909b9224bac5264a54259f9f84e3db12eae4be24cc3e9f90d97712a6fb4112903f4d92684f4284474d49966ef29404204f4c92684642844b12a65b9e04e4654912dd9410e1ab13a67b7d1290772749f4e18408bf9a30dd9349407e2f49a2ff4a88f0906393a53becd804208f4992a87d1408a3ff0f6b53bb893d910500>, + "isInternal": false, + "metadataHash": Fr<0x1eef7f2eaafa09e24b0475a918bd7388c4dec305145849f5f84d1b822202b944>, + "selector": Selector<0x038cda46>, + "vkHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, + }, + "privateFunctionTreeLeafIndex": 0, + "privateFunctionTreeSiblingPath": [ + Fr<0x0583aed5d42e43d977274df1e4536a029a27c2f443bd96eef4162f7f5d7d0b76>, + Fr<0x159e0bdd29fcc7efb79fdb56480e4cac622d6d71755adb98c7c614af8d76a645>, + Fr<0x19e9d55f327e3c96dd42e60a81a783b0bb968d074ee638d542d17f8a3a0b6990>, + Fr<0x06e62084ee7b602fe9abc15632dda3269f56fb0c6e12519a2eb2ec897091919d>, + Fr<0x03c9e2e67178ac638746f068907e6677b4cc7a9592ef234ab6ab518f17efffa0>, + ], + "unconstrainedFunctionsArtifactTreeRoot": Fr<0x0c3d79f3431c5bef8abf01dcf84cb2c64d136171adca23e40d20ed1c64951817>, +} +`; diff --git a/yarn-project/circuits.js/src/contract/contract_class_registered_event.test.ts b/yarn-project/circuits.js/src/contract/events/contract_class_registered_event.test.ts similarity index 90% rename from yarn-project/circuits.js/src/contract/contract_class_registered_event.test.ts rename to yarn-project/circuits.js/src/contract/events/contract_class_registered_event.test.ts index 295f65c40c0e..dc2819e4852c 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_registered_event.test.ts +++ b/yarn-project/circuits.js/src/contract/events/contract_class_registered_event.test.ts @@ -1,5 +1,5 @@ -import { getSampleContractClassRegisteredEventPayload } from '../tests/fixtures.js'; -import { computePublicBytecodeCommitment } from './contract_class_id.js'; +import { getSampleContractClassRegisteredEventPayload } from '../../tests/fixtures.js'; +import { computePublicBytecodeCommitment } from '../contract_class_id.js'; import { ContractClassRegisteredEvent } from './contract_class_registered_event.js'; describe('ContractClassRegisteredEvent', () => { diff --git a/yarn-project/circuits.js/src/contract/contract_class_registered_event.ts b/yarn-project/circuits.js/src/contract/events/contract_class_registered_event.ts similarity index 95% rename from yarn-project/circuits.js/src/contract/contract_class_registered_event.ts rename to yarn-project/circuits.js/src/contract/events/contract_class_registered_event.ts index 1c028930a4f0..6fd5de09b906 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_registered_event.ts +++ b/yarn-project/circuits.js/src/contract/events/contract_class_registered_event.ts @@ -7,9 +7,9 @@ import { ContractClassPublic } from '@aztec/types/contracts'; import chunk from 'lodash.chunk'; -import { REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE } from '../constants.gen.js'; -import { computeContractClassId, computePublicBytecodeCommitment } from './contract_class_id.js'; -import { unpackBytecode } from './public_bytecode.js'; +import { REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE } from '../../constants.gen.js'; +import { computeContractClassId, computePublicBytecodeCommitment } from '../contract_class_id.js'; +import { unpackBytecode } from '../public_bytecode.js'; /** Event emitted from the ContractClassRegisterer. */ export class ContractClassRegisteredEvent { @@ -80,6 +80,7 @@ export class ContractClassRegisteredEvent { privateFunctionsRoot: this.privateFunctionsRoot, publicFunctions: unpackBytecode(this.packedPublicBytecode), version: this.version, + privateFunctions: [], }; } } diff --git a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.test.ts b/yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.test.ts similarity index 96% rename from yarn-project/circuits.js/src/contract/contract_instance_deployed_event.test.ts rename to yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.test.ts index c7081791b4c6..031fbf9a8f3e 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.test.ts +++ b/yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.test.ts @@ -1,4 +1,4 @@ -import { getSampleContractInstanceDeployedEventPayload } from '../tests/fixtures.js'; +import { getSampleContractInstanceDeployedEventPayload } from '../../tests/fixtures.js'; import { ContractInstanceDeployedEvent } from './contract_instance_deployed_event.js'; describe('ContractInstanceDeployedEvent', () => { diff --git a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts b/yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.ts similarity index 98% rename from yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts rename to yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.ts index 67404dba4eb1..b307070424e4 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts +++ b/yarn-project/circuits.js/src/contract/events/contract_instance_deployed_event.ts @@ -5,7 +5,7 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader } from '@aztec/foundation/serialize'; import { ContractInstanceWithAddress } from '@aztec/types/contracts'; -import { DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE } from '../constants.gen.js'; +import { DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE } from '../../constants.gen.js'; /** Event emitted from the ContractInstanceDeployer. */ export class ContractInstanceDeployedEvent { diff --git a/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.test.ts b/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.test.ts new file mode 100644 index 000000000000..4b8c246db464 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.test.ts @@ -0,0 +1,13 @@ +import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; + +import { getSamplePrivateFunctionBroadcastedEventPayload } from '../../tests/fixtures.js'; +import { PrivateFunctionBroadcastedEvent } from './private_function_broadcasted_event.js'; + +describe('PrivateFunctionBroadcastedEvent', () => { + beforeAll(() => setupCustomSnapshotSerializers(expect)); + it('parses an event as emitted by the ContractClassRegisterer', () => { + const data = getSamplePrivateFunctionBroadcastedEventPayload(); + const event = PrivateFunctionBroadcastedEvent.fromLogData(data); + expect(event).toMatchSnapshot(); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.ts b/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.ts new file mode 100644 index 000000000000..c133ef30298e --- /dev/null +++ b/yarn-project/circuits.js/src/contract/events/private_function_broadcasted_event.ts @@ -0,0 +1,133 @@ +import { FunctionSelector, bufferFromFields } from '@aztec/foundation/abi'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, Tuple } from '@aztec/foundation/serialize'; +import { ExecutablePrivateFunctionWithMembershipProof, PrivateFunction } from '@aztec/types/contracts'; + +import chunk from 'lodash.chunk'; + +import { + ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, + FUNCTION_TREE_HEIGHT, + MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, + REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE, + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS, + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE, +} from '../../constants.gen.js'; + +/** Event emitted from the ContractClassRegisterer. */ +export class PrivateFunctionBroadcastedEvent { + constructor( + public readonly contractClassId: Fr, + public readonly artifactMetadataHash: Fr, + public readonly unconstrainedFunctionsArtifactTreeRoot: Fr, + public readonly privateFunctionTreeSiblingPath: Tuple, + public readonly privateFunctionTreeLeafIndex: number, + public readonly artifactFunctionTreeSiblingPath: Tuple, + public readonly artifactFunctionTreeLeafIndex: number, + public readonly privateFunction: BroadcastedPrivateFunction, + ) {} + + static isPrivateFunctionBroadcastedEvent(log: Buffer) { + return toBigIntBE(log.subarray(0, 32)) == REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE; + } + + static fromLogs(logs: { contractAddress: AztecAddress; data: Buffer }[], registererContractAddress: AztecAddress) { + return logs + .filter(log => PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log.data)) + .filter(log => log.contractAddress.equals(registererContractAddress)) + .map(log => this.fromLogData(log.data)); + } + + static fromLogData(log: Buffer) { + if (!this.isPrivateFunctionBroadcastedEvent(log)) { + throw new Error( + `Log data for PrivateFunctionBroadcastedEvent is not prefixed with magic value 0x${REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE}`, + ); + } + + const expectedLength = + 32 * + (MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS + + REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS); + if (log.length !== expectedLength) { + throw new Error( + `Unexpected PrivateFunctionBroadcastedEvent log length: got ${log.length} but expected ${expectedLength}`, + ); + } + + const reader = new BufferReader(log.subarray(32)); + const event = PrivateFunctionBroadcastedEvent.fromBuffer(reader); + if (!reader.isEmpty()) { + throw new Error( + `Unexpected data after parsing PrivateFunctionBroadcastedEvent: ${reader.readToEnd().toString('hex')}`, + ); + } + + return event; + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + const contractClassId = reader.readObject(Fr); + const artifactMetadataHash = reader.readObject(Fr); + const unconstrainedFunctionsArtifactTreeRoot = reader.readObject(Fr); + const privateFunctionTreeSiblingPath = reader.readArray(FUNCTION_TREE_HEIGHT, Fr); + const privateFunctionTreeLeafIndex = reader.readObject(Fr).toNumber(); + const artifactFunctionTreeSiblingPath = reader.readArray(ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, Fr); + const artifactFunctionTreeLeafIndex = reader.readObject(Fr).toNumber(); + const privateFunction = BroadcastedPrivateFunction.fromBuffer(reader); + + return new PrivateFunctionBroadcastedEvent( + contractClassId, + artifactMetadataHash, + unconstrainedFunctionsArtifactTreeRoot, + privateFunctionTreeSiblingPath, + privateFunctionTreeLeafIndex, + artifactFunctionTreeSiblingPath, + artifactFunctionTreeLeafIndex, + privateFunction, + ); + } + + toExecutableFunctionWithMembershipProof(): ExecutablePrivateFunctionWithMembershipProof { + return { + ...this.privateFunction, + bytecode: this.privateFunction.bytecode.toString('base64'), + functionMetadataHash: this.privateFunction.metadataHash, + artifactMetadataHash: this.artifactMetadataHash, + unconstrainedFunctionsArtifactTreeRoot: this.unconstrainedFunctionsArtifactTreeRoot, + privateFunctionTreeSiblingPath: this.privateFunctionTreeSiblingPath, + privateFunctionTreeLeafIndex: this.privateFunctionTreeLeafIndex, + artifactTreeSiblingPath: this.artifactFunctionTreeSiblingPath.filter(fr => !fr.isZero()), + artifactTreeLeafIndex: this.artifactFunctionTreeLeafIndex, + }; + } +} + +export class BroadcastedPrivateFunction implements PrivateFunction { + // TODO: Deleteme + public isInternal = false; + + constructor( + /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ + public readonly selector: FunctionSelector, + /** Artifact metadata hash */ + public readonly metadataHash: Fr, + /** Hash of the verification key associated to this private function. */ + public readonly vkHash: Fr, + /** ACIR and Brillig bytecode */ + public readonly bytecode: Buffer, + ) {} + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + const selector = FunctionSelector.fromField(reader.readObject(Fr)); + const metadataHash = reader.readObject(Fr); + const vkHash = reader.readObject(Fr); + const encodedBytecode = reader.readBytes(MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS * 32); + const bytecode = bufferFromFields(chunk(encodedBytecode, Fr.SIZE_IN_BYTES).map(Buffer.from).map(Fr.fromBuffer)); + return new BroadcastedPrivateFunction(selector, metadataHash, vkHash, bytecode); + } +} diff --git a/yarn-project/circuits.js/src/contract/function_membership_proof.test.ts b/yarn-project/circuits.js/src/contract/function_membership_proof.test.ts new file mode 100644 index 000000000000..64ca3bb4ee54 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/function_membership_proof.test.ts @@ -0,0 +1,50 @@ +import { ContractArtifact, FunctionArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; +import { ContractClass } from '@aztec/types/contracts'; + +import { getSampleContractArtifact } from '../tests/fixtures.js'; +import { computeVerificationKeyHash, getContractClassFromArtifact } from './contract_class.js'; +import { ContractClassIdPreimage } from './contract_class_id.js'; +import { + createPrivateFunctionMembershipProof, + isValidPrivateFunctionMembershipProof, +} from './function_membership_proof.js'; + +describe('FunctionMembershipProof', () => { + describe('private', () => { + let artifact: ContractArtifact; + let contractClass: ContractClass & ContractClassIdPreimage; + let privateFunction: FunctionArtifact; + let vkHash: Fr; + let selector: FunctionSelector; + + beforeAll(() => { + artifact = getSampleContractArtifact(); + contractClass = getContractClassFromArtifact(artifact); + privateFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.SECRET)!; + vkHash = computeVerificationKeyHash(privateFunction.verificationKey!); + selector = FunctionSelector.fromNameAndParameters(privateFunction); + }); + + it('computes and verifies a proof', () => { + const proof = createPrivateFunctionMembershipProof(selector, artifact); + const fn = { ...privateFunction, ...proof, selector, vkHash }; + expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).toBeTruthy(); + }); + + test.each([ + 'artifactTreeSiblingPath', + 'artifactMetadataHash', + 'functionMetadataHash', + 'unconstrainedFunctionsArtifactTreeRoot', + 'privateFunctionTreeSiblingPath', + ] as const)('fails proof if %s is mangled', field => { + const proof = createPrivateFunctionMembershipProof(selector, artifact); + const original = proof[field]; + const mangled = Array.isArray(original) ? [Fr.random(), ...original.slice(1)] : Fr.random(); + const wrong = { ...proof, [field]: mangled }; + const fn = { ...privateFunction, ...wrong, selector, vkHash }; + expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).toBeFalsy(); + }); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/function_membership_proof.ts b/yarn-project/circuits.js/src/contract/function_membership_proof.ts new file mode 100644 index 000000000000..bd11a3a98672 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/function_membership_proof.ts @@ -0,0 +1,158 @@ +import { ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { + ContractClassPublic, + ExecutablePrivateFunctionWithMembershipProof, + PrivateFunctionMembershipProof, +} from '@aztec/types/contracts'; + +import { computeRootFromSiblingPath } from '../merkle/index.js'; +import { + computeArtifactFunctionTree, + computeArtifactHash, + computeArtifactHashPreimage, + computeFunctionArtifactHash, + computeFunctionMetadataHash, + getArtifactMerkleTreeHasher, +} from './artifact_hash.js'; +import { getContractClassPrivateFunctionFromArtifact } from './contract_class.js'; +import { computePrivateFunctionLeaf, computePrivateFunctionsTree } from './private_function.js'; + +/** + * Creates a membership proof for a private function in a contract class, to be verified via `isValidPrivateFunctionMembershipProof`. + * @param selector - Selector of the function to create the proof for. + * @param artifact - Artifact of the contract class where the function is defined. + */ +export function createPrivateFunctionMembershipProof( + selector: FunctionSelector, + artifact: ContractArtifact, +): PrivateFunctionMembershipProof { + const log = createDebugLogger('aztec:circuits:function_membership_proof'); + + // Locate private function definition and artifact + const privateFunctions = artifact.functions + .filter(fn => fn.functionType === FunctionType.SECRET) + .map(getContractClassPrivateFunctionFromArtifact); + const privateFunction = privateFunctions.find(fn => fn.selector.equals(selector)); + const privateFunctionArtifact = artifact.functions.find(fn => selector.equals(fn)); + if (!privateFunction || !privateFunctionArtifact) { + throw new Error(`Private function with selector ${selector.toString()} not found`); + } + + // Compute preimage for the artifact hash + const { unconstrainedFunctionRoot: unconstrainedFunctionsArtifactTreeRoot, metadataHash: artifactMetadataHash } = + computeArtifactHashPreimage(artifact); + + // We need two sibling paths because private function information is split across two trees: + // The "private function tree" captures the selectors and verification keys, and is used in the kernel circuit for verifying the proof generated by the app circuit. + const functionLeaf = computePrivateFunctionLeaf(privateFunction); + const functionsTree = computePrivateFunctionsTree(privateFunctions); + const functionsTreeLeafIndex = functionsTree.getIndex(functionLeaf); + const functionsTreeSiblingPath = functionsTree.getSiblingPath(functionsTreeLeafIndex).map(Fr.fromBuffer); + + // And the "artifact tree" captures function bytecode and metadata, and is used by the pxe to check that its executing the code it's supposed to be executing, but it never goes into circuits. + const functionMetadataHash = computeFunctionMetadataHash(privateFunctionArtifact); + const functionArtifactHash = computeFunctionArtifactHash({ ...privateFunctionArtifact, functionMetadataHash }); + const artifactTree = computeArtifactFunctionTree(artifact, FunctionType.SECRET)!; + const artifactTreeLeafIndex = artifactTree.getIndex(functionArtifactHash.toBuffer()); + const artifactTreeSiblingPath = artifactTree.getSiblingPath(artifactTreeLeafIndex).map(Fr.fromBuffer); + + log.trace(`Computed proof for private function with selector ${selector.toString()}`, { + functionArtifactHash, + functionMetadataHash, + functionLeaf: '0x' + functionLeaf.toString('hex'), + artifactMetadataHash, + privateFunctionsTreeRoot: '0x' + functionsTree.root.toString('hex'), + unconstrainedFunctionsArtifactTreeRoot, + artifactFunctionTreeSiblingPath: artifactTreeSiblingPath.map(fr => fr.toString()).join(','), + privateFunctionTreeSiblingPath: functionsTreeSiblingPath.map(fr => fr.toString()).join(','), + }); + + return { + artifactTreeSiblingPath, + artifactTreeLeafIndex, + artifactMetadataHash, + functionMetadataHash, + unconstrainedFunctionsArtifactTreeRoot, + privateFunctionTreeSiblingPath: functionsTreeSiblingPath, + privateFunctionTreeLeafIndex: functionsTreeLeafIndex, + }; +} + +/** + * Verifies that a private function with a membership proof as emitted by the ClassRegisterer contract is valid, + * as defined in the yellow paper at contract-deployment/classes: + * + * ``` + * // Load contract class from local db + * contract_class = db.get_contract_class(contract_class_id) + * + * // Compute function leaf and assert it belongs to the private functions tree + * function_leaf = pedersen([selector as Field, vk_hash], GENERATOR__FUNCTION_LEAF) + * computed_private_function_tree_root = compute_root(function_leaf, private_function_tree_sibling_path) + * assert computed_private_function_tree_root == contract_class.private_function_root + * + * // Compute artifact leaf and assert it belongs to the artifact + * artifact_function_leaf = sha256(selector, metadata_hash, sha256(bytecode)) + * computed_artifact_private_function_tree_root = compute_root(artifact_function_leaf, artifact_function_tree_sibling_path) + * computed_artifact_hash = sha256(computed_artifact_private_function_tree_root, unconstrained_functions_artifact_tree_root, artifact_metadata_hash) + * assert computed_artifact_hash == contract_class.artifact_hash + * ``` + * @param fn - Function to check membership proof for. + * @param contractClass - In which contract class the function is expected to be. + */ +export function isValidPrivateFunctionMembershipProof( + fn: ExecutablePrivateFunctionWithMembershipProof, + contractClass: Pick, +) { + const log = createDebugLogger('aztec:circuits:function_membership_proof'); + + // Check private function tree membership + const functionLeaf = computePrivateFunctionLeaf(fn); + const computedPrivateFunctionTreeRoot = Fr.fromBuffer( + computeRootFromSiblingPath( + functionLeaf, + fn.privateFunctionTreeSiblingPath.map(fr => fr.toBuffer()), + fn.privateFunctionTreeLeafIndex, + ), + ); + if (!contractClass.privateFunctionsRoot.equals(computedPrivateFunctionTreeRoot)) { + log.trace(`Private function tree root mismatch`, { + expectedPrivateFunctionTreeRoot: contractClass.privateFunctionsRoot, + computedPrivateFunctionTreeRoot: computedPrivateFunctionTreeRoot, + computedFunctionLeaf: '0x' + functionLeaf.toString('hex'), + }); + return false; + } + + // Check artifact hash + const functionArtifactHash = computeFunctionArtifactHash(fn); + const computedArtifactPrivateFunctionTreeRoot = Fr.fromBuffer( + computeRootFromSiblingPath( + functionArtifactHash.toBuffer(), + fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), + fn.artifactTreeLeafIndex, + getArtifactMerkleTreeHasher(), + ), + ); + const computedArtifactHash = computeArtifactHash({ + privateFunctionRoot: computedArtifactPrivateFunctionTreeRoot, + unconstrainedFunctionRoot: fn.unconstrainedFunctionsArtifactTreeRoot, + metadataHash: fn.artifactMetadataHash, + }); + if (!contractClass.artifactHash.equals(computedArtifactHash)) { + log.trace(`Artifact hash mismatch`, { + expected: contractClass.artifactHash, + computedArtifactHash, + computedFunctionArtifactHash: functionArtifactHash, + computedArtifactPrivateFunctionTreeRoot, + unconstrainedFunctionRoot: fn.unconstrainedFunctionsArtifactTreeRoot, + metadataHash: fn.artifactMetadataHash, + artifactFunctionTreeSiblingPath: fn.artifactTreeSiblingPath.map(fr => fr.toString()).join(','), + }); + return false; + } + + return true; +} diff --git a/yarn-project/circuits.js/src/contract/index.ts b/yarn-project/circuits.js/src/contract/index.ts index 2fddca8e7673..fc922ca5f2e1 100644 --- a/yarn-project/circuits.js/src/contract/index.ts +++ b/yarn-project/circuits.js/src/contract/index.ts @@ -2,8 +2,10 @@ export * from './artifact_hash.js'; export * from './contract_address.js'; export * from './contract_class.js'; export * from './contract_class_id.js'; -export * from './contract_class_registered_event.js'; +export * from './events/contract_class_registered_event.js'; export * from './contract_instance.js'; -export * from './contract_instance_deployed_event.js'; +export * from './events/contract_instance_deployed_event.js'; +export * from './events/private_function_broadcasted_event.js'; export * from './private_function.js'; export * from './public_bytecode.js'; +export * from './function_membership_proof.js'; diff --git a/yarn-project/circuits.js/src/contract/private_function.ts b/yarn-project/circuits.js/src/contract/private_function.ts index 0e7bde1cdc9b..ffc5a90b5f9c 100644 --- a/yarn-project/circuits.js/src/contract/private_function.ts +++ b/yarn-project/circuits.js/src/contract/private_function.ts @@ -3,8 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { PrivateFunction } from '@aztec/types/contracts'; import { FUNCTION_TREE_HEIGHT, GeneratorIndex } from '../constants.gen.js'; -import { MerkleTree } from '../merkle/merkle_tree.js'; -import { MerkleTreeCalculator } from '../merkle/merkle_tree_calculator.js'; +import { MerkleTree, MerkleTreeCalculator } from '../merkle/index.js'; // Memoize the merkle tree calculators to avoid re-computing the zero-hash for each level in each call let privateFunctionTreeCalculator: MerkleTreeCalculator | undefined; diff --git a/yarn-project/circuits.js/src/merkle/index.ts b/yarn-project/circuits.js/src/merkle/index.ts index 5ab1f3d973c4..87b32526900c 100644 --- a/yarn-project/circuits.js/src/merkle/index.ts +++ b/yarn-project/circuits.js/src/merkle/index.ts @@ -1,2 +1,3 @@ export * from './merkle_tree_calculator.js'; export * from './merkle_tree.js'; +export * from './sibling_path.js'; diff --git a/yarn-project/circuits.js/src/merkle/merkle_tree.ts b/yarn-project/circuits.js/src/merkle/merkle_tree.ts index ec8f1182b5e0..53d516960d5e 100644 --- a/yarn-project/circuits.js/src/merkle/merkle_tree.ts +++ b/yarn-project/circuits.js/src/merkle/merkle_tree.ts @@ -45,4 +45,28 @@ export class MerkleTree { public getIndex(element: Buffer) { return this.leaves.findIndex(leaf => leaf.equals(element)); } + + /** Returns a nice string representation of the tree, useful for debugging purposes. */ + public drawTree() { + const levels: string[][] = []; + const tree = this.nodes; + const maxRowSize = Math.ceil(tree.length / 2); + let paddingSize = 1; + let rowSize = maxRowSize; + let rowOffset = 0; + while (rowSize > 0) { + levels.push( + tree + .slice(rowOffset, rowOffset + rowSize) + .map(n => n.toString('hex').slice(0, 8) + ' '.repeat((paddingSize - 1) * 9)), + ); + rowOffset += rowSize; + paddingSize <<= 1; + rowSize >>= 1; + } + return levels + .reverse() + .map(row => row.join(' ')) + .join('\n'); + } } diff --git a/yarn-project/circuits.js/src/merkle/sibling_path.test.ts b/yarn-project/circuits.js/src/merkle/sibling_path.test.ts new file mode 100644 index 000000000000..6b59138a4769 --- /dev/null +++ b/yarn-project/circuits.js/src/merkle/sibling_path.test.ts @@ -0,0 +1,21 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { MerkleTree } from './merkle_tree.js'; +import { MerkleTreeCalculator } from './merkle_tree_calculator.js'; +import { computeRootFromSiblingPath } from './sibling_path.js'; + +describe('sibling path', () => { + let tree: MerkleTree; + + beforeAll(() => { + const calculator = new MerkleTreeCalculator(4); + const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); + tree = calculator.computeTree(leaves); + }); + + test.each([0, 1, 2, 3, 4, 5, 6, 7])('recovers the root from a leaf at index %s and its sibling path', index => { + const leaf = tree.leaves[index]; + const siblingPath = tree.getSiblingPath(index); + expect(computeRootFromSiblingPath(leaf, siblingPath, index)).toEqual(tree.root); + }); +}); diff --git a/yarn-project/circuits.js/src/merkle/sibling_path.ts b/yarn-project/circuits.js/src/merkle/sibling_path.ts new file mode 100644 index 000000000000..52383dae87c7 --- /dev/null +++ b/yarn-project/circuits.js/src/merkle/sibling_path.ts @@ -0,0 +1,16 @@ +import { pedersenHash } from '@aztec/foundation/crypto'; + +/** Computes the expected root of a merkle tree given a leaf and its sibling path. */ +export function computeRootFromSiblingPath( + leaf: Buffer, + siblingPath: Buffer[], + index: number, + hasher = (left: Buffer, right: Buffer) => pedersenHash([left, right]).toBuffer(), +) { + let result = leaf; + for (const sibling of siblingPath) { + result = index & 1 ? hasher(sibling, result) : hasher(result, sibling); + index >>= 1; + } + return result; +} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 056fc3d51336..e61332d2f32d 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -3,7 +3,12 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { EthAddress } from '@aztec/foundation/eth-address'; import { numToUInt32BE } from '@aztec/foundation/serialize'; -import { ContractClassPublic, PrivateFunction, PublicFunction } from '@aztec/types/contracts'; +import { + ContractClassPublic, + ExecutablePrivateFunctionWithMembershipProof, + PrivateFunction, + PublicFunction, +} from '@aztec/types/contracts'; import { SchnorrSignature } from '../barretenberg/index.js'; import { @@ -1264,6 +1269,24 @@ export function makeBaseRollupInputs(seed = 0): BaseRollupInputs { }); } +export function makeExecutablePrivateFunctionWithMembershipProof( + seed = 0, +): ExecutablePrivateFunctionWithMembershipProof { + return { + selector: makeSelector(seed), + bytecode: makeBytes(100, seed + 1).toString('base64'), + artifactTreeSiblingPath: makeTuple(3, fr, seed + 2), + artifactTreeLeafIndex: seed + 2, + privateFunctionTreeSiblingPath: makeTuple(3, fr, seed + 3), + privateFunctionTreeLeafIndex: seed + 3, + artifactMetadataHash: fr(seed + 4), + functionMetadataHash: fr(seed + 5), + unconstrainedFunctionsArtifactTreeRoot: fr(seed + 6), + vkHash: fr(seed + 7), + isInternal: false, + }; +} + export function makeContractClassPublic(seed = 0): ContractClassPublic { const artifactHash = fr(seed + 1); const publicFunctions = makeTuple(3, makeContractClassPublicFunction, seed + 2); @@ -1277,6 +1300,7 @@ export function makeContractClassPublic(seed = 0): ContractClassPublic { packedBytecode, privateFunctionsRoot, publicFunctions, + privateFunctions: [], version: 1, }; } diff --git a/yarn-project/circuits.js/src/tests/fixtures.ts b/yarn-project/circuits.js/src/tests/fixtures.ts index ceb6611d3949..07bcf604e28b 100644 --- a/yarn-project/circuits.js/src/tests/fixtures.ts +++ b/yarn-project/circuits.js/src/tests/fixtures.ts @@ -25,6 +25,12 @@ export function getSampleContractInstanceDeployedEventPayload(): Buffer { return Buffer.from(readFileSync(path).toString(), 'hex'); } +// Generated from end-to-end/src/e2e_deploy_contract.test.ts with AZTEC_GENERATE_TEST_DATA +export function getSamplePrivateFunctionBroadcastedEventPayload(): Buffer { + const path = getPathToFixture('PrivateFunctionBroadcastedEventData.hex'); + return Buffer.from(readFileSync(path).toString(), 'hex'); +} + function getPathToFixture(name: string) { return resolve(dirname(fileURLToPath(import.meta.url)), `../../fixtures/${name}`); } diff --git a/yarn-project/cli/src/cmds/get_contract_data.ts b/yarn-project/cli/src/cmds/get_contract_data.ts index a102e0ba2a92..d11180f7e4b2 100644 --- a/yarn-project/cli/src/cmds/get_contract_data.ts +++ b/yarn-project/cli/src/cmds/get_contract_data.ts @@ -26,7 +26,7 @@ export async function getContractData( }); if (contractClass) { - log(`Bytecode: 0x${contractClass.packedBytecode.toString('hex')}`); + log(`Bytecode: ${contractClass.packedBytecode.toString('base64')}`); } log('\n'); } diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index 04b930f65aca..8cdeb673cc54 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -27,6 +27,7 @@ import { import { ContractClassIdPreimage, Point } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { writeTestData } from '@aztec/foundation/testing'; import { CounterContract, StatefulTestContract } from '@aztec/noir-contracts.js'; import { TestContract, TestContractArtifact } from '@aztec/noir-contracts.js/Test'; import { TokenContract, TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; @@ -293,18 +294,25 @@ describe('e2e_deploy_contract', () => { it('broadcasts a private function', async () => { const selector = contractClass.privateFunctions[0].selector; - await broadcastPrivateFunction(wallet, artifact, selector).send().wait(); - // TODO(#4428): Test that these functions are captured by the node and made available when - // requesting the corresponding contract class. + const tx = await broadcastPrivateFunction(wallet, artifact, selector).send().wait(); + const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash }); + const logData = logs.logs[0].log.data; + writeTestData('yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex', logData); + + const fetchedClass = await aztecNode.getContractClass(contractClass.id); + const fetchedPrivateFunction = fetchedClass!.privateFunctions[0]!; + expect(fetchedPrivateFunction).toBeDefined(); + expect(fetchedPrivateFunction.selector).toEqual(selector); }, 60_000); // TODO(@spalladino): Reenable this test it.skip('broadcasts an unconstrained function', async () => { const functionArtifact = artifact.functions.find(fn => fn.functionType === FunctionType.UNCONSTRAINED)!; const selector = FunctionSelector.fromNameAndParameters(functionArtifact); - await broadcastUnconstrainedFunction(wallet, artifact, selector).send().wait(); - // TODO(#4428): Test that these functions are captured by the node and made available when - // requesting the corresponding contract class. + const tx = await broadcastUnconstrainedFunction(wallet, artifact, selector).send().wait(); + const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash }); + const logData = logs.logs[0].log.data; + writeTestData('yarn-project/circuits.js/fixtures/UnconstrainedFunctionBroadcastedEvent.hex', logData); }, 60_000); const testDeployingAnInstance = (how: string, deployFn: (toDeploy: ContractInstanceWithAddress) => Promise) => diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index 9680f0c530e1..a28018638f42 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -355,3 +355,8 @@ function extendedEuclidean(a: bigint, modulus: bigint): [bigint, bigint, bigint] */ export type GrumpkinScalar = Fq; export const GrumpkinScalar = Fq; + +/** Wraps a function that returns a buffer so that all results are reduced into a field of the given type. */ +export function reduceFn(fn: (input: TInput) => Buffer, field: DerivedField) { + return (input: TInput) => fromBufferReduce(fn(input), field); +} diff --git a/yarn-project/foundation/src/log/log_fn.ts b/yarn-project/foundation/src/log/log_fn.ts index cfc045d8bb2e..5dd11204e28d 100644 --- a/yarn-project/foundation/src/log/log_fn.ts +++ b/yarn-project/foundation/src/log/log_fn.ts @@ -1,5 +1,5 @@ /** Structured log data to include with the message. */ -export type LogData = Record; +export type LogData = Record; /** A callable logger instance. */ export type LogFn = (msg: string, data?: LogData) => void; diff --git a/yarn-project/foundation/src/log/logger.ts b/yarn-project/foundation/src/log/logger.ts index 258abbe7e5c4..9c9998a98c48 100644 --- a/yarn-project/foundation/src/log/logger.ts +++ b/yarn-project/foundation/src/log/logger.ts @@ -5,7 +5,7 @@ import { isatty } from 'tty'; import { LogData, LogFn } from './log_fn.js'; // Matches a subset of Winston log levels -const LogLevels = ['silent', 'error', 'warn', 'info', 'verbose', 'debug'] as const; +const LogLevels = ['silent', 'error', 'warn', 'info', 'verbose', 'debug', 'trace'] as const; const DefaultLogLevel = process.env.NODE_ENV === 'test' ? ('silent' as const) : ('info' as const); /** @@ -50,6 +50,7 @@ export function createDebugLogger(name: string): DebugLogger { info: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'info', msg, data), verbose: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'verbose', msg, data), debug: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, data), + trace: (msg: string, data?: LogData) => logWithDebug(debugLogger, 'trace', msg, data), }; return Object.assign((msg: string, data?: LogData) => logWithDebug(debugLogger, 'debug', msg, data), logger); } @@ -111,8 +112,7 @@ function getPrefix(debugLogger: debug.Debugger, level: LogLevel) { * @param msg - What to log. */ function printLog(msg: string) { - // eslint-disable-next-line no-console - console.error(msg); + process.stderr.write(msg + '\n'); } /** @@ -132,6 +132,6 @@ function fmtErr(msg: string, err?: Error | unknown): string { */ function fmtLogData(data?: LogData): string { return Object.entries(data ?? {}) - .map(([key, value]) => `${key}=${value}`) + .map(([key, value]) => `${key}=${typeof value === 'object' && 'toString' in value ? value.toString() : value}`) .join(' '); } diff --git a/yarn-project/foundation/src/serialize/buffer_reader.ts b/yarn-project/foundation/src/serialize/buffer_reader.ts index 8905f7350b74..0e637d9e69d4 100644 --- a/yarn-project/foundation/src/serialize/buffer_reader.ts +++ b/yarn-project/foundation/src/serialize/buffer_reader.ts @@ -43,6 +43,11 @@ export class BufferReader { return new BufferReader(buf); } + /** Returns true if the underlying buffer has been consumed completely. */ + public isEmpty(): boolean { + return this.index === this.buffer.length; + } + /** * Reads a 32-bit unsigned integer from the buffer at the current index position. * Updates the index position by 4 bytes after reading the number. diff --git a/yarn-project/foundation/src/testing/test_data.ts b/yarn-project/foundation/src/testing/test_data.ts index 241ae5eead90..a92794e64f6a 100644 --- a/yarn-project/foundation/src/testing/test_data.ts +++ b/yarn-project/foundation/src/testing/test_data.ts @@ -41,6 +41,18 @@ export function getTestData(itemName: string): { toBuffer(): Buffer }[] { return testData[fullItemName]; } +/** Writes the contents specified to the target file if test data generation is enabled. */ +export function writeTestData(targetFileFromRepoRoot: string, contents: string | Buffer) { + if (!isGenerateTestDataEnabled()) { + return; + } + const targetFile = getPathToFile(targetFileFromRepoRoot); + const toWrite = typeof contents === 'string' ? contents : contents.toString('hex'); + writeFileSync(targetFile, toWrite); + const logger = createConsoleLogger('aztec:testing:test_data'); + logger(`Wrote test data to ${targetFile}`); +} + /** * Looks for a variable assignment in the target file and updates the value, only if test data generation is enabled. * Note that a magic inline comment would be a cleaner approach, like `/* TEST-DATA-START *\/` and `/* TEST-DATA-END *\/`, @@ -52,12 +64,7 @@ export function updateInlineTestData(targetFileFromRepoRoot: string, itemName: s return; } const logger = createConsoleLogger('aztec:testing:test_data'); - const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../'); - if (!existsSync(join(repoRoot, 'CODEOWNERS'))) { - throw new Error(`Path to repo root is incorrect (got ${repoRoot})`); - } - - const targetFile = join(repoRoot, targetFileFromRepoRoot); + const targetFile = getPathToFile(targetFileFromRepoRoot); const contents = readFileSync(targetFile, 'utf8').toString(); const regex = new RegExp(`let ${itemName} = .*;`, 'g'); if (!regex.exec(contents)) { @@ -68,3 +75,12 @@ export function updateInlineTestData(targetFileFromRepoRoot: string, itemName: s writeFileSync(targetFile, updatedContents); logger(`Updated test data in ${targetFile} for ${itemName} to ${value}`); } + +function getPathToFile(targetFileFromRepoRoot: string) { + const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../'); + if (!existsSync(join(repoRoot, 'CODEOWNERS'))) { + throw new Error(`Path to repo root is incorrect (got ${repoRoot})`); + } + + return join(repoRoot, targetFileFromRepoRoot); +} diff --git a/yarn-project/protocol-contracts/src/class-registerer/index.ts b/yarn-project/protocol-contracts/src/class-registerer/index.ts index bf0884f77d49..9f749bbfc860 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/index.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/index.ts @@ -13,5 +13,5 @@ export function getCanonicalClassRegisterer(): ProtocolContract { * @remarks This should not change often, hence we hardcode it to save from having to recompute it every time. */ export const ClassRegistererAddress = AztecAddress.fromString( - '0x2140db629d95644ef26140fa5ae87749ae28d373176af9a2e458052ced96c7b3', + '0x0a633ab81fc21d4e8efe3199e15358bd93bf110842c40f203c434a23b7b79699', ); diff --git a/yarn-project/types/src/contracts/contract_class.ts b/yarn-project/types/src/contracts/contract_class.ts index c575485ad337..0dca8f9d11cf 100644 --- a/yarn-project/types/src/contracts/contract_class.ts +++ b/yarn-project/types/src/contracts/contract_class.ts @@ -1,6 +1,5 @@ import { FunctionSelector } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; -import { PartialBy } from '@aztec/foundation/types'; const VERSION = 1 as const; @@ -61,6 +60,28 @@ interface ContractClassCommitments { /** A contract class with its precomputed id. */ export type ContractClassWithId = ContractClass & Pick; -/** A contract class with public bytecode information only. */ -export type ContractClassPublic = PartialBy & - Pick; +/** A contract class with public bytecode information and optional private. */ +export type ContractClassPublic = { + privateFunctions: ExecutablePrivateFunctionWithMembershipProof[]; +} & Pick & + Omit; + +/** Private function definition with executable bytecode. */ +export interface ExecutablePrivateFunction extends PrivateFunction { + /** ACIR and Brillig bytecode in hex */ + bytecode: string; +} + +/** Sibling paths and sibling commitments for proving membership of a private function within a contract class. */ +export type PrivateFunctionMembershipProof = { + artifactMetadataHash: Fr; + functionMetadataHash: Fr; + unconstrainedFunctionsArtifactTreeRoot: Fr; + privateFunctionTreeSiblingPath: Fr[]; + privateFunctionTreeLeafIndex: number; + artifactTreeSiblingPath: Fr[]; + artifactTreeLeafIndex: number; +}; + +/** A private function with a memebership proof. */ +export type ExecutablePrivateFunctionWithMembershipProof = ExecutablePrivateFunction & PrivateFunctionMembershipProof; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index a7437ba5451d..135e4bd48125 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -96,6 +96,7 @@ __metadata: "@jest/globals": ^29.5.0 "@types/debug": ^4.1.7 "@types/jest": ^29.5.0 + "@types/lodash.groupby": ^4.6.9 "@types/lodash.omit": ^4.5.7 "@types/node": ^18.15.11 "@types/ws": ^8.5.4 @@ -104,6 +105,7 @@ __metadata: jest: ^29.5.0 jest-mock-extended: ^3.0.4 lmdb: ^2.9.2 + lodash.groupby: ^4.6.0 lodash.omit: ^4.5.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -3425,6 +3427,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.groupby@npm:^4.6.9": + version: 4.6.9 + resolution: "@types/lodash.groupby@npm:4.6.9" + dependencies: + "@types/lodash": "*" + checksum: b8310a9f89badc42a504887ca0b9619c2a284b3fec8dc505cf72508eb6beba47b822df939c7d57c0f69bc685f51ff5a232e0480ecad6b18b7ab76fecc1d74691 + languageName: node + linkType: hard + "@types/lodash.isequal@npm:^4.5.6": version: 4.5.8 resolution: "@types/lodash.isequal@npm:4.5.8" @@ -9595,6 +9606,13 @@ __metadata: languageName: node linkType: hard +"lodash.groupby@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.groupby@npm:4.6.0" + checksum: e2d4d13d12790a1cacab3f5f120b7c072a792224e83b2f403218866d18efde76024b2579996dfebb230a61ce06469332e16639103669a35a605287e19ced6b9b + languageName: node + linkType: hard + "lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0"