@@ -30,7 +30,7 @@ use tap_core::{
30
30
31
31
#[ cfg( any( test, feature = "test" ) ) ]
32
32
use tap_core:: receipt:: checks:: CheckList ;
33
- use thegraph_core:: alloy:: primitives:: Address ;
33
+ use thegraph_core:: alloy:: { hex :: ToHexExt , primitives:: Address } ;
34
34
35
35
/// Trait for creating dummy aggregator clients in tests
36
36
#[ cfg( any( test, feature = "test" ) ) ]
@@ -72,10 +72,9 @@ struct TaskState<T: NetworkVersion> {
72
72
/// TAP Agent context for direct access to validation functions
73
73
tap_context : TapAgentContext < T > ,
74
74
/// Database connection pool
75
- #[ allow( dead_code) ]
76
75
pgpool : sqlx:: PgPool ,
77
76
/// Allocation/collection identifier for TAP operations
78
- #[ allow( dead_code) ]
77
+ #[ allow( dead_code) ] // Used in production code paths only
79
78
tap_allocation_id : Address ,
80
79
/// Sender address
81
80
#[ allow( dead_code) ]
@@ -637,7 +636,7 @@ where
637
636
}
638
637
}
639
638
} ;
640
-
639
+
641
640
#[ cfg( not( test) ) ]
642
641
let signature_valid = match state. tap_context . verify_signer ( signer_address) . await {
643
642
Ok ( is_valid) => is_valid,
@@ -822,18 +821,148 @@ where
822
821
tap_core:: receipt:: ReceiptWithState < tap_core:: receipt:: state:: Failed , TapReceipt > ,
823
822
> ,
824
823
) -> anyhow:: Result < ( ) > {
825
- // For now, just log the invalid receipts
826
- // TODO: Store in database table for tracking
827
- for receipt_with_state in invalid_receipts {
828
- // Access the receipt through the correct field name
824
+ if invalid_receipts. is_empty ( ) {
825
+ return Ok ( ( ) ) ;
826
+ }
827
+
828
+ // Collect data for batch insert
829
+ let mut signer_addresses = Vec :: new ( ) ;
830
+ let mut signatures = Vec :: new ( ) ;
831
+ let mut allocation_ids = Vec :: new ( ) ;
832
+ let mut timestamps = Vec :: new ( ) ;
833
+ let mut nonces = Vec :: new ( ) ;
834
+ let mut values = Vec :: new ( ) ;
835
+ let mut error_logs = Vec :: new ( ) ;
836
+
837
+ for receipt_with_state in & invalid_receipts {
829
838
let receipt = receipt_with_state. signed_receipt ( ) ;
830
- tracing:: warn!(
831
- allocation_id = ?state. allocation_id,
832
- receipt_value = receipt. value( ) ,
833
- receipt_timestamp = receipt. timestamp_ns( ) ,
834
- "Invalid receipt detected"
835
- ) ;
839
+ // Get the failed checks error message
840
+ let error_message = format ! ( "Failed validation: {receipt_with_state:?}" ) ;
841
+
842
+ // Extract receipt details based on version
843
+ match receipt {
844
+ TapReceipt :: V1 ( v1_receipt) => {
845
+ // For V1, we'll store the signer from the notification or use a placeholder
846
+ // In a real implementation, we'd recover the signer using domain separator
847
+ let signer = Address :: ZERO ; // TODO: Get actual signer from notification
848
+ signer_addresses. push ( signer. encode_hex ( ) ) ;
849
+ signatures. push ( v1_receipt. signature . as_bytes ( ) . to_vec ( ) ) ;
850
+ allocation_ids. push ( v1_receipt. message . allocation_id . encode_hex ( ) ) ;
851
+ timestamps. push ( v1_receipt. message . timestamp_ns as i64 ) ;
852
+ nonces. push ( v1_receipt. message . nonce as i64 ) ;
853
+ values. push ( v1_receipt. message . value as i64 ) ;
854
+ error_logs. push ( error_message. clone ( ) ) ;
855
+ }
856
+ TapReceipt :: V2 ( v2_receipt) => {
857
+ // For V2, we'll store the signer from the notification or use a placeholder
858
+ // In a real implementation, we'd recover the signer using domain separator
859
+ let signer = Address :: ZERO ; // TODO: Get actual signer from notification
860
+ signer_addresses. push ( signer. encode_hex ( ) ) ;
861
+ signatures. push ( v2_receipt. signature . as_bytes ( ) . to_vec ( ) ) ;
862
+ // For V2, we need the collection_id from the message
863
+ allocation_ids. push ( v2_receipt. message . collection_id . to_string ( ) ) ;
864
+ timestamps. push ( v2_receipt. message . timestamp_ns as i64 ) ;
865
+ nonces. push ( v2_receipt. message . nonce as i64 ) ;
866
+ values. push ( v2_receipt. message . value as i64 ) ;
867
+ error_logs. push ( error_message. clone ( ) ) ;
868
+ }
869
+ }
836
870
}
871
+
872
+ // Store in appropriate table based on version
873
+ match state. allocation_id {
874
+ AllocationId :: Legacy ( _) => {
875
+ // Store in scalar_tap_receipts_invalid table for V1 receipts
876
+ if !signer_addresses. is_empty ( ) {
877
+ let result = sqlx:: query!(
878
+ r#"
879
+ INSERT INTO scalar_tap_receipts_invalid
880
+ (signer_address, signature, allocation_id, timestamp_ns, nonce, value, error_log)
881
+ SELECT * FROM UNNEST($1::text[], $2::bytea[], $3::text[], $4::bigint[], $5::bigint[], $6::bigint[], $7::text[])
882
+ "# ,
883
+ & signer_addresses,
884
+ & signatures,
885
+ & allocation_ids,
886
+ & timestamps,
887
+ & nonces,
888
+ & values,
889
+ & error_logs
890
+ )
891
+ . execute ( & state. pgpool )
892
+ . await ;
893
+
894
+ match result {
895
+ Ok ( _) => {
896
+ tracing:: debug!(
897
+ allocation_id = ?state. allocation_id,
898
+ count = invalid_receipts. len( ) ,
899
+ "Stored invalid V1 receipts in scalar_tap_receipts_invalid"
900
+ ) ;
901
+ }
902
+ Err ( e) => {
903
+ tracing:: error!(
904
+ allocation_id = ?state. allocation_id,
905
+ error = %e,
906
+ "Failed to store invalid V1 receipts"
907
+ ) ;
908
+ return Err ( anyhow:: anyhow!(
909
+ "Database error storing invalid V1 receipts: {}" ,
910
+ e
911
+ ) ) ;
912
+ }
913
+ }
914
+ }
915
+ }
916
+ AllocationId :: Horizon ( _) => {
917
+ // Store in tap_horizon_receipts_invalid table for V2 receipts
918
+ if !signer_addresses. is_empty ( ) {
919
+ let result = sqlx:: query!(
920
+ r#"
921
+ INSERT INTO tap_horizon_receipts_invalid
922
+ (signer_address, signature, collection_id, timestamp_ns, nonce, value, error_log)
923
+ SELECT * FROM UNNEST($1::text[], $2::bytea[], $3::text[], $4::bigint[], $5::bigint[], $6::bigint[], $7::text[])
924
+ "# ,
925
+ & signer_addresses,
926
+ & signatures,
927
+ & allocation_ids,
928
+ & timestamps,
929
+ & nonces,
930
+ & values,
931
+ & error_logs
932
+ )
933
+ . execute ( & state. pgpool )
934
+ . await ;
935
+
936
+ match result {
937
+ Ok ( _) => {
938
+ tracing:: debug!(
939
+ allocation_id = ?state. allocation_id,
940
+ count = invalid_receipts. len( ) ,
941
+ "Stored invalid V2 receipts in tap_horizon_receipts_invalid"
942
+ ) ;
943
+ }
944
+ Err ( e) => {
945
+ tracing:: error!(
946
+ allocation_id = ?state. allocation_id,
947
+ error = %e,
948
+ "Failed to store invalid V2 receipts"
949
+ ) ;
950
+ return Err ( anyhow:: anyhow!(
951
+ "Database error storing invalid V2 receipts: {}" ,
952
+ e
953
+ ) ) ;
954
+ }
955
+ }
956
+ }
957
+ }
958
+ }
959
+
960
+ tracing:: info!(
961
+ allocation_id = ?state. allocation_id,
962
+ count = invalid_receipts. len( ) ,
963
+ "Stored invalid receipts in database"
964
+ ) ;
965
+
837
966
Ok ( ( ) )
838
967
}
839
968
0 commit comments