Skip to content

Commit 531318e

Browse files
committed
Make 117 compatible
1 parent bccf3cd commit 531318e

File tree

4 files changed

+115
-73
lines changed

4 files changed

+115
-73
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ panic = 'abort' # Abort on panic
3030

3131
[features]
3232
default = []
33+
vss = []
34+
vss-test = []
3335

3436
[dependencies]
3537
#lightning = { version = "0.0.116", features = ["max_level_trace", "std"] }

src/builder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ use bip39::Mnemonic;
4848

4949
use bitcoin::BlockHash;
5050

51+
#[cfg(feature = "vss")]
52+
use crate::io::vss_store::VssStore;
5153
use std::convert::TryInto;
5254
use std::default::Default;
5355
use std::fmt;
@@ -264,6 +266,16 @@ impl NodeBuilder {
264266
self.build_with_store(kv_store)
265267
}
266268

269+
/// Builds a [`Node`] instance with a [`VssStore`] backend and according to the options
270+
/// previously configured.
271+
#[cfg(feature = "vss")]
272+
pub fn build_with_vss_store(
273+
&self, url: &str, store_id: String,
274+
) -> Result<Node<VssStore>, BuildError> {
275+
let vss = Arc::new(VssStore::new(url, store_id));
276+
self.build_with_store(vss)
277+
}
278+
267279
/// Builds a [`Node`] instance according to the options previously configured.
268280
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
269281
&self, kv_store: Arc<K>,

src/io/vss_store.rs

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
use std::io::Cursor;
1+
use io::Error;
22
use std::io;
3+
use std::io::ErrorKind;
4+
#[cfg(test)]
5+
use std::panic::RefUnwindSafe;
36

4-
use lightning::util::persist::KVStorePersister;
5-
use lightning::util::ser::Writeable;
7+
use crate::io::utils::check_namespace_key_validity;
8+
use lightning::util::persist::KVStore;
69
use tokio::runtime::Runtime;
710
use vss_client::client::VssClient;
811
use vss_client::error::VssError;
912
use vss_client::types::{
1013
DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest,
1114
};
1215

