diff --git a/rest/Cargo.lock b/rest/Cargo.lock index 5c0cbf2c..1f81b72d 100644 --- a/rest/Cargo.lock +++ b/rest/Cargo.lock @@ -2363,6 +2363,7 @@ dependencies = [ "serde", "serde_json", "tendermint 0.23.7", + "tendermint-proto 0.23.9", "tendermint-rpc 0.23.7", "tokio", ] diff --git a/rest/Cargo.toml b/rest/Cargo.toml index 2649a440..e789d727 100644 --- a/rest/Cargo.toml +++ b/rest/Cargo.toml @@ -15,6 +15,7 @@ nomic = { path = "..", default-features = false, features = [ hex = "0.4.3" tendermint-rpc = { version = "=0.23.7", features = ["http-client"] } tendermint = "=0.23.7" +tendermint-proto = "=0.23.9" base64 = "0.13.0" serde = "1.0.136" serde_json = "1.0.78" diff --git a/rest/src/main.rs b/rest/src/main.rs index 9112c674..23c4ea5a 100644 --- a/rest/src/main.rs +++ b/rest/src/main.rs @@ -18,14 +18,19 @@ use std::sync::Arc; use tokio::sync::RwLock; use tendermint_rpc as tm; +use tendermint_proto::types::CommitSig as RawCommitSig; use tm::Client as _; lazy_static::lazy_static! { static ref QUERY_CACHE: Arc>> = Arc::new(RwLock::new(HashMap::new())); } +fn app_host() -> &'static str { + "http://localhost:26657" +} + fn app_client() -> AppClient { - nomic::app_client("http://localhost:26657") + nomic::app_client(app_host()) } // DONE /cosmos/bank/v1beta1/balances/{address} @@ -41,8 +46,8 @@ fn app_client() -> AppClient { // /ibc/apps/transfer/v1/denom_traces/{hash} // /ibc/core/channel/v1/channels/{channelId}/ports/{portId}/client_state -#[get("/cosmos/staking/v1beta1/validators")] -async fn validators() -> Value { +#[get("/cosmos/staking/v1beta1/validators?")] +async fn validators(status: Option) -> Value { let all_validators: Vec = app_client() .query(|app: InnerApp| app.staking.all_validators()) .await @@ -55,7 +60,7 @@ async fn validators() -> Value { .await .unwrap(); // TODO: cache - let status = if validator.unbonding { + let validator_status = if validator.unbonding { "BOND_STATUS_UNBONDING" } else if validator.in_active_set { "BOND_STATUS_BONDED" @@ -63,6 +68,10 @@ async fn validators() -> Value { "BOND_STATUS_UNBONDED" }; + if !status.is_none() && status != Some(validator_status.to_owned()) { + continue; + } + let info: DeclareInfo = serde_json::from_str(String::from_utf8(validator.info.to_vec()).unwrap().as_str()) .unwrap_or(DeclareInfo { @@ -80,7 +89,7 @@ async fn validators() -> Value { "key": base64::encode(cons_key) }, "jailed": validator.jailed, - "status": status, + "status": validator_status, "tokens": validator.amount_staked.to_string(), "delegator_shares": validator.amount_staked.to_string(), "description": { @@ -317,7 +326,7 @@ struct TxRequest { async fn txs(tx: &str) -> Result> { dbg!(tx); - let client = tm::HttpClient::new("http://localhost:26657").unwrap(); + let client = tm::HttpClient::new(app_host()).unwrap(); let tx_bytes = if let Some('{') = tx.chars().next() { let tx: TxRequest = serde_json::from_str(tx).unwrap(); @@ -363,7 +372,7 @@ struct TxRequest2 { async fn txs2(tx: &str) -> Result> { dbg!(tx); - let client = tm::HttpClient::new("http://localhost:26657").unwrap(); + let client = tm::HttpClient::new(app_host()).unwrap(); let tx_bytes = if let Some('{') = tx.chars().next() { let tx: TxRequest2 = serde_json::from_str(tx).unwrap(); @@ -423,7 +432,7 @@ async fn query(query: &str, height: Option) -> Result Value { } #[get("/cosmos/staking/v1beta1/pool")] -fn staking_pool() -> Value { +async fn staking_pool() -> Value { + let validators = app_client() + .query(|app| app.staking.all_validators()) + .await + .unwrap(); + + let total_bonded: u64 = validators + .iter() + .filter(|v| v.in_active_set) + .map(|v| -> u64 { v.amount_staked.into() }) + .sum(); + + let total_not_bonded: u64 = validators + .iter() + .filter(|v| !v.in_active_set) + .map(|v| -> u64 { v.amount_staked.into() }) + .sum(); + json!({ - "bonded_tokens": "0", - "not_bonded_tokens": "0" + "pool": { + "bonded_tokens": total_bonded.to_string(), + "not_bonded_tokens": total_not_bonded.to_string() + } }) } @@ -795,6 +823,181 @@ async fn slashing_params() -> Value { }) } +#[get("/cosmos/base/tendermint/v1beta1/blocks/latest")] +async fn latest_block() -> Value { + let client = tm::HttpClient::new(app_host()).unwrap(); + + let res = client + .latest_block() + .await + .unwrap(); + + let last_commit = res.block.last_commit.unwrap(); + let signatures: Vec<_> = last_commit.signatures + .iter() + .map(|signature| -> Value { + let signature_raw = RawCommitSig::from(signature.clone()); + + json!({ + "validator_address": base64::encode(signature_raw.validator_address), + "block_id_flag": signature_raw.block_id_flag, + "timestamp": signature_raw.timestamp, + "signature": base64::encode(signature_raw.signature), + }) + }) + .collect(); + + json!({ + "block_id": res.block_id, + "block": { + "header": res.block.header, + "data": res.block.data, + "evidence": res.block.evidence, + "last_commit": { + "block_id": last_commit.block_id, + "signatures": signatures + } + } + }) +} + +#[get("/cosmos/base/tendermint/v1beta1/blocks/")] +async fn block(height: u32) -> Value { + let client = tm::HttpClient::new(app_host()).unwrap(); + + let res = client + .block(tendermint::block::Height::from(height)) + .await + .unwrap(); + + let last_commit = res.block.last_commit.unwrap(); + let signatures: Vec<_> = last_commit.signatures + .iter() + .map(|signature| -> Value { + let signature_raw = RawCommitSig::from(signature.clone()); + + json!({ + "validator_address": base64::encode(signature_raw.validator_address), + "block_id_flag": signature_raw.block_id_flag, + "timestamp": signature_raw.timestamp, + "signature": base64::encode(signature_raw.signature), + }) + }) + .collect(); + + json!({ + "block_id": res.block_id, + "block": { + "header": res.block.header, + "data": res.block.data, + "evidence": res.block.evidence, + "last_commit": { + "block_id": last_commit.block_id, + "signatures": signatures + } + } + }) +} + +#[get("/cosmos/base/tendermint/v1beta1/validatorsets/latest")] +async fn latest_validator_set() -> Value { + let client = tm::HttpClient::new(app_host()).unwrap(); + + let block = client + .latest_block() + .await + .unwrap(); + + let res = client + .validators(block.block.header.height, tendermint_rpc::Paging::All) + .await + .unwrap(); + + let validators: Vec<_> = res.validators.iter() + .map(|validator| -> Value { + json!({ + "address": validator.address, + "voting_power": i64::from(validator.power).to_string(), + "proposer_priority": i64::from(validator.proposer_priority).to_string(), + "pub_key": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": base64::encode(validator.pub_key.ed25519().unwrap().to_bytes()), + } + }) + }) + .collect(); + + json!({ + "block_height": res.block_height, + "validators": validators, + "pagination": { + "next_key": null, + "total": res.validators.len(), + } + }) +} + +#[get("/cosmos/base/tendermint/v1beta1/validatorsets/")] +async fn validator_set(height: u32) -> Value { + let client = tm::HttpClient::new(app_host()).unwrap(); + + let res = client + .validators(height, tendermint_rpc::Paging::All) + .await + .unwrap(); + + let validators: Vec<_> = res.validators.iter() + .map(|validator| -> Value { + json!({ + "address": validator.address, + "voting_power": i64::from(validator.power).to_string(), + "proposer_priority": i64::from(validator.proposer_priority).to_string(), + "pub_key": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": base64::encode(validator.pub_key.ed25519().unwrap().to_bytes()), + } + }) + }) + .collect(); + + json!({ + "block_height": res.block_height, + "validators": validators, + "pagination": { + "next_key": null, + "total": res.validators.len(), + } + }) +} + +#[get("/cosmos/distribution/v1beta1/community_pool")] +async fn community_pool() -> Value { + let community_pool = app_client() + .query(|app| Ok(app.community_pool.amount)) + .await + .unwrap(); + + json!({ + "pool": [ + { + "denom": "unom", + "amount": community_pool.to_string() + } + ] + }) +} + +#[get("/cosmos/gov/v1beta1/proposals")] +fn proposals() -> Value { + json!({ + "proposals": [], + "pagination": { + "next_key": null, + "total": 0 + } + }) +} + use rocket::fairing::{Fairing, Info, Kind}; use rocket::http::Header; use rocket::{Request, Response}; @@ -850,7 +1053,13 @@ fn rocket() -> _ { validators, validator, staking_params, - slashing_params - ], + slashing_params, + latest_block, + block, + latest_validator_set, + validator_set, + community_pool, + proposals, + ] ) } diff --git a/src/app.rs b/src/app.rs index 69e11e45..8cef860b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -77,7 +77,7 @@ pub struct InnerApp { #[call] pub airdrop: Airdrop, - community_pool: Coin, + pub community_pool: Coin, incentive_pool: Coin, staking_rewards: Faucet,