Skip to content

Commit

Permalink
Merge pull request #87 from Vid201/feat/codehash
Browse files Browse the repository at this point in the history
Feat/codehash
  • Loading branch information
Vid201 authored Mar 29, 2023
2 parents ba51bad + 7d50dd7 commit 07fb714
Show file tree
Hide file tree
Showing 13 changed files with 706 additions and 511 deletions.
789 changes: 336 additions & 453 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ page_size = "0.5.0"
parking_lot = "0.12"
prost = "0.11"
regex = "1"
reth-db = { git = "https://github.com/paradigmxyz/reth.git", rev = "314ea0883b1062375ad86577be1fccc39233739d" }
reth-libmdbx = { git = "https://github.com/paradigmxyz/reth.git", rev = "314ea0883b1062375ad86577be1fccc39233739d" }
reth-db = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" }
reth-libmdbx = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" }
ron = "0.8"
rustc-hex = "^2.0.1"
serde = "1"
Expand Down Expand Up @@ -66,3 +66,7 @@ name = "bundler-rpc"
[[bin]]
path = "bin/create-wallet.rs"
name = "create-wallet"

[patch.crates-io]
revm = { git = "https://github.com/bluealloy/revm", rev = "3d8ca6641d2e72448c23f4596f769c8fd1c784d1" }
revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "3d8ca6641d2e72448c23f4596f769c8fd1c784d1" }
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# <h1 align="center"> AA - Bundler </h1>

<p align="center">Rust implementation for Bundler - EIP-4337 (Account Abstraction).</p>
<p align="center">Rust implementation for Bundler - ERC-4337 (Account Abstraction).</p>

<p align="center">
<img src="./docs/images/logo.jpeg" width="300" height="300">
Expand All @@ -21,7 +21,7 @@ Rust version: 1.67.1

## How to run?

Set up third-party dependencies (EIP-4337 smart contracts and bundler tests):
Set up third-party dependencies (ERC-4337 smart contracts and bundler tests):

