Skip to content

Commit

Permalink
Merge pull request #50 from will-bitlight/testnet4
Browse files Browse the repository at this point in the history
Add Mempool indexer
  • Loading branch information
dr-orlovsky authored Jul 16, 2024
2 parents 783d977 + 1604ca1 commit b63b7ac
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 41 deletions.
15 changes: 5 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,16 @@ shellexpand = { version = "3.1.0", optional = true }

[features]
default = []
all = ["electrum", "esplora", "fs", "cli", "clap", "log"]
cli = ["base64", "env_logger", "clap", "shellexpand", "fs", "serde", "electrum", "esplora", "log"]
all = ["electrum", "esplora", "mempool", "fs", "cli", "clap", "log"]
cli = ["base64", "env_logger", "clap", "shellexpand", "fs", "serde", "electrum", "esplora", "mempool", "log"]
log = ["env_logger"]
electrum = ["bp-electrum", "serde", "serde_json"]
esplora = ["bp-esplora"]
mempool = ["esplora"]
fs = ["serde"]
serde = ["serde_crate", "serde_yaml", "toml", "bp-std/serde"]

[patch.crates-io]
bp-std = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" }
psbt = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" }
descriptors = { git = "https://github.com/BP-WG/bp-std.git", branch = "master" }
34 changes: 21 additions & 13 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use strict_encoding::Ident;
use crate::cli::{
Config, DescrStdOpts, DescriptorOpts, ExecError, GeneralOpts, ResolverOpt, WalletOpts,
};
use crate::indexers::Client as IndexerClient;
use crate::{AnyIndexer, MayError, Wallet};

/// Command-line arguments
Expand Down Expand Up @@ -135,19 +136,26 @@ impl<C: Clone + Eq + Debug + Subcommand, O: DescriptorOpts> Args<C, O> {
};

if sync {
let indexer = match (&self.resolver.esplora, &self.resolver.electrum) {
(None, Some(url)) => AnyIndexer::Electrum(Box::new(electrum::Client::new(url)?)),
(Some(url), None) => {
AnyIndexer::Esplora(Box::new(esplora::Builder::new(url).build_blocking()?))
}
_ => {
eprintln!(
" - error: no blockchain indexer specified; use either --esplora or \
--electrum argument"
);
exit(1);
}
};
let network = self.general.network.to_string();
let indexer =
match (&self.resolver.esplora, &self.resolver.electrum, &self.resolver.mempool) {
(None, Some(url), None) => AnyIndexer::Electrum(Box::new(
electrum::Client::new(&url.replace("{network}", &network))?,
)),
(Some(url), None, None) => AnyIndexer::Esplora(Box::new(
IndexerClient::new_esplora(&url.replace("{network}", &network))?,
)),
(None, None, Some(url)) => AnyIndexer::Mempool(Box::new(
IndexerClient::new_mempool(&url.replace("{network}", &network))?,
)),
_ => {
eprintln!(
" - error: no blockchain indexer specified; use either --esplora \
--mempool or --electrum argument"
);
exit(1);
}
};
eprint!("Syncing");
if let MayError {
err: Some(errors), ..
Expand Down
26 changes: 19 additions & 7 deletions src/cli/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ pub const DATA_DIR: &str = "~/Documents";
pub const DATA_DIR: &str = ".";

pub const DEFAULT_ELECTRUM: &str = "example.com:50001";
pub const DEFAULT_ESPLORA: &str = "https://blockstream.info/testnet/api";
pub const DEFAULT_ESPLORA: &str = "https://blockstream.info/{network}/api";
pub const DEFAULT_MEMPOOL: &str = "https://mempool.space/{network}/api";

#[derive(Args, Clone, PartialEq, Eq, Debug)]
#[group(args = ["electrum", "esplora"])]
#[group(args = ["electrum", "esplora", "mempool"])]
pub struct ResolverOpt {
/// Electrum server to use.
/// Electrum server to use
#[arg(
conflicts_with = "esplora",
long,
global = true,
default_missing_value = DEFAULT_ELECTRUM,
Expand All @@ -62,9 +62,8 @@ pub struct ResolverOpt {
)]
pub electrum: Option<String>,

/// Esplora server to use.
/// Esplora server to use
#[arg(
conflicts_with = "electrum",
long,
global = true,
default_missing_value = DEFAULT_ESPLORA,
Expand All @@ -75,6 +74,19 @@ pub struct ResolverOpt {
value_name = "URL"
)]
pub esplora: Option<String>,

/// Mempool server to use
#[arg(
long,
global = true,
default_missing_value = DEFAULT_MEMPOOL,
num_args = 0..=1,
require_equals = true,
env = "MEMPOOL_SERVER",
value_hint = ValueHint::Url,
value_name = "URL"
)]
pub mempool: Option<String>,
}

