@aztec/txe 4.0.0-nightly.20250907 → 4.0.0-nightly.20260107
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/bin/index.d.ts +1 -1
- package/dest/bin/index.js +1 -1
- package/dest/constants.d.ts +3 -0
- package/dest/constants.d.ts.map +1 -0
- package/dest/constants.js +2 -0
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +8 -4
- package/dest/oracle/interfaces.d.ts +57 -0
- package/dest/oracle/interfaces.d.ts.map +1 -0
- package/dest/oracle/interfaces.js +3 -0
- package/dest/oracle/txe_oracle_public_context.d.ts +12 -12
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +30 -34
- package/dest/oracle/txe_oracle_top_level_context.d.ts +65 -0
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -0
- package/dest/oracle/txe_oracle_top_level_context.js +463 -0
- package/dest/rpc_translator.d.ts +246 -0
- package/dest/rpc_translator.d.ts.map +1 -0
- package/dest/{txe_service/txe_service.js → rpc_translator.js} +201 -124
- package/dest/state_machine/archiver.d.ts +34 -13
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +111 -16
- package/dest/state_machine/dummy_p2p_client.d.ts +5 -2
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +9 -1
- package/dest/state_machine/global_variable_builder.d.ts +5 -16
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +15 -22
- package/dest/state_machine/index.d.ts +5 -5
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +17 -21
- package/dest/state_machine/mock_epoch_cache.d.ts +6 -5
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +8 -7
- package/dest/state_machine/synchronizer.d.ts +5 -4
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +5 -4
- package/dest/txe_session.d.ts +42 -46
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +241 -93
- package/dest/util/encoding.d.ts +623 -24
- package/dest/util/encoding.d.ts.map +1 -1
- package/dest/util/encoding.js +1 -1
- package/dest/util/expected_failure_error.d.ts +1 -1
- package/dest/util/expected_failure_error.d.ts.map +1 -1
- package/dest/util/txe_account_store.d.ts +10 -0
- package/dest/util/txe_account_store.d.ts.map +1 -0
- package/dest/util/{txe_account_data_provider.js → txe_account_store.js} +1 -1
- package/dest/util/txe_contract_store.d.ts +12 -0
- package/dest/util/txe_contract_store.d.ts.map +1 -0
- package/dest/util/{txe_contract_data_provider.js → txe_contract_store.js} +4 -4
- package/dest/util/txe_public_contract_data_source.d.ts +8 -6
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +14 -12
- package/dest/utils/block_creation.d.ts +28 -0
- package/dest/utils/block_creation.d.ts.map +1 -0
- package/dest/utils/block_creation.js +45 -0
- package/dest/utils/tx_effect_creation.d.ts +6 -0
- package/dest/utils/tx_effect_creation.d.ts.map +1 -0
- package/dest/utils/tx_effect_creation.js +16 -0
- package/package.json +18 -17
- package/src/bin/index.ts +1 -1
- package/src/constants.ts +3 -0
- package/src/index.ts +20 -20
- package/src/oracle/interfaces.ts +86 -0
- package/src/oracle/txe_oracle_public_context.ts +37 -75
- package/src/oracle/txe_oracle_top_level_context.ts +716 -0
- package/src/{txe_service/txe_service.ts → rpc_translator.ts} +261 -125
- package/src/state_machine/archiver.ts +147 -29
- package/src/state_machine/dummy_p2p_client.ts +13 -2
- package/src/state_machine/global_variable_builder.ts +24 -41
- package/src/state_machine/index.ts +24 -21
- package/src/state_machine/mock_epoch_cache.ts +12 -11
- package/src/state_machine/synchronizer.ts +8 -7
- package/src/txe_session.ts +416 -115
- package/src/util/encoding.ts +1 -1
- package/src/util/{txe_account_data_provider.ts → txe_account_store.ts} +1 -1
- package/src/util/{txe_contract_data_provider.ts → txe_contract_store.ts} +5 -4
- package/src/util/txe_public_contract_data_source.ts +16 -13
- package/src/utils/block_creation.ts +94 -0
- package/src/utils/tx_effect_creation.ts +38 -0
- package/dest/oracle/txe_oracle.d.ts +0 -124
- package/dest/oracle/txe_oracle.d.ts.map +0 -1
- package/dest/oracle/txe_oracle.js +0 -770
- package/dest/oracle/txe_typed_oracle.d.ts +0 -42
- package/dest/oracle/txe_typed_oracle.d.ts.map +0 -1
- package/dest/oracle/txe_typed_oracle.js +0 -83
- package/dest/txe_constants.d.ts +0 -2
- package/dest/txe_constants.d.ts.map +0 -1
- package/dest/txe_constants.js +0 -7
- package/dest/txe_service/txe_service.d.ts +0 -231
- package/dest/txe_service/txe_service.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 -11
- package/dest/util/txe_contract_data_provider.d.ts.map +0 -1
- package/src/oracle/txe_oracle.ts +0 -1287
- package/src/oracle/txe_typed_oracle.ts +0 -142
- package/src/txe_constants.ts +0 -9
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
|
|
3
|
+
DEFAULT_DA_GAS_LIMIT,
|
|
4
|
+
DEFAULT_L2_GAS_LIMIT,
|
|
5
|
+
DEFAULT_TEARDOWN_DA_GAS_LIMIT,
|
|
6
|
+
DEFAULT_TEARDOWN_L2_GAS_LIMIT,
|
|
7
|
+
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
8
|
+
} from '@aztec/constants';
|
|
9
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
10
|
+
import { Schnorr } from '@aztec/foundation/crypto/schnorr';
|
|
11
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
12
|
+
import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
13
|
+
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
14
|
+
import type { KeyStore } from '@aztec/key-store';
|
|
15
|
+
import {
|
|
16
|
+
AddressStore,
|
|
17
|
+
CapsuleStore,
|
|
18
|
+
NoteStore,
|
|
19
|
+
ORACLE_VERSION,
|
|
20
|
+
PrivateEventStore,
|
|
21
|
+
RecipientTaggingStore,
|
|
22
|
+
SenderAddressBookStore,
|
|
23
|
+
SenderTaggingStore,
|
|
24
|
+
enrichPublicSimulationError,
|
|
25
|
+
} from '@aztec/pxe/server';
|
|
26
|
+
import {
|
|
27
|
+
ExecutionNoteCache,
|
|
28
|
+
ExecutionTaggingIndexCache,
|
|
29
|
+
HashedValuesCache,
|
|
30
|
+
type IMiscOracle,
|
|
31
|
+
Oracle,
|
|
32
|
+
PrivateExecutionOracle,
|
|
33
|
+
UtilityExecutionOracle,
|
|
34
|
+
executePrivateFunction,
|
|
35
|
+
generateSimulatedProvingResult,
|
|
36
|
+
} from '@aztec/pxe/simulator';
|
|
37
|
+
import {
|
|
38
|
+
ExecutionError,
|
|
39
|
+
WASMSimulator,
|
|
40
|
+
createSimulationError,
|
|
41
|
+
extractCallStack,
|
|
42
|
+
resolveAssertionMessageFromError,
|
|
43
|
+
toACVMWitness,
|
|
44
|
+
witnessMapToFields,
|
|
45
|
+
} from '@aztec/simulator/client';
|
|
46
|
+
import {
|
|
47
|
+
CppPublicTxSimulator,
|
|
48
|
+
GuardedMerkleTreeOperations,
|
|
49
|
+
PublicContractsDB,
|
|
50
|
+
PublicProcessor,
|
|
51
|
+
} from '@aztec/simulator/server';
|
|
52
|
+
import { type ContractArtifact, EventSelector, FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
53
|
+
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
54
|
+
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
55
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
56
|
+
import { type ContractInstanceWithAddress, computePartialAddress } from '@aztec/stdlib/contract';
|
|
57
|
+
import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
58
|
+
import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
|
|
59
|
+
import {
|
|
60
|
+
PartialPrivateTailPublicInputsForPublic,
|
|
61
|
+
PrivateKernelTailCircuitPublicInputs,
|
|
62
|
+
PrivateToPublicAccumulatedData,
|
|
63
|
+
PublicCallRequest,
|
|
64
|
+
} from '@aztec/stdlib/kernel';
|
|
65
|
+
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
66
|
+
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
67
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
68
|
+
import {
|
|
69
|
+
CallContext,
|
|
70
|
+
HashedValues,
|
|
71
|
+
PrivateCallExecutionResult,
|
|
72
|
+
PrivateExecutionResult,
|
|
73
|
+
Tx,
|
|
74
|
+
TxConstantData,
|
|
75
|
+
TxContext,
|
|
76
|
+
TxEffect,
|
|
77
|
+
TxHash,
|
|
78
|
+
collectNested,
|
|
79
|
+
} from '@aztec/stdlib/tx';
|
|
80
|
+
import type { UInt64 } from '@aztec/stdlib/types';
|
|
81
|
+
import { ForkCheckpoint } from '@aztec/world-state';
|
|
82
|
+
|
|
83
|
+
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
84
|
+
import type { TXEStateMachine } from '../state_machine/index.js';
|
|
85
|
+
import type { TXEAccountStore } from '../util/txe_account_store.js';
|
|
86
|
+
import type { TXEContractStore } from '../util/txe_contract_store.js';
|
|
87
|
+
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
88
|
+
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
89
|
+
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
90
|
+
|
|
91
|
+
export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
|
|
92
|
+
isMisc = true as const;
|
|
93
|
+
isTxe = true as const;
|
|
94
|
+
|
|
95
|
+
private logger: Logger;
|
|
96
|
+
|
|
97
|
+
constructor(
|
|
98
|
+
private stateMachine: TXEStateMachine,
|
|
99
|
+
private contractStore: TXEContractStore,
|
|
100
|
+
private noteStore: NoteStore,
|
|
101
|
+
private keyStore: KeyStore,
|
|
102
|
+
private addressStore: AddressStore,
|
|
103
|
+
private accountStore: TXEAccountStore,
|
|
104
|
+
private senderTaggingStore: SenderTaggingStore,
|
|
105
|
+
private recipientTaggingStore: RecipientTaggingStore,
|
|
106
|
+
private senderAddressBookStore: SenderAddressBookStore,
|
|
107
|
+
private capsuleStore: CapsuleStore,
|
|
108
|
+
private privateEventStore: PrivateEventStore,
|
|
109
|
+
private nextBlockTimestamp: bigint,
|
|
110
|
+
private version: Fr,
|
|
111
|
+
private chainId: Fr,
|
|
112
|
+
private authwits: Map<string, AuthWitness>,
|
|
113
|
+
) {
|
|
114
|
+
this.logger = createLogger('txe:top_level_context');
|
|
115
|
+
this.logger.debug('Entering Top Level Context');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
utilityAssertCompatibleOracleVersion(version: number): void {
|
|
119
|
+
if (version !== ORACLE_VERSION) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
127
|
+
// setup.
|
|
128
|
+
utilityGetRandomField(): Fr {
|
|
129
|
+
return Fr.random();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
133
|
+
utilityDebugLog(level: number, message: string, fields: Fr[]): void {
|
|
134
|
+
if (!LogLevels[level]) {
|
|
135
|
+
throw new Error(`Invalid debug log level: ${level}`);
|
|
136
|
+
}
|
|
137
|
+
const levelName = LogLevels[level];
|
|
138
|
+
|
|
139
|
+
this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
txeGetDefaultAddress(): AztecAddress {
|
|
143
|
+
return DEFAULT_ADDRESS;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async txeGetNextBlockNumber(): Promise<BlockNumber> {
|
|
147
|
+
return BlockNumber((await this.getLastBlockNumber()) + 1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
txeGetNextBlockTimestamp(): Promise<bigint> {
|
|
151
|
+
return Promise.resolve(this.nextBlockTimestamp);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async txeGetLastBlockTimestamp() {
|
|
155
|
+
return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async txeGetLastTxEffects() {
|
|
159
|
+
const block = await this.stateMachine.archiver.getL2Block('latest');
|
|
160
|
+
|
|
161
|
+
if (block!.body.txEffects.length != 1) {
|
|
162
|
+
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
163
|
+
throw new Error(`Expected a single transaction in the last block, found ${block!.body.txEffects.length}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const txEffects = block!.body.txEffects[0];
|
|
167
|
+
|
|
168
|
+
return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
|
|
172
|
+
return (
|
|
173
|
+
await this.privateEventStore.getPrivateEvents(selector, {
|
|
174
|
+
contractAddress,
|
|
175
|
+
scopes: [scope],
|
|
176
|
+
fromBlock: 0,
|
|
177
|
+
toBlock: (await this.getLastBlockNumber()) + 1,
|
|
178
|
+
})
|
|
179
|
+
).map(e => e.packedEvent);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async txeAdvanceBlocksBy(blocks: number) {
|
|
183
|
+
this.logger.debug(`time traveling ${blocks} blocks`);
|
|
184
|
+
|
|
185
|
+
for (let i = 0; i < blocks; i++) {
|
|
186
|
+
await this.mineBlock();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
txeAdvanceTimestampBy(duration: UInt64) {
|
|
191
|
+
this.logger.debug(`time traveling ${duration} seconds`);
|
|
192
|
+
this.nextBlockTimestamp += duration;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async txeDeploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
|
|
196
|
+
// Emit deployment nullifier
|
|
197
|
+
await this.mineBlock({
|
|
198
|
+
nullifiers: [
|
|
199
|
+
await siloNullifier(
|
|
200
|
+
AztecAddress.fromNumber(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS),
|
|
201
|
+
instance.address.toField(),
|
|
202
|
+
),
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (!secret.equals(Fr.ZERO)) {
|
|
207
|
+
await this.txeAddAccount(artifact, instance, secret);
|
|
208
|
+
} else {
|
|
209
|
+
await this.contractStore.addContractInstance(instance);
|
|
210
|
+
await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
|
|
211
|
+
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async txeAddAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
|
|
216
|
+
const partialAddress = await computePartialAddress(instance);
|
|
217
|
+
|
|
218
|
+
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
219
|
+
await this.contractStore.addContractInstance(instance);
|
|
220
|
+
await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
|
|
221
|
+
|
|
222
|
+
const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
|
|
223
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
224
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
225
|
+
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
226
|
+
|
|
227
|
+
return completeAddress;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async txeCreateAccount(secret: Fr) {
|
|
231
|
+
// This is a foot gun !
|
|
232
|
+
const completeAddress = await this.keyStore.addAccount(secret, secret);
|
|
233
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
234
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
235
|
+
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
236
|
+
|
|
237
|
+
return completeAddress;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async txeAddAuthWitness(address: AztecAddress, messageHash: Fr) {
|
|
241
|
+
const account = await this.accountStore.getAccount(address);
|
|
242
|
+
const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
|
|
243
|
+
|
|
244
|
+
const schnorr = new Schnorr();
|
|
245
|
+
const signature = await schnorr.constructSignature(messageHash.toBuffer(), privateKey);
|
|
246
|
+
|
|
247
|
+
const authWitness = new AuthWitness(messageHash, [...signature.toBuffer()]);
|
|
248
|
+
|
|
249
|
+
this.authwits.set(authWitness.requestHash.toString(), authWitness);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async mineBlock(options: { nullifiers?: Fr[] } = {}) {
|
|
253
|
+
const blockNumber = await this.txeGetNextBlockNumber();
|
|
254
|
+
|
|
255
|
+
const txEffect = TxEffect.empty();
|
|
256
|
+
txEffect.nullifiers = [getSingleTxBlockRequestHash(blockNumber), ...(options.nullifiers ?? [])];
|
|
257
|
+
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
258
|
+
|
|
259
|
+
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
260
|
+
await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
|
|
261
|
+
|
|
262
|
+
const globals = makeGlobalVariables(undefined, {
|
|
263
|
+
blockNumber,
|
|
264
|
+
timestamp: this.nextBlockTimestamp,
|
|
265
|
+
version: this.version,
|
|
266
|
+
chainId: this.chainId,
|
|
267
|
+
});
|
|
268
|
+
const block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
269
|
+
|
|
270
|
+
await forkedWorldTrees.close();
|
|
271
|
+
|
|
272
|
+
this.logger.info(`Created block ${blockNumber} with timestamp ${block.header.globalVariables.timestamp}`);
|
|
273
|
+
|
|
274
|
+
await this.stateMachine.handleL2Block(block);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async txePrivateCallNewFlow(
|
|
278
|
+
from: AztecAddress,
|
|
279
|
+
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
280
|
+
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
281
|
+
args: Fr[],
|
|
282
|
+
argsHash: Fr = Fr.zero(),
|
|
283
|
+
isStaticCall: boolean = false,
|
|
284
|
+
) {
|
|
285
|
+
this.logger.verbose(
|
|
286
|
+
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
290
|
+
if (!artifact) {
|
|
291
|
+
const message = functionSelector.equals(await FunctionSelector.fromSignature('verify_private_authwit(Field)'))
|
|
292
|
+
? 'Found no account contract artifact for a private authwit check - use `create_contract_account` instead of `create_light_account` for authwit support.'
|
|
293
|
+
: 'Function Artifact does not exist';
|
|
294
|
+
throw new Error(message);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Sync notes before executing private function to discover notes from previous transactions
|
|
298
|
+
const utilityExecutor = async (call: FunctionCall) => {
|
|
299
|
+
await this.executeUtilityCall(call);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
await this.contractStore.syncPrivateState(targetContractAddress, functionSelector, utilityExecutor);
|
|
303
|
+
|
|
304
|
+
const blockNumber = await this.txeGetNextBlockNumber();
|
|
305
|
+
|
|
306
|
+
const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
|
|
307
|
+
|
|
308
|
+
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
309
|
+
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
310
|
+
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
311
|
+
|
|
312
|
+
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
313
|
+
|
|
314
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
315
|
+
|
|
316
|
+
const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
|
|
317
|
+
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
318
|
+
const taggingIndexCache = new ExecutionTaggingIndexCache();
|
|
319
|
+
|
|
320
|
+
const simulator = new WASMSimulator();
|
|
321
|
+
|
|
322
|
+
const privateExecutionOracle = new PrivateExecutionOracle(
|
|
323
|
+
argsHash,
|
|
324
|
+
txContext,
|
|
325
|
+
callContext,
|
|
326
|
+
/** Header of a block whose state is used during private execution (not the block the transaction is included in). */
|
|
327
|
+
blockHeader,
|
|
328
|
+
utilityExecutor,
|
|
329
|
+
/** List of transient auth witnesses to be used during this simulation */
|
|
330
|
+
Array.from(this.authwits.values()),
|
|
331
|
+
/** List of transient auth witnesses to be used during this simulation */
|
|
332
|
+
[],
|
|
333
|
+
HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
334
|
+
noteCache,
|
|
335
|
+
taggingIndexCache,
|
|
336
|
+
this.contractStore,
|
|
337
|
+
this.noteStore,
|
|
338
|
+
this.keyStore,
|
|
339
|
+
this.addressStore,
|
|
340
|
+
this.stateMachine.node,
|
|
341
|
+
this.stateMachine.anchorBlockStore,
|
|
342
|
+
this.senderTaggingStore,
|
|
343
|
+
this.recipientTaggingStore,
|
|
344
|
+
this.senderAddressBookStore,
|
|
345
|
+
this.capsuleStore,
|
|
346
|
+
this.privateEventStore,
|
|
347
|
+
0,
|
|
348
|
+
1,
|
|
349
|
+
undefined, // log
|
|
350
|
+
undefined, // scopes
|
|
351
|
+
/**
|
|
352
|
+
* In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
353
|
+
* contract would perform, including setting senderForTags.
|
|
354
|
+
*/
|
|
355
|
+
from,
|
|
356
|
+
simulator,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
|
|
360
|
+
let result: PrivateExecutionResult;
|
|
361
|
+
let executionResult: PrivateCallExecutionResult;
|
|
362
|
+
try {
|
|
363
|
+
executionResult = await executePrivateFunction(
|
|
364
|
+
simulator,
|
|
365
|
+
privateExecutionOracle,
|
|
366
|
+
artifact,
|
|
367
|
+
targetContractAddress,
|
|
368
|
+
functionSelector,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
const publicCallRequests = collectNested([executionResult], r =>
|
|
372
|
+
r.publicInputs.publicCallRequests
|
|
373
|
+
.getActiveItems()
|
|
374
|
+
.map(r => r.inner)
|
|
375
|
+
.concat(r.publicInputs.publicTeardownCallRequest.isEmpty() ? [] : [r.publicInputs.publicTeardownCallRequest]),
|
|
376
|
+
);
|
|
377
|
+
const publicFunctionsCalldata = await Promise.all(
|
|
378
|
+
publicCallRequests.map(async r => {
|
|
379
|
+
const calldata = await privateExecutionOracle.privateLoadFromExecutionCache(r.calldataHash);
|
|
380
|
+
return new HashedValues(calldata, r.calldataHash);
|
|
381
|
+
}),
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// TXE's top level context does not track side effect counters, and as such, minRevertibleSideEffectCounter is always 0.
|
|
385
|
+
// This has the unfortunate consequence of always producing revertible nullifiers, which means we
|
|
386
|
+
// must set the firstNullifierHint to Fr.ZERO so the txRequestHash is always used as nonce generator
|
|
387
|
+
result = new PrivateExecutionResult(executionResult, Fr.ZERO, publicFunctionsCalldata);
|
|
388
|
+
} catch (err) {
|
|
389
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// According to the protocol rules, the nonce generator for the note hashes
|
|
393
|
+
// can either be the first nullifier in the tx or the hash of the initial tx request
|
|
394
|
+
// if there are none.
|
|
395
|
+
const nonceGenerator = result.firstNullifier.equals(Fr.ZERO) ? protocolNullifier : result.firstNullifier;
|
|
396
|
+
const { publicInputs } = await generateSimulatedProvingResult(result, nonceGenerator, this.contractStore);
|
|
397
|
+
|
|
398
|
+
const globals = makeGlobalVariables();
|
|
399
|
+
globals.blockNumber = blockNumber;
|
|
400
|
+
globals.timestamp = this.nextBlockTimestamp;
|
|
401
|
+
globals.chainId = this.chainId;
|
|
402
|
+
globals.version = this.version;
|
|
403
|
+
globals.gasFees = GasFees.empty();
|
|
404
|
+
|
|
405
|
+
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
406
|
+
|
|
407
|
+
const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
|
|
408
|
+
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
409
|
+
const config = PublicSimulatorConfig.from({
|
|
410
|
+
skipFeeEnforcement: true,
|
|
411
|
+
collectDebugLogs: true,
|
|
412
|
+
collectHints: false,
|
|
413
|
+
collectStatistics: false,
|
|
414
|
+
collectCallMetadata: true,
|
|
415
|
+
});
|
|
416
|
+
const processor = new PublicProcessor(
|
|
417
|
+
globals,
|
|
418
|
+
guardedMerkleTrees,
|
|
419
|
+
contractsDB,
|
|
420
|
+
new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config),
|
|
421
|
+
new TestDateProvider(),
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const tx = await Tx.create({
|
|
425
|
+
data: publicInputs,
|
|
426
|
+
chonkProof: ChonkProof.empty(),
|
|
427
|
+
contractClassLogFields: [],
|
|
428
|
+
publicFunctionCalldata: result.publicFunctionCalldata,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
let checkpoint;
|
|
432
|
+
if (isStaticCall) {
|
|
433
|
+
checkpoint = await ForkCheckpoint.new(forkedWorldTrees);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const results = await processor.process([tx]);
|
|
437
|
+
|
|
438
|
+
const [processedTx] = results[0];
|
|
439
|
+
const failedTxs = results[1];
|
|
440
|
+
|
|
441
|
+
if (failedTxs.length !== 0) {
|
|
442
|
+
throw new Error(`Public execution has failed: ${failedTxs[0].error}`);
|
|
443
|
+
} else if (!processedTx.revertCode.isOK()) {
|
|
444
|
+
if (processedTx.revertReason) {
|
|
445
|
+
try {
|
|
446
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
447
|
+
// eslint-disable-next-line no-empty
|
|
448
|
+
} catch {}
|
|
449
|
+
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
450
|
+
} else {
|
|
451
|
+
throw new Error('Contract execution has reverted');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (isStaticCall) {
|
|
456
|
+
await checkpoint!.revert();
|
|
457
|
+
|
|
458
|
+
await forkedWorldTrees.close();
|
|
459
|
+
return executionResult.returnValues ?? [];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const txEffect = TxEffect.empty();
|
|
463
|
+
|
|
464
|
+
txEffect.noteHashes = processedTx!.txEffect.noteHashes;
|
|
465
|
+
txEffect.nullifiers = processedTx!.txEffect.nullifiers;
|
|
466
|
+
txEffect.privateLogs = processedTx!.txEffect.privateLogs;
|
|
467
|
+
txEffect.publicLogs = processedTx!.txEffect.publicLogs;
|
|
468
|
+
txEffect.publicDataWrites = processedTx!.txEffect.publicDataWrites;
|
|
469
|
+
|
|
470
|
+
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
471
|
+
|
|
472
|
+
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
473
|
+
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
474
|
+
|
|
475
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
476
|
+
|
|
477
|
+
await this.stateMachine.handleL2Block(l2Block);
|
|
478
|
+
|
|
479
|
+
await forkedWorldTrees.close();
|
|
480
|
+
|
|
481
|
+
return executionResult.returnValues ?? [];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async txePublicCallNewFlow(
|
|
485
|
+
from: AztecAddress,
|
|
486
|
+
targetContractAddress: AztecAddress,
|
|
487
|
+
calldata: Fr[],
|
|
488
|
+
isStaticCall: boolean,
|
|
489
|
+
) {
|
|
490
|
+
this.logger.verbose(
|
|
491
|
+
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const blockNumber = await this.txeGetNextBlockNumber();
|
|
495
|
+
|
|
496
|
+
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
497
|
+
|
|
498
|
+
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
499
|
+
|
|
500
|
+
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
501
|
+
|
|
502
|
+
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
503
|
+
|
|
504
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
505
|
+
|
|
506
|
+
const calldataHash = await computeCalldataHash(calldata);
|
|
507
|
+
const calldataHashedValues = new HashedValues(calldata, calldataHash);
|
|
508
|
+
|
|
509
|
+
const globals = makeGlobalVariables();
|
|
510
|
+
globals.blockNumber = blockNumber;
|
|
511
|
+
globals.timestamp = this.nextBlockTimestamp;
|
|
512
|
+
globals.chainId = this.chainId;
|
|
513
|
+
globals.version = this.version;
|
|
514
|
+
globals.gasFees = GasFees.empty();
|
|
515
|
+
|
|
516
|
+
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
517
|
+
|
|
518
|
+
const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
|
|
519
|
+
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
520
|
+
const config = PublicSimulatorConfig.from({
|
|
521
|
+
skipFeeEnforcement: true,
|
|
522
|
+
collectDebugLogs: true,
|
|
523
|
+
collectHints: false,
|
|
524
|
+
collectStatistics: false,
|
|
525
|
+
collectCallMetadata: true,
|
|
526
|
+
});
|
|
527
|
+
const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config);
|
|
528
|
+
const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider());
|
|
529
|
+
|
|
530
|
+
// We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
|
|
531
|
+
// kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
|
|
532
|
+
// side-effect, which the AVM then expects to exist in order to use it as the nonce generator when siloing notes as
|
|
533
|
+
// unique.
|
|
534
|
+
const nonRevertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
535
|
+
nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
|
|
536
|
+
|
|
537
|
+
// The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
|
|
538
|
+
// may require producing reverts.
|
|
539
|
+
const revertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
540
|
+
revertibleAccumulatedData.publicCallRequests[0] = new PublicCallRequest(
|
|
541
|
+
from,
|
|
542
|
+
targetContractAddress,
|
|
543
|
+
isStaticCall,
|
|
544
|
+
calldataHash,
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
const inputsForPublic = new PartialPrivateTailPublicInputsForPublic(
|
|
548
|
+
nonRevertibleAccumulatedData,
|
|
549
|
+
revertibleAccumulatedData,
|
|
550
|
+
PublicCallRequest.empty(),
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
const constantData = new TxConstantData(anchorBlockHeader, txContext, Fr.zero(), Fr.zero());
|
|
554
|
+
|
|
555
|
+
const txData = new PrivateKernelTailCircuitPublicInputs(
|
|
556
|
+
constantData,
|
|
557
|
+
/*gasUsed=*/ new Gas(0, 0),
|
|
558
|
+
/*feePayer=*/ AztecAddress.zero(),
|
|
559
|
+
/*includeByTimestamp=*/ 0n,
|
|
560
|
+
inputsForPublic,
|
|
561
|
+
undefined,
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
const tx = await Tx.create({
|
|
565
|
+
data: txData,
|
|
566
|
+
chonkProof: ChonkProof.empty(),
|
|
567
|
+
contractClassLogFields: [],
|
|
568
|
+
publicFunctionCalldata: [calldataHashedValues],
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
let checkpoint;
|
|
572
|
+
if (isStaticCall) {
|
|
573
|
+
checkpoint = await ForkCheckpoint.new(forkedWorldTrees);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const results = await processor.process([tx]);
|
|
577
|
+
|
|
578
|
+
const [processedTx] = results[0];
|
|
579
|
+
const failedTxs = results[1];
|
|
580
|
+
|
|
581
|
+
if (failedTxs.length !== 0) {
|
|
582
|
+
throw new Error(`Public execution has failed: ${failedTxs[0].error}`);
|
|
583
|
+
} else if (!processedTx.revertCode.isOK()) {
|
|
584
|
+
if (processedTx.revertReason) {
|
|
585
|
+
try {
|
|
586
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
587
|
+
// eslint-disable-next-line no-empty
|
|
588
|
+
} catch {}
|
|
589
|
+
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
590
|
+
} else {
|
|
591
|
+
throw new Error('Contract execution has reverted');
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const returnValues = results[3][0].values;
|
|
596
|
+
|
|
597
|
+
if (isStaticCall) {
|
|
598
|
+
await checkpoint!.revert();
|
|
599
|
+
|
|
600
|
+
await forkedWorldTrees.close();
|
|
601
|
+
|
|
602
|
+
return returnValues ?? [];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const txEffect = TxEffect.empty();
|
|
606
|
+
|
|
607
|
+
txEffect.noteHashes = processedTx!.txEffect.noteHashes;
|
|
608
|
+
txEffect.nullifiers = processedTx!.txEffect.nullifiers;
|
|
609
|
+
txEffect.privateLogs = processedTx!.txEffect.privateLogs;
|
|
610
|
+
txEffect.publicLogs = processedTx!.txEffect.publicLogs;
|
|
611
|
+
txEffect.publicDataWrites = processedTx!.txEffect.publicDataWrites;
|
|
612
|
+
|
|
613
|
+
txEffect.txHash = new TxHash(new Fr(blockNumber));
|
|
614
|
+
|
|
615
|
+
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
616
|
+
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
617
|
+
|
|
618
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
619
|
+
|
|
620
|
+
await this.stateMachine.handleL2Block(l2Block);
|
|
621
|
+
|
|
622
|
+
await forkedWorldTrees.close();
|
|
623
|
+
|
|
624
|
+
return returnValues ?? [];
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async txeSimulateUtilityFunction(
|
|
628
|
+
targetContractAddress: AztecAddress,
|
|
629
|
+
functionSelector: FunctionSelector,
|
|
630
|
+
args: Fr[],
|
|
631
|
+
) {
|
|
632
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
633
|
+
if (!artifact) {
|
|
634
|
+
throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Sync notes before executing utility function to discover notes from previous transactions
|
|
638
|
+
await this.contractStore.syncPrivateState(targetContractAddress, functionSelector, async call => {
|
|
639
|
+
await this.executeUtilityCall(call);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
const call = new FunctionCall(
|
|
643
|
+
artifact.name,
|
|
644
|
+
targetContractAddress,
|
|
645
|
+
functionSelector,
|
|
646
|
+
FunctionType.UTILITY,
|
|
647
|
+
false,
|
|
648
|
+
false,
|
|
649
|
+
args,
|
|
650
|
+
[],
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
return this.executeUtilityCall(call);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
|
|
657
|
+
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
658
|
+
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
659
|
+
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
this.logger.verbose(`Executing utility function ${entryPointArtifact.name}`, {
|
|
663
|
+
contract: call.to,
|
|
664
|
+
selector: call.selector,
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
try {
|
|
668
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
669
|
+
const oracle = new UtilityExecutionOracle(
|
|
670
|
+
call.to,
|
|
671
|
+
[],
|
|
672
|
+
[],
|
|
673
|
+
anchorBlockHeader,
|
|
674
|
+
this.contractStore,
|
|
675
|
+
this.noteStore,
|
|
676
|
+
this.keyStore,
|
|
677
|
+
this.addressStore,
|
|
678
|
+
this.stateMachine.node,
|
|
679
|
+
this.stateMachine.anchorBlockStore,
|
|
680
|
+
this.recipientTaggingStore,
|
|
681
|
+
this.senderAddressBookStore,
|
|
682
|
+
this.capsuleStore,
|
|
683
|
+
this.privateEventStore,
|
|
684
|
+
);
|
|
685
|
+
const acirExecutionResult = await new WASMSimulator()
|
|
686
|
+
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
687
|
+
.catch((err: Error) => {
|
|
688
|
+
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
689
|
+
throw new ExecutionError(
|
|
690
|
+
err.message,
|
|
691
|
+
{
|
|
692
|
+
contractAddress: call.to,
|
|
693
|
+
functionSelector: call.selector,
|
|
694
|
+
},
|
|
695
|
+
extractCallStack(err, entryPointArtifact.debug),
|
|
696
|
+
{ cause: err },
|
|
697
|
+
);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
|
|
701
|
+
return witnessMapToFields(acirExecutionResult.returnWitness);
|
|
702
|
+
} catch (err) {
|
|
703
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility simulation'));
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
close(): [bigint, Map<string, AuthWitness>] {
|
|
708
|
+
this.logger.debug('Exiting Top Level Context');
|
|
709
|
+
return [this.nextBlockTimestamp, this.authwits];
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
private async getLastBlockNumber(): Promise<BlockNumber> {
|
|
713
|
+
const header = await this.stateMachine.node.getBlockHeader('latest');
|
|
714
|
+
return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
715
|
+
}
|
|
716
|
+
}
|