@aztec/end-to-end 0.0.1-commit.cd76b27 → 0.0.1-commit.ce4f8c4f2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dest/bench/client_flows/client_flows_benchmark.d.ts +1 -1
  2. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  3. package/dest/bench/client_flows/client_flows_benchmark.js +18 -10
  4. package/dest/bench/utils.d.ts +1 -1
  5. package/dest/bench/utils.d.ts.map +1 -1
  6. package/dest/bench/utils.js +6 -3
  7. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +1 -1
  8. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  9. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +14 -14
  10. package/dest/e2e_epochs/epochs_test.d.ts +3 -1
  11. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  12. package/dest/e2e_epochs/epochs_test.js +5 -2
  13. package/dest/e2e_fees/fees_test.d.ts +1 -1
  14. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  15. package/dest/e2e_fees/fees_test.js +13 -6
  16. package/dest/e2e_nested_contract/nested_contract_test.d.ts +1 -1
  17. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  18. package/dest/e2e_nested_contract/nested_contract_test.js +4 -6
  19. package/dest/e2e_p2p/p2p_network.d.ts +4 -3
  20. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  21. package/dest/e2e_p2p/p2p_network.js +19 -16
  22. package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
  23. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  24. package/dest/e2e_p2p/reqresp/utils.js +15 -2
  25. package/dest/e2e_p2p/shared.d.ts +21 -1
  26. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  27. package/dest/e2e_p2p/shared.js +33 -2
  28. package/dest/e2e_token_contract/token_contract_test.d.ts +1 -1
  29. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  30. package/dest/e2e_token_contract/token_contract_test.js +11 -11
  31. package/dest/fixtures/authwit_proxy.d.ts +3 -3
  32. package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
  33. package/dest/fixtures/e2e_prover_test.d.ts +4 -3
  34. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  35. package/dest/fixtures/e2e_prover_test.js +9 -11
  36. package/dest/fixtures/elu_monitor.d.ts +21 -0
  37. package/dest/fixtures/elu_monitor.d.ts.map +1 -0
  38. package/dest/fixtures/elu_monitor.js +102 -0
  39. package/dest/fixtures/get_bb_config.d.ts +1 -1
  40. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  41. package/dest/fixtures/get_bb_config.js +5 -5
  42. package/dest/fixtures/setup.d.ts +8 -4
  43. package/dest/fixtures/setup.d.ts.map +1 -1
  44. package/dest/fixtures/setup.js +16 -12
  45. package/dest/fixtures/token_utils.d.ts +2 -2
  46. package/dest/fixtures/token_utils.d.ts.map +1 -1
  47. package/dest/fixtures/token_utils.js +5 -4
  48. package/dest/shared/cross_chain_test_harness.d.ts +1 -1
  49. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  50. package/dest/shared/cross_chain_test_harness.js +13 -13
  51. package/dest/shared/gas_portal_test_harness.js +2 -2
  52. package/dest/shared/jest_setup.js +41 -1
  53. package/dest/shared/submit-transactions.js +1 -1
  54. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  55. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  56. package/dest/shared/uniswap_l1_l2.js +14 -17
  57. package/dest/simulators/lending_simulator.d.ts +1 -1
  58. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  59. package/dest/simulators/lending_simulator.js +4 -4
  60. package/dest/simulators/token_simulator.d.ts +1 -1
  61. package/dest/simulators/token_simulator.d.ts.map +1 -1
  62. package/dest/simulators/token_simulator.js +2 -2
  63. package/dest/spartan/setup_test_wallets.d.ts +4 -2
  64. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  65. package/dest/spartan/setup_test_wallets.js +79 -15
  66. package/dest/spartan/tx_metrics.js +1 -1
  67. package/dest/spartan/utils/config.d.ts +7 -1
  68. package/dest/spartan/utils/config.d.ts.map +1 -1
  69. package/dest/spartan/utils/config.js +3 -1
  70. package/dest/spartan/utils/index.d.ts +3 -1
  71. package/dest/spartan/utils/index.d.ts.map +1 -1
  72. package/dest/spartan/utils/index.js +4 -0
  73. package/dest/spartan/utils/nodes.d.ts +4 -5
  74. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  75. package/dest/spartan/utils/nodes.js +9 -9
  76. package/dest/spartan/utils/pod_logs.d.ts +25 -0
  77. package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
  78. package/dest/spartan/utils/pod_logs.js +74 -0
  79. package/dest/test-wallet/test_wallet.d.ts +10 -17
  80. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  81. package/dest/test-wallet/test_wallet.js +48 -49
  82. package/dest/test-wallet/wallet_worker_script.js +41 -33
  83. package/dest/test-wallet/worker_wallet.d.ts +1 -1
  84. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  85. package/dest/test-wallet/worker_wallet.js +51 -3
  86. package/dest/test-wallet/worker_wallet_schema.d.ts +7 -4
  87. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  88. package/package.json +42 -44
  89. package/src/bench/client_flows/client_flows_benchmark.ts +40 -31
  90. package/src/bench/utils.ts +7 -2
  91. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +18 -14
  92. package/src/e2e_epochs/epochs_test.ts +14 -2
  93. package/src/e2e_fees/fees_test.ts +13 -6
  94. package/src/e2e_nested_contract/nested_contract_test.ts +6 -4
  95. package/src/e2e_p2p/p2p_network.ts +25 -21
  96. package/src/e2e_p2p/reqresp/utils.ts +23 -2
  97. package/src/e2e_p2p/shared.ts +55 -2
  98. package/src/e2e_token_contract/token_contract_test.ts +16 -8
  99. package/src/fixtures/dumps/epoch_proof_result.json +1 -1
  100. package/src/fixtures/e2e_prover_test.ts +16 -16
  101. package/src/fixtures/elu_monitor.ts +126 -0
  102. package/src/fixtures/get_bb_config.ts +7 -6
  103. package/src/fixtures/setup.ts +25 -15
  104. package/src/fixtures/token_utils.ts +6 -3
  105. package/src/shared/cross_chain_test_harness.ts +13 -9
  106. package/src/shared/gas_portal_test_harness.ts +1 -1
  107. package/src/shared/jest_setup.ts +51 -1
  108. package/src/shared/submit-transactions.ts +1 -1
  109. package/src/shared/uniswap_l1_l2.ts +35 -28
  110. package/src/simulators/lending_simulator.ts +8 -4
  111. package/src/simulators/token_simulator.ts +6 -2
  112. package/src/spartan/setup_test_wallets.ts +107 -18
  113. package/src/spartan/tx_metrics.ts +1 -1
  114. package/src/spartan/utils/config.ts +2 -0
  115. package/src/spartan/utils/index.ts +6 -0
  116. package/src/spartan/utils/nodes.ts +15 -10
  117. package/src/spartan/utils/pod_logs.ts +99 -0
  118. package/src/test-wallet/test_wallet.ts +61 -66
  119. package/src/test-wallet/wallet_worker_script.ts +47 -30
  120. package/src/test-wallet/worker_wallet.ts +51 -3
@@ -6,12 +6,13 @@ import { Fr } from '@aztec/aztec.js/fields';
6
6
  import type { Logger } from '@aztec/aztec.js/log';
7
7
  import { TxHash } from '@aztec/aztec.js/tx';
8
8
  import type { RollupCheatCodes } from '@aztec/aztec/testing';
9
+ import type { EpochCacheInterface } from '@aztec/epoch-cache';
9
10
  import type {
10
11
  EmpireSlashingProposerContract,
11
12
  RollupContract,
12
13
  TallySlashingProposerContract,
13
14
  } from '@aztec/ethereum/contracts';
14
- import { EpochNumber } from '@aztec/foundation/branded-types';
15
+ import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
15
16
  import { timesAsync, unique } from '@aztec/foundation/collection';
16
17
  import { EthAddress } from '@aztec/foundation/eth-address';
17
18
  import { retryUntil } from '@aztec/foundation/retry';
