Skip to content

Commit 612970d

Browse files
authoredApr 30, 2025··
Merge pull request #88 from buffrr/wiring
Refactor & update dependencies
2 parents 7e44050 + 304e17c commit 612970d

File tree

13 files changed

+180
-359
lines changed

13 files changed

+180
-359
lines changed
 

‎Cargo.lock

Lines changed: 18 additions & 58 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎client/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,20 @@ path = "src/lib.rs"
1818
[dependencies]
1919
spaces_wallet = { path = "../wallet" }
2020
spaces_protocol = { path = "../protocol", version = "*", features = ["std"]}
21-
spacedb = { git = "https://github.com/spacesprotocol/spacedb", version = "0.0.5" }
21+
spacedb = { git = "https://github.com/spacesprotocol/spacedb", version = "0.0.6" }
2222

2323
tokio = { version = "1.37.0", features = ["signal"] }
2424
ctrlc = "3.4.4"
2525
anyhow = "1.0.86"
2626
clap = { version = "4.5.6", features = ["derive", "env"] }
2727
log = "0.4.21"
2828
serde = { version = "1.0.200", features = ["derive"] }
29-
toml = "0.8.14"
3029
hex = "0.4.3"
3130
jsonrpsee = { version = "0.22.5", features = ["server", "http-client", "macros"] }
3231
directories = "5.0.1"
3332
env_logger = "0.11.3"
3433
serde_json = "1.0.116"
35-
bincode = {version = "2.0.0-rc.3", features = ["serde", "derive"]}
34+
bincode = {version = "2.0.1", features = ["serde", "derive"]}
3635
base64 = "0.22.1"
3736
futures = "0.3.30"
3837
reqwest = { version = "0.12.5", default-features = false, features = ["json", "blocking", "rustls-tls"] }

