diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 499fbf035d8..f95b741d0a5 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -1285,7 +1285,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; } @@ -1477,12 +1477,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"); @@ -1507,6 +1507,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 0098093957e..07be9672103 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 37030d36b12..3fc9ae17d27 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 ca26dabb535..676882d8819 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 d48195e6b00..22c4b5e9393 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 00000000000..8c13ed14475 --- /dev/null +++ b/yarn-project/bb-prover/src/honk.ts @@ -0,0 +1,18 @@ +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); +} 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 885eb37007a..52b26ba4535 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 2c38db4869e..03a2bfa7929 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, 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 >(); @@ -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,13 @@ 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); 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 b5f4bacb1fa..908e49b5aee 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 8cfc2688de1..55b16f1b846 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 e0e59ed584c..77c9c6ff999 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 1ccfe85ce09..298210cc551 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,14 @@ const EMPTY_PROOF_SIZE = 42; export class Proof { // Make sure this type is not confused with other buffer wrappers readonly __proofBrand: any; + + // Honk proofs start with a 4 byte length prefix + // the proof metadata starts immediately after + private readonly metadataOffset = 4; + // the metadata is 3 Frs long + // the public inputs are after it + private readonly publicInputsOffset = 100; + constructor( /** * Holds the serialized proof data in a binary buffer format. @@ -55,11 +65,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(this.metadataOffset, 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 d7f5f4706c7..9b927bc834d 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 = 3; * 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 f77e6069050..594272d005b 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 e53fb312ec5..48d87598c82 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 @@ -348,7 +348,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 f5881b11a1d..0430ef690aa 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 266cf49864e..571794253af 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":"0x0682f798cbd4d79c13a2f650654b14b1c1f90cf56ab0eb53f2303137316fd110","block":"168412281b25723f31c01d87dcbd5eb4bfa1c9d497054276811f7b1b60e8a9260000000a000000000000000000000000000000000000000000000000000000000000000200770f3c19522567719f7c16a16168cd7d360a58a085351d24e19981ced404bd00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000a01c4f87a8c97de8261d1c8917492b3f4c52a4f7d62783a50c8068bc54a9f095f2000005002164b077e0d3ae48b923af55339853f6077a68b50272d8414a9336b484a7fdf3000005803001c6ef98334f2cc43764ea7d811fa76f1ae4920f794737f04227ed57f35a9b000005800000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000017d9770e08ec6a88f0e5308085c1da0e4271d46ae7a9a42a52d7967aaae9da14ef5a24a10000000b0000000200000000000000000000000000000000000000000000000000000000000bed2f5e000107aa36912351af5e47e85b6bde747868b34cb09daccda474616d09075bbce1780002098021189e3f5a9a448d3283e621791b2a280964f8e7e57bddd10f8faf79154b00000000000000000000000000000000000000000000000000000000000013880d87e811041d9b7f4747717d508f7be2570a92a24dd1f7572e871bae1cff83d800000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000040000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000bec47b00207d0acbb35869b1b8d7df8f9c9ea217f9cad8f9a4b2ab2a655acfe9ff672019d0f8434eac2f39429fc5a560045e27c6e272a4fdce8fb4f891a2abd2cfa4f72d2022cf34eca3766f7f0c15ed93ea433946d790d54ef538a51635bbe7b9fa7d09cc90e94192696ac67a8c77aecd3540d3654bbbf79a4a52bbe350557052764c7068600000000000000000000000000000000000000000000000000000000000000000408000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000000000000040c00000408000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000088560f0f994ba3f5bbd628798917d2603cf3459a90d8b2e12918ee2364b712034c8a99cb64ca689e91887a76a4704ef83e800a49fba0d4b8c9f1db7bac462020b110f454eaccbb0080fe5e8c2ca5e8725a1a4126a8428707c21475b05c6c879669ffefa13c2e31ed4fb7ede224b5271c5616d72847e415d8782620edd49dca52c6920644ef4b77f85abe3d2af9bd5b3c3810a30d76090bec7ed9df0a815b0544aa227930a27ae9d052b02cce25d703b392a48101fedc789dd9e93406f944014858347901598744d5c8aaeaa8daddb794023ac23385c49bdab78bcb8b3a7c1068dd8a545be5829fa9ccf3c28ee45be5ddf76e1c446ec57d422676056b7ea388f2ee1003dc5f085642323ac9405fe528cda36331f6f5fb0da478f0f4c6b79a76584f0bd2a67ec965743ee2edfb9a8dad254354c72aab7cad1add5be6f9189a797d93ca2dc84be3d4dcea7a9a1df295e9b690387f99ecbbd46cd35f036795f60ecb2a6dfad512fb601828602e62b0d4aaf54019e0613eb2882582dff3903d609bea13742e186e1a574ca42caa496f05a6e7cedc8f1b706ac27b179e045ab1c87749806aa19d6e6c8ed0957098fa38c99da5019278e0f86bcd1f83e801e10debef5900000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fb5cfc23d13c58bccbe4aba8f1b0649aad5f47c67aecac3d288dd72040d4b46cbce4fd84f3e184a7e0b995ccd758577cbf2e2516e369479063acfe2193aafc1c07cad0765fa4afa491214a84cdb36a65b21c0d0011cd24c5118b2e2132d4d6eae2923a211286d0d5106a9ef375139435ed7423910967724d33cb2c1455eabefc39395d3ca5482f4450672e1d7733e9737faa3755756eb92435ae5524bb4cc43a6f0751e92c8a78015099e97804fb3e09df278f828b4f2df39969e49dd42919fa9bc412ebe799302d7147ca7c3057f0e13d0dfbe8a0a799603cad60db29bbe587e0235dd3c271354b02103e9e3a3b0fec304fc07123331fe8efd4dee89c70a04707907e8a4737bc23ee7ea6aa737412f06adbbed64f3f88cab2b9f25e342bc37ccfd63ea6c2ea22fa0236863db4c659bafadb24041b51013403d813b9c923267629c4d21becd352980b84273d42320b8fe7b132c60e508832570fe23906ba61329934e265fbc4bf58a6a1c1b9e3d8816b6169acd318b3c5ee6083d2e41836dd5cd288d10367253af00a383368f798df4e5456986a756f2476c50309fd71741ca62d125903345799708ef0aef33099ef6e618282cb942e63bc0b5474e4ca651a20000022800000224000002200513f23dac12350cd76868b8a3d05065c3791a3789383d1fea9ac3ccc3be7c830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fb08e47bf4a5bacd50e4a162b67d37ddc33cd3aa7216a9e8ff64e301e30df68a39e315731c6cf7ec6fc433a3b8c1a5f0ea486d6b693c1f4180b57476b9b2c0eb87ae8203a12a3a53798bdd781187f28888ad2c4d0cb583c9102610fdb527ad793c1bb58f0542b77d710c2eb23b5f41c5fa75139cf5cbed830552adfd309ba56e4704ffb7d2f1a181d88a4c29dc8413fc873a5c127e221f5f0925613f06fbe49af6b243238ed369489e0e5a6e23f81968ebc1b45159de50996370730538a24316bffe41cc183f5afbbd630f8a6c1bc1d56782546e63baf390a9a95a8a4f707412f2ca990db35154b0eeee962bb363de1448b249cff1258e0c3964e5b922ea23e967025a05b79cdb949cc14156b74e306b254d304ea54662171c68c622e36687f3f8b029a40fde2493e336aec8e03b1b883bb11b849a100a30a591f805fa2e2a65d9b6aea0a78fb16047c7aca474818d342aec0e71d695b7e20af06a78e5f4052f6173139f83a3f183995062c4a7e3212c9c48ed0581909f39b4fe3f4a834c9f3be24047a2226fe7b78e7974521c71d114c8b318e93e3573a1da616d90dbe3d9d73963df0e3355a197feb54545ddaf20e376bbb0ade6b0e7c693510d1da27b9a0000000400000000","proof":"00004244000002120000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000006b0000000000000000000000000000000000000000000000000000000000000001168412281b25723f31c01d87dcbd5eb4bfa1c9d497054276811f7b1b60e8a926000000000000000000000000000000000000000000000000000000000000000a08ec6a88f0e5308085c1da0e4271d46ae7a9a42a52d7967aaae9da14ef5a24a1000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000016e43c5ced27da8b6f2fc788451dc743bd619c3763dd038512e1e02e4c8c6f440000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017d9770e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000682f798cbd4d79c13a2f650654b14b1c1f90cf56ab0eb53f2303137316fd1100000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000002e3059a3a513ebd2100000000000000000000000000000000000000000000000acb31b01e879a466900000000000000000000000000000000000000000000000e4156ce7e68f8d05b000000000000000000000000000000000000000000000000000207a06299fa9a00000000000000000000000000000000000000000000000165ad30b03ce3eb2d00000000000000000000000000000000000000000000000a3a7147ddb3b9458c0000000000000000000000000000000000000000000000016e5155e9178c65c8000000000000000000000000000000000000000000000000000068b23602e6fe00000000000000000000000000000000000000000000000f6efd39e580a3e6100000000000000000000000000000000000000000000000082e36b8da2b12bc49000000000000000000000000000000000000000000000008ed75e19a27997c5900000000000000000000000000000000000000000000000000017cef742b6868000000000000000000000000000000000000000000000000fd810553a9e73f3400000000000000000000000000000000000000000000000b145e0a2ee85822fc000000000000000000000000000000000000000000000003b13582511a99c9940000000000000000000000000000000000000000000000000002865bf9f95be9000000000000000000000000000000e890e23ea66995ac0cd157ad66203420ba00000000000000000000000000000000001c001ed135ba5936f3a916a97177a900000000000000000000000000000064678cfe06a8015932a3e281645a534c0200000000000000000000000000000000000675ccb0e38833042f9c8dabdfbbf300000000000000000000000000000007ee7d86d5d35b8497170aa93149d6ea9f000000000000000000000000000000000017a0ff465881fb66307b71208429c3000000000000000000000000000000fdde0ed9f5c731c7f476c7df130e7e4a200000000000000000000000000000000000017f5ddf57f45a9aa6288b3feb101800000000000000000000000000000027995bc3cd9f6ffb368b2d8524bd9b802b00000000000000000000000000000000001cec85cc344dcbf75dc4b68e640e34000000000000000000000000000000dceeaa901d6af7095576c5f01243416eb700000000000000000000000000000000001f0de24acb0d0392930bf4336c8ce8000000000000000000000000000000c3562d3de20254558799fe1cc6dcd83d47000000000000000000000000000000000012e94fb56eca2710e5ee89f76ca7bf000000000000000000000000000000c8e15d87aa3d60f470e3f45067e43f8ea900000000000000000000000000000000001d068a50ca319ecc7c91255068c4cd000000000000000000000000000000f10600b8565647170aba4864d7d764729600000000000000000000000000000000000890c5f08938a81e9f7749cb8d0fe5000000000000000000000000000000c5236a4737f4ba302ae6a186735d6703cd00000000000000000000000000000000001245132eeab701ce68da11c25e6f7400000000000000000000000000000058af1b0a892816a68b47f1d913396c02a4000000000000000000000000000000000007228bac9d853b2aaa5a424bd20eb8000000000000000000000000000000f18e69117cb80bf7fdea3aeeff5dab8d9900000000000000000000000000000000002c015dbb072d722403f4728fea05aa0000000000000000000000000000001156803aabf9b319bee320bbef73efaf6600000000000000000000000000000000000453bba56f5d72c28e338eae272c17000000000000000000000000000000c299833e9f322019bffb0bd9e75656a60300000000000000000000000000000000002330e2edafd8bbe9a0abb1cd860440000000000000000000000000000000372449d66cd4cc5db696dc322f7ee3012800000000000000000000000000000000001583bdb63325fbeb57bda9e2ff70f8000000000000000000000000000000be29e683f6ada33319ac9a523407fe264e00000000000000000000000000000000000de6b54f7f3d9d54d36e2961fa376c17b7f860176ee3972e7cc5bd5511f9c2abebff7e18143725b603eafba3a505f518ac5612c9c2bc9289d37ff92c6f5e9a7c47e8ca61a5396b8dde0a984c5afa0c20d87055e7d7872895512f79682588689bd04a7053bdf14c54973d913c2a8ad11c627a0c55d075e872109b2c525f2c904c860ad67e74228e47dc4e1082321b1117f1d69d1069d0c732313407ffa64e4d757689ec7df3b6d6e8dbada090f4f7e40ae8d5151e9b492b63f3c92f8f8276201887061c7b34bf1d8a4be1c6d1e8c2790c07331c29013c619653684d2d56af2b01f123f6bc659b5caf2e30d8f13c2d4628a582d3882a9af99e1216411f61ede2117ad47124bbfdf2a2c34cfe8a3ad55801431292fae6ae7152f2ccd6a1e97dd6a006c867c79ec9a88cebde40cea2837814d566cfbd8792b59aeb248ad90af535a01b91ea94039e4d73e246c68427fe320f89d71c640a23c60f5e98ac5951595bafd93b1cffb420994a5281456a5174602f6a07795c2b9df7994e0429c1830616218fc6c0a43a2013a934bf1ed6a83851294c4cf42781ba7ccbcd0c874e14ec3a73e76dacd2f4fa4c97ec61f681ff5fdd23c8c84c119e4e874e80e11b68a8e564ed6d5b3957023c8cfc3b085781fe59c617dc32cd82fe63fa9cdcaf5b711d9b5cb6584e97cbbac645c7b1964134c52e1e26bb7e9efeb4c2143181f3add3dc8113f124ed3bf393590824af3b1522dc3bf626db5481496d903b8101a71797aac69bdc8296591d77177ec38eb47d6a03ef5e006752754121fdcd97edaeda2fc4eaadc34598310fed1e51817b403bfcf8ace526079895e0b13bb1b39d335a3040e6bcd9647559aac3a9061281288859fd278d00ea848fc7253a53b8e12e578e1dcf176b4ab824fd977dd9d00fe1c644d1c83b2fb9cbf6fe97157f7ec36ae4491ce00589acd2404a2eeba16c1d2eba111806080e366300e8c7176ed2718dbf8fdfd48bb3898ab6b0d365b0322aac89562eb20518f27f51f4ee9a02c95e01e959f8a3a52a62de398387703dcf8c17e30a857d6f042a273ea54f5b686b0d83c242a2736ac45e9213d73a6ac3e18bb15016d9f9e8274239f20372290daaa50b8146c7a8bf1b151da1e3ab8d6366b655299b3302aa2b3be763a558172f183b9ef49f9bd423402d74db715c465cde710586c8c343d30abc860c1690ffafdc4caaf069ba2b47883a5af98f542e2c592d0e52fddb6f3d198e4fd8c00fb5294eb59709c928197f5cdeafe1e5a1b8e80a07a225cec02dde2d01556017cff634782415df3e0d2a366af18ee7d59610fbeffb8345b05546f71075899a23429760934844ae9fd56302dfa82e98a8be785f1c5be63885f1a2fd1ce066b240980d270630eb6fbc04d8389773651b293c143781305c42e5592d2802feb21684df8dddf3e437493d17d3b9cbdf47c1c916d7f2bd49ee3e766edbd626c47b3935b3603eaddaf1d7844a79b3163846ce49796c740021316f6c6ac83e076375c457b32bacabb4e21c7b99086b537768b9df5b8ebafbeba49b6a7f675f216904f2f790a0d90ebf52f9af807ca4ba4fde03334e500d21e419967a16f7d1300a15a76424279985d17286ff240ac9364c2623009f05c75f545bfba3e8baf202a79752fb46e21f792d96c75374a13fa33bdd787731835c83ec50be62dcf01e1cef40b75c12e728067f0d8c79f86bdbec7e63d8b157ec51a8d7876b5690efd30b98d76a073bfbe9485347582533dd4ea6ee29342876d1c53a45783ef7cad664090b57d866ccf481882059dbc98fe30e355a1684c1d3990ff36c555b85c0d52905d6a79b9ac60fae15d6902822f6f9a76572f46353e660d7da57abd11a71f3bc223dd3794e061e82130d9a7e452574838e49ef163cd19b0cad58fdf71b186875112af6f9281fd729e25f7314dad5f71321ac1b64936efd29253abe195e91d08b118bba2dd001287b9ddb54184cb7f20a77161599dd5b606bea6753b403de896716081afb6ca45dd1c8304df51be9b4fe53d2866c7318ad79db7bdb45ba6052e919cff7b5a11c4c274f1e7d6355034b909767bea8cf7de1ce7196d29eb5bc95a908283dda4b0fde95822093cc1ab63a35e1d40c1f4b231cac8c0359db70fc48f522ad94da4eaf4bfd93793ea2f383e80d14d15af940696c219c1bd8cc568d0abd11398622c040d733c5f81aad86abca86cd3f300a93832cb76fe0b5058492bf382675f859089e42831c53d1b8c675d36678979d1093f4728bb25cee192d3847db029864880302591ebdea08f4f6fe7e9ddfee962c008199a7ab5c38e093adf4411f62c0e3e8f4ec3d9f96ce4dc500ebdb2798f0bafae904da10fc171f69f13d7318516b67a1c414709e87bb4b981cff45d98cae9ae00a04fa5d6385f1fda248f126b46a97aae540020825d888c310c3f92176a9d279c8a044809726f9c6d3f86114f9317b53aacdfcdd75b0ce2aeac842b0b92ab8b7feb057a43c63b4a485967929496a8af1c25fff6e8564430facf472466d2476784ab74df515c150fa1be99d13fe388f77a03cb39148a389ef0036f89d9fa8845acfe5e481a7e4fe13b4d8160df767c37d1c36737b18df2cb0ddf710238c36e9a99b2d6162ea76410276416c1e2ffd4f11e221db4c5629cb516ee415e1524353092846220a5ccbd3f277f822039fa4197b3b5431f8ce16c2e60cfa14b36ebded00b859098f62a85106852ab1231ec238d4a775ed1dd411cf2c69ffd0550672490c6bffb504b28b014a354cae0a7a1cf1a50877d89c8f927a01270ce12e2842e058dd9d1ec8f1a8ef6177082014c6e1da7c984549c5405e1f02d0ddc1338ddaaf1424a1faafe3bb37dc1c60d51e29dccc27c1ac3240382e98555c265df7fe38d8a169adfae9b8e43aebc68bf3027af160f84ce4f5dd25a0c01af01d20b6c28bdf0870141b755050d4b848163613fcd1107536e6edbb270565f688b9a316c9be1cb78cf45b2b015f22425cf83d1413cb6685900b5daccecec7db62c3a444c89174fafe2484c042de9bbb9e64462f7625d117ef05575a1fa62d9e32f7423fc64e6ab89073ff2d64b382e1718cd10bffd54589b6a7d70eb86210858b84e64648450071c30213a0fe8739618fbd450d0198103d9087861566f436459362b131fe5138dec463b2596181b30df0e97d1b54c0e8f65e2367c45d41c7147b92603aa49174e96493238420fd739c8b473f1a6c805e1261159df95b55b0f25a0e1cef428912e19fed7d985152fcda7c71161273745a2f156839a1e0cdbcaeb1d830017bf30e89e12286e405a5ab3ffff3b6249eea7e5576a3d0b755193cf5de3be6739bcbde111746fa7509a98560f4b32a1207c5704e49ed9082181a85da3524fa0cd9182f20486abb7edf5e55cdda4e9d1544865b4f6d1623bd5b10037d2774bec886400abde4efe406439757c69333111234eb078dbcda1dfaf5799eb565f1ce2f74a19141f3deb171e82d2f07354d8d0a75afa7c749ace21add83097ef9ea0f43a8b46ebfe35abdd2c4346d8a7fbf3c2310e85b02b9665436bfba3b2f5913aefc1aefcf483464acc705c7bae16e3c0f1331e3e99164c0bfc273ff0aab9c9a290442ce82c1f7b59c47c52dc1dea65aea19c31df3d83e6cda11275929f992a3bf65da098a15462c7802c798085aa6da531755f409584f67e4e1fa3a8a1c49e8f9738e059da7773bc4764412155ce7695a06de4f09d47eaab8410674490f0f7e753c2de2728bfdead8365d01c0dc35a2e21da26c3f00a662c03d1fad9582802aa492de146a5cf7b33ae19c6c0888f7b3f10382783475be591fb2cb4413f10bba5ab8a3ca700ae606ab10023f3955c6e6db2d489c0396b3547b84e5dc9e3e93b43540c33ea6434772169e43110ed5e13cd4041a4b364800e252f48987f787cd25c49f8e8a541692de892522da6e4e96e64d267ea0c5efddb96fd3b98789005ef051d7448f1294ba417834ade96bd21f621e15cff207bc6b81b90b722d2914ac9a4419348b1c7c066b45af6a325e056cfb1703ca4768f96522dbcf8ea19c0509edc60b9987d845168dcec1c6cb6ef85169420baefc83e3689e63e456ebb533aa5b6c9b6d90cc033ee9c14a90c3ac7dda6ed2205c946255cc7d340c7a35687fb4ef5de1829bd3461e5a2faab8349276bda13d1462d49dcd5e6f9a36368520b6c7c1752b06ae0a0506565bc1a7d8c4c59e139b0d330d82aa9c303eea7cbb7fc2224d9f95b2287a681922425bf6ef7f743f71ed2fa2842381c1b2949a651e1bb299dac8e29ac9315b992664e5ba8a9b3ca3906b196efadbc05ca4657dd0ef2cfec897b01e70f868f1305e8b75da5bd94e47c1632094c7a36aca33f4d3372b2c50e55d2d15902fbd05a4e8fc9bb5d0f48e74acb2035095fdf5e00b5b74816f7a5147bd2209e1f86fd6e7380b6c7a0f21072aead00543fd3fa0d91069492434b5ec156790d9771280de3da610aee08984cfc1f81c066539a009ca24c0ad28eb8fd8d308648f00b4570c5b21f396d43d904b2cf1fd2707efd6b88459b769bbe2780a0e40eab6f75fbbd78a8ce2e314cc172913d58727400972c7f660514196631adf83977f9061a134732fdd95ef8d5d3947a8a01f0533d9f7ef930f5949c1c1c653c9fb90081643a100aa4ec653163313ac57aaaa1a61989b08984503a728d78353e244cf9808732c64cfc96aeb9e819bf10c0a671ef93e145144c4bf20f953671ab48d3f59bd3a25bb11813d634aa75a694919b6159de4f1e2baaa6c167aa26eaa59db15138e13329e5b1934f8955dde5366245d0247aded5fd1b78c249ce2b2f1e78687dc28da8854ecda9c496a1d4c19a0ede52513e61a5b394a643dcd6f130de3cdbd2db36030a7791baf39576bd2f54605a317833a6872380699eec46280a26304bc49a0f68c79db2fe44abd20b5bee690d92708963fec33f845e5720c7a25b7fce90e9102899172fab1ea26e6cc845c437f0e5ce793ec5d3948acb35f573c31e78e75b68fc76895e1583c710aef41aae5c90481424c02846be8668ae730f76587968037e34e522ebffeedcef3e81e0941e415c618ed6b810867c20facbfed55ca333d3a50c482609698f5b95d21e2279795295ce0e98d92fe1d2b43c42ed5559c6f4971ffae3329491473121aa7cb794ca00b7c3e7863507e82276fd36ffcf3c43d23e47ce5536bc76aa398368eeb579e7f11853b53920633bf35a45b9dc1152e54da494bbfc93077ee3099dd9a881850b02e4343e6772cb70201912893f7718cf3d9b7e0d0fe2e1ba6f1fdb2557db6f1f50a98771e423932f665fb9147123b4fba2853d93eb14152ccaf7aca70a2004ffd2457af6073b63c0efb1bbeb3940a0c916850fc1b63af7dff2b8650c02feff9be090222667949c1ffa8914d84037352820a9217cc6d8b5b86f52885eab7d6d47e267c0e4f355115ef7bec78eb1bb9b07f03537ac96e07a32024da4f61e32c6e1a0e6ca58ef7dabc94adf7f49167a946e1773314bd07caedea2b68ad18f9f0fc0d0cf941c6e7859e25aaaaeaee65f741bcbe44c1cc1f96ba18af294aee5af9593d04d4a99b71e196c51238d7dba3c1b70781a3f006e16e4f7c9a0dac3b453787c40f48eeae19d2c0a72762fa9a8e6b5aaddeb87c68c69e64f441d5ba7645531b431a787ffbecac74ed1de159491427b9b03e42b10fe1f98e8a4313f264d4fe067a05a3111e9f466f1738f37512359547155116970b042fc44b8ad98a7cf5dd00cb02e1524ff398f782f37222bdcb828b5dcfb4ea040ed8c8f3be119ed63625fb2e2a77380c84d607566fced8b0d3d8e909d9ff83f01527b190e40015a3d7def6d70f22fc9b4d8f06fdeee2c95c699ea8f8ecee88fa13c27cdf9c9fed4d38843f0019569ff7b02007bc0027f0a57c6b0b9e156f434a896d71b8b799efb621635a3512119ec00b23decb223eda213d5c2401dd1f14777cd6d612f0f7594d2cfc5701286f01cf5656ef4973e68f7df3e3b5215872b570d631eba74f4c55b27a4066f42fedf79141ddf98f44d7094dc562f62a590d88bd287429712f797d9658defa82229e4fd6f4f37b9f6b19b94470507f269b4a1473231d28e642946d95afd1923e082b35ea2f62f7f7b72cb5f365b08fc3bfe253d5247d6371ad6251a48eddb009177ae9cd062fbd258fbe07783d47fca0b290167da10536c29e4c86dc0877be242ca1b1ebba3fe8b526b6b5a6255f9aff4cecb7ece457020ddf108575355a98c2095bfce79a9b6a8d5c3ea400cbcf3f1c5228d4f14c4cb1c133e3ef9606daa69729bd87eede7202b55af964ec94c16100acf0045fceb020259acf5372628108a52dee8a37400289210ea94fc4d04320d0438e5446c61d9d015256d6afccb3cf1e1d92558cd7cebf73a10361e81e150ce610b06090910d19178cea3e3bc69cb9432958f2188617b97072f509c28a32a788615d4d591a752be53e55d89e80ec6bd72017be7dc9d99a3a2bca785c86ebc7539c6ddd18c1a0a37d8a918d316edddbf8238863eac1ffc39ff5017451e61d76e7d57a5c90a6e2b4964ce727c02bbe50e8104c0a87a31fc214b6f6a2e444fa3d5b96615262c059074eb41063bd0637c22c1591b5323e36d5acd765426b4f3b4fa2382a9cb45b9576207f780e82c5a4a3a204d4ffe24ac6810abbab362beb90ca637c6311a44ad31d6e3a868308e432feef18b2965854badd7ad634057f2e60258fabcd3c83d18de3031c4601783e6c8240085eabca90cec30013d00138685cc104c58e6d48e80b5582f4735a389cc3b781175b46dec8cb5249ffa38c7d54ceecd23ae4d3887cdd21b27d14982596004b9d17c6c69f79ad3cc750aa3b78bfd218170d5878c9332d345ba6d353cfc01edd2e133cf75325c79370a28117fa6ad8dbf04a0058a925c5f2ca117374c853e193c62355120f880ce444c81ac3d6558a8e9a345bf92aa75ea7d285e67bf01331d1561559f8502fe8d1c4b9150b91c5fefa5cb1159dd4ebdc25e9360a7d4e868a370b0fba1eda0da1014808d4e2ade1a5c0166f9076d99f36525000a638e03cc5936528b9341ff914e119da78f92a7355f42db985376707aff5609be44973ba5b926e2405c4715e456b9effc0fa6f000437a0bd37c04439958c647708f5b0d89acc5e245dc396aae5e61a3167916efeca48299130de7a9197ac5c91f41a2f8134a8d10ec0adca399237bfb1da227fc2ce8f6ee5a7b8707ca6cffed2a5bb6eeb8fbde91311dcc425d7a1c594a129963fbd366b34291492736a3e761f977c27e8140056287b33ac0a5d6cb9d5515c988fcfdf46b046e3800be37b85bd88877c4bb9e8ad1f1c2f1ce028010d6dddad64f7ec693353f5a69f86fb61b4fdeb60befc012fbf17250a793576c282e64ce1f1a8ae1b7cc1ca582f07dfb8a62a7f32d005592d1c18136f8ac60d1e0a0368da85a34337cb8fb61f9b6c93b945f0b694f8fd5c17dd004063c77a4235b56485926549450ce67d53211b80d8e22659d8ec39adc1533730387aa61745575304a44cdf5e46ff334bc1b5739f44e22955ea6cbf59ebe6450720a67099b41e4d3806a19b88b863742b1305c4fc80307f271380aeba22e949213da42eb6836f8a2477f025170295f4762ad8afc70384a829eafb2c330c0eef02e7b4a10f1880bb9a52d1b283d7ca2314450ff2974b5fb4246c2623531ccdab1a77c3b232427bc299b76f7380cde37298c1a684ac0e6349128a15afb4ee5fcb0524a99c8f4ce54a1c005e257baf2e31c4442d1cb1f42c336ee7d7d6b2c19625034a1ee973a46b2bebe839e776a66cebee1a741f094b7f10b58812d28508cf442c14e9e633d6e2640f79ff203d77caea167e7b7efe21ea52d2317df3898a0ebb0d3316f16bf92e3570d022b9bd5cbb277b709469ab03dc8981002862d232703c151039d50e1721e3f4b5e53bcc0dbee038c8f443ad769773ca6c5b8f0475dbad0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aedd39c45321954a31a94232c1514fc2d632d6f1fe182f7f127654038a76d8f2e1cc07f39c70c7a87d578756db548ebbd6c17bb0319b5b490e2d39fe55652ea0d9e9b4cfc7cdfc182a0b5514d0e3a583b499ab342b4cc5d2536073110aa7cb50741f0550dec2646df9f3e68cc6ef12bb85f34e1e74ff59bfa84806e4e4f419f2192a4130a392ab8d1caea80ac2fc4b39d125fc1ef06374e463ae27414040c962ed1dc5d1756a69e788374dd3716c1efe9e40f48547ff520dddf440a12881aef1c28d881ae2867c83c54c39c0143afc5e89cbf4f921577f6fc3151729ff846d728b1e554b1f115db276ad487936bd19d596c7bf889237e3ec4e4c4ee72af76730a586f58b60b9ab4e8c87ca91344a7ca8c81171a1d291e6c7d9d6c3d31dc18c61ed6d19da10b0eeceb81f44426ddffba12d1135825ed3908cc0fd1648a04544e29cd60006839f4586321e6f3eeb651e71223a0e4f1cc3d7a921e78382210d00b23950215b85405f7a7c5381bc3bc4dd6a953cb37a74e20305c416d411ba1c9130829350bfa6aa9998057922d982b2ac617b501372b988de766b247b4e156fadc08b813a22d284a4b5b7b6974fd7fbf0f694155bb82010de7e31c079f2ae027772d09a35579c1fdbfeb9a6f97716a936cd98988291ac5ee87b7a719e371ddc52b0e06e29be3bad2ff8d82b19db6a5003d4defbeffca6ed2b4c85fb234f7e5e3ee1dc2e039ce4ea34aed5500432bfba4e67117442b46302b0b17cc8fec23c3983b076be47f23284da334b5d02a4a60124482dc41d70492cce003c854334bc988cf0a1129a2c793c04576e2011c4e39ccb6dca3df91755e24763597ba336ac3c2a000c897ac8508951277d9578d2838c0865ad9340bbb3fe17e00155ff6e5c172880c48fa15afb7eb2d2d45e987d8f930b389fa97d2a577f453ccc509d7cbf89c9a084bab654462daad3297491ad6005a08dcc5bfc71f27e86a7898c9d1971337890c6ea6968ed79a9eb09eacdb1f1b300b887eec3d79dad17899b959be2e2934c608cf74eb496ee77f75e21ec80abf489f18685aed9cfa5a65988d678f3a10b68f0385bfa953fa078638a4d0bb697e2f347323c1b7582f6512826a3ce6d437b4e50e44ca62869e9d1b4dd8e7cf50fae25136050ea4642142fb537485989ad3aea121992bf4432347ca2fdb606a89340a9fe2612c5644e0004b84207b92abca88702b3f75bc130abed3704261e0e0c7bc6172d315554f60b7be40ebd250f89ac7e4087e943193c8bccbb159cab9ef52af004c36575d4b33b50bd580a26363ea5c5023a3a37df568dede4fb1d2ee6000a8e1ce9c5377ab5861193c029db7661d01652dc1c49ee330a65454b2eede670e39cab7b88128c6c707605cddae6a2059abb924d73ca2402e2d2d09affe5f3cf580c40bcbfaca0216069ef0ed2455657a63921818c2a8fcd6ae7d39e8ebc4984b875f3820224b1bdbcfb17e4a89aa8fef4a88225426beca80ea5140730767b2d12dcbfe4c84971efc2cc1db4536244ef31ae2066ee0901d265ba71a95a5ebcf960b9ad56a91e125483048b1c6fc49939fa68d1f2cb09b504e77ffa01264c004d2973a5c30c2dfe6c8b20818d5ba4eee65e0362648eb1a5a239f933cb9b579d4e432d2d0f85ee0bf7fc223103fff7bf3f3c5410d4cfbe1f878e512b3f5ce26a09921f0df624b7105ef4b80095151d0480e421610fbb3b11018866d6a7c1e55fffee264d04cab0fd467157b452685717298fd57159b78d3457ebf31e21758e7443639bd1d102cfd70a40ade42024ab06a38f8c41cef250cbc560e0ba83ee7e92dbbd6166ca711736cd65146bba4bb11a4a13dbe0bbf839aee17e01a2566a72e383bb2c64737b264121468f6dbd60d4e229511dd026c8a44ee3f4fee5c3059c05a660ba74f407142c9a39ced7dad8e3ce277e518166aaf5d1c680e8ebe44da44f7cfc9d140de342682bd049cc4e1ec4243874a740000000000000000000000000000002d544172ff58c414aec63e4d5f1dfef43300000000000000000000000000000000001f9355535c4011f0431fd5d78385d70000000000000000000000000000005b0861b99d5f160f6230f9f890be2ebed600000000000000000000000000000000002712498aa068f693d353be01177e76000000000000000000000000000000a3ed814facb4a323ecf496cfe22111b98e000000000000000000000000000000000000453eacc387f1fc8f03e972e861df000000000000000000000000000000b562fd6643d726082f602da446b919e91b00000000000000000000000000000000002904ac9aa8f8c73d5d123d0daf07f500000000000000000000000000000024b01c7f327c090af5aabcc61b7744c5f600000000000000000000000000000000001af13e910c661bdaed7c407ef22832000000000000000000000000000000c5f99bec72c68505b53689e505ba5073a4000000000000000000000000000000000025d37be6c22499780a76ddead2c0f4000000000000000000000000000000bc96ca0405b11887d07dae6d902fc1af8000000000000000000000000000000000000570316b99804a92474966927a1eeb000000000000000000000000000000c51afbeae269b1d50b50ce246e56e43d710000000000000000000000000000000000112b77cb044c9747fe69f93a39abef000000000000000000000000000000cfeaaf59760d5bbc4655db3aadfea1a0aa000000000000000000000000000000000022c10efe9bc26d9b4ee0b1611e135a0000000000000000000000000000007582cf1ec23d97ba97718f2188b1c93371000000000000000000000000000000000013d8c781f121595d4e5598a4030a6c0000000000000000000000000000000a03577c6032b26766c15e3e08700548690000000000000000000000000000000000087663ffc7008c874adb639af87158000000000000000000000000000000a686745b827bfece761fe5806e76e35251000000000000000000000000000000000009e390b295946c5ac093093d77f7e1000000000000000000000000000000f48ac2601aa03434eb86554c6a549ed3e700000000000000000000000000000000001df06322fcd37d73fb3f73c03527310000000000000000000000000000001a6b37aabc3d0edb4464b9b50f0389ed38000000000000000000000000000000000022b9f7519f313e4fd502b95694aed90000000000000000000000000000001be2abb66660e897fe5f82fb08fc27315e00000000000000000000000000000000001f8501ccdd5f18745aa2201802eb02000000000000000000000000000000351aeb7da4935c09161342c7d209a8afaa0000000000000000000000000000000000225a2e0d33b9be5bb96ff1eb92d98f0000000000000000000000000000008cf2f296c72458fee42619fd34f507075500000000000000000000000000000000001e8fff6a9ae9dd278cd2aadf0e4594000000000000000000000000000000449ea257819728f0f882fa5d2e396d71a200000000000000000000000000000000000977abd3552057a0ea284e4663b71d000000000000000000000000000000065e14c6475e60e9d4861a4464e61f254700000000000000000000000000000000000c6c2d75a8189325b7db63beb40e1700000000000000000000000000000031bed94f48899760d0298269afec303b050000000000000000000000000000000000080a945d72442ba1d6e21ab43a96c1000000000000000000000000000000ed0fac5f4c80e355c0be8e66047830f59f000000000000000000000000000000000024e5c1a9a6375cc88f61d419b84c53000000000000000000000000000000f1ba93b6a3e6ef60273914a2ecfc75e8cf0000000000000000000000000000000000028e915ebf8361451cdeeb95c3abcc000000000000000000000000000000db1215215845e883a175c9a4a6f7263f4f00000000000000000000000000000000001bc43ab52f8e49eedd73ab963220fc000000000000000000000000000000b05cb9b046f7334960c1a24ca41f3d495000000000000000000000000000000000000b549fae7a60f066880d9168c3cde0000000000000000000000000000000e76311a28721dbb951d7c059ce215233a2000000000000000000000000000000000021e9da757f03a2b5aece398c8f1931000000000000000000000000000000c5c4ec6e9154a076fedc74df770919800f00000000000000000000000000000000002753f03c77aa03bb83ebc39588300f0000000000000000000000000000003406e0dd884841bca4da43bea6ed4d4bde000000000000000000000000000000000021e99f09e1ae95843b7164b7b096750000000000000000000000000000005ba04fffc36b84ebf25a49db84fe1bd39b00000000000000000000000000000000001e3e8423aa44343e612bb8ba4a089600000000000000000000000000000018cc12d2403c522eac722d7509ffce8f1b00000000000000000000000000000000001c6679071a1163f2ed8f2cdc8774500000000000000000000000000000009ec6c30ee33ffb79e6acceedb714f2544700000000000000000000000000000000000f58de5c741ddb445d68e0b103dd2e000000000000000000000000000000e11c50687be4e1f1604ee9049fe590f04d00000000000000000000000000000000001bf4b5bcfc56dc5d4e110fbdd2caec00000000000000000000000000000050564da2d316b539c0b90e67051b3aae240000000000000000000000000000000000024f95673a0d92228aea675d32ec1400000000000000000000000000000078e152955aec020cc07d64080144cd4e2100000000000000000000000000000000000bc4d829ac3c1e7e343fba8ce93546000000000000000000000000000000c2116b1ea40649ccff4f52eaaae87705d200000000000000000000000000000000000ec5de6da728c2e9464ff06bea8a2a000000000000000000000000000000dc164daa4520700db4a7ccd0da9dc15d0a00000000000000000000000000000000001ccc9883777dd2b7823c81951414cb000000000000000000000000000000a8e5dda2672f008588528bdf5755e82ff3000000000000000000000000000000000005b91f947e4eef5e064820f30652d30000000000000000000000000000003abdfc9d089300d182a4ee2a3de0ea5233000000000000000000000000000000000019c08834d097f3cab625642decf921000000000000000000000000000000e2f74bdf88bdc17abecf32d8f6b373ef08000000000000000000000000000000000015f00c84e4a25a8296891934c039820000000000000000000000000000009fbebd8a792cf6c8052ba4294d92698ae10000000000000000000000000000000000152eb9b5244862d97216b89e225ff000000000000000000000000000000022d241f5abdc2d4e66aa8d42553294dbef0000000000000000000000000000000000010bde456e78f9667d4a3626e3b979000000000000000000000000000000620da80ba4347c36225e7aeebb0931385300000000000000000000000000000000000708e7820ee84707ab5f837adf3dbd000000000000000000000000000000670890818a869ce4a038075ea47797966500000000000000000000000000000000000b084ac3e55bec281cfb12fd482407000000000000000000000000000000d1d5b9d7e75041e7ea386045e8119df15200000000000000000000000000000000001b24df43985e524d880ac83e3b903f0000000000000000000000000000001f90a496153cc5018f349982d7867d028700000000000000000000000000000000002329d015ee424259e29b2f0c9104de000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e287483358a5b7c81992f3fca50c2b348a00000000000000000000000000000000000ca6f20f3b3593f453aff040297f3200000000000000000000000000000063afa6e0000bd6381aa541e8116f7a71bd000000000000000000000000000000000015782cd812d4be6f6a93a0e3c4ec4b00000000000000000000000000000096877293c61a6caf329e011bc0fc2c9a4300000000000000000000000000000000000ed171e13cf1f16dc7c2abfe2712350000000000000000000000000000005f21b82d180b91c46a74d3f35bd0ed1e8c000000000000000000000000000000000003c25739dc307a886612157f3a34ec0000006b","aggregationObject":["0x000000000000000000000000000000000000000000000002e3059a3a513ebd21","0x00000000000000000000000000000000000000000000000acb31b01e879a4669","0x00000000000000000000000000000000000000000000000e4156ce7e68f8d05b","0x000000000000000000000000000000000000000000000000000207a06299fa9a","0x00000000000000000000000000000000000000000000000165ad30b03ce3eb2d","0x00000000000000000000000000000000000000000000000a3a7147ddb3b9458c","0x0000000000000000000000000000000000000000000000016e5155e9178c65c8","0x000000000000000000000000000000000000000000000000000068b23602e6fe","0x00000000000000000000000000000000000000000000000f6efd39e580a3e610","0x0000000000000000000000000000000000000000000000082e36b8da2b12bc49","0x000000000000000000000000000000000000000000000008ed75e19a27997c59","0x00000000000000000000000000000000000000000000000000017cef742b6868","0x000000000000000000000000000000000000000000000000fd810553a9e73f34","0x00000000000000000000000000000000000000000000000b145e0a2ee85822fc","0x000000000000000000000000000000000000000000000003b13582511a99c994","0x0000000000000000000000000000000000000000000000000002865bf9f95be9"]} \ 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 e84b9efbc7f..b95d9da6c9e 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -133,6 +133,7 @@ import { type TreeLeafReadRequestHint, TxContext, type TxRequest, + VERIFICATION_KEY_LENGTH_IN_FIELDS, type VerificationKeyAsFields, } from '@aztec/circuits.js'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; @@ -252,6 +253,7 @@ import type { TreeLeafReadRequest as TreeLeafReadRequestNoir, TxContext as TxContextNoir, TxRequest as TxRequestNoir, + VerificationKey as VerificationKeyNoir, } from './types/index.js'; /* eslint-disable camelcase */ @@ -1580,9 +1582,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 45490c6332f..bb336b19735 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -27,7 +27,6 @@ import { } from '@aztec/circuit-types/interfaces'; import { type CircuitName } from '@aztec/circuit-types/stats'; import { - AGGREGATION_OBJECT_LENGTH, AvmCircuitInputs, type BaseOrMergeRollupPublicInputs, BaseParityInputs, @@ -63,7 +62,7 @@ import { sha256Trunc } from '@aztec/foundation/crypto'; import { AbortError } from '@aztec/foundation/error'; import { createDebugLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; -import { BufferReader, type Tuple } from '@aztec/foundation/serialize'; +import { type Tuple } from '@aztec/foundation/serialize'; import { pushTestData } from '@aztec/foundation/testing'; import { elapsed } from '@aztec/foundation/timer'; import { getVKIndex, getVKSiblingPath, getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; @@ -455,12 +454,7 @@ export class ProvingOrchestrator implements BlockProver { }) public async finaliseBlock() { try { - if ( - !this.provingState || - !this.provingState.blockRootRollupPublicInputs || - !this.provingState.finalProof || - !this.provingState.finalAggregationObject - ) { + if (!this.provingState || !this.provingState.blockRootRollupPublicInputs || !this.provingState.finalProof) { throw new Error(`Invalid proving state, a block must be proven before it can be finalised`); } if (this.provingState.block) { @@ -503,11 +497,13 @@ export class ProvingOrchestrator implements BlockProver { const blockResult: ProvingBlockResult = { proof: this.provingState.finalProof, - aggregationObject: this.provingState.finalAggregationObject, + aggregationObject: this.provingState.finalProof.extractAggregationObject(), block: l2Block, }; pushTestData('blockResults', { + proverId: this.proverId.toString(), + vkTreeRoot: getVKTreeRoot().toString(), block: l2Block.toString(), proof: this.provingState.finalProof.toString(), aggregationObject: blockResult.aggregationObject.map(x => x.toString()), @@ -878,10 +874,6 @@ export class ProvingOrchestrator implements BlockProver { ), result => { provingState.blockRootRollupPublicInputs = result.inputs; - provingState.finalAggregationObject = extractAggregationObject( - result.proof.binaryProof, - result.verificationKey.numPublicInputs, - ); provingState.finalProof = result.proof.binaryProof; const provingResult: ProvingResult = { @@ -1155,15 +1147,3 @@ export class ProvingOrchestrator implements BlockProver { ); } } - -function extractAggregationObject(proof: Proof, numPublicInputs: number): Fr[] { - const buffer = proof.buffer.subarray( - Fr.SIZE_IN_BYTES * (numPublicInputs - AGGREGATION_OBJECT_LENGTH), - Fr.SIZE_IN_BYTES * numPublicInputs, - ); - // TODO(#7159): Remove the following workaround - if (buffer.length === 0) { - return Array.from({ length: AGGREGATION_OBJECT_LENGTH }, () => Fr.ZERO); - } - return BufferReader.asReader(buffer).readArray(AGGREGATION_OBJECT_LENGTH, Fr); -} diff --git a/yarn-project/prover-client/src/orchestrator/proving-state.ts b/yarn-project/prover-client/src/orchestrator/proving-state.ts index 380720d09c9..4aaa96abd63 100644 --- a/yarn-project/prover-client/src/orchestrator/proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/proving-state.ts @@ -47,7 +47,6 @@ export class ProvingState { private rootParityInputs: Array | undefined> = []; private finalRootParityInputs: RootParityInput | undefined; public blockRootRollupPublicInputs: BlockRootOrBlockMergePublicInputs | undefined; - public finalAggregationObject: Fr[] | undefined; public finalProof: Proof | undefined; public block: L2Block | undefined; private txs: TxProvingState[] = []; diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index ad5e091ab34..5c617ab3624 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -332,7 +332,7 @@ export class L1Publisher { archive: archiveRoot.toBuffer(), proverId: proverId.toBuffer(), aggregationObject: serializeToBuffer(aggregationObject), - proof: proof.toBuffer(), + proof: proof.withoutPublicInputs(), }; // Process block diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 0d6aff51384..71ff91ca629 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -287,6 +287,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:^" @@ -306,6 +307,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