Skip to content

Commit

Permalink
refactor: implementing deserialize() in Noir structs (#4384)
Browse files Browse the repository at this point in the history
Implementing deserialize in a few structs
  • Loading branch information
benesjan authored Feb 3, 2024
1 parent 2bdb578 commit e63bbae
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 104 deletions.
67 changes: 6 additions & 61 deletions yarn-project/aztec-nr/aztec/src/context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -311,51 +311,8 @@ impl PrivateContext {

let item = PrivateCallStackItem {
contract_address: AztecAddress::from_field(reader.read()),
function_data: FunctionData {
selector: FunctionSelector::from_field(reader.read()),
is_internal: reader.read() as bool,
is_private: reader.read() as bool,
is_constructor: reader.read() as bool,
},
public_inputs: PrivateCircuitPublicInputs {
call_context: CallContext {
msg_sender : AztecAddress::from_field(reader.read()),
storage_contract_address : AztecAddress::from_field(reader.read()),
portal_contract_address : EthAddress::from_field(reader.read()),
function_selector: FunctionSelector::from_field(reader.read()), // practically same as fields[1]
is_delegate_call : reader.read() as bool,
is_static_call : reader.read() as bool,
is_contract_deployment: reader.read() as bool,
start_side_effect_counter: reader.read() as u32,
},
args_hash: reader.read(),
return_values: reader.read_array([0; RETURN_VALUES_LENGTH]), // +1
read_requests: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_READ_REQUESTS_PER_CALL]),
nullifier_key_validation_requests: reader.read_struct_array(NullifierKeyValidationRequest::deserialize, [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL]),
new_commitments: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]),
new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]),
private_call_stack_hashes: reader.read_array([0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]),
public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]),
new_l2_to_l1_msgs: reader.read_array([0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]),
end_side_effect_counter: reader.read() as u32,
encrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),
unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),
encrypted_log_preimages_length: reader.read(),
unencrypted_log_preimages_length: reader.read(),
historical_header: reader.read_struct(Header::deserialize),
contract_deployment_data: ContractDeploymentData {
public_key: GrumpkinPoint {
x: reader.read(),
y: reader.read()
},
initialization_hash : reader.read(),
contract_class_id : ContractClassId::from_field(reader.read()),
contract_address_salt : reader.read(),
portal_contract_address : EthAddress::from_field(reader.read()),
},
chain_id: reader.read(),
version: reader.read(),
},
function_data: reader.read_struct(FunctionData::deserialize),
public_inputs: reader.read_struct(PrivateCircuitPublicInputs::deserialize),
is_execution_request: reader.read() as bool,
};

