Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storage): add draft for storage #92

Merged
merged 11 commits into from
Sep 14, 2023
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/ark-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use anyhow::Result;
use ark_starknet::client::{StarknetClient, StarknetClientHttp};
use ark_storage::storage_manager::StorageManager;
use ark_storage::types::ContractType;
use dotenv::dotenv;
use managers::collection_manager::ContractType;
use managers::{BlockManager, CollectionManager, EventManager, TokenManager};
use starknet::core::types::*;
use std::env;
Expand Down Expand Up @@ -114,12 +114,12 @@

// Head of the chain requested -> check the last block and continue
// indexing loop.
return client
.block_number()
.await
.expect("Can't fetch last block number");

Check warning on line 120 in crates/ark-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

warning: unneeded `return` statement --> crates/ark-core/src/lib.rs:117:9 | 117 | / return client 118 | | .block_number() 119 | | .await 120 | | .expect("Can't fetch last block number"); | |____________________________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return help: remove `return` | 117 ~ client 118 + .block_number() 119 + .await 120 ~ .expect("Can't fetch last block number") |
} else {
return to;

Check warning on line 122 in crates/ark-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

warning: unneeded `return` statement --> crates/ark-core/src/lib.rs:122:9 | 122 | return to; | ^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return help: remove `return` | 122 - return to; 122 + to |
}
}

Expand Down
58 changes: 30 additions & 28 deletions crates/ark-core/src/managers/block_manager.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use ark_starknet::client::StarknetClient;
use ark_storage::storage_manager::StorageManager;
use ark_storage::types::{BlockIndexingStatus, BlockInfo};

Check warning on line 3 in crates/ark-core/src/managers/block_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `BlockInfo`

warning: unused import: `BlockInfo` --> crates/ark-core/src/managers/block_manager.rs:3:47 | 3 | use ark_storage::types::{BlockIndexingStatus, BlockInfo}; | ^^^^^^^^^
use starknet::core::types::*;

use std::env;
Expand All @@ -11,20 +12,6 @@
indexer_version: u64,
}

// TODO: this struct must come from Storage crate.
#[derive(Debug, PartialEq)]
pub enum BlockIndexingStatus {
None,
Processing,
Terminated,
}

// TODO: this struct must come from Storage crate.
pub struct BlockInfo {
pub indexer_version: u64,
pub status: BlockIndexingStatus,
}

