SHOWING: First 60
.into_response(),
)
}
async fn children_recursive(
Extension(index): Extension<Arc<Index>>,
Path(inscription_id): Path<InscriptionId>,
) -> ServerResult<Response> {
Self::children_recursive_paginated(Extension(index), Path((inscription_id, 0))).await
}
async fn children_recursive_paginated(
Extension(index): Extension<Arc<Index>>,
Path((parent, page)): Path<(InscriptionId, usize)>,
) -> ServerResult<Response> {
let parent_sequence_number = index
.get_inscription_entry(parent)?
.ok_or_not_found(|| format!("inscription {parent}"))?
.sequence_number;
let (ids, more) =
index.get_children_by_sequence_number_paginated(parent_sequence_number, 100, page)?;
Ok(Json(ChildrenJson { ids, more, page }).into_response())
}
async fn inscriptions(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
accept_json: AcceptJson,
) -> ServerResult<Response> {
Self::inscriptions_paginated(
#44,989,920text Extension(page_config),
Extension(index),
Path(0),
accept_json,
)
.await
}
async fn inscriptions_paginated(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(page_index): Path<usize>,
accept_json: AcceptJson,
) -> ServerResult<Response> {
let (inscriptions, more_inscriptions) = index.get_inscriptions_paginated(100, page_index)?;
let prev = page_index.checked_sub(1);
let next = more_inscriptions.then_some(page_index + 1);
Ok(if accept_json.0 {
Json(InscriptionsJson {
inscriptions,
page_index,
more: more_inscriptions,
})
.into_response()
} else {
InscriptionsHtml {
inscriptions,
next,
prev,
}
.page(page_config)
.into_response()
})
}
async fn inscriptions_in_block(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(block_height): Path<u32>,
#44,989,900text )]
csp_origin: Option<String>,
#[arg(
long,
help = "Listen on <HTTP_PORT> for incoming HTTP requests. [default: 80]."
)]
http_port: Option<u16>,
#[arg(
long,
group = "port",
help = "Listen on <HTTPS_PORT> for incoming HTTPS requests. [default: 443]."
)]
https_port: Option<u16>,
#[arg(long, help = "Store ACME TLS certificates in <ACME_CACHE>.")]
acme_cache: Option<PathBuf>,
#[arg(long, help = "Provide ACME contact <ACME_CONTACT>.")]
acme_contact: Vec<String>,
#[arg(long, help = "Serve HTTP traffic on <HTTP_PORT>.")]
http: bool,
#[arg(long, help = "Serve HTTPS traffic on <HTTPS_PORT>.")]
https: bool,
#[arg(long, help = "Redirect HTTP traffic to HTTPS.")]
redirect_http_to_https: bool,
#[arg(long, short = 'j', help = "Enable JSON API.")]
pub(crate) enable_json_api: bool,
}
impl Server {
pub(crate) fn run(self, options: Options, index: Arc<Index>, handle: Handle) -> SubcommandResult {
Runtime::new()?.block_on(async {
let index_clone = index.clone();
#44,978,648text accept_json: AcceptJson,
) -> ServerResult<Response> {
Self::inscriptions_in_block_paginated(
Extension(page_config),
Extension(index),
Path((block_height, 0)),
accept_json,
)
.await
}
async fn inscriptions_in_block_paginated(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path((block_height, page_index)): Path<(u32, usize)>,
accept_json: AcceptJson,
) -> ServerResult<Response> {
let page_size = 100;
let mut inscriptions = index
.get_inscriptions_in_block(block_height)?
.into_iter()
.skip(page_index.saturating_mul(page_size))
.take(page_size.saturating_add(1))
.collect::<Vec<InscriptionId>>();
let more = inscriptions.len() > page_size;
if more {
inscriptions.pop();
}
Ok(if accept_json.0 {
Json(InscriptionsJson {
inscriptions,
page_index,
more,
})
.into_response()
} else {
InscriptionsBlockHtml::new(
#44,978,057text Timestamp, WalletTxInfo,
},
jsonrpc_core::{IoHandler, Value},
jsonrpc_http_server::{CloseHandle, ServerBuilder},
serde::{Deserialize, Serialize},
server::Server,
state::State,
std::{
collections::{BTreeMap, BTreeSet, HashMap},
sync::{Arc, Mutex, MutexGuard},
thread,
time::Duration,
},
};
mod api;
mod server;
mod state;
pub fn builder() -> Builder {
Builder {
fail_lock_unspent: false,
network: Network::Bitcoin,
version: 240000,
}
}
pub struct Builder {
fail_lock_unspent: bool,
network: Network,
version: usize,
}
impl Builder {
pub fn fail_lock_unspent(self, fail_lock_unspent: bool) -> Self {
Self {
fail_lock_unspent,
..self
}
}
pub fn network(self, network: Network) -> Self {
Self { network, ..self }
}
pub fn version(self, version: usize) -> Self {
Self { version, ..self }
}
pub fn build(self) -> Handle {
let state = Arc::new(Mutex::new(State::new(
self.network,
self.version,
self.fail_lock_unspent,#44,976,630text pub output_values: &'a [u64],
pub outputs: usize,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Sent {
pub amount: f64,
pub address: Address,
pub locked: Vec<OutPoint>,
}
#[derive(Serialize, Deserialize)]
pub struct JsonOutPoint {
txid: bitcoin::Txid,
vout: u32,
}
impl From<OutPoint> for JsonOutPoint {
fn from(outpoint: OutPoint) -> Self {
Self {
txid: outpoint.txid,
vout: outpoint.vout,
}
}
}
impl<'a> Default for TransactionTemplate<'a> {
fn default() -> Self {
Self {
fee: 0,
inputs: &[],
op_return: None,
op_return_index: None,
output_values: &[],
outputs: 1,
}
}
}
pub struct Handle {
close_handle: Option<CloseHandle>,
port: u16,
state: Arc<Mutex<State>>,
}
impl Handle {
pub fn url(&self) -> String {
format!("http://127.0.0.1:{}", self.port)
}
fn state(&self) -> MutexGuard<State> {
self.state.lock().unwrap()
}
pub fn wallets(&self) -> BTreeSet<String> {
self.state().wallets.clone()
}
#44,976,627text )));
let server = Server::new(state.clone());
let mut io = IoHandler::default();
io.extend_with(server.to_delegate());
let rpc_server = ServerBuilder::new(io)
.threads(1)
.start_http(&"127.0.0.1:0".parse().unwrap())
.unwrap();
let close_handle = rpc_server.close_handle();
let port = rpc_server.address().port();
thread::spawn(|| rpc_server.wait());
for i in 0.. {
match reqwest::blocking::get(format!("http://127.0.0.1:{port}/")) {
Ok(_) => break,
Err(err) => {
if i == 400 {
panic!("Server failed to start: {err}");
}
}
}
thread::sleep(Duration::from_millis(25));
}
Handle {
close_handle: Some(close_handle),
port,
state,
}
}
}
pub fn spawn() -> Handle {
builder().build()
}
#[derive(Clone)]
pub struct TransactionTemplate<'a> {
pub fee: u64,
pub inputs: &'a [(usize, usize, usize, Witness)],
pub op_return: Option<ScriptBuf>,
pub op_return_index: Option<usize>,
#44,976,626textuse {
super::*,
bitcoin::{
secp256k1::{rand, KeyPair, Secp256k1, XOnlyPublicKey},
Witness,
},
bitcoincore_rpc::RawTx,
};
pub(crate) struct Server {
pub(crate) state: Arc<Mutex<State>>,
pub(crate) network: Network,
}
impl Server {
pub(crate) fn new(state: Arc<Mutex<State>>) -> Self {
let network = state.lock().unwrap().network;
Self { network, state }
}
fn state(&self) -> MutexGuard<State> {
self.state.lock().unwrap()
}
fn not_found() -> jsonrpc_core::Error {
jsonrpc_core::Error::new(jsonrpc_core::types::error::ErrorCode::ServerError(-8))
}
}
impl Api for Server {
fn get_balances(&self) -> Result<GetBalancesResult, jsonrpc_core::Error> {
Ok(GetBalancesResult {
mine: GetBalancesResultEntry {
immature: Amount::from_sat(0),
trusted: self
.list_unspent(None, None, None, None, None)?
.iter()
.map(|entry| entry.amount)
.sum(),
untrusted_pending: Amount::from_sat(0),
},
watchonly: None,
#44,976,625text
pub fn mine_blocks(&self, n: u64) -> Vec<Block> {
self.mine_blocks_with_subsidy(n, 50 * COIN_VALUE)
}
pub fn mine_blocks_with_subsidy(&self, n: u64, subsidy: u64) -> Vec<Block> {
let mut bitcoin_rpc_data = self.state();
(0..n)
.map(|_| bitcoin_rpc_data.push_block(subsidy))
.collect()
}
pub fn broadcast_tx(&self, template: TransactionTemplate) -> Txid {
self.state().broadcast_tx(template)
}
pub fn invalidate_tip(&self) -> BlockHash {
self.state().pop_block()
}
pub fn get_utxo_amount(&self, outpoint: &OutPoint) -> Option<Amount> {
self.state().utxos.get(outpoint).cloned()
}
pub fn tx(&self, bi: usize, ti: usize) -> Transaction {
let state = self.state();
state.blocks[&state.hashes[bi]].txdata[ti].clone()
}
pub fn mempool(&self) -> Vec<Transaction> {
self.state().mempool().to_vec()
}
pub fn descriptors(&self) -> Vec<String> {
self.state().descriptors.clone()
}
pub fn import_descriptor(&self, desc: String) {
self.state().descriptors.push(desc);
#44,976,624text }
pub fn sent(&self) -> Vec<Sent> {
self.state().sent.clone()
}
pub fn lock(&self, output: OutPoint) {
self.state().locked.insert(output);
}
pub fn network(&self) -> String {
match self.state().network {
Network::Bitcoin => "mainnet".to_string(),
Network::Testnet => Network::Testnet.to_string(),
Network::Signet => Network::Signet.to_string(),
Network::Regtest => Network::Regtest.to_string(),
_ => panic!(),
}
}
pub fn loaded_wallets(&self) -> BTreeSet<String> {
self.state().loaded_wallets.clone()
}
pub fn get_change_addresses(&self) -> Vec<Address> {
self.state().change_addresses.clone()
}
}
impl Drop for Handle {
fn drop(&mut self) {
self.close_handle.take().unwrap().close();
}
}
#44,976,621text{"p":"runes","op":"transfer","tick":"RUNES","amt":"1000", locked:"yes", lock-key:"CreateRunes.com"}
#34,700,186text