Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0c5ad08
fix(cli): quote TRAP triggered command
nymius Sep 3, 2025
aa8accd
fix(cli): pass empty bitcoin.conf to bitcoin-cli for instance isolation
nymius Sep 3, 2025
7b81c8d
refactor(cli): update transaction parameters with DrainWeights
nymius Sep 10, 2025
4bd6c13
fix(indexer): apply filter to all transactions in apply_block_with_fi…
nymius Sep 15, 2025
e17d1f8
feat(wallet): add method to retrieve the unspent script pubkeys
nymius Sep 15, 2025
c823833
fix(oracles): match against unspent script pubkeys in BlindbitSubscriber
nymius Sep 15, 2025
257d78f
feat(cli): add height optional parameter for scan-cbf command
nymius Sep 15, 2025
3a1a925
build(cli): update Cargo.lock for cli
nymius Sep 16, 2025
6d5eb05
build: add --workspace flag to build recipe
nymius Sep 4, 2025
9d2ffa6
refactor(cli): read DB_PATH from environment if possible
nymius Sep 3, 2025
6bda0a5
fix(oracles): do not error on bad blindbit requests
nymius Sep 16, 2025
8ea3fcf
feat(cli): add scan-cbf for regtest environment
nymius Sep 16, 2025
92665dd
fix(oracles): remove network parametrization from BlindbitClient
nymius Sep 17, 2025
93b1f3b
fix(cli): apply blocks even with empty partial secrets in scan-cbf
nymius Sep 18, 2025
6ea475f
fix(oracles): return empty tweaks if call to client errors
nymius Sep 18, 2025
5a941bd
doc(oracles): add comment explaining why unspent scripts are scanned
nymius Sep 18, 2025
c101f46
refactor(wallet): store BlockId as wallet birthday instead of chain h…
nymius Oct 18, 2025
0b62ebc
refactor(oracles): update kyoto-cbf to bip157 new name
nymius Oct 18, 2025
6726076
fix(cli): remove default from height/hash parameters in ScanCbf and C…
nymius Oct 6, 2025
d69e7cd
feat(cli): add Birthday command to sp-cli2
nymius Oct 6, 2025
b9904bb
refactor(cli): keep only last known peer serving filters
nymius Oct 7, 2025
3f6f01a
feat(cli): add optional extra_peer parameter to ScanCbf command
nymius Oct 7, 2025
fce6860
build(workspace): unify Cargo resolver edition on all crates
macgyver13 Oct 9, 2025
8458734
style(workspace): apply cargo fmt changes
nymius Oct 18, 2025
20316ad
build: add nix flake to build sp-cli2
nymius Aug 30, 2025
91a2ffe
build(tabconf7): add nix flake environment for workshop
nymius Aug 30, 2025
02c0a1a
build(tabconf7): add justfile to launch workshop on regtest
nymius Oct 18, 2025
7c47079
build(tabconf7): add command playbooks for silent payment demo
nymius Sep 4, 2025
cdf47be
build(tabconf7): add README.md for silent payments workshop
nymius Sep 5, 2025
f9d5d3c
chore(tabconf7): add .gitignore
nymius Sep 5, 2025
0c9ca54
docs(tabconf7): add presentation slides
nymius Sep 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,093 changes: 3,093 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions cli/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ start STATE="persistent":
short_date=$(/bin/date +%m%d%y)
exec {BASH_XTRACEFD}>>"$DEBUG_PREFIX.$short_date".log
set -euxo pipefail
trap just stop SIGHUP SIGINT SIGQUIT SIGTERM
trap "just stop" SIGHUP SIGINT SIGQUIT SIGTERM
just startcontainer

ENVDIR="/root/env"
Expand Down Expand Up @@ -274,7 +274,8 @@ cli COMMAND *ARGS:
short_date=$(/bin/date +%m%d%y)
exec {BASH_XTRACEFD}>>"$DEBUG_PREFIX.$short_date".log
set -euxo pipefail
bitcoin-cli --chain=regtest --rpcuser=__cookie__ --rpcpassword=$(just cookie) {{COMMAND}} {{ARGS}}
touch /tmp/empty.conf
bitcoin-cli --conf=/tmp/empty.conf -rpcconnect=127.0.0.1 -rpcport=18443 --chain=regtest --rpcuser=__cookie__ --rpcpassword=$(just cookie) {{COMMAND}} {{ARGS}}

