diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index f2dee5b90557..07a62fc99cb3 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -1286,7 +1286,7 @@ void prove_honk_output_all(const std::string& bytecodePath, using VerificationKey = Flavor::VerificationKey; bool honk_recursion = false; - if constexpr (IsAnyOf) { + if constexpr (IsAnyOf) { honk_recursion = true; } @@ -1478,12 +1478,12 @@ int main(int argc, char* argv[]) } else if (command == "prove_keccak_ultra_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); prove_honk(bytecode_path, witness_path, output_path); - } else if (command == "prove_keccak_ultra_honk_output_all") { + } else if (command == "prove_ultra_keccak_honk_output_all") { std::string output_path = get_option(args, "-o", "./proofs/proof"); prove_honk_output_all(bytecode_path, witness_path, output_path); } else if (command == "verify_ultra_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; - } else if (command == "verify_keccak_ultra_honk") { + } else if (command == "verify_ultra_keccak_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "write_vk_ultra_honk") { std::string output_path = get_option(args, "-o", "./target/vk"); @@ -1508,6 +1508,9 @@ int main(int argc, char* argv[]) } else if (command == "vk_as_fields_mega_honk") { std::string output_path = get_option(args, "-o", vk_path + "_fields.json"); vk_as_fields_honk(vk_path, output_path); + } else if (command == "vk_as_fields_ultra_keccak_honk") { + std::string output_path = get_option(args, "-o", vk_path + "_fields.json"); + vk_as_fields_honk(vk_path, output_path); } else { std::cerr << "Unknown command: " << command << "\n"; return 1; diff --git a/yarn-project/Earthfile b/yarn-project/Earthfile index 0098093957e1..07be9672103d 100644 --- a/yarn-project/Earthfile +++ b/yarn-project/Earthfile @@ -116,7 +116,7 @@ protocol-verification-keys: rollup-verifier-contract: FROM +bb-cli COPY --dir +protocol-verification-keys/usr/src/bb /usr/src - RUN --entrypoint write-contract -c RootRollupArtifact -n UltraHonkVerifier.sol + RUN --entrypoint write-contract -c BlockRootRollupArtifact -n UltraHonkVerifier.sol SAVE ARTIFACT /usr/src/bb /usr/src/bb txe: diff --git a/yarn-project/bb-prover/package.json b/yarn-project/bb-prover/package.json index 37030d36b122..3fc9ae17d27d 100644 --- a/yarn-project/bb-prover/package.json +++ b/yarn-project/bb-prover/package.json @@ -73,6 +73,7 @@ "tslib": "^2.4.0" }, "devDependencies": { + "@aztec/ethereum": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/memdown": "^3.0.0", @@ -81,7 +82,8 @@ "jest": "^29.5.0", "jest-mock-extended": "^3.0.3", "ts-node": "^10.9.1", - "typescript": "^5.0.4" + "typescript": "^5.0.4", + "viem": "^2.7.15" }, "files": [ "dest", diff --git a/yarn-project/bb-prover/src/bb/cli.ts b/yarn-project/bb-prover/src/bb/cli.ts index ca26dabb535e..676882d88194 100644 --- a/yarn-project/bb-prover/src/bb/cli.ts +++ b/yarn-project/bb-prover/src/bb/cli.ts @@ -25,38 +25,6 @@ export function getProgram(log: LogFn): Command { log(Object.keys(ProtocolCircuitArtifacts).reduce((prev: string, x: string) => prev.concat(`\n${x}`))); }); - program - .command('write-pk') - .description('Generates the proving key for the specified circuit') - .requiredOption( - '-w, --working-directory ', - 'A directory to use for storing input/output files', - BB_WORKING_DIRECTORY, - ) - .requiredOption('-b, --bb-path ', 'The path to the BB binary', BB_BINARY_PATH) - .requiredOption('-c, --circuit ', 'The name of a protocol circuit') - .action(async options => { - const compiledCircuit = ProtocolCircuitArtifacts[options.circuit as ProtocolArtifact]; - if (!compiledCircuit) { - log(`Failed to find circuit ${options.circuit}`); - return; - } - try { - await fs.access(options.workingDirectory, fs.constants.W_OK); - } catch (error) { - log(`Working directory does not exist`); - return; - } - await generateKeyForNoirCircuit( - options.bbPath, - options.workingDirectory, - options.circuit, - compiledCircuit, - 'pk', - log, - ); - }); - program .command('write-vk') .description('Generates the verification key for the specified circuit') @@ -67,6 +35,7 @@ export function getProgram(log: LogFn): Command { ) .requiredOption('-b, --bb-path ', 'The path to the BB binary', BB_BINARY_PATH) .requiredOption('-c, --circuit ', 'The name of a protocol circuit') + .requiredOption('-f, --flavor ', 'The name of the verification key flavor', 'ultra_honk') .action(async options => { const compiledCircuit = ProtocolCircuitArtifacts[options.circuit as ProtocolArtifact]; if (!compiledCircuit) { @@ -84,7 +53,8 @@ export function getProgram(log: LogFn): Command { options.workingDirectory, options.circuit, compiledCircuit, - 'vk', + options.flavor, + // (options.circuit as ServerProtocolArtifact) === 'RootRollupArtifact' ? 'ultra_keccak_honk' : 'ultra_honk', log, ); }); diff --git a/yarn-project/bb-prover/src/bb/execute.ts b/yarn-project/bb-prover/src/bb/execute.ts index a26a31ba5df4..5d941a5811ec 100644 --- a/yarn-project/bb-prover/src/bb/execute.ts +++ b/yarn-project/bb-prover/src/bb/execute.ts @@ -8,6 +8,8 @@ import * as proc from 'child_process'; import * as fs from 'fs/promises'; import { basename, dirname, join } from 'path'; +import { type UltraHonkFlavor } from '../honk.js'; + export const VK_FILENAME = 'vk'; export const VK_FIELDS_FILENAME = 'vk_fields.json'; export const PROOF_FILENAME = 'proof'; @@ -113,7 +115,7 @@ export async function generateKeyForNoirCircuit( workingDirectory: string, circuitName: string, compiledCircuit: NoirCompiledCircuit, - key: 'vk' | 'pk', + flavor: UltraHonkFlavor, log: LogFn, force = false, ): Promise { @@ -123,7 +125,7 @@ export async function generateKeyForNoirCircuit( // The bytecode hash file is also written here as /workingDirectory/pk/BaseParityArtifact/bytecode-hash // The bytecode is written to e.g. /workingDirectory/pk/BaseParityArtifact/bytecode // The bytecode is removed after the key is generated, leaving just the hash file - const circuitOutputDirectory = `${workingDirectory}/${key}/${circuitName}`; + const circuitOutputDirectory = `${workingDirectory}/vk/${circuitName}`; const outputPath = `${circuitOutputDirectory}`; const bytecodeHash = sha256(bytecode); @@ -148,11 +150,11 @@ export async function generateKeyForNoirCircuit( // args are the output path and the input bytecode path const args = ['-o', `${outputPath}/${VK_FILENAME}`, '-b', bytecodePath]; const timer = new Timer(); - let result = await executeBB(pathToBB, `write_${key}_ultra_honk`, args, log); + let result = await executeBB(pathToBB, `write_vk_${flavor}`, args, log); // If we succeeded and the type of key if verification, have bb write the 'fields' version too - if (result.status == BB_RESULT.SUCCESS && key === 'vk') { + if (result.status == BB_RESULT.SUCCESS) { const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v']; - result = await executeBB(pathToBB, `vk_as_fields_ultra_honk`, asFieldsArgs, log); + result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, asFieldsArgs, log); } const duration = timer.ms(); @@ -160,8 +162,8 @@ export async function generateKeyForNoirCircuit( return { status: BB_RESULT.SUCCESS, durationMs: duration, - pkPath: key === 'pk' ? outputPath : undefined, - vkPath: key === 'vk' ? outputPath : undefined, + pkPath: undefined, + vkPath: outputPath, proofPath: undefined, }; } @@ -179,8 +181,8 @@ export async function generateKeyForNoirCircuit( return { status: BB_RESULT.ALREADY_PRESENT, durationMs: 0, - pkPath: key === 'pk' ? outputPath : undefined, - vkPath: key === 'vk' ? outputPath : undefined, + pkPath: undefined, + vkPath: outputPath, }; } @@ -261,6 +263,7 @@ export async function computeVerificationKey( workingDirectory: string, circuitName: string, bytecode: Buffer, + flavor: UltraHonkFlavor, log: LogFn, ): Promise { // Check that the working directory exists @@ -293,7 +296,7 @@ export async function computeVerificationKey( }; let result = await executeBB( pathToBB, - 'write_vk_ultra_honk', + `write_vk_${flavor}`, ['-o', outputPath, '-b', bytecodePath, '-v'], logFunction, ); @@ -302,7 +305,7 @@ export async function computeVerificationKey( } result = await executeBB( pathToBB, - 'vk_as_fields_ultra_honk', + `vk_as_fields_${flavor}`, ['-o', outputPath + '_fields.json', '-k', outputPath, '-v'], logFunction, ); @@ -343,6 +346,7 @@ export async function generateProof( circuitName: string, bytecode: Buffer, inputWitnessFile: string, + flavor: UltraHonkFlavor, log: LogFn, ): Promise { // Check that the working directory exists @@ -355,7 +359,7 @@ export async function generateProof( // The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`; - // The proof is written to e.g. /workingDirectory/proof + // The proof is written to e.g. /workingDirectory/ultra_honk/proof const outputPath = `${workingDirectory}`; const binaryPresent = await fs @@ -374,7 +378,7 @@ export async function generateProof( const logFunction = (message: string) => { log(`${circuitName} BB out - ${message}`); }; - const result = await executeBB(pathToBB, 'prove_ultra_honk_output_all', args, logFunction); + const result = await executeBB(pathToBB, `prove_${flavor}_output_all`, args, logFunction); const duration = timer.ms(); if (result.status == BB_RESULT.SUCCESS) { @@ -599,9 +603,10 @@ export async function verifyProof( pathToBB: string, proofFullPath: string, verificationKeyPath: string, + ultraHonkFlavor: UltraHonkFlavor, log: LogFn, ): Promise { - return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'verify_ultra_honk', log); + return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify_${ultraHonkFlavor}`, log); } /** @@ -674,7 +679,7 @@ async function verifyProofInternal( pathToBB: string, proofFullPath: string, verificationKeyPath: string, - command: 'verify_ultra_honk' | 'avm_verify', + command: 'verify_ultra_honk' | 'verify_ultra_keccak_honk' | 'avm_verify', log: LogFn, ): Promise { const binaryPresent = await fs @@ -851,7 +856,7 @@ export async function generateContractForCircuit( workingDirectory, circuitName, compiledCircuit, - 'vk', + 'ultra_keccak_honk', log, force, ); diff --git a/yarn-project/bb-prover/src/honk.ts b/yarn-project/bb-prover/src/honk.ts new file mode 100644 index 000000000000..02000438bce4 --- /dev/null +++ b/yarn-project/bb-prover/src/honk.ts @@ -0,0 +1,32 @@ +import { RECURSIVE_PROOF_LENGTH, VERIFICATION_KEY_LENGTH_IN_FIELDS } from '@aztec/circuits.js'; +import { type ProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; + +export type UltraHonkFlavor = 'ultra_honk' | 'ultra_keccak_honk'; + +const UltraKeccakHonkCircuits = ['BlockRootRollupArtifact'] as const; +type UltraKeccakHonkCircuits = (typeof UltraKeccakHonkCircuits)[number]; +type UltraHonkCircuits = Exclude; + +export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkCircuits): 'ultra_keccak_honk'; +export function getUltraHonkFlavorForCircuit(artifact: UltraHonkCircuits): 'ultra_honk'; +export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor; +export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor { + return isUltraKeccakHonkCircuit(artifact) ? 'ultra_keccak_honk' : 'ultra_honk'; +} + +function isUltraKeccakHonkCircuit(artifact: ProtocolArtifact): artifact is UltraKeccakHonkCircuits { + return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkCircuits); +} + +// TODO (alexg) remove these once UltraKeccakHonk proofs are the same size as regular UltraHonk proofs +// see https://github.com/AztecProtocol/aztec-packages/pull/8243 +export function getExpectedVerificationKeyLength(artifact: ProtocolArtifact): number { + return getUltraHonkFlavorForCircuit(artifact) === 'ultra_keccak_honk' ? 120 : VERIFICATION_KEY_LENGTH_IN_FIELDS; +} + +export function getExpectedProofLength(artifact: UltraKeccakHonkCircuits): 393; +export function getExpectedProofLength(artifact: UltraHonkCircuits): typeof RECURSIVE_PROOF_LENGTH; +export function getExpectedProofLength(artifact: ProtocolArtifact): number; +export function getExpectedProofLength(artifact: ProtocolArtifact): number { + return isUltraKeccakHonkCircuit(artifact) ? 393 : RECURSIVE_PROOF_LENGTH; +} diff --git a/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts b/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts index 885eb37007a7..52b26ba4535d 100644 --- a/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts @@ -58,6 +58,7 @@ import { verifyProof, } from '../bb/execute.js'; import { type BBConfig } from '../config.js'; +import { type UltraHonkFlavor, getUltraHonkFlavorForCircuit } from '../honk.js'; import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; @@ -213,7 +214,12 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { this.log.debug(`${circuitType} BB out - ${message}`); }; - const result = await this.verifyProofFromKey(verificationKey.keyAsBytes, proof, logFunction); + const result = await this.verifyProofFromKey( + getUltraHonkFlavorForCircuit(circuitType), + verificationKey.keyAsBytes, + proof, + logFunction, + ); if (result.status === BB_RESULT.FAILURE) { const errorMessage = `Failed to verify ${circuitType} proof!`; @@ -224,6 +230,7 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { } private async verifyProofFromKey( + flavor: UltraHonkFlavor, verificationKey: Buffer, proof: Proof, logFunction: (message: string) => void = () => {}, @@ -234,7 +241,7 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { await fs.writeFile(proofFileName, proof.buffer); await fs.writeFile(verificationKeyPath, verificationKey); - return await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath!, logFunction); + return await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath!, flavor, logFunction); }; return await this.runInDirectory(operation); } @@ -301,7 +308,14 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { const timer = new Timer(); - const vkResult = await computeVerificationKey(this.bbBinaryPath, directory, circuitType, bytecode, this.log.debug); + const vkResult = await computeVerificationKey( + this.bbBinaryPath, + directory, + circuitType, + bytecode, + circuitType === 'App' ? 'ultra_honk' : getUltraHonkFlavorForCircuit(circuitType), + this.log.debug, + ); if (vkResult.status === BB_RESULT.FAILURE) { this.log.error(`Failed to generate proof for ${circuitType}${dbgCircuitName}: ${vkResult.reason}`); diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 2c38db4869eb..4bbf12607a8f 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -77,12 +77,12 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { + type BBFailure, type BBSuccess, BB_RESULT, PROOF_FIELDS_FILENAME, PROOF_FILENAME, VK_FILENAME, - type VerificationFunction, generateAvmProof, generateKeyForNoirCircuit, generateProof, @@ -92,6 +92,7 @@ import { writeProofAsFields, } from '../bb/execute.js'; import type { ACVMConfig, BBConfig } from '../config.js'; +import { type UltraHonkFlavor, getExpectedProofLength, getUltraHonkFlavorForCircuit } from '../honk.js'; import { ProverInstrumentation } from '../instrumentation.js'; import { PublicKernelArtifactMapping } from '../mappings/mappings.js'; import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; @@ -99,11 +100,6 @@ import { extractAvmVkData, extractVkData } from '../verification_key/verificatio const logger = createDebugLogger('aztec:bb-prover'); -const CIRCUITS_WITHOUT_AGGREGATION: Set = new Set([ - 'BaseParityArtifact', - 'EmptyNestedArtifact', -]); - export interface BBProverConfig extends BBConfig, ACVMConfig { // list of circuits supported by this prover. defaults to all circuits if empty circuitFilter?: ServerProtocolArtifact[]; @@ -113,8 +109,8 @@ export interface BBProverConfig extends BBConfig, ACVMConfig { * Prover implementation that uses barretenberg native proving */ export class BBNativeRollupProver implements ServerCircuitProver { - private verificationKeys: Map> = new Map< - ServerProtocolArtifact, + private verificationKeys = new Map< + `ultra${'_keccak_' | '_'}honk_${ServerProtocolArtifact}`, Promise >(); @@ -149,7 +145,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { const { circuitOutput, proof } = await this.createRecursiveProof( inputs, 'BaseParityArtifact', - RECURSIVE_PROOF_LENGTH, + getExpectedProofLength('BaseParityArtifact'), convertBaseParityInputsToWitnessMap, convertBaseParityOutputsFromWitnessMap, ); @@ -235,6 +231,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { ); await this.verifyWithKey( + getUltraHonkFlavorForCircuit(kernelOps.artifact), kernelRequest.inputs.previousKernel.vk, kernelRequest.inputs.previousKernel.proof.binaryProof, ); @@ -539,6 +536,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { circuitType, Buffer.from(artifact.bytecode, 'base64'), outputWitnessFile, + getUltraHonkFlavorForCircuit(circuitType), logger.debug, ); @@ -682,9 +680,8 @@ export class BBNativeRollupProver implements ServerCircuitProver { this.instrumentation.recordSize('circuitSize', 'tubeCircuit', tubeVK.circuitSize); // Sanity check the tube proof (can be removed later) - await this.verifyWithKey(tubeVK, tubeProof.binaryProof); + await this.verifyWithKey('ultra_honk', tubeVK, tubeProof.binaryProof); - // TODO(#7369): properly time tube construction logger.info( `Generated proof for tubeCircuit in ${Math.ceil(provingResult.durationMs)} ms, size: ${ tubeProof.proof.length @@ -762,22 +759,25 @@ export class BBNativeRollupProver implements ServerCircuitProver { */ public async verifyProof(circuitType: ServerProtocolArtifact, proof: Proof) { const verificationKey = await this.getVerificationKeyDataForCircuit(circuitType); - // info(`vkey in: ${verificationKey.keyAsFields.key}`); - return await this.verifyWithKey(verificationKey, proof); + return await this.verifyWithKey(getUltraHonkFlavorForCircuit(circuitType), verificationKey, proof); } public async verifyAvmProof(proof: Proof, verificationKey: AvmVerificationKeyData) { - return await this.verifyWithKeyInternal(proof, verificationKey, verifyAvmProof); + return await this.verifyWithKeyInternal(proof, verificationKey, (proofPath, vkPath) => + verifyAvmProof(this.config.bbBinaryPath, proofPath, vkPath, logger.debug), + ); } - public async verifyWithKey(verificationKey: VerificationKeyData, proof: Proof) { - return await this.verifyWithKeyInternal(proof, verificationKey, verifyProof); + public async verifyWithKey(flavor: UltraHonkFlavor, verificationKey: VerificationKeyData, proof: Proof) { + return await this.verifyWithKeyInternal(proof, verificationKey, (proofPath, vkPath) => + verifyProof(this.config.bbBinaryPath, proofPath, vkPath, flavor, logger.debug), + ); } private async verifyWithKeyInternal( proof: Proof, verificationKey: { keyAsBytes: Buffer }, - verificationFunction: VerificationFunction, + verificationFunction: (proofPath: string, vkPath: string) => Promise, ) { const operation = async (bbWorkingDirectory: string) => { const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME); @@ -786,16 +786,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { await fs.writeFile(proofFileName, proof.buffer); await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes); - const logFunction = (message: string) => { - logger.verbose(`BB out - ${message}`); - }; - - const result = await verificationFunction( - this.config.bbBinaryPath, - proofFileName, - verificationKeyPath!, - logFunction, - ); + const result = await verificationFunction(proofFileName, verificationKeyPath!); if (result.status === BB_RESULT.FAILURE) { const errorMessage = `Failed to verify proof from key!`; @@ -886,14 +877,15 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @returns The verification key data */ private async getVerificationKeyDataForCircuit(circuitType: ServerProtocolArtifact): Promise { - let promise = this.verificationKeys.get(circuitType); + const flavor = getUltraHonkFlavorForCircuit(circuitType); + let promise = this.verificationKeys.get(`${flavor}_${circuitType}`); if (!promise) { promise = generateKeyForNoirCircuit( this.config.bbBinaryPath, this.config.bbWorkingDirectory, circuitType, ServerCircuitArtifacts[circuitType], - 'vk', + flavor, logger.debug, ).then(result => { if (result.status === BB_RESULT.FAILURE) { @@ -901,7 +893,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { } return extractVkData(result.vkPath!); }); - this.verificationKeys.set(circuitType, promise); + this.verificationKeys.set(`${flavor}_${circuitType}`, promise); } const vk = await promise; return vk.clone(); @@ -916,10 +908,11 @@ export class BBNativeRollupProver implements ServerCircuitProver { filePath: string, circuitType: ServerProtocolArtifact, ): Promise { - let promise = this.verificationKeys.get(circuitType); + const flavor = getUltraHonkFlavorForCircuit(circuitType); + let promise = this.verificationKeys.get(`${flavor}_${circuitType}`); if (!promise) { promise = extractVkData(filePath); - this.verificationKeys.set(circuitType, promise); + this.verificationKeys.set(`${flavor}_${circuitType}`, promise); } return promise; } @@ -943,20 +936,14 @@ export class BBNativeRollupProver implements ServerCircuitProver { fs.readFile(proofFieldsFilename, { encoding: 'utf-8' }), ]); const json = JSON.parse(proofString); - const vkData = await this.verificationKeys.get(circuitType); - if (!vkData) { - throw new Error(`Invalid verification key for ${circuitType}`); - } + const vkData = await this.getVerificationKeyDataForCircuit(circuitType); + // TODO (alexg) is this needed anymore? Shouldn't I just use the vkData.numPublicInputs? const numPublicInputs = vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH; const fieldsWithoutPublicInputs = json .slice(0, 3) .map(Fr.fromString) .concat(json.slice(3 + numPublicInputs).map(Fr.fromString)); - logger.debug( - `num pub inputs ${vkData.numPublicInputs} and without aggregation ${CIRCUITS_WITHOUT_AGGREGATION.has( - circuitType, - )}`, - ); + logger.debug(`num pub inputs ${vkData.numPublicInputs} circuit=${circuitType}`); const proof = new RecursiveProof( fieldsWithoutPublicInputs, diff --git a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts index b5f4bacb1fa9..908e49b5aee1 100644 --- a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts +++ b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts @@ -3,11 +3,9 @@ import { AvmVerificationKeyAsFields, AvmVerificationKeyData, Fr, - VERIFICATION_KEY_LENGTH_IN_FIELDS, VerificationKeyAsFields, VerificationKeyData, } from '@aztec/circuits.js'; -import { type Tuple } from '@aztec/foundation/serialize'; import { strict as assert } from 'assert'; import * as fs from 'fs/promises'; @@ -29,10 +27,8 @@ export async function extractVkData(vkDirectoryPath: string): Promise, vkHash); - const vk = new VerificationKeyData(vkAsFields, rawBinary); - return vk; + const vkAsFields = new VerificationKeyAsFields(fields, vkHash); + return new VerificationKeyData(vkAsFields, rawBinary); } // TODO: This was adapted from the above function. A refactor might be needed. diff --git a/yarn-project/bb-prover/src/verifier/bb_verifier.ts b/yarn-project/bb-prover/src/verifier/bb_verifier.ts index 8cfc2688de1e..55b16f1b8462 100644 --- a/yarn-project/bb-prover/src/verifier/bb_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/bb_verifier.ts @@ -22,6 +22,7 @@ import { verifyProof, } from '../bb/execute.js'; import { type BBConfig } from '../config.js'; +import { getUltraHonkFlavorForCircuit } from '../honk.js'; import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; @@ -62,7 +63,7 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { workingDirectory, circuit, ProtocolCircuitArtifacts[circuit], - 'vk', + getUltraHonkFlavorForCircuit(circuit), logFn, ).then(result => { if (result.status === BB_RESULT.FAILURE) { @@ -103,7 +104,13 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { this.logger.debug(`${circuit} BB out - ${message}`); }; - const result = await verifyProof(this.config.bbBinaryPath, proofFileName, verificationKeyPath!, logFunction); + const result = await verifyProof( + this.config.bbBinaryPath, + proofFileName, + verificationKeyPath!, + getUltraHonkFlavorForCircuit(circuit), + logFunction, + ); if (result.status === BB_RESULT.FAILURE) { const errorMessage = `Failed to verify ${circuit} proof!`; diff --git a/yarn-project/bb-prover/tsconfig.json b/yarn-project/bb-prover/tsconfig.json index e0e59ed584cc..77c9c6ff999f 100644 --- a/yarn-project/bb-prover/tsconfig.json +++ b/yarn-project/bb-prover/tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../telemetry-client" + }, + { + "path": "../ethereum" } ], "include": ["src"] diff --git a/yarn-project/circuits.js/src/structs/proof.ts b/yarn-project/circuits.js/src/structs/proof.ts index 1ccfe85ce095..e93af115111a 100644 --- a/yarn-project/circuits.js/src/structs/proof.ts +++ b/yarn-project/circuits.js/src/structs/proof.ts @@ -1,6 +1,8 @@ -import { Fr } from '@aztec/bb.js'; +import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { AGGREGATION_OBJECT_LENGTH } from '../constants.gen.js'; + const EMPTY_PROOF_SIZE = 42; /** @@ -12,6 +14,9 @@ const EMPTY_PROOF_SIZE = 42; export class Proof { // Make sure this type is not confused with other buffer wrappers readonly __proofBrand: any; + + readonly publicInputsOffset = 100; + constructor( /** * Holds the serialized proof data in a binary buffer format. @@ -55,11 +60,22 @@ export class Proof { } public withoutPublicInputs(): Buffer { - if (this.numPublicInputs > 0) { - return this.buffer.subarray(Fr.SIZE_IN_BYTES * this.numPublicInputs); - } else { - return this.buffer; - } + return Buffer.concat([ + this.buffer.subarray(4, this.publicInputsOffset), + this.buffer.subarray(this.publicInputsOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs), + ]); + } + + public extractPublicInputs(): Fr[] { + const reader = BufferReader.asReader( + this.buffer.subarray(this.publicInputsOffset, this.publicInputsOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs), + ); + return reader.readArray(this.numPublicInputs, Fr); + } + + public extractAggregationObject(): Fr[] { + const publicInputs = this.extractPublicInputs(); + return publicInputs.slice(-1 * AGGREGATION_OBJECT_LENGTH); } /** diff --git a/yarn-project/circuits.js/src/structs/verification_key.ts b/yarn-project/circuits.js/src/structs/verification_key.ts index 331cc50ba109..3003f31e3673 100644 --- a/yarn-project/circuits.js/src/structs/verification_key.ts +++ b/yarn-project/circuits.js/src/structs/verification_key.ts @@ -1,7 +1,7 @@ import { makeTuple } from '@aztec/foundation/array'; import { times } from '@aztec/foundation/collection'; import { Fq, Fr } from '@aztec/foundation/fields'; -import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { strict as assert } from 'assert'; @@ -85,7 +85,7 @@ export const CIRCUIT_RECURSIVE_INDEX = 0; * Provides a 'fields' representation of a circuit's verification key */ export class VerificationKeyAsFields { - constructor(public key: Tuple, public hash: Fr) {} + constructor(public key: Fr[], public hash: Fr) {} public get numPublicInputs() { return Number(this.key[CIRCUIT_PUBLIC_INPUTS_INDEX]); @@ -104,10 +104,10 @@ export class VerificationKeyAsFields { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(this.key, this.hash); + return serializeToBuffer(...this.toFields()); } toFields() { - return [...this.key, this.hash]; + return [this.key.length, ...this.key, this.hash]; } /** @@ -117,7 +117,7 @@ export class VerificationKeyAsFields { */ static fromBuffer(buffer: Buffer | BufferReader): VerificationKeyAsFields { const reader = BufferReader.asReader(buffer); - return new VerificationKeyAsFields(reader.readArray(VERIFICATION_KEY_LENGTH_IN_FIELDS, Fr), reader.readObject(Fr)); + return new VerificationKeyAsFields(reader.readVector(Fr), reader.readObject(Fr)); } /** diff --git a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts index f77e60690500..594272d005bf 100644 --- a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts @@ -1,10 +1,9 @@ import { L2Block, deployL1Contract, fileURLToPath } from '@aztec/aztec.js'; import { BBCircuitVerifier } from '@aztec/bb-prover'; -import { AGGREGATION_OBJECT_LENGTH, Fr, HEADER_LENGTH, Proof } from '@aztec/circuits.js'; +import { Fr, Proof } from '@aztec/circuits.js'; import { type L1ContractAddresses } from '@aztec/ethereum'; import { type Logger } from '@aztec/foundation/log'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { AvailabilityOracleAbi, RollupAbi } from '@aztec/l1-artifacts'; +import { BufferReader } from '@aztec/foundation/serialize'; import { type Anvil } from '@viem/anvil'; import { readFile } from 'fs/promises'; @@ -34,6 +33,7 @@ import { getLogger, setupL1Contracts, startAnvil } from '../fixtures/utils.js'; describe('proof_verification', () => { let proof: Proof; let proverId: Fr; + let vkTreeRoot: Fr; let block: L2Block; let aggregationObject: Fr[]; let anvil: Anvil | undefined; @@ -53,12 +53,14 @@ describe('proof_verification', () => { if (!rpcUrl) { ({ anvil, rpcUrl } = await startAnvil()); } + logger.info('Anvil started'); ({ l1ContractAddresses, publicClient, walletClient } = await setupL1Contracts( rpcUrl, mnemonicToAccount(MNEMONIC), logger, )); + logger.info('l1 contracts done'); const bb = await getBBConfig(logger); const acvm = await getACVMConfig(logger); @@ -70,12 +72,16 @@ describe('proof_verification', () => { bbTeardown = bb!.cleanup; acvmTeardown = acvm!.cleanup; + logger.info('bb, acvm done'); + + const content = await circuitVerifier.generateSolidityContract('BlockRootRollupArtifact', 'UltraHonkVerifier.sol'); + logger.info('generated contract'); const input = { language: 'Solidity', sources: { 'UltraHonkVerifier.sol': { - content: await circuitVerifier.generateSolidityContract('BlockRootRollupArtifact', 'UltraHonkVerifier.sol'), + content, }, }, settings: { @@ -94,6 +100,7 @@ describe('proof_verification', () => { }; const output = JSON.parse(solc.compile(JSON.stringify(input))); + logger.info('compiled contract'); const abi = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].abi; const bytecode: string = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].evm.bytecode.object; @@ -104,6 +111,7 @@ describe('proof_verification', () => { client: publicClient, abi, }) as any; + logger.info('deployed verifier'); }); afterAll(async () => { @@ -121,10 +129,9 @@ describe('proof_verification', () => { ); block = L2Block.fromString(blockResult.block); - // TODO(#6624): Note that with honk proofs the below writes incorrect test data to file. - // The serialisation does not account for the prepended fields (circuit size, PI size, PI offset) in new Honk proofs, so the written data is shifted. proof = Proof.fromString(blockResult.proof); - proverId = Fr.ZERO; + proverId = Fr.fromString(blockResult.proverId); + vkTreeRoot = Fr.fromString(blockResult.vkTreeRoot); aggregationObject = blockResult.aggregationObject.map((x: string) => Fr.fromString(x)); }); @@ -133,68 +140,61 @@ describe('proof_verification', () => { await expect(circuitVerifier.verifyProofForCircuit('BlockRootRollupArtifact', proof)).resolves.toBeUndefined(); }); }); - // TODO(#6624) & TODO(#7346): The below PIs do not correspond to BlockRoot/Root circuits. - // They will need to be updated to whichever circuit we are using when switching on this test. + describe('HonkVerifier', () => { it('verifies full proof', async () => { - const reader = BufferReader.asReader(proof.buffer); - // +2 fields for archive - const archive = reader.readArray(2, Fr); - const header = reader.readArray(HEADER_LENGTH, Fr); - const aggObject = reader.readArray(AGGREGATION_OBJECT_LENGTH, Fr); - - const publicInputs = [...archive, ...header, ...aggObject].map(x => x.toString()); - - const proofStr = `0x${proof.buffer - .subarray((HEADER_LENGTH + 2 + AGGREGATION_OBJECT_LENGTH) * Fr.SIZE_IN_BYTES) - .toString('hex')}` as const; + // skip proof size which is an uint32 + const reader = BufferReader.asReader(proof.buffer.subarray(4)); + const [circuitSize, numPublicInputs, publicInputsOffset] = reader.readArray(3, Fr); + const publicInputs = reader.readArray(numPublicInputs.toNumber(), Fr).map(x => x.toString()); + + const proofStr = `0x${Buffer.concat([ + circuitSize.toBuffer(), + numPublicInputs.toBuffer(), + publicInputsOffset.toBuffer(), + reader.readToEnd(), + ]).toString('hex')}` as const; await expect(verifierContract.read.verify([proofStr, publicInputs])).resolves.toBeTruthy(); }); it('verifies proof taking public inputs from block', async () => { - const proofStr = `0x${proof.withoutPublicInputs().toString('hex')}`; - const publicInputs = [...block.archive.toFields(), ...block.header.toFields(), ...aggregationObject].map(x => - x.toString(), - ); + const reader = BufferReader.asReader(proof.buffer.subarray(4)); + const [circuitSize, numPublicInputs, publicInputsOffset] = reader.readArray(3, Fr); + const publicInputsFromProof = reader.readArray(numPublicInputs.toNumber(), Fr).map(x => x.toString()); + + const proofStr = `0x${Buffer.concat([ + circuitSize.toBuffer(), + numPublicInputs.toBuffer(), + publicInputsOffset.toBuffer(), + reader.readToEnd(), + ]).toString('hex')}` as const; + + const publicInputs = [ + block.header.lastArchive.root, + block.header.globalVariables.blockNumber, + block.archive.root, + new Fr(block.archive.nextAvailableLeafIndex), + Fr.ZERO, // prev block hash + block.hash(), + ...block.header.globalVariables.toFields(), // start global vars + ...block.header.globalVariables.toFields(), // end global vars + new Fr(block.header.contentCommitment.outHash), + block.header.globalVariables.coinbase.toField(), // the fee taker's address + block.header.totalFees, // how much they got + ...Array(62).fill(Fr.ZERO), // 31 other (fee takers, fee) pairs + vkTreeRoot, + proverId, // 0x51 + ...aggregationObject, + ].map((x: Fr) => x.toString()); + + expect(publicInputs.length).toEqual(publicInputsFromProof.length); + expect(publicInputs.slice(0, 27)).toEqual(publicInputsFromProof.slice(0, 27)); + expect(publicInputs.slice(27, 89)).toEqual(publicInputsFromProof.slice(27, 89)); + expect(publicInputs.slice(89, 91)).toEqual(publicInputsFromProof.slice(89, 91)); + expect(publicInputs.slice(91)).toEqual(publicInputsFromProof.slice(91)); await expect(verifierContract.read.verify([proofStr, publicInputs])).resolves.toBeTruthy(); }); }); - - describe('Rollup', () => { - let availabilityContract: GetContractReturnType; - let rollupContract: GetContractReturnType; - - beforeAll(async () => { - rollupContract = getContract({ - address: l1ContractAddresses.rollupAddress.toString(), - abi: RollupAbi, - client: walletClient, - }); - - availabilityContract = getContract({ - address: l1ContractAddresses.availabilityOracleAddress.toString(), - abi: AvailabilityOracleAbi, - client: walletClient, - }); - - await rollupContract.write.setVerifier([verifierContract.address]); - logger.info('Rollup only accepts valid proofs now'); - await availabilityContract.write.publish([`0x${block.body.toBuffer().toString('hex')}`]); - }); - // TODO(#6624) & TODO(#7346): Rollup.submitProof has changed to submitBlockRootProof/submitRootProof - // The inputs below may change depending on which submit fn we are using when we reinstate this test. - it('verifies proof', async () => { - const args = [ - `0x${block.header.toBuffer().toString('hex')}`, - `0x${block.archive.root.toBuffer().toString('hex')}`, - `0x${proverId.toBuffer().toString('hex')}`, - `0x${serializeToBuffer(aggregationObject).toString('hex')}`, - `0x${proof.withoutPublicInputs().toString('hex')}`, - ] as const; - - await expect(rollupContract.write.submitBlockRootProof(args)).resolves.toBeDefined(); - }); - }); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index b75763aa6dcf..946f3678b913 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -352,7 +352,7 @@ export class FullProverTest { const { walletClient, publicClient, l1ContractAddresses } = this.context.deployL1ContractsValues; const contract = await this.circuitProofVerifier.generateSolidityContract( - 'RootRollupArtifact', + 'BlockRootRollupArtifact', 'UltraHonkVerifier.sol', ); diff --git a/yarn-project/end-to-end/src/e2e_prover/full.test.ts b/yarn-project/end-to-end/src/e2e_prover/full.test.ts index f5881b11a1d9..0430ef690aa1 100644 --- a/yarn-project/end-to-end/src/e2e_prover/full.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/full.test.ts @@ -1,4 +1,3 @@ -import { type Fr } from '@aztec/aztec.js'; import { getTestData, isGenerateTestDataEnabled, writeTestData } from '@aztec/foundation/testing'; import { FullProverTest } from './e2e_prover_test.js'; @@ -18,7 +17,7 @@ describe('full_prover', () => { await t.applyBaseSnapshots(); await t.applyMintSnapshot(); await t.setup(); - // await t.deployVerifier(); + await t.deployVerifier(); ({ provenAssets, accounts, tokenSim, logger } = t); }); @@ -79,16 +78,7 @@ describe('full_prover', () => { // fail the test. User asked for fixtures but we don't have any throw new Error('No block result found in test data'); } - // TODO(#6624): Note that with honk proofs the below writes incorrect test data to file. - // The serialisation does not account for the prepended fields (circuit size, PI size, PI offset) in new Honk proofs, so the written data is shifted. - writeTestData( - 'yarn-project/end-to-end/src/fixtures/dumps/block_result.json', - JSON.stringify({ - block: blockResult.block.toString(), - proof: blockResult.proof.toString(), - aggregationObject: blockResult.aggregationObject.map((x: Fr) => x.toString()), - }), - ); + writeTestData('yarn-project/end-to-end/src/fixtures/dumps/block_result.json', JSON.stringify(blockResult)); } }, TIMEOUT, diff --git a/yarn-project/end-to-end/src/fixtures/dumps/block_result.json b/yarn-project/end-to-end/src/fixtures/dumps/block_result.json index 266cf49864e7..c72f6d926c85 100644 --- a/yarn-project/end-to-end/src/fixtures/dumps/block_result.json +++ b/yarn-project/end-to-end/src/fixtures/dumps/block_result.json @@ -1,22 +1 @@ -{ - "block": "1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000200d09e7feff5a1049661763ded52742f02aac5d9793b27a40d6b9c60a668bdf200747f2ee8836d7dd230b97572463dac0576259dedd98d86c56e2275d6d670d30007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c32818d15e97da7dd64b32439cc63b7d03601ccadce81009ab9792a2f487795d30000000100b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000008019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000010023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a90000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dfe666201fc6aee3c5fdcd21deedfa71790c6c7719d2af6919c068ef9b9f4c30000000200000000", - "proof": "00003e84000001f40000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000006b00000000000000000000000000000000000000000000000000000000000000011200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000000000000000000000000000000000000000000000000000000000000011dfe666201fc6aee3c5fdcd21deedfa71790c6c7719d2af6919c068ef9b9f4c3000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002ff40e125f72283eb833736e42285701b003d9e4270756ad3c5ba36ee0dbae760000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e54491432d6c962973b71fcfd7b5597486f108bc66cd620099a65702fa0181b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f3dac4a356c5fdd79000000000000000000000000000000000000000000000001f8b97ff8c78f3f9f00000000000000000000000000000000000000000000000cf72074e065bc22b30000000000000000000000000000000000000000000000000002dc10ffccda590000000000000000000000000000000000000000000000047bfb4dfb23cc889f00000000000000000000000000000000000000000000000871e52274633f4bf70000000000000000000000000000000000000000000000013c49830a0ce95ff20000000000000000000000000000000000000000000000000002c5ab7c0bb98e00000000000000000000000000000000000000000000000b0f322f57a86900ed000000000000000000000000000000000000000000000002e496ababf56e3cd6000000000000000000000000000000000000000000000005dd1141d5deb2050e000000000000000000000000000000000000000000000000000268dc87f9458f000000000000000000000000000000000000000000000003334a597cd9ec0a0e00000000000000000000000000000000000000000000000645a57625996ab518000000000000000000000000000000000000000000000006a2f7ffb16256c45b00000000000000000000000000000000000000000000000000027ca8c331291b000000000000000000000000000000f4ee11b0bde295507e62592c4338bc1f290000000000000000000000000000000000268abf37ebdc51e432b543b9cd13eb00000000000000000000000000000080fb57196db1edb119c4a43096ca53be4700000000000000000000000000000000000c10cb089cb171173b34c8ab3b952c00000000000000000000000000000064a8f95c06b2f690c389f6e9ffbac2f03d0000000000000000000000000000000000305b974421c33e120c6c35e2d49d7e0000000000000000000000000000006360b1b9dbd90a9b1ccb3cd3bc78cd9ecb00000000000000000000000000000000000cafe05c1184abbb1673bebfbdfd08000000000000000000000000000000e9a8914e09dba59c9d0469eac4258a756000000000000000000000000000000000000aab18264ff95058a2bd32aa92ef6f000000000000000000000000000000430eafce70b21dd200f81e952ae95ccea2000000000000000000000000000000000027f21d866b6071e7d5888a222b23f200000000000000000000000000000035e18690ea3538d27c3eb3e49ff771858a0000000000000000000000000000000000253559923d3ef097c24944960baaca0000000000000000000000000000000409efa67b85eec9db156ab6a7caac5f9b00000000000000000000000000000000001379a24c97a4e3f27a72888913923c000000000000000000000000000000968bb1d5b9def16c7ba73eb743ab2082a7000000000000000000000000000000000007ca785c1c0cfd82fbce0d58a4dd19000000000000000000000000000000edd674059047c461b4014800a82e531a550000000000000000000000000000000000227a11996c17a4514c44e17b3e5b92000000000000000000000000000000eac24cd380dd1e269e973ef0e61f1ceacd000000000000000000000000000000000005e248521b8b312e944537ae68ecdb000000000000000000000000000000d2d1bc3109deba532ba528c5732d0a5f640000000000000000000000000000000000015dc7ad1bc5e912a04cb7fa12ff760000000000000000000000000000007c9ccd204792110580933efcdc4e4429b500000000000000000000000000000000000dc9d4003e306af0e5bdf9671ed3b9000000000000000000000000000000a33a9a871c7276da857c0bddcfe44bbf8300000000000000000000000000000000002cb33c7ca066c3c2dcfa000582713e0000000000000000000000000000000283503468afcb144e05a54e030a9a263600000000000000000000000000000000001c444beb50f664ebc2590d48488f1a0000000000000000000000000000004ba5bd89da5d04580ea93ca6e0569750920000000000000000000000000000000000219de77d73911f7d4b40973b92260d20c3f91a2efb28325f9e9c7865a0785c99e3e78307f91517889b623e5248c1920fa05558b23677f758b1a93e1be0e0008e5000c571c05b79bb4693559db73e6f1a5162de6db9f031e6c0d24bbfe35bc0b6efafece270978e59125b4973a997e31c579d28bd16855fb8e7019f0624f0ff8d201cc7331881b9022884717556a7a910df1d8d4f02a36374e38d9ec8cf380ca5c577925a79479d298593db4d0e676a153bcc1799ae8895ffaac643470b7d08e3ed2b39e276ed5620899c30b53ddb180b60f927b3c132347fb728509be0db26cc7461185a6514559b8fa1b648a8fc4a2a8d01adcc80c703889d7085ac6ec52b081316f493ccf91bd5ec3b00c9ca9a392e014410ce4234c9ee9dc4046a89608910a607f85241cb239b4cf7f243af905f1e611e72fbbe893a8994d50241f507c1eef1e806a456b39fc5e823c07bcf897c2f20f1a0e0804767b56b7e14dc82678c3f03b3dd3f22528e6fd5cc4964dd1cc72d5c0d02b0448eadc2f8582fad135db51484d2082a67671b12fc9c19d173fb4119f0b799734b7223bc2056fb84a7798250cb27fa8111bc7f9fc4bf1439ed0b031ec89ed76f1837d4ec12c4b2ea6043510f34d5256445911d477e444338f83d37152974ab6c2851a4925bc5821ced7e10b18aca14c98c1c01ed759d09310273bd0cdb0e8928bcae871efd91d81146bce75ffdafd5dc9b636d121237a15aefe4ff2558b4e99f5aa79a83c160f622073577c99dfd85bc90a921468b5def1b5e6a5813a21e2cdd0fba4d28ab9778b18e2e479db80dea0f335ba0c99f48c6133a1cdd07461930985f5865c1f0b366270971ec3f16e116402951a2cd461365ab808a7d2490cec89fd1b8a8f7553efd519e09529e4eb27a5d5a2398a3010473a191ab09277bc2fbf183afef00e98419a93d6c0169633acff37be4ab5f2183e22c0ec2f214e363cbb7bdda19d2953138d091b902e6e067f4b11e07213175ff514ddfd6662f48f9edca6f052323b8751a5243993f497c10a9d2aad798d875e0caee6e2d0f11ef53cfa924be4c88950b2f8945a4c1b1687a76b3bda7c5f7c0c9b55e71d6db1eb47d29d356167a16eea82725dd97766a0f507d88c6e2e46d4b05b23d538b16163d2eafec2e3803ae06a8936145dc5fc5a9d5f5f449a4da772bcd8393f3839c2297b279aab987372a939f6cc3a8a17893b88ced49e650a7435a0207c0b3d3dd1fe89bc2464bbacf010fcf500b666fac9fac31edd1dc466200b9349c75277a4b0a4c0696b9053d260df2dfdfd28e1171a992d0a44ac4a79ee28358e43d712e04202b8e2915cb56aa73518d40b1ca8dbb8ec3db522e6c7cecb6a1e3820b3ec7f3111e26921335ba8970b1c83cea1d5c2f9a01629825b9f082fbe2692c30da51320a275bb5e10a3ba1c39fceebc54ea1773e6df342be5e10b6b92934d943a3891510d8124b20c13df7fc3febaee595d47eeb7d91a06c6938e354e0242b7f800b7515c391766e31ec5cebfae28caadd4f62f8cdaf9715476ebcefea6047ee8d5c1a08be8ce510de1b1ef721232bdfb66b5aa8a2d3936a72f61866182106b6c04fb126da8402d04044fc34320ea7dc25529adad17903aed9b6ba818fd1545d5d9a8e261fca9c2dd9f4e46ada8b6dab7597b138f496b23ac9130b5e0809ff7a7f81022650da504f0cf0e569f01fcb54f987b4077df2ab9ec7475cc2ee32f61766a7a01c71a792b2bbb401fb5bcc06c53bf885ed51eccb40c1d3dcb77738b12bfc1c7c2f001cdc50e33714ae43eec90a111e3a95c6a2f3e562be4ab584223da1eda2c71064e6eafa71b5b83e51ec68e7a2702e0a4b150b9bbc4439b7dbc2edda5c443205eba641ee7ca513f36d73e89e168eae1253038be675a202bf49f8e0d01c3ee027c4c71190b85111a4a993acd70d5d59a41c3745517ee9454884aad102c3a7460294a40fb2aedb6d7e393491c86cf95592f8eb4e341a9383048ac54b95a98fb91d72578a1f41fe8df57999f4fd5803a1d804e7817e8a313216e7fbb20ef4bfae1c7abaf6230db02f7668ad099b6ac61eb58ddbb6ff081b11494d88e7d08e7b0e1d5151bd5f45ad09bd5d6bd55bc4877298f397c43756f385468b54490f6dc47510a7a4cb94179d0786421ff47903284154fd27b0c855f1afdcfaa89fd255b45008de625de179b91e5b38518c7edcc4ac3839c7c509af9e6612d48fbb6cfb50790d10dc8189a076946bc04e7c8eedd1d9c1c59c3f8cef6084560f127a6c887f4e18a35a3ab369418cfc8ac80d4d1d499be3ffec00c99b1228cebd5b22f3f9e65c2a77f0d8c3d31007f194cde62aac442a0a393cbbba694d7ae12f6d923f2f604426a885e6fa355c5175d4b07d75871c1d63cfd2d41e7fdd9fbf263b46c99bc1a6261200b5c29e0429f41890894a5fa3cb2434c68fabf6d6244b6f60ccb9545e3420fef06a696a636f93caefbcde2b199463402141bf92061a6d5ca1364bbbdbd6144ae133a44eac10081422a15b2566d5cd830da3a631056bc03e7f516bd15ce22c1186cbb384febe13f34df839737c0f1246573f0ccafd319e641e4c41853cb61cb2c6e5f5823a4680fe13e5e91146bb03eb22c7080aa5e1d14c4a7f615455b818e66c7bf833d780dc90aa5bf3c7e58f486c02986f1c551bcb6bbfd8a41f4971050e29cec4cf0ab4ece2b9214c7f2daed4b015cac222fca15feac05f41ebbb90230cd65386bd237067c3164b158e374784734b5d318403001f6836597dea514f2f966959a8b2cddfa0b6d5b3cd3193cb8961157ae0157943c4a8b0423f8c22a222742cc7ce54b1bc8eeb9e3d54396ceae8eeecb03f1cdc61585a4833743bbbf423861708b6672940f39c4d30689675e73238bdb0cd0b6f2eaab9d6daa5bb7cba1be67b411470e13b86af488cf6ef1f7a0748d52fdef40e51a45eb8358579e2001e6e499a9fcdbf253c4e14ae6c75c75c95097e8440edb9a794209d14bf9b096b01417c8a62766e3514850c040dcf1bd57198026b3a0230df831e90cf0a42d3fc29c51e9b0df6c9776190a624cec3c0a174a6dc72e6504d1e2084197ef41a90f71da03690381582c4805ff33db82a6dbc22b43468096b5a8b3135e5a3020f3c610af5c738bafeb2766c800c62db8d4c9e0439adc18f6837f5b1ab9a6f8f54325d0d89314b393a45cac216729563f8f491e637910021b1b6841df2630cf0664401123dcf9f67697dfe3f9dfc320ef75403c7366c86230df14bbc5949683b1f7e101332826dca63e48427b0b4d5d63a40ffb3aa5a218314e8b2192ef7463893ce99018e6f655bb29a298c74052d5cc0f98e6b7f600fa1d7cd3e2851a40e8fdd62b80efd4f22af41916d69c491126aff5b33f3bee2b5c3f7f036abfc5f584a6d39af279b2ba34a897c0732981fe04d212e62cae5cc4f5977b3e0b34068a555c0f4aa0856249aa4710a61d0f964c307828d20c33f9b63367cbbe45f6a3e43e1920505042f24355d1a5ae06abb18e1f57f1d83b5c5df10f3eaac777c9c32e79472cbb00de22b3ca4aa28fe8c8e7f445ad92b29198332bc5c83196bb52c8a28e12f92b9038610671c4f8070b088ec0c05cafc506bf658aa408df9f2d67e4654313a23c817771f0bf5fa4c65f3863106ca75926c6b6879542658d5d926f03031abf7614427b547e5c6871ae6ac39b1c7fb6c3a575a53696085a0e2483a7408012da3f4e122027ac76b7c469e3e99c3db7878fc0db08b107be62918ca5285e3d1ab7694700283c066c81606e966e4d491b0697d11602de7514cd41ef647c0731855ec8da5303bdd477bfb8f7072b0e1ed3355718bbe5d696f0e26969810aa60dde2091fff275394c7a0f9b50a8d9371e95181d164eff0da35ecaa736c5c6e74c9e5ef00e20fd2f16c7e0c0cb972a4cf6f31a38818c67f16b74bc20758d24ec212d02bde762d956c1a157c1dc40cd8c1ac7810cd1513a74daf430241f593036c6bb4a22f1f05f2a899e13342493d11092351fab93680ada41df6d9abc2c89dac9e52d9f169177992e868b700a1931eaa60239e9f3ecf4363f2f4e11c1573b101da9013de1f2c0b5306fe527c45cf4c570a8c0381f0d3ec37295f53a4d99e3cc17fe60fc6762181aca245535b2bff6172e615e9a6aaeb7b7ef072de6ac6bb16ee088d8717e7147ed8537f2102c55d9309f4ad82c0eb27434bebee4797bf573199107a9b6e1f0ba7a3d2f4f6f6a5d57b8f4bfb250eae52eadcb7dccf8034f35366da8d1d13a92f9e614c0f590afad82a14b4fbe124b66aa439d17fa90024fe93b58e43ef93ac0af67c378aa5af71700602f4f8b87eba98eff57c660a6122c7d2e40cd5efca992912c0d6b78d88de7920809aa66ba74f245b01504ce8cee4eeefa51faf7a4c4d2cb1f0efab958c0e007f3ea281ede0930b97daa5d3e6b85ec22e2e2384deefb60343bc64efc8b156f54ad97a6f6a1fa91057c332575e56c01913f39d285b71dc06aab65f896e2590e6806ce9332955e0edf6d485c2e51466e1742d17e437a63c2795504e7dd7056e6d9975d60a27fa496c008696486c2b2135a095afe70e725d17193efe90ae26943854b35cee9f9b656b8616c22a955e307439645e4ad2736b1baac9e704d35fd2c502ebe8a1297a525e8b50b0ed2225cec9648dee61e54ae01c43d9de2249cb108d9f30715e2f6d4c1a411e58cde060e538b413ec1505abe718df610004a08f7ec0157efd617086e271381a3413e73327037ba8531a8dbcae0a4b909e3c22453979cbbba7c9e8b338eb7963b6a2897559a35be81479e4265a26ea6198415b70fd4f12c48c72c90cc777dbaa8a0daaaeecd1705aa7f8de1e2e1fcbbbdd9c79310b7618299468be821f95c1de326161ce26439c05beb5a0d16909025e276e1de97d3643187c340acae1547e36162bed13320222a7690fcc95d31f05a6ced3f78a27bb4fc737d66ce18025f95b254e29b28ff371c33ea1bd8e132de3f054361005d06033267c285b3d9baaef4f929b1d1182fe0b5c18b50496d4143d760243baf23fdc07d7384e9ae5330dd37f71a87758d5412cf1a139da5c3e0929f3bf6cb32fbd157308fc5f2ba0b8af838e93c7e3e1fee780e8a91b5ba7161fee73232e207cf7f91c65a2ec78d494eab6bf0e3f477dc32b7c0b78c521106e02219a2c26e76d1a1c173ed0bbffc02063019c0741f268fbbfa4f160e7fec1c516300372af2e190ed7f4212af578ca73017382150fec38fe0b5c279e1fb5a30611751f2b2ceb7003e13c353335bdfd021037ac2e164f70b7369b831188a5d3301c9b81bade3b43ca3e32ab4d1e1c3df573fff48435c41afd9e182bcc1c725f4f2416b598d7f90c4d87d2d48e377235155a7f34f0ed9d5960273caefd4868bb600d4c13ea609f29f2b39969780d60cb4d737752b3d7828618d4204a1e1f252c5f2bae870edb31ce84bb1d1fc6be8ecab20a45c184c12d73ccd4d92b64e985f2bb0da0cb84306678bc0f2ec5861f17012024e0eeebe8ca9b71050e8e59ea270aea238716cb6ea62eff41f7b10ead872176959d6ecc6046e3e1bfe600ae368afaaa04261e59bcd2eced7fd1bcadc84ed2788a59e6189e356c5bf9d2872249671a9408616c8b96a79de09262c726bd0a85b2ab758fc30ac52c0807086ddebe44c5d324ddca837c33f01b7c4a190cb1f294f574d6a8a022a858aab49e23020162abaa2b3c74d5bfdf8e677205713ab3524400cab5a78d23d9d47a54d2cc13bee6d4d1002cadcb2c914ff3180d62619508ff811aca4904f4d5c7e34d8cd5427316622d0cfdba13fbb72359562838a0dfe1c07dc96d3f29a3cdcb978c100ac75337f7ed264a617d779ef92efaabf31f8d8ee083ef13951e13b7341a32ea5a206ba0e74807fbdcc9fb35a66425f2468df7e3462a97f90c21c6b6ccf03653dcc4a07a13e4188beb58d73fab9bde45f64462d5df158bc5486e5ffcb61267358818b0a1c531206a02999da5ae0e0cb13cdfcbe448e75e91a085756f91f11204d24b9ac26c9f05b0909517430ce3afc8c0959296f0a1a9b708f468a2503ace0ea90dbec7d2e61293679bc3ca66d5cc8133883cc39c89f1650472d2dbc0addeabac8be5736a0f2927c30423c73636944981a57bf36564e3c454f40a4aeaecbcde88a2045b0d7f22ae98c1431ebb5f8827f43f16e6dc1809df7d40b745507cf40db1ae36a1289c206d0b8015b7cf444af29f7e552aef13d8ee4d0dcbdd0b18b35247663e14548319587bce9e2f949b13c8ca7e7e6a7db53ab00b82e8a0144b89a128f7d513ca7407319f5b0c2ab6cc53d3cf9e27ccdb7e98439506d9873e62c87989d7c525e5c62e1ebecaaef0968a8348a1afcaddeaa543ab271f868f4ff41ee72a782270dcb6095466996accc8f37d6b5054c8f0d3cd25af16c86cec489e5a97a2687f9f13d72f8ab24af842626811592ac14f00f47ae1d66e720e43050bea628a6b99a5e20b19e249b107819b5d02e4ac952fc11d716b971227bbf3bcdcddee7272a5c17d1517f3ef792dfed212cce5b4d30ae9d7bacf4432673c597e94a1011e6280c3fea201a709a353d1d7806ef92d17360bab66478ff180f0dfa3452b705a770e9c56b411bd16547623412a971554709dc5adb66ff0dc0913c1f173bd27f9f4807e72232edc4abf1c224085c921e5cec13064839780ef3e80ef272fed1e63a49b289fa30882621de391a717467195fcf23c2e7c7b043e6ee69d66b1c420f3416c33dedb251819bdcbef3b69a2f1a1dd4e6f9b638efb8b2a958de59ada940fe569919ce30b9138168402ea07cc57bc407820d261747f65c3a60c5d3c72e8dfb37eeb164429a574406a0e051276136f142695deed52557dc804ae74074585605e8cdfdc321cc161214c24b8930607b959c3b873f8887136b7a533609b0cd6e323637df3e719e28cd4075be0a5fe6afbbcb45103ea744a9b117fc79ff7c8e4ed2eb0f6ae5c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000755abab3f7b103f419606830d0d840388082435895411b90d4d8924e338d288225d20cb965acd1e966f30241e086c65dfb43f7a3b3e4a6264efe306397505692f3cae8e2b6e6c66d0ca6d9b1085f3529b78bd6b665110ca038427e6e9d5deaa1bbe84a25e455e3c83c39ca18892a482e47b82b7a89e9b5be7529a62dda6f91d0c7828de87520f74267502a1124d4128b9bf6937bdc4843714ec57484ae3faba0ec3703279721edd0682063125f10f22f57f1436b8c837bfad0d09501afb8b1401cc20f14c3fb3adfb07fd941b424725f3961a029488cf77733e63f3a521d47c2b4566e34ecccc6104ee261c33ead96a55f0ae11e1101dcba121d4c9e5eb2ab10034e2c91d4fe984aaefc2a6ca3613348a352b522ed427b004f5cf0d7bac0b481be7bba8fb303fc198a0a0d128592a6383d06df04cab44900c424b19d6e35bbd1d493542c799cd103e957f7396192d236a3c57bb662f3ae75bfc452022f6522f03c29c5a7aaeb266653eea78f42e09900e9e4938dc3a631de28eee2e9488a2762a77d7b4c7dee04743e2fcd5859820a14c81176e9cdc6309f3362ff1ed57777b0b52e5cf478b27a59deabfad2162bd98d9306dab3b22e59486089afc983ed8862b9a7d10611b8f64f05a7ee0de0b47d4bff6f7e19e7f3cbff886461fec1141552263408d7b25e069229b0f58c7300a2f9737c94a9393736c8c11e4c11341890a1d5019f4fdb6e14253909bc1c68466f133fdc96c3bd64700156e6b4568f37ffd1f05340dc04a7ebe8ad4e96c0d0c240ee10abd9b57c5cefc1258a345c56855df0340e35b136571723ca5c477df6c6ce878b90aec1d72dbe229c5be0e3c6fdec5004286e2554287cc7093855ecd4edc7f28fc3de48e040fc4793716e35dbd7d9a26334f4bfa504942cabff7275317ec9dd5c117542ef13c8f0761bef5232a0f42062539b418b01be15071dd250ebface7f45f09cefaea981d594e1fb651131a9c00e29a561cc35341d9bcf8479f03bc621d77840199eec0cd6322278c7c010f6c0a0c3cbff7f1595854424eb82ca3ed94fce1e88d256cd64a071420fe16a6c05e257d6fc9fbbbcf49f89dd2e55579b233386ca18d09a8fc47f3b2b768e60c9d812a664203c1d1e818c9441fbe811ed45d87846a322cb75e41a93cf0dc9f04b8521bb3763468fc29c6203915e8dcb535206d4bef5889a66ad062a0afb84cbc6cac0195bda43c78ef436e0f701f203a2440604c82babfeb07632abfd03011e82011282d27f4bf2b82a075d7580f601099bfeead7156eec207b3126ef0e955f03dca17fa89336ca99efc58b8a64a0e377e369e56db279c031f27d758a6c3b407edc90f38ac48b13d1f63252ff228e3cc9545a9c7bc4b38123dcf50d3f41b148ba10023a1e58f26d71dab1261af500d59f5d189dd43cdaeb9dc266882d8625bf669f911694cb35ab7aa98c1d1b716d150a4eb113f034a4ce7da5a09196ad3ee4e46b40e8aaf0ba5f8fff8ff4d20121441aef29adfc75d4820f78c44198912a93b2072296d7a2582177afbea66ddb25922caa396fe47961c2877e82f774dcc0c3758930e990ee99a8bccb5a50dc90b6721a874091dc762214e56d5b805595475577b2b28d8582ee519ab7a55bbf31cccea5475eb0eba4b6249cb694e7887a65dca7b922675f4bb3e5dca94f1a93ee6efb7165bf4d680cc122dfc48951b1c213c072e7a2c154c2bf538ca9b03f05fa292796086dacaf21a080c3d809674930a04b23598120096e180bb341e3da46ba1d6e1972a59fab49ef16c1e41ebbb783c0e96e7ca1e978911febab8b024df68a2e29fee6fa9f48f7fcf30dcca3dd13874fd034d5711faaf385ef0ad1c8d23a2f36e7c6d3bd14114522f95544c6cd1f8da0257758a000000000000000000000000000000b2bb591300a297e0a753862627bf61b14800000000000000000000000000000000001154933493f3bfd5a9c11b142c3717000000000000000000000000000000a0aafc71c55c4b8483d9e7aef3086cfc570000000000000000000000000000000000039357f77a572f20464ef0be7f46160000000000000000000000000000009cc129b2954978cc792b1535abe0e678ba0000000000000000000000000000000000210ca7c06f146386f9911070d2c18a0000000000000000000000000000003ced3aa175bdabd5eb4b50e2b1876f096700000000000000000000000000000000000f60d5d5c2236f2f9b27e95561781e000000000000000000000000000000565b50a254b922477f8a1b2493656891a500000000000000000000000000000000001a9b39d2ea0535d2953aaea59a8720000000000000000000000000000000bd15bd55c77c56b6390273d0054bdf4e1e00000000000000000000000000000000001564812d349548f16e4a041ec9cfdf00000000000000000000000000000076773335bab1d7b3e8bb293b1bed7b714700000000000000000000000000000000001fba381793ac902ccd92c2739c6f5200000000000000000000000000000075a46556945910ca4724c38e2abf6a64c000000000000000000000000000000000002c0f17e99fe87402a07fc23224fb9400000000000000000000000000000088fd65468ed8af24dafb5a1760b46f463b00000000000000000000000000000000000090a3201e6bd5559a8c73753c2fe500000000000000000000000000000008a63096070df14d043b9fc73bd466baec000000000000000000000000000000000021f085a0da7846087d69d27449e7e5000000000000000000000000000000661c396474836dfa9270a44f3645f067a700000000000000000000000000000000000ec1483f6cb6321f8a96fcde4ee618000000000000000000000000000000b81c5866353d91337b0f0eabe62c0d700300000000000000000000000000000000000d379ca8660e86b61c308c6e16518e00000000000000000000000000000053656c0b8d26d5d7c4ffbc84cb8c8dc78000000000000000000000000000000000002aa9ba2af5ea1164b866e13b2c16d1000000000000000000000000000000922021c4c51e3061eb00b48915e506ba45000000000000000000000000000000000015eca982d68e4abe53d58d4bcf075c00000000000000000000000000000085c04ffd527c9209edf992b974cbeba83900000000000000000000000000000000002ba65d580a93a360025f56b8f592a6000000000000000000000000000000d0f88ff226a3ef57f1f592db98e1d827910000000000000000000000000000000000042735accfa3e630a25d172a09f3f2000000000000000000000000000000847902d41483ffca3c114c8cdb4cedd145000000000000000000000000000000000023efc8d31277e79f56e7613272eded0000000000000000000000000000007a7c50b57185b83ac18ba3261e220d7fe10000000000000000000000000000000000199b7e3b8e089834da02b8b41c1ba50000000000000000000000000000008acbaf87e441a6f1fa602118b02a8793cd00000000000000000000000000000000002ec63fed5f835087f8e1418d0dfc1600000000000000000000000000000020f9c0878fb3277d8d19485475c595827000000000000000000000000000000000000499e7cc31fa2a39831fdf7df8513c000000000000000000000000000000184428a24e481dfeb599723ba6c02288c00000000000000000000000000000000000187c9d4dedd2f97e8a8fc0fd129383000000000000000000000000000000fd6667fb403d52b2991a570668382a43f300000000000000000000000000000000002230ae1eb1441db0886e5443aa7aa90000000000000000000000000000004459f189b482e036feae0da6e37c7fd7590000000000000000000000000000000000182ca7fc790dff23214209ba3d6566000000000000000000000000000000b95f9880fb46bbd90b5117dadf9e67726100000000000000000000000000000000000f15d77545d650bc2a882ed442b8a2000000000000000000000000000000700923f7fd6c51e4941184ab0d23b2f1db00000000000000000000000000000000001e7521d6bad6991d37eb8cf06971320000000000000000000000000000009d81f92a7875c29bd986187c67b812da40000000000000000000000000000000000007c0de4f110d28addf1d83d7749cb0000000000000000000000000000000f38cd83b6ce267d4aa382e6f207591f62000000000000000000000000000000000002f8dbc0ef0e84723c125d13bd0d1e80000000000000000000000000000007d03360528bc17f1fb1181c979746ab2b200000000000000000000000000000000000bde2ae9c687c6f2e4495cb6ceab9700000000000000000000000000000004bfa8f689b7df8174df8c96f57872ceb400000000000000000000000000000000000177e08abb1a6fd4f220bcccb07e62000000000000000000000000000000d59961d99890eb911c402b007b9cbe976d000000000000000000000000000000000001e44c4d03753c3c68e2f72c29b0af000000000000000000000000000000e10bcda37f9b020238ce42cafba364ad0b00000000000000000000000000000000000a2700ee4bcd2ee1f9d187fb9a58ee00000000000000000000000000000034e61a08bc6a2102504fff403b37d354a0000000000000000000000000000000000019060a7026ab5315caf29609416bff000000000000000000000000000000a0b9296028acb200c6d9216bf1fd05587c00000000000000000000000000000000002537afeb211ab20661ccd797030db90000000000000000000000000000008d6fd1fa3c9a7902ae33325a7481e6ead70000000000000000000000000000000000105d1d3ad744922019ce40015b731b000000000000000000000000000000653ae5b8b4e3bf838c8dad5f00baab715200000000000000000000000000000000002ca54cf76b2d0eabd4dfd4c2f269b7000000000000000000000000000000ed0fa724e64d0bb3e921d29010b254b52200000000000000000000000000000000002283dd0b6919a9b907efa0d1450c83000000000000000000000000000000247d9ae0da38579e4d8f9edbafee4b0e870000000000000000000000000000000000038619b10948cee4ab41f87863ea55000000000000000000000000000000c86cf7a365af37b59374be4838604fda5400000000000000000000000000000000000949507e05840a340738aa0059fca80000000000000000000000000000005b9a32f04b291a93fced33191d11eee64700000000000000000000000000000000001dbceafb63478c33422a6f7b27b729000000000000000000000000000000949df4a91191d0883796d0c17e02de6cfd00000000000000000000000000000000001617ac2fdecf6a50088e4f9e89e09c000000000000000000000000000000d46babede40775278eb51c34e5354e155f0000000000000000000000000000000000214599b8fdbaafc8ef1b5eda1d1294000000000000000000000000000000770087323d677abefbba29108b9a928c3300000000000000000000000000000000000806db3099dfbcd0b4b60d575268960000000000000000000000000000008cfc1070fca86e46a864c883af230b0f3b00000000000000000000000000000000000bf85e10e77edbfff9bb9c84b76ee700000000000000000000000000000015d43e3775742ef07d4d253ac41af7234a00000000000000000000000000000000000bb11cc6d7c81d0284afccb0bb848c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036a960d6f8e4ebd5c4e7619ef498b47415000000000000000000000000000000000003f8bc6c6f49222e71da7ef4f12f59000000000000000000000000000000da0e9ca5b0bc2a8b9f8dc55583a2f0607000000000000000000000000000000000002b34854ad662b37e0e76506e3eaf170000000000000000000000000000004eece60b91287df5941d8b6b85a1c163d4000000000000000000000000000000000025121f27276f7d6405b64dda2d13bb0000000000000000000000000000006b80ad59b5eaf076a2963328805fa2200f000000000000000000000000000000000018c545a23c08a38624afdc86a65d280000006b", - "aggregationObject": [ - "0x00000000000000000000000000000000000000000000000f3dac4a356c5fdd79", - "0x000000000000000000000000000000000000000000000001f8b97ff8c78f3f9f", - "0x00000000000000000000000000000000000000000000000cf72074e065bc22b3", - "0x0000000000000000000000000000000000000000000000000002dc10ffccda59", - "0x0000000000000000000000000000000000000000000000047bfb4dfb23cc889f", - "0x00000000000000000000000000000000000000000000000871e52274633f4bf7", - "0x0000000000000000000000000000000000000000000000013c49830a0ce95ff2", - "0x0000000000000000000000000000000000000000000000000002c5ab7c0bb98e", - "0x00000000000000000000000000000000000000000000000b0f322f57a86900ed", - "0x000000000000000000000000000000000000000000000002e496ababf56e3cd6", - "0x000000000000000000000000000000000000000000000005dd1141d5deb2050e", - "0x000000000000000000000000000000000000000000000000000268dc87f9458f", - "0x000000000000000000000000000000000000000000000003334a597cd9ec0a0e", - "0x00000000000000000000000000000000000000000000000645a57625996ab518", - "0x000000000000000000000000000000000000000000000006a2f7ffb16256c45b", - "0x00000000000000000000000000000000000000000000000000027ca8c331291b" - ] -} +{"proverId":"0x0000000000000000000000000000000000000000000000000000000000000051","vkTreeRoot":"0x07fb5e90182eefd8cf68a443ae3b87cd3c2d2a17f1fb1f1de931c4d4603b148f","block":"1e812b3df52749854266598c9ac051ad16abd2ccb23eb301552481adb298ef6500000009000000000000000000000000000000000000000000000000000000000000000200b46f4321fb295b47672e53d2df6674826a7dc26d0a8b47cd1f025ea64fc80100089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb30000009003533f0756b096c2128003467ad6694209acf0dddd7c23db4c2563b6482f7929000004800a61b1e1e6a51ae92c76aa05722659a7fb4dc14444cd377106b0b63353c7ace4000005001a0e7078785d1a251a9a18e64224ee1e9914920933b13287314ed9957de39db2000005000000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000066d6d5fc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000017db9c0a08fb2faa5f28f92d4ec55fabd85eaf856f362831a1d29850a8ef78791a1a44d40000000a0000000200000000000000000000000000000000000000000000000000000000000bec47b002039c7a317df8635cd038ce92519af7011d79bf518726cbabeedc559d38dc85b30fa6e781fa18d34a511f88df5c42237c69f3e0e24206daa534a3b4fd42a01e0a0204652cde1c31bf2d24fd90de0c1e39c9a6e3e2663bd494feb38b8c4223397ad41f8d6b83e24726b16d292e9f36315291f1b3a5a2b73f2ea33fa623ce83a8a60b00000000000000000000000000000000000000000000000000000000000000000408000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000000000000040c000004080000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6e5ce5a42578d9e7dfbceb8c9c5c9b716544833413702601a8868d800789eb470e2bff676f7605255d020812dc33a17b5d3ea19373c4bca05fee5bd5055cee503df935668b8418510a8139442696d8841641431cf38613c6024214c3fe38c9a6c81eb31d74451c8ac3ddbdb75b8fd9e4b0142ff5fbb1faae7b51e09bfde59406b6ef2785240977ebbcbf5f246343b302685ab28b80d7ee291749f1dda3f3acf42827011eab539dd456d8ff08dce7da737c89a608fbe403aef9b60783be8ef3c07d5ee2a453bff345d193a62e9d9a281b3a852080eebf3438e82a20027073667b9c1e26edf28a3f767d78674a1a4ebd6e52d664710c5e6c5e0fdb8da18f17c9706573b9fcad6364b1bcbc4d1a22ed457062f4c4e2580ae3337de89def5adf646903b02ebea7736f5359785b5744c1eed7d1c980b8c9a0c312244f02ab6ed6ec9f22e4998e0d48ccbeb7bcbe649cb7e798d57870b8ddc40fa16eaee6b2e6f418d1fa817816ddbd560dcd8571074b039442eccc4059696e39ad016dec14782e0624a6b057c159ad46224e9628c4095a0e09178531ea2be02be5777677bd988a32412f90ff4901963b5b9cc6d1b73111770cdab2889d62a1ca2f98520626d3e60af0000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000945878ef5b770cf2caefc1fd14531c5348feb5e4c59d3be7d0f0790a80db4bc00c77e5c5e0eca399cb43bca872036feb14e1f26d11e928313b4f3e6aad3b9484d9a8661d4196dab130afae0e3b4a41e37b3a0aa2c10380449faeca518049678a2303953a74cb9f76bf1edf1807c4f34f755b8f8fc24bd6555bab35f6d3f1b1a3d109c099a2c28da06b59bb2b77bb2a1d2db5134c8e455c05a8ea78d7ea1f3e3722282939fbcac8917eb3d0dc0f264598073c480208f1041874fe31f69abc25b594628b286db5a05b4f670ab9f4c58c99fdfdd3643ceffbf8f91c6b87f910a2b2eee0c4e07b168a4dfc057e4635d2973830ee28239d96646ab35ea24cc69faa08107e0d1b6b4accbc109531607f976cf22bb01f85a0597c87afe9428bda539b62f9191e4aa89cf0c0d032473216d8ba087699774c72a7970407bfe6bab05f9ef54bce3c1917f60c923e87e7f7d0789ad66d4bf82dcc11d53c43b888af1883f4d702485ad81d9d0930ef944f33168ea0c6287b018537239650f05b3ffa83000448f729fa47e00420b4088f18bed5690d4f3771e336e66d337038ea6980b32d7eaf387a3035b61eeda77cde0276787534e9ee7db5dfbd0d6d7d58b0fec992b389a40000022800000224000002201d0a6498e4f1fd6716f7167e2589451ddd0c03d259fd64e251496141f69fd286000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ecb5c0e9ffa674918a8deee9e0e38f9aedefe08087198e22c77eaaac36c228a7fbfbcb32a36a603485d90f169dc5d213cd76e0e96500e4809c3a037e0709a964ee296022c4ea63174ad7faf26bbed544ab1cbbd48b88b1573207d40286463e74bf8d8dec263466062c0062629a6adddf33716876a8ec4661d26952b9510ef4957600e30dc12f706bc583d67ff75db2a84cb6eadc65bbc3e9e6eb09d9462d3d83ba791deb54c31d4456b0fc18e1622eff3245ccb187e0ef311ab7dec8d856090bb9beecd088b1e3a2dcd1b780f9a3a84ac36fd54d7977fe2effbc21beb1f192c048fb9e4d2b2b2628ec86a2496d3d5b998d86452db745d5f64d68cf7cbe5c2c0552862196341d91a35f8ffadeb57f0d2c463b26c11669a7facb82d2f71b5c8d0b5ee8b8b60b15a5fe55beafdb5000dd2647b38c90759b0bca00cee614a60ad01233eaeff9d08e8afe14ef5d8b5a1da23905c4cf2d4d1cd5a9c16beb377e50ba7bd0b1d9e5bc3319d0aa20d4961aa79a35f2c8c39eb063fe221298951a03815107730d6377e91b8d81d107e28f487ead6b6b8a52564ae9892b2c21ba0ecae53ec7bb4c45db0c0f3715efbf5d485f88a54300fdf576bc99a22beea6c9b790ce7cd000000040000000000000000000000000000000000000000000000000000000000000000000bef545a000114b7af4d07a22682d559ededc223bf2f9a785efa3aa8261a284985f65ebd0d2900021a074b13bbd403b409f06afaaa14bb1ffc718ec250d9f488d6019a42eec9463700000000000000000000000000000000000000000000000000000000000013882a73706978ef632308b80c8f27f8f234bcc9680f84fbe714e0b7bfbfbebdc5b9000000000000000000000000000000000000000000000000000000000000138800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000400000000000000080000000000000000","proof":"00003e84000001f40000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000006b00000000000000000000000000000000000000000000000000000000000000011e812b3df52749854266598c9ac051ad16abd2ccb23eb301552481adb298ef65000000000000000000000000000000000000000000000000000000000000000908fb2faa5f28f92d4ec55fabd85eaf856f362831a1d29850a8ef78791a1a44d4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000572c74822c20edab7f32c1eaa6cc5d6fbf35e2642f613fbad1fe675cb65b2490000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000066d6d5fc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000066d6d5fc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017db9c0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007fb5e90182eefd8cf68a443ae3b87cd3c2d2a17f1fb1f1de931c4d4603b148f000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000667141da186fa2ee600000000000000000000000000000000000000000000000b604b01ef8f0d0cbf000000000000000000000000000000000000000000000004aa7261e91c2591c700000000000000000000000000000000000000000000000000022169bf4283ad00000000000000000000000000000000000000000000000da42aad5197c6fae700000000000000000000000000000000000000000000000df94cfc0977edd5b2000000000000000000000000000000000000000000000007c612055fec21ae22000000000000000000000000000000000000000000000000000230175557a7ad000000000000000000000000000000000000000000000001de0f097127c3548c00000000000000000000000000000000000000000000000c907f4f00a40d52dc000000000000000000000000000000000000000000000008101d8c6bd18462f100000000000000000000000000000000000000000000000000023015745a48e5000000000000000000000000000000000000000000000007a8046de88646c85d000000000000000000000000000000000000000000000007870e5c81c68314f700000000000000000000000000000000000000000000000e0abb05f5ec41457d00000000000000000000000000000000000000000000000000008a1d3a84de8a0000000000000000000000000000001f23fafa063b254a8c938bf73d0c2bb7f500000000000000000000000000000000002160241ec6448ef9b3dbc3490425700000000000000000000000000000003cb7ea1f50b4ab963661fcf722095ca6b500000000000000000000000000000000001d0f698346235ecefa3c5849d39f3800000000000000000000000000000008b2df2fcee85b292d3cc2497162b8e5460000000000000000000000000000000000041ced241611aa8b30b8768e7ee9ef000000000000000000000000000000f15d304041c9c10e532071fe58f28531110000000000000000000000000000000000040f11d5e49bf7151d589fce774d7c000000000000000000000000000000e8a3a26d6d023d9318c786305baa4736cf00000000000000000000000000000000000f65d2f8f9b4ece133c12f49c84b07000000000000000000000000000000cdf18fabfdb01dfe2c2cb4eb389e4b9d3800000000000000000000000000000000001443e085441ae71d27f780b0a43356000000000000000000000000000000c9df6dad369f2129e064b2164736947c4c0000000000000000000000000000000000044487a9cc727150abac573a3facee000000000000000000000000000000ba41d8227d0ec33f990c2e701bb5dab629000000000000000000000000000000000005886f8ffc752fc94a017a75a45fa300000000000000000000000000000049a4526224a61bb696cb36aae680434960000000000000000000000000000000000019561ec46d7cccb45c2b02aa5404d500000000000000000000000000000067cd0afd4e592538a5c418cfed003c0fe700000000000000000000000000000000001089aa72005765293614998b79cbda000000000000000000000000000000f2c1de9da0c658db79e6d5c56e83f4633f00000000000000000000000000000000001c93bd0bf50602d11738654de93779000000000000000000000000000000a1af1e36535d721316b00bba52ae988d6e00000000000000000000000000000000000b3a26657a58a697c05f01f4cee6a4000000000000000000000000000000151fea819284536d2b1a26c107d5c3a37e0000000000000000000000000000000000272cf9cdfada33e3e1616af9c5c8f40000000000000000000000000000006e0af12b8c79cc228fed340d6e90d5361900000000000000000000000000000000000b12a3c641fc797bab484aa549d6fb00000000000000000000000000000071dd80894db819246eeebcceed241778d8000000000000000000000000000000000024b77cbba26f503a8ab08989fc5972000000000000000000000000000000fd2372b33aaaccca5ada4259ccbde96ab600000000000000000000000000000000002375822e4a0cfe03e2c010aa3235ed10d078928845174878bc4971f90665fbef8cb9c0b7e8a5f9e9cb7615207dbf0d1f93d5e058ec88e13f93fc44887af26138a72e87c1d0ca975a167f7ecf8240f41fd39d410139af76ea93183e79aa803013a3149f0dcfb4615067a472154702332f6b6b9788e53c87b6e355bc71424f690ac4c70991a6f86b4addf727c5d90da21161e2a620a2af2dc7e07958ed6c0feb927b9b8e6d6e613fa98a20c8301f7029156b900b3be715336d257db43790fb19267e13ca675a2be47c0f9bc6275e76e1042c4f6249798278811a0bdd2b1c2294f067c1385040115303354538f9e497302e2968168f9c1e433eed468b5a5f3fe3574bcb65a90f84e2e90e804bc6d97bf40cc360bf406f6d0646eaa5d0ac510ea813147a1ddfdb7cdf65aac20c1228d73519e41abc9f3a9ae6f7a084714d2ed2d8183b43bd31b3d306c044c9dd1d39dc9e23af282f2d5352842738a29ff692411c6fa4f0756a6883c77939eff2a6548d7d10f1e082a3da4cde73fc119effcf5ded0a8f3b049a1f27569704d81e9929e3fd1579d400df48d4f0bea505cb9da1d4b18ed3182e258ee26f49e11c999ae1209c1d7f38654de7fc5f3cded01fa4a0d2f936267ac6ea04575bb13866932ee885390c104ba28ceb69874760886c9fb10b0dcc2055b356816acc672a71b83ec1f7762c4eb3703b2e910473c883c29ff3aaf276b252540dad8be93f828d4d1715aac31946fadb92b11283dd711be79967ad4f6404353510aebf5878acb2abbc84825528f9d482c445ab8a0c984654c2f3c39e8fb1035903dbd0c37723bb132402aa811bf5fc45985bf9fa715bb4c5f954cc50e78044450c737c0644745a8471a5e9811c4407ab143604db0ee21bf40328ff66ed6ccae51587c93ae03b4b9429c8172313fb7fabd1ee7e91e001e15f0bb25bf31283dc26f7d4615a1fa44c652d2fba3509ed3378489dd162016e6f2f905198b2416d2659d81f85b8864fdac20329f99904a1b00e39dd8bd0279c402f50776c0e5cf53ac6fe3a93dfd54da0bb22735e5313924eaff1067a2f31717cc12d08d351125d65f8d7b484afa715adb441f2125223334502d82e9ceeb0810569b3ad878ee8f618a5e967ebd9e068cf706ea9dcb128112cf143db03f332590540782b76ca2767111aacfadf9b81b1f77daf22728b207c825bd6df289460c5bf8f6203690635e1f2b01011352faf1b0e588489ec02240e33fe6f724d2e0ce3d79188f7a21c59bbab5d8b96c4c1b2beff121132c822283ff4f5e4940ff348ec92f7852e47572674cf532f5e475b2e1eea45505c93482677dfb213ed49d78f2945a77c5142ea7389f4c2cc19a109738d24717352eb3c0ede492407c3c6c41d11d3abc0bc8beda73359de14776c3c377572402389375b28334e116c628b033dfe5f5c85bb38461783d15e9f2110ca46ce6cc8ae7e213f2e325562375730aff4553280ad1c2c07695df662fedf961d52ccc4fae1edfe3a2acee76a358e36ad5b0effc188d069f8c381532cfa3bb3b01e4c494437165f8630106baf1d0d901811be719170898ecd81daaffdd423cbcd794abed6685f534b1d98de26f6ec1e8b639e47345a1b3b61e97c1dbd0cefe716ddc408974a8930cf1f2c00987e87951f8210b6ba84bb3440d7ecd36571c5670164f8060da3c665ed234f87f46164bdbe18fb36b0ddbcbff33ed93411e1e04aad2b2c6f477e91b4982fc446a067ceb109fca883a2b440577033018671b359ec3ca52f43de0ca0063a1eb769869f9f49fcd99e54db0e2154f6f986d25e4261e7e18eafaca02cfd7f242a821cbaedabd0a2de67f47951f85e445aed3a6c547e5cc9fc17f1856a4d3e8a2d673b81a74aa6392d2b34217a903550c6255fa4bc9b1e8d866eb079dc08776f2103f2fcd706df54116b74f66ad52a03188ec932dddc946419d9ff50fb4f18e716987a75c0c2190334ea7a824dfedcc64271d5ad717f06235e407e6499d362e600e02f05917fbb640884d1dde73e5b519830b0f15056f8395f800ffafee2128a03ae1f88e39293df5ec54655ba3e404ea522d81616019394e3bec843eb9a52b61a9c88edd13ae4207ad66d1a38baf46d6f5ddfd004e17d567a28f3a80bfc49b02f865be4360e4e15acd1e6e2fcb3e19dd8f6d462e2110169e35f4c33915ba42100b6ed0231a5483cb7f3d4f397a1d1af9e02a0d0778fb7eb9a765d7b432515b11b7ade835b451150a8f3976a6fdda92f9f6c1f838456769b9bd5ad33bbc641842153756338aa8ccb8c5a1dbb53a7161a3e2590871fab30aa68b3b8d23bff7ab527432369c72a3f1ee7b6fcb383a4128a7c097d29032e5142769237ece97e9f040b97558610eedb00574122cda08a43f1ba2af4cee462d6774e96ba87abd2744f293a3f6d171b4d1d62eb6855f8b8bcb73327998436bf3b4062e0522e81badad40f51880b43e04fc43488fb59d2c3e1f98c09272c2ef5cc8a35e6fedcad99cab313ef8eaa93afc3aacaf6b055637680a163c8ef3da49c7d2a3804b61690f1381f20d30af776051135f1e1467f87bc6898ee77715a6bd106f506ff2d8717b65f9a00881b5239007af56b833d7372bd2fdb07b7632bc1ea62e68a1dccfb2ea680f62bd5eee1e52c1165c480df83341f06c3683d9a07a4b51c9da8a7226820ad24ae160b6f5cc23749d593175d13ff0a313776ee26f07b301de69297600053f6e83c10e844b2d3981e581f3c808a9d18191087f526e1526bfaec04a5c405c05bd59d143d1da437aa44baf1eb5e8adc9baf39685fdc8a6e9637542a59004837954f5509882f9f56aae4aadddbd0a75407916f078ea3e52289c4498317ea77205946660f822bbd7fb2d903ab7a9be6a8bfb8219dc010cabb58da0ef69356d593e7301120603449292c7f6568bfd52c353e984a47829e1788eed3bace4f608c356bcc9101e0d119993b0f409cdee91e88472193da68809d9759d9c045633e1b1a696d0e29b8c5b666512554c931983d005148a5c0d35db645ed2b94f33c99fbe4e441462846f5eef01b770c6d5cdc484c4f9d6000a4237170982951daddbd998d2d4c25238f4392b6448cc3527e58567126c22a54b0770c0d3e16e925aef4a2dcd8d7e02d27ef72b946edea0fbf09256d4aeced9edfd37a75bb377a2ed8579b82f3a0b90fc92ba55a6abef345a93de418106ab9125f4ccb575e1e6fbac17cd05ca906a907f9778decc92b43dc2b9e02e1785720b825f9eb1bb8ed7aacd782d54ea643890f81a5ef3cfc1dbf61d853b88d957f9c727a2dcef200d434fad50c611697b5250fe55590819d6c0113f1ac0ecf0f06f01f17d509cc382d2aea8b8fc45a0a01f613a8d9bbe5d606bb3eb7903ce708efa3431a1aff7fce556daeb80c31a8d46d510b970198f084b08f4bab8f4e46e4bacc42da01e29e52a02322790773ab221aaa1b695e01d1b872dc0c499ce55d4675ebfd3a74a2611d55a9d3c19ceb4721788a1e0f5a77b4b5d2d2402d01337b270d2955d12f67a35df4b185ebb059540dd3fc0ee0f1756261836a278c1b420d1fae2091f0aa54562b85366e46952383671af40df80330e73fb1bb83361e1140ef3ad0c7a7f4b0fe7aecfc6301e84f450c65c21aa7983690d8ad510e9af1beb908d746f0013c8077b6bcd8186a4f9095ac4ccc2c7b5274b4f43ed06f8acf8e9ef04e4a8dbeb929b6dcd673526040c5ae58ab9418ba589f9c63410d1ecd39597b87a60b5ba7c722dd912f0c1d3f15f3ec08f2dc19967a9939d87964bf7b538a064c56afb08758b833b02da2c60fdcce9d91b58427616ea25a1102ce23a62c557944dcfeae2e3f63b8e1db13ce6db5ee56d38978301a80a0513a08387c9faef5243bc9f04761491b3b5c76b301f7a4b44224baf60ff2a1eb1af30f7c3f52f4f5316cb5471cd0fa74900c7b5e4c4b7ad61f0d77d208043494a7918b14a322fd401c14b9f0e0d8c5a76d21fbd756b2c096f5b196a20de1cce6847086f7c292236372303508aeaa1234a92a9d716c43f24128bcdfc519a424b9388429bbe75fbee7a9ed91286ea685801494e9a10218f9dd44c089da25e96ab3ba96e6ea6a9b908b8bdd5d9505b58fb14d979e5ee5559da9e38863f71a19c52af38f3a71c2d1b7b0c1e5a19c469bdf00dbe3f804fe39fbc42ab0951d206e0a9eaef931ae904b6b33ca40dba4e7ee23f6ab6ca6a76668e9c82f81a1ff1956d37364d37dc6faf37796304f89356e78f6882e54c11757018c18d4da1fd21a95a00c9fc2247a4303f9f1df4821d8f037308065ec120ea4c284ec9d6159df0d42b61c4b7595c2431fede96f22a5548291179523622c23ba5eaf08f08c6fb12e90859674c1b630f11c559820f8b65a32cd1ad78435f22dd0ae820c87544e790a1eb28e3ae17775fd8e19ad33e73d45a749347da52d1b7985546ab4dab457af250b9a086fcd04e8af09aa729725432a45ea67c39ab12788739aaa50182ee15830046b60fcc1c0c23a6a565fcd0dc7467f335c2f7a041537d5af1b7aab6ae0f821a22fb88a07117842f8d984c6ef6ff8326921794069c6d84a517acbd21b8b752d8331a7b0c060bd7b8f9ae7bf04cbb5cfc025dd1bb510c2e2f66f6451ec066d29973627aa69efe26e27c60bdd2aa5a0203d1daa2f2cb4fc426a031f80641d661231039abe534aa1d5cf64188e24d024a20566fe554e5a7c20197a8e3fe1ba2b129725cec1bdb56e78adde67262dcecb4b2ef7b46b68a64cff90f5aa9275a3272767ca462c15008124c8191c5327337832626d3fb676b58b7b25f0850702099e22290562e8e1d7eb08c0b5a70c9d2d8735c5e6f281af8b13468637116bec694a2daa79bc3fd180aaafbd7a6fbfa68b7978405558768c417481e4e0259117e95311e4c712eaa4f74d0e5ca6b6c3ce907244dba8c475705b12abdb1e8f6c45a9b10e031c7b7afb401e5acc66743bc10a34b59ff3a14baa5d1cdd56283c85b8be6a18cf3ed8d6888ec791addd0dfe10227d05d14f791ac4f9c04a550da132e39c2e08bdefce980dc6564e81252bb2b82aff55cd4d804c8fd5095c78b57819dc784a03b3b1f4ed79dbdbdfcad18db98779d44f2ec06d8f30de8e4efaa0d658ada7ca0503221e7ce843d6b3ab578075e340ac59db9b9eb148f4f085e838aad90139c104b84bb5a480b9d5af8efa5d6ea0476febeb2670b840608e5d91c3f4a6993767230cca072d23fd79f8d724fa71b878eb1905949c3f8aa6e04a91c1b9450303bd04170662b7a4d3594089a687d0f1e779d103167c1ff8f6443038b6959d8979860c8261f3311e1bf9e7b0c987c399dbed455ac22b5105ab1463caeac74b56b5642ad7241b21a547044079cce998b95e9b9ca56306b15cbf156cf0d294d42bd0fc258b8d45093d0457f467f702b3ff0c9be6aec6cccf5527dc00e466ba3c86687a2a3ebd80a90caed639c19b143170f383c55591ac206e5762ce6ef51ab0562a501f060480cedcfdaf1989fb04fabb4ca541e97c6f6dd4857e39d2ae7dbec100d908b923876c5bd193cc1c8f201da399dec5b5f61d336e81b1170118fbb66bcb160347028725e585e1fdd40e70961fdc9c7fb7cb34ca084aace4372a1f8903cda62c9391463411481770bce5d35b891b57b6657aaf564b55433a2622f51b452efe16860f3225d7ae19e372742fb84659be3e3f37c006305264599f835c0649b2f1052e6a41fd89dfa4726ac74d9244213ed2e0d0fc04be42a8452d75b45b42e42d0677daa913e4acdb6b00aa9cb0bdd698ce6c36259f57c36d5d2795797af649da2ae9f650ee64b782e3aa973a34cd6ce81ea0dcc56d36f53fed6db245e11ca0192d3ef69b4dddb040a25ac3626c2223f06677876e4d1d957c438a82b86ff7e5711bf14138bc4d00dd92f1ec7c66ae191f6cfe7ade089b7c23c82b3e8b80b336b414525731f3a3e0625d5b3d7f74bfcb82aefdbf5342a6b93685ac01e35617287f0f7f75479ba5753ee1484bd7cfe0568053e41f5db512d3c45c0833c12f73df4826797ba3e1c3244e0cd3a6f45f32ce3136088a01b186e33ca7f83cffcbc8f60b154b1e2127f7795ddb056d00b07762a65fe6b4dc70d8b7d7c2e3c87ab81193a3190cc1c42be59e868f650289b8ef15a5f499c9f4b03c4c45c4dd85452992a7e21513a661810eedc3ad332c50a5d9e01b215edaae5067e818f2eb2aa3e1a01f5410d3c4c15834b5ee5695bc3a8494e2180d0f5b2b66cf190ccda60dc0c270a682061b6f5b1c31ede459213b05b52583e59b3b26d2bad6da5f5f01882c56671b07034c9322ea72fb206caf70210d60cc0b8005f9e4e56b9b3bfe8bd48d42e944d11f56ef469176f0b4fa60a6e014c691deec1fdb0db6b76282f87f53dc5c2f5c64301d16dab5d730bab5de5360513f3591dd971178bd50bbde84eb62f27c9c0623047e36fd1607ddbfca8119d550bef9009a0f0c34e53f06e41c4550b8ed79a73b1bb265f84d3a366be954b8d53aaee05fab22fd6f5ed6faf793f2dc55d207fd94177b86f826190de68845795a7c6d11b88bd0bf2737bfe7542c060fa5d11f4a511f9d9e5374a3bdff6673fc46870414c3ce9b6c1d8b1dd6ebcee36cd0adbae05f10f8095807d8df48aff33485cf6b96a54e1df8f33064f3dd195d917197b65e231ca39d74c4a4d4a0d1646c41c651804ed80352f036cf83377f894642fb8475ec2fff1beb238076c0335237eddc67be6b8770f983a12108ab97eb8c6728424b78153858e8d23e7e458aa39d46da9e3feebf4888b3dec4cb65daf9444a0d889b402b5fd3d647985b05e62c203f8604074b23700be060ca80da87fefa8a389a9e060409771babfcf09e8daa783a185a43d463cf8f6f43d8c9820d5576d9ad2ac56b2293cdc311f1e09df5e9aec08553ec711d60b780e843f4b6e1ac8eaafa5b324b2bcdbc0b76162335e026b6a374aadcc096536e15c9ff2bd4d47cd7a280eb1b1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022df678d7df39449a632145842a2c9d2a84b2a8dee09477f98b6b082383d33ae1cb025706e0d92b6c5624f124aa3c7a06e77f6b093aed56ac0af4683e151709126d32131fa471c20acf40b6a0d66b008965e0676766cae62bb3b178438c100f209fc4fad21885046d27d03b566973d955f0265dd4711f58217d4644d6d379597227deff490125e37b303d0aa18d30977d448f89b23e1bf025a1ef4ad8375c454272f4a2b56e9090c13c27a0a6d4cef75a1ef8124545d2613af50604e78cee90216a65b031bd614613a0cd2a02a7621f6b3244e8e7da8b4a57e332a40be66d0db2d8c09e46ab777fc1f9523a657c9e886921c04c8c19b2d0c31255d36a6c9bf08247e8698791fc09ba3b9881a1745ae436b872eb92afac4a29005b340b1fd9e1217663070115f7c4b913c2e528b05e789930c42ec239731d34df59b3895ec36583030e3b66ff46aa33e4247036fa8065520544a6cfe6e01e0fc67eeadb84b462f19bc70fa4d5722c5f75656059b2c4e970ed7789afddb529f3fd6c185ba3efbb91e635c57d8ffd4c2f025169d426d99a6e0d8fb7550e5d7951b9b32ab15c17b590d88a842987278149be2e3a58d2126690fa9c764f6f0d4ec45f150e213be727817078f06b940e306d937eb03b80d8afec9eb5fad3e130739e8445ab24c8caad607b735f4b8dcb136683d7705507f5d6c12127e7b6c5a47ff1ebeabe8f748443710290695a88dd754ed720e3a9ec51703e85b18884fe7a3adda56421c95daaf6c06215bb0930b2369a3d3864a9eb86f51d7cdac587b916a650c7929f6380885d61cc5119679d4fc7e0465812edf339cf06bc38924c3141778ecbdb4a6aec7144815d347c81da3a5b14aba68f405bdf5a7d22a5e9ca8c877ae89cfde659a2eb85a1a261b3416e065370c87fe5472c133480011d8e69c8202451526e997fbcfeb902774a429176477a5030bbc0b1e1caed5338dde08737dbb9dbf0023dc1999a4f104f5d46cb776b641b2f829dde96c027cf8ec6447b001f517ca76acd240cddbbb19b3664dc72461e9a6e30f74121ea7b1afe2a583c43741474a716da6646e0a482c00a67b14e27ab1c832659d6804e698c227d65dfb6f81befc77cd5afe082ae20bf91fa9b2571261bf95227c60225fc1303dbd2cf6fdfbfcf2316aa22dae4f2701214afecbdb7e211538437ef6b4a352ad80633f89e29ad325e64f5fab8dc1162cfa4c0a55f6604c0752d5561fdaaaecbcad1a686081751b17944bcd6869816e1d45e1ab0bdc33786ccabda820aa0581cf65250c6cdad4844d29267bed208cb61607585280ef38ef99678a3418f9d13d79145aed7c3adbc7bd10321ab62d834a0351ed8637e5df4ae177b9131df7adeb3db98ed14e8ca7efbf1166cf2eb38c682330e1a1161091c2cd0b855467ab81c2e0cede50c25b9e65988708c2657cee082d2e8596ba2b2d8fa2db3a0a7c4cc1cb9526c11b6074b6858ec8adfcef8a41281ab2d5d7e7a5ca198ec9c64e6f0f2bb46baa4f40171b2487a50bcc50d56336940f5761ac402b1011ad1317904c78ed2e5c6ee3ec7d4556c28a136a3cf564d5ff1e92e5b18fb3ad4478d361be5fc85a1b815a30366160929569b5eae1b1d0a5cb1f19cbf922326dcaaa3616c481ab884b5fee576b39ff8ce37c538ae7543c3ff9045f72adbcd628d77c21cb9c8a5c26c3cdd6b4f87330c4bc640200d891cecda008860e8bf30d0ed0f4b99acfa187591a783b32191168afa017c924418c94c6b5150d737d8aadf8cb45dc2a0516dd9b95e2db4c06294d8d401e1e25ea80fc64e21f868e8fe90ab941a7ea8b57ffa7089a20d21ec42248d10e97110b4f08db6d1f02107457943693c74c4a7981cec70e624e814346ebff4c6e5d12b25c6d4f00c8000000000000000000000000000000287597af45536cb902ebf2d8d55e98a7e00000000000000000000000000000000000299ed570a926fba9ff15b1f026bf7e000000000000000000000000000000810d513449c91bc102818ebc44c20b43bf000000000000000000000000000000000001567389a8a8b2cfa6964da0e2d2a60000000000000000000000000000007bc08e5ff56bad55e91418536927a70ea50000000000000000000000000000000000002ebfae7ff41bd824e770395c2adf000000000000000000000000000000ccadaa8cac51b2775b4c583f954a3cd6cf000000000000000000000000000000000017f9742f19e461f1d74cd619bdbfb2000000000000000000000000000000e460e8ad60368c2420c0922e9df9ea38780000000000000000000000000000000000239df1eb63f39b379751ff17a84b18000000000000000000000000000000893e6d1649e77556f5ac73a6795f137197000000000000000000000000000000000029623c02d3c9addf874b23dc361b170000000000000000000000000000006a18a62ca37ac001c28c06476587c300790000000000000000000000000000000000236dc0b6e7fb8eb4d34b8db08984a1000000000000000000000000000000ea32e216fb7221a7b6f89d939f1a9d28d10000000000000000000000000000000000225b03864da60029d50701c264e624000000000000000000000000000000dd50e1dd547d444a9a4c9d209996d5e48400000000000000000000000000000000002b7ab5c36299c38c2c253329d8cea00000000000000000000000000000000cb9c800a4030940e66b0601a2510d2e5400000000000000000000000000000000002454af356b574f1933360793d518d30000000000000000000000000000006f93b6cccea3e5eef698b02a98ecc7a40a00000000000000000000000000000000002bac9db36de89e4e23b4dc38860f3800000000000000000000000000000061312e98fe6ff139acdb60888f8fc10b7f00000000000000000000000000000000002e87e2375017424cec063b6c3d36be000000000000000000000000000000e0df7f9e61b2eccea530b0390e9cc8b0a10000000000000000000000000000000000283b0d87bd82729f95c876767ba5210000000000000000000000000000006ccefa24d31abd3401ca53d5869a1a5d740000000000000000000000000000000000285db32d93396988f619dc365fa8e9000000000000000000000000000000444ba41c9eacb5ca7ecefa5b8b802d932500000000000000000000000000000000001a151b3210f6cf8694178825ffc6c500000000000000000000000000000050ccd2d83b5c62cdbde3a07f279984332800000000000000000000000000000000002dc59d99b1eced92ddddf155f11aff00000000000000000000000000000031de0724d942d75ad3d33313fe7031ae4300000000000000000000000000000000001510846c331f20b19cfaf99f97be9b00000000000000000000000000000079717742da29558faaebf3d15a0993325500000000000000000000000000000000002b82be8c0f0376dbd63daf1cd63158000000000000000000000000000000517a730d243357b21b3abf461cfc30a0ce00000000000000000000000000000000002e0e8bbaa223de23104e943705711a000000000000000000000000000000a98cc409fb1befb1fc984f73e41bf33757000000000000000000000000000000000021c385445c7209e4778ae6121b580c000000000000000000000000000000ab3d39ea2d24c26c1dd8d710aa3660e1f90000000000000000000000000000000000194aa61645beea76fd9f74433536560000000000000000000000000000000b1e0e06dfa8f2e957b294453b35e26de20000000000000000000000000000000000218f8069318cd9e406a90b6a782de7000000000000000000000000000000da837b8c56a0726eba730e17128c41f0b600000000000000000000000000000000000ebfd5fa7e9fe0d206eb8ed0c8aee6000000000000000000000000000000e3256a764f112b566e757b5e922fc433970000000000000000000000000000000000077cc1f0541bde33cd5e045b4d612e000000000000000000000000000000d7e1d0a569e9a3f54d3b9208004573b4480000000000000000000000000000000000107e5d65d9e6401793f384e3303f470000000000000000000000000000005c34c685fa73d0e612feff823019abf0c4000000000000000000000000000000000003af2bc76ff72c42f94baeafffc702000000000000000000000000000000d24a100002ca9d950a6d8d62c97472c6e8000000000000000000000000000000000016ea69f12cc1558e1c49ff610cfb85000000000000000000000000000000a292bdb84780232874310712faa2855a9000000000000000000000000000000000001a60176cc2f626a221609fa58343c500000000000000000000000000000065ab20d36a6edbb3635ae663f3e21e927a000000000000000000000000000000000007ccc956c13881a5ea506632ec290e00000000000000000000000000000072445a3cd121cce158639530e240d8fa3f000000000000000000000000000000000022f3f92c232dcf1c9c954f738700b90000000000000000000000000000000435a68a7adedc247edcb52389dfd70f4200000000000000000000000000000000001049a677170d1701ce16d9d6d58d2b0000000000000000000000000000000dccac93e31eaabe5d987dd3b70cf8fc2a00000000000000000000000000000000000a8076a640283be7a35acd9aaf9982000000000000000000000000000000a54bdbe26bec3cdbf46842ce86a4e0060600000000000000000000000000000000000f9d61d2c19166dd8c05f7dde542af000000000000000000000000000000842f827c933b85715050daee11dc7e00d0000000000000000000000000000000000007caa32a2b8d7a29211bf3519a84a1000000000000000000000000000000a77dd6fcdca4bf8e34421a52ed373a055a00000000000000000000000000000000001aa66599fe63272e2e39974ac44cf4000000000000000000000000000000f86dee0dc5993eda73e937732b844fad010000000000000000000000000000000000248046bb4258f3ec8042a1ce81f88a000000000000000000000000000000b8bfa0ffa025c34a880b56c9fcb0dc6d030000000000000000000000000000000000207bc01c1d93533f447b7ea4a631ac0000000000000000000000000000008c9c301ca78956e7398c1d56f0b3af2961000000000000000000000000000000000014bd6760d3d8e76c3471304d9e97130000000000000000000000000000007d2529c096c503b6fb6775eead5b7b63cb00000000000000000000000000000000002b6cd83a5eb3c0f478967410c13b5400000000000000000000000000000066290ec2d468c27172c77a30ae67cbeb460000000000000000000000000000000000192233b74f19e7c9d09774a461949c000000000000000000000000000000bef4315fbdc5c11b64e690590000fd639700000000000000000000000000000000001d2a497bcb2ebcdb792bb5b294dff5000000000000000000000000000000e3e8f17a00e241324f2e67fa86e65899f60000000000000000000000000000000000043d63eff2ab63ba64ef387552d7730000000000000000000000000000001fc99b5f4bbcffa61b42ad8e771bcb105900000000000000000000000000000000002e0b45b0fe21eea53131b8cab8bd9800000000000000000000000000000032374367a08c0354b55b4991571d09a57c000000000000000000000000000000000028fe0ae3d71303390266cf0c27aa700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005312bea3ca85fc921dca0b5e44614872680000000000000000000000000000000000123dfc33b59282789c9cb090c827a900000000000000000000000000000024a9e8c02433f76ea44b6827b0093ccc9f000000000000000000000000000000000014ea46e331036de9a4caea00daec29000000000000000000000000000000d6e16003d17f7c871ee46a90b9f0c1ab7c00000000000000000000000000000000001067dbc835f593cd06985bed789d2100000000000000000000000000000046a2d0b3d95f5f96aa57be679de0957be6000000000000000000000000000000000002c142dceccd7e356679912a654c3d0000006b","aggregationObject":["0x00000000000000000000000000000000000000000000000667141da186fa2ee6","0x00000000000000000000000000000000000000000000000b604b01ef8f0d0cbf","0x000000000000000000000000000000000000000000000004aa7261e91c2591c7","0x00000000000000000000000000000000000000000000000000022169bf4283ad","0x00000000000000000000000000000000000000000000000da42aad5197c6fae7","0x00000000000000000000000000000000000000000000000df94cfc0977edd5b2","0x000000000000000000000000000000000000000000000007c612055fec21ae22","0x000000000000000000000000000000000000000000000000000230175557a7ad","0x000000000000000000000000000000000000000000000001de0f097127c3548c","0x00000000000000000000000000000000000000000000000c907f4f00a40d52dc","0x000000000000000000000000000000000000000000000008101d8c6bd18462f1","0x00000000000000000000000000000000000000000000000000023015745a48e5","0x000000000000000000000000000000000000000000000007a8046de88646c85d","0x000000000000000000000000000000000000000000000007870e5c81c68314f7","0x00000000000000000000000000000000000000000000000e0abb05f5ec41457d","0x00000000000000000000000000000000000000000000000000008a1d3a84de8a"]} \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 150396a60dd9..cb1105b47159 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -129,6 +129,7 @@ import { type TransientDataIndexHint, TxContext, type TxRequest, + VERIFICATION_KEY_LENGTH_IN_FIELDS, type VerificationKeyAsFields, } from '@aztec/circuits.js'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; @@ -246,6 +247,7 @@ import type { TransientDataIndexHint as TransientDataIndexHintNoir, TxContext as TxContextNoir, TxRequest as TxRequestNoir, + VerificationKey as VerificationKeyNoir, } from './types/index.js'; /* eslint-disable camelcase */ @@ -1543,9 +1545,12 @@ export function mapKernelDataToNoir(kernelData: KernelData): KernelDataNoir { }; } -export function mapVerificationKeyToNoir(key: VerificationKeyAsFields) { +export function mapVerificationKeyToNoir(key: VerificationKeyAsFields): VerificationKeyNoir { + if (key.key.length !== VERIFICATION_KEY_LENGTH_IN_FIELDS) { + throw new Error(`Expected ${VERIFICATION_KEY_LENGTH_IN_FIELDS} fields, got ${key.key.length}`); + } return { - key: mapTuple(key.key, mapFieldToNoir), + key: mapTuple(key.key as Tuple, mapFieldToNoir), hash: mapFieldToNoir(key.hash), }; } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 45490c6332f8..9d1a0e7957c3 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -508,6 +508,8 @@ export class ProvingOrchestrator implements BlockProver { }; pushTestData('blockResults', { + proverId: this.proverId.toString(), + vkTreeRoot: getVKTreeRoot().toString(), block: l2Block.toString(), proof: this.provingState.finalProof.toString(), aggregationObject: blockResult.aggregationObject.map(x => x.toString()), @@ -1157,9 +1159,10 @@ export class ProvingOrchestrator implements BlockProver { } function extractAggregationObject(proof: Proof, numPublicInputs: number): Fr[] { + // TODO (alexg) fix this const buffer = proof.buffer.subarray( - Fr.SIZE_IN_BYTES * (numPublicInputs - AGGREGATION_OBJECT_LENGTH), - Fr.SIZE_IN_BYTES * numPublicInputs, + 4 + Fr.SIZE_IN_BYTES * (3 + numPublicInputs - AGGREGATION_OBJECT_LENGTH), + 4 + Fr.SIZE_IN_BYTES * (3 + numPublicInputs), ); // TODO(#7159): Remove the following workaround if (buffer.length === 0) { diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 56cea10eb581..d71d4ed00f09 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -284,6 +284,7 @@ __metadata: dependencies: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" + "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/noir-protocol-circuits-types": "workspace:^" "@aztec/simulator": "workspace:^" @@ -303,6 +304,7 @@ __metadata: ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 + viem: ^2.7.15 bin: bb-cli: ./dest/bb/index.js languageName: unknown