@aztec/simulator 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2
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/acvm/acvm.js +18 -21
- package/dest/acvm/acvm_types.js +3 -2
- package/dest/acvm/deserialize.js +9 -13
- package/dest/acvm/index.js +0 -1
- package/dest/acvm/oracle/index.js +0 -1
- package/dest/acvm/oracle/oracle.js +42 -23
- package/dest/acvm/oracle/typed_oracle.js +36 -38
- package/dest/acvm/serialize.js +7 -14
- package/dest/avm/avm_context.js +24 -27
- package/dest/avm/avm_contract_call_result.js +12 -7
- package/dest/avm/avm_execution_environment.js +10 -7
- package/dest/avm/avm_gas.js +93 -56
- package/dest/avm/avm_machine_state.js +60 -61
- package/dest/avm/avm_memory_types.js +166 -255
- package/dest/avm/avm_simulator.js +68 -47
- package/dest/avm/avm_tree.js +282 -276
- package/dest/avm/bytecode_utils.js +8 -6
- package/dest/avm/errors.js +46 -63
- package/dest/avm/fixtures/avm_simulation_tester.js +18 -17
- package/dest/avm/fixtures/base_avm_simulation_tester.js +21 -16
- package/dest/avm/fixtures/index.js +27 -26
- package/dest/avm/fixtures/simple_contract_data_source.js +9 -13
- package/dest/avm/index.js +0 -1
- package/dest/avm/journal/index.js +0 -1
- package/dest/avm/journal/journal.js +147 -200
- package/dest/avm/journal/nullifiers.js +43 -46
- package/dest/avm/journal/public_storage.js +73 -87
- package/dest/avm/opcodes/accrued_substate.js +140 -110
- package/dest/avm/opcodes/addressing_mode.js +29 -31
- package/dest/avm/opcodes/arithmetic.js +17 -15
- package/dest/avm/opcodes/bitwise.js +40 -26
- package/dest/avm/opcodes/comparators.js +12 -10
- package/dest/avm/opcodes/contract.js +31 -29
- package/dest/avm/opcodes/control_flow.js +47 -43
- package/dest/avm/opcodes/conversion.js +30 -26
- package/dest/avm/opcodes/ec_add.js +35 -34
- package/dest/avm/opcodes/environment_getters.js +33 -33
- package/dest/avm/opcodes/external_calls.js +83 -74
- package/dest/avm/opcodes/hashing.js +69 -61
- package/dest/avm/opcodes/index.js +0 -1
- package/dest/avm/opcodes/instruction.js +31 -40
- package/dest/avm/opcodes/instruction_impl.js +12 -15
- package/dest/avm/opcodes/memory.js +177 -156
- package/dest/avm/opcodes/misc.js +27 -25
- package/dest/avm/opcodes/multi_scalar_mul.js +43 -41
- package/dest/avm/opcodes/storage.js +28 -25
- package/dest/avm/serialization/buffer_cursor.js +4 -4
- package/dest/avm/serialization/bytecode_serialization.js +292 -89
- package/dest/avm/serialization/instruction_serialization.js +67 -28
- package/dest/avm/test_utils.js +6 -9
- package/dest/client/client_execution_context.js +197 -219
- package/dest/client/db_oracle.js +4 -7
- package/dest/client/execution_note_cache.js +80 -81
- package/dest/client/index.js +0 -1
- package/dest/client/pick_notes.js +27 -30
- package/dest/client/private_execution.js +13 -14
- package/dest/client/simulator.js +44 -48
- package/dest/client/unconstrained_execution.js +8 -11
- package/dest/client/view_data_oracle.js +130 -139
- package/dest/common/debug_fn_name.js +1 -4
- package/dest/common/errors.js +30 -39
- package/dest/common/hashed_values_cache.js +16 -20
- package/dest/common/index.js +0 -1
- package/dest/common/message_load_oracle_inputs.js +7 -7
- package/dest/common/simulation_provider.js +3 -6
- package/dest/common.js +0 -1
- package/dest/providers/acvm_native.js +46 -32
- package/dest/providers/acvm_wasm.js +18 -10
- package/dest/providers/acvm_wasm_with_blobs.js +2 -5
- package/dest/providers/factory.js +5 -5
- package/dest/providers/index.js +0 -1
- package/dest/public/bytecode_errors.js +1 -2
- package/dest/public/db_interfaces.js +1 -2
- package/dest/public/execution.js +2 -4
- package/dest/public/executor_metrics.js +16 -12
- package/dest/public/fee_payment.js +2 -5
- package/dest/public/fixtures/index.js +0 -1
- package/dest/public/fixtures/public_tx_simulation_tester.js +13 -17
- package/dest/public/fixtures/utils.js +11 -14
- package/dest/public/index.js +0 -1
- package/dest/public/public_db_sources.js +79 -87
- package/dest/public/public_processor.js +310 -306
- package/dest/public/public_processor_metrics.js +46 -27
- package/dest/public/public_tx_context.js +97 -118
- package/dest/public/public_tx_simulator.js +299 -314
- package/dest/public/side_effect_errors.js +1 -2
- package/dest/public/side_effect_trace.js +44 -71
- package/dest/public/side_effect_trace_interface.js +1 -2
- package/dest/public/unique_class_ids.js +22 -27
- package/dest/public/utils.js +16 -11
- package/dest/server.js +0 -1
- package/dest/stats/index.js +0 -1
- package/dest/stats/stats.js +1 -2
- package/dest/test/utils.js +5 -4
- package/package.json +11 -11
- package/src/acvm/oracle/typed_oracle.ts +34 -34
- package/src/avm/avm_machine_state.ts +18 -14
- package/src/avm/avm_memory_types.ts +43 -183
- package/src/avm/avm_simulator.ts +37 -11
- package/src/avm/opcodes/accrued_substate.ts +7 -21
- package/src/avm/opcodes/addressing_mode.ts +9 -2
- package/src/avm/opcodes/arithmetic.ts +1 -3
- package/src/avm/opcodes/bitwise.ts +2 -6
- package/src/avm/opcodes/comparators.ts +1 -3
- package/src/avm/opcodes/contract.ts +1 -3
- package/src/avm/opcodes/control_flow.ts +1 -9
- package/src/avm/opcodes/conversion.ts +1 -3
- package/src/avm/opcodes/ec_add.ts +1 -3
- package/src/avm/opcodes/environment_getters.ts +1 -3
- package/src/avm/opcodes/external_calls.ts +3 -6
- package/src/avm/opcodes/hashing.ts +3 -9
- package/src/avm/opcodes/memory.ts +6 -20
- package/src/avm/opcodes/misc.ts +1 -3
- package/src/avm/opcodes/multi_scalar_mul.ts +1 -7
- package/src/avm/opcodes/storage.ts +2 -6
- package/src/client/index.ts +2 -2
- package/dest/acvm/acvm.d.ts +0 -35
- package/dest/acvm/acvm.d.ts.map +0 -1
- package/dest/acvm/acvm_types.d.ts +0 -10
- package/dest/acvm/acvm_types.d.ts.map +0 -1
- package/dest/acvm/deserialize.d.ts +0 -36
- package/dest/acvm/deserialize.d.ts.map +0 -1
- package/dest/acvm/index.d.ts +0 -6
- package/dest/acvm/index.d.ts.map +0 -1
- package/dest/acvm/oracle/index.d.ts +0 -14
- package/dest/acvm/oracle/index.d.ts.map +0 -1
- package/dest/acvm/oracle/oracle.d.ts +0 -49
- package/dest/acvm/oracle/oracle.d.ts.map +0 -1
- package/dest/acvm/oracle/typed_oracle.d.ts +0 -75
- package/dest/acvm/oracle/typed_oracle.d.ts.map +0 -1
- package/dest/acvm/serialize.d.ts +0 -20
- package/dest/acvm/serialize.d.ts.map +0 -1
- package/dest/avm/avm_context.d.ts +0 -39
- package/dest/avm/avm_context.d.ts.map +0 -1
- package/dest/avm/avm_contract_call_result.d.ts +0 -30
- package/dest/avm/avm_contract_call_result.d.ts.map +0 -1
- package/dest/avm/avm_execution_environment.d.ts +0 -21
- package/dest/avm/avm_execution_environment.d.ts.map +0 -1
- package/dest/avm/avm_gas.d.ts +0 -60
- package/dest/avm/avm_gas.d.ts.map +0 -1
- package/dest/avm/avm_machine_state.d.ts +0 -93
- package/dest/avm/avm_machine_state.d.ts.map +0 -1
- package/dest/avm/avm_memory_types.d.ts +0 -310
- package/dest/avm/avm_memory_types.d.ts.map +0 -1
- package/dest/avm/avm_simulator.d.ts +0 -37
- package/dest/avm/avm_simulator.d.ts.map +0 -1
- package/dest/avm/avm_tree.d.ts +0 -281
- package/dest/avm/avm_tree.d.ts.map +0 -1
- package/dest/avm/bytecode_utils.d.ts +0 -5
- package/dest/avm/bytecode_utils.d.ts.map +0 -1
- package/dest/avm/errors.d.ts +0 -121
- package/dest/avm/errors.d.ts.map +0 -1
- package/dest/avm/fixtures/avm_simulation_tester.d.ts +0 -21
- package/dest/avm/fixtures/avm_simulation_tester.d.ts.map +0 -1
- package/dest/avm/fixtures/base_avm_simulation_tester.d.ts +0 -35
- package/dest/avm/fixtures/base_avm_simulation_tester.d.ts.map +0 -1
- package/dest/avm/fixtures/index.d.ts +0 -67
- package/dest/avm/fixtures/index.d.ts.map +0 -1
- package/dest/avm/fixtures/simple_contract_data_source.d.ts +0 -31
- package/dest/avm/fixtures/simple_contract_data_source.d.ts.map +0 -1
- package/dest/avm/index.d.ts +0 -4
- package/dest/avm/index.d.ts.map +0 -1
- package/dest/avm/journal/index.d.ts +0 -2
- package/dest/avm/journal/index.d.ts.map +0 -1
- package/dest/avm/journal/journal.d.ts +0 -176
- package/dest/avm/journal/journal.d.ts.map +0 -1
- package/dest/avm/journal/nullifiers.d.ts +0 -62
- package/dest/avm/journal/nullifiers.d.ts.map +0 -1
- package/dest/avm/journal/public_storage.d.ts +0 -66
- package/dest/avm/journal/public_storage.d.ts.map +0 -1
- package/dest/avm/opcodes/accrued_substate.d.ts +0 -75
- package/dest/avm/opcodes/accrued_substate.d.ts.map +0 -1
- package/dest/avm/opcodes/addressing_mode.d.ts +0 -27
- package/dest/avm/opcodes/addressing_mode.d.ts.map +0 -1
- package/dest/avm/opcodes/arithmetic.d.ts +0 -37
- package/dest/avm/opcodes/arithmetic.d.ts.map +0 -1
- package/dest/avm/opcodes/bitwise.d.ts +0 -50
- package/dest/avm/opcodes/bitwise.d.ts.map +0 -1
- package/dest/avm/opcodes/comparators.d.ts +0 -25
- package/dest/avm/opcodes/comparators.d.ts.map +0 -1
- package/dest/avm/opcodes/contract.d.ts +0 -21
- package/dest/avm/opcodes/contract.d.ts.map +0 -1
- package/dest/avm/opcodes/control_flow.d.ts +0 -41
- package/dest/avm/opcodes/control_flow.d.ts.map +0 -1
- package/dest/avm/opcodes/conversion.d.ts +0 -17
- package/dest/avm/opcodes/conversion.d.ts.map +0 -1
- package/dest/avm/opcodes/ec_add.d.ts +0 -19
- package/dest/avm/opcodes/ec_add.d.ts.map +0 -1
- package/dest/avm/opcodes/environment_getters.d.ts +0 -28
- package/dest/avm/opcodes/environment_getters.d.ts.map +0 -1
- package/dest/avm/opcodes/external_calls.d.ts +0 -50
- package/dest/avm/opcodes/external_calls.d.ts.map +0 -1
- package/dest/avm/opcodes/hashing.d.ts +0 -36
- package/dest/avm/opcodes/hashing.d.ts.map +0 -1
- package/dest/avm/opcodes/index.d.ts +0 -16
- package/dest/avm/opcodes/index.d.ts.map +0 -1
- package/dest/avm/opcodes/instruction.d.ts +0 -70
- package/dest/avm/opcodes/instruction.d.ts.map +0 -1
- package/dest/avm/opcodes/instruction_impl.d.ts +0 -19
- package/dest/avm/opcodes/instruction_impl.d.ts.map +0 -1
- package/dest/avm/opcodes/memory.d.ts +0 -74
- package/dest/avm/opcodes/memory.d.ts.map +0 -1
- package/dest/avm/opcodes/misc.d.ts +0 -17
- package/dest/avm/opcodes/misc.d.ts.map +0 -1
- package/dest/avm/opcodes/multi_scalar_mul.d.ts +0 -16
- package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +0 -1
- package/dest/avm/opcodes/storage.d.ts +0 -24
- package/dest/avm/opcodes/storage.d.ts.map +0 -1
- package/dest/avm/serialization/buffer_cursor.d.ts +0 -28
- package/dest/avm/serialization/buffer_cursor.d.ts.map +0 -1
- package/dest/avm/serialization/bytecode_serialization.d.ts +0 -21
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +0 -1
- package/dest/avm/serialization/instruction_serialization.d.ts +0 -105
- package/dest/avm/serialization/instruction_serialization.d.ts.map +0 -1
- package/dest/avm/test_utils.d.ts +0 -16
- package/dest/avm/test_utils.d.ts.map +0 -1
- package/dest/client/client_execution_context.d.ts +0 -214
- package/dest/client/client_execution_context.d.ts.map +0 -1
- package/dest/client/db_oracle.d.ts +0 -229
- package/dest/client/db_oracle.d.ts.map +0 -1
- package/dest/client/execution_note_cache.d.ts +0 -93
- package/dest/client/execution_note_cache.d.ts.map +0 -1
- package/dest/client/index.d.ts +0 -15
- package/dest/client/index.d.ts.map +0 -1
- package/dest/client/pick_notes.d.ts +0 -85
- package/dest/client/pick_notes.d.ts.map +0 -1
- package/dest/client/private_execution.d.ts +0 -19
- package/dest/client/private_execution.d.ts.map +0 -1
- package/dest/client/simulator.d.ts +0 -60
- package/dest/client/simulator.d.ts.map +0 -1
- package/dest/client/unconstrained_execution.d.ts +0 -10
- package/dest/client/unconstrained_execution.d.ts.map +0 -1
- package/dest/client/view_data_oracle.d.ts +0 -159
- package/dest/client/view_data_oracle.d.ts.map +0 -1
- package/dest/common/debug_fn_name.d.ts +0 -4
- package/dest/common/debug_fn_name.d.ts.map +0 -1
- package/dest/common/errors.d.ts +0 -54
- package/dest/common/errors.d.ts.map +0 -1
- package/dest/common/hashed_values_cache.d.ts +0 -28
- package/dest/common/hashed_values_cache.d.ts.map +0 -1
- package/dest/common/index.d.ts +0 -3
- package/dest/common/index.d.ts.map +0 -1
- package/dest/common/message_load_oracle_inputs.d.ts +0 -15
- package/dest/common/message_load_oracle_inputs.d.ts.map +0 -1
- package/dest/common/simulation_provider.d.ts +0 -19
- package/dest/common/simulation_provider.d.ts.map +0 -1
- package/dest/common.d.ts +0 -2
- package/dest/common.d.ts.map +0 -1
- package/dest/providers/acvm_native.d.ts +0 -40
- package/dest/providers/acvm_native.d.ts.map +0 -1
- package/dest/providers/acvm_wasm.d.ts +0 -15
- package/dest/providers/acvm_wasm.d.ts.map +0 -1
- package/dest/providers/acvm_wasm_with_blobs.d.ts +0 -19
- package/dest/providers/acvm_wasm_with_blobs.d.ts.map +0 -1
- package/dest/providers/factory.d.ts +0 -12
- package/dest/providers/factory.d.ts.map +0 -1
- package/dest/providers/index.d.ts +0 -5
- package/dest/providers/index.d.ts.map +0 -1
- package/dest/public/bytecode_errors.d.ts +0 -4
- package/dest/public/bytecode_errors.d.ts.map +0 -1
- package/dest/public/db_interfaces.d.ts +0 -105
- package/dest/public/db_interfaces.d.ts.map +0 -1
- package/dest/public/execution.d.ts +0 -102
- package/dest/public/execution.d.ts.map +0 -1
- package/dest/public/executor_metrics.d.ts +0 -13
- package/dest/public/executor_metrics.d.ts.map +0 -1
- package/dest/public/fee_payment.d.ts +0 -11
- package/dest/public/fee_payment.d.ts.map +0 -1
- package/dest/public/fixtures/index.d.ts +0 -3
- package/dest/public/fixtures/index.d.ts.map +0 -1
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts +0 -21
- package/dest/public/fixtures/public_tx_simulation_tester.d.ts.map +0 -1
- package/dest/public/fixtures/utils.d.ts +0 -17
- package/dest/public/fixtures/utils.d.ts.map +0 -1
- package/dest/public/index.d.ts +0 -9
- package/dest/public/index.d.ts.map +0 -1
- package/dest/public/public_db_sources.d.ts +0 -81
- package/dest/public/public_db_sources.d.ts.map +0 -1
- package/dest/public/public_processor.d.ts +0 -72
- package/dest/public/public_processor.d.ts.map +0 -1
- package/dest/public/public_processor_metrics.d.ts +0 -27
- package/dest/public/public_processor_metrics.d.ts.map +0 -1
- package/dest/public/public_tx_context.d.ts +0 -131
- package/dest/public/public_tx_context.d.ts.map +0 -1
- package/dest/public/public_tx_simulator.d.ts +0 -99
- package/dest/public/public_tx_simulator.d.ts.map +0 -1
- package/dest/public/side_effect_errors.d.ts +0 -4
- package/dest/public/side_effect_errors.d.ts.map +0 -1
- package/dest/public/side_effect_trace.d.ts +0 -126
- package/dest/public/side_effect_trace.d.ts.map +0 -1
- package/dest/public/side_effect_trace_interface.d.ts +0 -32
- package/dest/public/side_effect_trace_interface.d.ts.map +0 -1
- package/dest/public/unique_class_ids.d.ts +0 -37
- package/dest/public/unique_class_ids.d.ts.map +0 -1
- package/dest/public/utils.d.ts +0 -5
- package/dest/public/utils.d.ts.map +0 -1
- package/dest/server.d.ts +0 -6
- package/dest/server.d.ts.map +0 -1
- package/dest/stats/index.d.ts +0 -2
- package/dest/stats/index.d.ts.map +0 -1
- package/dest/stats/stats.d.ts +0 -4
- package/dest/stats/stats.d.ts.map +0 -1
- package/dest/test/utils.d.ts +0 -12
- package/dest/test/utils.d.ts.map +0 -1
package/dest/avm/avm_tree.js
CHANGED
|
@@ -4,19 +4,18 @@ import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
|
4
4
|
import { Fr } from '@aztec/foundation/fields';
|
|
5
5
|
import { strict as assert } from 'assert';
|
|
6
6
|
import cloneDeep from 'lodash.clonedeep';
|
|
7
|
-
/****************************************************/
|
|
8
|
-
/****** The AvmEphemeralForest Class ****************/
|
|
9
|
-
/****************************************************/
|
|
10
|
-
/**
|
|
7
|
+
/****************************************************/ /****** The AvmEphemeralForest Class ****************/ /****************************************************/ /**
|
|
11
8
|
* This provides a forkable abstraction over the EphemeralAvmTree class
|
|
12
9
|
* It contains the logic to look up into a read-only MerkleTreeDb to discover
|
|
13
10
|
* the sibling paths and low witnesses that weren't inserted as part of this tx
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
*/ export class AvmEphemeralForest {
|
|
12
|
+
treeDb;
|
|
13
|
+
treeMap;
|
|
14
|
+
indexedUpdates;
|
|
15
|
+
indexedSortedKeys;
|
|
16
|
+
constructor(treeDb, treeMap, // This contains the [leaf index,indexed leaf preimages] tuple that were updated or inserted in the ephemeral tree
|
|
18
17
|
// This is needed since we have a sparse collection of keys sorted leaves in the ephemeral tree
|
|
19
|
-
indexedUpdates, indexedSortedKeys)
|
|
18
|
+
indexedUpdates, indexedSortedKeys){
|
|
20
19
|
this.treeDb = treeDb;
|
|
21
20
|
this.treeMap = treeMap;
|
|
22
21
|
this.indexedUpdates = indexedUpdates;
|
|
@@ -24,7 +23,11 @@ export class AvmEphemeralForest {
|
|
|
24
23
|
}
|
|
25
24
|
static async create(treeDb) {
|
|
26
25
|
const treeMap = new Map();
|
|
27
|
-
for (const treeType of [
|
|
26
|
+
for (const treeType of [
|
|
27
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
28
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
29
|
+
MerkleTreeId.PUBLIC_DATA_TREE
|
|
30
|
+
]){
|
|
28
31
|
const treeInfo = await treeDb.getTreeInfo(treeType);
|
|
29
32
|
const tree = await EphemeralAvmTree.create(treeInfo.size, treeInfo.depth, treeDb, treeType);
|
|
30
33
|
treeMap.set(treeType, tree);
|
|
@@ -38,12 +41,11 @@ export class AvmEphemeralForest {
|
|
|
38
41
|
return new AvmEphemeralForest(this.treeDb, cloneDeep(this.treeMap), cloneDeep(this.indexedUpdates), cloneDeep(this.indexedSortedKeys));
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async getSiblingPath(treeId, index) {
|
|
44
|
+
* Gets sibling path for a leaf - if the sibling path is not found in the tree, it is fetched from the DB
|
|
45
|
+
* @param treeId - The tree to be queried for a sibling path.
|
|
46
|
+
* @param index - The index of the leaf for which a sibling path should be returned.
|
|
47
|
+
* @returns The sibling path of the leaf.
|
|
48
|
+
*/ async getSiblingPath(treeId, index) {
|
|
47
49
|
const tree = this.treeMap.get(treeId);
|
|
48
50
|
let path = await tree.getSiblingPath(index);
|
|
49
51
|
if (path === undefined) {
|
|
@@ -51,7 +53,7 @@ export class AvmEphemeralForest {
|
|
|
51
53
|
path = (await this.treeDb.getSiblingPath(treeId, index)).toFields();
|
|
52
54
|
// Since the sibling path could be outdated, we compare it with nodes in our tree
|
|
53
55
|
// if we encounter a mismatch, we replace it with the node we found in our tree.
|
|
54
|
-
for
|
|
56
|
+
for(let i = 0; i < path.length; i++){
|
|
55
57
|
const siblingIndex = index ^ 1n;
|
|
56
58
|
const node = tree.getNode(siblingIndex, tree.depth - i);
|
|
57
59
|
if (node !== undefined) {
|
|
@@ -66,14 +68,13 @@ export class AvmEphemeralForest {
|
|
|
66
68
|
return path;
|
|
67
69
|
}
|
|
68
70
|
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
async appendIndexedTree(treeId, lowLeafIndex, lowLeafPreimage, newLeafPreimage) {
|
|
71
|
+
* This does the work of appending the new leaf and updating the low witness
|
|
72
|
+
* @param treeId - The tree to be queried for a sibling path.
|
|
73
|
+
* @param lowWitnessIndex - The index of the low leaf in the tree.
|
|
74
|
+
* @param lowWitness - The preimage of the low leaf.
|
|
75
|
+
* @param newLeafPreimage - The preimage of the new leaf to be inserted.
|
|
76
|
+
* @returns The sibling path of the new leaf (i.e. the insertion path)
|
|
77
|
+
*/ async appendIndexedTree(treeId, lowLeafIndex, lowLeafPreimage, newLeafPreimage) {
|
|
77
78
|
const tree = this.treeMap.get(treeId);
|
|
78
79
|
const newLeaf = await this.hashPreimage(newLeafPreimage);
|
|
79
80
|
const insertIndex = tree.leafCount;
|
|
@@ -84,15 +85,14 @@ export class AvmEphemeralForest {
|
|
|
84
85
|
// Append the new leaf
|
|
85
86
|
tree.appendLeaf(newLeaf);
|
|
86
87
|
this.setIndexedUpdates(treeId, insertIndex, newLeafPreimage);
|
|
87
|
-
return
|
|
88
|
+
return await tree.getSiblingPath(insertIndex);
|
|
88
89
|
}
|
|
89
90
|
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
async writePublicStorage(slot, newValue) {
|
|
91
|
+
* This writes or updates a slot in the public data tree with a value
|
|
92
|
+
* @param slot - The slot to be written to.
|
|
93
|
+
* @param newValue - The value to be written or updated to
|
|
94
|
+
* @returns The insertion result which contains the insertion path, low leaf and the new leaf index
|
|
95
|
+
*/ async writePublicStorage(slot, newValue) {
|
|
96
96
|
// This only works for the public data tree
|
|
97
97
|
const treeId = MerkleTreeId.PUBLIC_DATA_TREE;
|
|
98
98
|
const tree = this.treeMap.get(treeId);
|
|
@@ -109,15 +109,19 @@ export class AvmEphemeralForest {
|
|
|
109
109
|
updatedPreimage.value = newValue;
|
|
110
110
|
tree.updateLeaf(await this.hashPreimage(updatedPreimage), lowLeafIndex);
|
|
111
111
|
this.setIndexedUpdates(treeId, lowLeafIndex, updatedPreimage);
|
|
112
|
-
this._updateSortedKeys(treeId, [
|
|
112
|
+
this._updateSortedKeys(treeId, [
|
|
113
|
+
updatedPreimage.slot
|
|
114
|
+
], [
|
|
115
|
+
lowLeafIndex
|
|
116
|
+
]);
|
|
113
117
|
return {
|
|
114
118
|
element: updatedPreimage,
|
|
115
119
|
lowWitness: {
|
|
116
120
|
preimage: preimage,
|
|
117
121
|
index: lowLeafIndex,
|
|
118
|
-
siblingPath: existingPublicDataSiblingPath
|
|
122
|
+
siblingPath: existingPublicDataSiblingPath
|
|
119
123
|
},
|
|
120
|
-
update: true
|
|
124
|
+
update: true
|
|
121
125
|
};
|
|
122
126
|
}
|
|
123
127
|
// We are writing to a new slot, so our preimage is a lowNullifier
|
|
@@ -129,42 +133,49 @@ export class AvmEphemeralForest {
|
|
|
129
133
|
const insertionPath = await this.appendIndexedTree(treeId, lowLeafIndex, updatedLowLeaf, newPublicDataLeaf);
|
|
130
134
|
// Even though the low leaf key is not updated, we still need to update the sorted keys in case we have
|
|
131
135
|
// not seen the low leaf before
|
|
132
|
-
this._updateSortedKeys(treeId, [
|
|
136
|
+
this._updateSortedKeys(treeId, [
|
|
137
|
+
newPublicDataLeaf.slot,
|
|
138
|
+
updatedLowLeaf.slot
|
|
139
|
+
], [
|
|
140
|
+
insertionIndex,
|
|
141
|
+
lowLeafIndex
|
|
142
|
+
]);
|
|
133
143
|
return {
|
|
134
144
|
element: newPublicDataLeaf,
|
|
135
145
|
lowWitness: {
|
|
136
146
|
preimage,
|
|
137
147
|
index: lowLeafIndex,
|
|
138
|
-
siblingPath
|
|
148
|
+
siblingPath
|
|
139
149
|
},
|
|
140
150
|
update: false,
|
|
141
151
|
leafIndex: insertionIndex,
|
|
142
|
-
insertionPath: insertionPath
|
|
152
|
+
insertionPath: insertionPath
|
|
143
153
|
};
|
|
144
154
|
}
|
|
145
155
|
_updateSortedKeys(treeId, keys, index) {
|
|
146
156
|
// This is a reference
|
|
147
157
|
const existingKeyVector = this.indexedSortedKeys.get(treeId);
|
|
148
158
|
// Should already be sorted so not need to re-sort if we just update or splice
|
|
149
|
-
for
|
|
150
|
-
const foundIndex = existingKeyVector.findIndex(x
|
|
159
|
+
for(let i = 0; i < keys.length; i++){
|
|
160
|
+
const foundIndex = existingKeyVector.findIndex((x)=>x[1] === index[i]);
|
|
151
161
|
if (foundIndex === -1) {
|
|
152
162
|
// New element, we splice it into the correct location
|
|
153
|
-
const spliceIndex = indexOrNextLowestInArray(keys[i], existingKeyVector.map(x
|
|
154
|
-
existingKeyVector.splice(spliceIndex, 0, [
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
const spliceIndex = indexOrNextLowestInArray(keys[i], existingKeyVector.map((x)=>x[0])) + 1;
|
|
164
|
+
existingKeyVector.splice(spliceIndex, 0, [
|
|
165
|
+
keys[i],
|
|
166
|
+
index[i]
|
|
167
|
+
]);
|
|
168
|
+
} else {
|
|
157
169
|
// Update the existing element
|
|
158
170
|
existingKeyVector[foundIndex][0] = keys[i];
|
|
159
171
|
}
|
|
160
172
|
}
|
|
161
173
|
}
|
|
162
174
|
/**
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
async appendNullifier(nullifier) {
|
|
175
|
+
* This appends a nullifier to the nullifier tree, and throws if the nullifier already exists
|
|
176
|
+
* @param value - The nullifier to be appended
|
|
177
|
+
* @returns The insertion result which contains the insertion path, low leaf and the new leaf index
|
|
178
|
+
*/ async appendNullifier(nullifier) {
|
|
168
179
|
const treeId = MerkleTreeId.NULLIFIER_TREE;
|
|
169
180
|
const tree = this.treeMap.get(treeId);
|
|
170
181
|
const [leafOrLowLeafInfo, pathAbsentInEphemeralTree] = await this._getLeafOrLowLeafInfo(treeId, nullifier);
|
|
@@ -184,24 +195,29 @@ export class AvmEphemeralForest {
|
|
|
184
195
|
const insertionPath = await this.appendIndexedTree(treeId, index, updatedLowNullifier, newNullifierLeaf);
|
|
185
196
|
// Even though the low nullifier key is not updated, we still need to update the sorted keys in case we have
|
|
186
197
|
// not seen the low nullifier before
|
|
187
|
-
this._updateSortedKeys(treeId, [
|
|
198
|
+
this._updateSortedKeys(treeId, [
|
|
199
|
+
newNullifierLeaf.nullifier,
|
|
200
|
+
updatedLowNullifier.nullifier
|
|
201
|
+
], [
|
|
202
|
+
insertionIndex,
|
|
203
|
+
index
|
|
204
|
+
]);
|
|
188
205
|
return {
|
|
189
206
|
element: newNullifierLeaf,
|
|
190
207
|
lowWitness: {
|
|
191
208
|
preimage,
|
|
192
209
|
index,
|
|
193
|
-
siblingPath
|
|
210
|
+
siblingPath
|
|
194
211
|
},
|
|
195
212
|
leafIndex: insertionIndex,
|
|
196
|
-
insertionPath: insertionPath
|
|
213
|
+
insertionPath: insertionPath
|
|
197
214
|
};
|
|
198
215
|
}
|
|
199
216
|
/**
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
async appendNoteHash(noteHash) {
|
|
217
|
+
* This appends a note hash to the note hash tree
|
|
218
|
+
* @param value - The note hash to be appended
|
|
219
|
+
* @returns The insertion result which contains the insertion path
|
|
220
|
+
*/ async appendNoteHash(noteHash) {
|
|
205
221
|
const tree = this.treeMap.get(MerkleTreeId.NOTE_HASH_TREE);
|
|
206
222
|
tree.appendLeaf(noteHash);
|
|
207
223
|
// We use leafCount - 1 here because we would have just appended a leaf
|
|
@@ -209,9 +225,8 @@ export class AvmEphemeralForest {
|
|
|
209
225
|
return insertionPath;
|
|
210
226
|
}
|
|
211
227
|
/**
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
setIndexedUpdates(treeId, index, preimage) {
|
|
228
|
+
* This is wrapper around treeId to set values in the indexedUpdates map
|
|
229
|
+
*/ setIndexedUpdates(treeId, index, preimage) {
|
|
215
230
|
let updates = this.indexedUpdates.get(treeId);
|
|
216
231
|
if (updates === undefined) {
|
|
217
232
|
updates = new Map();
|
|
@@ -220,10 +235,9 @@ export class AvmEphemeralForest {
|
|
|
220
235
|
updates.set(index, preimage);
|
|
221
236
|
}
|
|
222
237
|
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
getIndexedUpdate(treeId, index) {
|
|
238
|
+
* This is wrapper around treeId to get values in the indexedUpdates map.
|
|
239
|
+
* Should only be called if we know the value exists.
|
|
240
|
+
*/ getIndexedUpdate(treeId, index) {
|
|
227
241
|
const updates = this.indexedUpdates.get(treeId);
|
|
228
242
|
assert(updates !== undefined, `No updates exist in the ephemeral ${MerkleTreeId[treeId]} tree.`);
|
|
229
243
|
const preimage = updates.get(index);
|
|
@@ -231,9 +245,8 @@ export class AvmEphemeralForest {
|
|
|
231
245
|
return preimage;
|
|
232
246
|
}
|
|
233
247
|
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
hasLocalUpdates(treeId, index) {
|
|
248
|
+
* This is wrapper around treeId to check membership (i.e. has()) of index in the indexedUpdates map
|
|
249
|
+
*/ hasLocalUpdates(treeId, index) {
|
|
237
250
|
const updates = this.indexedUpdates.get(treeId);
|
|
238
251
|
if (updates === undefined) {
|
|
239
252
|
return false;
|
|
@@ -241,30 +254,28 @@ export class AvmEphemeralForest {
|
|
|
241
254
|
return updates.has(index);
|
|
242
255
|
}
|
|
243
256
|
/**
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
async getLeafOrLowLeafInfo(treeId, key) {
|
|
257
|
+
* Get the leaf or low leaf preimage and its index in the indexed tree given a key (slot or nullifier value).
|
|
258
|
+
* If the key is not found in the tree, it does an external lookup to the underlying merkle DB.
|
|
259
|
+
* @param treeId - The tree we are looking up in
|
|
260
|
+
* @param key - The key for which we are look up the leaf or low leaf for.
|
|
261
|
+
* @param T - The type of the preimage (PublicData or Nullifier)
|
|
262
|
+
* @returns The leaf or low leaf info (preimage & leaf index).
|
|
263
|
+
*/ async getLeafOrLowLeafInfo(treeId, key) {
|
|
252
264
|
const [leafOrLowLeafInfo, _] = await this._getLeafOrLowLeafInfo(treeId, key);
|
|
253
265
|
return leafOrLowLeafInfo;
|
|
254
266
|
}
|
|
255
267
|
/**
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
async _getLeafOrLowLeafInfo(treeId, key) {
|
|
268
|
+
* Internal helper to get the leaf or low leaf preimage and its index in the indexed tree given a key (slot or nullifier value).
|
|
269
|
+
* If the key is not found in the tree, it does an external lookup to the underlying merkle DB.
|
|
270
|
+
* Indicates whethe the sibling path is absent in the ephemeral tree.
|
|
271
|
+
* @param treeId - The tree we are looking up in
|
|
272
|
+
* @param key - The key for which we are look up the leaf or low leaf for.
|
|
273
|
+
* @param T - The type of the preimage (PublicData or Nullifier)
|
|
274
|
+
* @returns [
|
|
275
|
+
* getLeafResult - The leaf or low leaf info (preimage & leaf index),
|
|
276
|
+
* pathAbsentInEphemeralTree - whether its sibling path is absent in the ephemeral tree (useful during insertions)
|
|
277
|
+
* ]
|
|
278
|
+
*/ async _getLeafOrLowLeafInfo(treeId, key) {
|
|
268
279
|
const bigIntKey = key.toBigInt();
|
|
269
280
|
// In this function, "min" refers to the leaf with the
|
|
270
281
|
// largest key <= the specified key in the indexedUpdates.
|
|
@@ -276,7 +287,10 @@ export class AvmEphemeralForest {
|
|
|
276
287
|
const leafOrLowLeafWitnessFromExternalDb = await this._getLeafOrLowLeafWitnessInExternalDb(treeId, bigIntKey);
|
|
277
288
|
// If the indexed updates are empty, we can return the leaf from the DB
|
|
278
289
|
if (minIndexedLeafIndex === -1n) {
|
|
279
|
-
return [
|
|
290
|
+
return [
|
|
291
|
+
leafOrLowLeafWitnessFromExternalDb,
|
|
292
|
+
/*pathAbsentInEphemeralTree=*/ true
|
|
293
|
+
];
|
|
280
294
|
}
|
|
281
295
|
// Otherwise, we return the closest one. First fetch the leaf from the indexed updates.
|
|
282
296
|
const minIndexedUpdate = this.getIndexedUpdate(treeId, minIndexedLeafIndex);
|
|
@@ -285,56 +299,61 @@ export class AvmEphemeralForest {
|
|
|
285
299
|
const keyFromExternal = leafOrLowLeafWitnessFromExternalDb.preimage.getKey();
|
|
286
300
|
if (keyFromExternal > keyFromIndexed) {
|
|
287
301
|
// this.log.debug(`Using leaf from external DB for ${MerkleTreeId[treeId]}`);
|
|
288
|
-
return [
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
return [
|
|
303
|
+
leafOrLowLeafWitnessFromExternalDb,
|
|
304
|
+
/*pathAbsentInEphemeralTree=*/ true
|
|
305
|
+
];
|
|
306
|
+
} else {
|
|
291
307
|
// this.log.debug(`Using leaf from indexed DB for ${MerkleTreeId[treeId]}`);
|
|
292
308
|
const leafInfo = {
|
|
293
309
|
preimage: minIndexedUpdate,
|
|
294
310
|
index: minIndexedLeafIndex,
|
|
295
|
-
alreadyPresent: keyFromIndexed === bigIntKey
|
|
311
|
+
alreadyPresent: keyFromIndexed === bigIntKey
|
|
296
312
|
};
|
|
297
|
-
return [
|
|
313
|
+
return [
|
|
314
|
+
leafInfo,
|
|
315
|
+
/*pathAbsentInEphemeralTree=*/ false
|
|
316
|
+
];
|
|
298
317
|
}
|
|
299
318
|
}
|
|
300
319
|
/**
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
_getLeafIndexOrNextLowestInIndexedUpdates(treeId, key) {
|
|
320
|
+
* Helper to search for the leaf with the specified key in the indexedUpdates
|
|
321
|
+
* and return its leafIndex.
|
|
322
|
+
* If not present, return the leafIndex of the largest leaf <= the specified key
|
|
323
|
+
* (the leafIndex of the next lowest key).
|
|
324
|
+
*
|
|
325
|
+
* If no entry exists in indexedUpdates <= the specified key, return -1.
|
|
326
|
+
* @returns - The leafIndex of the leaf with the largest key <= the specified key.
|
|
327
|
+
*/ _getLeafIndexOrNextLowestInIndexedUpdates(treeId, key) {
|
|
310
328
|
const keyOrderedVector = this.indexedSortedKeys.get(treeId);
|
|
311
|
-
const indexInVector = indexOrNextLowestInArray(key, keyOrderedVector.map(x
|
|
329
|
+
const indexInVector = indexOrNextLowestInArray(key, keyOrderedVector.map((x)=>x[0]));
|
|
312
330
|
if (indexInVector !== -1) {
|
|
313
331
|
const [_, leafIndex] = keyOrderedVector[indexInVector];
|
|
314
332
|
return leafIndex;
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
333
|
+
} else {
|
|
317
334
|
// no leaf <= the specified key was found
|
|
318
335
|
return -1n;
|
|
319
336
|
}
|
|
320
337
|
}
|
|
321
338
|
/**
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
async _getLeafOrLowLeafWitnessInExternalDb(treeId, key) {
|
|
339
|
+
* Query the external DB to get leaf if present, low leaf if absent
|
|
340
|
+
*/ async _getLeafOrLowLeafWitnessInExternalDb(treeId, key) {
|
|
325
341
|
// "key" is siloed slot (leafSlot) or siloed nullifier
|
|
326
342
|
const previousValueIndex = await this.treeDb.getPreviousValueIndex(treeId, key);
|
|
327
343
|
assert(previousValueIndex !== undefined, `${MerkleTreeId[treeId]} low leaf index should always be found (even if target leaf does not exist)`);
|
|
328
344
|
const { index: leafIndex, alreadyPresent } = previousValueIndex;
|
|
329
345
|
const leafPreimage = await this.treeDb.getLeafPreimage(treeId, leafIndex);
|
|
330
346
|
assert(leafPreimage !== undefined, `${MerkleTreeId[treeId]} low leaf preimage should never be undefined (even if target leaf does not exist)`);
|
|
331
|
-
return {
|
|
347
|
+
return {
|
|
348
|
+
preimage: leafPreimage,
|
|
349
|
+
index: leafIndex,
|
|
350
|
+
alreadyPresent
|
|
351
|
+
};
|
|
332
352
|
}
|
|
333
353
|
/**
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const input = preimage.toHashInputs().map(x => Fr.fromBuffer(x));
|
|
354
|
+
* This hashes the preimage to a field element
|
|
355
|
+
*/ hashPreimage(preimage) {
|
|
356
|
+
const input = preimage.toHashInputs().map((x)=>Fr.fromBuffer(x));
|
|
338
357
|
return poseidon2Hash(input);
|
|
339
358
|
}
|
|
340
359
|
async getTreeSnapshot(id) {
|
|
@@ -343,42 +362,38 @@ export class AvmEphemeralForest {
|
|
|
343
362
|
return new AppendOnlyTreeSnapshot(root, Number(tree.leafCount));
|
|
344
363
|
}
|
|
345
364
|
}
|
|
346
|
-
/****************************************************/
|
|
347
|
-
/****** Some useful Structs and Enums **************/
|
|
348
|
-
/****************************************************/
|
|
349
|
-
var TreeType;
|
|
350
|
-
(function (TreeType) {
|
|
365
|
+
/****************************************************/ /****** Some useful Structs and Enums **************/ /****************************************************/ var TreeType = /*#__PURE__*/ function(TreeType) {
|
|
351
366
|
TreeType[TreeType["LEAF"] = 0] = "LEAF";
|
|
352
367
|
TreeType[TreeType["NODE"] = 1] = "NODE";
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
368
|
+
return TreeType;
|
|
369
|
+
}(TreeType || {});
|
|
370
|
+
var SiblingStatus = /*#__PURE__*/ function(SiblingStatus) {
|
|
356
371
|
SiblingStatus[SiblingStatus["MEMBER"] = 0] = "MEMBER";
|
|
357
372
|
SiblingStatus[SiblingStatus["NONMEMBER"] = 1] = "NONMEMBER";
|
|
358
373
|
SiblingStatus[SiblingStatus["ERROR"] = 2] = "ERROR";
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/****** Some Helpful Constructors for Trees ********/
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
});
|
|
372
|
-
/****************************************************/
|
|
373
|
-
/****** The EphemeralAvmTree Class *****************/
|
|
374
|
-
/****************************************************/
|
|
375
|
-
/**
|
|
374
|
+
return SiblingStatus;
|
|
375
|
+
}(SiblingStatus || {});
|
|
376
|
+
/****************************************************/ /****** Some Helpful Constructors for Trees ********/ /****************************************************/ const Node = (left, right)=>({
|
|
377
|
+
tag: 1,
|
|
378
|
+
leftTree: left,
|
|
379
|
+
rightTree: right
|
|
380
|
+
});
|
|
381
|
+
const Leaf = (value)=>({
|
|
382
|
+
tag: 0,
|
|
383
|
+
value
|
|
384
|
+
});
|
|
385
|
+
/****************************************************/ /****** The EphemeralAvmTree Class *****************/ /****************************************************/ /**
|
|
376
386
|
* This class contains a recursively defined tree that has leaves at different heights
|
|
377
387
|
* It is seeded by an existing merkle treeDb for which it derives a frontier
|
|
378
388
|
* It is intended to be a lightweight tree that contains only the necessary information to suppport appends or updates
|
|
379
|
-
*/
|
|
380
|
-
|
|
381
|
-
|
|
389
|
+
*/ export class EphemeralAvmTree {
|
|
390
|
+
root;
|
|
391
|
+
leafCount;
|
|
392
|
+
depth;
|
|
393
|
+
zeroHashes;
|
|
394
|
+
tree;
|
|
395
|
+
frontier;
|
|
396
|
+
constructor(root, leafCount, depth, zeroHashes){
|
|
382
397
|
this.root = root;
|
|
383
398
|
this.leafCount = leafCount;
|
|
384
399
|
this.depth = depth;
|
|
@@ -390,82 +405,78 @@ export class EphemeralAvmTree {
|
|
|
390
405
|
let zeroHash = Fr.zero();
|
|
391
406
|
// Can probably cache this elsewhere
|
|
392
407
|
const zeroHashes = [];
|
|
393
|
-
for
|
|
408
|
+
for(let i = 0; i < depth; i++){
|
|
394
409
|
zeroHashes.push(zeroHash);
|
|
395
|
-
zeroHash = await poseidon2Hash([
|
|
410
|
+
zeroHash = await poseidon2Hash([
|
|
411
|
+
zeroHash,
|
|
412
|
+
zeroHash
|
|
413
|
+
]);
|
|
396
414
|
}
|
|
397
415
|
const tree = new EphemeralAvmTree(Leaf(zeroHash), forkedLeafCount, depth, zeroHashes);
|
|
398
416
|
await tree.initializeFrontier(treeDb, merkleId);
|
|
399
417
|
return tree;
|
|
400
418
|
}
|
|
401
419
|
/**
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
appendLeaf(value) {
|
|
420
|
+
* This is a recursive function that inserts a leaf into the tree
|
|
421
|
+
* @param value - The value of the leaf to be inserted
|
|
422
|
+
*/ appendLeaf(value) {
|
|
406
423
|
const insertPath = this._derivePathLE(this.leafCount);
|
|
407
424
|
this.tree = this._insertLeaf(value, insertPath, this.depth, this.tree);
|
|
408
425
|
this.leafCount++;
|
|
409
426
|
}
|
|
410
427
|
/**
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
updateLeaf(value, index, depth = this.depth) {
|
|
428
|
+
* This is a recursive function that upserts a leaf into the tree at a index and depth
|
|
429
|
+
* @param value - The value of the leaf to be inserted
|
|
430
|
+
* @param index - The index of the leaf to be inserted
|
|
431
|
+
* @param depth - The depth of the leaf to be inserted (defaults to the bottom of the tree)
|
|
432
|
+
*/ updateLeaf(value, index, depth = this.depth) {
|
|
417
433
|
const insertPath = this._derivePathLE(index, depth);
|
|
418
434
|
this.tree = this._insertLeaf(value, insertPath, depth, this.tree);
|
|
419
435
|
}
|
|
420
436
|
/**
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
async getSiblingPath(index) {
|
|
437
|
+
* Get the sibling path of a leaf in the tree
|
|
438
|
+
* @param index - The index of the leaf for which a sibling path should be returned.
|
|
439
|
+
* @returns The sibling path of the leaf, can fail if the path is not found
|
|
440
|
+
*/ async getSiblingPath(index) {
|
|
426
441
|
const searchPath = this._derivePathLE(index);
|
|
427
442
|
// Handle cases where we error out
|
|
428
443
|
const { path, status } = await this._getSiblingPath(searchPath, this.tree, []);
|
|
429
|
-
if (status ===
|
|
444
|
+
if (status === 2) {
|
|
430
445
|
return undefined;
|
|
431
446
|
}
|
|
432
447
|
return path;
|
|
433
448
|
}
|
|
434
449
|
/**
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
for (let i = 0; i < siblingPath.length; i++) {
|
|
450
|
+
* This upserts the nodes of the sibling path into the tree
|
|
451
|
+
* @param index - The index of the leaf that the sibling path is derived from
|
|
452
|
+
* @param siblingPath - The sibling path of the index
|
|
453
|
+
*/ insertSiblingPath(index, siblingPath) {
|
|
454
|
+
for(let i = 0; i < siblingPath.length; i++){
|
|
441
455
|
// Flip(XOR) the last bit because we are inserting siblings of the leaf
|
|
442
456
|
const sibIndex = index ^ 1n;
|
|
443
457
|
const node = this.getNode(sibIndex, this.depth - i);
|
|
444
458
|
// If we are inserting a sibling path and we already have a branch at that index in our
|
|
445
459
|
// ephemeral tree, we should not overwrite it
|
|
446
|
-
if (node === undefined || node.tag ===
|
|
460
|
+
if (node === undefined || node.tag === 0) {
|
|
447
461
|
this.updateLeaf(siblingPath[i], sibIndex, this.depth - i);
|
|
448
462
|
}
|
|
449
463
|
index >>= 1n;
|
|
450
464
|
}
|
|
451
465
|
}
|
|
452
466
|
/**
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
// Do we really need LeafCount to be a bigint - log2 is on numbers only
|
|
467
|
+
* This is a helper function that computes the index of the frontier nodes at each depth
|
|
468
|
+
* @param leafCount - The number of leaves in the tree
|
|
469
|
+
* @returns An array of frontier indices at each depth, sorted from leaf to root
|
|
470
|
+
*/ // Do we really need LeafCount to be a bigint - log2 is on numbers only
|
|
458
471
|
static computeFrontierLeafIndices(leafCount) {
|
|
459
472
|
const numFrontierEntries = Math.floor(Math.log2(leafCount)) + 1;
|
|
460
473
|
const frontierIndices = [];
|
|
461
|
-
for
|
|
474
|
+
for(let i = 0; i < numFrontierEntries; i++){
|
|
462
475
|
if (leafCount === 0) {
|
|
463
476
|
frontierIndices.push(0);
|
|
464
|
-
}
|
|
465
|
-
else if (leafCount % 2 === 0) {
|
|
477
|
+
} else if (leafCount % 2 === 0) {
|
|
466
478
|
frontierIndices.push(leafCount - 2);
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
479
|
+
} else {
|
|
469
480
|
frontierIndices.push(leafCount - 1);
|
|
470
481
|
}
|
|
471
482
|
leafCount >>= 1;
|
|
@@ -473,11 +484,10 @@ export class EphemeralAvmTree {
|
|
|
473
484
|
return frontierIndices;
|
|
474
485
|
}
|
|
475
486
|
/**
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
async initializeFrontier(treeDb, merkleId) {
|
|
487
|
+
* This derives the frontier and inserts them into the tree
|
|
488
|
+
* @param treeDb - The treeDb to be queried for sibling paths
|
|
489
|
+
* @param merkleId - The treeId of the tree to be queried for sibling paths
|
|
490
|
+
*/ async initializeFrontier(treeDb, merkleId) {
|
|
481
491
|
// The frontier indices are sorted from the leaf to root
|
|
482
492
|
const frontierIndices = EphemeralAvmTree.computeFrontierLeafIndices(Number(this.leafCount));
|
|
483
493
|
// The frontier indices are level-based - i.e. index N at level L.
|
|
@@ -488,7 +498,7 @@ export class EphemeralAvmTree {
|
|
|
488
498
|
// NOTE: This is terribly inefficient and we should probably change the DB API to allow for getting paths to a node
|
|
489
499
|
const frontierValues = [];
|
|
490
500
|
// These are leaf indexes that pass through the frontier nodes
|
|
491
|
-
for
|
|
501
|
+
for(let i = 0; i < frontierIndices.length; i++){
|
|
492
502
|
// Given the index to a frontier, we first xor it so we can get its sibling index at depth L
|
|
493
503
|
// We then extend the path to that sibling index by shifting left the requisite number of times (for simplicity we just go left down the tree - it doesnt matter)
|
|
494
504
|
// This provides us the leaf index such that if we ask for this leafIndex's sibling path, it will pass through the frontier node
|
|
@@ -507,159 +517,156 @@ export class EphemeralAvmTree {
|
|
|
507
517
|
this.frontier = frontierValues;
|
|
508
518
|
}
|
|
509
519
|
/**
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
getRoot() {
|
|
520
|
+
* Computes the root of the tree
|
|
521
|
+
*/ getRoot() {
|
|
513
522
|
return this.hashTree(this.tree, this.depth);
|
|
514
523
|
}
|
|
515
524
|
/**
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
case
|
|
529
|
-
|
|
530
|
-
|
|
525
|
+
* Recursively hashes the subtree
|
|
526
|
+
* @param tree - The tree to be hashed
|
|
527
|
+
* @param depth - The depth of the tree
|
|
528
|
+
*/ async hashTree(tree, depth) {
|
|
529
|
+
switch(tree.tag){
|
|
530
|
+
case 1:
|
|
531
|
+
{
|
|
532
|
+
return poseidon2Hash([
|
|
533
|
+
await this.hashTree(tree.leftTree, depth - 1),
|
|
534
|
+
await this.hashTree(tree.rightTree, depth - 1)
|
|
535
|
+
]);
|
|
536
|
+
}
|
|
537
|
+
case 0:
|
|
538
|
+
{
|
|
539
|
+
return tree.value;
|
|
540
|
+
}
|
|
531
541
|
}
|
|
532
542
|
}
|
|
533
543
|
/**
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
getNode(index, depth) {
|
|
544
|
+
* Extracts the subtree from a given index and depth
|
|
545
|
+
* @param index - The index of the node to be extracted
|
|
546
|
+
* @param depth - The depth of the node to be extracted
|
|
547
|
+
* @returns The subtree rooted at the index and depth
|
|
548
|
+
*/ getNode(index, depth) {
|
|
540
549
|
const path = this._derivePathBE(index, depth);
|
|
541
550
|
const truncatedPath = path.slice(0, depth);
|
|
542
551
|
truncatedPath.reverse();
|
|
543
552
|
try {
|
|
544
553
|
return this._getNode(truncatedPath, this.tree);
|
|
545
|
-
}
|
|
546
|
-
catch (e) {
|
|
554
|
+
} catch (e) {
|
|
547
555
|
return undefined;
|
|
548
556
|
}
|
|
549
557
|
}
|
|
550
558
|
/**
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
_getNode(nodePath, tree) {
|
|
559
|
+
* This is the recursive helper for getNode
|
|
560
|
+
*/ _getNode(nodePath, tree) {
|
|
554
561
|
if (nodePath.length === 0) {
|
|
555
562
|
return tree;
|
|
556
563
|
}
|
|
557
|
-
switch
|
|
558
|
-
case
|
|
564
|
+
switch(tree.tag){
|
|
565
|
+
case 1:
|
|
559
566
|
return nodePath.pop() === 0 ? this._getNode(nodePath, tree.leftTree) : this._getNode(nodePath, tree.rightTree);
|
|
560
|
-
case
|
|
567
|
+
case 0:
|
|
561
568
|
throw new Error('Node not found');
|
|
562
569
|
}
|
|
563
570
|
}
|
|
564
571
|
/** Our tree traversal uses an array of 1s and 0s to represent the path to a leaf and expects them to be in LE order
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
_derivePathLE(index, depth = this.depth) {
|
|
572
|
+
* This helps with deriving it given an index and (optionally a depth)
|
|
573
|
+
* @param index - The index to derive a path to within the tree, does not have to terminate at a leaf
|
|
574
|
+
* @param depth - The depth to traverse, if not provided it will traverse to the bottom of the tree
|
|
575
|
+
* @returns The path to the leaf in LE order
|
|
576
|
+
*/ _derivePathLE(index, depth = this.depth) {
|
|
571
577
|
return this._derivePathBE(index, depth).reverse();
|
|
572
578
|
}
|
|
573
579
|
/** Sometimes we want it in BE order, to make truncating easier
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return index
|
|
580
|
-
.toString(2)
|
|
581
|
-
.padStart(depth, '0')
|
|
582
|
-
.split('')
|
|
583
|
-
.map(x => parseInt(x));
|
|
580
|
+
* @param index - The index to derive a path to within the tree, does not have to terminate at a leaf
|
|
581
|
+
* @param depth - The depth to traverse, if not provided it will traverse to the bottom of the tree
|
|
582
|
+
* @returns The path to the leaf in LE order
|
|
583
|
+
*/ _derivePathBE(index, depth = this.depth) {
|
|
584
|
+
return index.toString(2).padStart(depth, '0').split('').map((x)=>parseInt(x));
|
|
584
585
|
}
|
|
585
586
|
/**
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
_insertLeaf(value, insertPath, depth, tree) {
|
|
587
|
+
* This is a recursive function that upserts a leaf into the tree given a path
|
|
588
|
+
* @param value - The value of the leaf to be upserted
|
|
589
|
+
* @param insertPath - The path to the leaf, this should be ordered from leaf to root (i.e. LE encoded)
|
|
590
|
+
* @param depth - The depth of the tree
|
|
591
|
+
* @param tree - The current tree
|
|
592
|
+
* @param appendMode - If true we append the relevant zeroHashes to the tree as we traverse
|
|
593
|
+
*/ _insertLeaf(value, insertPath, depth, tree) {
|
|
594
594
|
if (insertPath.length > this.depth || depth > this.depth) {
|
|
595
595
|
throw new Error('PATH EXCEEDS DEPTH');
|
|
596
596
|
}
|
|
597
597
|
if (depth === 0 || insertPath.length === 0) {
|
|
598
598
|
return Leaf(value);
|
|
599
599
|
}
|
|
600
|
-
switch
|
|
601
|
-
case
|
|
602
|
-
|
|
603
|
-
? Node(this._insertLeaf(value, insertPath, depth - 1, tree.leftTree), tree.rightTree)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
: Node(zeroLeaf, this._insertLeaf(value, insertPath, depth - 1, zeroLeaf));
|
|
611
|
-
}
|
|
600
|
+
switch(tree.tag){
|
|
601
|
+
case 1:
|
|
602
|
+
{
|
|
603
|
+
return insertPath.pop() === 0 ? Node(this._insertLeaf(value, insertPath, depth - 1, tree.leftTree), tree.rightTree) : Node(tree.leftTree, this._insertLeaf(value, insertPath, depth - 1, tree.rightTree));
|
|
604
|
+
}
|
|
605
|
+
case 0:
|
|
606
|
+
{
|
|
607
|
+
const zeroLeaf = Leaf(this.zeroHashes[depth - 1]);
|
|
608
|
+
return insertPath.pop() === 0 ? Node(this._insertLeaf(value, insertPath, depth - 1, zeroLeaf), zeroLeaf) : Node(zeroLeaf, this._insertLeaf(value, insertPath, depth - 1, zeroLeaf));
|
|
609
|
+
}
|
|
612
610
|
}
|
|
613
611
|
}
|
|
614
612
|
/* Recursive helper for getSiblingPath, this only looks inside the tree and does not resolve using
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
async _getSiblingPath(searchPath, tree, acc) {
|
|
613
|
+
* a DB. If a path is not found, it returns an error status that is expected to be handled by the caller
|
|
614
|
+
* @param searchPath - The path to the leaf for which we would like the sibling pathin LE order
|
|
615
|
+
* @param tree - The current tree
|
|
616
|
+
* @param acc - The accumulated sibling path
|
|
617
|
+
*/ async _getSiblingPath(searchPath, tree, acc) {
|
|
621
618
|
// If we have reached the end of the path, we should be at a leaf or empty node
|
|
622
619
|
// If it is a leaf, we check if the value is equal to the leaf value
|
|
623
620
|
// If it is empty we check if the value is equal to zero
|
|
624
621
|
if (searchPath.length === 0) {
|
|
625
|
-
switch
|
|
626
|
-
case
|
|
627
|
-
return {
|
|
628
|
-
|
|
629
|
-
|
|
622
|
+
switch(tree.tag){
|
|
623
|
+
case 0:
|
|
624
|
+
return {
|
|
625
|
+
path: acc,
|
|
626
|
+
status: 0
|
|
627
|
+
};
|
|
628
|
+
case 1:
|
|
629
|
+
return {
|
|
630
|
+
path: [],
|
|
631
|
+
status: 2
|
|
632
|
+
};
|
|
630
633
|
}
|
|
631
634
|
}
|
|
632
635
|
// Keep exploring here
|
|
633
|
-
switch
|
|
634
|
-
case
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
? await this._getSiblingPath(searchPath, tree.leftTree, [
|
|
638
|
-
|
|
639
|
-
|
|
636
|
+
switch(tree.tag){
|
|
637
|
+
case 1:
|
|
638
|
+
{
|
|
639
|
+
// Look at the next element of the path to decided if we go left or right, note this mutates!
|
|
640
|
+
return searchPath.pop() === 0 ? await this._getSiblingPath(searchPath, tree.leftTree, [
|
|
641
|
+
await this.hashTree(tree.rightTree, searchPath.length)
|
|
642
|
+
].concat(acc)) : await this._getSiblingPath(searchPath, tree.rightTree, [
|
|
643
|
+
await this.hashTree(tree.leftTree, searchPath.length)
|
|
644
|
+
].concat(acc));
|
|
645
|
+
}
|
|
640
646
|
// In these two situations we are exploring a subtree we dont have information about
|
|
641
647
|
// We should return an error and look inside the DB
|
|
642
|
-
case
|
|
643
|
-
return {
|
|
648
|
+
case 0:
|
|
649
|
+
return {
|
|
650
|
+
path: [],
|
|
651
|
+
status: 2
|
|
652
|
+
};
|
|
644
653
|
}
|
|
645
654
|
}
|
|
646
655
|
}
|
|
647
656
|
/**
|
|
648
657
|
* Return the index of the key in the array, or index-1 if they key is not found.
|
|
649
|
-
*/
|
|
650
|
-
function indexOrNextLowestInArray(key, arr) {
|
|
658
|
+
*/ function indexOrNextLowestInArray(key, arr) {
|
|
651
659
|
// We are looking for the index of the largest element in the array that is less than the key
|
|
652
660
|
let start = 0;
|
|
653
661
|
let end = arr.length;
|
|
654
662
|
// Note that the easiest way is to increment the search key by 1 and then do a binary search
|
|
655
663
|
const keyPlus1 = key.add(Fr.ONE);
|
|
656
|
-
while
|
|
664
|
+
while(start < end){
|
|
657
665
|
const mid = Math.floor((start + end) / 2);
|
|
658
666
|
if (arr[mid].cmp(keyPlus1) < 0) {
|
|
659
667
|
// The key + 1 is greater than the midpoint, so we can continue searching the top half
|
|
660
668
|
start = mid + 1;
|
|
661
|
-
}
|
|
662
|
-
else {
|
|
669
|
+
} else {
|
|
663
670
|
// The key + 1 is LT or EQ the arr element, so we can continue searching the bottom half
|
|
664
671
|
end = mid;
|
|
665
672
|
}
|
|
@@ -669,4 +676,3 @@ function indexOrNextLowestInArray(key, arr) {
|
|
|
669
676
|
// greater than the key
|
|
670
677
|
return start - 1;
|
|
671
678
|
}
|
|
672
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXZtX3RyZWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXZtL2F2bV90cmVlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBc0IsWUFBWSxFQUFpQyxNQUFNLHNCQUFzQixDQUFDO0FBQ3ZHLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxxQkFBcUIsRUFBRSwwQkFBMEIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQy9HLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFHOUMsT0FBTyxFQUFFLE1BQU0sSUFBSSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDMUMsT0FBTyxTQUFTLE1BQU0sa0JBQWtCLENBQUM7QUF1RHpDLHNEQUFzRDtBQUN0RCxzREFBc0Q7QUFDdEQsc0RBQXNEO0FBRXREOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBQzdCLFlBQ1MsTUFBZ0MsRUFDaEMsT0FBNEM7SUFDbkQsa0hBQWtIO0lBQ2xILCtGQUErRjtJQUN4RixjQUF3RSxFQUN4RSxpQkFBcUQ7UUFMckQsV0FBTSxHQUFOLE1BQU0sQ0FBMEI7UUFDaEMsWUFBTyxHQUFQLE9BQU8sQ0FBcUM7UUFHNUMsbUJBQWMsR0FBZCxjQUFjLENBQTBEO1FBQ3hFLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBb0M7SUFDM0QsQ0FBQztJQUVKLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQWdDO1FBQ2xELE1BQU0sT0FBTyxHQUFHLElBQUksR0FBRyxFQUFrQyxDQUFDO1FBQzFELEtBQUssTUFBTSxRQUFRLElBQUksQ0FBQyxZQUFZLENBQUMsY0FBYyxFQUFFLFlBQVksQ0FBQyxjQUFjLEVBQUUsWUFBWSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztZQUNqSCxNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDcEQsTUFBTSxJQUFJLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztZQUM1RixPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUM5QixDQUFDO1FBQ0QsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsRUFBaUMsQ0FBQztRQUNuRSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLGNBQStCLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDeEUsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxnQkFBaUMsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMxRSxPQUFPLElBQUksa0JBQWtCLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxJQUFJLEdBQUcsRUFBRSxFQUFFLGlCQUFpQixDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUVELElBQUk7UUFDRixPQUFPLElBQUksa0JBQWtCLENBQzNCLElBQUksQ0FBQyxNQUFNLEVBQ1gsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFDdkIsU0FBUyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsRUFDOUIsU0FBUyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUNsQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsS0FBSyxDQUFDLGNBQWMsQ0FBQyxNQUFvQixFQUFFLEtBQWE7UUFDdEQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFFLENBQUM7UUFDdkMsSUFBSSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVDLElBQUksSUFBSSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3ZCLDRFQUE0RTtZQUM1RSxJQUFJLEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3BFLGlGQUFpRjtZQUNqRixnRkFBZ0Y7WUFDaEYsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxZQUFZLEdBQUcsS0FBSyxHQUFHLEVBQUUsQ0FBQztnQkFDaEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDeEQsSUFBSSxJQUFJLEtBQUssU0FBUyxFQUFFLENBQUM7b0JBQ3ZCLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO29CQUNsRCxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO3dCQUM5QixJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDO29CQUNyQixDQUFDO2dCQUNILENBQUM7Z0JBQ0QsS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUNmLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILEtBQUssQ0FBQyxpQkFBaUIsQ0FDckIsTUFBVSxFQUNWLFlBQW9CLEVBQ3BCLGVBQWtCLEVBQ2xCLGVBQWtCO1FBRWxCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBRSxDQUFDO1FBQ3ZDLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUN6RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDO1FBRW5DLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUN6RCxnQ0FBZ0M7UUFDaEMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDOUQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDdkMsc0JBQXNCO1FBQ3RCLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFFN0QsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLENBQUMsQ0FBRSxDQUFDO0lBQ25ELENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxJQUFRLEVBQUUsUUFBWTtRQUM3QywyQ0FBMkM7UUFDM0MsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGdCQUFnQixDQUFDO1FBQzdDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBRSxDQUFDO1FBQ3ZDLE1BQU0sQ0FBQyxpQkFBaUIsRUFBRSx5QkFBeUIsQ0FBQyxHQUFHLE1BQU0sSUFBSSxDQUFDLHFCQUFxQixDQUdyRixNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDaEIsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLGNBQWMsRUFBRSxNQUFNLEVBQUUsR0FBRyxpQkFBaUIsQ0FBQztRQUNwRixNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBRXBFLElBQUkseUJBQXlCLEVBQUUsQ0FBQztZQUM5Qix5R0FBeUc7WUFDekcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFFLENBQUMsaUJBQWlCLENBQUMsWUFBWSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ3pFLENBQUM7UUFFRCxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzVDLE1BQU0sNkJBQTZCLEdBQUcsV0FBVyxDQUFDO1lBQ2xELGVBQWUsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDO1lBRWpDLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLGVBQWUsQ0FBQyxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBQ3hFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1lBQzlELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO1lBRXZFLE9BQU87Z0JBQ0wsT0FBTyxFQUFFLGVBQWU7Z0JBQ3hCLFVBQVUsRUFBRTtvQkFDVixRQUFRLEVBQUUsUUFBUTtvQkFDbEIsS0FBSyxFQUFFLFlBQVk7b0JBQ25CLFdBQVcsRUFBRSw2QkFBNkI7aUJBQzNDO2dCQUNELE1BQU0sRUFBRSxJQUFJO2FBQ2IsQ0FBQztRQUNKLENBQUM7UUFDRCxrRUFBa0U7UUFDbEUsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUN0QyxNQUFNLGNBQWMsR0FBRyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDM0MsY0FBYyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDL0IsY0FBYyxDQUFDLFNBQVMsR0FBRyxjQUFjLENBQUM7UUFFMUMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLDBCQUEwQixDQUN0RCxJQUFJLEVBQ0osUUFBUSxFQUNSLElBQUksRUFBRSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxFQUM3QixRQUFRLENBQUMsWUFBWSxFQUFFLENBQ3hCLENBQUM7UUFDRixNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxFQUFFLGNBQWMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1FBRTVHLHVHQUF1RztRQUN2RywrQkFBK0I7UUFDL0IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxjQUFjLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztRQUU5RyxPQUFPO1lBQ0wsT0FBTyxFQUFFLGlCQUFpQjtZQUMxQixVQUFVLEVBQUU7Z0JBQ1YsUUFBUTtnQkFDUixLQUFLLEVBQUUsWUFBWTtnQkFDbkIsV0FBVzthQUNaO1lBQ0QsTUFBTSxFQUFFLEtBQUs7WUFDYixTQUFTLEVBQUUsY0FBYztZQUN6QixhQUFhLEVBQUUsYUFBYTtTQUM3QixDQUFDO0lBQ0osQ0FBQztJQUVPLGlCQUFpQixDQUFDLE1BQXFCLEVBQUUsSUFBVSxFQUFFLEtBQWU7UUFDMUUsc0JBQXNCO1FBQ3RCLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUUsQ0FBQztRQUM5RCw4RUFBOEU7UUFDOUUsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNyQyxNQUFNLFVBQVUsR0FBRyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDdkUsSUFBSSxVQUFVLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDdEIsc0RBQXNEO2dCQUN0RCxNQUFNLFdBQVcsR0FDZix3QkFBd0IsQ0FDdEIsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUNQLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNqQyxHQUFHLENBQUMsQ0FBQztnQkFDUixpQkFBaUIsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2hFLENBQUM7aUJBQU0sQ0FBQztnQkFDTiw4QkFBOEI7Z0JBQzlCLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FBQyxTQUFhO1FBQ2pDLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxjQUFjLENBQUM7UUFDM0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFFLENBQUM7UUFDdkMsTUFBTSxDQUFDLGlCQUFpQixFQUFFLHlCQUF5QixDQUFDLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBR3JGLE1BQU0sRUFBRSxTQUFTLENBQUMsQ0FBQztRQUNyQixNQUFNLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxjQUFjLEVBQUUsR0FBRyxpQkFBaUIsQ0FBQztRQUM5RCxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRTdELElBQUkseUJBQXlCLEVBQUUsQ0FBQztZQUM5Qix5R0FBeUc7WUFDekcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFFLENBQUMsaUJBQWlCLENBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxNQUFNLENBQUMsQ0FBQyxjQUFjLEVBQUUsa0VBQWtFLENBQUMsQ0FBQztRQUU1Riw2QkFBNkI7UUFDN0IsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUN0QyxNQUFNLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNoRCxtQkFBbUIsQ0FBQyxhQUFhLEdBQUcsU0FBUyxDQUFDO1FBQzlDLG1CQUFtQixDQUFDLFNBQVMsR0FBRyxjQUFjLENBQUM7UUFFL0MsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLHFCQUFxQixDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsYUFBYSxFQUFFLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMxRyxNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixFQUFFLGdCQUFnQixDQUFDLENBQUM7UUFFekcsNEdBQTRHO1FBQzVHLG9DQUFvQztRQUNwQyxJQUFJLENBQUMsaUJBQWlCLENBQ3BCLE1BQU0sRUFDTixDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsRUFDM0QsQ0FBQyxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQ3hCLENBQUM7UUFFRixPQUFPO1lBQ0wsT0FBTyxFQUFFLGdCQUFnQjtZQUN6QixVQUFVLEVBQUU7Z0JBQ1YsUUFBUTtnQkFDUixLQUFLO2dCQUNMLFdBQVc7YUFDWjtZQUNELFNBQVMsRUFBRSxjQUFjO1lBQ3pCLGFBQWEsRUFBRSxhQUFhO1NBQzdCLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsUUFBWTtRQUMvQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFFLENBQUM7UUFDNUQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMxQix1RUFBdUU7UUFDdkUsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDckUsT0FBTyxhQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ssaUJBQWlCLENBQ3ZCLE1BQVUsRUFDVixLQUFhLEVBQ2IsUUFBVztRQUVYLElBQUksT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlDLElBQUksT0FBTyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzFCLE9BQU8sR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBQ3BCLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMzQyxDQUFDO1FBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVEOzs7T0FHRztJQUNLLGdCQUFnQixDQUE4RCxNQUFVLEVBQUUsS0FBYTtRQUM3RyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoRCxNQUFNLENBQUMsT0FBTyxLQUFLLFNBQVMsRUFBRSxxQ0FBcUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNqRyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BDLE1BQU0sQ0FDSixPQUFPLEtBQUssU0FBUyxFQUNyQixxQ0FBcUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsS0FBSyxHQUFHLENBQ3pGLENBQUM7UUFDRixPQUFPLFFBQWEsQ0FBQztJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSyxlQUFlLENBQTJCLE1BQVUsRUFBRSxLQUFhO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hELElBQUksT0FBTyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzFCLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUNELE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILEtBQUssQ0FBQyxvQkFBb0IsQ0FDeEIsTUFBVSxFQUNWLEdBQU87UUFFUCxNQUFNLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQVEsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ3BGLE9BQU8saUJBQWlCLENBQUM7SUFDM0IsQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsS0FBSyxDQUFDLHFCQUFxQixDQUN6QixNQUFVLEVBQ1YsR0FBTztRQUVQLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNqQyxzREFBc0Q7UUFDdEQsMERBQTBEO1FBQzFELHlFQUF5RTtRQUV6RSw2REFBNkQ7UUFDN0QsdUVBQXVFO1FBQ3ZFLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDLHlDQUF5QyxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUV4RixvQ0FBb0M7UUFDcEMsTUFBTSxrQ0FBa0MsR0FBcUIsTUFBTSxJQUFJLENBQUMsb0NBQW9DLENBQzFHLE1BQU0sRUFDTixTQUFTLENBQ1YsQ0FBQztRQUVGLHVFQUF1RTtRQUN2RSxJQUFJLG1CQUFtQixLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDaEMsT0FBTyxDQUFDLGtDQUFrQyxFQUFFLDhCQUE4QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ25GLENBQUM7UUFFRCx1RkFBdUY7UUFDdkYsTUFBTSxnQkFBZ0IsR0FBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLG1CQUFtQixDQUFDLENBQUM7UUFFL0Usb0JBQW9CO1FBQ3BCLE1BQU0sY0FBYyxHQUFHLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ2pELE1BQU0sZUFBZSxHQUFHLGtDQUFrQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUU3RSxJQUFJLGVBQWUsR0FBRyxjQUFjLEVBQUUsQ0FBQztZQUNyQyw2RUFBNkU7WUFDN0UsT0FBTyxDQUFDLGtDQUFrQyxFQUFFLDhCQUE4QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ25GLENBQUM7YUFBTSxDQUFDO1lBQ04sNEVBQTRFO1lBQzVFLE1BQU0sUUFBUSxHQUFHO2dCQUNmLFFBQVEsRUFBRSxnQkFBZ0I7Z0JBQzFCLEtBQUssRUFBRSxtQkFBbUI7Z0JBQzFCLGNBQWMsRUFBRSxjQUFjLEtBQUssU0FBUzthQUM3QyxDQUFDO1lBQ0YsT0FBTyxDQUFDLFFBQVEsRUFBRSw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMxRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0sseUNBQXlDLENBQTJCLE1BQVUsRUFBRSxHQUFPO1FBQzdGLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUUsQ0FBQztRQUU3RCxNQUFNLGFBQWEsR0FBRyx3QkFBd0IsQ0FDNUMsR0FBRyxFQUNILGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNoQyxDQUFDO1FBRUYsSUFBSSxhQUFhLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxHQUFHLGdCQUFnQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQ3ZELE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7YUFBTSxDQUFDO1lBQ04seUNBQXlDO1lBQ3pDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDYixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLG9DQUFvQyxDQUNoRCxNQUFVLEVBQ1YsR0FBVztRQUVYLHNEQUFzRDtRQUN0RCxNQUFNLGtCQUFrQixHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDaEYsTUFBTSxDQUNKLGtCQUFrQixLQUFLLFNBQVMsRUFDaEMsR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLDZFQUE2RSxDQUNyRyxDQUFDO1FBQ0YsTUFBTSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLEdBQUcsa0JBQWtCLENBQUM7UUFFaEUsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDMUUsTUFBTSxDQUNKLFlBQVksS0FBSyxTQUFTLEVBQzFCLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxvRkFBb0YsQ0FDNUcsQ0FBQztRQUVGLE9BQU8sRUFBRSxRQUFRLEVBQUUsWUFBaUIsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFBRSxDQUFDO0lBQzNFLENBQUM7SUFFRDs7T0FFRztJQUNILFlBQVksQ0FBNkIsUUFBVztRQUNsRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsWUFBWSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pFLE9BQU8sYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRCxLQUFLLENBQUMsZUFBZSxDQUFDLEVBQWdCO1FBQ3BDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBRSxDQUFDO1FBQ25DLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2xDLE9BQU8sSUFBSSxzQkFBc0IsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7Q0FDRjtBQUVELHNEQUFzRDtBQUN0RCxxREFBcUQ7QUFDckQsc0RBQXNEO0FBQ3RELElBQUssUUFHSjtBQUhELFdBQUssUUFBUTtJQUNYLHVDQUFJLENBQUE7SUFDSix1Q0FBSSxDQUFBO0FBQ04sQ0FBQyxFQUhJLFFBQVEsS0FBUixRQUFRLFFBR1o7QUFjRCxJQUFLLGFBSUo7QUFKRCxXQUFLLGFBQWE7SUFDaEIscURBQU0sQ0FBQTtJQUNOLDJEQUFTLENBQUE7SUFDVCxtREFBSyxDQUFBO0FBQ1AsQ0FBQyxFQUpJLGFBQWEsS0FBYixhQUFhLFFBSWpCO0FBT0Qsc0RBQXNEO0FBQ3RELHNEQUFzRDtBQUN0RCxzREFBc0Q7QUFDdEQsTUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFVLEVBQUUsS0FBVyxFQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQy9DLEdBQUcsRUFBRSxRQUFRLENBQUMsSUFBSTtJQUNsQixRQUFRLEVBQUUsSUFBSTtJQUNkLFNBQVMsRUFBRSxLQUFLO0NBQ2pCLENBQUMsQ0FBQztBQUVILE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBUyxFQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ2pDLEdBQUcsRUFBRSxRQUFRLENBQUMsSUFBSTtJQUNsQixLQUFLO0NBQ04sQ0FBQyxDQUFDO0FBRUgsc0RBQXNEO0FBQ3RELHFEQUFxRDtBQUNyRCxzREFBc0Q7QUFFdEQ7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxnQkFBZ0I7SUFJM0IsWUFBNEIsSUFBVSxFQUFTLFNBQWlCLEVBQVMsS0FBYSxFQUFVLFVBQWdCO1FBQXBGLFNBQUksR0FBSixJQUFJLENBQU07UUFBUyxjQUFTLEdBQVQsU0FBUyxDQUFRO1FBQVMsVUFBSyxHQUFMLEtBQUssQ0FBUTtRQUFVLGVBQVUsR0FBVixVQUFVLENBQU07UUFDOUcsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVELE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUNqQixlQUF1QixFQUN2QixLQUFhLEVBQ2IsTUFBZ0MsRUFDaEMsUUFBc0I7UUFFdEIsSUFBSSxRQUFRLEdBQUcsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3pCLG9DQUFvQztRQUNwQyxNQUFNLFVBQVUsR0FBRyxFQUFFLENBQUM7UUFDdEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQy9CLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDMUIsUUFBUSxHQUFHLE1BQU0sYUFBYSxDQUFDLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUNELE1BQU0sSUFBSSxHQUFHLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLGVBQWUsRUFBRSxLQUFLLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDdEYsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2hELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7T0FHRztJQUNILFVBQVUsQ0FBQyxLQUFTO1FBQ2xCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsVUFBVSxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZFLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUNuQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxVQUFVLENBQUMsS0FBUyxFQUFFLEtBQWEsRUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUs7UUFDckQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsS0FBYTtRQUNoQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzdDLGtDQUFrQztRQUNsQyxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMvRSxJQUFJLE1BQU0sS0FBSyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbkMsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxpQkFBaUIsQ0FBQyxLQUFhLEVBQUUsV0FBaUI7UUFDaEQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUM1Qyx1RUFBdUU7WUFDdkUsTUFBTSxRQUFRLEdBQUcsS0FBSyxHQUFHLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ3BELHVGQUF1RjtZQUN2Riw2Q0FBNkM7WUFDN0MsSUFBSSxJQUFJLEtBQUssU0FBUyxJQUFJLElBQUksQ0FBQyxHQUFHLEtBQUssUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNyRCxJQUFJLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM1RCxDQUFDO1lBQ0QsS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILHVFQUF1RTtJQUN2RSxNQUFNLENBQUMsMEJBQTBCLENBQUMsU0FBaUI7UUFDakQsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDaEUsTUFBTSxlQUFlLEdBQUcsRUFBRSxDQUFDO1FBQzNCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxrQkFBa0IsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzVDLElBQUksU0FBUyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUNwQixlQUFlLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzFCLENBQUM7aUJBQU0sSUFBSSxTQUFTLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMvQixlQUFlLENBQUMsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN0QyxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDdEMsQ0FBQztZQUNELFNBQVMsS0FBSyxDQUFDLENBQUM7UUFDbEIsQ0FBQztRQUNELE9BQU8sZUFBZSxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGtCQUFrQixDQUFDLE1BQWdDLEVBQUUsUUFBc0I7UUFDL0Usd0RBQXdEO1FBQ3hELE1BQU0sZUFBZSxHQUFHLGdCQUFnQixDQUFDLDBCQUEwQixDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUM1RixrRUFBa0U7UUFDbEUsaUhBQWlIO1FBQ2pILHlFQUF5RTtRQUN6RSw0SEFBNEg7UUFDNUgsOEdBQThHO1FBQzlHLG1IQUFtSDtRQUVuSCxNQUFNLGNBQWMsR0FBRyxFQUFFLENBQUM7UUFDMUIsOERBQThEO1FBQzlELEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxlQUFlLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDaEQsNEZBQTRGO1lBQzVGLGlLQUFpSztZQUNqSyxnSUFBZ0k7WUFDaEksTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDMUQsMERBQTBEO1lBQzFELE1BQU0sSUFBSSxHQUFHLE1BQU0sTUFBTSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFFMUQseUdBQXlHO1lBQ3pHLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDcEYseUVBQXlFO1lBQ3pFLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN6QyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQ25DLHFEQUFxRDtZQUNyRCwyR0FBMkc7WUFDM0csSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsRUFBRSxZQUFZLEVBQUUsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7UUFDRCxJQUFJLENBQUMsUUFBUSxHQUFHLGNBQWMsQ0FBQztJQUNqQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPO1FBQ1osT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFVLEVBQUUsS0FBYTtRQUM3QyxRQUFRLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNqQixLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUNuQixPQUFPLGFBQWEsQ0FBQztvQkFDbkIsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsS0FBSyxHQUFHLENBQUMsQ0FBQztvQkFDN0MsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsS0FBSyxHQUFHLENBQUMsQ0FBQztpQkFDL0MsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUNELEtBQUssUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7Z0JBQ25CLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQztZQUNwQixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLE9BQU8sQ0FBQyxLQUFhLEVBQUUsS0FBYTtRQUN6QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUM5QyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMzQyxhQUFhLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDO1lBQ0gsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakQsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDWCxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssUUFBUSxDQUFDLFFBQWtCLEVBQUUsSUFBVTtRQUM3QyxJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDMUIsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQ0QsUUFBUSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDakIsS0FBSyxRQUFRLENBQUMsSUFBSTtnQkFDaEIsT0FBTyxRQUFRLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUVqSCxLQUFLLFFBQVEsQ0FBQyxJQUFJO2dCQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDdEMsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNLLGFBQWEsQ0FBQyxLQUFhLEVBQUUsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLO1FBQ3JELE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDcEQsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxhQUFhLENBQUMsS0FBYSxFQUFFLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSztRQUNyRCxPQUFPLEtBQUs7YUFDVCxRQUFRLENBQUMsQ0FBQyxDQUFDO2FBQ1gsUUFBUSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUM7YUFDcEIsS0FBSyxDQUFDLEVBQUUsQ0FBQzthQUNULEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0ssV0FBVyxDQUFDLEtBQVMsRUFBRSxVQUFvQixFQUFFLEtBQWEsRUFBRSxJQUFVO1FBQzVFLElBQUksVUFBVSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxJQUFJLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDekQsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ3hDLENBQUM7UUFDRCxJQUFJLEtBQUssS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMzQyxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNyQixDQUFDO1FBQ0QsUUFBUSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDakIsS0FBSyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDbkIsT0FBTyxVQUFVLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQztvQkFDM0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztvQkFDckYsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxLQUFLLEdBQUcsQ0FBQyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1lBQzFGLENBQUM7WUFDRCxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUNuQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDbEQsT0FBTyxVQUFVLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQztvQkFDM0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxRQUFRLENBQUMsRUFBRSxRQUFRLENBQUM7b0JBQzFFLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxLQUFLLEdBQUcsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDL0UsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxLQUFLLENBQUMsZUFBZSxDQUFDLFVBQW9CLEVBQUUsSUFBVSxFQUFFLEdBQVM7UUFDdkUsK0VBQStFO1FBQy9FLG9FQUFvRTtRQUNwRSx3REFBd0Q7UUFDeEQsSUFBSSxVQUFVLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzVCLFFBQVEsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO2dCQUNqQixLQUFLLFFBQVEsQ0FBQyxJQUFJO29CQUNoQixPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNyRCxLQUFLLFFBQVEsQ0FBQyxJQUFJO29CQUNoQixPQUFPLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3JELENBQUM7UUFDSCxDQUFDO1FBQ0Qsc0JBQXNCO1FBQ3RCLFFBQVEsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2pCLEtBQUssUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7Z0JBQ25CLDZGQUE2RjtnQkFDN0YsT0FBTyxVQUFVLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQztvQkFDM0IsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FDeEIsVUFBVSxFQUNWLElBQUksQ0FBQyxRQUFRLEVBQ2IsQ0FBQyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQ3JFO29CQUNILENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQ3hCLFVBQVUsRUFDVixJQUFJLENBQUMsU0FBUyxFQUNkLENBQUMsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUNwRSxDQUFDO1lBQ1IsQ0FBQztZQUNELG9GQUFvRjtZQUNwRixtREFBbUQ7WUFDbkQsS0FBSyxRQUFRLENBQUMsSUFBSTtnQkFDaEIsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNyRCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLHdCQUF3QixDQUFDLEdBQU8sRUFBRSxHQUFTO0lBQ2xELDZGQUE2RjtJQUM3RixJQUFJLEtBQUssR0FBRyxDQUFDLENBQUM7SUFDZCxJQUFJLEdBQUcsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDO0lBQ3JCLDRGQUE0RjtJQUM1RixNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNqQyxPQUFPLEtBQUssR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNuQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQzFDLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMvQixzRkFBc0Y7WUFDdEYsS0FBSyxHQUFHLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDbEIsQ0FBQzthQUFNLENBQUM7WUFDTix3RkFBd0Y7WUFDeEYsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBQ0Qsa0hBQWtIO0lBQ2xILHNIQUFzSDtJQUN0SCx1QkFBdUI7SUFDdkIsT0FBTyxLQUFLLEdBQUcsQ0FBQyxDQUFDO0FBQ25CLENBQUMifQ==
|