Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b078fd1
commit b9c8f91
Showing
17 changed files
with
4,290 additions
and
91 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} |
Oops, something went wrong.