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: hash logs inside circuit #5934

Merged
merged 38 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5193d02
feat: WIP first commit - logs hashed inside contexts
MirandaWood Apr 22, 2024
47a036d
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 22, 2024
b945e63
chore: oh boy deployer addr here we go
MirandaWood Apr 22, 2024
f7346e0
feat: acc logs len in circuit, add docs and tests
MirandaWood Apr 23, 2024
00f5857
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 23, 2024
7b594ac
fix: fix deploy.nr after portal removal
MirandaWood Apr 23, 2024
a163af6
fix: strs as logs, generic size enc log returns
MirandaWood Apr 24, 2024
56c1505
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 24, 2024
ae9eae4
fix: make enc log len generic, replace slices with arrs
MirandaWood Apr 24, 2024
75ec074
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 24, 2024
0e903b8
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 24, 2024
ab41572
feat: impl strs for unenc logs, generate more trait impls
MirandaWood Apr 24, 2024
b5227f9
feat: track nested public logs with arr, link counter issue
MirandaWood Apr 26, 2024
533c4cc
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 26, 2024
beddeb9
fix: merge fixes
MirandaWood Apr 26, 2024
247de1e
feat: add logscache, tests, remove old hacks for logs sorting
MirandaWood Apr 29, 2024
4c80dab
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 29, 2024
19c7801
fix: merge issues
MirandaWood Apr 29, 2024
9dadba6
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood Apr 30, 2024
fd92b48
chore: increase test timeout + clarify comments
MirandaWood Apr 30, 2024
462ef24
Merge branch 'master' into mw/logs-hash
MirandaWood Apr 30, 2024
57f8b11
fix: merge fix
MirandaWood Apr 30, 2024
a922640
feat: address comments + fmt
MirandaWood Apr 30, 2024
5d465fd
feat: flat hash logs in tail/L1
MirandaWood May 1, 2024
d7a68bd
chore: fmt
MirandaWood May 1, 2024
547fd5b
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 1, 2024
e7cc4c4
feat: revert arr, track logs with counters, sort in exec. res.
MirandaWood May 1, 2024
bfc54a6
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 1, 2024
9af800e
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 1, 2024
4050be3
chore: remove redundant check + comments
MirandaWood May 1, 2024
2b25410
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 1, 2024
91c51e7
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 2, 2024
1726fb3
chore: remove comments, remove redundant .reverse
MirandaWood May 2, 2024
18b9057
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 2, 2024
e591c87
chore: moved nested logs tests now e2e_nested has been refactored
MirandaWood May 2, 2024
5cf5b5a
chore: fmt + update docs for 0.37 release
MirandaWood May 2, 2024
529241d
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 2, 2024
afa345f
Merge remote-tracking branch 'origin' into mw/logs-hash
MirandaWood May 2, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,9 @@ In the future we will allow emitting arbitrary information.
(If you currently emit arbitrary information, PXE will fail to decrypt, process and store this data, so it will not be queryable).
:::

### Import library

To emit encrypted logs first import the `emit_encrypted_log` utility function which wraps an [oracle](../oracles/main.md):

#include_code encrypted_import /noir-projects/aztec-nr/address-note/src/address_note.nr rust

### Call emit_encrypted_log

After importing, you can call the function:
To emit encrypted logs you don't need to import any library. You call the context method `emit_encrypted_log`:

#include_code encrypted /noir-projects/aztec-nr/address-note/src/address_note.nr rust

Expand Down
14 changes: 13 additions & 1 deletion docs/docs/misc/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ Aztec is in full-speed development. Literally every version breaks compatibility

## 0.36.0

### [Aztec.nr] Emmiting encrypted logs

The `emit_encrypted_log` function is now a context method.

```diff
- use dep::aztec::log::emit_encrypted_log;
- use dep::aztec::logs::emit_encrypted_log;

- emit_encrypted_log(context, log1);
+ context.emit_encrypted_log(log1);
```

### `FieldNote` removed

