@@ -6,16 +6,9 @@ use bdk_tx::{
66} ;
77use bitcoin:: {
88 consensus:: encode:: serialize_hex, key:: Secp256k1 , psbt, secp256k1:: All , Amount , FeeRate , Psbt ,
9- Sequence , TxIn ,
9+ Sequence , Transaction , TxIn ,
1010} ;
1111use miniscript:: { Descriptor , DescriptorPublicKey } ;
12- use std:: str:: FromStr ;
13- use url:: Url ;
14-
15- mod common;
16-
17- use common:: Wallet ;
18-
1912use payjoin:: {
2013 io:: fetch_ohttp_keys,
2114 persist:: { NoopSessionPersister , OptionalTransitionOutcome } ,
@@ -26,6 +19,12 @@ use payjoin::{
2619 send:: v2:: SenderBuilder ,
2720 ImplementationError , PjUri , Request , Uri , UriExt ,
2821} ;
22+ use std:: str:: FromStr ;
23+ use url:: Url ;
24+
25+ mod common;
26+
27+ use common:: Wallet ;
2928
3029#[ tokio:: main]
3130async fn main ( ) -> anyhow:: Result < ( ) > {
@@ -34,7 +33,7 @@ async fn main() -> anyhow::Result<()> {
3433 let payjoin_directory = Url :: parse ( "https://payjo.in" ) ?;
3534 let ohttp_keys = fetch_ohttp_keys ( ohttp_relay. as_str ( ) , payjoin_directory. as_str ( ) ) . await ?;
3635
37- let ( mut receiver_wallet, receiver_signer, env, sender_wallet, sender_signer, sender_desc) =
36+ let ( mut receiver_wallet, receiver_signer, env, mut sender_wallet, sender_signer, sender_desc) =
3837 setup_wallets ( ) ?;
3938 let recv_persister = NoopSessionPersister :: default ( ) ;
4039 let send_persister = NoopSessionPersister :: default ( ) ;
@@ -63,14 +62,14 @@ async fn main() -> anyhow::Result<()> {
6362 let response_body = session
6463 . process_response ( response. bytes ( ) . await ?. to_vec ( ) . as_slice ( ) , ctx)
6564 . save ( & recv_persister) ?;
66- // No proposal yet since sender has not responded
65+
6766 let session = if let OptionalTransitionOutcome :: Stasis ( current_state) = response_body {
6867 current_state
6968 } else {
7069 panic ! ( "Should still be in initialized state" )
7170 } ;
7271
73- // SENDER PARSE THE PAYJOIN URI, BUILD PSBT AND SEND PAYJOIN REQUEST
72+ // SENDER PARSE THE PAYJOIN URI, BUILD PSBT
7473 let pj_uri = Uri :: from_str ( & session. pj_uri ( ) . to_string ( ) )
7574 . map_err ( |e| anyhow:: anyhow!( "{e}" ) ) ?
7675 . assume_checked ( )
@@ -85,36 +84,25 @@ async fn main() -> anyhow::Result<()> {
8584 & sender_desc,
8685 & secp,
8786 ) ?;
88-
89- dbg ! ( & psbt. clone( ) . extract_tx( ) ) ;
90-
9187 let req_ctx = SenderBuilder :: new ( psbt, pj_uri)
9288 . build_recommended ( FeeRate :: BROADCAST_MIN ) ?
9389 . save ( & send_persister) ?;
9490
95- let (
96- Request {
97- url,
98- body,
99- content_type,
100- ..
101- } ,
102- send_ctx,
103- ) = req_ctx. create_v2_post_request ( ohttp_relay. as_str ( ) ) ?;
91+ // SENDER SENDS PAYJOIN REQUEST
92+ let ( req, send_ctx) = req_ctx. create_v2_post_request ( ohttp_relay. as_str ( ) ) ?;
10493 let response = http
105- . post ( url)
106- . header ( "Content-Type" , content_type)
107- . body ( body)
94+ . post ( req . url )
95+ . header ( "Content-Type" , req . content_type )
96+ . body ( req . body )
10897 . send ( )
10998 . await ?;
110- println ! ( "Response: {response:?}" ) ;
11199
112100 assert ! (
113101 response. status( ) . is_success( ) ,
114102 "error response: {}" ,
115103 response. status( )
116104 ) ;
117- let _send_ctx = req_ctx
105+ let send_ctx = req_ctx
118106 . process_response ( & response. bytes ( ) . await ?, send_ctx)
119107 . save ( & send_persister) ?;
120108
@@ -135,12 +123,123 @@ async fn main() -> anyhow::Result<()> {
135123 _ => return Err ( anyhow:: anyhow!( "Expected a payjoin proposal!" ) ) ,
136124 } ;
137125
138- let _payjoin_proposal =
126+ let payjoin_proposal =
139127 handle_directory_proposal ( & receiver_wallet, & env, proposal, & receiver_signer, & secp) ?;
128+ let ( req, _) = payjoin_proposal. create_post_request ( ohttp_relay. as_str ( ) ) ?;
129+
130+ let _response = http
131+ . post ( req. url )
132+ . header ( "Content-Type" , req. content_type )
133+ . body ( req. body )
134+ . send ( )
135+ . await ?;
136+
137+ // SENDER SIGNS, FINALIZES AND BROADCASTS THE PAYJOIN TRANSACTION
138+ let (
139+ Request {
140+ url,
141+ body,
142+ content_type,
143+ ..
144+ } ,
145+ ohttp_ctx,
146+ ) = send_ctx. create_poll_request ( ohttp_relay. as_str ( ) ) ?;
147+ let response = http
148+ . post ( url)
149+ . header ( "Content-Type" , content_type)
150+ . body ( body)
151+ . send ( )
152+ . await ?;
153+ println ! ( "Response: {:#?}" , & response) ;
154+ let response = send_ctx
155+ . process_response ( & response. bytes ( ) . await ?, ohttp_ctx)
156+ . save ( & send_persister)
157+ . expect ( "psbt should exist" ) ;
158+
159+ let checked_payjoin_proposal_psbt = if let OptionalTransitionOutcome :: Progress ( psbt) = response
160+ {
161+ psbt
162+ } else {
163+ panic ! ( "psbt should exist" ) ;
164+ } ;
165+ let network_fees = checked_payjoin_proposal_psbt. fee ( ) ?;
166+
167+ let payjoin_tx = extract_pj_tx (
168+ & sender_wallet,
169+ checked_payjoin_proposal_psbt,
170+ & sender_signer,
171+ & secp,
172+ ) ?;
173+ let txid = env. rpc_client ( ) . send_raw_transaction ( & payjoin_tx) ?;
174+ println ! ( "Sent: {}" , txid) ;
175+
176+ assert_eq ! ( payjoin_tx. input. len( ) , 2 ) ;
177+ assert_eq ! ( payjoin_tx. output. len( ) , 2 ) ; // A CHANGE OUTPUT IS EXPECTED
178+
179+ // MINE A BLOCK TO CONFIRM THE TRANSACTION
180+ env. mine_blocks ( 1 , None ) ?;
181+ receiver_wallet. sync ( & env) ?;
182+ sender_wallet. sync ( & env) ?;
183+
184+ // RECEIVER WALLET SHOULD NOW SEE THE TRANSACTION
185+ if let Some ( tx_node) = receiver_wallet. graph . graph ( ) . get_tx ( txid) {
186+ let tx = tx_node. as_ref ( ) ;
187+ dbg ! ( tx) ;
188+ } else {
189+ println ! ( "Transaction not in receiver's graph yet" ) ;
190+ }
191+
192+ assert_eq ! (
193+ sender_wallet. balance( ) . confirmed,
194+ Amount :: from_btc( 45.0 ) ? - network_fees
195+ ) ;
196+ assert_eq ! ( receiver_wallet. balance( ) . confirmed, Amount :: from_btc( 55.0 ) ?) ;
140197
141198 Ok ( ( ) )
142199}
143200
201+ fn extract_pj_tx (
202+ wallet : & Wallet ,
203+ mut psbt : Psbt ,
204+ signer : & Signer ,
205+ secp : & Secp256k1 < All > ,
206+ ) -> anyhow:: Result < Transaction > {
207+ let assets = wallet. assets ( ) ;
208+ let mut plans = Vec :: new ( ) ;
209+
210+ for ( index, input) in psbt. unsigned_tx . input . iter ( ) . enumerate ( ) {
211+ let outpoint = input. previous_output ;
212+
213+ if let Some ( plan) = wallet. plan_of_output ( outpoint, & assets) {
214+ let psbt_input = & mut psbt. inputs [ index] ;
215+
216+ // Only update if not already finalized
217+ if psbt_input. final_script_sig . is_none ( ) && psbt_input. final_script_witness . is_none ( ) {
218+ plan. update_psbt_input ( psbt_input) ;
219+
220+ if let Some ( prev_tx) = wallet. graph . graph ( ) . get_tx ( outpoint. txid ) {
221+ psbt_input. non_witness_utxo = Some ( prev_tx. as_ref ( ) . clone ( ) ) ;
222+ if let Some ( txout) = prev_tx. output . get ( outpoint. vout as usize ) {
223+ psbt_input. witness_utxo = Some ( txout. clone ( ) ) ;
224+ }
225+ }
226+ }
227+
228+ plans. push ( ( outpoint, plan) ) ;
229+ }
230+ }
231+
232+ let finalizer = Finalizer :: new ( plans) ;
233+ let _ = psbt. sign ( signer, secp) ;
234+ let finalize_map = finalizer. finalize ( & mut psbt) ;
235+
236+ if !finalize_map. is_finalized ( ) {
237+ return Err ( anyhow ! ( "Failed to finalize PSBT: {finalize_map:?}" ) ) ;
238+ }
239+
240+ Ok ( psbt. extract_tx ( ) ?)
241+ }
242+
144243fn handle_directory_proposal (
145244 wallet : & Wallet ,
146245 env : & TestEnv ,
@@ -202,16 +301,20 @@ fn handle_directory_proposal(
202301
203302 let payjoin = payjoin
204303 . apply_fee_range (
205- Some ( FeeRate :: from_sat_per_vb_unchecked ( 1 ) ) ,
304+ Some ( FeeRate :: BROADCAST_MIN ) ,
206305 Some ( FeeRate :: from_sat_per_vb_unchecked ( 2 ) ) ,
207306 )
208307 . save ( & noop_persister) ?;
209308
210- //Sign anf finalize proposal PSBT
309+ // Sign and finalize proposal PSBT
211310 let payjoin = payjoin
212311 . finalize_proposal ( |psbt : & Psbt | {
213- finalize_psbt ( psbt, wallet, signer, secp)
214- . map_err ( |e| ImplementationError :: from ( e. to_string ( ) . as_str ( ) ) )
312+ let mut psbt = psbt. clone ( ) ;
313+
314+ finalize_psbt ( & mut psbt, wallet, signer, secp)
315+ . map_err ( |e| ImplementationError :: from ( e. to_string ( ) . as_str ( ) ) ) ?;
316+
317+ Ok ( psbt)
215318 } )
216319 . save ( & noop_persister) ?;
217320
@@ -229,8 +332,8 @@ fn build_psbt(
229332 let ( tip_height, tip_time) = wallet. tip_info ( env. rpc_client ( ) ) ?;
230333
231334 let target_amount = Amount :: from_btc ( 5.0 ) ?;
232- let target_feerate = FeeRate :: from_sat_per_vb_unchecked ( 5 ) ;
233- // let longterm_feerate = FeeRate::from_sat_per_vb_unchecked(1);
335+ let target_feerate = FeeRate :: from_sat_per_vb_unchecked ( 2 ) ;
336+ let longterm_feerate = FeeRate :: from_sat_per_vb_unchecked ( 1 ) ;
234337
235338 let target_outputs = vec ! [ Output :: with_script(
236339 pj_uri. address. script_pubkey( ) ,
@@ -242,12 +345,16 @@ fn build_psbt(
242345 . regroup ( group_by_spk ( ) )
243346 . filter ( filter_unspendable_now ( tip_height, tip_time) )
244347 . into_selection (
245- |selector| selector. select_until_target_met ( ) ,
348+ |selector| -> anyhow:: Result < ( ) > {
349+ selector. select_all ( ) ;
350+ Ok ( ( ) )
351+ } ,
352+ // selection_algorithm_lowest_fee_bnb(longterm_feerate, 100_000),
246353 SelectorParams :: new (
247354 target_feerate,
248355 target_outputs,
249356 ScriptSource :: Descriptor ( Box :: new ( desc. at_derivation_index ( 0 ) ?) ) ,
250- ChangePolicyType :: NoDust ,
357+ ChangePolicyType :: NoDustAndLeastWaste { longterm_feerate } ,
251358 wallet. change_weight ( ) ,
252359 ) ,
253360 ) ?;
@@ -269,13 +376,11 @@ fn build_psbt(
269376}
270377
271378fn finalize_psbt (
272- psbt : & Psbt ,
379+ psbt : & mut Psbt ,
273380 wallet : & Wallet ,
274381 signer : & Signer ,
275382 secp : & Secp256k1 < All > ,
276- ) -> anyhow:: Result < Psbt > {
277- let mut psbt = psbt. clone ( ) ;
278-
383+ ) -> anyhow:: Result < ( ) > {
279384 let assets = wallet. assets ( ) ;
280385 let mut plans = Vec :: new ( ) ;
281386
@@ -288,13 +393,9 @@ fn finalize_psbt(
288393
289394 let finalizer = Finalizer :: new ( plans) ;
290395 let _ = psbt. sign ( signer, secp) ;
291- let _finalize_map = finalizer. finalize ( & mut psbt) ;
396+ finalizer. finalize ( psbt) ;
292397
293- // if !finalize_map.is_finalized() {
294- // return Err(anyhow!("Failed to finalize PSBT: {:?}", res));
295- // }
296-
297- Ok ( psbt)
398+ Ok ( ( ) )
298399}
299400
300401fn select_inputs (
@@ -303,34 +404,46 @@ fn select_inputs(
303404 env : & TestEnv ,
304405) -> anyhow:: Result < Vec < InputPair > > {
305406 let ( tip_height, tip_time) = wallet. tip_info ( env. rpc_client ( ) ) ?;
407+ let assets = wallet. assets ( ) ;
306408
307409 let candidates = wallet
308410 . all_candidates ( )
309411 . filter ( |input| input. is_spendable_now ( tip_height, tip_time) ) ;
310412
311413 let inputs = candidates
312414 . inputs ( )
313- . map ( |input| {
415+ . filter_map ( |input| {
416+ let outpoint = input. prev_outpoint ( ) ;
417+ let plan = wallet. plan_of_output ( outpoint, & assets) ?;
418+
314419 let txin = TxIn {
315- previous_output : input . prev_outpoint ( ) ,
316- sequence : input. sequence ( ) . unwrap_or ( Sequence :: ENABLE_RBF_NO_LOCKTIME ) ,
420+ previous_output : outpoint ,
421+ // sequence: input.sequence().unwrap_or(Sequence::ENABLE_RBF_NO_LOCKTIME),
317422 ..Default :: default ( )
318423 } ;
319424
320- let psbt_input = psbt:: Input {
425+ let mut psbt_input = psbt:: Input {
321426 witness_utxo : Some ( input. prev_txout ( ) . clone ( ) ) ,
427+ // non_witness_utxo: input.prev_tx().cloned(),
322428 ..Default :: default ( )
323429 } ;
324- InputPair :: new ( txin, psbt_input, None )
325- . map_err ( |e| anyhow ! ( "Failed to create InputPair: {e:?}" ) )
430+
431+ // Update PSBT input with plan information
432+ plan. update_psbt_input ( & mut psbt_input) ;
433+
434+ InputPair :: new ( txin, psbt_input, None ) . ok ( )
326435 } )
327- . collect :: < Result < Vec < _ > , _ > > ( ) ?;
436+ . collect :: < Vec < _ > > ( ) ;
437+
438+ if inputs. is_empty ( ) {
439+ return Err ( anyhow ! ( "No suitable inputs available" ) ) ;
440+ }
328441
329- let selected_inputs = payjoin
442+ let selected_input = payjoin
330443 . try_preserving_privacy ( inputs)
331444 . map_err ( |e| anyhow ! ( "Failed to make privacy preserving selection: {e:?}" ) ) ?;
332445
333- Ok ( vec ! [ selected_inputs ] )
446+ Ok ( vec ! [ selected_input ] )
334447}
335448
336449// SETUP WALLET
0 commit comments