@aztec/txe 0.0.1-commit.934299a21 → 0.0.1-commit.949a33fd8
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 +8 -6
- package/dest/oracle/interfaces.d.ts +31 -28
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.d.ts +13 -13
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +12 -12
- package/dest/oracle/txe_oracle_top_level_context.d.ts +27 -19
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +72 -42
- package/dest/rpc_translator.d.ts +128 -82
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +458 -170
- package/dest/state_machine/archiver.d.ts +3 -3
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +7 -8
- package/dest/state_machine/dummy_p2p_client.d.ts +3 -2
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +5 -2
- package/dest/state_machine/global_variable_builder.d.ts +9 -4
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +9 -3
- package/dest/state_machine/index.d.ts +4 -2
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +8 -4
- package/dest/state_machine/mock_epoch_cache.d.ts +18 -3
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +35 -2
- package/dest/state_machine/synchronizer.d.ts +5 -5
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +3 -3
- package/dest/txe_session.d.ts +46 -1
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +86 -17
- package/dest/util/encoding.d.ts +71 -1
- package/dest/util/encoding.d.ts.map +1 -1
- package/dest/util/encoding.js +4 -0
- package/dest/util/txe_public_contract_data_source.d.ts +1 -1
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +1 -3
- package/package.json +15 -15
- package/src/index.ts +8 -5
- package/src/oracle/interfaces.ts +28 -32
- package/src/oracle/txe_oracle_public_context.ts +12 -12
- package/src/oracle/txe_oracle_top_level_context.ts +75 -46
- package/src/rpc_translator.ts +532 -204
- package/src/state_machine/archiver.ts +6 -5
- package/src/state_machine/dummy_p2p_client.ts +6 -2
- package/src/state_machine/global_variable_builder.ts +18 -3
- package/src/state_machine/index.ts +9 -2
- package/src/state_machine/mock_epoch_cache.ts +46 -3
- package/src/state_machine/synchronizer.ts +4 -4
- package/src/txe_session.ts +148 -17
- package/src/util/encoding.ts +5 -0
- package/src/util/txe_public_contract_data_source.ts +0 -2
|
@@ -17,7 +17,7 @@ export class TXEArchiver extends ArchiverDataSourceBase {
|
|
|
17
17
|
private readonly updater = new ArchiverDataStoreUpdater(this.store);
|
|
18
18
|
|
|
19
19
|
constructor(db: AztecAsyncKVStore) {
|
|
20
|
-
const store = new KVArchiverDataStore(db, 9999
|
|
20
|
+
const store = new KVArchiverDataStore(db, 9999);
|
|
21
21
|
super(store);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -76,15 +76,16 @@ export class TXEArchiver extends ArchiverDataSourceBase {
|
|
|
76
76
|
proven: tipId,
|
|
77
77
|
finalized: tipId,
|
|
78
78
|
checkpointed: tipId,
|
|
79
|
+
proposedCheckpoint: tipId,
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
public
|
|
83
|
-
throw new Error('TXE Archiver does not implement "
|
|
83
|
+
public getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
|
|
84
|
+
throw new Error('TXE Archiver does not implement "getSyncedL2SlotNumber"');
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
public
|
|
87
|
-
throw new Error('TXE Archiver does not implement "
|
|
87
|
+
public getSyncedL2EpochNumber(): Promise<EpochNumber | undefined> {
|
|
88
|
+
throw new Error('TXE Archiver does not implement "getSyncedL2EpochNumber"');
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
public isEpochComplete(_epochNumber: EpochNumber): Promise<boolean> {
|
|
@@ -61,8 +61,12 @@ export class DummyP2P implements P2P {
|
|
|
61
61
|
throw new Error('DummyP2P does not implement "registerBlockProposalHandler"');
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
public
|
|
65
|
-
throw new Error('DummyP2P does not implement "
|
|
64
|
+
public registerValidatorCheckpointProposalHandler(_handler: P2PCheckpointReceivedCallback): void {
|
|
65
|
+
throw new Error('DummyP2P does not implement "registerValidatorCheckpointProposalHandler"');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public registerAllNodesCheckpointProposalHandler(_handler: P2PCheckpointReceivedCallback): void {
|
|
69
|
+
throw new Error('DummyP2P does not implement "registerAllNodesCheckpointProposalHandler"');
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
public requestTxs(_txHashes: TxHash[]): Promise<(Tx | undefined)[]> {
|
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
import type { SimulationOverridesPlan } from '@aztec/ethereum/contracts';
|
|
1
2
|
import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { times } from '@aztec/foundation/collection';
|
|
2
4
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
5
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
6
|
+
import { FEE_ORACLE_LAG, GasFees } from '@aztec/stdlib/gas';
|
|
5
7
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
6
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type CheckpointGlobalVariables,
|
|
10
|
+
type FeeProvider,
|
|
11
|
+
type GlobalVariableBuilder,
|
|
12
|
+
GlobalVariables,
|
|
13
|
+
} from '@aztec/stdlib/tx';
|
|
7
14
|
|
|
8
|
-
|
|
15
|
+
/** Simple FeeProvider for TXE that returns zero fees. */
|
|
16
|
+
export class TXEFeeProvider implements FeeProvider {
|
|
9
17
|
public getCurrentMinFees(): Promise<GasFees> {
|
|
10
18
|
return Promise.resolve(new GasFees(0, 0));
|
|
11
19
|
}
|
|
12
20
|
|
|
21
|
+
public getPredictedMinFees(): Promise<GasFees[]> {
|
|
22
|
+
return Promise.resolve(times(FEE_ORACLE_LAG, () => new GasFees(0, 0)));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TXEGlobalVariablesBuilder implements GlobalVariableBuilder {
|
|
13
27
|
public buildGlobalVariables(
|
|
14
28
|
_blockNumber: BlockNumber,
|
|
15
29
|
_coinbase: EthAddress,
|
|
@@ -23,6 +37,7 @@ export class TXEGlobalVariablesBuilder implements GlobalVariableBuilder {
|
|
|
23
37
|
_coinbase: EthAddress,
|
|
24
38
|
_feeRecipient: AztecAddress,
|
|
25
39
|
_slotNumber: SlotNumber,
|
|
40
|
+
_simulationOverridesPlan?: SimulationOverridesPlan,
|
|
26
41
|
): Promise<CheckpointGlobalVariables> {
|
|
27
42
|
const vars = makeGlobalVariables();
|
|
28
43
|
return Promise.resolve({
|
|
@@ -4,6 +4,7 @@ import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
|
4
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { type AnchorBlockStore, type ContractStore, ContractSyncService, type NoteStore } from '@aztec/pxe/server';
|
|
7
|
+
import { MessageContextService } from '@aztec/pxe/simulator';
|
|
7
8
|
import { L2Block } from '@aztec/stdlib/block';
|
|
8
9
|
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
9
10
|
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
@@ -12,7 +13,7 @@ import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
|
12
13
|
|
|
13
14
|
import { TXEArchiver } from './archiver.js';
|
|
14
15
|
import { DummyP2P } from './dummy_p2p_client.js';
|
|
15
|
-
import { TXEGlobalVariablesBuilder } from './global_variable_builder.js';
|
|
16
|
+
import { TXEFeeProvider, TXEGlobalVariablesBuilder } from './global_variable_builder.js';
|
|
16
17
|
import { MockEpochCache } from './mock_epoch_cache.js';
|
|
17
18
|
import { TXESynchronizer } from './synchronizer.js';
|
|
18
19
|
|
|
@@ -26,6 +27,7 @@ export class TXEStateMachine {
|
|
|
26
27
|
public archiver: TXEArchiver,
|
|
27
28
|
public anchorBlockStore: AnchorBlockStore,
|
|
28
29
|
public contractSyncService: ContractSyncService,
|
|
30
|
+
public messageContextService: MessageContextService,
|
|
29
31
|
) {}
|
|
30
32
|
|
|
31
33
|
public static async create(
|
|
@@ -51,12 +53,15 @@ export class TXEStateMachine {
|
|
|
51
53
|
undefined,
|
|
52
54
|
undefined,
|
|
53
55
|
undefined,
|
|
56
|
+
undefined,
|
|
54
57
|
VERSION,
|
|
55
58
|
CHAIN_ID,
|
|
56
59
|
new TXEGlobalVariablesBuilder(),
|
|
60
|
+
new TXEFeeProvider(),
|
|
57
61
|
new MockEpochCache(),
|
|
58
62
|
getPackageVersion() ?? '',
|
|
59
63
|
new TestCircuitVerifier(),
|
|
64
|
+
new TestCircuitVerifier(),
|
|
60
65
|
undefined,
|
|
61
66
|
log,
|
|
62
67
|
);
|
|
@@ -68,7 +73,9 @@ export class TXEStateMachine {
|
|
|
68
73
|
createLogger('txe:contract_sync'),
|
|
69
74
|
);
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
const messageContextService = new MessageContextService(node);
|
|
77
|
+
|
|
78
|
+
return new this(node, synchronizer, archiver, anchorBlockStore, contractSyncService, messageContextService);
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
public async handleL2Block(block: L2Block) {
|
|
@@ -8,7 +8,7 @@ import { EmptyL1RollupConstants, type L1RollupConstants } from '@aztec/stdlib/ep
|
|
|
8
8
|
* Since in TXE we don't validate transactions, mock suffices here.
|
|
9
9
|
*/
|
|
10
10
|
export class MockEpochCache implements EpochCacheInterface {
|
|
11
|
-
getCommittee(): Promise<EpochCommitteeInfo> {
|
|
11
|
+
getCommittee(_slot: SlotTag = 'now'): Promise<EpochCommitteeInfo> {
|
|
12
12
|
return Promise.resolve({
|
|
13
13
|
committee: undefined,
|
|
14
14
|
seed: 0n,
|
|
@@ -17,6 +17,22 @@ export class MockEpochCache implements EpochCacheInterface {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
getSlotNow(): SlotNumber {
|
|
21
|
+
return SlotNumber(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getTargetSlot(): SlotNumber {
|
|
25
|
+
return SlotNumber(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getEpochNow(): EpochNumber {
|
|
29
|
+
return EpochNumber.ZERO;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getTargetEpoch(): EpochNumber {
|
|
33
|
+
return EpochNumber.ZERO;
|
|
34
|
+
}
|
|
35
|
+
|
|
20
36
|
getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
|
|
21
37
|
return {
|
|
22
38
|
epoch: EpochNumber.ZERO,
|
|
@@ -26,15 +42,27 @@ export class MockEpochCache implements EpochCacheInterface {
|
|
|
26
42
|
};
|
|
27
43
|
}
|
|
28
44
|
|
|
29
|
-
getEpochAndSlotInNextL1Slot(): EpochAndSlot & {
|
|
45
|
+
getEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint } {
|
|
30
46
|
return {
|
|
31
47
|
epoch: EpochNumber.ZERO,
|
|
32
48
|
slot: SlotNumber(0),
|
|
33
49
|
ts: 0n,
|
|
34
|
-
|
|
50
|
+
nowSeconds: 0n,
|
|
35
51
|
};
|
|
36
52
|
}
|
|
37
53
|
|
|
54
|
+
getTargetEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint } {
|
|
55
|
+
return this.getEpochAndSlotInNextL1Slot();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
isProposerPipeliningEnabled(): boolean {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pipeliningOffset(): number {
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
38
66
|
getProposerIndexEncoding(_epoch: EpochNumber, _slot: SlotNumber, _seed: bigint): `0x${string}` {
|
|
39
67
|
return '0x00';
|
|
40
68
|
}
|
|
@@ -50,6 +78,13 @@ export class MockEpochCache implements EpochCacheInterface {
|
|
|
50
78
|
};
|
|
51
79
|
}
|
|
52
80
|
|
|
81
|
+
getTargetAndNextSlot(): { targetSlot: SlotNumber; nextSlot: SlotNumber } {
|
|
82
|
+
return {
|
|
83
|
+
targetSlot: SlotNumber(0),
|
|
84
|
+
nextSlot: SlotNumber(0),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
53
88
|
getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined> {
|
|
54
89
|
return Promise.resolve(undefined);
|
|
55
90
|
}
|
|
@@ -66,6 +101,14 @@ export class MockEpochCache implements EpochCacheInterface {
|
|
|
66
101
|
return Promise.resolve([]);
|
|
67
102
|
}
|
|
68
103
|
|
|
104
|
+
isEscapeHatchOpen(_epoch: EpochNumber): Promise<boolean> {
|
|
105
|
+
return Promise.resolve(false);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
isEscapeHatchOpenAtSlot(_slot: SlotTag): Promise<boolean> {
|
|
109
|
+
return Promise.resolve(false);
|
|
110
|
+
}
|
|
111
|
+
|
|
69
112
|
getL1Constants(): L1RollupConstants {
|
|
70
113
|
return EmptyL1RollupConstants;
|
|
71
114
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
-
import type { L2Block } from '@aztec/stdlib/block';
|
|
4
|
+
import type { BlockHash, L2Block } from '@aztec/stdlib/block';
|
|
5
5
|
import type {
|
|
6
6
|
MerkleTreeReadOperations,
|
|
7
7
|
MerkleTreeWriteOperations,
|
|
@@ -33,12 +33,12 @@ export class TXESynchronizer implements WorldStateSynchronizer {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Forces an immediate sync to an optionally provided minimum block number
|
|
36
|
+
* Forces an immediate sync to an optionally provided minimum block number.
|
|
37
37
|
* @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
|
|
38
|
-
* @param
|
|
38
|
+
* @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash.
|
|
39
39
|
* @returns A promise that resolves with the block number the world state was synced to
|
|
40
40
|
*/
|
|
41
|
-
public syncImmediate(_minBlockNumber?: BlockNumber,
|
|
41
|
+
public syncImmediate(_minBlockNumber?: BlockNumber, _blockHash?: BlockHash): Promise<BlockNumber> {
|
|
42
42
|
return Promise.resolve(this.blockNumber);
|
|
43
43
|
}
|
|
44
44
|
|
package/src/txe_session.ts
CHANGED
|
@@ -3,10 +3,10 @@ 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 { AccessScopes } from '@aztec/pxe/client/lazy';
|
|
7
6
|
import {
|
|
8
7
|
AddressStore,
|
|
9
8
|
AnchorBlockStore,
|
|
9
|
+
CapsuleService,
|
|
10
10
|
CapsuleStore,
|
|
11
11
|
ContractStore,
|
|
12
12
|
JobCoordinator,
|
|
@@ -42,7 +42,7 @@ import { GasSettings } from '@aztec/stdlib/gas';
|
|
|
42
42
|
import { computeProtocolNullifier } from '@aztec/stdlib/hash';
|
|
43
43
|
import { PrivateContextInputs } from '@aztec/stdlib/kernel';
|
|
44
44
|
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
45
|
-
import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
|
|
45
|
+
import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
|
|
46
46
|
|
|
47
47
|
import { z } from 'zod';
|
|
48
48
|
|
|
@@ -117,6 +117,65 @@ export interface TXESessionStateHandler {
|
|
|
117
117
|
// TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
|
|
118
118
|
cycleJob(): Promise<string>;
|
|
119
119
|
getCurrentJob(): string;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
|
|
123
|
+
*/
|
|
124
|
+
withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
|
|
128
|
+
* oracle handler whenever a contract function emits an offchain message, at any call depth.
|
|
129
|
+
*/
|
|
130
|
+
recordOffchainEffect(data: Fr[]): void;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
|
|
134
|
+
* convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
|
|
135
|
+
* `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
|
|
136
|
+
* unqueried-messages warning doesn't fire on the next reset.
|
|
137
|
+
*/
|
|
138
|
+
getLastCallOffchainEffects(): { effects: Fr[][] };
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
|
|
142
|
+
* block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
|
|
143
|
+
* metadata, not effect consumption.
|
|
144
|
+
*/
|
|
145
|
+
getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
|
|
150
|
+
* call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
|
|
151
|
+
* of whether the call produced offchain effects.
|
|
152
|
+
*/
|
|
153
|
+
interface LastCallState {
|
|
154
|
+
/**
|
|
155
|
+
* Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
|
|
156
|
+
* at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
|
|
157
|
+
*/
|
|
158
|
+
offchainEffects: Fr[][];
|
|
159
|
+
/**
|
|
160
|
+
* Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
|
|
161
|
+
* without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
|
|
162
|
+
* drop delivery.
|
|
163
|
+
*/
|
|
164
|
+
queried: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
|
|
167
|
+
* utility execution). Populated by call executor handlers after execution completes.
|
|
168
|
+
*/
|
|
169
|
+
txHash: Fr;
|
|
170
|
+
/**
|
|
171
|
+
* Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
|
|
172
|
+
* was active when the call started. Populated by call executor handlers after execution completes.
|
|
173
|
+
*/
|
|
174
|
+
anchorBlockTimestamp: bigint;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function emptyLastCallState(): LastCallState {
|
|
178
|
+
return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
|
|
120
179
|
}
|
|
121
180
|
|
|
122
181
|
/**
|
|
@@ -126,6 +185,7 @@ export interface TXESessionStateHandler {
|
|
|
126
185
|
export class TXESession implements TXESessionStateHandler {
|
|
127
186
|
private state: SessionState = { name: 'TOP_LEVEL' };
|
|
128
187
|
private authwits: Map<string, AuthWitness> = new Map();
|
|
188
|
+
private lastCallInfo: LastCallState = emptyLastCallState();
|
|
129
189
|
|
|
130
190
|
constructor(
|
|
131
191
|
private logger: Logger,
|
|
@@ -185,6 +245,8 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
185
245
|
|
|
186
246
|
const initialJobId = jobCoordinator.beginJob();
|
|
187
247
|
|
|
248
|
+
const logger = createLogger('txe:session');
|
|
249
|
+
|
|
188
250
|
const topLevelOracleHandler = new TXEOracleTopLevelContext(
|
|
189
251
|
stateMachine,
|
|
190
252
|
contractStore,
|
|
@@ -202,10 +264,10 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
202
264
|
chainId,
|
|
203
265
|
new Map(),
|
|
204
266
|
);
|
|
205
|
-
await topLevelOracleHandler.
|
|
267
|
+
await topLevelOracleHandler.advanceBlocksBy(1);
|
|
206
268
|
|
|
207
269
|
return new TXESession(
|
|
208
|
-
|
|
270
|
+
logger,
|
|
209
271
|
stateMachine,
|
|
210
272
|
topLevelOracleHandler,
|
|
211
273
|
contractStore,
|
|
@@ -268,6 +330,50 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
268
330
|
return this.currentJobId;
|
|
269
331
|
}
|
|
270
332
|
|
|
333
|
+
private resetLastCall(): void {
|
|
334
|
+
const notQueriedMessageCount = this.lastCallInfo.queried
|
|
335
|
+
? 0
|
|
336
|
+
: this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
|
|
337
|
+
if (notQueriedMessageCount > 0) {
|
|
338
|
+
this.logger.warn(
|
|
339
|
+
`Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
|
|
340
|
+
`To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
|
|
341
|
+
`\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
|
|
342
|
+
`to \`let _ = env.offchain_messages()\` to silence this warning.`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
this.lastCallInfo = emptyLastCallState();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
recordOffchainEffect(data: Fr[]): void {
|
|
349
|
+
this.lastCallInfo.offchainEffects.push(data);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
|
|
353
|
+
this.lastCallInfo.txHash = txHash;
|
|
354
|
+
this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
|
|
358
|
+
this.resetLastCall();
|
|
359
|
+
// Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
|
|
360
|
+
// side effect, and that block's timestamp should not be attributed to this call's anchor.
|
|
361
|
+
const anchorBlockTimestamp = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
|
|
362
|
+
const { result, txHash } = await work();
|
|
363
|
+
this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
getLastCallOffchainEffects(): { effects: Fr[][] } {
|
|
368
|
+
this.lastCallInfo.queried = true;
|
|
369
|
+
return { effects: this.lastCallInfo.offchainEffects };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
|
|
373
|
+
const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
|
|
374
|
+
return { txHash, anchorBlockTimestamp };
|
|
375
|
+
}
|
|
376
|
+
|
|
271
377
|
async enterTopLevelState() {
|
|
272
378
|
switch (this.state.name) {
|
|
273
379
|
case 'PRIVATE': {
|
|
@@ -320,6 +426,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
320
426
|
anchorBlockNumber?: BlockNumber,
|
|
321
427
|
): Promise<PrivateContextInputs> {
|
|
322
428
|
this.exitTopLevelState();
|
|
429
|
+
this.resetLastCall();
|
|
323
430
|
|
|
324
431
|
// Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
|
|
325
432
|
// build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
|
|
@@ -328,7 +435,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
328
435
|
|
|
329
436
|
await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
|
|
330
437
|
contractAddress,
|
|
331
|
-
|
|
438
|
+
await this.keyStore.getAccounts(),
|
|
332
439
|
);
|
|
333
440
|
const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
|
|
334
441
|
|
|
@@ -364,11 +471,14 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
364
471
|
senderTaggingStore: this.senderTaggingStore,
|
|
365
472
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
366
473
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
367
|
-
|
|
474
|
+
capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
|
|
368
475
|
privateEventStore: this.privateEventStore,
|
|
369
476
|
contractSyncService: this.stateMachine.contractSyncService,
|
|
477
|
+
l2TipsStore: this.stateMachine.node,
|
|
370
478
|
jobId: this.currentJobId,
|
|
371
|
-
scopes:
|
|
479
|
+
scopes: await this.keyStore.getAccounts(),
|
|
480
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
481
|
+
simulator: new WASMSimulator(),
|
|
372
482
|
});
|
|
373
483
|
|
|
374
484
|
// We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
|
|
@@ -379,17 +489,22 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
379
489
|
this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
|
|
380
490
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
381
491
|
|
|
492
|
+
// Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
|
|
493
|
+
// via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
|
|
494
|
+
this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
|
|
495
|
+
|
|
382
496
|
return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
|
|
383
497
|
}
|
|
384
498
|
|
|
385
499
|
async enterPublicState(contractAddress?: AztecAddress) {
|
|
386
500
|
this.exitTopLevelState();
|
|
501
|
+
this.resetLastCall();
|
|
387
502
|
|
|
388
503
|
// The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
|
|
389
504
|
// the test. The block therefore gets the *next* block number and timestamp.
|
|
390
|
-
const
|
|
505
|
+
const latestHeader = (await this.stateMachine.node.getBlockHeader('latest'))!;
|
|
391
506
|
const globalVariables = makeGlobalVariables(undefined, {
|
|
392
|
-
blockNumber: BlockNumber(
|
|
507
|
+
blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
|
|
393
508
|
timestamp: this.nextBlockTimestamp,
|
|
394
509
|
version: this.version,
|
|
395
510
|
chainId: this.chainId,
|
|
@@ -404,10 +519,14 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
404
519
|
|
|
405
520
|
this.state = { name: 'PUBLIC' };
|
|
406
521
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
522
|
+
|
|
523
|
+
// Public state is anchored at the latest block.
|
|
524
|
+
this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
|
|
407
525
|
}
|
|
408
526
|
|
|
409
527
|
async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
|
|
410
528
|
this.exitTopLevelState();
|
|
529
|
+
this.resetLastCall();
|
|
411
530
|
|
|
412
531
|
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
413
532
|
|
|
@@ -421,7 +540,7 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
421
540
|
this.stateMachine.node,
|
|
422
541
|
anchorBlockHeader,
|
|
423
542
|
this.currentJobId,
|
|
424
|
-
).syncNoteNullifiers(contractAddress,
|
|
543
|
+
).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
|
|
425
544
|
|
|
426
545
|
this.oracleHandler = new UtilityExecutionOracle({
|
|
427
546
|
contractAddress,
|
|
@@ -435,14 +554,21 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
435
554
|
aztecNode: this.stateMachine.node,
|
|
436
555
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
437
556
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
438
|
-
|
|
557
|
+
capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
|
|
439
558
|
privateEventStore: this.privateEventStore,
|
|
559
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
560
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
561
|
+
l2TipsStore: this.stateMachine.node,
|
|
440
562
|
jobId: this.currentJobId,
|
|
441
|
-
scopes:
|
|
563
|
+
scopes: await this.keyStore.getAccounts(),
|
|
564
|
+
simulator: new WASMSimulator(),
|
|
442
565
|
});
|
|
443
566
|
|
|
444
567
|
this.state = { name: 'UTILITY' };
|
|
445
568
|
this.logger.debug(`Entered state ${this.state.name}`);
|
|
569
|
+
|
|
570
|
+
// Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
|
|
571
|
+
this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
|
|
446
572
|
}
|
|
447
573
|
|
|
448
574
|
private exitTopLevelState() {
|
|
@@ -452,8 +578,8 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
452
578
|
|
|
453
579
|
// Note that while all public and private contexts do is build a single block that we then process when exiting
|
|
454
580
|
// those, the top level context performs a large number of actions not captured in the following 'close' call. Among
|
|
455
|
-
// others, it will create empty blocks (via `
|
|
456
|
-
// `
|
|
581
|
+
// others, it will create empty blocks (via `advanceBlocksBy` and `deploy`), create blocks with transactions via
|
|
582
|
+
// `privateCallNewFlow` and `publicCallNewFlow`, add accounts to PXE via `addAccount`, etc. This is a
|
|
457
583
|
// slight inconsistency in the working model of this class, but is not too bad.
|
|
458
584
|
// TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
|
|
459
585
|
// level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
|
|
@@ -507,13 +633,14 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
507
633
|
}
|
|
508
634
|
|
|
509
635
|
private utilityExecutorForContractSync(anchorBlock: any) {
|
|
510
|
-
return async (call: FunctionCall, scopes:
|
|
636
|
+
return async (call: FunctionCall, scopes: AztecAddress[]) => {
|
|
511
637
|
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
512
638
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
513
639
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
514
640
|
}
|
|
515
641
|
|
|
516
642
|
try {
|
|
643
|
+
const simulator = new WASMSimulator();
|
|
517
644
|
const oracle = new UtilityExecutionOracle({
|
|
518
645
|
contractAddress: call.to,
|
|
519
646
|
authWitnesses: [],
|
|
@@ -526,12 +653,16 @@ export class TXESession implements TXESessionStateHandler {
|
|
|
526
653
|
aztecNode: this.stateMachine.node,
|
|
527
654
|
recipientTaggingStore: this.recipientTaggingStore,
|
|
528
655
|
senderAddressBookStore: this.senderAddressBookStore,
|
|
529
|
-
|
|
656
|
+
capsuleService: new CapsuleService(this.capsuleStore, scopes),
|
|
530
657
|
privateEventStore: this.privateEventStore,
|
|
658
|
+
messageContextService: this.stateMachine.messageContextService,
|
|
659
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
660
|
+
l2TipsStore: this.stateMachine.node,
|
|
531
661
|
jobId: this.currentJobId,
|
|
532
662
|
scopes,
|
|
663
|
+
simulator,
|
|
533
664
|
});
|
|
534
|
-
await
|
|
665
|
+
await simulator
|
|
535
666
|
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
536
667
|
.catch((err: Error) => {
|
|
537
668
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
package/src/util/encoding.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
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
|
}
|
|
@@ -26,8 +26,6 @@ export class TXEPublicContractDataSource implements ContractDataSource {
|
|
|
26
26
|
packedBytecode: contractClass.packedBytecode,
|
|
27
27
|
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
28
28
|
version: contractClass.version,
|
|
29
|
-
privateFunctions: [],
|
|
30
|
-
utilityFunctions: [],
|
|
31
29
|
};
|
|
32
30
|
}
|
|
33
31
|
|