This commit is contained in:
Hanh 2022-06-10 17:16:00 +08:00
parent e8a930c1ef
commit 0a458421e6
10 changed files with 203 additions and 43 deletions

View File

@ -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

3
Rocket.toml Normal file
View File

@ -0,0 +1,3 @@
[default]
allow_backup = true
allow_send = true

View File

@ -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};

View File

@ -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()

View File

@ -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"))
}
}
}

View File

@ -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};

View File

@ -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;

View File

@ -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,
}

View File

@ -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;

View File

@ -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};