Inscription #65483545#65,483,545htmlInscription #63435316#63,435,316htmlInscription #54266042#54,266,042htmlInscription #54266041#54,266,041htmlInscription #54266040#54,266,040htmlInscription #54266037#54,266,037htmlInscription #54266031#54,266,031htmlInscription #54265777#54,265,777htmlInscription #54264211#54,264,211htmlInscription #54264210#54,264,210htmlInscription #54263905#54,263,905htmlInscription #54262415#54,262,415htmlInscription #54262414#54,262,414htmlInscription #54262400#54,262,400htmlInscription #54262371#54,262,371htmlInscription #54262097#54,262,097htmlInscription #54261996#54,261,996htmlInscription #54261733#54,261,733htmlInscription #54261488#54,261,488htmlInscription #54261377#54,261,377htmlInscription #54260143#54,260,143htmlInscription #54259721#54,259,721htmlInscription #54257702#54,257,702htmlInscription #54257699#54,257,699htmlInscription #54257698#54,257,698htmlInscription #54257696#54,257,696htmlInscription #53999611#53,999,611htmlInscription #53994142#53,994,142gifInscription #53994140#53,994,140gifInscription #53994139#53,994,139gifInscription #53994133#53,994,133gifInscription #53994129#53,994,129gifInscription #53994127#53,994,127gifInscription #53994126#53,994,126gifInscription #45125243#45,125,243htmlInscription #45125076#45,125,076html .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,186textInscription #14815145#14,815,145htmlInscription #14813556#14,813,556htmlInscription #215412#215,412webp