|
3 | 3 |
|
4 | 4 | use std::str::FromStr; |
5 | 5 |
|
6 | | -pub use alloy_core; |
| 6 | +pub use alloy; |
7 | 7 | pub use alloy_rlp; |
8 | | -pub use alloy_signer; |
9 | | -pub use alloy_sol_types; |
10 | 8 |
|
11 | | -use alloy_core::primitives::Address; |
12 | | -use alloy_rlp::{RlpDecodable, RlpEncodable}; |
13 | | -use alloy_signer::Signature; |
14 | | -use alloy_sol_types::{sol, SolStruct}; |
| 9 | +use alloy::core::primitives::Address; |
| 10 | +use alloy::rlp::{RlpDecodable, RlpEncodable}; |
| 11 | +use alloy::signers::{Signature, SignerSync}; |
| 12 | +use alloy_sol_types::{sol, Eip712Domain, SolStruct}; |
| 13 | +use thiserror::Error; |
15 | 14 |
|
16 | 15 | sol! { |
17 | 16 | // EIP712 encoded bytes, ABI - ethers |
@@ -52,45 +51,72 @@ sol! { |
52 | 51 | } |
53 | 52 | } |
54 | 53 |
|
| 54 | +#[derive(Error, Debug, PartialEq)] |
| 55 | +pub enum AgreementVoucherValidationError { |
| 56 | + #[error("signature is not valid, error: {0}")] |
| 57 | + InvalidSignature(String), |
| 58 | + #[error("payer {0} not authorised")] |
| 59 | + PayerNotAuthorised(Address), |
| 60 | + #[error("voucher payee {actual} does not match the expected address {expected}")] |
| 61 | + UnexpectedPayee { expected: Address, actual: Address }, |
| 62 | +} |
| 63 | + |
| 64 | +impl IndexingAgreementVoucher { |
| 65 | + pub fn sign<S: SignerSync>( |
| 66 | + &self, |
| 67 | + domain: &Eip712Domain, |
| 68 | + signer: S, |
| 69 | + ) -> anyhow::Result<SignedIndexingAgreementVoucher> { |
| 70 | + let voucher = SignedIndexingAgreementVoucher { |
| 71 | + voucher: self.clone(), |
| 72 | + signature: signer.sign_typed_data_sync(self, domain)?.as_bytes().into(), |
| 73 | + }; |
| 74 | + |
| 75 | + Ok(voucher) |
| 76 | + } |
| 77 | +} |
| 78 | + |
55 | 79 | impl SignedIndexingAgreementVoucher { |
56 | | - // TODO: Validate all values, maybe return a useful error on failure. |
57 | | - pub fn is_valid( |
| 80 | + // TODO: Validate all values |
| 81 | + pub fn validate( |
58 | 82 | &self, |
| 83 | + domain: &Eip712Domain, |
59 | 84 | expected_payee: &Address, |
60 | 85 | allowed_payers: impl AsRef<[Address]>, |
61 | | - ) -> bool { |
62 | | - let sig = match Signature::from_str(&self.signature.to_string()) { |
63 | | - Ok(s) => s, |
64 | | - Err(_) => return false, |
65 | | - }; |
| 86 | + ) -> Result<(), AgreementVoucherValidationError> { |
| 87 | + let sig = Signature::from_str(&self.signature.to_string()) |
| 88 | + .map_err(|err| AgreementVoucherValidationError::InvalidSignature(err.to_string()))?; |
66 | 89 |
|
67 | 90 | let payer = sig |
68 | | - .recover_address_from_msg(self.voucher.eip712_hash_struct()) |
69 | | - .unwrap(); |
| 91 | + .recover_address_from_prehash(&self.voucher.eip712_signing_hash(domain)) |
| 92 | + .map_err(|err| AgreementVoucherValidationError::InvalidSignature(err.to_string()))?; |
70 | 93 |
|
71 | 94 | if allowed_payers.as_ref().is_empty() |
72 | 95 | || !allowed_payers.as_ref().iter().any(|addr| addr.eq(&payer)) |
73 | 96 | { |
74 | | - return false; |
| 97 | + return Err(AgreementVoucherValidationError::PayerNotAuthorised(payer)); |
75 | 98 | } |
76 | 99 |
|
77 | 100 | if !self.voucher.payee.eq(expected_payee) { |
78 | | - return false; |
| 101 | + return Err(AgreementVoucherValidationError::UnexpectedPayee { |
| 102 | + expected: *expected_payee, |
| 103 | + actual: self.voucher.payee, |
| 104 | + }); |
79 | 105 | } |
80 | 106 |
|
81 | | - true |
| 107 | + Ok(()) |
82 | 108 | } |
83 | 109 | } |
84 | 110 |
|
85 | 111 | #[cfg(test)] |
86 | 112 | mod test { |
87 | | - use alloy_core::primitives::{Address, FixedBytes, U256}; |
88 | | - use alloy_signer::SignerSync; |
89 | | - use alloy_signer_local::PrivateKeySigner; |
90 | | - use alloy_sol_types::SolStruct; |
| 113 | + use alloy::primitives::{Address, FixedBytes, U256}; |
| 114 | + use alloy::signers::local::PrivateKeySigner; |
| 115 | + use alloy::sol_types::SolStruct; |
| 116 | + use thegraph_core::attestation::eip712_domain; |
91 | 117 |
|
92 | 118 | use crate::{ |
93 | | - IndexingAgreementVoucher, SignedIndexingAgreementVoucher, SubgraphIndexingVoucherMetadata, |
| 119 | + AgreementVoucherValidationError, IndexingAgreementVoucher, SubgraphIndexingVoucherMetadata, |
94 | 120 | }; |
95 | 121 |
|
96 | 122 | #[test] |
@@ -119,17 +145,15 @@ mod test { |
119 | 145 | metadata: metadata.eip712_hash_struct().to_owned().into(), |
120 | 146 | }; |
121 | 147 |
|
122 | | - let signed = SignedIndexingAgreementVoucher { |
123 | | - voucher: voucher.clone(), |
124 | | - signature: payer |
125 | | - .sign_message_sync(voucher.eip712_hash_struct().as_slice()) |
126 | | - .unwrap() |
127 | | - .as_bytes() |
128 | | - .into(), |
129 | | - }; |
130 | | - |
131 | | - assert!(signed.is_valid(&payee_addr, vec![])); |
132 | | - assert!(signed.is_valid(&payee_addr, vec![payer_addr])); |
| 148 | + let domain = eip712_domain(0, Address::ZERO); |
| 149 | + let signed = voucher.sign(&domain, payer).unwrap(); |
| 150 | + assert_eq!( |
| 151 | + signed.validate(&domain, &payee_addr, vec![]).unwrap_err(), |
| 152 | + AgreementVoucherValidationError::PayerNotAuthorised(voucher.payer) |
| 153 | + ); |
| 154 | + assert!(signed |
| 155 | + .validate(&domain, &payee_addr, vec![payer_addr]) |
| 156 | + .is_ok()); |
133 | 157 | } |
134 | 158 |
|
135 | 159 | #[test] |
@@ -157,17 +181,16 @@ mod test { |
157 | 181 | durationEpochs: 1000, |
158 | 182 | metadata: metadata.eip712_hash_struct().to_owned().into(), |
159 | 183 | }; |
| 184 | + let domain = eip712_domain(0, Address::ZERO); |
160 | 185 |
|
161 | | - let mut signed = SignedIndexingAgreementVoucher { |
162 | | - voucher: voucher.clone(), |
163 | | - signature: payer |
164 | | - .sign_message_sync(voucher.eip712_hash_struct().as_slice()) |
165 | | - .unwrap() |
166 | | - .as_bytes() |
167 | | - .into(), |
168 | | - }; |
| 186 | + let mut signed = voucher.sign(&domain, payer).unwrap(); |
169 | 187 | signed.voucher.service = Address::repeat_byte(9); |
170 | 188 |
|
171 | | - assert!(signed.is_valid(&payee_addr, vec![payer_addr])); |
| 189 | + assert!(matches!( |
| 190 | + signed |
| 191 | + .validate(&domain, &payee_addr, vec![payer_addr]) |
| 192 | + .unwrap_err(), |
| 193 | + AgreementVoucherValidationError::PayerNotAuthorised(_) |
| 194 | + )); |
172 | 195 | } |
173 | 196 | } |
0 commit comments