@aztec/end-to-end 0.0.1-commit.181e2d196 → 0.0.1-commit.189eedb3
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.
- package/README.md +27 -0
- package/dest/bench/client_flows/benchmark.d.ts +15 -1
- package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
- package/dest/bench/client_flows/benchmark.js +17 -0
- package/dest/bench/client_flows/client_flows_benchmark.d.ts +2 -2
- package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
- package/dest/bench/client_flows/client_flows_benchmark.js +32 -36
- package/dest/bench/utils.d.ts +1 -1
- package/dest/bench/utils.d.ts.map +1 -1
- package/dest/bench/utils.js +8 -3
- package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +5 -4
- package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
- package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +28 -19
- package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -4
- package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
- package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +39 -7
- package/dest/e2e_deploy_contract/deploy_test.d.ts +3 -3
- package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
- package/dest/e2e_deploy_contract/deploy_test.js +2 -1
- package/dest/e2e_epochs/epochs_test.d.ts +25 -2
- package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
- package/dest/e2e_epochs/epochs_test.js +88 -14
- package/dest/e2e_fees/fees_test.d.ts +5 -2
- package/dest/e2e_fees/fees_test.d.ts.map +1 -1
- package/dest/e2e_fees/fees_test.js +44 -11
- package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
- package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
- package/dest/e2e_nested_contract/nested_contract_test.js +6 -7
- package/dest/e2e_p2p/inactivity_slash_test.d.ts +1 -1
- package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
- package/dest/e2e_p2p/inactivity_slash_test.js +4 -3
- package/dest/e2e_p2p/p2p_network.d.ts +11 -10
- package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
- package/dest/e2e_p2p/p2p_network.js +49 -31
- package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
- package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
- package/dest/e2e_p2p/reqresp/utils.js +42 -13
- package/dest/e2e_p2p/shared.d.ts +27 -12
- package/dest/e2e_p2p/shared.d.ts.map +1 -1
- package/dest/e2e_p2p/shared.js +72 -59
- package/dest/e2e_token_contract/token_contract_test.d.ts +3 -3
- package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
- package/dest/e2e_token_contract/token_contract_test.js +15 -13
- package/dest/fixtures/authwit_proxy.d.ts +3 -3
- package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
- package/dest/fixtures/authwit_proxy.js +4 -0
- package/dest/fixtures/e2e_prover_test.d.ts +6 -5
- package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
- package/dest/fixtures/e2e_prover_test.js +13 -17
- package/dest/fixtures/fixtures.d.ts +70 -1
- package/dest/fixtures/fixtures.d.ts.map +1 -1
- package/dest/fixtures/fixtures.js +65 -0
- package/dest/fixtures/get_bb_config.d.ts +1 -1
- package/dest/fixtures/get_bb_config.d.ts.map +1 -1
- package/dest/fixtures/get_bb_config.js +5 -5
- package/dest/fixtures/ha_setup.d.ts +2 -2
- package/dest/fixtures/ha_setup.d.ts.map +1 -1
- package/dest/fixtures/ha_setup.js +1 -1
- package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
- package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
- package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
- package/dest/fixtures/setup.d.ts +49 -11
- package/dest/fixtures/setup.d.ts.map +1 -1
- package/dest/fixtures/setup.js +161 -31
- package/dest/fixtures/setup_p2p_test.d.ts +10 -6
- package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
- package/dest/fixtures/setup_p2p_test.js +8 -8
- package/dest/fixtures/token_utils.d.ts +1 -1
- package/dest/fixtures/token_utils.d.ts.map +1 -1
- package/dest/fixtures/token_utils.js +2 -5
- package/dest/fixtures/utils.d.ts +2 -2
- package/dest/fixtures/utils.d.ts.map +1 -1
- package/dest/fixtures/utils.js +1 -1
- package/dest/forward-compatibility/wallet_rpc_client.d.ts +7 -0
- package/dest/forward-compatibility/wallet_rpc_client.d.ts.map +1 -0
- package/dest/forward-compatibility/wallet_rpc_client.js +15 -0
- package/dest/forward-compatibility/wallet_service.d.ts +3 -0
- package/dest/forward-compatibility/wallet_service.d.ts.map +1 -0
- package/dest/forward-compatibility/wallet_service.js +109 -0
- package/dest/install_legacy_contracts.d.cts +10 -0
- package/dest/install_legacy_contracts.d.cts.map +1 -0
- package/dest/legacy-jest-resolver.d.cts +3 -0
- package/dest/legacy-jest-resolver.d.cts.map +1 -0
- package/dest/shared/cross_chain_test_harness.d.ts +4 -2
- package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
- package/dest/shared/cross_chain_test_harness.js +22 -18
- package/dest/shared/gas_portal_test_harness.d.ts +8 -5
- package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
- package/dest/shared/gas_portal_test_harness.js +19 -10
- package/dest/shared/submit-transactions.d.ts +1 -1
- package/dest/shared/submit-transactions.d.ts.map +1 -1
- package/dest/shared/submit-transactions.js +1 -1
- package/dest/shared/uniswap_l1_l2.d.ts +1 -1
- package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
- package/dest/shared/uniswap_l1_l2.js +55 -40
- package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
- package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
- package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
- package/dest/simulators/lending_simulator.d.ts +10 -3
- package/dest/simulators/lending_simulator.d.ts.map +1 -1
- package/dest/simulators/lending_simulator.js +26 -14
- package/dest/simulators/token_simulator.d.ts +1 -1
- package/dest/simulators/token_simulator.d.ts.map +1 -1
- package/dest/simulators/token_simulator.js +2 -2
- package/dest/spartan/setup_test_wallets.d.ts +4 -2
- package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
- package/dest/spartan/setup_test_wallets.js +77 -40
- package/dest/spartan/tx_metrics.d.ts +18 -4
- package/dest/spartan/tx_metrics.d.ts.map +1 -1
- package/dest/spartan/tx_metrics.js +73 -20
- package/dest/spartan/utils/config.d.ts +9 -32
- package/dest/spartan/utils/config.d.ts.map +1 -1
- package/dest/spartan/utils/config.js +1 -0
- package/dest/spartan/utils/index.d.ts +2 -1
- package/dest/spartan/utils/index.d.ts.map +1 -1
- package/dest/spartan/utils/index.js +2 -0
- package/dest/spartan/utils/nodes.js +2 -2
- package/dest/spartan/utils/pod_logs.d.ts +25 -0
- package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
- package/dest/spartan/utils/pod_logs.js +74 -0
- package/dest/test-wallet/test_wallet.d.ts +25 -23
- package/dest/test-wallet/test_wallet.d.ts.map +1 -1
- package/dest/test-wallet/test_wallet.js +146 -87
- package/dest/test-wallet/utils.d.ts +3 -3
- package/dest/test-wallet/utils.d.ts.map +1 -1
- package/dest/test-wallet/utils.js +2 -7
- package/dest/test-wallet/wallet_worker_script.js +7 -2
- package/dest/test-wallet/worker_wallet.d.ts +5 -4
- package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
- package/dest/test-wallet/worker_wallet.js +6 -2
- package/dest/test-wallet/worker_wallet_schema.d.ts +110 -224
- package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
- package/dest/test-wallet/worker_wallet_schema.js +14 -2
- package/package.json +46 -42
- package/src/bench/client_flows/benchmark.ts +19 -0
- package/src/bench/client_flows/client_flows_benchmark.ts +59 -41
- package/src/bench/utils.ts +9 -3
- package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +42 -24
- package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +49 -11
- package/src/e2e_deploy_contract/deploy_test.ts +3 -2
- package/src/e2e_epochs/epochs_test.ts +99 -11
- package/src/e2e_fees/fees_test.ts +49 -9
- package/src/e2e_nested_contract/nested_contract_test.ts +9 -5
- package/src/e2e_p2p/inactivity_slash_test.ts +4 -3
- package/src/e2e_p2p/p2p_network.ts +61 -42
- package/src/e2e_p2p/reqresp/utils.ts +51 -12
- package/src/e2e_p2p/shared.ts +73 -74
- package/src/e2e_token_contract/token_contract_test.ts +28 -10
- package/src/fixtures/authwit_proxy.ts +4 -0
- package/src/fixtures/e2e_prover_test.ts +22 -19
- package/src/fixtures/fixtures.ts +83 -0
- package/src/fixtures/get_bb_config.ts +7 -6
- package/src/fixtures/ha_setup.ts +4 -2
- package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
- package/src/fixtures/setup.ts +196 -35
- package/src/fixtures/setup_p2p_test.ts +13 -9
- package/src/fixtures/token_utils.ts +1 -2
- package/src/fixtures/utils.ts +2 -0
- package/src/forward-compatibility/wallet_rpc_client.ts +14 -0
- package/src/forward-compatibility/wallet_service.ts +104 -0
- package/src/guides/up_quick_start.sh +0 -2
- package/src/install_legacy_contracts.cjs +75 -0
- package/src/legacy-jest-resolver.cjs +112 -0
- package/src/shared/cross_chain_test_harness.ts +27 -13
- package/src/shared/gas_portal_test_harness.ts +21 -11
- package/src/shared/submit-transactions.ts +1 -4
- package/src/shared/uniswap_l1_l2.ts +102 -51
- package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
- package/src/simulators/lending_simulator.ts +32 -14
- package/src/simulators/token_simulator.ts +6 -2
- package/src/spartan/setup_test_wallets.ts +95 -38
- package/src/spartan/tx_metrics.ts +81 -23
- package/src/spartan/utils/config.ts +1 -0
- package/src/spartan/utils/index.ts +3 -0
- package/src/spartan/utils/nodes.ts +2 -2
- package/src/spartan/utils/pod_logs.ts +99 -0
- package/src/test-wallet/test_wallet.ts +174 -104
- package/src/test-wallet/utils.ts +3 -7
- package/src/test-wallet/wallet_worker_script.ts +5 -2
- package/src/test-wallet/worker_wallet.ts +10 -5
- package/src/test-wallet/worker_wallet_schema.ts +2 -2
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/end-to-end",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.189eedb3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"exports":
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./dest/index.js",
|
|
7
|
+
"./*": "./dest/*"
|
|
8
|
+
},
|
|
6
9
|
"inherits": [
|
|
7
10
|
"../package.common.json",
|
|
8
11
|
"./package.local.json"
|
|
@@ -26,45 +29,45 @@
|
|
|
26
29
|
"formatting": "run -T prettier --check ./src && run -T eslint ./src"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"@aztec/accounts": "0.0.1-commit.
|
|
30
|
-
"@aztec/archiver": "0.0.1-commit.
|
|
31
|
-
"@aztec/aztec": "0.0.1-commit.
|
|
32
|
-
"@aztec/aztec-node": "0.0.1-commit.
|
|
33
|
-
"@aztec/aztec.js": "0.0.1-commit.
|
|
34
|
-
"@aztec/bb-prover": "0.0.1-commit.
|
|
35
|
-
"@aztec/bb.js": "0.0.1-commit.
|
|
36
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
37
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
38
|
-
"@aztec/bot": "0.0.1-commit.
|
|
39
|
-
"@aztec/cli": "0.0.1-commit.
|
|
40
|
-
"@aztec/constants": "0.0.1-commit.
|
|
41
|
-
"@aztec/entrypoints": "0.0.1-commit.
|
|
42
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
43
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
44
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
45
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
46
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
47
|
-
"@aztec/
|
|
48
|
-
"@aztec/
|
|
49
|
-
"@aztec/noir-
|
|
50
|
-
"@aztec/noir-
|
|
51
|
-
"@aztec/noir-
|
|
52
|
-
"@aztec/
|
|
53
|
-
"@aztec/
|
|
54
|
-
"@aztec/
|
|
55
|
-
"@aztec/prover-
|
|
56
|
-
"@aztec/
|
|
57
|
-
"@aztec/
|
|
58
|
-
"@aztec/
|
|
59
|
-
"@aztec/
|
|
60
|
-
"@aztec/
|
|
61
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
62
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
63
|
-
"@aztec/validator-client": "0.0.1-commit.
|
|
64
|
-
"@aztec/validator-ha-signer": "0.0.1-commit.
|
|
65
|
-
"@aztec/wallet-sdk": "0.0.1-commit.
|
|
66
|
-
"@aztec/wallets": "0.0.1-commit.
|
|
67
|
-
"@aztec/world-state": "0.0.1-commit.
|
|
32
|
+
"@aztec/accounts": "0.0.1-commit.189eedb3",
|
|
33
|
+
"@aztec/archiver": "0.0.1-commit.189eedb3",
|
|
34
|
+
"@aztec/aztec": "0.0.1-commit.189eedb3",
|
|
35
|
+
"@aztec/aztec-node": "0.0.1-commit.189eedb3",
|
|
36
|
+
"@aztec/aztec.js": "0.0.1-commit.189eedb3",
|
|
37
|
+
"@aztec/bb-prover": "0.0.1-commit.189eedb3",
|
|
38
|
+
"@aztec/bb.js": "0.0.1-commit.189eedb3",
|
|
39
|
+
"@aztec/blob-client": "0.0.1-commit.189eedb3",
|
|
40
|
+
"@aztec/blob-lib": "0.0.1-commit.189eedb3",
|
|
41
|
+
"@aztec/bot": "0.0.1-commit.189eedb3",
|
|
42
|
+
"@aztec/cli": "0.0.1-commit.189eedb3",
|
|
43
|
+
"@aztec/constants": "0.0.1-commit.189eedb3",
|
|
44
|
+
"@aztec/entrypoints": "0.0.1-commit.189eedb3",
|
|
45
|
+
"@aztec/epoch-cache": "0.0.1-commit.189eedb3",
|
|
46
|
+
"@aztec/ethereum": "0.0.1-commit.189eedb3",
|
|
47
|
+
"@aztec/foundation": "0.0.1-commit.189eedb3",
|
|
48
|
+
"@aztec/kv-store": "0.0.1-commit.189eedb3",
|
|
49
|
+
"@aztec/l1-artifacts": "0.0.1-commit.189eedb3",
|
|
50
|
+
"@aztec/node-keystore": "0.0.1-commit.189eedb3",
|
|
51
|
+
"@aztec/noir-contracts.js": "0.0.1-commit.189eedb3",
|
|
52
|
+
"@aztec/noir-noirc_abi": "0.0.1-commit.189eedb3",
|
|
53
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.189eedb3",
|
|
54
|
+
"@aztec/noir-test-contracts.js": "0.0.1-commit.189eedb3",
|
|
55
|
+
"@aztec/p2p": "0.0.1-commit.189eedb3",
|
|
56
|
+
"@aztec/protocol-contracts": "0.0.1-commit.189eedb3",
|
|
57
|
+
"@aztec/prover-client": "0.0.1-commit.189eedb3",
|
|
58
|
+
"@aztec/prover-node": "0.0.1-commit.189eedb3",
|
|
59
|
+
"@aztec/pxe": "0.0.1-commit.189eedb3",
|
|
60
|
+
"@aztec/sequencer-client": "0.0.1-commit.189eedb3",
|
|
61
|
+
"@aztec/simulator": "0.0.1-commit.189eedb3",
|
|
62
|
+
"@aztec/slasher": "0.0.1-commit.189eedb3",
|
|
63
|
+
"@aztec/standard-contracts": "0.0.1-commit.189eedb3",
|
|
64
|
+
"@aztec/stdlib": "0.0.1-commit.189eedb3",
|
|
65
|
+
"@aztec/telemetry-client": "0.0.1-commit.189eedb3",
|
|
66
|
+
"@aztec/validator-client": "0.0.1-commit.189eedb3",
|
|
67
|
+
"@aztec/validator-ha-signer": "0.0.1-commit.189eedb3",
|
|
68
|
+
"@aztec/wallet-sdk": "0.0.1-commit.189eedb3",
|
|
69
|
+
"@aztec/wallets": "0.0.1-commit.189eedb3",
|
|
70
|
+
"@aztec/world-state": "0.0.1-commit.189eedb3",
|
|
68
71
|
"@iarna/toml": "^2.2.5",
|
|
69
72
|
"@jest/globals": "^30.0.0",
|
|
70
73
|
"@noble/curves": "=1.0.0",
|
|
@@ -102,7 +105,7 @@
|
|
|
102
105
|
"typescript": "^5.3.3",
|
|
103
106
|
"util": "^0.12.5",
|
|
104
107
|
"viem": "npm:@aztec/viem@2.38.2",
|
|
105
|
-
"zod": "^
|
|
108
|
+
"zod": "^4"
|
|
106
109
|
},
|
|
107
110
|
"devDependencies": {
|
|
108
111
|
"0x": "^5.7.0",
|
|
@@ -160,6 +163,7 @@
|
|
|
160
163
|
"moduleNameMapper": {
|
|
161
164
|
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
|
|
162
165
|
},
|
|
166
|
+
"resolver": "<rootDir>/legacy-jest-resolver.cjs",
|
|
163
167
|
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
|
|
164
168
|
"rootDir": "./src",
|
|
165
169
|
"testTimeout": 120000,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type ProfileInteractionOptions,
|
|
6
6
|
} from '@aztec/aztec.js/contracts';
|
|
7
7
|
import type { Logger } from '@aztec/aztec.js/log';
|
|
8
|
+
import { MAX_APPS_PER_KERNEL } from '@aztec/constants';
|
|
8
9
|
import { createLogger } from '@aztec/foundation/log';
|
|
9
10
|
import { type PrivateExecutionStep, serializePrivateExecutionSteps } from '@aztec/stdlib/kernel';
|
|
10
11
|
import type {
|
|
@@ -320,6 +321,24 @@ export function convertProfileToGHBenchmark(benchmark: ClientFlowBenchmark): Git
|
|
|
320
321
|
return benches;
|
|
321
322
|
}
|
|
322
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Computes the expected number of `PrivateExecutionStep` entries the orchestrator will produce
|
|
326
|
+
* for a tx with `apps` private function calls.
|
|
327
|
+
*
|
|
328
|
+
* Step layout: `apps` app circuits + kernel iterations + 1 final reset + 1 tail + 1 hiding kernel.
|
|
329
|
+
* Each kernel iteration absorbs up to `MAX_APPS_PER_KERNEL` apps via the `init_K` / `inner_K`
|
|
330
|
+
* variants, so the kernel count is `ceil(apps / MAX_APPS_PER_KERNEL)`.
|
|
331
|
+
*
|
|
332
|
+
* Caveat: this assumes no mid-flow reset is triggered, which would split a batch into two kernels
|
|
333
|
+
* separated by a reset. For the flows currently exercised in this benchmark suite that holds, but
|
|
334
|
+
* a future flow that grows large enough to overflow per-tx limits before consuming all apps will
|
|
335
|
+
* not match this formula.
|
|
336
|
+
*/
|
|
337
|
+
export function expectedExecutionSteps(apps: number): number {
|
|
338
|
+
const kernels = Math.ceil(apps / MAX_APPS_PER_KERNEL);
|
|
339
|
+
return apps + kernels + 1 /* final reset */ + 1 /* tail */ + 1 /* hiding */;
|
|
340
|
+
}
|
|
341
|
+
|
|
323
342
|
export async function captureProfile(
|
|
324
343
|
label: string,
|
|
325
344
|
interaction: ContractFunctionInteraction | DeployMethod,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { NO_FROM } from '@aztec/aztec.js/account';
|
|
1
2
|
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
2
3
|
import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
|
|
3
4
|
import { type FeePaymentMethod, PrivateFeePaymentMethod, SponsoredFeePaymentMethod } from '@aztec/aztec.js/fee';
|
|
4
5
|
import { type Logger, createLogger } from '@aztec/aztec.js/log';
|
|
5
6
|
import type { AztecNode } from '@aztec/aztec.js/node';
|
|
6
7
|
import type { Wallet } from '@aztec/aztec.js/wallet';
|
|
7
|
-
import { CheatCodes } from '@aztec/aztec/testing';
|
|
8
|
+
import { CheatCodes, getTokenAllowedSetupFunctions } from '@aztec/aztec/testing';
|
|
8
9
|
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
9
10
|
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
10
11
|
import type { DeployAztecL1ContractsArgs } from '@aztec/ethereum/deploy-aztec-l1-contracts';
|
|
@@ -27,7 +28,7 @@ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
|
27
28
|
import { GasSettings } from '@aztec/stdlib/gas';
|
|
28
29
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
29
30
|
|
|
30
|
-
import { MNEMONIC } from '../../fixtures/fixtures.js';
|
|
31
|
+
import { AUTOMINE_E2E_OPTS, MNEMONIC, getPaddedMaxFeesPerGas } from '../../fixtures/fixtures.js';
|
|
31
32
|
import { type EndToEndContext, type SetupOptions, deployAccounts, setup, teardown } from '../../fixtures/setup.js';
|
|
32
33
|
import { mintTokensToPrivate } from '../../fixtures/token_utils.js';
|
|
33
34
|
import { setupSponsoredFPC } from '../../fixtures/utils.js';
|
|
@@ -87,33 +88,35 @@ export class ClientFlowsBenchmark {
|
|
|
87
88
|
|
|
88
89
|
public realProofs = ['true', '1'].includes(process.env.REAL_PROOFS ?? '');
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
91
|
+
// `apps` is the number of private function calls contributed by this payment method.
|
|
92
|
+
// Each app produces one execution step at proving time; the orchestrator additionally produces
|
|
93
|
+
// one kernel step per batch of N apps (see `expectedExecutionSteps` in `benchmark.ts`).
|
|
94
|
+
public paymentMethods: Record<BenchmarkingFeePaymentMethod, { forWallet: FeePaymentMethodGetter; apps: number }> = {
|
|
95
|
+
// eslint-disable-next-line camelcase
|
|
96
|
+
bridged_fee_juice: {
|
|
97
|
+
forWallet: this.getBridgedFeeJuicePaymentMethodForWallet.bind(this),
|
|
98
|
+
apps: 1, // FeeJuice claim
|
|
99
|
+
},
|
|
100
|
+
// eslint-disable-next-line camelcase
|
|
101
|
+
private_fpc: {
|
|
102
|
+
forWallet: this.getPrivateFPCPaymentMethodForWallet.bind(this),
|
|
103
|
+
apps:
|
|
104
|
+
1 + // FPC entrypoint
|
|
105
|
+
1 + // BananaCoin transfer_to_public
|
|
106
|
+
1 + // Account verify_private_authwit
|
|
107
|
+
1, // BananaCoin prepare_private_balance_increase
|
|
108
|
+
},
|
|
109
|
+
// eslint-disable-next-line camelcase
|
|
110
|
+
sponsored_fpc: {
|
|
111
|
+
forWallet: this.getSponsoredFPCPaymentMethodForWallet.bind(this),
|
|
112
|
+
apps: 1, // Sponsored FPC sponsor_unconditionally
|
|
113
|
+
},
|
|
114
|
+
// eslint-disable-next-line camelcase
|
|
115
|
+
fee_juice: {
|
|
116
|
+
forWallet: () => Promise.resolve(undefined),
|
|
117
|
+
apps: 0,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
117
120
|
|
|
118
121
|
public config: ClientFlowsConfig;
|
|
119
122
|
|
|
@@ -122,7 +125,7 @@ export class ClientFlowsBenchmark {
|
|
|
122
125
|
|
|
123
126
|
constructor(testName?: string, setupOptions: Partial<SetupOptions & DeployAztecL1ContractsArgs> = {}) {
|
|
124
127
|
this.logger = createLogger(`bench:client_flows${testName ? `:${testName}` : ''}`);
|
|
125
|
-
this.setupOptions = { startProverNode: true, ...setupOptions };
|
|
128
|
+
this.setupOptions = { ...AUTOMINE_E2E_OPTS, startProverNode: true, ...setupOptions };
|
|
126
129
|
this.config = BENCHMARK_CONFIG === 'key_flows' ? KEY_FLOWS_CONFIG : FULL_FLOWS_CONFIG;
|
|
127
130
|
ProxyLogger.create();
|
|
128
131
|
this.proxyLogger = ProxyLogger.getInstance();
|
|
@@ -130,11 +133,14 @@ export class ClientFlowsBenchmark {
|
|
|
130
133
|
|
|
131
134
|
async setup() {
|
|
132
135
|
this.logger.info('Setting up subsystems from fresh');
|
|
136
|
+
// Token allowlist entries are test-only: FPC-based fee payment with custom tokens won't work on mainnet alpha.
|
|
137
|
+
const tokenAllowList = await getTokenAllowedSetupFunctions();
|
|
133
138
|
this.context = await setup(0, {
|
|
134
139
|
...this.setupOptions,
|
|
135
140
|
fundSponsoredFPC: true,
|
|
136
141
|
skipAccountDeployment: true,
|
|
137
142
|
l1ContractsArgs: this.setupOptions,
|
|
143
|
+
txPublicSetupAllowListExtend: [...(this.setupOptions.txPublicSetupAllowListExtend ?? []), ...tokenAllowList],
|
|
138
144
|
});
|
|
139
145
|
await this.applyBaseSetup();
|
|
140
146
|
|
|
@@ -162,11 +168,15 @@ export class ClientFlowsBenchmark {
|
|
|
162
168
|
|
|
163
169
|
/** Admin mints bananaCoin tokens privately to the target address and redeems them. */
|
|
164
170
|
async mintPrivateBananas(amount: bigint, address: AztecAddress) {
|
|
165
|
-
const balanceBefore = await this.bananaCoin.methods
|
|
171
|
+
const { result: balanceBefore } = await this.bananaCoin.methods
|
|
172
|
+
.balance_of_private(address)
|
|
173
|
+
.simulate({ from: address });
|
|
166
174
|
|
|
167
175
|
await mintTokensToPrivate(this.bananaCoin, this.adminAddress, address, amount);
|
|
168
176
|
|
|
169
|
-
const balanceAfter = await this.bananaCoin.methods
|
|
177
|
+
const { result: balanceAfter } = await this.bananaCoin.methods
|
|
178
|
+
.balance_of_private(address)
|
|
179
|
+
.simulate({ from: address });
|
|
170
180
|
expect(balanceAfter).toEqual(balanceBefore + amount);
|
|
171
181
|
}
|
|
172
182
|
|
|
@@ -248,7 +258,9 @@ export class ClientFlowsBenchmark {
|
|
|
248
258
|
'BC',
|
|
249
259
|
'BC',
|
|
250
260
|
18n,
|
|
251
|
-
).send({
|
|
261
|
+
).send({
|
|
262
|
+
from: this.adminAddress,
|
|
263
|
+
});
|
|
252
264
|
this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`);
|
|
253
265
|
this.bananaCoin = bananaCoin;
|
|
254
266
|
this.bananaCoinInstance = bananaCoinInstance;
|
|
@@ -262,7 +274,9 @@ export class ClientFlowsBenchmark {
|
|
|
262
274
|
'CBC',
|
|
263
275
|
'CBC',
|
|
264
276
|
18n,
|
|
265
|
-
).send({
|
|
277
|
+
).send({
|
|
278
|
+
from: this.adminAddress,
|
|
279
|
+
});
|
|
266
280
|
this.logger.info(`CandyBarCoin deployed at ${candyBarCoin.address}`);
|
|
267
281
|
this.candyBarCoin = candyBarCoin;
|
|
268
282
|
this.candyBarCoinInstance = candyBarCoinInstance;
|
|
@@ -278,7 +292,9 @@ export class ClientFlowsBenchmark {
|
|
|
278
292
|
this.adminWallet,
|
|
279
293
|
bananaCoin.address,
|
|
280
294
|
this.adminAddress,
|
|
281
|
-
).send({
|
|
295
|
+
).send({
|
|
296
|
+
from: this.adminAddress,
|
|
297
|
+
});
|
|
282
298
|
|
|
283
299
|
this.logger.info(`BananaPay deployed at ${bananaFPC.address}`);
|
|
284
300
|
|
|
@@ -327,13 +343,13 @@ export class ClientFlowsBenchmark {
|
|
|
327
343
|
const claim = await this.feeJuiceBridgeTestHarness.prepareTokensOnL1(benchysAddress);
|
|
328
344
|
const behchysDeployMethod = await benchysAccountManager.getDeployMethod();
|
|
329
345
|
await behchysDeployMethod.send({
|
|
330
|
-
from:
|
|
346
|
+
from: NO_FROM,
|
|
331
347
|
fee: { paymentMethod: new FeeJuicePaymentMethodWithClaim(benchysAddress, claim) },
|
|
332
348
|
});
|
|
333
349
|
// Register benchy on the user's Wallet, where we're going to be interacting from
|
|
334
350
|
const accountManager = await this.userWallet.createAccount({
|
|
335
351
|
secret: benchysAccount.getSecretKey(),
|
|
336
|
-
salt: new Fr(
|
|
352
|
+
salt: new Fr(benchysAccountManager.getInstance().salt),
|
|
337
353
|
contract: benchysAccountManager.getAccountContract(),
|
|
338
354
|
});
|
|
339
355
|
return accountManager.address;
|
|
@@ -347,13 +363,15 @@ export class ClientFlowsBenchmark {
|
|
|
347
363
|
'LPT',
|
|
348
364
|
'LPT',
|
|
349
365
|
18n,
|
|
350
|
-
).send({
|
|
366
|
+
).send({
|
|
367
|
+
from: this.adminAddress,
|
|
368
|
+
});
|
|
351
369
|
const { contract: amm, instance: ammInstance } = await AMMContract.deploy(
|
|
352
370
|
this.adminWallet,
|
|
353
371
|
this.bananaCoin.address,
|
|
354
372
|
this.candyBarCoin.address,
|
|
355
373
|
liquidityToken.address,
|
|
356
|
-
).send({ from: this.adminAddress
|
|
374
|
+
).send({ from: this.adminAddress });
|
|
357
375
|
this.logger.info(`AMM deployed at ${amm.address}`);
|
|
358
376
|
await liquidityToken.methods.set_minter(amm.address, true).send({ from: this.adminAddress });
|
|
359
377
|
this.liquidityToken = liquidityToken;
|
|
@@ -370,8 +388,8 @@ export class ClientFlowsBenchmark {
|
|
|
370
388
|
public async getPrivateFPCPaymentMethodForWallet(wallet: Wallet, sender: AztecAddress) {
|
|
371
389
|
// The private fee paying method assembled on the app side requires knowledge of the maximum
|
|
372
390
|
// fee the user is willing to pay
|
|
373
|
-
const maxFeesPerGas =
|
|
374
|
-
const gasSettings = GasSettings.
|
|
391
|
+
const maxFeesPerGas = await getPaddedMaxFeesPerGas(this.aztecNode);
|
|
392
|
+
const gasSettings = GasSettings.fallback({ maxFeesPerGas });
|
|
375
393
|
return new PrivateFeePaymentMethod(this.bananaFPC.address, sender, wallet, gasSettings);
|
|
376
394
|
}
|
|
377
395
|
|
package/src/bench/utils.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { BenchmarkDataPoint, BenchmarkMetricsType, BenchmarkTelemetryClient
|
|
|
11
11
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
|
|
14
|
+
import { PIPELINING_SETUP_OPTS } from '../fixtures/fixtures.js';
|
|
14
15
|
import { type EndToEndContext, type SetupOptions, setup } from '../fixtures/utils.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -23,9 +24,9 @@ export async function benchmarkSetup(
|
|
|
23
24
|
benchOutput?: string;
|
|
24
25
|
},
|
|
25
26
|
) {
|
|
26
|
-
const context = await setup(1, { ...opts, telemetryConfig: { benchmark: true } });
|
|
27
|
+
const context = await setup(1, { ...PIPELINING_SETUP_OPTS, ...opts, telemetryConfig: { benchmark: true } });
|
|
27
28
|
const defaultAccountAddress = context.accounts[0];
|
|
28
|
-
const contract = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress });
|
|
29
|
+
const { contract } = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress });
|
|
29
30
|
context.logger.info(`Deployed benchmarking contract at ${contract.address}`);
|
|
30
31
|
const sequencer = (context.aztecNode as AztecNodeService).getSequencer()!;
|
|
31
32
|
const telemetry = context.telemetryClient as BenchmarkTelemetryClient;
|
|
@@ -149,7 +150,12 @@ export async function sendTxs(
|
|
|
149
150
|
context.logger.info(`Creating ${txCount} txs`);
|
|
150
151
|
const [from] = context.accounts;
|
|
151
152
|
context.logger.info(`Sending ${txCount} txs`);
|
|
152
|
-
return Promise.all(
|
|
153
|
+
return Promise.all(
|
|
154
|
+
calls.map(async call => {
|
|
155
|
+
const { txHash } = await call.send({ from, wait: NO_WAIT });
|
|
156
|
+
return txHash;
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
export async function waitTxs(txs: TxHash[], context: EndToEndContext, txWaitOpts?: WaitOpts) {
|
|
@@ -10,10 +10,19 @@ import { TokenBlacklistContract } from '@aztec/noir-contracts.js/TokenBlacklist'
|
|
|
10
10
|
import { GenericProxyContract } from '@aztec/noir-test-contracts.js/GenericProxy';
|
|
11
11
|
import { InvalidAccountContract } from '@aztec/noir-test-contracts.js/InvalidAccount';
|
|
12
12
|
import type { SequencerClient } from '@aztec/sequencer-client';
|
|
13
|
+
import type { AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
|
|
13
14
|
|
|
14
15
|
import { jest } from '@jest/globals';
|
|
15
16
|
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type EndToEndContext,
|
|
19
|
+
type SetupOptions,
|
|
20
|
+
deployAccounts,
|
|
21
|
+
ensureAuthRegistryPublished,
|
|
22
|
+
publicDeployAccounts,
|
|
23
|
+
setup,
|
|
24
|
+
teardown,
|
|
25
|
+
} from '../fixtures/setup.js';
|
|
17
26
|
import { TokenSimulator } from '../simulators/token_simulator.js';
|
|
18
27
|
import type { TestWallet } from '../test-wallet/test_wallet.js';
|
|
19
28
|
|
|
@@ -57,7 +66,7 @@ export class BlacklistTokenContractTest {
|
|
|
57
66
|
authwitProxy!: GenericProxyContract;
|
|
58
67
|
cheatCodes!: CheatCodes;
|
|
59
68
|
sequencer!: SequencerClient;
|
|
60
|
-
aztecNode!: AztecNode;
|
|
69
|
+
aztecNode!: AztecNode & AztecNodeDebug;
|
|
61
70
|
|
|
62
71
|
adminAddress!: AztecAddress;
|
|
63
72
|
otherAddress!: AztecAddress;
|
|
@@ -68,11 +77,13 @@ export class BlacklistTokenContractTest {
|
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
async crossTimestampOfChange() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
// Under AUTOMINE_E2E_OPTS, the 86400s warp crosses many epochs without any proofs being
|
|
81
|
+
// submitted. Mark current pending checkpoints as proven first so the rollup contract's
|
|
82
|
+
// pruning window doesn't reset the chain tip to genesis (which would make the warp's
|
|
83
|
+
// own empty-checkpoint propose fail with Rollup__InvalidArchive). See the AutomineSequencer
|
|
84
|
+
// README "Epoch proving caveat" and the equivalent pattern in lending_simulator.progressSlots.
|
|
85
|
+
await this.cheatCodes.rollup.markAsProven();
|
|
86
|
+
await this.cheatCodes.warpL2TimeAtLeastBy(this.aztecNode, BlacklistTokenContractTest.CHANGE_ROLES_DELAY);
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
/**
|
|
@@ -81,8 +92,9 @@ export class BlacklistTokenContractTest {
|
|
|
81
92
|
* 2. Publicly deploy accounts, deploy token contract and a "bad account".
|
|
82
93
|
*/
|
|
83
94
|
async applyBaseSetup() {
|
|
84
|
-
//
|
|
85
|
-
|
|
95
|
+
// Bumped from 2 min: pipelined cadence (~24s/dependent-tx) makes the 3-account deploy plus token/bad-account/
|
|
96
|
+
// proxy deploys exceed the original window.
|
|
97
|
+
jest.setTimeout(600_000);
|
|
86
98
|
|
|
87
99
|
this.logger.info('Deploying 3 accounts');
|
|
88
100
|
const { deployedAccounts } = await deployAccounts(
|
|
@@ -102,25 +114,30 @@ export class BlacklistTokenContractTest {
|
|
|
102
114
|
this.blacklistedAddress = deployedAccounts[2].address;
|
|
103
115
|
|
|
104
116
|
this.logger.info('Setting up blacklist token contract');
|
|
117
|
+
await ensureAuthRegistryPublished(this.wallet, this.adminAddress);
|
|
105
118
|
// Create the token contract state.
|
|
106
119
|
this.logger.verbose(`Public deploy accounts...`);
|
|
107
120
|
await publicDeployAccounts(this.wallet, [this.adminAddress, this.otherAddress, this.blacklistedAddress]);
|
|
108
121
|
|
|
109
122
|
this.logger.verbose(`Deploying TokenContract...`);
|
|
110
|
-
this.asset = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({
|
|
123
|
+
({ contract: this.asset } = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({
|
|
111
124
|
from: this.adminAddress,
|
|
112
|
-
});
|
|
125
|
+
}));
|
|
113
126
|
this.logger.verbose(`Token deployed to ${this.asset.address}`);
|
|
114
127
|
|
|
115
128
|
this.logger.verbose(`Deploying bad account...`);
|
|
116
|
-
this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({
|
|
129
|
+
({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({
|
|
130
|
+
from: this.adminAddress,
|
|
131
|
+
}));
|
|
117
132
|
this.logger.verbose(`Deployed to ${this.badAccount.address}.`);
|
|
118
133
|
|
|
119
134
|
// Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender
|
|
120
135
|
// (so their notes are in scope), but msg_sender in the target must differ from the note owner
|
|
121
136
|
// to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender.
|
|
122
137
|
this.logger.verbose(`Deploying generic proxy...`);
|
|
123
|
-
this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({
|
|
138
|
+
({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({
|
|
139
|
+
from: this.adminAddress,
|
|
140
|
+
}));
|
|
124
141
|
this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`);
|
|
125
142
|
|
|
126
143
|
await this.crossTimestampOfChange();
|
|
@@ -133,14 +150,15 @@ export class BlacklistTokenContractTest {
|
|
|
133
150
|
[this.adminAddress, this.otherAddress, this.blacklistedAddress],
|
|
134
151
|
);
|
|
135
152
|
|
|
136
|
-
expect(
|
|
137
|
-
|
|
138
|
-
);
|
|
153
|
+
expect(
|
|
154
|
+
(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result,
|
|
155
|
+
).toEqual(new Role().withAdmin().toNoirStruct());
|
|
139
156
|
}
|
|
140
157
|
|
|
141
|
-
async setup() {
|
|
158
|
+
async setup(opts: Partial<SetupOptions> = {}) {
|
|
142
159
|
this.logger.info('Setting up fresh context');
|
|
143
160
|
this.context = await setup(0, {
|
|
161
|
+
...opts,
|
|
144
162
|
fundSponsoredFPC: true,
|
|
145
163
|
skipAccountDeployment: true,
|
|
146
164
|
});
|
|
@@ -189,9 +207,9 @@ export class BlacklistTokenContractTest {
|
|
|
189
207
|
|
|
190
208
|
await this.crossTimestampOfChange();
|
|
191
209
|
|
|
192
|
-
expect(
|
|
193
|
-
|
|
194
|
-
);
|
|
210
|
+
expect(
|
|
211
|
+
(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result,
|
|
212
|
+
).toEqual(adminMinterRole.toNoirStruct());
|
|
195
213
|
|
|
196
214
|
this.logger.verbose(`Minting ${amount} publicly...`);
|
|
197
215
|
await asset.methods.mint_public(this.adminAddress, amount).send({ from: this.adminAddress });
|
|
@@ -199,7 +217,7 @@ export class BlacklistTokenContractTest {
|
|
|
199
217
|
this.logger.verbose(`Minting ${amount} privately...`);
|
|
200
218
|
const secret = Fr.random();
|
|
201
219
|
const secretHash = await computeSecretHash(secret);
|
|
202
|
-
const receipt = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress });
|
|
220
|
+
const { receipt } = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress });
|
|
203
221
|
|
|
204
222
|
await this.addPendingShieldNoteToPXE(asset, this.adminAddress, amount, secretHash, receipt.txHash);
|
|
205
223
|
await asset.methods.redeem_shield(this.adminAddress, amount, secret).send({ from: this.adminAddress });
|
|
@@ -207,20 +225,20 @@ export class BlacklistTokenContractTest {
|
|
|
207
225
|
|
|
208
226
|
tokenSim.mintPublic(this.adminAddress, amount);
|
|
209
227
|
|
|
210
|
-
const publicBalance = await asset.methods
|
|
228
|
+
const { result: publicBalance } = await asset.methods
|
|
211
229
|
.balance_of_public(this.adminAddress)
|
|
212
230
|
.simulate({ from: this.adminAddress });
|
|
213
231
|
this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`);
|
|
214
232
|
expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(this.adminAddress));
|
|
215
233
|
|
|
216
234
|
tokenSim.mintPrivate(this.adminAddress, amount);
|
|
217
|
-
const privateBalance = await asset.methods
|
|
235
|
+
const { result: privateBalance } = await asset.methods
|
|
218
236
|
.balance_of_private(this.adminAddress)
|
|
219
237
|
.simulate({ from: this.adminAddress });
|
|
220
238
|
this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`);
|
|
221
239
|
expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(this.adminAddress));
|
|
222
240
|
|
|
223
|
-
const totalSupply = await asset.methods.total_supply().simulate({ from: this.adminAddress });
|
|
241
|
+
const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: this.adminAddress });
|
|
224
242
|
this.logger.verbose(`Total supply: ${totalSupply}`);
|
|
225
243
|
expect(totalSupply).toEqual(tokenSim.totalSupply);
|
|
226
244
|
}
|