13-
use crate::io::get_namespace_and_key_from_prefixed;
14-
use crate::KVStore;
15-
1616
/// A [`KVStore`] implementation that writes to and reads from a [VSS](https://github.com/lightningdevkit/vss-server/blob/main/README.md) backend.
1717
pub struct VssStore {
1818
client: VssClient,
@@ -21,43 +21,54 @@ pub struct VssStore {
2121
}
2222

2323
impl VssStore {
24+
#[cfg(feature = "vss")]
2425
pub(crate) fn new(base_url: &str, store_id: String) -> Self {
2526
let client = VssClient::new(base_url);
2627
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
2728
Self { client, store_id, runtime }
2829
}
2930

30-
fn build_key(&self, namespace: &str, key: &str) -> io::Result<String> {
31+
fn build_key(&self, namespace: &str, sub_namespace: &str, key: &str) -> io::Result<String> {
3132
if key.is_empty() {
32-
return Err(io::Error::new(io::ErrorKind::Other, "Empty key is not allowed"));
33+
return Err(Error::new(ErrorKind::Other, "Empty key is not allowed"));
3334
}
35+
// But namespace and sub_namespace can be empty
3436
if namespace.is_empty() {
3537
Ok(key.to_string())
3638
} else {
37-
Ok(format!("{}/{}", namespace, key))
39+
Ok(format!("{}#{}#{}", namespace, sub_namespace, key))
3840
}
3941
}
4042

41-
fn split_key(&self, key: &str) -> (String, String) {
42-
get_namespace_and_key_from_prefixed(key).unwrap()
43+
fn split_key(&self, key: &str) -> io::Result<(String, String, String)> {
44+
let parts: Vec<&str> = key.split('#').collect();
45+
match parts.as_slice() {
46+
[namespace, sub_namespace, actual_key] => {
47+
Ok((namespace.to_string(), sub_namespace.to_string(), actual_key.to_string()))
48+
}
49+
_ => Err(Error::new(ErrorKind::InvalidData, "Invalid key format")),
50+
}
4351
}
4452

45-
async fn list_all_keys(&self, namespace: &str) -> Result<Vec<String>, VssError> {
53+
async fn list_all_keys(&self, namespace: &str, sub_namespace: &str) -> io::Result<Vec<String>> {
4654
let mut page_token = None;
4755
let mut keys = vec![];
48-
56+
let key_prefix = format!("{}#{}", namespace, sub_namespace);
4957
while page_token != Some("".to_string()) {
5058
let request = ListKeyVersionsRequest {
5159
store_id: self.store_id.to_string(),
52-
key_prefix: Some(namespace.to_string()),
60+
key_prefix: Some(key_prefix.to_string()),
5361
page_token,
5462
page_size: None,
5563
};
5664

57-
let response = self.client.list_key_versions(&request).await?;
65+
let response = self.client.list_key_versions(&request).await.map_err(|e| {
66+
let msg = format!("Failed to list keys in {}/{}: {}", namespace, sub_namespace, e);
67+
Error::new(ErrorKind::Other, msg)
68+
})?;
5869

5970
for kv in response.key_versions {
60-
keys.push(self.split_key(&kv.key).1);
71+
keys.push(self.split_key(&kv.key)?.2);
6172
}
6273
page_token = response.next_page_token;
6374
}
@@ -66,37 +77,38 @@ impl VssStore {
6677
}
6778

6879
impl KVStore for VssStore {
69-
type Reader = Cursor<Vec<u8>>;
70-
71-
fn read(&self, namespace: &str, key: &str) -> io::Result<Self::Reader> {
80+
fn read(&self, namespace: &str, sub_namespace: &str, key: &str) -> io::Result<Vec<u8>> {
81+
check_namespace_key_validity(namespace, sub_namespace, Some(key), "read")?;
7282
let request = GetObjectRequest {
7383
store_id: self.store_id.to_string(),
74-
key: self.build_key(namespace, key)?,
84+
key: self.build_key(namespace, sub_namespace, key)?,
7585
};
76-
86+
// self.runtime.spawn()
7787
let resp =
7888
tokio::task::block_in_place(|| self.runtime.block_on(self.client.get_object(&request)))
79-
.map_err(|e| {
80-
match e {
81-
VssError::NoSuchKeyError(..) => {
82-
let msg = format!("Failed to read as key could not be found: {}/{}. Details: {}", namespace, key, e);
83-
io::Error::new(io::ErrorKind::NotFound, msg)
84-
}
85-
_ => {
86-
let msg = format!("Failed to read from key {}/{}: {}", namespace, key, e);
87-
io::Error::new(io::ErrorKind::Other, msg)
88-
}
89+
.map_err(|e| match e {
90+
VssError::NoSuchKeyError(..) => {
91+
let msg = format!(
92+
"Failed to read as key could not be found: {}/{}. Details: {}",
93+
namespace, key, e
94+
);
95+
Error::new(ErrorKind::NotFound, msg)
96+
}
97+
_ => {
98+
let msg = format!("Failed to read from key {}/{}: {}", namespace, key, e);
99+
Error::new(ErrorKind::Other, msg)
89100
}
90101
})?;
91-
Ok(Cursor::new(resp.value.unwrap().value))
102+
Ok(resp.value.unwrap().value)
92103
}
93104

94-
fn write(&self, namespace: &str, key: &str, buf: &[u8]) -> io::Result<()> {
105+
fn write(&self, namespace: &str, sub_namespace: &str, key: &str, buf: &[u8]) -> io::Result<()> {
106+
check_namespace_key_validity(namespace, sub_namespace, Some(key), "write")?;
95107
let request = PutObjectRequest {
96108
store_id: self.store_id.to_string(),
97109
global_version: None,
98110
transaction_items: vec![KeyValue {
99-
key: self.build_key(namespace, key)?,
111+
key: self.build_key(namespace, sub_namespace, key)?,
100112
version: -1,
101113
value: buf.to_vec(),
102114
}],
@@ -106,17 +118,20 @@ impl KVStore for VssStore {
106118
tokio::task::block_in_place(|| self.runtime.block_on(self.client.put_object(&request)))
107119
.map_err(|e| {
108120
let msg = format!("Failed to write to key {}/{}: {}", namespace, key, e);
109-
io::Error::new(io::ErrorKind::Other, msg)
121+
Error::new(ErrorKind::Other, msg)
110122
})?;
111123

112124
Ok(())
113125
}
114126

115-
fn remove(&self, namespace: &str, key: &str) -> io::Result<()> {
127+
fn remove(
128+
&self, namespace: &str, sub_namespace: &str, key: &str, _lazy: bool,
129+
) -> io::Result<()> {
130+
check_namespace_key_validity(namespace, sub_namespace, Some(key), "remove")?;
116131
let request = DeleteObjectRequest {
117132
store_id: self.store_id.to_string(),
118133
key_value: Some(KeyValue {
119-
key: self.build_key(namespace, key)?,
134+
key: self.build_key(namespace, sub_namespace, key)?,
120135
version: -1,
121136
value: vec![],
122137
}),
@@ -125,54 +140,44 @@ impl KVStore for VssStore {
125140
tokio::task::block_in_place(|| self.runtime.block_on(self.client.delete_object(&request)))
126141
.map_err(|e| {
127142
let msg = format!("Failed to delete key {}/{}: {}", namespace, key, e);
128-
io::Error::new(io::ErrorKind::Other, msg)
143+
Error::new(ErrorKind::Other, msg)
129144
})?;
130145
Ok(())
131146
}
132147

133-
fn list(&self, namespace: &str) -> io::Result<Vec<String>> {
134-
let keys =
135-
tokio::task::block_in_place(|| self.runtime.block_on(self.list_all_keys(namespace)))
136-
.map_err(|e| {
137-
let msg =
138-
format!("Failed to retrieve keys in namespace: {} : {}", namespace, e);
139-
io::Error::new(io::ErrorKind::Other, msg)
140-
})?;
148+
fn list(&self, namespace: &str, sub_namespace: &str) -> io::Result<Vec<String>> {
149+
check_namespace_key_validity(namespace, sub_namespace, None, "list")?;
150+
151+
let keys = tokio::task::block_in_place(|| {
152+
self.runtime.block_on(self.list_all_keys(namespace, sub_namespace))
153+
})
154+
.map_err(|e| {
155+
let msg = format!("Failed to retrieve keys in namespace: {} : {}", namespace, e);
156+
Error::new(ErrorKind::Other, msg)
157+
})?;
141158

142159
Ok(keys)
143160
}
144161
}
145162

146-
impl KVStorePersister for VssStore {
147-
fn persist<W: Writeable>(&self, prefixed_key: &str, object: &W) -> io::Result<()> {
148-
let (namespace, key) = self.split_key(prefixed_key);
149-
self.write(&namespace, &key, &object.encode())?;
150-
Ok(())
151-
}
152-
}
163+
#[cfg(test)]
164+
impl RefUnwindSafe for VssStore {}
153165

154166
#[cfg(test)]
167+
#[cfg(feature = "vss-test")]
155168
mod tests {
156-
use proptest::prelude::*;
157-
158-
use crate::io::do_read_write_remove_list_persist;
159-
use crate::test::utils::random_storage_path;
160-
161169
use super::*;
162-
163-
proptest! {
164-
#[test]
165-
fn read_write_remove_list_persist(data in any::<[u8; 32]>()) {
166-
let vss_base_url = std::env::var("TEST_VSS_BASE_URL");
167-
if vss_base_url.is_ok()
168-
{
169-
let rand_store_id = random_storage_path();
170-
let vss_store = VssStore::new(&vss_base_url.unwrap(), rand_store_id);
171-
172-
do_read_write_remove_list_persist(&data, &vss_store);
173-
}else{
174-
eprintln!("** SKIPPING `VssStore` test-suite since environment variable `TEST_VSS_BASE_URL` is not set **");
175-
}
176-
}
170+
use crate::io::test_utils::do_read_write_remove_list_persist;
171+
use rand::distributions::Alphanumeric;
172+
use rand::{thread_rng, Rng};
173+
174+
#[test]
175+
fn read_write_remove_list_persist() {
176+
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
177+
let mut rng = thread_rng();
178+
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
179+
let vss_store = VssStore::new(&vss_base_url, rand_store_id);
180+
181+
do_read_write_remove_list_persist(&vss_store);
177182
}
178183
}

src/test/functional_tests.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ fn channel_full_cycle() {
1818
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, false);
1919
}
2020

21+
#[test]
22+
#[cfg(feature = "vss-test")]
23+
fn channel_full_cycle_with_vss_store() {
24+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
25+
println!("== Node A ==");
26+
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
27+
let config_a = random_config();
28+
let mut builder_a = NodeBuilder::from_config(config_a);
29+
builder_a.set_esplora_server(esplora_url.clone());
30+
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
31+
let node_a = builder_a.build_with_vss_store(&vss_base_url, "node_1_store".to_string()).unwrap();
32+
node_a.start().unwrap();
33+
34+
println!("\n== Node B ==");
35+
let config_b = random_config();
36+
let mut builder_b = NodeBuilder::from_config(config_b);
37+
builder_b.set_esplora_server(esplora_url);
38+
let node_b = builder_b.build_with_vss_store(&vss_base_url, "node_2_store".to_string()).unwrap();
39+
node_b.start().unwrap();
40+
41+
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, false);
42+
}
43+
2144
#[test]
2245
fn channel_full_cycle_0conf() {
2346
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)