Skip to content

Commit 1d487e8

Browse files
address comments
1 parent 08b9913 commit 1d487e8

File tree

1 file changed

+58
-68
lines changed

1 file changed

+58
-68
lines changed

wasm/src/programs/snapshot_query.rs

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// along with the Provable SDK library. If not, see <https://www.gnu.org/licenses/>.
1616

1717
use crate::types::native::CurrentNetwork;
18-
use crate::types::native::{ProgramIDNative, IdentifierNative, RecordPlaintextNative};
18+
use crate::types::native::{ProgramIDNative, IdentifierNative, RecordPlaintextNative, ViewKeyNative};
1919
use anyhow::{anyhow, bail, Result};
2020
use futures::future::join_all;
2121
use indexmap::IndexMap;
@@ -29,8 +29,7 @@ use snarkvm_console::{
2929
use snarkvm_ledger_query::QueryTrait;
3030
use std::str::FromStr;
3131

32-
/// A snapshot-based query object used to pin the block height, state root,
33-
/// and state paths to a single ledger view during online execution.
32+
/// A snapshot-based query object used to pin the block height, state root, and state paths to a single ledger view during online execution.
3433
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
3534
pub struct SnapshotQuery {
3635
block_height: u32,
@@ -75,47 +74,82 @@ impl SnapshotQuery {
7574
node_url: &str,
7675
program_id: &ProgramID<CurrentNetwork>,
7776
record_name: &Identifier<CurrentNetwork>,
78-
view_key: Field<CurrentNetwork>,
77+
view_key: &ViewKeyNative,
7978
js_inputs: &[JsValue],
8079
) -> Result<Self> {
8180
// 1) Extract commitments from inputs.
82-
let commitments = collect_commitments_from_inputs(program_id, record_name, view_key, js_inputs)?;
81+
let commitments = Self::collect_commitments_from_inputs(program_id, record_name, view_key, js_inputs)?;
8382

84-
// Fast path: if there are no record inputs, still pin a snapshot (height + root) for consistency.
85-
let (snap_root, snap_height) = snapshot_head(node_url).await?;
83+
// 2) Take snapshot and build base query.
84+
let (snap_root, snap_height) = Self::snapshot_head(node_url).await?;
8685
let mut query = SnapshotQuery::new(snap_height, &snap_root)?;
8786

88-
8987
if commitments.is_empty() {
9088
return Ok(query);
9189
}
9290

93-
// 2) Fetch state paths concurrently (anchored to the chosen snapshot).
94-
// If your node cannot fetch-at-root, you can fetch plain paths, parse their embedded roots,
95-
// and ensure they're consistent; otherwise re-snapshot and retry.
96-
// Precompute owned strings to avoid borrowing temporaries into async futures.
97-
let cm_strings: Vec<String> = commitments.iter().map(|c| c.to_string()).collect();
91+
// 3) Fetch state paths concurrently at that root.
92+
let commitment_strings: Vec<String> = commitments.iter().map(|c| c.to_string()).collect();
9893
let root_str = snap_root.clone();
99-
let futs = cm_strings
100-
.iter()
101-
.map(|cm_s| fetch_state_path_at_root(node_url, cm_s.as_str(), root_str.as_str()));
94+
let futs = commitment_strings.iter().map(|commitment_s| {
95+
Self::fetch_state_path_at_root(node_url, commitment_s.as_str(), root_str.as_str())
96+
});
10297
let results = join_all(futs).await;
10398

104-
// 3) Insert all paths; verify consistency against the pinned root.
105-
for (cm, res) in commitments.iter().zip(results.into_iter()) {
106-
let sp_str = res?;
107-
// Optional safety: parse and ensure the path's root matches the pinned root.
108-
let sp = StatePath::<CurrentNetwork>::from_str(&sp_str).map_err(|e| anyhow!(e.to_string()))?;
109-
let path_root = sp.global_state_root().to_string();
99+
// 4) Insert paths and sanity check.
100+
for (commitment, res) in commitments.iter().zip(results.into_iter()) {
101+
let state_path_str = res?;
102+
let state_path = StatePath::<CurrentNetwork>::from_str(&state_path_str)
103+
.map_err(|e| anyhow!(e.to_string()))?;
104+
let path_root = state_path.global_state_root().to_string();
110105
if path_root != snap_root {
111-
// Strategy: bail and let caller retry; or resnapshot + refetch here.
112106
bail!("State path root mismatch: expected {}, got {}", snap_root, path_root);
113107
}
114-
query.add_state_path(&cm.to_string(), &sp_str)?;
108+
query.add_state_path(&commitment.to_string(), &state_path_str)?;
115109
}
116110

117111
Ok(query)
118112
}
113+
114+
/// Detect plaintext records in `js_inputs` and compute their commitments.
115+
fn collect_commitments_from_inputs(
116+
program_id: &ProgramIDNative,
117+
record_name: &IdentifierNative,
118+
view_key: &ViewKeyNative,
119+
js_inputs: &[wasm_bindgen::JsValue],
120+
) -> anyhow::Result<Vec<Field<CurrentNetwork>>> {
121+
let mut out = Vec::new();
122+
123+
for js in js_inputs {
124+
if let Some(s) = js.as_string() {
125+
// Heuristic: plaintext record strings contain `_nonce`.
126+
if !s.contains("_nonce") { continue; }
127+
128+
if let Ok(rec) = RecordPlaintextNative::from_str(&s) {
129+
let record_view_key: Field<CurrentNetwork> =
130+
(*rec.nonce() * &**view_key).to_x_coordinate();
131+
132+
let commitment = rec
133+
.to_commitment(program_id, record_name, &record_view_key)?;
134+
out.push(commitment);
135+
}
136+
}
137+
}
138+
Ok(out)
139+
}
140+
141+
async fn snapshot_head(_node_url: &str) -> Result<(String, u32)> {
142+
Err(anyhow!("snapshot_head() not implemented"))
143+
}
144+
145+
async fn fetch_state_path_at_root(
146+
_node_url: &str,
147+
_commitment: &str,
148+
_state_root: &str,
149+
) -> anyhow::Result<String> {
150+
Err(anyhow!("fetch_state_path_at_root() not implemented"))
151+
}
152+
119153
}
120154

121155
#[async_trait::async_trait(?Send)]
@@ -156,47 +190,3 @@ impl QueryTrait<CurrentNetwork> for SnapshotQuery {
156190
Ok(self.block_height)
157191
}
158192
}
159-
160-
/* --------------------------- internal helpers --------------------------- */
161-
162-
/// Heuristic: detect plaintext records from JS inputs and compute commitments.
163-
fn collect_commitments_from_inputs(
164-
program_id: &ProgramIDNative,
165-
record_name: &IdentifierNative,
166-
view_key: Field<CurrentNetwork>,
167-
js_inputs: &[JsValue],
168-
) -> anyhow::Result<Vec<Field<CurrentNetwork>>> {
169-
let mut out = Vec::new();
170-
171-
for js in js_inputs {
172-
if let Some(s) = js.as_string() {
173-
// Quick filter: record plaintexts include a `_nonce` field.
174-
if !s.contains("_nonce") {
175-
continue;
176-
}
177-
// Try parse as a plaintext record. Skip if not a record.
178-
if let Ok(rec) = RecordPlaintextNative::from_str(&s) {
179-
let cm = rec.to_commitment(program_id, record_name, &view_key);
180-
out.push(cm?);
181-
}
182-
}
183-
}
184-
Ok(out)
185-
}
186-
187-
/// Return a single `(state_root_string, block_height)` snapshot.
188-
/// TODO: replace with real node client calls.
189-
async fn snapshot_head(_node_url: &str) -> Result<(String, u32)> {
190-
// Example (pseudo):
191-
// let head = client.latest_head(_node_url).await?;
192-
// Ok((head.state_root, head.block_height))
193-
Err(anyhow!("snapshot_head() not implemented"))
194-
}
195-
196-
/// Fetch a `StatePath` (as string) for `commitment` anchored to `state_root`
197-
/// TODO: replace with real node client call and ensure it returns a path at the requested root
198-
async fn fetch_state_path_at_root(_node_url: &str, _commitment: &str, _state_root: &str) -> anyhow::Result<String> {
199-
// Example (pseudo):
200-
// client.state_path_at_root(_node_url, _commitment, _state_root).await
201-
Err(anyhow!("fetch_state_path_at_root() not implemented"))
202-
}

0 commit comments

Comments
 (0)