1
1
use axum:: extract:: Query ;
2
+ use axum:: headers:: { HeaderMap , HeaderValue } ;
2
3
use axum:: http:: Uri ;
3
4
use axum:: {
4
5
http:: StatusCode ,
@@ -19,6 +20,7 @@ use tonic_openssl_lnd::LndLightningClient;
19
20
use tower_http:: cors:: { AllowHeaders , AllowMethods , Any , CorsLayer } ;
20
21
21
22
use crate :: nostr_dms:: listen_to_nostr_dms;
23
+ use crate :: payments:: PaymentsByIp ;
22
24
use bolt11:: { request_bolt11, Bolt11Request , Bolt11Response } ;
23
25
use channel:: { open_channel, ChannelRequest , ChannelResponse } ;
24
26
use lightning:: { pay_lightning, LightningRequest , LightningResponse } ;
@@ -30,6 +32,7 @@ mod channel;
30
32
mod lightning;
31
33
mod nostr_dms;
32
34
mod onchain;
35
+ mod payments;
33
36
mod setup;
34
37
35
38
#[ derive( Clone ) ]
@@ -39,6 +42,7 @@ pub struct AppState {
39
42
network : bitcoin:: Network ,
40
43
lightning_client : LndLightningClient ,
41
44
lnurl : AsyncClient ,
45
+ payments : PaymentsByIp ,
42
46
}
43
47
44
48
impl AppState {
@@ -55,6 +59,7 @@ impl AppState {
55
59
network,
56
60
lightning_client,
57
61
lnurl,
62
+ payments : PaymentsByIp :: new ( ) ,
58
63
}
59
64
}
60
65
}
@@ -138,19 +143,41 @@ async fn main() -> anyhow::Result<()> {
138
143
#[ axum:: debug_handler]
139
144
async fn onchain_handler (
140
145
Extension ( state) : Extension < AppState > ,
146
+ headers : HeaderMap ,
141
147
Json ( payload) : Json < OnchainRequest > ,
142
148
) -> Result < Json < OnchainResponse > , AppError > {
143
- let res = pay_onchain ( state, payload) . await ?;
149
+ // Extract the X-Forwarded-For header
150
+ let x_forwarded_for = headers
151
+ . get ( "x-forwarded-for" )
152
+ . and_then ( |x| HeaderValue :: to_str ( x) . ok ( ) )
153
+ . unwrap_or ( "Unknown" ) ;
154
+
155
+ if state. payments . get_total_payments ( x_forwarded_for) . await > MAX_SEND_AMOUNT * 10 {
156
+ return Err ( AppError :: new ( "Too many payments" ) ) ;
157
+ }
158
+
159
+ let res = pay_onchain ( state, x_forwarded_for, payload) . await ?;
144
160
145
161
Ok ( Json ( res) )
146
162
}
147
163
148
164
#[ axum:: debug_handler]
149
165
async fn lightning_handler (
150
166
Extension ( state) : Extension < AppState > ,
167
+ headers : HeaderMap ,
151
168
Json ( payload) : Json < LightningRequest > ,
152
169
) -> Result < Json < LightningResponse > , AppError > {
153
- let payment_hash = pay_lightning ( state, & payload. bolt11 ) . await ?;
170
+ // Extract the X-Forwarded-For header
171
+ let x_forwarded_for = headers
172
+ . get ( "x-forwarded-for" )
173
+ . and_then ( |x| HeaderValue :: to_str ( x) . ok ( ) )
174
+ . unwrap_or ( "Unknown" ) ;
175
+
176
+ if state. payments . get_total_payments ( x_forwarded_for) . await > MAX_SEND_AMOUNT * 10 {
177
+ return Err ( AppError :: new ( "Too many payments" ) ) ;
178
+ }
179
+
180
+ let payment_hash = pay_lightning ( state, x_forwarded_for, & payload. bolt11 ) . await ?;
154
181
155
182
Ok ( Json ( LightningResponse { payment_hash } ) )
156
183
}
@@ -178,10 +205,21 @@ pub struct LnurlWithdrawParams {
178
205
#[ axum:: debug_handler]
179
206
async fn lnurlw_callback_handler (
180
207
Extension ( state) : Extension < AppState > ,
208
+ headers : HeaderMap ,
181
209
Query ( payload) : Query < LnurlWithdrawParams > ,
182
210
) -> Result < Json < Value > , Json < Value > > {
183
211
if payload. k1 == "k1" {
184
- pay_lightning ( state, & payload. pr )
212
+ // Extract the X-Forwarded-For header
213
+ let x_forwarded_for = headers
214
+ . get ( "x-forwarded-for" )
215
+ . and_then ( |x| HeaderValue :: to_str ( x) . ok ( ) )
216
+ . unwrap_or ( "Unknown" ) ;
217
+
218
+ if state. payments . get_total_payments ( x_forwarded_for) . await > MAX_SEND_AMOUNT * 10 {
219
+ return Err ( Json ( json ! ( { "status" : "ERROR" , "reason" : "Incorrect k1" } ) ) ) ;
220
+ }
221
+
222
+ pay_lightning ( state, x_forwarded_for, & payload. pr )
185
223
. await
186
224
. map_err ( |e| Json ( json ! ( { "status" : "ERROR" , "reason" : format!( "{e}" ) } ) ) ) ?;
187
225
Ok ( Json ( json ! ( { "status" : "OK" } ) ) )
@@ -203,16 +241,33 @@ async fn bolt11_handler(
203
241
#[ axum:: debug_handler]
204
242
async fn channel_handler (
205
243
Extension ( state) : Extension < AppState > ,
244
+ headers : HeaderMap ,
206
245
Json ( payload) : Json < ChannelRequest > ,
207
246
) -> Result < Json < ChannelResponse > , AppError > {
208
- let txid = open_channel ( state, payload. clone ( ) ) . await ?;
247
+ // Extract the X-Forwarded-For header
248
+ let x_forwarded_for = headers
249
+ . get ( "x-forwarded-for" )
250
+ . and_then ( |x| HeaderValue :: to_str ( x) . ok ( ) )
251
+ . unwrap_or ( "Unknown" ) ;
252
+
253
+ if state. payments . get_total_payments ( x_forwarded_for) . await > MAX_SEND_AMOUNT * 10 {
254
+ return Err ( AppError :: new ( "Too many payments" ) ) ;
255
+ }
256
+
257
+ let txid = open_channel ( state, x_forwarded_for, payload) . await ?;
209
258
210
259
Ok ( Json ( ChannelResponse { txid } ) )
211
260
}
212
261
213
262
// Make our own error that wraps `anyhow::Error`.
214
263
struct AppError ( anyhow:: Error ) ;
215
264
265
+ impl AppError {
266
+ fn new ( msg : & ' static str ) -> Self {
267
+ AppError ( anyhow:: anyhow!( msg) )
268
+ }
269
+ }
270
+
216
271
// Tell axum how to convert `AppError` into a response.
217
272
impl IntoResponse for AppError {
218
273
fn into_response ( self ) -> Response {
0 commit comments