@@ -428,6 +428,30 @@ impl<D: Db> Service<D> {
428428 let timeout_response = Response :: builder ( ) . status ( StatusCode :: ACCEPTED ) . body ( empty ( ) ) ?;
429429 handle_peek ( self . db . wait_for_v2_payload ( & id) . await , timeout_response)
430430 }
431+
432+ /// Screen a V1 PSBT body against the address blocklist.
433+ ///
434+ /// Returns `Ok(())` if screening passes or is not configured.
435+ async fn check_v1_blocklist ( & self , body_str : & str ) -> Result < ( ) , HandlerError > {
436+ if let Some ( blocked) = self . v1 . as_ref ( ) . and_then ( |v| v. blocked_addresses . as_ref ( ) ) {
437+ let scripts = blocked. 0 . read ( ) . await ;
438+ if !scripts. is_empty ( ) {
439+ match screen_v1_addresses ( body_str, & scripts) {
440+ ScreenResult :: Blocked => {
441+ return Err ( HandlerError :: Forbidden ( anyhow:: anyhow!(
442+ "blocked address in V1 PSBT"
443+ ) ) ) ;
444+ }
445+ ScreenResult :: Clean => { }
446+ ScreenResult :: ParseError ( e) => {
447+ warn ! ( "Could not parse V1 PSBT: {e}" ) ;
448+ }
449+ }
450+ }
451+ }
452+ Ok ( ( ) )
453+ }
454+
431455 async fn put_payjoin_v1 (
432456 & self ,
433457 id : & str ,
@@ -446,6 +470,9 @@ impl<D: Db> Service<D> {
446470 return Err ( HandlerError :: PayloadTooLarge ) ;
447471 }
448472
473+ let body_str = std:: str:: from_utf8 ( & req) . map_err ( |e| HandlerError :: BadRequest ( e. into ( ) ) ) ?;
474+ self . check_v1_blocklist ( body_str) . await ?;
475+
449476 match self . db . post_v1_response ( & id, req. into ( ) ) . await {
450477 Ok ( _) => Ok ( ok_response) ,
451478 Err ( e) => Err ( HandlerError :: BadRequest ( e. into ( ) ) ) ,
@@ -479,23 +506,7 @@ impl<D: Db> Service<D> {
479506 Err ( _) => return Ok ( bad_request_body_res) ,
480507 } ;
481508
482- if let Some ( blocked) = self . v1 . as_ref ( ) . and_then ( |v| v. blocked_addresses . as_ref ( ) ) {
483- let scripts = blocked. 0 . read ( ) . await ;
484- if !scripts. is_empty ( ) {
485- match screen_v1_addresses ( & body_str, & scripts) {
486- ScreenResult :: Blocked => {
487- return Ok ( Response :: builder ( )
488- . status ( StatusCode :: FORBIDDEN )
489- . body ( empty ( ) ) ?) ;
490- }
491- ScreenResult :: Clean => { }
492- ScreenResult :: ParseError ( e) => {
493- warn ! ( "Could not screen V1 payload: {e}" ) ;
494- // fail-open: unparsable PSBTs can't complete transactions
495- }
496- }
497- }
498- }
509+ self . check_v1_blocklist ( & body_str) . await ?;
499510
500511 let v2_compat_body = format ! ( "{body_str}\n {query}" ) ;
501512 let id = ShortId :: from_str ( id) ?;
@@ -790,6 +801,8 @@ mod tests {
790801 ( parts. status , String :: from_utf8 ( bytes. to_vec ( ) ) . unwrap ( ) )
791802 }
792803
804+ // V1 routing
805+
793806 #[ tokio:: test]
794807 async fn post_v1_when_disabled_returns_version_unsupported ( ) {
795808 let mut svc = test_service ( None ) . await ;
@@ -840,24 +853,17 @@ mod tests {
840853 assert_eq ! ( status, StatusCode :: SERVICE_UNAVAILABLE ) ;
841854 assert_eq ! ( body, V1_UNAVAILABLE_RES_JSON ) ;
842855 }
843- }
844856
845- #[ cfg( test) ]
846- mod screen_tests {
847- use super :: * ;
848-
849- fn addr_to_script ( address : & str ) -> bitcoin:: ScriptBuf {
850- let addr: bitcoin:: Address < bitcoin:: address:: NetworkUnchecked > =
851- address. parse ( ) . expect ( "valid address" ) ;
852- addr. assume_checked ( ) . script_pubkey ( )
853- }
857+ // Address screening
854858
855859 fn make_test_psbt_base64 ( output_address : & str ) -> String {
856860 use bitcoin:: base64:: prelude:: { Engine , BASE64_STANDARD } ;
857861 use bitcoin:: psbt:: Psbt ;
858862 use bitcoin:: { Amount , Transaction , TxIn , TxOut } ;
859863
860- let script_pubkey = addr_to_script ( output_address) ;
864+ let addr: bitcoin:: Address < bitcoin:: address:: NetworkUnchecked > =
865+ output_address. parse ( ) . expect ( "valid address" ) ;
866+ let script_pubkey = addr. assume_checked ( ) . script_pubkey ( ) ;
861867
862868 let tx = Transaction {
863869 version : bitcoin:: transaction:: Version :: TWO ,
@@ -867,8 +873,32 @@ mod screen_tests {
867873 } ;
868874
869875 let psbt = Psbt :: from_unsigned_tx ( tx) . expect ( "valid psbt" ) ;
870- let serialized = psbt. serialize ( ) ;
871- BASE64_STANDARD . encode ( & serialized)
876+ BASE64_STANDARD . encode ( psbt. serialize ( ) )
877+ }
878+
879+ fn addr_to_script ( address : & str ) -> bitcoin:: ScriptBuf {
880+ let addr: bitcoin:: Address < bitcoin:: address:: NetworkUnchecked > =
881+ address. parse ( ) . expect ( "valid address" ) ;
882+ addr. assume_checked ( ) . script_pubkey ( )
883+ }
884+
885+ #[ tokio:: test]
886+ async fn post_v1_with_blocked_address_returns_forbidden ( ) {
887+ let blocked_addr = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" ;
888+ let blocked = BlockedAddresses :: from_address_lines ( blocked_addr) ;
889+ let mut svc = test_service ( Some ( V1 :: new ( Some ( blocked) ) ) ) . await ;
890+ let id = valid_short_id_path ( ) ;
891+ let psbt_b64 = make_test_psbt_base64 ( blocked_addr) ;
892+ let req = Request :: builder ( )
893+ . method ( Method :: POST )
894+ . uri ( format ! ( "http://localhost/{id}" ) )
895+ . body ( Full :: new ( Bytes :: from ( psbt_b64) ) )
896+ . unwrap ( ) ;
897+
898+ let res = tower:: Service :: call ( & mut svc, req) . await . unwrap ( ) ;
899+ let ( status, _body) = collect_body ( res) . await ;
900+
901+ assert_eq ! ( status, StatusCode :: FORBIDDEN ) ;
872902 }
873903
874904 #[ test]
0 commit comments