From 4ca52e3ccede34d6ef22b821ca92e73871a61600 Mon Sep 17 00:00:00 2001
From: Vid Kersic <vid.kersic@yahoo.com>
Date: Thu, 1 Dec 2022 10:40:44 +0100
Subject: [PATCH] User operation pack and hash, remove packages bytes and
 ethereum_types (#22)

---
 Cargo.lock                  |   3 +-
 Cargo.toml                  |   3 +-
 Makefile                    |   3 +
 src/rpc/eth.rs              |   2 +-
 src/rpc/eth_api.rs          |   2 +-
 src/types/user_operation.rs | 169 +++++++++++++++++++++++++++++++++++-
 src/uopool/mod.rs           |   4 +-
 7 files changed, 174 insertions(+), 12 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 508d093b..350c4496 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,14 +18,13 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-trait",
- "bytes",
  "clap",
  "dirs",
  "educe",
  "ethereum-interfaces",
- "ethereum-types",
  "ethers",
  "expanded-pathbuf",
+ "hex",
  "jsonrpsee",
  "parking_lot 0.12.1",
  "prost",
diff --git a/Cargo.toml b/Cargo.toml
index c599c85d..9812ac37 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,14 +9,13 @@ default-run = "bundler"
 [dependencies]
 anyhow = "1"
 async-trait = "0.1"
-bytes = { version = "1", features = ["serde"] }
 clap = { version = "4", features = ["derive"] }
 dirs = "4.0"
 educe = { version = "0.4", features = ["Debug", "Default"] }
 ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces" }
-ethereum-types = { version = "0.14", features = ["codec"] }
 ethers = "1.0.0"
 expanded-pathbuf = "0.1"
+hex = { version = "0.4.3", default-features = false, features = ["std"] }
 jsonrpsee = { version = "0.16", features = ["server", "macros"] }
 parking_lot = "0.12"
 prost = "0.11"
diff --git a/Makefile b/Makefile
index 7661010e..8c8f0f4a 100644
--- a/Makefile
+++ b/Makefile
@@ -13,5 +13,8 @@ run-create-wallet:
 cargo-fmt:
 	cargo fmt --all
 
+cargo-test:
+	cargo test 
+
 fetch-thirdparty:
 	git submodule update --init
\ No newline at end of file
diff --git a/src/rpc/eth.rs b/src/rpc/eth.rs
index 9106216e..50598c4a 100644
--- a/src/rpc/eth.rs
+++ b/src/rpc/eth.rs
@@ -1,6 +1,6 @@
 use crate::{rpc::eth_api::EthApiServer, types::user_operation::UserOperation};
 use async_trait::async_trait;
-use ethereum_types::{Address, U64};
+use ethers::types::{Address, U64};
 use jsonrpsee::{core::RpcResult, tracing::info};
 
 pub struct EthApiServerImpl {
diff --git a/src/rpc/eth_api.rs b/src/rpc/eth_api.rs
index 77511835..9e30550a 100644
--- a/src/rpc/eth_api.rs
+++ b/src/rpc/eth_api.rs
@@ -1,4 +1,4 @@
-use ethereum_types::{Address, U64};
+use ethers::types::{Address, U64};
 use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 
 use crate::types::user_operation::UserOperation;
diff --git a/src/types/user_operation.rs b/src/types/user_operation.rs
index 1d5bf96c..c0101c61 100644
--- a/src/types/user_operation.rs
+++ b/src/types/user_operation.rs
@@ -1,10 +1,13 @@
-use bytes::Bytes;
-use ethereum_types::{Address, H512, U256};
+use ethers::abi::AbiEncode;
+use ethers::prelude::{EthAbiCodec, EthAbiType};
+use ethers::types::{Address, Bytes, H256, U256};
+use ethers::utils::keccak256;
 use serde::{Deserialize, Serialize};
+use std::str::FromStr;
 
-pub type UoId = H512;
+pub type UserOperationHash = H256;
 
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, EthAbiCodec, EthAbiType)]
 #[serde(rename_all = "camelCase")]
 pub struct UserOperation {
     pub sender: Address,
@@ -19,3 +22,161 @@ pub struct UserOperation {
     pub paymaster_and_data: Bytes,
     pub signature: Bytes,
 }
+
+impl UserOperation {
+    pub fn pack(&self) -> Vec<u8> {
+        self.clone().encode_hex().into_bytes()
+    }
+
+    pub fn pack_for_signature(&self) -> Vec<u8> {
+        let mut encoded = String::from("0x");
+        let packed = hex::encode(
+            UserOperation {
+                signature: Bytes::from_str("0x").unwrap(),
+                ..self.clone()
+            }
+            .encode(),
+        );
+        encoded.push_str(&packed[..packed.len() - 64]);
+        encoded.into_bytes()
+    }
+
+    pub fn hash(&self, entry_point_address: Address, chain_id: U256) -> UserOperationHash {
+        H256::from_slice(
+            keccak256(
+                [
+                    keccak256(hex::decode(&self.pack_for_signature()[2..]).unwrap()).to_vec(),
+                    entry_point_address.encode(),
+                    chain_id.encode(),
+                ]
+                .concat(),
+            )
+            .as_slice(),
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn user_operation_pack() {
+        let user_operations =  vec![
+            UserOperation {
+                sender: Address::zero(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::zero(),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::zero(),
+                max_priority_fee_per_gas: U256::from(1e9 as u64),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::default(),
+            },
+            UserOperation {
+                sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::from(200000),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::from(3000000000 as u64),
+                max_priority_fee_per_gas: U256::from(1000000000),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(),
+            },
+        ];
+        assert_eq!(user_operations[0].pack(), String::from("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes());
+        assert_eq!(user_operations[1].pack(), String::from("0x000000000000000000000000663f3ad617193148711d28f5334ee4ed070166020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c00000000000000000000000000000000000000000000000000000000000000").into_bytes());
+    }
+
+    #[test]
+    fn user_operation_pack_for_signature() {
+        let user_operations =  vec![
+            UserOperation {
+                sender: Address::zero(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::zero(),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::zero(),
+                max_priority_fee_per_gas: U256::from(1e9 as u64),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::default(),
+            },
+            UserOperation {
+                sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::from(200000),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::from(3000000000 as u64),
+                max_priority_fee_per_gas: U256::from(1000000000),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(),
+            },
+        ];
+        assert_eq!(user_operations[0].pack_for_signature(), String::from("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes());
+        assert_eq!(user_operations[1].pack_for_signature(), String::from("0x000000000000000000000000663f3ad617193148711d28f5334ee4ed070166020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes());
+    }
+
+    #[test]
+    fn user_operation_hash() {
+        let user_operations =  vec![
+            UserOperation {
+                sender: Address::zero(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::zero(),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::zero(),
+                max_priority_fee_per_gas: U256::from(1e9 as u64),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::default(),
+            },
+            UserOperation {
+                sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(),
+                nonce: U256::zero(),
+                init_code: Bytes::default(),
+                call_data: Bytes::default(),
+                call_gas_limit: U256::from(200000),
+                verification_gas_limit: U256::from(100000),
+                pre_verification_gas: U256::from(21000),
+                max_fee_per_gas: U256::from(3000000000 as u64),
+                max_priority_fee_per_gas: U256::from(1000000000),
+                paymaster_and_data: Bytes::default(),
+                signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(),
+            },
+        ];
+        assert_eq!(
+            user_operations[0].hash(
+                "0x2DF1592238420ecFe7f2431360e224707e77fA0E"
+                    .parse()
+                    .unwrap(),
+                U256::from(1)
+            ),
+            H256::from_str("0x42e145138104ec4124367ea3f7994833071b2011927290f6844d593e05011279")
+                .unwrap()
+        );
+        assert_eq!(
+            user_operations[1].hash(
+                "0x2DF1592238420ecFe7f2431360e224707e77fA0E"
+                    .parse()
+                    .unwrap(),
+                U256::from(1)
+            ),
+            H256::from_str("0x583c8fcba470fd9da514f9482ccd31c299b0161a36b365aab353a6bfebaa0bb2")
+                .unwrap()
+        );
+    }
+}
diff --git a/src/uopool/mod.rs b/src/uopool/mod.rs
index 013ea288..f0131168 100644
--- a/src/uopool/mod.rs
+++ b/src/uopool/mod.rs
@@ -1,5 +1,5 @@
 use crate::{
-    types::user_operation::{UoId, UserOperation},
+    types::user_operation::{UserOperation, UserOperationHash},
     uopool::{server::server::uo_pool_server::UoPoolServer, services::UoPoolService},
 };
 use anyhow::Result;
@@ -15,7 +15,7 @@ pub mod services;
 #[derive(Educe)]
 #[educe(Debug)]
 pub struct UserOperationPool {
-    pub pool: Arc<RwLock<HashMap<UoId, UserOperation>>>,
+    pub pool: Arc<RwLock<HashMap<UserOperationHash, UserOperation>>>,
 }
 
 impl UserOperationPool {