@@ -41,7 +42,7 @@ export const submitComplexTxsTo = async (
41
42
  const spamCount = 15;
42
43
  for (let i = 0; i < numTxs; i++) {
43
44
  const method = spamContract.methods.spam(seed + BigInt(i * spamCount), spamCount, !!opts.callPublic);
44
- const txHash = await method.send({ from, wait: NO_WAIT });
45
+ const { txHash } = await method.send({ from, wait: NO_WAIT });
45
46
  logger.info(`Tx sent with hash ${txHash.toString()}`);
46
47
  txs.push(txHash);
47
48
  }
@@ -150,6 +151,58 @@ export async function awaitCommitteeExists({
150
151
  return committee!.map(c => c.toString() as `0x${string}`);
151
152
  }
152
153
 
154
+ /**
155
+ * Advance epochs until we find one where the target proposer is selected for at least one slot,
156
+ * then stop one epoch before it. This leaves time for the caller to start sequencers before
157
+ * warping to the target epoch, avoiding the race where the target epoch passes before sequencers
158
+ * are ready.
159
+ *
160
+ * Returns the target epoch number so the caller can warp to it after starting sequencers.
161
+ */
162
+ export async function advanceToEpochBeforeProposer({
163
+ epochCache,
164
+ cheatCodes,
165
+ targetProposer,
166
+ logger,
167
+ maxAttempts = 20,
168
+ }: {
169
+ epochCache: EpochCacheInterface;
170
+ cheatCodes: RollupCheatCodes;
171
+ targetProposer: EthAddress;
172
+ logger: Logger;
173
+ maxAttempts?: number;
174
+ }): Promise<{ targetEpoch: EpochNumber }> {
175
+ const { epochDuration } = await cheatCodes.getConfig();
176
+
177
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
178
+ const currentEpoch = await cheatCodes.getEpoch();
179
+ // Check the NEXT epoch's slots so we stay one epoch before the target,
180
+ // giving the caller time to start sequencers before the target epoch arrives.
181
+ const nextEpoch = Number(currentEpoch) + 1;
182
+ const startSlot = nextEpoch * Number(epochDuration);
183
+ const endSlot = startSlot + Number(epochDuration);
184
+
185
+ logger.info(
186
+ `Checking next epoch ${nextEpoch} (slots ${startSlot}-${endSlot - 1}) for proposer ${targetProposer} (current epoch: ${currentEpoch})`,
187
+ );
188
+
189
+ for (let s = startSlot; s < endSlot; s++) {
190
+ const proposer = await epochCache.getProposerAttesterAddressInSlot(SlotNumber(s));
191
+ if (proposer && proposer.equals(targetProposer)) {
192
+ logger.warn(
193
+ `Found target proposer ${targetProposer} in slot ${s} of epoch ${nextEpoch}. Staying at epoch ${currentEpoch} to allow sequencer startup.`,
194
+ );
195
+ return { targetEpoch: EpochNumber(nextEpoch) };
196
+ }
197
+ }
198
+
199
+ logger.info(`Target proposer not found in epoch ${nextEpoch}, advancing to next epoch`);
200
+ await cheatCodes.advanceToNextEpoch();
201
+ }
202
+
203
+ throw new Error(`Target proposer ${targetProposer} not found in any slot after ${maxAttempts} epoch attempts`);
204
+ }
205
+
153
206
  export async function awaitOffenseDetected({
154
207
  logger,
155
208
  nodeAdmin,
@@ -81,24 +81,28 @@ export class TokenContractTest {
81
81
  await publicDeployAccounts(this.wallet, [this.adminAddress, this.account1Address]);
82
82
 
83
83
  this.logger.verbose(`Deploying TokenContract...`);
84
- this.asset = await TokenContract.deploy(
84
+ ({ contract: this.asset } = await TokenContract.deploy(
85
85
  this.wallet,
86
86
  this.adminAddress,
87
87
  TokenContractTest.TOKEN_NAME,
88
88
  TokenContractTest.TOKEN_SYMBOL,
89
89
  TokenContractTest.TOKEN_DECIMALS,
90
- ).send({ from: this.adminAddress });
90
+ ).send({ from: this.adminAddress }));
91
91
  this.logger.verbose(`Token deployed to ${this.asset.address}`);
92
92
 
93
93
  this.logger.verbose(`Deploying bad account...`);
94
- this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({ from: this.adminAddress });
94
+ ({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({
95
+ from: this.adminAddress,
96
+ }));
95
97
  this.logger.verbose(`Deployed to ${this.badAccount.address}.`);
96
98
 
97
99
  // Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender
98
100
  // (so their notes are in scope), but msg_sender in the target must differ from the note owner
99
101
  // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender.
100
102
  this.logger.verbose(`Deploying generic proxy...`);
101
- this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress });
103
+ ({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({
104
+ from: this.adminAddress,
105
+ }));
102
106
  this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`);
103
107
 
104
108
  this.tokenSim = new TokenSimulator(this.asset, this.wallet, this.adminAddress, this.logger, [
@@ -106,7 +110,7 @@ export class TokenContractTest {
106
110
  this.account1Address,
107
111
  ]);
108
112
 
109
- expect(await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).toBe(
113
+ expect((await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).result).toBe(
110
114
  this.adminAddress.toBigInt(),
111
115
  );
112
116
  }
@@ -140,7 +144,9 @@ export class TokenContractTest {
140
144
  await asset.methods.mint_to_public(adminAddress, amount).send({ from: adminAddress });
141
145
  tokenSim.mintPublic(adminAddress, amount);
142
146
 
143
- const publicBalance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress });
147
+ const { result: publicBalance } = await asset.methods
148
+ .balance_of_public(adminAddress)
149
+ .simulate({ from: adminAddress });
144
150
  this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`);
145
151
  expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(adminAddress));
146
152
 
@@ -148,11 +154,13 @@ export class TokenContractTest {
148
154
  await mintTokensToPrivate(asset, adminAddress, adminAddress, amount);
149
155
  tokenSim.mintPrivate(adminAddress, amount);
150
156
 
151
- const privateBalance = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress });
157
+ const { result: privateBalance } = await asset.methods
158
+ .balance_of_private(adminAddress)
159
+ .simulate({ from: adminAddress });
152
160
  this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`);
153
161
  expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(adminAddress));
154
162
 
155
- const totalSupply = await asset.methods.total_supply().simulate({ from: adminAddress });
163
+ const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: adminAddress });
156
164
  this.logger.verbose(`Total supply: ${totalSupply}`);
157
165
  expect(totalSupply).toEqual(tokenSim.totalSupply);
158
166
 
@@ -1 +1 @@
1
- {"proof":"0x0000374024c73b726f6de9946fc77ee612a1e477439bb81cb8b2c6de62020d690a57de6e181ae5996cd0a8f7d1320121acdf5fa0ce0ded260334259e4579dc12da0cbcd300c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af09300b2670115a9834079b6ecf908fe5d3fd0668954a583e95fe4ac21fd5fc1c5950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000279957fa7f30a5180d14fa065ce2bfbed38e94b985f10b5d1fce4a95a36e59f914e8f9c951ad8f46a095c4f22714f5a2736cff40a950b3437e234331a756dccd000000000000000000000000000000000000000000000000000000000000000000e941ac2af91d7ea3e7b789a78258b8053812398befd8a0733610c3cc2c1c682df8aa820bd774c75ddc4b1e29f3b456f7848bba10a44773ec2745205bdf30a80000000000000000000000000000000000638f6bfae659e3b180dde13f7d41d40000000000000000000000000000000000124a260c41ccf0b34d81c5ecff279b00000000000000000000000000000000000000000000000000000000000073e700b352d50cf0ddebdd509f7e1542f13e39e5ba43f10b037e09a0c4a33df430b4000000000000000000000000000000a4a605cd1e2402ae9e1c1be817815e7675000000000000000000000000000000b4f17712dffb27daedda1599d714b82f77000000000000000000000000000000000028606bc16d10c402be38920df228fd0000000000000000000000000000004545a23d67d11d81e3eddff757701eb55e00000000000000000000000000000000001e7c338bdf8ab7cf63beb379e37c380000000000000000000000000000000b8ea432f8085b38a5c665b8a92e6604a0000000000000000000000000000000000005798e83a88c1416f580bcce5f4cbd000000000000000000000000000000269d2efe7f41c9fde13588412228ebea1f00000000000000000000000000000000000556c1b6526cf56282059a5a8259380b582f4ff44659b2eb32295a662bb9a8810b82cbf2ba699075051227aa8a29a808ee7ad865de7de4016a59eaf6bcaafe2edeeade3a60fe5caff1cb10ec576c0c27210ed98117926c6b59184049199fa3afd4bdee7cb86144909acf56a32dad060072f982040c59889c028132b2f4e6b8b33b169c7cbbee52d1da56a39043d7ce19766d6c767b883e7b2b6b4612f5702fc578d0203b144f0a4c58dd235d03869d25a8fc84e0eed62e84846ba30ba604ff385e23215b55325ba40ed55309ae762a193782db7b347fe2cd790ec1909d637cd360611d4ac4098134b48c576ba9d68115282242fa373432e29e5434049fcf9a19e6f6d19713297e90ac2ea20c3248852274438d5e01f4cc60bfcfd214d48dc18f59bdb6a7ed3f5f4ad6ef326dfd1db51e997e94591c00c715af82249a35d5420f1aaa2bb1372e6eba18c7a6224d8b5b261a45b90aa4afe7001992d4d1c763a251eb82f2180746ebf648c18e67543c2d241035ee2f2c4bc714c01e3f673232b644a7851a5772a2407c655da788b24ce206f08c6d4b5e6b3b30d60f99a7f423b539c4d3bb7c660ff1101040d83032159d0fbeb4cd23e0a9645b83c9b59302659f68293ae294f8c24cf3191870023dd9501165619de58350fefd4d1623178c420d5213138d7ac3d2a8d18d20d1580f316a204bdfbd9a87b25d543a38cca240e2a7b552a3d40421236118b87e61033a083a03d25e6e83b32d423bfd886e02d27a1811084f6694efecba9c53071eed74ebbd2c91f0045d7e72e77c52bd487eaede45172b98e1e4c983d6a78eee75028b144408447e9b89b448513b6c105c947cb0e784d7348e2104a3daf4a61a61787555d52701fa12d06842fbba66e9e591cf6e99d863ede2b4ca632c04c826380a1eab861ac1b405d9281ea923a665bb1ec65f7ff7c30bee57ccff7a0d89b58d0671a8671e1a510ecdf8be64fe5a51ea485767006775cdfdf07cec018c679ce8530f496c0e1c93c0d00a6d21ca8e2e5564d1a6f2e1eb534c68957a03e99b77ad0863a0350bc86358d20e34b9e795a5848c6b893329cf9bb822c59b0f119f4ff491b8a1ff2afdeadf940b47c0c1112387e29fb08628821b1ea02389f1d2271a314544635f132c7ab23be7a0ccaf1f15d6478dbcc8a2f434ccfdb4c761d7569288357d2a421feafb963686409a99ee9bd1d601fb17afdb7e8e5899db2c8ebf43993133a56227c6012d585c197c6fbd2a8d60e3528453178fb7e4e3885f6cbba3e7e6ae72cb1157a42b508d4de8eb6b007cc82a3f8cffd7f28236db5b94ca621dda36000cb506a1f1fce1fc240befd031207f127278de901b0df94e2bf1a49b65403cbf07082a0a732d567fe45d9ff33d3d21f4d5d1557454a6b59bfcaa059346f0ef02c50e1964fe8ac61ddf6338b6f7a53de09d6bf245d359d7d0b37e1bad6d71cd967b5928d4c74951a2485f643750663c56562d24e5e302bf76bcfed8cb2640d629444125b56ca5b679559f71d4a7fa644e63998011196a1015ed9af3ebc8ec9b8a97df192de00cae7adf155828519aa0bdc4f9f25e748793fc0968a2d048a1270889fb089c11c277e3cb8395d4c13448a3d2e63091d335f1eccfffada1a932a0418a1b0a4bc266b08a8510f87bf50a90a25806ac09593a757a7841c46c0e2f95289f6a06f7ceb03b51b6c25cfa109483900a1c2b49419751401419cc85e118ac4b519d1e4ee1f0be5ea51559cabde38a0e540fbaf353b4bd94060b534394111ed7368209ac8b1c9bcb0d357a96b7d997167c464641730f5dc62716e2289d1969c226b306d7cd821830318778fa0ef247a53837ebbe36503d42175fc58dd63b5d0673ce158af84948fd51fab8306d5ca6aa3cc66431fe19ed01c9d8dfe89310b5401f5f200906101aa357108f2b8a02f281555131a6e60d6c1d769ba24da1d08f80f73516f15c539992245c3f91e7d44402df966ab704236b1d560644854a0ab5d779a91cb92d0ee3cd9af7c2d98384af4a6fd06770a470c5964d1268c419e1f77e9be01e5c54a982e5ce1bb71e8b854e0d11217d561ffd6b5da4af7fd6a48c468e952326d29ecdc0c55fdafff21d97cd7223333459fb29803c403768fec657c5b56376166ae84a7a0c642c4056c56afe25dd4b527ec5baa60bc7a45a8fa47efbd9711500593dd8111e28984dc6ed99f4461a25b18ef1be130db3de50e9c26cd3faaee0246952137193b4546816443636d583e0a4ac93bdca1dfea71c2385013e75289e036dcd2bb880a5ba64f83cdade779dd4d3e1e22b3e8bfb2101b16351272eaea520e08c07031139adf078dd6048209e09f6f005ca89af419ee80e04fe1d842b40155dce9a1d5e73be5d35a3a137b76bf03c97c71f5f503f538bd2dea5d414ef311aa90623b7f9bb659f312150596eb55aa2bb0475ef1ab8aafd6a5a826ef1861b1c8e63d60728fc45650cd60638719893f7cd7d25604c3025d2ff5f8b5c7f7c6c01262d8df523daa88e8976fbab0db01ff5be218616ea4926eaa4f8f330a14b021a21cb8e61de2a7f6e27fc1c547ce396e35b10007926950f7ff766fd40922aa9167d97baa4f9d1c63cd25eb35458473698a2ffbb82481a6ec924cd2ee5fa0ef20cef74bc34a2034edc10eebb82cdb2f5a4a392bede70f42f0ba83921d6493fdd0dc4d66547c5356c23ea91249781c911c2d4b46d709adce6fff57ca18f5e1d481695a6964bee82165f10d8575d6b297a34ad0fe6db02f88d1913bf2aed68006b00061714750289588091fa49a5a60bba72a12d40652fb8870836db6d771f66111d84a756238702f94b1dbb47edc85c9170ff5f970f8d6d5a8561ed838aa15e072ade281f9c5f055698950fc1dac87fa6c062af30962fc7419606a372554732161ba34128b38aa95697eb4ef6085b38eaeed1c946ed5319fc2ecdc930399d9470241b2a831c13e19d6ea17bbe364a462b0b1708c9f9e609ece2cdd522a7d4185a26b4d9682232ef43619608abeee702606bccf9e063a28bd93ad828bbd5d2abc40045beae5d8a866ba6e0c81eff9639ecc2350a594f06061f7cd2249d3ce2f0702a31a11e69d51bbb8ebcabf677f32871000ca22322758b6fd7d360a6c344c738007fa843cc66c8787203ff79a06d23dc1c0bd8a73b7496861a76efb63980557f016216fd71b2a2b0ca8c41237b1faef627913adace1f1a0cb735628ecface3ce122f3bc8057682162b700c4a521f6cda6c713b863de8d727d0d82584fdb23be02d09adbf32fa028d6ce5063b46de2cd17ac2b92ca50a2746e9125c95085f0800179d78862e215061980d7d8d8cb281e783b8f5c48314bdda9c778a8a86edf5270ba2a65f32f47bcd28832d829cddb5b00de69776870d4164f469fede36ceede2119a74a477ff69358b03bea84b6c5f09ef629ea66aa7f6fc533d1628bd09869c07dde72e1dca93a72fbbb5cbdb141abe017e8fccbad8049060d6a0ce613cc5a12ab70421ae49e4bb9c73c68de6624e6ba9794f6c4f8b58be27d41fd5bf8481fe0633efa5549d2baca52e3686a5c2a4c2cfe581b11f2d267160a4386f121b381214d39e676cb7b46e896c433b20bdcde7536357952c0ca36f0e76557e3d757e982249e8c24f1f58406229b78e2f544f73e10f703c6a18a737986ffb90b8db17dc179cf1c09a8b517d37eab629d083d380c2a784713af5000061385f0052ddac0606fad9907bae335bc8f89bd2afe446c952c8129e8e108e843bc5909d3d7f72e90964e51e934779ebb5692fd00722a4b43d0e3bf7def5130f389dc57598fe00021ca41cdb77a667566126399f0722f011210fac75df845df079388f491af02f0d25fc2446ffcbdaa1709f753cd6225d61b279e70b55153b0a52fac37973727f1b1adc65fb3c6e09479a3cd2cf511f8712958743d004f7d0129fc44a86aea0f50d18ccb3743cd7f7af24b45a5ceaf1e38baa59d8cc2148d56ff532971d39d87a8c140ffabea5b4cc60f3b6f73beac2ecc16af03ab918bd07507b8a2a48be7a817c18e9fe02e2a9248d8d0d4c4701d9cf2192e07d18a5a10682112676ac06eacc0b0b7f161cd106bbd6c240d2759a934ae0ecacee6cd81d3d4c2ab9e1b7550abe560d1858a3c4e3973c3f55fe7f60c4a117a1796ad68536fdab080720c7a1a362592cbf0ce79014944bdce97dd98e7c57f6bf13f76a0c1f09dc70a3067bdfc558e71556aaa7aa38851c290f7f32cdfacb6b0bd331c5b40dc8b6db154fd09fee0e42296088d9e9d5efaa907c7cefa2b5e94f021feee27881ae022d23ee82b2b23ce004228398d4941fd27ce75f3f73442e64f8356c5ff71a79f0c6133533b972d2fa1158df34cb980f2d98d261ae284b05a088fe046ba5d440692fedf56f81b1510113d9f482d1d363a1fa5865a5080dc9a694b449359e090b65065ed1f64e4dc55e219f6977c792602cff3c7105654b5f72ce2e6b79f29ba6eaa0df48b8e5425c6d223d4a8413586b00ab8ad9c45add3b6395a3e03b799abed7bf7e3ed09a44cffe1f285925828080466c055a37e36bd31d98c8c1c5b3a7f4ffca8d0728b752870a2c713cf117b862ae90fb7a52477c1444377ff0a6cc5e358e726d9e6cd743c69420686f7d497838c7060324a2e50068dca9a3f4b1a2d1f6421c60936e2738d8c30986ca441275993ee08564128e8b4741c9bf6f8a09e97d0e697e2b04c0ef85631b5ea3dd92cb3a40298a8246c4d623ac0cb3df0e8cc0350ff6053df4747ce14d15d1b29212ef05b3dac93adddffb55744b26fd5d444ffd99253f1bb735827d2023e7c38ded4986550f5fb3592332ad3dfd7eb1b74622525ecc9b92390585df060a419fa6dca1a965fc4d8897498920066d0d606f0d0682b725848c62612e3b2f0b80250e8ea018752130192e4b09615cffb2d22f4ad680c833e5ce77c1dd4ee51d660de96cc28a0b41f0676545a0330331a2369410c6ca5b8fa7efe7c6e406fa25b46865b095e5ecfa206b8c478054ba25acf2c0d97bd9e6a5b367357229c5661dec9fee59e8d30eb7d96947dc6c9f76f3ba717bc35b64a61e6777189c362432201f1183c270a53de3160131113a4f88622739fe859ba01795f17571fff5c9241f23490e3aebec3b6c647ac3934d83690636084be65bbbfd272865dd6f2db492293dd46beb15ab4c4f1411034581202bd6609cb9d022837ef225f54dc3f440a720f0eade7579ca6646441c2f4624045d155f0a006de0a2c341abae74431f62922bf1cf062041a5f0c611ed9db9dbaaa557e93ea957faec000faade81ed8d112c1736070e5a051f510dee52aa27e2aa712e8ded0063ae87fd7ca0b776664c3f1021b60c3bff883eaae9b874d4895a6ed133dc5e1052bccbed6d70944d0bd6672b03f2e215178460f57919a38a44e412bacac1faecf3ed39ebcdc4d8f36a7f5d990fc417f2e84feb07a2d17b225f92573b401ae36fe10a594b060c16bf50b075b00d52f4206017ebbe54e262c9f9511a292eec211fa12e5dbbd247ec9189d6086b157efd4c12055537118c60ba78d19a10bfa73e73ba093facd809f29800acee8d17e4e618b02b912891a1de15ff594a3751761ca813cc60392879079a522c7856189084d3102048873f267f08b647ca43cc580906242976aabb032ee3fc24d9c22a568abbaae4d3b90cf011b4478359c01f17857531cc5352202c1a8e1a8662442d3656a165f5658be69c19f1a137b1329f13307a7b4563236e206dce50d2a4961ce2b1f3cf3e230c5c574cbb7adc3cbfb8949cd4a13642c176e0141bab528e400d2ff96d5b4a4d6cd6d01de123324c044108c0f3c3cca9bee6f9b02260910a270a7db9047f43179cffed6c50f567ee8164298188708360214a08c4cb887aee7506eeea95763166b4d37dc34a25def1aa9e53510004404a16827e991effda68950dd2cf34edcfe805e719b70b20abc4cec648adef329a1136245a9b5cf0381f912a350008ab5b5bd47d1c83bfc1546e9a2024fa8a1dbd14ebcd3b5a4b869d9ce32604b84eb1d9024ccd524a301f292613316b1be92787fefe9bbb9950d75f56f101868b2a8175010d5975b8e2d136b2c9df21e0248b26f103372375d5f445112f21ae2aeb3dd304bcb6d665c5b7aa5774530d4e2d712d7642336ad13c8d91eb8805751549b17e3cc00f15b4866ff7f938cdc81e8f426a5742e75a4c2b05b0051d297bdea7b7eb0ee67b284093cde821cb8310ce35a5a0c444ef565309e884a8462c1307c2b4a88297cdaf5f15b56f09e121e1a3c7959e760736b04b7d7e9a51481f4b257e2a8ae5f94a5180ff8e4fddf1c380cd011d5ec5e0810aa3c84e2fa29d103cd609cb27836962318f5246b38cdadbc4eda603a7bba0cc45285389b2820b136c9c2392b96ae13ca72eff4a7731f01831fc3b50d9817606df28df9106ca4302a57cd29870a238c3aef468d364a74b086e010314ccf3737114bbd3a0b04ab5111e8638d50f61e657df06e8a2fd2c5c31a39a10e1baec13a432b89d1ea42ee121c8906a8d401ca9d7044c55ca426a34952fe15d162b55090fd644039b8b66932df9adf2a6d123c86c58aa3d311adc1e8616b3726bc923e9ab1830a88fb57dae11282d1c1d9bfa96ddef19d1a5eb95ff81e30ffbd9295ccf37a37ec151c8a1aa2ce1142a627aa173016d575a3efcf06bf4dde3b6c49647b6e0cdeee094928f4a19e2f48967c7564e01ce9d4d3e873b3da01807ceb45b1dbf283fa4ed68e0cb871a7b68c821844b9d820a495081d67635b823b2e4af8d8cd2f46fca3854a143ba19915e480e138c02993a67356c741c7c9028e98c59583c64174e2c64fd6c8e8d2f0a04a9e6efc4fc7e11b8a562c1585805fc14e062f70276bcdaf83b16f8063f1718f1d998792b2b04a5914fc52d780080b11c5f0de3c355aeb53e50c0375be20396a73cae34d7a04c533e1fc6f8e385b550ceed389d656e48e8f74c4b90ebc617cfa5e2c225f6d5909f406ae5e2a3ea751ec3bec6eda3ddb369f1b33b4b6fb71d6846b226cb365d8e04a4604f2f2b623edc2e41d0a513bd7c24308860d540c9006e71bd43a3b1b0a17e375fdd32fe6b596cc06680dd825b65b7ae8ff409974419b82f159f0858784da5ab6c5c46e752f20795a39b447cfa428dd0db6b1ae29105f0a26cbd43ecac4332e1ee841ae04bcd28a8e86054a939f23eb99c5e6c4c7b27ba24f79d73e0500e6a6710aa134cc1e8fb125d2073d19d457185235fecff9d07fb54f1c8786fe15d0b94525c57df49bae434e831de959bbc278878a0b60400018f67e52e03da3a2a6d8284c2cf6b24cad13b7cf2c846083771f42d0c056ad715d8fc1b8765207a7aa58cdbaac92a48f2f9d82ab748dfa62af4152e667c99a715077aeb0651d17e0985da94102d66365401c84dac5cd2f46666d4f2dad3b25411195948204836afffb5640a17a69a5284f974fb0a3464009813ca0cd6843d4f16cbca83ec497bd89f3ce1b6398bbedc19bc506ae67408389c4bda87e1361f830b96e849d6c08d0951ddbc3d5462a3fe1f7a3f584d2ebf20071b1a28b38be6e02ce7f5c325247a28d825c2d977cad1e8584c7ce747b700c3591e3c1de5ca231224e8c61a1df3277b2ee8695914f1d3df29bb8794dd59cdc592843f11d806cd9728983525f54a7ac029e85439efade8a73baaac5197266103594c1ee1abc14c850328ed4cf1ef8a18fb877f8e01504163ed46dee37a8da091f361b356fcc455b02e48c2ba58176b05da0329b7e87f35278ed716aa64789bf60b7c5a85fa05ee80097f2fbc157e78028844e8574f9d36d43bf4c1d9484187417516f9e67828f64a2a20bc5c5c0d6ca591a17db3d92c235a9c2a7b5a746f56b456ce46d654f4352927a555cf335f67921664b389055ce3f4a7b82c4ed99cdc5fd3e3b67a127bb5570a49d8390b51356b42083940afa211357c779033cdaefe82942cb2958ed3166a2a0b3e7b47a4943bb44f83e21de5c2d52c18c7f71d70153a9fb0fc92301ee7a025cf3ad27e484d82be7ffa5e09437e19990ffd3c1a7f050a6d1d31c2194f4b8016c124cac082c8603b212b383ff50a6f5f68f04d20507e476053dc33d2ef249319174ea26ab899c707205790e515f466005ec5134ebecc4ff7b708d316813e771f910fce8d4a51dcf07e78fca57129668dfec1025dadbb7d1091dc79d58972351922ebffb60659b0ea920b8ed54d5b2adbc63122388b95dcc7016e72cf96a9790e5384235107011a9ec2dc21859038d0872416b581fceb060adf002e206eaf0f23c1503131210994c36f5b26ffb53a758de01c002a51c694bbc8b023d7c560af00ac1ba1a265d0cf31798d304897d12847bf2d1f3a89ff0831da9254e0758e1a13b555584c850b01985067fdbdc84ac0593bac2842cd61c5477aef2ed6fa06f8020a15480627e646dc6c947f65a6e7922804b0d8c6db1ce611d81153b5ff481c25ce0572905ec14c7d4897a1b376583f504e88e2a2f36197e51094fcd83bd5c21f19a730184dd723c1b421b5760b94803c44fae3d385da7933d817af97a8146e0f41c896c01c50dcd67f47ca898255b1f6e1348765bff661655f00800f77b31505f94a74d0758b6f91d53e9e93a43ca3ddbde19b0177ce254fb536ff4235acf425896cf69a5b5c01bacbe5aba78ab7161c3a6a83c7baffc895d21becb03844451cd6c63f5f63cf8cee7d02db380343e1a9d6775af58e8ccc9e8796a8d37ffc8e296b7647fe816011c0aeeefec2c3df76752368a6ffbd10da8e796957541516462dece24d2cc06e50fd90f27d526e88aa4a895a68886226d975af1c59b17ed6472e5ed6c592b225f24cb1e2fc961aeb296bbbfdf0a473e4d04d318a97b0d0991026d3b5101382af12e5ad25fdf61ddbbb9c275c0dc2fbde33ee180fc30ba31e07259ae93fbc44b6d6d23481868670293a1527d475c77baae96a84135176f2bdfb295369f4fce7612dfa1ceeb788b359d232ec668091d400f2441f794f9258925b2f888b5a4ff17e6e384c9360fe7298404cba352c53c8e7f63546813c62be0b581ad5b93b98c6971746071578d2e9f1e2f9d6f1cfd0572c1604570b4b9bae41391f172e9f937f86c558a16a77ae34e43208b1d147d2802d3cefbf35b024fd04e72acc3aaf616487c2b44bae15beaebf34fc9692363390e8e7906d769c0d9a8cdc0492d4a4250f59ad9ebaca89f38a89581eeba2a528bfe18f57d453a01d7725b600014d39b4ed425e8bf7a1347859d7e3c3e2260556aef50deb3b6603590ecfca11944c2b378e1cd48e49be0236ec63d958203fe5b0a70889982dc68661f9ba702d7ea05e151c0d5af415167983e874e03ad9a3f04fe6ba1154a09d33b26cdbc62d985cf73a4bd7f0383d3ba07fe6c48743c2e755df260a882149e015352e2db813ef249a74ab3e221084df8883148da363ddb002e8ed1253618b1d35969d61f31651ff530ad10f6479fdf402848821245f313ff8e1e6f440b9e139267d7659a32cc78412f1a34af408ed3414b68dffb38621a60f1e0cbb7dbaabf4932a927f2d1907fa04d4e761f8c8d570f3deefaefb7835778bea9807aad32e1db0eb74771e18bf1c8c22200af9bf3896fa9764c72c12455117c57ca20d461c72bd5f25601618cdba9fd5969c264d371eb76f4cd8a363fd9fdebc1d56447acc40cc4feadb2c190b2359ac8a8f74b7b6b3deaa1018cf6bbf37c4b1a784d437a3777825a387d01b916ee66048bbbb9195f25f5e3e5406529e32b7853ff2bebfb561be99eb89db1cc1786604be7460bd3663b34fd9f309b6ed25af7a6c7998dafdedaa40a45b0105418d649f9e0ec14953ce98b4732f4df4006e018367e226ba74722881567357285579b7d794ec262e5823d623d4d118cc43651389d6bf032f5dc2520c2edae21e963fe599d3ecfc4ce70a7ff358d7bb28c12d5fbc88430d78425d548e675f1e0250373e8e940fad93c1adf69c344b4a8b1f14c8f43036342bc18f0184dd270c0eeb952f89d6adcc09aba616986c3bdc6bc806e1e03f972d276aa85c234e150506ca166e3bf46518c75ff841b1e1c307f2d99f1a8e7530e319cf56ad5b7aaee111eaac15ca18d83bf522723174edc7ba1389b23a984cbff64f30f7b659dac1890f09b8de5584fd230e10747dcfffb3ff9c2704752de828972443fe523376a8200b25f81f8a2c745a633074163cbfd5d3ed16ef79d5703f0fc0f151e5585c7c65302752afcdb25e0c125db09bb73bd1e420e52ad747f295e84e954784c71b376c1101eba81304e6cd26afdc86ebe5804624381e2f1261451660bb50c4c45bfb5e1600b2a9cd851b3512522791a1711b46d514ea262199c9e0cd8845f938d1607109356f4ddddf1815913ac3364c43565ec96c973b33178b88160242a8ca45287e02a19fdadce2c63fd510d42b48641385c2afa44d1046bfcc8a8b3fe67a8e3119029c7fcd15082942b4e91e2b060a9de824d821951a3e3724014c91efe1ff4bba2c1983dda112c6f4a8d14a0c374b168d30579745f4a9aeba1fdd14d2d5d09aa509a2b6d7b07c0d90a8f20e06e31d88dfd895c60a3c65cb11792609aec48bc31b01f580ad8977c303f24cd82796cdd6e6f2e4d63da4b1441e860a86d7767de7d80171e32c41b51050447bdacd54e7074f8afcac3023fbea479f193bc1e4ab45d21e3f1048b1b95b35c4e9049110360acfd836d7fe23738179566884a16924f0cd27c9d1cf61646917fcaea3b20f605d530b45b9dcdb6954dfdf68105973c70d01041ba2ff3eea60ac493c56d0ca410467d05e89b3266ef354ddbc0504afa7ee1426001161e5f95acf80886931d4e0b90159560e958e372b4451ef05f009814b402ee4ca7af55a3fb838a0da163072b5d3bc5cdc1c82e9b2f5ec92162ab9c7870e172c1094812d91df6aaee3988c42d042d94631a7becb52b955c514d9e2e4c8ad01eced7327a26db95441708e33a8a968a0395dbbb2f508deba7f184fb6cf79b12a7b392fe2f501d3435e55a659f2a629fcd9ed0d99e968f5e636f285ccd7da522ac500f0284c9f247058083293d70dbc2fca6729a27bf1ef09e34be4ec49a8c3105e8f5a6a796d7c5a1648340aaf79f792e3d51527b42cba9ba9443d660a359c287b0bbc6f55d2f65ff5c5bb552e25091a13f2bc7d5beab63c0731d9588b611e2c95c8d450016cd0b1890e8df3ddc41844dc87dc65ada2732448335098e17cee00894310d74b07cc0afa9e399aa3f5c292d3efa5a2b7dc4d1ee58a714725d075044733c134c2608dfdfecf300596326fe217422373fde79cdfd756951372bc401c0ef7ffb913dfbb2bd48836bd71aa4ec4fbd96d3d8fe1720b820959d0e587d301bcb50c287eedd2f9e03b2971de6ea98bdeaa62f4f293f371ffb37bcf01a9010582a84dc47e461617b37fa486a5899ae43f4b671fba5926cf924d9a9cc1dea923780ed6b9936015ad8fefaf233f1ccd2450d4cfbf85fe6b0d28dd34f6117a5c064b6359682460cabccdfeec2e4cd8f48f81b9dad8ab686791f75f261791d31e1108fee4bb11b952b15b8ba31eca87ca7392510eba80f8a46d3d80dfbd7bce2c0e4c51771bb232828e065c10c7372e8588a4d7d1b1543c57c44b655220d9308606ceb49094d1aba143a5f5b08d29bd52e9ab13a59de37b6466503eb1d616eeb424f7639f106ce9d5e80cf9fcdca7e0d02cc958492ce4cab974734d46dfce7329161a1b4e3165895dd3137bb8d7ce21bce5901557e6a4d19ca838c79f9cf7bac12ea5a937e7f52554dbaf508efb1263cfb0b233a7350c076299f00e2ad9c2058707883c81a64bc5368ca2f64a7e44db24d98100c18c226c535e11ecbc0438ac6a0bf6184dea429e39214485905696fa76df29d8a0f102f0c1a319880e91f86cd50d1c6576b1b6b0510ef6ed84d5f7b49d759818270a12a8e44da17ba8fdf587680f39e243c4b130a4a82586569464e4fa625c39771395f034356bfa9f106598d90e70376482a2c6d114457f36d1e10ff0afdd9adeab3d0c222a96c13cda7a257521ef28f409669760e262f6dfbd727a74b831a12335ebe3c408add5136aab309a02e3cf3fbb362c9f566a539452b21724c2b909a827959f507fed7ee5d8dc3e53030a373d9e806e7a2ac9fb2bca656d5f4d84f30a550c46708ce506c22ba282200367823d58ce295925350915529d0e5f61848b0afd1fec77fe37568b201f391812b8cac270e06283ec97ccc078d20f0bd80101567c700a667a299af45bb332dd2dd7c7c6a5f456ddebb79e41f805192aaff2faa7e693b0dae73532238199459b04d692b52837c3f70f4912fa65e43e865da3ce123fff5d75d06a9490bf841afd0a13dc2ee06d800403d431f7dc574137f9627eee323908ff18e75df8f0e17a2a21341c8b3d228cc85c756e5008a8a650be402489039994dd3d69edbb9fd236f9043494e54bb5721952d8ec0bef09ff996b2cbb25cd4a35925ba11e6d275d9b4216a4aff940c8feb891de4cee98457e39d6c9c83a107f8e8ded131c1c5cb04d7504f31e7a9346407d84af9e579a9fabfd79766365f835d4480bbca9ea513f5df22dacf078f24430adebe4551698090f4336bf9aa3a95a1d9d15cc96fc1da0846c186793bc464cb53b83e31aa4f14612e2598aa4564ff7033247f6c3312e7bc0b5288a708dadffe1128446cb6c3442eb2086e75a0f21d16ea7b6248326b601e61318872bc2e02f9c6e45d24443d0b17bd7771b2556b39f985b70e9740e5ee63dd50e36dff036e604d055822ddecb7fb4f3722575f0a631e0add358da7d9675a2dd1d5f6bca28a211ad9bf97d42a012781b8a285c7138c5a234e88e1429a303e53e01f8d2c40bd85d0eb2b86c9ca26c20fd96846e17ea56c698f1bd9d6e43e1b4610cedd35e4ceca27a1e846776083290143c64e8f75e35f095fd1dca2f818b60652ec6d0804bdd0fe74ba56fbf2dd9c5a56602bf59482df4925f82318d2d4fede02f7a21860c14ab0a88f4a75a4d9107bd9e80ae050d8f0ad16ced682f261687b30a9417de6b1be04dc76391a9bf3a96c2543feda257cd8f8c42d4e093e1bed0380f0f63205dbc777a93098a42d1f02f5d2c50f4e9ab2327b08f1ac668d92cde2024ddf8a714ab6597ff34f6af94688b22977cd2c985e267fef21dcea041fe75ac1467af307aa6d853daeac22fe3c034b4bec288b72eea8f86399b771c5407d30217913bac43fd3824ab03a3fc8f7ec4b61483524d22e1ac23da98c54216be116b287f15eae4cfdec799c131ca2cc6e0299435045c773629fefc781caad9c052bd0c320d719771984f0f4d38a87f42bcee6336838db163c4f3b2b25a8f4617ed0c2f34b94c4183bfffa99f0ad67c4c46786870a7345ec223ba214a176716c381af198e486fb4b9becd805b8d43cc4706a33cc8453ce78fecbe924df95973499a432a9bc33f8e44330dc66db844b406d142f7d52611dd4f1e82dc99a522a982d4f92ac5776d9ed398eaef4d657393fc7869736a9ecf2276b7c94ec3e42d30375edc0ad76ac7d9e2a359474c55d46581e088d18d8428cabd1d1a096f7b26b191b93e143b1f0b7c7451037fbb1f5ad68dbb8056217d28a57fd42a1322ce7794c439961cecc6dd81097a2609cd4c7d0a30a27c38a099381a26adc42cb32b2d42008d6125779281b7f84ecb37bb6cea4027cff83f3aecb3cb76f8f230fdd884109b51742759873d94fcdcb4816aba0da294b568edab5d845ec17d01821bfb8b1a4c8c3603fe700181d6e5a87585766eebea662aebe97917196ec375fefd37dad95736fc2caff8eb84263d07ad19efc312ba46c042c6c3950bbd912a1a34aa2e62c97d97274686c34058378bf8f4aa45af9b6ce04678b21fa9b1ac4c782712abb1098eb101095122be10ff24c28dcd1e13ee0ea82aaaeb47f96827849a3a05f256904ceb2eaf049863bc0e49ba6fed03d0a36b03292b66591a2cf0dbce5cea86ac87b0bf01981b862a3ea8962ec92185cbc223bfc99adc4898e56f3db6fb56192a67e7f803f79e34c5b84fedbebd805e6f74f145126d43459877a3c1736dda40fa49116a0c8d117dd640bf37b374ed31b36ffa7efa18de64de8601163c4b2a87ba88acbc270dbfb81ca1b1993b0e4da9db2892fcf941819bba14dbeed41ebb2b3340fa310a44e8f1953b8a110d6e9fdea1c7cbb7894c6b8a9f6e4dd9000d5d057114cff5075d4425d471909bfc5b28c51440a0cc9e87376003504471617c1892a66c58040fbfcd0e4c3550b3f75f8acf37179073204e69342e77310362128367d519007c2ef42b20ff9cd13ce768c3bc474ccdb6eb3053aab7a635f1b3165178f515cc6a26cf9c7bbbdd777d7dba6f3134dd9604ae4717d9e8bd429cb153c848e67a3d0014b72cf48549f11aee178fffea2a9bdf515ad96e37a0cab7a19123b5412f0a051947e4e74eda847e3bc25a9e61533283692c7f19341de9e03d5f10b1e972345026374ce4651715c9c60906a2ed0fbe1690a3a08b7f34bd6d676bd7fe50a09b1c04f85ae240a691c7a16fb08268738f18ece452d1ae1a127e58e6fb3f2d21e82c062f50497296f510a35a710e75d9325136a5b6bdb1a7fe9a49d081c0a1a77bdd00000077","publicInputs":"0x24c73b726f6de9946fc77ee612a1e477439bb81cb8b2c6de62020d690a57de6e181ae5996cd0a8f7d1320121acdf5fa0ce0ded260334259e4579dc12da0cbcd300c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af09300b2670115a9834079b6ecf908fe5d3fd0668954a583e95fe4ac21fd5fc1c5950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000279957fa7f30a5180d14fa065ce2bfbed38e94b985f10b5d1fce4a95a36e59f914e8f9c951ad8f46a095c4f22714f5a2736cff40a950b3437e234331a756dccd000000000000000000000000000000000000000000000000000000000000000000e941ac2af91d7ea3e7b789a78258b8053812398befd8a0733610c3cc2c1c682df8aa820bd774c75ddc4b1e29f3b456f7848bba10a44773ec2745205bdf30a873e7124a260c41ccf0b34d81c5ecff279b638f6bfae659e3b180dde13f7d41d41352d50cf0ddebdd509f7e1542f13e39e5ba43f10b037e09a0c4a33df430b4a4a605cd1e2402ae9e1c1be817815e7675128508202fc4d93cc3bceb9ad073766efe80196dc0d29d2bed1413f5a8226cb788c86ffa17dccd213f6bc054bdafbc1c"}
1
+ {"proof":"0x000037401d2b0ace7ac4fbf52963e9b24dcc562a0da10fa52b4fbf5e55b2b6d587d8bae4047fbe8e6ab5633664ebd80615a60c25b32093ef31ccb533d403ce748075fe5b00c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af0930053eeeca2750c390a6b05c04c3eaf7148eaa37fc7f4677b1ea5c58207d438d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a1eb370e7929f0aa3638be832f05670dde96c539c0f377410bba9040bd0b1f281d963227735128f9f3b7d68bdc36e40df56b99c91d4490af4ad84d3cb072ac0000000000000000000000000000000000000000000000000000000000000000009d5ebb50ae6bb050adcc9b3cf872968e1aa337634853aafcce8e9818e94c25011df24633b43c3d7346227c4b84b70d3fb6c9fa5b2cbb698f5d500d60456adb00000000000000000000000000000000000d82c97ccbb9dfb5a09d0a47efd9de0000000000000000000000000000000000bd18f88e1691544daea4ab46e1cfac0000000000000000000000000000000000000000000000000000000000004e300096c6f7ce44c2b7c5d983b6d5b05c9ef1be47c4f5e93a2f99c3a04536c503fc000000000000000000000000000000967b0d22683d31e5b17e35426a3d6734a7000000000000000000000000000000847ef028539261225473627ddd0f8d18cf000000000000000000000000000000000001216ceaa8b5174d1cf9735d7cac060000000000000000000000000000001b55a7b492f10c0956c2b057f45069ab67000000000000000000000000000000000028a596b117f20bf5fe81a3fe27928900000000000000000000000000000021f20a78cb627a4ad3791652f224adff3b0000000000000000000000000000000000166403a92b86cf8b3786c6d6689bc900000000000000000000000000000054aee4df1be0aab6836278b2e07b6253f500000000000000000000000000000000000bc879c8af743bf9fe8cdb9edf641b1230f3240709381e84dcaef98cfbf65d781b584d8ccbfe86c67ade9d01f0d1d62349c9fe3dee70a5deffc982e456f2e08452c9cb24086d2051d6d3a31dcb5feb06a80455cdc287b36e71f3e7cba51872aa6c8956c82691053f96e283cba283101e6b727abb948ffe5dd37332abf949c841a3579018009e22f6843853710e133017603933ba8842f87aed9446150dc346d51ba93b3fec225511b924cbca4122800aff736906500019bd563c31f395a941de6666bd00f954484288f6283f2f6f7f193782db7b347fe2cd790ec1909d637cd360611d4ac4098134b48c576ba9d68115282242fa373432e29e5434049fcf9a19e6f6d19713297e90ac2ea20c3248852274438d5e01f4cc60bfcfd214d48dc18f59bdb6a7ed3f5f4ad6ef326dfd1db51e997e94591c00c715af82249a35d5420f1aaa2bb1372e6eba18c7a6224d8b5b014cb5dfc1ec646156de9d04768fd6a2a5b7e1a020f879c08f173f1ee3a9d1b32fef96cdab484f2f77d87efdd805afec744e88a06892d542f25f940e2039c9a4216d888dec263852e9a12c298ef408f500f571bdd79b46e3a49a4ef7492ffa3b093d108f129bc8fabc56f1bd2f848a51a29a8499cf8151d4e214ece3c00b7a710e80b0a039c8ed59e60e692cf8947b4cb3b67dafc6c7599349e11dea41f953251e4576ab418a0762c51b94b8b726c0191b9a9fe8deaafa19a763172704b9c741197aebb65892636d263b557f12f352eec433d9e52b9f0360ad000c95507f7dd016e962bc889f3cbc9214f0376e8e056e64000e634e1a6d3096e1e8fe9f8082312e21e6d94f1d49a7228756a0b48c87fc8e0a9024a18220db9aa3bf5bcb0883192f5f8fa9c51f62b15af530f829a3ea8438eddb110db79ea7c319bab353a44f532e18769d01b08b50430744dd274abba184186d0bfd3c1b6283144bfae15c1f5330635e714739208da837a09cc6548bf49ef8c062586d248cd1463fd86b0917b00424441ccc83e278ab68e9dfbc2d3b886cfbc42faabf52a2604e9dcd8596bff41636bbec86f259b2bd67479ead9b4aba8a93357a5c8060e949190d49e27adb980b76d2117206f8a5ecd1b102c7b51d63a7b40617228d75fc4823087485568f2a2cdae7da65634e3b4b0fd5dc25bb70823f701c85d2b5e91d640b654a6b06a3ab103648e577a38aa655e57020109e7e58745728bcfccbd21bc9d92332d81af9e706725153301904f2f2f89b51d46d2c41c89d4cc7e2cf4754bdc53b48232ba09f0695bfd151f1f8a0dd25e0bc5166e0a2ea6c7aa9f46ed8fb66a4609b385cbf47189b9a278d4b181ffc40bb8dc3d04b6c27d85480ad74d4f41f3681f992d04bc504fb15ba4f248e0ac1663b5c21e90a0850ebcc6f6890c5241c91860a6d65c2ed080975eefbf10b40ee825a3b1ebea01e4fd3765c41ef376cc517daa33374c13011b483f96780d7b35f09339eea08ce1d1745d4f02c2b307d9a7dd15c22079fff05ea1b17bb0699f34ac1665e73a08fb4102d9f619e889234104501a7dc81fe10270e8eb26dcfe587d05ea86e1bd2cf7f473d16f5c86017d990fd2ea9bc26f4140bd0cfbe16eb960adbb463e4189316cd4b050dc98d8c429b27b6946c3e36b80c0750e4c6c12da827c39aa8629e9a00ebfe52fe8cff2903c900c94894d83801d821ca58e309cdda4c02298081f66f63986c907cdd945ee8e38b36657e629809331fb9b317be60419780a8863930401073817d8bea66f9965a8c1fea2aed5e3a9a2c71d074e1be8cddd55fb3e7c4542bd4cdcebb4bca1a918f08724b8a5d7837c425bda7eefdc26da5e2e276006a0e3bde51244b367b91345d07dcdf075d920eb504f9e3d87b7cf7af3a1fe71704ccd3cd1649fe0225699b5b52139ebeff6c356109f38d1d029cb870c992752805364c021ae170758e1e9095df679aebb8d900aa1e2d347f767481387a099fa34d17b826c2a811e5e8278f10bd6226d8325c04a4182fae94aae2b8a35356b9216fda7a97435704c0d75b2d8de462467e2e56be9305e40a0e57bea2ba7ce90153e456f2b973dd3570c0c3013b49473b7a4642c226273b0974f765ccafec8213c12d5d6fd61c58a3c27540bbf2b9e727ce8cfc1a3114d0561768c28b8130e5e838f1a2f010a00e2e4ffebab8764089ac794c73a0fc0138fe5273e80d6ee57fed97cfcb8b2a6c728aadd1e979a00dffde8b57f7b5fe05f87ced5cdc0e75820cb49b37e2ca1f97dba23c23237f8161234668379d2e511c6546f086f2758e0df88d03f820f1802ecb2da684859b179ecb35fc21054c9605b094220562896abe675b9ff1678e66f2213e202dbc7198471f8c17f984897e2f9c66027cd8a8bfa36246747ce6d0cd5db6a053f79f8157802823e33d4d43ca179d24555a9d788b681f091697992bbd20c1f34fa5f7fef61a5c47bf770379881ef5a4c6a1dc7c15962ffdbe9ed0eae175d472ab0b631baf01b20eb24950fa7a14c7ab41a9d06d3c867997c64a37e29365db762c2e8298cb2f3eb6695205cb512f74e148268134083e82bc60287af30a3e33935523f74c6c3ae553a9448ad66f1249977a6c25eda4a5395d1aac5c7d5d3e9adf60e7fcdfd66aa46a656fed8bc204ec6ad59f9c88f9ef6f8b44fab34cdb5f48ce14a71e355877a265c59e84f70f1577bd08165a24abc5d89ffd9b31eedd510374639003ce5d6bf0179c2a58e7a10a30764f62bc9e9106a7bd2638054e45b2b77c81c205f614ba4334c11420bcab1f96dce8aa24acbb6c5aae9ae7863a52d7e7956b4e22b7003c18fc1afbdf96c02a67f8588cbf1486118b8428310187b584295ecfb280441d7e01fa2b0326930300b4665c2194cb0103f3e46a8c1dcc0d7dd448c47dfc0f5b2e3dc88ffbfcc7ec23bf76b3f5f6bf44bbf311cc41594db63544b93d14bf7f3c65fc6b86cfd4e65013c7c987f174a180ee17209cf420e790612a74f2ac356f674ca54f4ca0b8334324f6bdaac0c9377beb80138d20e17b7a1e2222ad37a0f416cc9b8bbba4f0c9ea2573ff282b6b9b64a4f808ff6aa5317ec987ca06f5c600699bde6ede4608293322a0b8d9d72b49c53a0de6993e301310f7b7ba8b92a7e5b5c07cb00cacb1dc6f034c39b454ba40df58f09545fe26864cf6cf65f8e59ca32456f4cbe9053c950c2c83d5c830d7c636c0d3d5fd0387a6c23bc0b4f22517d1ac35a8df9794d4dd000041dee56b9e0031f77b25ca46a884ae6df09decf0aed625832f97b3ed970e06128b10229a3b7f38aa50792bc52b7fef704a8d0464ceec284d5a5a272963960326eb718dcf1f03c08f271ad563680064f975381ce54dcd60b11b4232a723c77d02c3654c4cf8eb3f5f98dabe1ce91fe51fb585f66400b76ef886ec214d3d4e1e0322681bfd626f3f994153923af9f6aa279bf8b5917ccee77a2091366b8112a20d6408ae20fb63fcb64a1b1f96e749088801384caaa3830bca35d714e917192527667c80833a84ed70b3bff2abcd9b74e1d9e2f420b30f8c400016024faa696e18e740514afb42f9e98877aacec29aadb819ecf70b5c56c8261593c66957faea05b0c746cd36940c78e330d949827586976213306e50ea6b57c7a0f34cfe02b322b5d053ccfdb77b07b61ec65a33cb204c2aa9ec425d936a4ed11f709930db681569f38f6d04e66fb12e579aeba8fa1e4c6d00ba09100818053a7021ce6f81aa11fc375d0800714e30050cbe3ee25939c3949ead3ec9d8642769e0e5811731931e8f39b443e9e34d398a0dcd7871c1b54c1065cd66635702c5c7c3dc334d5d171f84b3c5f7faf0c57b0151193638465707a88e6b54fe235297da592540913f562778c2e80a201d144b27d8da7ca08ae99114c6a342cc93c4d941998106b89d0124aa6c77734f5ff4ab381453d934e7915ad634c5dd113248db818d9506e3136113496a219b7e1b1d92c1e34fc780f4b87e3b8c5e765b6efe6f2832cd304a6757030c05cdb6bf6f0ef20373baefeb765d5778fdbb5e090cfbc48a8d80c123f09d06f9d083a8fa4a99f47c6a2c54ab9372bf9862625c1df946081f9d367f884528305b15dccd93a85fe4ca37b3ae066d21435dcb4af0cdbcc73608f0fa5ad8ab1a05bf5bbb7c9927f31f03fdf729ec3084f162065cef6b225d223dd0807ce45cca1fbd66c2fe4629404099b8d781bd30e747ff602912ac0d2def73c734657c59531e2d7c3a9f9838022a499570fbe7c86adf0dc54d3f2ef1fd2e59e1d7ff70d2d4154fb270fbb70fb71c0a7b077befde6719b3e662505bfc37b424f6e1b8f0633707445b04fca697d2b9e029c8be3e6535967cae209d1957d4f3bf93e44989d1ab04531aa2a2feaa2bc724675123d1dad0fa2411261528f9ef91782c22874359722d54b62174f2cf4fff40a0aaca3ca3ef4e44a77f66d862d739344a921c7de1fc00a2ea2597b2e29a0bee5fdc657683ca7d1b5321979ed7f39c71c9fc1d9735db10895a10746389aa7e8edd78ec20bbb119248babe90986653acfe640f8d1c4920937eb32f6292d3c0b3e7d2817718dfa934f44f7c269d9673b7254e4ce8ef6562a98ac753ae9bcde69d6713862bce21bdad75e7ed259f93872f895994dfaa79a0363c5363d78f373455a8c0f4475157bb5fa469fcaebb22139e76030f75e2487278071ee51810df76484c3e5488185a33ef4fc2bf59836e567380928c02eb32c29dac2bc2f2d13ebe865a01e8689f9da4f0a08c0ab617a1406ae0c5f04999a001390dd0a2751f743eb8bd4d33eaef53131c8fd4fee098d20cdc00695a2396b5322cb1738ae736216f1b114db99e990dd5a2bb76705c6bccd139fc870b61cd610145451f46ce92691259b6892392d3ba0aab9726d396de2c4bea7cf6b1450eda9160955b7649d7036438a4126d5833d623be71374d649edcd468d666f7bf1846b2a161e6b99ac0d07d99b036bfc8d4080c864056fb4e38495bffb08e7a8049c1d28c63ee84b975624c39955ad785b0ce816d90ed66528553652db5e2da27897d822b26bb9c76b3e415e61dd145c2affee307fb4592784e248945b5e0057252ab61b2d4a7e39e47c1816c336df6bb00bd1dbf412fda941a817ca218019c2c449110415d5fd33c28b8c7edd9bc9b2d3b743698d251c79f09c5cc33a628c8ad4d9350d4132dc90c9c9e95b4003c6692c738905383ef21317541ac6964f45dd7a53482b79be7a28ed7eb33ce70cb76810c0b763a8bfb3b8125d33722a8861e977490700dd3a818ddf8822c9531dacfebed6a473a599287a7846fe6154aad3da7592300bad6bb710bc2e6a04fcba27ca9d6a77f565b8403f117bd5a28f9e1b453cb2a51443280aae14725582505c72784ff8a9689a94098869271031b97d84d8b2f45f2e7a8ee41b264085329471deea8da611900eb1f55043c5d5d36a5a8f4e4e40541e7b59a4f5b57e0f7c96241cec698512ff5ecaad61162bec7dbbaa4a0c629745195941175be77f726454231baf6c6d7725951add6fddc080256846033c9556c91a197b65c54f04245aa9356fcec2a69787a79a435bdcc98a29cc00d20624a2d11eb235ae6fdfb2c34d9c8593b0120ff100f9743b27a109fe3bd273bc67dbb7770661ec2c1d931aa1deee676924473a9d369a705944422aeec57db6f3a1ce24e82c60152e5a3308d97151ba03175d333dfd2d9b2eab6e0fef340a07b453b185312facefb814a5e4120e89dea1986c15118389298ec9787570f515a8141635d9660dfbc4f36157c4930c2da472e0193600adfb35d52675d99e3dd3d85bc9257ed7195031479ac64a93edf1f538a263c9a77332cfcb2179740f551cadcfb6061a8d1da6fd89ee8a09c2a1bb07db0a7ed7b927f7c0dfe51af6b0c1dac1d9d3e7bb5c1f58314ff3eddea73c992d2ed7a45ef6117f08a08908b70932b05c72b211b84d19fbec2a4fc61f00a1b509f43347bab0dab36075bdbcb1d6879e5a5e44f2e36c1b6f2df69b90302334b46ee6b4e347defc48efa97e51a0173b13507513cb0d6d1f29f62ae5733e7ea55f71d3e55cf54ff5a82d36cb43d639e4b9143512f018e516b5026876b79528e8f1eb42cbc6b381b50eb74154150a055a1283969878a375173fd1b1c62731daf637535a290b1717d9f58c77b2785beb51e297b4011a5cdc12c6e4e1570f6de5e964d4bedc21bbf440d17ced5b30e4bfe30b17a4ac013a320e87c9ce9358d6846c62f84f620a033b9c5fe48322f0e3de41c6d95350a873490371320681b5d82f7394bcb22113ea3c69b7057b7821b491147f945c7301b4472af1d0661c4ad1713ceaf1543c46e66ab6fcb8a8738b9051567ceb56b2efc0471a74bd19f7c2254cec6a2f10d94853b2bb0d9328f00dca76854f5b1991b851691e50e90b380f8a8842d8939ca5cd5cbb9f1733c533184d206ca54fe766e08427197573d439b29a48e284766d99e59dedd2fd15ad933b22ac10f777d90e081ace08a1132012eff9c7a621f3a509e82c0ba3f33e906cbbc494b83125f9d06650b518ec4edb63b193031084723e98adfaa4cbbb27c93d7b35ea51405317a54b5f481eaac5d4dd11a08e296c86673346757684c5caf3f01fe726d74b7ff532fde7b402e60b0e87d4bcb00bda5813294b5fccd6583104c15b7cef983f5e50510f4ac4134934b05bd150a742244457f7f4315a86cbface176db18cf863931d993bdac31481d871b9ebc4fbf0519e13731e3752238ae7c218cd8a94fd12341609fabe5d2581866e4a7498a33b7ddb54be930050c84c65caee1d7ff803053529f4bbbe732947d73d5abf41824f7bb103d45fc32b34c8c49ccc0958b87074e6c2b7d2b7310dda03396186e4e9694b9b7eb2c07dd6d4a78cf90ec480d5451f6456c2531fb81713d7e73ce5b65e9e9832e13a9f17c9dadca98fcc91c6c98f6ef2f5e480a16d04951ca7bde9bc1aa9ffd13013c4567e2decc5fbb3781af953b4ac1e7934203604e498b26fd15fae48951496307b306fbd413e0fc0999df3c50e41e81e4d16a32fce06e6a58a9a591d7ff2d4b6c12749d61dbf251a56ca0d973d1838f6cc32de226d4b0d20f985cf8983d9360011402eaf888b6ff0ddbea2c9492928b78e35e003e3b54833b8d0d456187c89dbcace080ae7e0b43aa01166b0545d4374ad490318a5d7a39faf93b890e73f3d56f7e1a157ba5bdd1020cf842f33305f40ad3fd420f3d67e4121d7f92f00bb6a2c01087a5560ac2d79489bf1e342d401f8f34dc12f852072b635384e6ac1c76a924f72ffc74a30abb68c79a2c7cbb012d4c6438902539b67d9c93cec8e0bcdf9865d195b7eece823e918814c65213851fc7d1cb42c54f20771c6adebaebe1e10f047fbc64bf0190b5960aaa9c32c32d68221e6c001bc9298f33c51f0a4ae2e1c1ad94f03566aa814ba1d0487c0072a3037381bd907988c3cffa0b252a9e72b677a6b64956e1c3ccdfeb22201ce22f134144ec63519b89e29b8776018eb8d2b7552930505eccc7d7eefd5c015b5e2c925a2bf332c17fa59aad8bb54fb28a03af550d2226f3df5535bc4f1307184b2f3eef1cb9f0c1bcfd9fe92c11f0cead04c3082770666346c66c406f8f924509e8f588789f7e103a0042da54e90d798eee38370fe5e6f32ff8f5863fcc656cf6cbf5d211768880430d1606f1d3c959186790269c8c619cf203ee69271a570d932a75f7dd90ca51506f2dceb63751cdbd75e9b9923deed45fe705aacbc0322ad09065a332f45d5113adaacb5855e823be6851a42fc65a450e3207e7fb8933c043b7acfe8141e22294db301a0bdb309b263e452f45b76ce7e51033232626e7b9bcc781f34a1ad891a7863c5e21470084e2a46695ae3e7211b2c69b717ccebc5ae03d44f6eb48b551a06ec9aac1734b47e22b9b79a55cd50f3326094514e79204b92256e5a33ca520679613333f74b7c147b2af2ccd2093e4d58dddb06129ad1f5a02af3c44b324a1d908d0b1bcaaa5a72c0195a9ef8f98280861bcd1b58e05c3871307b77f92e151248b8ac6063e0f8839a77a7d98606106988ff12870041d5c562dd077fce999b2e764223c913522f4be6986d1853091cd61cb80baaaff0f7c649cbf4f8f079fb1e850a548c9ca1120092178bbc5390a35b6a85cb627b6ac30ca8bef76cf24b6122b85885ba19e48b21b9eb56f70734abae2c7431d995898586bfce75dda17aff250e6968b574b21610b4835b34303b3246541215c71d219fc2e36211e316fbe722e3303b22d73d33ab2f351e8fbb0c2aaf702d53df5f97873128c29cc7d275bd20898a9803b56329bee77ab0d05f9981b4e7e54177ff2999db00b8c9f201834200303ec7d0955b5f395aa53b8cbe126b9b352d50514b34688223f03c17b00b501c8762e1c44344325fc5a1ec11ba7593fd06a3db105c97608c51dd42b2a8cae2074d2c7886df4ef332c5362c07e1c6f8006bf661cca91aab99418668fc5180f91f8d38680952f286b84e1e474e1e638144f385db37c6b6516d69aa8abe09ae6e2f098127f71dc9354f80091c9686f6f03099d409954522684da024d8b14d57270ebae3aa7255a3d3e0b568e0269e41886819fc89bc5002366f36c43a2ca269202378b89d398099553c09c98195a44003919252ec98b922bbfcea7f56d5cea0c90ec41550fa9e57e2a1112480ec41d51d46d16da89809c30044e710c20335cef8304a4c50425b1ff0c04510594222e1f88009b9bd3bd29d50cbcefc76f19bb20b16981bcf821cdc4a5fa276f6735c7d7c19932a757ea7a929849a03da50d7dee81522b3c346f098d9b7a0687000a0b19c9b25215a97d8ffa26c64f9f69bf9dc422c2d13ae924c9830523f113d6f6b74cbf578ebf9387643a98a04705e2c58107913deecac5f62013746f5a7bce6c05e2ee82e2797cc09e494c1f5559c726c7eb324ef72f32baedd84d92f44424609f41a0417f3b82695f65a7f9135ec5ea377a821624a1badea514b298f4b6216cebe76e7e6976e90678041e7a15c823883e2481386a9b070b0a081e5beb3fa2a7fbcac9a0de04c1261528ad8796bdde20891ec20352ad5523b8e808220fa16254a3daf7a72b6db2ab247c8dde97ac3db8d24d71bae4ebfd0a1e3037176913c8be42a1fcb123aa50799d28c421801a14425612d2c2634dde04c6f955e793db42b36767bb438bd35e419194137b9b7c4c261aa1018397c930334511e764f80c51bc6e79f0b4462de39a23b4db71f8e1eb0904b341dbfa71e50f9adea17d8d2a331a66275123faa18af436ca773b936bbb0e63c080ad5059e6b3d284013d1e3c15af021bc94aac1a6e7697abb88edffb6113d82ae18d6ba390dbbd58531c1f099d6c2a91c2ccf4626a140121930eeae06b13a296b2e1a80702d7efd08afb2818e28e2310d2315d9bea60605735321df837577b7081764a3989cc367c1b316c76777f8714759c8d8ae497274d0781312073e600b521d718762bce44836bbea72a38054cc7e75242b6eca88091d8d46fb04e28454430c08fd25f2857a1f3bd2f5677993bbcd470d6763cba2fc6529ca40e1d96752be033796304b5b95e2a75b7080446dd476faa6b3d5dd959b2a76ea04e0eab9a4f11295ef502e9dd29830d129d31d09e6a856587d36694641f7136578192fc573de0e50ffbbc8d4f0f0bf574344165b95f4c1c6f3362e6ff3610a23ee5daf8bacb21014126ffcf166e5a1c9ad60e3a82223f2ad09794818aa7ce26cb347873fe27d0ffcf63fe400ff70dd51e7233cad2a4d41451e258f0165494fb7d61c7bd574330846048901f46b492670fd05b2ad3816ff57cb9d9e1f6ff921ec06c93763e31f2d51687fa9ded4f81462933e455ad73f7c4787847e7e77c3cb8cbe720121ad0a2240cad647b8c1b429c25b22b90d889dd76570b53562bed0f5f530f3e28d62f020515889858729c101eb18b2c3300f8b5dd3d97628b828bac9783dadf7f3657a2029ff686851d8077f3ee87258a65073c3d63120bd355885347768b371dca45618353bdad01c3bde20d5b493271d06da4ca959689c8df11ea1b1c99cf26d90b8066fd2b03540cda23167f7eea76fb631e86ff305192371a0da4fd9fd331d97e307390316a7f296f64d945c1ad30edf4317ad680f2913213707e8ac1ae138c4f91a371a5e43d5848ee4c47681fa0443b6c21b1340b8cf5da35b64c7508b61c69d06a2ad9ac09742306975eb8b7af69f957a9debaf826cd58e28fa1004d7bbd6e60d847ad494dc8e0cd3579bff0e137fb087c143b70823a5fcc7a93f766055acaf1960558271a9f215d584fcd142113cc7dff6d7008fc27ad5005ad70174789bba1674102f8dafb55e53215eb637c6f52fda2bf9079b1e16402e13ff06261f54e71131fe16f0d4cabaa3ab95dfb593c1ac46b009aa7ffeb71fb843d57ff74908db1d64cab3080aca8ac059208f489a3b9c0abc83cf22e0526df38165ac201be12d13f2b581bd680df45a4ab2b69d16b97193ded7b829232174abf2b57b0d0634f306cd52b44e0514365fe138adc09a9797b7f962b7a7d5c4ed2088d4711ab983dc0eb0f10cba67009c1487dd4a178ac2477fc8f06efac26b22486494d53174b5a507ee53b96bacde952282fc7cf40fec74f7b2c8714d003734454eae18193d5b011bfbf713887ac271705dbbf52f60b798aab781e2e0a9fc18d95090da3604142a2ebce2d5f2cd7a57b7d271d6fea3045a5134a7587e79c5533eddffd290347b8c198132af43c7255b2e3c57b50cf087c2517d33ff84e72bfc3c34c3fd0b4a0cc01b1b0ed73d26eb9eb95cf650963371db6a35efb08f1654b464cfa0b9f0cd34581a67939eaa7b9a87b5b502b3ccb836f009eb5da5ff71db980ba9a9db2852071f3031d0007a6721f14fb3c74f535510d77046fa59e8a13a9f333737cfa8cbf0b805443687a8678321e80ba8c4a77a671770af4112ba8e39fdf475ee35d44e708703dda51b5c4c74fcdf54ec9c3b49464f575875a10f36c4e6c13203efecfab1391488a4a88eca9ad20d99405eec8aecd8a0c13c2454ef58a6efd94123780545360945c2ad36da244a07d55f4879edf37a113ab04ce5497c796ce07391a6e0a8a200d866e9083b19625828fd093f17f19966608756af95f330cca23084c49eee3d1feccf20c6c8285d537fde29e6f63ddcfffe7d3771ea8e0f709bc6b5b14198811be0985d0de150b9148d71422a5fd35623ca1cd8f93a20257e9d5779a505a89e01483712fe6f4d547a8b799763f0d3bde623f7484f69d22df5ba1586b955c44328daf70c164ce963f1276c7e24a0350405a9f2d54d60494c57d6959228ca64f3232158736227bcd1d79d18da4c0cacd4bb5c53957b3f1032a56a98ba00ec555507c9438105f9d9ad8b33f42f6fbf22c9ab3a84fd2e7fafe5525fc8e50ec0498a303ef9a74e7a0eb4ae1a53e132705d4bc208be2636689c2998577ce8bb51c3441f671a537fbf9abceaee7e35ccd96ee07ced7d2bc701900638e424227181e69c2bb7dc0a249fee4c9f98f70d6596491652ccea30e160d58096b0afc3e52e3c2616c62bb1714bd91a9c6f067feda66a9b5f908937c49a431aca4c4955c3c0598f2d932c84ea2bca2fd8b61f36ead421675b5ef3a167f49b7abdff33ae0e5bad421d2892726a75557ced30eda4664c666823c80dc865b1a95f83fcaa826f70036e0f2f7d8cf457863c345b08d528e77a9257f83caa6c04149f669254d3db77c67a0720e9a75d57691708f36591d2185d1a1ca61d2c2b3f47ebc5b6cdb78d7b4eb62fedc57fd72deb59150eb40ddc4bf5d29b055f6d268d9fc9c13a65225f44c704108e1fd52cf5d81af773ae9f2a23eb6177446344032da5e60f67aadffe281bce1985a46fb23f587de1d00cc981cf892f8d32a4221a8d8c0749121bd43b6946b81b1380b588db51f32a9d09e7f2189b06ca68d0e69545aac503adfd83a29bea9505fc3afeb9e11d0b9c618151163dd979bf6a32ae403598edcf68dc49ad7061061f63e90dc541625362963b76fc4b3bf9760b5652a4e78668ff02d83fa40461b02ae7728202beafdbeb841703f467e0d83dfd909f3ecd8be5b1b07901b1a062e100a4b405d0f8b75a1dfa0ff99310d4da652cd13131013f7dc765b6a8e9d45ec816bd712d321ddf8b167bb515ae04433acfd143d993b6fdbcd5364668add4b0d51b0c9a9040babcfffdd0add341551392856f335b913218d88b0febd958ee965717fa8876913b46960863ec32ad62cfdc1eae3770c76831fbd6febb4afbbb1424006c806b45c000d89396bf2a4dbb46f22708bc4d65cdc68d28fa7589ea6463bf1c8742d186eaba1d0ce77f8fc6e56cb297bdfd14f476eebbd4d7ff496a90f66508cbcfb0d0e332c591342db760578687090867fa35e58940d02148b31c6521900b01f00dc72389a9a05893c6dc10473e244a2fcb0590fb8fa6dc65bde2bd2e1d1b8c8546c6d65aed1f2cdb0bbc20bdfe2c9f9dcbd469a1fefdf4ebc21b40e62618b8333b7988aff9ebf25802e4192a698c396c091c9dbd4e594f3e852b40230e2d12ddbbdc7bbe0dcc785f0308036aa3b6ef339d346516ca1ec3d8e155f8be6d08a1ca67b2358f2a8b758f47783f4f34f974deea124920b941068340602efed12f7cb373868484322b652a77bc82f8b2ca3ccc67610294dc83aa9a9c04eedb701d7e4a736dc29b687c119b55b14611670921a946fddac0a9ab699de77632aaae07ed5403f182e4e202051fd4ae3481a221319cdc9e6958f0ea147443de3ab25222b405289e0d05addc5ff61ae69b4cbc069b9ef27a9e3ff37db6b9a4a118c7f5109a0ec73359a8bb6deb507b98ac8da6bc7e1f12b48b4d4f5246eed25140c2dc00279899805474826336b1be018b04de37f82bd5a088dafdf7808141749ccdf002b5e4cb9a33758415cfe2d15a5660dfcc18b1aa4e54ca184557bf30aa29e9cc14946eaef239d83fb7bebc340078e94b57cc1648d78853ce51aeed5a8bbc18102fdcccee178f1c8da645b121e778e5c8f1fc8a233daabbf5bea172cbd88df97b1001dc0612a38c7159acd4c8a3b75a383461520772cc716cb4f65234be05d65c1f1becb411fe5519cb8c9b022b6158177bdf24d65aa778ad0893b1628574610301767d14bec7bf8ad62b4e3340733cc005a444a934b65954de178e59a1c1f11d1cb3f177b7611fc98a0e07dd6e121ef2079c123523b70f976ef5c695c2a798440b9d458bc50fd2745afcac4f5a6958c7ae814dbb4587494b7494b98719507cac0bfb54d3c82395746c616e7a25e63005cd5948fcfbbe3fd8aa9b21ada39223b12d40dc8bb25832445122349e3cf9ed7795f942c34c44282f81d40a9bfa1df4991fee2598345f996ef808adf1b0ec72ccbe17ce688b520fdb3e84bea8c57699482b5d90c534d4c0945fae2deb0310b36c721c490c3536ec93d79aba399967addb1bcca91827b54cf3479b14fa93d70995c94006bbe9b0af7601eab7830612c50218eedfd48b3a229ad3d345552f320120c3e24d9659f423bad70a9be3aa3ce5e00b22b2de41404d37c2ac7424f13526e37ad4042d640c31cabc57b24f72d878f800e2321600093ac31a503ca1c6090c767af78e3c26cb3651d9d49d33c7a8008f0a7ea8c1963dbbc53d145130339a98a79d839b35fdf84eca22619e9327006912196203b3e22a91c62e181baaf86a771d1c39f0c3d0a6c4d2014280971ec22845064bfe5d8d67cae71f725ea8c84f87a127d7a1178ce290e2040270bc41f9ffb71473351ea395808ac920200fd04d484b7e502b6616ee0cfd96c3ddfd6c1cfff316f4a75ea09c94a3ac11d378a7598dab2bc76e8ac09c9a82a2ec47df3b0bcb572c25b106bc37cb71ee911bc841d489f2a0b6d4a9a1fde23d8061800447c0306c18c109f10c4fb3dcd9f091426b1756a8ec97e7c1ac79dfd4fc2b3f89b47bd68a13ceabce414bc9bfc357474d300e4a28bbe77566ac14f45cf07bcfcc9c5d6cb01082800db45568f8f11a59e79eee0b6680d35854cf0cfba4a362593396adf2b008260c014f79f3204f63e0a3f479c38c014f3121056f12133c73b662602f497117fc6eebe870a9cfe3f7f2ef654a6eeb473b8cab1c9010ffc2f1b62ecdd779200bff45b80e8575f5dc4e9bd8ad5b90561fbe2613575753ba6d898612895bd5d628ebb7970d821248fe62e1150175ecd0dd008360aa2df3ecd508ce56f4d738a3206867693760649d25b5e8443dd1eb6d805ebd783fc013fe6679f7af5ed6b2f22b7e66648b897aac615564c70cc97dd10c15c5f40864272a5a9468efda9fa89215dd201087439719ca9c03d69e7c8e289e608d099bc7e1b42406a949528854a603fac705ac5ede99fa6695dc1bf6a08205d92b9a8a768dfb3dc95e2bf73bbb16266cb44d88254c241c0a335f5b0c90f49eb6103dfc6184539d67aacad913a9b12d28f5b9a39430fc5a7bf54c8b12fcadf2f639d01d3559090681727d7aaab1600e6de0adf471a573d03b40b9c1b28e3df2dace28324398453527b8ca9c79b5850037087a896d4319291b0dd22eebd0cae462d8669cb4dfaa864a35c9df0f356109645a8a5a633e0da0b88e0686ab470d03b26f95b0dc8bf6987fc721f6fd440500000077","publicInputs":"0x1d2b0ace7ac4fbf52963e9b24dcc562a0da10fa52b4fbf5e55b2b6d587d8bae4047fbe8e6ab5633664ebd80615a60c25b32093ef31ccb533d403ce748075fe5b00c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af0930053eeeca2750c390a6b05c04c3eaf7148eaa37fc7f4677b1ea5c58207d438d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a1eb370e7929f0aa3638be832f05670dde96c539c0f377410bba9040bd0b1f281d963227735128f9f3b7d68bdc36e40df56b99c91d4490af4ad84d3cb072ac0000000000000000000000000000000000000000000000000000000000000000009d5ebb50ae6bb050adcc9b3cf872968e1aa337634853aafcce8e9818e94c25011df24633b43c3d7346227c4b84b70d3fb6c9fa5b2cbb698f5d500d60456adb4e30bd18f88e1691544daea4ab46e1cfac0d82c97ccbb9dfb5a09d0a47efd9de16c6f7ce44c2b7c5d983b6d5b05c9ef1be47c4f5e93a2f99c3a04536c503fc967b0d22683d31e5b17e35426a3d6734a706df32e687ab5239d9f94c6f781ec93788f5335299e7824d699b722d99f94c8ed3a09014fba85c2a284d5019e3bc749d"}
@@ -4,12 +4,7 @@ import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses';
4
4
  import { type Logger, createLogger } from '@aztec/aztec.js/log';
5
5
  import type { AztecNode } from '@aztec/aztec.js/node';
6
6
  import { CheatCodes } from '@aztec/aztec/testing';
7
- import {
8
- BBCircuitVerifier,
9
- type ClientProtocolCircuitVerifier,
10
- QueuedIVCVerifier,
11
- TestCircuitVerifier,
12
- } from '@aztec/bb-prover';
7
+ import type { ClientProtocolCircuitVerifier } from '@aztec/bb-prover';
13
8
  import { BackendType, Barretenberg } from '@aztec/bb.js';
14
9
  import type { DeployAztecL1ContractsReturnType } from '@aztec/ethereum/deploy-aztec-l1-contracts';
15
10
  import { Buffer32 } from '@aztec/foundation/buffer';
@@ -68,7 +63,10 @@ export class FullProverTest {
68
63
  private provenComponents: ProvenSetup[] = [];
69
64
  private bbConfigCleanup?: () => Promise<void>;
70
65
  private acvmConfigCleanup?: () => Promise<void>;
71
- circuitProofVerifier?: ClientProtocolCircuitVerifier;
66
+ /** Returns the proof verifier from the prover node (for test assertions). */
67
+ get circuitProofVerifier(): ClientProtocolCircuitVerifier | undefined {
68
+ return this.proverAztecNode?.getProofVerifier();
69
+ }
72
70
  provenAsset!: TokenContract;
73
71
  context!: EndToEndContext;
74
72
  private proverAztecNode!: AztecNodeService;
@@ -106,7 +104,9 @@ export class FullProverTest {
106
104
  await publicDeployAccounts(this.wallet, this.accounts.slice(0, 2));
107
105
 
108
106
  this.logger.info('Applying base setup: deploying token contract');
109
- const { contract: asset, instance } = await TokenContract.deploy(
107
+ const {
108
+ receipt: { contract: asset, instance },
109
+ } = await TokenContract.deploy(
110
110
  this.wallet,
111
111
  this.accounts[0],
112
112
  FullProverTest.TOKEN_NAME,
@@ -121,7 +121,7 @@ export class FullProverTest {
121
121
 
122
122
  this.tokenSim = new TokenSimulator(this.fakeProofsAsset, this.wallet, this.accounts[0], this.logger, this.accounts);
123
123
 
124
- expect(await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).toBe(
124
+ expect((await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).result).toBe(
125
125
  this.accounts[0].toBigInt(),
126
126
  );
127
127
  }
@@ -168,9 +168,6 @@ export class FullProverTest {
168
168
 
169
169
  await Barretenberg.initSingleton({ backend: BackendType.NativeUnixSocket });
170
170
 
171
- const verifier = await BBCircuitVerifier.new(bbConfig);
172
- this.circuitProofVerifier = new QueuedIVCVerifier(bbConfig, verifier);
173
-
174
171
  this.logger.debug(`Configuring the node for real proofs...`);
175
172
  await this.aztecNodeAdmin.setConfig({
176
173
  realProofs: true,
@@ -178,7 +175,6 @@ export class FullProverTest {
178
175
  });
179
176
  } else {
180
177
  this.logger.debug(`Configuring the node min txs per block ${this.minNumberOfTxsPerBlock}...`);
181
- this.circuitProofVerifier = new TestCircuitVerifier();
182
178
  await this.aztecNodeAdmin.setConfig({
183
179
  minTxsPerBlock: this.minNumberOfTxsPerBlock,
184
180
  });
@@ -310,16 +306,20 @@ export class FullProverTest {
310
306
  } = this;
311
307
  tokenSim.mintPublic(address, publicAmount);
312
308
 
313
- const publicBalance = await fakeProofsAsset.methods.balance_of_public(address).simulate({ from: address });
309
+ const { result: publicBalance } = await fakeProofsAsset.methods
310
+ .balance_of_public(address)
311
+ .simulate({ from: address });
314
312
  this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`);
