Skip to content

Commit 75c8e85

Browse files
authored
Merge pull request #35 from zcash/components-refactor
Components refactor
2 parents c833b8a + 3542cbd commit 75c8e85

File tree

17 files changed

+361
-198
lines changed

17 files changed

+361
-198
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ zip32 = "0.2"
7676
tonic = "0.12"
7777

7878
[patch.crates-io]
79+
abscissa_core = { git = "https://github.com/str4d/abscissa.git", rev = "8da8e10cad02aa4a93099cb4942b48fbdc54eb89" }
80+
abscissa_tokio = { git = "https://github.com/str4d/abscissa.git", rev = "8da8e10cad02aa4a93099cb4942b48fbdc54eb89" }
7981
transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "4a8cd251ef5cc7779d23ee02d8c60feb56520eaa" }
8082
zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "4a8cd251ef5cc7779d23ee02d8c60feb56520eaa" }
8183
zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "4a8cd251ef5cc7779d23ee02d8c60feb56520eaa" }

zallet/src/application.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl Application for ZalletApp {
6464
.build()
6565
.expect("failed to build Tokio runtime"),
6666
)));
67+
command.register_components(&mut components);
6768
self.state.components_mut().register(components)
6869
}
6970

zallet/src/commands.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
33
use std::path::PathBuf;
44

5-
use abscissa_core::{config::Override, Configurable, FrameworkError, Runnable};
5+
use abscissa_core::{config::Override, Component, Configurable, FrameworkError, Runnable};
66

77
use crate::{
8+
application::ZalletApp,
89
cli::{EntryPoint, ZalletCmd},
910
config::ZalletConfig,
1011
};
@@ -46,3 +47,12 @@ impl Configurable<ZalletConfig> for EntryPoint {
4647
}
4748
}
4849
}
50+
51+
impl EntryPoint {
52+
pub(crate) fn register_components(&self, components: &mut Vec<Box<dyn Component<ZalletApp>>>) {
53+
match &self.cmd {
54+
ZalletCmd::Start(cmd) => cmd.register_components(components),
55+
ZalletCmd::MigrateZcashdConf(_) => (),
56+
}
57+
}
58+
}

zallet/src/commands/start.rs

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,43 @@
11
//! `start` subcommand
22
3-
use abscissa_core::{config, tracing::Instrument, FrameworkError, Runnable, Shutdown};
3+
use abscissa_core::{config, Component, FrameworkError, Runnable, Shutdown};
44
use tokio::{pin, select};
55

66
use crate::{
7+
application::ZalletApp,
78
cli::StartCmd,
8-
components::{json_rpc, wallet::Wallet},
9+
components::{database::Database, json_rpc::JsonRpc, sync::WalletSync},
910
config::ZalletConfig,
10-
error::{Error, ErrorKind},
11+
error::Error,
1112
prelude::*,
1213
};
1314