Expand Down Expand Up @@ -420,25 +377,13 @@ impl PrivateContext {

let mut reader = Reader::new(fields);

// Note: Not using PublicCirclePublicInputs::deserialize here, because everything below args_hash is 0 and
// there is no more data in fields because there is only ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE fields!
let item = PublicCallStackItem {
contract_address: AztecAddress::from_field(reader.read()),
function_data: FunctionData {
selector: FunctionSelector::from_field(reader.read()),
is_internal: reader.read() as bool,
is_private: reader.read() as bool,
is_constructor: reader.read() as bool,
},
function_data: reader.read_struct(FunctionData::deserialize),
public_inputs: PublicCircuitPublicInputs {
call_context: CallContext {
msg_sender : AztecAddress::from_field(reader.read()),
storage_contract_address : AztecAddress::from_field(reader.read()),
portal_contract_address : EthAddress::from_field(reader.read()),
function_selector: FunctionSelector::from_field(reader.read()), // practically same as fields[1]
is_delegate_call : reader.read() as bool,
is_static_call : reader.read() as bool,
is_contract_deployment: reader.read() as bool,
start_side_effect_counter: reader.read() as u32,
},
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
return_values: [0; RETURN_VALUES_LENGTH],
contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL],
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec-nr/aztec/src/utils.nr
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub fn full_field_greater_than(lhs: Field, rhs: Field) -> bool {
rhs.lt(lhs)
}

// TODO(benesjan): also in circuits. NUKE THIS!
struct Reader<N> {
data: [Field; N],
offset: Field,
Expand All @@ -41,6 +42,7 @@ impl<N> Reader<N> {
result
}

// TODO(#4394)
pub fn read_struct<T, K>(&mut self, deserialise: fn([Field; K]) -> T) -> T {
let result = deserialise(self.read_array([0; K]));
result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ use crate::{
GENERATOR_INDEX__CALL_CONTEXT,
},
hash::pedersen_hash,
traits::{
Deserialize,
Hash,
Serialize,
},
};
use dep::std::cmp::Eq;
use crate::traits::{Hash, Serialize};

// docs:start:call-context
struct CallContext {
Expand Down Expand Up @@ -75,6 +79,21 @@ impl Serialize<CALL_CONTEXT_LENGTH> for CallContext {
}
}

impl Deserialize<CALL_CONTEXT_LENGTH> for CallContext {
fn deserialize(serialized: [Field; CALL_CONTEXT_LENGTH]) -> CallContext {
CallContext {
msg_sender: AztecAddress::from_field(serialized[0]),
storage_contract_address: AztecAddress::from_field(serialized[1]),
portal_contract_address: EthAddress::from_field(serialized[2]),
function_selector: FunctionSelector::from_field(serialized[3]),
is_delegate_call: serialized[4] as bool,
is_static_call: serialized[5] as bool,
is_contract_deployment: serialized[6] as bool,
start_side_effect_counter: serialized[7] as u32,
}
}
}

#[test]
fn serialization_smoke() {
let context: CallContext = dep::std::unsafe::zeroed();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
use crate::abis::function_selector::FunctionSelector;
use crate::constants::GENERATOR_INDEX__FUNCTION_DATA;
use crate::{
abis::function_selector::FunctionSelector,
constants::{
GENERATOR_INDEX__FUNCTION_DATA,
FUNCTION_DATA_LENGTH,
},
hash::pedersen_hash,
traits::{
Serialize,
Hash,
Deserialize,
},
};

struct FunctionData {
// First four bytes of the abi encoding
Expand All @@ -10,16 +21,33 @@ struct FunctionData {
is_constructor : bool,
}

impl FunctionData {
impl Hash for FunctionData {
fn hash(self) -> Field {
pedersen_hash(self.serialize(), GENERATOR_INDEX__FUNCTION_DATA)
}
}

impl Serialize<FUNCTION_DATA_LENGTH> for FunctionData {
// A field is ~256 bits
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/3057): Since, function data can fit into a Field,
// This method will simply return a bit packed Field instead of hashing
fn hash(self) -> Field {
dep::std::hash::pedersen_hash_with_separator([
fn serialize(self) -> [Field; FUNCTION_DATA_LENGTH] {
[
self.selector.to_field(),
self.is_internal as Field,
self.is_private as Field,
self.is_constructor as Field,
], GENERATOR_INDEX__FUNCTION_DATA)
]
}
}

impl Deserialize<FUNCTION_DATA_LENGTH> for FunctionData {
fn deserialize(serialized: [Field; FUNCTION_DATA_LENGTH]) -> Self {
Self {
selector: FunctionSelector::from_field(serialized[0]),
is_internal: serialized[1] as bool,
is_private: serialized[2] as bool,
is_constructor: serialized[3] as bool,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use crate::{
abis::{
call_context::CallContext,
nullifier_key_validation_request::NullifierKeyValidationRequest,
side_effect::{SideEffect, SideEffectLinkedToNoteHash},
side_effect::{
SideEffect,
SideEffectLinkedToNoteHash,
},
},
contrakt::deployment_data::ContractDeploymentData,
hash::pedersen_hash,
header::Header,
};
use crate::constants::{
constants::{
MAX_READ_REQUESTS_PER_CALL,
MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL,
MAX_NEW_COMMITMENTS_PER_CALL,
Expand All @@ -20,8 +19,17 @@ use crate::constants::{
RETURN_VALUES_LENGTH,
PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH,
GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS,
},
contrakt::deployment_data::ContractDeploymentData,
header::Header,
hash::pedersen_hash,
traits::{
Deserialize,
Hash,
Serialize,
},
utils::reader::Reader,
};
use crate::traits::{Hash, Serialize};

struct PrivateCircuitPublicInputs {
call_context: CallContext,
Expand Down Expand Up @@ -97,6 +105,37 @@ impl Serialize<PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PrivateCircuitPublicInp
}
}

impl Deserialize<PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PrivateCircuitPublicInputs {
fn deserialize(serialized: [Field; PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH]) -> Self {
// TODO(#4390): This should accept a reader ^ to avoid copying data.
let mut reader = Reader::new(serialized);
let inputs = Self {
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
return_values: reader.read_array([0; RETURN_VALUES_LENGTH]),
read_requests: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_READ_REQUESTS_PER_CALL]),
nullifier_key_validation_requests: reader.read_struct_array(NullifierKeyValidationRequest::deserialize, [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL]),
new_commitments: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]),
new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]),
private_call_stack_hashes: reader.read_array([0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]),
public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]),
new_l2_to_l1_msgs: reader.read_array([0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]),
end_side_effect_counter: reader.read() as u32,
encrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),
unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),
encrypted_log_preimages_length: reader.read(),
unencrypted_log_preimages_length: reader.read(),
historical_header: reader.read_struct(Header::deserialize),
contract_deployment_data: reader.read_struct(ContractDeploymentData::deserialize),
chain_id: reader.read(),
version: reader.read(),
};

reader.finish();
inputs
}
}

