99
1010use bitcoin:: Network ;
1111
12- use bitcoin_payment_instructions:: hrn_resolution:: DummyHrnResolver ;
12+ use bitcoin_payment_instructions:: amount:: Amount ;
13+ use bitcoin_payment_instructions:: hrn:: HumanReadableName ;
14+ use bitcoin_payment_instructions:: hrn_resolution:: {
15+ DummyHrnResolver , HrnResolution , HrnResolutionFuture , HrnResolver , LNURLResolutionFuture ,
16+ } ;
1317use bitcoin_payment_instructions:: PaymentInstructions ;
1418
19+ use lightning_invoice:: Bolt11Invoice ;
20+
1521use std:: future:: Future ;
1622use std:: pin:: Pin ;
23+ use std:: sync:: Mutex ;
1724use std:: task:: { Context , Poll , RawWaker , RawWakerVTable , Waker } ;
1825
1926// Emulate Waker::noop until we fuzz on 1.85
@@ -25,13 +32,90 @@ fn clone_fn(p: *const ()) -> RawWaker {
2532
2633fn dummy_fn ( _: * const ( ) ) { }
2734
35+ struct Resolver < ' a > (
36+ Mutex < ( Option < Result < HrnResolution , & ' static str > > , Option < Result < & ' a [ u8 ] , & ' static str > > ) > ,
37+ ) ;
38+
39+ impl HrnResolver for Resolver < ' _ > {
40+ fn resolve_hrn < ' a > ( & ' a self , _: & ' a HumanReadableName ) -> HrnResolutionFuture < ' a > {
41+ Box :: pin ( async {
42+ let us = self . 0 . lock ( ) . unwrap ( ) ;
43+ us. 0 . take ( ) . unwrap ( )
44+ } )
45+ }
46+
47+ fn resolve_lnurl < ' a > ( & ' a self , _: String , _: Amount , _: [ u8 ; 32 ] ) -> LNURLResolutionFuture < ' a > {
48+ Box :: pin ( async {
49+ let us = self . 0 . lock ( ) . unwrap ( ) ;
50+ Bolt11Invoice :: from_str ( us. 1 . take ( ) . unwrap ( ) ) . map_err ( |_| "Failed to parse invoice" )
51+ } )
52+ }
53+ }
54+
2855#[ inline]
29- pub fn do_test ( data : & [ u8 ] ) {
56+ pub fn do_test ( mut data : & [ u8 ] ) {
57+ if data. len ( ) < 2 {
58+ return ;
59+ }
60+
61+ let mut bolt11 = None ;
62+
63+ let resolution = if ( data[ 0 ] & 0b1100_0000 ) == 0b1100_0000 {
64+ Err ( "HRN resolution failed in fuzzing" )
65+ } else if ( data[ 0 ] & 0b1100_0000 ) == 0b1000_0000 {
66+ let result_len = ( ( ( data[ 0 ] & 0b0011_1111 ) as usize ) << 8 ) | ( data[ 1 ] as usize ) ;
67+ if data. len ( ) <= result_len + 2 {
68+ return ;
69+ }
70+ let result = if let Ok ( s) = String :: from_utf8 ( data[ 2 ..result_len + 2 ] . to_vec ( ) ) {
71+ s
72+ } else {
73+ return ;
74+ } ;
75+
76+ data = & data[ result_len + 2 ..] ;
77+ Ok ( HrnResolution :: DNSSEC { result, proof : Some ( vec ! [ 8 ; 32 ] ) } )
78+ } else {
79+ if data. len ( ) <= 16 + 2 {
80+ return ;
81+ }
82+ let lnurl_resolution_fails = ( data[ 0 ] & 0b1100_0000 ) == 0b0100_0000 ;
83+ let min = Amount :: from_milli_sats ( u64:: from_le_bytes ( ( & data[ ..8 ] ) . try_into ( ) . unwrap ( ) ) ) ;
84+ data = & data[ 8 ..] ;
85+ let max = Amount :: from_milli_sats ( u64:: from_le_bytes ( ( & data[ ..8 ] ) . try_into ( ) . unwrap ( ) ) ) ;
86+ data = & data[ 8 ..] ;
87+
88+ let bolt11_len = ( ( data[ 0 ] as usize ) << 8 ) | ( data[ 1 ] as usize ) ;
89+ if data. len ( ) <= bolt11_len + 2 {
90+ return ;
91+ }
92+
93+ bolt11 = if lnurl_resolution_fails {
94+ Some ( Err ( "LNURL resolution failed in fuzzing" ) )
95+ } else {
96+ Some ( Ok ( & data[ 2 ..bolt11_len + 2 ] ) )
97+ } ;
98+ data = & data[ bolt11_len + 2 ..] ;
99+
100+ let mut expected_description_hash = [ 0 ; 32 ] ;
101+ expected_description_hash[ 31 ] = 42 ;
102+
103+ Ok ( HrnResolution :: LNURLPay {
104+ min_value : if let Ok ( min) = min { min } else { return } ,
105+ max_value : if let Ok ( max) = max { max } else { return } ,
106+ expected_description_hash,
107+ recipient_description : Some ( "Payment in fuzzing" . to_owned ( ) ) ,
108+ callback : "https://callback.uri/in/fuzzing" . to_owned ( ) ,
109+ } )
110+ } ;
111+
112+ let resolver = Resolver ( Mutex :: new ( ( Some ( resolution) , bolt11) ) ) ;
113+
30114 if let Ok ( s) = std:: str:: from_utf8 ( data) {
31115 let waker = unsafe { Waker :: from_raw ( clone_fn ( std:: ptr:: null ( ) ) ) } ;
32116
33- let fut = PaymentInstructions :: parse ( s, Network :: Bitcoin , & DummyHrnResolver , true ) ;
34- // With a DummyHrnResolver , all instructions should resolve on the first `poll`.
117+ let fut = PaymentInstructions :: parse ( s, Network :: Bitcoin , & resolver , true ) ;
118+ // With our resolver , all instructions should resolve on the first `poll`.
35119 let res = Future :: poll ( Pin :: new ( & mut Box :: pin ( fut) ) , & mut Context :: from_waker ( & waker) ) ;
36120 assert ! ( matches!( res, Poll :: Ready ( _) ) ) ;
37121
0 commit comments