Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
65 changes: 40 additions & 25 deletions l1-contracts/src/core/libraries/decoders/TxsDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,38 @@ import {Hash} from "../Hash.sol";
* -------------------
* L2 Body Data Specification
* -------------------
* | byte start | num bytes | name
* | --- | --- | ---
* | 0x0 | 0x4 | len(numTxs) (denoted t)
* | | | TxEffect 0 {
* | 0x4 | 0x1 | len(newNoteHashes) (denoted b)
* | 0x4 + 0x1 | b * 0x20 | newNoteHashes
* | 0x4 + 0x1 + b * 0x20 | 0x1 | len(newNullifiers) (denoted c)
* | 0x4 + 0x1 + b * 0x20 + 0x1 | c * 0x20 | newNullifiers
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 | 0x1 | len(newL2ToL1Msgs) (denoted d)
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 | d * 0x20 | newL2ToL1Msgs
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 | 0x1 | len(newPublicDataWrites) (denoted e)
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 | e * 0x40 | newPublicDataWrites
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 | 0x04 | byteLen(newEncryptedLogs) (denoted f)
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 | f | newEncryptedLogs
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 + f | 0x04 | byteLen(newUnencryptedLogs) (denoted g)
* | 0x4 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 + f + 0x4 | g | newUnencryptedLogs
* | | | },
* | | | TxEffect 1 {
* | | | ...
* | | | },
* | | | ...
* | | | TxEffect (t - 1) {
* | | | ...
* | | | },
* | byte start | num bytes | name
* | --- | --- | ---
* | 0x0 | 0x4 | len(numTxs) (denoted t)
* | | | TxEffect 0 {
* | 0x4 | 0x8 | daGasUsed
* | 0x4 + 0x8 | 0x8 | computeGasUsed
* | 0x4 + 0x8 + 0x8 | 0x1 | revertCode
* | 0x4 + 0x8 + 0x8 + 0x1 | 0x1 | len(newNoteHashes) (denoted b)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 | b * 0x20 | newNoteHashes
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 | 0x1 | len(newNullifiers) (denoted c)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 | c * 0x20 | newNullifiers
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 | 0x1 | len(newL2ToL1Msgs) (denoted d)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 | d * 0x20 | newL2ToL1Msgs
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 | 0x1 | len(newPublicDataWrites) (denoted e)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 | e * 0x40 | newPublicDataWrites
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 | 0x04 | byteLen(newEncryptedLogs) (denoted f)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 | f | newEncryptedLogs
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 + f | 0x04 | byteLen(newUnencryptedLogs) (denoted g)
* | 0x4 + 0x8 + 0x8 + 0x1 + 0x1 + b * 0x20 + 0x1 + c * 0x20 + 0x1 + d * 0x20 + 0x01 + e * 0x40 + 0x4 + f + 0x4 | g | newUnencryptedLogs
* | | | },
* | | | TxEffect 1 {
* | | | ...
* | | | },
* | | | ...
* | | | TxEffect (t - 1) {
* | | | ...
* | | | },
*/
library TxsDecoder {
struct ArrayOffsets {
uint256 daGasUsed;
uint256 computeGasUsed;
uint256 revertCode;
uint256 noteHash;
uint256 nullifier;
Expand Down Expand Up @@ -105,6 +110,14 @@ library TxsDecoder {
* Zero values.
*/

// daGasUsed
offsets.daGasUsed = offset;
offset += 0x8;

// computeGasUsed
offsets.computeGasUsed = offset;
offset += 0x8;

// Revert Code
offsets.revertCode = offset;
offset += 0x1;
Expand Down Expand Up @@ -146,7 +159,9 @@ library TxsDecoder {

// Insertions are split into multiple `bytes.concat` to work around stack too deep.
vars.baseLeaf = bytes.concat(
// pad the revert code to 32 bytes to match the hash preimage
// pad these values to 32 bytes to match the hash preimage
sliceAndPadLeft(_body, offsets.daGasUsed, 0x8, 0x20),
sliceAndPadLeft(_body, offsets.computeGasUsed, 0x8, 0x20),
sliceAndPadLeft(_body, offsets.revertCode, 0x1, 0x20),
bytes.concat(
sliceAndPadRight(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod base_rollup_inputs;
mod state_diff_hints;
mod calldata_gas;

use base_rollup_inputs::BaseRollupInputs;
use crate::abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use dep::types::abis::accumulated_data::CombinedAccumulatedData;
use dep::types::abis::side_effect::{SideEffect, SideEffectLinkedToNoteHash};
use dep::types::traits::is_empty;
use dep::types::constants::{
MAX_NEW_NULLIFIERS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
};

global FIXED_DA_GAS: u64 = 272;
global DA_GAS_PER_BYTE: u64 = 16;

pub fn compute_calldata_da_gas(data: CombinedAccumulatedData) -> u64 {
let mut non_zero_bytes = (data.unencrypted_log_preimages_length + data.encrypted_log_preimages_length) as u64;

for i in 0..MAX_NEW_NOTE_HASHES_PER_TX {
if !is_empty(data.new_note_hashes[i]) {
non_zero_bytes += 32;
}
}

for i in 0..MAX_NEW_NULLIFIERS_PER_TX {
if !is_empty(data.new_nullifiers[i]) {
non_zero_bytes += 32;
}
}

for i in 0..MAX_NEW_L2_TO_L1_MSGS_PER_TX {
if !is_empty(data.new_l2_to_l1_msgs[i]) {
non_zero_bytes += 32;
}
}

for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX {
if !is_empty(data.public_data_update_requests[i]) {
non_zero_bytes += 64;
}
}

non_zero_bytes * DA_GAS_PER_BYTE + FIXED_DA_GAS
}

pub fn compute_calldata_compute_gas(data: CombinedAccumulatedData) -> u64 {
data.compute_gas_used
}

mod tests {

use dep::types::abis::accumulated_data::{CombinedAccumulatedDataBuilder, CombinedAccumulatedData};

use crate::base::calldata_gas::compute_calldata_da_gas;

#[test]
fn test_compute_calldata_gas() {
let mut builder: CombinedAccumulatedDataBuilder = dep::std::unsafe::zeroed();
builder.unencrypted_log_preimages_length = 4;
builder.encrypted_log_preimages_length = 4;
let data = builder.finish();
// see calldata_tx_effect_factory.test.ts for the expected value
assert_eq(compute_calldata_da_gas(data), 400);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::{
call_request::CallRequest, caller_context::CallerContext,
public_data_update_request::PublicDataUpdateRequest,
side_effect::{SideEffect, SideEffectLinkedToNoteHash}
}
},
traits::Empty
};
use crate::constants::{
MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX,
Expand All @@ -21,6 +22,9 @@ use crate::traits::is_empty;
use crate::utils::arrays::{array_cp, array_concat, array_to_bounded_vec};

struct CombinedAccumulatedData {
da_gas_used: u64,
compute_gas_used: u64,

revert_code: u8,

new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX],
Expand Down Expand Up @@ -52,6 +56,8 @@ impl CombinedAccumulatedData {
revertible: PublicAccumulatedRevertibleData
) -> CombinedAccumulatedData {
CombinedAccumulatedData {
da_gas_used: 0,
compute_gas_used: 1,
revert_code: non_revertible.revert_code,
new_note_hashes: array_concat(non_revertible.new_note_hashes, revertible.new_note_hashes),
new_nullifiers: array_concat(non_revertible.new_nullifiers, revertible.new_nullifiers),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ impl CombinedAccumulatedDataBuilder {

pub fn finish(self) -> CombinedAccumulatedData {
CombinedAccumulatedData {
da_gas_used: 0,
compute_gas_used: 1,
revert_code: self.revert_code,
new_note_hashes: self.new_note_hashes.storage,
new_nullifiers: self.new_nullifiers.storage,
Expand Down
12 changes: 12 additions & 0 deletions yarn-project/circuit-types/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from 'jest';

const config: Config = {
preset: 'ts-jest/presets/default-esm',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.[cm]?js$': '$1',
},
testRegex: './src/.*\\.test\\.(js|mjs|ts)$',
rootDir: './src',
};

export default config;
11 changes: 0 additions & 11 deletions yarn-project/circuit-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,6 @@
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests"
},
"inherits": [
"../package.common.json"
],
"jest": {
"preset": "ts-jest/presets/default-esm",
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
},
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
"rootDir": "./src"
},
"dependencies": {
"@aztec/circuits.js": "workspace:^",
"@aztec/ethereum": "workspace:^",
Expand Down
34 changes: 34 additions & 0 deletions yarn-project/circuit-types/src/calldata_tx_effect_factory.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
Fr,
MAX_NEW_L2_TO_L1_MSGS_PER_TX,
MAX_NEW_NOTE_HASHES_PER_TX,
MAX_NEW_NULLIFIERS_PER_TX,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
RevertCode,
} from '@aztec/circuits.js';
import { makeTuple } from '@aztec/foundation/array';

import { CalldataTxEffectFactory, DA_BYTE_GAS, FIXED_DA_GAS, TxL2Logs } from './index.js';
import { PublicDataWrite } from './public_data_write.js';
import { ITxEffectWithoutGasUsed } from './tx_effect.js';

describe('calldata_tx_effect_factory', () => {
it('correctly calculates DA gas for empty TxEffect', () => {
const effect: ITxEffectWithoutGasUsed = {
revertCode: RevertCode.OK,
noteHashes: makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, Fr.zero),
nullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Fr.zero),
l2ToL1Msgs: makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero),
publicDataWrites: makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataWrite.empty),
encryptedLogs: TxL2Logs.empty(),
unencryptedLogs: TxL2Logs.empty(),
};

const gasUsed = CalldataTxEffectFactory.build(effect).daGasUsed;

// 4n for each log, due to encoding of the length of the logs
expect(gasUsed.value).toEqual(FIXED_DA_GAS + 4n * DA_BYTE_GAS + 4n * DA_BYTE_GAS);
});

// TODO(@just-mitch) more tests please
});
78 changes: 78 additions & 0 deletions yarn-project/circuit-types/src/calldata_tx_effect_factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Fr, GasUsed, RevertCode } from '@aztec/circuits.js';
import { arrayNonEmptyLength } from '@aztec/foundation/collection';

import { PublicDataWrite } from './public_data_write.js';
import { GasProfiler, GasType, ITxEffectWithoutGasUsed, TxEffect, TxEffectFactory } from './tx_effect.js';

export const DA_BYTE_GAS = 16n;

export const FIXED_BYTES = // 17 bytes
GasUsed.PACKED_SIZE_IN_BYTES + // da_gas_used
GasUsed.PACKED_SIZE_IN_BYTES + // compute_gas_used
RevertCode.PACKED_SIZE_IN_BYTES; // revert_code

export const FIXED_DA_GAS = FIXED_BYTES * DA_BYTE_GAS; // 272n

const getComputeGasUsed = (_effect: ITxEffectWithoutGasUsed) => {
// Just a dummy for now
return new GasUsed(1n);
};

/**
* Note. This does not exactly match ethereum calldata cost.
* It is correlated, but simplified to ease circuit calculations:
* We don't want to bitwise deconstruct the calldata to count the non-zero bytes in the circuit.
*
* We overcompensate by
* - assuming our FIXED_BYTE "header" is always non-zero.
* - assuming there is no zero byte in any non-zero field
*
* We undercompensate by
* - not counting the bytes used to store the lengths of the various arrays
*
* @param effect the TxEffect to calculate the DA gas used for
* @returns our interpretation of the DA gas used
*/
const getDAGasUsed = (effect: ITxEffectWithoutGasUsed) => {
const nonEmptyFields =
arrayNonEmptyLength(effect.noteHashes, Fr.isZero) +
arrayNonEmptyLength(effect.nullifiers, Fr.isZero) +
arrayNonEmptyLength(effect.l2ToL1Msgs, Fr.isZero) +
2 * arrayNonEmptyLength(effect.publicDataWrites, PublicDataWrite.isEmpty);

const gasUsed =
FIXED_DA_GAS +
DA_BYTE_GAS *
BigInt(
Fr.SIZE_IN_BYTES * nonEmptyFields +
effect.encryptedLogs.getSerializedLength() +
effect.unencryptedLogs.getSerializedLength(),
);

return new GasUsed(gasUsed);
};

const gasProfiler: GasProfiler = (effect: ITxEffectWithoutGasUsed) => {
return {
[GasType.DA]: getDAGasUsed(effect),
[GasType.COMPUTE]: getComputeGasUsed(effect),
};
};

export const CalldataTxEffectFactory: TxEffectFactory = {
gasProfiler,
build(effect: ITxEffectWithoutGasUsed) {
const { [GasType.DA]: daGasUsed, [GasType.COMPUTE]: computeGasUsed } = this.gasProfiler(effect);
return new TxEffect(
daGasUsed,
computeGasUsed,
effect.revertCode,
effect.noteHashes,
effect.nullifiers,
effect.l2ToL1Msgs,
effect.publicDataWrites,
effect.encryptedLogs,
effect.unencryptedLogs,
);
},
};
21 changes: 11 additions & 10 deletions yarn-project/circuit-types/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
export { CompleteAddress, GrumpkinPrivateKey, PartialAddress, PublicKey } from '@aztec/circuits.js';
export * from './auth_witness.js';
export * from './aztec_node/rpc/index.js';
export * from './body.js';
export * from './calldata_tx_effect_factory.js';
export * from './function_call.js';
export * from './interfaces/index.js';
export * from './keys/index.js';
export * from './notes/index.js';
export * from './messaging/index.js';
export * from './l2_block.js';
export * from './body.js';
export * from './l2_block_context.js';
export * from './l2_block_downloader/index.js';
export * from './l2_block_source.js';
export * from './tx_effect.js';
export * from './logs/index.js';
export * from './merkle_tree_id.js';
export * from './messaging/index.js';
export * from './mocks.js';
export * from './notes/index.js';
export * from './packed_arguments.js';
export * from './public_data_write.js';
export * from './simulation_error.js';
export * from './sibling_path/index.js';
export * from './simulation_error.js';
export * from './tx/index.js';
export * from './tx_effect.js';
export * from './tx_execution_request.js';
export * from './packed_arguments.js';
export * from './interfaces/index.js';
export * from './auth_witness.js';
export * from './aztec_node/rpc/index.js';
export { CompleteAddress, PublicKey, PartialAddress, GrumpkinPrivateKey } from '@aztec/circuits.js';
Loading