Skip to content

Commit

Permalink
feat: Use tail public inputs as transaction hash (#11100)
Browse files Browse the repository at this point in the history
Implements #9269
Separates the role of the first nullifier and the transaction hash. The
transaction hash is now the hash of the tail public inputs. The first
nullifier is still used for note uniqueness and replayability protection
  • Loading branch information
sirasistant authored Jan 10, 2025
1 parent 85d389f commit 34be2c3
Show file tree
Hide file tree
Showing 50 changed files with 387 additions and 192 deletions.
7 changes: 4 additions & 3 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,8 @@ void AvmTraceBuilder::insert_private_revertible_state(const std::vector<FF>& sil

for (size_t i = 0; i < siloed_note_hashes.size(); i++) {
size_t note_index_in_tx = i + get_inserted_note_hashes_count();
FF nonce = AvmMerkleTreeTraceBuilder::unconstrained_compute_note_hash_nonce(get_tx_hash(), note_index_in_tx);
FF nonce =
AvmMerkleTreeTraceBuilder::unconstrained_compute_note_hash_nonce(get_first_nullifier(), note_index_in_tx);
unique_note_hashes.push_back(
AvmMerkleTreeTraceBuilder::unconstrained_compute_unique_note_hash(nonce, siloed_note_hashes.at(i)));
}
Expand Down Expand Up @@ -3101,8 +3102,8 @@ AvmError AvmTraceBuilder::op_emit_note_hash(uint8_t indirect, uint32_t note_hash
AppendTreeHint note_hash_write_hint = execution_hints.note_hash_write_hints.at(note_hash_write_counter++);
FF siloed_note_hash = AvmMerkleTreeTraceBuilder::unconstrained_silo_note_hash(
current_public_call_request.contract_address, row.main_ia);
FF nonce =
AvmMerkleTreeTraceBuilder::unconstrained_compute_note_hash_nonce(get_tx_hash(), inserted_note_hashes_count);
FF nonce = AvmMerkleTreeTraceBuilder::unconstrained_compute_note_hash_nonce(get_first_nullifier(),
inserted_note_hashes_count);
FF unique_note_hash = AvmMerkleTreeTraceBuilder::unconstrained_compute_unique_note_hash(nonce, siloed_note_hash);

ASSERT(unique_note_hash == note_hash_write_hint.leaf_value);
Expand Down
2 changes: 1 addition & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ class AvmTraceBuilder {
uint32_t get_inserted_note_hashes_count();
uint32_t get_inserted_nullifiers_count();
uint32_t get_public_data_writes_count();
FF get_tx_hash() const { return public_inputs.previous_non_revertible_accumulated_data.nullifiers[0]; }
FF get_first_nullifier() const { return public_inputs.previous_non_revertible_accumulated_data.nullifiers[0]; }

// TODO: remove these once everything is constrained.
AvmMemoryTag unconstrained_get_memory_tag(AddressWithMode addr);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// docs:start:declaration
mod test;
use dep::aztec::macros::aztec;

#[aztec]
contract EasyPrivateVoting {
// docs:end:declaration
// docs:start:imports
use dep::aztec::{
keys::getters::get_public_keys,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use types::{
};

pub(crate) struct TxEffect {
pub(crate) tx_hash: Field,
pub(crate) revert_code: u8,
pub(crate) transaction_fee: Field,
pub(crate) note_hashes: [Field; MAX_NOTE_HASHES_PER_TX],
Expand All @@ -25,6 +26,7 @@ pub(crate) struct TxEffect {
impl Empty for TxEffect {
fn empty() -> Self {
TxEffect {
tx_hash: 0,
revert_code: 0,
transaction_fee: 0,
note_hashes: [0; MAX_NOTE_HASHES_PER_TX],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl PrivateBaseRollupInputs {

let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs);
let tx_effect = TxEffect {
tx_hash: self.tube_data.public_inputs.hash(),
revert_code: 0,
transaction_fee,
note_hashes: self.tube_data.public_inputs.end.note_hashes,
Expand Down Expand Up @@ -241,7 +242,7 @@ mod tests {
NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT,
NULLIFIERS_PREFIX, PRIVATE_LOG_SIZE_IN_FIELDS, PRIVATE_LOGS_PREFIX,
PUBLIC_DATA_TREE_HEIGHT, REVERT_CODE_PREFIX, TUBE_VK_INDEX, TX_FEE_PREFIX,
TX_START_PREFIX, UNENCRYPTED_LOGS_PREFIX,
TX_START_PREFIX,
},
data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage},
hash::silo_l2_to_l1_message,
Expand Down Expand Up @@ -816,26 +817,29 @@ mod tests {
unconstrained fn non_empty_tx_effects_sponge() {
let mut builder = PrivateBaseRollupInputsBuilder::new();
builder.tube_data.append_note_hashes(50);
let outputs = builder.execute();
let mut tx_effects = [0; 53];
// TODO(#8954): This test uses 50 notes and 3 extra absorbed fields
let inputs = builder.build_inputs();
let outputs = inputs.execute();
let mut tx_effects = [0; 54];
// TODO(#8954): This test uses 50 notes and 4 extra absorbed fields
// This may change when logs are deconstructed
// Initial field = TX_START_PREFIX | 0 | txlen[0] txlen[1] | 0 | REVERT_CODE_PREFIX | 0 | revert_code
// The first 3 are: i=0 init field, i=1: tx fee, i=2: note prefix
// The first 4 are: i=0 init field, i=1: tx hash, i=2: tx fee, i=3: note prefix
tx_effects[0] = field_from_bytes(
array_concat(
TX_START_PREFIX.to_be_bytes::<8>(),
[0, 0, tx_effects.len() as u8, 0, REVERT_CODE_PREFIX, 0, 0],
),
true,
);
tx_effects[1] = field_from_bytes(
// TX hash
tx_effects[1] = inputs.tube_data.public_inputs.hash();
tx_effects[2] = field_from_bytes(
array_concat([TX_FEE_PREFIX, 0], (0).to_be_bytes::<29>()),
true,
);
tx_effects[2] = encode_blob_prefix(NOTES_PREFIX, 50);
tx_effects[3] = encode_blob_prefix(NOTES_PREFIX, 50);
for i in 0..50 {
tx_effects[i + 3] = builder.tube_data.note_hashes.storage()[i].value();
tx_effects[i + 4] = builder.tube_data.note_hashes.storage()[i].value();
}
let mut expected_sponge = outputs.start_sponge_blob;

Expand All @@ -849,21 +853,8 @@ mod tests {
let NUM_NULLIFIERS = 3;
let NUM_MSGS = 5;
let NUM_PRIV_EVENT_LOGS = 4;
let NUM_UNENC_LOGS = 6;
let NUM_CC_LOGS = 1;
let TOTAL_BLOB_FIELDS = 2 // revert code and tx fee
+ NUM_NOTES
+ 1 // notes and prefix
+ NUM_NULLIFIERS
+ 1 // nullifiers and prefix
+ NUM_MSGS
+ 1 // L2 to L1 msgs and prefix
+ NUM_UNENC_LOGS
+ 1 // unenc. logs and prefix
+ NUM_CC_LOGS
+ 1 // contract class logs and prefix
+ (NUM_NOTES + NUM_PRIV_EVENT_LOGS) * PRIVATE_LOG_SIZE_IN_FIELDS
+ 1; // private logs and prefix

let mut builder = PrivateBaseRollupInputsBuilder::new();
builder.tube_data.set_gas_used(100, 200);
builder.constants.global_variables.gas_fees.fee_per_da_gas = 1;
Expand All @@ -887,17 +878,19 @@ mod tests {
builder.tube_data.append_private_logs(NUM_PRIV_EVENT_LOGS);
// Below will only work with NUM_CC_LOGS=1
builder.tube_data.add_contract_class_log_hash(1, 2);
let outputs = builder.execute();
let inputs = builder.build_inputs();
let outputs = inputs.execute();

let mut reconstructed_tx_effects = [0; TX_EFFECTS_BLOB_HASH_INPUT_FIELDS];

// tx hash
reconstructed_tx_effects[1] = inputs.tube_data.public_inputs.hash();
// tx fee
reconstructed_tx_effects[1] = field_from_bytes(
reconstructed_tx_effects[2] = field_from_bytes(
array_concat([TX_FEE_PREFIX, 0], tx_fee.to_be_bytes::<29>()),
true,
);
// notes
let mut offset = 2;
let mut offset = 3;
let notes_prefix = encode_blob_prefix(NOTES_PREFIX, NUM_NOTES);
reconstructed_tx_effects[offset] = notes_prefix;
offset += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl PublicBaseRollupInputs {
.fold(0, |len, l: ScopedLogHash| len + l.log_hash.length);

TxEffect {
tx_hash: self.tube_data.public_inputs.hash(),
revert_code,
transaction_fee: from_public.transaction_fee,
note_hashes: from_public.accumulated_data.note_hashes,
Expand Down Expand Up @@ -916,26 +917,29 @@ mod tests {
unconstrained fn non_empty_tx_effects_sponge() {
let mut builder = PublicBaseRollupInputsBuilder::new();
builder.avm_data.append_note_hashes(50);
let outputs = builder.execute();
let mut tx_effects = [0; 53];
// TODO(#8954): This test uses 50 notes and 3 extra absorbed fields
let inputs = builder.build_inputs();
let outputs = inputs.execute();
let mut tx_effects = [0; 54];
// TODO(#8954): This test uses 50 notes and 4 extra absorbed fields
// This may change when logs are deconstructed
// Initial field = TX_START_PREFIX | 0 | txlen[0] txlen[1] | 0 | REVERT_CODE_PREFIX | 0 | revert_code
// The first 3 are: i=0 init field, i=1: tx fee, i=2: note prefix
// The first 4 are: i=0 init field, i=1: tx hash, i=2: tx fee, i=3: note prefix
tx_effects[0] = field_from_bytes(
array_concat(
TX_START_PREFIX.to_be_bytes::<8>(),
[0, 0, tx_effects.len() as u8, 0, REVERT_CODE_PREFIX, 0, 0],
),
true,
);
tx_effects[1] = field_from_bytes(
// TX hash
tx_effects[1] = inputs.tube_data.public_inputs.hash();
tx_effects[2] = field_from_bytes(
array_concat([TX_FEE_PREFIX, 0], (0).to_be_bytes::<29>()),
true,
);
tx_effects[2] = encode_blob_prefix(NOTES_PREFIX, 50);
tx_effects[3] = encode_blob_prefix(NOTES_PREFIX, 50);
for i in 0..50 {
tx_effects[i + 3] = builder.avm_data.note_hashes.storage()[i].value();
tx_effects[i + 4] = builder.avm_data.note_hashes.storage()[i].value();
}
let mut expected_sponge = outputs.start_sponge_blob;

Expand All @@ -953,7 +957,7 @@ mod tests {
let NUM_CC_LOGS = 1;
let PUB_DATA_SLOT = 25;
let PUB_DATA_VALUE = 60;
let TOTAL_BLOB_FIELDS = 2 // revert code and tx fee
let TOTAL_BLOB_FIELDS = 3 // revert code, tx hash and tx fee
+ NUM_NOTES
+ 1 // notes and prefix
+ NUM_NULLIFIERS
Expand Down Expand Up @@ -993,7 +997,8 @@ mod tests {
builder.avm_data.append_unencrypted_log_hashes(NUM_UNENC_LOGS);
// Below will only work with NUM_CC_LOGS=1
builder.tube_data.add_contract_class_log_hash(1, 2);
let outputs = builder.execute();
let inputs = builder.build_inputs();
let outputs = inputs.execute();

let mut reconstructed_tx_effects = [0; TX_EFFECTS_BLOB_HASH_INPUT_FIELDS];
// Initial field = TX_START_PREFIX | 0 | txlen[0] txlen[1] | 0 | REVERT_CODE_PREFIX | 0 | revert_code
Expand All @@ -1014,13 +1019,15 @@ mod tests {
),
true,
);
// tx hash
reconstructed_tx_effects[1] = inputs.tube_data.public_inputs.hash();
// tx fee
reconstructed_tx_effects[1] = field_from_bytes(
reconstructed_tx_effects[2] = field_from_bytes(
array_concat([TX_FEE_PREFIX, 0], tx_fee.to_be_bytes::<29>()),
true,
);
// notes
let mut offset = 2;
let mut offset = 3;
let notes_prefix = encode_blob_prefix(NOTES_PREFIX, NUM_NOTES);
reconstructed_tx_effects[offset] = notes_prefix;
offset += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,26 +202,28 @@ pub(crate) mod tests {

// The below values are generated from block_building_helper.test.ts.
let blob_fields_0 = [
0x000000000000000000000000000000000074785f737461727400000400010000,
0x000000000000000000000000000000000074785f737461727400000500010000,
0x000000000000000000000000000000000000000000000000000000000000002a,
0x0002000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000004000001,
0x0000000000000000000000000000000000000000000000000000000000000123,
];
let blob_fields_1 = [
0x000000000000000000000000000000000074785f737461727400000600010000,
0x000000000000000000000000000000000074785f737461727400000700010000,
0x000000000000000000000000000000000000000000000000000000000000002b,
0x0002000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000003000001,
0x0000000000000000000000000000000000000000000000000000000000006789,
0x0000000000000000000000000000000000000000000000000000000004000001,
0x0000000000000000000000000000000000000000000000000000000000000045,
];
let expected_blob_commitment = [
0x00ad8be66e7276942652627bb00fe1e65dc1c3c6701ab27cc05eff662950071d,
0x00000000000000000000000000000071baf7a9af9757f1d3878b37b438797213,
0x008c32fe581c8fdba12c0d7597911dead2d937d68525bae655508412bb53bb98,
0x0000000000000000000000000000006aaa0680f21270e7d8de4e19da5164f95c,
];
let expected_blobs_hash =
0x00dc577f5c94c82b847693b76ee69cd33d4e5eee3adb6f37d8d7ab662c84725d;
let expected_z = 0x1582b354f32263abde313d597582ebceafe17d4e2a68dd47533383e85b4cb780;
0x00a965619c8668b834755678b32d023b9c5e8588ce449f44f7fa9335455b5cc5;
let expected_z = 0x1f92b871671f27a378d23f1cef10fbd8f0d90dd7172da9e3c3fc1aa745a072c3;

let mut builder = TestBuilder::new_with_blobs_fields(blob_fields_0, blob_fields_1);
builder.data.blob_commitments[0].inner = expected_blob_commitment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pub fn encode_blob_prefix(input_type: u8, array_len: u32) -> Field {

// Tx effects consist of
// 1 field for revert code
// 1 field for tx hash
// 1 field for transaction fee
// MAX_NOTE_HASHES_PER_TX fields for note hashes
// MAX_NULLIFIERS_PER_TX fields for nullifiers
Expand All @@ -151,6 +152,7 @@ pub fn encode_blob_prefix(input_type: u8, array_len: u32) -> Field {
// MAX_CONTRACT_CLASS_LOGS_PER_TX fields for contract class logs
// 7 fields for prefixes for each of the above categories
pub(crate) global TX_EFFECTS_BLOB_HASH_INPUT_FIELDS: u32 = 1
+ 1
+ 1
+ MAX_NOTE_HASHES_PER_TX
+ MAX_NULLIFIERS_PER_TX
Expand All @@ -172,8 +174,8 @@ pub(crate) fn append_tx_effects_for_blob(
let mut out_sponge = start_sponge_blob;

// If we have an empty tx (usually a padding tx), we don't want to absorb anything
// An empty tx will only have 2 effects - revert code and fee - hence offset = 2
if offset != 2 {
// An empty tx will only have 3 effects - revert code, tx hash and fee - hence offset = 3
if offset != 3 {
out_sponge.absorb(tx_effects_hash_input, offset);
}

Expand Down Expand Up @@ -208,6 +210,9 @@ fn get_tx_effects_hash_input(
// We only know the value once the appending is complete, hence we overwrite input[0] below
offset += 1;

assert_eq(tx_effects_hash_input[offset], tx_effect.tx_hash);
offset += 1;

// TX FEE
// Using 29 bytes to encompass all reasonable fee lengths
assert_eq(
Expand Down Expand Up @@ -400,6 +405,9 @@ unconstrained fn get_tx_effects_hash_input_helper(
tx_effects_hash_input[offset] = 0;
offset += 1;

tx_effects_hash_input[offset] = tx_effect.tx_hash;
offset += 1;

// TX FEE
// Using 29 bytes to encompass all reasonable fee lengths
tx_effects_hash_input[offset] = field_from_bytes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ use crate::{
validation_requests::RollupValidationRequests,
},
address::AztecAddress,
constants::PRIVATE_TO_PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH,
traits::{Deserialize, Empty, Serialize},
constants::{
GENERATOR_INDEX__PUBLIC_TX_HASH, PRIVATE_TO_PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH,
},
hash::poseidon2_hash_with_separator,
traits::{Deserialize, Empty, Hash, Serialize},
utils::reader::Reader,
};

Expand Down Expand Up @@ -65,6 +68,12 @@ impl Serialize<PRIVATE_TO_PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH> for Privat
}
}

impl Hash for PrivateToPublicKernelCircuitPublicInputs {
fn hash(self) -> Field {
poseidon2_hash_with_separator(self.serialize(), GENERATOR_INDEX__PUBLIC_TX_HASH)
}
}

impl Deserialize<PRIVATE_TO_PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PrivateToPublicKernelCircuitPublicInputs {
fn deserialize(
fields: [Field; PRIVATE_TO_PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ use crate::{
tx_constant_data::TxConstantData, validation_requests::RollupValidationRequests,
},
address::AztecAddress,
constants::PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH,
traits::{Deserialize, Empty, Serialize},
constants::{
GENERATOR_INDEX__PRIVATE_TX_HASH, PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH,
},
hash::poseidon2_hash_with_separator,
traits::{Deserialize, Empty, Hash, Serialize},
utils::reader::Reader,
};
use std::meta::derive;
Expand Down Expand Up @@ -48,6 +51,12 @@ impl Serialize<PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH> for Privat
}
}

impl Hash for PrivateToRollupKernelCircuitPublicInputs {
fn hash(self) -> Field {
poseidon2_hash_with_separator(self.serialize(), GENERATOR_INDEX__PRIVATE_TX_HASH)
}
}

impl Deserialize<PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PrivateToRollupKernelCircuitPublicInputs {
fn deserialize(
fields: [Field; PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH],
Expand Down
Loading

1 comment on commit 34be2c3

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: 34be2c3 Previous: 535a14c Ratio
wasmClientIVCBench/Full/6 79875.7591 ms/iter 71873.770908 ms/iter 1.11

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.