Skip to main content

gl_client/
persist.rs

1mod canonical;
2
3use anyhow::anyhow;
4use lightning_signer::bitcoin::secp256k1::PublicKey;
5use lightning_signer::chain::tracker::ChainTracker;
6use lightning_signer::channel::ChannelId;
7use lightning_signer::channel::ChannelStub;
8use lightning_signer::node::NodeConfig;
9use lightning_signer::node::NodeState;
10use lightning_signer::persist::ChainTrackerListenerEntry;
11use lightning_signer::persist::{Error, Persist, SignerId};
12use lightning_signer::policy::validator::ValidatorFactory;
13use lightning_signer::SendSync;
14use log::{trace, warn};
15use serde::de::{self, SeqAccess, Visitor};
16use serde::ser::SerializeSeq;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18use std::collections::BTreeMap;
19use std::fmt;
20use std::str::FromStr;
21use std::sync::Arc;
22use std::sync::Mutex;
23
24use self::canonical::{canonical_json_bytes, CanonicalJsonValue};
25
26const NODE_PREFIX: &str = "nodes";
27const NODE_STATE_PREFIX: &str = "nodestates";
28const CHANNEL_PREFIX: &str = "channels";
29const ALLOWLIST_PREFIX: &str = "allowlists";
30const TRACKER_PREFIX: &str = "trackers";
31const TOMBSTONE_VERSION: u64 = u64::MAX;
32
33#[derive(Clone, Debug, PartialEq)]
34struct StateEntry {
35    version: u64,
36    value: serde_json::Value,
37    signature: Vec<u8>,
38}
39
40impl StateEntry {
41    fn new(version: u64, value: serde_json::Value) -> Self {
42        Self {
43            version,
44            value,
45            signature: vec![],
46        }
47    }
48
49    fn canonical_value_bytes(&self) -> anyhow::Result<Vec<u8>> {
50        canonical_json_bytes(&self.value)
51    }
52}
53
54impl Serialize for StateEntry {
55    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56    where
57        S: Serializer,
58    {
59        let mut seq = if self.signature.is_empty() {
60            serializer.serialize_seq(Some(2))?
61        } else {
62            serializer.serialize_seq(Some(3))?
63        };
64
65        seq.serialize_element(&self.version)?;
66        seq.serialize_element(&CanonicalJsonValue(&self.value))?;
67        if !self.signature.is_empty() {
68            seq.serialize_element(&self.signature)?;
69        }
70        seq.end()
71    }
72}
73
74impl<'de> Deserialize<'de> for StateEntry {
75    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76    where
77        D: Deserializer<'de>,
78    {
79        struct StateEntryVisitor;
80
81        impl<'de> Visitor<'de> for StateEntryVisitor {
82            type Value = StateEntry;
83
84            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
85                formatter.write_str("a tuple [version, value] or [version, value, signature]")
86            }
87
88            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
89            where
90                A: SeqAccess<'de>,
91            {
92                let version = seq
93                    .next_element()?
94                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
95                let value = seq
96                    .next_element()?
97                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
98                let signature = seq.next_element()?.unwrap_or_default();
99
100                if seq.next_element::<de::IgnoredAny>()?.is_some() {
101                    return Err(de::Error::invalid_length(4, &self));
102                }
103
104                Ok(StateEntry {
105                    version,
106                    value,
107                    signature,
108                })
109            }
110        }
111
112        deserializer.deserialize_seq(StateEntryVisitor)
113    }
114}
115
116#[derive(Clone, Serialize, Deserialize)]
117pub struct State {
118    values: BTreeMap<String, StateEntry>,
119}
120
121impl State {
122    fn insert_node(
123        &mut self,
124        key: &str,
125        node_entry: vls_persist::model::NodeEntry,
126        node_state_entry: vls_persist::model::NodeStateEntry,
127    ) -> Result<(), Error> {
128        let node_key = format!("{NODE_PREFIX}/{key}");
129        let state_key = format!("{NODE_STATE_PREFIX}/{key}");
130        self.ensure_not_tombstone(&node_key)?;
131        self.ensure_not_tombstone(&state_key)?;
132        let node_version = self.next_version(&node_key);
133        let state_version = self.next_version(&state_key);
134
135        self.values.insert(
136            node_key,
137            StateEntry::new(node_version, serde_json::to_value(node_entry).unwrap()),
138        );
139        self.values.insert(
140            state_key,
141            StateEntry::new(
142                state_version,
143                serde_json::to_value(node_state_entry).unwrap(),
144            ),
145        );
146        Ok(())
147    }
148
149    fn update_node(
150        &mut self,
151        key: &str,
152        node_state: vls_persist::model::NodeStateEntry,
153    ) -> Result<(), Error> {
154        trace!(
155            "Update node: {}",
156            serde_json::to_string(&node_state).unwrap()
157        );
158        let key = format!("{NODE_STATE_PREFIX}/{key}");
159        self.ensure_not_tombstone(&key)?;
160        let v = self
161            .values
162            .get_mut(&key)
163            .expect("attempting to update non-existent node");
164        *v = StateEntry::new(v.version + 1, serde_json::to_value(node_state).unwrap());
165        Ok(())
166    }
167
168    fn delete_node(&mut self, key: &str) -> Result<(), Error> {
169        let node_key = format!("{NODE_PREFIX}/{key}");
170        let state_key = format!("{NODE_STATE_PREFIX}/{key}");
171        let allowlist_key = format!("{ALLOWLIST_PREFIX}/{key}");
172        let tracker_key = format!("{TRACKER_PREFIX}/{key}");
173        let channel_prefix = format!("{CHANNEL_PREFIX}/{key}");
174        let channel_keys: Vec<String> = self
175            .values
176            .keys()
177            .filter(|k| k.starts_with(&channel_prefix))
178            .cloned()
179            .collect();
180
181        self.put_tombstone(&node_key);
182        self.put_tombstone(&state_key);
183        self.put_tombstone(&allowlist_key);
184        self.put_tombstone(&tracker_key);
185        for channel_key in channel_keys {
186            self.put_tombstone(&channel_key);
187        }
188        Ok(())
189    }
190
191    fn insert_channel(
192        &mut self,
193        key: &str,
194        channel_entry: vls_persist::model::ChannelEntry,
195    ) -> Result<(), Error> {
196        let key = format!("{CHANNEL_PREFIX}/{key}");
197        self.ensure_not_tombstone(&key)?;
198        let version = self.next_version(&key);
199        self.values.insert(
200            key,
201            StateEntry::new(version, serde_json::to_value(channel_entry).unwrap()),
202        );
203        Ok(())
204    }
205
206    fn delete_channel(&mut self, key: &str) {
207        let live_key = format!("{CHANNEL_PREFIX}/{key}");
208        self.put_tombstone(&live_key);
209    }
210
211    fn update_channel(
212        &mut self,
213        key: &str,
214        channel_entry: vls_persist::model::ChannelEntry,
215    ) -> Result<(), Error> {
216        trace!("Updating channel {key}");
217        let key = format!("{CHANNEL_PREFIX}/{key}");
218        self.ensure_not_tombstone(&key)?;
219        let v = self
220            .values
221            .get_mut(&key)
222            .expect("attempting to update non-existent channel");
223        *v = StateEntry::new(v.version + 1, serde_json::to_value(channel_entry).unwrap());
224        Ok(())
225    }
226
227    fn get_channel(
228        &self,
229        key: &str,
230    ) -> Result<lightning_signer::persist::model::ChannelEntry, Error> {
231        let key = format!("{CHANNEL_PREFIX}/{key}");
232        if self.is_tombstone(&key) {
233            return Err(Error::Internal(format!("channel {} has been deleted", key)));
234        }
235        let value = self
236            .values
237            .get(&key)
238            .ok_or_else(|| Error::Internal(format!("missing channel state for key {}", key)))?;
239        let entry: vls_persist::model::ChannelEntry =
240            serde_json::from_value(value.value.clone()).unwrap();
241        Ok(entry.into())
242    }
243
244    fn get_node_channels(
245        &self,
246        node_id: &PublicKey,
247    ) -> Result<
248        Vec<(
249            lightning_signer::channel::ChannelId,
250            lightning_signer::persist::model::ChannelEntry,
251        )>,
252        Error,
253    > {
254        let prefix = hex::encode(node_id.serialize());
255        let prefix = format!("{CHANNEL_PREFIX}/{prefix}");
256        Ok(self
257            .values
258            .iter()
259            .filter(|(k, _)| k.starts_with(&prefix))
260            .filter(|(k, _)| !self.is_tombstone(k))
261            .map(|(k, v)| {
262                let key = k.split('/').last().unwrap();
263                let key = vls_persist::model::NodeChannelId(hex::decode(&key).unwrap());
264
265                let value: vls_persist::model::ChannelEntry =
266                    serde_json::from_value(v.value.clone()).unwrap();
267                (key.channel_id(), value.into())
268            })
269            .collect())
270    }
271
272    fn new_chain_tracker(
273        &mut self,
274        node_id: &PublicKey,
275        tracker: &ChainTracker<lightning_signer::monitor::ChainMonitor>,
276    ) -> Result<(), Error> {
277        let key = hex::encode(node_id.serialize());
278        let key = format!("{TRACKER_PREFIX}/{key}");
279        self.ensure_not_tombstone(&key)?;
280        let version = self.next_version(&key);
281
282        let tracker: vls_persist::model::ChainTrackerEntry = tracker.into();
283
284        self.values.insert(
285            key,
286            StateEntry::new(version, serde_json::to_value(tracker).unwrap()),
287        );
288        Ok(())
289    }
290
291    pub fn clear(&mut self) -> Result<(), Error> {
292        self.values.clear();
293        Ok(())
294    }
295
296    fn put_tombstone(&mut self, live_key: &str) {
297        self.values.insert(
298            live_key.to_owned(),
299            StateEntry::new(TOMBSTONE_VERSION, serde_json::Value::Null),
300        );
301    }
302
303    fn next_version(&self, live_key: &str) -> u64 {
304        self.values
305            .get(live_key)
306            .map(|v| v.version.saturating_add(1))
307            .unwrap_or(0)
308    }
309
310    fn is_tombstone(&self, live_key: &str) -> bool {
311        self.values
312            .get(live_key)
313            .map(|v| v.version == TOMBSTONE_VERSION)
314            .unwrap_or(false)
315    }
316
317    fn ensure_not_tombstone(&self, key: &str) -> Result<(), Error> {
318        if self.is_tombstone(key) {
319            return Err(Error::Internal(format!("key {} has been deleted", key)));
320        }
321        Ok(())
322    }
323}
324
325#[derive(Debug)]
326pub struct StateChange {
327    key: String,
328    old: Option<StateEntry>,
329    new: StateEntry,
330}
331
332#[derive(Debug, Default)]
333pub struct MergeResult {
334    pub changes: Vec<(String, Option<u64>, u64)>,
335    pub conflict_count: usize,
336}
337
338impl MergeResult {
339    pub fn has_conflicts(&self) -> bool {
340        self.conflict_count > 0
341    }
342}
343
344use core::fmt::Display;
345
346impl Display for StateChange {
347    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
348        match &self.old {
349            Some(o) => f.write_str(&format!(
350                "StateChange[{}]: old_version={}, new_version={}, old_value={}, new_value={}",
351                self.key,
352                o.version,
353                self.new.version,
354                serde_json::to_string(&o.value).unwrap(),
355                serde_json::to_string(&self.new.value).unwrap()
356            )),
357            None => f.write_str(&format!(
358                "StateChange[{}]: old_version=null, new_version={}, old_value=null, new_value={}",
359                self.key,
360                self.new.version,
361                serde_json::to_string(&self.new.value).unwrap()
362            )),
363        }
364    }
365}
366
367impl State {
368    pub fn new() -> Self {
369        State {
370            values: BTreeMap::new(),
371        }
372    }
373
374    pub fn len(&self) -> usize {
375        self.values.len()
376    }
377
378    /// Merge incoming state and track potential version conflicts.
379    ///
380    /// A conflict means the incoming state is stale or incompatible with local
381    /// tombstone knowledge. Callers may use this signal to trigger a full sync.
382    pub fn merge(&mut self, other: &State) -> anyhow::Result<MergeResult> {
383        let mut res = MergeResult::default();
384        for (key, incoming) in other.values.iter() {
385            let newver = incoming.version;
386            let incoming_is_tombstone = newver == TOMBSTONE_VERSION;
387            match self.values.get_mut(key) {
388                None => {
389                    trace!("Insert new key {}: version={}", key, newver);
390                    res.changes.push((key.to_owned(), None, newver));
391                    let mut inserted = incoming.clone();
392                    if incoming_is_tombstone {
393                        inserted.value = serde_json::Value::Null;
394                    }
395                    self.values.insert(key.clone(), inserted);
396                }
397                Some(v) => {
398                    if incoming_is_tombstone {
399                        if v.version == TOMBSTONE_VERSION {
400                            continue;
401                        }
402                        trace!(
403                            "Tombstoning key {}: version={} => version={}",
404                            key,
405                            v.version,
406                            newver
407                        );
408                        res.changes.push((key.to_owned(), Some(v.version), newver));
409                        *v = StateEntry {
410                            version: newver,
411                            value: serde_json::Value::Null,
412                            signature: incoming.signature.clone(),
413                        };
414                        continue;
415                    }
416
417                    if v.version == TOMBSTONE_VERSION {
418                        trace!(
419                            "Ignoring live key {} version={} because local key is tombstoned",
420                            key,
421                            newver
422                        );
423                        res.conflict_count += 1;
424                        continue;
425                    }
426
427                    if v.version == newver {
428                        if v.value == incoming.value && v.signature != incoming.signature {
429                            trace!("Updating signature for key {} at version={}", key, newver);
430                            res.changes.push((key.to_owned(), Some(v.version), newver));
431                            v.signature = incoming.signature.clone();
432                        }
433                        continue;
434                    } else if v.version > newver {
435                        warn!(
436                            "Ignoring outdated state version newver={}, we have oldver={}: newval={:?} vs oldval={:?}",
437                            newver,
438                            v.version,
439                            serde_json::to_string(&incoming.value),
440                            serde_json::to_string(&v.value)
441                        );
442                        res.conflict_count += 1;
443                        continue;
444                    } else {
445                        trace!(
446                            "Updating key {}: version={} => version={}",
447                            key,
448                            v.version,
449                            newver
450                        );
451                        res.changes.push((key.to_owned(), Some(v.version), newver));
452                        *v = incoming.clone();
453                    }
454                }
455            }
456        }
457        Ok(res)
458    }
459
460    pub fn diff(&self, other: &State) -> anyhow::Result<Vec<StateChange>> {
461        Ok(other
462            .values
463            .iter()
464            .map(|(key, new)| (key, self.values.get(key), new))
465            .map(|(key, old, new)| StateChange {
466                key: key.clone(),
467                old: old.map(|o| o.clone()),
468                new: new.clone(),
469            })
470            .filter(|c| match (&c.old, &c.new) {
471                (None, _) => true,
472                (Some(old), new) => old.version < new.version,
473            })
474            .collect())
475    }
476
477    /// Return a `State` containing only entries that are newer in `other` than in `self`.
478    /// This is useful for sending compact state diffs.
479    pub fn diff_state(&self, other: &State) -> State {
480        let mut values = BTreeMap::new();
481        for (key, new_entry) in other.values.iter() {
482            match self.values.get(key) {
483                None => {
484                    values.insert(key.clone(), new_entry.clone());
485                }
486                Some(old_entry) if old_entry.version < new_entry.version => {
487                    values.insert(key.clone(), new_entry.clone());
488                }
489                Some(old_entry)
490                    if old_entry.version == new_entry.version
491                        && old_entry.signature != new_entry.signature =>
492                {
493                    values.insert(key.clone(), new_entry.clone());
494                }
495                _ => {}
496            }
497        }
498        State { values }
499    }
500
501    /// Sign entries missing signatures and return how many signatures were added.
502    pub fn resign_signatures<F>(&mut self, mut signer: F) -> anyhow::Result<usize>
503    where
504        F: FnMut(&str, u64, &[u8]) -> anyhow::Result<Vec<u8>>,
505    {
506        let mut changed = 0usize;
507        for (key, entry) in self.values.iter_mut() {
508            if !entry.signature.is_empty() {
509                continue;
510            }
511            let value = entry.canonical_value_bytes()?;
512            let signature = signer(key, entry.version, &value)?;
513            entry.signature = signature;
514            changed += 1;
515        }
516        Ok(changed)
517    }
518
519    pub fn sketch(&self) -> StateSketch {
520        StateSketch::from_state(self)
521    }
522
523    // Return a copy of the state with tombstoned entries omitted.
524    pub fn omit_tombstones(&self) -> State {
525        let values = self
526            .values
527            .iter()
528            .filter(|(_, value)| value.version != TOMBSTONE_VERSION)
529            .map(|(key, value)| (key.clone(), value.clone()))
530            .collect();
531        State { values }
532    }
533}
534
535#[derive(Clone, Serialize, Deserialize, Debug, Default)]
536struct SketchEntry {
537    version: u64,
538    signature: Vec<u8>,
539}
540
541#[derive(Clone, Serialize, Deserialize, Debug, Default)]
542pub struct StateSketch {
543    versions: BTreeMap<String, SketchEntry>,
544}
545
546impl StateSketch {
547    pub fn new() -> Self {
548        Self::default()
549    }
550
551    pub fn from_state(state: &State) -> Self {
552        let mut sketch = Self::new();
553        sketch.apply_state(state);
554        sketch
555    }
556
557    /// Apply versions from `state` without clearing existing entries.
558    pub fn apply_state(&mut self, state: &State) {
559        for (key, value) in state.values.iter() {
560            self.versions.insert(
561                key.clone(),
562                SketchEntry {
563                    version: value.version,
564                    signature: value.signature.clone(),
565                },
566            );
567        }
568    }
569
570    /// Build a `State` containing entries newer than those recorded in the sketch.
571    pub fn diff_state(&self, state: &State) -> State {
572        let mut values = BTreeMap::new();
573        for (key, new_entry) in state.values.iter() {
574            match self.versions.get(key) {
575                None => {
576                    values.insert(key.clone(), new_entry.clone());
577                }
578                Some(old) if old.version < new_entry.version => {
579                    values.insert(key.clone(), new_entry.clone());
580                }
581                Some(old)
582                    if old.version == new_entry.version && old.signature != new_entry.signature =>
583                {
584                    values.insert(key.clone(), new_entry.clone());
585                }
586                _ => {}
587            }
588        }
589        State { values }
590    }
591}
592
593impl Into<Vec<crate::pb::SignerStateEntry>> for State {
594    fn into(self) -> Vec<crate::pb::SignerStateEntry> {
595        self.values
596            .iter()
597            .map(|(k, v)| crate::pb::SignerStateEntry {
598                key: k.to_owned(),
599                value: v
600                    .canonical_value_bytes()
601                    .expect("canonical signer state value"),
602                version: v.version,
603                signature: v.signature.clone(),
604            })
605            .collect()
606    }
607}
608
609impl TryFrom<&[crate::pb::SignerStateEntry]> for State {
610    type Error = anyhow::Error;
611
612    fn try_from(v: &[crate::pb::SignerStateEntry]) -> Result<State, Self::Error> {
613        let values = v
614            .iter()
615            .map(|entry| -> anyhow::Result<(String, StateEntry)> {
616                let value = serde_json::from_slice(&entry.value).map_err(|e| {
617                    anyhow!(
618                        "failed to decode signer state value for key {}: {}",
619                        entry.key,
620                        e
621                    )
622                })?;
623
624                Ok((
625                    entry.key.to_owned(),
626                    StateEntry {
627                        version: entry.version,
628                        value,
629                        signature: entry.signature.clone(),
630                    },
631                ))
632            })
633            .collect::<anyhow::Result<BTreeMap<_, _>>>()?;
634
635        Ok(State { values })
636    }
637}
638
639impl From<Vec<crate::pb::SignerStateEntry>> for State {
640    fn from(v: Vec<crate::pb::SignerStateEntry>) -> State {
641        State::try_from(v.as_slice())
642            .expect("signer state entries must contain valid JSON payloads")
643    }
644}
645
646pub(crate) struct MemoryPersister {
647    state: Arc<Mutex<State>>,
648}
649
650impl MemoryPersister {
651    pub fn new() -> Self {
652        let state = Arc::new(Mutex::new(State {
653            values: BTreeMap::new(),
654        }));
655        MemoryPersister { state }
656    }
657
658    pub fn state(&self) -> Arc<Mutex<State>> {
659        self.state.clone()
660    }
661}
662
663impl SendSync for MemoryPersister {}
664
665impl Persist for MemoryPersister {
666    fn new_node(
667        &self,
668        node_id: &lightning_signer::bitcoin::secp256k1::PublicKey,
669        config: &NodeConfig,
670        state: &NodeState,
671    ) -> Result<(), Error> {
672        let key = hex::encode(node_id.serialize());
673        self.state.lock().unwrap().insert_node(
674            &key,
675            vls_persist::model::NodeEntry {
676                key_derivation_style: config.key_derivation_style as u8,
677                network: config.network.to_string(),
678            },
679            state.into(),
680        )
681    }
682
683    fn delete_channel(&self, node_id: &PublicKey, channel: &ChannelId) -> Result<(), Error> {
684        let node_channel_id = vls_persist::model::NodeChannelId::new(node_id, &channel);
685        let id = hex::encode(node_channel_id.0);
686        self.state.lock().unwrap().delete_channel(&id);
687        Ok(())
688    }
689
690    fn update_node(
691        &self,
692        node_id: &lightning_signer::bitcoin::secp256k1::PublicKey,
693        state: &NodeState,
694    ) -> Result<(), Error> {
695        let key = hex::encode(node_id.serialize());
696        self.state.lock().unwrap().update_node(&key, state.into())
697    }
698
699    fn delete_node(
700        &self,
701        node_id: &lightning_signer::bitcoin::secp256k1::PublicKey,
702    ) -> Result<(), Error> {
703        let key = hex::encode(node_id.serialize());
704        self.state.lock().unwrap().delete_node(&key)
705    }
706
707    fn new_channel(
708        &self,
709        node_id: &lightning_signer::bitcoin::secp256k1::PublicKey,
710        stub: &ChannelStub,
711    ) -> Result<(), Error> {
712        let id = vls_persist::model::NodeChannelId::new(node_id, &stub.id0);
713        let channel_value_satoshis = 0;
714        let enforcement_state = lightning_signer::policy::validator::EnforcementState::new(0);
715        let entry = vls_persist::model::ChannelEntry {
716            channel_value_satoshis,
717            channel_setup: None,
718            id: None,
719            enforcement_state,
720            // birth blockheight for stub, None for channel
721            blockheight: Some(stub.blockheight),
722        };
723        let id = hex::encode(id.0);
724
725        self.state.lock().unwrap().insert_channel(&id, entry)
726    }
727
728    fn update_channel(
729        &self,
730        node_id: &lightning_signer::bitcoin::secp256k1::PublicKey,
731        channel: &lightning_signer::channel::Channel,
732    ) -> Result<(), Error> {
733        let node_channel_id = vls_persist::model::NodeChannelId::new(node_id, &channel.id0);
734        let id = hex::encode(node_channel_id.0);
735        let channel_value_satoshis = channel.setup.channel_value_sat;
736        let entry = vls_persist::model::ChannelEntry {
737            channel_value_satoshis,
738            channel_setup: Some(channel.setup.clone()),
739            id: channel.id.clone(),
740            enforcement_state: channel.enforcement_state.clone(),
741            blockheight: None,
742        };
743        self.state.lock().unwrap().update_channel(&id, entry)
744    }
745
746    fn get_channel(
747        &self,
748        node_id: &PublicKey,
749        channel_id: &ChannelId,
750    ) -> Result<lightning_signer::persist::model::ChannelEntry, Error> {
751        let id = vls_persist::model::NodeChannelId::new(node_id, channel_id);
752        let id = hex::encode(id.0);
753        self.state.lock().unwrap().get_channel(&id)
754    }
755
756    fn new_tracker(
757        &self,
758        node_id: &PublicKey,
759        tracker: &ChainTracker<lightning_signer::monitor::ChainMonitor>,
760    ) -> Result<(), Error> {
761        self.state
762            .lock()
763            .unwrap()
764            .new_chain_tracker(node_id, tracker)
765    }
766
767    fn update_tracker(
768        &self,
769        node_id: &PublicKey,
770        tracker: &ChainTracker<lightning_signer::monitor::ChainMonitor>,
771    ) -> Result<(), Error> {
772        let key = hex::encode(node_id.serialize());
773        let key = format!("{TRACKER_PREFIX}/{key}");
774
775        let mut state = self.state.lock().unwrap();
776        state.ensure_not_tombstone(&key)?;
777        let v = state.values.get_mut(&key).unwrap();
778        let tracker: vls_persist::model::ChainTrackerEntry = tracker.into();
779        *v = StateEntry::new(v.version + 1, serde_json::to_value(tracker).unwrap());
780        Ok(())
781    }
782
783    fn get_tracker(
784        &self,
785        node_id: PublicKey,
786        validator_factory: Arc<dyn ValidatorFactory>,
787    ) -> Result<
788        (
789            ChainTracker<lightning_signer::monitor::ChainMonitor>,
790            Vec<ChainTrackerListenerEntry>,
791        ),
792        Error,
793    > {
794        let key = hex::encode(node_id.serialize());
795        let key = format!("{TRACKER_PREFIX}/{key}");
796
797        let state = self.state.lock().unwrap();
798        if state.is_tombstone(&key) {
799            return Err(Error::Internal(format!("tracker {} has been deleted", key)));
800        }
801        let v: vls_persist::model::ChainTrackerEntry = serde_json::from_value(
802            state
803                .values
804                .get(&key)
805                .ok_or_else(|| Error::Internal(format!("missing tracker state {}", key)))?
806                .value
807                .clone(),
808        )
809        .unwrap();
810
811        Ok(v.into_tracker(node_id, validator_factory))
812    }
813
814    fn get_node_channels(
815        &self,
816        node_id: &PublicKey,
817    ) -> Result<Vec<(ChannelId, lightning_signer::persist::model::ChannelEntry)>, Error> {
818        self.state.lock().unwrap().get_node_channels(node_id)
819    }
820
821    fn update_node_allowlist(
822        &self,
823        node_id: &PublicKey,
824        allowlist: Vec<std::string::String>,
825    ) -> Result<(), Error> {
826        let key = hex::encode(node_id.serialize());
827        let key = format!("{ALLOWLIST_PREFIX}/{key}");
828
829        let mut state = self.state.lock().unwrap();
830        state.ensure_not_tombstone(&key)?;
831        match state.values.get_mut(&key) {
832            Some(v) => {
833                *v = StateEntry::new(v.version + 1, serde_json::to_value(allowlist).unwrap());
834            }
835            None => {
836                let version = state.next_version(&key);
837                state.values.insert(
838                    key,
839                    StateEntry::new(version, serde_json::to_value(allowlist).unwrap()),
840                );
841            }
842        }
843        Ok(())
844    }
845
846    fn get_node_allowlist(&self, node_id: &PublicKey) -> Result<Vec<std::string::String>, Error> {
847        let state = self.state.lock().unwrap();
848        let key = hex::encode(node_id.serialize());
849        let key = format!("{ALLOWLIST_PREFIX}/{key}");
850        if state.is_tombstone(&key) {
851            return Ok(Vec::new());
852        }
853
854        // If allowlist doesn't exist (e.g., node created before VLS 0.14), default to empty
855        let allowlist: Vec<String> = match state.values.get(&key) {
856            Some(value) => serde_json::from_value(value.value.clone()).unwrap_or_default(),
857            None => Vec::new(),
858        };
859
860        Ok(allowlist)
861    }
862
863    fn get_nodes(
864        &self,
865    ) -> Result<Vec<(PublicKey, lightning_signer::persist::model::NodeEntry)>, Error> {
866        use lightning_signer::node::NodeState as CoreNodeState;
867
868        let state = self.state.lock().unwrap();
869        let node_ids: Vec<&str> = state
870            .values
871            .keys()
872            .filter(|k| k.starts_with(&format!("{NODE_PREFIX}/")))
873            .filter(|k| !state.is_tombstone(k))
874            .filter_map(|k| k.split('/').last())
875            .collect();
876
877        let mut res = Vec::new();
878        for node_id in node_ids.iter() {
879            let node_key = format!("{NODE_PREFIX}/{node_id}");
880            let state_key = format!("{NODE_STATE_PREFIX}/{node_id}");
881            let allowlist_key = format!("{ALLOWLIST_PREFIX}/{node_id}");
882
883            if state.is_tombstone(&node_key) || state.is_tombstone(&state_key) {
884                continue;
885            }
886
887            let node: vls_persist::model::NodeEntry = match state.values.get(&node_key) {
888                Some(value) => serde_json::from_value(value.value.clone()).unwrap(),
889                None => continue,
890            };
891            let state_e: vls_persist::model::NodeStateEntry = match state.values.get(&state_key) {
892                Some(value) => serde_json::from_value(value.value.clone()).unwrap(),
893                None => continue,
894            };
895
896            // Load allowlist, defaulting to empty if not found (for nodes created before VLS 0.14)
897            let allowlist_strings: Vec<String> = if state.is_tombstone(&allowlist_key) {
898                Vec::new()
899            } else {
900                match state.values.get(&allowlist_key) {
901                    Some(value) => serde_json::from_value(value.value.clone()).unwrap_or_default(),
902                    None => Vec::new(),
903                }
904            };
905
906            // Parse allowlist strings into Allowable objects
907            use lightning_signer::node::Allowable;
908            let network = lightning_signer::bitcoin::Network::from_str(&node.network)
909                .map_err(|e| Error::Internal(format!("Invalid network: {}", e)))?;
910
911            let allowlist: Vec<Allowable> = allowlist_strings
912                .into_iter()
913                .filter_map(|s| match Allowable::from_str(&s, network) {
914                    Ok(a) => Some(a),
915                    Err(e) => {
916                        warn!("Failed to parse allowlist entry '{}': {}", s, e);
917                        None
918                    }
919                })
920                .collect();
921
922            let state = CoreNodeState::restore(
923                state_e.invoices,
924                state_e.issued_invoices,
925                state_e.preimages,
926                0,
927                state_e.velocity_control.into(),
928                state_e.fee_velocity_control.into(),
929                0u64,
930                /* dbid_high_water_mark: prevents reuse of
931                 * channel dbid, 0 disables enforcement. */
932                allowlist,
933            );
934
935            let entry = lightning_signer::persist::model::NodeEntry {
936                key_derivation_style: node.key_derivation_style,
937                network: node.network,
938                state,
939            };
940
941            let key: Vec<u8> = hex::decode(node_id).unwrap();
942            res.push((PublicKey::from_slice(key.as_slice()).unwrap(), entry));
943        }
944
945        let nodes = res;
946        Ok(nodes)
947    }
948    fn clear_database(&self) -> Result<(), Error> {
949        self.state.lock().unwrap().clear()
950    }
951
952    fn signer_id(&self) -> SignerId {
953        // The signers are clones of each other in Greenlight, and as
954        // such we should not need to differentiate them. We therefore
955        // just return a static dummy ID.
956        [0u8; 16]
957    }
958}
959
960#[cfg(test)]
961mod tests {
962    use crate::persist::TOMBSTONE_VERSION;
963
964    use super::{
965        State, StateEntry, StateSketch, ALLOWLIST_PREFIX, CHANNEL_PREFIX, NODE_PREFIX,
966        NODE_STATE_PREFIX, TRACKER_PREFIX,
967    };
968    use crate::pb::SignerStateEntry;
969    use serde_json::json;
970    use std::collections::BTreeMap;
971
972    fn mk_state(entries: Vec<(&str, u64, serde_json::Value)>) -> State {
973        let mut values = BTreeMap::new();
974        for (key, version, value) in entries {
975            values.insert(key.to_string(), StateEntry::new(version, value));
976        }
977        State { values }
978    }
979
980    fn assert_entry(
981        state: &State,
982        key: &str,
983        expected_version: u64,
984        expected_value: serde_json::Value,
985    ) {
986        let actual = state
987            .values
988            .get(key)
989            .unwrap_or_else(|| panic!("expected state to include key: {key}"));
990        assert_eq!(actual.version, expected_version);
991        assert_eq!(actual.value, expected_value);
992    }
993
994    fn assert_entry_absent(state: &State, key: &str) {
995        assert!(
996            state.values.get(key).is_none(),
997            "expected state to omit key {key}"
998        );
999    }
1000
1001    fn assert_tombstone(state: &State, key: &str) {
1002        assert_entry(state, key, TOMBSTONE_VERSION, serde_json::Value::Null);
1003    }
1004
1005    #[test]
1006    fn state_deserialize_legacy_tuple_defaults_empty_signature() {
1007        let raw = r#"{"values":{"k":[1,{"v":1}]}}"#;
1008        let state: State = serde_json::from_str(raw).unwrap();
1009        let entry = state.values.get("k").unwrap();
1010        assert_eq!(entry.version, 1);
1011        assert_eq!(entry.value, json!({"v": 1}));
1012        assert!(entry.signature.is_empty());
1013    }
1014
1015    #[test]
1016    fn state_deserialize_extended_tuple_preserves_signature() {
1017        let raw = r#"{"values":{"k":[2,{"v":2},[1,2,3]]}}"#;
1018        let state: State = serde_json::from_str(raw).unwrap();
1019        let entry = state.values.get("k").unwrap();
1020        assert_eq!(entry.version, 2);
1021        assert_eq!(entry.value, json!({"v": 2}));
1022        assert_eq!(entry.signature, vec![1, 2, 3]);
1023    }
1024
1025    #[test]
1026    fn state_serialize_empty_signature_emits_legacy_tuple() {
1027        let state = mk_state(vec![("k", 3, json!({"v": 3}))]);
1028        let v: serde_json::Value =
1029            serde_json::from_slice(&serde_json::to_vec(&state).unwrap()).unwrap();
1030        let values = v.get("values").unwrap().as_object().unwrap();
1031        let tuple = values.get("k").unwrap().as_array().unwrap();
1032        assert_eq!(tuple.len(), 2);
1033        assert_eq!(tuple[0], json!(3));
1034        assert_eq!(tuple[1], json!({"v": 3}));
1035    }
1036
1037    #[test]
1038    fn state_serialize_non_empty_signature_emits_extended_tuple() {
1039        let mut values = BTreeMap::new();
1040        values.insert(
1041            "k".to_string(),
1042            StateEntry {
1043                version: 4,
1044                value: json!({"v": 4}),
1045                signature: vec![7, 8, 9],
1046            },
1047        );
1048        let state = State { values };
1049        let v: serde_json::Value =
1050            serde_json::from_slice(&serde_json::to_vec(&state).unwrap()).unwrap();
1051        let values = v.get("values").unwrap().as_object().unwrap();
1052        let tuple = values.get("k").unwrap().as_array().unwrap();
1053        assert_eq!(tuple.len(), 3);
1054        assert_eq!(tuple[0], json!(4));
1055        assert_eq!(tuple[1], json!({"v": 4}));
1056        assert_eq!(tuple[2], json!([7, 8, 9]));
1057    }
1058
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
1071    #[test]
1072    fn signer_state_entry_conversions_preserve_signature() {
1073        let entries = vec![SignerStateEntry {
1074            version: 5,
1075            key: "k".to_string(),
1076            value: serde_json::to_vec(&json!({"v": 5})).unwrap(),
1077            signature: vec![11, 12],
1078        }];
1079
1080        let state: State = entries.clone().into();
1081        let entry = state.values.get("k").unwrap();
1082        assert_eq!(entry.version, 5);
1083        assert_eq!(entry.value, json!({"v": 5}));
1084        assert_eq!(entry.signature, vec![11, 12]);
1085
1086        let roundtrip: Vec<SignerStateEntry> = state.into();
1087        assert_eq!(roundtrip, entries);
1088    }
1089
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
1106    #[test]
1107    fn merge_newer_entry_propagates_signature() {
1108        let mut base = mk_state(vec![("k", 1, json!({"v": 1}))]);
1109        let mut incoming_values = BTreeMap::new();
1110        incoming_values.insert(
1111            "k".to_string(),
1112            StateEntry {
1113                version: 2,
1114                value: json!({"v": 2}),
1115                signature: vec![21, 22, 23],
1116            },
1117        );
1118        let incoming = State {
1119            values: incoming_values,
1120        };
1121
1122        let res = base.merge(&incoming).unwrap();
1123        assert_eq!(res.conflict_count, 0);
1124        let merged = base.values.get("k").unwrap();
1125        assert_eq!(merged.version, 2);
1126        assert_eq!(merged.value, json!({"v": 2}));
1127        assert_eq!(merged.signature, vec![21, 22, 23]);
1128    }
1129
1130    #[test]
1131    fn merge_same_version_updates_signature_when_value_matches() {
1132        let mut base_values = BTreeMap::new();
1133        base_values.insert(
1134            "k".to_string(),
1135            StateEntry {
1136                version: 2,
1137                value: json!({"v": 1}),
1138                signature: vec![1],
1139            },
1140        );
1141        let mut base = State {
1142            values: base_values,
1143        };
1144
1145        let mut incoming_values = BTreeMap::new();
1146        incoming_values.insert(
1147            "k".to_string(),
1148            StateEntry {
1149                version: 2,
1150                value: json!({"v": 1}),
1151                signature: vec![9, 9],
1152            },
1153        );
1154        let incoming = State {
1155            values: incoming_values,
1156        };
1157
1158        let res = base.merge(&incoming).unwrap();
1159        assert_eq!(res.conflict_count, 0);
1160        let merged = base.values.get("k").unwrap();
1161        assert_eq!(merged.version, 2);
1162        assert_eq!(merged.value, json!({"v": 1}));
1163        assert_eq!(merged.signature, vec![9, 9]);
1164    }
1165
1166    #[test]
1167    fn merge_same_version_does_not_overwrite_value() {
1168        let mut base_values = BTreeMap::new();
1169        base_values.insert(
1170            "k".to_string(),
1171            StateEntry {
1172                version: 2,
1173                value: json!({"v": 1}),
1174                signature: vec![1],
1175            },
1176        );
1177        let mut base = State {
1178            values: base_values,
1179        };
1180
1181        let mut incoming_values = BTreeMap::new();
1182        incoming_values.insert(
1183            "k".to_string(),
1184            StateEntry {
1185                version: 2,
1186                value: json!({"v": 999}),
1187                signature: vec![9, 9],
1188            },
1189        );
1190        let incoming = State {
1191            values: incoming_values,
1192        };
1193
1194        let _ = base.merge(&incoming).unwrap();
1195        let merged = base.values.get("k").unwrap();
1196        assert_eq!(merged.version, 2);
1197        assert_eq!(merged.value, json!({"v": 1}));
1198        assert_eq!(merged.signature, vec![1]);
1199    }
1200
1201    #[test]
1202    fn omit_tombstones_omits_tombstoned_entries() {
1203        let state = mk_state(vec![
1204            ("k1", 1, json!({"v": 1})),
1205            ("k2", TOMBSTONE_VERSION, serde_json::Value::Null),
1206        ]);
1207
1208        let filtered = state.omit_tombstones();
1209
1210        assert_eq!(filtered.values.len(), 1);
1211        assert_entry(&filtered, "k1", 1, json!({"v": 1}));
1212        assert_entry_absent(&filtered, "k2");
1213    }
1214
1215    #[test]
1216    fn diff_state_only_includes_new_or_newer_entries() {
1217        let old = mk_state(vec![
1218            ("k1", 1, json!({"v": 1})),
1219            ("k2", 2, json!({"v": 2})),
1220            ("k3", 3, json!({"v": 3})),
1221        ]);
1222        let new = mk_state(vec![
1223            // unchanged version, changed value should still be ignored
1224            ("k1", 1, json!({"v": 999})),
1225            // newer version should be included
1226            ("k2", 3, json!({"v": 22})),
1227            // older version should be ignored
1228            ("k3", 2, json!({"v": 33})),
1229            // brand new key should be included
1230            ("k4", 0, json!({"v": 4})),
1231        ]);
1232
1233        let diff = old.diff_state(&new);
1234
1235        assert_eq!(diff.values.len(), 2);
1236        assert_entry(&diff, "k2", 3, json!({"v": 22}));
1237        assert_entry(&diff, "k4", 0, json!({"v": 4}));
1238        assert_entry_absent(&diff, "k1");
1239        assert_entry_absent(&diff, "k3");
1240    }
1241
1242    #[test]
1243    fn diff_state_includes_signature_only_changes() {
1244        let mut old_values = BTreeMap::new();
1245        old_values.insert(
1246            "k".to_string(),
1247            StateEntry {
1248                version: 5,
1249                value: json!({"v": 5}),
1250                signature: vec![1],
1251            },
1252        );
1253        let old = State { values: old_values };
1254
1255        let mut new_values = BTreeMap::new();
1256        new_values.insert(
1257            "k".to_string(),
1258            StateEntry {
1259                version: 5,
1260                value: json!({"v": 5}),
1261                signature: vec![2, 3],
1262            },
1263        );
1264        let new = State { values: new_values };
1265
1266        let diff = old.diff_state(&new);
1267        assert_eq!(diff.values.len(), 1);
1268        let entry = diff.values.get("k").unwrap();
1269        assert_eq!(entry.version, 5);
1270        assert_eq!(entry.signature, vec![2, 3]);
1271    }
1272
1273    #[test]
1274    fn sate_diff_with_empty_old_state_includes_all_entries() {
1275        let old = State::new();
1276        let new = mk_state(vec![("k1", 1, json!({"v": 1})), ("k2", 2, json!({"v": 2}))]);
1277
1278        let diff = old.diff_state(&new);
1279
1280        assert_eq!(diff.values.len(), 2);
1281        assert_entry(&diff, "k1", 1, json!({"v": 1}));
1282        assert_entry(&diff, "k2", 2, json!({"v": 2}));
1283    }
1284
1285    #[test]
1286    fn sketch_diff_matches_state_diff() {
1287        let old = mk_state(vec![
1288            ("a", 5, json!(1)),
1289            ("b", 2, json!(2)),
1290            ("c", 7, json!(3)),
1291        ]);
1292        let new = mk_state(vec![
1293            ("a", 5, json!(10)),
1294            ("b", 3, json!(20)),
1295            ("c", 6, json!(30)),
1296            ("d", 1, json!(40)),
1297        ]);
1298
1299        let state_diff = old.diff_state(&new);
1300        let sketch_diff = old.sketch().diff_state(&new);
1301
1302        assert_eq!(state_diff.values, sketch_diff.values);
1303    }
1304
1305    #[test]
1306    fn sketch_diff_with_empty_sketch_includes_all_entries() {
1307        let state = mk_state(vec![("a", 1, json!(1)), ("b", 2, json!(2))]);
1308        let sketch = StateSketch::new();
1309
1310        let diff = sketch.diff_state(&state);
1311
1312        assert_eq!(diff.values.len(), 2);
1313        assert_entry(&diff, "a", 1, json!(1));
1314        assert_entry(&diff, "b", 2, json!(2));
1315    }
1316
1317    #[test]
1318    fn sketch_apply_follow_version_updates() {
1319        let base = mk_state(vec![("a", 1, json!(1)), ("b", 2, json!(2))]);
1320        let mut sketch = StateSketch::new();
1321        sketch.apply_state(&base);
1322
1323        let next = mk_state(vec![
1324            ("a", 2, json!(10)),
1325            ("b", 2, json!(20)),
1326            ("c", 0, json!(30)),
1327        ]);
1328        let first_diff = sketch.diff_state(&next);
1329        assert_eq!(first_diff.values.len(), 2);
1330        assert_entry(&first_diff, "a", 2, json!(10));
1331        assert_entry(&first_diff, "c", 0, json!(30));
1332        assert_entry_absent(&first_diff, "b");
1333
1334        sketch.apply_state(&first_diff);
1335        let second_diff = sketch.diff_state(&next);
1336        assert!(second_diff.values.is_empty());
1337    }
1338
1339    #[test]
1340    fn sketch_diff_includes_signature_only_changes() {
1341        let mut old_values = BTreeMap::new();
1342        old_values.insert(
1343            "k".to_string(),
1344            StateEntry {
1345                version: 8,
1346                value: json!({"v": 8}),
1347                signature: vec![1],
1348            },
1349        );
1350        let old = State { values: old_values };
1351
1352        let mut new_values = BTreeMap::new();
1353        new_values.insert(
1354            "k".to_string(),
1355            StateEntry {
1356                version: 8,
1357                value: json!({"v": 8}),
1358                signature: vec![4, 5, 6],
1359            },
1360        );
1361        let new = State { values: new_values };
1362
1363        let diff = old.sketch().diff_state(&new);
1364        assert_eq!(diff.values.len(), 1);
1365        let entry = diff.values.get("k").unwrap();
1366        assert_eq!(entry.version, 8);
1367        assert_eq!(entry.signature, vec![4, 5, 6]);
1368    }
1369
1370    #[test]
1371    fn merge_tombstone_deletes_older_live_entry() {
1372        let live_key = format!("{CHANNEL_PREFIX}/abc");
1373        let mut state = mk_state(vec![(live_key.as_str(), 2, json!({"v": 1}))]);
1374        let incoming = mk_state(vec![(live_key.as_str(), u64::MAX, serde_json::Value::Null)]);
1375
1376        let res = state.merge(&incoming).unwrap();
1377
1378        assert_tombstone(&state, &live_key);
1379        assert_eq!(res.conflict_count, 0);
1380    }
1381
1382    #[test]
1383    fn merge_ignores_live_entry_if_key_is_tombstone() {
1384        let live_key = format!("{CHANNEL_PREFIX}/abc");
1385        let mut state = mk_state(vec![(live_key.as_str(), u64::MAX, serde_json::Value::Null)]);
1386        let incoming = mk_state(vec![(live_key.as_str(), 4, json!({"v": 1}))]);
1387
1388        let res = state.merge(&incoming).unwrap();
1389
1390        assert_tombstone(&state, &live_key);
1391        assert_eq!(res.conflict_count, 1);
1392    }
1393
1394    #[test]
1395    fn merge_ignores_newer_live_entry_if_key_is_tombstone() {
1396        let live_key = format!("{CHANNEL_PREFIX}/abc");
1397        let mut state = mk_state(vec![(live_key.as_str(), u64::MAX, serde_json::Value::Null)]);
1398        let incoming = mk_state(vec![(live_key.as_str(), 6, json!({"v": 2}))]);
1399
1400        let res = state.merge(&incoming).unwrap();
1401
1402        assert_tombstone(&state, &live_key);
1403        assert_eq!(res.conflict_count, 1);
1404    }
1405
1406    #[test]
1407    fn safe_merge_reports_conflict_for_outdated_live_version() {
1408        let mut state = mk_state(vec![("k1", 5, json!({"v": 5}))]);
1409        let incoming = mk_state(vec![("k1", 4, json!({"v": 4}))]);
1410
1411        let res = state.merge(&incoming).unwrap();
1412
1413        assert_eq!(res.conflict_count, 1);
1414        assert_entry(&state, "k1", 5, json!({"v": 5}));
1415    }
1416
1417    #[test]
1418    fn signer_state_entry_try_from_rejects_invalid_json_value() {
1419        let entries = vec![SignerStateEntry {
1420            version: 1,
1421            key: "bad".to_string(),
1422            value: b"not-json".to_vec(),
1423            signature: vec![],
1424        }];
1425
1426        let res = State::try_from(entries.as_slice());
1427        assert!(res.is_err());
1428    }
1429
1430    #[test]
1431    fn resign_signatures_only_signs_missing_entries() {
1432        let mut values = BTreeMap::new();
1433        values.insert(
1434            "signed".to_string(),
1435            StateEntry {
1436                version: 1,
1437                value: json!({"v": 1}),
1438                signature: vec![9, 9],
1439            },
1440        );
1441        values.insert(
1442            "unsigned".to_string(),
1443            StateEntry {
1444                version: 2,
1445                value: json!({"v": 2}),
1446                signature: vec![],
1447            },
1448        );
1449
1450        let mut state = State { values };
1451        let mut calls = 0usize;
1452        let changed = state
1453            .resign_signatures(|_, _, _| {
1454                calls += 1;
1455                Ok(vec![1, 2, 3, 4])
1456            })
1457            .unwrap();
1458
1459        assert_eq!(calls, 1);
1460        assert_eq!(changed, 1);
1461        assert_eq!(state.values.get("signed").unwrap().signature, vec![9, 9]);
1462        assert_eq!(
1463            state.values.get("unsigned").unwrap().signature,
1464            vec![1, 2, 3, 4]
1465        );
1466    }
1467
1468    #[test]
1469    fn delete_channel_marks_channel_with_tombstone_version() {
1470        let live_key = format!("{CHANNEL_PREFIX}/abc");
1471        let mut state = mk_state(vec![(live_key.as_str(), 7, json!({"v": 1}))]);
1472
1473        state.delete_channel("abc");
1474
1475        assert_tombstone(&state, &live_key);
1476    }
1477
1478    #[test]
1479    fn delete_node_creates_tombstones_for_node_related_keys() {
1480        let node_id = "deadbeef";
1481        let node_key = format!("{NODE_PREFIX}/{node_id}");
1482        let node_state_key = format!("{NODE_STATE_PREFIX}/{node_id}");
1483        let allowlist_key = format!("{ALLOWLIST_PREFIX}/{node_id}");
1484        let tracker_key = format!("{TRACKER_PREFIX}/{node_id}");
1485        let channel_key = format!("{CHANNEL_PREFIX}/{node_id}cafebabe");
1486
1487        let mut state = mk_state(vec![
1488            (node_key.as_str(), 1, json!({"n": 1})),
1489            (node_state_key.as_str(), 2, json!({"s": 1})),
1490            (allowlist_key.as_str(), 3, json!(["127.0.0.1"])),
1491            (tracker_key.as_str(), 4, json!({"t": 1})),
1492            (channel_key.as_str(), 5, json!({"c": 1})),
1493        ]);
1494
1495        state.delete_node(node_id).unwrap();
1496
1497        for (live_key, old_version) in vec![
1498            (node_key, 1u64),
1499            (node_state_key, 2u64),
1500            (allowlist_key, 3u64),
1501            (tracker_key, 4u64),
1502            (channel_key, 5u64),
1503        ] {
1504            assert_tombstone(&state, &live_key);
1505            assert!(old_version < u64::MAX);
1506        }
1507    }
1508}