impl Hash for PrivateCircuitPublicInputs {
fn hash(self) -> Field {
pedersen_hash(self.serialize(), GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
use crate::constants::{
MAX_NEW_L2_TO_L1_MSGS_PER_CALL,
MAX_NEW_NULLIFIERS_PER_CALL,
MAX_NEW_COMMITMENTS_PER_CALL,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
MAX_PUBLIC_DATA_READS_PER_CALL,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
NUM_FIELDS_PER_SHA256,
RETURN_VALUES_LENGTH,
GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS,
PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH,
};
use crate::{
abis::{
call_context::CallContext,
side_effect::{SideEffect, SideEffectLinkedToNoteHash},
},
address::AztecAddress,
constants::{
MAX_NEW_L2_TO_L1_MSGS_PER_CALL,
MAX_NEW_NULLIFIERS_PER_CALL,
MAX_NEW_COMMITMENTS_PER_CALL,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
MAX_PUBLIC_DATA_READS_PER_CALL,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
NUM_FIELDS_PER_SHA256,
RETURN_VALUES_LENGTH,
GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS,
PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH,
},
contrakt::{
storage_read::StorageRead,
storage_update_request::StorageUpdateRequest,
},
hash::pedersen_hash,
header::Header,
traits::{
Hash,
Serialize,
Deserialize,
},
utils::reader::Reader,
};
use crate::traits::{Hash, Serialize, Deserialize};

struct PublicCircuitPublicInputs{
call_context: CallContext,
Expand Down Expand Up @@ -82,6 +87,31 @@ impl Serialize<PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PublicCircuitPublicInput
}
}

impl Deserialize<PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH> for PublicCircuitPublicInputs {
fn deserialize(serialized: [Field; PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH]) -> Self {
// TODO(#4390): This should accept a reader ^ to avoid copying data.
let mut reader = Reader::new(serialized);
let inputs = PublicCircuitPublicInputs {
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
return_values: reader.read_array([0; RETURN_VALUES_LENGTH]),
contract_storage_update_requests: reader.read_struct_array(StorageUpdateRequest::deserialize, [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL]),
contract_storage_reads: reader.read_struct_array(StorageRead::deserialize, [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL]),
public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]),
new_commitments: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]),
new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]),
new_l2_to_l1_msgs: reader.read_array([0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]),
unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),
unencrypted_log_preimages_length: reader.read(),
historical_header: reader.read_struct(Header::deserialize),
prover_address: reader.read_struct(AztecAddress::deserialize),
};

reader.finish();
inputs
}
}

impl Hash for PublicCircuitPublicInputs {
fn hash(self) -> Field {
pedersen_hash(self.serialize(), GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS)
Expand Down
Loading

0 comments on commit e63bbae

Please sign in to comment.