@@ -370,8 +370,11 @@ impl BitcoindChainSource {
370370 let cur_height = channel_manager. current_best_block ( ) . height ;
371371
372372 let now = SystemTime :: now ( ) ;
373- let unconfirmed_txids = self . onchain_wallet . get_unconfirmed_txids ( ) ;
374- match self . api_client . get_updated_mempool_transactions ( cur_height, unconfirmed_txids) . await
373+ let bdk_unconfirmed_txids = self . onchain_wallet . get_unconfirmed_txids ( ) ;
374+ match self
375+ . api_client
376+ . get_updated_mempool_transactions ( cur_height, bdk_unconfirmed_txids)
377+ . await
375378 {
376379 Ok ( ( unconfirmed_txs, evicted_txids) ) => {
377380 log_trace ! (
@@ -754,7 +757,7 @@ impl BitcoindClient {
754757 async fn get_raw_transaction_rpc (
755758 rpc_client : Arc < RpcClient > , txid : & Txid ,
756759 ) -> std:: io:: Result < Option < Transaction > > {
757- let txid_hex = bitcoin :: consensus :: encode :: serialize_hex ( txid) ;
760+ let txid_hex = txid. to_string ( ) ;
758761 let txid_json = serde_json:: json!( txid_hex) ;
759762 match rpc_client
760763 . call_method :: < GetRawTransactionResponse > ( "getrawtransaction" , & [ txid_json] )
@@ -792,7 +795,7 @@ impl BitcoindClient {
792795 async fn get_raw_transaction_rest (
793796 rest_client : Arc < RestClient > , txid : & Txid ,
794797 ) -> std:: io:: Result < Option < Transaction > > {
795- let txid_hex = bitcoin :: consensus :: encode :: serialize_hex ( txid) ;
798+ let txid_hex = txid. to_string ( ) ;
796799 let tx_path = format ! ( "tx/{}.json" , txid_hex) ;
797800 match rest_client
798801 . request_resource :: < JsonResponse , GetRawTransactionResponse > ( & tx_path)
@@ -889,7 +892,7 @@ impl BitcoindClient {
889892 async fn get_mempool_entry_inner (
890893 client : Arc < RpcClient > , txid : Txid ,
891894 ) -> std:: io:: Result < Option < MempoolEntry > > {
892- let txid_hex = bitcoin :: consensus :: encode :: serialize_hex ( & txid) ;
895+ let txid_hex = txid. to_string ( ) ;
893896 let txid_json = serde_json:: json!( txid_hex) ;
894897
895898 match client. call_method :: < GetMempoolEntryResponse > ( "getmempoolentry" , & [ txid_json] ) . await {
@@ -964,11 +967,12 @@ impl BitcoindClient {
964967 /// - mempool transactions, alongside their first-seen unix timestamps.
965968 /// - transactions that have been evicted from the mempool, alongside the last time they were seen absent.
966969 pub ( crate ) async fn get_updated_mempool_transactions (
967- & self , best_processed_height : u32 , unconfirmed_txids : Vec < Txid > ,
970+ & self , best_processed_height : u32 , bdk_unconfirmed_txids : Vec < Txid > ,
968971 ) -> std:: io:: Result < ( Vec < ( Transaction , u64 ) > , Vec < ( Txid , u64 ) > ) > {
969972 let mempool_txs =
970973 self . get_mempool_transactions_and_timestamp_at_height ( best_processed_height) . await ?;
971- let evicted_txids = self . get_evicted_mempool_txids_and_timestamp ( unconfirmed_txids) . await ?;
974+ let evicted_txids =
975+ self . get_evicted_mempool_txids_and_timestamp ( bdk_unconfirmed_txids) . await ?;
972976 Ok ( ( mempool_txs, evicted_txids) )
973977 }
974978
@@ -1078,22 +1082,22 @@ impl BitcoindClient {
10781082 // To this end, we first update our local mempool_entries_cache and then return all unconfirmed
10791083 // wallet `Txid`s that don't appear in the mempool still.
10801084 async fn get_evicted_mempool_txids_and_timestamp (
1081- & self , unconfirmed_txids : Vec < Txid > ,
1085+ & self , bdk_unconfirmed_txids : Vec < Txid > ,
10821086 ) -> std:: io:: Result < Vec < ( Txid , u64 ) > > {
10831087 match self {
10841088 BitcoindClient :: Rpc { latest_mempool_timestamp, mempool_entries_cache, .. } => {
10851089 Self :: get_evicted_mempool_txids_and_timestamp_inner (
10861090 latest_mempool_timestamp,
10871091 mempool_entries_cache,
1088- unconfirmed_txids ,
1092+ bdk_unconfirmed_txids ,
10891093 )
10901094 . await
10911095 } ,
10921096 BitcoindClient :: Rest { latest_mempool_timestamp, mempool_entries_cache, .. } => {
10931097 Self :: get_evicted_mempool_txids_and_timestamp_inner (
10941098 latest_mempool_timestamp,
10951099 mempool_entries_cache,
1096- unconfirmed_txids ,
1100+ bdk_unconfirmed_txids ,
10971101 )
10981102 . await
10991103 } ,
@@ -1103,13 +1107,13 @@ impl BitcoindClient {
11031107 async fn get_evicted_mempool_txids_and_timestamp_inner (
11041108 latest_mempool_timestamp : & AtomicU64 ,
11051109 mempool_entries_cache : & tokio:: sync:: Mutex < HashMap < Txid , MempoolEntry > > ,
1106- unconfirmed_txids : Vec < Txid > ,
1110+ bdk_unconfirmed_txids : Vec < Txid > ,
11071111 ) -> std:: io:: Result < Vec < ( Txid , u64 ) > > {
11081112 let latest_mempool_timestamp = latest_mempool_timestamp. load ( Ordering :: Relaxed ) ;
11091113 let mempool_entries_cache = mempool_entries_cache. lock ( ) . await ;
1110- let evicted_txids = unconfirmed_txids
1114+ let evicted_txids = bdk_unconfirmed_txids
11111115 . into_iter ( )
1112- . filter ( |txid| mempool_entries_cache. contains_key ( txid) )
1116+ . filter ( |txid| ! mempool_entries_cache. contains_key ( txid) )
11131117 . map ( |txid| ( txid, latest_mempool_timestamp) )
11141118 . collect ( ) ;
11151119 Ok ( evicted_txids)
@@ -1236,7 +1240,7 @@ impl TryInto<GetRawMempoolResponse> for JsonResponse {
12361240
12371241 for hex in res {
12381242 let txid = if let Some ( hex_str) = hex. as_str ( ) {
1239- match bitcoin :: consensus :: encode :: deserialize_hex ( hex_str ) {
1243+ match hex_str . parse :: < Txid > ( ) {
12401244 Ok ( txid) => txid,
12411245 Err ( _) => {
12421246 return Err ( std:: io:: Error :: new (
@@ -1407,3 +1411,164 @@ impl std::fmt::Display for HttpError {
14071411 write ! ( f, "status_code: {}, contents: {}" , self . status_code, contents)
14081412 }
14091413}
1414+
1415+ #[ cfg( test) ]
1416+ mod tests {
1417+ use bitcoin:: hashes:: Hash ;
1418+ use bitcoin:: { FeeRate , OutPoint , ScriptBuf , Transaction , TxIn , TxOut , Txid , Witness } ;
1419+ use lightning_block_sync:: http:: JsonResponse ;
1420+ use proptest:: { arbitrary:: any, collection:: vec, prop_assert_eq, prop_compose, proptest} ;
1421+ use serde_json:: json;
1422+
1423+ use crate :: chain:: bitcoind:: {
1424+ FeeResponse , GetMempoolEntryResponse , GetRawMempoolResponse , GetRawTransactionResponse ,
1425+ MempoolMinFeeResponse ,
1426+ } ;
1427+
1428+ prop_compose ! {
1429+ fn arbitrary_witness( ) (
1430+ witness_elements in vec( vec( any:: <u8 >( ) , 0 ..100 ) , 0 ..20 )
1431+ ) -> Witness {
1432+ let mut witness = Witness :: new( ) ;
1433+ for element in witness_elements {
1434+ witness. push( element) ;
1435+ }
1436+ witness
1437+ }
1438+ }
1439+
1440+ prop_compose ! {
1441+ fn arbitrary_txin( ) (
1442+ outpoint_hash in any:: <[ u8 ; 32 ] >( ) ,
1443+ outpoint_vout in any:: <u32 >( ) ,
1444+ script_bytes in vec( any:: <u8 >( ) , 0 ..100 ) ,
1445+ witness in arbitrary_witness( ) ,
1446+ sequence in any:: <u32 >( )
1447+ ) -> TxIn {
1448+ TxIn {
1449+ previous_output: OutPoint {
1450+ txid: Txid :: from_byte_array( outpoint_hash) ,
1451+ vout: outpoint_vout,
1452+ } ,
1453+ script_sig: ScriptBuf :: from_bytes( script_bytes) ,
1454+ sequence: bitcoin:: Sequence :: from_consensus( sequence) ,
1455+ witness,
1456+ }
1457+ }
1458+ }
1459+
1460+ prop_compose ! {
1461+ fn arbitrary_txout( ) (
1462+ value in 0u64 ..21_000_000_00_000_000u64 ,
1463+ script_bytes in vec( any:: <u8 >( ) , 0 ..100 )
1464+ ) -> TxOut {
1465+ TxOut {
1466+ value: bitcoin:: Amount :: from_sat( value) ,
1467+ script_pubkey: ScriptBuf :: from_bytes( script_bytes) ,
1468+ }
1469+ }
1470+ }
1471+
1472+ prop_compose ! {
1473+ fn arbitrary_transaction( ) (
1474+ version in any:: <i32 >( ) ,
1475+ inputs in vec( arbitrary_txin( ) , 1 ..20 ) ,
1476+ outputs in vec( arbitrary_txout( ) , 1 ..20 ) ,
1477+ lock_time in any:: <u32 >( )
1478+ ) -> Transaction {
1479+ Transaction {
1480+ version: bitcoin:: transaction:: Version ( version) ,
1481+ input: inputs,
1482+ output: outputs,
1483+ lock_time: bitcoin:: absolute:: LockTime :: from_consensus( lock_time) ,
1484+ }
1485+ }
1486+ }
1487+
1488+ proptest ! {
1489+ #![ proptest_config( proptest:: test_runner:: Config :: with_cases( 20 ) ) ]
1490+
1491+ #[ test]
1492+ fn prop_get_raw_mempool_response_roundtrip( txids in vec( any:: <[ u8 ; 32 ] >( ) , 0 ..10 ) ) {
1493+ let txid_vec: Vec <Txid > = txids. into_iter( ) . map( Txid :: from_byte_array) . collect( ) ;
1494+ let original = GetRawMempoolResponse ( txid_vec. clone( ) ) ;
1495+
1496+ let json_vec: Vec <String > = txid_vec. iter( ) . map( |t| t. to_string( ) ) . collect( ) ;
1497+ let json_val = serde_json:: Value :: Array ( json_vec. iter( ) . map( |s| json!( s) ) . collect( ) ) ;
1498+
1499+ let resp = JsonResponse ( json_val) ;
1500+ let decoded: GetRawMempoolResponse = resp. try_into( ) . unwrap( ) ;
1501+
1502+ prop_assert_eq!( original. 0 . len( ) , decoded. 0 . len( ) ) ;
1503+
1504+ prop_assert_eq!( original. 0 , decoded. 0 ) ;
1505+ }
1506+
1507+ #[ test]
1508+ fn prop_get_mempool_entry_response_roundtrip(
1509+ time in any:: <u64 >( ) ,
1510+ height in any:: <u32 >( )
1511+ ) {
1512+ let json_val = json!( {
1513+ "time" : time,
1514+ "height" : height
1515+ } ) ;
1516+
1517+ let resp = JsonResponse ( json_val) ;
1518+ let decoded: GetMempoolEntryResponse = resp. try_into( ) . unwrap( ) ;
1519+
1520+ prop_assert_eq!( decoded. time, time) ;
1521+ prop_assert_eq!( decoded. height, height) ;
1522+ }
1523+
1524+ #[ test]
1525+ fn prop_get_raw_transaction_response_roundtrip( tx in arbitrary_transaction( ) ) {
1526+ let hex = bitcoin:: consensus:: encode:: serialize_hex( & tx) ;
1527+ let json_val = serde_json:: Value :: String ( hex. clone( ) ) ;
1528+
1529+ let resp = JsonResponse ( json_val) ;
1530+ let decoded: GetRawTransactionResponse = resp. try_into( ) . unwrap( ) ;
1531+
1532+ prop_assert_eq!( decoded. 0 . compute_txid( ) , tx. compute_txid( ) ) ;
1533+ prop_assert_eq!( decoded. 0 . compute_wtxid( ) , tx. compute_wtxid( ) ) ;
1534+
1535+ prop_assert_eq!( decoded. 0 , tx) ;
1536+ }
1537+
1538+ #[ test]
1539+ fn prop_fee_response_roundtrip( fee_rate in any:: <f64 >( ) ) {
1540+ let fee_rate = fee_rate. abs( ) ;
1541+ let json_val = json!( {
1542+ "feerate" : fee_rate,
1543+ "errors" : serde_json:: Value :: Null
1544+ } ) ;
1545+
1546+ let resp = JsonResponse ( json_val) ;
1547+ let decoded: FeeResponse = resp. try_into( ) . unwrap( ) ;
1548+
1549+ let expected = {
1550+ let fee_rate_sat_per_kwu = ( fee_rate * 25_000_000.0 ) . round( ) as u64 ;
1551+ FeeRate :: from_sat_per_kwu( fee_rate_sat_per_kwu)
1552+ } ;
1553+ prop_assert_eq!( decoded. 0 , expected) ;
1554+ }
1555+
1556+ #[ test]
1557+ fn prop_mempool_min_fee_response_roundtrip( fee_rate in any:: <f64 >( ) ) {
1558+ let fee_rate = fee_rate. abs( ) ;
1559+ let json_val = json!( {
1560+ "mempoolminfee" : fee_rate
1561+ } ) ;
1562+
1563+ let resp = JsonResponse ( json_val) ;
1564+ let decoded: MempoolMinFeeResponse = resp. try_into( ) . unwrap( ) ;
1565+
1566+ let expected = {
1567+ let fee_rate_sat_per_kwu = ( fee_rate * 25_000_000.0 ) . round( ) as u64 ;
1568+ FeeRate :: from_sat_per_kwu( fee_rate_sat_per_kwu)
1569+ } ;
1570+ prop_assert_eq!( decoded. 0 , expected) ;
1571+ }
1572+
1573+ }
1574+ }
0 commit comments