From 508d1533c19e888caa0b7e7c48e680abf3193fc1 Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Wed, 10 Nov 2021 18:44:21 -0300 Subject: [PATCH] jormungandr: remove explorer --- jormungandr/src/blockchain/bootstrap.rs | 1 - jormungandr/src/blockchain/process.rs | 41 +- jormungandr/src/blockchain/tip.rs | 14 +- .../src/explorer/graphql/certificates.rs | 369 ---- .../src/explorer/graphql/config_param.rs | 525 ------ jormungandr/src/explorer/graphql/mod.rs | 1636 ----------------- jormungandr/src/intercom.rs | 6 - jormungandr/src/main.rs | 47 +- jormungandr/src/rest/context.rs | 1 - jormungandr/src/rest/mod.rs | 29 +- jormungandr/src/start_up/error.rs | 5 +- 11 files changed, 12 insertions(+), 2662 deletions(-) delete mode 100644 jormungandr/src/explorer/graphql/certificates.rs delete mode 100644 jormungandr/src/explorer/graphql/config_param.rs delete mode 100644 jormungandr/src/explorer/graphql/mod.rs diff --git a/jormungandr/src/blockchain/bootstrap.rs b/jormungandr/src/blockchain/bootstrap.rs index a8adaea979..4eec0b7f93 100644 --- a/jormungandr/src/blockchain/bootstrap.rs +++ b/jormungandr/src/blockchain/bootstrap.rs @@ -42,7 +42,6 @@ where blockchain.clone(), None, None, - None, Metrics::builder().build(), ); diff --git a/jormungandr/src/blockchain/process.rs b/jormungandr/src/blockchain/process.rs index 057e8b2825..219949955e 100644 --- a/jormungandr/src/blockchain/process.rs +++ b/jormungandr/src/blockchain/process.rs @@ -7,7 +7,7 @@ use super::{ use crate::{ blockcfg::{Block, Header, HeaderHash}, blockchain::Checkpoints, - intercom::{self, BlockMsg, ExplorerMsg, NetworkMsg, PropagateMsg, TransactionMsg, WatchMsg}, + intercom::{self, BlockMsg, NetworkMsg, PropagateMsg, TransactionMsg, WatchMsg}, metrics::{Metrics, MetricsBackend}, network::p2p::Address, utils::{ @@ -56,7 +56,6 @@ pub struct TaskData { pub stats_counter: Metrics, pub network_msgbox: MessageBox, pub fragment_msgbox: MessageBox, - pub explorer_msgbox: Option>, pub watch_msgbox: MessageBox, pub garbage_collection_interval: Duration, } @@ -74,7 +73,6 @@ struct Process { stats_counter: Metrics, network_msgbox: MessageBox, fragment_msgbox: MessageBox, - explorer_msgbox: Option>, watch_msgbox: MessageBox, garbage_collection_interval: Duration, tip_update_mbox: MessageBox>, @@ -145,7 +143,6 @@ pub async fn start( stats_counter, network_msgbox, fragment_msgbox, - explorer_msgbox, garbage_collection_interval, watch_msgbox, } = task_data; @@ -162,7 +159,6 @@ pub async fn start( stats_counter, network_msgbox, fragment_msgbox, - explorer_msgbox, watch_msgbox, garbage_collection_interval, tip_update_mbox, @@ -186,7 +182,6 @@ impl Process { self.blockchain_tip.clone(), self.blockchain.clone(), Some(self.fragment_msgbox.clone()), - self.explorer_msgbox.clone(), Some(self.watch_msgbox.clone()), self.stats_counter.clone(), ); @@ -204,7 +199,6 @@ impl Process { let blockchain = self.blockchain.clone(); let blockchain_tip = self.blockchain_tip.clone(); let network_msg_box = self.network_msgbox.clone(); - let explorer_msg_box = self.explorer_msgbox.clone(); let watch_msg_box = self.watch_msgbox.clone(); let stats_counter = self.stats_counter.clone(); tracing::trace!("handling new blockchain task item"); @@ -228,7 +222,6 @@ impl Process { blockchain, self.tip_update_mbox.clone(), network_msg_box, - explorer_msg_box, watch_msg_box, leadership_block, ) @@ -278,7 +271,6 @@ impl Process { self.blockchain.clone(), self.tip_update_mbox.clone(), network_msg_box, - explorer_msg_box, watch_msg_box, self.get_next_block_scheduler.clone(), handle, @@ -362,7 +354,6 @@ async fn process_leadership_block( mut blockchain: Blockchain, tip_update_mbox: MessageBox>, network_msg_box: MessageBox, - explorer_msg_box: Option>, mut watch_msg_box: MessageBox, leadership_block: LeadershipBlock, ) -> chain::Result<()> { @@ -373,10 +364,6 @@ async fn process_leadership_block( .send(WatchMsg::NewBlock(block.clone())) .await?; - if let Some(mut msg_box) = explorer_msg_box { - msg_box.send(ExplorerMsg::NewBlock(block)).await?; - } - process_and_propagate_new_ref(Arc::clone(&new_block_ref), tip_update_mbox, network_msg_box) .await?; @@ -448,7 +435,6 @@ async fn process_network_blocks( blockchain: Blockchain, tip_update_mbox: MessageBox>, network_msg_box: MessageBox, - mut explorer_msg_box: Option>, mut watch_msg_box: MessageBox, mut get_next_block_scheduler: GetNextBlockScheduler, handle: intercom::RequestStreamHandle, @@ -464,7 +450,6 @@ async fn process_network_blocks( let res = process_network_block( &blockchain, block.clone(), - explorer_msg_box.as_mut(), &mut watch_msg_box, &mut get_next_block_scheduler, ) @@ -513,7 +498,6 @@ async fn process_network_blocks( async fn process_network_block( blockchain: &Blockchain, block: Block, - explorer_msg_box: Option<&mut MessageBox>, watch_msg_box: &mut MessageBox, get_next_block_scheduler: &mut GetNextBlockScheduler, ) -> Result>, chain::Error> { @@ -544,14 +528,7 @@ async fn process_network_block( Err(Error::MissingParentBlock(parent_hash)) } PreCheckedHeader::HeaderWithCache { parent_ref, .. } => { - let r = check_and_apply_block( - blockchain, - parent_ref, - block, - explorer_msg_box, - watch_msg_box, - ) - .await; + let r = check_and_apply_block(blockchain, parent_ref, block, watch_msg_box).await; r } } @@ -564,10 +541,8 @@ async fn check_and_apply_block( blockchain: &Blockchain, parent_ref: Arc, block: Block, - explorer_msg_box: Option<&mut MessageBox>, watch_msg_box: &mut MessageBox, ) -> Result>, chain::Error> { - let explorer_enabled = explorer_msg_box.is_some(); let post_checked = blockchain .post_check_header( block.header().clone(), @@ -576,22 +551,14 @@ async fn check_and_apply_block( ) .await?; tracing::debug!("applying block to storage"); - let mut block_for_explorer = if explorer_enabled { - Some(block.clone()) - } else { - None - }; + let block_for_watchers = block.clone(); + let applied_block = blockchain .apply_and_store_block(post_checked, block) .await?; if let AppliedBlock::New(block_ref) = applied_block { tracing::debug!("applied block to storage"); - if let Some(msg_box) = explorer_msg_box { - msg_box - .try_send(ExplorerMsg::NewBlock(block_for_explorer.take().unwrap())) - .unwrap_or_else(|err| tracing::error!("cannot add block to explorer: {}", err)); - } watch_msg_box .try_send(WatchMsg::NewBlock(block_for_watchers)) diff --git a/jormungandr/src/blockchain/tip.rs b/jormungandr/src/blockchain/tip.rs index 39ac7088c8..d3f9a6631c 100644 --- a/jormungandr/src/blockchain/tip.rs +++ b/jormungandr/src/blockchain/tip.rs @@ -4,7 +4,7 @@ use crate::{ chain_selection::{self, ComparisonResult}, storage, Blockchain, Branch, Error, Ref, MAIN_BRANCH_TAG, }, - intercom::{ExplorerMsg, TransactionMsg, WatchMsg}, + intercom::{TransactionMsg, WatchMsg}, metrics::{Metrics, MetricsBackend}, utils::async_msg::{self, MessageBox, MessageQueue}, }; @@ -26,7 +26,6 @@ const BRANCH_REPROCESSING_INTERVAL: Duration = Duration::from_secs(60); pub struct TipUpdater { tip: Tip, blockchain: Blockchain, - explorer_mbox: Option>, watch_mbox: Option>, fragment_mbox: Option>, stats_counter: Metrics, @@ -37,7 +36,6 @@ impl TipUpdater { tip: Tip, blockchain: Blockchain, fragment_mbox: Option>, - explorer_mbox: Option>, watch_mbox: Option>, stats_counter: Metrics, ) -> Self { @@ -45,7 +43,6 @@ impl TipUpdater { tip, blockchain, fragment_mbox, - explorer_mbox, watch_mbox, stats_counter, } @@ -172,15 +169,6 @@ impl TipUpdater { } self.stats_counter.set_tip_block(&block, &candidate); - if let Some(ref mut msg_box) = self.explorer_mbox { - tracing::debug!("sending new tip to explorer {}", candidate_hash); - msg_box - .send(ExplorerMsg::NewTip(candidate_hash)) - .await - .unwrap_or_else(|err| { - tracing::error!("cannot send new tip to explorer: {}", err) - }); - } if let Some(ref mut msg_box) = self.watch_mbox { tracing::debug!("sending new tip to watch subscribers {}", candidate_hash); diff --git a/jormungandr/src/explorer/graphql/certificates.rs b/jormungandr/src/explorer/graphql/certificates.rs deleted file mode 100644 index ccd2e210f3..0000000000 --- a/jormungandr/src/explorer/graphql/certificates.rs +++ /dev/null @@ -1,369 +0,0 @@ -use super::config_param::ConfigParams; -use super::error::ApiError; -use async_graphql::{Context, FieldResult, Object, Union}; -use chain_impl_mockchain::certificate; -use std::convert::TryFrom; - -use super::scalars::{PayloadType, PoolId, PublicKey, TimeOffsetSeconds, VotePlanId}; -use super::{Address, BftLeader, BlockDate, ExplorerAddress, Pool, Proposal, TaxType}; -use crate::rest::explorer::EContext as RestContext; - -// interface for grouping certificates as a graphl union -#[derive(Union)] -pub enum Certificate { - StakeDelegation(StakeDelegation), - OwnerStakeDelegation(OwnerStakeDelegation), - PoolRegistration(PoolRegistration), - PoolRetirement(PoolRetirement), - PoolUpdate(PoolUpdate), - VotePlan(VotePlan), - VoteCast(VoteCast), - VoteTally(VoteTally), - EncryptedVoteTally(EncryptedVoteTally), - UpdateProposal(UpdateProposal), - UpdateVote(UpdateVote), -} - -pub struct StakeDelegation(certificate::StakeDelegation); - -pub struct PoolRegistration(certificate::PoolRegistration); - -pub struct OwnerStakeDelegation(certificate::OwnerStakeDelegation); - -/// Retirement info for a pool -pub struct PoolRetirement(certificate::PoolRetirement); - -pub struct PoolUpdate(certificate::PoolUpdate); - -pub struct VotePlan(certificate::VotePlan); - -pub struct VoteCast(certificate::VoteCast); - -pub struct VoteTally(certificate::VoteTally); - -pub struct EncryptedVoteTally(certificate::EncryptedVoteTally); - -pub struct UpdateProposal(certificate::UpdateProposal); - -pub struct UpdateVote(certificate::UpdateVote); - -#[Object] -impl StakeDelegation { - // FIXME: Maybe a new Account type would be better? - pub async fn account(&self, context: &Context<'_>) -> FieldResult
{ - let discrimination = context - .data_unchecked::() - .get() - .await - .unwrap() - .db - .blockchain_config - .discrimination; - self.0 - .account_id - .to_single_account() - .ok_or_else(|| - // TODO: Multisig address? - ApiError::Unimplemented.into()) - .map(|single| { - chain_addr::Address(discrimination, chain_addr::Kind::Account(single.into())) - }) - .map(|addr| Address::from(&ExplorerAddress::New(addr))) - } - - pub async fn pools(&self) -> Vec { - use chain_impl_mockchain::account::DelegationType; - - match self.0.get_delegation_type() { - DelegationType::NonDelegated => vec![], - DelegationType::Full(id) => vec![Pool::from_valid_id(id.clone())], - DelegationType::Ratio(delegation_ratio) => delegation_ratio - .pools() - .iter() - .cloned() - .map(|(p, _)| Pool::from_valid_id(p)) - .collect(), - } - } -} - -#[Object] -impl PoolRegistration { - pub async fn pool(&self) -> Pool { - Pool::from_valid_id(self.0.to_id()) - } - - /// Beginning of validity for this pool, this is used - /// to keep track of the period of the expected key and the expiry - pub async fn start_validity(&self) -> TimeOffsetSeconds { - self.0.start_validity.into() - } - - /// Management threshold for owners, this need to be <= #owners and > 0 - pub async fn management_threshold(&self) -> i32 { - // XXX: u8 fits in i32, but maybe some kind of custom scalar is better? - self.0.management_threshold().into() - } - - /// Owners of this pool - pub async fn owners(&self) -> Vec { - self.0.owners.iter().map(PublicKey::from).collect() - } - - pub async fn operators(&self) -> Vec { - self.0.operators.iter().map(PublicKey::from).collect() - } - - pub async fn rewards(&self) -> TaxType { - TaxType(self.0.rewards) - } - - /// Reward account - pub async fn reward_account(&self, context: &Context<'_>) -> Option
{ - use chain_impl_mockchain::transaction::AccountIdentifier; - let discrimination = context - .data_unchecked::() - .get() - .await - .unwrap() - .db - .blockchain_config - .discrimination; - - // FIXME: Move this transformation to a point earlier - - self.0 - .reward_account - .clone() - .map(|acc_id| match acc_id { - AccountIdentifier::Single(d) => ExplorerAddress::New(chain_addr::Address( - discrimination, - chain_addr::Kind::Account(d.into()), - )), - AccountIdentifier::Multi(d) => { - let mut bytes = [0u8; 32]; - bytes.copy_from_slice(&d.as_ref()[0..32]); - ExplorerAddress::New(chain_addr::Address( - discrimination, - chain_addr::Kind::Multisig(bytes), - )) - } - }) - .map(|explorer_address| Address { - id: explorer_address, - }) - } - - // Genesis Praos keys - // pub keys: GenesisPraosLeader, -} - -#[Object] -impl OwnerStakeDelegation { - async fn pools(&self) -> Vec { - use chain_impl_mockchain::account::DelegationType; - - match self.0.get_delegation_type() { - DelegationType::NonDelegated => vec![], - DelegationType::Full(id) => vec![Pool::from_valid_id(id.clone())], - DelegationType::Ratio(delegation_ratio) => delegation_ratio - .pools() - .iter() - .cloned() - .map(|(p, _)| Pool::from_valid_id(p)) - .collect(), - } - } -} - -#[Object] -impl PoolRetirement { - pub async fn pool_id(&self) -> PoolId { - PoolId(self.0.pool_id.clone()) - } - - pub async fn retirement_time(&self) -> TimeOffsetSeconds { - self.0.retirement_time.into() - } -} - -#[Object] -impl PoolUpdate { - pub async fn pool_id(&self) -> PoolId { - PoolId(self.0.pool_id.clone()) - } - - pub async fn start_validity(&self) -> TimeOffsetSeconds { - self.0.new_pool_reg.start_validity.into() - } - - // TODO: Previous keys? - // TODO: Updated keys? -} - -#[Object] -impl VotePlan { - /// the vote start validity - pub async fn vote_start(&self) -> BlockDate { - self.0.vote_start().into() - } - - /// the duration within which it is possible to vote for one of the proposals - /// of this voting plan. - pub async fn vote_end(&self) -> BlockDate { - self.0.vote_end().into() - } - - /// the committee duration is the time allocated to the committee to open - /// the ballots and publish the results on chain - pub async fn committee_end(&self) -> BlockDate { - self.0.committee_end().into() - } - - pub async fn payload_type(&self) -> PayloadType { - self.0.payload_type().into() - } - - /// the proposals to vote for - pub async fn proposals(&self) -> Vec { - self.0.proposals().iter().cloned().map(Proposal).collect() - } -} - -#[Object] -impl VoteCast { - pub async fn vote_plan(&self) -> VotePlanId { - self.0.vote_plan().clone().into() - } - - pub async fn proposal_index(&self) -> i32 { - self.0.proposal_index() as i32 - } -} - -#[Object] -impl VoteTally { - pub async fn vote_plan(&self) -> VotePlanId { - self.0.id().clone().into() - } -} - -#[Object] -impl EncryptedVoteTally { - pub async fn vote_plan(&self) -> VotePlanId { - self.0.id().clone().into() - } -} - -#[Object] -impl UpdateProposal { - pub async fn changes(&self) -> ConfigParams { - self.0.changes().into() - } - - pub async fn proposer_id(&self) -> BftLeader { - self.0.proposer_id().clone().into() - } -} - -#[Object] -impl UpdateVote { - pub async fn proposal_id(&self) -> String { - format!("{}", self.0.proposal_id()) - } - - pub async fn voter_id(&self) -> BftLeader { - self.0.voter_id().clone().into() - } -} - -/*------------------------------*/ -/*------- Conversions ---------*/ -/*----------------------------*/ - -impl TryFrom for Certificate { - type Error = super::error::ApiError; - fn try_from( - original: chain_impl_mockchain::certificate::Certificate, - ) -> Result { - match original { - certificate::Certificate::StakeDelegation(c) => { - Ok(Certificate::StakeDelegation(StakeDelegation(c))) - } - certificate::Certificate::OwnerStakeDelegation(c) => { - Ok(Certificate::OwnerStakeDelegation(OwnerStakeDelegation(c))) - } - certificate::Certificate::PoolRegistration(c) => { - Ok(Certificate::PoolRegistration(PoolRegistration(c))) - } - certificate::Certificate::PoolRetirement(c) => { - Ok(Certificate::PoolRetirement(PoolRetirement(c))) - } - certificate::Certificate::PoolUpdate(c) => Ok(Certificate::PoolUpdate(PoolUpdate(c))), - certificate::Certificate::VotePlan(c) => Ok(Certificate::VotePlan(VotePlan(c))), - certificate::Certificate::VoteCast(c) => Ok(Certificate::VoteCast(VoteCast(c))), - certificate::Certificate::VoteTally(c) => Ok(Certificate::VoteTally(VoteTally(c))), - certificate::Certificate::EncryptedVoteTally(c) => { - Ok(Certificate::EncryptedVoteTally(EncryptedVoteTally(c))) - } - certificate::Certificate::UpdateProposal(c) => { - Ok(Certificate::UpdateProposal(UpdateProposal(c))) - } - certificate::Certificate::UpdateVote(c) => Ok(Certificate::UpdateVote(UpdateVote(c))), - } - } -} - -impl From for StakeDelegation { - fn from(delegation: certificate::StakeDelegation) -> StakeDelegation { - StakeDelegation(delegation) - } -} - -impl From for OwnerStakeDelegation { - fn from(owner_stake_delegation: certificate::OwnerStakeDelegation) -> OwnerStakeDelegation { - OwnerStakeDelegation(owner_stake_delegation) - } -} - -impl From for PoolRegistration { - fn from(registration: certificate::PoolRegistration) -> PoolRegistration { - PoolRegistration(registration) - } -} - -impl From for PoolRetirement { - fn from(pool_retirement: certificate::PoolRetirement) -> PoolRetirement { - PoolRetirement(pool_retirement) - } -} - -impl From for PoolUpdate { - fn from(pool_update: certificate::PoolUpdate) -> PoolUpdate { - PoolUpdate(pool_update) - } -} - -impl From for VotePlan { - fn from(vote_plan: certificate::VotePlan) -> VotePlan { - VotePlan(vote_plan) - } -} - -impl From for VoteCast { - fn from(vote_cast: certificate::VoteCast) -> VoteCast { - VoteCast(vote_cast) - } -} - -impl From for UpdateProposal { - fn from(update_proposal: certificate::UpdateProposal) -> Self { - UpdateProposal(update_proposal) - } -} - -impl From for UpdateVote { - fn from(update_vote: certificate::UpdateVote) -> Self { - UpdateVote(update_vote) - } -} diff --git a/jormungandr/src/explorer/graphql/config_param.rs b/jormungandr/src/explorer/graphql/config_param.rs deleted file mode 100644 index 2cb5efddfc..0000000000 --- a/jormungandr/src/explorer/graphql/config_param.rs +++ /dev/null @@ -1,525 +0,0 @@ -use super::{scalars::Value, BftLeader, Ratio, TaxType}; -use async_graphql::{Enum, SimpleObject, Union}; -use chain_addr::Discrimination as DiscriminationLib; -use chain_impl_mockchain::{ - chaintypes::ConsensusType as ContainerTypeLib, - config::{ - Block0Date as Block0DateLib, ConfigParam as ConfigParamLib, RewardParams as RewardParamsLib, - }, - fee::{ - LinearFee as LinearFeeLib, PerCertificateFee as PerCertificateFeeLib, - PerVoteCertificateFee as PerVoteCertificateFeeLib, - }, - fragment::ConfigParams as ConfigParamsLib, - key::BftLeaderId, - milli::Milli as MilliLib, - rewards::{Ratio as RatioLib, TaxType as TaxTypeLib}, - value::Value as ValueLib, - vote::CommitteeId, -}; -use std::num::{NonZeroU32, NonZeroU64}; - -#[derive(SimpleObject)] -pub struct Block0Date { - block0_date: u64, -} - -impl From<&Block0DateLib> for Block0Date { - fn from(v: &Block0DateLib) -> Self { - Self { block0_date: v.0 } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Enum)] -pub enum DiscriminationEnum { - Production, - Test, -} - -#[derive(SimpleObject)] -pub struct Discrimination { - discrimination: DiscriminationEnum, -} - -impl From<&DiscriminationLib> for Discrimination { - fn from(v: &DiscriminationLib) -> Self { - match v { - DiscriminationLib::Production => Self { - discrimination: DiscriminationEnum::Production, - }, - DiscriminationLib::Test => Self { - discrimination: DiscriminationEnum::Test, - }, - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Enum)] -pub enum ConsensusTypeEnum { - Bft, - GenesisPraos, -} - -#[derive(SimpleObject)] -pub struct ConsensusType { - consensus_type: ConsensusTypeEnum, -} - -impl From<&ContainerTypeLib> for ConsensusType { - fn from(v: &ContainerTypeLib) -> Self { - match v { - ContainerTypeLib::Bft => Self { - consensus_type: ConsensusTypeEnum::Bft, - }, - ContainerTypeLib::GenesisPraos => Self { - consensus_type: ConsensusTypeEnum::GenesisPraos, - }, - } - } -} - -#[derive(SimpleObject)] -pub struct SlotsPerEpoch { - slots_per_epoch: u32, -} - -impl From<&u32> for SlotsPerEpoch { - fn from(v: &u32) -> Self { - Self { - slots_per_epoch: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct SlotDuration { - slot_duration: u8, -} - -impl From<&u8> for SlotDuration { - fn from(v: &u8) -> Self { - Self { slot_duration: *v } - } -} - -#[derive(SimpleObject)] -pub struct EpochStabilityDepth { - epoch_stability_depth: u32, -} - -impl From<&u32> for EpochStabilityDepth { - fn from(v: &u32) -> Self { - Self { - epoch_stability_depth: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct Milli { - milli: u64, -} - -impl From<&MilliLib> for Milli { - fn from(v: &MilliLib) -> Self { - Self { - milli: (*v).to_millis(), - } - } -} - -#[derive(SimpleObject)] -pub struct BlockContentMaxSize { - block_content_max_size: u32, -} - -impl From<&u32> for BlockContentMaxSize { - fn from(v: &u32) -> Self { - Self { - block_content_max_size: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct AddBftLeader { - add_bft_leader: BftLeader, -} - -impl From<&BftLeaderId> for AddBftLeader { - fn from(v: &BftLeaderId) -> Self { - Self { - add_bft_leader: v.clone().into(), - } - } -} - -#[derive(SimpleObject)] -pub struct RemoveBftLeader { - remove_bft_leader: BftLeader, -} - -impl From<&BftLeaderId> for RemoveBftLeader { - fn from(v: &BftLeaderId) -> Self { - Self { - remove_bft_leader: v.clone().into(), - } - } -} - -#[derive(SimpleObject)] -pub struct LinearFee { - constant: u64, - coefficient: u64, - certificate: u64, - per_certificate_fees: PerCertificateFee, - per_vote_certificate_fees: PerVoteCertificateFee, -} - -#[derive(SimpleObject)] -pub struct PerCertificateFee { - certificate_pool_registration: Option, - certificate_stake_delegation: Option, - certificate_owner_stake_delegation: Option, -} - -impl From<&PerCertificateFeeLib> for PerCertificateFee { - fn from(v: &PerCertificateFeeLib) -> Self { - Self { - certificate_pool_registration: v.certificate_pool_registration, - certificate_stake_delegation: v.certificate_owner_stake_delegation, - certificate_owner_stake_delegation: v.certificate_owner_stake_delegation, - } - } -} - -#[derive(SimpleObject)] -pub struct PerVoteCertificateFee { - certificate_vote_plan: Option, - certificate_vote_cast: Option, -} - -impl From<&PerVoteCertificateFeeLib> for PerVoteCertificateFee { - fn from(v: &PerVoteCertificateFeeLib) -> Self { - Self { - certificate_vote_plan: v.certificate_vote_plan, - certificate_vote_cast: v.certificate_vote_cast, - } - } -} - -impl From<&LinearFeeLib> for LinearFee { - fn from(v: &LinearFeeLib) -> Self { - Self { - constant: v.constant, - coefficient: v.coefficient, - certificate: v.certificate, - per_certificate_fees: PerCertificateFee { - certificate_pool_registration: v.per_certificate_fees.certificate_pool_registration, - certificate_stake_delegation: v.per_certificate_fees.certificate_stake_delegation, - certificate_owner_stake_delegation: v - .per_certificate_fees - .certificate_owner_stake_delegation, - }, - per_vote_certificate_fees: PerVoteCertificateFee { - certificate_vote_plan: v.per_vote_certificate_fees.certificate_vote_plan, - certificate_vote_cast: v.per_vote_certificate_fees.certificate_vote_cast, - }, - } - } -} - -#[derive(SimpleObject)] -pub struct ProposalExpiration { - proposal_expiration: u32, -} - -impl From<&u32> for ProposalExpiration { - fn from(v: &u32) -> Self { - Self { - proposal_expiration: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct KesUpdateSpeed { - kes_update_speed: u32, -} - -impl From<&u32> for KesUpdateSpeed { - fn from(v: &u32) -> Self { - Self { - kes_update_speed: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct TreasuryAdd { - treasury_add: Value, -} - -impl From<&ValueLib> for TreasuryAdd { - fn from(v: &ValueLib) -> Self { - Self { - treasury_add: Value(*v), - } - } -} - -#[derive(SimpleObject)] -pub struct TreasuryParams { - treasury_params: TaxType, -} - -impl From<&TaxTypeLib> for TreasuryParams { - fn from(v: &TaxTypeLib) -> Self { - Self { - treasury_params: TaxType(*v), - } - } -} - -#[derive(SimpleObject)] -pub struct RewardPot { - reward_pot: Value, -} - -impl From<&ValueLib> for RewardPot { - fn from(v: &ValueLib) -> Self { - Self { - reward_pot: Value(*v), - } - } -} - -#[derive(SimpleObject)] -pub struct LinearRewardParams { - constant: u64, - ratio: Ratio, - epoch_start: u32, - epoch_rate: NonZeroU32, -} - -#[derive(SimpleObject)] -pub struct HalvingRewardParams { - constant: u64, - ratio: Ratio, - epoch_start: u32, - epoch_rate: NonZeroU32, -} - -#[derive(Union)] -pub enum RewardParamsUnion { - Linear(LinearRewardParams), - Halving(HalvingRewardParams), -} - -#[derive(SimpleObject)] -pub struct RewardParams { - reward_params: RewardParamsUnion, -} - -impl From<&RewardParamsLib> for RewardParams { - fn from(v: &RewardParamsLib) -> Self { - match v { - RewardParamsLib::Linear { - constant, - ratio, - epoch_start, - epoch_rate, - } => Self { - reward_params: RewardParamsUnion::Linear(LinearRewardParams { - constant: *constant, - ratio: Ratio(*ratio), - epoch_start: *epoch_start, - epoch_rate: *epoch_rate, - }), - }, - RewardParamsLib::Halving { - constant, - ratio, - epoch_start, - epoch_rate, - } => Self { - reward_params: RewardParamsUnion::Halving(HalvingRewardParams { - constant: *constant, - ratio: Ratio(*ratio), - epoch_start: *epoch_start, - epoch_rate: *epoch_rate, - }), - }, - } - } -} - -#[derive(SimpleObject)] -pub struct FeesInTreasury { - fees_in_treasury: bool, -} - -impl From<&bool> for FeesInTreasury { - fn from(v: &bool) -> Self { - Self { - fees_in_treasury: *v, - } - } -} - -#[derive(SimpleObject)] -pub struct RewardLimitNone { - reward_limit_none: bool, -} - -#[derive(SimpleObject)] -pub struct RewardLimitByAbsoluteStake { - reward_limit_by_absolute_stake: Ratio, -} - -impl From<&RatioLib> for RewardLimitByAbsoluteStake { - fn from(v: &RatioLib) -> Self { - Self { - reward_limit_by_absolute_stake: Ratio(*v), - } - } -} - -#[derive(SimpleObject)] -pub struct PoolRewardParticipationCapping { - min: NonZeroU32, - max: NonZeroU32, -} - -impl From<&(NonZeroU32, NonZeroU32)> for PoolRewardParticipationCapping { - fn from(v: &(NonZeroU32, NonZeroU32)) -> Self { - Self { min: v.0, max: v.1 } - } -} - -#[derive(SimpleObject)] -pub struct AddCommitteeId { - add_committee_id: String, -} - -impl From<&CommitteeId> for AddCommitteeId { - fn from(v: &CommitteeId) -> Self { - Self { - add_committee_id: (*v).to_hex(), - } - } -} - -#[derive(SimpleObject)] -pub struct RemoveCommitteeId { - remove_committee_id: String, -} - -impl From<&CommitteeId> for RemoveCommitteeId { - fn from(v: &CommitteeId) -> Self { - Self { - remove_committee_id: (*v).to_hex(), - } - } -} - -#[derive(SimpleObject)] -pub struct TransactionMaxExpiryEpochs { - transaction_max_expiry_epochs: u8, -} - -impl From<&u8> for TransactionMaxExpiryEpochs { - fn from(v: &u8) -> Self { - Self { - transaction_max_expiry_epochs: *v, - } - } -} - -#[derive(Union)] -pub enum ConfigParam { - Block0Date(Block0Date), - Discrimination(Discrimination), - ConsensusVersion(ConsensusType), - SlotsPerEpoch(SlotsPerEpoch), - SlotDuration(SlotDuration), - EpochStabilityDepth(EpochStabilityDepth), - ConsensusGenesisPraosActiveSlotsCoeff(Milli), - BlockContentMaxSize(BlockContentMaxSize), - AddBftLeader(AddBftLeader), - RemoveBftLeader(RemoveBftLeader), - LinearFee(LinearFee), - ProposalExpiration(ProposalExpiration), - KesUpdateSpeed(KesUpdateSpeed), - TreasuryAdd(TreasuryAdd), - TreasuryParams(TreasuryParams), - RewardPot(RewardPot), - RewardParams(RewardParams), - PerCertificateFee(PerCertificateFee), - FeesInTreasury(FeesInTreasury), - RewardLimitNone(RewardLimitNone), - RewardLimitByAbsoluteStake(RewardLimitByAbsoluteStake), - PoolRewardParticipationCapping(PoolRewardParticipationCapping), - AddCommitteeId(AddCommitteeId), - RemoveCommitteeId(RemoveCommitteeId), - PerVoteCertificateFees(PerVoteCertificateFee), - TransactionMaxExpiryEpochs(TransactionMaxExpiryEpochs), -} - -#[derive(SimpleObject)] -pub struct ConfigParams { - config_params: Vec, -} - -impl From<&ConfigParamLib> for ConfigParam { - fn from(v: &ConfigParamLib) -> Self { - match v { - ConfigParamLib::Block0Date(v) => Self::Block0Date(v.into()), - ConfigParamLib::Discrimination(v) => Self::Discrimination(v.into()), - ConfigParamLib::ConsensusVersion(v) => Self::ConsensusVersion(v.into()), - ConfigParamLib::SlotsPerEpoch(v) => Self::SlotsPerEpoch(v.into()), - ConfigParamLib::SlotDuration(v) => Self::SlotDuration(v.into()), - ConfigParamLib::EpochStabilityDepth(v) => Self::EpochStabilityDepth(v.into()), - ConfigParamLib::ConsensusGenesisPraosActiveSlotsCoeff(v) => { - Self::ConsensusGenesisPraosActiveSlotsCoeff(v.into()) - } - ConfigParamLib::BlockContentMaxSize(v) => Self::BlockContentMaxSize(v.into()), - ConfigParamLib::AddBftLeader(v) => Self::AddBftLeader(v.into()), - ConfigParamLib::RemoveBftLeader(v) => Self::RemoveBftLeader(v.into()), - ConfigParamLib::LinearFee(v) => Self::LinearFee(v.into()), - ConfigParamLib::ProposalExpiration(v) => Self::ProposalExpiration(v.into()), - ConfigParamLib::KesUpdateSpeed(v) => Self::KesUpdateSpeed(v.into()), - ConfigParamLib::TreasuryAdd(v) => Self::TreasuryAdd(v.into()), - ConfigParamLib::TreasuryParams(v) => Self::TreasuryParams(v.into()), - ConfigParamLib::RewardPot(v) => Self::RewardPot(v.into()), - ConfigParamLib::RewardParams(v) => Self::RewardParams(v.into()), - ConfigParamLib::PerCertificateFees(v) => Self::PerCertificateFee(v.into()), - ConfigParamLib::FeesInTreasury(v) => Self::FeesInTreasury(v.into()), - ConfigParamLib::RewardLimitNone => Self::RewardLimitNone(RewardLimitNone { - reward_limit_none: true, - }), - ConfigParamLib::RewardLimitByAbsoluteStake(v) => { - Self::RewardLimitByAbsoluteStake(v.into()) - } - ConfigParamLib::PoolRewardParticipationCapping(v) => { - Self::PoolRewardParticipationCapping(v.into()) - } - ConfigParamLib::AddCommitteeId(v) => Self::AddCommitteeId(v.into()), - ConfigParamLib::RemoveCommitteeId(v) => Self::RemoveCommitteeId(v.into()), - ConfigParamLib::PerVoteCertificateFees(v) => Self::PerVoteCertificateFees(v.into()), - ConfigParamLib::TransactionMaxExpiryEpochs(v) => { - Self::TransactionMaxExpiryEpochs(v.into()) - } - } - } -} - -impl From<&ConfigParamsLib> for ConfigParams { - fn from(v: &ConfigParamsLib) -> Self { - let mut config_params = Vec::new(); - for el in v.iter() { - config_params.push(el.into()); - } - Self { config_params } - } -} diff --git a/jormungandr/src/explorer/graphql/mod.rs b/jormungandr/src/explorer/graphql/mod.rs deleted file mode 100644 index ea4524e710..0000000000 --- a/jormungandr/src/explorer/graphql/mod.rs +++ /dev/null @@ -1,1636 +0,0 @@ -mod certificates; -mod config_param; -mod connections; -mod error; -mod scalars; - -use self::config_param::{EpochStabilityDepth, LinearFee}; -use self::connections::{ - compute_interval, ConnectionFields, InclusivePaginationInterval, PaginationInterval, - ValidatedPaginationArguments, -}; -use self::error::ApiError; -use self::scalars::{ - BlockCount, ChainLength, EpochNumber, ExternalProposalId, IndexCursor, NonZero, PayloadType, - PoolCount, PoolId, PublicKey, Slot, TransactionCount, Value, VoteOptionRange, VotePlanId, - VotePlanStatusCount, Weight, -}; -use super::indexing::{ - BlockProducer, EpochData, ExplorerAddress, ExplorerBlock, ExplorerTransaction, StakePoolData, -}; -use super::persistent_sequence::PersistentSequence; -use crate::blockcfg::{self, FragmentId, HeaderHash}; -use crate::explorer::indexing::ExplorerVote; -use crate::explorer::{ExplorerDb, Settings as ChainSettings}; -use async_graphql::connection::{query, Connection, Edge, EmptyFields}; -use async_graphql::{ - Context, EmptyMutation, FieldError, FieldResult, Object, SimpleObject, Subscription, Union, -}; -use cardano_legacy_address::Addr as OldAddress; -use certificates::*; -use chain_impl_mockchain::certificate; -use chain_impl_mockchain::key::BftLeaderId; -use chain_impl_mockchain::vote::{EncryptedVote, ProofOfCorrectVote}; -use std::convert::{TryFrom, TryInto}; -use std::str::FromStr; -use std::sync::Arc; - -pub(crate) type RestContext = crate::rest::explorer::EContext; - -pub struct Branch { - state: super::multiverse::Ref, - id: HeaderHash, -} - -impl Branch { - async fn try_from_id(id: HeaderHash, context: &EContext) -> FieldResult { - context - .db - .get_branch(&id) - .await - .map(|state| Branch { state, id }) - .ok_or_else(|| ApiError::NotFound("branch not found".to_string()).into()) - } - - fn from_id_and_state(id: HeaderHash, state: super::multiverse::Ref) -> Branch { - Branch { state, id } - } -} - -#[Object] -impl Branch { - pub async fn id(&self) -> String { - format!("{}", self.id) - } - - pub async fn block(&self) -> Block { - Block::from_contents(Arc::clone( - self.state.state().blocks.lookup(&self.id).unwrap(), - )) - } - - pub async fn blocks( - &self, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult, EmptyFields>> - { - let block0 = 0u32; - let chain_length = self.state.state().blocks.size(); - - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let boundaries = PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: block0, - // this try_from cannot fail, as there can't be more than 2^32 - // blocks (because ChainLength is u32) - upper_bound: u32::try_from(chain_length).unwrap(), - }); - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => Default::default(), - PaginationInterval::Inclusive(range) => { - let a = range.lower_bound.into(); - let b = range.upper_bound.checked_add(1).unwrap().into(); - self.state.state().get_block_hash_range(a, b) - } - }; - - connection.append(edges.iter().map(|(h, chain_length)| { - Edge::new( - IndexCursor::from(u32::from(*chain_length)), - Block::from_valid_hash(*h), - ) - })); - - Ok(connection) - }, - ) - .await - } - - async fn transactions_by_address( - &self, - address_bech32: String, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult< - Connection, EmptyFields>, - > { - let address = chain_addr::AddressReadable::from_string_anyprefix(&address_bech32) - .map(|adr| ExplorerAddress::New(adr.to_address())) - .or_else(|_| OldAddress::from_str(&address_bech32).map(ExplorerAddress::Old)) - .map_err(|_| ApiError::InvalidAddress(address_bech32.to_string()))?; - - let transactions = self - .state - .state() - .transactions_by_address(&address) - .unwrap_or_else(PersistentSequence::::new); - - let len = transactions.len(); - - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let boundaries = if len > 0 { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u64, - upper_bound: len, - }) - } else { - PaginationInterval::Empty - }; - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(TryInto::try_into).transpose()?, - after: after.map(TryInto::try_into).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => (range.lower_bound..=range.upper_bound) - .filter_map(|i| transactions.get(i).map(|h| (HeaderHash::clone(h), i))) - .collect(), - }; - - connection.append(edges.iter().map(|(h, i)| { - Edge::new(IndexCursor::from(*i), Transaction::from_valid_id(*h)) - })); - - Ok(connection) - }, - ) - .await - } - - pub async fn all_vote_plans( - &self, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult< - Connection, EmptyFields>, - > { - let mut vote_plans = self.state.state().get_vote_plans(); - - vote_plans.sort_unstable_by_key(|(id, _data)| id.clone()); - - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let boundaries = if !vote_plans.is_empty() { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u32, - upper_bound: vote_plans - .len() - .checked_sub(1) - .unwrap() - .try_into() - .expect("tried to paginate more than 2^32 elements"), - }) - } else { - PaginationInterval::Empty - }; - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => { - let from = range.lower_bound; - let to = range.upper_bound; - - (from..=to) - .map(|i: u32| { - let (_pool_id, vote_plan_data) = - &vote_plans[usize::try_from(i).unwrap()]; - ( - VotePlanStatus::vote_plan_from_data(Arc::clone(vote_plan_data)), - i, - ) - }) - .collect::>() - } - }; - - connection.append( - edges - .iter() - .map(|(vps, cursor)| Edge::new(IndexCursor::from(*cursor), vps.clone())), - ); - - Ok(connection) - }, - ) - .await - } - - pub async fn all_stake_pools( - &self, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult, EmptyFields>> { - let mut stake_pools = self.state.state().get_stake_pools(); - - // Although it's probably not a big performance concern - // There are a few alternatives to not have to sort this - // - A separate data structure can be used to track InsertionOrder -> PoolId - // (or any other order) - // - Find some way to rely in the Hamt iterator order (but I think this is probably not a good idea) - stake_pools.sort_unstable_by_key(|(id, _data)| id.clone()); - - query( - after.map(Into::into), - before.map(Into::into), - first, - last, - |after, before, first, last| async move { - let boundaries = if !stake_pools.is_empty() { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u32, - upper_bound: stake_pools - .len() - .checked_sub(1) - .unwrap() - .try_into() - .expect("tried to paginate more than 2^32 elements"), - }) - } else { - PaginationInterval::Empty - }; - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => { - let from = range.lower_bound; - let to = range.upper_bound; - - (from..=to) - .map(|i: u32| { - let (pool_id, stake_pool_data) = - &stake_pools[usize::try_from(i).unwrap()]; - ( - Pool::new_with_data( - certificate::PoolId::clone(pool_id), - Arc::clone(stake_pool_data), - ), - i, - ) - }) - .collect::>() - } - }; - - connection.append( - edges - .iter() - .map(|(pool, cursor)| Edge::new(IndexCursor::from(*cursor), pool.clone())), - ); - - Ok(connection) - }, - ) - .await - } - - /// Get a paginated view of all the blocks in this epoch - pub async fn blocks_by_epoch( - &self, - context: &Context<'_>, - epoch: EpochNumber, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult< - Option, EmptyFields>>, - > { - let epoch_data = match extract_context(context).await.db.get_epoch(epoch.0).await { - Some(epoch_data) => epoch_data, - None => return Ok(None), - }; - - Some( - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let epoch_lower_bound = self - .state - .state() - .blocks - .lookup(&epoch_data.first_block) - .map(|block| u32::from(block.chain_length)) - .expect("Epoch lower bound"); - - let epoch_upper_bound = self - .state - .state() - .blocks - .lookup(&epoch_data.last_block) - .map(|block| u32::from(block.chain_length)) - .expect("Epoch upper bound"); - - let boundaries = PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0, - upper_bound: epoch_upper_bound.checked_sub(epoch_lower_bound).expect( - "pagination upper_bound to be greater or equal than lower_bound", - ), - }); - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => { - unreachable!("No blocks found (not even genesis)") - } - PaginationInterval::Inclusive(range) => self - .state - .state() - .get_block_hash_range( - (range.lower_bound + epoch_lower_bound).into(), - (range.upper_bound + epoch_lower_bound + 1u32).into(), - ) - .iter() - .map(|(hash, index)| (*hash, u32::from(*index) - epoch_lower_bound)) - .collect::>(), - }; - - connection.append(edges.iter().map(|(id, cursor)| { - Edge::new(IndexCursor::from(*cursor), Block::from_valid_hash(*id)) - })); - - Ok(connection) - }, - ) - .await, - ) - .transpose() - } -} - -pub struct Block { - hash: HeaderHash, - contents: tokio::sync::Mutex>>, -} - -impl Block { - async fn from_string_hash(hash: String, db: &ExplorerDb) -> FieldResult { - let hash = HeaderHash::from_str(&hash)?; - let block = Block { - hash, - contents: Default::default(), - }; - - block.fetch_explorer_block(db).await.map(|_| block) - } - - fn from_valid_hash(hash: HeaderHash) -> Block { - Block { - hash, - contents: Default::default(), - } - } - - fn from_contents(block: Arc) -> Block { - Block { - hash: block.id(), - contents: tokio::sync::Mutex::new(Some(block)), - } - } - - async fn fetch_explorer_block(&self, db: &ExplorerDb) -> FieldResult> { - let mut contents = self.contents.lock().await; - if let Some(block) = &*contents { - Ok(Arc::clone(block)) - } else { - let block = db.get_block(&self.hash).await.ok_or_else(|| { - ApiError::InternalError("Couldn't find block's contents in explorer".to_owned()) - })?; - - *contents = Some(Arc::clone(&block)); - Ok(block) - } - } - - async fn get_branches(&self, db: &ExplorerDb) -> FieldResult> { - let (block, mut branches) = - db.get_block_with_branches(&self.hash) - .await - .ok_or_else(|| { - ApiError::InternalError("Couldn't find block's contents in explorer".to_owned()) - })?; - - let mut contents = self.contents.lock().await; - contents.get_or_insert(block); - - Ok(branches - .drain(..) - .map(|(hash, state)| Branch::from_id_and_state(hash, state)) - .collect()) - } -} - -/// A Block -#[Object] -impl Block { - /// The Block unique identifier - pub async fn id(&self) -> String { - format!("{}", self.hash) - } - - /// Date the Block was included in the blockchain - pub async fn date(&self, context: &Context<'_>) -> FieldResult { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|b| b.date().into()) - } - - /// The transactions contained in the block - pub async fn transactions( - &self, - context: &Context<'_>, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult< - Connection, EmptyFields>, - > { - let explorer_block = self - .fetch_explorer_block(&extract_context(context).await.db) - .await?; - - let mut transactions: Vec<&ExplorerTransaction> = - explorer_block.transactions.values().collect(); - - // TODO: This may be expensive at some point, but I can't rely in - // the HashMap's order (also, I'm assuming the order in the block matters) - transactions - .as_mut_slice() - .sort_unstable_by_key(|tx| tx.offset_in_block); - - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let boundaries = if !transactions.is_empty() { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u32, - upper_bound: transactions - .len() - .checked_sub(1) - .unwrap() - .try_into() - .expect("tried to paginate more than 2^32 elements"), - }) - } else { - PaginationInterval::Empty - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => { - let from = usize::try_from(range.lower_bound).unwrap(); - let to = usize::try_from(range.upper_bound).unwrap(); - - (from..=to) - .map(|i| { - ( - Transaction::from_contents(transactions[i].clone()), - i.try_into().unwrap(), - ) - }) - .collect::>() - } - }; - - connection.append( - edges - .iter() - .map(|(tx, cursor)| Edge::new(IndexCursor::from(*cursor), tx.clone())), - ); - - Ok(connection) - }, - ) - .await - } - - pub async fn chain_length(&self, context: &Context<'_>) -> FieldResult { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|block| ChainLength(block.chain_length())) - } - - pub async fn leader(&self, context: &Context<'_>) -> FieldResult> { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|block| match block.producer() { - BlockProducer::StakePool(pool) => { - Some(Leader::StakePool(Pool::from_valid_id(pool.clone()))) - } - BlockProducer::BftLeader(id) => { - Some(Leader::BftLeader(BftLeader { id: id.clone() })) - } - BlockProducer::None => None, - }) - } - - pub async fn previous_block(&self, context: &Context<'_>) -> FieldResult { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|b| Block::from_valid_hash(b.parent_hash)) - } - - pub async fn total_input(&self, context: &Context<'_>) -> FieldResult { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|block| Value(block.total_input)) - } - - pub async fn total_output(&self, context: &Context<'_>) -> FieldResult { - self.fetch_explorer_block(&extract_context(context).await.db) - .await - .map(|block| Value(block.total_output)) - } - - pub async fn treasury(&self, context: &Context<'_>) -> FieldResult> { - let treasury = extract_context(context) - .await - .db - .blockchain() - .get_ref(self.hash) - .await - .unwrap_or(None) - .map(|reference| { - let ledger = reference.ledger(); - let treasury_tax = reference.epoch_ledger_parameters().treasury_tax; - Treasury { - rewards: Value(ledger.remaining_rewards()), - treasury: Value(ledger.treasury_value()), - treasury_tax: TaxType(treasury_tax), - } - }); - - Ok(treasury) - } - - pub async fn is_confirmed(&self, context: &Context<'_>) -> bool { - extract_context(context) - .await - .db - .is_block_confirmed(&self.hash) - .await - } - - pub async fn branches(&self, context: &Context<'_>) -> FieldResult> { - let branches = self - .get_branches(&extract_context(context).await.db) - .await?; - - Ok(branches) - } -} - -#[derive(Clone)] -pub struct BftLeader { - id: BftLeaderId, -} - -impl From for BftLeader { - fn from(id: BftLeaderId) -> Self { - Self { id } - } -} - -#[Object] -impl BftLeader { - async fn id(&self) -> PublicKey { - self.id.as_public_key().into() - } -} - -#[derive(Union)] -pub enum Leader { - StakePool(Pool), - BftLeader(BftLeader), -} - -impl From> for Block { - fn from(block: Arc) -> Block { - Block::from_valid_hash(block.id()) - } -} - -/// Block's date, composed of an Epoch and a Slot -#[derive(Clone, SimpleObject)] -pub struct BlockDate { - epoch: Epoch, - slot: Slot, -} - -impl From for BlockDate { - fn from(date: blockcfg::BlockDate) -> BlockDate { - BlockDate { - epoch: Epoch { id: date.epoch }, - slot: Slot(date.slot_id), - } - } -} - -#[derive(Clone)] -pub struct Transaction { - id: FragmentId, - block_hashes: Vec, - contents: Option, -} - -impl Transaction { - async fn from_id(id: FragmentId, context: &Context<'_>) -> FieldResult { - let block_hashes = extract_context(context) - .await - .db - .find_blocks_by_transaction(&id) - .await; - - if block_hashes.is_empty() { - return Err(ApiError::NotFound(format!("transaction not found: {}", &id,)).into()); - } else { - Ok(Transaction { - id, - block_hashes, - contents: None, - }) - } - } - - fn from_valid_id(id: FragmentId) -> Transaction { - Transaction { - id, - block_hashes: Default::default(), - contents: None, - } - } - - fn from_contents(contents: ExplorerTransaction) -> Transaction { - Transaction { - id: contents.id, - block_hashes: Default::default(), - contents: Some(contents), - } - } - - async fn get_blocks(&self, context: &Context<'_>) -> FieldResult>> { - let block_ids = if self.block_hashes.is_empty() { - extract_context(context) - .await - .db - .find_blocks_by_transaction(&self.id) - .await - } else { - self.block_hashes.clone() - }; - - if block_ids.is_empty() { - return Err(FieldError::from(ApiError::InternalError( - "Transaction is not present in any block".to_owned(), - ))); - } - - let mut result = Vec::new(); - - for block_id in block_ids { - let block = extract_context(context) - .await - .db - .get_block(&block_id) - .await - .ok_or_else(|| { - FieldError::from(ApiError::InternalError( - "transaction is in explorer but couldn't find its block".to_owned(), - )) - })?; - - result.push(block); - } - - Ok(result) - } - - async fn get_contents(&self, context: &Context<'_>) -> FieldResult { - if let Some(c) = &self.contents { - Ok(c.clone()) - } else { - //TODO: maybe store transactions outside blocks? as Arc, as doing it this way is pretty wasty - - let block = context - .data_unchecked::() - .get() - .await - .unwrap() - .db - .get_block(&self.block_hashes[0]) - .await - .ok_or_else(|| { - FieldError::from(ApiError::InternalError( - "failed to fetch block containing the transaction".to_owned(), - )) - })?; - - Ok(block - .transactions - .get(&self.id) - .ok_or_else(|| { - ApiError::InternalError( - "transaction was not found in respective block".to_owned(), - ) - })? - .clone()) - } - } -} - -/// A transaction in the blockchain -#[Object] -impl Transaction { - /// The hash that identifies the transaction - pub async fn id(&self) -> String { - format!("{}", self.id) - } - - /// All the blocks this transaction is included in - pub async fn blocks(&self, context: &Context<'_>) -> FieldResult> { - let blocks = self.get_blocks(context).await?; - - Ok(blocks.iter().map(|b| Block::from(Arc::clone(b))).collect()) - } - - pub async fn inputs(&self, context: &Context<'_>) -> FieldResult> { - let transaction = self.get_contents(context).await?; - Ok(transaction - .inputs() - .iter() - .map(|input| TransactionInput { - address: Address::from(&input.address), - amount: Value(input.value), - }) - .collect()) - } - - pub async fn outputs(&self, context: &Context<'_>) -> FieldResult> { - let transaction = self.get_contents(context).await?; - Ok(transaction - .outputs() - .iter() - .map(|input| TransactionOutput { - address: Address::from(&input.address), - amount: Value(input.value), - }) - .collect()) - } - - pub async fn certificate( - &self, - context: &Context<'_>, - ) -> FieldResult> { - let transaction = self.get_contents(context).await?; - match transaction.certificate { - Some(c) => Certificate::try_from(c).map(Some).map_err(|e| e.into()), - None => Ok(None), - } - } -} - -#[derive(SimpleObject)] -pub struct TransactionInput { - amount: Value, - address: Address, -} - -#[derive(SimpleObject)] -pub struct TransactionOutput { - amount: Value, - address: Address, -} - -#[derive(Clone)] -pub struct Address { - id: ExplorerAddress, -} - -impl Address { - fn from_bech32(bech32: &str) -> FieldResult
{ - let addr = chain_addr::AddressReadable::from_string_anyprefix(bech32) - .map(|adr| ExplorerAddress::New(adr.to_address())) - .or_else(|_| OldAddress::from_str(bech32).map(ExplorerAddress::Old)) - .map_err(|_| ApiError::InvalidAddress(bech32.to_string()))?; - - Ok(Address { id: addr }) - } -} - -impl From<&ExplorerAddress> for Address { - fn from(addr: &ExplorerAddress) -> Address { - Address { id: addr.clone() } - } -} - -#[Object] -impl Address { - /// The base32 representation of an address - async fn id(&self, context: &Context<'_>) -> String { - match &self.id { - ExplorerAddress::New(addr) => chain_addr::AddressReadable::from_address( - &extract_context(context) - .await - .settings - .address_bech32_prefix, - addr, - ) - .to_string(), - ExplorerAddress::Old(addr) => format!("{}", addr), - } - } - - async fn delegation(&self, _context: &Context<'_>) -> FieldResult { - Err(ApiError::Unimplemented.into()) - } -} - -pub struct TaxType(chain_impl_mockchain::rewards::TaxType); - -#[Object] -impl TaxType { - /// what get subtracted as fixed value - pub async fn fixed(&self) -> Value { - Value(self.0.fixed) - } - /// Ratio of tax after fixed amout subtracted - pub async fn ratio(&self) -> Ratio { - Ratio(self.0.ratio) - } - - /// Max limit of tax - pub async fn max_limit(&self) -> Option { - self.0.max_limit.map(NonZero) - } -} - -pub struct Ratio(chain_impl_mockchain::rewards::Ratio); - -#[Object] -impl Ratio { - pub async fn numerator(&self) -> Value { - Value::from(self.0.numerator) - } - - pub async fn denominator(&self) -> NonZero { - NonZero(self.0.denominator) - } -} - -pub struct Proposal(certificate::Proposal); - -#[Object] -impl Proposal { - pub async fn external_id(&self) -> ExternalProposalId { - ExternalProposalId(self.0.external_id().to_string()) - } - - /// get the vote options range - /// - /// this is the available range of choices to make for the given - /// proposal. all casted votes for this proposals ought to be in - /// within the given range - pub async fn options(&self) -> VoteOptionRange { - self.0.options().clone().into() - } -} - -#[derive(Clone)] -pub struct Pool { - id: certificate::PoolId, - data: Option>, - blocks: Option>>, -} - -impl Pool { - async fn from_string_id(id: &str, db: &ExplorerDb) -> FieldResult { - let id = certificate::PoolId::from_str(id)?; - let blocks = db - .get_stake_pool_blocks(&id) - .await - .ok_or_else(|| ApiError::NotFound("Stake pool not found".to_owned()))?; - - let data = db - .get_stake_pool_data(&id) - .await - .ok_or_else(|| ApiError::NotFound("Stake pool not found".to_owned()))?; - - Ok(Pool { - id, - data: Some(data), - blocks: Some(blocks), - }) - } - - fn from_valid_id(id: certificate::PoolId) -> Pool { - Pool { - id, - blocks: None, - data: None, - } - } - - fn new_with_data(id: certificate::PoolId, data: Arc) -> Self { - Pool { - id, - blocks: None, - data: Some(data), - } - } -} - -#[Object] -impl Pool { - pub async fn id(&self) -> PoolId { - PoolId(self.id.clone()) - } - - pub async fn blocks( - &self, - context: &Context<'_>, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult>> { - let blocks = match &self.blocks { - Some(b) => b.clone(), - None => extract_context(context) - .await - .db - .get_stake_pool_blocks(&self.id) - .await - .ok_or_else(|| { - ApiError::InternalError("Stake pool in block is not indexed".to_owned()) - })?, - }; - - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let bounds = if blocks.len() > 0 { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u32, - upper_bound: blocks - .len() - .checked_sub(1) - .unwrap() - .try_into() - .expect("Tried to paginate more than 2^32 blocks"), - }) - } else { - PaginationInterval::Empty - }; - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(bounds, pagination_arguments)?; - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => (range.lower_bound..=range.upper_bound) - .filter_map(|i| blocks.get(i).map(|h| (*h.as_ref(), i))) - .collect(), - }; - - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - connection.append( - edges - .iter() - .map(|(h, i)| Edge::new(IndexCursor::from(*i), Block::from_valid_hash(*h))), - ); - - Ok(connection) - }, - ) - .await - } - - pub async fn registration(&self, context: &Context<'_>) -> FieldResult { - match &self.data { - Some(data) => Ok(data.registration.clone().into()), - None => extract_context(context) - .await - .db - .get_stake_pool_data(&self.id) - .await - .map(|data| PoolRegistration::from(data.registration.clone())) - .ok_or_else(|| ApiError::NotFound("Stake pool not found".to_owned()).into()), - } - } - - pub async fn retirement(&self, context: &Context<'_>) -> FieldResult> { - match &self.data { - Some(data) => Ok(data.retirement.clone().map(PoolRetirement::from)), - None => extract_context(context) - .await - .db - .get_stake_pool_data(&self.id) - .await - .ok_or_else(|| ApiError::NotFound("Stake pool not found".to_owned()).into()) - .map(|data| { - data.retirement - .as_ref() - .map(|r| PoolRetirement::from(r.clone())) - }), - } - } -} - -pub struct Settings {} - -#[Object] -impl Settings { - pub async fn fees(&self, context: &Context<'_>) -> LinearFee { - let res = &extract_context(context).await.db.blockchain_config.fees; - res.into() - } - - pub async fn epoch_stability_depth(&self, context: &Context<'_>) -> EpochStabilityDepth { - let res = &extract_context(context) - .await - .db - .blockchain_config - .epoch_stability_depth; - res.into() - } -} - -#[derive(SimpleObject)] -pub struct Treasury { - rewards: Value, - treasury: Value, - treasury_tax: TaxType, -} - -#[derive(SimpleObject)] -pub struct FeeSettings { - constant: Value, - coefficient: Value, - certificate: Value, - certificate_pool_registration: Value, - certificate_stake_delegation: Value, - certificate_owner_stake_delegation: Value, - certificate_vote_plan: Value, - certificate_vote_cast: Value, -} - -#[derive(Clone)] -pub struct Epoch { - id: blockcfg::Epoch, -} - -impl Epoch { - fn from_epoch_number(id: blockcfg::Epoch) -> Epoch { - Epoch { id } - } - - async fn get_epoch_data(&self, db: &ExplorerDb) -> Option { - db.get_epoch(self.id).await - } -} - -#[Object] -impl Epoch { - pub async fn id(&self) -> EpochNumber { - EpochNumber(self.id) - } - - /// Not yet implemented - pub async fn stake_distribution(&self) -> FieldResult { - Err(ApiError::Unimplemented.into()) - } - - pub async fn first_block(&self, context: &Context<'_>) -> Option { - self.get_epoch_data(&extract_context(context).await.db) - .await - .map(|data| Block::from_valid_hash(data.first_block)) - } - - pub async fn last_block(&self, context: &Context<'_>) -> Option { - self.get_epoch_data(&extract_context(context).await.db) - .await - .map(|data| Block::from_valid_hash(data.last_block)) - } - - pub async fn total_blocks(&self, context: &Context<'_>) -> BlockCount { - self.get_epoch_data(&extract_context(context).await.db) - .await - .map_or(0u32.into(), |data| data.total_blocks.into()) - } -} - -#[derive(SimpleObject)] -pub struct StakeDistribution { - pools: Vec, -} - -#[derive(SimpleObject)] -pub struct PoolStakeDistribution { - pool: Pool, - delegated_stake: Value, -} - -#[derive(Clone)] -pub struct VotePayloadPublicStatus { - choice: i32, -} - -#[derive(Clone)] -pub struct VotePayloadPrivateStatus { - proof: ProofOfCorrectVote, - encrypted_vote: EncryptedVote, -} - -#[Object] -impl VotePayloadPublicStatus { - pub async fn choice(&self, _context: &Context<'_>) -> i32 { - self.choice - } -} - -#[Object] -impl VotePayloadPrivateStatus { - pub async fn proof(&self, _context: &Context<'_>) -> String { - let bytes_proof = self.proof.serialize(); - base64::encode_config(bytes_proof, base64::URL_SAFE) - } - - pub async fn encrypted_vote(&self, _context: &Context<'_>) -> String { - let encrypted_bote_bytes = self.encrypted_vote.serialize(); - base64::encode_config(encrypted_bote_bytes, base64::URL_SAFE) - } -} - -#[derive(Clone, Union)] -pub enum VotePayloadStatus { - Public(VotePayloadPublicStatus), - Private(VotePayloadPrivateStatus), -} - -// TODO do proper vote tally -#[derive(Clone, SimpleObject)] -pub struct TallyPublicStatus { - results: Vec, - options: VoteOptionRange, -} - -#[derive(Clone, SimpleObject)] -pub struct TallyPrivateStatus { - results: Option>, - options: VoteOptionRange, -} - -#[derive(Clone, Union)] -pub enum TallyStatus { - Public(TallyPublicStatus), - Private(TallyPrivateStatus), -} - -#[derive(Clone, SimpleObject)] -pub struct VotePlanStatus { - id: VotePlanId, - vote_start: BlockDate, - vote_end: BlockDate, - committee_end: BlockDate, - payload_type: PayloadType, - proposals: Vec, -} - -impl VotePlanStatus { - pub async fn vote_plan_from_id( - vote_plan_id: VotePlanId, - context: &Context<'_>, - ) -> FieldResult { - let vote_plan_id = chain_impl_mockchain::certificate::VotePlanId::from_str(&vote_plan_id.0) - .map_err(|err| -> FieldError { ApiError::InvalidAddress(err.to_string()).into() })?; - if let Some(vote_plan) = extract_context(context) - .await - .db - .get_vote_plan_by_id(&vote_plan_id) - .await - { - return Ok(Self::vote_plan_from_data(vote_plan)); - } - - Err(ApiError::NotFound(format!( - "Vote plan with id {} not found", - vote_plan_id.to_string() - )) - .into()) - } - - pub fn vote_plan_from_data(vote_plan: Arc) -> Self { - let super::indexing::ExplorerVotePlan { - id, - vote_start, - vote_end, - committee_end, - payload_type, - proposals, - } = (*vote_plan).clone(); - - VotePlanStatus { - id: VotePlanId::from(id), - vote_start: BlockDate::from(vote_start), - vote_end: BlockDate::from(vote_end), - committee_end: BlockDate::from(committee_end), - payload_type: PayloadType::from(payload_type), - proposals: proposals - .into_iter() - .map(|proposal| VoteProposalStatus { - proposal_id: ExternalProposalId::from(proposal.proposal_id), - options: VoteOptionRange::from(proposal.options), - tally: proposal.tally.map(|tally| match tally { - super::indexing::ExplorerVoteTally::Public { results, options } => { - TallyStatus::Public(TallyPublicStatus { - results: results.into_iter().map(Into::into).collect(), - options: options.into(), - }) - } - super::indexing::ExplorerVoteTally::Private { results, options } => { - TallyStatus::Private(TallyPrivateStatus { - results: results - .map(|res| res.into_iter().map(Into::into).collect()), - options: options.into(), - }) - } - }), - votes: proposal - .votes - .iter() - .map(|(key, vote)| match vote.as_ref() { - ExplorerVote::Public(choice) => VoteStatus { - address: key.into(), - payload: VotePayloadStatus::Public(VotePayloadPublicStatus { - choice: choice.as_byte().into(), - }), - }, - ExplorerVote::Private { - proof, - encrypted_vote, - } => VoteStatus { - address: key.into(), - payload: VotePayloadStatus::Private(VotePayloadPrivateStatus { - proof: proof.clone(), - encrypted_vote: encrypted_vote.clone(), - }), - }, - }) - .collect(), - }) - .collect(), - } - } -} - -#[derive(Clone, SimpleObject)] -pub struct VoteStatus { - address: Address, - payload: VotePayloadStatus, -} - -#[derive(Clone)] -pub struct VoteProposalStatus { - proposal_id: ExternalProposalId, - options: VoteOptionRange, - tally: Option, - votes: Vec, -} - -#[Object] -impl VoteProposalStatus { - pub async fn proposal_id(&self) -> &ExternalProposalId { - &self.proposal_id - } - - pub async fn options(&self) -> &VoteOptionRange { - &self.options - } - - pub async fn tally(&self) -> Option<&TallyStatus> { - self.tally.as_ref() - } - - pub async fn votes( - &self, - first: Option, - last: Option, - before: Option, - after: Option, - ) -> FieldResult, EmptyFields>> { - query( - after, - before, - first, - last, - |after, before, first, last| async move { - let boundaries = if !self.votes.is_empty() { - PaginationInterval::Inclusive(InclusivePaginationInterval { - lower_bound: 0u32, - upper_bound: self - .votes - .len() - .checked_sub(1) - .unwrap() - .try_into() - .expect("tried to paginate more than 2^32 elements"), - }) - } else { - PaginationInterval::Empty - }; - - let pagination_arguments = ValidatedPaginationArguments { - first, - last, - before: before.map(u32::try_from).transpose()?, - after: after.map(u32::try_from).transpose()?, - }; - - let (range, page_meta) = compute_interval(boundaries, pagination_arguments)?; - let mut connection = Connection::with_additional_fields( - page_meta.has_previous_page, - page_meta.has_next_page, - ConnectionFields { - total_count: page_meta.total_count, - }, - ); - - let edges = match range { - PaginationInterval::Empty => vec![], - PaginationInterval::Inclusive(range) => { - let from = range.lower_bound; - let to = range.upper_bound; - - (from..=to) - .map(|i: u32| (self.votes[i as usize].clone(), i)) - .collect::>() - } - }; - - connection.append( - edges - .iter() - .map(|(vs, cursor)| Edge::new(IndexCursor::from(*cursor), vs.clone())), - ); - - Ok(connection) - }, - ) - .await - } -} - -pub struct Query; - -#[Object] -impl Query { - async fn block(&self, context: &Context<'_>, id: String) -> FieldResult { - Block::from_string_hash(id, &extract_context(context).await.db).await - } - - async fn blocks_by_chain_length( - &self, - context: &Context<'_>, - length: ChainLength, - ) -> FieldResult> { - let blocks = extract_context(context) - .await - .db - .find_blocks_by_chain_length(length.0) - .await - .iter() - .cloned() - .map(Block::from_valid_hash) - .collect(); - - Ok(blocks) - } - - async fn transaction(&self, context: &Context<'_>, id: String) -> FieldResult { - let id = FragmentId::from_str(&id)?; - - Transaction::from_id(id, context).await - } - - /// get all current tips, sorted (descending) by their length - pub async fn branches(&self, context: &Context<'_>) -> Vec { - extract_context(context) - .await - .db - .get_branches() - .await - .iter() - .cloned() - .map(|(id, state_ref)| Branch::from_id_and_state(id, state_ref)) - .collect() - } - - /// get the block that the ledger currently considers as the main branch's - /// tip - async fn tip(&self, context: &Context<'_>) -> Branch { - let (hash, state_ref) = extract_context(context).await.db.get_tip().await; - Branch::from_id_and_state(hash, state_ref) - } - - pub async fn branch(&self, context: &Context<'_>, id: String) -> FieldResult { - let id = HeaderHash::from_str(&id)?; - Branch::try_from_id(id, &extract_context(context).await).await - } - - pub async fn epoch(&self, _context: &Context<'_>, id: EpochNumber) -> Epoch { - Epoch::from_epoch_number(id.0) - } - - pub async fn address(&self, _context: &Context<'_>, bech32: String) -> FieldResult
{ - Address::from_bech32(&bech32) - } - - pub async fn stake_pool(&self, context: &Context<'_>, id: PoolId) -> FieldResult { - Pool::from_string_id(&id.0.to_string(), &extract_context(context).await.db).await - } - - pub async fn settings(&self, _context: &Context<'_>) -> FieldResult { - Ok(Settings {}) - } - - pub async fn vote_plan( - &self, - context: &Context<'_>, - id: String, - ) -> FieldResult { - VotePlanStatus::vote_plan_from_id(VotePlanId(id), context).await - } -} - -pub struct Subscription; - -#[Subscription] -impl Subscription { - async fn tip(&self, context: &Context<'_>) -> impl futures::Stream { - use futures::StreamExt; - context - .data_unchecked::() - .get() - .await - .unwrap() - .db - .tip_subscription() - // missing a tip update doesn't seem that important, so I think it's - // fine to ignore the error - .filter_map(|tip| async move { - tip.ok() - .map(|(hash, state)| Branch::from_id_and_state(hash, state)) - }) - } -} - -pub type Schema = async_graphql::Schema; - -pub struct EContext { - pub db: ExplorerDb, - pub settings: ChainSettings, -} - -async fn extract_context(context: &Context<'_>) -> EContext { - context.data_unchecked::().get().await.unwrap() -} diff --git a/jormungandr/src/intercom.rs b/jormungandr/src/intercom.rs index 343a6a48bc..4b1acea794 100644 --- a/jormungandr/src/intercom.rs +++ b/jormungandr/src/intercom.rs @@ -621,12 +621,6 @@ pub enum TopologyMsg { ListQuarantined(ReplyHandle>), } -/// Messages to the explorer task -pub enum ExplorerMsg { - NewBlock(Block), - NewTip(HeaderHash), -} - /// Messages to the notifier task pub enum WatchMsg { NewBlock(Block), diff --git a/jormungandr/src/main.rs b/jormungandr/src/main.rs index 40a14d49cb..841cb42ba2 100644 --- a/jormungandr/src/main.rs +++ b/jormungandr/src/main.rs @@ -64,7 +64,6 @@ pub struct BootstrappedNode { blockchain: Blockchain, blockchain_tip: blockchain::Tip, block0_hash: HeaderHash, - explorer_db: Option, rest_context: Option, services: Services, initial_peers: Vec, @@ -74,7 +73,6 @@ pub struct BootstrappedNode { const BLOCK_TASK_QUEUE_LEN: usize = 32; const FRAGMENT_TASK_QUEUE_LEN: usize = 1024; const NETWORK_TASK_QUEUE_LEN: usize = 64; -const EXPLORER_TASK_QUEUE_LEN: usize = 32; const CLIENT_TASK_QUEUE_LEN: usize = 32; const TOPOLOGY_TASK_QUEUE_LEN: usize = 32; const WATCH_CLIENT_TASK_QUEUE_LEN: usize = 32; @@ -127,28 +125,6 @@ fn start_services(bootstrapped_node: BootstrappedNode) -> Result<(), start_up::E stats_counter.set_tip_block(&block, &block_ref); } - let explorer = { - if bootstrapped_node.settings.explorer { - let explorer_db = bootstrapped_node - .explorer_db - .expect("explorer db to be bootstrapped"); - - let explorer = explorer::Explorer::new(explorer_db); - - // Context to give to the rest api - let context = explorer.clone(); - - let (explorer_msgbox, explorer_queue) = async_msg::channel(EXPLORER_TASK_QUEUE_LEN); - - services.spawn_future("explorer", move |info| async move { - explorer.start(info, explorer_queue).await - }); - Some((explorer_msgbox, context)) - } else { - None - } - }; - let (watch_msgbox, watch_client) = { let (msgbox, queue) = async_msg::channel(WATCH_CLIENT_TASK_QUEUE_LEN); @@ -170,7 +146,6 @@ fn start_services(bootstrapped_node: BootstrappedNode) -> Result<(), start_up::E let blockchain_tip = blockchain_tip.clone(); let network_msgbox = network_msgbox.clone(); let fragment_msgbox = fragment_msgbox.clone(); - let explorer_msgbox = explorer.as_ref().map(|(msg_box, _context)| msg_box.clone()); // TODO: we should get this value from the configuration let block_cache_ttl: Duration = Duration::from_secs(120); let stats_counter = stats_counter.clone(); @@ -181,7 +156,6 @@ fn start_services(bootstrapped_node: BootstrappedNode) -> Result<(), start_up::E stats_counter, network_msgbox, fragment_msgbox, - explorer_msgbox, watch_msgbox, garbage_collection_interval: block_cache_ttl, }; @@ -339,7 +313,6 @@ fn start_services(bootstrapped_node: BootstrappedNode) -> Result<(), start_up::E leadership_logs, enclave, network_state, - explorer: explorer.as_ref().map(|(_msg_box, context)| context.clone()), #[cfg(feature = "prometheus-metrics")] prometheus: prometheus_metric, }; @@ -404,7 +377,6 @@ fn bootstrap(initialized_node: InitializedNode) -> Result Result, rest_context: Option, settings: Settings, initial_peers: Vec, @@ -463,8 +433,6 @@ async fn bootstrap_internal( let block0_hash = block0.header().hash(); - let block0_explorer = block0.clone(); - let cache_capacity = 102_400; let (blockchain, blockchain_tip) = @@ -517,17 +485,6 @@ async fn bootstrap_internal( } }; - let explorer_db = if settings.explorer { - futures::select! { - explorer_result = explorer::ExplorerDb::bootstrap(block0_explorer, &blockchain).fuse() => { - Some(explorer_result?) - }, - _ = cancellation_token.cancelled().fuse() => return Err(start_up::Error::Interrupted), - } - } else { - None - }; - if let Some(context) = &rest_context { let mut context = context.write().await; context.remove_bootstrap_stopper(); @@ -537,7 +494,6 @@ async fn bootstrap_internal( block0_hash, blockchain, blockchain_tip, - explorer_db, rest_context, settings, initial_peers: network_res @@ -670,14 +626,15 @@ fn initialize_node() -> Result { let context = Arc::new(RwLock::new(context)); let service_context = context.clone(); + let rest = rest::Config { listen: rest.listen, tls: rest.tls, cors: rest.cors, - enable_explorer: settings.explorer, #[cfg(feature = "prometheus-metrics")] enable_prometheus: settings.prometheus, }; + let server_handler = rest::start_rest_server(rest, context.clone()); services.spawn_future("rest", move |info| async move { service_context.write().await.set_span(info.span().clone()); diff --git a/jormungandr/src/rest/context.rs b/jormungandr/src/rest/context.rs index 25dcc75e61..9a9344a9ec 100644 --- a/jormungandr/src/rest/context.rs +++ b/jormungandr/src/rest/context.rs @@ -147,7 +147,6 @@ pub struct FullContext { pub leadership_logs: LeadershipLogs, pub enclave: Enclave, pub network_state: NetworkStateR, - pub explorer: Option, #[cfg(feature = "prometheus-metrics")] pub prometheus: Option>, } diff --git a/jormungandr/src/rest/mod.rs b/jormungandr/src/rest/mod.rs index 0e320c131e..ff24bddbb2 100644 --- a/jormungandr/src/rest/mod.rs +++ b/jormungandr/src/rest/mod.rs @@ -1,7 +1,6 @@ //! REST API of the node pub mod context; -pub mod explorer; #[cfg(feature = "prometheus-metrics")] mod prometheus; pub mod v0; @@ -19,7 +18,6 @@ pub struct Config { pub listen: SocketAddr, pub tls: Option, pub cors: Option, - pub enable_explorer: bool, #[cfg(feature = "prometheus-metrics")] pub enable_prometheus: bool, } @@ -70,14 +68,13 @@ pub async fn start_rest_server(config: Config, context: ContextLock) { span })); - setup_prometheus(api, config, context, stopper_rx).await; + setup_prometheus(api, config, stopper_rx).await; } #[cfg(feature = "prometheus-metrics")] async fn setup_prometheus( app: App, config: Config, - context: ContextLock, shutdown_signal: impl Future + Send + 'static, ) where App: Filter + Clone + Send + Sync + 'static, @@ -85,9 +82,9 @@ async fn setup_prometheus( { if config.enable_prometheus { let prometheus = prometheus::filter(context.clone()); - setup_explorer(app.or(prometheus), config, context, shutdown_signal).await; + setup_cors(app.or(prometheus), config, shutdown_signal).await; } else { - setup_explorer(app, config, context, shutdown_signal).await; + setup_cors(app, config, shutdown_signal).await; } } @@ -95,30 +92,12 @@ async fn setup_prometheus( async fn setup_prometheus( app: App, config: Config, - context: ContextLock, shutdown_signal: impl Future + Send + 'static, ) where App: Filter + Clone + Send + Sync + 'static, App::Extract: warp::Reply, { - setup_explorer(app, config, context, shutdown_signal).await; -} - -async fn setup_explorer( - app: App, - config: Config, - context: ContextLock, - shutdown_signal: impl Future + Send + 'static, -) where - App: Filter + Clone + Send + Sync + 'static, - App::Extract: warp::Reply, -{ - if config.enable_explorer { - let explorer = explorer::filter(context); - setup_cors(app.or(explorer), config, shutdown_signal).await; - } else { - setup_cors(app, config, shutdown_signal).await; - } + setup_cors(app, config, shutdown_signal).await; } async fn setup_cors( diff --git a/jormungandr/src/start_up/error.rs b/jormungandr/src/start_up/error.rs index 8b2d01eb7e..7bf281a25e 100644 --- a/jormungandr/src/start_up/error.rs +++ b/jormungandr/src/start_up/error.rs @@ -2,7 +2,7 @@ use crate::{ blockcfg, blockchain, blockchain::StorageError, diagnostic::DiagnosticError, - explorer, network, secure, + network, secure, settings::{self, logging}, }; use std::io; @@ -53,8 +53,6 @@ pub enum Error { NodeSecrets(#[from] secure::NodeSecretFromFileError), #[error("Block 0 is set to start in the future")] Block0InFuture, - #[error("Error while loading the explorer from storage")] - ExplorerBootstrapError(#[from] explorer::error::ExplorerError), #[error("A service has terminated with an error")] ServiceTerminatedWithError(#[from] crate::utils::task::ServiceError), #[error("Unable to get system limits: {0}")] @@ -89,7 +87,6 @@ impl Error { Error::NodeSecrets { .. } => 8, Error::FetchBlock0 { .. } => 9, Error::NetworkBootstrapError { .. } => 10, - Error::ExplorerBootstrapError { .. } => 11, Error::ServiceTerminatedWithError { .. } => 12, Error::DiagnosticError { .. } => 13, }