From f337b26cfbc3e7c6da6d09552cfcb820115474b8 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Fri, 22 Jan 2021 15:49:06 +0100 Subject: [PATCH] Improve sled-based light store (#773) * Encode heights used as keys in sled-based light store in big-endian representation One needs to represent the keys in big-endian form for sled's iterators and ordered operations to work properly. See [1] and [2] for more information. [1] https://github.com/spacejam/sled#a-note-on-lexicographic-ordering-and-endianness [2] https://github.com/spacejam/sled/blob/aa3fcf37d844e2d1382e0a58267dc3b3ae3b25aa/examples/structured.rs#L23-L30 * Use sled trees instead of key prefixes * Use next_back instead of max_by to get the latest block in the light store This avoids a full linear scan, and is even likely constant time. * Rename LightStore::latest* to highest* and introduce LightStore::lowest * Feature-guard the sled-based lightstore * Hide sled dependency by introducing SledStore::open * Update changelog * Formatting * Add LightStore::lowest_trusted_or_verified * Add more tests --- CHANGELOG.md | 11 +- light-client/Cargo.toml | 1 + light-client/src/builder/light_client.rs | 2 +- light-client/src/components/scheduler.rs | 8 +- light-client/src/light_client.rs | 2 +- light-client/src/store.rs | 21 ++- light-client/src/store/memory.rs | 10 +- light-client/src/store/sled.rs | 110 ++++++++++--- light-client/src/store/sled/utils.rs | 200 +++++++++++------------ light-client/src/supervisor.rs | 2 +- light-client/src/utils/std_ext.rs | 9 + light-node/Cargo.toml | 3 +- light-node/src/commands/initialize.rs | 2 +- light-node/src/commands/start.rs | 2 +- 14 files changed, 228 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a7a144b..9b26f75d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,14 @@ ### BUG FIXES +* `[light-client]` Fix potential block ordering problem with sled-based lightstore ([#769]) +* `[light-client]` Improve the API of the light store. ([#428]) * `[light-client]` The `sled`-backed lightstore is now feature-guarded under - the `lightstore-sled` feature, which is enabled by default for now. ([#772]) + the `lightstore-sled` feature, which is enabled by default for now. ([#428]) +[#769]: https://github.com/informalsystems/tendermint-rs/issues/769 [#764]: https://github.com/informalsystems/tendermint-rs/issues/764 -[#772]: https://github.com/informalsystems/tendermint-rs/pull/772 +[#428]: https://github.com/informalsystems/tendermint-rs/issues/428 ## v0.17.1 @@ -36,8 +39,8 @@ Client's correct and reliable operation. ### BUG FIXES: -- `[tendermint]` `Time` values were not always formatted properly, - causing the light client to sometimes return malformed light blocks. ([#774]) +* `[tendermint]` `Time` values were not always formatted properly, + causing the light client to sometimes return malformed light blocks ([#774]) [#774]: https://github.com/informalsystems/tendermint-rs/issues/774 diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 17dbec708..07899168e 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -57,3 +57,4 @@ tendermint-testgen = { path = "../testgen"} serde_json = "1.0.51" gumdrop = "0.8.0" rand = "0.7.3" +tempdir = "0.3.7" diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index e70da0c12..1d4f5b36d 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -133,7 +133,7 @@ impl LightClientBuilder { pub fn trust_from_store(self) -> Result, Error> { let trusted_state = self .light_store - .latest_trusted_or_verified() + .highest_trusted_or_verified() .ok_or(error::Kind::NoTrustedStateInStore)?; self.trust_light_block(trusted_state) diff --git a/light-client/src/components/scheduler.rs b/light-client/src/components/scheduler.rs index a1b085f31..6210cd67a 100644 --- a/light-client/src/components/scheduler.rs +++ b/light-client/src/components/scheduler.rs @@ -20,7 +20,7 @@ pub trait Scheduler: Send + Sync { /// /// ## Postcondition /// - The resulting height must be valid according to `valid_schedule`. [LCV-SCHEDULE-POST.1] - #[pre(light_store.latest_trusted_or_verified().is_some())] + #[pre(light_store.highest_trusted_or_verified().is_some())] #[post(valid_schedule(ret, target_height, current_height, light_store))] fn schedule( &self, @@ -53,7 +53,7 @@ where /// /// ## Postcondition /// - The resulting height must be valid according to `valid_schedule`. [LCV-SCHEDULE-POST.1] -#[pre(light_store.latest_trusted_or_verified().is_some())] +#[pre(light_store.highest_trusted_or_verified().is_some())] #[post(valid_schedule(ret, target_height, current_height, light_store))] pub fn basic_bisecting_schedule( light_store: &dyn LightStore, @@ -61,7 +61,7 @@ pub fn basic_bisecting_schedule( target_height: Height, ) -> Height { let trusted_height = light_store - .latest_trusted_or_verified() + .highest_trusted_or_verified() .map(|lb| lb.height()) .unwrap(); @@ -105,7 +105,7 @@ pub fn valid_schedule( light_store: &dyn LightStore, ) -> bool { let latest_trusted_height = light_store - .latest_trusted_or_verified() + .highest_trusted_or_verified() .map(|lb| lb.height()) .unwrap(); diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index f3de21f87..3413f2f39 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -172,7 +172,7 @@ impl LightClient { // Get the latest trusted state let trusted_state = state .light_store - .latest_trusted_or_verified() + .highest_trusted_or_verified() .ok_or(ErrorKind::NoInitialTrustedState)?; if target_height < trusted_state.height() { diff --git a/light-client/src/store.rs b/light-client/src/store.rs index 9e18da84c..6a058de04 100644 --- a/light-client/src/store.rs +++ b/light-client/src/store.rs @@ -39,7 +39,10 @@ pub trait LightStore: Debug + Send + Sync { fn remove(&mut self, height: Height, status: Status); /// Get the light block of greatest height with the given status. - fn latest(&self, status: Status) -> Option; + fn highest(&self, status: Status) -> Option; + + /// Get the light block of lowest height with the given status. + fn lowest(&self, status: Status) -> Option; /// Get an iterator of all light blocks with the given status. fn all(&self, status: Status) -> Box>; @@ -62,15 +65,25 @@ pub trait LightStore: Debug + Send + Sync { } /// Get the light block of greatest height with the trusted or verified status. - fn latest_trusted_or_verified(&self) -> Option { - let latest_trusted = self.latest(Status::Trusted); - let latest_verified = self.latest(Status::Verified); + fn highest_trusted_or_verified(&self) -> Option { + let latest_trusted = self.highest(Status::Trusted); + let latest_verified = self.highest(Status::Verified); std_ext::option::select(latest_trusted, latest_verified, |t, v| { std_ext::cmp::max_by_key(t, v, |lb| lb.height()) }) } + /// Get the light block of lowest height with the trusted or verified status. + fn lowest_trusted_or_verified(&self) -> Option { + let latest_trusted = self.lowest(Status::Trusted); + let latest_verified = self.lowest(Status::Verified); + + std_ext::option::select(latest_trusted, latest_verified, |t, v| { + std_ext::cmp::min_by_key(t, v, |lb| lb.height()) + }) + } + /// Get the light block of the given height with the trusted or verified status. fn get_trusted_or_verified(&self, height: Height) -> Option { self.get(height, Status::Trusted) diff --git a/light-client/src/store/memory.rs b/light-client/src/store/memory.rs index 66b1714ae..6af7389c5 100644 --- a/light-client/src/store/memory.rs +++ b/light-client/src/store/memory.rs @@ -65,7 +65,7 @@ impl LightStore for MemoryStore { self.insert(light_block.clone(), status); } - fn latest(&self, status: Status) -> Option { + fn highest(&self, status: Status) -> Option { self.store .iter() .filter(|(_, e)| e.status == status) @@ -73,6 +73,14 @@ impl LightStore for MemoryStore { .map(|(_, e)| e.light_block.clone()) } + fn lowest(&self, status: Status) -> Option { + self.store + .iter() + .filter(|(_, e)| e.status == status) + .min_by_key(|(&height, _)| height) + .map(|(_, e)| e.light_block.clone()) + } + fn all(&self, status: Status) -> Box> { let light_blocks: Vec<_> = self .store diff --git a/light-client/src/store/sled.rs b/light-client/src/store/sled.rs index f30989936..ddc47ca8e 100644 --- a/light-client/src/store/sled.rs +++ b/light-client/src/store/sled.rs @@ -5,26 +5,25 @@ pub mod utils; use std::path::Path; use crate::{ - store::sled::utils::*, + store::sled::utils::HeightIndexedDb, types::{Height, LightBlock}, }; use super::{LightStore, Status}; -use ::sled::Db as SledDb; -const UNVERIFIED_PREFIX: &str = "light_store/unverified"; -const VERIFIED_PREFIX: &str = "light_store/verified"; -const TRUSTED_PREFIX: &str = "light_store/trusted"; -const FAILED_PREFIX: &str = "light_store/failed"; +const UNVERIFIED: &str = "unverified"; +const VERIFIED: &str = "verified"; +const TRUSTED: &str = "trusted"; +const FAILED: &str = "failed"; /// Persistent store backed by an on-disk `sled` database. #[derive(Debug, Clone)] pub struct SledStore { - db: SledDb, - unverified_db: KeyValueDb, - verified_db: KeyValueDb, - trusted_db: KeyValueDb, - failed_db: KeyValueDb, + unverified_db: HeightIndexedDb, + verified_db: HeightIndexedDb, + trusted_db: HeightIndexedDb, + failed_db: HeightIndexedDb, + db: sled::Db, } impl SledStore { @@ -34,17 +33,17 @@ impl SledStore { } /// Create a new persistent store from a sled database that is already open. - pub fn new(db: SledDb) -> Self { + pub fn new(db: sled::Db) -> Self { Self { + unverified_db: HeightIndexedDb::new(db.open_tree(UNVERIFIED).unwrap()), + verified_db: HeightIndexedDb::new(db.open_tree(VERIFIED).unwrap()), + trusted_db: HeightIndexedDb::new(db.open_tree(TRUSTED).unwrap()), + failed_db: HeightIndexedDb::new(db.open_tree(FAILED).unwrap()), db, - unverified_db: KeyValueDb::new(UNVERIFIED_PREFIX), - verified_db: KeyValueDb::new(VERIFIED_PREFIX), - trusted_db: KeyValueDb::new(TRUSTED_PREFIX), - failed_db: KeyValueDb::new(FAILED_PREFIX), } } - fn db(&self, status: Status) -> &KeyValueDb { + fn db(&self, status: Status) -> &HeightIndexedDb { match status { Status::Unverified => &self.unverified_db, Status::Verified => &self.verified_db, @@ -56,7 +55,7 @@ impl SledStore { impl LightStore for SledStore { fn get(&self, height: Height, status: Status) -> Option { - self.db(status).get(&self.db, &height).ok().flatten() + self.db(status).get(height).ok().flatten() } fn update(&mut self, light_block: &LightBlock, status: Status) { @@ -64,30 +63,87 @@ impl LightStore for SledStore { for other in Status::iter() { if status != *other { - self.db(*other).remove(&self.db, &height).ok(); + self.db(*other).remove(height).ok(); } } - self.db(status).insert(&self.db, &height, light_block).ok(); + self.db(status).insert(height, light_block).ok(); } fn insert(&mut self, light_block: LightBlock, status: Status) { self.db(status) - .insert(&self.db, &light_block.height(), &light_block) + .insert(light_block.height(), &light_block) .ok(); } fn remove(&mut self, height: Height, status: Status) { - self.db(status).remove(&self.db, &height).ok(); + self.db(status).remove(height).ok(); } - fn latest(&self, status: Status) -> Option { - self.db(status) - .iter(&self.db) - .max_by(|first, second| first.height().cmp(&second.height())) + fn highest(&self, status: Status) -> Option { + self.db(status).iter().next_back() + } + + fn lowest(&self, status: Status) -> Option { + self.db(status).iter().next() } fn all(&self, status: Status) -> Box> { - Box::new(self.db(status).iter(&self.db)) + Box::new(self.db(status).iter()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempdir::TempDir; + use tendermint_testgen::{light_block::TMLightBlock as TGLightBlock, Generator, LightChain}; + + #[test] + fn highest_returns_latest_block() { + with_blocks(10, |mut db, blocks| { + let initial_block = blocks[0].clone(); + db.insert(initial_block.clone(), Status::Verified); + assert_eq!(db.lowest(Status::Verified).as_ref(), Some(&initial_block)); + + for block in blocks.into_iter().skip(1) { + db.insert(block, Status::Verified); + assert_eq!(db.lowest(Status::Verified).as_ref(), Some(&initial_block)); + } + }) + } + + #[test] + fn lowest_returns_earliest_block() { + with_blocks(10, |mut db, blocks| { + for block in blocks { + db.insert(block.clone(), Status::Verified); + assert_eq!(db.highest(Status::Verified), Some(block)); + } + }) + } + + fn with_blocks(height: u64, f: impl FnOnce(SledStore, Vec)) { + let tmp_dir = TempDir::new("tendermint_light_client_sled_test").unwrap(); + let db = SledStore::open(tmp_dir).unwrap(); + + let chain = LightChain::default_with_length(height); + let blocks = chain + .light_blocks + .into_iter() + .map(|lb| lb.generate().unwrap()) + .map(testgen_to_lb) + .collect::>(); + + f(db, blocks) + } + + fn testgen_to_lb(tm_lb: TGLightBlock) -> LightBlock { + LightBlock { + signed_header: tm_lb.signed_header, + validators: tm_lb.validators, + next_validators: tm_lb.next_validators, + provider: tm_lb.provider, + } } } diff --git a/light-client/src/store/sled/utils.rs b/light-client/src/store/sled/utils.rs index 1d24d4221..26e194243 100644 --- a/light-client/src/store/sled/utils.rs +++ b/light-client/src/store/sled/utils.rs @@ -2,84 +2,50 @@ //! by taking care of (de)serializing keys and values with the //! CBOR binary encoding. -use serde::{de::DeserializeOwned, Serialize}; use std::marker::PhantomData; -use crate::errors::{Error, ErrorKind}; - -/// Provides a view over the database for storing a single value at the given prefix. -pub fn single(prefix: impl Into>) -> SingleDb { - SingleDb::new(prefix) -} - -/// Provides a view over the database for storing key/value pairs at the given prefix. -pub fn key_value(prefix: impl Into>) -> KeyValueDb { - KeyValueDb::new(prefix) -} - -/// Provides a view over the database for storing a single value at the given prefix. -pub struct SingleDb(KeyValueDb<(), V>); - -impl SingleDb { - /// Create a view over the database for storing a single value at the given prefix. - pub fn new(prefix: impl Into>) -> Self { - Self(KeyValueDb::new(prefix)) - } -} - -impl SingleDb -where - V: Serialize + DeserializeOwned, -{ - /// Get the value associated with this view in the given sled database. - pub fn get(&self, db: &sled::Db) -> Result, Error> { - self.0.get(&db, &()) - } +use serde::{de::DeserializeOwned, Serialize}; - /// Set a value associated with this view in the given sled database. - pub fn set(&self, db: &sled::Db, value: &V) -> Result<(), Error> { - self.0.insert(&db, &(), &value) - } -} +use crate::errors::{Error, ErrorKind}; +use crate::types::Height; /// Provides a view over the database for storing key/value pairs at the given prefix. #[derive(Clone, Debug)] -pub struct KeyValueDb { - prefix: Vec, - marker: PhantomData<(K, V)>, +pub struct HeightIndexedDb { + tree: sled::Tree, + marker: PhantomData, } -impl KeyValueDb { +impl HeightIndexedDb { /// Create a view over the database for storing key/value pairs at the given prefix. - pub fn new(prefix: impl Into>) -> Self { + pub fn new(tree: sled::Tree) -> Self { Self { - prefix: prefix.into(), + tree, marker: PhantomData, } } } -impl KeyValueDb +fn key_bytes(height: Height) -> [u8; 8] { + // we need to store the height in big-endian form for + // sled's iterators and ordered operations to work properly. + // See https://github.com/spacejam/sled#a-note-on-lexicographic-ordering-and-endianness + height.value().to_be_bytes() +} + +impl HeightIndexedDb where - K: Serialize, V: Serialize + DeserializeOwned, { - fn prefixed_key(&self, mut key_bytes: Vec) -> Vec { - let mut prefix_bytes = self.prefix.clone(); - prefix_bytes.append(&mut key_bytes); - prefix_bytes - } - - /// Get the value associated with a key within this view in the given sled database. - pub fn get(&self, db: &sled::Db, key: &K) -> Result, Error> { - let key_bytes = serde_cbor::to_vec(&key).map_err(|e| ErrorKind::Store.context(e))?; - let prefixed_key_bytes = self.prefixed_key(key_bytes); - - let value_bytes = db - .get(prefixed_key_bytes) + /// Get the value associated with the given height within this tree + pub fn get(&self, height: Height) -> Result, Error> { + let key = key_bytes(height); + let value = self + .tree + .get(key) .map_err(|e| ErrorKind::Store.context(e))?; - match value_bytes { + match value { Some(bytes) => { let value = serde_cbor::from_slice(&bytes).map_err(|e| ErrorKind::Store.context(e))?; @@ -89,76 +55,92 @@ where } } - /// Check whether there exists a key within this view in the given sled database. - pub fn contains_key(&self, db: &sled::Db, key: &K) -> Result { - let key_bytes = serde_cbor::to_vec(&key).map_err(|e| ErrorKind::Store.context(e))?; - let prefixed_key_bytes = self.prefixed_key(key_bytes); + /// Check whether there exists a value associated with the given height within this tree + pub fn contains_key(&self, height: Height) -> Result { + let key = key_bytes(height); - let exists = db - .contains_key(prefixed_key_bytes) + let exists = self + .tree + .contains_key(key) .map_err(|e| ErrorKind::Store.context(e))?; Ok(exists) } - /// Insert a value associated with a key within this view in the given sled database. - pub fn insert(&self, db: &sled::Db, key: &K, value: &V) -> Result<(), Error> { - let key_bytes = serde_cbor::to_vec(&key).map_err(|e| ErrorKind::Store.context(e))?; - let prefixed_key_bytes = self.prefixed_key(key_bytes); - let value_bytes = serde_cbor::to_vec(&value).map_err(|e| ErrorKind::Store.context(e))?; + /// Insert a value associated with a height within this tree + pub fn insert(&self, height: Height, value: &V) -> Result<(), Error> { + let key = key_bytes(height); + let bytes = serde_cbor::to_vec(&value).map_err(|e| ErrorKind::Store.context(e))?; - db.insert(prefixed_key_bytes, value_bytes) - .map(|_| ()) + self.tree + .insert(key, bytes) .map_err(|e| ErrorKind::Store.context(e))?; Ok(()) } - /// Remove the value associated with a key within this view in the given sled database. - pub fn remove(&self, db: &sled::Db, key: &K) -> Result<(), Error> { - let key_bytes = serde_cbor::to_vec(&key).map_err(|e| ErrorKind::Store.context(e))?; - let prefixed_key_bytes = self.prefixed_key(key_bytes); + /// Remove the value associated with a height within this tree + pub fn remove(&self, height: Height) -> Result<(), Error> { + let key = key_bytes(height); - db.remove(prefixed_key_bytes) + self.tree + .remove(key) .map_err(|e| ErrorKind::Store.context(e))?; Ok(()) } - /// Iterate over all values within this view in the given sled database. - pub fn iter(&self, db: &sled::Db) -> impl DoubleEndedIterator { - db.iter() - .flatten() - .map(|(_, v)| serde_cbor::from_slice(&v)) + /// Return an iterator over all values within this tree + pub fn iter(&self) -> impl DoubleEndedIterator { + self.tree + .iter() .flatten() + .flat_map(|(_, v)| serde_cbor::from_slice(&v)) } } -// TODO: The test below is currently disabled because it fails on CI as we don't have -// access to `/tmp`. Need to figure out how to specify a proper temp dir. - -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::types::Height; - -// #[test] -// fn iter_next_back_returns_highest_height() { -// const DB_PATH: &str = "/tmp/tendermint_light_client_sled_test/"; -// std::fs::remove_dir_all(DB_PATH).unwrap(); -// let db = sled::open(DB_PATH).unwrap(); -// let kv: KeyValueDb = key_value("light_store/verified"); - -// kv.insert(&db, &1, &1).unwrap(); -// kv.insert(&db, &589473798493, &589473798493).unwrap(); -// kv.insert(&db, &12342425, &12342425).unwrap(); -// kv.insert(&db, &4, &4).unwrap(); - -// let mut iter = kv.iter(&db); -// assert_eq!(iter.next_back(), Some(589473798493)); -// assert_eq!(iter.next_back(), Some(12342425)); -// assert_eq!(iter.next_back(), Some(4)); -// assert_eq!(iter.next_back(), Some(1)); -// assert_eq!(iter.next_back(), None); -// } -// } +#[cfg(test)] +mod tests { + use super::*; + use tempdir::TempDir; + + #[test] + fn iter_next_returns_lowest_height() { + let tmp_dir = TempDir::new("tendermint_light_client_sled_utils_test").unwrap(); + let db = sled::open(tmp_dir).unwrap(); + let kv = HeightIndexedDb::new(db.open_tree("light_store/verified").unwrap()); + + for i in 1..=1000_u32 { + kv.insert(i.into(), &i).unwrap(); + } + + for i in (1000..=2000_u32).rev() { + kv.insert(i.into(), &i).unwrap(); + } + + let mut iter = kv.iter(); + for i in 1..=2000_u32 { + assert_eq!(iter.next(), Some(i)); + } + } + + #[test] + fn iter_next_back_returns_highest_height() { + let tmp_dir = TempDir::new("tendermint_light_client_sled_utils_test").unwrap(); + let db = sled::open(tmp_dir).unwrap(); + let kv = HeightIndexedDb::new(db.open_tree("light_store/verified").unwrap()); + + for i in 1..=1000_u32 { + kv.insert(i.into(), &i).unwrap(); + } + + for i in (1000..=2000_u32).rev() { + kv.insert(i.into(), &i).unwrap(); + } + + let mut iter = kv.iter(); + for i in (1..=2000_u32).rev() { + assert_eq!(iter.next_back(), Some(i)); + } + } +} diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 12a0b74cc..112d4b359 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -72,7 +72,7 @@ impl Instance { /// Get the latest trusted block. pub fn latest_trusted(&self) -> Option { - self.state.light_store.latest(Status::Trusted) + self.state.light_store.highest(Status::Trusted) } /// Trust the given block. diff --git a/light-client/src/utils/std_ext.rs b/light-client/src/utils/std_ext.rs index 22664df5c..40598b386 100644 --- a/light-client/src/utils/std_ext.rs +++ b/light-client/src/utils/std_ext.rs @@ -8,6 +8,15 @@ pub mod cmp { b } } + + /// Stable version of `std::cmp::min_by_key`. + pub fn min_by_key(a: A, b: A, key: impl Fn(&A) -> B) -> A { + if key(&a) <= key(&b) { + a + } else { + b + } + } } pub mod option { diff --git a/light-node/Cargo.toml b/light-node/Cargo.toml index 1d9dfc979..eeb1f12f2 100644 --- a/light-node/Cargo.toml +++ b/light-node/Cargo.toml @@ -36,10 +36,11 @@ jsonrpc-http-server = "14.2" jsonrpc-derive = "14.2" serde = { version = "1", features = ["serde_derive"] } serde_json = "1.0" +thiserror = "1.0" + tendermint = { version = "0.17.1", path = "../tendermint" } tendermint-light-client = { version = "0.17.1", path = "../light-client", features = ["lightstore-sled"] } tendermint-rpc = { version = "0.17.1", path = "../rpc", features = ["http-client"] } -thiserror = "1.0" [dependencies.abscissa_core] version = "0.5.0" diff --git a/light-node/src/commands/initialize.rs b/light-node/src/commands/initialize.rs index 4ff7623e2..b1013b288 100644 --- a/light-node/src/commands/initialize.rs +++ b/light-node/src/commands/initialize.rs @@ -71,7 +71,7 @@ fn initialize_subjectively( let light_store = SledStore::open(&config.db_path).map_err(|e| format!("could not open database: {}", e))?; - if let Some(trusted_state) = light_store.latest_trusted_or_verified() { + if let Some(trusted_state) = light_store.highest_trusted_or_verified() { status_warn!( "already existing trusted or verified state of height {} in database: {:?}", trusted_state.signed_header.header.height, diff --git a/light-node/src/commands/start.rs b/light-node/src/commands/start.rs index d8e63eef1..88e7ea1b7 100644 --- a/light-node/src/commands/start.rs +++ b/light-node/src/commands/start.rs @@ -101,7 +101,7 @@ impl StartCmd { let primary_store = SledStore::open(db_path).map_err(|e| format!("could not open database: {}", e))?; - if primary_store.latest_trusted_or_verified().is_none() { + if primary_store.highest_trusted_or_verified().is_none() { return Err("no trusted or verified state in store for primary, please initialize with the `initialize` subcommand first".to_string()); }