Skip to content

Commit 0f5bb43

Browse files
committed
fix(eip712): enhance receipt uniqueness verification
Signed-off-by: Joseph Livesey <[email protected]>
1 parent d47b17a commit 0f5bb43

File tree

6 files changed

+68
-43
lines changed

6 files changed

+68
-43
lines changed

tap_core/src/manager/tap_manager.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,13 @@ where
138138
let mut failed_receipts = vec![];
139139

140140
// check for timestamp
141-
let (checking_receipts, already_failed) =
142-
TimestampCheck(min_timestamp_ns).check_batch(checking_receipts);
141+
let (checking_receipts, already_failed) = TimestampCheck(min_timestamp_ns)
142+
.check_batch(&self.domain_separator, checking_receipts)?;
143143
failed_receipts.extend(already_failed);
144144

145145
// check for uniqueness
146-
let (checking_receipts, already_failed) = UniqueCheck.check_batch(checking_receipts);
146+
let (checking_receipts, already_failed) =
147+
UniqueCheck.check_batch(&self.domain_separator, checking_receipts)?;
147148
failed_receipts.extend(already_failed);
148149

149150
for receipt in checking_receipts.into_iter() {

tap_core/tests/manager_test.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ async fn manager_verify_and_store_varying_initial_checks(
139139
&signer,
140140
)
141141
.unwrap();
142-
let query_id = signed_receipt.unique_hash();
142+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
143143
query_appraisals.write().unwrap().insert(query_id, value);
144144
escrow_storage
145145
.write()
@@ -182,7 +182,7 @@ async fn manager_create_rav_request_all_valid_receipts(
182182
&signer,
183183
)
184184
.unwrap();
185-
let query_id = signed_receipt.unique_hash();
185+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
186186
stored_signed_receipts.push(signed_receipt.clone());
187187
query_appraisals.write().unwrap().insert(query_id, value);
188188
assert!(manager
@@ -277,7 +277,7 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts(
277277
&signer,
278278
)
279279
.unwrap();
280-
let query_id = signed_receipt.unique_hash();
280+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
281281
stored_signed_receipts.push(signed_receipt.clone());
282282
query_appraisals.write().unwrap().insert(query_id, value);
283283
assert!(manager
@@ -321,7 +321,7 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts(
321321
)
322322
.unwrap();
323323

324-
let query_id = signed_receipt.unique_hash();
324+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
325325
stored_signed_receipts.push(signed_receipt.clone());
326326
query_appraisals.write().unwrap().insert(query_id, value);
327327
assert!(manager
@@ -389,7 +389,7 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_tim
389389
receipt.timestamp_ns = starting_min_timestamp + query_id + 1;
390390
let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap();
391391

392-
let query_id = signed_receipt.unique_hash();
392+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
393393
stored_signed_receipts.push(signed_receipt.clone());
394394
query_appraisals.write().unwrap().insert(query_id, value);
395395
assert!(manager
@@ -436,7 +436,7 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_tim
436436
let mut receipt = Receipt::new(allocation_ids[0], value).unwrap();
437437
receipt.timestamp_ns = starting_min_timestamp + query_id + 1;
438438
let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap();
439-
let query_id = signed_receipt.unique_hash();
439+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
440440
stored_signed_receipts.push(signed_receipt.clone());
441441
query_appraisals.write().unwrap().insert(query_id, value);
442442
assert!(manager

tap_core/tests/received_receipt_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async fn partial_then_full_check_valid_receipt(
113113
)
114114
.unwrap();
115115

116-
let query_id = signed_receipt.unique_hash();
116+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
117117

118118
// add escrow for sender
119119
escrow_storage
@@ -156,7 +156,7 @@ async fn partial_then_finalize_valid_receipt(
156156
&signer,
157157
)
158158
.unwrap();
159-
let query_id = signed_receipt.unique_hash();
159+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
160160

161161
// add escrow for sender
162162
escrow_storage
@@ -203,7 +203,7 @@ async fn standard_lifetime_valid_receipt(
203203
)
204204
.unwrap();
205205

206-
let query_id = signed_receipt.unique_hash();
206+
let query_id = signed_receipt.unique_hash(&domain_separator).unwrap();
207207

208208
// add escrow for sender
209209
escrow_storage

tap_eip712_message/src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
use serde::{Deserialize, Serialize};
2626
use thegraph_core::alloy::{
2727
dyn_abi::Eip712Domain,
28-
primitives::{Address, Signature},
28+
primitives::{keccak256, Address, Signature},
2929
signers::{local::PrivateKeySigner, SignerSync},
3030
sol_types::SolStruct,
3131
};
@@ -105,8 +105,21 @@ impl<M: SolStruct> Eip712SignedMessage<M> {
105105
Ok(recovered_address)
106106
}
107107

108-
/// Use this as a simple key for testing
109-
pub fn unique_hash(&self) -> MessageId {
110-
MessageId(self.message.eip712_hash_struct().into())
108+
/// Generate a uniqueness identifier using both the message content and the recovered signer
109+
pub fn unique_hash(&self, domain_separator: &Eip712Domain) -> Result<MessageId, Eip712Error> {
110+
// Get the hash of just the message content
111+
let message_hash = self.message.eip712_hash_struct();
112+
113+
// Recover the signer address
114+
let signer = self.recover_signer(domain_separator)?;
115+
116+
// Create a new hash combining both the message hash and the signer address
117+
let mut input = Vec::with_capacity(32 + 20); // 32 bytes for hash, 20 bytes for address
118+
input.extend_from_slice(&message_hash.0);
119+
input.extend_from_slice(signer.as_ref());
120+
121+
let combined_hash = keccak256(&input);
122+
123+
Ok(MessageId(*combined_hash))
111124
}
112125
}

tap_receipt/src/checks.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ use std::{
3636
sync::{Arc, RwLock},
3737
};
3838

39+
use tap_eip712_message::Eip712Error;
40+
use thegraph_core::alloy::dyn_abi::Eip712Domain;
41+
3942
use super::{
4043
state::{Checking, Failed},
4144
Context, ReceiptError, ReceiptWithState, WithUniqueId, WithValueAndTimestamp,
@@ -94,8 +97,9 @@ type CheckBatchResponse<Rcpt> = (
9497
pub trait CheckBatch<Rcpt> {
9598
fn check_batch(
9699
&self,
100+
domain_separator: &Eip712Domain,
97101
receipts: Vec<ReceiptWithState<Checking, Rcpt>>,
98-
) -> CheckBatchResponse<Rcpt>;
102+
) -> Result<CheckBatchResponse<Rcpt>, Eip712Error>;
99103
}
100104

101105
/// Provides a built-in check to verify that the timestamp of a receipt
@@ -153,11 +157,9 @@ where
153157
{
154158
fn check_batch(
155159
&self,
160+
_domain_separator: &Eip712Domain,
156161
receipts: Vec<ReceiptWithState<Checking, Rcpt>>,
157-
) -> (
158-
Vec<ReceiptWithState<Checking, Rcpt>>,
159-
Vec<ReceiptWithState<Failed, Rcpt>>,
160-
) {
162+
) -> Result<CheckBatchResponse<Rcpt>, Eip712Error> {
161163
let (mut checking, mut failed) = (vec![], vec![]);
162164
for receipt in receipts.into_iter() {
163165
let receipt_timestamp_ns = receipt.signed_receipt().timestamp_ns();
@@ -171,7 +173,7 @@ where
171173
}));
172174
}
173175
}
174-
(checking, failed)
176+
Ok((checking, failed))
175177
}
176178
}
177179

@@ -187,23 +189,21 @@ where
187189
{
188190
fn check_batch(
189191
&self,
192+
domain_separator: &Eip712Domain,
190193
receipts: Vec<ReceiptWithState<Checking, Rcpt>>,
191-
) -> (
192-
Vec<ReceiptWithState<Checking, Rcpt>>,
193-
Vec<ReceiptWithState<Failed, Rcpt>>,
194-
) {
194+
) -> Result<CheckBatchResponse<Rcpt>, Eip712Error> {
195195
let mut signatures = HashSet::new();
196196
let (mut checking, mut failed) = (vec![], vec![]);
197197

198198
for received_receipt in receipts.into_iter() {
199-
let unique_id = received_receipt.receipt.unique_id();
199+
let unique_id = received_receipt.receipt.unique_id(domain_separator)?;
200200
if signatures.insert(unique_id) {
201201
checking.push(received_receipt);
202202
} else {
203203
failed.push(received_receipt.perform_state_error(ReceiptError::NonUniqueReceipt));
204204
}
205205
}
206-
(checking, failed)
206+
Ok((checking, failed))
207207
}
208208
}
209209

@@ -236,16 +236,20 @@ mod tests {
236236
}
237237
}
238238

239-
fn create_signed_receipt_with_custom_value(
240-
value: u128,
241-
) -> ReceiptWithState<Checking, Eip712SignedMessage<MyReceipt>> {
242-
let wallet: PrivateKeySigner = PrivateKeySigner::random();
243-
let eip712_domain_separator: Eip712Domain = eip712_domain! {
239+
fn create_test_domain_separator() -> Eip712Domain {
240+
eip712_domain! {
244241
name: "TAP",
245242
version: "1",
246243
chain_id: 1,
247244
verifying_contract: Address:: from([0x11u8; 20]),
248-
};
245+
}
246+
}
247+
248+
fn create_signed_receipt_with_custom_value(
249+
value: u128,
250+
) -> ReceiptWithState<Checking, Eip712SignedMessage<MyReceipt>> {
251+
let wallet: PrivateKeySigner = PrivateKeySigner::random();
252+
let eip712_domain_separator = create_test_domain_separator();
249253

250254
let timestamp = SystemTime::now()
251255
.duration_since(SystemTime::UNIX_EPOCH)
@@ -273,7 +277,10 @@ mod tests {
273277
let signed_receipt_2 = create_signed_receipt_with_custom_value(15);
274278
let signed_receipt_copy = signed_receipt.clone();
275279
let receipts_batch = vec![signed_receipt, signed_receipt_2, signed_receipt_copy];
276-
let (valid_receipts, invalid_receipts) = UniqueCheck.check_batch(receipts_batch);
280+
let domain_separator = create_test_domain_separator();
281+
let (valid_receipts, invalid_receipts) = UniqueCheck
282+
.check_batch(&domain_separator, receipts_batch)
283+
.unwrap();
277284
assert_eq!(valid_receipts.len(), 2);
278285
assert_eq!(invalid_receipts.len(), 1);
279286
}
@@ -282,10 +289,14 @@ mod tests {
282289
async fn test_receipt_timestamp_check() {
283290
let signed_receipt = create_signed_receipt_with_custom_value(10);
284291
let signed_receipt_2 = create_signed_receipt_with_custom_value(15);
292+
293+
let domain_separator = create_test_domain_separator();
294+
285295
let receipts_batch = vec![signed_receipt.clone(), signed_receipt_2];
286296
let min_time_stamp = signed_receipt.receipt.message.timestamp_ns + 1;
287-
let (valid_receipts, invalid_receipts) =
288-
TimestampCheck(min_time_stamp).check_batch(receipts_batch);
297+
let (valid_receipts, invalid_receipts) = TimestampCheck(min_time_stamp)
298+
.check_batch(&domain_separator, receipts_batch)
299+
.unwrap();
289300
assert_eq!(valid_receipts.len(), 1);
290301
assert_eq!(invalid_receipts.len(), 1);
291302
}

tap_receipt/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ pub mod state;
2727

2828
pub use error::ReceiptError;
2929
pub use received_receipt::ReceiptWithState;
30-
use tap_eip712_message::{Eip712SignedMessage, SignatureBytes, SignatureBytesExt};
31-
use thegraph_core::alloy::sol_types::SolStruct;
30+
use tap_eip712_message::{Eip712Error, Eip712SignedMessage, MessageId};
31+
use thegraph_core::alloy::{dyn_abi::Eip712Domain, sol_types::SolStruct};
3232

3333
/// Result type for receipt
3434
pub type ReceiptResult<T> = Result<T, ReceiptError>;
@@ -45,7 +45,7 @@ pub trait WithValueAndTimestamp {
4545
/// Extension that allows UniqueCheck for any SolStruct receipt
4646
pub trait WithUniqueId {
4747
type Output: Eq + std::hash::Hash;
48-
fn unique_id(&self) -> Self::Output;
48+
fn unique_id(&self, domain_separator: &Eip712Domain) -> Result<Self::Output, Eip712Error>;
4949
}
5050

5151
impl<T> WithValueAndTimestamp for Eip712SignedMessage<T>
@@ -65,9 +65,9 @@ impl<T> WithUniqueId for Eip712SignedMessage<T>
6565
where
6666
T: SolStruct,
6767
{
68-
type Output = SignatureBytes;
68+
type Output = MessageId;
6969

70-
fn unique_id(&self) -> Self::Output {
71-
self.signature.get_signature_bytes()
70+
fn unique_id(&self, domain_separator: &Eip712Domain) -> Result<Self::Output, Eip712Error> {
71+
self.unique_hash(domain_separator)
7272
}
7373
}

0 commit comments

Comments
 (0)