@@ -3,6 +3,7 @@ use bdk_bitcoind_rpc::{
33 bitcoincore_rpc:: { Auth , Client , RpcApi } ,
44 Emitter , NO_EXPECTED_MEMPOOL_TXIDS ,
55} ;
6+ use bdk_coin_select:: DrainWeights ;
67use bdk_file_store:: Store ;
78use bdk_sp:: {
89 bitcoin:: {
@@ -26,13 +27,13 @@ use bdk_sp::{
2627 } ,
2728} ;
2829use bdk_sp_oracles:: {
29- filters:: kyoto:: { FilterEvent , FilterSubscriber } ,
30- kyoto:: {
30+ bip157:: {
3131 self ,
3232 tokio:: { self , select} ,
33- AddrV2 , Client as KyotoClient , HeaderCheckpoint , IndexedBlock , Info , NodeBuilder ,
34- ServiceFlags , TrustedPeer , UnboundedReceiver , Warning ,
33+ AddrV2 , Builder , Client as KyotoClient , HeaderCheckpoint , IndexedBlock , Info , ServiceFlags ,
34+ TrustedPeer , UnboundedReceiver , Warning ,
3535 } ,
36+ filters:: kyoto:: { FilterEvent , FilterSubscriber } ,
3637 tweaks:: blindbit:: { BlindbitSubscriber , TweakEvent } ,
3738} ;
3839use bdk_sp_wallet:: {
@@ -49,14 +50,14 @@ use rand::RngCore;
4950use serde_json:: json;
5051use std:: {
5152 collections:: { HashMap , HashSet } ,
52- net:: { IpAddr , Ipv4Addr } ,
53+ env,
54+ net:: Ipv4Addr ,
5355 str:: FromStr ,
5456 sync:: Mutex ,
5557 time:: { Duration , Instant } ,
5658} ;
5759
5860const DB_MAGIC : & [ u8 ] = b"bdk_example_silentpayments" ;
59- const DB_PATH : & str = ".bdk_example_silentpayments.db" ;
6061
6162/// Delay for printing status to stdout.
6263const STDOUT_PRINT_DELAY : Duration = Duration :: from_secs ( 6 ) ;
@@ -153,14 +154,23 @@ pub enum Commands {
153154 } ,
154155 ScanCbf {
155156 tweak_server_url : String ,
157+ #[ clap( long) ]
158+ extra_peer : Option < String > ,
159+ #[ clap( long) ]
160+ height : Option < u32 > ,
161+ #[ clap( long) ]
162+ hash : Option < BlockHash > ,
156163 } ,
157164 Create {
158165 /// Network
159166 #[ clap( long, short, default_value = "signet" ) ]
160167 network : Network ,
161168 /// The block height at which to begin scanning outputs for this wallet
162- #[ clap( long, short, default_value = "signet" ) ]
163- birthday : u32 ,
169+ #[ clap( long) ]
170+ birthday_height : u32 ,
171+ /// The block hash at which to begin scanning outputs for this wallet
172+ #[ clap( long) ]
173+ birthday_hash : BlockHash ,
164174 /// Genesis Hash
165175 genesis_hash : Option < BlockHash > ,
166176 } ,
@@ -199,18 +209,25 @@ pub enum Commands {
199209 descriptor : Option < String > ,
200210 } ,
201211 Balance ,
212+ Birthday ,
202213}
203214
204215#[ tokio:: main]
205216async fn main ( ) -> anyhow:: Result < ( ) > {
206217 let subscriber = tracing_subscriber:: FmtSubscriber :: new ( ) ;
207218 tracing:: subscriber:: set_global_default ( subscriber) . unwrap ( ) ;
208219
220+ let db_path = if let Ok ( db_path) = env:: var ( "DB_PATH" ) {
221+ db_path
222+ } else {
223+ ".bdk_example_silentpayments.db" . to_string ( )
224+ } ;
225+
209226 let Init {
210227 args,
211228 mut wallet,
212229 db,
213- } = match init_or_load ( DB_MAGIC , DB_PATH ) ? {
230+ } = match init_or_load ( DB_MAGIC , & db_path ) ? {
214231 Some ( init) => init,
215232 None => return Ok ( ( ) ) ,
216233 } ;
@@ -283,19 +300,18 @@ async fn main() -> anyhow::Result<()> {
283300 println ! ( "{}" , serde_json:: to_string_pretty( & obj) ?) ;
284301 }
285302 }
286- Commands :: ScanCbf { tweak_server_url } => {
303+ Commands :: ScanCbf {
304+ tweak_server_url,
305+ extra_peer : maybe_extra_peer,
306+ height,
307+ hash,
308+ } => {
287309 async fn trace (
288- mut log_rx : kyoto:: Receiver < String > ,
289- mut info_rx : kyoto:: Receiver < Info > ,
310+ mut info_rx : bip157:: Receiver < Info > ,
290311 mut warn_rx : UnboundedReceiver < Warning > ,
291312 ) {
292313 loop {
293314 select ! {
294- log = log_rx. recv( ) => {
295- if let Some ( log) = log {
296- tracing:: info!( "{log}" ) ;
297- }
298- }
299315 info = info_rx. recv( ) => {
300316 if let Some ( info) = info {
301317 tracing:: info!( "{info}" ) ;
@@ -312,52 +328,91 @@ async fn main() -> anyhow::Result<()> {
312328
313329 tracing:: info!( "Wallet main SP address: {}" , wallet. get_address( ) ) ;
314330
315- let sync_height = if wallet. birthday <= wallet. chain ( ) . tip ( ) . height ( ) {
316- wallet. chain ( ) . tip ( ) . height ( )
331+ let sync_point = if let ( Some ( height) , Some ( hash) ) = ( height, hash) {
332+ HeaderCheckpoint :: new ( height, hash)
333+ } else if wallet. birthday . height <= wallet. chain ( ) . tip ( ) . height ( ) {
334+ let height = wallet. chain ( ) . tip ( ) . height ( ) ;
335+ let hash = wallet. chain ( ) . tip ( ) . hash ( ) ;
336+ HeaderCheckpoint :: new ( height, hash)
317337 } else {
318- wallet. birthday
338+ let checkpoint = wallet
339+ . chain ( )
340+ . get ( wallet. birthday . height )
341+ . expect ( "should be something" ) ;
342+ let height = checkpoint. height ( ) ;
343+ let hash = checkpoint. hash ( ) ;
344+ HeaderCheckpoint :: new ( height, hash)
319345 } ;
320346
321- tracing:: info!( "Synchronizing from block {sync_height}" ) ;
347+ tracing:: info!(
348+ "Synchronizing from block {}:{}" ,
349+ sync_point. hash,
350+ sync_point. height
351+ ) ;
322352
323353 // Set up the light client
324- let checkpoint =
325- HeaderCheckpoint :: closest_checkpoint_below_height ( sync_height, wallet. network ( ) ) ;
326- let peer_1 = IpAddr :: V4 ( Ipv4Addr :: new ( 95 , 217 , 198 , 121 ) ) ;
327- let peer_2 = TrustedPeer :: new (
328- AddrV2 :: Ipv4 ( Ipv4Addr :: new ( 23 , 137 , 57 , 100 ) ) ,
329- None ,
330- ServiceFlags :: P2P_V2 ,
331- ) ;
332- let builder = NodeBuilder :: new ( wallet. network ( ) ) ;
333- let ( node, client) = builder
334- . after_checkpoint ( checkpoint)
335- . add_peers ( vec ! [ ( peer_1, None ) . into( ) , peer_2] )
336- . required_peers ( 2 )
337- . build ( )
338- . unwrap ( ) ;
354+ let checkpoint = if wallet. network ( ) == Network :: Bitcoin {
355+ HeaderCheckpoint :: taproot_activation ( )
356+ } else {
357+ HeaderCheckpoint :: from_genesis ( wallet. network ( ) )
358+ } ;
359+
360+ let mut peers = vec ! [ ] ;
361+ match wallet. network ( ) {
362+ Network :: Regtest => {
363+ peers. push ( TrustedPeer :: new (
364+ AddrV2 :: Ipv4 ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) ) ,
365+ None ,
366+ ServiceFlags :: P2P_V2 ,
367+ ) ) ;
368+ }
369+ Network :: Signet => {
370+ peers. push ( TrustedPeer :: new (
371+ AddrV2 :: Ipv4 ( Ipv4Addr :: new ( 170 , 75 , 162 , 231 ) ) ,
372+ None ,
373+ ServiceFlags :: P2P_V2 ,
374+ ) ) ;
375+ }
376+ _ => unimplemented ! ( "Not mainnet nor testnet environments" ) ,
377+ } ;
378+
379+ if let Some ( extra_peer) = maybe_extra_peer {
380+ peers. push ( TrustedPeer :: new (
381+ AddrV2 :: Ipv4 ( Ipv4Addr :: from_str ( & extra_peer) ?) ,
382+ None ,
383+ ServiceFlags :: P2P_V2 ,
384+ ) ) ;
385+ } ;
386+
387+ let ( node, client) = {
388+ let builder = Builder :: new ( wallet. network ( ) ) ;
389+ builder
390+ . chain_state ( bip157:: chain:: ChainState :: Checkpoint ( checkpoint) )
391+ . add_peers ( peers)
392+ . required_peers ( 1 )
393+ . build ( )
394+ } ;
339395 let ( changes_tx, changes_rx) = tokio:: sync:: mpsc:: unbounded_channel :: < FilterEvent > ( ) ;
340396 let ( matches_tx, mut matches_rx) = tokio:: sync:: mpsc:: unbounded_channel :: < TweakEvent > ( ) ;
341397
342398 let KyotoClient {
343399 requester,
344- log_rx,
345400 info_rx,
346401 warn_rx,
347402 event_rx,
348403 } = client;
349404
350405 let ( mut blindbit_subscriber, db_buffer) = BlindbitSubscriber :: new (
406+ wallet. unspent_spks ( ) ,
351407 wallet. indexer ( ) . clone ( ) ,
352408 tweak_server_url,
353- wallet. network ( ) ,
354409 changes_rx,
355410 matches_tx,
356411 )
357412 . unwrap ( ) ;
358413
359414 let mut filter_subscriber =
360- FilterSubscriber :: new ( db_buffer, event_rx, changes_tx, sync_height ) ;
415+ FilterSubscriber :: new ( db_buffer, event_rx, changes_tx, sync_point . height ) ;
361416
362417 tracing:: info!( "Starting the node..." ) ;
363418 tokio:: task:: spawn ( async move {
@@ -371,7 +426,7 @@ async fn main() -> anyhow::Result<()> {
371426 tokio:: task:: spawn ( async move { blindbit_subscriber. run ( ) . await } ) ;
372427
373428 tracing:: info!( "Initializing log loop..." ) ;
374- tokio:: task:: spawn ( async move { trace ( log_rx , info_rx, warn_rx) . await } ) ;
429+ tokio:: task:: spawn ( async move { trace ( info_rx, warn_rx) . await } ) ;
375430
376431 tracing:: info!( "Starting filter subscriber..." ) ;
377432 tokio:: task:: spawn ( async move { filter_subscriber. run ( ) . await } ) ;
@@ -409,10 +464,10 @@ async fn main() -> anyhow::Result<()> {
409464 }
410465 }
411466
412- if !partial_secrets . is_empty ( ) {
413- tracing :: info! ( "Block {hash} is relevant, indexing." ) ;
414- wallet . apply_block_relevant ( block , partial_secrets , height ) ;
415- }
467+ tracing :: info! ( "Block {hash} is relevant, indexing." ) ;
468+ // if all outputs in transactions in the block are not p2tr
469+ // outputs, partial secrets is going to be empty
470+ wallet . apply_block_relevant ( block , partial_secrets , height ) ;
416471 }
417472 }
418473 }
@@ -607,11 +662,22 @@ async fn main() -> anyhow::Result<()> {
607662 SelectorParams :: new (
608663 FeeRate :: from_sat_per_vb_unchecked ( fee_rate) ,
609664 outputs,
610- bdk_tx:: ChangeDescriptor :: Manual {
611- script_pubkey : wallet. get_change_address ( ) . get_placeholder_p2tr_spk ( ) ,
612- max_weight_to_satisfy_wu : SpWallet :: DEFAULT_SPENDING_WEIGHT ,
613- } ,
665+ bdk_tx:: ScriptSource :: from_script (
666+ wallet. get_change_address ( ) . get_placeholder_p2tr_spk ( ) ,
667+ ) ,
614668 bdk_tx:: ChangePolicyType :: NoDustAndLeastWaste { longterm_feerate } ,
669+ DrainWeights {
670+ output_weight : TxOut {
671+ script_pubkey : wallet
672+ . get_change_address ( )
673+ . get_placeholder_p2tr_spk ( ) ,
674+ value : Amount :: ZERO ,
675+ }
676+ . weight ( )
677+ . to_wu ( ) ,
678+ spend_weight : SpWallet :: DEFAULT_SPENDING_WEIGHT ,
679+ n_outputs : 1 ,
680+ } ,
615681 ) ,
616682 ) ?;
617683
@@ -660,6 +726,13 @@ async fn main() -> anyhow::Result<()> {
660726 ) ;
661727 println ! ( "{}" , serde_json:: to_string_pretty( & obj) ?) ;
662728 }
729+ Commands :: Birthday => {
730+ let BlockId { height, hash } = wallet. birthday ;
731+ let mut obj = serde_json:: Map :: new ( ) ;
732+ obj. insert ( "height" . to_string ( ) , json ! ( height) ) ;
733+ obj. insert ( "hash" . to_string ( ) , json ! ( hash) ) ;
734+ println ! ( "{}" , serde_json:: to_string_pretty( & obj) ?) ;
735+ }
663736 Commands :: Create { .. } => {
664737 unreachable ! ( "already handled by init_or_load" )
665738 }
@@ -689,7 +762,8 @@ pub fn init_or_load(db_magic: &[u8], db_path: &str) -> anyhow::Result<Option<Ini
689762 match args. command {
690763 Commands :: Create {
691764 network,
692- birthday,
765+ birthday_height,
766+ birthday_hash,
693767 genesis_hash,
694768 } => {
695769 let secp = Secp256k1 :: new ( ) ;
@@ -712,8 +786,13 @@ pub fn init_or_load(db_magic: &[u8], db_path: &str) -> anyhow::Result<Option<Ini
712786 genesis_block. block_hash ( )
713787 } ;
714788
789+ let birthday = BlockId {
790+ height : birthday_height,
791+ hash : birthday_hash,
792+ } ;
793+
715794 let wallet = SpWallet :: new ( birthday, block_hash, & tr_xprv, network) . unwrap ( ) ;
716- let mut db = Store :: < ChangeSet > :: create ( DB_MAGIC , DB_PATH ) ?;
795+ let mut db = Store :: < ChangeSet > :: create ( db_magic , db_path ) ?;
717796 if let Some ( stage) = wallet. staged ( ) {
718797 db. append ( stage) . unwrap ( ) ;
719798 }
0 commit comments