```bash
make fetch-thirdparty
Expand Down
32 changes: 31 additions & 1 deletion src/types/simulation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use ethers::types::{Address, U256};
use ethers::abi::{AbiDecode, AbiEncode};
use ethers::prelude::EthAbiType;
use ethers::{
prelude::EthAbiCodec,
types::{Address, Bytes, H256, U256},
};
use jsonrpsee::types::{error::ErrorCode, ErrorObject};
use lazy_static::lazy_static;
use reth_db::table::{Compress, Decompress};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;

const SIMULATE_VALIDATION_ERROR_CODE: i32 = -32500;
Expand Down Expand Up @@ -48,13 +55,33 @@ pub struct StakeInfo {
pub stake: (U256, U256),
}

#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize, EthAbiCodec, EthAbiType)]
pub struct CodeHash {
pub address: Address,
pub hash: H256,
}

impl Compress for CodeHash {
type Compressed = Bytes;
fn compress(self) -> Self::Compressed {
Bytes::from(self.encode())
}
}

impl Decompress for CodeHash {
fn decompress<B: Into<prost::bytes::Bytes>>(value: B) -> Result<Self, reth_db::Error> {
Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError)
}
}

#[derive(Debug)]
pub enum SimulateValidationError {
UserOperationRejected { message: String },
OpcodeValidation { entity: String, opcode: String },
UserOperationExecution { message: String },
StorageAccessValidation { slot: String },
CallStackValidation { message: String },
CodeHashesValidation { message: String },
UnknownError { error: String },
}

Expand All @@ -80,6 +107,9 @@ impl From<SimulateValidationError> for SimulationError {
SimulateValidationError::CallStackValidation { message } => {
SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::<bool>)
}
SimulateValidationError::CodeHashesValidation { message } => {
SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::<bool>)
}
SimulateValidationError::UnknownError { error } => {
SimulationError::owned(ErrorCode::InternalError.code(), error, None::<bool>)
}
Expand Down
3 changes: 3 additions & 0 deletions src/types/user_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::ops::Deref;
use std::str::FromStr;
use std::vec;

use super::utils::as_checksum;

#[derive(
Eq, Hash, PartialEq, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialOrd, Ord,
)]
Expand Down Expand Up @@ -50,6 +52,7 @@ impl Encode for UserOperationHash {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, EthAbiCodec, EthAbiType)]
#[serde(rename_all = "camelCase")]
pub struct UserOperation {
#[serde(serialize_with = "as_checksum")]
pub sender: Address,
pub nonce: U256,
pub init_code: Bytes,
Expand Down
78 changes: 54 additions & 24 deletions src/types/utils.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,60 @@
use ethers::types::Address;
use reth_db::table::{Decode, Encode};
use ethers::{
types::{Address, Bytes},
utils::to_checksum,
};
use reth_db::table::{Compress, Decode, Decompress, Encode};
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct WrapAddress(Address);

impl Decode for WrapAddress {
fn decode<B: Into<prost::bytes::Bytes>>(value: B) -> Result<Self, reth_db::Error> {
Ok(Address::from_slice(value.into().as_ref()).into())
}
pub fn as_checksum<S>(val: &Address, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&to_checksum(val, None))
}

impl Encode for WrapAddress {
type Encoded = [u8; 20];
fn encode(self) -> Self::Encoded {
*self.0.as_fixed_bytes()
}
}
macro_rules! construct_wrap_hash {
($type:ty, $name:ident, $n_bytes:expr ) => {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
pub struct $name($type);

impl From<Address> for WrapAddress {
fn from(value: Address) -> Self {
Self(value)
}
}
impl Decode for $name {
fn decode<B: Into<prost::bytes::Bytes>>(value: B) -> Result<Self, reth_db::Error> {
Ok(<$type>::from_slice(value.into().as_ref()).into())
}
}

impl Encode for $name {
type Encoded = [u8; $n_bytes];
fn encode(self) -> Self::Encoded {
*self.0.as_fixed_bytes()
}
}

impl From<WrapAddress> for Address {
fn from(value: WrapAddress) -> Self {
value.0
}
impl From<$type> for $name {
fn from(value: $type) -> Self {
Self(value)
}
}

impl From<$name> for $type {
fn from(value: $name) -> Self {
value.0
}
}

impl Compress for $name {
type Compressed = Bytes;
fn compress(self) -> Self::Compressed {
Bytes::from(self.encode())
}
}

impl Decompress for $name {
fn decompress<B: Into<prost::bytes::Bytes>>(value: B) -> Result<Self, reth_db::Error> {
Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError)
}
}
};
}

construct_wrap_hash!(Address, WrapAddress, 20);
66 changes: 59 additions & 7 deletions src/uopool/database_mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use std::{fmt::Display, path::PathBuf};

use super::Mempool;
use crate::types::{
simulation::CodeHash,
user_operation::{UserOperation, UserOperationHash},
utils::WrapAddress,
};
use ethers::types::{Address, U256};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
database::{Database, DatabaseGAT},
dupsort,
mdbx::{
tx::{self, Tx},
DatabaseFlags, Environment, EnvironmentFlags, EnvironmentKind, Geometry, Mode, PageSize,
Expand All @@ -23,18 +25,24 @@ use reth_db::{
table!(
/// UserOperation DB
( UserOperationDB ) UserOperationHash | UserOperation
// Sender to UserOperationHash
);

table!(
/// SenderUserOperation DB
/// Benefit for merklization is that hashed addresses/keys are sorted.
( SenderUserOperationDB ) WrapAddress | UserOperation
);

dupsort!(
/// CodeHash DB
( CodeHashDB ) UserOperationHash | [WrapAddress] CodeHash
);

/// Default tables that should be present inside database.
pub const TABLES: [(TableType, &str); 2] = [
pub const TABLES: [(TableType, &str); 3] = [
(TableType::Table, UserOperationDB::const_name()),
(TableType::DupSort, SenderUserOperationDB::const_name()),
(TableType::DupSort, CodeHashDB::const_name()),
];

impl DupSort for SenderUserOperationDB {
Expand Down Expand Up @@ -96,6 +104,7 @@ impl Display for DBError {

impl<E: EnvironmentKind> Mempool for DatabaseMempool<E> {
type UserOperations = Vec<UserOperation>;
type CodeHashes = Vec<CodeHash>;
type Error = DBError;
fn add(
&mut self,
Expand Down Expand Up @@ -128,7 +137,7 @@ impl<E: EnvironmentKind> Mempool for DatabaseMempool<E> {
.and_then(|tx| {
let mut cursor = tx.cursor_dup_read::<SenderUserOperationDB>()?;
let res = cursor
.walk_dup(wrap_sender.clone(), Address::default().into())?
.walk_dup(Some(wrap_sender.clone()), Some(Address::default().into()))?
.map(|a| a.map(|(_, v)| v))
.collect::<Result<Vec<_>, _>>()?;
tx.commit()?;
Expand All @@ -144,19 +153,62 @@ impl<E: EnvironmentKind> Mempool for DatabaseMempool<E> {
.and_then(|tx| {
let mut cursor = tx.cursor_dup_read::<SenderUserOperationDB>()?;
let res = cursor
.walk_dup(wrap_sender.clone(), Address::default().into())?
.walk_dup(Some(wrap_sender.clone()), Some(Address::default().into()))?
.count();
tx.commit()?;
Ok(res)
})
.unwrap_or(0)
}

fn has_code_hashes(
&self,
user_operation_hash: &UserOperationHash,
) -> anyhow::Result<bool, Self::Error> {
let tx = self.env.tx()?;
let res = tx.get::<CodeHashDB>(*user_operation_hash)?;
tx.commit()?;
Ok(res.is_some())
}

fn get_code_hashes(&self, user_operation_hash: &UserOperationHash) -> Self::CodeHashes {
self.env
.tx()
.and_then(|tx| {
let mut cursor = tx.cursor_dup_read::<CodeHashDB>()?;
let res = cursor
.walk_dup(Some(*user_operation_hash), Some(Address::default().into()))?
.map(|a| a.map(|(_, v)| v))
.collect::<Result<Vec<_>, _>>()?;
tx.commit()?;
Ok(res)
})
.unwrap_or_else(|_| vec![])
}

fn set_code_hashes(
&mut self,
user_operation_hash: &UserOperationHash,
code_hashes: &Self::CodeHashes,
) -> anyhow::Result<(), Self::Error> {
let tx = self.env.tx_mut()?;
let res = tx.get::<CodeHashDB>(*user_operation_hash)?;
if res.is_some() {
tx.delete::<CodeHashDB>(*user_operation_hash, None)?;
}
for code_hash in code_hashes {
tx.put::<CodeHashDB>(*user_operation_hash, code_hash.clone())?;
}
tx.commit()?;
Ok(())
}

fn remove(&mut self, user_operation_hash: &UserOperationHash) -> Result<(), DBError> {
let tx = self.env.tx_mut()?;
if let Some(user_op) = tx.get::<UserOperationDB>(*user_operation_hash)? {
tx.delete::<UserOperationDB>(*user_operation_hash, None)?;
tx.delete::<SenderUserOperationDB>(user_op.sender.into(), Some(user_op))?;
tx.delete::<CodeHashDB>(*user_operation_hash, None)?;
tx.commit()?;
Ok(())
} else {
Expand All @@ -170,7 +222,7 @@ impl<E: EnvironmentKind> Mempool for DatabaseMempool<E> {
.and_then(|tx| {
let mut cursor = tx.cursor_read::<UserOperationDB>()?;
let mut user_ops = cursor
.walk(UserOperationHash::default())?
.walk(Some(UserOperationHash::default()))?
.map(|a| a.map(|(_, uo)| uo))
.collect::<Result<Vec<_>, _>>()?;
user_ops
Expand All @@ -186,7 +238,7 @@ impl<E: EnvironmentKind> Mempool for DatabaseMempool<E> {
.and_then(|tx| {
let mut c = tx.cursor_read::<UserOperationDB>()?;
let res = c
.walk(UserOperationHash::default())?
.walk(Some(UserOperationHash::default()))?
.map(|a| a.map(|(_, v)| v))
.collect::<Result<Vec<_>, _>>()?;
tx.commit()?;
Expand Down Expand Up @@ -280,7 +332,7 @@ mod tests {

#[allow(clippy::unit_cmp)]
#[tokio::test]
async fn memory_mempool() {
async fn database_mempool() {
let dir = TempDir::new("test-userop-db").unwrap();
let mempool: DatabaseMempool<NoWriteMap> = DatabaseMempool::new(dir.into_path()).unwrap();
mempool
Expand Down
Loading

0 comments on commit 07fb714

Please sign in to comment.