‎client/src/app.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use std::path::PathBuf;
2+
use std::sync::Arc;
3+
use anyhow::anyhow;
4+
use tokio::sync::{broadcast, mpsc};
5+
use tokio::task::{JoinHandle, JoinSet};
6+
use crate::config::Args;
7+
use crate::rpc::{AsyncChainState, RpcServerImpl, WalletLoadRequest, WalletManager};
8+
use crate::source::{BitcoinBlockSource, BitcoinRpc};
9+
use crate::spaces::Spaced;
10+
use crate::store::LiveSnapshot;
11+
use crate::wallets::RpcWallet;
12+
13+
pub struct App {
14+
shutdown: broadcast::Sender<()>,
15+
services: JoinSet<anyhow::Result<()>>,
16+
}
17+
18+
impl App {
19+
pub fn new(shutdown: broadcast::Sender<()>) -> Self {
20+
Self {
21+
shutdown,
22+
services: JoinSet::new(),
23+
}
24+
}
25+
26+
async fn setup_rpc_wallet(&mut self, spaced: &Spaced, rx: mpsc::Receiver<WalletLoadRequest>, cbf: bool) {
27+
let wallet_service = RpcWallet::service(
28+
spaced.network,
29+
spaced.rpc.clone(),
30+
spaced.chain.state.clone(),
31+
rx,
32+
self.shutdown.clone(),
33+
spaced.num_workers,
34+
cbf
35+
);
36+
37+
self.services.spawn(async move {
38+
wallet_service
39+
.await
40+
.map_err(|e| anyhow!("Wallet service error: {}", e))
41+
});
42+
}
43+
44+
async fn setup_rpc_services(&mut self, spaced: &Spaced) {
45+
let (wallet_loader_tx, wallet_loader_rx) = mpsc::channel(1);
46+
47+
let wallet_manager = WalletManager {
48+
data_dir: spaced.data_dir.join("wallets"),
49+
network: spaced.network,
50+
rpc: spaced.rpc.clone(),
51+
wallet_loader: wallet_loader_tx,
52+
wallets: Arc::new(Default::default()),
53+
};
54+
55+
let (async_chain_state, async_chain_state_handle) = create_async_store(
56+
spaced.rpc.clone(),
57+
spaced.anchors_path.clone(),
58+
spaced.chain.state.clone(),
59+
spaced.block_index.as_ref().map(|index| index.state.clone()),
60+
self.shutdown.subscribe(),
61+
)
62+
.await;
63+
64+
self.services.spawn(async {
65+
async_chain_state_handle
66+
.await
67+
.map_err(|e| anyhow!("Chain state error: {}", e))
68+
});
69+
let rpc_server = RpcServerImpl::new(async_chain_state.clone(), wallet_manager);
70+
71+
let bind = spaced.bind.clone();
72+
let shutdown = self.shutdown.clone();
73+
74+
self.services.spawn(async move {
75+
rpc_server
76+
.listen(bind, shutdown)
77+
.await
78+
.map_err(|e| anyhow!("RPC Server error: {}", e))
79+
});
80+
81+
self.setup_rpc_wallet(spaced, wallet_loader_rx, spaced.cbf).await;
82+
}
83+
84+
async fn setup_sync_service(&mut self, mut spaced: Spaced) {
85+
let (spaced_sender, spaced_receiver) = tokio::sync::oneshot::channel();
86+
87+
let shutdown = self.shutdown.clone();
88+
let rpc = spaced.rpc.clone();
89+
90+
std::thread::spawn(move || {
91+
let source = BitcoinBlockSource::new(rpc);
92+
_ = spaced_sender.send(spaced.protocol_sync(source, shutdown));
93+
});
94+
95+
self.services.spawn(async move {
96+
spaced_receiver
97+
.await?
98+
.map_err(|e| anyhow!("Protocol sync error: {}", e))
99+
});
100+
}
101+
102+
pub async fn run(&mut self, args: Vec<String>) -> anyhow::Result<()> {
103+
let shutdown_receiver = self.shutdown.subscribe();
104+
let spaced = Args::configure(args, shutdown_receiver).await?;
105+
self.setup_rpc_services(&spaced).await;
106+
self.setup_sync_service(spaced).await;
107+
108+
while let Some(res) = self.services.join_next().await {
109+
res??
110+
}
111+
112+
Ok(())
113+
}
114+
}
115+
116+
async fn create_async_store(
117+
rpc: BitcoinRpc,
118+
anchors: Option<PathBuf>,
119+
chain_state: LiveSnapshot,
120+
block_index: Option<LiveSnapshot>,
121+
shutdown: broadcast::Receiver<()>,
122+
) -> (AsyncChainState, JoinHandle<()>) {
123+
let (tx, rx) = mpsc::channel(32);
124+
let async_store = AsyncChainState::new(tx);
125+
let client = reqwest::Client::new();
126+
let handle = tokio::spawn(async move {
127+
AsyncChainState::handler(
128+
&client,
129+
rpc,
130+
anchors,
131+
chain_state,
132+
block_index,
133+
rx,
134+
shutdown,
135+
)
136+
.await
137+
});
138+
(async_store, handle)
139+
}

‎client/src/bin/spaced.rs

Lines changed: 7 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,33 @@
1-
use std::{path::PathBuf, sync::Arc};
1+
use std::{env};
22

3-
use anyhow::anyhow;
43
use env_logger::Env;
54
use log::error;
65
use spaces_client::{
7-
config::{safe_exit, Args},
8-
rpc::{AsyncChainState, RpcServerImpl, WalletLoadRequest, WalletManager},
9-
source::{BitcoinBlockSource, BitcoinRpc},
10-
store,
11-
spaces::Spaced,
12-
wallets::RpcWallet,
6+
config::{safe_exit},
137
};
14-
use store::LiveSnapshot;
158
use tokio::{
16-
sync::{broadcast, mpsc},
17-
task::{JoinHandle, JoinSet},
9+
sync::{broadcast},
1810
};
11+
use spaces_client::app::App;
1912

