@aztec/txe 0.0.1-commit.858058eac → 0.0.1-commit.85d7d01
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/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +82 -50
- package/dest/oracle/interfaces.d.ts +4 -3
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.d.ts +6 -7
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +34 -25
- package/dest/rpc_translator.d.ts +3 -3
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +17 -5
- package/dest/state_machine/dummy_p2p_client.d.ts +8 -5
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +13 -4
- package/dest/state_machine/index.d.ts +1 -1
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +1 -1
- package/dest/txe_session.d.ts +9 -6
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +21 -18
- package/dest/util/txe_public_contract_data_source.d.ts +2 -3
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +5 -22
- package/package.json +15 -15
- package/src/index.ts +83 -49
- package/src/oracle/interfaces.ts +6 -1
- package/src/oracle/txe_oracle_top_level_context.ts +46 -23
- package/src/rpc_translator.ts +19 -4
- package/src/state_machine/dummy_p2p_client.ts +19 -7
- package/src/state_machine/index.ts +1 -0
- package/src/txe_session.ts +26 -17
- package/src/util/txe_public_contract_data_source.ts +10 -36
- package/dest/util/txe_contract_store.d.ts +0 -12
- package/dest/util/txe_contract_store.d.ts.map +0 -1
- package/dest/util/txe_contract_store.js +0 -22
- package/src/util/txe_contract_store.ts +0 -36
package/src/txe_session.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { KeyStore } from '@aztec/key-store';
|
|
5
5
|
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
|
|
6
|
-
import type {
|
|
6
|
+
import type { AccessScopes } from '@aztec/pxe/client/lazy';
|
|
7
7
|
import {
|
|
8
8
|
AddressStore,
|
|
9
9
|
AnchorBlockStore,
|
|
10
10
|
CapsuleStore,
|
|
11
|
+
ContractStore,
|
|
11
12
|
JobCoordinator,
|
|
12
13
|
NoteService,
|
|
13
14
|
NoteStore,
|
|
@@ -54,7 +55,6 @@ import { TXEArchiver } from './state_machine/archiver.js';
|
|
|
54
55
|
import { TXEStateMachine } from './state_machine/index.js';
|
|
55
56
|
import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
|
|
56
57
|
import { TXEAccountStore } from './util/txe_account_store.js';
|
|
57
|
-
import { TXEContractStore } from './util/txe_contract_store.js';
|
|
58
58
|
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
|
|
59
59
|
import { makeTxEffect } from './utils/tx_effect_creation.js';
|
|
60
60
|
|
|
@@ -113,6 +113,10 @@ export interface TXESessionStateHandler {
|
|
|
113
113
|
enterPublicState(contractAddress?: AztecAddress): Promise<void>;
|
|
114
114
|
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
|
|
115
115
|
enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
|
|
116
|
+
|
|
117
|
+
// TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
|
|
118
|
+
cycleJob(): Promise<string>;
|
|
119
|
+
getCurrentJob(): string;
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
/**
|
|
@@ -131,7 +135,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
131
135
|
| IPrivateExecutionOracle
|
|
132
136
|
| IAvmExecutionOracle
|
|
133
137
|
| ITxeExecutionOracle,
|
|
134
|
-
private contractStore:
|
|
138
|
+
private contractStore: ContractStore,
|
|
135
139
|
private noteStore: NoteStore,
|
|
136
140
|
private keyStore: KeyStore,
|
|
137
141
|
private addressStore: AddressStore,
|
|
@@ -148,12 +152,11 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
148
152
|
private nextBlockTimestamp: bigint,
|
|
149
153
|
) {}
|
|
150
154
|
|
|
151
|
-
static async init(
|
|
155
|
+
static async init(contractStore: ContractStore) {
|
|
152
156
|
const store = await openTmpStore('txe-session');
|
|
153
157
|
|
|
154
158
|
const addressStore = new AddressStore(store);
|
|
155
159
|
const privateEventStore = new PrivateEventStore(store);
|
|
156
|
-
const contractStore = new TXEContractStore(store);
|
|
157
160
|
const noteStore = new NoteStore(store);
|
|
158
161
|
const senderTaggingStore = new SenderTaggingStore(store);
|
|
159
162
|
const recipientTaggingStore = new RecipientTaggingStore(store);
|
|
@@ -172,12 +175,6 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
172
175
|
noteStore,
|
|
173
176
|
]);
|
|
174
177
|
|
|
175
|
-
// Register protocol contracts.
|
|
176
|
-
for (const { contractClass, instance, artifact } of protocolContracts) {
|
|
177
|
-
await contractStore.addContractArtifact(contractClass.id, artifact);
|
|
178
|
-
await contractStore.addContractInstance(instance);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
178
|
const archiver = new TXEArchiver(store);
|
|
182
179
|
const anchorBlockStore = new AnchorBlockStore(store);
|
|
183
180
|
const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
|
|
@@ -200,7 +197,6 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
200
197
|
senderAddressBookStore,
|
|
201
198
|
capsuleStore,
|
|
202
199
|
privateEventStore,
|
|
203
|
-
initialJobId,
|
|
204
200
|
nextBlockTimestamp,
|
|
205
201
|
version,
|
|
206
202
|
chainId,
|
|
@@ -261,6 +257,17 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
261
257
|
}
|
|
262
258
|
}
|
|
263
259
|
|
|
260
|
+
getCurrentJob(): string {
|
|
261
|
+
return this.currentJobId;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Commits the current job and begins a new one. Returns the new job ID. */
|
|
265
|
+
async cycleJob(): Promise<string> {
|
|
266
|
+
await this.jobCoordinator.commitJob(this.currentJobId);
|
|
267
|
+
this.currentJobId = this.jobCoordinator.beginJob();
|
|
268
|
+
return this.currentJobId;
|
|
269
|
+
}
|
|
270
|
+
|
|
264
271
|
async enterTopLevelState() {
|
|
265
272
|
switch (this.state.name) {
|
|
266
273
|
case 'PRIVATE': {
|
|
@@ -284,8 +291,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
284
291
|
}
|
|
285
292
|
|
|
286
293
|
// Commit all staged stores from the job that was just completed, then begin a new job
|
|
287
|
-
await this.
|
|
288
|
-
this.currentJobId = this.jobCoordinator.beginJob();
|
|
294
|
+
await this.cycleJob();
|
|
289
295
|
|
|
290
296
|
this.oracleHandler = new TXEOracleTopLevelContext(
|
|
291
297
|
this.stateMachine,
|
|
@@ -299,7 +305,6 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
299
305
|
this.senderAddressBookStore,
|
|
300
306
|
this.capsuleStore,
|
|
301
307
|
this.privateEventStore,
|
|
302
|
-
this.currentJobId,
|
|
303
308
|
this.nextBlockTimestamp,
|
|
304
309
|
this.version,
|
|
305
310
|
this.chainId,
|
|
@@ -323,6 +328,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
323
328
|
|
|
324
329
|
await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
|
|
325
330
|
contractAddress,
|
|
331
|
+
'ALL_SCOPES',
|
|
326
332
|
);
|
|
327
333
|
const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
|
|
328
334
|
|
|
@@ -362,6 +368,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
362
368
|
privateEventStore: this.privateEventStore,
|
|
363
369
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
364
370
|
jobId: this.currentJobId,
|
|
371
|
+
scopes: 'ALL_SCOPES',
|
|
365
372
|
});
|
|
366
373
|
|
|
367
374
|
// We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
|
|
@@ -414,7 +421,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
414
421
|
this.stateMachine.node,
|
|
415
422
|
anchorBlockHeader,
|
|
416
423
|
this.currentJobId,
|
|
417
|
-
).syncNoteNullifiers(contractAddress);
|
|
424
|
+
).syncNoteNullifiers(contractAddress, 'ALL_SCOPES');
|
|
418
425
|
|
|
419
426
|
this.oracleHandler = new UtilityExecutionOracle({
|
|
420
427
|
contractAddress,
|
|
@@ -431,6 +438,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
431
438
|
capsuleStore: this.capsuleStore,
|
|
432
439
|
privateEventStore: this.privateEventStore,
|
|
433
440
|
jobId: this.currentJobId,
|
|
441
|
+
scopes: 'ALL_SCOPES',
|
|
434
442
|
});
|
|
435
443
|
|
|
436
444
|
this.state = { name: 'UTILITY' };
|
|
@@ -499,7 +507,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
499
507
|
}
|
|
500
508
|
|
|
501
509
|
private utilityExecutorForContractSync(anchorBlock: any) {
|
|
502
|
-
return async (call: FunctionCall) => {
|
|
510
|
+
return async (call: FunctionCall, scopes: AccessScopes) => {
|
|
503
511
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
504
512
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
505
513
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
@@ -521,6 +529,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
521
529
|
capsuleStore: this.capsuleStore,
|
|
522
530
|
privateEventStore: this.privateEventStore,
|
|
523
531
|
jobId: this.currentJobId,
|
|
532
|
+
scopes,
|
|
524
533
|
});
|
|
525
534
|
await new WASMSimulator()
|
|
526
535
|
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import type { ContractStore } from '@aztec/pxe/server';
|
|
4
|
-
import { type ContractArtifact, FunctionSelector
|
|
4
|
+
import { type ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi';
|
|
5
5
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
-
import {
|
|
7
|
-
type ContractClassPublic,
|
|
8
|
-
type ContractDataSource,
|
|
9
|
-
type ContractInstanceWithAddress,
|
|
10
|
-
computePrivateFunctionsRoot,
|
|
11
|
-
computePublicBytecodeCommitment,
|
|
12
|
-
getContractClassPrivateFunctionFromArtifact,
|
|
13
|
-
} from '@aztec/stdlib/contract';
|
|
6
|
+
import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
14
7
|
|
|
15
8
|
export class TXEPublicContractDataSource implements ContractDataSource {
|
|
16
|
-
#privateFunctionsRoot: Map<string, Buffer> = new Map();
|
|
17
9
|
constructor(
|
|
18
10
|
private blockNumber: BlockNumber,
|
|
19
11
|
private contractStore: ContractStore,
|
|
@@ -24,42 +16,24 @@ export class TXEPublicContractDataSource implements ContractDataSource {
|
|
|
24
16
|
}
|
|
25
17
|
|
|
26
18
|
async getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
27
|
-
const contractClass = await this.contractStore.
|
|
19
|
+
const contractClass = await this.contractStore.getContractClassWithPreimage(id);
|
|
28
20
|
if (!contractClass) {
|
|
29
21
|
return;
|
|
30
22
|
}
|
|
31
|
-
const artifact = await this.contractStore.getContractArtifact(id);
|
|
32
|
-
if (!artifact) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let privateFunctionsRoot;
|
|
37
|
-
if (!this.#privateFunctionsRoot.has(id.toString())) {
|
|
38
|
-
const privateFunctions = await Promise.all(
|
|
39
|
-
artifact.functions
|
|
40
|
-
.filter(fn => fn.functionType === FunctionType.PRIVATE)
|
|
41
|
-
.map(fn => getContractClassPrivateFunctionFromArtifact(fn)),
|
|
42
|
-
);
|
|
43
|
-
privateFunctionsRoot = await computePrivateFunctionsRoot(privateFunctions);
|
|
44
|
-
this.#privateFunctionsRoot.set(id.toString(), privateFunctionsRoot.toBuffer());
|
|
45
|
-
} else {
|
|
46
|
-
privateFunctionsRoot = Fr.fromBuffer(this.#privateFunctionsRoot.get(id.toString())!);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
23
|
return {
|
|
50
|
-
id,
|
|
51
|
-
artifactHash: contractClass
|
|
52
|
-
packedBytecode: contractClass
|
|
53
|
-
privateFunctionsRoot,
|
|
54
|
-
version: contractClass
|
|
24
|
+
id: contractClass.id,
|
|
25
|
+
artifactHash: contractClass.artifactHash,
|
|
26
|
+
packedBytecode: contractClass.packedBytecode,
|
|
27
|
+
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
28
|
+
version: contractClass.version,
|
|
55
29
|
privateFunctions: [],
|
|
56
30
|
utilityFunctions: [],
|
|
57
31
|
};
|
|
58
32
|
}
|
|
59
33
|
|
|
60
34
|
async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
|
|
61
|
-
const contractClass = await this.contractStore.
|
|
62
|
-
return contractClass
|
|
35
|
+
const contractClass = await this.contractStore.getContractClassWithPreimage(id);
|
|
36
|
+
return contractClass?.publicBytecodeCommitment;
|
|
63
37
|
}
|
|
64
38
|
|
|
65
39
|
async getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ContractArtifact } from '@aztec/aztec.js/abi';
|
|
2
|
-
import { Fr } from '@aztec/aztec.js/fields';
|
|
3
|
-
import { ContractStore } from '@aztec/pxe/server';
|
|
4
|
-
export type ContractArtifactWithHash = ContractArtifact & {
|
|
5
|
-
artifactHash: Fr;
|
|
6
|
-
};
|
|
7
|
-
export declare class TXEContractStore extends ContractStore {
|
|
8
|
-
#private;
|
|
9
|
-
addContractArtifact(id: Fr, artifact: ContractArtifact | ContractArtifactWithHash): Promise<void>;
|
|
10
|
-
getContractArtifact(contractClassId: Fr): Promise<ContractArtifact | ContractArtifactWithHash | undefined>;
|
|
11
|
-
}
|
|
12
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhlX2NvbnRyYWN0X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbC90eGVfY29udHJhY3Rfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDNUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRWxELE1BQU0sTUFBTSx3QkFBd0IsR0FBRyxnQkFBZ0IsR0FBRztJQUFFLFlBQVksRUFBRSxFQUFFLENBQUE7Q0FBRSxDQUFDO0FBTy9FLHFCQUFhLGdCQUFpQixTQUFRLGFBQWE7O0lBRzNCLG1CQUFtQixDQUN2QyxFQUFFLEVBQUUsRUFBRSxFQUNOLFFBQVEsRUFBRSxnQkFBZ0IsR0FBRyx3QkFBd0IsR0FDcEQsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUtmO0lBRXFCLG1CQUFtQixDQUN2QyxlQUFlLEVBQUUsRUFBRSxHQUNsQixPQUFPLENBQUMsZ0JBQWdCLEdBQUcsd0JBQXdCLEdBQUcsU0FBUyxDQUFDLENBUWxFO0NBQ0YifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"txe_contract_store.d.ts","sourceRoot":"","sources":["../../src/util/txe_contract_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,gBAAgB,GAAG;IAAE,YAAY,EAAE,EAAE,CAAA;CAAE,CAAC;AAO/E,qBAAa,gBAAiB,SAAQ,aAAa;;IAG3B,mBAAmB,CACvC,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,gBAAgB,GAAG,wBAAwB,GACpD,OAAO,CAAC,IAAI,CAAC,CAKf;IAEqB,mBAAmB,CACvC,eAAe,EAAE,EAAE,GAClB,OAAO,CAAC,gBAAgB,GAAG,wBAAwB,GAAG,SAAS,CAAC,CAQlE;CACF"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Fr } from '@aztec/aztec.js/fields';
|
|
2
|
-
import { ContractStore } from '@aztec/pxe/server';
|
|
3
|
-
/*
|
|
4
|
-
* A contract store that stores contract artifacts with their hashes. Since
|
|
5
|
-
* TXE typically deploys the same contract again and again for multiple tests, caching
|
|
6
|
-
* the *very* expensive artifact hash computation improves testing speed significantly.
|
|
7
|
-
*/ export class TXEContractStore extends ContractStore {
|
|
8
|
-
#artifactHashes = new Map();
|
|
9
|
-
async addContractArtifact(id, artifact) {
|
|
10
|
-
if ('artifactHash' in artifact) {
|
|
11
|
-
this.#artifactHashes.set(id.toString(), artifact.artifactHash.toBuffer());
|
|
12
|
-
}
|
|
13
|
-
await super.addContractArtifact(id, artifact);
|
|
14
|
-
}
|
|
15
|
-
async getContractArtifact(contractClassId) {
|
|
16
|
-
const artifact = await super.getContractArtifact(contractClassId);
|
|
17
|
-
if (artifact && this.#artifactHashes.has(contractClassId.toString())) {
|
|
18
|
-
artifact.artifactHash = Fr.fromBuffer(this.#artifactHashes.get(contractClassId.toString()));
|
|
19
|
-
}
|
|
20
|
-
return artifact;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { ContractArtifact } from '@aztec/aztec.js/abi';
|
|
2
|
-
import { Fr } from '@aztec/aztec.js/fields';
|
|
3
|
-
import { ContractStore } from '@aztec/pxe/server';
|
|
4
|
-
|
|
5
|
-
export type ContractArtifactWithHash = ContractArtifact & { artifactHash: Fr };
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
* A contract store that stores contract artifacts with their hashes. Since
|
|
9
|
-
* TXE typically deploys the same contract again and again for multiple tests, caching
|
|
10
|
-
* the *very* expensive artifact hash computation improves testing speed significantly.
|
|
11
|
-
*/
|
|
12
|
-
export class TXEContractStore extends ContractStore {
|
|
13
|
-
#artifactHashes: Map<string, Buffer> = new Map();
|
|
14
|
-
|
|
15
|
-
public override async addContractArtifact(
|
|
16
|
-
id: Fr,
|
|
17
|
-
artifact: ContractArtifact | ContractArtifactWithHash,
|
|
18
|
-
): Promise<void> {
|
|
19
|
-
if ('artifactHash' in artifact) {
|
|
20
|
-
this.#artifactHashes.set(id.toString(), artifact.artifactHash.toBuffer());
|
|
21
|
-
}
|
|
22
|
-
await super.addContractArtifact(id, artifact);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public override async getContractArtifact(
|
|
26
|
-
contractClassId: Fr,
|
|
27
|
-
): Promise<ContractArtifact | ContractArtifactWithHash | undefined> {
|
|
28
|
-
const artifact = await super.getContractArtifact(contractClassId);
|
|
29
|
-
if (artifact && this.#artifactHashes.has(contractClassId.toString())) {
|
|
30
|
-
(artifact as ContractArtifactWithHash).artifactHash = Fr.fromBuffer(
|
|
31
|
-
this.#artifactHashes.get(contractClassId.toString())!,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
return artifact;
|
|
35
|
-
}
|
|
36
|
-
}
|