@@ -2,9 +2,7 @@ use anyhow::Result;
22use chrono:: { DateTime , Utc } ;
33use offer:: GolemBaseOffer ;
44use std:: collections:: HashSet ;
5- use std:: fs;
6- use std:: path:: Path ;
7- use std:: str:: FromStr ;
5+ use std:: env;
86use std:: sync:: { Arc , Mutex } ;
97use std:: time:: Duration ;
108use tokio:: task:: JoinHandle ;
@@ -26,16 +24,13 @@ use crate::db::model::{Offer as ModelOffer, SubscriptionId};
2624use crate :: identity:: { IdentityApi , IdentityError , YagnaIdSigner } ;
2725use crate :: protocol:: discovery:: error:: * ;
2826use crate :: protocol:: discovery:: message:: * ;
29- use crate :: testing:: MatcherError ;
3027use arkiv_sdk:: client:: ArkivClient ;
3128use arkiv_sdk:: entity:: Create ;
3229use arkiv_sdk:: events:: Event ;
3330use arkiv_sdk:: rpc:: { QueryOptions , SearchResult } ;
3431use arkiv_sdk:: signers:: TransactionSigner ;
3532use arkiv_sdk:: { Address , Hash } ;
36- use glob:: glob;
3733use rand:: { thread_rng, Rng } ;
38- use serde_json:: Value ;
3934
4035const ARKIV_CALLER : & str = "Arkiv" ;
4136
@@ -311,74 +306,87 @@ impl Discovery {
311306 Ok ( ( ) )
312307 }
313308
314- async fn read_offer_from_file ( path : & Path ) -> anyhow:: Result < ModelOffer > {
315- // Read the file
316- let data = fs:: read_to_string ( path) ?;
309+ async fn offers_events_loop ( & self , _starting_block : u64 ) -> anyhow:: Result < ( ) > {
310+ let default_identity = self
311+ . inner
312+ . identity
313+ . default_identity ( )
314+ . await
315+ . expect ( "Failed to get default identity" ) ;
317316
318- let val: Value = serde_json:: from_str ( & data) ?;
317+ loop {
318+ // Wait for either Ctrl+C or timeout to continue loop
319+ tokio:: select! {
320+ _ = tokio:: signal:: ctrl_c( ) => {
321+ log:: info!( "Received Ctrl+C, shutting down offers events loop" ) ;
322+ break ;
323+ }
324+ _ = tokio:: time:: sleep( Duration :: from_secs( 10 ) ) => { }
325+ }
319326
320- let hash = Hash :: from_str (
321- val. as_object ( )
322- . ok_or_else ( || {
323- MatcherError :: GolemBaseOfferError (
324- "Offer serialized to non-object JSON" . to_string ( ) ,
327+ let matcher = env:: var ( "YAGNA_MARKET_MATCHER_URL" ) ;
328+ if let Ok ( matcher) = matcher {
329+ let client = reqwest:: Client :: new ( ) ;
330+ let res = client
331+ . post ( & ( matcher + "/requestor/demand/take-from-queue" ) )
332+ . header ( "Content-Type" , "application/json" )
333+ . body (
334+ "{
335+ \" demandId\" "
336+ . to_string ( )
337+ + ": \" "
338+ + & default_identity. to_string ( )
339+ + "\" }" ,
325340 )
326- } ) ?
327- . get ( "id" )
328- . ok_or_else ( || {
329- MatcherError :: GolemBaseOfferError ( "Offer JSON missing 'id' field" . to_string ( ) )
330- } ) ?
331- . as_str ( )
332- . ok_or_else ( || {
333- MatcherError :: GolemBaseOfferError (
334- "Offer 'id' field is not a string" . to_string ( ) ,
335- )
336- } ) ?,
337- )
338- . map_err ( |e| {
339- MatcherError :: GolemBaseOfferError ( format ! ( "Failed to hash from id field: {}" , e) )
340- } ) ?;
341-
342- let offer: GolemBaseOffer = serde_json:: from_str ( & data) ?;
343-
344- let offer = offer. into_model_offer ( hash) ?;
345-
346- Ok ( offer)
347- }
348-
349- async fn offers_events_loop ( & self , _starting_block : u64 ) -> anyhow:: Result < ( ) > {
350- loop {
351- //read file from offer.json
352- if let Ok ( paths) = glob ( "incoming-offer-*.json" ) {
353- #[ allow( clippy:: manual_flatten) ]
354- for entry in paths {
355- if let Ok ( path) = entry {
356- match Self :: read_offer_from_file ( & path) . await {
357- Ok ( offer) => {
358- log:: info!( "Registering incoming offer from file: {:?}" , path) ;
359- self . register_incoming_offers ( vec ! [ offer] ) . await ?;
341+ . send ( )
342+ . await ;
343+ match res {
344+ Ok ( response) => {
345+ if response. status ( ) . is_success ( ) {
346+ let data = response. text ( ) . await . unwrap_or_default ( ) ;
347+
348+ let offer = serde_json:: from_str :: < ModelOffer > ( & data) ;
349+ match offer {
350+ Ok ( offer) => {
351+ log:: info!(
352+ "Registering incoming offer from matcher: {:?}" ,
353+ offer. id
354+ ) ;
355+ self . register_incoming_offers ( vec ! [ offer] ) . await ?;
356+ }
357+ Err ( e) => {
358+ log:: error!(
359+ "Failed to parse offer from matcher response: {}" ,
360+ e
361+ ) ;
362+ }
360363 }
361- Err ( e) => {
364+ } else if response. status ( ) == reqwest:: StatusCode :: NOT_FOUND {
365+ let response_text = response. text ( ) . await . unwrap_or_default ( ) ;
366+ if response_text. is_empty ( ) {
362367 log:: error!(
363- "Failed to read and register offer from file {:?}: {}" ,
364- path,
365- e
368+ "Failed to take offer due to other error (no body): {}" ,
369+ default_identity
370+ ) ;
371+ } else {
372+ log:: info!(
373+ "No offers available in matcher queue for demand {}" ,
374+ response_text
366375 ) ;
367376 }
377+ } else {
378+ log:: error!(
379+ "Failed to take offer, other status: {}" ,
380+ response. status( )
381+ ) ;
368382 }
369- fs :: remove_file ( path ) . map_err ( |e| {
370- anyhow :: anyhow! ( "Failed to remove invalid offer file: {}" , e )
371- } ) ? ;
383+ }
384+ Err ( e ) => {
385+ log :: error! ( "Failed to take offer due to other error: {}" , e ) ;
372386 }
373387 }
374- }
375- // Wait for either Ctrl+C or timeout to continue loop
376- tokio:: select! {
377- _ = tokio:: signal:: ctrl_c( ) => {
378- log:: info!( "Received Ctrl+C, shutting down offers events loop" ) ;
379- break ;
380- }
381- _ = tokio:: time:: sleep( Duration :: from_secs( 1 ) ) => { }
388+ } else {
389+ log:: warn!( "YAGNA_MARKET_MATCHER_URL not set, skipping demand notification" ) ;
382390 }
383391 }
384392 Ok ( ( ) )
@@ -402,7 +410,7 @@ impl Discovery {
402410 DiscoveryInitError :: GolemBaseInitFailed ( format ! ( "Failed to register offers: {}" , e) )
403411 } ) ?;
404412
405- let handle = tokio:: spawn ( async move {
413+ let handle = tokio:: task :: spawn_local ( async move {
406414 discovery
407415 . offers_events_loop ( starting_block)
408416 . await
0 commit comments