After a long discussion with @benjaminbollen we arrived at the following steps (which are not necessarily definite or correct, currently), to generate a full circuit that guarantees that block events are actually retrieved correct and that filtering over a fixed address provides the correct subset of events with that same address:
[x] The original event commitment merkle tree root, C_n, as well as the F-encoding of the fixed address are part of public events:
let event_root_commitment_targets = builder.add_virtual_hash();
let event_address_targets = builder.add_virtual_targets(5);
[x] For each event in the event set events: Vec<Vec<F>>, add targets for each
let event_targets = builder.add_virtual_targets(event.len());
[x] Generate a Merkle tree root HashOutTarget<F> from the event targets in the previous step:
let mut leaves_event_hash_targets = vec![];
for event_targets in all_event_targets {
// event_targets is of vec![Target; 5];
let hashed_targets = builder.hash_or_noop(event_targets)
}
...
// now connect the leaves together in a tree
let mut final_height = false
while !final_height {
// hash siblings
let parent_hashed_targets = builder.hash_or_noop(left_hash_sibling_targets, right_hash_sibling_targets);
...
}
// when having the final root, connect it to the to the merkle root targets 'attached' to `C_n`:
for i in 0..4 {
builder.connect(event_root_commitment_targets[i], computed_event_root_targets[i]);
}
[x] Check which events have corresponding address to the above (defined in the first point), as well as the addresses are selected correctly from the event data:
for each (event, event_targets) in events.iter().zip(all_events_targets) {
let event_address = event.address;
let event_address_targets = builder.add_virtual_targets(5); // each address has length 5
// connect each event address targets to the correct event_targets
// this ensures that event address targets correspond well to the range
// in the event target
for i in 0..5 {
builder.connect(event_address_targets[i], event_targets[2 + i]);
}
// continue by filtering events according to the fixed address
...
}
[ ] Check that each select event obtained in the previous point is connected to an actual event. We can do this in many ways, a possibility (to be further discussed) is that events as Vec<index, ..encoding>, in which case for each event, we generate targets as follows (as opposed to the first point):
for (index, event) in events.enumerate() {
event_targets = {
let event_index_target = builder.add_virtual_target(builder.constant(F::from_canonical_u64(index)));
let event_targets = builder.add_virtual_targets(event.len() + 1);
// store event targets in `all_event_targets
// connect the first target with `event_index_target`
builder.connect(event_index_target, event_targets[0]);
};
WARNING: If following this approach, do not hash the first index target to generate the Merkle root (on the builder), see point 2.
// loop over every selected event, i.e., events whose address matches that of the original one
for event_targets in selected_all_event_targets {
let index = builder.target_as_constant(event_targets[0]).to_canonical_u64();
builder.connect(all_event_targets[index as usize], selected_all_event_targets[index as usize]); // ignore cast math overflow issues for now
}
If we follow this approach we need to verify that indexes are well formed:
for (index, event_targets) in all_event_targets.enumerate() {
builder.connect(builder._true, builder.is_equal(event_targets[0], builder.constand(F::from_canonical_u64(index));
}
After a long discussion with @benjaminbollen we arrived at the following steps (which are not necessarily definite or correct, currently), to generate a full circuit that guarantees that block events are actually retrieved correct and that filtering over a fixed address provides the correct subset of events with that same address:
C_n
, as well as theF
-encoding of the fixed address are part of public events:events: Vec<Vec<F>>
, add targets for eachHashOutTarget<F>
from the event targets in the previous step:Vec<index, ..encoding>
, in which case for each event, we generate targets as follows (as opposed to the first point):WARNING: If following this approach, do not hash the first index target to generate the Merkle root (on the builder), see point 2.
If we follow this approach we need to verify that indexes are well formed: