2022-06-09 10:05:58 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate rocket;
|
|
|
|
|
2022-06-10 03:41:03 -07:00
|
|
|
use anyhow::anyhow;
|
2022-07-21 18:08:29 -07:00
|
|
|
use lazy_static::lazy_static;
|
2022-06-10 02:16:00 -07:00
|
|
|
use rocket::fairing::AdHoc;
|
2022-06-10 11:41:05 -07:00
|
|
|
use rocket::http::Status;
|
2022-06-10 03:41:03 -07:00
|
|
|
use rocket::response::Responder;
|
2022-06-10 03:09:37 -07:00
|
|
|
use rocket::serde::{json::Json, Deserialize, Serialize};
|
2022-06-10 11:41:05 -07:00
|
|
|
use rocket::{response, Request, Response, State};
|
|
|
|
use std::collections::HashMap;
|
2022-08-26 05:54:52 -07:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{BufReader, Read};
|
2022-08-31 08:35:21 -07:00
|
|
|
use std::sync::Mutex;
|
2022-06-10 11:41:05 -07:00
|
|
|
use thiserror::Error;
|
2022-06-10 02:16:00 -07:00
|
|
|
use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
|
2022-06-10 11:41:05 -07:00
|
|
|
use warp_api_ffi::api::payment_uri::PaymentURI;
|
2022-07-26 19:11:36 -07:00
|
|
|
use warp_api_ffi::{
|
2022-08-26 05:54:52 -07:00
|
|
|
derive_zip32, get_best_server, AccountInfo, AccountRec, CoinConfig, KeyPack, RaptorQDrops, Tx,
|
|
|
|
TxRec,
|
2022-07-26 19:11:36 -07:00
|
|
|
};
|
2022-06-10 03:41:03 -07:00
|
|
|
|
2022-07-21 18:08:29 -07:00
|
|
|
lazy_static! {
|
2022-08-31 08:35:21 -07:00
|
|
|
static ref SYNC_CANCELED: Mutex<bool> = Mutex::new(false);
|
2022-07-21 18:08:29 -07:00
|
|
|
}
|
|
|
|
|
2022-08-26 05:54:52 -07:00
|
|
|
#[derive(Error, Debug)]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub enum Error {
|
2022-08-26 05:54:52 -07:00
|
|
|
#[error(transparent)]
|
|
|
|
Reqwest(#[from] reqwest::Error),
|
2022-06-10 03:41:03 -07:00
|
|
|
#[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()
|
|
|
|
}
|
|
|
|
}
|
2022-06-09 10:05:58 -07:00
|
|
|
|
2022-06-10 04:14:21 -07:00
|
|
|
fn init(coin: u8, config: HashMap<String, String>) -> anyhow::Result<()> {
|
2022-06-10 03:09:37 -07:00
|
|
|
warp_api_ffi::init_coin(
|
2022-06-10 04:14:21 -07:00
|
|
|
coin,
|
2022-06-10 11:41:05 -07:00
|
|
|
config
|
|
|
|
.get("db_path")
|
|
|
|
.ok_or(anyhow!("Missing configuration value"))?,
|
2022-06-10 03:09:37 -07:00
|
|
|
)?;
|
|
|
|
warp_api_ffi::set_coin_lwd_url(
|
2022-06-10 04:14:21 -07:00
|
|
|
coin,
|
2022-06-10 11:41:05 -07:00
|
|
|
config
|
|
|
|
.get("lwd_url")
|
|
|
|
.ok_or(anyhow!("Missing configuration value"))?,
|
2022-06-10 03:09:37 -07:00
|
|
|
);
|
2022-06-10 04:14:21 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rocket::main]
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
2022-06-10 06:01:07 -07:00
|
|
|
env_logger::init();
|
2022-06-10 04:14:21 -07:00
|
|
|
let _ = dotenv::dotenv();
|
|
|
|
|
|
|
|
let rocket = rocket::build();
|
|
|
|
let figment = rocket.figment();
|
|
|
|
let zec: HashMap<String, String> = figment.extract_inner("zec")?;
|
|
|
|
init(0, zec)?;
|
|
|
|
let yec: HashMap<String, String> = figment.extract_inner("yec")?;
|
|
|
|
init(1, yec)?;
|
2022-06-09 10:05:58 -07:00
|
|
|
|
2022-06-10 11:41:05 -07:00
|
|
|
let _ = rocket
|
|
|
|
.mount(
|
2022-06-09 10:05:58 -07:00
|
|
|
"/",
|
|
|
|
routes![
|
2022-06-10 02:16:00 -07:00
|
|
|
set_active,
|
2022-06-09 10:05:58 -07:00
|
|
|
new_account,
|
2022-06-10 03:41:03 -07:00
|
|
|
list_accounts,
|
2022-06-09 10:05:58 -07:00
|
|
|
sync,
|
2022-06-10 02:16:00 -07:00
|
|
|
rewind,
|
|
|
|
get_latest_height,
|
|
|
|
get_backup,
|
|
|
|
get_balance,
|
|
|
|
get_address,
|
|
|
|
get_tx_history,
|
|
|
|
pay,
|
2022-06-10 11:41:05 -07:00
|
|
|
mark_synced,
|
2022-06-10 22:40:32 -07:00
|
|
|
create_offline_tx,
|
|
|
|
sign_offline_tx,
|
2022-06-10 11:41:05 -07:00
|
|
|
broadcast_tx,
|
|
|
|
new_diversified_address,
|
|
|
|
make_payment_uri,
|
|
|
|
parse_payment_uri,
|
2022-06-20 02:05:11 -07:00
|
|
|
split_data,
|
|
|
|
merge_data,
|
2022-07-26 19:11:36 -07:00
|
|
|
derive_keys,
|
2022-08-26 05:54:52 -07:00
|
|
|
instant_sync,
|
2022-06-09 10:05:58 -07:00
|
|
|
],
|
|
|
|
)
|
2022-06-10 02:16:00 -07:00
|
|
|
.attach(AdHoc::config::<Config>())
|
2022-06-09 10:05:58 -07:00
|
|
|
.launch()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[post("/set_active?<coin>&<id_account>")]
|
|
|
|
pub fn set_active(coin: u8, id_account: u32) {
|
|
|
|
warp_api_ffi::set_active_account(coin, id_account);
|
2022-06-12 22:54:37 -07:00
|
|
|
warp_api_ffi::set_active(coin);
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
2022-06-10 03:09:37 -07:00
|
|
|
#[post("/new_account", format = "application/json", data = "<seed>")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub fn new_account(seed: Json<AccountSeed>) -> Result<String, Error> {
|
2022-06-10 03:09:37 -07:00
|
|
|
let id_account = warp_api_ffi::api::account::new_account(
|
|
|
|
seed.coin,
|
|
|
|
&seed.name,
|
|
|
|
seed.key.clone(),
|
|
|
|
seed.index,
|
2022-06-10 03:41:03 -07:00
|
|
|
)?;
|
2022-06-10 02:16:00 -07:00
|
|
|
warp_api_ffi::set_active_account(seed.coin, id_account);
|
2022-06-10 03:41:03 -07:00
|
|
|
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))
|
2022-06-09 10:05:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/sync?<offset>")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub async fn sync(offset: Option<u32>) -> Result<(), Error> {
|
|
|
|
let c = CoinConfig::get_active();
|
2022-08-03 08:30:34 -07:00
|
|
|
warp_api_ffi::api::sync::coin_sync(
|
|
|
|
c.coin,
|
|
|
|
true,
|
|
|
|
offset.unwrap_or(0),
|
|
|
|
50,
|
|
|
|
|_| {},
|
|
|
|
&SYNC_CANCELED,
|
|
|
|
)
|
|
|
|
.await?;
|
2022-06-10 03:41:03 -07:00
|
|
|
Ok(())
|
2022-06-09 10:05:58 -07:00
|
|
|
}
|
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[post("/rewind?<height>")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub async fn rewind(height: u32) -> Result<(), Error> {
|
|
|
|
warp_api_ffi::api::sync::rewind_to_height(height).await?;
|
|
|
|
Ok(())
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
2022-06-10 11:41:05 -07:00
|
|
|
#[post("/mark_synced")]
|
|
|
|
pub async fn mark_synced() -> Result<(), Error> {
|
|
|
|
let c = CoinConfig::get_active();
|
|
|
|
warp_api_ffi::api::sync::skip_to_last_height(c.coin).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[get("/latest_height")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub async fn get_latest_height() -> Result<Json<Heights>, Error> {
|
|
|
|
let latest = warp_api_ffi::api::sync::get_latest_height().await?;
|
|
|
|
let synced = warp_api_ffi::api::sync::get_synced_height()?;
|
|
|
|
Ok(Json(Heights { latest, synced }))
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/address")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub fn get_address() -> Result<String, Error> {
|
2022-06-10 02:16:00 -07:00
|
|
|
let c = CoinConfig::get_active();
|
2022-06-10 03:41:03 -07:00
|
|
|
let db = c.db()?;
|
|
|
|
let address = db.get_address(c.id_account)?;
|
|
|
|
Ok(address)
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/backup")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub fn get_backup(config: &State<Config>) -> Result<Json<Backup>, Error> {
|
2022-06-10 02:16:00 -07:00
|
|
|
if !config.allow_backup {
|
2022-06-10 03:41:03 -07:00
|
|
|
Err(anyhow!("Backup API not enabled").into())
|
2022-06-10 03:09:37 -07:00
|
|
|
} else {
|
2022-06-10 02:16:00 -07:00
|
|
|
let c = CoinConfig::get_active();
|
2022-06-10 03:41:03 -07:00
|
|
|
let db = c.db()?;
|
|
|
|
let (seed, sk, fvk) = db.get_backup(c.id_account)?;
|
2022-06-10 03:09:37 -07:00
|
|
|
Ok(Json(Backup { seed, sk, fvk }))
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/tx_history")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub fn get_tx_history() -> Result<Json<Vec<TxRec>>, Error> {
|
2022-06-10 02:16:00 -07:00
|
|
|
let c = CoinConfig::get_active();
|
2022-06-10 03:41:03 -07:00
|
|
|
let db = c.db()?;
|
|
|
|
let txs = db.get_txs(c.id_account)?;
|
|
|
|
Ok(Json(txs))
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/balance")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub fn get_balance() -> Result<String, Error> {
|
2022-06-10 02:16:00 -07:00
|
|
|
let c = CoinConfig::get_active();
|
2022-06-10 03:41:03 -07:00
|
|
|
let db = c.db()?;
|
|
|
|
let balance = db.get_balance(c.id_account)?;
|
|
|
|
Ok(balance.to_string())
|
2022-06-10 02:16:00 -07:00
|
|
|
}
|
|
|
|
|
2022-06-10 22:40:32 -07:00
|
|
|
#[post("/create_offline_tx", data = "<payment>")]
|
|
|
|
pub async fn create_offline_tx(payment: Json<Payment>) -> Result<Json<Tx>, Error> {
|
|
|
|
let c = CoinConfig::get_active();
|
|
|
|
let latest = warp_api_ffi::api::sync::get_latest_height().await?;
|
|
|
|
let from = {
|
|
|
|
let db = c.db()?;
|
|
|
|
db.get_address(c.id_account)?
|
|
|
|
};
|
|
|
|
let recipients: Vec<_> = payment
|
|
|
|
.recipients
|
|
|
|
.iter()
|
|
|
|
.map(|p| RecipientMemo::from_recipient(&from, p))
|
|
|
|
.collect();
|
2022-06-16 03:13:58 -07:00
|
|
|
let tx = warp_api_ffi::api::payment::build_only_multi_payment(
|
|
|
|
latest,
|
|
|
|
&recipients,
|
|
|
|
false,
|
|
|
|
payment.confirmations,
|
|
|
|
)
|
|
|
|
.await?;
|
2022-06-10 22:40:32 -07:00
|
|
|
Ok(Json(tx))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/sign_offline_tx", data = "<tx>")]
|
|
|
|
pub async fn sign_offline_tx(tx: Json<Tx>, config: &State<Config>) -> Result<String, Error> {
|
|
|
|
if !config.allow_send {
|
|
|
|
Err(anyhow!("Payment API not enabled").into())
|
|
|
|
} else {
|
2022-06-16 03:13:58 -07:00
|
|
|
let tx_hex =
|
|
|
|
warp_api_ffi::api::payment::sign_only_multi_payment(&tx, Box::new(|_| {})).await?;
|
2022-06-10 22:40:32 -07:00
|
|
|
Ok(hex::encode(tx_hex))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-10 03:09:37 -07:00
|
|
|
#[post("/pay", data = "<payment>")]
|
2022-06-10 03:41:03 -07:00
|
|
|
pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<String, Error> {
|
2022-06-10 02:16:00 -07:00
|
|
|
if !config.allow_send {
|
2022-06-10 03:41:03 -07:00
|
|
|
Err(anyhow!("Payment API not enabled").into())
|
2022-06-10 03:09:37 -07:00
|
|
|
} else {
|
2022-06-10 02:16:00 -07:00
|
|
|
let c = CoinConfig::get_active();
|
2022-06-10 03:41:03 -07:00
|
|
|
let latest = warp_api_ffi::api::sync::get_latest_height().await?;
|
2022-06-10 02:16:00 -07:00
|
|
|
let from = {
|
2022-06-10 03:41:03 -07:00
|
|
|
let db = c.db()?;
|
|
|
|
db.get_address(c.id_account)?
|
2022-06-10 02:16:00 -07:00
|
|
|
};
|
2022-06-10 03:09:37 -07:00
|
|
|
let recipients: Vec<_> = payment
|
|
|
|
.recipients
|
|
|
|
.iter()
|
|
|
|
.map(|p| RecipientMemo::from_recipient(&from, p))
|
|
|
|
.collect();
|
2022-06-10 02:16:00 -07:00
|
|
|
let txid = warp_api_ffi::api::payment::build_sign_send_multi_payment(
|
|
|
|
latest,
|
|
|
|
&recipients,
|
|
|
|
false,
|
|
|
|
payment.confirmations,
|
2022-06-10 03:09:37 -07:00
|
|
|
Box::new(|_| {}),
|
|
|
|
)
|
2022-06-10 03:41:03 -07:00
|
|
|
.await?;
|
2022-06-10 02:16:00 -07:00
|
|
|
Ok(txid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-10 11:41:05 -07:00
|
|
|
#[post("/broadcast_tx?<tx_hex>")]
|
|
|
|
pub async fn broadcast_tx(tx_hex: String) -> Result<String, Error> {
|
|
|
|
let tx = hex::decode(tx_hex.trim_end()).map_err(|e| anyhow!(e.to_string()))?;
|
|
|
|
let tx_id = warp_api_ffi::api::payment::broadcast_tx(&tx).await?;
|
|
|
|
Ok(tx_id)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/new_diversified_address")]
|
|
|
|
pub fn new_diversified_address() -> Result<String, Error> {
|
|
|
|
let address = warp_api_ffi::api::account::new_diversified_address()?;
|
|
|
|
Ok(address)
|
|
|
|
}
|
|
|
|
|
2022-06-11 05:29:14 -07:00
|
|
|
#[post("/make_payment_uri", data = "<payment>")]
|
2022-06-10 11:41:05 -07:00
|
|
|
pub fn make_payment_uri(payment: Json<PaymentURI>) -> Result<String, Error> {
|
|
|
|
let uri = warp_api_ffi::api::payment_uri::make_payment_uri(
|
|
|
|
&payment.address,
|
|
|
|
payment.amount,
|
|
|
|
&payment.memo,
|
|
|
|
)?;
|
|
|
|
Ok(uri)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/parse_payment_uri?<uri>")]
|
|
|
|
pub fn parse_payment_uri(uri: String) -> Result<Json<PaymentURI>, Error> {
|
|
|
|
let payment = warp_api_ffi::api::payment_uri::parse_payment_uri(&uri)?;
|
|
|
|
Ok(Json(payment))
|
|
|
|
}
|
|
|
|
|
2022-06-20 02:05:11 -07:00
|
|
|
#[get("/split?<id>&<data>")]
|
|
|
|
pub fn split_data(id: u32, data: String) -> Result<Json<RaptorQDrops>, Error> {
|
|
|
|
let result = warp_api_ffi::FountainCodes::encode_into_drops(id, &hex::decode(data).unwrap())?;
|
|
|
|
Ok(Json(result))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/merge?<data>")]
|
|
|
|
pub fn merge_data(data: String) -> Result<String, Error> {
|
|
|
|
let result = warp_api_ffi::put_drop(&data)?
|
|
|
|
.map(|data| hex::encode(&data))
|
|
|
|
.unwrap_or(String::new());
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2022-07-30 16:11:58 -07:00
|
|
|
#[post("/zip32?<account>&<external>&<address>")]
|
2022-07-26 19:11:36 -07:00
|
|
|
pub fn derive_keys(
|
|
|
|
account: u32,
|
|
|
|
external: u32,
|
|
|
|
address: Option<u32>,
|
|
|
|
) -> Result<Json<KeyPack>, Error> {
|
|
|
|
let active = CoinConfig::get_active();
|
2022-07-30 16:11:58 -07:00
|
|
|
let result = warp_api_ffi::api::account::derive_keys(
|
|
|
|
active.coin,
|
|
|
|
active.id_account,
|
|
|
|
account,
|
|
|
|
external,
|
|
|
|
address,
|
|
|
|
)?;
|
2022-07-26 19:11:36 -07:00
|
|
|
Ok(Json(result))
|
|
|
|
}
|
|
|
|
|
2022-08-26 05:54:52 -07:00
|
|
|
#[post("/instant_sync")]
|
|
|
|
pub async fn instant_sync() -> Result<(), Error> {
|
|
|
|
let c = CoinConfig::get_active();
|
|
|
|
let fvk = {
|
|
|
|
let db = c.db()?;
|
|
|
|
let (_, _, fvk) = db.get_backup(c.id_account)?;
|
|
|
|
fvk
|
|
|
|
};
|
2022-08-26 21:43:36 -07:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
|
|
.post(format!("https://zec.hanh00.fun/api/scan_fvk?fvk={}", fvk))
|
|
|
|
.send()
|
|
|
|
.await?;
|
2022-08-26 05:54:52 -07:00
|
|
|
// let r = response.text().await?;
|
|
|
|
// println!("{}", r);
|
|
|
|
let account_info: AccountInfo = response.json().await?;
|
|
|
|
let mut db = c.db().unwrap();
|
|
|
|
db.import_from_syncdata(&account_info)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(crate = "rocket::serde")]
|
|
|
|
pub struct Config {
|
|
|
|
allow_backup: bool,
|
|
|
|
allow_send: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(crate = "rocket::serde")]
|
|
|
|
pub struct AccountSeed {
|
|
|
|
coin: u8,
|
|
|
|
name: String,
|
|
|
|
key: Option<String>,
|
|
|
|
index: Option<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
#[serde(crate = "rocket::serde")]
|
|
|
|
pub struct Heights {
|
|
|
|
latest: u32,
|
|
|
|
synced: u32,
|
|
|
|
}
|
2022-06-09 10:05:58 -07:00
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[derive(Serialize)]
|
|
|
|
#[serde(crate = "rocket::serde")]
|
|
|
|
pub struct Backup {
|
|
|
|
seed: Option<String>,
|
|
|
|
sk: Option<String>,
|
|
|
|
fvk: String,
|
|
|
|
}
|
2022-06-09 10:05:58 -07:00
|
|
|
|
2022-06-10 02:16:00 -07:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(crate = "rocket::serde")]
|
|
|
|
pub struct Payment {
|
|
|
|
recipients: Vec<Recipient>,
|
|
|
|
confirmations: u32,
|
2022-06-09 10:05:58 -07:00
|
|
|
}
|