@aztec/sequencer-client 0.55.1 → 0.57.0
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/block_builder/index.d.ts +5 -24
- package/dest/block_builder/index.d.ts.map +1 -1
- package/dest/block_builder/index.js +3 -40
- package/dest/block_builder/light.d.ts +26 -0
- package/dest/block_builder/light.d.ts.map +1 -0
- package/dest/block_builder/light.js +60 -0
- package/dest/block_builder/orchestrator.d.ts +23 -0
- package/dest/block_builder/orchestrator.d.ts.map +1 -0
- package/dest/block_builder/orchestrator.js +33 -0
- package/dest/client/sequencer-client.d.ts +2 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +3 -3
- package/dest/config.js +8 -5
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +2 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +7 -2
- package/dest/publisher/index.d.ts +1 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +1 -1
- package/dest/publisher/l1-publisher.d.ts +46 -34
- package/dest/publisher/l1-publisher.d.ts.map +1 -1
- package/dest/publisher/l1-publisher.js +259 -116
- package/dest/publisher/utils.d.ts +1 -1
- package/dest/publisher/utils.js +1 -1
- package/dest/sequencer/sequencer.d.ts +7 -3
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +62 -23
- package/dest/tx_validator/gas_validator.d.ts +1 -0
- package/dest/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/tx_validator/gas_validator.js +13 -8
- package/dest/tx_validator/phases_validator.d.ts +1 -0
- package/dest/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/tx_validator/phases_validator.js +20 -20
- package/dest/tx_validator/test_utils.js +4 -4
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +5 -4
- package/package.json +22 -19
- package/src/block_builder/index.ts +5 -49
- package/src/block_builder/light.ts +102 -0
- package/src/block_builder/orchestrator.ts +38 -0
- package/src/client/sequencer-client.ts +4 -3
- package/src/config.ts +7 -4
- package/src/global_variable_builder/global_builder.ts +1 -0
- package/src/publisher/config.ts +6 -1
- package/src/publisher/index.ts +1 -1
- package/src/publisher/l1-publisher.ts +373 -144
- package/src/publisher/utils.ts +1 -1
- package/src/sequencer/sequencer.ts +74 -26
- package/src/tx_validator/gas_validator.ts +13 -10
- package/src/tx_validator/phases_validator.ts +5 -6
- package/src/tx_validator/test_utils.ts +3 -3
- package/src/tx_validator/tx_validator_factory.ts +5 -4
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type BlockAttestation,
|
|
3
|
+
type EpochProofQuote,
|
|
3
4
|
type L1ToL2MessageSource,
|
|
4
5
|
type L2Block,
|
|
5
6
|
type L2BlockSource,
|
|
6
7
|
type ProcessedTx,
|
|
7
|
-
Signature,
|
|
8
8
|
Tx,
|
|
9
9
|
type TxHash,
|
|
10
10
|
type TxValidator,
|
|
11
11
|
type WorldStateStatus,
|
|
12
12
|
type WorldStateSynchronizer,
|
|
13
13
|
} from '@aztec/circuit-types';
|
|
14
|
-
import { type AllowedElement, BlockProofError
|
|
14
|
+
import { type AllowedElement, BlockProofError } from '@aztec/circuit-types/interfaces';
|
|
15
15
|
import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats';
|
|
16
16
|
import {
|
|
17
17
|
AppendOnlyTreeSnapshot,
|
|
18
|
-
AztecAddress,
|
|
19
18
|
ContentCommitment,
|
|
20
|
-
EthAddress,
|
|
21
19
|
GENESIS_ARCHIVE_ROOT,
|
|
22
20
|
Header,
|
|
23
21
|
StateReference,
|
|
24
22
|
} from '@aztec/circuits.js';
|
|
23
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
24
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
25
|
+
import { Signature } from '@aztec/foundation/eth-signature';
|
|
25
26
|
import { Fr } from '@aztec/foundation/fields';
|
|
26
27
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
27
28
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
@@ -31,10 +32,12 @@ import { type PublicProcessorFactory } from '@aztec/simulator';
|
|
|
31
32
|
import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
32
33
|
import { type ValidatorClient } from '@aztec/validator-client';
|
|
33
34
|
|
|
35
|
+
import { inspect } from 'util';
|
|
36
|
+
|
|
34
37
|
import { type BlockBuilderFactory } from '../block_builder/index.js';
|
|
35
38
|
import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
36
39
|
import { type L1Publisher } from '../publisher/l1-publisher.js';
|
|
37
|
-
import {
|
|
40
|
+
import { prettyLogViemError } from '../publisher/utils.js';
|
|
38
41
|
import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js';
|
|
39
42
|
import { type SequencerConfig } from './config.js';
|
|
40
43
|
import { SequencerMetrics } from './metrics.js';
|
|
@@ -311,7 +314,7 @@ export class Sequencer {
|
|
|
311
314
|
this.log.debug(`Can propose block ${proposalBlockNumber} at slot ${slot}`);
|
|
312
315
|
return slot;
|
|
313
316
|
} catch (err) {
|
|
314
|
-
|
|
317
|
+
prettyLogViemError(err, this.log);
|
|
315
318
|
throw err;
|
|
316
319
|
}
|
|
317
320
|
}
|
|
@@ -424,7 +427,7 @@ export class Sequencer {
|
|
|
424
427
|
|
|
425
428
|
const blockBuildingTimer = new Timer();
|
|
426
429
|
const blockBuilder = this.blockBuilderFactory.create(this.worldState.getLatest());
|
|
427
|
-
|
|
430
|
+
await blockBuilder.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages);
|
|
428
431
|
|
|
429
432
|
const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() =>
|
|
430
433
|
processor.process(validTxs, blockSize, blockBuilder, this.txValidatorFactory.validatorForProcessedTxs()),
|
|
@@ -443,23 +446,15 @@ export class Sequencer {
|
|
|
443
446
|
processedTxsCount: processedTxs.length,
|
|
444
447
|
})
|
|
445
448
|
) {
|
|
446
|
-
|
|
449
|
+
// TODO: Roll back changes to world state
|
|
447
450
|
throw new Error('Should not propose the block');
|
|
448
451
|
}
|
|
449
452
|
|
|
450
453
|
// All real transactions have been added, set the block as full and complete the proving.
|
|
451
|
-
await blockBuilder.setBlockCompleted();
|
|
454
|
+
const block = await blockBuilder.setBlockCompleted();
|
|
452
455
|
|
|
453
|
-
// Here we are now waiting for the block to be proven (using simulated[fake] proofs).
|
|
454
456
|
// TODO(@PhilWindle) We should probably periodically check for things like another
|
|
455
457
|
// block being published before ours instead of just waiting on our block
|
|
456
|
-
const result = await blockTicket.provingPromise;
|
|
457
|
-
if (result.status === PROVING_STATUS.FAILURE) {
|
|
458
|
-
throw new Error(`Block proving failed, reason: ${result.reason}`);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// Block is ready, now finalise
|
|
462
|
-
const { block } = await blockBuilder.finaliseBlock();
|
|
463
458
|
|
|
464
459
|
await this.publisher.validateBlockForSubmission(block.header);
|
|
465
460
|
|
|
@@ -488,13 +483,17 @@ export class Sequencer {
|
|
|
488
483
|
const attestations = await this.collectAttestations(block, txHashes);
|
|
489
484
|
this.log.verbose('Attestations collected');
|
|
490
485
|
|
|
486
|
+
this.log.verbose('Collecting proof quotes');
|
|
487
|
+
const proofQuote = await this.createProofClaimForPreviousEpoch(newGlobalVariables.slotNumber.toBigInt());
|
|
488
|
+
this.log.verbose(proofQuote ? `Using proof quote ${inspect(proofQuote.payload)}` : 'No proof quote available');
|
|
489
|
+
|
|
491
490
|
try {
|
|
492
|
-
await this.publishL2Block(block, attestations, txHashes);
|
|
491
|
+
await this.publishL2Block(block, attestations, txHashes, proofQuote);
|
|
493
492
|
this.metrics.recordPublishedBlock(workDuration);
|
|
494
493
|
this.log.info(
|
|
495
|
-
`Submitted rollup block ${block.number} with ${
|
|
496
|
-
|
|
497
|
-
}
|
|
494
|
+
`Submitted rollup block ${block.number} with ${processedTxs.length} transactions duration=${Math.ceil(
|
|
495
|
+
workDuration,
|
|
496
|
+
)}ms (Submitter: ${this.publisher.getSenderAddress()})`,
|
|
498
497
|
);
|
|
499
498
|
} catch (err) {
|
|
500
499
|
this.metrics.recordFailedBlock();
|
|
@@ -537,7 +536,51 @@ export class Sequencer {
|
|
|
537
536
|
this.log.verbose(`Collected attestations from validators, number of attestations: ${attestations.length}`);
|
|
538
537
|
|
|
539
538
|
// note: the smart contract requires that the signatures are provided in the order of the committee
|
|
540
|
-
return
|
|
539
|
+
return orderAttestations(attestations, committee);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
protected async createProofClaimForPreviousEpoch(slotNumber: bigint): Promise<EpochProofQuote | undefined> {
|
|
543
|
+
try {
|
|
544
|
+
// Find out which epoch we are currently in
|
|
545
|
+
const epochForBlock = await this.publisher.getEpochForSlotNumber(slotNumber);
|
|
546
|
+
if (epochForBlock < 1n) {
|
|
547
|
+
// It's the 0th epoch, nothing to be proven yet
|
|
548
|
+
this.log.verbose(`First epoch has no claim`);
|
|
549
|
+
return undefined;
|
|
550
|
+
}
|
|
551
|
+
const epochToProve = epochForBlock - 1n;
|
|
552
|
+
// Find out the next epoch that can be claimed
|
|
553
|
+
const canClaim = await this.publisher.nextEpochToClaim();
|
|
554
|
+
if (canClaim != epochToProve) {
|
|
555
|
+
// It's not the one we are looking to claim
|
|
556
|
+
this.log.verbose(`Unable to claim previous epoch (${canClaim} != ${epochToProve})`);
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
559
|
+
// Get quotes for the epoch to be proven
|
|
560
|
+
const quotes = await this.p2pClient.getEpochProofQuotes(epochToProve);
|
|
561
|
+
this.log.verbose(`Retrieved ${quotes.length} quotes, slot: ${slotNumber}, epoch to prove: ${epochToProve}`);
|
|
562
|
+
for (const quote of quotes) {
|
|
563
|
+
this.log.verbose(inspect(quote.payload));
|
|
564
|
+
}
|
|
565
|
+
// ensure these quotes are still valid for the slot and have the contract validate them
|
|
566
|
+
const validQuotesPromise = Promise.all(
|
|
567
|
+
quotes.filter(x => x.payload.validUntilSlot >= slotNumber).map(x => this.publisher.validateProofQuote(x)),
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const validQuotes = (await validQuotesPromise).filter((q): q is EpochProofQuote => !!q);
|
|
571
|
+
if (!validQuotes.length) {
|
|
572
|
+
this.log.verbose(`Failed to find any valid proof quotes`);
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
// pick the quote with the lowest fee
|
|
576
|
+
const sortedQuotes = validQuotes.sort(
|
|
577
|
+
(a: EpochProofQuote, b: EpochProofQuote) => a.payload.basisPointFee - b.payload.basisPointFee,
|
|
578
|
+
);
|
|
579
|
+
return sortedQuotes[0];
|
|
580
|
+
} catch (err) {
|
|
581
|
+
this.log.error(`Failed to create proof claim for previous epoch: ${err}`);
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
541
584
|
}
|
|
542
585
|
|
|
543
586
|
/**
|
|
@@ -547,11 +590,16 @@ export class Sequencer {
|
|
|
547
590
|
@trackSpan('Sequencer.publishL2Block', block => ({
|
|
548
591
|
[Attributes.BLOCK_NUMBER]: block.number,
|
|
549
592
|
}))
|
|
550
|
-
protected async publishL2Block(
|
|
593
|
+
protected async publishL2Block(
|
|
594
|
+
block: L2Block,
|
|
595
|
+
attestations?: Signature[],
|
|
596
|
+
txHashes?: TxHash[],
|
|
597
|
+
proofQuote?: EpochProofQuote,
|
|
598
|
+
) {
|
|
551
599
|
// Publishes new block to the network and awaits the tx to be mined
|
|
552
600
|
this.state = SequencerState.PUBLISHING_BLOCK;
|
|
553
601
|
|
|
554
|
-
const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes);
|
|
602
|
+
const publishedL2Block = await this.publisher.proposeL2Block(block, attestations, txHashes, proofQuote);
|
|
555
603
|
if (publishedL2Block) {
|
|
556
604
|
this.lastPublishedBlock = block.number;
|
|
557
605
|
} else {
|
|
@@ -666,12 +714,12 @@ export enum SequencerState {
|
|
|
666
714
|
*
|
|
667
715
|
* @todo: perform this logic within the memory attestation store instead?
|
|
668
716
|
*/
|
|
669
|
-
|
|
717
|
+
function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Signature[] {
|
|
670
718
|
// Create a map of sender addresses to BlockAttestations
|
|
671
719
|
const attestationMap = new Map<string, BlockAttestation>();
|
|
672
720
|
|
|
673
721
|
for (const attestation of attestations) {
|
|
674
|
-
const sender =
|
|
722
|
+
const sender = attestation.getSender();
|
|
675
723
|
if (sender) {
|
|
676
724
|
attestationMap.set(sender.toString(), attestation);
|
|
677
725
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type AztecAddress, type Fr } from '@aztec/circuits.js';
|
|
1
|
+
import { PublicKernelPhase, type Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
+
import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js';
|
|
3
3
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
5
|
-
import { AbstractPhaseManager, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
|
|
4
|
+
import { EnqueuedCallsProcessor, computeFeePayerBalanceStorageSlot } from '@aztec/simulator';
|
|
6
5
|
|
|
7
6
|
/** Provides a view into public contract state */
|
|
8
7
|
export interface PublicStateSource {
|
|
@@ -34,6 +33,10 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
34
33
|
return [validTxs, invalidTxs];
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
validateTx(tx: Tx): Promise<boolean> {
|
|
37
|
+
return this.#validateTxFee(tx);
|
|
38
|
+
}
|
|
39
|
+
|
|
37
40
|
async #validateTxFee(tx: Tx): Promise<boolean> {
|
|
38
41
|
const feePayer = tx.data.feePayer;
|
|
39
42
|
// TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx
|
|
@@ -55,20 +58,20 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
55
58
|
);
|
|
56
59
|
|
|
57
60
|
// If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance
|
|
58
|
-
const
|
|
61
|
+
const setupFns = EnqueuedCallsProcessor.getExecutionRequestsByPhase(tx, PublicKernelPhase.SETUP);
|
|
59
62
|
const claimFunctionCall = setupFns.find(
|
|
60
63
|
fn =>
|
|
61
64
|
fn.contractAddress.equals(this.#feeJuiceAddress) &&
|
|
62
65
|
fn.callContext.msgSender.equals(this.#feeJuiceAddress) &&
|
|
63
|
-
fn.
|
|
64
|
-
|
|
65
|
-
) &&
|
|
66
|
-
fn.args[
|
|
66
|
+
fn.args.length > 2 &&
|
|
67
|
+
// Public functions get routed through the dispatch function, whose first argument is the target function selector.
|
|
68
|
+
fn.args[0].equals(FunctionSelector.fromSignature('_increase_public_balance((Field),Field)').toField()) &&
|
|
69
|
+
fn.args[1].equals(feePayer) &&
|
|
67
70
|
!fn.callContext.isStaticCall &&
|
|
68
71
|
!fn.callContext.isDelegateCall,
|
|
69
72
|
);
|
|
70
73
|
|
|
71
|
-
const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[
|
|
74
|
+
const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance;
|
|
72
75
|
if (balance.lt(feeLimit)) {
|
|
73
76
|
this.#log.info(`Rejecting transaction due to not enough fee payer balance`, { feePayer, balance, feeLimit });
|
|
74
77
|
return false;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AllowedElement,
|
|
3
3
|
type PublicExecutionRequest,
|
|
4
|
-
|
|
4
|
+
PublicKernelPhase,
|
|
5
5
|
Tx,
|
|
6
6
|
type TxValidator,
|
|
7
7
|
} from '@aztec/circuit-types';
|
|
8
8
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
9
|
-
import {
|
|
9
|
+
import { ContractsDataSourcePublicDB, EnqueuedCallsProcessor } from '@aztec/simulator';
|
|
10
10
|
import { type ContractDataSource } from '@aztec/types/contracts';
|
|
11
11
|
|
|
12
12
|
export class PhasesTxValidator implements TxValidator<Tx> {
|
|
@@ -27,7 +27,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
27
27
|
// which is what we're trying to do as part of the current txs.
|
|
28
28
|
await this.contractDataSource.addNewContracts(tx);
|
|
29
29
|
|
|
30
|
-
if (await this
|
|
30
|
+
if (await this.validateTx(tx)) {
|
|
31
31
|
validTxs.push(tx);
|
|
32
32
|
} else {
|
|
33
33
|
invalidTxs.push(tx);
|
|
@@ -39,14 +39,13 @@ export class PhasesTxValidator implements TxValidator<Tx> {
|
|
|
39
39
|
return Promise.resolve([validTxs, invalidTxs]);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
async
|
|
42
|
+
async validateTx(tx: Tx): Promise<boolean> {
|
|
43
43
|
if (!tx.data.forPublic) {
|
|
44
44
|
this.#log.debug(`Tx ${Tx.getHash(tx)} does not contain enqueued public functions. Skipping phases validation.`);
|
|
45
45
|
return true;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const
|
|
49
|
-
|
|
48
|
+
const setupFns = EnqueuedCallsProcessor.getExecutionRequestsByPhase(tx, PublicKernelPhase.SETUP);
|
|
50
49
|
for (const setupFn of setupFns) {
|
|
51
50
|
if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
|
|
52
51
|
this.#log.warn(
|
|
@@ -32,9 +32,9 @@ function patchFn(
|
|
|
32
32
|
tx.enqueuedPublicFunctionCalls[index] = fn;
|
|
33
33
|
|
|
34
34
|
const request = tx.data.forPublic![where].publicCallStack[index];
|
|
35
|
-
request.
|
|
36
|
-
request.
|
|
37
|
-
request.
|
|
35
|
+
request.contractAddress = fn.contractAddress;
|
|
36
|
+
request.callContext = fn.callContext;
|
|
37
|
+
request.argsHash = computeVarArgsHash(fn.args);
|
|
38
38
|
tx.data.forPublic![where].publicCallStack[index] = request;
|
|
39
39
|
|
|
40
40
|
return {
|
|
@@ -2,7 +2,7 @@ import { type AllowedElement, type ProcessedTx, type Tx, type TxValidator } from
|
|
|
2
2
|
import { type GlobalVariables } from '@aztec/circuits.js';
|
|
3
3
|
import { AggregateTxValidator, DataTxValidator, DoubleSpendTxValidator, MetadataTxValidator } from '@aztec/p2p';
|
|
4
4
|
import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
|
|
5
|
-
import { WorldStateDB
|
|
5
|
+
import { WorldStateDB } from '@aztec/simulator';
|
|
6
6
|
import { type ContractDataSource } from '@aztec/types/contracts';
|
|
7
7
|
import { type MerkleTreeOperations } from '@aztec/world-state';
|
|
8
8
|
|
|
@@ -17,16 +17,17 @@ export class TxValidatorFactory {
|
|
|
17
17
|
) {}
|
|
18
18
|
|
|
19
19
|
validatorForNewTxs(globalVariables: GlobalVariables, setupAllowList: AllowedElement[]): TxValidator<Tx> {
|
|
20
|
+
const worldStateDB = new WorldStateDB(this.merkleTreeDb, this.contractDataSource);
|
|
20
21
|
return new AggregateTxValidator(
|
|
21
22
|
new DataTxValidator(),
|
|
22
23
|
new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber),
|
|
23
|
-
new DoubleSpendTxValidator(
|
|
24
|
+
new DoubleSpendTxValidator(worldStateDB),
|
|
24
25
|
new PhasesTxValidator(this.contractDataSource, setupAllowList),
|
|
25
|
-
new GasTxValidator(
|
|
26
|
+
new GasTxValidator(worldStateDB, FeeJuiceAddress, this.enforceFees),
|
|
26
27
|
);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
validatorForProcessedTxs(): TxValidator<ProcessedTx> {
|
|
30
|
-
return new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb));
|
|
31
|
+
return new DoubleSpendTxValidator(new WorldStateDB(this.merkleTreeDb, this.contractDataSource));
|
|
31
32
|
}
|
|
32
33
|
}
|