Skip to content
Draft
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ alloy = { version = "1.2.1", features = [
"rpc-client",
"rpc-types-beacon",
] }
alloy-primitives = "1.5.7"

# common
anyhow = "1.0.56"
Expand Down
2 changes: 2 additions & 0 deletions libs/driver/src/clients/synchronizer_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ impl SynchronizerClient for HttpSynchronizerClient {

let state_header = StateHeader::new(
payload.block_number,
payload.block_timestamp,
payload.block_hash,
payload.created_root,
payload.nullifiers_root,
payload.prior_state_history_root,
Expand Down
2 changes: 2 additions & 0 deletions libs/driver/src/object_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ mod tests {
GroundingWitness::new(
StateHeader::new(
1,
1,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
Expand Down
2 changes: 2 additions & 0 deletions libs/driver/src/pexe_catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,8 @@ description = "consume a Foo to make a Bar"
txlib::GroundingWitness::new(
txlib::StateHeader::new(
1,
1,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
Expand Down
4 changes: 4 additions & 0 deletions libs/driver/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ fn dummy_grounding_witness() -> GroundingWitness {
GroundingWitness::new(
StateHeader::new(
1,
1,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
pod2::middleware::EMPTY_HASH,
Expand All @@ -66,6 +68,8 @@ fn state_header(state: &TestState) -> StateHeader {
let (created_root, nullifiers_root, prior_state_history_root) = state.roots();
StateHeader::new(
state.block_number,
state.block_timestamp,
state.block_hash,
created_root,
nullifiers_root,
prior_state_history_root,
Expand Down
21 changes: 18 additions & 3 deletions libs/payload/src/test_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use pod2::{
backends::plonky2::primitives::merkletree::MerkleProof,
middleware::{
Hash, Value,
F, Hash, Value,
containers::{Array, Set},
},
};
Expand All @@ -15,6 +15,8 @@ use pod2::{
#[derive(Clone, Debug)]
pub struct TestState {
pub block_number: i64,
pub block_timestamp: i64,
pub block_hash: Hash,
created: Array,
created_index: HashMap<Hash, i64>,
nullifiers: Set,
Expand All @@ -27,10 +29,18 @@ impl Default for TestState {
}
}

pub struct BlockMetadata {
pub number: u32,
pub timestamp: u64,
pub hash: Hash,
}

impl TestState {
pub fn empty(block_number: i64) -> Self {
Self {
block_number,
block_timestamp: block_number * 1000,
block_hash: Hash([F(0), F(0), F(0), F(block_number as u64)]),
created: Array::new(Vec::<Value>::new()),
created_index: HashMap::new(),
nullifiers: Set::new(HashSet::<Value>::new()),
Expand All @@ -54,15 +64,20 @@ impl TestState {
pub fn build_grounding_witness<W>(
&self,
input_commitments: &[Hash],
build: impl FnOnce(i64, Hash, Hash, Hash, HashMap<Hash, (i64, MerkleProof)>) -> W,
build: impl FnOnce(BlockMetadata, Hash, Hash, Hash, HashMap<Hash, (i64, MerkleProof)>) -> W,
) -> W {
let created_proofs = input_commitments
.iter()
.map(|commitment| (*commitment, self.created_membership_proof(*commitment)))
.collect::<HashMap<_, _>>();
let (created_root, nullifiers_root, prior_state_history_root) = self.roots();
let meta = BlockMetadata {
number: self.block_number as u32,
timestamp: self.block_timestamp as u64,
hash: self.block_hash,
};
build(
self.block_number,
meta,
created_root,
nullifiers_root,
prior_state_history_root,
Expand Down
9 changes: 8 additions & 1 deletion libs/pexe/src/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,14 @@ pub fn build_synthetic_state(
indices.insert(commitment, index);
}

let state_header = StateHeader::new(1, created.commitment(), EMPTY_HASH, EMPTY_HASH);
let state_header = StateHeader::new(
1,
1,
EMPTY_HASH,
created.commitment(),
EMPTY_HASH,
EMPTY_HASH,
);

let mut created_proofs: HashMap<Hash, _> = HashMap::with_capacity(objs.len());
for obj in objs {
Expand Down
1 change: 1 addition & 0 deletions libs/pod2utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ serde = { workspace = true }
serde_json = { workspace = true }
plonky2 = { workspace = true }
rand = { workspace = true }
alloy-primitives = { workspace = true }
16 changes: 15 additions & 1 deletion libs/pod2utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
pub mod macros;
pub mod mockintro;

use alloy_primitives::B256;
use plonky2::field::types::Field;
use pod2::middleware::{F, RawValue};
use pod2::middleware::{EMPTY_HASH, F, Hash, RawValue};
use rand::{RngCore, SeedableRng, rngs::StdRng};
use std::array;
use std::cell::RefCell;
Expand Down Expand Up @@ -35,3 +36,16 @@ pub fn rand_raw_value() -> RawValue {
}
})
}

/// Lossy conversion of a 256 bit value to a pod2 Hash
pub fn b256_to_hash(v: B256) -> Hash {
let mut limbs: [[u8; 8]; 4] = Default::default();
for (i, b) in v.0.iter().enumerate() {
limbs[i / 8][i % 8] = *b;
}
let mut hash = EMPTY_HASH;
for (i, l) in limbs.into_iter().enumerate() {
hash.0[i] = F::from_noncanonical_u64(u64::from_le_bytes(l));
}
hash
}
54 changes: 43 additions & 11 deletions libs/sdk/src/fmt_podlang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,13 @@ struct SubActionCall {
/// Same, for the sub's `out` record.
sub_out_var: Option<String>,
/// Script-side alias name (the `pick` in `var pick = action.subaction(...)`).
/// `None` if the user didn't bind via `var`. Used to skip the alias from
/// the parent's private wildcards list.
/// `None` if the user didn't bind via `var`. An alias the parent body
/// references becomes a parent wildcard pinned to the sub's out record;
/// an unreferenced alias is skipped from the private wildcards list.
alias: Option<String>,
/// Name of the sub's first out entry (always slot 0), the one the
/// alias refers to. `None` if the sub produces nothing.
first_out_entry: Option<String>,
}

/// Walk the parent action's Insts and gather one `SubActionCall` per
Expand Down Expand Up @@ -325,12 +329,14 @@ fn collect_sub_action_calls(action: &ActionContext, loader: &Loader) -> Vec<SubA
} else {
Some(alias_name)
};
let first_out_entry = sub_meta.out_entries.first().map(|e| e.varname.clone());

calls.push(SubActionCall {
sub_name: sub_name.clone(),
sub_in_var,
sub_out_var,
alias,
first_out_entry,
});
}
}
Expand Down Expand Up @@ -372,12 +378,17 @@ fn fmt_action(action: &ActionContext, loader: &Loader, w: &mut dyn fmt::Write) -
}
write!(w, "chain0, chain")?;

// Sub-action aliases: parent vars that hold a sub's first producing
// Object Ref. They're not real wildcards in the parent's predicate
// (the binding is structural to the script, not the proof) so we
// skip them from the private list.
let alias_names: std::collections::HashSet<String> =
sub_calls.iter().filter_map(|c| c.alias.clone()).collect();
// Sub-action aliases: parent vars that hold a sub's first out
// entry's Object Ref. A referenced alias becomes a real parent
// wildcard (pinned to the sub's out record by an ArrayContains
// clause below); an unreferenced alias is structural only and is
// skipped from the private list.
let referenced = crate::body_referenced_vars(&action.insts);
let alias_names: std::collections::HashSet<String> = sub_calls
.iter()
.filter_map(|c| c.alias.clone())
.filter(|alias| !referenced.contains(alias))
.collect();

// Private wildcards: every (var, ts) except sub-action aliases,
// chain endpoints (public chain0/chain), packed chain intermediates
Expand Down Expand Up @@ -483,6 +494,24 @@ fn fmt_action(action: &ActionContext, loader: &Loader, w: &mut dyn fmt::Write) -
)?;
}
}
// Pin each referenced sub-action alias to its sub's first out
// entry.
for call in &sub_calls {
let Some(alias) = &call.alias else { continue };
if !referenced.contains(alias) {
continue;
}
let (Some(out_var), Some(entry)) = (&call.sub_out_var, &call.first_out_entry) else {
continue;
};
writeln!(
w,
" ArrayContains({out_var}, {}::{}, {})",
schema_name(&call.sub_name, Side::Out),
entry,
fmt_var_at(alias, 0, meta.max_ts(alias)),
)?;
}

// ---- Body (Insts other than Object) ----
let mut sub_call_idx: usize = 0;
Expand Down Expand Up @@ -629,7 +658,10 @@ fn fmt_bridges(loader: &Loader, w: &mut dyn fmt::Write) -> fmt::Result {

// Bridge predicate signature: state, chain0, chain (public);
// in <ActionIn>, out <ActionOut> private as needed.
write!(w, "{bridge_name}(state, chain0, chain")?;
write!(
w,
"{bridge_name}(state, state_header StateHeader, chain0, chain"
)?;
let mut priv_parts: Vec<String> = Vec::new();
if !meta.in_entries.is_empty() {
priv_parts.push(format!("in {}", schema_name(&meta.name, Side::In)));
Expand Down Expand Up @@ -686,7 +718,7 @@ fn fmt_class(loader: &Loader, w: &mut dyn fmt::Write, class: &ClassMeta) -> fmt:
let o = &meta.object_refs[*obj_index];
let multi = is_multi_class(&meta.object_refs, &o.class);
let bridge_name = bridge_predicate_name(&o.class, action_name, &o.varname, multi);
writeln!(w, " {bridge_name}(state, chain0, chain)")?;
writeln!(w, " {bridge_name}(state, state_header, chain0, chain)")?;
}
writeln!(w, ")")?;
Ok(())
Expand All @@ -702,7 +734,7 @@ pub(crate) fn fmt(loader: &Loader, w: &mut dyn fmt::Write) -> fmt::Result {
// `tx`
writeln!(
w,
"record StateHeader = (block_number, created, nullifiers, prior_state_history)"
"record StateHeader = (block_number, block_time, block_hash, created, nullifiers, prior_state_history)"
)?;
fmt_record_decls(loader, w)?;
writeln!(w, "\n// Actions\n")?;
Expand Down
Loading