impl<'a, T: StorageManager, C: StarknetClient> BlockManager<'a, T, C> {
pub fn new(storage: &'a T, client: &'a C) -> Self {
let v: &u64 = &env::var("INDEXER_VERSION")
Expand Down Expand Up @@ -69,28 +56,43 @@
.unwrap_or(false);

if *do_force {
log::debug!("Block #{} forced", block_number);
// TODO: self.storage.clean_block(block_number);
match self.storage.clean_block(block_number) {
Ok(_) => log::debug!("Block cleaned successfully!"),
Err(e) => log::debug!("Error cleaning block: {:?}", e),
}
return true;
}

// TODO: self.storage.get_block_info(...);
let info = BlockInfo {
indexer_version: 0,
status: BlockIndexingStatus::None,
let info = match self.storage.get_block_info(block_number) {
Ok(block_info) => {
log::debug!("Retrieved block info: {:?}", block_info);
Some(block_info) // Assign the value of block_info to `info`
}
Err(e) => {
log::debug!("Error retrieving block info: {:?}", e);
None // Assigns None to `info` in case of error
}
};

if info.status == BlockIndexingStatus::None {
return true;
}
// Use the retrieved info to determine some actions
if let Some(actual_info) = info {
if actual_info.status == BlockIndexingStatus::None {
return true;
}

if info.indexer_version > self.indexer_version {
log::debug!("Block #{} new version", block_number);
// TODO: self.storage.clean_block(block_number);
return true;
if actual_info.indexer_version > self.indexer_version {
log::debug!("Block #{} new version", block_number);
match self.storage.clean_block(block_number) {
Ok(_) => log::debug!("Block cleaned successfully!"),
Err(e) => log::debug!("Error cleaning block: {:?}", e),
}
return true;
}
} else {
log::debug!("Info is not available for the block.");
}

log::debug!("Block #{} not candidate", block_number);
// If no conditions are met, return false or whatever default you want
false
}
}
59 changes: 19 additions & 40 deletions crates/ark-core/src/managers/collection_manager.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
use anyhow::{anyhow, Result};

Check warning on line 1 in crates/ark-core/src/managers/collection_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `anyhow`

warning: unused import: `anyhow` --> crates/ark-core/src/managers/collection_manager.rs:1:14 | 1 | use anyhow::{anyhow, Result}; | ^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use ark_starknet::client::StarknetClient;
use ark_storage::storage_manager::StorageManager;

use serde::{Deserialize, Serialize};
use ark_storage::types::StorageError;
use ark_storage::types::{ContractInfo, ContractType};
use starknet::core::types::{BlockId, BlockTag, FieldElement};
use starknet::core::utils::{get_selector_from_name, parse_cairo_short_string};

use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ContractType {
Other,
ERC721,
ERC1155,
}

impl ToString for ContractType {
fn to_string(&self) -> String {
match self {
ContractType::Other => "other".to_string(),
ContractType::ERC721 => "erc721".to_string(),
ContractType::ERC1155 => "erc1155".to_string(),
}
}
}

// TODO: this struct must come from Storage crate.
#[derive(Debug, Clone)]
pub struct ContractInfo {
pub name: String,
pub symbol: String,
pub r#type: ContractType,
}

pub struct CollectionManager<'a, T: StorageManager, C: StarknetClient> {
storage: &'a T,
client: &'a C,
Expand All @@ -52,18 +25,20 @@
}

/// Gets the contract info from local cache, or fetch is from the DB.
fn get_cached_or_fetch_info(&mut self, address: FieldElement) -> Result<ContractInfo> {
match self.cache.get(&address) {
Some(info) => Ok(info.clone()),
None => {
log::trace!("Cache miss for contract {address}");
// TODO: self.storage.get_contract_info();
// If no info available -> return error.
// For now, return error to simulate it's not available.
Err(anyhow!("Info not found in storage for contract {address}"))
}
fn get_cached_or_fetch_info(&mut self, address: FieldElement) -> Result<ContractInfo, StorageError> {
if let Some(info) = self.cache.get(&address) {
return Ok(info.clone());
}

log::trace!("Cache miss for contract {}", address);

let info = self.storage.get_contract_info(&address)?;

self.cache.insert(address, info.clone()); // Adding to the cache

Ok(info)
}


/// Identifies a contract from it's address only.
pub async fn identify_contract(&mut self, address: FieldElement) -> Result<ContractInfo> {
Expand All @@ -87,7 +62,11 @@
};

self.cache.insert(address, info.clone());
// TODO: self.storage.register_contract_info(...);

match self.storage.register_contract_info(&address, &info) {
Ok(_) => log::debug!("Contract info registered successfully!"),
Err(e) => log::debug!("Error registering contract info: {:?}", e),
}

Ok(info)
}
Expand Down
10 changes: 7 additions & 3 deletions crates/ark-core/src/managers/event_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#[derive(Debug)]
pub struct EventManager<'a, T: StorageManager, C: StarknetClient> {
storage: &'a T,
client: &'a C,

Check warning on line 15 in crates/ark-core/src/managers/event_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

field `client` is never read

warning: field `client` is never read --> crates/ark-core/src/managers/event_manager.rs:15:5 | 13 | pub struct EventManager<'a, T: StorageManager, C: StarknetClient> { | ------------ field in this struct 14 | storage: &'a T, 15 | client: &'a C, | ^^^^^^ | = note: `EventManager` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis = note: `#[warn(dead_code)]` on by default
token_event: TokenEvent,
}

Expand All @@ -28,7 +28,7 @@

/// Returns the selectors used to filter events.
pub fn keys_selector(&self) -> Option<Vec<Vec<FieldElement>>> {
return Some(vec![vec![TRANSFER_SELECTOR]]);

Check warning on line 31 in crates/ark-core/src/managers/event_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

warning: unneeded `return` statement --> crates/ark-core/src/managers/event_manager.rs:31:9 | 31 | return Some(vec![vec![TRANSFER_SELECTOR]]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default help: remove `return` | 31 - return Some(vec![vec![TRANSFER_SELECTOR]]); 31 + Some(vec![vec![TRANSFER_SELECTOR]]) |
}

/// Formats a token event based on the event content.
Expand Down Expand Up @@ -57,10 +57,11 @@
let (from, to, token_id) = event_info;

// TODO: why do we need this entry 2 times for the felt and the string?
// TODO move that to storage
// self.token_event.from_address = format!("{:#064x}", from);
// self.token_event.to_address = format!("{:#064x}", to);
self.token_event.from_address_field_element = from;
self.token_event.from_address = format!("{:#064x}", from);
self.token_event.to_address_field_element = to;
self.token_event.to_address = format!("{:#064x}", to);
self.token_event.contract_address = format!("{:#064x}", event.from_address);
self.token_event.transaction_hash = format!("{:#064x}", event.transaction_hash);
self.token_event.token_id = token_id.clone();
Expand All @@ -72,7 +73,10 @@

info!("Event identified: {:?}", self.token_event.event_type);

// TODO: self.storage.register_event(self.token_event);
match self.storage.register_event(&self.token_event) {
Ok(_) => log::debug!("Event registered successfully!"),
Err(e) => log::debug!("Error registering event: {:?}", e),
}
// TODO: check depending on event type if it's a create/update etc...?

Ok(self.token_event.clone())
Expand Down
12 changes: 11 additions & 1 deletion crates/ark-core/src/managers/token_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
self.token.padded_token_id = event.padded_token_id.clone();
self.token.from_address = event.from_address.clone();
self.token.to_address = event.to_address.clone();
self.token.timestamp = event.timestamp.clone();

Check warning on line 35 in crates/ark-core/src/managers/token_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

using `clone` on type `u64` which implements the `Copy` trait

warning: using `clone` on type `u64` which implements the `Copy` trait --> crates/ark-core/src/managers/token_manager.rs:35:32 | 35 | self.token.timestamp = event.timestamp.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `event.timestamp` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy = note: `#[warn(clippy::clone_on_copy)]` on by default
self.token.mint_transaction_hash = if event.event_type == EventType::Mint {
Some(event.transaction_hash.clone())
} else {
Expand All @@ -50,8 +50,8 @@
.get_token_owner(
FieldElement::from_hex_be(&event.contract_address)
.expect("Contract address bad format"),
FieldElement::from(event.token_id.low),

Check warning on line 53 in crates/ark-core/src/managers/token_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `starknet::core::types::FieldElement`

warning: useless conversion to the same type: `starknet::core::types::FieldElement` --> crates/ark-core/src/managers/token_manager.rs:53:17 | 53 | FieldElement::from(event.token_id.low), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `FieldElement::from()`: `event.token_id.low` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default
FieldElement::from(event.token_id.high),

Check warning on line 54 in crates/ark-core/src/managers/token_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `starknet::core::types::FieldElement`

warning: useless conversion to the same type: `starknet::core::types::FieldElement` --> crates/ark-core/src/managers/token_manager.rs:54:17 | 54 | FieldElement::from(event.token_id.high), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `FieldElement::from()`: `event.token_id.high` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
)
.await?[0];

Expand All @@ -63,7 +63,17 @@
event.contract_address
);

// TODO: self.storage.register_token(self.token.clone()).await?;
match self.storage.register_token(&self.token) {
Ok(_) => log::debug!("Token registered successfully!"),
Err(e) => log::debug!("Error registering token: {:?}", e),
}

if event.event_type == EventType::Mint {
match self.storage.register_mint(&self.token) {
Ok(_) => log::debug!("Mint registered successfully!"),
Err(e) => log::debug!("Error registering mint: {:?}", e),
}
}

Ok(())
}
Expand Down
4 changes: 3 additions & 1 deletion crates/ark-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ edition = "2021"

[dependencies]
starknet = "0.5.0"
num-bigint = { version = "0.4.3", default-features = false }
num-bigint = { version = "0.4.3", default-features = false }
serde = { version = "1.0.130", features = ["derive"] }
log = "0.4.14"
117 changes: 101 additions & 16 deletions crates/ark-storage/src/storage_manager.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,123 @@
use crate::types::{TokenEvent, TokenFromEvent};
use crate::types::{
BlockIndexing, BlockIndexingStatus, BlockInfo, ContractInfo, StorageError,
TokenEvent, TokenFromEvent,
};
use log;
use starknet::core::types::FieldElement;

pub trait StorageManager {
// Store a new token in the storage
fn create_token(&self, token: &TokenFromEvent);
fn register_mint(&self, token: &TokenFromEvent) -> Result<(), StorageError>;

// Update an existing token's owner in the storage
fn update_token_owner(&self, token: &TokenFromEvent, new_owner: &str);
fn register_token(&self, token: &TokenFromEvent) -> Result<(), StorageError>;

// Log or store a token-related event
fn create_event(&self, event: &TokenEvent);
fn clean_block(&self, block_number: u64) -> Result<(), StorageError>;

fn get_block_info(&self, block_number: u64) -> Result<BlockInfo, StorageError>;

fn get_contract_info(
&self,
contract_address: &FieldElement,
) -> Result<ContractInfo, StorageError>;

fn register_contract_info(
&self,
contract_address: &FieldElement,
info: &ContractInfo,
) -> Result<(), StorageError>;

fn register_event(&self, event: &TokenEvent) -> Result<(), StorageError>;

fn set_block_info(&self, block_number: u64, info: BlockInfo) -> Result<(), StorageError>;

fn set_indexer_progress(&self, progress: BlockIndexing) -> Result<(), StorageError>;
}

// Default implementation (Logging for this example)
pub struct DefaultStorage;

impl DefaultStorage {
pub fn new() -> Self {
Self
}

Check warning on line 40 in crates/ark-storage/src/storage_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

you should consider adding a `Default` implementation for `DefaultStorage`

warning: you should consider adding a `Default` implementation for `DefaultStorage` --> crates/ark-storage/src/storage_manager.rs:38:5 | 38 | / pub fn new() -> Self { 39 | | Self 40 | | } | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default = note: `#[warn(clippy::new_without_default)]` on by default help: try adding this | 37 + impl Default for DefaultStorage { 38 + fn default() -> Self { 39 + Self::new() 40 + } 41 + } |
}

impl StorageManager for DefaultStorage {
fn create_event(&self, event: &TokenEvent) {
println!("STORAGE MANAGER: Event created: {:?}", event);
fn register_token(&self, token: &TokenFromEvent) -> Result<(), StorageError> {
log::debug!("Registering token {:?}", token);
// TODO: In future, handle and return potential errors
// Err(StorageError::DuplicateToken)
Ok(())
}

fn register_mint(&self, token: &TokenFromEvent) -> Result<(), StorageError> {
log::debug!("Registering mint {:?}", token);
// TODO: In future, handle and return potential errors
// Err(StorageError::InvalidMintData)
Ok(())
}

fn clean_block(&self, block_number: u64) -> Result<(), StorageError> {
log::debug!("Cleaning block #{}", block_number);
// TODO: In future, handle and return potential errors
// Err(StorageError::DatabaseError)
Ok(())
}

fn get_block_info(&self, block_number: u64) -> Result<BlockInfo, StorageError> {
log::debug!("Getting block info for block #{}", block_number);
// TODO: In future, handle and return potential errors
// Err(StorageError::NotFound)
Ok(BlockInfo {
indexer_version: 0,
indexer_indentifier: "42".to_string(),
status: BlockIndexingStatus::None,
})
}

fn get_contract_info(
&self,
contract_address: &FieldElement,
) -> Result<ContractInfo, StorageError> {
log::debug!("Getting contract info for contract {}", contract_address);
// TODO: In future, handle and return potential errors
Err(StorageError::NotFound)
}

fn register_contract_info(
&self,
contract_address: &FieldElement,
info: &ContractInfo,
) -> Result<(), StorageError> {
log::debug!(
"Registering contract info {:?} for contract {}",
info,
contract_address
);
// TODO: In future, handle and return potential errors
// Err(StorageError::DuplicateToken)
Ok(())
}

fn register_event(&self, event: &TokenEvent) -> Result<(), StorageError> {
log::debug!("Registering event {:?}", event);
// TODO: In future, handle and return potential errors
// Err(StorageError::DatabaseError)
Ok(())
}

fn create_token(&self, token: &TokenFromEvent) {
println!("STORAGE MANAGER: Token created: {:?}", token);
fn set_block_info(&self, block_number: u64, info: BlockInfo) -> Result<(), StorageError> {
log::debug!("Setting block info {:?} for block #{}", info, block_number);
// TODO: In future, handle and return potential errors
// Err(StorageError::DatabaseError)
Ok(())
}

fn update_token_owner(&self, token: &TokenFromEvent, new_owner: &str) {
println!(
"STORAGE MANAGER: Token updated. Previous Owner: {}, New Owner: {}",
token.owner, new_owner
fn set_indexer_progress(&self, indexer_progress: BlockIndexing) -> Result<(), StorageError> {
log::debug!(
"Setting indexer progress to block #{}",
indexer_progress.percentage
);
// TODO: In future, handle and return potential errors
// Err(StorageError::DatabaseError)
Ok(())
}
}
Loading
Loading