Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Min and max block times for sequencer #7630

Merged
merged 2 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/docs/reference/sandbox_reference/sandbox-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ ROLLUP_CONTRACT_ADDRESS=0x01234567890abcde01234567890abcde
SEQ_PUBLISHER_PRIVATE_KEY=0x01234567890abcde01234567890abcde # Private key of an ethereum account that will be used by the sequencer to publish blocks.
SEQ_MAX_TX_PER_BLOCK=32 # Maximum txs to go on a block. (default: 32)
SEQ_MIN_TX_PER_BLOCK=1 # Minimum txs to go on a block. (default: 1)
SEQ_MAX_SECONDS_BETWEEN_BLOCKS=0 # Sequencer will produce a block with less than the min number of txs once this threshold is reached. (default: 0, means disabled)
SEQ_MIN_SECONDS_BETWEEN_BLOCKS=0 # Minimum seconds to wait between consecutive blocks. (default: 0)
```

**PXE**
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/aztec/terraform/node/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,14 @@ resource "aws_ecs_task_definition" "aztec-node" {
name = "SEQ_MIN_TX_PER_BLOCK"
value = var.SEQ_MIN_TX_PER_BLOCK
},
{
name = "SEQ_MAX_SECONDS_BETWEEN_BLOCKS"
value = var.SEQ_MAX_SECONDS_BETWEEN_BLOCKS
},
{
name = "SEQ_MIN_SECONDS_BETWEEN_BLOCKS"
value = var.SEQ_MIN_SECONDS_BETWEEN_BLOCKS
},
{
name = "SEQ_PUBLISHER_PRIVATE_KEY"
value = local.sequencer_private_keys[count.index]
Expand Down
12 changes: 11 additions & 1 deletion yarn-project/aztec/terraform/node/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,17 @@ variable "SEQ_MAX_TX_PER_BLOCK" {

variable "SEQ_MIN_TX_PER_BLOCK" {
type = string
default = 1
default = 0
}

variable "SEQ_MAX_SECONDS_BETWEEN_BLOCKS" {
type = string
default = 60
}

variable "SEQ_MIN_SECONDS_BETWEEN_BLOCKS" {
type = string
default = 30
}

variable "P2P_MIN_PEERS" {
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/circuit-types/src/interfaces/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export interface SequencerConfig {
maxTxsPerBlock?: number;
/** The minimum number of txs to include in a block. */
minTxsPerBlock?: number;
/** The minimum number of seconds inbetween consecutive blocks. */
minSecondsBetweenBlocks?: number;
/** The maximum number of seconds inbetween consecutive blocks. Sequencer will produce a block with less than minTxsPerBlock once this threshold is reached. */
maxSecondsBetweenBlocks?: number;
/** Recipient of block reward. */
coinbase?: EthAddress;
/** Address to receive fees. */
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/end-to-end/scripts/docker-compose-p2p.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ services:
WS_CHECK_INTERVAL: 50
SEQ_MAX_TX_PER_BLOCK: 32
SEQ_MIN_TX_PER_BLOCK: 1
SEQ_MAX_SECONDS_BETWEEN_BLOCKS: 0
SEQ_MIN_SECONDS_BETWEEN_BLOCKS: 0
P2P_TCP_LISTEN_ADDR: '0.0.0.0:40400'
P2P_NAT_ENABLED: 'false'
P2P_ENABLED: 'true'
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/end-to-end/scripts/start_p2p_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export WS_CHECK_INTERVAL=50
export SEQ_TX_POLLING_INTERVAL=50
export SEQ_MAX_TX_PER_BLOCK=32
export SEQ_MIN_TX_PER_BLOCK=32
export SEQ_MAX_SECONDS_BETWEEN_BLOCKS=0
export SEQ_MIN_SECONDS_BETWEEN_BLOCKS=0
export BOOTSTRAP_NODES='/ip4/127.0.0.1/tcp/40400/p2p/12D3KooWGBpbC6qQFkaCYphjNeY6sV99o4SnEWyTeBigoVriDn4D'
export P2P_TCP_LISTEN_ADDR='0.0.0.0:40400'
export P2P_NAT_ENABLED='false'
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/sequencer-client/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function getConfigEnvVars(): SequencerClientConfig {
SEQ_TX_POLLING_INTERVAL_MS,
SEQ_MAX_TX_PER_BLOCK,
SEQ_MIN_TX_PER_BLOCK,
SEQ_MAX_SECONDS_BETWEEN_BLOCKS,
SEQ_MIN_SECONDS_BETWEEN_BLOCKS,
SEQ_ALLOWED_SETUP_FN,
SEQ_ALLOWED_TEARDOWN_FN,
SEQ_MAX_BLOCK_SIZE_IN_BYTES,
Expand All @@ -58,6 +60,8 @@ export function getConfigEnvVars(): SequencerClientConfig {
l1Contracts: getL1ContractAddressesFromEnv(),
maxTxsPerBlock: SEQ_MAX_TX_PER_BLOCK ? +SEQ_MAX_TX_PER_BLOCK : 32,
minTxsPerBlock: SEQ_MIN_TX_PER_BLOCK ? +SEQ_MIN_TX_PER_BLOCK : 1,
maxSecondsBetweenBlocks: SEQ_MAX_SECONDS_BETWEEN_BLOCKS ? +SEQ_MAX_SECONDS_BETWEEN_BLOCKS : 0,
minSecondsBetweenBlocks: SEQ_MIN_SECONDS_BETWEEN_BLOCKS ? +SEQ_MIN_SECONDS_BETWEEN_BLOCKS : 0,
sequencerSkipSubmitProofs: ['1', 'true'].includes(SEQ_SKIP_SUBMIT_PROOFS ?? ''),
// TODO: undefined should not be allowed for the following 2 values in PROD
coinbase: COINBASE ? EthAddress.fromString(COINBASE) : undefined,
Expand Down
67 changes: 55 additions & 12 deletions yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export class Sequencer {
private pollingIntervalMs: number = 1000;
private maxTxsPerBlock = 32;
private minTxsPerBLock = 1;
private minSecondsBetweenBlocks = 0;
private maxSecondsBetweenBlocks = 0;
// TODO: zero values should not be allowed for the following 2 values in PROD
private _coinbase = EthAddress.ZERO;
private _feeRecipient = AztecAddress.ZERO;
Expand Down Expand Up @@ -78,15 +80,21 @@ export class Sequencer {
* @param config - New parameters.
*/
public updateConfig(config: SequencerConfig) {
if (config.transactionPollingIntervalMS) {
if (config.transactionPollingIntervalMS !== undefined) {
this.pollingIntervalMs = config.transactionPollingIntervalMS;
}
if (config.maxTxsPerBlock) {
if (config.maxTxsPerBlock !== undefined) {
this.maxTxsPerBlock = config.maxTxsPerBlock;
}
if (config.minTxsPerBlock) {
if (config.minTxsPerBlock !== undefined) {
this.minTxsPerBLock = config.minTxsPerBlock;
}
if (config.minSecondsBetweenBlocks !== undefined) {
this.minSecondsBetweenBlocks = config.minSecondsBetweenBlocks;
}
if (config.maxSecondsBetweenBlocks !== undefined) {
this.maxSecondsBetweenBlocks = config.maxSecondsBetweenBlocks;
}
if (config.coinbase) {
this._coinbase = config.coinbase;
}
Expand All @@ -96,7 +104,7 @@ export class Sequencer {
if (config.allowedInSetup) {
this.allowedInSetup = config.allowedInSetup;
}
if (config.maxBlockSizeInBytes) {
if (config.maxBlockSizeInBytes !== undefined) {
this.maxBlockSizeInBytes = config.maxBlockSizeInBytes;
}
// TODO(#5917) remove this. it is no longer needed since we don't need to whitelist functions in teardown
Expand Down Expand Up @@ -184,16 +192,37 @@ export class Sequencer {
return;
}

this.state = SequencerState.WAITING_FOR_TXS;
// Compute time elapsed since the previous block
const lastBlockTime = historicalHeader?.globalVariables.timestamp.toNumber() || 0;
const currentTime = Math.floor(Date.now() / 1000);
const elapsedSinceLastBlock = currentTime - lastBlockTime;

// Get txs to build the new block
const pendingTxs = this.p2pClient.getTxs('pending');
if (pendingTxs.length < this.minTxsPerBLock) {
// Do not go forward with new block if not enough time has passed since last block
if (elapsedSinceLastBlock < this.minSecondsBetweenBlocks) {
this.log.debug(
`Not creating block because there are not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
`Not creating block because not enough time has passed since last block (last block at ${lastBlockTime} current time ${currentTime})`,
);
return;
}