2013
#[tokio::main]
2114
async fn main() {
2215
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
2316
let sigterm = tokio::signal::ctrl_c();
2417

25-
let mut app = Composer::new();
26-
let shutdown = app.shutdown.clone();
18+
let (shutdown, _) = broadcast::channel(1);
19+
let mut app = App::new(shutdown.clone());
2720

2821
tokio::spawn(async move {
2922
sigterm.await.expect("could not listen for shutdown");
3023
let _ = shutdown.send(());
3124
});
3225

33-
match app.run().await {
26+
match app.run(env::args().collect()).await {
3427
Ok(_) => {}
3528
Err(e) => {
3629
error!("{}", e.to_string());
3730
safe_exit(1);
3831
}
3932
}
4033
}
41-
42-
struct Composer {
43-
shutdown: broadcast::Sender<()>,
44-
services: JoinSet<anyhow::Result<()>>,
45-
}
46-
47-
impl Composer {
48-
fn new() -> Self {
49-
let (shutdown, _) = broadcast::channel(1);
50-
Self {
51-
shutdown,
52-
services: JoinSet::new(),
53-
}
54-
}
55-
56-
async fn setup_rpc_wallet(&mut self, spaced: &Spaced, rx: mpsc::Receiver<WalletLoadRequest>, cbf: bool) {
57-
let wallet_service = RpcWallet::service(
58-
spaced.network,
59-
spaced.rpc.clone(),
60-
spaced.chain.state.clone(),
61-
rx,
62-
self.shutdown.clone(),
63-
spaced.num_workers,
64-
cbf
65-
);
66-
67-
self.services.spawn(async move {
68-
wallet_service
69-
.await
70-
.map_err(|e| anyhow!("Wallet service error: {}", e))
71-
});
72-
}
73-
74-
async fn setup_rpc_services(&mut self, spaced: &Spaced) {
75-
let (wallet_loader_tx, wallet_loader_rx) = mpsc::channel(1);
76-
77-
let wallet_manager = WalletManager {
78-
data_dir: spaced.data_dir.join("wallets"),
79-
network: spaced.network,
80-
rpc: spaced.rpc.clone(),
81-
wallet_loader: wallet_loader_tx,
82-
wallets: Arc::new(Default::default()),
83-
};
84-
85-
let (async_chain_state, async_chain_state_handle) = create_async_store(
86-
spaced.rpc.clone(),
87-
spaced.anchors_path.clone(),
88-
spaced.chain.state.clone(),
89-
spaced.block_index.as_ref().map(|index| index.state.clone()),
90-
self.shutdown.subscribe(),
91-
)
92-
.await;
93-
94-
self.services.spawn(async {
95-
async_chain_state_handle
96-
.await
97-
.map_err(|e| anyhow!("Chain state error: {}", e))
98-
});
99-
let rpc_server = RpcServerImpl::new(async_chain_state.clone(), wallet_manager);
100-
101-
let bind = spaced.bind.clone();
102-
let shutdown = self.shutdown.clone();
103-
104-
self.services.spawn(async move {
105-
rpc_server
106-
.listen(bind, shutdown)
107-
.await
108-
.map_err(|e| anyhow!("RPC Server error: {}", e))
109-
});
110-
111-
self.setup_rpc_wallet(spaced, wallet_loader_rx, spaced.cbf).await;
112-
}
113-
114-
async fn setup_sync_service(&mut self, mut spaced: Spaced) {
115-
let (spaced_sender, spaced_receiver) = tokio::sync::oneshot::channel();
116-
117-
let shutdown = self.shutdown.clone();
118-
let rpc = spaced.rpc.clone();
119-
120-
std::thread::spawn(move || {
121-
let source = BitcoinBlockSource::new(rpc);
122-
_ = spaced_sender.send(spaced.protocol_sync(source, shutdown));
123-
});
124-
125-
self.services.spawn(async move {
126-
spaced_receiver
127-
.await?
128-
.map_err(|e| anyhow!("Protocol sync error: {}", e))
129-
});
130-
}
131-
132-
async fn run(&mut self) -> anyhow::Result<()> {
133-
let shutdown_receiver = self.shutdown.subscribe();
134-
let spaced = Args::configure(shutdown_receiver).await?;
135-
self.setup_rpc_services(&spaced).await;
136-
self.setup_sync_service(spaced).await;
137-
138-
while let Some(res) = self.services.join_next().await {
139-
res??
140-
}
141-
142-
Ok(())
143-
}
144-
}
145-
146-
async fn create_async_store(
147-
rpc: BitcoinRpc,
148-
anchors: Option<PathBuf>,
149-
chain_state: LiveSnapshot,
150-
block_index: Option<LiveSnapshot>,
151-
shutdown: broadcast::Receiver<()>,
152-
) -> (AsyncChainState, JoinHandle<()>) {
153-
let (tx, rx) = mpsc::channel(32);
154-
let async_store = AsyncChainState::new(tx);
155-
let client = reqwest::Client::new();
156-
let handle = tokio::spawn(async move {
157-
AsyncChainState::handler(
158-
&client,
159-
rpc,
160-
anchors,
161-
chain_state,
162-
block_index,
163-
rx,
164-
shutdown,
165-
)
166-
.await
167-
});
168-
(async_store, handle)
169-
}

‎client/src/config.rs

Lines changed: 2 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
use std::{
2-
collections::{HashMap, HashSet},
3-
env,
4-
ffi::OsString,
52
fmt::Display,
63
fs,
74
net::{IpAddr, SocketAddr},
85
path::PathBuf,
96
};
107

118
use clap::{
12-
error::{ContextKind, ContextValue},
139
ArgGroup, Parser, ValueEnum,
1410
};
1511
use directories::ProjectDirs;
1612
use jsonrpsee::core::Serialize;
1713
use log::error;
1814
use serde::Deserialize;
1915
use spaces_protocol::bitcoin::Network;
20-
use toml::Value;
2116

2217
use crate::{
2318
source::{BitcoinRpc, BitcoinRpcAuth},
@@ -37,9 +32,6 @@ const RPC_OPTIONS: &str = "RPC Server Options";
3732
))]
3833
#[command(args_override_self = true, author, version, about, long_about = None)]
3934
pub struct Args {
40-
/// Path to a configuration file
41-
#[arg(long, env = "SPACED_CONFIG")]
42-
config: Option<PathBuf>,
4335
#[arg(long, env = "SPACED_BLOCK_INDEX", default_value = "false")]
4436
block_index: bool,
4537
#[arg(long, env = "SPACED_DATA_DIR")]
@@ -109,24 +101,10 @@ impl ExtendedNetwork {
109101
impl Args {
110102
/// Configures spaced node by processing command line arguments
111103
/// and configuration files
112-
pub async fn configure(shutdown: tokio::sync::broadcast::Receiver<()>) -> anyhow::Result<Spaced> {
113-
let mut args = Args::merge_args_config(None);
104+
pub async fn configure(args: Vec<String>, shutdown: tokio::sync::broadcast::Receiver<()>) -> anyhow::Result<Spaced> {
105+
let mut args = Args::try_parse_from(args)?;
114106
let default_dirs = get_default_node_dirs();
115107

116-
// Update from a configuration file if it exists
117-
args = match args.config {
118-
None => {
119-
let default_path = default_dirs.config_dir().join("Spaces.toml");
120-
args.config = Some(default_path.clone());
121-
122-
match default_path.exists() {
123-
true => Args::merge_args_config(Some(default_path)),
124-
false => args,
125-
}
126-
}
127-
Some(user_specified_path) => Args::merge_args_config(Some(user_specified_path)),
128-
};
129-
130108
if args.bitcoin_rpc_url.is_none() {
131109
args.bitcoin_rpc_url = Some(default_bitcoin_rpc_url(&args.chain).to_string())
132110
}
@@ -227,40 +205,6 @@ impl Args {
227205
cbf: args.bitcoin_rpc_light
228206
})
229207
}
230-
231-
/// Merges configuration file if set and command line arguments (latter takes priority)
232-
fn merge_args_config(config_file: Option<PathBuf>) -> Self {
233-
let mut config_args_keys = None;
234-
let config_args = match load_args(config_file) {
235-
None => Args::try_parse_from(env::args_os()),
236-
Some((config_args, keys)) => {
237-
config_args_keys = Some(keys);
238-
let cmd_args = env::args_os().collect::<Vec<OsString>>();
239-
let config_args: Vec<OsString> = config_args.iter().map(|s| s.into()).collect();
240-
let all_args = cmd_args
241-
.iter()
242-
.take(1)
243-
.chain(config_args.iter())
244-
.chain(cmd_args.iter().skip(1));
245-
Args::try_parse_from(all_args)
246-
}
247-
};
248-
249-
let args = match config_args {
250-
Ok(args) => args,
251-
Err(mut err) => {
252-
if let Some(config_args_keys) = config_args_keys {
253-
err = style_config_error(err, config_args_keys);
254-
error!("{}", err.to_string().replace("argument", "option"));
255-
safe_exit(err.exit_code());
256-
} else {
257-
err.exit()
258-
}
259-
}
260-
};
261-
262-
args
263-
}
264208
}
265209

266210
fn get_default_node_dirs() -> ProjectDirs {
@@ -290,92 +234,6 @@ pub fn default_bitcoin_rpc_url(network: &ExtendedNetwork) -> &'static str {
290234
}
291235
}
292236

293-
fn toml_value_to_string(value: Value) -> Option<String> {
294-
match value {
295-
Value::String(v) => Some(v),
296-
Value::Integer(i) => Some(i.to_string()),
297-
Value::Float(f) => Some(f.to_string()),
298-
Value::Boolean(b) => Some(b.to_string()),
299-
Value::Datetime(d) => Some(d.to_string()),
300-
Value::Array(_) => None,
301-
Value::Table(_) => None,
302-
}
303-
}
304-
305-
fn load_args(path: Option<PathBuf>) -> Option<(Vec<String>, HashSet<String>)> {
306-
let path = match path {
307-
None => return None,
308-
Some(p) => p,
309-
};
310-
311-
let mut config_args = HashSet::new();
312-
if let Ok(config_str) = fs::read_to_string(path.clone()) {
313-
let config: HashMap<String, toml::Value> = toml::from_str(&config_str).expect("parse");
314-
let mut args = Vec::new();
315-
for (key, value) in config {
316-
let value = match toml_value_to_string(value) {
317-
None => continue,
318-
Some(v) => v,
319-
};
320-
let arg = format!("--{}", key.replace('_', "-"));
321-
config_args.insert(arg.clone());
322-
args.push(arg);
323-
args.push(value);
324-
}
325-
return Some((args, config_args));
326-
}
327-
328-
error!("could not read configuration at {}", path.to_str().unwrap());
329-
safe_exit(1);
330-
}
331-
332-
fn maybe_style_arg(str: &str, set: &HashSet<String>) -> String {
333-
let arg_name = str.split_once(' ');
334-
if let Some((arg_name, _)) = arg_name {
335-
if set.contains(arg_name) {
336-
return str
337-
.replace("--", "config.")
338-
.replace('-', "_")
339-
.replace('<', "= <")
340-
.replace('>', ">");
341-
}
342-
}
343-
344-
return str.to_string();
345-
}
346-
347-
fn style_config_error(err: clap::Error, config_args: HashSet<String>) -> clap::Error {
348-
let mut new_error = clap::Error::new(err.kind());
349-
for (ck, cv) in err.context() {
350-
match ck {
351-
ContextKind::Usage
352-
| ContextKind::Suggested
353-
| ContextKind::SuggestedCommand
354-
| ContextKind::SuggestedSubcommand
355-
| ContextKind::Custom => {
356-
// skip
357-
}
358-
_ => {
359-
let new_cv = match cv {
360-
ContextValue::String(str) => {
361-
ContextValue::String(maybe_style_arg(str, &config_args))
362-
}
363-
ContextValue::Strings(strings) => {
364-
let mut strs = strings.clone();
365-
strs[0] = maybe_style_arg(&strs[0], &config_args);
366-
ContextValue::Strings(strs)
367-
}
368-
_ => cv.clone(),
369-
};
370-
371-
new_error.insert(ck, new_cv);
372-
}
373-
}
374-
}
375-
376-
new_error
377-
}
378-
379237
impl Display for ExtendedNetwork {
380238
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381239
let str = match self {

‎client/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod store;
1919
pub mod spaces;
2020
pub mod wallets;
2121
mod cbf;
22+
pub mod app;
2223

2324
fn std_wait<F>(mut predicate: F, wait: Duration)
2425
where

‎client/src/store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ impl LiveSnapshot {
285285
self.insert_raw(key.into(), value);
286286
}
287287

288-
pub fn get<K: KeyHash + Into<Hash>, T: Decode>(
288+
pub fn get<K: KeyHash + Into<Hash>, T: Decode<()>>(
289289
&mut self,
290290
key: K,
291291
) -> spacedb::Result<Option<T>> {

‎protocol/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ bitcoin = { version = "0.32.2", features = ["base64", "serde"], default-features
88
log = "0.4.14"
99

1010
## optional features
11-
bincode = { version = "2.0.0-rc.3", features = [ "derive", "serde", "alloc" ], default-features = false, optional = true }
11+
bincode = { version = "2.0.1", features = [ "derive", "serde", "alloc" ], default-features = false, optional = true }
1212
serde = { version = "^1.0", features = ["derive"], default-features = false, optional = true }
1313

1414
[dev-dependencies]

‎protocol/src/constants.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ pub mod bincode_impl {
117117
}
118118
}
119119

120-
impl Decode for ChainAnchor {
121-
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
120+
impl<Context> Decode<Context> for ChainAnchor {
121+
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
122122
Ok(Self {
123123
hash: BlockHash::from_byte_array(Decode::decode(decoder)?),
124124
height: Decode::decode(decoder)?,

‎protocol/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ pub mod bincode_bytes_impl {
231231
}
232232
}
233233

234-
impl Decode for Bytes {
235-
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
234+
impl<Context> Decode<Context> for Bytes {
235+
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
236236
let raw: Vec<u8> = Decode::decode(decoder)?;
237237
Ok(Bytes::new(raw))
238238
}

‎protocol/src/slabel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub mod bincode_impl {
3636
}
3737
}
3838

39-
impl Decode for SLabel {
40-
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
39+
impl<Context> Decode<Context> for SLabel {
40+
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
4141
let reader = decoder.reader();
4242
let mut buf = [0u8; MAX_LABEL_LEN + 1];
4343

‎veritas/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ crate-type = ["cdylib", "rlib"]
88

99
[dependencies]
1010
spaces_protocol = { path = "../protocol", default-features = false, features = ["std"]}
11-
bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc"]}
12-
spacedb = { git = "https://github.com/spacesprotocol/spacedb", version = "0.0.5", default-features = false }
11+
bincode = { version = "2.0.1", default-features = false, features = ["alloc"]}
12+
spacedb = { git = "https://github.com/spacesprotocol/spacedb", version = "0.0.6", default-features = false }
1313

1414
# optional wasm feature
1515
wasm-bindgen = { version ="0.2.100", optional = true }

‎wallet/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ anyhow = "1.0.80"
1313
bech32 = "0.11.0"
1414
serde = { version = "^1.0", features = ["derive"] }
1515
serde_json = "1.0"
16-
bincode = { version = "2.0.0-rc.3", features = ["serde"] }
16+
bincode = { version = "2.0.1", features = ["serde"] }
1717
jsonrpc = "0.18.0"
1818
ctrlc = "3.4.4"
1919
hex = "0.4.3"

0 commit comments

Comments
 (0)
Please sign in to comment.