Skip to content

Commit 6f4a3db

Browse files
committed
Conanicalize state values
1 parent f9d2735 commit 6f4a3db

3 files changed

Lines changed: 83 additions & 5 deletions

File tree

libs/gl-client/src/persist.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod canonical;
2+
13
use anyhow::anyhow;
24
use lightning_signer::bitcoin::secp256k1::PublicKey;
35
use lightning_signer::chain::tracker::ChainTracker;
@@ -19,6 +21,8 @@ use std::str::FromStr;
1921
use std::sync::Arc;
2022
use std::sync::Mutex;
2123

24+
use self::canonical::{canonical_json_bytes, CanonicalJsonValue};
25+
2226
const NODE_PREFIX: &str = "nodes";
2327
const NODE_STATE_PREFIX: &str = "nodestates";
2428
const CHANNEL_PREFIX: &str = "channels";
@@ -41,6 +45,10 @@ impl StateEntry {
4145
signature: vec![],
4246
}
4347
}
48+
49+
fn canonical_value_bytes(&self) -> anyhow::Result<Vec<u8>> {
50+
canonical_json_bytes(&self.value)
51+
}
4452
}
4553

4654
impl Serialize for StateEntry {
@@ -55,7 +63,7 @@ impl Serialize for StateEntry {
5563
};
5664

5765
seq.serialize_element(&self.version)?;
58-
seq.serialize_element(&self.value)?;
66+
seq.serialize_element(&CanonicalJsonValue(&self.value))?;
5967
if !self.signature.is_empty() {
6068
seq.serialize_element(&self.signature)?;
6169
}
@@ -500,8 +508,7 @@ impl State {
500508
if !entry.signature.is_empty() {
501509
continue;
502510
}
503-
let value = serde_json::to_vec(&entry.value)
504-
.map_err(|e| anyhow!("failed to serialize state value for key {key}: {e}"))?;
511+
let value = entry.canonical_value_bytes()?;
505512
let signature = signer(key, entry.version, &value)?;
506513
entry.signature = signature;
507514
changed += 1;
@@ -589,7 +596,9 @@ impl Into<Vec<crate::pb::SignerStateEntry>> for State {
589596
.iter()
590597
.map(|(k, v)| crate::pb::SignerStateEntry {
591598
key: k.to_owned(),
592-
value: serde_json::to_vec(&v.value).unwrap(),
599+
value: v
600+
.canonical_value_bytes()
601+
.expect("canonical signer state value"),
593602
version: v.version,
594603
signature: v.signature.clone(),
595604
})
@@ -1047,6 +1056,18 @@ mod tests {
10471056
assert_eq!(tuple[2], json!([7, 8, 9]));
10481057
}
10491058

1059+
#[test]
1060+
fn state_entry_canonical_value_bytes_sorts_nested_object_keys() {
1061+
let entry = StateEntry::new(0, json!({
1062+
"z": {"b": 1, "a": 2},
1063+
"a": [{"d": 4, "c": 3}]
1064+
}));
1065+
1066+
let bytes = entry.canonical_value_bytes().unwrap();
1067+
1068+
assert_eq!(bytes, br#"{"a":[{"c":3,"d":4}],"z":{"a":2,"b":1}}"#);
1069+
}
1070+
10501071
#[test]
10511072
fn signer_state_entry_conversions_preserve_signature() {
10521073
let entries = vec![SignerStateEntry {
@@ -1066,6 +1087,22 @@ mod tests {
10661087
assert_eq!(roundtrip, entries);
10671088
}
10681089

1090+
#[test]
1091+
fn signer_state_entry_conversions_emit_canonical_value_bytes() {
1092+
let entries = vec![SignerStateEntry {
1093+
version: 6,
1094+
key: "k".to_string(),
1095+
value: br#"{ "b": 1, "a": 2 }"#.to_vec(),
1096+
signature: vec![11, 12],
1097+
}];
1098+
1099+
let state: State = entries.into();
1100+
let roundtrip: Vec<SignerStateEntry> = state.into();
1101+
assert_eq!(roundtrip.len(), 1);
1102+
assert_eq!(roundtrip[0].value, br#"{"a":2,"b":1}"#);
1103+
assert_eq!(roundtrip[0].signature, vec![11, 12]);
1104+
}
1105+
10691106
#[test]
10701107
fn merge_newer_entry_propagates_signature() {
10711108
let mut base = mk_state(vec![("k", 1, json!({"v": 1}))]);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use anyhow::anyhow;
2+
use serde::ser::{SerializeMap, SerializeSeq};
3+
use serde::{Serialize, Serializer};
4+
5+
pub struct CanonicalJsonValue<'a>(pub &'a serde_json::Value);
6+
7+
impl Serialize for CanonicalJsonValue<'_> {
8+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
9+
where
10+
S: Serializer,
11+
{
12+
match self.0 {
13+
serde_json::Value::Null => serializer.serialize_unit(),
14+
serde_json::Value::Bool(v) => serializer.serialize_bool(*v),
15+
serde_json::Value::Number(v) => v.serialize(serializer),
16+
serde_json::Value::String(v) => serializer.serialize_str(v),
17+
serde_json::Value::Array(values) => {
18+
let mut seq = serializer.serialize_seq(Some(values.len()))?;
19+
for value in values {
20+
seq.serialize_element(&CanonicalJsonValue(value))?;
21+
}
22+
seq.end()
23+
}
24+
serde_json::Value::Object(values) => {
25+
let mut entries: Vec<_> = values.iter().collect();
26+
entries.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
27+
28+
let mut map = serializer.serialize_map(Some(entries.len()))?;
29+
for (key, value) in entries {
30+
map.serialize_entry(key, &CanonicalJsonValue(value))?;
31+
}
32+
map.end()
33+
}
34+
}
35+
}
36+
}
37+
38+
pub fn canonical_json_bytes(value: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
39+
serde_json::to_vec(&CanonicalJsonValue(value))
40+
.map_err(|e| anyhow!("failed to serialize canonical signer state value: {e}"))
41+
}

libs/gl-client/src/signer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ impl Signer {
411411
version: u64,
412412
value: &[u8],
413413
) -> Result<Vec<u8>, anyhow::Error> {
414-
let digest = Self::state_signature_digest(key, version, value);
414+
let digest = Self::state_signature_digest(key, version, &value);
415415
let msg = SecpMessage::from_digest_slice(&digest).map_err(|e| {
416416
anyhow!(
417417
"failed to build state signature digest for key {}: {}",

0 commit comments

Comments
 (0)