this.state = SequencerState.WAITING_FOR_TXS;

// Get txs to build the new block.
const pendingTxs = this.p2pClient.getTxs('pending');

// If we haven't hit the maxSecondsBetweenBlocks, we need to have at least minTxsPerBLock txs.
if (pendingTxs.length < this.minTxsPerBLock) {
if (this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
this.log.debug(
`Creating block with only ${pendingTxs.length} txs as more than ${this.maxSecondsBetweenBlocks}s have passed since last block`,
);
} else {
this.log.debug(
`Not creating block because not enough txs in the pool (got ${pendingTxs.length} min ${this.minTxsPerBLock})`,
);
return;
}
}
this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`);

const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(
Expand All @@ -215,11 +244,15 @@ export class Sequencer {
// may break if we start emitting lots of log data from public-land.
const validTxs = this.takeTxsWithinMaxSize(allValidTxs);

if (validTxs.length < this.minTxsPerBLock) {
// Bail if we don't have enough valid txs
if (!this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock) && validTxs.length < this.minTxsPerBLock) {
this.log.debug(
`Not creating block because not enough valid txs loaded from the pool (got ${validTxs.length} min ${this.minTxsPerBLock})`,
);
return;
}

await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader);
await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader, elapsedSinceLastBlock);
} catch (err) {
if (BlockProofError.isBlockProofError(err)) {
const txHashes = err.txHashes.filter(h => !h.isZero());
Expand All @@ -233,13 +266,19 @@ export class Sequencer {
}
}

/** Whether to skip the check of min txs per block if more than maxSecondsBetweenBlocks has passed since the previous block. */
private skipMinTxsPerBlockCheck(elapsed: number): boolean {
return this.maxSecondsBetweenBlocks > 0 && elapsed >= this.maxSecondsBetweenBlocks;
}

@trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({
[Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(),
}))
private async buildBlockAndPublish(
validTxs: Tx[],
newGlobalVariables: GlobalVariables,
historicalHeader: Header | undefined,
elapsedSinceLastBlock: number,
): Promise<void> {
const workTimer = new Timer();
this.state = SequencerState.CREATING_BLOCK;
Expand Down Expand Up @@ -281,7 +320,11 @@ export class Sequencer {
await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData));
}

if (processedTxs.length === 0) {
// TODO: This check should be processedTxs.length < this.minTxsPerBLock, so we don't publish a block with
// less txs than the minimum. But that'd cause the entire block to be aborted and retried. Instead, we should
// go back to the p2p pool and load more txs until we hit our minTxsPerBLock target. Only if there are no txs
// we should bail.
if (processedTxs.length === 0 && !this.skipMinTxsPerBlockCheck(elapsedSinceLastBlock)) {
this.log.verbose('No txs processed correctly to build block. Exiting');
this.prover.cancelBlock();
return;
Expand Down
Loading