bdk_kyoto 0.15.2

BDK blockchain integration using P2P light client Kyoto
Documentation
use bdk_kyoto::builder::{Builder, BuilderExt};
use bdk_kyoto::{
    HeaderCheckpoint, Info, LightClient, Receiver, ScanType, UnboundedReceiver, Warning,
};
use bdk_wallet::bitcoin::Network;
use bdk_wallet::{KeychainKind, Wallet};
use tokio::select;

const RECV: &str = "wpkh([9122d9e0/84'/1'/0']tpubDCYVtmaSaDzTxcgvoP5AHZNbZKZzrvoNH9KARep88vESc6MxRqAp4LmePc2eeGX6XUxBcdhAmkthWTDqygPz2wLAyHWisD299Lkdrj5egY6/0/*)";
const CHANGE: &str = "wpkh([9122d9e0/84'/1'/0']tpubDCYVtmaSaDzTxcgvoP5AHZNbZKZzrvoNH9KARep88vESc6MxRqAp4LmePc2eeGX6XUxBcdhAmkthWTDqygPz2wLAyHWisD299Lkdrj5egY6/1/*)";
const NETWORK: Network = Network::Signet;
const USER_SCRIPTS_USED: u32 = 1_000;

/* Sync a bdk wallet */

async fn traces(
    mut info_subscriber: Receiver<Info>,
    mut warning_subscriber: UnboundedReceiver<Warning>,
) {
    loop {
        select! {
            info = info_subscriber.recv() => {
                if let Some(info) = info {
                    match info {
                        Info::Progress(p) => {
                            tracing::info!("chain height: {}, filter download progress: {}%", p.chain_height(), p.percentage_complete());
                        },
                        Info::BlockReceived(b) => {
                            tracing::info!("downloaded block: {b}");
                        },
                        _ => (),
                    }
                }
            }
            warn = warning_subscriber.recv() => {
                if let Some(warn) = warn {
                    tracing::warn!("{warn}")
                }
            }
        }
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let subscriber = tracing_subscriber::FmtSubscriber::new();
    tracing::subscriber::set_global_default(subscriber)?;

    let mut wallet = Wallet::create(RECV, CHANGE)
        .network(NETWORK)
        .create_wallet_no_persist()?;

    let scan_type = ScanType::Recovery {
        used_script_index: USER_SCRIPTS_USED,
        checkpoint: HeaderCheckpoint::from_genesis(NETWORK),
    };

    // The light client builder handles the logic of inserting the SPKs
    let LightClient {
        requester,
        info_subscriber,
        warning_subscriber,
        mut update_subscriber,
        node,
    } = Builder::new(NETWORK)
        .build_with_wallet(&wallet, scan_type)
        .unwrap();

    tokio::task::spawn(async move { node.run().await });
    tokio::task::spawn(async move { traces(info_subscriber, warning_subscriber).await });

    // Sync and apply updates. We can do this in a continual loop while the "application" is running.
    // Often this would occur on a separate thread than the underlying application user interface.
    let update = update_subscriber.update().await?;
    wallet.apply_update(update)?;
    tracing::info!("Tx count: {}", wallet.transactions().count());
    tracing::info!("Balance: {:#}", wallet.balance().total());
    let last_revealed = wallet.derivation_index(KeychainKind::External);
    tracing::info!("Last revealed External: {:?}", last_revealed);
    tracing::info!(
        "Last revealed Internal: {:?}",
        wallet.derivation_index(KeychainKind::Internal)
    );
    tracing::info!("Local chain tip: {}", wallet.local_chain().tip().height());
    let next = wallet.reveal_next_address(KeychainKind::External).address;
    tracing::info!("Next receiving address: {next}");
    let fee_filter = requester.broadcast_min_feerate().await.unwrap();
    tracing::info!("Broadcast minimum fee rate: {:#}", fee_filter);
    return Ok(());
}