@aztec/txe 0.0.1-commit.fce3e4f → 0.0.1-commit.ffe5b04ea
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/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 +91 -56
- package/dest/oracle/interfaces.d.ts +33 -29
- package/dest/oracle/interfaces.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.d.ts +16 -16
- package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_public_context.js +20 -22
- package/dest/oracle/txe_oracle_top_level_context.d.ts +36 -27
- package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
- package/dest/oracle/txe_oracle_top_level_context.js +206 -98
- package/dest/rpc_translator.d.ts +91 -79
- package/dest/rpc_translator.d.ts.map +1 -1
- package/dest/rpc_translator.js +330 -170
- package/dest/state_machine/archiver.d.ts +21 -52
- package/dest/state_machine/archiver.d.ts.map +1 -1
- package/dest/state_machine/archiver.js +63 -94
- package/dest/state_machine/dummy_p2p_client.d.ts +20 -15
- package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
- package/dest/state_machine/dummy_p2p_client.js +42 -25
- package/dest/state_machine/global_variable_builder.d.ts +6 -5
- package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
- package/dest/state_machine/global_variable_builder.js +13 -1
- package/dest/state_machine/index.d.ts +7 -7
- package/dest/state_machine/index.d.ts.map +1 -1
- package/dest/state_machine/index.js +40 -23
- package/dest/state_machine/mock_epoch_cache.d.ts +9 -6
- package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
- package/dest/state_machine/mock_epoch_cache.js +14 -7
- package/dest/state_machine/synchronizer.d.ts +3 -2
- package/dest/state_machine/synchronizer.d.ts.map +1 -1
- package/dest/state_machine/synchronizer.js +5 -4
- package/dest/txe_session.d.ts +26 -15
- package/dest/txe_session.d.ts.map +1 -1
- package/dest/txe_session.js +161 -55
- package/dest/util/encoding.d.ts +618 -19
- package/dest/util/encoding.d.ts.map +1 -1
- package/dest/util/encoding.js +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_public_contract_data_source.d.ts +8 -8
- package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
- package/dest/util/txe_public_contract_data_source.js +12 -29
- package/dest/utils/block_creation.d.ts +21 -6
- package/dest/utils/block_creation.d.ts.map +1 -1
- package/dest/utils/block_creation.js +38 -4
- package/dest/utils/tx_effect_creation.d.ts +3 -3
- package/dest/utils/tx_effect_creation.d.ts.map +1 -1
- package/dest/utils/tx_effect_creation.js +4 -7
- package/package.json +18 -18
- package/src/constants.ts +3 -0
- package/src/index.ts +103 -63
- package/src/oracle/interfaces.ts +36 -32
- package/src/oracle/txe_oracle_public_context.ts +21 -28
- package/src/oracle/txe_oracle_top_level_context.ts +251 -138
- package/src/rpc_translator.ts +368 -174
- package/src/state_machine/archiver.ts +59 -114
- package/src/state_machine/dummy_p2p_client.ts +58 -33
- package/src/state_machine/global_variable_builder.ts +21 -4
- package/src/state_machine/index.ts +60 -21
- package/src/state_machine/mock_epoch_cache.ts +15 -11
- package/src/state_machine/synchronizer.ts +6 -5
- package/src/txe_session.ts +222 -103
- 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_public_contract_data_source.ts +20 -45
- package/src/utils/block_creation.ts +49 -15
- package/src/utils/tx_effect_creation.ts +5 -12
- 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/src/util/txe_contract_data_provider.ts +0 -36
|
@@ -6,15 +6,23 @@ import {
|
|
|
6
6
|
DEFAULT_TEARDOWN_L2_GAS_LIMIT,
|
|
7
7
|
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
|
|
8
8
|
} from '@aztec/constants';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
10
|
+
import { Schnorr } from '@aztec/foundation/crypto/schnorr';
|
|
11
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
11
12
|
import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
|
|
12
13
|
import { TestDateProvider } from '@aztec/foundation/timer';
|
|
13
14
|
import type { KeyStore } from '@aztec/key-store';
|
|
15
|
+
import type { AccessScopes } from '@aztec/pxe/client/lazy';
|
|
14
16
|
import {
|
|
15
|
-
|
|
17
|
+
AddressStore,
|
|
18
|
+
CapsuleStore,
|
|
19
|
+
type ContractStore,
|
|
20
|
+
NoteStore,
|
|
16
21
|
ORACLE_VERSION,
|
|
17
|
-
|
|
22
|
+
PrivateEventStore,
|
|
23
|
+
RecipientTaggingStore,
|
|
24
|
+
SenderAddressBookStore,
|
|
25
|
+
SenderTaggingStore,
|
|
18
26
|
enrichPublicSimulationError,
|
|
19
27
|
} from '@aztec/pxe/server';
|
|
20
28
|
import {
|
|
@@ -38,16 +46,15 @@ import {
|
|
|
38
46
|
witnessMapToFields,
|
|
39
47
|
} from '@aztec/simulator/client';
|
|
40
48
|
import {
|
|
49
|
+
CppPublicTxSimulator,
|
|
41
50
|
GuardedMerkleTreeOperations,
|
|
42
51
|
PublicContractsDB,
|
|
43
52
|
PublicProcessor,
|
|
44
|
-
PublicTxSimulator,
|
|
45
53
|
} from '@aztec/simulator/server';
|
|
46
|
-
import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
54
|
+
import { type ContractArtifact, EventSelector, FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
47
55
|
import { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
48
56
|
import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
49
57
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
50
|
-
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
51
58
|
import { type ContractInstanceWithAddress, computePartialAddress } from '@aztec/stdlib/contract';
|
|
52
59
|
import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
53
60
|
import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
|
|
@@ -58,7 +65,7 @@ import {
|
|
|
58
65
|
PublicCallRequest,
|
|
59
66
|
} from '@aztec/stdlib/kernel';
|
|
60
67
|
import { ChonkProof } from '@aztec/stdlib/proofs';
|
|
61
|
-
import {
|
|
68
|
+
import { makeGlobalVariables } from '@aztec/stdlib/testing';
|
|
62
69
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
63
70
|
import {
|
|
64
71
|
CallContext,
|
|
@@ -75,15 +82,11 @@ import {
|
|
|
75
82
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
76
83
|
import { ForkCheckpoint } from '@aztec/world-state';
|
|
77
84
|
|
|
85
|
+
import { DEFAULT_ADDRESS } from '../constants.js';
|
|
78
86
|
import type { TXEStateMachine } from '../state_machine/index.js';
|
|
79
|
-
import type {
|
|
80
|
-
import type { TXEContractDataProvider } from '../util/txe_contract_data_provider.js';
|
|
87
|
+
import type { TXEAccountStore } from '../util/txe_account_store.js';
|
|
81
88
|
import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
|
|
82
|
-
import {
|
|
83
|
-
getSingleTxBlockRequestHash,
|
|
84
|
-
insertTxEffectIntoWorldTrees,
|
|
85
|
-
makeTXEBlockHeader,
|
|
86
|
-
} from '../utils/block_creation.js';
|
|
89
|
+
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
|
|
87
90
|
import type { ITxeExecutionOracle } from './interfaces.js';
|
|
88
91
|
|
|
89
92
|
export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracle {
|
|
@@ -94,11 +97,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
94
97
|
|
|
95
98
|
constructor(
|
|
96
99
|
private stateMachine: TXEStateMachine,
|
|
97
|
-
private
|
|
100
|
+
private contractStore: ContractStore,
|
|
101
|
+
private noteStore: NoteStore,
|
|
98
102
|
private keyStore: KeyStore,
|
|
99
|
-
private
|
|
100
|
-
private
|
|
101
|
-
private
|
|
103
|
+
private addressStore: AddressStore,
|
|
104
|
+
private accountStore: TXEAccountStore,
|
|
105
|
+
private senderTaggingStore: SenderTaggingStore,
|
|
106
|
+
private recipientTaggingStore: RecipientTaggingStore,
|
|
107
|
+
private senderAddressBookStore: SenderAddressBookStore,
|
|
108
|
+
private capsuleStore: CapsuleStore,
|
|
109
|
+
private privateEventStore: PrivateEventStore,
|
|
102
110
|
private nextBlockTimestamp: bigint,
|
|
103
111
|
private version: Fr,
|
|
104
112
|
private chainId: Fr,
|
|
@@ -108,7 +116,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
108
116
|
this.logger.debug('Entering Top Level Context');
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
assertCompatibleOracleVersion(version: number): void {
|
|
112
120
|
if (version !== ORACLE_VERSION) {
|
|
113
121
|
throw new Error(
|
|
114
122
|
`Incompatible oracle version. TXE is using version '${ORACLE_VERSION}', but got a request for '${version}'.`,
|
|
@@ -118,34 +126,40 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
118
126
|
|
|
119
127
|
// This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
|
|
120
128
|
// setup.
|
|
121
|
-
|
|
129
|
+
getRandomField(): Fr {
|
|
122
130
|
return Fr.random();
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
// We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
|
|
126
|
-
|
|
134
|
+
log(level: number, message: string, fields: Fr[]): Promise<void> {
|
|
127
135
|
if (!LogLevels[level]) {
|
|
128
|
-
throw new Error(`Invalid
|
|
136
|
+
throw new Error(`Invalid log level: ${level}`);
|
|
129
137
|
}
|
|
130
138
|
const levelName = LogLevels[level];
|
|
131
139
|
|
|
132
140
|
this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
|
|
141
|
+
return Promise.resolve();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getDefaultAddress(): AztecAddress {
|
|
145
|
+
return DEFAULT_ADDRESS;
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
async
|
|
136
|
-
return (await this.getLastBlockNumber()) + 1;
|
|
148
|
+
async getNextBlockNumber(): Promise<BlockNumber> {
|
|
149
|
+
return BlockNumber((await this.getLastBlockNumber()) + 1);
|
|
137
150
|
}
|
|
138
151
|
|
|
139
|
-
|
|
152
|
+
getNextBlockTimestamp(): Promise<bigint> {
|
|
140
153
|
return Promise.resolve(this.nextBlockTimestamp);
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
async
|
|
156
|
+
async getLastBlockTimestamp() {
|
|
144
157
|
return (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
|
|
145
158
|
}
|
|
146
159
|
|
|
147
|
-
async
|
|
148
|
-
const
|
|
160
|
+
async getLastTxEffects() {
|
|
161
|
+
const latestBlockNumber = await this.stateMachine.archiver.getBlockNumber();
|
|
162
|
+
const block = await this.stateMachine.archiver.getBlock(latestBlockNumber);
|
|
149
163
|
|
|
150
164
|
if (block!.body.txEffects.length != 1) {
|
|
151
165
|
// Note that calls like env.mine() will result in blocks with no transactions, hitting this
|
|
@@ -157,7 +171,37 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
157
171
|
return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
|
|
158
172
|
}
|
|
159
173
|
|
|
160
|
-
async
|
|
174
|
+
async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
|
|
175
|
+
if (contractAddress.equals(DEFAULT_ADDRESS)) {
|
|
176
|
+
this.logger.debug(`Skipping sync in getPrivateEvents because the events correspond to the default address.`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
181
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(
|
|
182
|
+
contractAddress,
|
|
183
|
+
null,
|
|
184
|
+
async (call, execScopes) => {
|
|
185
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
186
|
+
},
|
|
187
|
+
blockHeader,
|
|
188
|
+
jobId,
|
|
189
|
+
[scope],
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async getPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
|
|
194
|
+
return (
|
|
195
|
+
await this.privateEventStore.getPrivateEvents(selector, {
|
|
196
|
+
contractAddress,
|
|
197
|
+
scopes: [scope],
|
|
198
|
+
fromBlock: 0,
|
|
199
|
+
toBlock: (await this.getLastBlockNumber()) + 1,
|
|
200
|
+
})
|
|
201
|
+
).map(e => e.packedEvent);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async advanceBlocksBy(blocks: number) {
|
|
161
205
|
this.logger.debug(`time traveling ${blocks} blocks`);
|
|
162
206
|
|
|
163
207
|
for (let i = 0; i < blocks; i++) {
|
|
@@ -165,12 +209,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
165
209
|
}
|
|
166
210
|
}
|
|
167
211
|
|
|
168
|
-
|
|
212
|
+
advanceTimestampBy(duration: UInt64) {
|
|
169
213
|
this.logger.debug(`time traveling ${duration} seconds`);
|
|
170
214
|
this.nextBlockTimestamp += duration;
|
|
171
215
|
}
|
|
172
216
|
|
|
173
|
-
async
|
|
217
|
+
async deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
|
|
174
218
|
// Emit deployment nullifier
|
|
175
219
|
await this.mineBlock({
|
|
176
220
|
nullifiers: [
|
|
@@ -182,41 +226,41 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
182
226
|
});
|
|
183
227
|
|
|
184
228
|
if (!secret.equals(Fr.ZERO)) {
|
|
185
|
-
await this.
|
|
229
|
+
await this.addAccount(artifact, instance, secret);
|
|
186
230
|
} else {
|
|
187
|
-
await this.
|
|
188
|
-
await this.
|
|
231
|
+
await this.contractStore.addContractInstance(instance);
|
|
232
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
189
233
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
190
234
|
}
|
|
191
235
|
}
|
|
192
236
|
|
|
193
|
-
async
|
|
237
|
+
async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: Fr) {
|
|
194
238
|
const partialAddress = await computePartialAddress(instance);
|
|
195
239
|
|
|
196
240
|
this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
|
|
197
|
-
await this.
|
|
198
|
-
await this.
|
|
241
|
+
await this.contractStore.addContractInstance(instance);
|
|
242
|
+
await this.contractStore.addContractArtifact(artifact);
|
|
199
243
|
|
|
200
244
|
const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
|
|
201
|
-
await this.
|
|
202
|
-
await this.
|
|
245
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
246
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
203
247
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
204
248
|
|
|
205
249
|
return completeAddress;
|
|
206
250
|
}
|
|
207
251
|
|
|
208
|
-
async
|
|
209
|
-
// This is a
|
|
252
|
+
async createAccount(secret: Fr) {
|
|
253
|
+
// This is a foot gun !
|
|
210
254
|
const completeAddress = await this.keyStore.addAccount(secret, secret);
|
|
211
|
-
await this.
|
|
212
|
-
await this.
|
|
255
|
+
await this.accountStore.setAccount(completeAddress.address, completeAddress);
|
|
256
|
+
await this.addressStore.addCompleteAddress(completeAddress);
|
|
213
257
|
this.logger.debug(`Created account ${completeAddress.address}`);
|
|
214
258
|
|
|
215
259
|
return completeAddress;
|
|
216
260
|
}
|
|
217
261
|
|
|
218
|
-
async
|
|
219
|
-
const account = await this.
|
|
262
|
+
async addAuthWitness(address: AztecAddress, messageHash: Fr) {
|
|
263
|
+
const account = await this.accountStore.getAccount(address);
|
|
220
264
|
const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey);
|
|
221
265
|
|
|
222
266
|
const schnorr = new Schnorr();
|
|
@@ -228,7 +272,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
228
272
|
}
|
|
229
273
|
|
|
230
274
|
async mineBlock(options: { nullifiers?: Fr[] } = {}) {
|
|
231
|
-
const blockNumber = await this.
|
|
275
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
232
276
|
|
|
233
277
|
const txEffect = TxEffect.empty();
|
|
234
278
|
txEffect.nullifiers = [getSingleTxBlockRequestHash(blockNumber), ...(options.nullifiers ?? [])];
|
|
@@ -237,19 +281,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
237
281
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
238
282
|
await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
|
|
239
283
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
version: this.version,
|
|
248
|
-
chainId: this.chainId,
|
|
249
|
-
}),
|
|
250
|
-
),
|
|
251
|
-
new Body([txEffect]),
|
|
252
|
-
);
|
|
284
|
+
const globals = makeGlobalVariables(undefined, {
|
|
285
|
+
blockNumber,
|
|
286
|
+
timestamp: this.nextBlockTimestamp,
|
|
287
|
+
version: this.version,
|
|
288
|
+
chainId: this.chainId,
|
|
289
|
+
});
|
|
290
|
+
const block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
253
291
|
|
|
254
292
|
await forkedWorldTrees.close();
|
|
255
293
|
|
|
@@ -258,19 +296,20 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
258
296
|
await this.stateMachine.handleL2Block(block);
|
|
259
297
|
}
|
|
260
298
|
|
|
261
|
-
async
|
|
299
|
+
async privateCallNewFlow(
|
|
262
300
|
from: AztecAddress,
|
|
263
301
|
targetContractAddress: AztecAddress = AztecAddress.zero(),
|
|
264
302
|
functionSelector: FunctionSelector = FunctionSelector.empty(),
|
|
265
303
|
args: Fr[],
|
|
266
304
|
argsHash: Fr = Fr.zero(),
|
|
267
305
|
isStaticCall: boolean = false,
|
|
306
|
+
jobId: string,
|
|
268
307
|
) {
|
|
269
308
|
this.logger.verbose(
|
|
270
|
-
`Executing external function ${await this.
|
|
309
|
+
`Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
271
310
|
);
|
|
272
311
|
|
|
273
|
-
const artifact = await this.
|
|
312
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
274
313
|
if (!artifact) {
|
|
275
314
|
const message = functionSelector.equals(await FunctionSelector.fromSignature('verify_private_authwit(Field)'))
|
|
276
315
|
? 'Found no account contract artifact for a private authwit check - use `create_contract_account` instead of `create_light_account` for authwit support.'
|
|
@@ -278,51 +317,77 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
278
317
|
throw new Error(message);
|
|
279
318
|
}
|
|
280
319
|
|
|
281
|
-
|
|
320
|
+
// When `from` is the zero address (e.g. when deploying a new account contract), we return an
|
|
321
|
+
// empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
|
|
322
|
+
const effectiveScopes = from.isZero() ? [] : [from];
|
|
323
|
+
|
|
324
|
+
// Sync notes before executing private function to discover notes from previous transactions
|
|
325
|
+
const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
|
|
326
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
330
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(
|
|
331
|
+
targetContractAddress,
|
|
332
|
+
functionSelector,
|
|
333
|
+
utilityExecutor,
|
|
334
|
+
blockHeader,
|
|
335
|
+
jobId,
|
|
336
|
+
effectiveScopes,
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
282
340
|
|
|
283
341
|
const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
|
|
284
342
|
|
|
285
343
|
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
286
|
-
|
|
287
344
|
const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
|
|
288
|
-
|
|
289
345
|
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
290
346
|
|
|
291
347
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
292
348
|
|
|
293
|
-
const blockHeader = await this.pxeOracleInterface.getAnchorBlockHeader();
|
|
294
|
-
|
|
295
349
|
const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
|
|
296
350
|
const noteCache = new ExecutionNoteCache(protocolNullifier);
|
|
351
|
+
// In production, the account contract sets the min revertible counter before calling the app function.
|
|
352
|
+
// Since TXE bypasses the account contract, we simulate this by setting minRevertibleSideEffectCounter to 1,
|
|
353
|
+
// marking all side effects as revertible.
|
|
354
|
+
const minRevertibleSideEffectCounter = 1;
|
|
355
|
+
await noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
297
356
|
const taggingIndexCache = new ExecutionTaggingIndexCache();
|
|
298
357
|
|
|
299
358
|
const simulator = new WASMSimulator();
|
|
300
359
|
|
|
301
|
-
const privateExecutionOracle = new PrivateExecutionOracle(
|
|
360
|
+
const privateExecutionOracle = new PrivateExecutionOracle({
|
|
302
361
|
argsHash,
|
|
303
362
|
txContext,
|
|
304
363
|
callContext,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
[],
|
|
311
|
-
HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
364
|
+
anchorBlockHeader: blockHeader,
|
|
365
|
+
utilityExecutor,
|
|
366
|
+
authWitnesses: Array.from(this.authwits.values()),
|
|
367
|
+
capsules: [],
|
|
368
|
+
executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
|
|
312
369
|
noteCache,
|
|
313
370
|
taggingIndexCache,
|
|
314
|
-
this.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
371
|
+
contractStore: this.contractStore,
|
|
372
|
+
noteStore: this.noteStore,
|
|
373
|
+
keyStore: this.keyStore,
|
|
374
|
+
addressStore: this.addressStore,
|
|
375
|
+
aztecNode: this.stateMachine.node,
|
|
376
|
+
senderTaggingStore: this.senderTaggingStore,
|
|
377
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
378
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
379
|
+
capsuleStore: this.capsuleStore,
|
|
380
|
+
privateEventStore: this.privateEventStore,
|
|
381
|
+
contractSyncService: this.stateMachine.contractSyncService,
|
|
382
|
+
jobId,
|
|
383
|
+
totalPublicCalldataCount: 0,
|
|
384
|
+
sideEffectCounter: minRevertibleSideEffectCounter,
|
|
385
|
+
scopes: effectiveScopes,
|
|
386
|
+
// In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
|
|
387
|
+
// contract would perform, including setting senderForTags.
|
|
388
|
+
senderForTags: from,
|
|
324
389
|
simulator,
|
|
325
|
-
);
|
|
390
|
+
});
|
|
326
391
|
|
|
327
392
|
// 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.
|
|
328
393
|
let result: PrivateExecutionResult;
|
|
@@ -344,24 +409,27 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
344
409
|
);
|
|
345
410
|
const publicFunctionsCalldata = await Promise.all(
|
|
346
411
|
publicCallRequests.map(async r => {
|
|
347
|
-
const calldata = await privateExecutionOracle.
|
|
412
|
+
const calldata = await privateExecutionOracle.loadFromExecutionCache(r.calldataHash);
|
|
348
413
|
return new HashedValues(calldata, r.calldataHash);
|
|
349
414
|
}),
|
|
350
415
|
);
|
|
351
416
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
result = new PrivateExecutionResult(executionResult, Fr.ZERO, publicFunctionsCalldata);
|
|
417
|
+
noteCache.finish();
|
|
418
|
+
const nonceGenerator = noteCache.getNonceGenerator();
|
|
419
|
+
result = new PrivateExecutionResult(executionResult, nonceGenerator, publicFunctionsCalldata);
|
|
356
420
|
} catch (err) {
|
|
357
421
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
358
422
|
}
|
|
359
423
|
|
|
360
|
-
// According to the protocol rules,
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
const
|
|
364
|
-
|
|
424
|
+
// According to the protocol rules, there must be at least one nullifier in the tx. The first nullifier is used as
|
|
425
|
+
// the nonce generator for the note hashes.
|
|
426
|
+
// We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
|
|
427
|
+
const { publicInputs } = await generateSimulatedProvingResult(
|
|
428
|
+
result,
|
|
429
|
+
(addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
|
|
430
|
+
this.stateMachine.node,
|
|
431
|
+
minRevertibleSideEffectCounter,
|
|
432
|
+
);
|
|
365
433
|
|
|
366
434
|
const globals = makeGlobalVariables();
|
|
367
435
|
globals.blockNumber = blockNumber;
|
|
@@ -372,7 +440,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
372
440
|
|
|
373
441
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
374
442
|
|
|
375
|
-
const
|
|
443
|
+
const bindings = this.logger.getBindings();
|
|
444
|
+
const contractsDB = new PublicContractsDB(
|
|
445
|
+
new TXEPublicContractDataSource(blockNumber, this.contractStore),
|
|
446
|
+
bindings,
|
|
447
|
+
);
|
|
376
448
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
377
449
|
const config = PublicSimulatorConfig.from({
|
|
378
450
|
skipFeeEnforcement: true,
|
|
@@ -385,8 +457,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
385
457
|
globals,
|
|
386
458
|
guardedMerkleTrees,
|
|
387
459
|
contractsDB,
|
|
388
|
-
new
|
|
460
|
+
new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings),
|
|
389
461
|
new TestDateProvider(),
|
|
462
|
+
undefined,
|
|
463
|
+
createLogger('simulator:public-processor', bindings),
|
|
390
464
|
);
|
|
391
465
|
|
|
392
466
|
const tx = await Tx.create({
|
|
@@ -411,7 +485,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
411
485
|
} else if (!processedTx.revertCode.isOK()) {
|
|
412
486
|
if (processedTx.revertReason) {
|
|
413
487
|
try {
|
|
414
|
-
await enrichPublicSimulationError(processedTx.revertReason, this.
|
|
488
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
415
489
|
// eslint-disable-next-line no-empty
|
|
416
490
|
} catch {}
|
|
417
491
|
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
@@ -440,13 +514,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
440
514
|
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
441
515
|
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
442
516
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
const l2Block = new L2Block(
|
|
446
|
-
makeAppendOnlyTreeSnapshot(),
|
|
447
|
-
await makeTXEBlockHeader(forkedWorldTrees, globals),
|
|
448
|
-
body,
|
|
449
|
-
);
|
|
517
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
450
518
|
|
|
451
519
|
await this.stateMachine.handleL2Block(l2Block);
|
|
452
520
|
|
|
@@ -455,17 +523,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
455
523
|
return executionResult.returnValues ?? [];
|
|
456
524
|
}
|
|
457
525
|
|
|
458
|
-
async
|
|
526
|
+
async publicCallNewFlow(
|
|
459
527
|
from: AztecAddress,
|
|
460
528
|
targetContractAddress: AztecAddress,
|
|
461
529
|
calldata: Fr[],
|
|
462
530
|
isStaticCall: boolean,
|
|
463
531
|
) {
|
|
464
532
|
this.logger.verbose(
|
|
465
|
-
`Executing public function ${await this.
|
|
533
|
+
`Executing public function ${await this.contractStore.getDebugFunctionName(targetContractAddress, FunctionSelector.fromField(calldata[0]))}@${targetContractAddress} isStaticCall=${isStaticCall}`,
|
|
466
534
|
);
|
|
467
535
|
|
|
468
|
-
const blockNumber = await this.
|
|
536
|
+
const blockNumber = await this.getNextBlockNumber();
|
|
469
537
|
|
|
470
538
|
const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
|
|
471
539
|
|
|
@@ -475,7 +543,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
475
543
|
|
|
476
544
|
const txContext = new TxContext(this.chainId, this.version, gasSettings);
|
|
477
545
|
|
|
478
|
-
const anchorBlockHeader = await this.
|
|
546
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
479
547
|
|
|
480
548
|
const calldataHash = await computeCalldataHash(calldata);
|
|
481
549
|
const calldataHashedValues = new HashedValues(calldata, calldataHash);
|
|
@@ -489,7 +557,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
489
557
|
|
|
490
558
|
const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
|
|
491
559
|
|
|
492
|
-
const
|
|
560
|
+
const bindings2 = this.logger.getBindings();
|
|
561
|
+
const contractsDB = new PublicContractsDB(
|
|
562
|
+
new TXEPublicContractDataSource(blockNumber, this.contractStore),
|
|
563
|
+
bindings2,
|
|
564
|
+
);
|
|
493
565
|
const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
|
|
494
566
|
const config = PublicSimulatorConfig.from({
|
|
495
567
|
skipFeeEnforcement: true,
|
|
@@ -498,17 +570,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
498
570
|
collectStatistics: false,
|
|
499
571
|
collectCallMetadata: true,
|
|
500
572
|
});
|
|
501
|
-
const simulator = new
|
|
502
|
-
const processor = new PublicProcessor(
|
|
573
|
+
const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
|
|
574
|
+
const processor = new PublicProcessor(
|
|
575
|
+
globals,
|
|
576
|
+
guardedMerkleTrees,
|
|
577
|
+
contractsDB,
|
|
578
|
+
simulator,
|
|
579
|
+
new TestDateProvider(),
|
|
580
|
+
undefined,
|
|
581
|
+
createLogger('simulator:public-processor', bindings2),
|
|
582
|
+
);
|
|
503
583
|
|
|
504
584
|
// We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
|
|
505
585
|
// kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
|
|
506
586
|
// side-effect, which the AVM then expects to exist in order to use it as the nonce generator when siloing notes as
|
|
507
587
|
// unique.
|
|
508
588
|
const nonRevertibleAccumulatedData = PrivateToPublicAccumulatedData.empty();
|
|
509
|
-
|
|
510
|
-
nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
|
|
511
|
-
}
|
|
589
|
+
nonRevertibleAccumulatedData.nullifiers[0] = getSingleTxBlockRequestHash(blockNumber);
|
|
512
590
|
|
|
513
591
|
// The enqueued public call itself we make be revertible so that the public execution is itself revertible, as tests
|
|
514
592
|
// may require producing reverts.
|
|
@@ -532,7 +610,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
532
610
|
constantData,
|
|
533
611
|
/*gasUsed=*/ new Gas(0, 0),
|
|
534
612
|
/*feePayer=*/ AztecAddress.zero(),
|
|
535
|
-
/*
|
|
613
|
+
/*expirationTimestamp=*/ 0n,
|
|
536
614
|
inputsForPublic,
|
|
537
615
|
undefined,
|
|
538
616
|
);
|
|
@@ -559,7 +637,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
559
637
|
} else if (!processedTx.revertCode.isOK()) {
|
|
560
638
|
if (processedTx.revertReason) {
|
|
561
639
|
try {
|
|
562
|
-
await enrichPublicSimulationError(processedTx.revertReason, this.
|
|
640
|
+
await enrichPublicSimulationError(processedTx.revertReason, this.contractStore, this.logger);
|
|
563
641
|
// eslint-disable-next-line no-empty
|
|
564
642
|
} catch {}
|
|
565
643
|
throw new Error(`Contract execution has reverted: ${processedTx.revertReason.getMessage()}`);
|
|
@@ -591,13 +669,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
591
669
|
const l1ToL2Messages = Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero);
|
|
592
670
|
await forkedWorldTrees.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages);
|
|
593
671
|
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
const l2Block = new L2Block(
|
|
597
|
-
makeAppendOnlyTreeSnapshot(),
|
|
598
|
-
await makeTXEBlockHeader(forkedWorldTrees, globals),
|
|
599
|
-
body,
|
|
600
|
-
);
|
|
672
|
+
const l2Block = await makeTXEBlock(forkedWorldTrees, globals, [txEffect]);
|
|
601
673
|
|
|
602
674
|
await this.stateMachine.handleL2Block(l2Block);
|
|
603
675
|
|
|
@@ -606,23 +678,46 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
606
678
|
return returnValues ?? [];
|
|
607
679
|
}
|
|
608
680
|
|
|
609
|
-
async
|
|
681
|
+
async executeUtilityFunction(
|
|
610
682
|
targetContractAddress: AztecAddress,
|
|
611
683
|
functionSelector: FunctionSelector,
|
|
612
684
|
args: Fr[],
|
|
685
|
+
jobId: string,
|
|
613
686
|
) {
|
|
614
|
-
const artifact = await this.
|
|
687
|
+
const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
|
|
615
688
|
if (!artifact) {
|
|
616
689
|
throw new Error(`Cannot call ${functionSelector} as there is no artifact found at ${targetContractAddress}.`);
|
|
617
690
|
}
|
|
618
691
|
|
|
619
|
-
|
|
692
|
+
// Sync notes before executing utility function to discover notes from previous transactions
|
|
693
|
+
const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
694
|
+
await this.stateMachine.contractSyncService.ensureContractSynced(
|
|
695
|
+
targetContractAddress,
|
|
696
|
+
functionSelector,
|
|
697
|
+
async (call, execScopes) => {
|
|
698
|
+
await this.executeUtilityCall(call, execScopes, jobId);
|
|
699
|
+
},
|
|
700
|
+
blockHeader,
|
|
701
|
+
jobId,
|
|
702
|
+
'ALL_SCOPES',
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const call = FunctionCall.from({
|
|
620
706
|
name: artifact.name,
|
|
621
|
-
selector: functionSelector,
|
|
622
707
|
to: targetContractAddress,
|
|
623
|
-
|
|
708
|
+
selector: functionSelector,
|
|
709
|
+
type: FunctionType.UTILITY,
|
|
710
|
+
hideMsgSender: false,
|
|
711
|
+
isStatic: false,
|
|
712
|
+
args,
|
|
713
|
+
returnTypes: [],
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
|
|
717
|
+
}
|
|
624
718
|
|
|
625
|
-
|
|
719
|
+
private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
|
|
720
|
+
const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
|
|
626
721
|
if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
|
|
627
722
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
|
|
628
723
|
}
|
|
@@ -633,9 +728,26 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
633
728
|
});
|
|
634
729
|
|
|
635
730
|
try {
|
|
636
|
-
const
|
|
731
|
+
const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
|
|
732
|
+
const oracle = new UtilityExecutionOracle({
|
|
733
|
+
contractAddress: call.to,
|
|
734
|
+
authWitnesses: [],
|
|
735
|
+
capsules: [],
|
|
736
|
+
anchorBlockHeader,
|
|
737
|
+
contractStore: this.contractStore,
|
|
738
|
+
noteStore: this.noteStore,
|
|
739
|
+
keyStore: this.keyStore,
|
|
740
|
+
addressStore: this.addressStore,
|
|
741
|
+
aztecNode: this.stateMachine.node,
|
|
742
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
743
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
744
|
+
capsuleStore: this.capsuleStore,
|
|
745
|
+
privateEventStore: this.privateEventStore,
|
|
746
|
+
jobId,
|
|
747
|
+
scopes,
|
|
748
|
+
});
|
|
637
749
|
const acirExecutionResult = await new WASMSimulator()
|
|
638
|
-
.executeUserCircuit(toACVMWitness(0, args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
750
|
+
.executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
|
|
639
751
|
.catch((err: Error) => {
|
|
640
752
|
err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
|
|
641
753
|
throw new ExecutionError(
|
|
@@ -649,10 +761,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
649
761
|
);
|
|
650
762
|
});
|
|
651
763
|
|
|
652
|
-
this.logger.verbose(`Utility
|
|
764
|
+
this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
|
|
653
765
|
return witnessMapToFields(acirExecutionResult.returnWitness);
|
|
654
766
|
} catch (err) {
|
|
655
|
-
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility
|
|
767
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
|
|
656
768
|
}
|
|
657
769
|
}
|
|
658
770
|
|
|
@@ -661,7 +773,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
|
|
|
661
773
|
return [this.nextBlockTimestamp, this.authwits];
|
|
662
774
|
}
|
|
663
775
|
|
|
664
|
-
private async getLastBlockNumber(): Promise<
|
|
665
|
-
|
|
776
|
+
private async getLastBlockNumber(): Promise<BlockNumber> {
|
|
777
|
+
const header = await this.stateMachine.node.getBlockHeader('latest');
|
|
778
|
+
return header ? header.globalVariables.blockNumber : BlockNumber.ZERO;
|
|
666
779
|
}
|
|
667
780
|
}
|