1#![doc = include_str!("../README.md")]
4use std::time::{SystemTime, UNIX_EPOCH};
10
11use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::Address, sol_types::eip712_domain};
12use thiserror::Error;
13
14mod error;
15pub mod manager;
16pub mod rav_request;
17pub mod receipt;
18pub mod signed_message;
19
20pub use error::Error;
21use error::Result;
22
23fn get_current_timestamp_u64_ns() -> Result<u64> {
24 Ok(SystemTime::now()
25 .duration_since(UNIX_EPOCH)
26 .map_err(|err| Error::InvalidSystemTime {
27 source_error_message: err.to_string(),
28 })?
29 .as_nanos() as u64)
30}
31
32#[derive(Debug, Clone, Copy)]
44pub enum TapVersion {
45 V1,
46 V2,
47}
48
49impl TapVersion {
50 pub fn as_str(&self) -> &'static str {
51 match self {
52 TapVersion::V1 => "1",
53 TapVersion::V2 => "2",
54 }
55 }
56}
57
58pub fn tap_eip712_domain(
64 chain_id: u64,
65 verifying_contract_address: Address,
66 version: TapVersion,
67) -> Eip712Domain {
68 let name = match version {
69 TapVersion::V1 => "TAP",
70 TapVersion::V2 => "GraphTallyCollector",
71 };
72
73 eip712_domain! {
74 name: name,
75 version: "1",
76 chain_id: chain_id,
77 verifying_contract: verifying_contract_address,
78 }
79}
80
81#[cfg(test)]
82mod tap_tests {
83 use std::str::FromStr;
84
85 use rstest::*;
86 use tap_graph::{Receipt, ReceiptAggregateVoucher};
87 use thegraph_core::alloy::{
88 dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner,
89 };
90
91 use crate::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion};
92
93 #[fixture]
94 fn keys() -> (PrivateKeySigner, Address) {
95 let wallet = PrivateKeySigner::random();
96 let address = wallet.address();
97
98 (wallet, address)
99 }
100
101 #[fixture]
102 fn allocation_ids() -> Vec<Address> {
103 vec![
104 Address::from_str("0xabababababababababababababababababababab").unwrap(),
105 Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(),
106 Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(),
107 Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(),
108 ]
109 }
110
111 #[fixture]
112 fn domain_separator() -> Eip712Domain {
113 tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1)
114 }
115
116 #[rstest]
117 #[case::basic_rav_test (vec![45,56,34,23])]
118 #[case::rav_from_zero_valued_receipts (vec![0,0,0,0])]
119 #[test]
120 fn signed_rav_is_valid_with_no_previous_rav(
121 keys: (PrivateKeySigner, Address),
122 allocation_ids: Vec<Address>,
123 domain_separator: Eip712Domain,
124 #[case] values: Vec<u128>,
125 ) {
126 let mut receipts = Vec::new();
128 for value in values {
129 receipts.push(
130 Eip712SignedMessage::new(
131 &domain_separator,
132 Receipt::new(allocation_ids[0], value).unwrap(),
133 &keys.0,
134 )
135 .unwrap(),
136 );
137 }
138
139 let rav = ReceiptAggregateVoucher::aggregate_receipts(allocation_ids[0], &receipts, None)
142 .unwrap();
143 let signed_rav = Eip712SignedMessage::new(&domain_separator, rav, &keys.0).unwrap();
144 assert!(signed_rav.recover_signer(&domain_separator).unwrap() == keys.1);
145 }
146
147 #[rstest]
148 #[case::basic_rav_test(vec![45,56,34,23])]
149 #[case::rav_from_zero_valued_receipts(vec![0,0,0,0])]
150 #[test]
151 fn signed_rav_is_valid_with_previous_rav(
152 keys: (PrivateKeySigner, Address),
153 allocation_ids: Vec<Address>,
154 domain_separator: Eip712Domain,
155 #[case] values: Vec<u128>,
156 ) {
157 let mut receipts = Vec::new();
159 for value in values {
160 receipts.push(
161 Eip712SignedMessage::new(
162 &domain_separator,
163 Receipt::new(allocation_ids[0], value).unwrap(),
164 &keys.0,
165 )
166 .unwrap(),
167 );
168 }
169
170 let prev_rav = ReceiptAggregateVoucher::aggregate_receipts(
172 allocation_ids[0],
173 &receipts[0..receipts.len() / 2],
174 None,
175 )
176 .unwrap();
177 let signed_prev_rav =
178 Eip712SignedMessage::new(&domain_separator, prev_rav, &keys.0).unwrap();
179
180 let rav = ReceiptAggregateVoucher::aggregate_receipts(
182 allocation_ids[0],
183 &receipts[receipts.len() / 2..receipts.len()],
184 Some(signed_prev_rav),
185 )
186 .unwrap();
187 let signed_rav = Eip712SignedMessage::new(&domain_separator, rav, &keys.0).unwrap();
188
189 assert!(signed_rav.recover_signer(&domain_separator).unwrap() == keys.1);
190 }
191}