From 7904dc6bfe459eee07a6c4abb9ca39cc47de9b57 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 6 Jun 2024 10:53:27 +0100 Subject: [PATCH] feat: Estimate tx size --- yarn-project/circuit-types/src/tx/tx.ts | 13 ++++++++++ .../src/structs/content_commitment.ts | 4 ++++ .../circuits.js/src/structs/gas_settings.ts | 4 ++++ .../src/structs/global_variables.ts | 4 ++++ .../circuits.js/src/structs/header.ts | 10 ++++++++ .../kernel/combined_accumulated_data.ts | 17 +++++++++++++ .../structs/kernel/combined_constant_data.ts | 4 ++++ ...ivate_kernel_tail_circuit_public_inputs.ts | 24 +++++++++++++++++++ .../structs/kernel/public_accumulated_data.ts | 15 ++++++++++++ .../src/structs/partial_state_reference.ts | 4 ++++ .../src/structs/public_call_request.ts | 4 ++++ .../rollup/append_only_tree_snapshot.ts | 4 ++++ .../src/structs/rollup_validation_requests.ts | 4 ++++ .../src/structs/state_reference.ts | 4 ++++ .../circuits.js/src/structs/tx_context.ts | 4 ++++ .../src/structs/validation_requests.ts | 12 ++++++++++ .../foundation/src/collection/array.ts | 14 +++++++++++ yarn-project/foundation/src/fields/fields.ts | 5 ++++ .../src/sequencer/sequencer.ts | 4 ++-- 19 files changed, 152 insertions(+), 2 deletions(-) diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index 9496f3ced83..d10e8fdb5f6 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -4,6 +4,7 @@ import { Proof, PublicCallRequest, } from '@aztec/circuits.js'; +import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type GetUnencryptedLogsResponse } from '../logs/get_unencrypted_logs_response.js'; @@ -193,6 +194,18 @@ export class Tx { }; } + getSize() { + return ( + this.data.getSize() + + this.proof.buffer.length + + this.noteEncryptedLogs.getSerializedLength() + + this.encryptedLogs.getSerializedLength() + + this.unencryptedLogs.getSerializedLength() + + arraySerializedSizeOfNonEmpty(this.enqueuedPublicFunctionCalls) + + arraySerializedSizeOfNonEmpty([this.publicTeardownFunctionCall]) + ); + } + /** * Convenience function to get a hash out of a tx or a tx-like. * @param tx - Tx-like object. diff --git a/yarn-project/circuits.js/src/structs/content_commitment.ts b/yarn-project/circuits.js/src/structs/content_commitment.ts index e95bd6f9189..6de8796aa5a 100644 --- a/yarn-project/circuits.js/src/structs/content_commitment.ts +++ b/yarn-project/circuits.js/src/structs/content_commitment.ts @@ -27,6 +27,10 @@ export class ContentCommitment { } } + getSize() { + return this.toBuffer().length; + } + toBuffer() { return serializeToBuffer(this.txTreeHeight, this.txsEffectsHash, this.inHash, this.outHash); } diff --git a/yarn-project/circuits.js/src/structs/gas_settings.ts b/yarn-project/circuits.js/src/structs/gas_settings.ts index e777c9467d3..fc9bb8b9842 100644 --- a/yarn-project/circuits.js/src/structs/gas_settings.ts +++ b/yarn-project/circuits.js/src/structs/gas_settings.ts @@ -21,6 +21,10 @@ export class GasSettings { public readonly inclusionFee: Fr, ) {} + getSize(): number { + return this.toBuffer().length; + } + static from(args: { gasLimits: FieldsOf; teardownGasLimits: FieldsOf; diff --git a/yarn-project/circuits.js/src/structs/global_variables.ts b/yarn-project/circuits.js/src/structs/global_variables.ts index 0ed91fa7e79..eb0fef0e080 100644 --- a/yarn-project/circuits.js/src/structs/global_variables.ts +++ b/yarn-project/circuits.js/src/structs/global_variables.ts @@ -28,6 +28,10 @@ export class GlobalVariables { public gasFees: GasFees, ) {} + getSize(): number { + return this.toBuffer().length; + } + static from(fields: FieldsOf): GlobalVariables { return new GlobalVariables(...GlobalVariables.getFields(fields)); } diff --git a/yarn-project/circuits.js/src/structs/header.ts b/yarn-project/circuits.js/src/structs/header.ts index e642d97b105..4b647f2b851 100644 --- a/yarn-project/circuits.js/src/structs/header.ts +++ b/yarn-project/circuits.js/src/structs/header.ts @@ -35,6 +35,16 @@ export class Header { ] as const; } + getSize() { + return ( + this.lastArchive.getSize() + + this.contentCommitment.getSize() + + this.state.getSize() + + this.globalVariables.getSize() + + this.totalFees.size + ); + } + toBuffer() { return serializeToBuffer(...Header.getFields(this)); } diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index 035c1999988..342a04b0caa 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -1,4 +1,5 @@ import { type FieldsOf, makeTuple } from '@aztec/foundation/array'; +import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -67,6 +68,22 @@ export class CombinedAccumulatedData { public gasUsed: Gas, ) {} + getSize() { + return ( + arraySerializedSizeOfNonEmpty(this.newNoteHashes) + + arraySerializedSizeOfNonEmpty(this.newNullifiers) + + arraySerializedSizeOfNonEmpty(this.newL2ToL1Msgs) + + this.noteEncryptedLogsHash.size + + this.encryptedLogsHash.size + + this.unencryptedLogsHash.size + + this.noteEncryptedLogPreimagesLength.size + + this.encryptedLogPreimagesLength.size + + this.unencryptedLogPreimagesLength.size + + arraySerializedSizeOfNonEmpty(this.publicDataUpdateRequests) + + this.gasUsed.toBuffer().length + ); + } + static getFields(fields: FieldsOf) { return [ fields.newNoteHashes, diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts index b15f89b852b..de03065d49f 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts @@ -31,6 +31,10 @@ export class CombinedConstantData { return serializeToBuffer(this.historicalHeader, this.txContext, this.globalVariables); } + getSize() { + return this.historicalHeader.getSize() + this.txContext.getSize() + this.globalVariables.getSize(); + } + static from({ historicalHeader, txContext, globalVariables }: FieldsOf): CombinedConstantData { return new CombinedConstantData(historicalHeader, txContext, globalVariables); } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts index ff36e0806fd..c3b4276d217 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts @@ -1,5 +1,6 @@ import { makeTuple } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX } from '../../constants.gen.js'; @@ -35,6 +36,15 @@ export class PartialPrivateTailPublicInputsForPublic { public publicTeardownCallStack: Tuple, ) {} + getSize() { + return ( + this.validationRequests.getSize() + + this.endNonRevertibleData.getSize() + + this.end.getSize() + + arraySerializedSizeOfNonEmpty(this.publicTeardownCallStack) + ); + } + get needsSetup() { return !this.endNonRevertibleData.publicCallStack[0].isEmpty(); } @@ -87,6 +97,10 @@ export class PartialPrivateTailPublicInputsForRollup { ); } + getSize() { + return this.rollupValidationRequests.getSize() + this.end.getSize(); + } + toBuffer() { return serializeToBuffer(this.rollupValidationRequests, this.end); } @@ -131,6 +145,16 @@ export class PrivateKernelTailCircuitPublicInputs { return (this.forPublic ?? this.forRollup)!; } + getSize() { + return ( + (this.forPublic?.getSize() ?? 0) + + (this.forRollup?.getSize() ?? 0) + + this.constants.getSize() + + this.revertCode.getSerializedLength() + + this.feePayer.size + ); + } + toPublicKernelCircuitPublicInputs() { if (!this.forPublic) { throw new Error('Private tail public inputs is not for public circuit.'); diff --git a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts index e9f1559e353..88061cc72d4 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -67,6 +68,20 @@ export class PublicAccumulatedData { public readonly gasUsed: Gas, ) {} + getSize() { + return ( + arraySerializedSizeOfNonEmpty(this.newNoteHashes) + + arraySerializedSizeOfNonEmpty(this.newNullifiers) + + arraySerializedSizeOfNonEmpty(this.newL2ToL1Msgs) + + arraySerializedSizeOfNonEmpty(this.noteEncryptedLogsHashes) + + arraySerializedSizeOfNonEmpty(this.encryptedLogsHashes) + + arraySerializedSizeOfNonEmpty(this.unencryptedLogsHashes) + + arraySerializedSizeOfNonEmpty(this.publicDataUpdateRequests) + + arraySerializedSizeOfNonEmpty(this.publicCallStack) + + this.gasUsed.toBuffer().length + ); + } + toBuffer() { return serializeToBuffer( this.newNoteHashes, diff --git a/yarn-project/circuits.js/src/structs/partial_state_reference.ts b/yarn-project/circuits.js/src/structs/partial_state_reference.ts index 4ccac8d2318..af0722edd57 100644 --- a/yarn-project/circuits.js/src/structs/partial_state_reference.ts +++ b/yarn-project/circuits.js/src/structs/partial_state_reference.ts @@ -17,6 +17,10 @@ export class PartialStateReference { public readonly publicDataTree: AppendOnlyTreeSnapshot, ) {} + getSize() { + return this.noteHashTree.getSize() + this.nullifierTree.getSize() + this.publicDataTree.getSize(); + } + static fromBuffer(buffer: Buffer | BufferReader): PartialStateReference { const reader = BufferReader.asReader(buffer); return new PartialStateReference( diff --git a/yarn-project/circuits.js/src/structs/public_call_request.ts b/yarn-project/circuits.js/src/structs/public_call_request.ts index d8fc26b3dfc..7c9384c95bd 100644 --- a/yarn-project/circuits.js/src/structs/public_call_request.ts +++ b/yarn-project/circuits.js/src/structs/public_call_request.ts @@ -44,6 +44,10 @@ export class PublicCallRequest { public args: Fr[], ) {} + getSize() { + return this.isEmpty() ? 0 : this.toBuffer().length; + } + /** * Serialize this as a buffer. * @returns The buffer. diff --git a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts index 6929e58b412..c831cbef31f 100644 --- a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts +++ b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts @@ -26,6 +26,10 @@ export class AppendOnlyTreeSnapshot { public nextAvailableLeafIndex: UInt32, ) {} + getSize() { + return this.root.size + 4; + } + toBuffer() { return serializeToBuffer(this.root, this.nextAvailableLeafIndex); } diff --git a/yarn-project/circuits.js/src/structs/rollup_validation_requests.ts b/yarn-project/circuits.js/src/structs/rollup_validation_requests.ts index d8ee528c1f8..4113649893d 100644 --- a/yarn-project/circuits.js/src/structs/rollup_validation_requests.ts +++ b/yarn-project/circuits.js/src/structs/rollup_validation_requests.ts @@ -14,6 +14,10 @@ export class RollupValidationRequests { public maxBlockNumber: MaxBlockNumber, ) {} + getSize() { + return this.toBuffer().length; + } + toBuffer() { return serializeToBuffer(this.maxBlockNumber); } diff --git a/yarn-project/circuits.js/src/structs/state_reference.ts b/yarn-project/circuits.js/src/structs/state_reference.ts index f3a78608ad9..5aa48b38c1c 100644 --- a/yarn-project/circuits.js/src/structs/state_reference.ts +++ b/yarn-project/circuits.js/src/structs/state_reference.ts @@ -16,6 +16,10 @@ export class StateReference { public partial: PartialStateReference, ) {} + getSize() { + return this.l1ToL2MessageTree.getSize() + this.partial.getSize(); + } + toBuffer() { // Note: The order here must match the order in the HeaderLib solidity library. return serializeToBuffer(this.l1ToL2MessageTree, this.partial); diff --git a/yarn-project/circuits.js/src/structs/tx_context.ts b/yarn-project/circuits.js/src/structs/tx_context.ts index 22697d79a5d..32ca812373e 100644 --- a/yarn-project/circuits.js/src/structs/tx_context.ts +++ b/yarn-project/circuits.js/src/structs/tx_context.ts @@ -25,6 +25,10 @@ export class TxContext { this.version = new Fr(version); } + getSize() { + return this.chainId.size + this.version.size + this.gasSettings.getSize(); + } + clone() { return new TxContext(this.chainId, this.version, this.gasSettings.clone()); } diff --git a/yarn-project/circuits.js/src/structs/validation_requests.ts b/yarn-project/circuits.js/src/structs/validation_requests.ts index db970973f42..cdfa2bbf2b4 100644 --- a/yarn-project/circuits.js/src/structs/validation_requests.ts +++ b/yarn-project/circuits.js/src/structs/validation_requests.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { type Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -54,6 +55,17 @@ export class ValidationRequests { public publicDataReads: Tuple, ) {} + getSize() { + return ( + this.forRollup.getSize() + + arraySerializedSizeOfNonEmpty(this.noteHashReadRequests) + + arraySerializedSizeOfNonEmpty(this.nullifierReadRequests) + + arraySerializedSizeOfNonEmpty(this.nullifierNonExistentReadRequests) + + arraySerializedSizeOfNonEmpty(this.scopedKeyValidationRequestsAndGenerators) + + arraySerializedSizeOfNonEmpty(this.publicDataReads) + ); + } + toBuffer() { return serializeToBuffer( this.forRollup, diff --git a/yarn-project/foundation/src/collection/array.ts b/yarn-project/foundation/src/collection/array.ts index 3a397db8b50..23c75cc8e4f 100644 --- a/yarn-project/foundation/src/collection/array.ts +++ b/yarn-project/foundation/src/collection/array.ts @@ -68,3 +68,17 @@ export function arrayNonEmptyLength(arr: T[], isEmpty: (item: T) => boolean): export function times(n: number, fn: (i: number) => T): T[] { return [...Array(n).keys()].map(i => fn(i)); } + +/** + * Returns the serialized size of all non-empty items in an array. + * @param arr - Array + * @returns The serialized size in bytes. + */ +export function arraySerializedSizeOfNonEmpty( + arr: (({ isZero: () => boolean } | { isEmpty: () => boolean }) & { toBuffer: () => Buffer })[], +) { + return arr + .filter(x => x && ('isZero' in x ? !x.isZero() : !x.isEmpty())) + .map(x => x!.toBuffer().length) + .reduce((a, b) => a + b, 0); +} diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index 9c6874ff648..c01abe041c6 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -42,6 +42,11 @@ abstract class BaseField { return this.toBigInt(); } + /** Returns the size in bytes. */ + get size(): number { + return BaseField.SIZE_IN_BYTES; + } + protected constructor(value: number | bigint | boolean | BaseField | Buffer) { if (value instanceof Buffer) { if (value.length > BaseField.SIZE_IN_BYTES) { diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 593e6c85f0c..8723226c5c0 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -321,10 +321,10 @@ export class Sequencer { const toReturn: Tx[] = []; for (const tx of txs) { - const txSize = tx.getStats().size - tx.proof.toBuffer().length; + const txSize = tx.getSize() - tx.proof.toBuffer().length; if (totalSize + txSize > maxSize) { this.log.warn( - `Dropping tx ${tx.getTxHash()} with size ${txSize} due to exceeding ${maxSize} block size limit (currently at ${totalSize})`, + `Dropping tx ${tx.getTxHash()} with estimated size ${txSize} due to exceeding ${maxSize} block size limit (currently at ${totalSize})`, ); continue; }