Error handling in RPC

This commit is contained in:
Hanh 2022-06-10 18:41:03 +08:00
parent ef97829bf7
commit 4f8103e907
3 changed files with 93 additions and 40 deletions

View File

@ -938,6 +938,23 @@ impl DbAdapter {
Ok(()) Ok(())
} }
pub fn get_accounts(&self) -> anyhow::Result<Vec<AccountRec>> {
let mut s = self.connection.prepare("SELECT id_account, name, address FROM accounts")?;
let accounts = s.query_map([], |row| {
let id_account: u32 = row.get(0)?;
let name: String = row.get(1)?;
let address: String = row.get(2)?;
Ok(AccountRec {
id_account, name, address
})
})?;
let mut account_recs = vec![];
for row in accounts {
account_recs.push(row?);
}
Ok(account_recs)
}
pub fn get_txs(&self, account: u32) -> anyhow::Result<Vec<TxRec>> { pub fn get_txs(&self, account: u32) -> anyhow::Result<Vec<TxRec>> {
let mut s = self.connection.prepare("SELECT txid, height, timestamp, value, address, memo FROM transactions WHERE account = ?1")?; let mut s = self.connection.prepare("SELECT txid, height, timestamp, value, address, memo FROM transactions WHERE account = ?1")?;
let tx_rec = s.query_map(params![account], |row| { let tx_rec = s.query_map(params![account], |row| {
@ -1004,6 +1021,13 @@ pub struct TxRec {
memo: String, memo: String,
} }
#[derive(Serialize)]
pub struct AccountRec {
id_account: u32,
name: String,
address: String,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH}; use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};

View File

@ -68,7 +68,7 @@ pub use crate::coinconfig::{
init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig, init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig,
}; };
pub use crate::commitment::{CTree, Witness}; pub use crate::commitment::{CTree, Witness};
pub use crate::db::{DbAdapter, TxRec}; pub use crate::db::{DbAdapter, AccountRec, TxRec};
pub use crate::hash::pedersen_hash; pub use crate::hash::pedersen_hash;
pub use crate::key::{generate_random_enc_key, KeyHelpers}; pub use crate::key::{generate_random_enc_key, KeyHelpers};
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient; pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;

View File

@ -1,18 +1,37 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use anyhow::anyhow;
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::response::Responder;
use rocket::serde::{json::Json, Deserialize, Serialize}; use rocket::serde::{json::Json, Deserialize, Serialize};
use rocket::State; use rocket::{Request, response, Response, State};
use rocket::http::Status;
use warp_api_ffi::api::payment::{Recipient, RecipientMemo}; use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
use warp_api_ffi::{CoinConfig, TxRec}; use warp_api_ffi::{AccountRec, CoinConfig, TxRec};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl<'r> Responder<'r, 'static> for Error {
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
let error = self.to_string();
Response::build_from(error.respond_to(req)?)
.status(Status::InternalServerError)
.ok()
}
}
#[rocket::main] #[rocket::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
dotenv::dotenv()?; let _ = dotenv::dotenv();
warp_api_ffi::init_coin( warp_api_ffi::init_coin(
0, 0,
&dotenv::var("ZEC_DB_PATH").unwrap_or("/tmp/zec.db".to_string()), &dotenv::var("ZEC_DB_PATH").unwrap_or("./zec.db".to_string()),
)?; )?;
warp_api_ffi::set_coin_lwd_url( warp_api_ffi::set_coin_lwd_url(
0, 0,
@ -26,6 +45,7 @@ async fn main() -> anyhow::Result<()> {
set_lwd, set_lwd,
set_active, set_active,
new_account, new_account,
list_accounts,
sync, sync,
rewind, rewind,
get_latest_height, get_latest_height,
@ -54,81 +74,91 @@ pub fn set_active(coin: u8, id_account: u32) {
} }
#[post("/new_account", format = "application/json", data = "<seed>")] #[post("/new_account", format = "application/json", data = "<seed>")]
pub fn new_account(seed: Json<AccountSeed>) -> String { pub fn new_account(seed: Json<AccountSeed>) -> Result<String, Error> {
let id_account = warp_api_ffi::api::account::new_account( let id_account = warp_api_ffi::api::account::new_account(
seed.coin, seed.coin,
&seed.name, &seed.name,
seed.key.clone(), seed.key.clone(),
seed.index, seed.index,
) )?;
.unwrap();
warp_api_ffi::set_active_account(seed.coin, id_account); warp_api_ffi::set_active_account(seed.coin, id_account);
id_account.to_string() Ok(id_account.to_string())
}
#[get("/accounts")]
pub fn list_accounts() -> Result<Json<Vec<AccountRec>>, Error> {
let c = CoinConfig::get_active();
let db = c.db()?;
let accounts = db.get_accounts()?;
Ok(Json(accounts))
} }
#[post("/sync?<offset>")] #[post("/sync?<offset>")]
pub async fn sync(offset: Option<u32>) { pub async fn sync(offset: Option<u32>) -> Result<(), Error> {
let coin = CoinConfig::get_active(); let c = CoinConfig::get_active();
let _ = warp_api_ffi::api::sync::coin_sync(coin.coin, true, offset.unwrap_or(0), |_| {}).await; warp_api_ffi::api::sync::coin_sync(c.coin, true, offset.unwrap_or(0), |_| {}).await?;
Ok(())
} }
#[post("/rewind?<height>")] #[post("/rewind?<height>")]
pub async fn rewind(height: u32) { pub async fn rewind(height: u32) -> Result<(), Error> {
let _ = warp_api_ffi::api::sync::rewind_to_height(height).await; warp_api_ffi::api::sync::rewind_to_height(height).await?;
Ok(())
} }
#[get("/latest_height")] #[get("/latest_height")]
pub async fn get_latest_height() -> Json<Heights> { pub async fn get_latest_height() -> Result<Json<Heights>, Error> {
let latest = warp_api_ffi::api::sync::get_latest_height().await.unwrap(); let latest = warp_api_ffi::api::sync::get_latest_height().await?;
let synced = warp_api_ffi::api::sync::get_synced_height().unwrap(); let synced = warp_api_ffi::api::sync::get_synced_height()?;
Json(Heights { latest, synced }) Ok(Json(Heights { latest, synced }))
} }
#[get("/address")] #[get("/address")]
pub fn get_address() -> String { pub fn get_address() -> Result<String, Error> {
let c = CoinConfig::get_active(); let c = CoinConfig::get_active();
let db = c.db().unwrap(); let db = c.db()?;
db.get_address(c.id_account).unwrap() let address = db.get_address(c.id_account)?;
Ok(address)
} }
#[get("/backup")] #[get("/backup")]
pub fn get_backup(config: &State<Config>) -> Result<Json<Backup>, String> { pub fn get_backup(config: &State<Config>) -> Result<Json<Backup>, Error> {
if !config.allow_backup { if !config.allow_backup {
Err("Backup API not enabled".to_string()) Err(anyhow!("Backup API not enabled").into())
} else { } else {
let c = CoinConfig::get_active(); let c = CoinConfig::get_active();
let db = c.db().unwrap(); let db = c.db()?;
let (seed, sk, fvk) = db.get_backup(c.id_account).unwrap(); let (seed, sk, fvk) = db.get_backup(c.id_account)?;
Ok(Json(Backup { seed, sk, fvk })) Ok(Json(Backup { seed, sk, fvk }))
} }
} }
#[get("/tx_history")] #[get("/tx_history")]
pub fn get_tx_history() -> Json<Vec<TxRec>> { pub fn get_tx_history() -> Result<Json<Vec<TxRec>>, Error> {
let c = CoinConfig::get_active(); let c = CoinConfig::get_active();
let db = c.db().unwrap(); let db = c.db()?;
let txs = db.get_txs(c.id_account).unwrap(); let txs = db.get_txs(c.id_account)?;
Json(txs) Ok(Json(txs))
} }
#[get("/balance")] #[get("/balance")]
pub fn get_balance() -> String { pub fn get_balance() -> Result<String, Error> {
let c = CoinConfig::get_active(); let c = CoinConfig::get_active();
let db = c.db().unwrap(); let db = c.db()?;
let balance = db.get_balance(c.id_account).unwrap(); let balance = db.get_balance(c.id_account)?;
balance.to_string() Ok(balance.to_string())
} }
#[post("/pay", data = "<payment>")] #[post("/pay", data = "<payment>")]
pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<String, String> { pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<String, Error> {
if !config.allow_send { if !config.allow_send {
Err("Backup API not enabled".to_string()) Err(anyhow!("Payment API not enabled").into())
} else { } else {
let c = CoinConfig::get_active(); let c = CoinConfig::get_active();
let latest = warp_api_ffi::api::sync::get_latest_height().await.unwrap(); let latest = warp_api_ffi::api::sync::get_latest_height().await?;
let from = { let from = {
let db = c.db().unwrap(); let db = c.db()?;
db.get_address(c.id_account).unwrap() db.get_address(c.id_account)?
}; };
let recipients: Vec<_> = payment let recipients: Vec<_> = payment
.recipients .recipients
@ -142,8 +172,7 @@ pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<Strin
payment.confirmations, payment.confirmations,
Box::new(|_| {}), Box::new(|_| {}),
) )
.await .await?;
.unwrap();
Ok(txid) Ok(txid)
} }
} }