Skip to content

Commit 1ffcccd

Browse files
authored
Merge pull request #115 from bitfinity-network/EPROD-704_genesys_account_data
[Eprod-704] genesys account data
2 parents c8c5db0 + 3db6a63 commit 1ffcccd

10 files changed

Lines changed: 403 additions & 65 deletions

File tree

src/ethereum-json-rpc-client/src/lib.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::pin::Pin;
44
use anyhow::Context;
55
use did::transaction::StorableExecutionResult;
66
use ethers_core::types::{
7-
Block, BlockNumber, Log, Transaction, TransactionReceipt, H160, H256, U64,
7+
Block, BlockNumber, Log, Transaction, TransactionReceipt, H160, H256, U256, U64,
88
};
99
use itertools::Itertools;
1010
use jsonrpc_core::{Call, Id, MethodCall, Output, Params, Request, Response, Version};
@@ -25,7 +25,8 @@ const ETH_BLOCK_NUMBER_METHOD: &str = "eth_blockNumber";
2525
const ETH_GET_TRANSACTION_RECEIPT_METHOD: &str = "eth_getTransactionReceipt";
2626
const ETH_SEND_RAW_TRANSACTION_METHOD: &str = "eth_sendRawTransaction";
2727
const ETH_GET_LOGS_METHOD: &str = "eth_getLogs";
28-
const ETH_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD: &str = "ic_getExeResultByHash";
28+
const IC_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD: &str = "ic_getExeResultByHash";
29+
const IC_GET_GENESIS_BALANCES: &str = "ic_getGenesisBalances";
2930

