diff --git a/Cargo.lock b/Cargo.lock index 28bd8c8..93d9bc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,8 +935,6 @@ dependencies = [ [[package]] name = "ethers" version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5344eea9b20effb5efeaad29418215c4d27017639fd1f908260f59cbbd226e" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -950,9 +948,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf35eb7d2e2092ad41f584951e08ec7c077b142dba29c4f1b8f52d2efddc49c" +version = "2.0.11" dependencies = [ "ethers-core", "once_cell", @@ -963,8 +959,6 @@ dependencies = [ [[package]] name = "ethers-contract" version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0111ead599d17a7bff6985fd5756f39ca7033edc79a31b23026a8d5d64fa95cd" dependencies = [ "const-hex", "ethers-contract-abigen", @@ -981,9 +975,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbdfb952aafd385b31d316ed80d7b76215ce09743c172966d840e96924427e0c" +version = "2.0.11" dependencies = [ "Inflector", "const-hex", @@ -1005,9 +997,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7465c814a2ecd0de0442160da13584205d1cdc08f4717a6511cad455bd5d7dc4" +version = "2.0.11" dependencies = [ "Inflector", "const-hex", @@ -1021,9 +1011,7 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918b1a9ba585ea61022647def2f27c29ba19f6d2a4a4c8f68a9ae97fd5769737" +version = "2.0.11" dependencies = [ "arrayvec", "bytes", @@ -1051,9 +1039,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "facabf8551b4d1a3c08cb935e7fca187804b6c2525cc0dafb8e5a6dd453a24de" +version = "2.0.11" dependencies = [ "chrono", "ethers-core", @@ -1068,8 +1054,6 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681ece6eb1d10f7cf4f873059a77c04ff1de4f35c63dd7bccde8f438374fcb93" dependencies = [ "async-trait", "auto_impl", @@ -1095,8 +1079,6 @@ dependencies = [ [[package]] name = "ethers-providers" version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d6c0c9455d93d4990c06e049abf9b30daf148cf461ee939c11d88907c60816" dependencies = [ "async-trait", "auto_impl", @@ -1133,8 +1115,6 @@ dependencies = [ [[package]] name = "ethers-signers" version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb1b714e227bbd2d8c53528adb580b203009728b17d0d0e4119353aa9bc5532" dependencies = [ "async-trait", "coins-bip32", @@ -1151,9 +1131,7 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2e46e3ec8ef0c986145901fa9864205dc4dcee701f9846be2d56112d34bdea" +version = "2.0.11" dependencies = [ "cfg-if", "const-hex", diff --git a/Cargo.toml b/Cargo.toml index 48d79e8..d9c49e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,9 +25,13 @@ async-trait = "0.1" jsonrpsee = { version = "0.21", features = ["macros", "server", "client-core"] } anyhow = "1.0" thiserror = "1.0" -ethers = { version = "2.0.11", features = ["abigen"] } +ethers = { path = "../../../../gakonst/ethers-rs/ethers", features = ["abigen"] } ctor = "0.2" lib-didethresolver = { git = "https://github.com/xmtp/didethresolver", package = "lib-didethresolver", branch = "main" } gateway-types = { path = "./gateway-types" } rustc-hex = "2.1" hex = "0.4" + +[patch.crates-io] +ethers = { path = "../../../../gakonst/ethers-rs/ethers", features = ["abigen"] } + diff --git a/registry/src/lib.rs b/registry/src/lib.rs index 5b58f4c..e7d3485 100644 --- a/registry/src/lib.rs +++ b/registry/src/lib.rs @@ -81,18 +81,18 @@ where String::from_utf8_lossy(&attribute) ); - self.registry - .revoke_attribute_signed( - address, - signature.v.try_into()?, - signature.r.into(), - signature.s.into(), - attribute, - value.into(), - ) - .send() - .await? - .await?; + let res = self.registry.revoke_attribute_signed( + address, + signature.v.try_into()?, + signature.r.into(), + signature.s.into(), + attribute, + value.into(), + ); + + let res = res.send().await; + println!("{:?}", res); + res?.await?; Ok(()) } diff --git a/xps-gateway/Cargo.toml b/xps-gateway/Cargo.toml index 7f777b8..80b8beb 100644 --- a/xps-gateway/Cargo.toml +++ b/xps-gateway/Cargo.toml @@ -31,4 +31,3 @@ jsonrpsee = { workspace = true, features = ["macros", "server", "client"]} tokio = { workspace = true, features = ["macros", "rt", "time"]} futures = "0.3" serde_json.workspace = true - diff --git a/xps-gateway/src/lib.rs b/xps-gateway/src/lib.rs index c6b3e8a..d25ac23 100644 --- a/xps-gateway/src/lib.rs +++ b/xps-gateway/src/lib.rs @@ -11,8 +11,10 @@ use gateway_types::DID_ETH_REGISTRY; use jsonrpsee::server::Server; use std::str::FromStr; -pub use crate::rpc::{XpsMethods, XpsServer}; -use crate::types::GatewayContext; +pub use crate::{ + rpc::{XpsMethods, XpsServer}, + types::GatewayContext, +}; /// Entrypoint for the xps Gateway pub async fn run(host: String, port: u16) -> Result<()> { @@ -37,3 +39,131 @@ pub async fn run(host: String, port: u16) -> Result<()> { handle.stopped().await; Ok(()) } + +#[cfg(test)] +mod test { + use ethers::{ + abi::AbiEncode, + prelude::{Transaction, TransactionReceipt}, + providers::MockProvider, + types::{Block, FeeHistory, TxHash, U256, U64}, + }; + use serde::Serialize; + + pub trait MockProviderExt { + /// Set the response for a call to a contract + /// This must be called for each transaction that a function might send. + /// + /// + /// # Example + /// ``` + /// use ethers::{ + /// providers::{MockProvider, Provider}, + /// prelude::TransactionRequest, + /// types::{Address, Transaction} + /// }; + /// use std::time::Duration; + /// + /// let (mut provider, mut mock) = Provider::mocked(); + /// provider.set_interval(Duration::from_millis(1)); + /// + /// let to = Address::from_str("0x7e575682a8e450e33eb0493f9972821ae333cd7f").unwrap(); + /// let from = Address::from_str("0x0000000000000000000000000000000000000000").unwrap(); + /// let tx = TransactionRequest::new().to(to).value(1000).from(from); + /// mock.set_transaction_response(TransactionReceipt::default()); + /// let pending = provider.send_transaction(tx, None).await.unwrap().await.unwrap(); + /// ``` + fn set_transaction_response(&mut self, response: TransactionReceipt); + + /// Set the response for a transaction to a Contract + /// + /// # Example + /// ``` + /// + /// let (context, mut mock) = GatewayContext::mocked().await; + /// let methods = XpsMethods::new(&context); + /// let attr = XmtpAttribute { + /// encoding: KeyEncoding::Hex, + /// purpose: XmtpKeyPurpose::Installation, + /// }; + /// let value = vec![0x01, 0x02, 0x03]; + /// mock.set_contract_response(Default::default()); + /// let res = methods + /// .revoke_installation( + /// Address::default().to_string(), + /// attr, + /// value, + /// Signature { + /// r: [0x01; 32].into(), + /// s: [0x02; 32].into(), + /// v: 0x01, + /// }, + /// ) + /// .await; + /// ``` + fn set_contract_response(&mut self, response: TransactionReceipt); + + /// Set the response for a call to a contract + /// + /// # Example + /// + /// ``` + /// let (context, mut mock) = GatewayContext::mocked().await; + /// let registry = DIDRegistry::new(Address::default(), context.signer.clone()); + /// mock.set_call_response(ChangedReturn(U256::zero())); + /// registry.changed(Address::default()).call().await.unwrap(); + /// + /// ``` + /// + fn set_call_response(&mut self, response: T); + } + + impl MockProviderExt for MockProvider { + fn set_transaction_response(&mut self, response: TransactionReceipt) { + self.push(response).unwrap(); + self.push(Transaction { + block_number: Some(1.into()), + ..Transaction::default() + }) + .unwrap(); // eth_getTransaction + self.push(TxHash::default()).unwrap(); // eth_sendTransaction + self.push(U64::from(0)).unwrap(); // eth_estimateGas + self.push(U64::from(0)).unwrap(); // eth_GasPrice + } + + fn set_contract_response(&mut self, response: TransactionReceipt) { + self.push(response).unwrap(); + self.push(Transaction { + block_number: Some(1.into()), + ..Transaction::default() + }) + .unwrap(); // eth_getTransaction + self.push(TxHash::default()).unwrap(); // eth_sendRawTransaction + self.push(U64::from(0)).unwrap(); // estimateGas + self.push(fee_history()).unwrap(); // eth_feeHistory + self.push(Block { + //eth_getBlockByNumber + // base_fee_per_gas needs to be Some() for EIP-1559 + base_fee_per_gas: Some(U256::zero()), + ..Block::::default() + }) + .unwrap(); + self.push(U64::from(0)).unwrap(); // transactionCount + } + + fn set_call_response(&mut self, response: T) { + self.push::(&response.encode_hex()) + .unwrap(); + } + } + + // internal fn to return an empty fee history + fn fee_history() -> FeeHistory { + FeeHistory { + base_fee_per_gas: vec![U256::zero()], + gas_used_ratio: vec![0.0], + oldest_block: U256::zero(), + reward: Vec::new(), + } + } +} diff --git a/xps-gateway/src/rpc/methods.rs b/xps-gateway/src/rpc/methods.rs index 262eb46..520cf5b 100644 --- a/xps-gateway/src/rpc/methods.rs +++ b/xps-gateway/src/rpc/methods.rs @@ -11,6 +11,7 @@ use ethers::{core::types::Signature, providers::Middleware}; use gateway_types::GrantInstallationResult; use jsonrpsee::types::ErrorObjectOwned; use lib_didethresolver::types::XmtpAttribute; + use rand::{rngs::StdRng, SeedableRng}; use std::sync::Arc; use thiserror::Error; @@ -100,6 +101,7 @@ enum RpcError { impl From> for ErrorObjectOwned { fn from(error: RpcError) -> Self { + println!("{:?}", error); match error { RpcError::ContactOperation(c) => { ErrorObjectOwned::owned(-31999, c.to_string(), None::<()>) @@ -107,3 +109,87 @@ impl From> for ErrorObjectOwned { } } } + +#[cfg(test)] +mod tests { + use crate::test::MockProviderExt; + use ethers::providers::MockResponse; + use lib_didethresolver::types::{KeyEncoding, XmtpKeyPurpose}; + + use super::*; + + fn type_of(_: T) -> &'static str { + std::any::type_name::() + } + + #[tokio::test] + async fn test_rpc_wallet_address() { + let (context, _) = GatewayContext::mocked().await; + let methods = XpsMethods::new(&context); + + let res = methods.wallet_address().await.unwrap(); + assert_eq!(type_of(res), "primitive_types::H160"); + } + + #[tokio::test] + async fn test_rpc_revoke_installation() { + let (context, mut mock) = GatewayContext::mocked().await; + + let methods = XpsMethods::new(&context); + + let attr = XmtpAttribute { + encoding: KeyEncoding::Hex, + purpose: XmtpKeyPurpose::Installation, + }; + let value = vec![0x01, 0x02, 0x03]; + + mock.set_contract_response(Default::default()); + + let res = methods + .revoke_installation( + "0x7e575682a8e450e33eb0493f9972821ae333cd7f".to_string(), + attr, + value, + Signature { + r: [0x01; 32].into(), + s: [0x02; 32].into(), + v: 0x01, + }, + ) + .await; + assert!(res.is_ok()); + } + + #[tokio::test] + async fn test_rpc_revoke_installation_error() { + let (context, mut mock) = GatewayContext::mocked().await; + + let methods = XpsMethods::new(&context); + + let attr = XmtpAttribute { + encoding: KeyEncoding::Hex, + purpose: XmtpKeyPurpose::Installation, + }; + let value = vec![0x01, 0x02, 0x03]; + + mock.push_response(MockResponse::Error(JsonRpcError { + code: -32000, + message: "VM Exception while processing transaction: revert".to_string(), + data: None, + })); + + let res = methods + .revoke_installation( + "0x7e575682a8e450e33eb0493f9972821ae333cd7f".to_string(), + attr, + value, + Signature { + r: [0x01; 32].into(), + s: [0x02; 32].into(), + v: 0x01, + }, + ) + .await; + println!("{:?}", res); + } +} diff --git a/xps-gateway/src/types.rs b/xps-gateway/src/types.rs index 7d8eac2..01386b4 100644 --- a/xps-gateway/src/types.rs +++ b/xps-gateway/src/types.rs @@ -12,6 +12,7 @@ pub type GatewaySigner

= SignerMiddleware; pub struct GatewayContext { pub registry: DIDRegistry>, pub signer: Arc>, + pub wallet: LocalWallet, } impl GatewayContext

{ @@ -20,28 +21,42 @@ impl GatewayContext

{ let signer = Arc::new(SignerMiddleware::new_with_provider_chain(provider, wallet.clone()).await?); let registry = DIDRegistry::new(registry, signer.clone()); - Ok(Self { registry, signer }) + Ok(Self { + registry, + signer, + wallet, + }) } } #[cfg(test)] mod tests { - use ethers::{providers::Provider, types::U64}; - use std::str::FromStr; + use ethers::{prelude::MockProvider, providers::Provider, types::U64}; use super::*; + impl GatewayContext> { + pub async fn mocked() -> (Self, MockProvider) { + let (mut provider, mock) = Provider::mocked(); + provider.set_interval(std::time::Duration::from_millis(1)); + mock.push(U64::from(2)).unwrap(); + + let gateway = GatewayContext::new(Address::default(), provider) + .await + .unwrap(); + + (gateway, mock) + } + } + #[tokio::test] async fn test_gateway_constructor() { let (provider, mock) = Provider::mocked(); mock.push(U64::from(2)).unwrap(); - let gateway = GatewayContext::new( - Address::from_str("0x0000000000000000000000000000000000000000").unwrap(), - provider, - ) - .await - .unwrap(); + let gateway = GatewayContext::new(Address::default(), provider) + .await + .unwrap(); assert!(gateway.registry.address().is_zero()); assert!(gateway.signer.is_signer().await);