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

Network identifier support #71

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ clap = { version = "4.5.11", features = ["derive", "env"] }
coinbase-mesh = "0.1.0"
convert_case = "0.6.0"
cynic = { version = "3.7.3", features = ["http-reqwest-blocking"] }
dashmap = "6.1.0"
derive_more = { version = "1.0.0", features = ["full"] }
dotenv = "0.15.0"
envy = "0.4.2"
Expand Down
3 changes: 2 additions & 1 deletion src/api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod account_balance;
mod block;
mod cache;
mod call;
mod construction_combine;
mod construction_derive;
Expand All @@ -11,8 +12,8 @@ mod construction_preprocess;
mod construction_submit;
mod mempool;
mod mempool_transaction;
mod network_health_check;
mod network_list;
mod network_options;
mod network_status;
mod search_transactions;
mod validate_network;
10 changes: 4 additions & 6 deletions src/api/account_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ use crate::{

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/account.ml#L11
impl MinaMesh {
pub async fn account_balance(
&self,
AccountBalanceRequest { account_identifier, block_identifier: maybe_block_identifier, .. }: AccountBalanceRequest,
) -> Result<AccountBalanceResponse, MinaMeshError> {
let AccountIdentifier { address, metadata, .. } = *account_identifier;
match maybe_block_identifier {
pub async fn account_balance(&self, req: AccountBalanceRequest) -> Result<AccountBalanceResponse, MinaMeshError> {
self.validate_network(&req.network_identifier).await?;
let AccountIdentifier { address, metadata, .. } = *req.account_identifier;
match req.block_identifier {
Some(block_identifier) => self.block_balance(address, metadata, *block_identifier).await,
None => self.frontier_balance(address).await,
}
Expand Down
1 change: 1 addition & 0 deletions src/api/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/block.ml#L7
impl MinaMesh {
pub async fn block(&self, request: BlockRequest) -> Result<BlockResponse, MinaMeshError> {
self.validate_network(&request.network_identifier).await?;
let partial_block_identifier = *request.block_identifier;
let metadata = match self.block_metadata(&partial_block_identifier).await? {
Some(metadata) => metadata,
Expand Down
20 changes: 20 additions & 0 deletions src/api/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::time::Instant;

use crate::{CacheKey, MinaMesh};

impl MinaMesh {
/// Checks the cache for a valid entry.
pub fn get_from_cache(&self, key: CacheKey) -> Option<String> {
if let Some(cached_entry) = self.cache.get(key.to_string().as_str()) {
let (cached_value, timestamp) = &*cached_entry;
if timestamp.elapsed() < self.cache_ttl {
return Some(cached_value.clone());
}
}
None
}

pub fn insert_into_cache(&self, key: CacheKey, value: String) {
self.cache.insert(key.to_string(), (value, Instant::now()));
}
}
3 changes: 2 additions & 1 deletion src/api/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L849
impl MinaMesh {
pub async fn call(&self, _request: CallRequest) -> Result<CallResponse> {
pub async fn call(&self, request: CallRequest) -> Result<CallResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(CallResponse::new(serde_json::Value::Null, true))
}
}
6 changes: 2 additions & 4 deletions src/api/construction_combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L561
impl MinaMesh {
pub async fn construction_combine(
&self,
_request: ConstructionCombineRequest,
) -> Result<ConstructionCombineResponse> {
pub async fn construction_combine(&self, request: ConstructionCombineRequest) -> Result<ConstructionCombineResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionCombineResponse::new("".to_string()))
}
}
3 changes: 2 additions & 1 deletion src/api/construction_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L162
impl MinaMesh {
pub async fn construction_derive(&self, _request: ConstructionDeriveRequest) -> Result<ConstructionDeriveResponse> {
pub async fn construction_derive(&self, request: ConstructionDeriveRequest) -> Result<ConstructionDeriveResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionDeriveResponse::new())
}
}
3 changes: 2 additions & 1 deletion src/api/construction_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L786
impl MinaMesh {
pub async fn construction_hash(&self, _request: ConstructionHashRequest) -> Result<TransactionIdentifier> {
pub async fn construction_hash(&self, request: ConstructionHashRequest) -> Result<TransactionIdentifier> {
self.validate_network(&request.network_identifier).await?;
Ok(TransactionIdentifier::new("".to_string()))
}
}
3 changes: 2 additions & 1 deletion src/api/construction_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use crate::MinaMesh;
impl MinaMesh {
pub async fn construction_metadata(
&self,
_request: ConstructionMetadataRequest,
request: ConstructionMetadataRequest,
) -> Result<ConstructionMetadataResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionMetadataResponse::new(serde_json::Value::Null))
}
}
3 changes: 2 additions & 1 deletion src/api/construction_parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L615
impl MinaMesh {
pub async fn construction_parse(&self, _request: ConstructionParseRequest) -> Result<ConstructionParseResponse> {
pub async fn construction_parse(&self, request: ConstructionParseRequest) -> Result<ConstructionParseResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionParseResponse::new(vec![]))
}
}
3 changes: 2 additions & 1 deletion src/api/construction_payloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use crate::MinaMesh;
impl MinaMesh {
pub async fn construction_payloads(
&self,
_request: ConstructionPayloadsRequest,
request: ConstructionPayloadsRequest,
) -> Result<ConstructionPayloadsResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionPayloadsResponse::new("".to_string(), vec![]))
}
}
3 changes: 2 additions & 1 deletion src/api/construction_preprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use crate::MinaMesh;
impl MinaMesh {
pub async fn construction_preprocess(
&self,
_request: ConstructionPreprocessRequest,
request: ConstructionPreprocessRequest,
) -> Result<ConstructionPreprocessResponse> {
self.validate_network(&request.network_identifier).await?;
Ok(ConstructionPreprocessResponse::new())
}
}
3 changes: 2 additions & 1 deletion src/api/construction_submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::MinaMesh;

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L849
impl MinaMesh {
pub async fn construction_submit(&self, _request: ConstructionSubmitRequest) -> Result<TransactionIdentifier> {
pub async fn construction_submit(&self, request: ConstructionSubmitRequest) -> Result<TransactionIdentifier> {
self.validate_network(&request.network_identifier).await?;
Ok(TransactionIdentifier::new("".to_string()))
}
}
5 changes: 3 additions & 2 deletions src/api/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
#![allow(clippy::just_underscores_and_digits)]

use anyhow::Result;
use coinbase_mesh::models::{MempoolResponse, TransactionIdentifier};
use coinbase_mesh::models::{MempoolResponse, NetworkRequest, TransactionIdentifier};
use cynic::QueryBuilder;

use crate::{graphql::QueryMempool, MinaMesh};

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/mempool.ml#L56
impl MinaMesh {
pub async fn mempool(&self) -> Result<MempoolResponse> {
pub async fn mempool(&self, req: NetworkRequest) -> Result<MempoolResponse> {
self.validate_network(&req.network_identifier).await?;
let QueryMempool { daemon_status: _0, initial_peers: _1, pooled_user_commands } =
self.graphql_client.send(QueryMempool::build(())).await?;
let hashes = pooled_user_commands
Expand Down
1 change: 1 addition & 0 deletions src/api/mempool_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ impl MinaMesh {
&self,
request: MempoolTransactionRequest,
) -> Result<MempoolTransactionResponse, MinaMeshError> {
self.validate_network(&request.network_identifier).await?;
let QueryMempoolTransactions { daemon_status: _daemon_status, initial_peers: _initial_peers, pooled_user_commands } =
self
.graphql_client
Expand Down
18 changes: 0 additions & 18 deletions src/api/network_health_check.rs

This file was deleted.

5 changes: 3 additions & 2 deletions src/api/network_options.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// TODO: double-check the data is correct
// TODO: why do long string literals in the error metadata break rustfmt?

use coinbase_mesh::models::{Allow, Case, Error, NetworkOptionsResponse, OperationStatus, Version};
use coinbase_mesh::models::{Allow, Case, Error, NetworkOptionsResponse, NetworkRequest, OperationStatus, Version};

use crate::{MinaMesh, MinaMeshError};

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/network.ml#L444
impl MinaMesh {
pub async fn network_options(&self) -> Result<NetworkOptionsResponse, MinaMeshError> {
pub async fn network_options(&self, req: NetworkRequest) -> Result<NetworkOptionsResponse, MinaMeshError> {
self.validate_network(&req.network_identifier).await?;
let errors: Vec<Error> = MinaMeshError::all_errors().into_iter().map(Error::from).collect();

Ok(NetworkOptionsResponse::new(Version::new("1.4.9".to_string(), "1.0.0".to_string()), Allow {
Expand Down
5 changes: 3 additions & 2 deletions src/api/network_status.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TODO: get genesis block identifier from env

use coinbase_mesh::models::{BlockIdentifier, NetworkStatusResponse, Peer};
use coinbase_mesh::models::{BlockIdentifier, NetworkRequest, NetworkStatusResponse, Peer};
use cynic::QueryBuilder;

use crate::{
Expand All @@ -10,7 +10,8 @@ use crate::{

/// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/network.ml#L201
impl MinaMesh {
pub async fn network_status(&self) -> Result<NetworkStatusResponse, MinaMeshError> {
pub async fn network_status(&self, req: NetworkRequest) -> Result<NetworkStatusResponse, MinaMeshError> {
self.validate_network(&req.network_identifier).await?;
let QueryNetworkStatus { best_chain, daemon_status: DaemonStatus3 { peers }, sync_status } =
self.graphql_client.send(QueryNetworkStatus::build(())).await?;
let blocks = best_chain.ok_or(MinaMeshError::ChainInfoMissing)?;
Expand Down
1 change: 1 addition & 0 deletions src/api/search_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl MinaMesh {
&self,
req: SearchTransactionsRequest,
) -> Result<SearchTransactionsResponse, MinaMeshError> {
self.validate_network(&req.network_identifier).await?;
let original_offset = req.offset.unwrap_or(0);
let mut offset = original_offset;
let mut limit = req.limit.unwrap_or(100);
Expand Down
35 changes: 35 additions & 0 deletions src/api/validate_network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use anyhow::Result;
use coinbase_mesh::models::NetworkIdentifier;
use cynic::QueryBuilder;

use crate::{graphql::QueryNetworkId, CacheKey::NetworkId, MinaMesh, MinaMeshError};

impl MinaMesh {
// Validate that the network identifier matches the network id of the GraphQL
// server
pub async fn validate_network(&self, network_identifier: &NetworkIdentifier) -> Result<(), MinaMeshError> {
// Check the cache
if let Some(cached_network_id) = self.get_from_cache(NetworkId) {
return self.compare_network_ids(&cached_network_id, network_identifier);
}

// Fetch from GraphQL if cache is empty or expired
let QueryNetworkId { network_id } = self.graphql_client.send(QueryNetworkId::build(())).await?;
self.insert_into_cache(NetworkId, network_id.clone());
self.compare_network_ids(&network_id, network_identifier)
}

fn compare_network_ids(
&self,
fetched_network_id: &str,
network_identifier: &NetworkIdentifier,
) -> Result<(), MinaMeshError> {
let expected_network_id = format!("{}:{}", network_identifier.blockchain, network_identifier.network);

if fetched_network_id != expected_network_id {
Err(MinaMeshError::NetworkDne(expected_network_id, fetched_network_id.to_string()))
} else {
Ok(())
}
}
}
6 changes: 3 additions & 3 deletions src/commands/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ create_handler!(construction_parse, ConstructionParseRequest);
create_handler!(construction_payloads, ConstructionPayloadsRequest);
create_handler!(construction_preprocess, ConstructionPreprocessRequest);
create_handler!(construction_submit, ConstructionSubmitRequest);
create_handler!(mempool);
create_handler!(mempool, NetworkRequest);
create_handler!(mempool_transaction, MempoolTransactionRequest);
create_handler!(network_list);
create_handler!(network_options);
create_handler!(network_status);
create_handler!(network_options, NetworkRequest);
create_handler!(network_status, NetworkRequest);
create_handler!(search_transactions, SearchTransactionsRequest);

#[debug_handler]
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::time::Duration;
use anyhow::Result;
use clap::{Args, Parser};
use coinbase_mesh::models::BlockIdentifier;
use dashmap::DashMap;
use sqlx::postgres::PgPoolOptions;

use crate::{graphql::GraphQLClient, util::default_mina_proxy_url, MinaMesh, MinaMeshError};
Expand Down Expand Up @@ -69,6 +70,8 @@ impl MinaMeshConfig {
self.genesis_block_identifier_state_hash.to_owned(),
),
search_tx_optimized: self.use_search_tx_optimizations,
cache: DashMap::new(),
cache_ttl: Duration::from_secs(300),
})
}
}
7 changes: 5 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub enum MinaMeshError {
#[error("GraphQL query failed: {0}")]
GraphqlMinaQuery(String),

#[error("Network doesn't exist")]
#[error("Network doesn't exist, expected: {0}, actual: {1}")]
NetworkDne(String, String),

#[error("Chain info missing")]
Expand Down Expand Up @@ -113,7 +113,7 @@ impl MinaMeshError {
MinaMeshError::Sql("SQL syntax error".to_string()),
MinaMeshError::JsonParse(Some("Missing field".to_string())),
MinaMeshError::GraphqlMinaQuery("Timeout".to_string()),
MinaMeshError::NetworkDne("blockchain".to_string(), "network".to_string()),
MinaMeshError::NetworkDne("mina:expected".to_string(), "mina:actual".to_string()),
MinaMeshError::ChainInfoMissing,
MinaMeshError::AccountNotFound("Account ID".to_string()),
MinaMeshError::InvariantViolation,
Expand Down Expand Up @@ -206,6 +206,9 @@ impl MinaMeshError {
MinaMeshError::Exception(msg) => json!({
"error": msg,
}),
MinaMeshError::NetworkDne(expected, actual) => json!({
"error": format!("You are requesting the status for the network {}, but you are connected to the network {}", expected, actual),
}),
_ => json!(""),
}
}
Expand Down
Loading