`FieldNote` only existed for testing purposes, and was not a note type that should be used in any real application. Its name unfortunately led users to think that it was a note type suitable to store a `Field` value, which it wasn't.
Expand Down Expand Up @@ -171,7 +183,7 @@ Note that gas limits are not yet enforced. For now, it is suggested you use `dep

Note that this is not required when enqueuing a public function from a private one, since top-level enqueued public functions will always consume all gas available for the transaction, as it is not possible to handle any out-of-gas errors.

### [Aztec.nr] Emmiting unencrypted logs
### [Aztec.nr] Emitting unencrypted logs

The `emit_unencrypted_logs` function is now a context method.

Expand Down
17 changes: 5 additions & 12 deletions docs/docs/protocol-specs/logs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Logs on Aztec are similar to logs on Ethereum, enabling smart contracts to conve

### Hash Function

The protocol uses **SHA256** as the hash function for logs, and then reduces the 256-bit result to 253 bits for representation as a field element.
The protocol uses **SHA256** as the hash function for logs, and then reduces the 256-bit result to 248 bits for representation as a field element.

<!-- TODO: explicitly detail how the truncation is being done, so that we can check that it is secure. -->

Expand Down Expand Up @@ -227,10 +227,7 @@ Following the iterations for all private or public calls, the tail kernel circui

2. Accumulate all the hashes and output the final hash to the public inputs:

- _`accumulated_logs_hash = hash(logs_hash_a, logs_hash_b)`_
- For tail public kernel circuit, it begins with _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_a)`_ if the _accumulated_logs_hash_ outputted from the tail private kernel circuit is not empty.
- _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_c)`_
- Repeat the process until all _logs_hashes_ are collectively hashed.
- `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs.

### Encoding

Expand Down Expand Up @@ -273,11 +270,9 @@ After successfully decrypting an encrypted log, one can use the _randomness_ in
- _`log_hash_a = hash(log_hash_a, contract_address_tag_a)`_
- Repeat the process for all _log_hashes_ in the transaction.

2. Accumulate all the hashes and outputs the final hash to the public inputs:
2. Accumulate all the hashes in the tail and outputs the final hash to the public inputs:

- _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_
- _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_
- Repeat the process until all _logs_hashes_ are collectively hashed.
- `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs, with hashes defined above.

### Encoding

Expand Down Expand Up @@ -310,9 +305,7 @@ As each encrypted note preimage can be associated with a note in the same transa

The kernel circuit simply accumulates all the hashes:

- _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_
- _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_
- Repeat the process until all _logs_hashes_ are collectively hashed.
- `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs.

### Encoding

Expand Down
19 changes: 16 additions & 3 deletions l1-contracts/src/core/libraries/decoders/TxsDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ library TxsDecoder {
uint256 remainingLogsLength = read4(_body, offset);
offset += 0x4;

bytes32 kernelPublicInputsLogsHash; // The hash on the output of kernel iteration
bytes memory flattenedLogHashes; // The hash input

// Iterate until all the logs were processed
while (remainingLogsLength > 0) {
Expand All @@ -245,13 +245,26 @@ library TxsDecoder {
bytes32 singleLogHash = Hash.sha256ToField(slice(_body, offset, singleCallLogsLength));
offset += singleCallLogsLength;

kernelPublicInputsLogsHash =
Hash.sha256ToField(bytes.concat(kernelPublicInputsLogsHash, singleLogHash));
flattenedLogHashes = bytes.concat(flattenedLogHashes, singleLogHash);

privateCircuitPublicInputLogsLength -= (singleCallLogsLength + 0x4);
}
}

// Not having a 0 value hash for empty logs causes issues with empty txs used for padding.
if (flattenedLogHashes.length == 0) {
return (0, offset);
}

// padded to MAX_LOGS * 32 bytes
// NB: this assumes MAX_ENCRYPTED_LOGS_PER_TX == MAX_UNENCRYPTED_LOGS_PER_TX
flattenedLogHashes = bytes.concat(
flattenedLogHashes,
new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - flattenedLogHashes.length)
);

bytes32 kernelPublicInputsLogsHash = Hash.sha256ToField(flattenedLogHashes);

return (kernelPublicInputsLogsHash, offset);
}

Expand Down
27 changes: 13 additions & 14 deletions l1-contracts/test/decoders/Decoders.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@ pragma solidity >=0.8.18;
import {DecoderBase} from "./Base.sol";

import {Hash} from "../../src/core/libraries/Hash.sol";
import {DataStructures} from "../../src/core/libraries/DataStructures.sol";

import {HeaderLibHelper} from "./helpers/HeaderLibHelper.sol";
import {TxsDecoderHelper} from "./helpers/TxsDecoderHelper.sol";
import {HeaderLib} from "../../src/core/libraries/HeaderLib.sol";

import {TxsDecoder} from "../../src/core/libraries/decoders/TxsDecoder.sol";

import {AvailabilityOracle} from "../../src/core/availability_oracle/AvailabilityOracle.sol";
import {Constants} from "../../src/core/libraries/ConstantsGen.sol";

