diff --git a/tap_core/src/manager/adapters/escrow.rs b/tap_core/src/manager/adapters/escrow.rs deleted file mode 100644 index 6570c513..00000000 --- a/tap_core/src/manager/adapters/escrow.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2023-, Semiotic AI, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use alloy::{dyn_abi::Eip712Domain, primitives::Address}; -use async_trait::async_trait; - -use crate::{ - rav::SignedRAV, - receipt::{state::AwaitingReserve, ReceiptError, ReceiptResult, ReceiptWithState}, - Error, -}; - -/// Manages the escrow operations -/// -/// # Example -/// -/// For example code see [crate::manager::context::memory::EscrowStorage] - -#[async_trait] -pub trait EscrowHandler: Send + Sync { - /// Defines the user-specified error type. - /// - /// This error type should implement the `Error` and `Debug` traits from - /// the standard library. - /// Errors of this type are returned to the user when an operation fails. - type AdapterError: std::error::Error + std::fmt::Debug + Send + Sync + 'static; - - /// Retrieves the local accounting amount of available escrow for a specified sender. - async fn get_available_escrow(&self, sender_id: Address) -> Result; - - /// Deducts a specified value from the local accounting of available escrow for a specified sender. - async fn subtract_escrow( - &self, - sender_id: Address, - value: u128, - ) -> Result<(), Self::AdapterError>; - - /// Verifies the signer of the receipt - /// - /// Used by [`Self::check_rav_signature`] to verify the signer of the receipt - async fn verify_signer(&self, signer_address: Address) -> Result; - - /// Checks and reserves escrow for the received receipt - async fn check_and_reserve_escrow( - &self, - received_receipt: &ReceiptWithState, - domain_separator: &Eip712Domain, - ) -> ReceiptResult<()> { - let signed_receipt = &received_receipt.signed_receipt; - let receipt_signer_address = - signed_receipt - .recover_signer(domain_separator) - .map_err(|err| ReceiptError::InvalidSignature { - source_error_message: err.to_string(), - })?; - - if self - .subtract_escrow(receipt_signer_address, signed_receipt.message.value) - .await - .is_err() - { - return Err(ReceiptError::SubtractEscrowFailed); - } - - Ok(()) - } - - /// Checks the signature of the RAV - async fn check_rav_signature( - &self, - signed_rav: &SignedRAV, - domain_separator: &Eip712Domain, - ) -> Result<(), Error> { - let recovered_address = signed_rav.recover_signer(domain_separator)?; - if self - .verify_signer(recovered_address) - .await - .map_err(|e| Error::FailedToVerifySigner(e.to_string()))? - { - Ok(()) - } else { - Err(Error::InvalidRecoveredSigner { - address: recovered_address, - }) - } - } -} diff --git a/tap_core/src/manager/adapters/mod.rs b/tap_core/src/manager/adapters/mod.rs index 8ba2a9a0..867a39ae 100644 --- a/tap_core/src/manager/adapters/mod.rs +++ b/tap_core/src/manager/adapters/mod.rs @@ -8,10 +8,10 @@ //! allows for easy integration with various storage solutions and verification //! procedures, thereby making the library adaptable to a wide range of use cases. -mod escrow; mod rav; mod receipt; +mod signature; -pub use escrow::EscrowHandler; pub use rav::*; pub use receipt::*; +pub use signature::SignatureChecker; diff --git a/tap_core/src/manager/adapters/signature.rs b/tap_core/src/manager/adapters/signature.rs new file mode 100644 index 00000000..ffb34ae3 --- /dev/null +++ b/tap_core/src/manager/adapters/signature.rs @@ -0,0 +1,46 @@ +// Copyright 2023-, Semiotic AI, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use alloy::{dyn_abi::Eip712Domain, primitives::Address, sol_types::SolStruct}; +use async_trait::async_trait; + +use crate::{signed_message::EIP712SignedMessage, Error}; + +/// Manages the escrow operations +/// +/// # Example +/// +/// For example code see [crate::manager::context::memory::EscrowStorage] + +#[async_trait] +pub trait SignatureChecker: Send + Sync { + /// Defines the user-specified error type. + /// + /// This error type should implement the `Error` and `Debug` traits from + /// the standard library. + /// Errors of this type are returned to the user when an operation fails. + type AdapterError: std::error::Error + std::fmt::Debug + Send + Sync + 'static; + + /// Verifies the signer of the receipt + async fn verify_signer(&self, signer_address: Address) -> Result; + + /// Checks if the signed message has a sender signature + async fn check_signature( + &self, + signed_message: &EIP712SignedMessage, + domain_separator: &Eip712Domain, + ) -> Result<(), Error> { + let recovered_address = signed_message.recover_signer(domain_separator)?; + if self + .verify_signer(recovered_address) + .await + .map_err(|e| Error::FailedToVerifySigner(e.to_string()))? + { + Ok(()) + } else { + Err(Error::InvalidRecoveredSigner { + address: recovered_address, + }) + } + } +} diff --git a/tap_core/src/manager/context/memory.rs b/tap_core/src/manager/context/memory.rs index 455b123d..4882fe66 100644 --- a/tap_core/src/manager/context/memory.rs +++ b/tap_core/src/manager/context/memory.rs @@ -241,18 +241,8 @@ impl InMemoryContext { } #[async_trait] -impl EscrowHandler for InMemoryContext { +impl SignatureChecker for InMemoryContext { type AdapterError = InMemoryError; - async fn get_available_escrow(&self, sender_id: Address) -> Result { - self.escrow(sender_id) - } - async fn subtract_escrow( - &self, - sender_id: Address, - value: u128, - ) -> Result<(), Self::AdapterError> { - self.reduce_escrow(sender_id, value) - } async fn verify_signer(&self, signer_address: Address) -> Result { Ok(self diff --git a/tap_core/src/manager/tap_manager.rs b/tap_core/src/manager/tap_manager.rs index 304e4d17..6bd0123a 100644 --- a/tap_core/src/manager/tap_manager.rs +++ b/tap_core/src/manager/tap_manager.rs @@ -3,12 +3,14 @@ use alloy::dyn_abi::Eip712Domain; -use super::adapters::{EscrowHandler, RAVRead, RAVStore, ReceiptDelete, ReceiptRead, ReceiptStore}; +use super::adapters::{ + RAVRead, RAVStore, ReceiptDelete, ReceiptRead, ReceiptStore, SignatureChecker, +}; use crate::{ rav::{RAVRequest, ReceiptAggregateVoucher, SignedRAV}, receipt::{ checks::{CheckBatch, CheckList, TimestampCheck, UniqueCheck}, - state::{Failed, Reserved}, + state::{Checked, Failed}, Context, ReceiptError, ReceiptWithState, SignedReceipt, }, Error, @@ -42,7 +44,7 @@ impl Manager { impl Manager where - E: RAVStore + EscrowHandler, + E: RAVStore + SignatureChecker, { /// Verify `signed_rav` matches all values on `expected_rav`, and that `signed_rav` has a valid signer. /// @@ -56,7 +58,7 @@ where signed_rav: SignedRAV, ) -> std::result::Result<(), Error> { self.context - .check_rav_signature(&signed_rav, &self.domain_separator) + .check_signature(&signed_rav, &self.domain_separator) .await?; if signed_rav.message != expected_rav { @@ -95,7 +97,7 @@ where impl Manager where - E: ReceiptRead + EscrowHandler, + E: ReceiptRead + SignatureChecker, { async fn collect_receipts( &self, @@ -105,7 +107,7 @@ where limit: Option, ) -> Result< ( - Vec>, + Vec>, Vec>, ), Error, @@ -126,9 +128,8 @@ where source_error: anyhow::Error::new(err), })?; - let mut awaiting_reserve_receipts = vec![]; + let mut checked_receipts = vec![]; let mut failed_receipts = vec![]; - let mut reserved_receipts = vec![]; // check for timestamp let (checking_receipts, already_failed) = @@ -146,27 +147,18 @@ where .map_err(|e| Error::ReceiptError(ReceiptError::RetryableCheck(e)))?; match receipt { - Ok(checked) => awaiting_reserve_receipts.push(checked), - Err(failed) => failed_receipts.push(failed), - } - } - for checked in awaiting_reserve_receipts { - match checked - .check_and_reserve_escrow(&self.context, &self.domain_separator) - .await - { - Ok(reserved) => reserved_receipts.push(reserved), + Ok(checked) => checked_receipts.push(checked), Err(failed) => failed_receipts.push(failed), } } - Ok((reserved_receipts, failed_receipts)) + Ok((checked_receipts, failed_receipts)) } } impl Manager where - E: ReceiptRead + RAVRead + EscrowHandler, + E: ReceiptRead + RAVRead + SignatureChecker, { /// Completes remaining checks on all receipts up to /// (current time - `timestamp_buffer_ns`). Returns them in two lists @@ -210,7 +202,7 @@ where } fn generate_expected_rav( - receipts: &[ReceiptWithState], + receipts: &[ReceiptWithState], previous_rav: Option, ) -> Result { if receipts.is_empty() { diff --git a/tap_core/src/rav/request.rs b/tap_core/src/rav/request.rs index 3854d7f2..553698b8 100644 --- a/tap_core/src/rav/request.rs +++ b/tap_core/src/rav/request.rs @@ -4,7 +4,7 @@ use crate::{ rav::{ReceiptAggregateVoucher, SignedRAV}, receipt::{ - state::{Failed, Reserved}, + state::{Checked, Failed}, ReceiptWithState, }, Error, @@ -14,7 +14,7 @@ use crate::{ #[derive(Debug)] pub struct RAVRequest { /// List of checked and reserved receipts to aggregate - pub valid_receipts: Vec>, + pub valid_receipts: Vec>, /// Optional previous RAV to aggregate with pub previous_rav: Option, /// List of failed receipt used to log invalid receipts diff --git a/tap_core/src/receipt/received_receipt.rs b/tap_core/src/receipt/received_receipt.rs index 708d6dab..119d1a89 100644 --- a/tap_core/src/receipt/received_receipt.rs +++ b/tap_core/src/receipt/received_receipt.rs @@ -13,14 +13,11 @@ //! This module is useful for managing and tracking the state of received receipts, as well as //! their progress through various checks and stages of inclusion in RAV requests and received RAVs. -use alloy::dyn_abi::Eip712Domain; - use super::{checks::CheckError, Context, Receipt, ReceiptError, ReceiptResult, SignedReceipt}; use crate::{ - manager::adapters::EscrowHandler, receipt::{ checks::ReceiptCheck, - state::{AwaitingReserve, Checking, Failed, ReceiptState, Reserved}, + state::{Checked, Checking, Failed, ReceiptState}, }, signed_message::EIP712SignedMessage, }; @@ -50,30 +47,6 @@ where pub(crate) _state: S, } -impl ReceiptWithState { - /// Perform the checks implemented by the context and reserve escrow if - /// all checks pass - /// - /// Returns a [`ReceiptWithState`] if successful, otherwise - /// returns a [`ReceiptWithState`] - pub async fn check_and_reserve_escrow( - self, - context: &E, - domain_separator: &Eip712Domain, - ) -> ResultReceipt - where - E: EscrowHandler, - { - match context - .check_and_reserve_escrow(&self, domain_separator) - .await - { - Ok(_) => Ok(self.perform_state_changes(Reserved)), - Err(e) => Err(self.perform_state_error(e)), - } - } -} - impl ReceiptWithState { /// Creates a new `ReceiptWithState` in the `Checking` state pub fn new(signed_receipt: SignedReceipt) -> ReceiptWithState { @@ -115,14 +88,14 @@ impl ReceiptWithState { mut self, ctx: &Context, checks: &[ReceiptCheck], - ) -> Result, String> { + ) -> Result, String> { let all_checks_passed = self.perform_checks(ctx, checks).await; if let Err(ReceiptError::RetryableCheck(e)) = all_checks_passed { Err(e.to_string()) } else if let Err(e) = all_checks_passed { Ok(Err(self.perform_state_error(e))) } else { - let checked = self.perform_state_changes(AwaitingReserve); + let checked = self.perform_state_changes(Checked); Ok(Ok(checked)) } } diff --git a/tap_core/src/receipt/state.rs b/tap_core/src/receipt/state.rs index 38e7e08b..78f1f3de 100644 --- a/tap_core/src/receipt/state.rs +++ b/tap_core/src/receipt/state.rs @@ -21,18 +21,12 @@ pub struct Failed { pub error: ReceiptError, } -/// AwaitingReserve state represents a receipt that has passed all checks -/// and is awaiting escrow reservation. -#[derive(Debug, Clone)] -pub struct AwaitingReserve; - /// Reserved state represents a receipt that has successfully reserved escrow. #[derive(Debug, Clone)] -pub struct Reserved; +pub struct Checked; /// Trait for the different states a receipt can be in. pub trait ReceiptState {} impl ReceiptState for Checking {} -impl ReceiptState for AwaitingReserve {} -impl ReceiptState for Reserved {} +impl ReceiptState for Checked {} impl ReceiptState for Failed {} diff --git a/tap_core/tests/escrow_test.rs b/tap_core/tests/escrow_test.rs deleted file mode 100644 index 1b4b5bfb..00000000 --- a/tap_core/tests/escrow_test.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2023-, Semiotic AI, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::HashMap, - sync::{Arc, RwLock}, -}; - -use alloy::signers::local::PrivateKeySigner; -use rstest::*; -use tap_core::{ - manager::{adapters::EscrowHandler, context::memory::InMemoryContext}, - receipt::checks::StatefulTimestampCheck, -}; - -#[fixture] -fn context() -> InMemoryContext { - let escrow_storage = Arc::new(RwLock::new(HashMap::new())); - let rav_storage = Arc::new(RwLock::new(None)); - let receipt_storage = Arc::new(RwLock::new(HashMap::new())); - - let timestamp_check = Arc::new(StatefulTimestampCheck::new(0)); - InMemoryContext::new( - rav_storage, - receipt_storage.clone(), - escrow_storage.clone(), - timestamp_check, - ) -} - -#[rstest] -#[tokio::test] -async fn escrow_handler_test(mut context: InMemoryContext) { - let wallet = PrivateKeySigner::random(); - let sender_id = wallet.address(); - - let invalid_wallet = PrivateKeySigner::random(); - let invalid_sender_id = invalid_wallet.address(); - - let initial_value = 500u128; - - context.increase_escrow(sender_id, initial_value); - - // Check that sender exists and has valid value through adapter - assert!(context.get_available_escrow(sender_id).await.is_ok()); - assert_eq!( - context.get_available_escrow(sender_id).await.unwrap(), - initial_value - ); - - // Check that subtracting is valid for valid sender, and results in expected value - assert!(context - .subtract_escrow(sender_id, initial_value) - .await - .is_ok()); - assert!(context.get_available_escrow(sender_id).await.is_ok()); - assert_eq!(context.get_available_escrow(sender_id).await.unwrap(), 0); - - // Check that subtracting to negative escrow results in err - assert!(context - .subtract_escrow(sender_id, initial_value) - .await - .is_err()); - - // Check that accessing non initialized sender results in err - assert!(context - .get_available_escrow(invalid_sender_id) - .await - .is_err()); -} diff --git a/tap_core/tests/received_receipt_test.rs b/tap_core/tests/received_receipt_test.rs index 2b6c6e0b..bb0fc743 100644 --- a/tap_core/tests/received_receipt_test.rs +++ b/tap_core/tests/received_receipt_test.rs @@ -10,9 +10,7 @@ use std::{ use alloy::{dyn_abi::Eip712Domain, primitives::Address, signers::local::PrivateKeySigner}; use rstest::*; use tap_core::{ - manager::context::memory::{ - checks::get_full_list_of_checks, EscrowStorage, InMemoryContext, QueryAppraisals, - }, + manager::context::memory::{checks::get_full_list_of_checks, EscrowStorage, QueryAppraisals}, receipt::{ checks::{ReceiptCheck, StatefulTimestampCheck}, Context, Receipt, ReceiptWithState, @@ -56,7 +54,6 @@ fn domain_separator() -> Eip712Domain { } struct ContextFixture { - context: InMemoryContext, escrow_storage: EscrowStorage, query_appraisals: QueryAppraisals, checks: Vec, @@ -71,18 +68,9 @@ fn context( ) -> ContextFixture { let (signer, sender_ids) = sender_ids; let escrow_storage = Arc::new(RwLock::new(HashMap::new())); - let rav_storage = Arc::new(RwLock::new(None)); - let receipt_storage = Arc::new(RwLock::new(HashMap::new())); let query_appraisals = Arc::new(RwLock::new(HashMap::new())); let timestamp_check = Arc::new(StatefulTimestampCheck::new(0)); - let context = InMemoryContext::new( - rav_storage, - receipt_storage.clone(), - escrow_storage.clone(), - timestamp_check.clone(), - ) - .with_sender_address(signer.address()); let mut checks = get_full_list_of_checks( domain_separator, sender_ids.iter().cloned().collect(), @@ -93,7 +81,6 @@ fn context( ContextFixture { signer, - context, escrow_storage, query_appraisals, checks, @@ -153,7 +140,6 @@ async fn partial_then_finalize_valid_receipt( ) { let ContextFixture { checks, - context, escrow_storage, query_appraisals, signer, @@ -187,12 +173,8 @@ async fn partial_then_finalize_valid_receipt( .await; assert!(awaiting_escrow_receipt.is_ok()); - let awaiting_escrow_receipt = awaiting_escrow_receipt.unwrap(); - let receipt = awaiting_escrow_receipt - .unwrap() - .check_and_reserve_escrow(&context, &domain_separator) - .await; - assert!(receipt.is_ok()); + let checked_receipt = awaiting_escrow_receipt.unwrap(); + assert!(checked_receipt.is_ok()); } #[rstest] @@ -204,7 +186,6 @@ async fn standard_lifetime_valid_receipt( ) { let ContextFixture { checks, - context, escrow_storage, query_appraisals, signer, @@ -239,10 +220,6 @@ async fn standard_lifetime_valid_receipt( .await; assert!(awaiting_escrow_receipt.is_ok()); - let awaiting_escrow_receipt = awaiting_escrow_receipt.unwrap(); - let receipt = awaiting_escrow_receipt - .unwrap() - .check_and_reserve_escrow(&context, &domain_separator) - .await; - assert!(receipt.is_ok()); + let checked_receipt = awaiting_escrow_receipt.unwrap(); + assert!(checked_receipt.is_ok()); } diff --git a/tap_integration_tests/tests/indexer_mock.rs b/tap_integration_tests/tests/indexer_mock.rs index c020bb8c..8676ef5e 100644 --- a/tap_integration_tests/tests/indexer_mock.rs +++ b/tap_integration_tests/tests/indexer_mock.rs @@ -18,7 +18,7 @@ use jsonrpsee_core::client::ClientT; use tap_aggregator::jsonrpsee_helpers; use tap_core::{ manager::{ - adapters::{EscrowHandler, RAVRead, RAVStore, ReceiptRead, ReceiptStore}, + adapters::{RAVRead, RAVStore, ReceiptRead, ReceiptStore, SignatureChecker}, Manager, }, rav::SignedRAV, @@ -84,7 +84,7 @@ where #[async_trait] impl RpcServer for RpcManager where - E: ReceiptStore + ReceiptRead + RAVStore + RAVRead + EscrowHandler + Send + Sync + 'static, + E: ReceiptStore + ReceiptRead + RAVStore + RAVRead + SignatureChecker + Send + Sync + 'static, { async fn request( &self, @@ -148,7 +148,7 @@ where + ReceiptRead + RAVStore + RAVRead - + EscrowHandler + + SignatureChecker + Clone + Send + Sync @@ -183,7 +183,7 @@ async fn request_rav( threshold: usize, ) -> Result<()> where - E: ReceiptRead + RAVRead + RAVStore + EscrowHandler, + E: ReceiptRead + RAVRead + RAVStore + SignatureChecker, { // Create the aggregate_receipts request params let rav_request = manager