Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ impl ResultHandler {
ExpressionResult::Log(query_res) => {
println!("{}", to_table(query_res)?);
}
ExpressionResult::Sum(query_res) => {
println!("{}", to_table(query_res)?);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions crates/cli/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ impl Repl {
queue!(stdout(), MoveToNextLine(1), Print(line.cyan())).unwrap();
});
}
ExpressionResult::Sum(query_res) => {
let table = to_table(query_res)?;
table.to_string().split("\n").for_each(|line| {
queue!(stdout(), MoveToNextLine(1), Print(line.green())).unwrap();
});
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions crates/core/src/common/query_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ pub enum ExpressionResult {
Transaction(Vec<TransactionQueryRes>),
#[serde(rename = "log")]
Log(Vec<LogQueryRes>),
#[serde(rename = "sum")]
Sum(Vec<SumQueryRes>),
}


#[serde_with::skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
pub struct SumQueryRes {
pub sum: Option<U256>,
}

impl Default for SumQueryRes {
fn default() -> Self {
Self {
sum: None,
}
}
}

// TODO: should this be replaced with Alloy's Block?
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/common/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(crate) fn dump_results(result: &ExpressionResult, dump: &Dump) -> Result<(),
ExpressionResult::Block(blocks) => serialize_csv(blocks)?,
ExpressionResult::Transaction(txs) => serialize_csv(txs)?,
ExpressionResult::Log(logs) => serialize_csv(logs)?,
ExpressionResult::Sum(sums) => serialize_csv(sums)?,
};

std::fs::write(dump.path(), content)?;
Expand Down Expand Up @@ -59,6 +60,7 @@ fn serialize_parquet(result: &ExpressionResult) -> Result<Vec<u8>, Box<dyn Error
create_parquet_schema_and_data(transactions)?
}
ExpressionResult::Log(logs) => create_parquet_schema_and_data(logs)?,
ExpressionResult::Sum(sums) => create_parquet_schema_and_data(sums)?,
};

let batch = RecordBatch::try_new(Arc::new(schema), data)?;
Expand Down
15 changes: 15 additions & 0 deletions crates/core/src/common/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ use pest::iterators::Pairs;
#[derive(Debug, PartialEq)]
pub enum Expression {
Get(GetExpression),
Sum(SumExpression),
}


#[derive(Debug, PartialEq)]
pub struct SumExpression {
pub query: GetExpression,
}

