@aztec/txe 0.80.0 → 0.82.0
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/dest/bin/index.js +1 -0
- package/dest/node/txe_node.d.ts +10 -39
- package/dest/node/txe_node.d.ts.map +1 -1
- package/dest/node/txe_node.js +64 -88
- package/dest/oracle/txe_oracle.d.ts +8 -7
- package/dest/oracle/txe_oracle.d.ts.map +1 -1
- package/dest/oracle/txe_oracle.js +23 -20
- package/dest/txe_service/txe_service.d.ts +11 -2
- package/dest/txe_service/txe_service.d.ts.map +1 -1
- package/dest/txe_service/txe_service.js +54 -42
- package/dest/util/encoding.d.ts +14 -3
- package/dest/util/encoding.d.ts.map +1 -1
- package/dest/util/encoding.js +27 -8
- package/dest/util/txe_public_contract_data_source.d.ts +1 -3
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +0 -15
- package/dest/util/{txe_world_state_db.d.ts → txe_public_dbs.d.ts} +4 -5
- package/dest/util/txe_public_dbs.d.ts.map +1 -0
- package/dest/util/{txe_world_state_db.js → txe_public_dbs.js} +4 -4
- package/package.json +12 -12
- package/src/bin/index.ts +1 -0
- package/src/node/txe_node.ts +86 -129
- package/src/oracle/txe_oracle.ts +49 -33
- package/src/txe_service/txe_service.ts +76 -21
- package/src/util/encoding.ts +33 -8
- package/src/util/txe_public_contract_data_source.ts +0 -14
- package/src/util/{txe_world_state_db.ts → txe_public_dbs.ts} +4 -5
- package/dest/util/txe_world_state_db.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ContractInstanceWithAddress, Fr } from '@aztec/aztec.js';
|
|
1
|
+
import { type ContractInstanceWithAddress, Fr, Point } from '@aztec/aztec.js';
|
|
2
2
|
import { DEPLOYER_CONTRACT_ADDRESS } from '@aztec/constants';
|
|
3
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
4
4
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
@@ -11,6 +11,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
11
11
|
import { computePartialAddress } from '@aztec/stdlib/contract';
|
|
12
12
|
import { SimulationError } from '@aztec/stdlib/errors';
|
|
13
13
|
import { computePublicDataTreeLeafSlot, siloNullifier } from '@aztec/stdlib/hash';
|
|
14
|
+
import { LogWithTxData } from '@aztec/stdlib/logs';
|
|
14
15
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
15
16
|
|
|
16
17
|
import { TXE } from '../oracle/txe_oracle.js';
|
|
@@ -23,9 +24,11 @@ import {
|
|
|
23
24
|
fromArray,
|
|
24
25
|
fromSingle,
|
|
25
26
|
fromUintArray,
|
|
27
|
+
fromUintBoundedVec,
|
|
26
28
|
toArray,
|
|
27
29
|
toForeignCallResult,
|
|
28
30
|
toSingle,
|
|
31
|
+
toSingleOrArray,
|
|
29
32
|
} from '../util/encoding.js';
|
|
30
33
|
import { ExpectedFailureError } from '../util/expected_failure_error.js';
|
|
31
34
|
|
|
@@ -260,15 +263,15 @@ export class TXEService {
|
|
|
260
263
|
return toForeignCallResult([toArray(newValues)]);
|
|
261
264
|
}
|
|
262
265
|
|
|
263
|
-
async
|
|
266
|
+
async getPublicDataWitness(blockNumber: ForeignCallSingle, leafSlot: ForeignCallSingle) {
|
|
264
267
|
const parsedBlockNumber = fromSingle(blockNumber).toNumber();
|
|
265
268
|
const parsedLeafSlot = fromSingle(leafSlot);
|
|
266
269
|
|
|
267
|
-
const witness = await this.typedOracle.
|
|
270
|
+
const witness = await this.typedOracle.getPublicDataWitness(parsedBlockNumber, parsedLeafSlot);
|
|
268
271
|
if (!witness) {
|
|
269
272
|
throw new Error(`Public data witness not found for slot ${parsedLeafSlot} at block ${parsedBlockNumber}.`);
|
|
270
273
|
}
|
|
271
|
-
return toForeignCallResult(
|
|
274
|
+
return toForeignCallResult(witness.toNoirRepresentation());
|
|
272
275
|
}
|
|
273
276
|
|
|
274
277
|
async getNotes(
|
|
@@ -346,7 +349,7 @@ export class TXEService {
|
|
|
346
349
|
fromSingle(noteHash),
|
|
347
350
|
fromSingle(counter).toNumber(),
|
|
348
351
|
);
|
|
349
|
-
return toForeignCallResult([
|
|
352
|
+
return toForeignCallResult([]);
|
|
350
353
|
}
|
|
351
354
|
|
|
352
355
|
async notifyNullifiedNote(
|
|
@@ -359,12 +362,12 @@ export class TXEService {
|
|
|
359
362
|
fromSingle(noteHash),
|
|
360
363
|
fromSingle(counter).toNumber(),
|
|
361
364
|
);
|
|
362
|
-
return toForeignCallResult([
|
|
365
|
+
return toForeignCallResult([]);
|
|
363
366
|
}
|
|
364
367
|
|
|
365
368
|
async notifyCreatedNullifier(innerNullifier: ForeignCallSingle) {
|
|
366
369
|
await this.typedOracle.notifyCreatedNullifier(fromSingle(innerNullifier));
|
|
367
|
-
return toForeignCallResult([
|
|
370
|
+
return toForeignCallResult([]);
|
|
368
371
|
}
|
|
369
372
|
|
|
370
373
|
async checkNullifierExists(innerNullifier: ForeignCallSingle) {
|
|
@@ -374,15 +377,15 @@ export class TXEService {
|
|
|
374
377
|
|
|
375
378
|
async getContractInstance(address: ForeignCallSingle) {
|
|
376
379
|
const instance = await this.typedOracle.getContractInstance(addressFromSingle(address));
|
|
377
|
-
return toForeignCallResult(
|
|
378
|
-
|
|
380
|
+
return toForeignCallResult(
|
|
381
|
+
[
|
|
379
382
|
instance.salt,
|
|
380
383
|
instance.deployer.toField(),
|
|
381
384
|
instance.currentContractClassId,
|
|
382
385
|
instance.initializationHash,
|
|
383
386
|
...instance.publicKeys.toFields(),
|
|
384
|
-
]),
|
|
385
|
-
|
|
387
|
+
].map(toSingle),
|
|
388
|
+
);
|
|
386
389
|
}
|
|
387
390
|
|
|
388
391
|
async getPublicKeysAndPartialAddress(address: ForeignCallSingle) {
|
|
@@ -393,7 +396,7 @@ export class TXEService {
|
|
|
393
396
|
|
|
394
397
|
async getKeyValidationRequest(pkMHash: ForeignCallSingle) {
|
|
395
398
|
const keyValidationRequest = await this.typedOracle.getKeyValidationRequest(fromSingle(pkMHash));
|
|
396
|
-
return toForeignCallResult(
|
|
399
|
+
return toForeignCallResult(keyValidationRequest.toFields().map(toSingle));
|
|
397
400
|
}
|
|
398
401
|
|
|
399
402
|
async callPrivateFunction(
|
|
@@ -419,7 +422,7 @@ export class TXEService {
|
|
|
419
422
|
if (!witness) {
|
|
420
423
|
throw new Error(`Nullifier membership witness not found at block ${parsedBlockNumber}.`);
|
|
421
424
|
}
|
|
422
|
-
return toForeignCallResult(
|
|
425
|
+
return toForeignCallResult(witness.toNoirRepresentation());
|
|
423
426
|
}
|
|
424
427
|
|
|
425
428
|
async getAuthWitness(messageHash: ForeignCallSingle) {
|
|
@@ -482,7 +485,7 @@ export class TXEService {
|
|
|
482
485
|
if (!header) {
|
|
483
486
|
throw new Error(`Block header not found for block ${blockNumber}.`);
|
|
484
487
|
}
|
|
485
|
-
return toForeignCallResult(
|
|
488
|
+
return toForeignCallResult(header.toFields().map(toSingle));
|
|
486
489
|
}
|
|
487
490
|
|
|
488
491
|
async getMembershipWitness(blockNumber: ForeignCallSingle, treeId: ForeignCallSingle, leafValue: ForeignCallSingle) {
|
|
@@ -495,7 +498,7 @@ export class TXEService {
|
|
|
495
498
|
`Membership witness in tree ${MerkleTreeId[parsedTreeId]} not found for value ${parsedLeafValue} at block ${parsedBlockNumber}.`,
|
|
496
499
|
);
|
|
497
500
|
}
|
|
498
|
-
return toForeignCallResult([toArray(witness)]);
|
|
501
|
+
return toForeignCallResult([toSingle(witness[0]), toArray(witness.slice(1))]);
|
|
499
502
|
}
|
|
500
503
|
|
|
501
504
|
async getLowNullifierMembershipWitness(blockNumber: ForeignCallSingle, nullifier: ForeignCallSingle) {
|
|
@@ -505,7 +508,7 @@ export class TXEService {
|
|
|
505
508
|
if (!witness) {
|
|
506
509
|
throw new Error(`Low nullifier witness not found for nullifier ${nullifier} at block ${parsedBlockNumber}.`);
|
|
507
510
|
}
|
|
508
|
-
return toForeignCallResult(
|
|
511
|
+
return toForeignCallResult(witness.toNoirRepresentation());
|
|
509
512
|
}
|
|
510
513
|
|
|
511
514
|
async getIndexedTaggingSecretAsSender(sender: ForeignCallSingle, recipient: ForeignCallSingle) {
|
|
@@ -521,6 +524,42 @@ export class TXEService {
|
|
|
521
524
|
return toForeignCallResult([]);
|
|
522
525
|
}
|
|
523
526
|
|
|
527
|
+
public async deliverNote(
|
|
528
|
+
contractAddress: ForeignCallSingle,
|
|
529
|
+
storageSlot: ForeignCallSingle,
|
|
530
|
+
nonce: ForeignCallSingle,
|
|
531
|
+
content: ForeignCallArray,
|
|
532
|
+
contentLength: ForeignCallSingle,
|
|
533
|
+
noteHash: ForeignCallSingle,
|
|
534
|
+
nullifier: ForeignCallSingle,
|
|
535
|
+
txHash: ForeignCallSingle,
|
|
536
|
+
recipient: ForeignCallSingle,
|
|
537
|
+
) {
|
|
538
|
+
await this.typedOracle.deliverNote(
|
|
539
|
+
AztecAddress.fromField(fromSingle(contractAddress)),
|
|
540
|
+
fromSingle(storageSlot),
|
|
541
|
+
fromSingle(nonce),
|
|
542
|
+
fromArray(content.slice(0, Number(BigInt(contentLength)))),
|
|
543
|
+
fromSingle(noteHash),
|
|
544
|
+
fromSingle(nullifier),
|
|
545
|
+
fromSingle(txHash),
|
|
546
|
+
AztecAddress.fromField(fromSingle(recipient)),
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
return toForeignCallResult([toSingle(Fr.ONE)]);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async getLogByTag(tag: ForeignCallSingle) {
|
|
553
|
+
// TODO(AD): this was warning that getLogByTag did not return a promise.
|
|
554
|
+
const log = await Promise.resolve(this.typedOracle.getLogByTag(fromSingle(tag)));
|
|
555
|
+
|
|
556
|
+
if (log == null) {
|
|
557
|
+
return toForeignCallResult([toSingle(Fr.ZERO), ...LogWithTxData.noirSerializationOfEmpty().map(toSingleOrArray)]);
|
|
558
|
+
} else {
|
|
559
|
+
return toForeignCallResult([toSingle(Fr.ONE), ...log.toNoirSerialization().map(toSingleOrArray)]);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
524
563
|
async storeCapsule(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, capsule: ForeignCallArray) {
|
|
525
564
|
await this.typedOracle.storeCapsule(
|
|
526
565
|
AztecAddress.fromField(fromSingle(contractAddress)),
|
|
@@ -567,15 +606,31 @@ export class TXEService {
|
|
|
567
606
|
return toForeignCallResult([]);
|
|
568
607
|
}
|
|
569
608
|
|
|
570
|
-
// TODO: I forgot to add a corresponding function here, when I introduced an oracle method to txe_oracle.ts.
|
|
571
|
-
|
|
572
|
-
|
|
609
|
+
// TODO: I forgot to add a corresponding function here, when I introduced an oracle method to txe_oracle.ts.
|
|
610
|
+
// The compiler didn't throw an error, so it took me a while to learn of the existence of this file, and that I need
|
|
611
|
+
// to implement this function here. Isn't there a way to programmatically identify that this is missing, given the
|
|
612
|
+
// existence of a txe_oracle method?
|
|
613
|
+
async aes128Decrypt(
|
|
614
|
+
ciphertextBVecStorage: ForeignCallArray,
|
|
615
|
+
ciphertextLength: ForeignCallSingle,
|
|
616
|
+
iv: ForeignCallArray,
|
|
617
|
+
symKey: ForeignCallArray,
|
|
618
|
+
) {
|
|
619
|
+
const ciphertext = fromUintBoundedVec(ciphertextBVecStorage, ciphertextLength, 8);
|
|
573
620
|
const ivBuffer = fromUintArray(iv, 8);
|
|
574
621
|
const symKeyBuffer = fromUintArray(symKey, 8);
|
|
575
622
|
|
|
576
|
-
const plaintextBuffer = await this.typedOracle.aes128Decrypt(
|
|
623
|
+
const plaintextBuffer = await this.typedOracle.aes128Decrypt(ciphertext, ivBuffer, symKeyBuffer);
|
|
577
624
|
|
|
578
|
-
return toForeignCallResult(arrayToBoundedVec(bufferToU8Array(plaintextBuffer),
|
|
625
|
+
return toForeignCallResult(arrayToBoundedVec(bufferToU8Array(plaintextBuffer), ciphertextBVecStorage.length));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async getSharedSecret(address: ForeignCallSingle, ephPk: ForeignCallArray) {
|
|
629
|
+
const secret = await this.typedOracle.getSharedSecret(
|
|
630
|
+
AztecAddress.fromField(fromSingle(address)),
|
|
631
|
+
Point.fromFields(fromArray(ephPk)),
|
|
632
|
+
);
|
|
633
|
+
return toForeignCallResult(secret.toFields().map(toSingle));
|
|
579
634
|
}
|
|
580
635
|
|
|
581
636
|
// AVM opcodes
|
package/src/util/encoding.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function fromArray(obj: ForeignCallArray) {
|
|
|
31
31
|
/**
|
|
32
32
|
* Converts an array of Noir unsigned integers to a single tightly-packed buffer.
|
|
33
33
|
* @param uintBitSize If it's an array of Noir u8's, put `8`, etc.
|
|
34
|
-
* @returns
|
|
34
|
+
* @returns A buffer where each byte is correctly represented as a single byte in the buffer.
|
|
35
35
|
*/
|
|
36
36
|
export function fromUintArray(obj: ForeignCallArray, uintBitSize: number): Buffer {
|
|
37
37
|
if (uintBitSize % 8 !== 0) {
|
|
@@ -41,6 +41,24 @@ export function fromUintArray(obj: ForeignCallArray, uintBitSize: number): Buffe
|
|
|
41
41
|
return Buffer.concat(obj.map(str => hexToBuffer(str).slice(-uintByteSize)));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Converts a Noir BoundedVec of unsigned integers into a Buffer. Note that BoundedVecs are structs, and therefore
|
|
46
|
+
* translated as two separate ForeignCallArray and ForeignCallSingle values (an array and a single field).
|
|
47
|
+
*
|
|
48
|
+
* @param storage The array with the BoundedVec's storage (i.e. BoundedVec::storage())
|
|
49
|
+
* @param length The length of the BoundedVec (i.e. BoundedVec::len())
|
|
50
|
+
* @param uintBitSize If it's an array of Noir u8's, put `8`, etc.
|
|
51
|
+
* @returns A buffer containing the unsigned integers tightly packed
|
|
52
|
+
*/
|
|
53
|
+
export function fromUintBoundedVec(storage: ForeignCallArray, length: ForeignCallSingle, uintBitSize: number): Buffer {
|
|
54
|
+
if (uintBitSize % 8 !== 0) {
|
|
55
|
+
throw new Error(`u${uintBitSize} is not a supported type in Noir`);
|
|
56
|
+
}
|
|
57
|
+
const uintByteSize = uintBitSize / 8;
|
|
58
|
+
const boundedStorage = storage.slice(0, fromSingle(length).toNumber());
|
|
59
|
+
return Buffer.concat(boundedStorage.map(str => hexToBuffer(str).slice(-uintByteSize)));
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
export function toSingle(obj: Fr | AztecAddress): ForeignCallSingle {
|
|
45
63
|
return obj.toString().slice(2);
|
|
46
64
|
}
|
|
@@ -49,6 +67,10 @@ export function toArray(objs: Fr[]): ForeignCallArray {
|
|
|
49
67
|
return objs.map(obj => obj.toString());
|
|
50
68
|
}
|
|
51
69
|
|
|
70
|
+
export function toSingleOrArray(value: Fr | Fr[]) {
|
|
71
|
+
return Array.isArray(value) ? value.map(toSingle) : toSingle(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
export function bufferToU8Array(buffer: Buffer): ForeignCallArray {
|
|
53
75
|
return toArray(Array.from(buffer).map(byte => new Fr(byte)));
|
|
54
76
|
}
|
|
@@ -57,21 +79,24 @@ export function bufferToU8Array(buffer: Buffer): ForeignCallArray {
|
|
|
57
79
|
* Converts a ForeignCallArray into a tuple which represents a nr BoundedVec.
|
|
58
80
|
* If the input array is shorter than the maxLen, it pads the result with zeros,
|
|
59
81
|
* so that nr can correctly coerce this result into a BoundedVec.
|
|
60
|
-
* @param array
|
|
82
|
+
* @param bVecStorage - The array underlying the BoundedVec.
|
|
61
83
|
* @param maxLen - the max length of the BoundedVec.
|
|
62
84
|
* @returns a tuple representing a BoundedVec.
|
|
63
85
|
*/
|
|
64
|
-
export function arrayToBoundedVec(
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
export function arrayToBoundedVec(
|
|
87
|
+
bVecStorage: ForeignCallArray,
|
|
88
|
+
maxLen: number,
|
|
89
|
+
): [ForeignCallArray, ForeignCallSingle] {
|
|
90
|
+
if (bVecStorage.length > maxLen) {
|
|
91
|
+
throw new Error(`Array of length ${bVecStorage.length} larger than maxLen ${maxLen}`);
|
|
67
92
|
}
|
|
68
|
-
const lengthDiff = maxLen -
|
|
93
|
+
const lengthDiff = maxLen - bVecStorage.length;
|
|
69
94
|
// We pad the array to the maxLen of the BoundedVec.
|
|
70
95
|
const zeroPaddingArray = toArray(Array(lengthDiff).fill(new Fr(0)));
|
|
71
96
|
|
|
72
97
|
// These variable names match with the BoundedVec members in nr:
|
|
73
|
-
const storage =
|
|
74
|
-
const len = toSingle(new Fr(
|
|
98
|
+
const storage = bVecStorage.concat(zeroPaddingArray);
|
|
99
|
+
const len = toSingle(new Fr(bVecStorage.length));
|
|
75
100
|
return [storage, len];
|
|
76
101
|
}
|
|
77
102
|
|
|
@@ -16,14 +16,6 @@ import type { TXE } from '../oracle/txe_oracle.js';
|
|
|
16
16
|
export class TXEPublicContractDataSource implements ContractDataSource {
|
|
17
17
|
constructor(private txeOracle: TXE) {}
|
|
18
18
|
|
|
19
|
-
async getPublicFunction(address: AztecAddress, selector: FunctionSelector): Promise<PublicFunction | undefined> {
|
|
20
|
-
const bytecode = await this.txeOracle.getContractDataProvider().getBytecode(address, selector);
|
|
21
|
-
if (!bytecode) {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
return { bytecode, selector };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
19
|
getBlockNumber(): Promise<number> {
|
|
28
20
|
return this.txeOracle.getBlockNumber();
|
|
29
21
|
}
|
|
@@ -92,10 +84,4 @@ export class TXEPublicContractDataSource implements ContractDataSource {
|
|
|
92
84
|
registerContractFunctionSignatures(_address: AztecAddress, _signatures: []): Promise<void> {
|
|
93
85
|
return Promise.resolve();
|
|
94
86
|
}
|
|
95
|
-
|
|
96
|
-
// TODO(#10007): Remove this method.
|
|
97
|
-
addContractClass(_contractClass: ContractClassPublic): Promise<void> {
|
|
98
|
-
// We don't really need to do anything for the txe here
|
|
99
|
-
return Promise.resolve();
|
|
100
|
-
}
|
|
101
87
|
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { Fr } from '@aztec/foundation/fields';
|
|
2
|
-
import {
|
|
2
|
+
import { PublicTreesDB } from '@aztec/simulator/server';
|
|
3
3
|
import { PublicDataWrite } from '@aztec/stdlib/avm';
|
|
4
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
-
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
6
5
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
7
6
|
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
|
|
8
7
|
import { MerkleTreeId, type PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
|
|
9
8
|
|
|
10
9
|
import type { TXE } from '../oracle/txe_oracle.js';
|
|
11
10
|
|
|
12
|
-
export class
|
|
13
|
-
constructor(private merkleDb: MerkleTreeWriteOperations,
|
|
14
|
-
super(merkleDb
|
|
11
|
+
export class TXEPublicTreesDB extends PublicTreesDB {
|
|
12
|
+
constructor(private merkleDb: MerkleTreeWriteOperations, private txe: TXE) {
|
|
13
|
+
super(merkleDb);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
override async storageRead(contract: AztecAddress, slot: Fr): Promise<Fr> {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"txe_world_state_db.d.ts","sourceRoot":"","sources":["../../src/util/txe_world_state_db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAGjF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,qBAAa,eAAgB,SAAQ,YAAY;IACnC,OAAO,CAAC,QAAQ;IAA6D,OAAO,CAAC,GAAG;gBAAhF,QAAQ,EAAE,yBAAyB,EAAE,UAAU,EAAE,kBAAkB,EAAU,GAAG,EAAE,GAAG;IAI1F,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IAgB1D,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAK3F"}
|