@aztec/txe 0.0.1-commit.d3ec352c → 0.0.1-commit.d58ff9d0
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/AuthRegistry-QVGDDH5X.js +3 -0
- package/dest/AuthRegistry-QVGDDH5X.js.map +7 -0
- package/dest/ContractClassRegistry-VWR3F42P.js +3 -0
- package/dest/ContractClassRegistry-VWR3F42P.js.map +7 -0
- package/dest/ContractInstanceRegistry-FHIWE33X.js +3 -0
- package/dest/ContractInstanceRegistry-FHIWE33X.js.map +7 -0
- package/dest/FeeJuice-SAOWVGG7.js +3 -0
- package/dest/FeeJuice-SAOWVGG7.js.map +7 -0
- package/dest/HandshakeRegistry-GY5CPEPS.js +3 -0
- package/dest/HandshakeRegistry-GY5CPEPS.js.map +7 -0
- package/dest/MultiCallEntrypoint-QG5H33ZZ.js +3 -0
- package/dest/MultiCallEntrypoint-QG5H33ZZ.js.map +7 -0
- package/dest/SchnorrAccount-NME3E45O.js +3 -0
- package/dest/SchnorrAccount-NME3E45O.js.map +7 -0
- package/dest/SchnorrInitializerlessAccount-WTGXU6SW.js +3 -0
- package/dest/SchnorrInitializerlessAccount-WTGXU6SW.js.map +7 -0
- package/dest/bin/check_txe_oracle_version.d.ts +2 -0
- package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
- package/dest/bin/check_txe_oracle_version.js +53 -0
- package/dest/bin/index.js +3 -30
- package/dest/bin/index.js.map +7 -0
- package/dest/bin/oracle_test_server.d.ts +3 -0
- package/dest/bin/oracle_test_server.d.ts.map +1 -0
- package/dest/bin/oracle_test_server.js +41 -0
- package/dest/chunk-32UHX7YY.js +3 -0
- package/dest/chunk-32UHX7YY.js.map +7 -0
- package/dest/chunk-GSUKUCKV.js +260 -0
- package/dest/chunk-GSUKUCKV.js.map +7 -0
- package/dest/chunk-UP4KKUVA.js +304 -0
- package/dest/chunk-UP4KKUVA.js.map +7 -0
- package/dest/constants.d.ts +7 -0
- package/dest/constants.d.ts.map +1 -0
- package/dest/constants.js +10 -0
- package/dest/dispatcher_pool.d.ts +67 -0
- package/dest/dispatcher_pool.d.ts.map +1 -0
- package/dest/dispatcher_pool.js +286 -0
- package/dest/index.d.ts +51 -7
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +75 -158
- package/dest/metafile.json +38759 -0
- package/dest/msgpackr_fr_extension.d.ts +2 -0
- package/dest/msgpackr_fr_extension.d.ts.map +1 -0
- package/dest/msgpackr_fr_extension.js +21 -0
- package/dest/oracle/interfaces.d.ts +71 -30
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/tagging_secret_strategy.d.ts +12 -0
- package/dest/oracle/tagging_secret_strategy.d.ts.map +1 -0
- package/dest/oracle/tagging_secret_strategy.js +12 -0
- package/dest/oracle/test-resolver/default_fixtures.d.ts +16 -0
- package/dest/oracle/test-resolver/default_fixtures.d.ts.map +1 -0
- package/dest/oracle/test-resolver/default_fixtures.js +200 -0
- package/dest/oracle/test-resolver/index.d.ts +9 -0
- package/dest/oracle/test-resolver/index.d.ts.map +1 -0
- package/dest/oracle/test-resolver/index.js +32 -0
- package/dest/oracle/test-resolver/resolver.d.ts +48 -0
- package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
- package/dest/oracle/test-resolver/resolver.js +181 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +40 -16
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +69 -23
- package/dest/oracle/txe_oracle_registry.d.ts +172 -0
- package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_registry.js +767 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts +79 -30
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +653 -285
- package/dest/oracle/txe_oracle_version.d.ts +17 -0
- package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_version.js +14 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
- package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
- package/dest/oracle/txe_private_execution_oracle.js +15 -0
- package/dest/rpc_server.d.ts +14 -0
- package/dest/rpc_server.d.ts.map +1 -0
- package/dest/rpc_server.js +78 -0
- package/dest/rpc_translator.d.ts +115 -216
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +901 -560
- package/dest/server.bundle.js +3 -0
- package/dest/server.bundle.js.map +7 -0
- package/dest/state_machine/archiver.d.ts +23 -58
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +76 -112
- package/dest/state_machine/dummy_p2p_client.d.ts +31 -17
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +63 -25
- package/dest/state_machine/global_variable_builder.d.ts +10 -5
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +20 -5
- package/dest/state_machine/index.d.ts +14 -8
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +84 -27
- package/dest/state_machine/mock_epoch_cache.d.ts +24 -8
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +43 -9
- package/dest/state_machine/synchronizer.d.ts +14 -6
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +23 -10
- package/dest/txe_session.d.ts +102 -20
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +442 -75
- package/dest/utils/block_creation.d.ts +20 -6
- package/dest/utils/block_creation.d.ts.map +1 -1
- package/dest/utils/block_creation.js +40 -6
- package/dest/utils/encoding.d.ts +192 -0
- package/dest/utils/encoding.d.ts.map +1 -0
- package/dest/{util → utils}/encoding.js +8 -3
- package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
- package/dest/utils/expected_failure_error.d.ts.map +1 -0
- package/dest/utils/tx_effect_creation.d.ts +2 -3
- package/dest/utils/tx_effect_creation.d.ts.map +1 -1
- package/dest/utils/tx_effect_creation.js +3 -7
- package/dest/utils/txe_account_store.d.ts +10 -0
- package/dest/utils/txe_account_store.d.ts.map +1 -0
- package/dest/{util/txe_account_data_provider.js → utils/txe_account_store.js} +1 -1
- package/dest/utils/txe_artifact_resolver.d.ts +37 -0
- package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
- package/dest/utils/txe_artifact_resolver.js +161 -0
- package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
- package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
- package/dest/utils/txe_public_contract_data_source.js +51 -0
- package/dest/worker.bundle.js +3 -0
- package/dest/worker.bundle.js.map +7 -0
- package/dest/worker.d.ts +2 -0
- package/dest/worker.d.ts.map +1 -0
- package/dest/worker.js +92 -0
- package/package.json +39 -22
- package/src/bin/check_txe_oracle_version.ts +67 -0
- package/src/bin/index.ts +11 -2
- package/src/bin/oracle_test_server.ts +51 -0
- package/src/constants.ts +13 -0
- package/src/dispatcher_pool.ts +317 -0
- package/src/index.ts +113 -201
- package/src/msgpackr_fr_extension.ts +23 -0
- package/src/oracle/interfaces.ts +77 -35
- package/src/oracle/tagging_secret_strategy.ts +25 -0
- package/src/oracle/test-resolver/default_fixtures.ts +271 -0
- package/src/oracle/test-resolver/index.ts +44 -0
- package/src/oracle/test-resolver/resolver.ts +245 -0
- package/src/oracle/txe_oracle_public_context.ts +82 -27
- package/src/oracle/txe_oracle_registry.ts +556 -0
- package/src/oracle/txe_oracle_top_level_context.ts +555 -189
- package/src/oracle/txe_oracle_version.ts +17 -0
- package/src/oracle/txe_private_execution_oracle.ts +30 -0
- package/src/rpc_server.ts +87 -0
- package/src/rpc_translator.ts +1115 -923
- package/src/state_machine/archiver.ts +81 -132
- package/src/state_machine/dummy_p2p_client.ts +96 -38
- package/src/state_machine/global_variable_builder.ts +28 -10
- package/src/state_machine/index.ts +118 -44
- package/src/state_machine/mock_epoch_cache.ts +53 -14
- package/src/state_machine/synchronizer.ts +32 -12
- package/src/txe_session.ts +727 -123
- package/src/utils/block_creation.ts +50 -16
- package/src/{util → utils}/encoding.ts +9 -3
- package/src/utils/tx_effect_creation.ts +3 -13
- package/src/{util/txe_account_data_provider.ts → utils/txe_account_store.ts} +1 -1
- package/src/utils/txe_artifact_resolver.ts +217 -0
- package/src/utils/txe_public_contract_data_source.ts +60 -0
- package/src/worker.ts +98 -0
- package/dest/util/encoding.d.ts +0 -121
- package/dest/util/encoding.d.ts.map +0 -1
- package/dest/util/expected_failure_error.d.ts.map +0 -1
- package/dest/util/txe_account_data_provider.d.ts +0 -10
- package/dest/util/txe_account_data_provider.d.ts.map +0 -1
- package/dest/util/txe_contract_data_provider.d.ts +0 -12
- package/dest/util/txe_contract_data_provider.d.ts.map +0 -1
- package/dest/util/txe_contract_data_provider.js +0 -22
- package/dest/util/txe_public_contract_data_source.d.ts +0 -21
- package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
- package/dest/util/txe_public_contract_data_source.js +0 -67
- package/src/util/txe_contract_data_provider.ts +0 -36
- package/src/util/txe_public_contract_data_source.ts +0 -86
- /package/dest/{util → utils}/expected_failure_error.js +0 -0
- /package/src/{util → utils}/expected_failure_error.ts +0 -0
|
@@ -4,13 +4,12 @@ import {
|
|
|
4
4
|
NULLIFIER_SUBTREE_HEIGHT,
|
|
5
5
|
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
6
6
|
} from '@aztec/constants';
|
|
7
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
7
|
+
import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
|
|
8
8
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
9
|
-
import { Fr } from '@aztec/foundation/
|
|
10
|
-
import {
|
|
11
|
-
import { makeContentCommitment } from '@aztec/stdlib/testing';
|
|
9
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
10
|
+
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
12
11
|
import { AppendOnlyTreeSnapshot, MerkleTreeId, type MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
|
|
13
|
-
import { GlobalVariables, TxEffect } from '@aztec/stdlib/tx';
|
|
12
|
+
import { BlockHeader, GlobalVariables, TxEffect } from '@aztec/stdlib/tx';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Returns a transaction request hash that is valid for transactions that are the only ones in a block.
|
|
@@ -24,6 +23,7 @@ export function getSingleTxBlockRequestHash(blockNumber: BlockNumber): Fr {
|
|
|
24
23
|
export async function insertTxEffectIntoWorldTrees(
|
|
25
24
|
txEffect: TxEffect,
|
|
26
25
|
worldTrees: MerkleTreeWriteOperations,
|
|
26
|
+
l1ToL2Messages: Fr[] = [],
|
|
27
27
|
): Promise<void> {
|
|
28
28
|
await worldTrees.appendLeaves(
|
|
29
29
|
MerkleTreeId.NOTE_HASH_TREE,
|
|
@@ -38,7 +38,7 @@ export async function insertTxEffectIntoWorldTrees(
|
|
|
38
38
|
|
|
39
39
|
await worldTrees.appendLeaves(
|
|
40
40
|
MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
|
|
41
|
-
padArrayEnd<Fr, number>(
|
|
41
|
+
padArrayEnd<Fr, number>(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP),
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
// We do not need to add public data writes because we apply them as we go.
|
|
@@ -47,18 +47,52 @@ export async function insertTxEffectIntoWorldTrees(
|
|
|
47
47
|
export async function makeTXEBlockHeader(
|
|
48
48
|
worldTrees: MerkleTreeWriteOperations,
|
|
49
49
|
globalVariables: GlobalVariables,
|
|
50
|
-
): Promise<
|
|
50
|
+
): Promise<BlockHeader> {
|
|
51
51
|
const stateReference = await worldTrees.getStateReference();
|
|
52
52
|
const archiveInfo = await worldTrees.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
53
53
|
|
|
54
|
-
return
|
|
55
|
-
new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
|
|
56
|
-
|
|
57
|
-
stateReference,
|
|
54
|
+
return BlockHeader.from({
|
|
55
|
+
lastArchive: new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
|
|
56
|
+
spongeBlobHash: Fr.ZERO,
|
|
57
|
+
state: stateReference,
|
|
58
58
|
globalVariables,
|
|
59
|
-
Fr.ZERO,
|
|
60
|
-
Fr.ZERO,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
totalFees: Fr.ZERO,
|
|
60
|
+
totalManaUsed: Fr.ZERO,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates an L2Block with proper archive chaining.
|
|
66
|
+
* This function:
|
|
67
|
+
* 1. Gets the current archive state as lastArchive for the header
|
|
68
|
+
* 2. Creates the block header
|
|
69
|
+
* 3. Updates the archive tree with the header hash
|
|
70
|
+
* 4. Gets the new archive state for the block's archive
|
|
71
|
+
*
|
|
72
|
+
* @param worldTrees - The world trees to read/write from
|
|
73
|
+
* @param globalVariables - Global variables for the block
|
|
74
|
+
* @param txEffects - Transaction effects to include in the block
|
|
75
|
+
* @returns The created L2Block with proper archive chaining
|
|
76
|
+
*/
|
|
77
|
+
export async function makeTXEBlock(
|
|
78
|
+
worldTrees: MerkleTreeWriteOperations,
|
|
79
|
+
globalVariables: GlobalVariables,
|
|
80
|
+
txEffects: TxEffect[],
|
|
81
|
+
): Promise<L2Block> {
|
|
82
|
+
const header = await makeTXEBlockHeader(worldTrees, globalVariables);
|
|
83
|
+
|
|
84
|
+
// Update the archive tree with this block's header hash
|
|
85
|
+
await worldTrees.updateArchive(header);
|
|
86
|
+
|
|
87
|
+
// Get the new archive state after updating
|
|
88
|
+
const newArchiveInfo = await worldTrees.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
89
|
+
const newArchive = new AppendOnlyTreeSnapshot(new Fr(newArchiveInfo.root), Number(newArchiveInfo.size));
|
|
90
|
+
|
|
91
|
+
// L2Block requires checkpointNumber and indexWithinCheckpoint.
|
|
92
|
+
// TXE uses 1-block-per-checkpoint for testing simplicity, so we can use block number as checkpoint number.
|
|
93
|
+
// This uses the deprecated fromBlockNumber method intentionally for the TXE testing environment.
|
|
94
|
+
const checkpointNumber = CheckpointNumber.fromBlockNumber(globalVariables.blockNumber);
|
|
95
|
+
const indexWithinCheckpoint = IndexWithinCheckpoint(0);
|
|
96
|
+
|
|
97
|
+
return new L2Block(newArchive, header, new Body(txEffects), checkpointNumber, indexWithinCheckpoint);
|
|
64
98
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
1
2
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
3
3
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
4
4
|
import { type ContractArtifact, ContractArtifactSchema } from '@aztec/stdlib/abi';
|
|
5
5
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
6
7
|
import { type ContractInstanceWithAddress, ContractInstanceWithAddressSchema } from '@aztec/stdlib/contract';
|
|
7
8
|
|
|
8
9
|
import { z } from 'zod';
|
|
@@ -25,6 +26,10 @@ export function addressFromSingle(obj: ForeignCallSingle) {
|
|
|
25
26
|
return new AztecAddress(fromSingle(obj));
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
export function blockHashFromSingle(obj: ForeignCallSingle) {
|
|
30
|
+
return new BlockHash(fromSingle(obj));
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
export function fromArray(obj: ForeignCallArray) {
|
|
29
34
|
return obj.map(str => Fr.fromBuffer(hexToBuffer(str)));
|
|
30
35
|
}
|
|
@@ -142,9 +147,10 @@ export function arrayOfArraysToBoundedVecOfArrays(
|
|
|
142
147
|
|
|
143
148
|
const numFieldsToPad = maxLen * nestedArrayLength - flattenedStorage.length;
|
|
144
149
|
|
|
145
|
-
|
|
150
|
+
// Pad with hex-encoded zeros so the result is uniformly `ForeignCallSingle` (string); raw Fr
|
|
151
|
+
// instances would JSON-serialize as `{ asBigInt: "0" }` and nargo would reject the response.
|
|
152
|
+
const flattenedStorageWithPadding = flattenedStorage.concat(toArray(Array(numFieldsToPad).fill(new Fr(0))));
|
|
146
153
|
|
|
147
|
-
// At last we get the actual length of the BoundedVec and return the values.
|
|
148
154
|
const len = toSingle(new Fr(bVecStorage.length));
|
|
149
155
|
return [flattenedStorageWithPadding, len];
|
|
150
156
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { Fr } from '@aztec/foundation/
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import type { ExecutionNoteCache } from '@aztec/pxe/simulator';
|
|
4
4
|
import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash } from '@aztec/stdlib/hash';
|
|
5
5
|
import { TxEffect, TxHash } from '@aztec/stdlib/tx';
|
|
6
6
|
|
|
7
|
-
export async function makeTxEffect(
|
|
8
|
-
noteCache: ExecutionNoteCache,
|
|
9
|
-
protocolNullifier: Fr,
|
|
10
|
-
txBlockNumber: BlockNumber,
|
|
11
|
-
): Promise<TxEffect> {
|
|
7
|
+
export async function makeTxEffect(noteCache: ExecutionNoteCache, txBlockNumber: BlockNumber): Promise<TxEffect> {
|
|
12
8
|
const txEffect = TxEffect.empty();
|
|
13
9
|
|
|
14
|
-
const
|
|
15
|
-
const nonceGenerator = usedProtocolNullifierForNonces ? protocolNullifier : noteCache.getAllNullifiers()[0];
|
|
16
|
-
|
|
10
|
+
const nonceGenerator = noteCache.getNonceGenerator();
|
|
17
11
|
txEffect.noteHashes = await Promise.all(
|
|
18
12
|
noteCache
|
|
19
13
|
.getAllNotes()
|
|
@@ -28,10 +22,6 @@ export async function makeTxEffect(
|
|
|
28
22
|
// Nullifiers are already siloed
|
|
29
23
|
txEffect.nullifiers = noteCache.getAllNullifiers();
|
|
30
24
|
|
|
31
|
-
if (usedProtocolNullifierForNonces) {
|
|
32
|
-
txEffect.nullifiers.unshift(protocolNullifier);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
25
|
txEffect.txHash = new TxHash(new Fr(txBlockNumber));
|
|
36
26
|
|
|
37
27
|
return txEffect;
|
|
@@ -2,7 +2,7 @@ import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
|
2
2
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
3
3
|
import { CompleteAddress } from '@aztec/stdlib/contract';
|
|
4
4
|
|
|
5
|
-
export class
|
|
5
|
+
export class TXEAccountStore {
|
|
6
6
|
#accounts: AztecAsyncMap<string, Buffer>;
|
|
7
7
|
|
|
8
8
|
constructor(store: AztecAsyncKVStore) {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js/abi';
|
|
2
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
3
|
+
import {
|
|
4
|
+
type ContractInstanceWithAddress,
|
|
5
|
+
getContractInstanceFromInstantiationParams,
|
|
6
|
+
} from '@aztec/aztec.js/contracts';
|
|
7
|
+
import { Fr } from '@aztec/aztec.js/fields';
|
|
8
|
+
import { PublicKeys, deriveKeys } from '@aztec/aztec.js/keys';
|
|
9
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
10
|
+
import type { ContractStore } from '@aztec/pxe/client/lazy';
|
|
11
|
+
import type { ContractArtifactWithHash } from '@aztec/stdlib/contract';
|
|
12
|
+
import { computeArtifactHash } from '@aztec/stdlib/contract';
|
|
13
|
+
|
|
14
|
+
import { createHash } from 'crypto';
|
|
15
|
+
import { createReadStream } from 'fs';
|
|
16
|
+
import { readFile, readdir } from 'fs/promises';
|
|
17
|
+
import { join, parse } from 'path';
|
|
18
|
+
|
|
19
|
+
export interface ResolvedArtifact {
|
|
20
|
+
artifact: ContractArtifactWithHash;
|
|
21
|
+
instance: ContractInstanceWithAddress;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Cache + in-flight map pair. Lookup hits the cache, then awaits an in-flight `compute()` if one
|
|
26
|
+
* exists, otherwise starts one and stores it. Guarantees `compute()` runs at most once per `key`
|
|
27
|
+
* across concurrent callers, which matters because `computeArtifactHash` is expensive.
|
|
28
|
+
*/
|
|
29
|
+
class AsyncCache<K, V> {
|
|
30
|
+
#cache = new Map<K, V>();
|
|
31
|
+
#inFlight = new Map<K, Promise<V>>();
|
|
32
|
+
|
|
33
|
+
getOrCompute(key: K, compute: () => Promise<V>): Promise<V> {
|
|
34
|
+
const cached = this.#cache.get(key);
|
|
35
|
+
if (cached !== undefined) {
|
|
36
|
+
return Promise.resolve(cached);
|
|
37
|
+
}
|
|
38
|
+
let pending = this.#inFlight.get(key);
|
|
39
|
+
if (!pending) {
|
|
40
|
+
pending = (async () => {
|
|
41
|
+
try {
|
|
42
|
+
const value = await compute();
|
|
43
|
+
this.#cache.set(key, value);
|
|
44
|
+
return value;
|
|
45
|
+
} finally {
|
|
46
|
+
this.#inFlight.delete(key);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
this.#inFlight.set(key, pending);
|
|
50
|
+
}
|
|
51
|
+
return pending;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolves and caches contract artifacts and their associated instances.
|
|
57
|
+
*
|
|
58
|
+
* Artifact hash computation is expensive, so this service deduplicates both completed and in-flight computations.
|
|
59
|
+
* Two cache levels are kept: full deploys (artifact + computed instance) keyed by the full deploy context, and loaded +
|
|
60
|
+
* hashed artifacts keyed by the compiled-bytecode hash so deploys of the same contract with different args / salt /
|
|
61
|
+
* deployer reuse the same `ContractArtifactWithHash`.
|
|
62
|
+
*/
|
|
63
|
+
export class TXEArtifactResolver {
|
|
64
|
+
#deployments = new AsyncCache<string, ResolvedArtifact>();
|
|
65
|
+
#artifacts = new AsyncCache<string, ContractArtifactWithHash>();
|
|
66
|
+
#logger = createLogger('txe:artifact_resolver');
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
private readonly contractStore: ContractStore,
|
|
70
|
+
private readonly schnorrClassId: Fr,
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
/** Resolves the Schnorr account contract artifact and instance for the given secret, caching the result. */
|
|
74
|
+
resolveAccountArtifact(secret: Fr): Promise<ResolvedArtifact> {
|
|
75
|
+
return this.#deployments.getOrCompute(`SchnorrAccountContract-${secret}`, () =>
|
|
76
|
+
this.#computeAccountArtifact(secret),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Resolves a contract artifact from disk by path, computes its instance, and caches the result. */
|
|
81
|
+
async resolveDeployArtifact({
|
|
82
|
+
rootPath,
|
|
83
|
+
packageName,
|
|
84
|
+
contractPath,
|
|
85
|
+
initializer,
|
|
86
|
+
args,
|
|
87
|
+
secret,
|
|
88
|
+
salt,
|
|
89
|
+
deployer,
|
|
90
|
+
}: {
|
|
91
|
+
rootPath: string;
|
|
92
|
+
packageName: string;
|
|
93
|
+
contractPath: string;
|
|
94
|
+
initializer: string;
|
|
95
|
+
args: Fr[];
|
|
96
|
+
secret: Fr;
|
|
97
|
+
salt: Fr;
|
|
98
|
+
deployer: AztecAddress;
|
|
99
|
+
}): Promise<ResolvedArtifact> {
|
|
100
|
+
const publicKeys = secret.equals(Fr.ZERO) ? PublicKeys.default() : (await deriveKeys(secret)).publicKeys;
|
|
101
|
+
const publicKeysHash = await publicKeys.hash();
|
|
102
|
+
|
|
103
|
+
const artifactPath = await this.#resolveArtifactPath(rootPath, packageName, contractPath);
|
|
104
|
+
const fileHash = await this.#fastHashFile(artifactPath);
|
|
105
|
+
|
|
106
|
+
const { dir: contractDirectory, base: contractFilename } = parse(contractPath);
|
|
107
|
+
const cacheKey = `${contractDirectory ?? ''}-${contractFilename}-${initializer}-${args
|
|
108
|
+
.map(arg => arg.toString())
|
|
109
|
+
.join('-')}-${publicKeysHash}-${salt}-${deployer}-${fileHash}`;
|
|
110
|
+
|
|
111
|
+
return this.#deployments.getOrCompute(cacheKey, () =>
|
|
112
|
+
this.#computeDeployArtifact(
|
|
113
|
+
artifactPath,
|
|
114
|
+
fileHash,
|
|
115
|
+
initializer,
|
|
116
|
+
args,
|
|
117
|
+
salt,
|
|
118
|
+
publicKeys,
|
|
119
|
+
publicKeysHash,
|
|
120
|
+
deployer,
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async #resolveArtifactPath(rootPath: string, packageName: string, contractPath: string): Promise<string> {
|
|
126
|
+
const { dir: contractDirectory, base: contractFilename } = parse(contractPath);
|
|
127
|
+
if (contractDirectory) {
|
|
128
|
+
if (contractDirectory.includes('@')) {
|
|
129
|
+
// env.deploy("../path/to/workspace/root@packageName/contractName")
|
|
130
|
+
const [workspace, pkg] = contractDirectory.split('@');
|
|
131
|
+
const targetPath = join(rootPath, workspace, '/target');
|
|
132
|
+
this.#logger.debug(`Looking for compiled artifact in workspace ${targetPath}`);
|
|
133
|
+
return join(targetPath, `${pkg}-${contractFilename}.json`);
|
|
134
|
+
} else {
|
|
135
|
+
// env.deploy("../path/to/contract/root/contractName")
|
|
136
|
+
const targetPath = join(rootPath, contractDirectory, '/target');
|
|
137
|
+
this.#logger.debug(`Looking for compiled artifact in ${targetPath}`);
|
|
138
|
+
const [artifactPath] = (await readdir(targetPath)).filter(file => file.endsWith(`-${contractFilename}.json`));
|
|
139
|
+
return artifactPath;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
// env.deploy("contractName")
|
|
143
|
+
return join(rootPath, './target', `${packageName}-${contractFilename}.json`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async #computeAccountArtifact(secret: Fr): Promise<ResolvedArtifact> {
|
|
148
|
+
const [artifactFromStore, classWithPreimage] = await Promise.all([
|
|
149
|
+
this.contractStore.getContractArtifact(this.schnorrClassId),
|
|
150
|
+
this.contractStore.getContractClassWithPreimage(this.schnorrClassId),
|
|
151
|
+
]);
|
|
152
|
+
if (!artifactFromStore || !classWithPreimage) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`SchnorrAccount not found in shared contract store at class id ${this.schnorrClassId.toString()}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const artifact: ContractArtifactWithHash = { ...artifactFromStore, artifactHash: classWithPreimage.artifactHash };
|
|
158
|
+
const keys = await deriveKeys(secret);
|
|
159
|
+
const args = [keys.publicKeys.ivpkM.x, keys.publicKeys.ivpkM.y];
|
|
160
|
+
const instance = await getContractInstanceFromInstantiationParams(artifact, {
|
|
161
|
+
constructorArgs: args,
|
|
162
|
+
skipArgsDecoding: true,
|
|
163
|
+
salt: Fr.ONE,
|
|
164
|
+
publicKeys: keys.publicKeys,
|
|
165
|
+
constructorArtifact: 'constructor',
|
|
166
|
+
deployer: AztecAddress.ZERO,
|
|
167
|
+
});
|
|
168
|
+
return { artifact, instance };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async #computeDeployArtifact(
|
|
172
|
+
artifactPath: string,
|
|
173
|
+
fileHash: string,
|
|
174
|
+
initializer: string,
|
|
175
|
+
args: Fr[],
|
|
176
|
+
salt: Fr,
|
|
177
|
+
publicKeys: PublicKeys,
|
|
178
|
+
publicKeysHash: Fr,
|
|
179
|
+
deployer: AztecAddress,
|
|
180
|
+
): Promise<ResolvedArtifact> {
|
|
181
|
+
// Inner cache: artifact load + hash depends only on the compiled bytecode (`fileHash`), so subsequent deploys of
|
|
182
|
+
// the same contract — regardless of constructor args / deployer / salt — reuse the same `ContractArtifactWithHash`.
|
|
183
|
+
const artifact = await this.#artifacts.getOrCompute(fileHash, async () => {
|
|
184
|
+
this.#logger.debug(`Loading compiled artifact ${artifactPath}`);
|
|
185
|
+
const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
|
|
186
|
+
const artifactWithoutHash = loadContractArtifact(artifactJSON);
|
|
187
|
+
return { ...artifactWithoutHash, artifactHash: await computeArtifactHash(artifactWithoutHash) };
|
|
188
|
+
});
|
|
189
|
+
this.#logger.debug(
|
|
190
|
+
`Deploy ${artifact.name} with initializer ${initializer}(${args}) and public keys hash ${publicKeysHash}`,
|
|
191
|
+
);
|
|
192
|
+
const instance = await getContractInstanceFromInstantiationParams(artifact, {
|
|
193
|
+
constructorArgs: args,
|
|
194
|
+
skipArgsDecoding: true,
|
|
195
|
+
salt,
|
|
196
|
+
publicKeys,
|
|
197
|
+
constructorArtifact: initializer || undefined,
|
|
198
|
+
deployer,
|
|
199
|
+
});
|
|
200
|
+
return { artifact, instance };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#fastHashFile(path: string): Promise<string> {
|
|
204
|
+
return new Promise(resolve => {
|
|
205
|
+
const fd = createReadStream(path);
|
|
206
|
+
const hash = createHash('sha1');
|
|
207
|
+
hash.setEncoding('hex');
|
|
208
|
+
|
|
209
|
+
fd.on('end', function () {
|
|
210
|
+
hash.end();
|
|
211
|
+
resolve(hash.read() as string);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
fd.pipe(hash);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import type { ContractStore } from '@aztec/pxe/server';
|
|
4
|
+
import { type ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi';
|
|
5
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
+
import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
7
|
+
|
|
8
|
+
export class TXEPublicContractDataSource implements ContractDataSource {
|
|
9
|
+
constructor(
|
|
10
|
+
private blockNumber: BlockNumber,
|
|
11
|
+
private contractStore: ContractStore,
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
getBlockNumber(): Promise<BlockNumber> {
|
|
15
|
+
return Promise.resolve(this.blockNumber);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
|
|
19
|
+
const contractClass = await this.contractStore.getContractClassWithPreimage(id);
|
|
20
|
+
if (!contractClass) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
id: contractClass.id,
|
|
25
|
+
artifactHash: contractClass.artifactHash,
|
|
26
|
+
packedBytecode: contractClass.packedBytecode,
|
|
27
|
+
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
28
|
+
version: contractClass.version,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
|
|
33
|
+
const contractClass = await this.contractStore.getContractClassWithPreimage(id);
|
|
34
|
+
return contractClass?.publicBytecodeCommitment;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
|
|
38
|
+
const instance = await this.contractStore.getContractInstance(address);
|
|
39
|
+
// TXE has no contract updates, so the current class always equals the original.
|
|
40
|
+
return instance && { ...instance, address, currentContractClassId: instance.originalContractClassId };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getContractClassIds(): Promise<Fr[]> {
|
|
44
|
+
throw new Error('Method not implemented.');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
|
|
48
|
+
const instance = await this.getContract(address);
|
|
49
|
+
return instance && this.contractStore.getContractArtifact(instance.originalContractClassId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
|
|
53
|
+
const instance = await this.getContract(address);
|
|
54
|
+
return instance && this.contractStore.getDebugFunctionName(instance.originalContractClassId, selector);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
registerContractFunctionSignatures(_signatures: []): Promise<void> {
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/worker.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BackendType, Barretenberg, BarretenbergSync } from '@aztec/bb.js';
|
|
2
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
|
|
4
|
+
import { parentPort, workerData } from 'node:worker_threads';
|
|
5
|
+
|
|
6
|
+
// Importing `./index.js` registers the msgpackr Fr extension transitively (via
|
|
7
|
+
// ./msgpackr_fr_extension.js); this must happen before any `sendMessage` call.
|
|
8
|
+
import { TXEDispatcher, type TXEDispatcherOptions, type TXEForeignCallInput, activeSessionCount } from './index.js';
|
|
9
|
+
|
|
10
|
+
// Seed both bb.js singletons with the WASM backend before any crypto call. `initSingleton`
|
|
11
|
+
// binds the singleton to whichever backend the first call requests, so this pre-empts the
|
|
12
|
+
// implicit `Barretenberg.initSingleton()` inside `poseidon2Hash` from `@aztec/foundation/crypto`.
|
|
13
|
+
// TXE only needs hashing (no proving, no verification), so WASM is sufficient and
|
|
14
|
+
// `skipSrsInit: true` skips the CRS load. `threads: 1` keeps the WASM backend on a single
|
|
15
|
+
// thread — additional threads would each spawn a nested worker_thread, multiplying memory cost
|
|
16
|
+
// per pool worker.
|
|
17
|
+
void Barretenberg.initSingleton({ backend: BackendType.Wasm, skipSrsInit: true, threads: 1 });
|
|
18
|
+
void BarretenbergSync.initSingleton({ backend: BackendType.Wasm });
|
|
19
|
+
|
|
20
|
+
if (!parentPort) {
|
|
21
|
+
throw new Error('worker.ts must be loaded as a worker_thread');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const port = parentPort;
|
|
25
|
+
const logger: Logger = createLogger('txe:worker');
|
|
26
|
+
|
|
27
|
+
// The pool builds a template LMDB containing the protocol contracts + the SchnorrAccount
|
|
28
|
+
// artifact on the main thread and passes its data dir via `workerData`. The dispatcher clones
|
|
29
|
+
// that LMDB into a per-worker store on first use, so this worker gets a writable copy already
|
|
30
|
+
// populated.
|
|
31
|
+
const dispatcherOpts: TXEDispatcherOptions = {
|
|
32
|
+
contractStoreSourceDir: workerData.contractStoreSourceDir,
|
|
33
|
+
schnorrClassId: workerData.schnorrClassId,
|
|
34
|
+
};
|
|
35
|
+
const dispatcher = new TXEDispatcher(logger, dispatcherOpts);
|
|
36
|
+
|
|
37
|
+
interface ForeignCallRequest {
|
|
38
|
+
type: 'foreign-call';
|
|
39
|
+
requestId: number;
|
|
40
|
+
callData: TXEForeignCallInput;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DisposeSessionMessage {
|
|
44
|
+
type: 'dispose-session';
|
|
45
|
+
sessionId: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type IncomingMessage = ForeignCallRequest | DisposeSessionMessage;
|
|
49
|
+
|
|
50
|
+
interface SerializedError {
|
|
51
|
+
message: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
stack?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function serializeError(err: unknown): SerializedError {
|
|
57
|
+
if (err instanceof Error) {
|
|
58
|
+
return { message: err.message, name: err.name, stack: err.stack };
|
|
59
|
+
}
|
|
60
|
+
return { message: String(err) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Periodic memstat for diagnostic builds — set TXE_WORKER_MEMSTAT=1 to enable. Posts JS-heap
|
|
64
|
+
// breakdown + active-session count back to the dispatcher so we can attribute RSS growth
|
|
65
|
+
// between V8 heap (would show in heapUsed) and native (LMDB / world-state / WASM).
|
|
66
|
+
if (process.env.TXE_WORKER_MEMSTAT === '1') {
|
|
67
|
+
setInterval(() => {
|
|
68
|
+
const m = process.memoryUsage();
|
|
69
|
+
port.postMessage({
|
|
70
|
+
type: 'memstat',
|
|
71
|
+
sessions: activeSessionCount(),
|
|
72
|
+
rss: m.rss,
|
|
73
|
+
heapTotal: m.heapTotal,
|
|
74
|
+
heapUsed: m.heapUsed,
|
|
75
|
+
external: m.external,
|
|
76
|
+
arrayBuffers: m.arrayBuffers,
|
|
77
|
+
});
|
|
78
|
+
}, 2000).unref();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
port.on('message', (msg: IncomingMessage) => {
|
|
82
|
+
switch (msg.type) {
|
|
83
|
+
case 'foreign-call':
|
|
84
|
+
void (async () => {
|
|
85
|
+
try {
|
|
86
|
+
const value = await dispatcher.resolve_foreign_call(msg.callData);
|
|
87
|
+
port.postMessage({ type: 'result', requestId: msg.requestId, ok: true, value });
|
|
88
|
+
} catch (err) {
|
|
89
|
+
port.postMessage({ type: 'result', requestId: msg.requestId, ok: false, error: serializeError(err) });
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
return;
|
|
93
|
+
case 'dispose-session':
|
|
94
|
+
// Fire-and-forget; the main thread does not wait for confirmation.
|
|
95
|
+
void dispatcher.disposeSession(msg.sessionId).catch(err => logger.warn(`disposeSession failed`, err));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
});
|