@@ -94,6 +94,17 @@ pub trait Rpc {
9494 receipts : Vec < Eip712SignedMessage < Receipt > > ,
9595 previous_rav : Option < Eip712SignedMessage < ReceiptAggregateVoucher > > ,
9696 ) -> JsonRpcResult < Eip712SignedMessage < ReceiptAggregateVoucher > > ;
97+
98+ /// Aggregates the given v2 receipts into a v2 receipt aggregate voucher.
99+ /// Uses the Horizon protocol for collection-based aggregation.
100+ #[ cfg( feature = "v2" ) ]
101+ #[ method( name = "aggregate_receipts_v2" ) ]
102+ fn aggregate_receipts_v2 (
103+ & self ,
104+ api_version : String ,
105+ receipts : Vec < Eip712SignedMessage < tap_graph:: v2:: Receipt > > ,
106+ previous_rav : Option < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > ,
107+ ) -> JsonRpcResult < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > ;
97108}
98109
99110#[ derive( Clone ) ]
@@ -141,6 +152,8 @@ fn aggregate_receipts_(
141152 receipts : Vec < Eip712SignedMessage < Receipt > > ,
142153 previous_rav : Option < Eip712SignedMessage < ReceiptAggregateVoucher > > ,
143154) -> JsonRpcResult < Eip712SignedMessage < ReceiptAggregateVoucher > > {
155+ use crate :: receipt_classifier:: validate_v1_receipt_batch;
156+
144157 // Return an error if the API version is not supported.
145158 let api_version = match parse_api_version ( api_version. as_str ( ) ) {
146159 Ok ( v) => v,
@@ -157,16 +170,85 @@ fn aggregate_receipts_(
157170 DEPRECATION_WARNING_COUNT . inc ( ) ;
158171 }
159172
160- let res = match api_version {
161- TapRpcApiVersion :: V0_0 => aggregator:: v1:: check_and_aggregate_receipts (
162- domain_separator,
163- & receipts,
164- previous_rav,
165- wallet,
166- accepted_addresses,
167- ) ,
173+ // This endpoint handles v1 receipts for legacy aggregation
174+ // V2 receipts are handled through the aggregate_receipts_v2 endpoint
175+ if let Err ( e) = validate_v1_receipt_batch ( & receipts) {
176+ return Err ( jsonrpsee:: types:: ErrorObject :: owned (
177+ JsonRpcErrorCode :: Aggregation as i32 ,
178+ e. to_string ( ) ,
179+ None :: < ( ) > ,
180+ ) ) ;
181+ }
182+
183+ log:: debug!( "Processing V1 receipts" ) ;
184+
185+ // Execute v1 aggregation
186+ let res = aggregator:: v1:: check_and_aggregate_receipts (
187+ domain_separator,
188+ & receipts,
189+ previous_rav,
190+ wallet,
191+ accepted_addresses,
192+ ) ;
193+
194+ // Handle aggregation error
195+ match res {
196+ Ok ( res) => Ok ( JsonRpcResponse :: warn ( res, warnings) ) ,
197+ Err ( e) => Err ( jsonrpsee:: types:: ErrorObject :: owned (
198+ JsonRpcErrorCode :: Aggregation as i32 ,
199+ e. to_string ( ) ,
200+ None :: < ( ) > ,
201+ ) ) ,
202+ }
203+ }
204+
205+ #[ cfg( feature = "v2" ) ]
206+ fn aggregate_receipts_v2_ (
207+ api_version : String ,
208+ wallet : & PrivateKeySigner ,
209+ accepted_addresses : & HashSet < Address > ,
210+ domain_separator : & Eip712Domain ,
211+ receipts : Vec < Eip712SignedMessage < tap_graph:: v2:: Receipt > > ,
212+ previous_rav : Option < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > ,
213+ ) -> JsonRpcResult < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > {
214+ use crate :: receipt_classifier:: validate_v2_receipt_batch;
215+
216+ // Return an error if the API version is not supported.
217+ let api_version = match parse_api_version ( api_version. as_str ( ) ) {
218+ Ok ( v) => v,
219+ Err ( e) => {
220+ VERSION_ERROR_COUNT . inc ( ) ;
221+ return Err ( e) ;
222+ }
168223 } ;
169224
225+ // Add a warning if the API version is to be deprecated.
226+ let mut warnings: Vec < JsonRpcWarning > = Vec :: new ( ) ;
227+ if let Some ( w) = check_api_version_deprecation ( & api_version) {
228+ warnings. push ( w) ;
229+ DEPRECATION_WARNING_COUNT . inc ( ) ;
230+ }
231+
232+ // Validate v2 receipt batch for horizon processing
233+ if let Err ( e) = validate_v2_receipt_batch ( & receipts) {
234+ return Err ( jsonrpsee:: types:: ErrorObject :: owned (
235+ JsonRpcErrorCode :: Aggregation as i32 ,
236+ e. to_string ( ) ,
237+ None :: < ( ) > ,
238+ ) ) ;
239+ }
240+
241+ log:: debug!( "Processing V2 receipts with Horizon protocol" ) ;
242+
243+ // Execute v2 aggregation
244+ let res = aggregator:: v2:: check_and_aggregate_receipts (
245+ domain_separator,
246+ & receipts,
247+ previous_rav,
248+ wallet,
249+ accepted_addresses,
250+ ) ;
251+
170252 // Handle aggregation error
171253 match res {
172254 Ok ( res) => Ok ( JsonRpcResponse :: warn ( res, warnings) ) ,
@@ -337,6 +419,47 @@ impl RpcServer for RpcImpl {
337419 }
338420 }
339421 }
422+
423+ #[ cfg( feature = "v2" ) ]
424+ fn aggregate_receipts_v2 (
425+ & self ,
426+ api_version : String ,
427+ receipts : Vec < Eip712SignedMessage < tap_graph:: v2:: Receipt > > ,
428+ previous_rav : Option < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > ,
429+ ) -> JsonRpcResult < Eip712SignedMessage < tap_graph:: v2:: ReceiptAggregateVoucher > > {
430+ // Values for Prometheus metrics
431+ let receipts_grt: u128 = receipts. iter ( ) . map ( |r| r. message . value ) . sum ( ) ;
432+ let receipts_count: u64 = receipts. len ( ) as u64 ;
433+
434+ match aggregate_receipts_v2_ (
435+ api_version,
436+ & self . wallet ,
437+ & self . accepted_addresses ,
438+ & self . domain_separator ,
439+ receipts,
440+ previous_rav,
441+ ) {
442+ Ok ( res) => {
443+ TOTAL_GRT_AGGREGATED . inc_by ( receipts_grt as f64 ) ;
444+ TOTAL_AGGREGATED_RECEIPTS . inc_by ( receipts_count) ;
445+ AGGREGATION_SUCCESS_COUNTER . inc ( ) ;
446+ if let Some ( kafka) = & self . kafka {
447+ // V2 RAVs use collectionId instead of allocationId
448+ produce_kafka_records (
449+ kafka,
450+ & self . wallet . address ( ) ,
451+ & res. data . message . collectionId ,
452+ res. data . message . valueAggregate ,
453+ ) ;
454+ }
455+ Ok ( res)
456+ }
457+ Err ( e) => {
458+ AGGREGATION_FAILURE_COUNTER . inc ( ) ;
459+ Err ( e)
460+ }
461+ }
462+ }
340463}
341464
342465#[ allow( clippy:: too_many_arguments) ]
0 commit comments