@aztec/end-to-end 3.0.0-nightly.20251114 → 3.0.0-nightly.20251118
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.
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BBWASMBundlePrivateKernelProver } from '@aztec/bb-prover/client/wasm/bundle';
|
|
1
|
+
import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
|
|
3
2
|
import { createLogger, logger } from '@aztec/foundation/log';
|
|
4
3
|
import { Timer } from '@aztec/foundation/timer';
|
|
5
4
|
import { WASMSimulator } from '@aztec/simulator/client';
|
|
@@ -7,26 +6,6 @@ import { Decoder } from 'msgpackr';
|
|
|
7
6
|
import { readFile, readdir, writeFile } from 'node:fs/promises';
|
|
8
7
|
import { join } from 'node:path';
|
|
9
8
|
import { ProxyLogger, generateBenchmark } from './benchmark.js';
|
|
10
|
-
async function createProver(config = {}, log) {
|
|
11
|
-
const simulator = new WASMSimulator();
|
|
12
|
-
if (!config.bbBinaryPath || !config.bbWorkingDirectory) {
|
|
13
|
-
return {
|
|
14
|
-
prover: new BBWASMBundlePrivateKernelProver(simulator, 16, log),
|
|
15
|
-
type: 'wasm'
|
|
16
|
-
};
|
|
17
|
-
} else {
|
|
18
|
-
const bbConfig = config;
|
|
19
|
-
return {
|
|
20
|
-
prover: await BBNativePrivateKernelProver.new({
|
|
21
|
-
bbSkipCleanup: false,
|
|
22
|
-
numConcurrentIVCVerifiers: 1,
|
|
23
|
-
bbIVCConcurrency: 1,
|
|
24
|
-
...bbConfig
|
|
25
|
-
}, simulator, log),
|
|
26
|
-
type: 'native'
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
9
|
async function main() {
|
|
31
10
|
ProxyLogger.create();
|
|
32
11
|
const proxyLogger = ProxyLogger.getInstance();
|
|
@@ -36,10 +15,9 @@ async function main() {
|
|
|
36
15
|
}
|
|
37
16
|
const flows = await readdir(ivcFolder);
|
|
38
17
|
logger.info(`Flows in ${ivcFolder}: \n${flows.map((flowName)=>`\t- ${flowName}`).join('\n')}`);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}, proxyLogger.createLogger('bb:prover'));
|
|
18
|
+
const simulator = new WASMSimulator();
|
|
19
|
+
const log = proxyLogger.createLogger('bb:prover');
|
|
20
|
+
const prover = new BBBundlePrivateKernelProver(simulator, log);
|
|
43
21
|
const userLog = createLogger('chonk_flows:data_processor');
|
|
44
22
|
for (const flow of flows){
|
|
45
23
|
userLog.info(`Processing flow ${flow}`);
|
|
@@ -85,7 +63,7 @@ async function main() {
|
|
|
85
63
|
if (!profile.stats.timings.proving) {
|
|
86
64
|
profile.stats.timings.proving = provingTime;
|
|
87
65
|
}
|
|
88
|
-
const benchmark = generateBenchmark(flow, currentLogs, profile.stats, privateExecutionSteps,
|
|
66
|
+
const benchmark = generateBenchmark(flow, currentLogs, profile.stats, privateExecutionSteps, 'native', error);
|
|
89
67
|
await writeFile(join(ivcFolder, flow, 'benchmark.json'), JSON.stringify(benchmark, null, 2));
|
|
90
68
|
proxyLogger.flushLogs();
|
|
91
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2e_prover_test.d.ts","sourceRoot":"","sources":["../../src/fixtures/e2e_prover_test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAEL,KAAK,6BAA6B,EAGnC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"e2e_prover_test.d.ts","sourceRoot":"","sources":["../../src/fixtures/e2e_prover_test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAEL,KAAK,6BAA6B,EAGnC,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAInE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAMvD,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAGlE,OAAO,EAEL,KAAK,iBAAiB,EAIvB,MAAM,uBAAuB,CAAC;AAU/B;;;;;GAKG;AAEH,qBAAa,cAAc;IA6BvB,OAAO,CAAC,sBAAsB;IAE9B,OAAO,CAAC,UAAU;IA9BpB,MAAM,CAAC,UAAU,SAAU;IAC3B,MAAM,CAAC,YAAY,SAAS;IAC5B,MAAM,CAAC,cAAc,SAAO;IAC5B,OAAO,CAAC,eAAe,CAAmB;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAG,UAAU,CAAC;IACpB,YAAY,EAAG,UAAU,CAAC;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,gBAAgB,EAAG,kBAAkB,EAAE,CAAC;IACxC,eAAe,EAAG,aAAa,CAAC;IAChC,QAAQ,EAAG,cAAc,CAAC;IAC1B,SAAS,EAAG,SAAS,CAAC;IACtB,cAAc,EAAG,cAAc,CAAC;IAChC,UAAU,EAAG,UAAU,CAAC;IACxB,QAAQ,EAAG,cAAc,CAAC;IAC1B,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,oBAAoB,CAAC,EAAE,6BAA6B,CAAC;IACrD,WAAW,EAAG,aAAa,CAAC;IAC5B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,mBAAmB,CAAc;IAClC,WAAW,EAAG,2BAA2B,CAAC;IAC1C,aAAa,EAAG,UAAU,CAAC;gBAGhC,QAAQ,EAAE,MAAM,EACR,sBAAsB,EAAE,MAAM,EACtC,QAAQ,EAAE,UAAU,EACZ,UAAU,UAAO;IAa3B;;;;OAIG;IACG,kBAAkB;IAsDlB,KAAK;YAuIG,YAAY;IAS1B,QAAQ,GAAI,CAAC,EACX,MAAM,MAAM,EACZ,OAAO,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,EACjD,UAAS,CAAC,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAA2B,KAChG,OAAO,CAAC,IAAI,CAAC,CAAwD;IAElE,QAAQ;IAgBR,iBAAiB;CA8CxB"}
|
|
@@ -2,6 +2,7 @@ import { createArchiver } from '@aztec/archiver';
|
|
|
2
2
|
import { EthAddress } from '@aztec/aztec.js/addresses';
|
|
3
3
|
import { createLogger } from '@aztec/aztec.js/log';
|
|
4
4
|
import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
5
|
+
import { BackendType, Barretenberg } from '@aztec/bb.js';
|
|
5
6
|
import { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
6
7
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
7
8
|
import { SecretValue } from '@aztec/foundation/config';
|
|
@@ -122,9 +123,9 @@ const { E2E_DATA_PATH: dataPath } = process.env;
|
|
|
122
123
|
}
|
|
123
124
|
this.acvmConfigCleanup = acvmConfig.cleanup;
|
|
124
125
|
this.bbConfigCleanup = bbConfig.cleanup;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
126
|
+
await Barretenberg.initSingleton({
|
|
127
|
+
backend: BackendType.NativeUnixSocket
|
|
128
|
+
});
|
|
128
129
|
const verifier = await BBCircuitVerifier.new(bbConfig);
|
|
129
130
|
this.circuitProofVerifier = new QueuedIVCVerifier(bbConfig, verifier);
|
|
130
131
|
this.logger.debug(`Configuring the node for real proofs...`);
|
|
@@ -145,9 +146,7 @@ const { E2E_DATA_PATH: dataPath } = process.env;
|
|
|
145
146
|
await this.context.cheatCodes.rollup.markAsProven();
|
|
146
147
|
this.logger.verbose(`Main setup completed, initializing full prover PXE, Node, and Prover Node`);
|
|
147
148
|
const { wallet: provenWallet, teardown: provenTeardown } = await setupPXEAndGetWallet(this.aztecNode, {
|
|
148
|
-
proverEnabled: this.realProofs
|
|
149
|
-
bbBinaryPath: bbConfig?.bbBinaryPath,
|
|
150
|
-
bbWorkingDirectory: bbConfig?.bbWorkingDirectory
|
|
149
|
+
proverEnabled: this.realProofs
|
|
151
150
|
}, undefined, true);
|
|
152
151
|
this.logger.debug(`Contract address ${this.fakeProofsAsset.address}`);
|
|
153
152
|
await provenWallet.registerContract(this.fakeProofsAsset);
|
|
@@ -242,6 +241,7 @@ const { E2E_DATA_PATH: dataPath } = process.env;
|
|
|
242
241
|
}
|
|
243
242
|
// clean up the full prover node
|
|
244
243
|
await this.proverNode.stop();
|
|
244
|
+
await Barretenberg.destroySingleton();
|
|
245
245
|
await this.bbConfigCleanup?.();
|
|
246
246
|
await this.acvmConfigCleanup?.();
|
|
247
247
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/end-to-end",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.20251118",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./dest/index.js",
|
|
6
6
|
"inherits": [
|
|
@@ -25,42 +25,43 @@
|
|
|
25
25
|
"formatting": "run -T prettier --check ./src && run -T eslint ./src"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aztec/accounts": "3.0.0-nightly.
|
|
29
|
-
"@aztec/archiver": "3.0.0-nightly.
|
|
30
|
-
"@aztec/aztec": "3.0.0-nightly.
|
|
31
|
-
"@aztec/aztec-node": "3.0.0-nightly.
|
|
32
|
-
"@aztec/aztec.js": "3.0.0-nightly.
|
|
33
|
-
"@aztec/bb-prover": "3.0.0-nightly.
|
|
34
|
-
"@aztec/
|
|
35
|
-
"@aztec/blob-
|
|
36
|
-
"@aztec/
|
|
37
|
-
"@aztec/
|
|
38
|
-
"@aztec/
|
|
39
|
-
"@aztec/
|
|
40
|
-
"@aztec/
|
|
41
|
-
"@aztec/
|
|
42
|
-
"@aztec/
|
|
43
|
-
"@aztec/
|
|
44
|
-
"@aztec/
|
|
45
|
-
"@aztec/
|
|
46
|
-
"@aztec/
|
|
47
|
-
"@aztec/
|
|
48
|
-
"@aztec/noir-
|
|
49
|
-
"@aztec/noir-
|
|
50
|
-
"@aztec/noir-
|
|
51
|
-
"@aztec/
|
|
52
|
-
"@aztec/
|
|
53
|
-
"@aztec/
|
|
54
|
-
"@aztec/prover-
|
|
55
|
-
"@aztec/
|
|
56
|
-
"@aztec/
|
|
57
|
-
"@aztec/
|
|
58
|
-
"@aztec/
|
|
59
|
-
"@aztec/
|
|
60
|
-
"@aztec/
|
|
61
|
-
"@aztec/
|
|
62
|
-
"@aztec/
|
|
63
|
-
"@aztec/
|
|
28
|
+
"@aztec/accounts": "3.0.0-nightly.20251118",
|
|
29
|
+
"@aztec/archiver": "3.0.0-nightly.20251118",
|
|
30
|
+
"@aztec/aztec": "3.0.0-nightly.20251118",
|
|
31
|
+
"@aztec/aztec-node": "3.0.0-nightly.20251118",
|
|
32
|
+
"@aztec/aztec.js": "3.0.0-nightly.20251118",
|
|
33
|
+
"@aztec/bb-prover": "3.0.0-nightly.20251118",
|
|
34
|
+
"@aztec/bb.js": "3.0.0-nightly.20251118",
|
|
35
|
+
"@aztec/blob-lib": "3.0.0-nightly.20251118",
|
|
36
|
+
"@aztec/blob-sink": "3.0.0-nightly.20251118",
|
|
37
|
+
"@aztec/bot": "3.0.0-nightly.20251118",
|
|
38
|
+
"@aztec/cli": "3.0.0-nightly.20251118",
|
|
39
|
+
"@aztec/constants": "3.0.0-nightly.20251118",
|
|
40
|
+
"@aztec/entrypoints": "3.0.0-nightly.20251118",
|
|
41
|
+
"@aztec/epoch-cache": "3.0.0-nightly.20251118",
|
|
42
|
+
"@aztec/ethereum": "3.0.0-nightly.20251118",
|
|
43
|
+
"@aztec/foundation": "3.0.0-nightly.20251118",
|
|
44
|
+
"@aztec/kv-store": "3.0.0-nightly.20251118",
|
|
45
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251118",
|
|
46
|
+
"@aztec/merkle-tree": "3.0.0-nightly.20251118",
|
|
47
|
+
"@aztec/node-keystore": "3.0.0-nightly.20251118",
|
|
48
|
+
"@aztec/noir-contracts.js": "3.0.0-nightly.20251118",
|
|
49
|
+
"@aztec/noir-noirc_abi": "3.0.0-nightly.20251118",
|
|
50
|
+
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251118",
|
|
51
|
+
"@aztec/noir-test-contracts.js": "3.0.0-nightly.20251118",
|
|
52
|
+
"@aztec/p2p": "3.0.0-nightly.20251118",
|
|
53
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251118",
|
|
54
|
+
"@aztec/prover-client": "3.0.0-nightly.20251118",
|
|
55
|
+
"@aztec/prover-node": "3.0.0-nightly.20251118",
|
|
56
|
+
"@aztec/pxe": "3.0.0-nightly.20251118",
|
|
57
|
+
"@aztec/sequencer-client": "3.0.0-nightly.20251118",
|
|
58
|
+
"@aztec/simulator": "3.0.0-nightly.20251118",
|
|
59
|
+
"@aztec/slasher": "3.0.0-nightly.20251118",
|
|
60
|
+
"@aztec/stdlib": "3.0.0-nightly.20251118",
|
|
61
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251118",
|
|
62
|
+
"@aztec/test-wallet": "3.0.0-nightly.20251118",
|
|
63
|
+
"@aztec/validator-client": "3.0.0-nightly.20251118",
|
|
64
|
+
"@aztec/world-state": "3.0.0-nightly.20251118",
|
|
64
65
|
"@iarna/toml": "^2.2.5",
|
|
65
66
|
"@jest/globals": "^30.0.0",
|
|
66
67
|
"@noble/curves": "=1.0.0",
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BBNativePrivateKernelProver } from '@aztec/bb-prover/client/native';
|
|
3
|
-
import { BBWASMBundlePrivateKernelProver } from '@aztec/bb-prover/client/wasm/bundle';
|
|
1
|
+
import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
|
|
4
2
|
import { createLogger, logger } from '@aztec/foundation/log';
|
|
5
3
|
import { Timer } from '@aztec/foundation/timer';
|
|
6
4
|
import { WASMSimulator } from '@aztec/simulator/client';
|
|
@@ -11,26 +9,7 @@ import { Decoder } from 'msgpackr';
|
|
|
11
9
|
import { readFile, readdir, writeFile } from 'node:fs/promises';
|
|
12
10
|
import { join } from 'node:path';
|
|
13
11
|
|
|
14
|
-
import { type Log,
|
|
15
|
-
|
|
16
|
-
type NativeProverConfig = { bbBinaryPath?: string; bbWorkingDirectory?: string };
|
|
17
|
-
|
|
18
|
-
async function createProver(config: NativeProverConfig = {}, log: Logger) {
|
|
19
|
-
const simulator = new WASMSimulator();
|
|
20
|
-
if (!config.bbBinaryPath || !config.bbWorkingDirectory) {
|
|
21
|
-
return { prover: new BBWASMBundlePrivateKernelProver(simulator, 16, log), type: 'wasm' as ProverType };
|
|
22
|
-
} else {
|
|
23
|
-
const bbConfig = config as Required<NativeProverConfig>;
|
|
24
|
-
return {
|
|
25
|
-
prover: await BBNativePrivateKernelProver.new(
|
|
26
|
-
{ bbSkipCleanup: false, numConcurrentIVCVerifiers: 1, bbIVCConcurrency: 1, ...bbConfig },
|
|
27
|
-
simulator,
|
|
28
|
-
log,
|
|
29
|
-
),
|
|
30
|
-
type: 'native' as ProverType,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
12
|
+
import { type Log, ProxyLogger, generateBenchmark } from './benchmark.js';
|
|
34
13
|
|
|
35
14
|
async function main() {
|
|
36
15
|
ProxyLogger.create();
|
|
@@ -41,10 +20,9 @@ async function main() {
|
|
|
41
20
|
}
|
|
42
21
|
const flows = await readdir(ivcFolder);
|
|
43
22
|
logger.info(`Flows in ${ivcFolder}: \n${flows.map(flowName => `\t- ${flowName}`).join('\n')}`);
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
23
|
+
const simulator = new WASMSimulator();
|
|
24
|
+
const log = proxyLogger.createLogger('bb:prover');
|
|
25
|
+
const prover = new BBBundlePrivateKernelProver(simulator, log);
|
|
48
26
|
|
|
49
27
|
const userLog = createLogger('chonk_flows:data_processor');
|
|
50
28
|
|
|
@@ -96,7 +74,7 @@ async function main() {
|
|
|
96
74
|
if (!(profile.stats.timings as ProvingTimings).proving) {
|
|
97
75
|
(profile.stats.timings as ProvingTimings).proving = provingTime;
|
|
98
76
|
}
|
|
99
|
-
const benchmark = generateBenchmark(flow, currentLogs, profile.stats, privateExecutionSteps,
|
|
77
|
+
const benchmark = generateBenchmark(flow, currentLogs, profile.stats, privateExecutionSteps, 'native', error);
|
|
100
78
|
await writeFile(join(ivcFolder, flow, 'benchmark.json'), JSON.stringify(benchmark, null, 2));
|
|
101
79
|
proxyLogger.flushLogs();
|
|
102
80
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
QueuedIVCVerifier,
|
|
11
11
|
TestCircuitVerifier,
|
|
12
12
|
} from '@aztec/bb-prover';
|
|
13
|
+
import { BackendType, Barretenberg } from '@aztec/bb.js';
|
|
13
14
|
import { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
14
15
|
import type { BlobSinkServer } from '@aztec/blob-sink/server';
|
|
15
16
|
import type { DeployL1ContractsReturnType } from '@aztec/ethereum';
|
|
@@ -183,9 +184,7 @@ export class FullProverTest {
|
|
|
183
184
|
this.acvmConfigCleanup = acvmConfig.cleanup;
|
|
184
185
|
this.bbConfigCleanup = bbConfig.cleanup;
|
|
185
186
|
|
|
186
|
-
|
|
187
|
-
throw new Error(`Test must be run with BB native configuration`);
|
|
188
|
-
}
|
|
187
|
+
await Barretenberg.initSingleton({ backend: BackendType.NativeUnixSocket });
|
|
189
188
|
|
|
190
189
|
const verifier = await BBCircuitVerifier.new(bbConfig);
|
|
191
190
|
this.circuitProofVerifier = new QueuedIVCVerifier(bbConfig, verifier);
|
|
@@ -212,11 +211,7 @@ export class FullProverTest {
|
|
|
212
211
|
this.logger.verbose(`Main setup completed, initializing full prover PXE, Node, and Prover Node`);
|
|
213
212
|
const { wallet: provenWallet, teardown: provenTeardown } = await setupPXEAndGetWallet(
|
|
214
213
|
this.aztecNode,
|
|
215
|
-
{
|
|
216
|
-
proverEnabled: this.realProofs,
|
|
217
|
-
bbBinaryPath: bbConfig?.bbBinaryPath,
|
|
218
|
-
bbWorkingDirectory: bbConfig?.bbWorkingDirectory,
|
|
219
|
-
},
|
|
214
|
+
{ proverEnabled: this.realProofs },
|
|
220
215
|
undefined,
|
|
221
216
|
true,
|
|
222
217
|
);
|
|
@@ -321,6 +316,7 @@ export class FullProverTest {
|
|
|
321
316
|
// clean up the full prover node
|
|
322
317
|
await this.proverNode.stop();
|
|
323
318
|
|
|
319
|
+
await Barretenberg.destroySingleton();
|
|
324
320
|
await this.bbConfigCleanup?.();
|
|
325
321
|
await this.acvmConfigCleanup?.();
|
|
326
322
|
}
|