Skip to content

Commit

Permalink
add basic external explorer
Browse files Browse the repository at this point in the history
  • Loading branch information
ecioppettini committed Jun 8, 2021
1 parent b078fd1 commit b9c8f91
Show file tree
Hide file tree
Showing 17 changed files with 4,290 additions and 91 deletions.
403 changes: 312 additions & 91 deletions Cargo.lock

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions explorer/Cargo.toml
@@ -0,0 +1,53 @@
[package]
authors = ["dev@iohk.io"]
description = "explorer service for jormungandr"
documentation = "https://github.com/input-output-hk/jormungandr#USAGE.md"
edition = "2018"
homepage = "https://github.com/input-output-hk/jormungandr#README.md"
license = "MIT OR Apache-2.0"
name = "explorer"
repository = "https://github.com/input-output-hk/jormungandr"
version = "0.9.1"

[dependencies]
backoff = {version = "0.3.0", features = ["tokio"]}
futures = "0.3.5"
futures-channel = "0.3.5"
futures-util = "0.3.5"
async-graphql = "2.5.1"
async-graphql-warp = "2.6.0"
serde = {version = "1.0.114", features = ["derive"]}
serde_json = "1.0.56"
serde_yaml = "0.8.13"
sloggers = "1.0.1"
structopt = "0.3.15"
thiserror = "1.0.20"
url = "2.1.1"
warp = {version = "0.3.1", features = ["tls"]}
tracing = "0.1"
tracing-futures = "0.2"
tracing-gelf = { version = "0.5", optional = true }
tracing-journald = { version = "0.1.0", optional = true }
tracing-subscriber = { version = "0.2", features = ["fmt", "json"] }
tracing-appender = "0.1.2"
tokio = { version = "^1.4", features = ["rt-multi-thread", "time", "sync", "rt", "signal", "test-util"] }
tokio-stream = { version = "0.1.4", features = ["sync"] }
tokio-util = { version = "0.6.0", features = ["time"] }
tonic = "0.4"
multiaddr = { package = "parity-multiaddr", version = "0.11" }
rand = "0.8.3"
rand_chacha = "0.3.0"

jormungandr-lib = {path = "../jormungandr-lib"}

cardano-legacy-address = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-addr = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-core = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-crypto = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-impl-mockchain = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-time = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-vote = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-network = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-ser = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
chain-watch = { git = "https://github.com/input-output-hk/chain-libs.git", branch = "heterogeneous-client-api" }
imhamt = {git = "https://github.com/input-output-hk/chain-libs.git", branch = "master"}
310 changes: 310 additions & 0 deletions explorer/src/api/graphql/certificates.rs
@@ -0,0 +1,310 @@
use super::{error::ApiError, extract_context};
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, BlockDate, ExplorerAddress, Pool, Proposal, TaxType};

// interface for grouping certificates as a graphl union
#[derive(Union)]
#[graphql(Context = Context)]
pub enum Certificate {
StakeDelegation(StakeDelegation),
OwnerStakeDelegation(OwnerStakeDelegation),
PoolRegistration(PoolRegistration),
PoolRetirement(PoolRetirement),
PoolUpdate(PoolUpdate),
VotePlan(VotePlan),
VoteCast(VoteCast),
VoteTally(VoteTally),
EncryptedVoteTally(EncryptedVoteTally),
}

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);

#[Object]
impl StakeDelegation {
// FIXME: Maybe a new Account type would be better?
pub async fn account(&self, context: &Context<'_>) -> FieldResult<Address> {
let discrimination = extract_context(context).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<Pool> {
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<PublicKey> {
self.0.owners.iter().map(PublicKey::from).collect()
}

pub async fn operators(&self) -> Vec<PublicKey> {
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<Address> {
use chain_impl_mockchain::transaction::AccountIdentifier;
let discrimination = extract_context(context).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<Pool> {
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<Proposal> {
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()
}
}

/*------------------------------*/
/*------- Conversions ---------*/
/*----------------------------*/

impl TryFrom<chain_impl_mockchain::certificate::Certificate> for Certificate {
type Error = super::error::ApiError;
fn try_from(
original: chain_impl_mockchain::certificate::Certificate,
) -> Result<Certificate, Self::Error> {
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)))
}
}
}
}

impl From<certificate::StakeDelegation> for StakeDelegation {
fn from(delegation: certificate::StakeDelegation) -> StakeDelegation {
StakeDelegation(delegation)
}
}

impl From<certificate::OwnerStakeDelegation> for OwnerStakeDelegation {
fn from(owner_stake_delegation: certificate::OwnerStakeDelegation) -> OwnerStakeDelegation {
OwnerStakeDelegation(owner_stake_delegation)
}
}

impl From<certificate::PoolRegistration> for PoolRegistration {
fn from(registration: certificate::PoolRegistration) -> PoolRegistration {
PoolRegistration(registration)
}
}

impl From<certificate::PoolRetirement> for PoolRetirement {
fn from(pool_retirement: certificate::PoolRetirement) -> PoolRetirement {
PoolRetirement(pool_retirement)
}
}

impl From<certificate::PoolUpdate> for PoolUpdate {
fn from(pool_update: certificate::PoolUpdate) -> PoolUpdate {
PoolUpdate(pool_update)
}
}

impl From<certificate::VotePlan> for VotePlan {
fn from(vote_plan: certificate::VotePlan) -> VotePlan {
VotePlan(vote_plan)
}
}

impl From<certificate::VoteCast> for VoteCast {
fn from(vote_cast: certificate::VoteCast) -> VoteCast {
VoteCast(vote_cast)
}
}

0 comments on commit b9c8f91

Please sign in to comment.