pub trait DescriptorOpts: clap::Args + Clone + Eq + Debug {
Expand Down Expand Up @@ -145,7 +157,7 @@ pub struct GeneralOpts {
pub data_dir: PathBuf,

/// Network to use.
#[arg(short, long, global = true, default_value = "testnet", env = "LNPBP_NETWORK")]
#[arg(short, long, global = true, default_value = "testnet3", env = "LNPBP_NETWORK")]
pub network: Network,
}

Expand Down
21 changes: 20 additions & 1 deletion src/indexers/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ pub enum AnyIndexer {
#[cfg(feature = "esplora")]
#[from]
/// Esplora indexer
Esplora(Box<esplora::blocking::BlockingClient>),
Esplora(Box<super::esplora::Client>),
#[cfg(feature = "mempool")]
/// Mempool indexer
Mempool(Box<super::esplora::Client>),
}

#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -73,6 +76,14 @@ impl Indexer for AnyIndexer {
err: result.err.map(|v| v.into_iter().map(|e| e.into()).collect()),
}
}
#[cfg(feature = "mempool")]
AnyIndexer::Mempool(inner) => {
let result = inner.create::<K, D, L2>(descr);
MayError {
ok: result.ok,
err: result.err.map(|v| v.into_iter().map(|e| e.into()).collect()),
}
}
}
}

Expand All @@ -98,6 +109,14 @@ impl Indexer for AnyIndexer {
err: result.err.map(|v| v.into_iter().map(|e| e.into()).collect()),
}
}
#[cfg(feature = "mempool")]
AnyIndexer::Mempool(inner) => {
let result = inner.update::<K, D, L2>(descr, cache);
MayError {
ok: result.ok,
err: result.err.map(|v| v.into_iter().map(|e| e.into()).collect()),
}
}
}
}
}
Expand Down
66 changes: 60 additions & 6 deletions src/indexers/esplora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,53 @@
use std::collections::BTreeMap;
use std::num::NonZeroU32;

use bpstd::{Address, LockTime, Outpoint, ScriptPubkey, SeqNo, Witness};
use bpstd::{Address, DerivedAddr, LockTime, Outpoint, SeqNo, Witness};
use descriptors::Descriptor;
use esplora::{BlockingClient, Error};

#[cfg(feature = "mempool")]
use super::mempool::Mempool;
use super::BATCH_SIZE;
use crate::{
Indexer, Layer2, MayError, MiningInfo, Party, TxCredit, TxDebit, TxStatus, WalletAddr,
WalletCache, WalletDescr, WalletTx,
};

/// Represents a client for interacting with the Esplora indexer.
#[derive(Debug, Clone)]
pub struct Client {
pub(crate) inner: BlockingClient,
pub(crate) kind: ClientKind,
}

/// Represents the kind of client used for interacting with the Esplora indexer.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ClientKind {
Esplora,
#[cfg(feature = "mempool")]
Mempool,
}

impl Client {
/// Creates a new Esplora client with the specified URL.
///
/// # Arguments
///
/// * `url` - The URL of the Esplora server.
///
/// # Errors
///
/// Returns an error if the client fails to connect to the Esplora server.
pub fn new_esplora(url: &str) -> Result<Self, Error> {
let inner = esplora::Builder::new(url).build_blocking()?;
let client = Self {
inner,
kind: ClientKind::Esplora,
};
Ok(client)
}
}

impl From<esplora::TxStatus> for TxStatus {
fn from(status: esplora::TxStatus) -> Self {
if let esplora::TxStatus {
Expand Down Expand Up @@ -97,15 +134,32 @@ impl From<esplora::Tx> for WalletTx {
}
}

/// Retrieves all transactions associated with a given script hash.
///
/// # Arguments
///
/// * `client` - The Esplora client.
/// * `derive` - The derived address.
///
/// # Errors
///
/// Returns an error if there was a problem retrieving the transactions.
fn get_scripthash_txs_all(
client: &BlockingClient,
script: &ScriptPubkey,
client: &Client,
derive: &DerivedAddr,
) -> Result<Vec<esplora::Tx>, Error> {
const PAGE_SIZE: usize = 25;
let mut res = Vec::new();
let mut last_seen = None;
let script = derive.addr.script_pubkey();
let address = derive.addr.to_string();

loop {
let r = client.scripthash_txs(script, last_seen)?;
let r = match client.kind {
ClientKind::Esplora => client.inner.scripthash_txs(&script, last_seen)?,
#[cfg(feature = "mempool")]
ClientKind::Mempool => client.inner.address_txs(&address, last_seen)?,
};
match &r[..] {
[a @ .., esplora::Tx { txid, .. }] if a.len() >= PAGE_SIZE - 1 => {
last_seen = Some(*txid);
Expand All @@ -120,7 +174,7 @@ fn get_scripthash_txs_all(
Ok(res)
}

impl Indexer for BlockingClient {
impl Indexer for Client {
type Error = Error;

fn create<K, D: Descriptor<K>, L2: Layer2>(
Expand All @@ -139,7 +193,7 @@ impl Indexer for BlockingClient {

eprint!(".");
let mut txids = Vec::new();
match get_scripthash_txs_all(self, &script) {
match get_scripthash_txs_all(self, &derive) {
Err(err) => {
errors.push(err);
break;
Expand Down
Loading

0 comments on commit b63b7ac

Please sign in to comment.