-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement ns-inscriber cli tool
- Loading branch information
Showing
34 changed files
with
1,684 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,16 @@ | ||
BITCOIN_RPC_URL=http://127.0.0.1:18443 | ||
BITCOIN_RPC_USER=test | ||
BITCOIN_RPC_PASSWORD=123456 | ||
|
||
# ----- ns-indexer env ----- | ||
LOG_LEVEL=info | ||
|
||
INDEXER_SERVER_WORKER_THREADS=0 # defaults to the number of cpus on the system | ||
INDEXER_SERVER_ADDR=0.0.0.0:8080 | ||
INDEXER_SERVER_NOSCAN=false # run as API server | ||
INDEXER_UTXO=false # index UTXO when scanning | ||
INDEXER_START_HEIGHT=0 | ||
# more nodes split by comma | ||
SCYLLA_NODES=127.0.0.1:9042 | ||
|
||
SCYLLA_NODES=127.0.0.1:9042 # more nodes split by comma | ||
SCYLLA_USERNAME="" | ||
SCYLLA_PASSWORD="" | ||
SCYLLA_KEYSPACE=ns_indexer | ||
|
||
# ----- ns-inscriber env ----- | ||
BITCOIN_RPC_URL=http://127.0.0.1:18443 | ||
BITCOIN_RPC_USER=test | ||
BITCOIN_RPC_PASSWORD=123456 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use axum::{ | ||
extract::{Query, State}, | ||
Extension, | ||
}; | ||
use bitcoin::{Address, AddressType}; | ||
use std::{collections::BTreeMap, str::FromStr, sync::Arc}; | ||
use validator::Validate; | ||
|
||
use axum_web::{ | ||
context::ReqContext, | ||
erring::{HTTPError, SuccessResponse}, | ||
object::PackObject, | ||
}; | ||
|
||
use crate::api::{IndexerAPI, QueryAddress}; | ||
use crate::db; | ||
use crate::utxo::UTXO; | ||
|
||
pub struct UtxoAPI; | ||
|
||
impl UtxoAPI { | ||
pub async fn list( | ||
State(app): State<Arc<IndexerAPI>>, | ||
Extension(ctx): Extension<Arc<ReqContext>>, | ||
to: PackObject<()>, | ||
input: Query<QueryAddress>, | ||
) -> Result<PackObject<SuccessResponse<Vec<UTXO>>>, HTTPError> { | ||
input.validate()?; | ||
|
||
let address = Address::from_str(input.address.as_str()) | ||
.map_err(|_| HTTPError::new(400, format!("invalid address: {}", input.address)))? | ||
.assume_checked(); | ||
|
||
match address.address_type() { | ||
Some(AddressType::P2tr) | Some(AddressType::P2wpkh) => {} | ||
other => { | ||
return Err(HTTPError::new( | ||
400, | ||
format!("only support p2tr address, got: {:?}", other), | ||
)); | ||
} | ||
} | ||
|
||
ctx.set_kvs(vec![("action", "list_utxos_by_address".into())]) | ||
.await; | ||
|
||
let address = address.script_pubkey().as_bytes().to_vec(); | ||
let utxos = db::Utxo::list(&app.scylla, &address).await?; | ||
let mut utxos: BTreeMap<(&Vec<u8>, u32), UTXO> = utxos | ||
.iter() | ||
.map(|utxo| ((&utxo.txid, utxo.vout as u32), utxo.to_utxo())) | ||
.collect(); | ||
|
||
let confirming_utxos = app.state.confirming_utxos.read().await; | ||
for utxo in confirming_utxos.iter() { | ||
for spent in &utxo.1 { | ||
utxos.remove(&(&spent.txid, spent.vout)); | ||
} | ||
for (_, unspent) in &utxo.2 { | ||
utxos.insert((&unspent.txid, unspent.vout), unspent.clone()); | ||
} | ||
} | ||
|
||
let mut utxos = utxos.into_values().collect::<Vec<_>>(); | ||
utxos.sort_by(|a, b| a.amount.partial_cmp(&b.amount).unwrap()); | ||
Ok(to.with(SuccessResponse::new(utxos))) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
use std::vec; | ||
|
||
use scylla_orm::{ColumnsMap, CqlValue, ToCqlVal}; | ||
use scylla_orm_macros::CqlOrm; | ||
|
||
use crate::db::scylladb; | ||
use crate::utxo; | ||
|
||
#[derive(Debug, Default, Clone, CqlOrm, PartialEq)] | ||
pub struct Utxo { | ||
pub txid: Vec<u8>, | ||
pub vout: i32, | ||
pub amount: i64, | ||
pub address: Vec<u8>, | ||
|
||
pub _fields: Vec<String>, // selected fields,field with `_` will be ignored by CqlOrm | ||
} | ||
|
||
impl Utxo { | ||
pub fn from_utxo(address: Vec<u8>, value: &utxo::UTXO) -> Self { | ||
Self { | ||
txid: value.txid.clone(), | ||
vout: value.vout as i32, | ||
amount: value.amount as i64, | ||
address, | ||
_fields: vec![], | ||
} | ||
} | ||
|
||
pub fn to_utxo(&self) -> utxo::UTXO { | ||
utxo::UTXO { | ||
txid: self.txid.clone(), | ||
vout: self.vout as u32, | ||
amount: self.amount as u64, | ||
} | ||
} | ||
|
||
pub async fn handle_utxo( | ||
db: &scylladb::ScyllaDB, | ||
spent: &Vec<utxo::UTXO>, | ||
unspent: &Vec<(Vec<u8>, utxo::UTXO)>, | ||
) -> anyhow::Result<()> { | ||
// delete spent utxos | ||
let mut start = 0; | ||
while start < spent.len() { | ||
let end = if start + 1000 > spent.len() { | ||
spent.len() | ||
} else { | ||
start + 1000 | ||
}; | ||
let mut statements: Vec<&str> = Vec::with_capacity(end - start); | ||
let mut values: Vec<Vec<CqlValue>> = Vec::with_capacity(end - start); | ||
let query = "DELETE FROM utxo WHERE txid=? AND vout=?"; | ||
|
||
for tx in &spent[start..end] { | ||
statements.push(query); | ||
values.push(vec![tx.txid.to_cql(), (tx.vout as i32).to_cql()]); | ||
} | ||
|
||
if statements.len() > 500 { | ||
log::info!(target: "ns-indexer", | ||
action = "handle_spent_utxos", | ||
statements = statements.len(); | ||
"", | ||
); | ||
} | ||
|
||
let _ = db.batch(statements, values).await?; | ||
start = end; | ||
} | ||
|
||
let mut start = 0; | ||
while start < unspent.len() { | ||
let end = if start + 1000 > unspent.len() { | ||
unspent.len() | ||
} else { | ||
start + 1000 | ||
}; | ||
let mut statements: Vec<&str> = Vec::with_capacity(unspent.len()); | ||
let mut values: Vec<Vec<CqlValue>> = Vec::with_capacity(unspent.len()); | ||
let query = "INSERT INTO utxo (txid,vout,amount,address) VALUES (?,?,?,?)"; | ||
|
||
for tx in &unspent[start..end] { | ||
statements.push(query); | ||
let tx = Self::from_utxo(tx.0.clone(), &tx.1); | ||
values.push(vec![ | ||
tx.txid.to_cql(), | ||
tx.vout.to_cql(), | ||
tx.amount.to_cql(), | ||
tx.address.to_cql(), | ||
]); | ||
} | ||
|
||
if statements.len() > 500 { | ||
log::info!(target: "ns-indexer", | ||
action = "handle_unspent_utxos", | ||
statements = statements.len(); | ||
"", | ||
); | ||
} | ||
|
||
let _ = db.batch(statements, values).await?; | ||
start = end; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub async fn list(db: &scylladb::ScyllaDB, address: &Vec<u8>) -> anyhow::Result<Vec<Self>> { | ||
let fields = Self::fields(); | ||
|
||
let query = format!( | ||
"SELECT {} FROM utxo WHERE address=? USING TIMEOUT 3s", | ||
fields.clone().join(",") | ||
); | ||
let params = (address.to_cql(),); | ||
let rows = db.execute_iter(query, params).await?; | ||
|
||
let mut res: Vec<Self> = Vec::with_capacity(rows.len()); | ||
for row in rows { | ||
let mut doc = Self::default(); | ||
let mut cols = ColumnsMap::with_capacity(fields.len()); | ||
cols.fill(row, &fields)?; | ||
doc.fill(&cols); | ||
doc._fields = fields.clone(); | ||
res.push(doc); | ||
} | ||
|
||
Ok(res) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.