@aztec/end-to-end 0.0.1-commit.5476d83 → 0.0.1-commit.5914bae
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 +3 -2
- package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
- package/dest/bench/client_flows/benchmark.js +21 -1
- package/dest/bench/client_flows/client_flows_benchmark.d.ts +15 -16
- package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
- package/dest/bench/client_flows/client_flows_benchmark.js +138 -144
- package/dest/bench/client_flows/config.d.ts +2 -2
- package/dest/bench/client_flows/config.d.ts.map +1 -1
- package/dest/bench/client_flows/config.js +18 -0
- package/dest/bench/client_flows/data_extractor.js +3 -1
- package/dest/bench/utils.d.ts +9 -8
- package/dest/bench/utils.d.ts.map +1 -1
- package/dest/bench/utils.js +31 -19
- package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +11 -9
- 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 +108 -114
- package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +20 -14
- 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 +92 -71
- package/dest/e2e_deploy_contract/deploy_test.d.ts +5 -4
- package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
- package/dest/e2e_deploy_contract/deploy_test.js +18 -13
- package/dest/e2e_epochs/epochs_test.d.ts +27 -15
- package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
- package/dest/e2e_epochs/epochs_test.js +104 -52
- package/dest/e2e_fees/bridging_race.notest.js +4 -6
- package/dest/e2e_fees/fees_test.d.ts +21 -17
- package/dest/e2e_fees/fees_test.d.ts.map +1 -1
- package/dest/e2e_fees/fees_test.js +137 -145
- package/dest/e2e_l1_publisher/write_json.d.ts +4 -3
- package/dest/e2e_l1_publisher/write_json.d.ts.map +1 -1
- package/dest/e2e_l1_publisher/write_json.js +14 -16
- package/dest/e2e_multi_validator/utils.js +1 -1
- package/dest/e2e_nested_contract/nested_contract_test.d.ts +6 -9
- package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
- package/dest/e2e_nested_contract/nested_contract_test.js +30 -40
- package/dest/e2e_p2p/inactivity_slash_test.d.ts +4 -4
- package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
- package/dest/e2e_p2p/inactivity_slash_test.js +7 -9
- package/dest/e2e_p2p/p2p_network.d.ts +16 -13
- package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
- package/dest/e2e_p2p/p2p_network.js +140 -114
- package/dest/e2e_p2p/reqresp/utils.d.ts +22 -0
- package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -0
- package/dest/e2e_p2p/reqresp/utils.js +177 -0
- package/dest/e2e_p2p/shared.d.ts +39 -7
- package/dest/e2e_p2p/shared.d.ts.map +1 -1
- package/dest/e2e_p2p/shared.js +73 -22
- package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.d.ts +2 -0
- package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.d.ts.map +1 -0
- package/dest/e2e_storage_proof/fixtures/storage_proof_fetcher.js +184 -0
- package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.d.ts +18 -0
- package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.d.ts.map +1 -0
- package/dest/e2e_storage_proof/fixtures/storage_proof_fixture.js +120 -0
- package/dest/e2e_token_contract/token_contract_test.d.ts +19 -10
- package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
- package/dest/e2e_token_contract/token_contract_test.js +100 -92
- package/dest/fixtures/authwit_proxy.d.ts +15 -0
- package/dest/fixtures/authwit_proxy.d.ts.map +1 -0
- package/dest/fixtures/authwit_proxy.js +34 -0
- package/dest/fixtures/e2e_prover_test.d.ts +13 -21
- package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
- package/dest/fixtures/e2e_prover_test.js +114 -133
- package/dest/fixtures/fixtures.d.ts +6 -3
- package/dest/fixtures/fixtures.d.ts.map +1 -1
- package/dest/fixtures/fixtures.js +8 -3
- package/dest/fixtures/get_acvm_config.js +1 -1
- package/dest/fixtures/ha_setup.d.ts +71 -0
- package/dest/fixtures/ha_setup.d.ts.map +1 -0
- package/dest/fixtures/ha_setup.js +116 -0
- package/dest/fixtures/index.d.ts +2 -1
- package/dest/fixtures/index.d.ts.map +1 -1
- package/dest/fixtures/index.js +1 -0
- package/dest/fixtures/l1_to_l2_messaging.d.ts +4 -3
- package/dest/fixtures/l1_to_l2_messaging.d.ts.map +1 -1
- package/dest/fixtures/l1_to_l2_messaging.js +2 -2
- package/dest/fixtures/setup.d.ts +238 -0
- package/dest/fixtures/setup.d.ts.map +1 -0
- package/dest/fixtures/setup.js +609 -0
- package/dest/fixtures/setup_p2p_test.d.ts +16 -9
- package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
- package/dest/fixtures/setup_p2p_test.js +43 -32
- package/dest/fixtures/token_utils.d.ts +2 -2
- package/dest/fixtures/token_utils.d.ts.map +1 -1
- package/dest/fixtures/token_utils.js +12 -8
- package/dest/fixtures/utils.d.ts +5 -637
- package/dest/fixtures/utils.d.ts.map +1 -1
- package/dest/fixtures/utils.js +4 -647
- package/dest/fixtures/web3signer.js +1 -1
- package/dest/fixtures/with_telemetry_utils.d.ts +2 -2
- package/dest/fixtures/with_telemetry_utils.d.ts.map +1 -1
- package/dest/fixtures/with_telemetry_utils.js +2 -2
- 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/legacy-jest-resolver.d.cts +3 -0
- package/dest/legacy-jest-resolver.d.cts.map +1 -0
- package/dest/quality_of_service/grafana_client.d.ts +41 -0
- package/dest/quality_of_service/grafana_client.d.ts.map +1 -0
- package/dest/quality_of_service/{alert_checker.js → grafana_client.js} +1 -1
- package/dest/quality_of_service/prometheus_client.d.ts +38 -0
- package/dest/quality_of_service/prometheus_client.d.ts.map +1 -0
- package/dest/quality_of_service/prometheus_client.js +67 -0
- package/dest/shared/cross_chain_test_harness.d.ts +18 -6
- package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
- package/dest/shared/cross_chain_test_harness.js +27 -27
- package/dest/shared/gas_portal_test_harness.d.ts +12 -2
- package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
- package/dest/shared/gas_portal_test_harness.js +3 -3
- package/dest/shared/index.d.ts +3 -2
- package/dest/shared/index.d.ts.map +1 -1
- package/dest/shared/index.js +1 -0
- package/dest/shared/mock_state_view.d.ts +86 -0
- package/dest/shared/mock_state_view.d.ts.map +1 -0
- package/dest/shared/mock_state_view.js +186 -0
- package/dest/shared/submit-transactions.d.ts +4 -4
- package/dest/shared/submit-transactions.d.ts.map +1 -1
- package/dest/shared/submit-transactions.js +9 -11
- package/dest/shared/uniswap_l1_l2.d.ts +3 -27
- package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
- package/dest/shared/uniswap_l1_l2.js +58 -39
- package/dest/simulators/lending_simulator.d.ts +6 -2
- package/dest/simulators/lending_simulator.d.ts.map +1 -1
- package/dest/simulators/lending_simulator.js +6 -6
- 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 +3 -24
- package/dest/spartan/setup_test_wallets.d.ts +15 -5
- package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
- package/dest/spartan/setup_test_wallets.js +113 -42
- package/dest/spartan/tx_metrics.d.ts +88 -0
- package/dest/spartan/tx_metrics.d.ts.map +1 -0
- package/dest/spartan/tx_metrics.js +422 -0
- package/dest/spartan/utils/bot.d.ts +28 -0
- package/dest/spartan/utils/bot.d.ts.map +1 -0
- package/dest/spartan/utils/bot.js +142 -0
- package/dest/spartan/utils/chaos.d.ts +79 -0
- package/dest/spartan/utils/chaos.d.ts.map +1 -0
- package/dest/spartan/utils/chaos.js +142 -0
- package/dest/spartan/utils/clients.d.ts +39 -0
- package/dest/spartan/utils/clients.d.ts.map +1 -0
- package/dest/spartan/utils/clients.js +90 -0
- package/dest/spartan/utils/config.d.ts +39 -0
- package/dest/spartan/utils/config.d.ts.map +1 -0
- package/dest/spartan/utils/config.js +21 -0
- package/dest/spartan/utils/health.d.ts +63 -0
- package/dest/spartan/utils/health.d.ts.map +1 -0
- package/dest/spartan/utils/health.js +202 -0
- package/dest/spartan/utils/helm.d.ts +15 -0
- package/dest/spartan/utils/helm.d.ts.map +1 -0
- package/dest/spartan/utils/helm.js +47 -0
- package/dest/spartan/utils/index.d.ts +10 -0
- package/dest/spartan/utils/index.d.ts.map +1 -0
- package/dest/spartan/utils/index.js +20 -0
- package/dest/spartan/utils/k8s.d.ts +128 -0
- package/dest/spartan/utils/k8s.d.ts.map +1 -0
- package/dest/spartan/utils/k8s.js +381 -0
- package/dest/spartan/utils/nodes.d.ts +41 -0
- package/dest/spartan/utils/nodes.d.ts.map +1 -0
- package/dest/spartan/utils/nodes.js +461 -0
- package/dest/spartan/utils/scripts.d.ts +30 -0
- package/dest/spartan/utils/scripts.d.ts.map +1 -0
- package/dest/spartan/utils/scripts.js +81 -0
- package/dest/spartan/utils.d.ts +2 -220
- package/dest/spartan/utils.d.ts.map +1 -1
- package/dest/spartan/utils.js +1 -782
- package/dest/test-wallet/test_wallet.d.ts +84 -0
- package/dest/test-wallet/test_wallet.d.ts.map +1 -0
- package/dest/test-wallet/test_wallet.js +249 -0
- package/dest/test-wallet/utils.d.ts +41 -0
- package/dest/test-wallet/utils.d.ts.map +1 -0
- package/dest/test-wallet/utils.js +71 -0
- package/dest/test-wallet/wallet_worker_script.d.ts +2 -0
- package/dest/test-wallet/wallet_worker_script.d.ts.map +1 -0
- package/dest/test-wallet/wallet_worker_script.js +48 -0
- package/dest/test-wallet/worker_wallet.d.ts +52 -0
- package/dest/test-wallet/worker_wallet.d.ts.map +1 -0
- package/dest/test-wallet/worker_wallet.js +151 -0
- package/dest/test-wallet/worker_wallet_schema.d.ts +274 -0
- package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -0
- package/dest/test-wallet/worker_wallet_schema.js +10 -0
- package/package.json +47 -41
- package/src/bench/client_flows/benchmark.ts +24 -2
- package/src/bench/client_flows/client_flows_benchmark.ts +157 -216
- package/src/bench/client_flows/config.ts +9 -1
- package/src/bench/client_flows/data_extractor.ts +1 -1
- package/src/bench/utils.ts +36 -21
- package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +123 -151
- package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +140 -126
- package/src/e2e_deploy_contract/deploy_test.ts +22 -15
- package/src/e2e_epochs/epochs_test.ts +172 -111
- package/src/e2e_fees/bridging_race.notest.ts +5 -11
- package/src/e2e_fees/fees_test.ts +178 -228
- package/src/e2e_l1_publisher/write_json.ts +16 -16
- package/src/e2e_multi_validator/utils.ts +1 -1
- package/src/e2e_nested_contract/nested_contract_test.ts +35 -56
- package/src/e2e_p2p/inactivity_slash_test.ts +8 -10
- package/src/e2e_p2p/p2p_network.ts +203 -180
- package/src/e2e_p2p/reqresp/utils.ts +235 -0
- package/src/e2e_p2p/shared.ts +129 -29
- package/src/e2e_storage_proof/fixtures/storage_proof.json +915 -0
- package/src/e2e_storage_proof/fixtures/storage_proof_fetcher.ts +190 -0
- package/src/e2e_storage_proof/fixtures/storage_proof_fixture.ts +173 -0
- package/src/e2e_token_contract/token_contract_test.ts +121 -119
- package/src/fixtures/authwit_proxy.ts +54 -0
- package/src/fixtures/dumps/epoch_proof_result.json +1 -1
- package/src/fixtures/e2e_prover_test.ts +136 -177
- package/src/fixtures/fixtures.ts +12 -5
- package/src/fixtures/get_acvm_config.ts +1 -1
- package/src/fixtures/ha_setup.ts +186 -0
- package/src/fixtures/index.ts +1 -0
- package/src/fixtures/l1_to_l2_messaging.ts +4 -2
- package/src/fixtures/setup.ts +905 -0
- package/src/fixtures/setup_p2p_test.ts +43 -47
- package/src/fixtures/token_utils.ts +10 -6
- package/src/fixtures/utils.ts +27 -966
- package/src/fixtures/web3signer.ts +1 -1
- package/src/fixtures/with_telemetry_utils.ts +2 -2
- 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 +3 -3
- package/src/legacy-jest-resolver.cjs +135 -0
- package/src/quality_of_service/{alert_checker.ts → grafana_client.ts} +1 -1
- package/src/quality_of_service/prometheus_client.ts +113 -0
- package/src/shared/cross_chain_test_harness.ts +30 -43
- package/src/shared/gas_portal_test_harness.ts +3 -3
- package/src/shared/index.ts +2 -1
- package/src/shared/mock_state_view.ts +188 -0
- package/src/shared/submit-transactions.ts +14 -16
- package/src/shared/uniswap_l1_l2.ts +86 -102
- package/src/simulators/lending_simulator.ts +11 -7
- package/src/simulators/token_simulator.ts +6 -30
- package/src/spartan/DEVELOP.md +7 -0
- package/src/spartan/setup_test_wallets.ts +168 -27
- package/src/spartan/tx_metrics.ts +376 -0
- package/src/spartan/utils/bot.ts +188 -0
- package/src/spartan/utils/chaos.ts +253 -0
- package/src/spartan/utils/clients.ts +100 -0
- package/src/spartan/utils/config.ts +27 -0
- package/src/spartan/utils/health.ts +255 -0
- package/src/spartan/utils/helm.ts +84 -0
- package/src/spartan/utils/index.ts +68 -0
- package/src/spartan/utils/k8s.ts +535 -0
- package/src/spartan/utils/nodes.ts +538 -0
- package/src/spartan/utils/scripts.ts +99 -0
- package/src/spartan/utils.ts +1 -982
- package/src/test-wallet/test_wallet.ts +351 -0
- package/src/test-wallet/utils.ts +112 -0
- package/src/test-wallet/wallet_worker_script.ts +60 -0
- package/src/test-wallet/worker_wallet.ts +214 -0
- package/src/test-wallet/worker_wallet_schema.ts +13 -0
- package/dest/fixtures/setup_l1_contracts.d.ts +0 -477
- package/dest/fixtures/setup_l1_contracts.d.ts.map +0 -1
- package/dest/fixtures/setup_l1_contracts.js +0 -17
- package/dest/fixtures/snapshot_manager.d.ts +0 -95
- package/dest/fixtures/snapshot_manager.d.ts.map +0 -1
- package/dest/fixtures/snapshot_manager.js +0 -505
- package/dest/quality_of_service/alert_checker.d.ts +0 -41
- package/dest/quality_of_service/alert_checker.d.ts.map +0 -1
- package/src/fixtures/setup_l1_contracts.ts +0 -26
- package/src/fixtures/snapshot_manager.ts +0 -665
|
@@ -1,15 +1,19 @@
|
|
|
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 {
|
|
8
|
+
import { CheatCodes, getTokenAllowedSetupFunctions } from '@aztec/aztec/testing';
|
|
9
|
+
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
10
|
+
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
11
|
+
import type { DeployAztecL1ContractsArgs } from '@aztec/ethereum/deploy-aztec-l1-contracts';
|
|
12
|
+
import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
|
|
9
13
|
import { ChainMonitor } from '@aztec/ethereum/test';
|
|
10
|
-
import { randomBytes } from '@aztec/foundation/crypto';
|
|
14
|
+
import { randomBytes } from '@aztec/foundation/crypto/random';
|
|
15
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
11
16
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
12
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
13
17
|
import { TestERC20Abi } from '@aztec/l1-artifacts/TestERC20Abi';
|
|
14
18
|
import { TestERC20Bytecode } from '@aztec/l1-artifacts/TestERC20Bytecode';
|
|
15
19
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
@@ -23,38 +27,31 @@ import { type PXEConfig, getPXEConfig } from '@aztec/pxe/server';
|
|
|
23
27
|
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
24
28
|
import { GasSettings } from '@aztec/stdlib/gas';
|
|
25
29
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
26
|
-
import { TestWallet } from '@aztec/test-wallet/server';
|
|
27
30
|
|
|
28
31
|
import { MNEMONIC } from '../../fixtures/fixtures.js';
|
|
29
|
-
import {
|
|
30
|
-
type ISnapshotManager,
|
|
31
|
-
type SubsystemsContext,
|
|
32
|
-
createSnapshotManager,
|
|
33
|
-
deployAccounts,
|
|
34
|
-
} from '../../fixtures/snapshot_manager.js';
|
|
32
|
+
import { type EndToEndContext, type SetupOptions, deployAccounts, setup, teardown } from '../../fixtures/setup.js';
|
|
35
33
|
import { mintTokensToPrivate } from '../../fixtures/token_utils.js';
|
|
36
|
-
import {
|
|
34
|
+
import { setupSponsoredFPC } from '../../fixtures/utils.js';
|
|
37
35
|
import { CrossChainTestHarness } from '../../shared/cross_chain_test_harness.js';
|
|
38
36
|
import {
|
|
39
37
|
FeeJuicePortalTestingHarnessFactory,
|
|
40
38
|
type GasBridgingTestHarness,
|
|
41
39
|
} from '../../shared/gas_portal_test_harness.js';
|
|
40
|
+
import { TestWallet } from '../../test-wallet/test_wallet.js';
|
|
42
41
|
import { ProxyLogger } from './benchmark.js';
|
|
43
42
|
import { type ClientFlowsConfig, FULL_FLOWS_CONFIG, KEY_FLOWS_CONFIG } from './config.js';
|
|
44
43
|
|
|
45
|
-
const {
|
|
44
|
+
const { BENCHMARK_CONFIG } = process.env;
|
|
46
45
|
|
|
47
46
|
export type AccountType = 'ecdsar1' | 'schnorr';
|
|
48
47
|
export type FeePaymentMethodGetter = (wallet: Wallet, sender: AztecAddress) => Promise<FeePaymentMethod | undefined>;
|
|
49
48
|
export type BenchmarkingFeePaymentMethod = 'bridged_fee_juice' | 'private_fpc' | 'sponsored_fpc' | 'fee_juice';
|
|
50
49
|
|
|
51
50
|
export class ClientFlowsBenchmark {
|
|
52
|
-
private snapshotManager: ISnapshotManager;
|
|
53
|
-
|
|
54
51
|
public logger: Logger;
|
|
55
52
|
public aztecNode!: AztecNode;
|
|
56
53
|
public cheatCodes!: CheatCodes;
|
|
57
|
-
public context!:
|
|
54
|
+
public context!: EndToEndContext;
|
|
58
55
|
public chainMonitor!: ChainMonitor;
|
|
59
56
|
public feeJuiceBridgeTestHarness!: GasBridgingTestHarness;
|
|
60
57
|
public adminWallet!: TestWallet;
|
|
@@ -122,33 +119,40 @@ export class ClientFlowsBenchmark {
|
|
|
122
119
|
public config: ClientFlowsConfig;
|
|
123
120
|
|
|
124
121
|
private proxyLogger: ProxyLogger;
|
|
122
|
+
private setupOptions: Partial<SetupOptions & DeployAztecL1ContractsArgs>;
|
|
125
123
|
|
|
126
|
-
constructor(testName?: string, setupOptions: Partial<SetupOptions &
|
|
124
|
+
constructor(testName?: string, setupOptions: Partial<SetupOptions & DeployAztecL1ContractsArgs> = {}) {
|
|
127
125
|
this.logger = createLogger(`bench:client_flows${testName ? `:${testName}` : ''}`);
|
|
128
|
-
this.
|
|
129
|
-
`bench_client_flows${testName ? `/${testName}` : ''}`,
|
|
130
|
-
dataPath,
|
|
131
|
-
{ startProverNode: true, ...setupOptions },
|
|
132
|
-
{ ...setupOptions },
|
|
133
|
-
);
|
|
126
|
+
this.setupOptions = { startProverNode: true, ...setupOptions };
|
|
134
127
|
this.config = BENCHMARK_CONFIG === 'key_flows' ? KEY_FLOWS_CONFIG : FULL_FLOWS_CONFIG;
|
|
135
128
|
ProxyLogger.create();
|
|
136
129
|
this.proxyLogger = ProxyLogger.getInstance();
|
|
137
130
|
}
|
|
138
131
|
|
|
139
132
|
async setup() {
|
|
140
|
-
|
|
141
|
-
|
|
133
|
+
this.logger.info('Setting up subsystems from fresh');
|
|
134
|
+
// Token allowlist entries are test-only: FPC-based fee payment with custom tokens won't work on mainnet alpha.
|
|
135
|
+
const tokenAllowList = await getTokenAllowedSetupFunctions();
|
|
136
|
+
this.context = await setup(0, {
|
|
137
|
+
...this.setupOptions,
|
|
138
|
+
fundSponsoredFPC: true,
|
|
139
|
+
skipAccountDeployment: true,
|
|
140
|
+
l1ContractsArgs: this.setupOptions,
|
|
141
|
+
txPublicSetupAllowListExtend: [...(this.setupOptions.txPublicSetupAllowListExtend ?? []), ...tokenAllowList],
|
|
142
|
+
});
|
|
143
|
+
await this.applyBaseSetup();
|
|
144
|
+
|
|
145
|
+
await this.context.aztecNodeService.setConfig({ feeRecipient: this.sequencerAddress, coinbase: this.coinbase });
|
|
142
146
|
|
|
143
|
-
const rollupContract = RollupContract.getFromConfig(context.
|
|
144
|
-
this.chainMonitor = new ChainMonitor(rollupContract, context.dateProvider, this.logger, 200).start();
|
|
147
|
+
const rollupContract = RollupContract.getFromConfig(this.context.config);
|
|
148
|
+
this.chainMonitor = new ChainMonitor(rollupContract, this.context.dateProvider, this.logger, 200).start();
|
|
145
149
|
|
|
146
150
|
return this;
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
async teardown() {
|
|
150
154
|
await this.chainMonitor.stop();
|
|
151
|
-
await this.
|
|
155
|
+
await teardown(this.context);
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
async mintAndBridgeFeeJuice(address: AztecAddress) {
|
|
@@ -156,21 +160,20 @@ export class ClientFlowsBenchmark {
|
|
|
156
160
|
const { claimSecret: secret, messageLeafIndex: index } = claim;
|
|
157
161
|
await this.feeJuiceContract.methods
|
|
158
162
|
.claim(address, claim.claimAmount, secret, index)
|
|
159
|
-
.send({ from: this.adminAddress })
|
|
160
|
-
.wait();
|
|
163
|
+
.send({ from: this.adminAddress });
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
/** Admin mints bananaCoin tokens privately to the target address and redeems them. */
|
|
164
167
|
async mintPrivateBananas(amount: bigint, address: AztecAddress) {
|
|
165
|
-
const balanceBefore = await this.bananaCoin.methods
|
|
168
|
+
const { result: balanceBefore } = await this.bananaCoin.methods
|
|
166
169
|
.balance_of_private(address)
|
|
167
|
-
.simulate({ from:
|
|
170
|
+
.simulate({ from: address });
|
|
168
171
|
|
|
169
172
|
await mintTokensToPrivate(this.bananaCoin, this.adminAddress, address, amount);
|
|
170
173
|
|
|
171
|
-
const balanceAfter = await this.bananaCoin.methods
|
|
174
|
+
const { result: balanceAfter } = await this.bananaCoin.methods
|
|
172
175
|
.balance_of_private(address)
|
|
173
|
-
.simulate({ from:
|
|
176
|
+
.simulate({ from: address });
|
|
174
177
|
expect(balanceAfter).toEqual(balanceBefore + amount);
|
|
175
178
|
}
|
|
176
179
|
|
|
@@ -190,161 +193,117 @@ export class ClientFlowsBenchmark {
|
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
195
|
|
|
193
|
-
public async
|
|
194
|
-
await this.
|
|
195
|
-
await this.
|
|
196
|
+
public async applyBaseSetup() {
|
|
197
|
+
await this.applyInitialAccounts();
|
|
198
|
+
await this.applySetupFeeJuice();
|
|
196
199
|
}
|
|
197
200
|
|
|
198
|
-
async
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.adminAddress = adminAddress;
|
|
211
|
-
this.sequencerAddress = sequencerAddress;
|
|
212
|
-
|
|
213
|
-
const canonicalFeeJuice = await getCanonicalFeeJuice();
|
|
214
|
-
this.feeJuiceContract = FeeJuiceContract.at(canonicalFeeJuice.address, this.adminWallet);
|
|
215
|
-
this.coinbase = EthAddress.random();
|
|
216
|
-
|
|
217
|
-
const userPXEConfig = getPXEConfig();
|
|
218
|
-
const userPXEConfigWithContracts = {
|
|
219
|
-
...userPXEConfig,
|
|
220
|
-
proverEnabled: this.realProofs,
|
|
221
|
-
} as PXEConfig;
|
|
222
|
-
|
|
223
|
-
this.userWallet = await TestWallet.create(this.aztecNode, userPXEConfigWithContracts, {
|
|
224
|
-
loggers: {
|
|
225
|
-
prover: this.proxyLogger.createLogger('pxe:bb:wasm:bundle:proxied'),
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
},
|
|
229
|
-
);
|
|
230
|
-
}
|
|
201
|
+
async applyInitialAccounts() {
|
|
202
|
+
this.logger.info('Applying initial accounts setup');
|
|
203
|
+
const { deployedAccounts } = await deployAccounts(
|
|
204
|
+
2,
|
|
205
|
+
this.logger,
|
|
206
|
+
)({
|
|
207
|
+
wallet: this.context.wallet,
|
|
208
|
+
initialFundedAccounts: this.context.initialFundedAccounts,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const [{ address: adminAddress }, { address: sequencerAddress }] = deployedAccounts;
|
|
231
212
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
213
|
+
this.adminWallet = this.context.wallet;
|
|
214
|
+
this.aztecNode = this.context.aztecNodeService;
|
|
215
|
+
this.cheatCodes = this.context.cheatCodes;
|
|
216
|
+
|
|
217
|
+
this.adminAddress = adminAddress;
|
|
218
|
+
this.sequencerAddress = sequencerAddress;
|
|
219
|
+
|
|
220
|
+
const canonicalFeeJuice = await getCanonicalFeeJuice();
|
|
221
|
+
this.feeJuiceContract = FeeJuiceContract.at(canonicalFeeJuice.address, this.adminWallet);
|
|
222
|
+
this.coinbase = EthAddress.random();
|
|
223
|
+
|
|
224
|
+
const userPXEConfig = getPXEConfig();
|
|
225
|
+
const userPXEConfigWithContracts = {
|
|
226
|
+
...userPXEConfig,
|
|
227
|
+
proverEnabled: this.realProofs,
|
|
228
|
+
} as PXEConfig;
|
|
229
|
+
|
|
230
|
+
this.userWallet = await TestWallet.create(this.aztecNode, userPXEConfigWithContracts, {
|
|
231
|
+
loggers: {
|
|
232
|
+
prover: this.proxyLogger.createLogger('pxe:bb:wasm:bundle:proxied'),
|
|
248
233
|
},
|
|
249
|
-
);
|
|
234
|
+
});
|
|
250
235
|
}
|
|
251
236
|
|
|
252
|
-
async
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
.send({ from: this.adminAddress })
|
|
264
|
-
.wait();
|
|
265
|
-
this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`);
|
|
266
|
-
return { bananaCoinAddress: bananaCoin.address, bananaCoinInstance };
|
|
267
|
-
},
|
|
268
|
-
({ bananaCoinAddress, bananaCoinInstance }) => {
|
|
269
|
-
this.bananaCoin = BananaCoin.at(bananaCoinAddress, this.adminWallet);
|
|
270
|
-
this.bananaCoinInstance = bananaCoinInstance;
|
|
271
|
-
return Promise.resolve();
|
|
272
|
-
},
|
|
273
|
-
);
|
|
237
|
+
async applySetupFeeJuice() {
|
|
238
|
+
this.logger.info('Applying fee juice setup');
|
|
239
|
+
this.feeJuiceContract = FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, this.adminWallet);
|
|
240
|
+
|
|
241
|
+
this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({
|
|
242
|
+
aztecNode: this.context.aztecNodeService,
|
|
243
|
+
aztecNodeAdmin: this.context.aztecNodeService,
|
|
244
|
+
l1Client: this.context.deployL1ContractsValues.l1Client,
|
|
245
|
+
wallet: this.adminWallet,
|
|
246
|
+
logger: this.logger,
|
|
247
|
+
});
|
|
274
248
|
}
|
|
275
249
|
|
|
276
|
-
async
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
.send({ from: this.adminAddress })
|
|
288
|
-
.wait();
|
|
289
|
-
this.logger.info(`CandyBarCoin deployed at ${candyBarCoin.address}`);
|
|
290
|
-
return { candyBarCoinAddress: candyBarCoin.address, candyBarCoinInstance };
|
|
291
|
-
},
|
|
292
|
-
({ candyBarCoinAddress, candyBarCoinInstance }) => {
|
|
293
|
-
this.candyBarCoin = TokenContract.at(candyBarCoinAddress, this.adminWallet);
|
|
294
|
-
this.candyBarCoinInstance = candyBarCoinInstance;
|
|
295
|
-
return Promise.resolve();
|
|
296
|
-
},
|
|
297
|
-
);
|
|
250
|
+
async applyDeployBananaToken() {
|
|
251
|
+
this.logger.info('Applying banana token deployment');
|
|
252
|
+
const {
|
|
253
|
+
receipt: { contract: bananaCoin, instance: bananaCoinInstance },
|
|
254
|
+
} = await BananaCoin.deploy(this.adminWallet, this.adminAddress, 'BC', 'BC', 18n).send({
|
|
255
|
+
from: this.adminAddress,
|
|
256
|
+
wait: { returnReceipt: true },
|
|
257
|
+
});
|
|
258
|
+
this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`);
|
|
259
|
+
this.bananaCoin = bananaCoin;
|
|
260
|
+
this.bananaCoinInstance = bananaCoinInstance;
|
|
298
261
|
}
|
|
299
262
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
263
|
+
async applyDeployCandyBarToken() {
|
|
264
|
+
this.logger.info('Applying candy bar token deployment');
|
|
265
|
+
const {
|
|
266
|
+
receipt: { contract: candyBarCoin, instance: candyBarCoinInstance },
|
|
267
|
+
} = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'CBC', 'CBC', 18n).send({
|
|
268
|
+
from: this.adminAddress,
|
|
269
|
+
wait: { returnReceipt: true },
|
|
270
|
+
});
|
|
271
|
+
this.logger.info(`CandyBarCoin deployed at ${candyBarCoin.address}`);
|
|
272
|
+
this.candyBarCoin = candyBarCoin;
|
|
273
|
+
this.candyBarCoinInstance = candyBarCoinInstance;
|
|
274
|
+
}
|
|
306
275
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
276
|
+
public async applyFPCSetup() {
|
|
277
|
+
this.logger.info('Applying FPC setup');
|
|
278
|
+
const feeJuiceContract = this.feeJuiceBridgeTestHarness.feeJuice;
|
|
279
|
+
expect((await this.context.wallet.getContractMetadata(feeJuiceContract.address)).isContractPublished).toBe(true);
|
|
280
|
+
|
|
281
|
+
const bananaCoin = this.bananaCoin;
|
|
282
|
+
const {
|
|
283
|
+
receipt: { contract: bananaFPC, instance: bananaFPCInstance },
|
|
284
|
+
} = await FPCContract.deploy(this.adminWallet, bananaCoin.address, this.adminAddress).send({
|
|
285
|
+
from: this.adminAddress,
|
|
286
|
+
wait: { returnReceipt: true },
|
|
287
|
+
});
|
|
315
288
|
|
|
316
|
-
|
|
289
|
+
this.logger.info(`BananaPay deployed at ${bananaFPC.address}`);
|
|
317
290
|
|
|
318
|
-
|
|
291
|
+
await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2(bananaFPC.address, this.adminAddress);
|
|
319
292
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
data => {
|
|
323
|
-
this.bananaFPC = FPCContract.at(data.bananaFPCAddress, this.adminWallet);
|
|
324
|
-
this.bananaFPCInstance = data.bananaFPCInstance;
|
|
325
|
-
return Promise.resolve();
|
|
326
|
-
},
|
|
327
|
-
);
|
|
293
|
+
this.bananaFPC = bananaFPC;
|
|
294
|
+
this.bananaFPCInstance = bananaFPCInstance;
|
|
328
295
|
}
|
|
329
296
|
|
|
330
|
-
async
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
return { sponsoredFPCAddress: sponsoredFPCInstance.address, sponsoredFPCInstance };
|
|
337
|
-
},
|
|
338
|
-
({ sponsoredFPCAddress, sponsoredFPCInstance }) => {
|
|
339
|
-
this.sponsoredFPC = SponsoredFPCContract.at(sponsoredFPCAddress, this.adminWallet);
|
|
340
|
-
this.sponsoredFPCInstance = sponsoredFPCInstance;
|
|
341
|
-
return Promise.resolve();
|
|
342
|
-
},
|
|
343
|
-
);
|
|
297
|
+
async applyDeploySponsoredFPC() {
|
|
298
|
+
this.logger.info('Applying sponsored FPC deployment');
|
|
299
|
+
const sponsoredFPCInstance = await setupSponsoredFPC(this.adminWallet);
|
|
300
|
+
this.logger.info(`SponsoredFPC at ${sponsoredFPCInstance.address}`);
|
|
301
|
+
this.sponsoredFPC = SponsoredFPCContract.at(sponsoredFPCInstance.address, this.adminWallet);
|
|
302
|
+
this.sponsoredFPCInstance = sponsoredFPCInstance;
|
|
344
303
|
}
|
|
345
304
|
|
|
346
305
|
public async createCrossChainTestHarness(owner: AztecAddress) {
|
|
347
|
-
const l1Client = createExtendedL1Client(this.context.
|
|
306
|
+
const l1Client = createExtendedL1Client(this.context.config.l1RpcUrls, MNEMONIC);
|
|
348
307
|
|
|
349
308
|
const underlyingERC20Address = await deployL1Contract(l1Client, TestERC20Abi, TestERC20Bytecode, [
|
|
350
309
|
'Underlying',
|
|
@@ -373,12 +332,10 @@ export class ClientFlowsBenchmark {
|
|
|
373
332
|
const benchysAddress = benchysAccountManager.address;
|
|
374
333
|
const claim = await this.feeJuiceBridgeTestHarness.prepareTokensOnL1(benchysAddress);
|
|
375
334
|
const behchysDeployMethod = await benchysAccountManager.getDeployMethod();
|
|
376
|
-
await behchysDeployMethod
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
})
|
|
381
|
-
.wait();
|
|
335
|
+
await behchysDeployMethod.send({
|
|
336
|
+
from: NO_FROM,
|
|
337
|
+
fee: { paymentMethod: new FeeJuicePaymentMethodWithClaim(benchysAddress, claim) },
|
|
338
|
+
});
|
|
382
339
|
// Register benchy on the user's Wallet, where we're going to be interacting from
|
|
383
340
|
const accountManager = await this.userWallet.createAccount({
|
|
384
341
|
secret: benchysAccount.getSecretKey(),
|
|
@@ -388,44 +345,28 @@ export class ClientFlowsBenchmark {
|
|
|
388
345
|
return accountManager.address;
|
|
389
346
|
}
|
|
390
347
|
|
|
391
|
-
public async
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
await liquidityToken.methods.set_minter(amm.address, true).send({ from: this.adminAddress }).wait();
|
|
414
|
-
return {
|
|
415
|
-
ammAddress: amm.address,
|
|
416
|
-
ammInstance,
|
|
417
|
-
liquidityTokenAddress: liquidityToken.address,
|
|
418
|
-
liquidityTokenInstance,
|
|
419
|
-
};
|
|
420
|
-
},
|
|
421
|
-
({ ammAddress, ammInstance, liquidityTokenAddress, liquidityTokenInstance }) => {
|
|
422
|
-
this.liquidityToken = TokenContract.at(liquidityTokenAddress, this.adminWallet);
|
|
423
|
-
this.liquidityTokenInstance = liquidityTokenInstance;
|
|
424
|
-
this.amm = AMMContract.at(ammAddress, this.adminWallet);
|
|
425
|
-
this.ammInstance = ammInstance;
|
|
426
|
-
return Promise.resolve();
|
|
427
|
-
},
|
|
428
|
-
);
|
|
348
|
+
public async applyDeployAmm() {
|
|
349
|
+
this.logger.info('Applying AMM deployment');
|
|
350
|
+
const {
|
|
351
|
+
receipt: { contract: liquidityToken, instance: liquidityTokenInstance },
|
|
352
|
+
} = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'LPT', 'LPT', 18n).send({
|
|
353
|
+
from: this.adminAddress,
|
|
354
|
+
wait: { returnReceipt: true },
|
|
355
|
+
});
|
|
356
|
+
const {
|
|
357
|
+
receipt: { contract: amm, instance: ammInstance },
|
|
358
|
+
} = await AMMContract.deploy(
|
|
359
|
+
this.adminWallet,
|
|
360
|
+
this.bananaCoin.address,
|
|
361
|
+
this.candyBarCoin.address,
|
|
362
|
+
liquidityToken.address,
|
|
363
|
+
).send({ from: this.adminAddress, wait: { returnReceipt: true } });
|
|
364
|
+
this.logger.info(`AMM deployed at ${amm.address}`);
|
|
365
|
+
await liquidityToken.methods.set_minter(amm.address, true).send({ from: this.adminAddress });
|
|
366
|
+
this.liquidityToken = liquidityToken;
|
|
367
|
+
this.liquidityTokenInstance = liquidityTokenInstance;
|
|
368
|
+
this.amm = amm;
|
|
369
|
+
this.ammInstance = ammInstance;
|
|
429
370
|
}
|
|
430
371
|
|
|
431
372
|
public async getBridgedFeeJuicePaymentMethodForWallet(_wallet: Wallet, sender: AztecAddress) {
|
|
@@ -436,8 +377,8 @@ export class ClientFlowsBenchmark {
|
|
|
436
377
|
public async getPrivateFPCPaymentMethodForWallet(wallet: Wallet, sender: AztecAddress) {
|
|
437
378
|
// The private fee paying method assembled on the app side requires knowledge of the maximum
|
|
438
379
|
// fee the user is willing to pay
|
|
439
|
-
const maxFeesPerGas = (await this.aztecNode.
|
|
440
|
-
const gasSettings = GasSettings.
|
|
380
|
+
const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1.5);
|
|
381
|
+
const gasSettings = GasSettings.fallback({ maxFeesPerGas });
|
|
441
382
|
return new PrivateFeePaymentMethod(this.bananaFPC.address, sender, wallet, gasSettings);
|
|
442
383
|
}
|
|
443
384
|
|
|
@@ -6,7 +6,7 @@ export type ClientFlowConfig = {
|
|
|
6
6
|
recursions?: number[];
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
type ClientFlows = 'accountDeployments' | 'deployments' | 'transfers' | 'bridging' | 'amm';
|
|
9
|
+
type ClientFlows = 'accountDeployments' | 'deployments' | 'transfers' | 'bridging' | 'amm' | 'storageProof';
|
|
10
10
|
|
|
11
11
|
export type ClientFlowsConfig = {
|
|
12
12
|
[key in ClientFlows]: ClientFlowConfig;
|
|
@@ -34,6 +34,10 @@ export const KEY_FLOWS_CONFIG: ClientFlowsConfig = {
|
|
|
34
34
|
feePaymentMethods: ['sponsored_fpc', 'private_fpc'],
|
|
35
35
|
recursions: [0, 1],
|
|
36
36
|
},
|
|
37
|
+
storageProof: {
|
|
38
|
+
accounts: ['ecdsar1'],
|
|
39
|
+
feePaymentMethods: ['sponsored_fpc'],
|
|
40
|
+
},
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
export const FULL_FLOWS_CONFIG: ClientFlowsConfig = {
|
|
@@ -58,4 +62,8 @@ export const FULL_FLOWS_CONFIG: ClientFlowsConfig = {
|
|
|
58
62
|
feePaymentMethods: ['sponsored_fpc', 'private_fpc'],
|
|
59
63
|
recursions: [0, 1, 2],
|
|
60
64
|
},
|
|
65
|
+
storageProof: {
|
|
66
|
+
accounts: ['ecdsar1', 'schnorr'],
|
|
67
|
+
feePaymentMethods: ['sponsored_fpc', 'private_fpc'],
|
|
68
|
+
},
|
|
61
69
|
};
|
|
@@ -22,7 +22,7 @@ async function main() {
|
|
|
22
22
|
logger.info(`Flows in ${ivcFolder}: \n${flows.map(flowName => `\t- ${flowName}`).join('\n')}`);
|
|
23
23
|
const simulator = new WASMSimulator();
|
|
24
24
|
const log = proxyLogger.createLogger('bb:prover');
|
|
25
|
-
const prover = new BBBundlePrivateKernelProver(simulator, log);
|
|
25
|
+
const prover = new BBBundlePrivateKernelProver(simulator, { logger: log });
|
|
26
26
|
|
|
27
27
|
const userLog = createLogger('chonk_flows:data_processor');
|
|
28
28
|
|