[group("Logs")]
[doc("Print all logs to console.")]
Expand Down
179 changes: 129 additions & 50 deletions cli/v2/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bdk_bitcoind_rpc::{
bitcoincore_rpc::{Auth, Client, RpcApi},
Emitter, NO_EXPECTED_MEMPOOL_TXIDS,
};
use bdk_coin_select::DrainWeights;
use bdk_file_store::Store;
use bdk_sp::{
bitcoin::{
Expand All @@ -26,13 +27,13 @@ use bdk_sp::{
},
};
use bdk_sp_oracles::{
filters::kyoto::{FilterEvent, FilterSubscriber},
kyoto::{
bip157::{
self,
tokio::{self, select},
AddrV2, Client as KyotoClient, HeaderCheckpoint, IndexedBlock, Info, NodeBuilder,
ServiceFlags, TrustedPeer, UnboundedReceiver, Warning,
AddrV2, Builder, Client as KyotoClient, HeaderCheckpoint, IndexedBlock, Info, ServiceFlags,
TrustedPeer, UnboundedReceiver, Warning,
},
filters::kyoto::{FilterEvent, FilterSubscriber},
tweaks::blindbit::{BlindbitSubscriber, TweakEvent},
};
use bdk_sp_wallet::{
Expand All @@ -49,14 +50,14 @@ use rand::RngCore;
use serde_json::json;
use std::{
collections::{HashMap, HashSet},
net::{IpAddr, Ipv4Addr},
env,
net::Ipv4Addr,
str::FromStr,
sync::Mutex,
time::{Duration, Instant},
};

const DB_MAGIC: &[u8] = b"bdk_example_silentpayments";
const DB_PATH: &str = ".bdk_example_silentpayments.db";

/// Delay for printing status to stdout.
const STDOUT_PRINT_DELAY: Duration = Duration::from_secs(6);
Expand Down Expand Up @@ -153,14 +154,23 @@ pub enum Commands {
},
ScanCbf {
tweak_server_url: String,
#[clap(long)]
extra_peer: Option<String>,
#[clap(long)]
height: Option<u32>,
#[clap(long)]
hash: Option<BlockHash>,
},
Create {
/// Network
#[clap(long, short, default_value = "signet")]
network: Network,
/// The block height at which to begin scanning outputs for this wallet
#[clap(long, short, default_value = "signet")]
birthday: u32,
#[clap(long)]
birthday_height: u32,
/// The block hash at which to begin scanning outputs for this wallet
#[clap(long)]
birthday_hash: BlockHash,
/// Genesis Hash
genesis_hash: Option<BlockHash>,
},
Expand Down Expand Up @@ -199,18 +209,25 @@ pub enum Commands {
descriptor: Option<String>,
},
Balance,
Birthday,
}

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

let db_path = if let Ok(db_path) = env::var("DB_PATH") {
db_path
} else {
".bdk_example_silentpayments.db".to_string()
};

let Init {
args,
mut wallet,
db,
} = match init_or_load(DB_MAGIC, DB_PATH)? {
} = match init_or_load(DB_MAGIC, &db_path)? {
Some(init) => init,
None => return Ok(()),
};
Expand Down Expand Up @@ -283,19 +300,18 @@ async fn main() -> anyhow::Result<()> {
println!("{}", serde_json::to_string_pretty(&obj)?);
}
}
Commands::ScanCbf { tweak_server_url } => {
Commands::ScanCbf {
tweak_server_url,
extra_peer: maybe_extra_peer,
height,
hash,
} => {
async fn trace(
mut log_rx: kyoto::Receiver<String>,
mut info_rx: kyoto::Receiver<Info>,
mut info_rx: bip157::Receiver<Info>,
mut warn_rx: UnboundedReceiver<Warning>,
) {
loop {
select! {
log = log_rx.recv() => {
if let Some(log) = log {
tracing::info!("{log}");
}
}
info = info_rx.recv() => {
if let Some(info) = info {
tracing::info!("{info}");
Expand All @@ -312,52 +328,91 @@ async fn main() -> anyhow::Result<()> {

tracing::info!("Wallet main SP address: {}", wallet.get_address());

let sync_height = if wallet.birthday <= wallet.chain().tip().height() {
wallet.chain().tip().height()
let sync_point = if let (Some(height), Some(hash)) = (height, hash) {
HeaderCheckpoint::new(height, hash)
} else if wallet.birthday.height <= wallet.chain().tip().height() {
let height = wallet.chain().tip().height();
let hash = wallet.chain().tip().hash();
HeaderCheckpoint::new(height, hash)
} else {
wallet.birthday
let checkpoint = wallet
.chain()
.get(wallet.birthday.height)
.expect("should be something");
let height = checkpoint.height();
let hash = checkpoint.hash();
HeaderCheckpoint::new(height, hash)
};

tracing::info!("Synchronizing from block {sync_height}");
tracing::info!(
"Synchronizing from block {}:{}",
sync_point.hash,
sync_point.height
);

// Set up the light client
let checkpoint =
HeaderCheckpoint::closest_checkpoint_below_height(sync_height, wallet.network());
let peer_1 = IpAddr::V4(Ipv4Addr::new(95, 217, 198, 121));
let peer_2 = TrustedPeer::new(
AddrV2::Ipv4(Ipv4Addr::new(23, 137, 57, 100)),
None,
ServiceFlags::P2P_V2,
);
let builder = NodeBuilder::new(wallet.network());
let (node, client) = builder
.after_checkpoint(checkpoint)
.add_peers(vec![(peer_1, None).into(), peer_2])
.required_peers(2)
.build()
.unwrap();
let checkpoint = if wallet.network() == Network::Bitcoin {
HeaderCheckpoint::taproot_activation()
} else {
HeaderCheckpoint::from_genesis(wallet.network())
};

let mut peers = vec![];
match wallet.network() {
Network::Regtest => {
peers.push(TrustedPeer::new(
AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)),
None,
ServiceFlags::P2P_V2,
));
}
Network::Signet => {
peers.push(TrustedPeer::new(
AddrV2::Ipv4(Ipv4Addr::new(170, 75, 162, 231)),
None,
ServiceFlags::P2P_V2,
));
}
_ => unimplemented!("Not mainnet nor testnet environments"),
};

if let Some(extra_peer) = maybe_extra_peer {
peers.push(TrustedPeer::new(
AddrV2::Ipv4(Ipv4Addr::from_str(&extra_peer)?),
None,
ServiceFlags::P2P_V2,
));
};

let (node, client) = {
let builder = Builder::new(wallet.network());
builder
.chain_state(bip157::chain::ChainState::Checkpoint(checkpoint))
.add_peers(peers)
.required_peers(1)
.build()
};
let (changes_tx, changes_rx) = tokio::sync::mpsc::unbounded_channel::<FilterEvent>();
let (matches_tx, mut matches_rx) = tokio::sync::mpsc::unbounded_channel::<TweakEvent>();

let KyotoClient {
requester,
log_rx,
info_rx,
warn_rx,
event_rx,
} = client;

let (mut blindbit_subscriber, db_buffer) = BlindbitSubscriber::new(
wallet.unspent_spks(),
wallet.indexer().clone(),
tweak_server_url,
wallet.network(),
changes_rx,
matches_tx,
)
.unwrap();

let mut filter_subscriber =
FilterSubscriber::new(db_buffer, event_rx, changes_tx, sync_height);
FilterSubscriber::new(db_buffer, event_rx, changes_tx, sync_point.height);

tracing::info!("Starting the node...");
tokio::task::spawn(async move {
Expand All @@ -371,7 +426,7 @@ async fn main() -> anyhow::Result<()> {
tokio::task::spawn(async move { blindbit_subscriber.run().await });

tracing::info!("Initializing log loop...");
tokio::task::spawn(async move { trace(log_rx, info_rx, warn_rx).await });
tokio::task::spawn(async move { trace(info_rx, warn_rx).await });

tracing::info!("Starting filter subscriber...");
tokio::task::spawn(async move { filter_subscriber.run().await });
Expand Down Expand Up @@ -409,10 +464,10 @@ async fn main() -> anyhow::Result<()> {
}
}

if !partial_secrets.is_empty() {
tracing::info!("Block {hash} is relevant, indexing.");
wallet.apply_block_relevant(block, partial_secrets, height);
}
tracing::info!("Block {hash} is relevant, indexing.");
// if all outputs in transactions in the block are not p2tr
// outputs, partial secrets is going to be empty
wallet.apply_block_relevant(block, partial_secrets, height);
}
}
}
Expand Down Expand Up @@ -607,11 +662,22 @@ async fn main() -> anyhow::Result<()> {
SelectorParams::new(
FeeRate::from_sat_per_vb_unchecked(fee_rate),
outputs,
bdk_tx::ChangeDescriptor::Manual {
script_pubkey: wallet.get_change_address().get_placeholder_p2tr_spk(),
max_weight_to_satisfy_wu: SpWallet::DEFAULT_SPENDING_WEIGHT,
},
bdk_tx::ScriptSource::from_script(
wallet.get_change_address().get_placeholder_p2tr_spk(),
),
bdk_tx::ChangePolicyType::NoDustAndLeastWaste { longterm_feerate },
DrainWeights {
output_weight: TxOut {
script_pubkey: wallet
.get_change_address()
.get_placeholder_p2tr_spk(),
value: Amount::ZERO,
}
.weight()
.to_wu(),
spend_weight: SpWallet::DEFAULT_SPENDING_WEIGHT,
n_outputs: 1,
},
),
)?;

Expand Down Expand Up @@ -660,6 +726,13 @@ async fn main() -> anyhow::Result<()> {
);
println!("{}", serde_json::to_string_pretty(&obj)?);
}
Commands::Birthday => {
let BlockId { height, hash } = wallet.birthday;
let mut obj = serde_json::Map::new();
obj.insert("height".to_string(), json!(height));
obj.insert("hash".to_string(), json!(hash));
println!("{}", serde_json::to_string_pretty(&obj)?);
}
Commands::Create { .. } => {
unreachable!("already handled by init_or_load")
}
Expand Down Expand Up @@ -689,7 +762,8 @@ pub fn init_or_load(db_magic: &[u8], db_path: &str) -> anyhow::Result<Option<Ini
match args.command {
Commands::Create {
network,
birthday,
birthday_height,
birthday_hash,
genesis_hash,
} => {
let secp = Secp256k1::new();
Expand All @@ -712,8 +786,13 @@ pub fn init_or_load(db_magic: &[u8], db_path: &str) -> anyhow::Result<Option<Ini
genesis_block.block_hash()
};

let birthday = BlockId {
height: birthday_height,
hash: birthday_hash,
};

let wallet = SpWallet::new(birthday, block_hash, &tr_xprv, network).unwrap();
let mut db = Store::<ChangeSet>::create(DB_MAGIC, DB_PATH)?;
let mut db = Store::<ChangeSet>::create(db_magic, db_path)?;
if let Some(stage) = wallet.staged() {
db.append(stage).unwrap();
}
Expand Down
2 changes: 1 addition & 1 deletion dleq/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "dleq"
version = "0.1.0"
edition = "2024"
edition = "2021"
authors.workspace = true

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion dleq/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(non_snake_case)]

use bitcoin::hashes::{Hash, HashEngine, sha256t_hash_newtype};
use bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
use bitcoin::key::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};

Expand Down
2 changes: 1 addition & 1 deletion dleq/tests/functional_tests/dleq_generate_proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use dleq::dleq_generate_proof;

use crate::serialization::{GENERATE_PROOF_VECTORS, GenerateTestCase};
use crate::serialization::{GenerateTestCase, GENERATE_PROOF_VECTORS};

fn check_generated_proof(test_case: &GenerateTestCase) {
let GenerateTestCase {
Expand Down
2 changes: 1 addition & 1 deletion dleq/tests/functional_tests/dleq_verify_proofs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::serialization::{VERIFY_PROOF_VECTORS, VerifyTestCase};
use crate::serialization::{VerifyTestCase, VERIFY_PROOF_VECTORS};
use dleq::dleq_verify_proof;

fn check_generated_proof(test_case: &VerifyTestCase) {
Expand Down
2 changes: 1 addition & 1 deletion dleq/tests/functional_tests/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bitcoin::secp256k1::{PublicKey, SecretKey};
use once_cell::sync::Lazy;

use csv::ReaderBuilder;
use serde::{self, Deserialize, de::Error};
use serde::{self, de::Error, Deserialize};

#[derive(Debug, Deserialize)]
pub struct VerifyTestCase {
Expand Down
2 changes: 2 additions & 0 deletions doc/tabconf7/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.*
!/.gitignore
Loading
Loading