diff --git a/Cargo.toml b/Cargo.toml index 305bdaf..2d616a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ rstest = "0.25.0" tap_aggregator = { version = "0.6.0", path = "tap_aggregator" } tap_eip712_message = { version = "0.2.2", path = "tap_eip712_message" } tap_core = { version = "6.0.0", path = "tap_core" } -tap_graph = { version = "0.3.4", path = "tap_graph", features = ["v2"] } +tap_graph = { version = "0.3.4", path = "tap_graph" } tap_receipt = { version = "1.1.3", path = "tap_receipt" } thegraph-core = "0.15.1" thiserror = "2.0.12" diff --git a/tap_aggregator/Cargo.toml b/tap_aggregator/Cargo.toml index e8472e6..7d5285b 100644 --- a/tap_aggregator/Cargo.toml +++ b/tap_aggregator/Cargo.toml @@ -8,10 +8,6 @@ repository.workspace = true readme = "README.md" description = "A JSON-RPC service for the Timeline Aggregation Protocol that lets clients request an aggregate receipt from a list of individual receipts." -[features] -default = ["v2"] -v2 = ["tap_graph/v2"] - [[bin]] name = "tap_aggregator" path = "src/main.rs" diff --git a/tap_aggregator/build.rs b/tap_aggregator/build.rs index 028aaf5..e78b06d 100644 --- a/tap_aggregator/build.rs +++ b/tap_aggregator/build.rs @@ -5,14 +5,7 @@ fn main() -> Result<(), Box> { tonic_prost_build::configure() .build_server(true) - .compile_protos( - &[ - "proto/uint128.proto", - "proto/tap_aggregator.proto", - "proto/v2.proto", - ], - &["proto"], - )?; + .compile_protos(&["proto/uint128.proto", "proto/v2.proto"], &["proto"])?; Ok(()) } diff --git a/tap_aggregator/proto/tap_aggregator.proto b/tap_aggregator/proto/tap_aggregator.proto deleted file mode 100644 index 94c8e15..0000000 --- a/tap_aggregator/proto/tap_aggregator.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto3"; -package tap_aggregator.v1; - -import "uint128.proto"; - -message Receipt { - bytes allocation_id = 1; - uint64 timestamp_ns = 2; - uint64 nonce = 3; - grpc.uint128.Uint128 value = 4; -} - -message SignedReceipt { - Receipt message = 1; - bytes signature = 2; -} - -message ReceiptAggregateVoucher { - bytes allocation_id = 1; - uint64 timestamp_ns = 2; - grpc.uint128.Uint128 value_aggregate = 3; -} - -message SignedRav { - ReceiptAggregateVoucher message = 1; - bytes signature = 2; -} - -message RavRequest { - repeated SignedReceipt receipts = 1; - optional SignedRav previous_rav = 2; -} - -message RavResponse { - SignedRav rav = 1; -} - -service TapAggregator { - rpc AggregateReceipts(RavRequest) returns (RavResponse); -} diff --git a/tap_aggregator/src/aggregator.rs b/tap_aggregator/src/aggregator.rs index ae6adc7..24617d8 100644 --- a/tap_aggregator/src/aggregator.rs +++ b/tap_aggregator/src/aggregator.rs @@ -1,2 +1,468 @@ -pub mod v1; -pub mod v2; +use std::collections::HashSet; + +use anyhow::{bail, Ok, Result}; +use rayon::prelude::*; +use tap_core::{receipt::WithUniqueId, signed_message::Eip712SignedMessage}; +use tap_graph::{Receipt, ReceiptAggregateVoucher}; +use thegraph_core::alloy::{ + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, + sol_types::SolStruct, +}; + +pub fn check_and_aggregate_receipts( + domain_separator: &Eip712Domain, + receipts: &[Eip712SignedMessage], + previous_rav: Option>, + wallet: &PrivateKeySigner, + accepted_addresses: &HashSet
, +) -> Result> { + check_signatures_unique(receipts)?; + + // Check that the receipts are signed by an accepted signer address + receipts.par_iter().try_for_each(|receipt| { + check_signature_is_from_one_of_addresses(receipt, domain_separator, accepted_addresses) + })?; + + // Check that the previous rav is signed by an accepted signer address + if let Some(previous_rav) = &previous_rav { + check_signature_is_from_one_of_addresses( + previous_rav, + domain_separator, + accepted_addresses, + )?; + } + + // Check that the receipts timestamp is greater than the previous rav + check_receipt_timestamps(receipts, previous_rav.as_ref())?; + + // Get the allocation id from the first receipt, return error if there are no receipts + let (collection_id, payer, data_service, service_provider) = match receipts.first() { + Some(receipt) => ( + receipt.message.collection_id, + receipt.message.payer, + receipt.message.data_service, + receipt.message.service_provider, + ), + None => return Err(tap_core::Error::NoValidReceiptsForRavRequest.into()), + }; + + // Check that the receipts all have the same collection id + check_collection_id( + receipts, + collection_id, + payer, + data_service, + service_provider, + )?; + + // Check that the rav has the correct collection id + if let Some(previous_rav) = &previous_rav { + let prev_id = previous_rav.message.collectionId; + let prev_payer = previous_rav.message.payer; + let prev_data_service = previous_rav.message.dataService; + let prev_service_provider = previous_rav.message.serviceProvider; + if prev_id != collection_id { + return Err(tap_core::Error::RavCollectionIdMismatch { + prev_id: format!("{prev_id:#X}"), + new_id: format!("{collection_id:#X}"), + } + .into()); + } + if prev_payer != payer { + return Err(tap_core::Error::RavCollectionIdMismatch { + prev_id: format!("{prev_id:#X}"), + new_id: format!("{collection_id:#X}"), + } + .into()); + } + + if prev_data_service != data_service { + return Err(tap_core::Error::RavCollectionIdMismatch { + prev_id: format!("{prev_id:#X}"), + new_id: format!("{collection_id:#X}"), + } + .into()); + } + if prev_service_provider != service_provider { + return Err(tap_core::Error::RavCollectionIdMismatch { + prev_id: format!("{prev_id:#X}"), + new_id: format!("{collection_id:#X}"), + } + .into()); + } + } + + // Aggregate the receipts + let rav = ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + receipts, + previous_rav, + )?; + + // Sign the rav and return + Ok(Eip712SignedMessage::new(domain_separator, rav, wallet)?) +} + +fn check_signature_is_from_one_of_addresses( + message: &Eip712SignedMessage, + domain_separator: &Eip712Domain, + accepted_addresses: &HashSet
, +) -> Result<()> { + let recovered_address = message.recover_signer(domain_separator)?; + if !accepted_addresses.contains(&recovered_address) { + bail!(tap_core::Error::InvalidRecoveredSigner { + address: recovered_address, + }); + } + Ok(()) +} + +fn check_collection_id( + receipts: &[Eip712SignedMessage], + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, +) -> Result<()> { + for receipt in receipts.iter() { + let receipt = &receipt.message; + if receipt.collection_id != collection_id { + return Err(tap_core::Error::RavCollectionIdNotUniform.into()); + } + if receipt.payer != payer { + return Err(tap_core::Error::RavCollectionIdNotUniform.into()); + } + if receipt.data_service != data_service { + return Err(tap_core::Error::RavCollectionIdNotUniform.into()); + } + if receipt.service_provider != service_provider { + return Err(tap_core::Error::RavCollectionIdNotUniform.into()); + } + } + Ok(()) +} + +fn check_signatures_unique(receipts: &[Eip712SignedMessage]) -> Result<()> { + let mut receipt_signatures = HashSet::new(); + for receipt in receipts.iter() { + let signature = receipt.unique_id(); + if !receipt_signatures.insert(signature) { + return Err(tap_core::Error::DuplicateReceiptSignature(format!( + "{:?}", + receipt.unique_id() + )) + .into()); + } + } + Ok(()) +} + +fn check_receipt_timestamps( + receipts: &[Eip712SignedMessage], + previous_rav: Option<&Eip712SignedMessage>, +) -> Result<()> { + if let Some(previous_rav) = &previous_rav { + for receipt in receipts.iter() { + let receipt = &receipt.message; + if previous_rav.message.timestampNs >= receipt.timestamp_ns { + return Err(tap_core::Error::ReceiptTimestampLowerThanRav { + rav_ts: previous_rav.message.timestampNs, + receipt_ts: receipt.timestamp_ns, + } + .into()); + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use rstest::*; + use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain}; + use tap_graph::{Receipt, ReceiptAggregateVoucher}; + use thegraph_core::alloy::{ + dyn_abi::Eip712Domain, + primitives::{address, fixed_bytes, Address, Bytes, FixedBytes}, + signers::local::PrivateKeySigner, + }; + + #[fixture] + fn keys() -> (PrivateKeySigner, Address) { + let wallet = PrivateKeySigner::random(); + let address = wallet.address(); + (wallet, address) + } + + #[fixture] + fn collection_id() -> FixedBytes<32> { + fixed_bytes!("deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead") + } + + #[fixture] + fn payer() -> Address { + address!("abababababababababababababababababababab") + } + + #[fixture] + fn data_service() -> Address { + address!("deaddeaddeaddeaddeaddeaddeaddeaddeaddead") + } + + #[fixture] + fn service_provider() -> Address { + address!("beefbeefbeefbeefbeefbeefbeefbeefbeefbeef") + } + + #[fixture] + fn other_collection_id() -> FixedBytes<32> { + fixed_bytes!("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + } + #[fixture] + fn domain_separator() -> Eip712Domain { + tap_eip712_domain(1, Address::from([0x11u8; 20])) + } + + #[rstest] + #[test] + fn check_signatures_unique_fail( + keys: (PrivateKeySigner, Address), + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, + domain_separator: Eip712Domain, + ) { + // Create the same receipt twice (replay attack) + let mut receipts = Vec::new(); + let receipt = Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), + &keys.0, + ) + .unwrap(); + receipts.push(receipt.clone()); + receipts.push(receipt); + + let res = super::check_signatures_unique(&receipts); + assert!(res.is_err()); + } + + #[rstest] + #[test] + fn check_signatures_unique_ok( + keys: (PrivateKeySigner, Address), + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, + domain_separator: Eip712Domain, + ) { + // Create 2 different receipts + let receipts = vec![ + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), + &keys.0, + ) + .unwrap(), + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), + &keys.0, + ) + .unwrap(), + ]; + + let res = super::check_signatures_unique(&receipts); + assert!(res.is_ok()); + } + + #[rstest] + #[test] + /// Test that a receipt with a timestamp greater than the rav timestamp passes + fn check_receipt_timestamps( + keys: (PrivateKeySigner, Address), + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, + domain_separator: Eip712Domain, + ) { + // Create receipts with consecutive timestamps + let receipt_timestamp_range = 10..20; + let mut receipts = Vec::new(); + for i in receipt_timestamp_range.clone() { + receipts.push( + Eip712SignedMessage::new( + &domain_separator, + Receipt { + collection_id, + payer, + data_service, + service_provider, + timestamp_ns: i, + nonce: 0, + value: 42, + }, + &keys.0, + ) + .unwrap(), + ); + } + + // Create rav with max_timestamp below the receipts timestamps + let rav = Eip712SignedMessage::new( + &domain_separator, + ReceiptAggregateVoucher { + collectionId: collection_id, + dataService: data_service, + payer, + serviceProvider: service_provider, + timestampNs: receipt_timestamp_range.clone().min().unwrap() - 1, + valueAggregate: 42, + metadata: Bytes::new(), + }, + &keys.0, + ) + .unwrap(); + assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_ok()); + + // Create rav with max_timestamp equal to the lowest receipt timestamp + // Aggregation should fail + let rav = Eip712SignedMessage::new( + &domain_separator, + ReceiptAggregateVoucher { + collectionId: collection_id, + dataService: data_service, + payer, + serviceProvider: service_provider, + timestampNs: receipt_timestamp_range.clone().min().unwrap(), + valueAggregate: 42, + metadata: Bytes::new(), + }, + &keys.0, + ) + .unwrap(); + assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_err()); + + // Create rav with max_timestamp above highest receipt timestamp + // Aggregation should fail + let rav = Eip712SignedMessage::new( + &domain_separator, + ReceiptAggregateVoucher { + collectionId: collection_id, + dataService: data_service, + payer, + serviceProvider: service_provider, + timestampNs: receipt_timestamp_range.clone().max().unwrap() + 1, + valueAggregate: 42, + metadata: Bytes::new(), + }, + &keys.0, + ) + .unwrap(); + assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_err()); + } + + #[rstest] + #[test] + /// Test check_allocation_id with 2 receipts that have the correct allocation id + /// and 1 receipt that has the wrong allocation id + fn check_allocation_id_fail( + keys: (PrivateKeySigner, Address), + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, + other_collection_id: FixedBytes<32>, + domain_separator: Eip712Domain, + ) { + let receipts = vec![ + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), + &keys.0, + ) + .unwrap(), + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 43).unwrap(), + &keys.0, + ) + .unwrap(), + Eip712SignedMessage::new( + &domain_separator, + Receipt::new( + other_collection_id, + payer, + data_service, + service_provider, + 44, + ) + .unwrap(), + &keys.0, + ) + .unwrap(), + ]; + + let res = super::check_collection_id( + &receipts, + collection_id, + payer, + data_service, + service_provider, + ); + + assert!(res.is_err()); + } + + #[rstest] + #[test] + /// Test check_allocation_id with 3 receipts that have the correct allocation id + fn check_allocation_id_ok( + keys: (PrivateKeySigner, Address), + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, + domain_separator: Eip712Domain, + ) { + let receipts = vec![ + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), + &keys.0, + ) + .unwrap(), + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 43).unwrap(), + &keys.0, + ) + .unwrap(), + Eip712SignedMessage::new( + &domain_separator, + Receipt::new(collection_id, payer, data_service, service_provider, 44).unwrap(), + &keys.0, + ) + .unwrap(), + ]; + + let res = super::check_collection_id( + &receipts, + collection_id, + payer, + data_service, + service_provider, + ); + + assert!(res.is_ok()); + } +} diff --git a/tap_aggregator/src/aggregator/v1.rs b/tap_aggregator/src/aggregator/v1.rs deleted file mode 100644 index f452463..0000000 --- a/tap_aggregator/src/aggregator/v1.rs +++ /dev/null @@ -1,411 +0,0 @@ -use std::collections::HashSet; - -use anyhow::{bail, Ok, Result}; -use rayon::prelude::*; -use tap_core::{receipt::WithUniqueId, signed_message::Eip712SignedMessage}; -use tap_graph::{Receipt, ReceiptAggregateVoucher}; -use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, - sol_types::SolStruct, -}; - -pub fn check_and_aggregate_receipts( - domain_separator: &Eip712Domain, - receipts: &[Eip712SignedMessage], - previous_rav: Option>, - wallet: &PrivateKeySigner, - accepted_addresses: &HashSet
, -) -> Result> { - check_signatures_unique(receipts)?; - - // Check that the receipts are signed by an accepted signer address - receipts.par_iter().try_for_each(|receipt| { - check_signature_is_from_one_of_addresses(receipt, domain_separator, accepted_addresses) - })?; - - // Check that the previous rav is signed by an accepted signer address - if let Some(previous_rav) = &previous_rav { - check_signature_is_from_one_of_addresses( - previous_rav, - domain_separator, - accepted_addresses, - )?; - } - - // Check that the receipts timestamp is greater than the previous rav - check_receipt_timestamps(receipts, previous_rav.as_ref())?; - - // Get the allocation id from the first receipt, return error if there are no receipts - let allocation_id = match receipts.first() { - Some(receipt) => receipt.message.allocation_id, - None => return Err(tap_core::Error::NoValidReceiptsForRavRequest.into()), - }; - - // Check that the receipts all have the same allocation id - check_allocation_id(receipts, allocation_id)?; - - // Check that the rav has the correct allocation id - if let Some(previous_rav) = &previous_rav { - let prev_id = previous_rav.message.allocationId; - if prev_id != allocation_id { - return Err(tap_core::Error::RavAllocationIdMismatch { - prev_id: format!("{prev_id:#X}"), - new_id: format!("{allocation_id:#X}"), - } - .into()); - } - } - - // Aggregate the receipts - let rav = ReceiptAggregateVoucher::aggregate_receipts(allocation_id, receipts, previous_rav)?; - - // Sign the rav and return - Ok(Eip712SignedMessage::new(domain_separator, rav, wallet)?) -} - -fn check_signature_is_from_one_of_addresses( - message: &Eip712SignedMessage, - domain_separator: &Eip712Domain, - accepted_addresses: &HashSet
, -) -> Result<()> { - let recovered_address = message.recover_signer(domain_separator)?; - if !accepted_addresses.contains(&recovered_address) { - bail!(tap_core::Error::InvalidRecoveredSigner { - address: recovered_address, - }); - } - Ok(()) -} - -fn check_allocation_id( - receipts: &[Eip712SignedMessage], - allocation_id: Address, -) -> Result<()> { - for receipt in receipts.iter() { - let receipt = &receipt.message; - if receipt.allocation_id != allocation_id { - return Err(tap_core::Error::RavAllocationIdNotUniform.into()); - } - } - Ok(()) -} - -fn check_signatures_unique(receipts: &[Eip712SignedMessage]) -> Result<()> { - let mut receipt_signatures = HashSet::new(); - for receipt in receipts.iter() { - let signature = receipt.unique_id(); - if !receipt_signatures.insert(signature) { - return Err(tap_core::Error::DuplicateReceiptSignature(format!( - "{:?}", - receipt.unique_id() - )) - .into()); - } - } - Ok(()) -} - -fn check_receipt_timestamps( - receipts: &[Eip712SignedMessage], - previous_rav: Option<&Eip712SignedMessage>, -) -> Result<()> { - if let Some(previous_rav) = &previous_rav { - for receipt in receipts.iter() { - let receipt = &receipt.message; - if previous_rav.message.timestampNs >= receipt.timestamp_ns { - return Err(tap_core::Error::ReceiptTimestampLowerThanRav { - rav_ts: previous_rav.message.timestampNs, - receipt_ts: receipt.timestamp_ns, - } - .into()); - } - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use rstest::*; - use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; - use tap_graph::{Receipt, ReceiptAggregateVoucher}; - use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, - primitives::{Address, U256}, - signers::{local::PrivateKeySigner, Signature}, - }; - - use super::*; - - #[fixture] - fn keys() -> (PrivateKeySigner, Address) { - let wallet = PrivateKeySigner::random(); - let address = wallet.address(); - (wallet, address) - } - - #[fixture] - fn allocation_ids() -> Vec
{ - vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), - ] - } - - #[fixture] - fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) - } - - #[rstest] - #[test] - fn test_signature_malleability_vulnerability( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - // Create a test receipt - let receipt = Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), - &keys.0, - ) - .unwrap(); - - // Get the original signature components - let r = receipt.signature.r(); - let s = receipt.signature.s(); - let v = receipt.signature.v(); - - // Create a malleated signature by changing the s value and flipping v - // Get the Secp256k1 curve order - let n = U256::from_str_radix( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", - 16, - ) - .unwrap(); - let s_malleated = n - s; - let v_malleated = !v; // Flip the parity bit - - // Create a new signature with the malleated components - let signature_malleated = Signature::new(r, s_malleated, v_malleated); - - // Create a new signed message with the malleated signature but same message - let receipt_malleated = Eip712SignedMessage { - message: receipt.message.clone(), - signature: signature_malleated, - }; - - // Verify that both signatures recover to the same signer - let original_signer = receipt.recover_signer(&domain_separator).unwrap(); - let malleated_signer = receipt_malleated.recover_signer(&domain_separator).unwrap(); - - assert_eq!( - original_signer, malleated_signer, - "Both signatures should recover to the same signer" - ); - - // Try to check if signatures are unique using the current implementation - let receipts = vec![receipt, receipt_malleated]; - - // This should return an error because the signatures are different - // but the messages are the same, which if allowed would present a security vulnerability - let result = check_signatures_unique(&receipts); - - // The result should be an error because the malleated signature is not treated as unique - // and is detected as a duplicate - assert!(result.is_err()); - } - - #[rstest] - #[test] - fn check_signatures_unique_fail( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - // Create the same receipt twice (replay attack) - let mut receipts = Vec::new(); - let receipt = Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), - &keys.0, - ) - .unwrap(); - receipts.push(receipt.clone()); - receipts.push(receipt); - - let res = check_signatures_unique(&receipts); - assert!(res.is_err()); - } - - #[rstest] - #[test] - fn check_signatures_unique_ok( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - // Create 2 different receipts - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 43).unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = check_signatures_unique(&receipts); - assert!(res.is_ok()); - } - - #[rstest] - #[test] - /// Test that a receipt with a timestamp greater then the rav timestamp passes - fn test_check_receipt_timestamps( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - // Create receipts with consecutive timestamps - let receipt_timestamp_range = 10..20; - let mut receipts = Vec::new(); - for i in receipt_timestamp_range.clone() { - receipts.push( - Eip712SignedMessage::new( - &domain_separator, - Receipt { - allocation_id: allocation_ids[0], - timestamp_ns: i, - nonce: 0, - value: 42, - }, - &keys.0, - ) - .unwrap(), - ); - } - - // Create rav with max_timestamp below the receipts timestamps - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - allocationId: allocation_ids[0], - timestampNs: receipt_timestamp_range.clone().min().unwrap() - 1, - valueAggregate: 42, - }, - &keys.0, - ) - .unwrap(); - assert!(check_receipt_timestamps(&receipts, Some(&rav)).is_ok()); - - // Create rav with max_timestamp equal to the lowest receipt timestamp - // Aggregation should fail - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - allocationId: allocation_ids[0], - timestampNs: receipt_timestamp_range.clone().min().unwrap(), - valueAggregate: 42, - }, - &keys.0, - ) - .unwrap(); - assert!(check_receipt_timestamps(&receipts, Some(&rav)).is_err()); - - // Create rav with max_timestamp above highest receipt timestamp - // Aggregation should fail - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - allocationId: allocation_ids[0], - timestampNs: receipt_timestamp_range.clone().max().unwrap() + 1, - valueAggregate: 42, - }, - &keys.0, - ) - .unwrap(); - assert!(check_receipt_timestamps(&receipts, Some(&rav)).is_err()); - } - - #[rstest] - #[test] - /// Test check_allocation_id with 2 receipts that have the correct allocation id - /// and 1 receipt that has the wrong allocation id - fn check_allocation_id_fail( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 43).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[1], 44).unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = check_allocation_id(&receipts, allocation_ids[0]); - - assert!(res.is_err()); - } - - #[rstest] - #[test] - /// Test check_allocation_id with 3 receipts that have the correct allocation id - fn check_allocation_id_ok( - keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, - domain_separator: Eip712Domain, - ) { - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 43).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(allocation_ids[0], 44).unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = check_allocation_id(&receipts, allocation_ids[0]); - - assert!(res.is_ok()); - } -} diff --git a/tap_aggregator/src/aggregator/v2.rs b/tap_aggregator/src/aggregator/v2.rs deleted file mode 100644 index 16e9b36..0000000 --- a/tap_aggregator/src/aggregator/v2.rs +++ /dev/null @@ -1,468 +0,0 @@ -use std::collections::HashSet; - -use anyhow::{bail, Ok, Result}; -use rayon::prelude::*; -use tap_core::{receipt::WithUniqueId, signed_message::Eip712SignedMessage}; -use tap_graph::v2::{Receipt, ReceiptAggregateVoucher}; -use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, - primitives::{Address, FixedBytes}, - signers::local::PrivateKeySigner, - sol_types::SolStruct, -}; - -pub fn check_and_aggregate_receipts( - domain_separator: &Eip712Domain, - receipts: &[Eip712SignedMessage], - previous_rav: Option>, - wallet: &PrivateKeySigner, - accepted_addresses: &HashSet
, -) -> Result> { - check_signatures_unique(receipts)?; - - // Check that the receipts are signed by an accepted signer address - receipts.par_iter().try_for_each(|receipt| { - check_signature_is_from_one_of_addresses(receipt, domain_separator, accepted_addresses) - })?; - - // Check that the previous rav is signed by an accepted signer address - if let Some(previous_rav) = &previous_rav { - check_signature_is_from_one_of_addresses( - previous_rav, - domain_separator, - accepted_addresses, - )?; - } - - // Check that the receipts timestamp is greater than the previous rav - check_receipt_timestamps(receipts, previous_rav.as_ref())?; - - // Get the allocation id from the first receipt, return error if there are no receipts - let (collection_id, payer, data_service, service_provider) = match receipts.first() { - Some(receipt) => ( - receipt.message.collection_id, - receipt.message.payer, - receipt.message.data_service, - receipt.message.service_provider, - ), - None => return Err(tap_core::Error::NoValidReceiptsForRavRequest.into()), - }; - - // Check that the receipts all have the same collection id - check_collection_id( - receipts, - collection_id, - payer, - data_service, - service_provider, - )?; - - // Check that the rav has the correct allocation id - if let Some(previous_rav) = &previous_rav { - let prev_id = previous_rav.message.collectionId; - let prev_payer = previous_rav.message.payer; - let prev_data_service = previous_rav.message.dataService; - let prev_service_provider = previous_rav.message.serviceProvider; - if prev_id != collection_id { - return Err(tap_core::Error::RavAllocationIdMismatch { - prev_id: format!("{prev_id:#X}"), - new_id: format!("{collection_id:#X}"), - } - .into()); - } - if prev_payer != payer { - return Err(tap_core::Error::RavAllocationIdMismatch { - prev_id: format!("{prev_id:#X}"), - new_id: format!("{collection_id:#X}"), - } - .into()); - } - - if prev_data_service != data_service { - return Err(tap_core::Error::RavAllocationIdMismatch { - prev_id: format!("{prev_id:#X}"), - new_id: format!("{collection_id:#X}"), - } - .into()); - } - if prev_service_provider != service_provider { - return Err(tap_core::Error::RavAllocationIdMismatch { - prev_id: format!("{prev_id:#X}"), - new_id: format!("{collection_id:#X}"), - } - .into()); - } - } - - // Aggregate the receipts - let rav = ReceiptAggregateVoucher::aggregate_receipts( - collection_id, - payer, - data_service, - service_provider, - receipts, - previous_rav, - )?; - - // Sign the rav and return - Ok(Eip712SignedMessage::new(domain_separator, rav, wallet)?) -} - -fn check_signature_is_from_one_of_addresses( - message: &Eip712SignedMessage, - domain_separator: &Eip712Domain, - accepted_addresses: &HashSet
, -) -> Result<()> { - let recovered_address = message.recover_signer(domain_separator)?; - if !accepted_addresses.contains(&recovered_address) { - bail!(tap_core::Error::InvalidRecoveredSigner { - address: recovered_address, - }); - } - Ok(()) -} - -fn check_collection_id( - receipts: &[Eip712SignedMessage], - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, -) -> Result<()> { - for receipt in receipts.iter() { - let receipt = &receipt.message; - if receipt.collection_id != collection_id { - return Err(tap_core::Error::RavAllocationIdNotUniform.into()); - } - if receipt.payer != payer { - return Err(tap_core::Error::RavAllocationIdNotUniform.into()); - } - if receipt.data_service != data_service { - return Err(tap_core::Error::RavAllocationIdNotUniform.into()); - } - if receipt.service_provider != service_provider { - return Err(tap_core::Error::RavAllocationIdNotUniform.into()); - } - } - Ok(()) -} - -fn check_signatures_unique(receipts: &[Eip712SignedMessage]) -> Result<()> { - let mut receipt_signatures = HashSet::new(); - for receipt in receipts.iter() { - let signature = receipt.unique_id(); - if !receipt_signatures.insert(signature) { - return Err(tap_core::Error::DuplicateReceiptSignature(format!( - "{:?}", - receipt.unique_id() - )) - .into()); - } - } - Ok(()) -} - -fn check_receipt_timestamps( - receipts: &[Eip712SignedMessage], - previous_rav: Option<&Eip712SignedMessage>, -) -> Result<()> { - if let Some(previous_rav) = &previous_rav { - for receipt in receipts.iter() { - let receipt = &receipt.message; - if previous_rav.message.timestampNs >= receipt.timestamp_ns { - return Err(tap_core::Error::ReceiptTimestampLowerThanRav { - rav_ts: previous_rav.message.timestampNs, - receipt_ts: receipt.timestamp_ns, - } - .into()); - } - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use rstest::*; - use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; - use tap_graph::v2::{Receipt, ReceiptAggregateVoucher}; - use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, - primitives::{address, fixed_bytes, Address, Bytes, FixedBytes}, - signers::local::PrivateKeySigner, - }; - - #[fixture] - fn keys() -> (PrivateKeySigner, Address) { - let wallet = PrivateKeySigner::random(); - let address = wallet.address(); - (wallet, address) - } - - #[fixture] - fn collection_id() -> FixedBytes<32> { - fixed_bytes!("deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead") - } - - #[fixture] - fn payer() -> Address { - address!("abababababababababababababababababababab") - } - - #[fixture] - fn data_service() -> Address { - address!("deaddeaddeaddeaddeaddeaddeaddeaddeaddead") - } - - #[fixture] - fn service_provider() -> Address { - address!("beefbeefbeefbeefbeefbeefbeefbeefbeefbeef") - } - - #[fixture] - fn other_collection_id() -> FixedBytes<32> { - fixed_bytes!("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - } - #[fixture] - fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V2) - } - - #[rstest] - #[test] - fn check_signatures_unique_fail( - keys: (PrivateKeySigner, Address), - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, - domain_separator: Eip712Domain, - ) { - // Create the same receipt twice (replay attack) - let mut receipts = Vec::new(); - let receipt = Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), - &keys.0, - ) - .unwrap(); - receipts.push(receipt.clone()); - receipts.push(receipt); - - let res = super::check_signatures_unique(&receipts); - assert!(res.is_err()); - } - - #[rstest] - #[test] - fn check_signatures_unique_ok( - keys: (PrivateKeySigner, Address), - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, - domain_separator: Eip712Domain, - ) { - // Create 2 different receipts - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = super::check_signatures_unique(&receipts); - assert!(res.is_ok()); - } - - #[rstest] - #[test] - /// Test that a receipt with a timestamp greater than the rav timestamp passes - fn check_receipt_timestamps( - keys: (PrivateKeySigner, Address), - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, - domain_separator: Eip712Domain, - ) { - // Create receipts with consecutive timestamps - let receipt_timestamp_range = 10..20; - let mut receipts = Vec::new(); - for i in receipt_timestamp_range.clone() { - receipts.push( - Eip712SignedMessage::new( - &domain_separator, - Receipt { - collection_id, - payer, - data_service, - service_provider, - timestamp_ns: i, - nonce: 0, - value: 42, - }, - &keys.0, - ) - .unwrap(), - ); - } - - // Create rav with max_timestamp below the receipts timestamps - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - collectionId: collection_id, - dataService: data_service, - payer, - serviceProvider: service_provider, - timestampNs: receipt_timestamp_range.clone().min().unwrap() - 1, - valueAggregate: 42, - metadata: Bytes::new(), - }, - &keys.0, - ) - .unwrap(); - assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_ok()); - - // Create rav with max_timestamp equal to the lowest receipt timestamp - // Aggregation should fail - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - collectionId: collection_id, - dataService: data_service, - payer, - serviceProvider: service_provider, - timestampNs: receipt_timestamp_range.clone().min().unwrap(), - valueAggregate: 42, - metadata: Bytes::new(), - }, - &keys.0, - ) - .unwrap(); - assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_err()); - - // Create rav with max_timestamp above highest receipt timestamp - // Aggregation should fail - let rav = Eip712SignedMessage::new( - &domain_separator, - ReceiptAggregateVoucher { - collectionId: collection_id, - dataService: data_service, - payer, - serviceProvider: service_provider, - timestampNs: receipt_timestamp_range.clone().max().unwrap() + 1, - valueAggregate: 42, - metadata: Bytes::new(), - }, - &keys.0, - ) - .unwrap(); - assert!(super::check_receipt_timestamps(&receipts, Some(&rav)).is_err()); - } - - #[rstest] - #[test] - /// Test check_allocation_id with 2 receipts that have the correct allocation id - /// and 1 receipt that has the wrong allocation id - fn check_allocation_id_fail( - keys: (PrivateKeySigner, Address), - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, - other_collection_id: FixedBytes<32>, - domain_separator: Eip712Domain, - ) { - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 43).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new( - other_collection_id, - payer, - data_service, - service_provider, - 44, - ) - .unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = super::check_collection_id( - &receipts, - collection_id, - payer, - data_service, - service_provider, - ); - - assert!(res.is_err()); - } - - #[rstest] - #[test] - /// Test check_allocation_id with 3 receipts that have the correct allocation id - fn check_allocation_id_ok( - keys: (PrivateKeySigner, Address), - collection_id: FixedBytes<32>, - payer: Address, - data_service: Address, - service_provider: Address, - domain_separator: Eip712Domain, - ) { - let receipts = vec![ - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 43).unwrap(), - &keys.0, - ) - .unwrap(), - Eip712SignedMessage::new( - &domain_separator, - Receipt::new(collection_id, payer, data_service, service_provider, 44).unwrap(), - &keys.0, - ) - .unwrap(), - ]; - - let res = super::check_collection_id( - &receipts, - collection_id, - payer, - data_service, - service_provider, - ); - - assert!(res.is_ok()); - } -} diff --git a/tap_aggregator/src/grpc.rs b/tap_aggregator/src/grpc.rs index d07aced..f4760be 100644 --- a/tap_aggregator/src/grpc.rs +++ b/tap_aggregator/src/grpc.rs @@ -16,127 +16,6 @@ pub mod uint128 { } } -pub mod v1 { - use anyhow::anyhow; - use tap_core::signed_message::Eip712SignedMessage; - - tonic::include_proto!("tap_aggregator.v1"); - - impl TryFrom for tap_graph::Receipt { - type Error = anyhow::Error; - fn try_from(receipt: self::Receipt) -> Result { - Ok(Self { - allocation_id: receipt.allocation_id.as_slice().try_into()?, - timestamp_ns: receipt.timestamp_ns, - value: receipt.value.ok_or(anyhow!("Missing value"))?.into(), - nonce: receipt.nonce, - }) - } - } - - impl TryFrom for tap_graph::SignedReceipt { - type Error = anyhow::Error; - fn try_from(receipt: self::SignedReceipt) -> Result { - Ok(Self { - signature: receipt.signature.as_slice().try_into()?, - message: receipt - .message - .ok_or(anyhow!("Missing message"))? - .try_into()?, - }) - } - } - - impl From for self::Receipt { - fn from(value: tap_graph::Receipt) -> Self { - Self { - allocation_id: value.allocation_id.as_slice().to_vec(), - timestamp_ns: value.timestamp_ns, - nonce: value.nonce, - value: Some(value.value.into()), - } - } - } - - impl From for self::SignedReceipt { - fn from(value: tap_graph::SignedReceipt) -> Self { - Self { - message: Some(value.message.into()), - signature: value.signature.as_bytes().to_vec(), - } - } - } - - impl TryFrom for Eip712SignedMessage { - type Error = anyhow::Error; - fn try_from(voucher: self::SignedRav) -> Result { - Ok(Self { - signature: voucher.signature.as_slice().try_into()?, - message: voucher - .message - .ok_or(anyhow!("Missing message"))? - .try_into()?, - }) - } - } - - impl From> for self::SignedRav { - fn from(voucher: Eip712SignedMessage) -> Self { - Self { - signature: voucher.signature.as_bytes().to_vec(), - message: Some(voucher.message.into()), - } - } - } - - impl TryFrom for tap_graph::ReceiptAggregateVoucher { - type Error = anyhow::Error; - fn try_from(voucher: self::ReceiptAggregateVoucher) -> Result { - Ok(Self { - allocationId: voucher.allocation_id.as_slice().try_into()?, - timestampNs: voucher.timestamp_ns, - valueAggregate: voucher - .value_aggregate - .ok_or(anyhow!("Missing Value Aggregate"))? - .into(), - }) - } - } - - impl From for self::ReceiptAggregateVoucher { - fn from(voucher: tap_graph::ReceiptAggregateVoucher) -> Self { - Self { - allocation_id: voucher.allocationId.to_vec(), - timestamp_ns: voucher.timestampNs, - value_aggregate: Some(voucher.valueAggregate.into()), - } - } - } - - impl self::RavRequest { - pub fn new( - receipts: Vec, - previous_rav: Option, - ) -> Self { - Self { - receipts: receipts.into_iter().map(Into::into).collect(), - previous_rav: previous_rav.map(Into::into), - } - } - } - - impl self::RavResponse { - pub fn signed_rav(mut self) -> anyhow::Result { - let signed_rav: tap_graph::SignedRav = self - .rav - .take() - .ok_or(anyhow!("Couldn't find rav"))? - .try_into()?; - Ok(signed_rav) - } - } -} - pub mod v2 { use anyhow::anyhow; use tap_core::signed_message::Eip712SignedMessage; @@ -144,7 +23,7 @@ pub mod v2 { tonic::include_proto!("tap_aggregator.v2"); - impl TryFrom for tap_graph::v2::Receipt { + impl TryFrom for tap_graph::Receipt { type Error = anyhow::Error; fn try_from(receipt: self::Receipt) -> Result { Ok(Self { @@ -159,7 +38,7 @@ pub mod v2 { } } - impl TryFrom for tap_graph::v2::SignedReceipt { + impl TryFrom for tap_graph::SignedReceipt { type Error = anyhow::Error; fn try_from(receipt: self::SignedReceipt) -> Result { Ok(Self { @@ -172,8 +51,8 @@ pub mod v2 { } } - impl From for self::Receipt { - fn from(value: tap_graph::v2::Receipt) -> Self { + impl From for self::Receipt { + fn from(value: tap_graph::Receipt) -> Self { Self { collection_id: value.collection_id.as_slice().to_vec(), timestamp_ns: value.timestamp_ns, @@ -186,8 +65,8 @@ pub mod v2 { } } - impl From for self::SignedReceipt { - fn from(value: tap_graph::v2::SignedReceipt) -> Self { + impl From for self::SignedReceipt { + fn from(value: tap_graph::SignedReceipt) -> Self { Self { message: Some(value.message.into()), signature: value.signature.as_bytes().to_vec(), @@ -195,7 +74,7 @@ pub mod v2 { } } - impl TryFrom for Eip712SignedMessage { + impl TryFrom for Eip712SignedMessage { type Error = anyhow::Error; fn try_from(voucher: self::SignedRav) -> Result { Ok(Self { @@ -208,8 +87,8 @@ pub mod v2 { } } - impl From> for self::SignedRav { - fn from(voucher: Eip712SignedMessage) -> Self { + impl From> for self::SignedRav { + fn from(voucher: Eip712SignedMessage) -> Self { Self { signature: voucher.signature.as_bytes().to_vec(), message: Some(voucher.message.into()), @@ -217,7 +96,7 @@ pub mod v2 { } } - impl TryFrom for tap_graph::v2::ReceiptAggregateVoucher { + impl TryFrom for tap_graph::ReceiptAggregateVoucher { type Error = anyhow::Error; fn try_from(voucher: self::ReceiptAggregateVoucher) -> Result { Ok(Self { @@ -235,8 +114,8 @@ pub mod v2 { } } - impl From for self::ReceiptAggregateVoucher { - fn from(voucher: tap_graph::v2::ReceiptAggregateVoucher) -> Self { + impl From for self::ReceiptAggregateVoucher { + fn from(voucher: tap_graph::ReceiptAggregateVoucher) -> Self { Self { collection_id: voucher.collectionId.to_vec(), timestamp_ns: voucher.timestampNs, @@ -251,8 +130,8 @@ pub mod v2 { impl self::RavRequest { pub fn new( - receipts: Vec, - previous_rav: Option, + receipts: Vec, + previous_rav: Option, ) -> Self { Self { receipts: receipts.into_iter().map(Into::into).collect(), @@ -262,8 +141,8 @@ pub mod v2 { } impl self::RavResponse { - pub fn signed_rav(mut self) -> anyhow::Result { - let signed_rav: tap_graph::v2::SignedRav = self + pub fn signed_rav(mut self) -> anyhow::Result { + let signed_rav: tap_graph::SignedRav = self .rav .take() .ok_or(anyhow!("Couldn't find rav"))? diff --git a/tap_aggregator/src/lib.rs b/tap_aggregator/src/lib.rs index 7ccab5e..7f98a9f 100644 --- a/tap_aggregator/src/lib.rs +++ b/tap_aggregator/src/lib.rs @@ -4,6 +4,4 @@ pub mod error_codes; pub mod grpc; pub mod jsonrpsee_helpers; pub mod metrics; -pub mod protocol_mode; -pub mod receipt_classifier; pub mod server; diff --git a/tap_aggregator/src/main.rs b/tap_aggregator/src/main.rs index 81bc764..61f2641 100644 --- a/tap_aggregator/src/main.rs +++ b/tap_aggregator/src/main.rs @@ -6,7 +6,7 @@ use anyhow::Result; use clap::Parser; use log::{debug, info}; use tap_aggregator::{metrics, server}; -use tap_core::{tap_eip712_domain, TapVersion}; +use tap_core::tap_eip712_domain; use thegraph_core::alloy::{ dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, }; @@ -62,18 +62,10 @@ struct Args { #[arg(long, env = "TAP_DOMAIN_CHAIN_ID")] domain_chain_id: Option, - /// [TAP v1] Domain verifying contract to be used for the EIP-712 domain separator. + /// Domain verifying contract to be used for the EIP-712 domain separator. #[arg(long, env = "TAP_DOMAIN_VERIFYING_CONTRACT")] domain_verifying_contract: Option
, - /// [TAP v2] Domain verifying contract to be used for the EIP-712 domain separator. - #[arg(long, env = "TAP_DOMAIN_VERIFYING_CONTRACT_V2")] - domain_verifying_contract_v2: Option
, - - /// [Shared] Domain salt to be used for the EIP-712 domain separator. - #[arg(long, env = "TAP_DOMAIN_SALT")] - domain_salt: Option, - #[arg(long, env = "TAP_KAFKA_CONFIG")] kafka_config: Option, } @@ -91,11 +83,6 @@ impl std::fmt::Debug for Args { .field("metrics_port", &self.metrics_port) .field("domain_chain_id", &self.domain_chain_id) .field("domain_verifying_contract", &self.domain_verifying_contract) - .field( - "domain_verifying_contract_v2", - &self.domain_verifying_contract_v2, - ) - .field("domain_salt", &self.domain_salt) .field("kafka_config", &self.kafka_config) .finish() } @@ -123,8 +110,7 @@ async fn main() -> Result<()> { info!("Wallet address: {:#40x}", wallet.address()); // Create the EIP-712 domain separator. - let domain_separator = create_eip712_domain(&args, TapVersion::V1)?; - let domain_separator_v2 = create_eip712_domain(&args, TapVersion::V2)?; + let domain_separator = create_eip712_domain(&args)?; // Create HashSet of *all* allowed signers let mut accepted_addresses: HashSet
= std::collections::HashSet::new(); @@ -151,7 +137,6 @@ async fn main() -> Result<()> { wallet, accepted_addresses, domain_separator, - domain_separator_v2, args.max_request_body_size, args.max_response_body_size, args.max_connections, @@ -168,11 +153,8 @@ async fn main() -> Result<()> { Ok(()) } -/// Creates the TAP EIP-712 domain separator based on the provided arguments and TAP version -fn create_eip712_domain(args: &Args, version: TapVersion) -> Result { - // Transform the args into the types expected by Eip712Domain::new(). - // Transform optional strings into optional Cow. - // Transform optional strings into optional U256. +/// Creates the TAP EIP-712 domain separator based on the provided arguments +fn create_eip712_domain(args: &Args) -> Result { if args.domain_chain_id.is_some() { debug!("Parsing domain chain ID..."); } @@ -182,20 +164,11 @@ fn create_eip712_domain(args: &Args, version: TapVersion) -> Result = match version { - TapVersion::V1 => args.domain_verifying_contract, - TapVersion::V2 => args.domain_verifying_contract_v2, - }; + let verifying_contract = args.domain_verifying_contract; // Create the EIP-712 domain separator. Ok(tap_eip712_domain( chain_id.unwrap_or(1), verifying_contract.unwrap_or_default(), - version, )) } diff --git a/tap_aggregator/src/protocol_mode.rs b/tap_aggregator/src/protocol_mode.rs deleted file mode 100644 index e88acda..0000000 --- a/tap_aggregator/src/protocol_mode.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2024 The Graph Foundation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ProtocolMode { - /// Pre-horizon: V1 receipts accepted, V1 aggregation used - Legacy, - /// Post-horizon: V2 for new receipts, V1 only for legacy receipt aggregation - Horizon, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ReceiptType { - /// V1 receipt created before horizon activation (legacy, still needs aggregation) - LegacyV1, - /// V2 receipt (collection-based, created after horizon activation) - V2, -} diff --git a/tap_aggregator/src/receipt_classifier.rs b/tap_aggregator/src/receipt_classifier.rs deleted file mode 100644 index b7091bc..0000000 --- a/tap_aggregator/src/receipt_classifier.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2024 The Graph Foundation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::Result; - -use crate::protocol_mode::ProtocolMode; - -/// Validate that a batch of v1 receipts is valid for legacy processing -pub fn validate_v1_receipt_batch(receipts: &[T]) -> Result { - if receipts.is_empty() { - return Err(anyhow::anyhow!("Cannot aggregate empty receipt batch")); - } - // All v1 receipts use legacy mode - Ok(ProtocolMode::Legacy) -} - -/// Validate that a batch of v2 receipts is valid for horizon processing -#[cfg(feature = "v2")] -pub fn validate_v2_receipt_batch(receipts: &[T]) -> Result { - if receipts.is_empty() { - return Err(anyhow::anyhow!("Cannot aggregate empty receipt batch")); - } - // All v2 receipts use horizon mode - Ok(ProtocolMode::Horizon) -} - -#[cfg(test)] -mod tests { - use tap_graph::Receipt; - - use super::*; - - #[test] - fn test_validate_v1_batch() { - use thegraph_core::alloy::primitives::Address; - let receipt = Receipt::new(Address::ZERO, 100).unwrap(); - let receipts = vec![receipt]; - assert_eq!( - validate_v1_receipt_batch(&receipts).unwrap(), - ProtocolMode::Legacy - ); - } - - #[test] - fn test_validate_v1_empty_batch_fails() { - let receipts: Vec = vec![]; - assert!(validate_v1_receipt_batch(&receipts).is_err()); - } - - #[cfg(feature = "v2")] - #[test] - fn test_validate_v2_batch() { - use tap_graph::v2; - use thegraph_core::alloy::primitives::{Address, FixedBytes}; - let receipt = v2::Receipt::new( - FixedBytes::ZERO, - Address::ZERO, - Address::ZERO, - Address::ZERO, - 100, - ) - .unwrap(); - let receipts = vec![receipt]; - assert_eq!( - validate_v2_receipt_batch(&receipts).unwrap(), - ProtocolMode::Horizon - ); - } - - #[cfg(feature = "v2")] - #[test] - fn test_validate_v2_empty_batch_fails() { - use tap_graph::v2; - let receipts: Vec = vec![]; - assert!(validate_v2_receipt_batch(&receipts).is_err()); - } -} diff --git a/tap_aggregator/src/server.rs b/tap_aggregator/src/server.rs index 6d9df68..f6392c2 100644 --- a/tap_aggregator/src/server.rs +++ b/tap_aggregator/src/server.rs @@ -11,7 +11,7 @@ use lazy_static::lazy_static; use log::{error, info}; use prometheus::{register_counter, register_int_counter, Counter, IntCounter}; use tap_core::signed_message::Eip712SignedMessage; -use tap_graph::{Receipt, ReceiptAggregateVoucher, SignedReceipt}; +use tap_graph::{Receipt, ReceiptAggregateVoucher}; use thegraph_core::alloy::{ dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, }; @@ -27,7 +27,7 @@ use crate::{ TAP_RPC_API_VERSIONS_DEPRECATED, }, error_codes::{JsonRpcErrorCode, JsonRpcWarningCode}, - grpc::{v1, v2}, + grpc::v2, jsonrpsee_helpers::{JsonRpcError, JsonRpcResponse, JsonRpcResult, JsonRpcWarning}, }; @@ -93,12 +93,6 @@ pub trait Rpc { #[method(name = "eip712domain_info")] fn eip712_domain_info(&self) -> JsonRpcResult; - /// Returns the v2 EIP-712 domain separator information used by this server. - /// The client is able to verify the signatures of the receipts and receipt aggregate vouchers. - #[cfg(feature = "v2")] - #[method(name = "eip712domain_info_v2")] - fn eip712_domain_info_v2(&self) -> JsonRpcResult; - /// Aggregates the given receipts into a receipt aggregate voucher. /// Returns an error if the user expected API version is not supported. #[method(name = "aggregate_receipts")] @@ -108,17 +102,6 @@ pub trait Rpc { receipts: Vec>, previous_rav: Option>, ) -> JsonRpcResult>; - - /// Aggregates the given v2 receipts into a v2 receipt aggregate voucher. - /// Uses the Horizon protocol for collection-based aggregation. - #[cfg(feature = "v2")] - #[method(name = "aggregate_receipts_v2")] - fn aggregate_receipts_v2( - &self, - api_version: String, - receipts: Vec>, - previous_rav: Option>, - ) -> JsonRpcResult>; } #[derive(Clone)] @@ -126,8 +109,6 @@ struct RpcImpl { wallet: PrivateKeySigner, accepted_addresses: HashSet
, domain_separator: Eip712Domain, - #[cfg(feature = "v2")] - domain_separator_v2: Eip712Domain, kafka: Option>, } @@ -168,67 +149,6 @@ fn aggregate_receipts_( receipts: Vec>, previous_rav: Option>, ) -> JsonRpcResult> { - use crate::receipt_classifier::validate_v1_receipt_batch; - - // Return an error if the API version is not supported. - let api_version = match parse_api_version(api_version.as_str()) { - Ok(v) => v, - Err(e) => { - VERSION_ERROR_COUNT.inc(); - return Err(e); - } - }; - - // Add a warning if the API version is to be deprecated. - let mut warnings: Vec = Vec::new(); - if let Some(w) = check_api_version_deprecation(&api_version) { - warnings.push(w); - DEPRECATION_WARNING_COUNT.inc(); - } - - // This endpoint handles v1 receipts for legacy aggregation - // V2 receipts are handled through the aggregate_receipts_v2 endpoint - if let Err(e) = validate_v1_receipt_batch(&receipts) { - return Err(jsonrpsee::types::ErrorObject::owned( - JsonRpcErrorCode::Aggregation as i32, - e.to_string(), - None::<()>, - )); - } - - log::debug!("Processing V1 receipts"); - - // Execute v1 aggregation - let res = aggregator::v1::check_and_aggregate_receipts( - domain_separator, - &receipts, - previous_rav, - wallet, - accepted_addresses, - ); - - // Handle aggregation error - match res { - Ok(res) => Ok(JsonRpcResponse::warn(res, warnings)), - Err(e) => Err(jsonrpsee::types::ErrorObject::owned( - JsonRpcErrorCode::Aggregation as i32, - e.to_string(), - None::<()>, - )), - } -} - -#[cfg(feature = "v2")] -fn aggregate_receipts_v2_( - api_version: String, - wallet: &PrivateKeySigner, - accepted_addresses: &HashSet
, - domain_separator: &Eip712Domain, - receipts: Vec>, - previous_rav: Option>, -) -> JsonRpcResult> { - use crate::receipt_classifier::validate_v2_receipt_batch; - // Return an error if the API version is not supported. let api_version = match parse_api_version(api_version.as_str()) { Ok(v) => v, @@ -245,19 +165,19 @@ fn aggregate_receipts_v2_( DEPRECATION_WARNING_COUNT.inc(); } - // Validate v2 receipt batch for horizon processing - if let Err(e) = validate_v2_receipt_batch(&receipts) { + // Validate receipt batch is not empty + if receipts.is_empty() { return Err(jsonrpsee::types::ErrorObject::owned( JsonRpcErrorCode::Aggregation as i32, - e.to_string(), + "Cannot aggregate empty receipt batch".to_string(), None::<()>, )); } - log::debug!("Processing V2 receipts with Horizon protocol"); + log::debug!("Processing receipts"); - // Execute v2 aggregation - let res = aggregator::v2::check_and_aggregate_receipts( + // Execute aggregation + let res = aggregator::check_and_aggregate_receipts( domain_separator, &receipts, previous_rav, @@ -276,62 +196,6 @@ fn aggregate_receipts_v2_( } } -#[tonic::async_trait] -impl v1::tap_aggregator_server::TapAggregator for RpcImpl { - async fn aggregate_receipts( - &self, - request: Request, - ) -> Result, Status> { - let rav_request = request.into_inner(); - let receipts: Vec = rav_request - .receipts - .into_iter() - .map(TryFrom::try_from) - .collect::>() - .map_err(|_| Status::invalid_argument("Error while getting list of signed_receipts"))?; - - let previous_rav = rav_request - .previous_rav - .map(TryFrom::try_from) - .transpose() - .map_err(|_| Status::invalid_argument("Error while getting previous rav"))?; - - let receipts_grt: u128 = receipts.iter().map(|r| r.message.value).sum(); - let receipts_count: u64 = receipts.len() as u64; - - match aggregator::v1::check_and_aggregate_receipts( - &self.domain_separator, - receipts.as_slice(), - previous_rav, - &self.wallet, - &self.accepted_addresses, - ) { - Ok(res) => { - TOTAL_GRT_AGGREGATED.inc_by(receipts_grt as f64); - TOTAL_AGGREGATED_RECEIPTS.inc_by(receipts_count); - AGGREGATION_SUCCESS_COUNTER.inc(); - if let Some(kafka) = &self.kafka { - produce_kafka_records( - kafka, - &self.wallet.address(), - &res.message.allocationId, - res.message.valueAggregate, - ); - } - - let response = v1::RavResponse { - rav: Some(res.into()), - }; - Ok(Response::new(response)) - } - Err(e) => { - AGGREGATION_FAILURE_COUNTER.inc(); - Err(Status::failed_precondition(e.to_string())) - } - } - } -} - #[tonic::async_trait] impl v2::tap_aggregator_server::TapAggregator for RpcImpl { async fn aggregate_receipts( @@ -339,7 +203,7 @@ impl v2::tap_aggregator_server::TapAggregator for RpcImpl { request: Request, ) -> Result, Status> { let rav_request = request.into_inner(); - let receipts: Vec = rav_request + let receipts: Vec = rav_request .receipts .into_iter() .map(TryFrom::try_from) @@ -355,8 +219,8 @@ impl v2::tap_aggregator_server::TapAggregator for RpcImpl { let receipts_grt: u128 = receipts.iter().map(|r| r.message.value).sum(); let receipts_count: u64 = receipts.len() as u64; - match aggregator::v2::check_and_aggregate_receipts( - &self.domain_separator_v2, + match aggregator::check_and_aggregate_receipts( + &self.domain_separator, receipts.as_slice(), previous_rav, &self.wallet, @@ -397,11 +261,6 @@ impl RpcServer for RpcImpl { Ok(JsonRpcResponse::ok(self.domain_separator.clone())) } - #[cfg(feature = "v2")] - fn eip712_domain_info_v2(&self) -> JsonRpcResult { - Ok(JsonRpcResponse::ok(self.domain_separator_v2.clone())) - } - fn aggregate_receipts( &self, api_version: String, @@ -425,47 +284,6 @@ impl RpcServer for RpcImpl { TOTAL_AGGREGATED_RECEIPTS.inc_by(receipts_count); AGGREGATION_SUCCESS_COUNTER.inc(); if let Some(kafka) = &self.kafka { - produce_kafka_records( - kafka, - &self.wallet.address(), - &res.data.message.allocationId, - res.data.message.valueAggregate, - ); - } - Ok(res) - } - Err(e) => { - AGGREGATION_FAILURE_COUNTER.inc(); - Err(e) - } - } - } - - #[cfg(feature = "v2")] - fn aggregate_receipts_v2( - &self, - api_version: String, - receipts: Vec>, - previous_rav: Option>, - ) -> JsonRpcResult> { - // Values for Prometheus metrics - let receipts_grt: u128 = receipts.iter().map(|r| r.message.value).sum(); - let receipts_count: u64 = receipts.len() as u64; - - match aggregate_receipts_v2_( - api_version, - &self.wallet, - &self.accepted_addresses, - &self.domain_separator_v2, - receipts, - previous_rav, - ) { - Ok(res) => { - TOTAL_GRT_AGGREGATED.inc_by(receipts_grt as f64); - TOTAL_AGGREGATED_RECEIPTS.inc_by(receipts_count); - AGGREGATION_SUCCESS_COUNTER.inc(); - if let Some(kafka) = &self.kafka { - // V2 RAVs use collectionId instead of allocationId produce_kafka_records( kafka, &self.wallet.address(), @@ -489,7 +307,6 @@ pub async fn run_server( wallet: PrivateKeySigner, accepted_addresses: HashSet
, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, max_request_body_size: u32, max_response_body_size: u32, max_concurrent_connections: u32, @@ -501,7 +318,6 @@ pub async fn run_server( wallet, accepted_addresses, domain_separator, - domain_separator_v2, kafka, }; let (json_rpc_service, _) = create_json_rpc_service( @@ -602,10 +418,6 @@ async fn shutdown_handler() { fn create_grpc_service(rpc_impl: RpcImpl) -> Result { let grpc_service = Routes::new( - v1::tap_aggregator_server::TapAggregatorServer::new(rpc_impl.clone()) - .accept_compressed(CompressionEncoding::Zstd), - ) - .add_service( v2::tap_aggregator_server::TapAggregatorServer::new(rpc_impl) .accept_compressed(CompressionEncoding::Zstd), ) @@ -669,10 +481,12 @@ mod tests { use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; use rstest::*; - use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; + use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain}; use tap_graph::{Receipt, ReceiptAggregateVoucher}; use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, }; use crate::server; @@ -690,22 +504,28 @@ mod tests { } #[fixture] - fn allocation_ids() -> Vec
{ - vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), - ] + fn collection_id() -> FixedBytes<32> { + FixedBytes::from([0xab; 32]) } #[fixture] - fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() } + #[fixture] - fn domain_separator_v2() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x22u8; 20]), TapVersion::V2) + fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() + } + + #[fixture] + fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() + } + + #[fixture] + fn domain_separator() -> Eip712Domain { + tap_eip712_domain(1, Address::from([0x11u8; 20])) } #[fixture] @@ -732,7 +552,6 @@ mod tests { #[tokio::test] async fn protocol_version( domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -747,7 +566,6 @@ mod tests { keys_main.wallet, HashSet::from([keys_main.address]), domain_separator, - domain_separator_v2, http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -775,12 +593,14 @@ mod tests { #[tokio::test] async fn signed_rav_is_valid_with_no_previous_rav( domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, request_timeout: Duration, - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, #[case] values: Vec, #[values("0.0")] api_version: &str, #[values(0, 1, 2)] random_seed: u64, @@ -803,7 +623,6 @@ mod tests { keys_main.wallet.clone(), HashSet::from([keys_main.address, keys_0.address, keys_1.address]), domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -824,7 +643,8 @@ mod tests { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value) + .unwrap(), &all_wallets.choose(&mut rng).unwrap().wallet, ) .unwrap(), @@ -843,11 +663,17 @@ mod tests { let remote_rav = res.data; - let local_rav = - ReceiptAggregateVoucher::aggregate_receipts(allocation_ids[0], &receipts, None) - .unwrap(); + let local_rav = ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + &receipts, + None, + ) + .unwrap(); - assert!(remote_rav.message.allocationId == local_rav.allocationId); + assert!(remote_rav.message.collectionId == local_rav.collectionId); assert!(remote_rav.message.timestampNs == local_rav.timestampNs); assert!(remote_rav.message.valueAggregate == local_rav.valueAggregate); @@ -862,12 +688,14 @@ mod tests { #[tokio::test] async fn signed_rav_is_valid_with_previous_rav( domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, request_timeout: Duration, - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, #[case] values: Vec, #[values("0.0")] api_version: &str, #[values(0, 1, 2, 3, 4)] random_seed: u64, @@ -890,7 +718,6 @@ mod tests { keys_main.wallet.clone(), HashSet::from([keys_main.address, keys_0.address, keys_1.address]), domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -911,7 +738,8 @@ mod tests { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value) + .unwrap(), &all_wallets.choose(&mut rng).unwrap().wallet, ) .unwrap(), @@ -920,7 +748,10 @@ mod tests { // Create previous RAV from first half of receipts locally let prev_rav = ReceiptAggregateVoucher::aggregate_receipts( - allocation_ids[0], + collection_id, + payer, + data_service, + service_provider, &receipts[0..receipts.len() / 2], None, ) @@ -956,12 +787,14 @@ mod tests { #[tokio::test] async fn invalid_api_version( domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, request_timeout: Duration, - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, ) { // The keys that will be used to sign the new RAVs let keys_main = keys(); @@ -972,7 +805,6 @@ mod tests { keys_main.wallet.clone(), HashSet::from([keys_main.address]), domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -990,7 +822,7 @@ mod tests { // Create receipts let receipts = vec![Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], 42).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, 42).unwrap(), &keys_main.wallet, ) .unwrap()]; @@ -1043,11 +875,13 @@ mod tests { #[tokio::test] async fn request_size_limit( domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_response_size_limit: u32, http_max_concurrent_connections: u32, request_timeout: Duration, - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, #[values("0.0")] api_version: &str, ) { // The keys that will be used to sign the new RAVs @@ -1058,9 +892,9 @@ mod tests { let http_request_size_limit = 100 * 1024; // Number of receipts that is just above the number that would fit within the - // request size limit. This value is hard-coded here because it supports the - // maximum number of receipts per aggregate value we wrote in the spec / docs. - let number_of_receipts_to_exceed_limit = 300; + // request size limit. V2 receipts are larger than V1 (more fields), so this + // value accounts for the larger receipt size. + let number_of_receipts_to_exceed_limit = 210; // Start the JSON-RPC server. let (handle, local_addr) = server::run_server( @@ -1068,7 +902,6 @@ mod tests { keys_main.wallet.clone(), HashSet::from([keys_main.address]), domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -1089,7 +922,14 @@ mod tests { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], u128::MAX / 1000).unwrap(), + Receipt::new( + collection_id, + payer, + data_service, + service_provider, + u128::MAX / 1000, + ) + .unwrap(), &keys_main.wallet, ) .unwrap(), diff --git a/tap_aggregator/tests/aggregate_test.rs b/tap_aggregator/tests/aggregate_test.rs index f18d68b..d4134c3 100644 --- a/tap_aggregator/tests/aggregate_test.rs +++ b/tap_aggregator/tests/aggregate_test.rs @@ -2,19 +2,21 @@ use std::{collections::HashSet, str::FromStr, time::Duration}; use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; use tap_aggregator::{ - grpc::v1::{tap_aggregator_client::TapAggregatorClient, RavRequest}, + grpc::v2::{tap_aggregator_client::TapAggregatorClient, RavRequest}, jsonrpsee_helpers::JsonRpcResponse, server, }; -use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; +use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain}; use tap_graph::{Receipt, ReceiptAggregateVoucher}; -use thegraph_core::alloy::{primitives::Address, signers::local::PrivateKeySigner}; +use thegraph_core::alloy::{ + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, +}; use tonic::codec::CompressionEncoding; #[tokio::test] async fn aggregation_test() { - let domain_separator = tap_eip712_domain(1, Address::ZERO, TapVersion::V1); - let domain_separator_v2 = tap_eip712_domain(1, Address::ZERO, TapVersion::V2); + let domain_separator = tap_eip712_domain(1, Address::ZERO); let wallet = PrivateKeySigner::random(); @@ -29,7 +31,6 @@ async fn aggregation_test() { wallet.clone(), accepted_addresses, domain_separator.clone(), - domain_separator_v2.clone(), max_request_body_size, max_response_body_size, max_concurrent_connections, @@ -46,7 +47,10 @@ async fn aggregation_test() { .unwrap() .send_compressed(CompressionEncoding::Zstd); - let allocation_id = Address::from_str("0xabababababababababababababababababababab").unwrap(); + let collection_id = FixedBytes::from([0xab; 32]); + let payer = Address::from_str("0xabababababababababababababababababababab").unwrap(); + let data_service = Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(); + let service_provider = Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(); // Create receipts let mut receipts = Vec::new(); @@ -54,7 +58,7 @@ async fn aggregation_test() { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_id, value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value).unwrap(), &wallet, ) .unwrap(), @@ -73,7 +77,7 @@ async fn aggregation_test() { .request( "aggregate_receipts", rpc_params!( - "0.0", // TODO: Set the version in a smarter place. + "0.0", // API version receipts, previous_rav ), diff --git a/tap_aggregator/tests/aggregate_v1_and_v2.rs b/tap_aggregator/tests/aggregate_v1_and_v2.rs deleted file mode 100644 index 19f1b37..0000000 --- a/tap_aggregator/tests/aggregate_v1_and_v2.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::{collections::HashSet, time::Duration}; - -use tap_aggregator::{ - grpc::{ - v1::{tap_aggregator_client::TapAggregatorClient as ClientV1, RavRequest as ReqV1}, - v2::{tap_aggregator_client::TapAggregatorClient as ClientV2, RavRequest as ReqV2}, - }, - server, -}; -use tap_core::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; -use tap_graph::{v2::Receipt as ReceiptV2, Receipt as ReceiptV1}; -use thegraph_core::alloy::{ - primitives::{address, Address, FixedBytes}, - signers::local::PrivateKeySigner, -}; -use tonic::codec::CompressionEncoding; - -#[tokio::test] -async fn aggregation_test() { - let domain_separator = tap_eip712_domain(1, Address::ZERO, TapVersion::V2); - let domain_separator_v2 = tap_eip712_domain(1, Address::ZERO, TapVersion::V2); - - let wallet = PrivateKeySigner::random(); - - let max_request_body_size = 1024 * 100; - let max_response_body_size = 1024 * 100; - let max_concurrent_connections = 1; - - let accepted_addresses = HashSet::from([wallet.address()]); - - let (_, local_addr) = server::run_server( - 0, - wallet.clone(), - accepted_addresses, - domain_separator.clone(), - domain_separator_v2.clone(), - max_request_body_size, - max_response_body_size, - max_concurrent_connections, - Duration::from_secs(60), - None, - ) - .await - .unwrap(); - - let endpoint = format!("http://127.0.0.1:{}", local_addr.port()); - - let mut client = ClientV1::connect(endpoint.clone()) - .await - .unwrap() - .send_compressed(CompressionEncoding::Zstd); - - let allocation_id = address!("abababababababababababababababababababab"); - let mut padded = [0u8; 32]; - padded[12..].copy_from_slice(allocation_id.as_slice()); - - let collection_id = FixedBytes::<32>::from(padded); - - // Create receipts - let mut receipts = Vec::new(); - for value in 50..60 { - receipts.push( - Eip712SignedMessage::new( - &domain_separator, - ReceiptV1::new(allocation_id, value).unwrap(), - &wallet, - ) - .unwrap(), - ); - } - - let rav_request = ReqV1::new(receipts.clone(), None); - let res = client.aggregate_receipts(rav_request).await; - - assert!(res.is_ok()); - - let mut client = ClientV2::connect(endpoint.clone()) - .await - .unwrap() - .send_compressed(CompressionEncoding::Zstd); - - let payer = address!("abababababababababababababababababababab"); - let data_service = address!("abababababababababababababababababababab"); - let service_provider = address!("abababababababababababababababababababab"); - - // Create receipts - let mut receipts = Vec::new(); - for value in 50..60 { - receipts.push( - Eip712SignedMessage::new( - &domain_separator, - ReceiptV2::new(collection_id, payer, data_service, service_provider, value) - .unwrap(), - &wallet, - ) - .unwrap(), - ); - } - - let rav_request = ReqV2::new(receipts.clone(), None); - let res = client.aggregate_receipts(rav_request).await; - - assert!(res.is_ok()); -} diff --git a/tap_core/src/error.rs b/tap_core/src/error.rs index eb8a7c5..2dd57c2 100644 --- a/tap_core/src/error.rs +++ b/tap_core/src/error.rs @@ -45,15 +45,15 @@ pub enum Error { #[error("Failed to produce rav request, no valid receipts")] NoValidReceiptsForRavRequest, - /// Error when the previous RAV allocation id does not match the allocation id from the new receipt - #[error("Previous RAV allocation id ({prev_id}) doesn't match the allocation id from the new receipt ({new_id}).")] - RavAllocationIdMismatch { prev_id: String, new_id: String }, + /// Error when the previous RAV collection id does not match the collection id from the new receipt + #[error("Previous RAV collection id ({prev_id}) doesn't match the collection id from the new receipt ({new_id}).")] + RavCollectionIdMismatch { prev_id: String, new_id: String }, - /// Error when all receipts do not have the same allocation id + /// Error when all receipts do not have the same collection id /// /// Used in tap_aggregator - #[error("All receipts should have the same allocation id, but they don't")] - RavAllocationIdNotUniform, + #[error("All receipts should have the same collection id, but they don't")] + RavCollectionIdNotUniform, /// Error when the receipt signature is duplicated. /// /// Used in tap_aggregator diff --git a/tap_core/src/lib.rs b/tap_core/src/lib.rs index 250464a..278bba2 100644 --- a/tap_core/src/lib.rs +++ b/tap_core/src/lib.rs @@ -31,45 +31,19 @@ fn get_current_timestamp_u64_ns() -> Result { /// /// This is the current domain separator that is used for the [EIP712](https://eips.ethereum.org/EIPS/eip-712) signature scheme. /// -/// /// It's used to validate the signature of the `ReceiptAggregateVoucher` and `Receipt` structs. /// /// You can take a look on deployed [GraphTallyCollector](https://github.com/graphprotocol/contracts/blob/main/packages/horizon/contracts/payments/collectors/GraphTallyCollector.sol) /// contract [here](https://github.com/graphprotocol/contracts/blob/main/packages/horizon/addresses.json) /// -/// TAP protocol version for EIP-712 domain separator -#[derive(Debug, Clone, Copy)] -pub enum TapVersion { - V1, - V2, -} - -impl TapVersion { - pub fn as_str(&self) -> &'static str { - match self { - TapVersion::V1 => "1", - TapVersion::V2 => "2", - } - } -} - /// The domain separator is defined as: -/// - `name`: "TAP" for V1, "GraphTallyCollector" for V2 - This could be a fn argument but we don't want to change the function signature. -/// - `version`: always set to "1", what changes is the domain name. +/// - `name`: "GraphTallyCollector" +/// - `version`: "1" /// - `chain_id`: The chain ID of the chain where the domain separator is deployed. /// - `verifying_contract`: The address of the contract that is verifying the signature. -pub fn tap_eip712_domain( - chain_id: u64, - verifying_contract_address: Address, - version: TapVersion, -) -> Eip712Domain { - let name = match version { - TapVersion::V1 => "TAP", - TapVersion::V2 => "GraphTallyCollector", - }; - +pub fn tap_eip712_domain(chain_id: u64, verifying_contract_address: Address) -> Eip712Domain { eip712_domain! { - name: name, + name: "GraphTallyCollector", version: "1", chain_id: chain_id, verifying_contract: verifying_contract_address, @@ -83,10 +57,12 @@ mod tap_tests { use rstest::*; use tap_graph::{Receipt, ReceiptAggregateVoucher}; use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, }; - use crate::{signed_message::Eip712SignedMessage, tap_eip712_domain, TapVersion}; + use crate::{signed_message::Eip712SignedMessage, tap_eip712_domain}; #[fixture] fn keys() -> (PrivateKeySigner, Address) { @@ -97,18 +73,28 @@ mod tap_tests { } #[fixture] - fn allocation_ids() -> Vec
{ - vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), - ] + fn collection_id() -> FixedBytes<32> { + FixedBytes::from([0xab; 32]) + } + + #[fixture] + fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() + } + + #[fixture] + fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() + } + + #[fixture] + fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() } #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } #[rstest] @@ -117,7 +103,10 @@ mod tap_tests { #[test] fn signed_rav_is_valid_with_no_previous_rav( keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, #[case] values: Vec, ) { @@ -127,7 +116,8 @@ mod tap_tests { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value) + .unwrap(), &keys.0, ) .unwrap(), @@ -136,8 +126,15 @@ mod tap_tests { // Skipping receipts validation in this test, aggregate_receipts assumes receipts are valid. - let rav = ReceiptAggregateVoucher::aggregate_receipts(allocation_ids[0], &receipts, None) - .unwrap(); + let rav = ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + &receipts, + None, + ) + .unwrap(); let signed_rav = Eip712SignedMessage::new(&domain_separator, rav, &keys.0).unwrap(); assert!(signed_rav.recover_signer(&domain_separator).unwrap() == keys.1); } @@ -148,7 +145,10 @@ mod tap_tests { #[test] fn signed_rav_is_valid_with_previous_rav( keys: (PrivateKeySigner, Address), - allocation_ids: Vec
, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, #[case] values: Vec, ) { @@ -158,7 +158,8 @@ mod tap_tests { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value) + .unwrap(), &keys.0, ) .unwrap(), @@ -167,7 +168,10 @@ mod tap_tests { // Create previous RAV from first half of receipts let prev_rav = ReceiptAggregateVoucher::aggregate_receipts( - allocation_ids[0], + collection_id, + payer, + data_service, + service_provider, &receipts[0..receipts.len() / 2], None, ) @@ -177,7 +181,10 @@ mod tap_tests { // Create new RAV from last half of receipts and prev_rav let rav = ReceiptAggregateVoucher::aggregate_receipts( - allocation_ids[0], + collection_id, + payer, + data_service, + service_provider, &receipts[receipts.len() / 2..receipts.len()], Some(signed_prev_rav), ) diff --git a/tap_core/src/manager/context/memory.rs b/tap_core/src/manager/context/memory.rs index 8aeb287..68e319c 100644 --- a/tap_core/src/manager/context/memory.rs +++ b/tap_core/src/manager/context/memory.rs @@ -252,7 +252,10 @@ pub mod checks { }; use tap_graph::SignedReceipt; - use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::Address}; + use thegraph_core::alloy::{ + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + }; use crate::{ receipt::{ @@ -266,13 +269,13 @@ pub mod checks { pub fn get_full_list_of_checks( domain_separator: Eip712Domain, valid_signers: HashSet
, - allocation_ids: Arc>>, + collection_ids: Arc>>>, _query_appraisals: Arc>>, ) -> Vec> { vec![ // Arc::new(UniqueCheck ), // Arc::new(ValueCheck { query_appraisals }), - Arc::new(AllocationIdCheck { allocation_ids }), + Arc::new(CollectionIdCheck { collection_ids }), Arc::new(SignatureCheck { domain_separator, valid_signers, @@ -280,29 +283,29 @@ pub mod checks { ] } - struct AllocationIdCheck { - allocation_ids: Arc>>, + struct CollectionIdCheck { + collection_ids: Arc>>>, } #[async_trait::async_trait] - impl Check for AllocationIdCheck { + impl Check for CollectionIdCheck { async fn check( &self, _: &Context, receipt: &ReceiptWithState, ) -> CheckResult { - let received_allocation_id = receipt.signed_receipt().message.allocation_id; + let received_collection_id = receipt.signed_receipt().message.collection_id; if self - .allocation_ids + .collection_ids .read() .unwrap() - .contains(&received_allocation_id) + .contains(&received_collection_id) { Ok(()) } else { Err(CheckError::Failed( - ReceiptError::InvalidAllocationID { - received_allocation_id, + ReceiptError::InvalidCollectionID { + received_collection_id, } .into(), )) diff --git a/tap_core/src/manager/mod.rs b/tap_core/src/manager/mod.rs index 9e7575d..288f00c 100644 --- a/tap_core/src/manager/mod.rs +++ b/tap_core/src/manager/mod.rs @@ -58,12 +58,16 @@ //! } //! # #[tokio::main(flavor = "current_thread")] //! # async fn main() { -//! # use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner}; +//! # use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::{Address, FixedBytes}, signers::local::PrivateKeySigner}; //! # use tap_graph::{Receipt, SignedReceipt}; //! # use tap_core::signed_message::Eip712SignedMessage; //! # let domain_separator = Eip712Domain::default(); //! # let wallet = PrivateKeySigner::random(); -//! # let message = Receipt::new(Address::from([0x11u8; 20]), 100).unwrap(); +//! # let collection_id = FixedBytes::from([0xab; 32]); +//! # let payer = Address::from([0x11u8; 20]); +//! # let data_service = Address::from([0x22u8; 20]); +//! # let service_provider = Address::from([0x33u8; 20]); +//! # let message = Receipt::new(collection_id, payer, data_service, service_provider, 100).unwrap(); //! //! let receipt = Eip712SignedMessage::new(&domain_separator, message, &wallet).unwrap(); //! diff --git a/tap_core/src/signed_message.rs b/tap_core/src/signed_message.rs index 63d45dd..f2292a6 100644 --- a/tap_core/src/signed_message.rs +++ b/tap_core/src/signed_message.rs @@ -5,13 +5,17 @@ //! //! # Example //! ```rust -//! # use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner}; +//! # use thegraph_core::alloy::{dyn_abi::Eip712Domain, primitives::{Address, FixedBytes}, signers::local::PrivateKeySigner}; //! # use tap_graph::{Receipt}; //! # let domain_separator = Eip712Domain::default(); //! use tap_core::signed_message::Eip712SignedMessage; //! # let wallet = PrivateKeySigner::random(); //! # let wallet_address = wallet.address(); -//! # let message = Receipt::new(Address::from([0x11u8; 20]), 100).unwrap(); +//! # let collection_id = FixedBytes::from([0xab; 32]); +//! # let payer = Address::from([0x11u8; 20]); +//! # let data_service = Address::from([0x22u8; 20]); +//! # let service_provider = Address::from([0x33u8; 20]); +//! # let message = Receipt::new(collection_id, payer, data_service, service_provider, 100).unwrap(); //! //! let signed_message = Eip712SignedMessage::new(&domain_separator, message, &wallet).unwrap(); //! let signer = signed_message.recover_signer(&domain_separator).unwrap(); diff --git a/tap_core/tests/manager_test.rs b/tap_core/tests/manager_test.rs index 5da4cf7..f9e0e6a 100644 --- a/tap_core/tests/manager_test.rs +++ b/tap_core/tests/manager_test.rs @@ -8,7 +8,9 @@ use std::{ use anyhow::anyhow; use rstest::*; use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, + dyn_abi::Eip712Domain, + primitives::{Address, Bytes, FixedBytes}, + signers::local::PrivateKeySigner, }; fn get_current_timestamp_u64_ns() -> anyhow::Result { @@ -29,7 +31,7 @@ use tap_core::{ Context, ReceiptWithState, }, signed_message::Eip712SignedMessage, - tap_eip712_domain, TapVersion, + tap_eip712_domain, }; use tap_graph::{Receipt, ReceiptAggregateVoucher, SignedReceipt}; @@ -39,15 +41,30 @@ fn signer() -> PrivateKeySigner { } #[fixture] -fn allocation_ids() -> Vec
{ +fn collection_ids() -> Vec> { vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), + FixedBytes::from([0xab; 32]), + FixedBytes::from([0xcd; 32]), + FixedBytes::from([0xef; 32]), + FixedBytes::from([0x12; 32]), ] } +#[fixture] +fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() +} + +#[fixture] +fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() +} + +#[fixture] +fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() +} + #[fixture] fn sender_ids(signer: PrivateKeySigner) -> (PrivateKeySigner, Vec
) { let address = signer.address(); @@ -64,7 +81,7 @@ fn sender_ids(signer: PrivateKeySigner) -> (PrivateKeySigner, Vec
) { #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } struct ContextFixture { @@ -78,7 +95,7 @@ struct ContextFixture { #[fixture] fn context( domain_separator: Eip712Domain, - allocation_ids: Vec
, + collection_ids: Vec>, sender_ids: (PrivateKeySigner, Vec
), ) -> ContextFixture { let (signer, sender_ids) = sender_ids; @@ -98,7 +115,7 @@ fn context( let mut checks = get_full_list_of_checks( domain_separator, sender_ids.iter().cloned().collect(), - Arc::new(RwLock::new(allocation_ids.iter().cloned().collect())), + Arc::new(RwLock::new(collection_ids.iter().cloned().collect())), query_appraisals.clone(), ); checks.push(timestamp_check); @@ -116,7 +133,10 @@ fn context( #[rstest] #[tokio::test] async fn manager_verify_and_store_varying_initial_checks( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -133,7 +153,14 @@ async fn manager_verify_and_store_varying_initial_checks( let value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -153,7 +180,10 @@ async fn manager_verify_and_store_varying_initial_checks( #[rstest] #[tokio::test] async fn manager_create_rav_request_all_valid_receipts( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -176,7 +206,14 @@ async fn manager_create_rav_request_all_valid_receipts( let value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -212,7 +249,14 @@ async fn manager_create_rav_request_all_valid_receipts( #[rstest] #[tokio::test] -async fn deny_rav_due_to_wrong_value(domain_separator: Eip712Domain, context: ContextFixture) { +async fn deny_rav_due_to_wrong_value( + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, + domain_separator: Eip712Domain, + context: ContextFixture, +) { let ContextFixture { context, checks, @@ -222,15 +266,23 @@ async fn deny_rav_due_to_wrong_value(domain_separator: Eip712Domain, context: Co let manager = Manager::new(domain_separator.clone(), context, checks); let rav = ReceiptAggregateVoucher { - allocationId: Address::from_str("0xabababababababababababababababababababab").unwrap(), + collectionId: collection_ids[0], timestampNs: 1232442, valueAggregate: 20u128, + payer, + dataService: data_service, + serviceProvider: service_provider, + metadata: Bytes::new(), }; let rav_wrong_value = ReceiptAggregateVoucher { - allocationId: Address::from_str("0xabababababababababababababababababababab").unwrap(), + collectionId: collection_ids[0], timestampNs: 1232442, valueAggregate: 10u128, + payer, + dataService: data_service, + serviceProvider: service_provider, + metadata: Bytes::new(), }; let signed_rav_with_wrong_aggregate = @@ -245,7 +297,10 @@ async fn deny_rav_due_to_wrong_value(domain_separator: Eip712Domain, context: Co #[rstest] #[tokio::test] async fn manager_create_multiple_rav_requests_all_valid_receipts( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -271,7 +326,14 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts( let value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -314,7 +376,14 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts( let value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -357,7 +426,10 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts( #[rstest] #[tokio::test] async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_timestamps( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, #[values(true, false)] remove_old_receipts: bool, context: ContextFixture, @@ -383,7 +455,14 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_tim let mut expected_accumulated_value = 0; for query_id in 0..10 { let value = 20u128; - let mut receipt = Receipt::new(allocation_ids[0], value).unwrap(); + let mut receipt = Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(); receipt.timestamp_ns = starting_min_timestamp + query_id + 1; let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap(); @@ -431,7 +510,14 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_tim stored_signed_receipts.clear(); for query_id in 10..20 { let value = 20u128; - let mut receipt = Receipt::new(allocation_ids[0], value).unwrap(); + let mut receipt = Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + value, + ) + .unwrap(); receipt.timestamp_ns = starting_min_timestamp + query_id + 1; let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap(); let query_id = signed_receipt.unique_hash(); @@ -487,7 +573,10 @@ async fn manager_create_multiple_rav_requests_all_valid_receipts_consecutive_tim #[rstest] #[tokio::test] async fn manager_create_rav_and_ignore_invalid_receipts( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -510,10 +599,13 @@ async fn manager_create_rav_and_ignore_invalid_receipts( //Forcing all receipts but one to be invalid by making all the same for _ in 0..10 { let receipt = Receipt { - allocation_id: allocation_ids[0], + collection_id: collection_ids[0], timestamp_ns: 1, nonce: 1, value: 20u128, + payer, + data_service, + service_provider, }; let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap(); stored_signed_receipts.push(signed_receipt.clone()); @@ -539,7 +631,10 @@ async fn manager_create_rav_and_ignore_invalid_receipts( #[rstest] #[tokio::test] async fn test_retryable_checks( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -591,10 +686,13 @@ async fn test_retryable_checks( let mut stored_signed_receipts = Vec::new(); for i in 0..10 { let receipt = Receipt { - allocation_id: allocation_ids[0], + collection_id: collection_ids[0], timestamp_ns: i + 1, nonce: i, value: 20u128, + payer, + data_service, + service_provider, }; let signed_receipt = Eip712SignedMessage::new(&domain_separator, receipt, &signer).unwrap(); stored_signed_receipts.push(signed_receipt.clone()); diff --git a/tap_core/tests/rav_test.rs b/tap_core/tests/rav_test.rs index 2e9a1ed..2c6f6b5 100644 --- a/tap_core/tests/rav_test.rs +++ b/tap_core/tests/rav_test.rs @@ -12,19 +12,16 @@ use tap_core::{ }, receipt::checks::StatefulTimestampCheck, signed_message::Eip712SignedMessage, - tap_eip712_domain, TapVersion, + tap_eip712_domain, }; use tap_graph::{Receipt, ReceiptAggregateVoucher}; #[allow(deprecated)] -use thegraph_core::alloy::primitives::{Address, Signature}; -use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, - signers::local::{coins_bip39::English, MnemonicBuilder, PrivateKeySigner}, -}; +use thegraph_core::alloy::primitives::{Address, FixedBytes, Signature, B256}; +use thegraph_core::alloy::{dyn_abi::Eip712Domain, signers::local::PrivateKeySigner}; #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } #[fixture] @@ -42,23 +39,50 @@ fn context() -> InMemoryContext { ) } +#[fixture] +fn collection_id() -> FixedBytes<32> { + FixedBytes::from([0xab; 32]) +} + +#[fixture] +fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() +} + +#[fixture] +fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() +} + +#[fixture] +fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() +} + #[rstest] -fn check_for_rav_serialization(domain_separator: Eip712Domain) { - let allocation_id = Address::from_str("0xabababababababababababababababababababab").unwrap(); - let wallet = MnemonicBuilder::::default() - .phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") - .build() - .unwrap(); +fn check_for_rav_serialization( + domain_separator: Eip712Domain, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, +) { + // Use a deterministic private key for reproducible signatures + let private_key = B256::repeat_byte(0x01); + let wallet = PrivateKeySigner::from_bytes(&private_key).unwrap(); let mut receipts = Vec::new(); for value in 50..60 { receipts.push( Eip712SignedMessage::new( &domain_separator, Receipt { - allocation_id, + collection_id, value, nonce: value as u64, timestamp_ns: value as u64, + payer, + data_service, + service_provider, }, &wallet, ) @@ -68,7 +92,15 @@ fn check_for_rav_serialization(domain_separator: Eip712Domain) { let signed_rav = Eip712SignedMessage::new( &domain_separator, - ReceiptAggregateVoucher::aggregate_receipts(allocation_id, &receipts, None).unwrap(), + ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + &receipts, + None, + ) + .unwrap(), &wallet, ) .unwrap(); @@ -89,18 +121,23 @@ fn check_for_rav_serialization(domain_separator: Eip712Domain) { #[rstest] #[tokio::test] -async fn rav_storage_adapter_test(domain_separator: Eip712Domain, context: InMemoryContext) { +async fn rav_storage_adapter_test( + domain_separator: Eip712Domain, + context: InMemoryContext, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, +) { let wallet = PrivateKeySigner::random(); - let allocation_id = Address::from_str("0xabababababababababababababababababababab").unwrap(); - // Create receipts let mut receipts = Vec::new(); for value in 50..60 { receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_id, value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value).unwrap(), &wallet, ) .unwrap(), @@ -109,7 +146,15 @@ async fn rav_storage_adapter_test(domain_separator: Eip712Domain, context: InMem let signed_rav = Eip712SignedMessage::new( &domain_separator, - ReceiptAggregateVoucher::aggregate_receipts(allocation_id, &receipts, None).unwrap(), + ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + &receipts, + None, + ) + .unwrap(), &wallet, ) .unwrap(); @@ -128,7 +173,7 @@ async fn rav_storage_adapter_test(domain_separator: Eip712Domain, context: InMem receipts.push( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_id, value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value).unwrap(), &wallet, ) .unwrap(), @@ -137,7 +182,15 @@ async fn rav_storage_adapter_test(domain_separator: Eip712Domain, context: InMem let signed_rav = Eip712SignedMessage::new( &domain_separator, - ReceiptAggregateVoucher::aggregate_receipts(allocation_id, &receipts, None).unwrap(), + ReceiptAggregateVoucher::aggregate_receipts( + collection_id, + payer, + data_service, + service_provider, + &receipts, + None, + ) + .unwrap(), &wallet, ) .unwrap(); diff --git a/tap_core/tests/receipt_test.rs b/tap_core/tests/receipt_test.rs index 6bbb58c..b330088 100644 --- a/tap_core/tests/receipt_test.rs +++ b/tap_core/tests/receipt_test.rs @@ -9,16 +9,18 @@ use tap_core::{ manager::{adapters::ReceiptStore, context::memory::InMemoryContext}, receipt::{checks::StatefulTimestampCheck, state::Checking, ReceiptWithState}, signed_message::Eip712SignedMessage, - tap_eip712_domain, TapVersion, + tap_eip712_domain, }; use tap_graph::{Receipt, SignedReceipt}; use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, }; #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } #[fixture] @@ -36,19 +38,44 @@ fn context() -> InMemoryContext { ) } +#[fixture] +fn collection_id() -> FixedBytes<32> { + FixedBytes::from([0xab; 32]) +} + +#[fixture] +fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() +} + +#[fixture] +fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() +} + +#[fixture] +fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() +} + #[rstest] #[tokio::test] -async fn receipt_adapter_test(domain_separator: Eip712Domain, mut context: InMemoryContext) { +async fn receipt_adapter_test( + domain_separator: Eip712Domain, + mut context: InMemoryContext, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, +) { let wallet = PrivateKeySigner::random(); - let allocation_id = Address::from_str("0xabababababababababababababababababababab").unwrap(); - // Create receipts let value = 100u128; let received_receipt = ReceiptWithState::new( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_id, value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value).unwrap(), &wallet, ) .unwrap(), @@ -77,18 +104,23 @@ async fn receipt_adapter_test(domain_separator: Eip712Domain, mut context: InMem #[rstest] #[tokio::test] -async fn multi_receipt_adapter_test(domain_separator: Eip712Domain, mut context: InMemoryContext) { +async fn multi_receipt_adapter_test( + domain_separator: Eip712Domain, + mut context: InMemoryContext, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, +) { let wallet = PrivateKeySigner::random(); - let allocation_id = Address::from_str("0xabababababababababababababababababababab").unwrap(); - // Create receipts let mut received_receipts = Vec::new(); for value in 50..60 { received_receipts.push(ReceiptWithState::new( Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_id, value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, value).unwrap(), &wallet, ) .unwrap(), @@ -150,13 +182,19 @@ async fn multi_receipt_adapter_test(domain_separator: Eip712Domain, mut context: #[case(vec![1, 1, 1, 1, 2, 3], 3, vec![])] #[test] fn safe_truncate_receipts_test( - domain_separator: Eip712Domain, #[case] input: Vec, #[case] limit: u64, #[case] expected: Vec, ) { + use std::str::FromStr; + use rand::{rng, seq::SliceRandom}; + let domain_separator = tap_eip712_domain(1, Address::from([0x11u8; 20])); + let collection_id = FixedBytes::from([0xab; 32]); + let payer = Address::from_str("0xabababababababababababababababababababab").unwrap(); + let data_service = Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(); + let service_provider = Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(); let wallet = PrivateKeySigner::random(); // Vec of (id, receipt) @@ -168,10 +206,13 @@ fn safe_truncate_receipts_test( Eip712SignedMessage::new( &domain_separator, Receipt { - allocation_id: Address::ZERO, + collection_id, timestamp_ns: *timestamp, nonce: 0, value: 0, + payer, + data_service, + service_provider, }, &wallet, ) diff --git a/tap_core/tests/received_receipt_test.rs b/tap_core/tests/received_receipt_test.rs index 98b49dd..2cc50e8 100644 --- a/tap_core/tests/received_receipt_test.rs +++ b/tap_core/tests/received_receipt_test.rs @@ -12,11 +12,13 @@ use tap_core::{ Context, ReceiptWithState, }, signed_message::Eip712SignedMessage, - tap_eip712_domain, TapVersion, + tap_eip712_domain, }; use tap_graph::{Receipt, SignedReceipt}; use thegraph_core::alloy::{ - dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner, + dyn_abi::Eip712Domain, + primitives::{Address, FixedBytes}, + signers::local::PrivateKeySigner, }; #[fixture] @@ -25,15 +27,30 @@ fn signer() -> PrivateKeySigner { } #[fixture] -fn allocation_ids() -> Vec
{ +fn collection_ids() -> Vec> { vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), + FixedBytes::from([0xab; 32]), + FixedBytes::from([0xcd; 32]), + FixedBytes::from([0xef; 32]), + FixedBytes::from([0x12; 32]), ] } +#[fixture] +fn payer() -> Address { + Address::from_str("0xabababababababababababababababababababab").unwrap() +} + +#[fixture] +fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() +} + +#[fixture] +fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() +} + #[fixture] fn sender_ids(signer: PrivateKeySigner) -> (PrivateKeySigner, Vec
) { let address = signer.address(); @@ -50,7 +67,7 @@ fn sender_ids(signer: PrivateKeySigner) -> (PrivateKeySigner, Vec
) { #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } struct ContextFixture { @@ -63,7 +80,7 @@ struct ContextFixture { #[fixture] fn context( domain_separator: Eip712Domain, - allocation_ids: Vec
, + collection_ids: Vec>, sender_ids: (PrivateKeySigner, Vec
), ) -> ContextFixture { let (signer, sender_ids) = sender_ids; @@ -74,7 +91,7 @@ fn context( let mut checks = get_full_list_of_checks( domain_separator, sender_ids.iter().cloned().collect(), - Arc::new(RwLock::new(allocation_ids.iter().cloned().collect())), + Arc::new(RwLock::new(collection_ids.iter().cloned().collect())), query_appraisals.clone(), ); checks.push(timestamp_check); @@ -91,7 +108,10 @@ fn context( #[tokio::test] async fn partial_then_full_check_valid_receipt( domain_separator: Eip712Domain, - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, context: ContextFixture, ) { let ContextFixture { @@ -105,7 +125,14 @@ async fn partial_then_full_check_valid_receipt( let query_value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], query_value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + query_value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -134,7 +161,10 @@ async fn partial_then_full_check_valid_receipt( #[rstest] #[tokio::test] async fn partial_then_finalize_valid_receipt( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -149,7 +179,14 @@ async fn partial_then_finalize_valid_receipt( let query_value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], query_value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + query_value, + ) + .unwrap(), &signer, ) .unwrap(); @@ -180,7 +217,10 @@ async fn partial_then_finalize_valid_receipt( #[rstest] #[tokio::test] async fn standard_lifetime_valid_receipt( - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, context: ContextFixture, ) { @@ -195,7 +235,14 @@ async fn standard_lifetime_valid_receipt( let query_value = 20u128; let signed_receipt = Eip712SignedMessage::new( &domain_separator, - Receipt::new(allocation_ids[0], query_value).unwrap(), + Receipt::new( + collection_ids[0], + payer, + data_service, + service_provider, + query_value, + ) + .unwrap(), &signer, ) .unwrap(); diff --git a/tap_core/tests/snapshots/rav_test__check_for_rav_serialization-2.snap b/tap_core/tests/snapshots/rav_test__check_for_rav_serialization-2.snap index 836c4c7..4c1073b 100644 --- a/tap_core/tests/snapshots/rav_test__check_for_rav_serialization-2.snap +++ b/tap_core/tests/snapshots/rav_test__check_for_rav_serialization-2.snap @@ -1,18 +1,21 @@ --- source: tap_core/tests/rav_test.rs expression: signed_rav -snapshot_kind: text --- { "message": { - "allocationId": "0xabababababababababababababababababababab", + "collectionId": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "serviceProvider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", + "dataService": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", "timestampNs": 59, - "valueAggregate": 545 + "valueAggregate": 545, + "metadata": "0x" }, "signature": { - "r": "0x11d760201bac73d4e772d0999a1f9b5f6b8d8979d94ee29b7cb2659d23f2a551", - "s": "0x1036a994ff414de83f24601ab7cc22e0ece134d2199e0b675d94bd8cf7015226", - "yParity": "0x0", - "v": "0x0" + "r": "0xed2663ecb148daffaaf6ff148d2ca5f215f0be02192457da01a378c148bca060", + "s": "0x67d9342b97278aad634d4d8ccc7a8f874ce2581e34198732f201b38604a570d5", + "yParity": "0x1", + "v": "0x1" } } diff --git a/tap_core/tests/snapshots/rav_test__check_for_rav_serialization.snap b/tap_core/tests/snapshots/rav_test__check_for_rav_serialization.snap index 2020b5b..b2b16f1 100644 --- a/tap_core/tests/snapshots/rav_test__check_for_rav_serialization.snap +++ b/tap_core/tests/snapshots/rav_test__check_for_rav_serialization.snap @@ -1,147 +1,176 @@ --- source: tap_core/tests/rav_test.rs expression: receipts -snapshot_kind: text --- [ { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 50, "nonce": 50, "value": 50 }, "signature": { - "r": "0x5257bab234e33525cd999db4defc805c2d3b4e51cde3697f43e37ce39473720f", - "s": "0x6c3af14c3d400dfd047fd2da90eb9e8cee863e77cc52742ebcbf080b8d6ec2", - "yParity": "0x1", - "v": "0x1" + "r": "0xc23da011db5a5b9d35a324dee7e794946758b144407b4d8b6a357acfc3dcdaee", + "s": "0x7b11193ab8bd0f31d47f7447cb5edf15537ae763ba04a3d34ae61a78a9739868", + "yParity": "0x0", + "v": "0x0" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 51, "nonce": 51, "value": 51 }, "signature": { - "r": "0x1596dd0d380ede7aa5dec5ed09ea7d1fa8e4bc8dfdb43a4e965bb4f16906e321", - "s": "0x788b69625a031fbd2e769928b63505387df16e7c51f19ff67c782bfec101a387", - "yParity": "0x0", - "v": "0x0" + "r": "0x40512211d2c63a96c40e3219b43c751a06bfa08f29b149c5d3167738623c3868", + "s": "0x3a065c929c835bb474d0f3b2281ecb7f036b0d23560788b67c66ad365fe3f0f2", + "yParity": "0x1", + "v": "0x1" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 52, "nonce": 52, "value": 52 }, "signature": { - "r": "0xb3b8e2c1249fc14183024e28b14f7749ef852c898906c2442f380a26bf07a625", - "s": "0x6925e7dce01d539a658d552e43cfd92d9d204d6997604f8f613977251b964db3", - "yParity": "0x1", - "v": "0x1" + "r": "0x4fb41ea14b06d1d123856a5da35f7fc564b58d93a608f725326c0ab55c8c1fbe", + "s": "0x6723ab05ebbf95edb7574557406a55979b7afd8394c3f263fae7e2fafc66c183", + "yParity": "0x0", + "v": "0x0" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 53, "nonce": 53, "value": 53 }, "signature": { - "r": "0x3b4d08db319497c2cc0d515d25057e28ce44b194a23e84b9d35682f97027c7e3", - "s": "0x232e02eb4b52d302d620867a4c10829e5a307404ea1bcbbd2ee33e8422a18a16", - "yParity": "0x1", - "v": "0x1" + "r": "0xe69f76ca12929f3b5710d384ab0f01362b5efe643e6b96848ad390ba91402edf", + "s": "0x7fbca24cb6d63a4ae22ba0b111b2055f6711141ecf2e51464609c7de3b3f1e92", + "yParity": "0x0", + "v": "0x0" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 54, "nonce": 54, "value": 54 }, "signature": { - "r": "0x619d84f659ea3941cdb0656100b2ea8a3d2f5658dbd67f796ebfb8840156530b", - "s": "0x163b236f88207b89452255da8ce196997d8f2f0880081659c780f2093797f75e", + "r": "0xad13f399d173eb3af8a42454dba16b952d664c4348b00d29e8b12056307c9cbc", + "s": "0x716ed2429d1f8af9ee3100bf2e7efe4899887f9702dbb9468f60ef60ff0592e6", "yParity": "0x1", "v": "0x1" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 55, "nonce": 55, "value": 55 }, "signature": { - "r": "0x48e1e0e31eaf40eabbcbc3c6d125b7656c0796d51188f89d27194e22f2c5d6bb", - "s": "0xd26efc0ae8cc3646993a20b5aabac1125ecb149ad91d733c702ac9f03222b66", - "yParity": "0x1", - "v": "0x1" + "r": "0x44e9cd755c0bbd7416e1967bc35846500d5afcd4d070472003cd97b45c2d43", + "s": "0x4d85489dd104f506413109b66b9a0120e37c7775fc71be2f93a00b86a14533e4", + "yParity": "0x0", + "v": "0x0" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 56, "nonce": 56, "value": 56 }, "signature": { - "r": "0xc3adb8be5db130f563d3a18fc9e742fca84f69a903413e04dc567b9c3aca8626", - "s": "0x564dd73bdd33897c7a085e4eb1bc0ce002b1d65c6006781ab54cd670846fe358", - "yParity": "0x0", - "v": "0x0" + "r": "0xfe528704d3fbfca05f343f87af1d0f860714f4d282494bf264bb0359adab84de", + "s": "0x6f6d4d80b6366211adbd48b80d665b1e724feaca37324e04127c13e4815cc39a", + "yParity": "0x1", + "v": "0x1" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 57, "nonce": 57, "value": 57 }, "signature": { - "r": "0xa0fe51e1b7253daed14f99c9320d0a539ef18b6ead6552947e5c93dde6f40dea", - "s": "0x4277d66d3a8c9f67cddc8d96a71ef8437e47e34a5b5789d7843eb691c3b9864", + "r": "0x4e5b6f63cd871d2a31701cbf3e351ec814493c9c68b7b61e330ad9d16a1b9f78", + "s": "0x73fa48da496c38189e4b865a354b7199e8f4bde4f942c4b71bd9d9b8523b88f7", "yParity": "0x0", "v": "0x0" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 58, "nonce": 58, "value": 58 }, "signature": { - "r": "0x26f1657e4b8759867820be12c25e982e15ff9d70aa99fe1fd2587cbb644829de", - "s": "0x5cabbd965f93e544b07e5956c2831148dbf7960b4e2edadfa6ecbf1209dacda4", - "yParity": "0x0", - "v": "0x0" + "r": "0x5cc672c3be1b83461b053750f1c9062656e6204e8ccdca74cc5791782e36c0ca", + "s": "0x456220b71c8011b9ea7fbf5951726af2dbd25dab87d03850c9c30b27d1117037", + "yParity": "0x1", + "v": "0x1" } }, { "message": { - "allocation_id": "0xabababababababababababababababababababab", + "collection_id": "0xabababababababababababababababababababababababababababababababab", + "payer": "0xabababababababababababababababababababab", + "data_service": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "service_provider": "0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", "timestamp_ns": 59, "nonce": 59, "value": 59 }, "signature": { - "r": "0x90ce08049b9ce9fa38077ebeed0e24558442d8ae001aeff6b9f4b06f4f553c69", - "s": "0x7a873491448ae696555f9d1314b4e78a7cc98a19a6b0c26aad562053dc26a202", - "yParity": "0x1", - "v": "0x1" + "r": "0x86024ce796db0a6c24feccae54217933002e4220a76f1911f2896e94f5377422", + "s": "0x29c1931ad9b688519400540e302ed1bb080f5b8ebdec4628bb82f33581ff6798", + "yParity": "0x0", + "v": "0x0" } } ] diff --git a/tap_graph/Cargo.toml b/tap_graph/Cargo.toml index 13fb16c..8efa45e 100644 --- a/tap_graph/Cargo.toml +++ b/tap_graph/Cargo.toml @@ -17,8 +17,3 @@ thegraph-core.workspace = true [dev-dependencies] rstest.workspace = true - - -[features] -default = [] -v2 = [] diff --git a/tap_graph/src/lib.rs b/tap_graph/src/lib.rs index 7e572b7..b2a66ff 100644 --- a/tap_graph/src/lib.rs +++ b/tap_graph/src/lib.rs @@ -3,9 +3,8 @@ //! These structs are used for communication between The Graph systems. //! -mod v1; +mod rav; +mod receipt; -#[cfg(any(test, feature = "v2"))] -pub mod v2; - -pub use v1::{Receipt, ReceiptAggregateVoucher, SignedRav, SignedReceipt}; +pub use rav::{ReceiptAggregateVoucher, SignedRav}; +pub use receipt::{Receipt, SignedReceipt}; diff --git a/tap_graph/src/v2/rav.rs b/tap_graph/src/rav.rs similarity index 98% rename from tap_graph/src/v2/rav.rs rename to tap_graph/src/rav.rs index 410e6e1..8878700 100644 --- a/tap_graph/src/v2/rav.rs +++ b/tap_graph/src/rav.rs @@ -1,4 +1,4 @@ -//! # Receipt Aggregate Voucher v2 +//! # Receipt Aggregate Voucher use std::cmp; @@ -14,7 +14,7 @@ use thegraph_core::alloy::{ sol, }; -use super::{Receipt, SignedReceipt}; +use crate::{Receipt, SignedReceipt}; /// EIP712 signed message for ReceiptAggregateVoucher pub type SignedRav = Eip712SignedMessage; diff --git a/tap_graph/src/v2/receipt.rs b/tap_graph/src/receipt.rs similarity index 99% rename from tap_graph/src/v2/receipt.rs rename to tap_graph/src/receipt.rs index fa1a62a..5619648 100644 --- a/tap_graph/src/v2/receipt.rs +++ b/tap_graph/src/receipt.rs @@ -1,4 +1,4 @@ -//! Receipt v2 +//! Receipt use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; diff --git a/tap_graph/src/v1.rs b/tap_graph/src/v1.rs deleted file mode 100644 index 0230cf1..0000000 --- a/tap_graph/src/v1.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod rav; -mod receipt; - -pub use rav::{ReceiptAggregateVoucher, SignedRav}; -pub use receipt::{Receipt, SignedReceipt}; diff --git a/tap_graph/src/v1/rav.rs b/tap_graph/src/v1/rav.rs deleted file mode 100644 index 68a1012..0000000 --- a/tap_graph/src/v1/rav.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! # Receipt Aggregate Voucher -//! -//! Receipts Aggregate Voucher or RAV is the struct that is sent to the -//! blockchain to redeem the aggregated receipts. Receipts are aggregated -//! into a single RAV via a [`RAVRequest`] and then sent to `tap_aggregator`. -//! The request is verified and signed by the aggregator and the response -//! is stored on the indexer side. -//! -//! Every time you have enough receipts to aggregate, you can send another -//! RAV request to the aggregator. The aggregator will verify the request and -//! increment the total amount that has been aggregated. -//! -//! Once the allocation is closed or anytime the user doesn't want to serve -//! anymore(sender considered malicious, not enough in escrow to cover the RAV, etc), -//! the user can redeem the RAV on the blockchain and get the aggregated amount. -//! -//! The system is considered to have minimal trust because you only need to trust -//! the sender until you receive the RAV. The value of non-aggregated receipts must -//! be less than the value you are willing to lose if the sender is malicious. -//! -//! # How to send a request to the aggregator -//! -//! 1. Create a [`RAVRequest`] with the valid receipts and the previous RAV. -//! 2. Send the request to the aggregator. -//! 3. The aggregator will verify the request and increment the total amount that -//! has been aggregated. -//! 4. The aggregator will return a [`SignedRAV`]. -//! 5. Store the [`SignedRAV`]. -//! 6. Repeat the process until the allocation is closed. -//! 7. Redeem the RAV on the blockchain and get the aggregated amount. -//! -//! # How to create RAV Requests -//! -//! Rav requests should be created using the -//! [`crate::manager::Manager::create_rav_request`] function. - -use std::cmp; - -use serde::{Deserialize, Serialize}; -use tap_eip712_message::Eip712SignedMessage; -use tap_receipt::{ - rav::{Aggregate, AggregationError}, - state::Checked, - ReceiptWithState, WithValueAndTimestamp, -}; -use thegraph_core::alloy::{primitives::Address, sol}; - -use super::{Receipt, SignedReceipt}; - -/// A Rav wrapped in an Eip712SignedMessage -pub type SignedRav = Eip712SignedMessage; - -sol! { - /// ReceiptAggregateVoucher struct sent to Arbitrum to redeem payments - /// - /// We use camelCase for field names to match the Ethereum ABI encoding - #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] - struct ReceiptAggregateVoucher { - /// Unique allocation id this RAV belongs to - address allocationId; - /// Unix Epoch timestamp in nanoseconds (Truncated to 64-bits) - /// corresponding to max timestamp from receipt batch aggregated - uint64 timestampNs; - /// Aggregated value from receipt batch and any previous RAV provided - /// (truncate to lower bits) - uint128 valueAggregate; - } -} - -impl ReceiptAggregateVoucher { - /// Aggregates a batch of validated receipts with optional validated previous RAV, - /// returning a new RAV if all provided items are valid or an error if not. - /// - /// # Errors - /// - /// Returns [`Error::AggregateOverflow`] if any receipt value causes aggregate - /// value to overflow - pub fn aggregate_receipts( - allocation_id: Address, - receipts: &[Eip712SignedMessage], - previous_rav: Option>, - ) -> Result { - //TODO(#29): When receipts in flight struct in created check that the state - // of every receipt is OK with all checks complete (relies on #28) - // If there is a previous RAV get initialize values from it, otherwise get default values - let mut timestamp_max = 0u64; - let mut value_aggregate = 0u128; - - if let Some(prev_rav) = previous_rav { - timestamp_max = prev_rav.message.timestampNs; - value_aggregate = prev_rav.message.valueAggregate; - } - - for receipt in receipts { - value_aggregate = value_aggregate - .checked_add(receipt.message.value) - .ok_or(AggregationError::AggregateOverflow)?; - - timestamp_max = cmp::max(timestamp_max, receipt.message.timestamp_ns) - } - - Ok(Self { - allocationId: allocation_id, - timestampNs: timestamp_max, - valueAggregate: value_aggregate, - }) - } -} - -impl Aggregate for ReceiptAggregateVoucher { - fn aggregate_receipts( - receipts: &[ReceiptWithState], - previous_rav: Option>, - ) -> Result { - if receipts.is_empty() { - return Err(AggregationError::NoValidReceiptsForRavRequest); - } - let allocation_id = receipts[0].signed_receipt().message.allocation_id; - let receipts = receipts - .iter() - .map(|rx_receipt| rx_receipt.signed_receipt().clone()) - .collect::>(); - ReceiptAggregateVoucher::aggregate_receipts( - allocation_id, - receipts.as_slice(), - previous_rav, - ) - } -} - -impl WithValueAndTimestamp for ReceiptAggregateVoucher { - fn value(&self) -> u128 { - self.valueAggregate - } - - fn timestamp_ns(&self) -> u64 { - self.timestampNs - } -} diff --git a/tap_graph/src/v1/receipt.rs b/tap_graph/src/v1/receipt.rs deleted file mode 100644 index f5324d3..0000000 --- a/tap_graph/src/v1/receipt.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! Module containing Receipt type used for providing and verifying a payment -//! -//! Receipts are used as single transaction promise of payment. A payment sender -//! creates a receipt and ECDSA signs it, then sends it to a payment receiver. -//! The payment receiver would verify the received receipt and store it to be -//! accumulated with other received receipts in the future. - -use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; - -use rand::{rng, Rng}; -use serde::{Deserialize, Serialize}; -use tap_eip712_message::Eip712SignedMessage; -use tap_receipt::WithValueAndTimestamp; -use thegraph_core::alloy::{primitives::Address, sol}; - -/// A Receipt wrapped in an Eip712SignedMessage -pub type SignedReceipt = Eip712SignedMessage; - -sol! { - /// Receipt struct used to pay for an off-chain service - #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] - struct Receipt { - /// Unique allocation id this receipt belongs to - address allocation_id; - /// Unix Epoch timestamp in nanoseconds (Truncated to 64-bits) - uint64 timestamp_ns; - /// Random value used to avoid collisions from multiple receipts with one timestamp - uint64 nonce; - /// GRT value for transaction (truncate to lower bits) - uint128 value; - } -} - -fn get_current_timestamp_u64_ns() -> Result { - Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() as u64) -} - -impl Receipt { - /// Returns a receipt with provided values - pub fn new(allocation_id: Address, value: u128) -> Result { - let timestamp_ns = get_current_timestamp_u64_ns()?; - let nonce = rng().random::(); - Ok(Self { - allocation_id, - timestamp_ns, - nonce, - value, - }) - } -} - -impl WithValueAndTimestamp for Receipt { - fn value(&self) -> u128 { - self.value - } - - fn timestamp_ns(&self) -> u64 { - self.timestamp_ns - } -} - -#[cfg(test)] -mod receipt_unit_test { - use std::{ - str::FromStr, - time::{SystemTime, UNIX_EPOCH}, - }; - - use rstest::*; - - use super::*; - - #[fixture] - fn allocation_ids() -> Vec
{ - vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap(), - Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), - ] - } - - #[rstest] - fn test_new_receipt(allocation_ids: Vec
) { - let value = 1234; - - let receipt = Receipt::new(allocation_ids[0], value).unwrap(); - - assert_eq!(receipt.allocation_id, allocation_ids[0]); - assert_eq!(receipt.value, value); - - // Check that the timestamp is within a reasonable range - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Current system time should be greater than `UNIX_EPOCH`") - .as_nanos() as u64; - assert!(receipt.timestamp_ns <= now); - assert!(receipt.timestamp_ns >= now - 5000000); // 5 second tolerance - } - - #[rstest] - fn test_unique_nonce_and_timestamp(allocation_ids: Vec
) { - let value = 1234; - - let receipt1 = Receipt::new(allocation_ids[0], value).unwrap(); - let receipt2 = Receipt::new(allocation_ids[0], value).unwrap(); - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Current system time should be greater than `UNIX_EPOCH`") - .as_nanos() as u64; - - // Check that nonces are different - // Note: This test has an *extremely low* (~1/2^64) probability of false failure, if a failure happens - // once it is not neccessarily a sign of an issue. If this test fails more than once, especially - // in a short period of time (within a ) then there may be an issue with randomness - // of the nonce generation. - assert_ne!(receipt1.nonce, receipt2.nonce); - - assert!(receipt1.timestamp_ns <= now); - assert!(receipt1.timestamp_ns >= now - 5000000); // 5 second tolerance - - assert!(receipt2.timestamp_ns <= now); - assert!(receipt2.timestamp_ns >= now - 5000000); // 5 second tolerance - } -} diff --git a/tap_graph/src/v2.rs b/tap_graph/src/v2.rs deleted file mode 100644 index 0230cf1..0000000 --- a/tap_graph/src/v2.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod rav; -mod receipt; - -pub use rav::{ReceiptAggregateVoucher, SignedRav}; -pub use receipt::{Receipt, SignedReceipt}; diff --git a/tap_integration_tests/tests/showcase.rs b/tap_integration_tests/tests/showcase.rs index b991a4b..a857abe 100644 --- a/tap_integration_tests/tests/showcase.rs +++ b/tap_integration_tests/tests/showcase.rs @@ -21,12 +21,12 @@ use tap_core::{ manager::context::memory::{checks::get_full_list_of_checks, *}, receipt::checks::{CheckList, StatefulTimestampCheck}, signed_message::{Eip712SignedMessage, MessageId}, - tap_eip712_domain, TapVersion, + tap_eip712_domain, }; use tap_graph::{Receipt, SignedRav, SignedReceipt}; use thegraph_core::alloy::{ dyn_abi::Eip712Domain, - primitives::Address, + primitives::{Address, FixedBytes}, signers::local::{coins_bip39::English, MnemonicBuilder, PrivateKeySigner}, }; use tokio::task::JoinHandle; @@ -97,13 +97,25 @@ fn wrong_keys_sender() -> PrivateKeySigner { .unwrap() } -// Allocation IDs are used to ensure receipts cannot be double-counted +// Collection IDs are used for V2 receipts #[fixture] -fn allocation_ids() -> Vec
{ - vec![ - Address::from_str("0xabababababababababababababababababababab").unwrap(), - Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap(), - ] +fn collection_ids() -> Vec> { + vec![FixedBytes::from([0xab; 32]), FixedBytes::from([0xde; 32])] +} + +#[fixture] +fn payer() -> Address { + Address::from_str("0xfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbfb").unwrap() +} + +#[fixture] +fn data_service() -> Address { + Address::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead").unwrap() +} + +#[fixture] +fn service_provider() -> Address { + Address::from_str("0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef").unwrap() } #[fixture] @@ -119,12 +131,7 @@ fn sender_ids() -> Vec
{ // Domain separator is used to sign receipts/RAVs according to EIP-712 #[fixture] fn domain_separator() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x11u8; 20]), TapVersion::V1) -} - -#[fixture] -fn domain_separator_v2() -> Eip712Domain { - tap_eip712_domain(1, Address::from([0x22u8; 20]), TapVersion::V2) + tap_eip712_domain(1, Address::from([0x11u8; 20])) } // Query price will typically be set by the Indexer. It's assumed to be part of the Indexer service. @@ -167,7 +174,7 @@ struct ContextFixture { #[fixture] fn context( domain_separator: Eip712Domain, - allocation_ids: Vec
, + collection_ids: Vec>, sender_ids: Vec
, query_appraisals: QueryAppraisals, ) -> ContextFixture { @@ -184,7 +191,7 @@ fn context( let checks = get_full_list_of_checks( domain_separator, sender_ids.iter().cloned().collect(), - Arc::new(RwLock::new(allocation_ids.iter().cloned().collect())), + Arc::new(RwLock::new(collection_ids.iter().cloned().collect())), query_appraisals, ); @@ -210,7 +217,10 @@ fn requests_1( keys_sender: PrivateKeySigner, query_price: &[u128], num_batches: u64, - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, ) -> Vec> { // Create your Receipt here @@ -218,7 +228,10 @@ fn requests_1( query_price, num_batches, &keys_sender, - allocation_ids[0], + collection_ids[0], + payer, + data_service, + service_provider, &domain_separator, ) } @@ -228,7 +241,10 @@ fn requests_2( keys_sender: PrivateKeySigner, query_price: &[u128], num_batches: u64, - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, ) -> Vec> { // Create your Receipt here @@ -236,7 +252,10 @@ fn requests_2( query_price, num_batches, &keys_sender, - allocation_ids[1], + collection_ids[1], + payer, + data_service, + service_provider, &domain_separator, ) } @@ -245,7 +264,10 @@ fn requests_2( fn repeated_timestamp_request( keys_sender: PrivateKeySigner, query_price: &[u128], - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, num_batches: u64, receipt_threshold_1: u64, @@ -255,7 +277,10 @@ fn repeated_timestamp_request( query_price, num_batches, &keys_sender, - allocation_ids[0], + collection_ids[0], + payer, + data_service, + service_provider, &domain_separator, ); @@ -265,7 +290,10 @@ fn repeated_timestamp_request( .timestamp_ns; let target_receipt = &requests[receipt_threshold_1 as usize].message; let repeat_receipt = Receipt { - allocation_id: target_receipt.allocation_id, + collection_id: target_receipt.collection_id, + payer: target_receipt.payer, + data_service: target_receipt.data_service, + service_provider: target_receipt.service_provider, timestamp_ns: repeat_timestamp, nonce: target_receipt.nonce, value: target_receipt.value, @@ -281,7 +309,10 @@ fn repeated_timestamp_request( fn repeated_timestamp_incremented_by_one_request( keys_sender: PrivateKeySigner, query_price: &[u128], - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, num_batches: u64, receipt_threshold_1: u64, @@ -291,7 +322,10 @@ fn repeated_timestamp_incremented_by_one_request( query_price, num_batches, &keys_sender, - allocation_ids[0], + collection_ids[0], + payer, + data_service, + service_provider, &domain_separator, ); @@ -302,7 +336,10 @@ fn repeated_timestamp_incremented_by_one_request( + 1; let target_receipt = &requests[receipt_threshold_1 as usize].message; let repeat_receipt = Receipt { - allocation_id: target_receipt.allocation_id, + collection_id: target_receipt.collection_id, + payer: target_receipt.payer, + data_service: target_receipt.data_service, + service_provider: target_receipt.service_provider, timestamp_ns: repeat_timestamp, nonce: target_receipt.nonce, value: target_receipt.value, @@ -320,7 +357,10 @@ fn wrong_requests( wrong_keys_sender: PrivateKeySigner, query_price: &[u128], num_batches: u64, - allocation_ids: Vec
, + collection_ids: Vec>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: Eip712Domain, ) -> Vec> { // Create your Receipt here @@ -329,7 +369,10 @@ fn wrong_requests( query_price, num_batches, &wrong_keys_sender, - allocation_ids[0], + collection_ids[0], + payer, + data_service, + service_provider, &domain_separator, ) } @@ -339,7 +382,6 @@ fn wrong_requests( async fn single_indexer_test_server( keys_sender: PrivateKeySigner, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -351,7 +393,6 @@ async fn single_indexer_test_server( let (sender_aggregator_handle, sender_aggregator_addr) = start_sender_aggregator( keys_sender, domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -380,7 +421,6 @@ async fn single_indexer_test_server( async fn two_indexers_test_servers( keys_sender: PrivateKeySigner, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -400,7 +440,6 @@ async fn two_indexers_test_servers( let (sender_aggregator_handle, sender_aggregator_addr) = start_sender_aggregator( keys_sender, domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -452,7 +491,6 @@ async fn two_indexers_test_servers( async fn single_indexer_wrong_sender_test_server( wrong_keys_sender: PrivateKeySigner, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -464,7 +502,6 @@ async fn single_indexer_wrong_sender_test_server( let (sender_aggregator_handle, sender_aggregator_addr) = start_sender_aggregator( wrong_keys_sender, domain_separator.clone(), - domain_separator_v2.clone(), http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -576,8 +613,7 @@ async fn test_manager_wrong_aggregator_keys( let indexer_1_address = "http://".to_string() + &socket_addr.to_string(); let client_1 = HttpClientBuilder::default().build(indexer_1_address)?; - let mut counter = 1; - for receipt_1 in requests_1 { + for (counter, receipt_1) in (1..).zip(requests_1) { let result: Result<(), jsonrpsee::core::ClientError> = client_1.request("request", (receipt_1,)).await; // The rav request is being made with messages that have been signed with a key that differs from the sender aggregator's. @@ -597,7 +633,6 @@ async fn test_manager_wrong_aggregator_keys( result.unwrap_err() ); } - counter += 1; } Ok(()) @@ -661,8 +696,7 @@ async fn test_tap_manager_rav_timestamp_cuttoff( let client_1 = HttpClientBuilder::default().build(indexer_1_address)?; let client_2 = HttpClientBuilder::default().build(indexer_2_address)?; - let mut counter = 1; - for receipt_1 in repeated_timestamp_request { + for (counter, receipt_1) in (1..).zip(repeated_timestamp_request) { let result: Result<(), jsonrpsee::core::ClientError> = client_1.request("request", (receipt_1,)).await; @@ -679,7 +713,6 @@ async fn test_tap_manager_rav_timestamp_cuttoff( result.unwrap_err() ); } - counter += 1; } server_handle_1.stop()?; @@ -701,7 +734,6 @@ async fn test_tap_manager_rav_timestamp_cuttoff( async fn test_tap_aggregator_rav_timestamp_cuttoff( keys_sender: PrivateKeySigner, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -713,7 +745,6 @@ async fn test_tap_aggregator_rav_timestamp_cuttoff( let (sender_handle, sender_addr) = start_sender_aggregator( keys_sender, domain_separator, - domain_separator_v2, http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, @@ -781,7 +812,10 @@ fn generate_requests( query_price: &[u128], num_batches: u64, sender_key: &PrivateKeySigner, - allocation_id: Address, + collection_id: FixedBytes<32>, + payer: Address, + data_service: Address, + service_provider: Address, domain_separator: &Eip712Domain, ) -> Vec> { let mut requests: Vec> = Vec::new(); @@ -791,7 +825,8 @@ fn generate_requests( requests.push( Eip712SignedMessage::new( domain_separator, - Receipt::new(allocation_id, *value).unwrap(), + Receipt::new(collection_id, payer, data_service, service_provider, *value) + .unwrap(), sender_key, ) .unwrap(), @@ -838,7 +873,6 @@ async fn start_indexer_server( async fn start_sender_aggregator( keys: PrivateKeySigner, domain_separator: Eip712Domain, - domain_separator_v2: Eip712Domain, http_request_size_limit: u32, http_response_size_limit: u32, http_max_concurrent_connections: u32, @@ -855,7 +889,6 @@ async fn start_sender_aggregator( keys, accepted_addresses, domain_separator, - domain_separator_v2, http_request_size_limit, http_response_size_limit, http_max_concurrent_connections, diff --git a/tap_receipt/src/error.rs b/tap_receipt/src/error.rs index fd25d54..a1d8a78 100644 --- a/tap_receipt/src/error.rs +++ b/tap_receipt/src/error.rs @@ -1,11 +1,13 @@ use serde::{Deserialize, Serialize}; -use thegraph_core::alloy::primitives::Address; +use thegraph_core::alloy::primitives::FixedBytes; /// Error type for receipts #[derive(thiserror::Error, Debug, Clone, Serialize, Deserialize)] pub enum ReceiptError { - #[error("invalid allocation ID: {received_allocation_id}")] - InvalidAllocationID { received_allocation_id: Address }, + #[error("invalid collection ID: {received_collection_id}")] + InvalidCollectionID { + received_collection_id: FixedBytes<32>, + }, #[error("Signature check failed:\n{source_error_message}")] InvalidSignature { source_error_message: String }, #[error("invalid timestamp: {received_timestamp} (expected min {timestamp_min})")]