2024-02-14 11:11:16 +01:00
|
|
|
// Copyright (C) 2024 Riften Labs AS
|
2024-02-05 16:32:42 +01:00
|
|
|
//
|
|
|
|
|
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
|
|
|
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
|
|
2024-03-01 09:49:40 +01:00
|
|
|
use anyhow::{Context, Result};
|
2024-03-04 16:40:50 +01:00
|
|
|
use bitcoin_hashes::hex::{FromHex, ToHex};
|
2024-02-16 09:11:40 +01:00
|
|
|
use bitcoincash::{consensus::deserialize, Block, BlockHash, Transaction, Txid};
|
2024-02-14 11:11:16 +01:00
|
|
|
use chain::{get_new_headers, Chain, StoreBlockUndoer};
|
2024-03-14 12:14:13 +01:00
|
|
|
use db::{config::config_get, DBPool};
|
2024-02-16 09:11:40 +01:00
|
|
|
use electrum::{electrum_fetch_mempool, electrum_get_tip};
|
2023-11-29 12:25:04 +01:00
|
|
|
use electrum_client::{Client, ElectrumApi, Param};
|
2024-02-14 11:11:16 +01:00
|
|
|
use log::{debug, error, info};
|
2024-01-05 14:21:50 +01:00
|
|
|
use rayon::prelude::*;
|
|
|
|
|
use riftenlabs_defi::cauldron::{parse_cauldron, ParsedContract};
|
2023-11-29 12:25:04 +01:00
|
|
|
use rocket::{
|
|
|
|
|
get,
|
|
|
|
|
http::Status,
|
|
|
|
|
launch,
|
|
|
|
|
response::status::Custom,
|
|
|
|
|
routes,
|
2024-05-06 11:18:05 +02:00
|
|
|
serde::json::{json, Json},
|
2023-11-29 12:25:04 +01:00
|
|
|
State,
|
|
|
|
|
};
|
2024-01-19 14:38:31 +01:00
|
|
|
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
2023-11-29 12:25:04 +01:00
|
|
|
use serde_json::Value;
|
|
|
|
|
use std::{
|
|
|
|
|
backtrace::Backtrace,
|
2024-02-16 09:11:40 +01:00
|
|
|
collections::HashSet,
|
2023-11-29 12:25:04 +01:00
|
|
|
panic,
|
|
|
|
|
path::Path,
|
|
|
|
|
process,
|
|
|
|
|
sync::{mpsc::sync_channel, Arc, Mutex},
|
|
|
|
|
thread,
|
2024-04-03 09:49:44 +02:00
|
|
|
time::Duration,
|
2023-11-29 12:25:04 +01:00
|
|
|
};
|
2024-02-14 11:11:16 +01:00
|
|
|
use stderrlog::LogLevelNum;
|
2024-04-03 09:49:44 +02:00
|
|
|
use timeutil::time_now;
|
2023-11-29 12:25:04 +01:00
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
use crate::{
|
|
|
|
|
db::{
|
2024-03-04 16:40:50 +01:00
|
|
|
config::config_set,
|
|
|
|
|
header::{db_get_header, load_all_headers, store_headers},
|
|
|
|
|
prepare_tables,
|
|
|
|
|
tx::{self, insert_block_tx},
|
|
|
|
|
utxo_funding::insert_utxo_funding,
|
|
|
|
|
utxo_spending::insert_utxo_spending,
|
2024-02-16 09:11:40 +01:00
|
|
|
},
|
|
|
|
|
electrum::electrum_get_tx,
|
2023-11-29 12:25:04 +01:00
|
|
|
};
|
|
|
|
|
|
2024-02-14 11:11:16 +01:00
|
|
|
// The block where first cauldron contract was deployed. (Block 799870)
|
|
|
|
|
const RIFTEN_LABS_GENESIS_BLOCK: &str =
|
|
|
|
|
"000000000000000000ed24c811077f7268a21ecf25cb437655aaba33d8ff4997";
|
|
|
|
|
|
2023-11-29 12:25:04 +01:00
|
|
|
// Last indexed block height.
|
|
|
|
|
const KEY_LAST_INDEXED: &str = "last_indexed";
|
|
|
|
|
|
2024-04-03 21:40:33 +02:00
|
|
|
mod cashaddr;
|
2024-02-14 11:11:16 +01:00
|
|
|
mod chain;
|
2023-11-29 12:25:04 +01:00
|
|
|
mod db;
|
2024-02-14 11:11:16 +01:00
|
|
|
mod electrum;
|
2024-03-04 16:40:50 +01:00
|
|
|
mod rpc;
|
2024-04-03 09:49:44 +02:00
|
|
|
mod timeutil;
|
2023-11-29 12:25:04 +01:00
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
fn parse_cauldrons(tx: &Transaction) -> Vec<ParsedContract> {
|
|
|
|
|
tx.input
|
2023-11-29 12:25:04 +01:00
|
|
|
.par_iter()
|
2024-02-16 09:11:40 +01:00
|
|
|
.enumerate()
|
|
|
|
|
.filter_map(move |(i, _)| parse_cauldron(i, tx))
|
2023-11-29 12:25:04 +01:00
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
fn update_mempool(db: DBPool, electrum: Arc<Mutex<Client>>) -> Result<()> {
|
|
|
|
|
let our_mempool_txs: HashSet<Txid> = db::mempool::load_mempool(&db.get().unwrap())?;
|
2024-02-16 09:11:40 +01:00
|
|
|
let node_mempool: HashSet<Txid> = electrum_fetch_mempool(&electrum.lock().unwrap())?;
|
|
|
|
|
|
|
|
|
|
let txs_to_delete = our_mempool_txs.difference(&node_mempool);
|
|
|
|
|
let txs_to_add = node_mempool.difference(&our_mempool_txs);
|
|
|
|
|
|
|
|
|
|
let txs_to_add: Vec<Transaction> = txs_to_add
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(
|
|
|
|
|
|txid| match electrum_get_tx(&electrum.lock().unwrap(), txid) {
|
|
|
|
|
Ok(tx) => Some(tx),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
info!("Failed to get mempool tx {}: {}", txid, e);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.collect();
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let mut db_conn = db.get().unwrap();
|
|
|
|
|
let db_tx = db_conn.transaction()?;
|
2024-02-16 09:11:40 +01:00
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
let mut all_cauldrons = vec![];
|
|
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
for txid in txs_to_delete {
|
|
|
|
|
debug!("mempool remove {}", txid.to_hex());
|
2024-03-04 16:40:50 +01:00
|
|
|
db::mempool::delete_mempool_tx(&db_tx, txid)?;
|
2024-02-16 09:11:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for tx in txs_to_add {
|
|
|
|
|
let txid = tx.txid();
|
|
|
|
|
debug!("mempool add {}", txid.to_hex());
|
2024-03-04 16:40:50 +01:00
|
|
|
tx::insert_mempool_tx(&db_tx, &txid)?;
|
2024-02-16 09:11:40 +01:00
|
|
|
let cauldrons: Vec<ParsedContract> = tx
|
|
|
|
|
.input
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.filter_map(|(i, _)| parse_cauldron(i, &tx))
|
|
|
|
|
.collect();
|
|
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
insert_utxo_funding(&db_tx, &cauldrons, &txid, false)?;
|
|
|
|
|
insert_utxo_spending(&db_tx, &cauldrons, &txid, false)?;
|
|
|
|
|
|
|
|
|
|
all_cauldrons.extend(cauldrons);
|
2024-02-16 09:11:40 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
db::pool::update_pool_history(&db_tx, all_cauldrons).context("update pool history")?;
|
|
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
Ok(db_tx.commit()?)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 11:11:16 +01:00
|
|
|
fn index_blocks(
|
|
|
|
|
chain: Arc<Mutex<Chain>>,
|
2024-03-14 12:14:13 +01:00
|
|
|
pool: DBPool,
|
2024-02-14 11:11:16 +01:00
|
|
|
client: Arc<Mutex<Client>>,
|
|
|
|
|
) -> Result<BlockHash> {
|
|
|
|
|
let (tip_header, _) = electrum_get_tip(&client.lock().unwrap())?;
|
|
|
|
|
|
|
|
|
|
let (block_send, block_recv) = sync_channel::<Option<(u64, u64, Block)>>(10);
|
|
|
|
|
|
|
|
|
|
// Update header chain (and undo any blocks that may have reorged away)
|
|
|
|
|
{
|
|
|
|
|
let chain = chain.lock().unwrap();
|
|
|
|
|
if tip_header.block_hash() != chain.tip_hash() {
|
|
|
|
|
debug!(
|
|
|
|
|
"Updating header chain from {} to {}",
|
|
|
|
|
chain.tip_hash().to_hex(),
|
|
|
|
|
tip_header.block_hash().to_hex()
|
|
|
|
|
);
|
|
|
|
|
let new_headers =
|
|
|
|
|
get_new_headers(&client.lock().unwrap(), &chain, &tip_header.block_hash())?;
|
|
|
|
|
debug!("Storing headers");
|
2024-02-16 09:11:40 +01:00
|
|
|
{
|
2024-03-14 12:14:13 +01:00
|
|
|
let mut db_conn = pool.get().unwrap();
|
2024-02-16 09:11:40 +01:00
|
|
|
for chunk in new_headers.chunks(100000) {
|
2024-03-14 12:14:13 +01:00
|
|
|
let db_tx = db_conn.transaction()?;
|
2024-02-16 09:11:40 +01:00
|
|
|
store_headers(&db_tx, chunk)?;
|
|
|
|
|
db_tx.commit()?;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-14 12:14:13 +01:00
|
|
|
let undoer = StoreBlockUndoer::new(pool.clone())?;
|
2024-02-14 11:11:16 +01:00
|
|
|
chain.update(undoer, new_headers, None)?;
|
|
|
|
|
debug!("Header update done");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-29 12:25:04 +01:00
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let pool_cpy = pool.clone();
|
2023-11-29 12:25:04 +01:00
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
2024-03-14 12:14:13 +01:00
|
|
|
let pool = pool_cpy;
|
2024-02-14 11:11:16 +01:00
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let last_indexed = config_get(&pool.get().unwrap(), KEY_LAST_INDEXED).unwrap();
|
2024-02-14 11:11:16 +01:00
|
|
|
let last_indexed = last_indexed.unwrap_or(RIFTEN_LABS_GENESIS_BLOCK.to_string());
|
|
|
|
|
let mut last_indexed = BlockHash::from_hex(&last_indexed).unwrap();
|
2023-11-29 12:25:04 +01:00
|
|
|
|
2024-03-01 09:49:40 +01:00
|
|
|
// Check if last indexed has been orphaned
|
|
|
|
|
loop {
|
|
|
|
|
if chain.lock().unwrap().contains(&last_indexed) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
info!(
|
|
|
|
|
"Last indexed block ({}) has been orphaned",
|
|
|
|
|
last_indexed.to_hex()
|
|
|
|
|
);
|
2024-03-14 12:14:13 +01:00
|
|
|
let header = db_get_header(&pool.get().unwrap(), &last_indexed)
|
2024-03-01 09:49:40 +01:00
|
|
|
.context("Failed to get header for last indexed")
|
|
|
|
|
.unwrap();
|
|
|
|
|
last_indexed = header.prev_blockhash;
|
|
|
|
|
|
|
|
|
|
info!(
|
|
|
|
|
"Last indexed block rolled back to {}",
|
|
|
|
|
last_indexed.to_hex()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 12:25:04 +01:00
|
|
|
loop {
|
2024-02-14 11:11:16 +01:00
|
|
|
if tip_header.block_hash() == last_indexed {
|
2023-11-29 12:25:04 +01:00
|
|
|
block_send.send(None).unwrap();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-02-14 11:11:16 +01:00
|
|
|
|
|
|
|
|
let next_height = chain
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.get_block_height(&last_indexed)
|
2024-03-01 09:49:40 +01:00
|
|
|
.expect("last_indexed height not found in main chain")
|
2024-02-14 11:11:16 +01:00
|
|
|
+ 1;
|
|
|
|
|
|
2023-11-29 12:25:04 +01:00
|
|
|
let res = client
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
2024-02-14 11:11:16 +01:00
|
|
|
.raw_call("blockchain.block.get", vec![Param::U32(next_height as u32)])
|
2023-11-29 12:25:04 +01:00
|
|
|
.unwrap();
|
2024-02-14 11:11:16 +01:00
|
|
|
|
2023-11-29 12:25:04 +01:00
|
|
|
let block_hex: String = serde_json::from_str(&res.to_string()).unwrap();
|
|
|
|
|
let block: Block = deserialize(&hex::decode(&block_hex).unwrap()).unwrap();
|
2024-02-14 11:11:16 +01:00
|
|
|
let block_hash = block.block_hash();
|
|
|
|
|
|
|
|
|
|
block_send
|
|
|
|
|
.send(Some((
|
|
|
|
|
next_height,
|
|
|
|
|
chain.lock().unwrap().get_mtp(next_height).unwrap(),
|
|
|
|
|
block,
|
|
|
|
|
)))
|
|
|
|
|
.unwrap();
|
2023-11-29 12:25:04 +01:00
|
|
|
|
2024-02-14 11:11:16 +01:00
|
|
|
last_indexed = block_hash;
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let (block_height, mtp, block) = match block_recv.recv()? {
|
|
|
|
|
Some(res) => res,
|
|
|
|
|
None => break,
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let mut db_conn = pool.get().unwrap();
|
|
|
|
|
let db_tx = db_conn.transaction()?;
|
2024-02-16 09:11:40 +01:00
|
|
|
|
2024-02-14 11:11:16 +01:00
|
|
|
let blockhash = block.block_hash();
|
2024-02-16 09:11:40 +01:00
|
|
|
|
|
|
|
|
let mut total_cauldrons = 0;
|
|
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
let mut all_cauldrons = vec![];
|
|
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
for tx in block.txdata {
|
|
|
|
|
let cauldrons = parse_cauldrons(&tx);
|
2024-03-04 16:40:50 +01:00
|
|
|
if cauldrons.is_empty() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
let txid = tx.txid();
|
2024-03-04 16:40:50 +01:00
|
|
|
insert_block_tx(&db_tx, &txid, &blockhash, mtp as i64).context("inserting block tx")?;
|
|
|
|
|
insert_utxo_funding(&db_tx, &cauldrons, &txid, true)
|
|
|
|
|
.context("inserting funding utxos")?;
|
|
|
|
|
insert_utxo_spending(&db_tx, &cauldrons, &txid, true)
|
|
|
|
|
.context("inserting spending utxos")?;
|
2024-02-16 09:11:40 +01:00
|
|
|
total_cauldrons += cauldrons.len();
|
2024-03-04 16:40:50 +01:00
|
|
|
|
|
|
|
|
all_cauldrons.extend(cauldrons);
|
2024-02-16 09:11:40 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
// Figuring out initial utxo needs to be done on all cauldrons in a block.
|
|
|
|
|
db::pool::update_pool_history(&db_tx, all_cauldrons).context("update pool history")?;
|
|
|
|
|
|
2024-02-16 09:11:40 +01:00
|
|
|
config_set(&db_tx, KEY_LAST_INDEXED, &blockhash.to_hex());
|
|
|
|
|
db_tx.commit()?;
|
2023-11-29 12:25:04 +01:00
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
"Indexed {}; mtp: {}, height {}, {} trades.",
|
|
|
|
|
block.header.block_hash().to_hex(),
|
|
|
|
|
mtp,
|
|
|
|
|
block_height,
|
2024-02-16 09:11:40 +01:00
|
|
|
total_cauldrons
|
2023-11-29 12:25:04 +01:00
|
|
|
);
|
|
|
|
|
}
|
2024-02-14 11:11:16 +01:00
|
|
|
Ok(tip_header.block_hash())
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-19 10:24:51 +01:00
|
|
|
#[get("/tokens/list_by_volume?<duration>&<limit>")]
|
|
|
|
|
fn list_by_volume(
|
|
|
|
|
duration: Option<usize>,
|
|
|
|
|
limit: Option<usize>,
|
2024-03-14 12:14:13 +01:00
|
|
|
conn: &State<DBPool>,
|
2024-01-19 10:24:51 +01:00
|
|
|
) -> Result<Json<Vec<Value>>, Custom<String>> {
|
|
|
|
|
let thirty_days = 24 * 60 * 60 * 30;
|
|
|
|
|
let duration = duration.unwrap_or(thirty_days);
|
|
|
|
|
let limit = limit.unwrap_or(50);
|
|
|
|
|
let limit = std::cmp::max(limit, 1000);
|
|
|
|
|
|
2024-01-19 14:03:23 +01:00
|
|
|
let list: Vec<(String, u64, u64, u64, u64, u64, u64)> =
|
2024-03-14 12:14:13 +01:00
|
|
|
rpc::list_tokens_by_volume(&conn.get().unwrap(), duration, limit)
|
2024-01-19 10:24:51 +01:00
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
let result: Vec<Value> = list
|
|
|
|
|
.into_par_iter()
|
2024-01-19 14:03:23 +01:00
|
|
|
.map(
|
|
|
|
|
|(
|
|
|
|
|
token_id,
|
|
|
|
|
trade_volume,
|
|
|
|
|
trade_count,
|
|
|
|
|
tvl_sats,
|
|
|
|
|
tvl_token,
|
|
|
|
|
best_contract_sats,
|
|
|
|
|
best_contract_tokens,
|
|
|
|
|
)| {
|
|
|
|
|
json!({
|
|
|
|
|
"token_id": token_id,
|
|
|
|
|
"trade_volume": trade_volume,
|
|
|
|
|
"trade_count": trade_count,
|
|
|
|
|
"tvl_sats": tvl_sats,
|
|
|
|
|
"tvl_tokens": tvl_token,
|
|
|
|
|
"best_contract_sats": best_contract_sats,
|
|
|
|
|
"best_contract_tokens": best_contract_tokens
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
)
|
2024-01-19 10:24:51 +01:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Json(result))
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-21 11:15:44 +01:00
|
|
|
#[get("/contract/volume?<end>")]
|
|
|
|
|
fn contract_volume(
|
|
|
|
|
end: Option<i64>,
|
|
|
|
|
conn: &State<DBPool>,
|
|
|
|
|
) -> Result<Json<Vec<Value>>, Custom<String>> {
|
|
|
|
|
let end_timestamp = match end {
|
|
|
|
|
Some(e) => e,
|
2024-04-03 09:49:44 +02:00
|
|
|
None => time_now(),
|
2024-03-21 11:15:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let db = conn
|
|
|
|
|
.get()
|
|
|
|
|
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
|
|
|
|
let volume = rpc::contract_volume(&db, end_timestamp as u64)
|
|
|
|
|
.map_err(|e| Custom(Status::BadRequest, format!("Error: {}", e)))?;
|
|
|
|
|
|
|
|
|
|
let result: Vec<Value> = volume
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|(token, (total, thirty_days, one_day))| {
|
|
|
|
|
json!({
|
|
|
|
|
"token_id": token,
|
|
|
|
|
"total_sats": total,
|
|
|
|
|
"thirty_days_sats": thirty_days,
|
|
|
|
|
"one_day_sats": one_day
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Json(result))
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 12:25:04 +01:00
|
|
|
fn set_panic_hook() {
|
|
|
|
|
panic::set_hook(Box::new(|panic_info| {
|
2024-03-04 16:40:50 +01:00
|
|
|
error!("A thread panicked, terminating the program.");
|
|
|
|
|
if let Some(error) = panic_info.payload().downcast_ref::<anyhow::Error>() {
|
|
|
|
|
error!("Panic occurred: {:?}", error);
|
|
|
|
|
error!("Anyhow backtrace:\n{}", error.backtrace());
|
|
|
|
|
let mut source = error.source();
|
|
|
|
|
while let Some(cause) = source {
|
|
|
|
|
error!("Caused by: {:?}", cause);
|
|
|
|
|
source = cause.source();
|
|
|
|
|
}
|
|
|
|
|
} else if let Some(message) = panic_info.payload().downcast_ref::<&str>() {
|
2024-02-14 11:11:16 +01:00
|
|
|
error!("Panic occurred: {}", message);
|
|
|
|
|
} else {
|
2024-03-04 16:40:50 +01:00
|
|
|
error!("Panic info: {:?}", panic_info);
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-04 16:40:50 +01:00
|
|
|
let backtrace = Backtrace::capture();
|
|
|
|
|
error!("Backtrace (if RUST_BACKTRACE=1):\n{}", backtrace);
|
2023-11-29 12:25:04 +01:00
|
|
|
process::exit(1);
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
fn start_program() -> Result<DBPool> {
|
2023-11-29 12:25:04 +01:00
|
|
|
let db_path = "cauldron.db";
|
|
|
|
|
let db_exists = Path::new(db_path).exists();
|
2024-03-14 12:14:13 +01:00
|
|
|
|
2024-03-14 15:00:38 +01:00
|
|
|
let manager = r2d2_sqlite::SqliteConnectionManager::file(db_path)
|
|
|
|
|
.with_init(|c| c.execute_batch("PRAGMA foreign_keys=1;"));
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let pool = Arc::new(r2d2::Pool::new(manager).expect("Failed to initialize database"));
|
2023-11-29 12:25:04 +01:00
|
|
|
if !db_exists {
|
2024-03-14 12:14:13 +01:00
|
|
|
prepare_tables(&pool.get().expect("failed to get sqlite connection"));
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-14 11:11:16 +01:00
|
|
|
let client = Arc::new(Mutex::new(
|
|
|
|
|
Client::new("tcp://rostrum.cauldron.quest:50001").unwrap(),
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
let genesis = client
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.raw_call("blockchain.block.get", vec![Param::U32(0)])
|
|
|
|
|
.unwrap();
|
|
|
|
|
let genesis: Block = deserialize(&hex::decode(genesis.as_str().unwrap()).unwrap()).unwrap();
|
|
|
|
|
let chain = Arc::new(Mutex::new(chain::Chain::new(genesis.header)));
|
|
|
|
|
|
|
|
|
|
info!("Loading block headers...");
|
2024-03-14 12:14:13 +01:00
|
|
|
let all_headers = load_all_headers(&pool.get().unwrap()).unwrap();
|
2024-02-14 11:11:16 +01:00
|
|
|
info!("Initializing {} headers...", all_headers.len());
|
|
|
|
|
chain.lock().unwrap().load(all_headers).unwrap();
|
|
|
|
|
info!("Headers loaded.");
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let pool_cpy = Arc::clone(&pool);
|
2024-02-14 11:11:16 +01:00
|
|
|
let client_cpy = client.clone();
|
2023-11-29 12:25:04 +01:00
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
2024-03-14 12:14:13 +01:00
|
|
|
let pool = pool_cpy;
|
2024-02-14 11:11:16 +01:00
|
|
|
let client = client_cpy;
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let mut tip: BlockHash = match index_blocks(chain.clone(), pool.clone(), client.clone()) {
|
2024-02-14 11:11:16 +01:00
|
|
|
Ok(tip) => tip,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
panic!("Initial index failed: {}", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-11-29 12:25:04 +01:00
|
|
|
loop {
|
2024-02-14 11:11:16 +01:00
|
|
|
let new_tip = electrum_get_tip(&client.lock().unwrap())
|
|
|
|
|
.unwrap()
|
|
|
|
|
.0
|
|
|
|
|
.block_hash();
|
2023-11-29 12:25:04 +01:00
|
|
|
if new_tip != tip {
|
2024-03-14 12:14:13 +01:00
|
|
|
tip = index_blocks(chain.clone(), pool.clone(), client.clone())
|
2024-03-04 16:40:50 +01:00
|
|
|
.expect("Indexing failed");
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
2024-03-14 12:14:13 +01:00
|
|
|
if let Err(e) = update_mempool(pool.clone(), client.clone()) {
|
2024-02-16 09:11:40 +01:00
|
|
|
error!("Failed to update mempool: {}", e);
|
|
|
|
|
}
|
|
|
|
|
thread::sleep(Duration::from_secs(5));
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
Ok(pool)
|
2024-02-14 11:11:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[launch]
|
|
|
|
|
fn launch() -> _ {
|
|
|
|
|
stderrlog::new()
|
|
|
|
|
.verbosity(LogLevelNum::Debug)
|
|
|
|
|
.init()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
set_panic_hook();
|
|
|
|
|
|
2024-03-14 12:14:13 +01:00
|
|
|
let dbpool = match start_program() {
|
2024-02-14 11:11:16 +01:00
|
|
|
Ok(db) => db,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
let backtrace = Backtrace::capture();
|
|
|
|
|
error!(
|
|
|
|
|
"Backtrace (if RUST_BACKTRACE=1):\n{}",
|
|
|
|
|
backtrace.to_string()
|
|
|
|
|
);
|
|
|
|
|
error!("Error: {}", e.to_string());
|
|
|
|
|
panic!("Failed at program startup")
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-01-19 14:38:31 +01:00
|
|
|
let allowed_origins = AllowedOrigins::all();
|
|
|
|
|
|
|
|
|
|
let cors = rocket_cors::CorsOptions {
|
|
|
|
|
allowed_origins,
|
|
|
|
|
allowed_methods: vec![rocket::http::Method::Get]
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(From::from)
|
|
|
|
|
.collect(),
|
|
|
|
|
allowed_headers: AllowedHeaders::some(&["Authorization", "Accept"]),
|
|
|
|
|
allow_credentials: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
.to_cors()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2024-01-05 14:21:50 +01:00
|
|
|
rocket::build()
|
2024-03-14 12:14:13 +01:00
|
|
|
.manage(dbpool)
|
2024-03-04 16:40:50 +01:00
|
|
|
.mount(
|
|
|
|
|
"/cauldron/",
|
2024-03-18 12:34:46 +01:00
|
|
|
routes![
|
2024-04-03 09:49:44 +02:00
|
|
|
rpc::tvl::deprecated_tvl,
|
|
|
|
|
rpc::tvl::valuelocked_token,
|
|
|
|
|
rpc::tvl::valuelocked_all,
|
2024-03-18 12:34:46 +01:00
|
|
|
list_by_volume,
|
2024-04-03 11:39:49 +02:00
|
|
|
rpc::price::price_history,
|
|
|
|
|
rpc::price::price_current,
|
2024-04-03 21:40:33 +02:00
|
|
|
rpc::pool::list_pools_by_apy,
|
|
|
|
|
rpc::pool::list_active_pools,
|
2024-04-03 11:39:49 +02:00
|
|
|
rpc::contract::contract_count_token,
|
|
|
|
|
rpc::contract::contract_count_all,
|
2024-03-21 11:15:44 +01:00
|
|
|
contract_volume
|
2024-03-18 12:34:46 +01:00
|
|
|
],
|
2024-03-04 16:40:50 +01:00
|
|
|
)
|
2024-01-19 14:38:31 +01:00
|
|
|
.attach(cors)
|
2023-11-29 12:25:04 +01:00
|
|
|
}
|