@aztec/simulator 0.34.0 → 0.35.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/acvm/acvm.d.ts +4 -1
- package/dest/acvm/acvm.d.ts.map +1 -1
- package/dest/acvm/acvm.js +5 -5
- package/dest/acvm/deserialize.d.ts +3 -5
- package/dest/acvm/deserialize.d.ts.map +1 -1
- package/dest/acvm/deserialize.js +6 -9
- package/dest/acvm/oracle/oracle.d.ts +2 -0
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +12 -4
- package/dest/acvm/oracle/typed_oracle.d.ts +2 -0
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +7 -1
- package/dest/avm/avm_execution_environment.d.ts +4 -2
- package/dest/avm/avm_execution_environment.d.ts.map +1 -1
- package/dest/avm/avm_execution_environment.js +7 -5
- package/dest/avm/avm_gas.js +2 -2
- package/dest/avm/avm_machine_state.d.ts +2 -0
- package/dest/avm/avm_machine_state.d.ts.map +1 -1
- package/dest/avm/avm_machine_state.js +24 -6
- package/dest/avm/avm_memory_types.js +2 -2
- package/dest/avm/avm_simulator.js +2 -2
- package/dest/avm/fixtures/index.d.ts +3 -0
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +11 -4
- package/dest/avm/journal/journal.d.ts +1 -0
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +12 -1
- package/dest/avm/opcodes/accrued_substate.d.ts +2 -1
- package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/avm/opcodes/accrued_substate.js +13 -5
- package/dest/avm/opcodes/context_getters.js +2 -2
- package/dest/avm/opcodes/external_calls.d.ts +2 -2
- package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/avm/opcodes/external_calls.js +7 -6
- package/dest/avm/opcodes/hashing.d.ts +8 -8
- package/dest/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/avm/opcodes/hashing.js +35 -43
- package/dest/avm/serialization/instruction_serialization.d.ts +1 -1
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +2 -2
- package/dest/client/client_execution_context.d.ts +13 -3
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +23 -8
- package/dest/client/private_execution.d.ts.map +1 -1
- package/dest/client/private_execution.js +8 -6
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +6 -5
- package/dest/client/unconstrained_execution.d.ts.map +1 -1
- package/dest/client/unconstrained_execution.js +5 -4
- package/dest/common/index.d.ts +1 -1
- package/dest/common/index.d.ts.map +1 -1
- package/dest/common/index.js +2 -2
- package/dest/common/packed_values_cache.d.ts +28 -0
- package/dest/common/packed_values_cache.d.ts.map +1 -0
- package/dest/common/packed_values_cache.js +50 -0
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/mocks/fixtures.d.ts +42 -0
- package/dest/mocks/fixtures.d.ts.map +1 -0
- package/dest/mocks/fixtures.js +84 -0
- package/dest/mocks/index.d.ts +2 -0
- package/dest/mocks/index.d.ts.map +1 -0
- package/dest/mocks/index.js +2 -0
- package/dest/public/abstract_phase_manager.d.ts +82 -0
- package/dest/public/abstract_phase_manager.d.ts.map +1 -0
- package/dest/public/abstract_phase_manager.js +320 -0
- package/dest/public/app_logic_phase_manager.d.ts +29 -0
- package/dest/public/app_logic_phase_manager.d.ts.map +1 -0
- package/dest/public/app_logic_phase_manager.js +50 -0
- package/dest/public/execution.d.ts +3 -0
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -1
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +17 -13
- package/dest/public/hints_builder.d.ts +23 -0
- package/dest/public/hints_builder.d.ts.map +1 -0
- package/dest/public/hints_builder.js +62 -0
- package/dest/public/index.d.ts +5 -0
- package/dest/public/index.d.ts.map +1 -1
- package/dest/public/index.js +6 -1
- package/dest/public/phase_manager_factory.d.ts +18 -0
- package/dest/public/phase_manager_factory.d.ts.map +1 -0
- package/dest/public/phase_manager_factory.js +56 -0
- package/dest/public/public_execution_context.d.ts +15 -5
- package/dest/public/public_execution_context.d.ts.map +1 -1
- package/dest/public/public_execution_context.js +28 -12
- package/dest/public/public_executor.d.ts +79 -0
- package/dest/public/public_executor.d.ts.map +1 -0
- package/dest/public/public_executor.js +198 -0
- package/dest/public/public_kernel.d.ts +37 -0
- package/dest/public/public_kernel.d.ts.map +1 -0
- package/dest/public/public_kernel.js +97 -0
- package/dest/public/public_kernel_circuit_simulator.d.ts +31 -0
- package/dest/public/public_kernel_circuit_simulator.d.ts.map +1 -0
- package/dest/public/public_kernel_circuit_simulator.js +2 -0
- package/dest/public/public_processor.d.ts +53 -0
- package/dest/public/public_processor.d.ts.map +1 -0
- package/dest/public/public_processor.js +144 -0
- package/dest/public/setup_phase_manager.d.ts +30 -0
- package/dest/public/setup_phase_manager.d.ts.map +1 -0
- package/dest/public/setup_phase_manager.js +46 -0
- package/dest/public/tail_phase_manager.d.ts +30 -0
- package/dest/public/tail_phase_manager.d.ts.map +1 -0
- package/dest/public/tail_phase_manager.js +60 -0
- package/dest/public/teardown_phase_manager.d.ts +30 -0
- package/dest/public/teardown_phase_manager.d.ts.map +1 -0
- package/dest/public/teardown_phase_manager.js +46 -0
- package/dest/public/transitional_adaptors.d.ts +2 -1
- package/dest/public/transitional_adaptors.d.ts.map +1 -1
- package/dest/public/transitional_adaptors.js +11 -10
- package/dest/public/utils.d.ts +8 -0
- package/dest/public/utils.d.ts.map +1 -0
- package/dest/public/utils.js +29 -0
- package/dest/simulator/acvm_native.d.ts +19 -3
- package/dest/simulator/acvm_native.d.ts.map +1 -1
- package/dest/simulator/acvm_native.js +75 -48
- package/dest/simulator/acvm_wasm.d.ts.map +1 -1
- package/dest/simulator/acvm_wasm.js +3 -4
- package/package.json +8 -5
- package/src/acvm/acvm.ts +8 -5
- package/src/acvm/deserialize.ts +5 -9
- package/src/acvm/oracle/oracle.ts +13 -3
- package/src/acvm/oracle/typed_oracle.ts +8 -0
- package/src/avm/avm_execution_environment.ts +9 -1
- package/src/avm/avm_gas.ts +1 -1
- package/src/avm/avm_machine_state.ts +26 -5
- package/src/avm/avm_memory_types.ts +1 -1
- package/src/avm/avm_simulator.ts +1 -1
- package/src/avm/fixtures/index.ts +13 -1
- package/src/avm/journal/journal.ts +13 -0
- package/src/avm/opcodes/accrued_substate.ts +16 -4
- package/src/avm/opcodes/context_getters.ts +1 -1
- package/src/avm/opcodes/external_calls.ts +8 -5
- package/src/avm/opcodes/hashing.ts +38 -54
- package/src/avm/serialization/instruction_serialization.ts +1 -1
- package/src/client/client_execution_context.ts +25 -6
- package/src/client/private_execution.ts +7 -6
- package/src/client/simulator.ts +7 -3
- package/src/client/unconstrained_execution.ts +4 -3
- package/src/common/index.ts +1 -1
- package/src/common/packed_values_cache.ts +55 -0
- package/src/index.ts +1 -0
- package/src/mocks/fixtures.ts +169 -0
- package/src/mocks/index.ts +1 -0
- package/src/public/abstract_phase_manager.ts +571 -0
- package/src/public/app_logic_phase_manager.ts +76 -0
- package/src/public/execution.ts +4 -0
- package/src/public/executor.ts +18 -13
- package/src/public/hints_builder.ts +119 -0
- package/src/public/index.ts +5 -0
- package/src/public/phase_manager_factory.ts +126 -0
- package/src/public/public_execution_context.ts +29 -18
- package/src/public/public_executor.ts +267 -0
- package/src/public/public_kernel.ts +139 -0
- package/src/public/public_kernel_circuit_simulator.ts +36 -0
- package/src/public/public_processor.ts +212 -0
- package/src/public/setup_phase_manager.ts +66 -0
- package/src/public/tail_phase_manager.ts +120 -0
- package/src/public/teardown_phase_manager.ts +66 -0
- package/src/public/transitional_adaptors.ts +14 -5
- package/src/public/utils.ts +31 -0
- package/src/simulator/acvm_native.ts +94 -47
- package/src/simulator/acvm_wasm.ts +7 -3
- package/dest/common/packed_args_cache.d.ts +0 -28
- package/dest/common/packed_args_cache.d.ts.map +0 -1
- package/dest/common/packed_args_cache.js +0 -50
- package/src/common/packed_args_cache.ts +0 -55
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type PublicKernelRequest, PublicKernelType, type Tx } from '@aztec/circuit-types';
|
|
2
|
+
import {
|
|
3
|
+
type GlobalVariables,
|
|
4
|
+
type Header,
|
|
5
|
+
type Proof,
|
|
6
|
+
type PublicKernelCircuitPublicInputs,
|
|
7
|
+
} from '@aztec/circuits.js';
|
|
8
|
+
import { type PublicExecutor, type PublicStateDB } from '@aztec/simulator';
|
|
9
|
+
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
10
|
+
|
|
11
|
+
import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
|
|
12
|
+
import { type ContractsDataSourcePublicDB } from './public_executor.js';
|
|
13
|
+
import { type PublicKernelCircuitSimulator } from './public_kernel_circuit_simulator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The phase manager responsible for performing the fee preparation phase.
|
|
17
|
+
*/
|
|
18
|
+
export class SetupPhaseManager extends AbstractPhaseManager {
|
|
19
|
+
constructor(
|
|
20
|
+
protected db: MerkleTreeOperations,
|
|
21
|
+
protected publicExecutor: PublicExecutor,
|
|
22
|
+
protected publicKernel: PublicKernelCircuitSimulator,
|
|
23
|
+
protected globalVariables: GlobalVariables,
|
|
24
|
+
protected historicalHeader: Header,
|
|
25
|
+
protected publicContractsDB: ContractsDataSourcePublicDB,
|
|
26
|
+
protected publicStateDB: PublicStateDB,
|
|
27
|
+
public phase: PublicKernelPhase = PublicKernelPhase.SETUP,
|
|
28
|
+
) {
|
|
29
|
+
super(db, publicExecutor, publicKernel, globalVariables, historicalHeader, phase);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override async handle(
|
|
33
|
+
tx: Tx,
|
|
34
|
+
previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
|
|
35
|
+
previousPublicKernelProof: Proof,
|
|
36
|
+
) {
|
|
37
|
+
this.log.verbose(`Processing tx ${tx.getTxHash()}`);
|
|
38
|
+
const [kernelInputs, publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs, revertReason] =
|
|
39
|
+
await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof).catch(
|
|
40
|
+
// the abstract phase manager throws if simulation gives error in a non-revertible phase
|
|
41
|
+
async err => {
|
|
42
|
+
await this.publicStateDB.rollbackToCommit();
|
|
43
|
+
throw err;
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
|
|
47
|
+
await this.publicStateDB.checkpoint();
|
|
48
|
+
|
|
49
|
+
// Return a list of setup proving requests
|
|
50
|
+
const kernelRequests = kernelInputs.map(input => {
|
|
51
|
+
const request: PublicKernelRequest = {
|
|
52
|
+
type: PublicKernelType.SETUP,
|
|
53
|
+
inputs: input,
|
|
54
|
+
};
|
|
55
|
+
return request;
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
kernelRequests,
|
|
59
|
+
kernelInputs,
|
|
60
|
+
publicKernelOutput,
|
|
61
|
+
publicKernelProof,
|
|
62
|
+
revertReason,
|
|
63
|
+
returnValues: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type PublicKernelRequest, PublicKernelType, type Tx } from '@aztec/circuit-types';
|
|
2
|
+
import {
|
|
3
|
+
type Fr,
|
|
4
|
+
type GlobalVariables,
|
|
5
|
+
type Header,
|
|
6
|
+
type KernelCircuitPublicInputs,
|
|
7
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
8
|
+
type Proof,
|
|
9
|
+
type PublicKernelCircuitPublicInputs,
|
|
10
|
+
PublicKernelTailCircuitPrivateInputs,
|
|
11
|
+
type SideEffect,
|
|
12
|
+
makeEmptyProof,
|
|
13
|
+
mergeAccumulatedData,
|
|
14
|
+
sortByCounter,
|
|
15
|
+
} from '@aztec/circuits.js';
|
|
16
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
17
|
+
import { type PublicExecutor, type PublicStateDB } from '@aztec/simulator';
|
|
18
|
+
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
19
|
+
|
|
20
|
+
import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
|
|
21
|
+
import { type ContractsDataSourcePublicDB } from './public_executor.js';
|
|
22
|
+
import { type PublicKernelCircuitSimulator } from './public_kernel_circuit_simulator.js';
|
|
23
|
+
|
|
24
|
+
export class TailPhaseManager extends AbstractPhaseManager {
|
|
25
|
+
constructor(
|
|
26
|
+
protected db: MerkleTreeOperations,
|
|
27
|
+
protected publicExecutor: PublicExecutor,
|
|
28
|
+
protected publicKernel: PublicKernelCircuitSimulator,
|
|
29
|
+
protected globalVariables: GlobalVariables,
|
|
30
|
+
protected historicalHeader: Header,
|
|
31
|
+
protected publicContractsDB: ContractsDataSourcePublicDB,
|
|
32
|
+
protected publicStateDB: PublicStateDB,
|
|
33
|
+
public readonly phase: PublicKernelPhase = PublicKernelPhase.TAIL,
|
|
34
|
+
) {
|
|
35
|
+
super(db, publicExecutor, publicKernel, globalVariables, historicalHeader, phase);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async handle(tx: Tx, previousPublicKernelOutput: PublicKernelCircuitPublicInputs, previousPublicKernelProof: Proof) {
|
|
39
|
+
this.log.verbose(`Processing tx ${tx.getTxHash()}`);
|
|
40
|
+
const [inputs, finalKernelOutput] = await this.runTailKernelCircuit(
|
|
41
|
+
previousPublicKernelOutput,
|
|
42
|
+
previousPublicKernelProof,
|
|
43
|
+
).catch(
|
|
44
|
+
// the abstract phase manager throws if simulation gives error in non-revertible phase
|
|
45
|
+
async err => {
|
|
46
|
+
await this.publicStateDB.rollbackToCommit();
|
|
47
|
+
throw err;
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// commit the state updates from this transaction
|
|
52
|
+
await this.publicStateDB.commit();
|
|
53
|
+
|
|
54
|
+
// Return a tail proving request
|
|
55
|
+
const request: PublicKernelRequest = {
|
|
56
|
+
type: PublicKernelType.TAIL,
|
|
57
|
+
inputs: inputs,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
kernelRequests: [request],
|
|
62
|
+
publicKernelOutput: previousPublicKernelOutput,
|
|
63
|
+
finalKernelOutput,
|
|
64
|
+
publicKernelProof: makeEmptyProof(),
|
|
65
|
+
revertReason: undefined,
|
|
66
|
+
returnValues: undefined,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private async runTailKernelCircuit(
|
|
71
|
+
previousOutput: PublicKernelCircuitPublicInputs,
|
|
72
|
+
previousProof: Proof,
|
|
73
|
+
): Promise<[PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs]> {
|
|
74
|
+
const [inputs, output] = await this.simulate(previousOutput, previousProof);
|
|
75
|
+
|
|
76
|
+
// Temporary hack. Should sort them in the tail circuit.
|
|
77
|
+
const noteHashes = mergeAccumulatedData(
|
|
78
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
79
|
+
previousOutput.endNonRevertibleData.newNoteHashes,
|
|
80
|
+
previousOutput.end.newNoteHashes,
|
|
81
|
+
);
|
|
82
|
+
output.end.newNoteHashes = this.sortNoteHashes<typeof MAX_NEW_NOTE_HASHES_PER_TX>(noteHashes);
|
|
83
|
+
|
|
84
|
+
return [inputs, output];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async simulate(
|
|
88
|
+
previousOutput: PublicKernelCircuitPublicInputs,
|
|
89
|
+
previousProof: Proof,
|
|
90
|
+
): Promise<[PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs]> {
|
|
91
|
+
const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
|
|
92
|
+
|
|
93
|
+
const { validationRequests, endNonRevertibleData, end } = previousOutput;
|
|
94
|
+
const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints(
|
|
95
|
+
validationRequests.nullifierReadRequests,
|
|
96
|
+
endNonRevertibleData.newNullifiers,
|
|
97
|
+
end.newNullifiers,
|
|
98
|
+
);
|
|
99
|
+
const nullifierNonExistentReadRequestHints = await this.hintsBuilder.getNullifierNonExistentReadRequestHints(
|
|
100
|
+
validationRequests.nullifierNonExistentReadRequests,
|
|
101
|
+
endNonRevertibleData.newNullifiers,
|
|
102
|
+
end.newNullifiers,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// We take a deep copy (clone) of these to pass to the prover
|
|
106
|
+
const inputs = new PublicKernelTailCircuitPrivateInputs(
|
|
107
|
+
previousKernel,
|
|
108
|
+
nullifierReadRequestHints,
|
|
109
|
+
nullifierNonExistentReadRequestHints,
|
|
110
|
+
);
|
|
111
|
+
return [inputs.clone(), await this.publicKernel.publicKernelCircuitTail(inputs)];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private sortNoteHashes<N extends number>(noteHashes: Tuple<SideEffect, N>): Tuple<Fr, N> {
|
|
115
|
+
return sortByCounter(noteHashes.map(n => ({ ...n, counter: n.counter.toNumber() }))).map(n => n.value) as Tuple<
|
|
116
|
+
Fr,
|
|
117
|
+
N
|
|
118
|
+
>;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type PublicKernelRequest, PublicKernelType, type Tx } from '@aztec/circuit-types';
|
|
2
|
+
import {
|
|
3
|
+
type GlobalVariables,
|
|
4
|
+
type Header,
|
|
5
|
+
type Proof,
|
|
6
|
+
type PublicKernelCircuitPublicInputs,
|
|
7
|
+
} from '@aztec/circuits.js';
|
|
8
|
+
import { type PublicExecutor, type PublicStateDB } from '@aztec/simulator';
|
|
9
|
+
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
10
|
+
|
|
11
|
+
import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js';
|
|
12
|
+
import { type ContractsDataSourcePublicDB } from './public_executor.js';
|
|
13
|
+
import { type PublicKernelCircuitSimulator } from './public_kernel_circuit_simulator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The phase manager responsible for performing the fee preparation phase.
|
|
17
|
+
*/
|
|
18
|
+
export class TeardownPhaseManager extends AbstractPhaseManager {
|
|
19
|
+
constructor(
|
|
20
|
+
protected db: MerkleTreeOperations,
|
|
21
|
+
protected publicExecutor: PublicExecutor,
|
|
22
|
+
protected publicKernel: PublicKernelCircuitSimulator,
|
|
23
|
+
protected globalVariables: GlobalVariables,
|
|
24
|
+
protected historicalHeader: Header,
|
|
25
|
+
protected publicContractsDB: ContractsDataSourcePublicDB,
|
|
26
|
+
protected publicStateDB: PublicStateDB,
|
|
27
|
+
public phase: PublicKernelPhase = PublicKernelPhase.TEARDOWN,
|
|
28
|
+
) {
|
|
29
|
+
super(db, publicExecutor, publicKernel, globalVariables, historicalHeader, phase);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override async handle(
|
|
33
|
+
tx: Tx,
|
|
34
|
+
previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
|
|
35
|
+
previousPublicKernelProof: Proof,
|
|
36
|
+
) {
|
|
37
|
+
this.log.verbose(`Processing tx ${tx.getTxHash()}`);
|
|
38
|
+
const [kernelInputs, publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs, revertReason] =
|
|
39
|
+
await this.processEnqueuedPublicCalls(tx, previousPublicKernelOutput, previousPublicKernelProof).catch(
|
|
40
|
+
// the abstract phase manager throws if simulation gives error in a non-revertible phase
|
|
41
|
+
async err => {
|
|
42
|
+
await this.publicStateDB.rollbackToCommit();
|
|
43
|
+
throw err;
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
|
|
47
|
+
await this.publicStateDB.checkpoint();
|
|
48
|
+
|
|
49
|
+
// Return a list of teardown proving requests
|
|
50
|
+
const kernelRequests = kernelInputs.map(input => {
|
|
51
|
+
const request: PublicKernelRequest = {
|
|
52
|
+
type: PublicKernelType.TEARDOWN,
|
|
53
|
+
inputs: input,
|
|
54
|
+
};
|
|
55
|
+
return request;
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
kernelRequests,
|
|
59
|
+
kernelInputs,
|
|
60
|
+
publicKernelOutput,
|
|
61
|
+
publicKernelProof,
|
|
62
|
+
revertReason,
|
|
63
|
+
returnValues: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ContractStorageRead,
|
|
6
6
|
ContractStorageUpdateRequest,
|
|
7
7
|
FunctionData,
|
|
8
|
+
Gas,
|
|
8
9
|
type GlobalVariables,
|
|
9
10
|
type Header,
|
|
10
11
|
L2ToL1Message,
|
|
@@ -16,11 +17,12 @@ import { Fr } from '@aztec/foundation/fields';
|
|
|
16
17
|
|
|
17
18
|
import { type AvmContext } from '../avm/avm_context.js';
|
|
18
19
|
import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
|
|
20
|
+
import { type AvmMachineState } from '../avm/avm_machine_state.js';
|
|
19
21
|
import { AvmContractCallResults } from '../avm/avm_message_call_result.js';
|
|
20
22
|
import { type JournalData } from '../avm/journal/journal.js';
|
|
21
23
|
import { Mov } from '../avm/opcodes/memory.js';
|
|
22
24
|
import { createSimulationError } from '../common/errors.js';
|
|
23
|
-
import {
|
|
25
|
+
import { PackedValuesCache, SideEffectCounter } from '../index.js';
|
|
24
26
|
import { type PublicExecution, type PublicExecutionResult } from './execution.js';
|
|
25
27
|
import { PublicExecutionContext } from './public_execution_context.js';
|
|
26
28
|
|
|
@@ -42,15 +44,17 @@ export function createAvmExecutionEnvironment(
|
|
|
42
44
|
current.callContext.msgSender, // TODO: origin is not available
|
|
43
45
|
current.callContext.msgSender,
|
|
44
46
|
current.callContext.portalContractAddress,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
globalVariables.gasFees.feePerL1Gas,
|
|
48
|
+
globalVariables.gasFees.feePerL2Gas,
|
|
49
|
+
globalVariables.gasFees.feePerDaGas,
|
|
48
50
|
/*contractCallDepth=*/ Fr.zero(),
|
|
49
51
|
header,
|
|
50
52
|
globalVariables,
|
|
51
53
|
current.callContext.isStaticCall,
|
|
52
54
|
current.callContext.isDelegateCall,
|
|
53
55
|
current.args,
|
|
56
|
+
current.callContext.gasSettings,
|
|
57
|
+
current.callContext.transactionFee,
|
|
54
58
|
current.functionData.selector,
|
|
55
59
|
);
|
|
56
60
|
}
|
|
@@ -62,9 +66,12 @@ export function createPublicExecutionContext(avmContext: AvmContext, calldata: F
|
|
|
62
66
|
storageContractAddress: avmContext.environment.storageAddress,
|
|
63
67
|
portalContractAddress: avmContext.environment.portal,
|
|
64
68
|
functionSelector: avmContext.environment.temporaryFunctionSelector,
|
|
69
|
+
gasLeft: Gas.from(avmContext.machineState.gasLeft),
|
|
65
70
|
isDelegateCall: avmContext.environment.isDelegateCall,
|
|
66
71
|
isStaticCall: avmContext.environment.isStaticCall,
|
|
67
72
|
sideEffectCounter: sideEffectCounter,
|
|
73
|
+
gasSettings: avmContext.environment.gasSettings,
|
|
74
|
+
transactionFee: avmContext.environment.transactionFee,
|
|
68
75
|
});
|
|
69
76
|
const functionData = new FunctionData(avmContext.environment.temporaryFunctionSelector, /*isPrivate=*/ false);
|
|
70
77
|
const execution: PublicExecution = {
|
|
@@ -73,7 +80,7 @@ export function createPublicExecutionContext(avmContext: AvmContext, calldata: F
|
|
|
73
80
|
args: calldata,
|
|
74
81
|
functionData,
|
|
75
82
|
};
|
|
76
|
-
const packedArgs =
|
|
83
|
+
const packedArgs = PackedValuesCache.create([]);
|
|
77
84
|
|
|
78
85
|
const context = new PublicExecutionContext(
|
|
79
86
|
execution,
|
|
@@ -101,6 +108,7 @@ export async function convertAvmResults(
|
|
|
101
108
|
executionContext: PublicExecutionContext,
|
|
102
109
|
newWorldState: JournalData,
|
|
103
110
|
result: AvmContractCallResults,
|
|
111
|
+
endMachineState: AvmMachineState,
|
|
104
112
|
): Promise<PublicExecutionResult> {
|
|
105
113
|
const execution = executionContext.execution;
|
|
106
114
|
|
|
@@ -162,6 +170,7 @@ export async function convertAvmResults(
|
|
|
162
170
|
unencryptedLogs,
|
|
163
171
|
reverted: result.reverted,
|
|
164
172
|
revertReason: result.revertReason ? createSimulationError(result.revertReason) : undefined,
|
|
173
|
+
gasLeft: endMachineState.gasLeft,
|
|
165
174
|
};
|
|
166
175
|
}
|
|
167
176
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Tx } from '@aztec/circuit-types';
|
|
2
|
+
import { CallRequest } from '@aztec/circuits.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Looks at the side effects of a transaction and returns the highest counter
|
|
6
|
+
* @param tx - A transaction
|
|
7
|
+
* @returns The highest side effect counter in the transaction so far
|
|
8
|
+
*/
|
|
9
|
+
export function lastSideEffectCounter(tx: Tx): number {
|
|
10
|
+
const data = tx.data.forPublic!;
|
|
11
|
+
const sideEffectCounters = [
|
|
12
|
+
...data.endNonRevertibleData.newNoteHashes,
|
|
13
|
+
...data.endNonRevertibleData.newNullifiers,
|
|
14
|
+
...data.endNonRevertibleData.publicCallStack,
|
|
15
|
+
...data.end.newNoteHashes,
|
|
16
|
+
...data.end.newNullifiers,
|
|
17
|
+
...data.end.publicCallStack,
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
let max = 0;
|
|
21
|
+
for (const sideEffect of sideEffectCounters) {
|
|
22
|
+
if (sideEffect instanceof CallRequest) {
|
|
23
|
+
// look at both start and end counters because for enqueued public calls start > 0 while end === 0
|
|
24
|
+
max = Math.max(max, sideEffect.startSideEffectCounter.toNumber(), sideEffect.endSideEffectCounter.toNumber());
|
|
25
|
+
} else {
|
|
26
|
+
max = Math.max(max, sideEffect.counter.toNumber());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return max;
|
|
31
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from '@aztec/foundation/crypto';
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { Timer } from '@aztec/foundation/timer';
|
|
2
4
|
import { type NoirCompiledCircuit } from '@aztec/types/noir';
|
|
3
5
|
|
|
4
6
|
import { type WitnessMap } from '@noir-lang/types';
|
|
@@ -7,6 +9,26 @@ import fs from 'fs/promises';
|
|
|
7
9
|
|
|
8
10
|
import { type SimulationProvider } from './simulation_provider.js';
|
|
9
11
|
|
|
12
|
+
const logger = createDebugLogger('aztec:acvm-native');
|
|
13
|
+
|
|
14
|
+
export enum ACVM_RESULT {
|
|
15
|
+
SUCCESS,
|
|
16
|
+
FAILURE,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ACVMSuccess = {
|
|
20
|
+
status: ACVM_RESULT.SUCCESS;
|
|
21
|
+
duration: number;
|
|
22
|
+
witness: Map<number, string>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ACVMFailure = {
|
|
26
|
+
status: ACVM_RESULT.FAILURE;
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ACVMResult = ACVMSuccess | ACVMFailure;
|
|
31
|
+
|
|
10
32
|
/**
|
|
11
33
|
* Parses a TOML format witness map string into a Map structure
|
|
12
34
|
* @param outputString - The witness map in TOML format
|
|
@@ -29,7 +51,8 @@ function parseIntoWitnessMap(outputString: string) {
|
|
|
29
51
|
* @param inputWitness - The circuit's input witness
|
|
30
52
|
* @param bytecode - The circuit bytecode
|
|
31
53
|
* @param workingDirectory - A directory to use for temporary files by the ACVM
|
|
32
|
-
* @param pathToAcvm - The path to the
|
|
54
|
+
* @param pathToAcvm - The path to the ACVM binary
|
|
55
|
+
* @param outputFilename - If specified, the output will be stored as a file, encoded using Bincode
|
|
33
56
|
* @returns The completed partial witness outputted from the circuit
|
|
34
57
|
*/
|
|
35
58
|
export async function executeNativeCircuit(
|
|
@@ -37,7 +60,8 @@ export async function executeNativeCircuit(
|
|
|
37
60
|
bytecode: Buffer,
|
|
38
61
|
workingDirectory: string,
|
|
39
62
|
pathToAcvm: string,
|
|
40
|
-
|
|
63
|
+
outputFilename?: string,
|
|
64
|
+
): Promise<ACVMResult> {
|
|
41
65
|
const bytecodeFilename = 'bytecode';
|
|
42
66
|
const witnessFilename = 'input_witness.toml';
|
|
43
67
|
|
|
@@ -47,55 +71,69 @@ export async function executeNativeCircuit(
|
|
|
47
71
|
witnessMap = witnessMap.concat(`${key} = '${value}'\n`);
|
|
48
72
|
});
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
await fs.writeFile(`${workingDirectory}/${witnessFilename}`, witnessMap);
|
|
57
|
-
|
|
58
|
-
// Execute the ACVM using the given args
|
|
59
|
-
const args = [
|
|
60
|
-
`execute`,
|
|
61
|
-
`--working-directory`,
|
|
62
|
-
`${workingDirectory}`,
|
|
63
|
-
`--bytecode`,
|
|
64
|
-
`${bytecodeFilename}`,
|
|
65
|
-
`--input-witness`,
|
|
66
|
-
`${witnessFilename}`,
|
|
67
|
-
`--print`,
|
|
68
|
-
];
|
|
69
|
-
const processPromise = new Promise<string>((resolve, reject) => {
|
|
70
|
-
let outputWitness = Buffer.alloc(0);
|
|
71
|
-
let errorBuffer = Buffer.alloc(0);
|
|
72
|
-
const acvm = proc.spawn(pathToAcvm, args);
|
|
73
|
-
acvm.stdout.on('data', data => {
|
|
74
|
-
outputWitness = Buffer.concat([outputWitness, data]);
|
|
75
|
-
});
|
|
76
|
-
acvm.stderr.on('data', data => {
|
|
77
|
-
errorBuffer = Buffer.concat([errorBuffer, data]);
|
|
78
|
-
});
|
|
79
|
-
acvm.on('close', code => {
|
|
80
|
-
if (code === 0) {
|
|
81
|
-
resolve(outputWitness.toString('utf-8'));
|
|
82
|
-
} else {
|
|
83
|
-
reject(errorBuffer.toString('utf-8'));
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
});
|
|
74
|
+
try {
|
|
75
|
+
// Check that the directory exists
|
|
76
|
+
await fs.access(workingDirectory);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return { status: ACVM_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
79
|
+
}
|
|
87
80
|
|
|
88
81
|
try {
|
|
82
|
+
// Write the bytecode and input witness to the working directory
|
|
83
|
+
await fs.writeFile(`${workingDirectory}/${bytecodeFilename}`, bytecode);
|
|
84
|
+
await fs.writeFile(`${workingDirectory}/${witnessFilename}`, witnessMap);
|
|
85
|
+
|
|
86
|
+
// Execute the ACVM using the given args
|
|
87
|
+
const args = [
|
|
88
|
+
`execute`,
|
|
89
|
+
`--working-directory`,
|
|
90
|
+
`${workingDirectory}`,
|
|
91
|
+
`--bytecode`,
|
|
92
|
+
`${bytecodeFilename}`,
|
|
93
|
+
`--input-witness`,
|
|
94
|
+
`${witnessFilename}`,
|
|
95
|
+
'--print',
|
|
96
|
+
'--output-witness',
|
|
97
|
+
'output-witness',
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
logger.debug(`Calling ACVM with ${args.join(' ')}`);
|
|
101
|
+
|
|
102
|
+
const processPromise = new Promise<string>((resolve, reject) => {
|
|
103
|
+
let outputWitness = Buffer.alloc(0);
|
|
104
|
+
let errorBuffer = Buffer.alloc(0);
|
|
105
|
+
const acvm = proc.spawn(pathToAcvm, args);
|
|
106
|
+
acvm.stdout.on('data', data => {
|
|
107
|
+
outputWitness = Buffer.concat([outputWitness, data]);
|
|
108
|
+
});
|
|
109
|
+
acvm.stderr.on('data', data => {
|
|
110
|
+
errorBuffer = Buffer.concat([errorBuffer, data]);
|
|
111
|
+
});
|
|
112
|
+
acvm.on('close', code => {
|
|
113
|
+
if (code === 0) {
|
|
114
|
+
resolve(outputWitness.toString('utf-8'));
|
|
115
|
+
} else {
|
|
116
|
+
logger.error(`From ACVM: ${errorBuffer.toString('utf-8')}`);
|
|
117
|
+
reject(errorBuffer.toString('utf-8'));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const duration = new Timer();
|
|
89
123
|
const output = await processPromise;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
124
|
+
if (outputFilename) {
|
|
125
|
+
const outputWitnessFileName = `${workingDirectory}/output-witness.gz`;
|
|
126
|
+
await fs.copyFile(outputWitnessFileName, outputFilename);
|
|
127
|
+
}
|
|
128
|
+
const witness = parseIntoWitnessMap(output);
|
|
129
|
+
return { status: ACVM_RESULT.SUCCESS, witness, duration: duration.ms() };
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return { status: ACVM_RESULT.FAILURE, reason: `${error}` };
|
|
94
132
|
}
|
|
95
133
|
}
|
|
96
134
|
|
|
97
135
|
export class NativeACVMSimulator implements SimulationProvider {
|
|
98
|
-
constructor(private workingDirectory: string, private pathToAcvm: string) {}
|
|
136
|
+
constructor(private workingDirectory: string, private pathToAcvm: string, private witnessFilename?: string) {}
|
|
99
137
|
async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
|
|
100
138
|
// Execute the circuit on those initial witness values
|
|
101
139
|
|
|
@@ -103,10 +141,19 @@ export class NativeACVMSimulator implements SimulationProvider {
|
|
|
103
141
|
const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
|
|
104
142
|
|
|
105
143
|
// Provide a unique working directory so we don't get clashes with parallel executions
|
|
106
|
-
const directory = `${this.workingDirectory}/${randomBytes(
|
|
144
|
+
const directory = `${this.workingDirectory}/${randomBytes(8).toString('hex')}`;
|
|
145
|
+
|
|
146
|
+
await fs.mkdir(directory, { recursive: true });
|
|
147
|
+
|
|
107
148
|
// Execute the circuit
|
|
108
|
-
const
|
|
149
|
+
const result = await executeNativeCircuit(input, decodedBytecode, directory, this.pathToAcvm, this.witnessFilename);
|
|
150
|
+
|
|
151
|
+
await fs.rm(directory, { force: true, recursive: true });
|
|
152
|
+
|
|
153
|
+
if (result.status == ACVM_RESULT.FAILURE) {
|
|
154
|
+
throw new Error(`Failed to generate witness: ${result.reason}`);
|
|
155
|
+
}
|
|
109
156
|
|
|
110
|
-
return
|
|
157
|
+
return result.witness;
|
|
111
158
|
}
|
|
112
159
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types';
|
|
1
2
|
import { type NoirCompiledCircuit } from '@aztec/types/noir';
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -26,9 +27,12 @@ export class WASMSimulator implements SimulationProvider {
|
|
|
26
27
|
const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
|
|
27
28
|
//
|
|
28
29
|
// Execute the circuit
|
|
29
|
-
const _witnessMap = await executeCircuitWithBlackBoxSolver(
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const _witnessMap = await executeCircuitWithBlackBoxSolver(
|
|
31
|
+
await getSolver(),
|
|
32
|
+
decodedBytecode,
|
|
33
|
+
input,
|
|
34
|
+
foreignCallHandler, // handle calls to debug_log
|
|
35
|
+
);
|
|
32
36
|
|
|
33
37
|
return _witnessMap;
|
|
34
38
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { PackedArguments } from '@aztec/circuit-types';
|
|
2
|
-
import { Fr } from '@aztec/circuits.js';
|
|
3
|
-
/**
|
|
4
|
-
* A cache for packed arguments during transaction execution.
|
|
5
|
-
*/
|
|
6
|
-
export declare class PackedArgsCache {
|
|
7
|
-
private cache;
|
|
8
|
-
constructor(initialArguments?: PackedArguments[]);
|
|
9
|
-
/**
|
|
10
|
-
* Creates a new packed arguments cache.
|
|
11
|
-
* @param initialArguments - The initial arguments to add to the cache.
|
|
12
|
-
* @returns The new packed arguments cache.
|
|
13
|
-
*/
|
|
14
|
-
static create(initialArguments?: PackedArguments[]): PackedArgsCache;
|
|
15
|
-
/**
|
|
16
|
-
* Unpacks packed arguments.
|
|
17
|
-
* @param hash - The hash of the packed arguments.
|
|
18
|
-
* @returns The unpacked arguments.
|
|
19
|
-
*/
|
|
20
|
-
unpack(hash: Fr): Fr[];
|
|
21
|
-
/**
|
|
22
|
-
* Packs arguments.
|
|
23
|
-
* @param args - The arguments to pack.
|
|
24
|
-
* @returns The hash of the packed arguments.
|
|
25
|
-
*/
|
|
26
|
-
pack(args: Fr[]): Fr;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=packed_args_cache.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"packed_args_cache.d.ts","sourceRoot":"","sources":["../../src/common/packed_args_cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAExC;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAoB;gBAErB,gBAAgB,GAAE,eAAe,EAAO;IAOpD;;;;OAIG;WACW,MAAM,CAAC,gBAAgB,GAAE,eAAe,EAAO;IAI7D;;;;OAIG;IACI,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE;IAW7B;;;;OAIG;IACI,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE;CAQvB"}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { PackedArguments } from '@aztec/circuit-types';
|
|
2
|
-
import { Fr } from '@aztec/circuits.js';
|
|
3
|
-
/**
|
|
4
|
-
* A cache for packed arguments during transaction execution.
|
|
5
|
-
*/
|
|
6
|
-
export class PackedArgsCache {
|
|
7
|
-
constructor(initialArguments = []) {
|
|
8
|
-
this.cache = new Map();
|
|
9
|
-
for (const initialArg of initialArguments) {
|
|
10
|
-
this.cache.set(initialArg.hash.toBigInt(), initialArg.args);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Creates a new packed arguments cache.
|
|
15
|
-
* @param initialArguments - The initial arguments to add to the cache.
|
|
16
|
-
* @returns The new packed arguments cache.
|
|
17
|
-
*/
|
|
18
|
-
static create(initialArguments = []) {
|
|
19
|
-
return new PackedArgsCache(initialArguments);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Unpacks packed arguments.
|
|
23
|
-
* @param hash - The hash of the packed arguments.
|
|
24
|
-
* @returns The unpacked arguments.
|
|
25
|
-
*/
|
|
26
|
-
unpack(hash) {
|
|
27
|
-
if (hash.equals(Fr.ZERO)) {
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
const packedArgs = this.cache.get(hash.value);
|
|
31
|
-
if (!packedArgs) {
|
|
32
|
-
throw new Error(`Packed arguments for hash ${hash.toString()} not found in cache`);
|
|
33
|
-
}
|
|
34
|
-
return packedArgs;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Packs arguments.
|
|
38
|
-
* @param args - The arguments to pack.
|
|
39
|
-
* @returns The hash of the packed arguments.
|
|
40
|
-
*/
|
|
41
|
-
pack(args) {
|
|
42
|
-
if (args.length === 0) {
|
|
43
|
-
return Fr.ZERO;
|
|
44
|
-
}
|
|
45
|
-
const packedArguments = PackedArguments.fromArgs(args);
|
|
46
|
-
this.cache.set(packedArguments.hash.value, packedArguments.args);
|
|
47
|
-
return packedArguments.hash;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFja2VkX2FyZ3NfY2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbW9uL3BhY2tlZF9hcmdzX2NhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFeEM7O0dBRUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQUcxQixZQUFZLG1CQUFzQyxFQUFFO1FBQ2xELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUN2QixLQUFLLE1BQU0sVUFBVSxJQUFJLGdCQUFnQixFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUQsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLE1BQU0sQ0FBQyxtQkFBc0MsRUFBRTtRQUMzRCxPQUFPLElBQUksZUFBZSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsSUFBUTtRQUNwQixJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDekIsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO1FBQ0QsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLDZCQUE2QixJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixDQUFDLENBQUM7UUFDckYsQ0FBQztRQUNELE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksSUFBSSxDQUFDLElBQVU7UUFDcEIsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3RCLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQztRQUNqQixDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQUcsZUFBZSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakUsT0FBTyxlQUFlLENBQUMsSUFBSSxDQUFDO0lBQzlCLENBQUM7Q0FDRiJ9
|