/**
* Blocks are generated using the `integration_l1_publisher.test.ts` tests.
Expand Down Expand Up @@ -196,13 +192,12 @@ contract DecodersTest is DecoderBase {
abi.encodePacked(hex"0000000c00000008", hex"00000004", firstFunctionCallLogs);
(bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs);

// Zero because this is the first iteration
bytes32 previousKernelPublicInputsLogsHash = bytes32(0);
bytes32 privateCircuitPublicInputsLogsHashFirstCall = Hash.sha256ToField(firstFunctionCallLogs);

bytes32 referenceLogsHash = Hash.sha256ToField(
abi.encodePacked(
previousKernelPublicInputsLogsHash, privateCircuitPublicInputsLogsHashFirstCall
privateCircuitPublicInputsLogsHashFirstCall,
new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 32)
)
);

Expand All @@ -229,15 +224,16 @@ contract DecodersTest is DecoderBase {
);
(bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs);

bytes32 referenceLogsHashFromIteration1 =
Hash.sha256ToField(abi.encodePacked(bytes32(0), Hash.sha256ToField(firstFunctionCallLogs)));
bytes32 referenceLogsHashFromIteration1 = Hash.sha256ToField(firstFunctionCallLogs);

bytes32 privateCircuitPublicInputsLogsHashSecondCall =
Hash.sha256ToField(secondFunctionCallLogs);

bytes32 referenceLogsHashFromIteration2 = Hash.sha256ToField(
abi.encodePacked(
referenceLogsHashFromIteration1, privateCircuitPublicInputsLogsHashSecondCall
referenceLogsHashFromIteration1,
privateCircuitPublicInputsLogsHashSecondCall,
new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64)
)
);

Expand Down Expand Up @@ -269,16 +265,19 @@ contract DecodersTest is DecoderBase {
);
(bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs);

bytes32 referenceLogsHashFromIteration1 =
Hash.sha256ToField(abi.encodePacked(bytes32(0), Hash.sha256ToField(firstFunctionCallLogs)));
bytes32 referenceLogsHashFromIteration1 = Hash.sha256ToField(firstFunctionCallLogs);

// Note: as of resolving #5017, we now hash logs inside the circuits
// Following the YP, we skip any zero length logs, hence no use of secondFunctionCallLogs here

bytes32 privateCircuitPublicInputsLogsHashThirdCall = Hash.sha256ToField(thirdFunctionCallLogs);

bytes32 referenceLogsHashFromIteration3 = Hash.sha256ToField(
abi.encodePacked(referenceLogsHashFromIteration1, privateCircuitPublicInputsLogsHashThirdCall)
abi.encodePacked(
referenceLogsHashFromIteration1,
privateCircuitPublicInputsLogsHashThirdCall,
new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64)
)
);

assertEq(bytesAdvanced, encodedLogs.length, "Advanced by an incorrect number of bytes");
Expand Down
6 changes: 1 addition & 5 deletions noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// docs:start:encrypted_import
use dep::aztec::log::emit_encrypted_log;
// docs:end:encrypted_import
use dep::aztec::{
protocol_types::{address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER},
note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption},
Expand Down Expand Up @@ -45,8 +42,7 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
fn broadcast(self, context: &mut PrivateContext, slot: Field) {
let encryption_pub_key = get_public_key(self.owner);
// docs:start:encrypted
emit_encrypted_log(
context,
context.emit_encrypted_log(
(*context).this_address(),
slot,
Self::get_note_type_id(),
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/context/avm_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl PublicContextInterface for AvmContext {
nullifier_exists(unsiloed_nullifier, address.to_field()) == 1
}

fn emit_unencrypted_log<T>(&mut self, log: T) {
fn emit_unencrypted_log<T,N,M>(&mut self, log: T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The AVM context and interface was only changed to allow the public and private contexts to use traits in logs_traits (these define fixed lengths for many types, so we can sha hash inside the circuit). These don't need to be carried over!

let event_selector = 5; // Matches current PublicContext.
self.emit_unencrypted_log(event_selector, log);
}
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/context/interface.nr
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ trait PublicContextInterface {
fn fee_per_l2_gas(self) -> Field;
fn message_portal(&mut self, recipient: EthAddress, content: Field);
fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field);
fn emit_unencrypted_log<T>(&mut self, log: T);
fn emit_unencrypted_log<T,N,M>(&mut self, log: T);
fn call_public_function<RETURNS_COUNT>(
self: &mut Self,
contract_address: AztecAddress,
Expand Down
Loading
Loading