315
313
  expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address));
316
314
 
317
315
  tokenSim.mintPrivate(address, publicAmount);
318
- const privateBalance = await fakeProofsAsset.methods.balance_of_private(address).simulate({ from: address });
316
+ const { result: privateBalance } = await fakeProofsAsset.methods
317
+ .balance_of_private(address)
318
+ .simulate({ from: address });
319
319
  this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`);
320
320
  expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address));
321
321
 
322
- const totalSupply = await fakeProofsAsset.methods.total_supply().simulate({ from: address });
322
+ const { result: totalSupply } = await fakeProofsAsset.methods.total_supply().simulate({ from: address });
323
323
  this.logger.verbose(`Total supply: ${totalSupply}`);
324
324
  expect(totalSupply).toEqual(tokenSim.totalSupply);
325
325
  }
@@ -0,0 +1,126 @@
1
+ import { appendFileSync } from 'node:fs';
2
+ import { type EventLoopUtilization, type IntervalHistogram, monitorEventLoopDelay, performance } from 'node:perf_hooks';
3
+
4
+ const NANOS_PER_MS = 1_000_000;
5
+
6
+ /** Samples event-loop utilization, delay histogram, and heap usage per test, writing columnar text to a file. */
7
+ export class EluMonitor {
8
+ private filePath: string;
9
+ private intervalMs: number;
10
+ private timer: ReturnType<typeof setInterval> | undefined;
11
+ private lastELU: EventLoopUtilization | undefined;
12
+ private histogram: IntervalHistogram;
13
+ private testName: string | undefined;
14
+ private testStart: number | undefined;
15
+ private eluSamples: number[] = [];
16
+
17
+ constructor(filePath: string, intervalMs?: number) {
18
+ this.filePath = filePath;
19
+ this.intervalMs = intervalMs ?? 2000;
20
+ this.histogram = monitorEventLoopDelay({ resolution: 20 });
21
+ }
22
+
23
+ /** Begin sampling for a test. Writes a header line and starts the periodic sampler. */
24
+ startTest(testName: string): void {
25
+ this.stopTest();
26
+
27
+ this.testName = testName;
28
+ this.testStart = performance.now();
29
+ this.eluSamples = [];
30
+
31
+ appendFileSync(this.filePath, `\n=== Test: ${testName} ===\n`);
32
+ appendFileSync(
33
+ this.filePath,
34
+ padColumns('TIME', 'ELU', 'EL_DLY_P50', 'EL_DLY_P99', 'EL_DLY_MAX', 'HEAP_MB') + '\n',
35
+ );
36
+
37
+ this.lastELU = performance.eventLoopUtilization();
38
+ this.histogram.enable();
39
+
40
+ this.timer = setInterval(() => this.sample(), this.intervalMs);
41
+ // Allow the process to exit even if the timer is still running.
42
+ this.timer.unref();
43
+ }
44
+
45
+ /** Stop sampling and write a summary line. */
46
+ stopTest(): void {
47
+ if (!this.timer) {
48
+ return;
49
+ }
50
+
51
+ // Take a final sample before stopping.
52
+ this.sample();
53
+
54
+ clearInterval(this.timer);
55
+ this.timer = undefined;
56
+ this.histogram.disable();
57
+ this.histogram.reset();
58
+
59
+ this.writeSummary();
60
+
61
+ this.lastELU = undefined;
62
+ this.testName = undefined;
63
+ this.testStart = undefined;
64
+ this.eluSamples = [];
65
+ }
66
+
67
+ /** Alias for stopTest — call on process exit to flush any remaining data. */
68
+ stop(): void {
69
+ this.stopTest();
70
+ }
71
+
72
+ private sample(): void {
73
+ const newELU = performance.eventLoopUtilization();
74
+ const delta = performance.eventLoopUtilization(newELU, this.lastELU);
75
+ this.lastELU = newELU;
76
+
77
+ const elu = delta.utilization;
78
+ this.eluSamples.push(elu);
79
+
80
+ const p50 = this.histogram.percentile(50) / NANOS_PER_MS;
81
+ const p99 = this.histogram.percentile(99) / NANOS_PER_MS;
82
+ const max = this.histogram.max / NANOS_PER_MS;
83
+ const heapMb = Math.round(process.memoryUsage().heapUsed / (1024 * 1024));
84
+
85
+ const now = new Date();
86
+ const time = [now.getHours(), now.getMinutes(), now.getSeconds()].map(n => String(n).padStart(2, '0')).join(':');
87
+
88
+ const line = padColumns(
89
+ time,
90
+ elu.toFixed(2),
91
+ `${p50.toFixed(1)}ms`,
92
+ `${p99.toFixed(1)}ms`,
93
+ `${max.toFixed(1)}ms`,
94
+ String(heapMb),
95
+ );
96
+ appendFileSync(this.filePath, line + '\n');
97
+
98
+ // Reset histogram so next sample only reflects the new interval.
99
+ this.histogram.reset();
100
+ }
101
+
102
+ private writeSummary(): void {
103
+ if (this.eluSamples.length === 0 || this.testStart === undefined) {
104
+ return;
105
+ }
106
+
107
+ const mean = this.eluSamples.reduce((a, b) => a + b, 0) / this.eluSamples.length;
108
+ const maxElu = Math.max(...this.eluSamples);
109
+ const sorted = [...this.eluSamples].sort((a, b) => a - b);
110
+ const p90Elu = sorted[Math.floor(sorted.length * 0.9)] ?? maxElu;
111
+ const durationS = ((performance.now() - this.testStart) / 1000).toFixed(1);
112
+
113
+ let summary = `--- Summary: mean_elu=${mean.toFixed(2)} max_elu=${maxElu.toFixed(2)} p90_elu=${p90Elu.toFixed(2)} duration=${durationS}s`;
114
+ if (maxElu > 0.85) {
115
+ summary += ' WARNING:ELU>0.85';
116
+ }
117
+ summary += ' ---\n';
118
+
119
+ appendFileSync(this.filePath, summary);
120
+ }
121
+ }
122
+
123
+ function padColumns(...cols: string[]): string {
124
+ const widths = [11, 7, 12, 12, 12, 8];
125
+ return cols.map((col, i) => col.padEnd(widths[i] ?? 10)).join('');
126
+ }
@@ -13,8 +13,10 @@ const {
13
13
  BB_SKIP_CLEANUP = '',
14
14
  TEMP_DIR = tmpdir(),
15
15
  BB_WORKING_DIRECTORY = '',
16
- BB_NUM_IVC_VERIFIERS = '1',
16
+ BB_NUM_IVC_VERIFIERS = '8',
17
17
  BB_IVC_CONCURRENCY = '1',
18
+ BB_CHONK_VERIFY_MAX_BATCH = '16',
19
+ BB_CHONK_VERIFY_BATCH_CONCURRENCY = '6',
18
20
  } = process.env;
19
21
 
20
22
  export const getBBConfig = async (
@@ -41,16 +43,15 @@ export const getBBConfig = async (
41
43
  const bbSkipCleanup = ['1', 'true'].includes(BB_SKIP_CLEANUP);
42
44
  const cleanup = bbSkipCleanup ? () => Promise.resolve() : () => tryRmDir(directoryToCleanup);
43
45
 
44
- const numIvcVerifiers = Number(BB_NUM_IVC_VERIFIERS);
45
- const ivcConcurrency = Number(BB_IVC_CONCURRENCY);
46
-
47
46
  return {
48
47
  bbSkipCleanup,
49
48
  bbBinaryPath,
50
49
  bbWorkingDirectory,
51
50
  cleanup,
52
- numConcurrentIVCVerifiers: numIvcVerifiers,
53
- bbIVCConcurrency: ivcConcurrency,
51
+ numConcurrentIVCVerifiers: Number(BB_NUM_IVC_VERIFIERS),
52
+ bbIVCConcurrency: Number(BB_IVC_CONCURRENCY),
53
+ bbChonkVerifyMaxBatch: Number(BB_CHONK_VERIFY_MAX_BATCH),
54
+ bbChonkVerifyConcurrency: Number(BB_CHONK_VERIFY_BATCH_CONCURRENCY),
54
55
  };
55
56
  } catch (err) {
56
57
  logger.error(`Native BB not available, error: ${err}`);