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