3031
/// A client for interacting with an Ethereum node over JSON-RPC.
3132
#[derive(Clone)]
@@ -176,7 +177,7 @@ impl<C: Client> EthJsonRcpClient<C> {
176177
) -> anyhow::Result<StorableExecutionResult> {
177178
let transaction = self
178179
.single_request::<Option<StorableExecutionResult>>(
179-
ETH_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD.to_string(),
180+
IC_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD.to_string(),
180181
make_params_array!(hash),
181182
Id::Str(hash.to_string()),
182183
)
@@ -202,7 +203,7 @@ impl<C: Client> EthJsonRcpClient<C> {
202203

203204
Ok(self
204205
.batch_request::<Option<StorableExecutionResult>>(
205-
ETH_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD.to_string(),
206+
IC_GET_TX_EXECUTION_RESULT_BY_HASH_METHOD.to_string(),
206207
params,
207208
max_batch_size,
208209
)
@@ -212,6 +213,16 @@ impl<C: Client> EthJsonRcpClient<C> {
212213
.collect())
213214
}
214215

216+
/// Returns the genesis accounts
217+
pub async fn get_genesis_balances(&self) -> anyhow::Result<Vec<(H160, U256)>> {
218+
self.single_request(
219+
IC_GET_GENESIS_BALANCES.to_string(),
220+
make_params_array!(),
221+
Id::Str(IC_GET_GENESIS_BALANCES.to_string()),
222+
)
223+
.await
224+
}
225+
215226
/// Performs a request.
216227
pub async fn request(&self, request: Request) -> anyhow::Result<Response> {
217228
self.client.send_rpc_request(request).await

src/evm-block-extractor/src/block_extractor.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ethereum_json_rpc_client::EthJsonRcpClient;
55
use itertools::Itertools;
66
use tokio::time::Duration;
77

8-
use crate::database::DatabaseClient;
8+
use crate::database::{AccountBalance, DatabaseClient};
99

1010
/// Extracts blocks from an EVMC and stores them in a database
1111
pub struct BlockExtractor {
@@ -32,6 +32,7 @@ impl BlockExtractor {
3232

3333
/// Collects blocks from the EVMC and stores them in the database.
3434
/// Returns the inclusive range of blocks that were collected.
35+
/// This collects also the genesis accounts if needed.
3536
pub async fn collect_blocks(
3637
&mut self,
3738
from_block_inclusive: u64,
@@ -43,6 +44,8 @@ impl BlockExtractor {
4344
to_block_inclusive
4445
);
4546

47+
self.collect_genesis_balances().await?;
48+
4649
let client = self.client.clone();
4750

4851
let request_time_out_secs = self.request_time_out_secs;
@@ -116,4 +119,36 @@ impl BlockExtractor {
116119

117120
Ok((from_block_inclusive, to_block_inclusive))
118121
}
122+
123+
/// Collects blocks from the EVMC and stores them in the database.
124+
/// Returns the inclusive range of blocks that were collected.
125+
/// This collects also the genesis accounts if needed.
126+
async fn collect_genesis_balances(&self) -> anyhow::Result<()> {
127+
if self.blockchain.get_genesis_balances().await?.is_some() {
128+
log::debug!("Genesis balances already present in the DB. Skipping");
129+
return Ok(());
130+
}
131+
132+
log::info!("Genesis balances not present in the DB. Collecting them");
133+
134+
match self.client.get_genesis_balances().await {
135+
Ok(genesis_balances) => {
136+
let genesis_balances = genesis_balances
137+
.into_iter()
138+
.map(|(address, balance)| AccountBalance {
139+
address: address.into(),
140+
balance: balance.into(),
141+
})
142+
.collect::<Vec<_>>();
143+
self.blockchain
144+
.insert_genesis_balances(&genesis_balances)
145+
.await?;
146+
}
147+
Err(e) => {
148+
log::error!("Error getting genesis balances: {:?}. The process will not be stopped but there will be missing genesis balances in the DB", e);
149+
}
150+
}
151+
152+
Ok(())
153+
}
119154
}

src/evm-block-extractor/src/database/big_query_db_client.rs

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ use serde::de::DeserializeOwned;
1818
use serde::Serialize;
1919
use serde_json::Value;
2020

21-
use super::DatabaseClient;
21+
use super::{AccountBalance, DatabaseClient};
2222

2323
const BQ_EXE_RESULTS_TABLE_ID: &str = "exe_results";
2424
const BQ_BLOCKS_TABLE_ID: &str = "blocks";
2525
const BQ_TRANSACTIONS_TABLE_ID: &str = "transactions";
26+
const BQ_KEY_VALUE_TABLE_ID: &str = "key_value_data";
27+
28+
const GENESIS_BALANCES_KEY: &str = "genesis_balances";
2629

2730
#[derive(Clone)]
2831
/// A client for BigQuery that can be used to query and insert data
@@ -67,7 +70,16 @@ impl BigQueryDbClient {
6770
})
6871
}
6972

70-
async fn execute_query<T: DeserializeOwned>(&self, query: QueryRequest) -> anyhow::Result<T> {
73+
async fn query_one<T: DeserializeOwned>(&self, query: QueryRequest) -> anyhow::Result<T> {
74+
self.query_one_optional(query)
75+
.await?
76+
.ok_or(anyhow::anyhow!("No data found for the query"))
77+
}
78+
79+
async fn query_one_optional<T: DeserializeOwned>(
80+
&self,
81+
query: QueryRequest,
82+
) -> anyhow::Result<Option<T>> {
7183
let mut response = self.client.job().query(&self.project_id, query).await?;
7284

7385
if response.next_row() {
@@ -79,12 +91,11 @@ impl BigQueryDbClient {
7991

8092
let result: T = serde_json::from_str(&result_str)?;
8193

82-
Ok(result)
94+
Ok(Some(result))
8395
} else {
84-
Err(anyhow::anyhow!("No data found for the query"))
96+
Ok(None)
8597
}
8698
}
87-
8899
async fn insert_batch_data(
89100
&self,
90101
table_id: &str,
@@ -110,6 +121,8 @@ impl BigQueryDbClient {
110121
async fn create_tables_if_not_present(&self) -> anyhow::Result<()> {
111122
let dataset = Dataset::new(&self.project_id, &self.dataset_id);
112123

124+
log::info!("Creating tables if not present");
125+
113126
// Make sure the dataset exists
114127
if self
115128
.client
@@ -145,6 +158,13 @@ impl BigQueryDbClient {
145158
TableFieldSchema::integer("block_number"),
146159
],
147160
),
161+
(
162+
BQ_KEY_VALUE_TABLE_ID,
163+
vec![
164+
TableFieldSchema::string("key"),
165+
TableFieldSchema::json("data"),
166+
],
167+
),
148168
];
149169

150170
// Check each table and create if it does not exist
@@ -173,6 +193,51 @@ impl BigQueryDbClient {
173193

174194
Ok(())
175195
}
196+
197+
async fn fetch_key_value_data<D: DeserializeOwned>(
198+
&self,
199+
key: &str,
200+
) -> anyhow::Result<Option<D>> {
201+
let query_request = QueryRequest {
202+
query_parameters: Some(vec![QueryParameter {
203+
name: Some("key".to_string()),
204+
parameter_type: Some(QueryParameterType {
205+
r#type: "STRING".to_string(),
206+
..Default::default()
207+
}),
208+
parameter_value: Some(QueryParameterValue {
209+
value: Some(key.to_string()),
210+
..Default::default()
211+
}),
212+
}]),
213+
query: format!(
214+
"SELECT data FROM `{project_id}.{dataset_id}.{table_id}` WHERE key = @key",
215+
project_id = self.project_id,
216+
dataset_id = self.dataset_id,
217+
table_id = BQ_KEY_VALUE_TABLE_ID,
218+
),
219+
..Default::default()
220+
};
221+
222+
self.query_one_optional(query_request).await
223+
}
224+
225+
async fn insert_key_value_data<D: Serialize>(&self, key: &str, data: D) -> anyhow::Result<()> {
226+
let json = KeyValueDataRow {
227+
key: key.to_string(),
228+
data: serde_json::to_value(data).expect("Failed to serialize data"),
229+
};
230+
231+
let key_value_row = TableDataInsertAllRequestRows {
232+
insert_id: Some(key.to_string()),
233+
json: serde_json::to_value(json)?,
234+
};
235+
236+
log::debug!("Inserting key value data with key [{}]", key);
237+
238+
self.insert_batch_data(BQ_KEY_VALUE_TABLE_ID, vec![key_value_row])
239+
.await
240+
}
176241
}
177242

178243
#[async_trait::async_trait]
@@ -185,7 +250,6 @@ impl DatabaseClient for BigQueryDbClient {
185250
if !self.check_if_same_block_hash(&block).await? {
186251
if reset_database {
187252
self.clear().await?;
188-
self.create_tables_if_not_present().await?;
189253
} else {
190254
return Err(anyhow::anyhow!(
191255
"The block hash in the database is different from the one in the block"
@@ -210,8 +274,9 @@ impl DatabaseClient for BigQueryDbClient {
210274
delete_table(BQ_BLOCKS_TABLE_ID.to_owned()).await?;
211275
delete_table(BQ_EXE_RESULTS_TABLE_ID.to_owned()).await?;
212276
delete_table(BQ_TRANSACTIONS_TABLE_ID.to_owned()).await?;
277+
delete_table(BQ_KEY_VALUE_TABLE_ID.to_owned()).await?;
213278

214-
Ok(())
279+
self.create_tables_if_not_present().await
215280
}
216281

217282
async fn get_block_by_number(&self, block_number: u64) -> anyhow::Result<Block<H256>> {
@@ -236,7 +301,7 @@ impl DatabaseClient for BigQueryDbClient {
236301
..Default::default()
237302
};
238303

239-
self.execute_query(query_request).await
304+
self.query_one(query_request).await
240305
}
241306

242307
async fn get_full_block_by_number(
@@ -402,7 +467,7 @@ impl DatabaseClient for BigQueryDbClient {
402467
..Default::default()
403468
};
404469

405-
let exe_result: StorableExecutionResult = self.execute_query(query_request).await?;
470+
let exe_result: StorableExecutionResult = self.query_one(query_request).await?;
406471

407472
Ok(TransactionReceipt::from(exe_result))
408473
}
@@ -458,6 +523,18 @@ impl DatabaseClient for BigQueryDbClient {
458523
))
459524
}
460525
}
526+
527+
async fn get_genesis_balances(&self) -> anyhow::Result<Option<Vec<AccountBalance>>> {
528+
self.fetch_key_value_data(GENESIS_BALANCES_KEY).await
529+
}
530+
531+
async fn insert_genesis_balances(
532+
&self,
533+
genesis_balances: &[AccountBalance],
534+
) -> anyhow::Result<()> {
535+
self.insert_key_value_data(GENESIS_BALANCES_KEY, genesis_balances)
536+
.await
537+
}
461538
}
462539

463540
/// A row in the BigQuery table
@@ -482,3 +559,11 @@ pub struct TransactionRow {
482559
transaction: Value,
483560
block_number: u64,
484561
}
562+
563+
/// A row in the BigQuery table
564+
#[derive(Debug, Serialize)]
565+
pub struct KeyValueDataRow {
566+
key: String,
567+
#[serde(serialize_with = "serialize_json_as_string")]
568+
data: Value,
569+
}

src/evm-block-extractor/src/database/mod.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ pub mod big_query_db_client;
22
pub mod postgres_db_client;
33

44
use did::transaction::StorableExecutionResult;
5-
use did::{Block, Transaction, TransactionReceipt, H256};
5+
use did::{Block, Transaction, TransactionReceipt, H160, H256, U256};
6+
use serde::{Deserialize, Serialize};
7+
8+
/// Account balance
9+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10+
pub struct AccountBalance {
11+
pub address: H160,
12+
pub balance: U256,
13+
}
614

715
/// A trait for interacting with a blockchain database
816
#[async_trait::async_trait]
@@ -37,6 +45,15 @@ pub trait DatabaseClient: Send + Sync {
3745
transactions: &[Transaction],
3846
) -> anyhow::Result<()>;
3947

48+
/// Get genesis balances
49+
async fn get_genesis_balances(&self) -> anyhow::Result<Option<Vec<AccountBalance>>>;
50+
51+
/// Insert genesis balances
52+
async fn insert_genesis_balances(
53+
&self,
54+
genesis_balances: &[AccountBalance],
55+
) -> anyhow::Result<()>;
56+
4057
/// Get a transaction receipt from the database
4158
async fn get_transaction_receipt(&self, tx_hash: H256) -> anyhow::Result<TransactionReceipt>;
4259

0 commit comments

Comments
 (0)