Skip to content

Commit 8ff0f51

Browse files
committed
fix: return proper json when querying program mappings
1 parent c5728b7 commit 8ff0f51

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed

node/rest/src/helpers/json.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2019-2025 Provable Inc.
2+
// This file is part of the snarkOS library.
3+
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at:
7+
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use snarkvm::prelude::{Network, Plaintext, Value};
17+
18+
use anyhow::Result;
19+
20+
/// Convert a `Value` to JSON.
21+
/// `Value` implements Serialize by just calling `to_string()` on the value, which leads to Display
22+
/// output which (sometimes) looks like JSON but isn't.
23+
/// We produce actual JSON by calling `serde_json::to_value` on the inner object.
24+
pub(crate) fn value_to_json<N: Network>(mapping_value: Option<&Value<N>>) -> Result<serde_json::Value> {
25+
let json_value = if let Some(mapping_value) = mapping_value {
26+
match mapping_value {
27+
Value::Plaintext(plaintext) => match plaintext {
28+
Plaintext::Array(array, _) => serde_json::to_value(array)?,
29+
Plaintext::Struct(map, _) => serde_json::to_value(map)?,
30+
Plaintext::Literal(literal, _) => serde_json::to_value(literal)?,
31+
},
32+
Value::Record(record) => serde_json::to_value(record)?,
33+
Value::Future(future) => serde_json::to_value(future)?,
34+
}
35+
} else {
36+
serde_json::Value::Object(Default::default())
37+
};
38+
39+
Ok(json_value)
40+
}
41+
42+
pub(crate) fn mapping_to_json<N: Network>(mapping_values: &[(Plaintext<N>, Value<N>)]) -> Result<serde_json::Value> {
43+
let mut json = serde_json::Map::new();
44+
for (key, value) in mapping_values {
45+
let key = match key {
46+
// TODO: not sure if keys can be anything other than literals
47+
Plaintext::Array(array, _) => serde_json::to_string(&array)?,
48+
Plaintext::Struct(map, _) => serde_json::to_string(&map)?,
49+
// We can not use json here as it would cause the key to be escaped
50+
Plaintext::Literal(literal, _) => literal.to_string(),
51+
};
52+
json.insert(key, value_to_json(Some(value))?);
53+
}
54+
55+
Ok(serde_json::Value::Object(json))
56+
}

node/rest/src/helpers/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ pub use auth::*;
1818

1919
mod error;
2020
pub use error::*;
21+
22+
mod json;
23+
pub(crate) use json::*;

node/rest/src/routes.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// limitations under the License.
1515

1616
use super::*;
17+
use crate::helpers::{mapping_to_json, value_to_json};
18+
1719
use snarkos_node_router::messages::UnconfirmedSolution;
1820
use snarkvm::{
1921
ledger::puzzle::Solution,
@@ -353,15 +355,16 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
353355
let mapping_value = rest.ledger.vm().finalize_store().get_value_confirmed(id, name, &key)?;
354356

355357
// Check if metadata is requested and return the value with metadata if so.
356-
if metadata.metadata.unwrap_or(false) {
357-
return Ok(ErasedJson::pretty(json!({
358-
"data": mapping_value,
358+
let json_value = if metadata.metadata.is_some() {
359+
json!({
360+
"data": value_to_json(mapping_value.as_ref())?,
359361
"height": rest.ledger.latest_height(),
360-
})));
361-
}
362+
})
363+
} else {
364+
value_to_json(mapping_value.as_ref())?
365+
};
362366

363-
// Return the value without metadata.
364-
Ok(ErasedJson::pretty(mapping_value))
367+
Ok(ErasedJson::pretty(json_value))
365368
}
366369

367370
// GET /<network>/program/{programID}/mapping/{mappingName}?all={true}&metadata={true}
@@ -384,15 +387,17 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
384387
{
385388
Ok(Ok(mapping_values)) => {
386389
// Check if metadata is requested and return the mapping with metadata if so.
387-
if metadata.metadata.unwrap_or(false) {
388-
return Ok(ErasedJson::pretty(json!({
389-
"data": mapping_values,
390+
let json_value = if metadata.metadata.is_some() {
391+
json!({
392+
"data": mapping_to_json(&mapping_values)?,
390393
"height": height,
391-
})));
392-
}
394+
})
395+
} else {
396+
// Return the full mapping without metadata.
397+
mapping_to_json(&mapping_values)?
398+
};
393399

394-
// Return the full mapping without metadata.
395-
Ok(ErasedJson::pretty(mapping_values))
400+
Ok(ErasedJson::pretty(json_value))
396401
}
397402
Ok(Err(err)) => Err(RestError(format!("Unable to read mapping - {err}"))),
398403
Err(err) => Err(RestError(format!("Unable to read mapping - {err}"))),

0 commit comments

Comments
 (0)