1415
impl StartCmd {
15-
async fn start(&self) -> Result<(), Error> {
16-
let config = APP.config();
17-
18-
// Open the wallet.
19-
let wallet = {
20-
let path = config
21-
.wallet_db
22-
.as_ref()
23-
.ok_or_else(|| ErrorKind::Init.context("wallet_db must be set (for now)"))?;
24-
if path.is_relative() {
25-
return Err(ErrorKind::Init
26-
.context("wallet_db must be an absolute path (for now)")
27-
.into());
28-
}
29-
30-
info!("Opening wallet");
31-
Wallet::open(path, config.network(), self.lwd_server.clone()).await?
32-
};
16+
pub(crate) fn register_components(&self, components: &mut Vec<Box<dyn Component<ZalletApp>>>) {
17+
components.push(Box::new(Database::default()));
18+
components.push(Box::new(JsonRpc::default()));
19+
components.push(Box::new(WalletSync::new(self.lwd_server.clone())));
20+
}
3321

34-
// Launch RPC server.
35-
let rpc_task_handle = if !config.rpc.bind.is_empty() {
36-
if config.rpc.bind.len() > 1 {
37-
return Err(ErrorKind::Init
38-
.context("Only one RPC bind address is supported (for now)")
39-
.into());
40-
}
41-
info!("Spawning RPC server");
42-
info!("Trying to open RPC endpoint at {}...", config.rpc.bind[0]);
43-
json_rpc::server::spawn(config.rpc.clone(), wallet.clone()).await?
44-
} else {
45-
warn!("Configure `rpc.bind` to start the RPC server");
46-
// Emulate a normally-operating ongoing task to simplify subsequent logic.
47-
tokio::spawn(std::future::pending().in_current_span())
22+
async fn start(&self) -> Result<(), Error> {
23+
let (rpc_task_handle, wallet_sync_task_handle) = {
24+
let mut components = APP.state().components_mut();
25+
(
26+
components
27+
.get_downcast_mut::<JsonRpc>()
28+
.expect("JsonRpc component is registered")
29+
.rpc_task
30+
.take()
31+
.expect("TokioComponent initialized"),
32+
components
33+
.get_downcast_mut::<WalletSync>()
34+
.expect("WalletSync component is registered")
35+
.sync_task
36+
.take()
37+
.expect("TokioComponent initialized"),
38+
)
4839
};
4940

50-
// Start the wallet sync process.
51-
let wallet_sync_task_handle = wallet.spawn_sync().await?;
52-
5341
info!("Spawned Zallet tasks");
5442

5543
// ongoing tasks.

zallet/src/components.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Components of Zallet.
22
3+
pub(crate) mod database;
34
pub(crate) mod json_rpc;
4-
pub(crate) mod wallet;
5+
pub(crate) mod sync;

zallet/src/components/database.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::fmt;
2+
use std::path::PathBuf;
3+
4+
use abscissa_core::{
5+
component::Injectable, tracing::info, Component, FrameworkError, FrameworkErrorKind,
6+
};
7+
use abscissa_tokio::TokioComponent;
8+
use tokio::fs;
9+
use zcash_client_sqlite::wallet::init::{init_wallet_db, WalletMigrationError};
10+
11+
use crate::{
12+
application::ZalletApp,
13+
config::ZalletConfig,
14+
error::{Error, ErrorKind},
15+
};
16+
17+
mod connection;
18+
pub(crate) use connection::DbConnection;
19+
20+
pub(crate) type DbHandle = deadpool::managed::Object<connection::WalletManager>;
21+
22+
#[derive(Clone, Default, Injectable)]
23+
#[component(inject = "init_tokio(abscissa_tokio::TokioComponent)")]
24+
pub(crate) struct Database {
25+
path: Option<PathBuf>,
26+
db_data_pool: Option<connection::WalletPool>,
27+
}
28+
29+
impl fmt::Debug for Database {
30+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31+
f.debug_struct("Database")
32+
.field("path", &self.path)
33+
.finish_non_exhaustive()
34+
}
35+
}
36+
37+
impl Component<ZalletApp> for Database {
38+
fn after_config(&mut self, config: &ZalletConfig) -> Result<(), FrameworkError> {
39+
let path = config.wallet_db.clone().ok_or_else(|| {
40+
FrameworkErrorKind::ComponentError
41+
.context(ErrorKind::Init.context("wallet_db must be set (for now)"))
42+
})?;
43+
if path.is_relative() {
44+
return Err(FrameworkErrorKind::ComponentError
45+
.context(ErrorKind::Init.context("wallet_db must be an absolute path (for now)"))
46+
.into());
47+
}
48+
49+
self.db_data_pool = Some(
50+
connection::pool(&path, config.network())
51+
.map_err(|e| FrameworkErrorKind::ComponentError.context(e))?,
52+
);
53+
self.path = Some(path);
54+
55+
Ok(())
56+
}
57+
}
58+
59+
impl Database {
60+
/// Called automatically after `TokioComponent` is initialized
61+
pub fn init_tokio(&mut self, tokio_cmp: &TokioComponent) -> Result<(), FrameworkError> {
62+
let runtime = tokio_cmp.runtime()?;
63+
64+
// Initialize the database before we go any further.
65+
runtime
66+
.block_on(async {
67+
let path = self.path.as_ref().expect("configured");
68+
69+
let db_exists = fs::try_exists(path)
70+
.await
71+
.map_err(|e| ErrorKind::Init.context(e))?;
72+
73+
if db_exists {
74+
info!("Applying latest database migrations");
75+
} else {
76+
info!("Creating empty database");
77+
}
78+
let handle = self.handle().await?;
79+
handle.with_mut(|mut db_data| {
80+
match init_wallet_db(&mut db_data, None) {
81+
Ok(()) => Ok(()),
82+
// TODO: Support single-seed migrations once we have key storage.
83+
// https://github.com/zcash/wallet/issues/18
84+
// TODO: Support multi-seed or seed-absent migrations.
85+
// https://github.com/zcash/librustzcash/issues/1284
86+
Err(schemerz::MigratorError::Migration {
87+
error: WalletMigrationError::SeedRequired,
88+
..
89+
}) => {
90+
Err(ErrorKind::Init.context("TODO: Support seed-required migrations"))
91+
}
92+
Err(e) => Err(ErrorKind::Init.context(e)),
93+
}?;
94+
95+
Ok::<(), Error>(())
96+
})?;
97+
98+
Ok::<_, Error>(())
99+
})
100+
.map_err(|e| FrameworkErrorKind::ComponentError.context(e))?;
101+
102+
Ok(())
103+
}
104+
105+
pub(crate) async fn handle(&self) -> Result<DbHandle, Error> {
106+
self.db_data_pool
107+
.as_ref()
108+
.ok_or_else(|| {
109+
ErrorKind::Init
110+
.context("Database component must be configured before calling `handle`")
111+
})?
112+
.get()
113+
.await
114+
.map_err(|e| ErrorKind::Generic.context(e).into())
115+
}
116+
}

zallet/src/components/wallet/connection.rs renamed to zallet/src/components/database/connection.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl WalletManager {
5959
}
6060

6161
impl deadpool::managed::Manager for WalletManager {
62-
type Type = WalletConnection;
62+
type Type = DbConnection;
6363
type Error = rusqlite::Error;
6464

6565
async fn create(&self) -> Result<Self::Type, Self::Error> {
@@ -68,7 +68,7 @@ impl deadpool::managed::Manager for WalletManager {
6868
.interact(|conn| rusqlite::vtab::array::load_module(conn))
6969
.await
7070
.map_err(|_| rusqlite::Error::UnwindingPanic)??;
71-
Ok(WalletConnection {
71+
Ok(DbConnection {
7272
inner,
7373
lock: self.lock.clone(),
7474
params: self.params,
@@ -84,13 +84,13 @@ impl deadpool::managed::Manager for WalletManager {
8484
}
8585
}
8686

87-
pub(crate) struct WalletConnection {
87+
pub(crate) struct DbConnection {
8888
inner: deadpool_sync::SyncWrapper<rusqlite::Connection>,
8989
lock: Arc<RwLock<()>>,
9090
params: Network,
9191
}
9292

93-
impl WalletConnection {
93+
impl DbConnection {
9494
pub(crate) fn params(&self) -> &Network {
9595
&self.params
9696
}
@@ -124,7 +124,7 @@ impl WalletConnection {
124124
}
125125
}
126126

127-
impl WalletRead for WalletConnection {
127+
impl WalletRead for DbConnection {
128128
type Error = <WalletDb<rusqlite::Connection, Network, SystemClock> as WalletRead>::Error;
129129
type AccountId =
130130
<WalletDb<rusqlite::Connection, Network, SystemClock> as WalletRead>::AccountId;
@@ -330,7 +330,7 @@ impl WalletRead for WalletConnection {
330330
}
331331
}
332332

333-
impl InputSource for WalletConnection {
333+
impl InputSource for DbConnection {
334334
type Error = <WalletDb<rusqlite::Connection, Network, SystemClock> as InputSource>::Error;
335335
type AccountId =
336336
<WalletDb<rusqlite::Connection, Network, SystemClock> as InputSource>::AccountId;
@@ -386,7 +386,7 @@ impl InputSource for WalletConnection {
386386
}
387387
}
388388

389-
impl WalletWrite for WalletConnection {
389+
impl WalletWrite for DbConnection {
390390
type UtxoRef = <WalletDb<rusqlite::Connection, Network, SystemClock> as WalletWrite>::UtxoRef;
391391

392392
fn create_account(
@@ -500,7 +500,7 @@ impl WalletWrite for WalletConnection {
500500
}
501501
}
502502

503-
impl WalletCommitmentTrees for WalletConnection {
503+
impl WalletCommitmentTrees for DbConnection {
504504
type Error =
505505
<WalletDb<rusqlite::Connection, Network, SystemClock> as WalletCommitmentTrees>::Error;
506506
type SaplingShardStore<'a> =

0 commit comments

Comments
 (0)