RPC
This commit is contained in:
parent
e8a930c1ef
commit
0a458421e6
|
@ -28,7 +28,6 @@ name = "warp_api_ffi"
|
|||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
dotenv = "0.15.0"
|
||||
env_logger = "0.9.0"
|
||||
anyhow = "1.0.40"
|
||||
thiserror = "1.0.25"
|
||||
|
@ -79,12 +78,13 @@ allo-isolate = { version = "0.1", optional = true }
|
|||
once_cell = { version = "1.8.0", optional = true }
|
||||
android_logger = { version = "0.10.0", optional = true }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"], optional = true }
|
||||
dotenv = { version = "0.15.0", optional = true }
|
||||
|
||||
[features]
|
||||
ledger = ["ledger-apdu", "hmac", "ed25519-bip32", "ledger-transport-hid"]
|
||||
ledger_sapling = ["ledger"]
|
||||
dart_ffi = ["allo-isolate", "once_cell", "android_logger"]
|
||||
rpc = ["rocket"]
|
||||
rpc = ["rocket", "dotenv"]
|
||||
|
||||
# librustzcash synced to 35023ed8ca2fb1061e78fd740b640d4eefcc5edd
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[default]
|
||||
allow_backup = true
|
||||
allow_send = true
|
|
@ -1,6 +1,6 @@
|
|||
// Account creation
|
||||
|
||||
use crate::coinconfig::{CoinConfig, ACTIVE_COIN};
|
||||
use crate::coinconfig::CoinConfig;
|
||||
use crate::key2::decode_key;
|
||||
use anyhow::anyhow;
|
||||
use bip39::{Language, Mnemonic};
|
||||
|
|
|
@ -34,10 +34,10 @@ async fn coin_sync_impl(
|
|||
c.coin_type,
|
||||
chunk_size,
|
||||
get_tx,
|
||||
&c.db_path,
|
||||
&c.db_path.as_ref().unwrap(),
|
||||
target_height_offset,
|
||||
progress_callback,
|
||||
&c.lwd_url,
|
||||
&c.lwd_url.as_ref().unwrap(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
@ -50,6 +50,12 @@ pub async fn get_latest_height() -> anyhow::Result<u32> {
|
|||
Ok(last_height)
|
||||
}
|
||||
|
||||
pub fn get_synced_height() -> anyhow::Result<u32> {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db()?;
|
||||
db.get_last_sync_height().map(|h| h.unwrap_or(0))
|
||||
}
|
||||
|
||||
pub async fn skip_to_last_height(coin: u8) -> anyhow::Result<()> {
|
||||
let c = if coin == 0xFF {
|
||||
CoinConfig::get_active()
|
||||
|
|
|
@ -3,6 +3,7 @@ use lazy_static::lazy_static;
|
|||
use lazycell::AtomicLazyCell;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use anyhow::anyhow;
|
||||
use tonic::transport::Channel;
|
||||
use zcash_params::coin::{get_coin_chain, CoinChain, CoinType};
|
||||
use zcash_params::{OUTPUT_PARAMS, SPEND_PARAMS};
|
||||
|
@ -34,7 +35,7 @@ pub fn set_active_account(coin: u8, id: u32) {
|
|||
|
||||
pub fn set_coin_lwd_url(coin: u8, lwd_url: &str) {
|
||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||
c.lwd_url = lwd_url.to_string();
|
||||
c.lwd_url = Some(lwd_url.to_string());
|
||||
}
|
||||
|
||||
pub fn init_coin(coin: u8, db_path: &str) -> anyhow::Result<()> {
|
||||
|
@ -49,8 +50,8 @@ pub struct CoinConfig {
|
|||
pub coin_type: CoinType,
|
||||
pub id_account: u32,
|
||||
pub height: u32,
|
||||
pub lwd_url: String,
|
||||
pub db_path: String,
|
||||
pub lwd_url: Option<String>,
|
||||
pub db_path: Option<String>,
|
||||
pub mempool: Arc<Mutex<MemPool>>,
|
||||
pub db: Option<Arc<Mutex<DbAdapter>>>,
|
||||
pub chain: &'static (dyn CoinChain + Send),
|
||||
|
@ -64,8 +65,8 @@ impl CoinConfig {
|
|||
coin_type,
|
||||
id_account: 0,
|
||||
height: 0,
|
||||
lwd_url: String::new(),
|
||||
db_path: String::new(),
|
||||
lwd_url: None,
|
||||
db_path: None,
|
||||
db: None,
|
||||
mempool: Arc::new(Mutex::new(MemPool::new(coin))),
|
||||
chain,
|
||||
|
@ -73,8 +74,8 @@ impl CoinConfig {
|
|||
}
|
||||
|
||||
pub fn set_db_path(&mut self, db_path: &str) -> anyhow::Result<()> {
|
||||
self.db_path = db_path.to_string();
|
||||
let db = DbAdapter::new(self.coin_type, &self.db_path)?;
|
||||
self.db_path = Some(db_path.to_string());
|
||||
let db = DbAdapter::new(self.coin_type, &db_path)?;
|
||||
db.init_db()?;
|
||||
self.db = Some(Arc::new(Mutex::new(db)));
|
||||
Ok(())
|
||||
|
@ -108,7 +109,12 @@ impl CoinConfig {
|
|||
}
|
||||
|
||||
pub async fn connect_lwd(&self) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
|
||||
connect_lightwalletd(&self.lwd_url).await
|
||||
if let Some(lwd_url) = &self.lwd_url {
|
||||
connect_lightwalletd(lwd_url).await
|
||||
}
|
||||
else {
|
||||
Err(anyhow!("LWD URL Not set"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
36
src/db.rs
36
src/db.rs
|
@ -590,8 +590,8 @@ impl DbAdapter {
|
|||
params![account],
|
||||
|row| {
|
||||
let seed: Option<String> = row.get(0)?;
|
||||
let sk: Option<String> = row.get(0)?;
|
||||
let ivk: String = row.get(0)?;
|
||||
let sk: Option<String> = row.get(1)?;
|
||||
let ivk: String = row.get(2)?;
|
||||
Ok((seed, sk, ivk))
|
||||
},
|
||||
)?;
|
||||
|
@ -938,6 +938,28 @@ impl DbAdapter {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
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 tx_rec = s.query_map(params![account], |row| {
|
||||
let mut txid: Vec<u8> = row.get(0)?;
|
||||
txid.reverse();
|
||||
let txid = hex::encode(txid);
|
||||
let height: u32 = row.get(1)?;
|
||||
let timestamp: u32 = row.get(2)?;
|
||||
let value: i64 = row.get(3)?;
|
||||
let address: String = row.get(4)?;
|
||||
let memo: String = row.get(5)?;
|
||||
Ok(TxRec {
|
||||
txid, height, timestamp, value, address, memo
|
||||
})
|
||||
})?;
|
||||
let mut txs = vec![];
|
||||
for row in tx_rec {
|
||||
txs.push(row?);
|
||||
}
|
||||
Ok(txs)
|
||||
}
|
||||
|
||||
fn network(&self) -> &'static Network {
|
||||
let chain = get_coin_chain(self.coin_type);
|
||||
chain.network()
|
||||
|
@ -967,6 +989,16 @@ impl ZMessage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct TxRec {
|
||||
txid: String,
|
||||
height: u32,
|
||||
timestamp: u32,
|
||||
value: i64,
|
||||
address: String,
|
||||
memo: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
|
||||
|
|
|
@ -66,7 +66,7 @@ pub use crate::chain::{
|
|||
ChainError, DecryptNode,
|
||||
};
|
||||
pub use crate::commitment::{CTree, Witness};
|
||||
pub use crate::db::DbAdapter;
|
||||
pub use crate::db::{DbAdapter, TxRec};
|
||||
pub use crate::hash::pedersen_hash;
|
||||
pub use crate::key::{generate_random_enc_key, KeyHelpers};
|
||||
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||
|
|
164
src/main/rpc.rs
164
src/main/rpc.rs
|
@ -1,34 +1,148 @@
|
|||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::serde::{Deserialize, json::Json};
|
||||
use warp_api_ffi::CoinConfig;
|
||||
use rocket::fairing::AdHoc;
|
||||
use rocket::serde::{Serialize, Deserialize, json::Json};
|
||||
use rocket::State;
|
||||
use warp_api_ffi::{CoinConfig, TxRec};
|
||||
use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
|
||||
|
||||
#[rocket::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
warp_api_ffi::init_coin(0, "/tmp/zec.db")?;
|
||||
dotenv::dotenv()?;
|
||||
warp_api_ffi::init_coin(0, &dotenv::var("ZEC_DB_PATH").unwrap_or("/tmp/zec.db".to_string()))?;
|
||||
warp_api_ffi::set_coin_lwd_url(0, &dotenv::var("ZEC_LWD_URL").unwrap_or("https://mainnet.lightwalletd.com:9067".to_string()));
|
||||
|
||||
let _ = rocket::build()
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
set_lwd,
|
||||
set_active,
|
||||
new_account,
|
||||
sync,
|
||||
// get_address,
|
||||
// sync,
|
||||
// rewind,
|
||||
// balance,
|
||||
// pay,
|
||||
// tx_history
|
||||
rewind,
|
||||
get_latest_height,
|
||||
get_backup,
|
||||
get_balance,
|
||||
get_address,
|
||||
get_tx_history,
|
||||
pay,
|
||||
],
|
||||
)
|
||||
.attach(AdHoc::config::<Config>())
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/set_lwd?<coin>&<lwd_url>")]
|
||||
pub fn set_lwd(coin: u8, lwd_url: String) {
|
||||
warp_api_ffi::set_coin_lwd_url(coin, &lwd_url);
|
||||
}
|
||||
|
||||
#[post("/set_active?<coin>&<id_account>")]
|
||||
pub fn set_active(coin: u8, id_account: u32) {
|
||||
warp_api_ffi::set_active_account(coin, id_account);
|
||||
}
|
||||
|
||||
#[post("/new_account", format = "application/json", data="<seed>")]
|
||||
pub fn new_account(seed: Json<AccountSeed>) -> String {
|
||||
let id_account = warp_api_ffi::api::account::new_account(seed.coin, &seed.name, seed.key.clone(), seed.index).unwrap();
|
||||
warp_api_ffi::set_active_account(seed.coin, id_account);
|
||||
id_account.to_string()
|
||||
}
|
||||
|
||||
#[post("/sync?<offset>")]
|
||||
pub async fn sync(offset: Option<u32>) {
|
||||
let coin = CoinConfig::get_active();
|
||||
let _ = warp_api_ffi::api::sync::coin_sync(coin.coin, true, offset.unwrap_or(0), |_| {}).await;
|
||||
}
|
||||
|
||||
#[post("/rewind?<height>")]
|
||||
pub async fn rewind(height: u32) {
|
||||
let _ = warp_api_ffi::api::sync::rewind_to_height(height).await;
|
||||
}
|
||||
|
||||
#[get("/latest_height")]
|
||||
pub async fn get_latest_height() -> Json<Heights> {
|
||||
let latest = warp_api_ffi::api::sync::get_latest_height().await.unwrap();
|
||||
let synced = warp_api_ffi::api::sync::get_synced_height().unwrap();
|
||||
Json(Heights { latest, synced })
|
||||
}
|
||||
|
||||
#[get("/address")]
|
||||
pub fn get_address() -> String {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db().unwrap();
|
||||
db.get_address(c.id_account).unwrap()
|
||||
}
|
||||
|
||||
#[get("/backup")]
|
||||
pub fn get_backup(config: &State<Config>) -> Result<Json<Backup>, String> {
|
||||
if !config.allow_backup {
|
||||
Err("Backup API not enabled".to_string())
|
||||
}
|
||||
else {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db().unwrap();
|
||||
let (seed, sk, fvk) = db.get_backup(c.id_account).unwrap();
|
||||
Ok(Json(Backup {
|
||||
seed,
|
||||
sk,
|
||||
fvk
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/tx_history")]
|
||||
pub fn get_tx_history() -> Json<Vec<TxRec>> {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db().unwrap();
|
||||
let txs = db.get_txs(c.id_account).unwrap();
|
||||
Json(txs)
|
||||
}
|
||||
|
||||
#[get("/balance")]
|
||||
pub fn get_balance() -> String {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db().unwrap();
|
||||
let balance = db.get_balance(c.id_account).unwrap();
|
||||
balance.to_string()
|
||||
}
|
||||
|
||||
#[post("/pay", data="<payment>")]
|
||||
pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<String, String> {
|
||||
if !config.allow_send {
|
||||
Err("Backup API not enabled".to_string())
|
||||
}
|
||||
else {
|
||||
let c = CoinConfig::get_active();
|
||||
let latest = warp_api_ffi::api::sync::get_latest_height().await.unwrap();
|
||||
let from = {
|
||||
let db = c.db().unwrap();
|
||||
db.get_address(c.id_account).unwrap()
|
||||
};
|
||||
let recipients: Vec<_> = payment.recipients.iter().map(|p| RecipientMemo::from_recipient(&from, p)).collect();
|
||||
let txid = warp_api_ffi::api::payment::build_sign_send_multi_payment(
|
||||
latest,
|
||||
&recipients,
|
||||
false,
|
||||
payment.confirmations,
|
||||
Box::new(|_| {})
|
||||
).await.unwrap();
|
||||
Ok(txid)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Config {
|
||||
allow_backup: bool,
|
||||
allow_send: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct AccountSeed {
|
||||
|
@ -38,24 +152,24 @@ pub struct AccountSeed {
|
|||
index: Option<u32>,
|
||||
}
|
||||
|
||||
#[post("/set_lwd?<coin>&<lwd_url>")]
|
||||
pub fn set_lwd(coin: u8, lwd_url: String) {
|
||||
warp_api_ffi::set_coin_lwd_url(coin, &lwd_url);
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Heights {
|
||||
latest: u32,
|
||||
synced: u32,
|
||||
}
|
||||
|
||||
#[post("/new_account", format = "application/json", data="<seed>")]
|
||||
pub fn new_account(seed: Json<AccountSeed>) -> std::result::Result<String, String> {
|
||||
let id_account = warp_api_ffi::api::account::new_account(seed.coin, &seed.name, seed.key.clone(), seed.index);
|
||||
id_account.map(|v| v.to_string()).map_err(|e| e.to_string())
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Backup {
|
||||
seed: Option<String>,
|
||||
sk: Option<String>,
|
||||
fvk: String,
|
||||
}
|
||||
|
||||
#[post("/sync?<offset>")]
|
||||
pub async fn sync(offset: Option<u32>) {
|
||||
let coin = CoinConfig::get_active();
|
||||
let _ = warp_api_ffi::api::sync::coin_sync(coin.coin, true, offset.unwrap_or(0), |_| {}).await;
|
||||
}
|
||||
|
||||
|
||||
pub fn get_backup(id_account: u32) {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Payment {
|
||||
recipients: Vec<Recipient>,
|
||||
confirmations: u32,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use tonic::transport::Channel;
|
|||
use tonic::Request;
|
||||
|
||||
use crate::coinconfig::CoinConfig;
|
||||
use zcash_params::coin::CoinChain;
|
||||
use zcash_primitives::consensus::BlockHeight;
|
||||
use zcash_primitives::sapling::note_encryption::try_sapling_compact_note_decryption;
|
||||
use zcash_primitives::sapling::SaplingIvk;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::coinconfig::CoinConfig;
|
||||
use crate::{
|
||||
AddressList, CompactTxStreamerClient, DbAdapter, GetAddressUtxosArg, GetAddressUtxosReply,
|
||||
AddressList, CompactTxStreamerClient, GetAddressUtxosArg, GetAddressUtxosReply,
|
||||
};
|
||||
use bip39::{Language, Mnemonic, Seed};
|
||||
use ripemd::{Digest, Ripemd160};
|
||||
|
|
Loading…
Reference in New Issue