11use std:: str:: FromStr ;
22use std:: sync:: { Arc , RwLock } ;
3- use std:: time:: Duration ;
43
54pub use error:: {
65 AddressParseError , InputContributionError , InputPairError , JsonReply , OutputSubstitutionError ,
@@ -9,14 +8,19 @@ pub use error::{
98} ;
109use payjoin:: bitcoin:: consensus:: Decodable ;
1110use payjoin:: bitcoin:: psbt:: Psbt ;
12- use payjoin:: bitcoin:: { Amount , FeeRate } ;
11+ use payjoin:: bitcoin:: FeeRate ;
1312use payjoin:: persist:: { MaybeFatalTransition , NextStateTransition } ;
1413
1514use crate :: error:: ForeignError ;
16- pub use crate :: error:: { ImplementationError , SerdeJsonError } ;
15+ pub use crate :: error:: { ImplementationError , PrimitiveError , SerdeJsonError } ;
1716use crate :: ohttp:: OhttpKeys ;
1817use crate :: receive:: error:: { ReceiverPersistedError , ReceiverReplayError } ;
1918use crate :: uri:: error:: FeeRateError ;
19+ use crate :: validation:: {
20+ validate_amount_sat, validate_expiration_secs, validate_fee_rate_sat_per_kwu_opt,
21+ validate_fee_rate_sat_per_vb_opt, validate_optional_script, validate_script_bytes,
22+ validate_script_vec, validate_weight_units, validate_witness_stack,
23+ } ;
2024use crate :: { ClientResponse , OutputSubstitution , Request } ;
2125
2226pub mod error;
@@ -270,12 +274,11 @@ pub struct PlainTxOut {
270274 pub script_pubkey : Vec < u8 > ,
271275}
272276
273- impl From < PlainTxOut > for payjoin:: bitcoin:: TxOut {
274- fn from ( value : PlainTxOut ) -> Self {
275- payjoin:: bitcoin:: TxOut {
276- value : Amount :: from_sat ( value. value_sat ) ,
277- script_pubkey : payjoin:: bitcoin:: ScriptBuf :: from_bytes ( value. script_pubkey ) ,
278- }
277+ impl PlainTxOut {
278+ fn into_core ( self ) -> Result < payjoin:: bitcoin:: TxOut , PrimitiveError > {
279+ let value = validate_amount_sat ( self . value_sat ) ?;
280+ let script_pubkey = validate_script_vec ( "script_pubkey" , self . script_pubkey , false ) ?;
281+ Ok ( payjoin:: bitcoin:: TxOut { value, script_pubkey } )
279282 }
280283}
281284
@@ -299,6 +302,8 @@ pub struct PlainTxIn {
299302
300303impl PlainTxIn {
301304 fn into_core ( self ) -> Result < payjoin:: bitcoin:: TxIn , InputPairError > {
305+ validate_script_bytes ( "script_sig" , & self . script_sig , true ) ?;
306+ validate_witness_stack ( & self . witness ) ?;
302307 let previous_output = self . previous_output . into_core ( ) ?;
303308 Ok ( payjoin:: bitcoin:: TxIn {
304309 previous_output,
@@ -341,13 +346,20 @@ pub struct PlainPsbtInput {
341346}
342347
343348impl PlainPsbtInput {
344- fn into_core ( self ) -> payjoin:: bitcoin:: psbt:: Input {
345- payjoin:: bitcoin:: psbt:: Input {
346- witness_utxo : self . witness_utxo . map ( Into :: into) ,
347- redeem_script : self . redeem_script . map ( payjoin:: bitcoin:: ScriptBuf :: from_bytes) ,
348- witness_script : self . witness_script . map ( payjoin:: bitcoin:: ScriptBuf :: from_bytes) ,
349+ fn into_core ( self ) -> Result < payjoin:: bitcoin:: psbt:: Input , InputPairError > {
350+ let witness_utxo = self
351+ . witness_utxo
352+ . map ( |utxo| utxo. into_core ( ) )
353+ . transpose ( )
354+ . map_err ( InputPairError :: from) ?;
355+ let redeem_script = validate_optional_script ( "redeem_script" , self . redeem_script ) ?;
356+ let witness_script = validate_optional_script ( "witness_script" , self . witness_script ) ?;
357+ Ok ( payjoin:: bitcoin:: psbt:: Input {
358+ witness_utxo,
359+ redeem_script,
360+ witness_script,
349361 ..Default :: default ( )
350- }
362+ } )
351363 }
352364}
353365
@@ -357,8 +369,10 @@ pub struct PlainWeight {
357369 pub weight_units : u64 ,
358370}
359371
360- impl From < PlainWeight > for payjoin:: bitcoin:: Weight {
361- fn from ( value : PlainWeight ) -> Self { payjoin:: bitcoin:: Weight :: from_wu ( value. weight_units ) }
372+ impl PlainWeight {
373+ fn into_core ( self ) -> Result < payjoin:: bitcoin:: Weight , PrimitiveError > {
374+ validate_weight_units ( self . weight_units )
375+ }
362376}
363377
364378impl From < payjoin:: bitcoin:: Weight > for PlainWeight {
@@ -395,12 +409,14 @@ impl ReceiverBuilder {
395409 ) )
396410 }
397411
398- pub fn with_amount ( & self , amount_sats : u64 ) -> Self {
399- Self ( self . 0 . clone ( ) . with_amount ( Amount :: from_sat ( amount_sats) ) )
412+ pub fn with_amount ( & self , amount_sats : u64 ) -> Result < Self , PrimitiveError > {
413+ let amount = validate_amount_sat ( amount_sats) ?;
414+ Ok ( Self ( self . 0 . clone ( ) . with_amount ( amount) ) )
400415 }
401416
402- pub fn with_expiration ( & self , expiration : u64 ) -> Self {
403- Self ( self . 0 . clone ( ) . with_expiration ( Duration :: from_secs ( expiration) ) )
417+ pub fn with_expiration ( & self , expiration : u64 ) -> Result < Self , PrimitiveError > {
418+ let expiration = validate_expiration_secs ( expiration) ?;
419+ Ok ( Self ( self . 0 . clone ( ) . with_expiration ( expiration) ) )
404420 }
405421
406422 /// Set the maximum effective fee rate the receiver is willing to pay for their own input/output contributions
@@ -622,17 +638,15 @@ impl UncheckedOriginalPayload {
622638 & self ,
623639 min_fee_rate : Option < u64 > ,
624640 can_broadcast : Arc < dyn CanBroadcast > ,
625- ) -> UncheckedOriginalPayloadTransition {
626- UncheckedOriginalPayloadTransition ( Arc :: new ( RwLock :: new ( Some (
627- self . 0 . clone ( ) . check_broadcast_suitability (
628- min_fee_rate. map ( FeeRate :: from_sat_per_kwu) ,
629- |transaction| {
630- can_broadcast
631- . callback ( payjoin:: bitcoin:: consensus:: encode:: serialize ( transaction) )
632- . map_err ( |e| ImplementationError :: new ( e) . into ( ) )
633- } ,
634- ) ,
635- ) ) ) )
641+ ) -> Result < UncheckedOriginalPayloadTransition , PrimitiveError > {
642+ let min_fee_rate = validate_fee_rate_sat_per_kwu_opt ( min_fee_rate) ?;
643+ Ok ( UncheckedOriginalPayloadTransition ( Arc :: new ( RwLock :: new ( Some (
644+ self . 0 . clone ( ) . check_broadcast_suitability ( min_fee_rate, |transaction| {
645+ can_broadcast
646+ . callback ( payjoin:: bitcoin:: consensus:: encode:: serialize ( transaction) )
647+ . map_err ( |e| ImplementationError :: new ( e) . into ( ) )
648+ } ) ,
649+ ) ) ) ) )
636650 }
637651
638652 /// Call this method if the only way to initiate a Payjoin with this receiver
@@ -837,9 +851,11 @@ impl WantsOutputs {
837851 replacement_outputs : Vec < PlainTxOut > ,
838852 drain_script_pubkey : Vec < u8 > ,
839853 ) -> Result < WantsOutputs , OutputSubstitutionError > {
840- let replacement_outputs: Vec < payjoin:: bitcoin:: TxOut > =
841- replacement_outputs. into_iter ( ) . map ( Into :: into) . collect ( ) ;
842- let drain_script = payjoin:: bitcoin:: ScriptBuf :: from_bytes ( drain_script_pubkey) ;
854+ let replacement_outputs = replacement_outputs
855+ . into_iter ( )
856+ . map ( |output| output. into_core ( ) )
857+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
858+ let drain_script = validate_script_vec ( "drain_script_pubkey" , drain_script_pubkey, false ) ?;
843859 self . 0
844860 . clone ( )
845861 . replace_receiver_outputs ( replacement_outputs, & drain_script)
@@ -851,7 +867,8 @@ impl WantsOutputs {
851867 & self ,
852868 output_script_pubkey : Vec < u8 > ,
853869 ) -> Result < WantsOutputs , OutputSubstitutionError > {
854- let output_script = payjoin:: bitcoin:: ScriptBuf :: from_bytes ( output_script_pubkey) ;
870+ let output_script =
871+ validate_script_vec ( "output_script_pubkey" , output_script_pubkey, false ) ?;
855872 self . 0
856873 . clone ( )
857874 . substitute_receiver_script ( & output_script)
@@ -945,8 +962,8 @@ impl InputPair {
945962 expected_weight : Option < PlainWeight > ,
946963 ) -> Result < Self , InputPairError > {
947964 let txin = txin. into_core ( ) ?;
948- let psbtin = psbtin. into_core ( ) ;
949- let expected_weight = expected_weight. map ( Into :: into ) ;
965+ let psbtin = psbtin. into_core ( ) ? ;
966+ let expected_weight = expected_weight. map ( |weight| weight . into_core ( ) ) . transpose ( ) ? ;
950967 payjoin:: receive:: InputPair :: new ( txin, psbtin, expected_weight)
951968 . map ( Self )
952969 . map_err ( |err| InputPairError :: InvalidPsbtInput ( Arc :: new ( err. into ( ) ) ) )
@@ -1014,10 +1031,14 @@ impl WantsFeeRange {
10141031 & self ,
10151032 min_fee_rate_sat_per_vb : Option < u64 > ,
10161033 max_effective_fee_rate_sat_per_vb : Option < u64 > ,
1017- ) -> WantsFeeRangeTransition {
1018- WantsFeeRangeTransition ( Arc :: new ( RwLock :: new ( Some ( self . 0 . clone ( ) . apply_fee_range (
1019- min_fee_rate_sat_per_vb. and_then ( FeeRate :: from_sat_per_vb) ,
1020- max_effective_fee_rate_sat_per_vb. and_then ( FeeRate :: from_sat_per_vb) ,
1034+ ) -> Result < WantsFeeRangeTransition , PrimitiveError > {
1035+ let min_fee_rate_sat_per_vb = validate_fee_rate_sat_per_vb_opt ( min_fee_rate_sat_per_vb) ?;
1036+ let max_effective_fee_rate_sat_per_vb =
1037+ validate_fee_rate_sat_per_vb_opt ( max_effective_fee_rate_sat_per_vb) ?;
1038+ Ok ( WantsFeeRangeTransition ( Arc :: new ( RwLock :: new ( Some (
1039+ self . 0
1040+ . clone ( )
1041+ . apply_fee_range ( min_fee_rate_sat_per_vb, max_effective_fee_rate_sat_per_vb) ,
10211042 ) ) ) ) )
10221043 }
10231044}
0 commit comments