impl SumExpression {
fn new(query: GetExpression) -> Self {
Self {
query
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Make constructor public and add input validation.

The constructor should be public to allow creating instances from other modules.

 impl SumExpression {
-    fn new(query: GetExpression) -> Self {
+    pub fn new(query: GetExpression) -> Result<Self, GetExpressionError> {
+        // Validate that the query returns numeric fields that can be summed
         Self {
             query
         }
     }
+
+    pub fn query(&self) -> &GetExpression {
+        &self.query
+    }
 }

Committable suggestion skipped: line range outside the PR's diff.


#[derive(Debug, PartialEq)]
Expand Down
86 changes: 82 additions & 4 deletions crates/core/src/interpreter/backend/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use super::{
resolve_block::resolve_block_query,
resolve_logs::resolve_log_query,
resolve_transaction::resolve_transaction_query,
resolve_sum_query::resolve_sum_query
};
use crate::common::{
entity::Entity, query_result::{ExpressionResult, QueryResult}, serializer::dump_results, types::{Expression, GetExpression}
entity::Entity, query_result::{ExpressionResult, QueryResult}, serializer::dump_results, types::{Expression, GetExpression, SumExpression}
};
use anyhow::Result;

Expand Down Expand Up @@ -36,6 +37,10 @@ impl ExecutionEngine {
let result = self.run_get_expr(&get_expr).await?;
query_results.push(QueryResult::new(result));
}
Expression::Sum(sum_expr) => {
let result = self.run_sum_expr(&sum_expr).await?;
query_results.push(QueryResult::new(result));
}
}
}

Expand All @@ -59,20 +64,29 @@ impl ExecutionEngine {

Ok(result)
}

async fn run_sum_expr(
&self,
expr: &SumExpression,
) -> Result<ExpressionResult> {
let query_result = self.run_get_expr(&expr.query).await?;
let result = resolve_sum_query( &query_result)?;
Ok(result)
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::common::{
account::{Account, AccountField},
account::{Account, AccountField, AccountFilter},
block::{Block, BlockField, BlockId, BlockRange},
chain::{Chain, ChainOrRpc},
dump::{Dump, DumpFormat},
ens::NameOrAddress,
logs::{LogField, LogFilter, Logs},
query_result::{AccountQueryRes, BlockQueryRes, LogQueryRes, TransactionQueryRes},
transaction::{Transaction, TransactionField},
query_result::{AccountQueryRes, BlockQueryRes, LogQueryRes, SumQueryRes, TransactionQueryRes},
transaction::{Transaction, TransactionField, TransactionFilter},
types::{Expression, GetExpression},
};
use alloy::{
Expand All @@ -82,6 +96,70 @@ mod test {
use pretty_assertions::assert_eq;
use std::str::FromStr;

#[tokio::test]
async fn test_sum_balance_of_mutiple_chain() {
let execution_engine = ExecutionEngine::new();
let expressions = vec![Expression::Sum(
SumExpression {
query: GetExpression {
entity: Entity::Account(
Account::new(
Some(vec![NameOrAddress::Name(String::from("vitalik.eth"))]),
None,
vec![AccountField::Balance],
)
),
chains: vec![ChainOrRpc::Chain(Chain::Ethereum), ChainOrRpc::Chain(Chain::Base),ChainOrRpc::Chain(Chain::Arbitrum)],
dump: None,
}
})];

let execution_result = execution_engine.run(expressions).await;
let expected = vec![SumQueryRes {
sum: Some(U256::from(0)),
}];

match execution_result {
Ok(results) => {
assert_eq!(results[0].result, ExpressionResult::Sum(expected));
}
Err(_) => panic!("Error"),
}
}

#[tokio::test]
async fn test_sum_value_of_mutile_block_for_mutiple_chain() {
let execution_engine = ExecutionEngine::new();
let expressions = vec![Expression::Sum(SumExpression {
query: GetExpression {
entity: Entity::Transaction(Transaction::new(
Some(vec![
b256!("8cfc4f5f4729423f59dd1d263ead2f824b3f133b02b9e27383964c7d50cd47cb"),
b256!("72546b3ca8ef0dfb85fe66d19645e44cb519858c72fbcad0e1c1699256fed890")
]),
None,
vec![TransactionField::Value],
)),
chains: vec![ChainOrRpc::Chain(Chain::Ethereum), ChainOrRpc::Chain(Chain::Base),ChainOrRpc::Chain(Chain::Arbitrum)],
dump: None,
},
})];
println!("{:?}", expressions);

let execution_result = execution_engine.run(expressions).await;
let expected = vec![SumQueryRes {
sum: Some(U256::from(0)),
}];

match execution_result {
Ok(results) => {
assert_eq!(results[0].result, ExpressionResult::Sum(expected));
}
Err(_) => panic!("Error")
}
}


#[tokio::test]
async fn test_get_logs() {
let execution_engine = ExecutionEngine::new();
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/interpreter/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ mod resolve_account;
mod resolve_block;
mod resolve_logs;
mod resolve_transaction;
mod resolve_sum_query;
pub mod execution_engine;
58 changes: 58 additions & 0 deletions crates/core/src/interpreter/backend/resolve_sum_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::common::{
account::{Account, AccountField},
chain::{Chain, ChainOrRpc},
ens::NameOrAddress,
query_result::{ExpressionResult, SumQueryRes},
};
use alloy::{
primitives::Address,
providers::{Provider, ProviderBuilder, RootProvider},
transports::http::{Client, Http},
};
use anyhow::Result;
use futures::future::try_join_all;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum AccountResolverErrors {
#[error("Mismatch between Entity and EntityId, {0} can't be resolved as a account id")]
MismatchEntityAndEntityId(String),
}
Comment on lines +17 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Utilize the error enum for error handling.

The AccountResolverErrors enum is defined but not used in the function. Consider using it for proper error handling when resolving sum queries.

Apply this diff to add error handling:

 pub fn resolve_sum_query(exp: &ExpressionResult) -> Result<ExpressionResult> {
+    match exp {
+        ExpressionResult::Account(_) => {
+            // Handle account summation
+            let res = ExpressionResult::Sum(vec![SumQueryRes::default()]);
+            Ok(res)
+        }
+        _ => Err(anyhow::anyhow!(AccountResolverErrors::MismatchEntityAndEntityId(
+            "Cannot sum non-account entities".to_string(),
+        ))),
+    }
-    let res = ExpressionResult::Sum(vec![SumQueryRes::default()]);
-    Ok(res)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum AccountResolverErrors {
#[error("Mismatch between Entity and EntityId, {0} can't be resolved as a account id")]
MismatchEntityAndEntityId(String),
}
pub fn resolve_sum_query(exp: &ExpressionResult) -> Result<ExpressionResult> {
match exp {
ExpressionResult::Account(_) => {
// Handle account summation
let res = ExpressionResult::Sum(vec![SumQueryRes::default()]);
Ok(res)
}
_ => Err(anyhow::anyhow!(AccountResolverErrors::MismatchEntityAndEntityId(
"Cannot sum non-account entities".to_string(),
))),
}
}


/// Resolve the query to get accounts after receiving an account entity expression
/// Iterate through entity_ids and map them to a futures list. Execute all futures concurrently and collect the results.
pub fn resolve_sum_query(
exp: &ExpressionResult,
) -> Result<ExpressionResult>{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation and improve documentation.

The function lacks input validation and return type documentation.

 /// Resolve the query to get accounts after receiving an account entity expression
 /// Iterate through entity_ids and map them to a futures list. Execute all futures concurrently and collect the results.
+/// 
+/// # Arguments
+/// * `exp` - The expression result to process
+/// 
+/// # Returns
+/// * `Result<ExpressionResult>` - The sum query result or an error
 pub fn resolve_sum_query(
     exp: &ExpressionResult,
 ) -> Result<ExpressionResult>{
+    // Validate input
+    match exp {
+        ExpressionResult::Account(_) => (),
+        _ => return Err(anyhow::anyhow!("Expected Account expression result")),
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn resolve_sum_query(
exp: &ExpressionResult,
) -> Result<ExpressionResult>{
/// Resolve the query to get accounts after receiving an account entity expression
/// Iterate through entity_ids and map them to a futures list. Execute all futures concurrently and collect the results.
///
/// # Arguments
/// * `exp` - The expression result to process
///
/// # Returns
/// * `Result<ExpressionResult>` - The sum query result or an error
pub fn resolve_sum_query(
exp: &ExpressionResult,
) -> Result<ExpressionResult> {
// Validate input
match exp {
ExpressionResult::Account(_) => (),
_ => return Err(anyhow::anyhow!("Expected Account expression result")),
}
// TODO: Implement the actual sum query resolution logic
}

//let mut result = Vec::new();

// for chain in chains {
// let provider = Arc::new(ProviderBuilder::new().on_http(chain.rpc_url()?));

// // TODO: Handle filter
// // TODO: Remove unwrap
// for account_id in account.ids().unwrap() {
// let fields = account.fields().clone();
// let provider = provider.clone();

// let account_future = async move {
// match account_id {
// NameOrAddress::Address(address) => {
// get_account(address, fields, &provider, chain).await
// }
// NameOrAddress::Name(name) => {
// let address = to_address(name).await?;
// get_account(&address, fields, &provider, chain).await
// }
// }
// };

// all_account_futures.push(account_future);
// }
// }

// let account_res = try_join_all(all_account_futures).await?;
let res = ExpressionResult::Sum(vec![SumQueryRes::default()]);
Ok(res)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the commented-out code.

The function currently returns a default result without implementing the actual summation logic. The commented-out code should be properly implemented to handle multiple chains and account IDs concurrently.

Would you like me to help implement the complete solution for handling multiple chains and account IDs concurrently?