Skip to content

Commit

Permalink
feat(network): implement get stake pool parameters query (#554)
Browse files Browse the repository at this point in the history
  • Loading branch information
sterraf authored Dec 17, 2024
1 parent 3a614d2 commit 9133163
Show file tree
Hide file tree
Showing 5 changed files with 375 additions and 8 deletions.
15 changes: 14 additions & 1 deletion examples/n2c-miniprotocols/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use pallas::{
facades::NodeClient,
miniprotocols::{
chainsync,
localstate::queries_v16::{self, Addr, Addrs, StakeAddr, TransactionInput},
localstate::queries_v16::{self, Addr, Addrs, Pools, StakeAddr, TransactionInput},
Point, PRE_PRODUCTION_MAGIC,
},
},
Expand Down Expand Up @@ -66,6 +66,19 @@ async fn do_localstate_query(client: &mut NodeClient) {
.unwrap();
info!("result: {:?}", result);

let pool_id1 = "fdb5834ba06eb4baafd50550d2dc9b3742d2c52cc5ee65bf8673823b";
let pool_id1 = Bytes::from_str(pool_id1).unwrap();
let pool_id2 = "1e3105f23f2ac91b3fb4c35fa4fe301421028e356e114944e902005b";
let pool_id2 = Bytes::from_str(pool_id2).unwrap();
let mut pools: Pools = BTreeSet::new();
pools.insert(pool_id1);
pools.insert(pool_id2);

let result = queries_v16::get_stake_pool_params(client, era, pools.into())
.await
.unwrap();
info!("result: {:?}", result);

let result = queries_v16::get_block_epoch_number(client, era)
.await
.unwrap();
Expand Down
23 changes: 23 additions & 0 deletions pallas-network/src/miniprotocols/localstate/queries_v16/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,29 @@ impl<C> minicbor::encode::Encode<C> for RationalNumber {
}
}

impl<'b, C> minicbor::decode::Decode<'b, C> for PoolIds {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
d.tag()?;

Ok(PoolIds {
hashes: d.decode_with(ctx)?,
})
}
}

impl<C> minicbor::encode::Encode<C> for PoolIds {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.tag(Tag::new(258))?;
e.encode_with(self.hashes.clone(), ctx)?;

Ok(())
}
}

impl<'b, C> minicbor::decode::Decode<'b, C> for TransactionOutput {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
match d.datatype()? {
Expand Down
70 changes: 67 additions & 3 deletions pallas-network/src/miniprotocols/localstate/queries_v16/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// TODO: this should move to pallas::ledger crate at some point

use pallas_crypto::hash::Hash;
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use std::hash::Hash as StdHash;
// required for derive attrs to work
use pallas_codec::minicbor::{self};

use pallas_codec::utils::{AnyUInt, Bytes, KeyValuePairs, TagWrap};
use pallas_codec::utils::{AnyUInt, Bytes, KeyValuePairs, Nullable, TagWrap};
use pallas_codec::{
minicbor::{Decode, Encode},
utils::AnyCbor,
};

pub mod primitives;

pub use primitives::{PoolMetadata, Relay};

use crate::miniprotocols::Point;

use super::{Client, ClientError};
Expand Down Expand Up @@ -39,7 +43,7 @@ pub enum BlockQuery {
GetRewardProvenance,
GetUTxOByTxIn(TxIns),
GetStakePools,
GetStakePoolParams(AnyCbor),
GetStakePoolParams(PoolIds),
GetRewardInfoPools,
GetPoolState(AnyCbor),
GetStakeSnapshots(Pools),
Expand Down Expand Up @@ -197,6 +201,19 @@ pub struct StakeDistribution {
pub pools: KeyValuePairs<Bytes, Pool>,
}

/// The use of `BTreeMap`s as per [Pools] definition ensures that the hashes are
/// in order (otherwise, the node will reject some queries).
#[derive(Debug, PartialEq, Clone)]
pub struct PoolIds {
pub hashes: Pools,
}

impl From<Pools> for PoolIds {
fn from(hashes: Pools) -> Self {
Self { hashes }
}
}

#[derive(Debug, Encode, Decode, PartialEq, Clone)]
pub struct Pool {
#[n(0)]
Expand All @@ -206,6 +223,39 @@ pub struct Pool {
pub hashes: Bytes,
}

// Essentially the `PoolRegistration` component of `Certificate` at
// `pallas-primitives/src/alonzo/model.rs`, with types modified for the present
// context
#[derive(Debug, Encode, Decode, PartialEq, Clone)]
pub struct PoolParams {
#[n(0)]
pub operator: Bytes,

#[n(1)]
pub vrf_keyhash: Bytes,

#[n(2)]
pub pledge: Coin,

#[n(3)]
pub cost: Coin,

#[n(4)]
pub margin: UnitInterval,

#[n(5)]
pub reward_account: Addr,

#[n(6)]
pub pool_owners: PoolIds,

#[n(7)]
pub relays: Vec<Relay>,

#[n(8)]
pub pool_metadata: Nullable<PoolMetadata>,
}

/// Type used at [GenesisConfig], which is a fraction that is CBOR-encoded
/// as an untagged array.
#[derive(Debug, Encode, Decode, PartialEq, Clone)]
Expand Down Expand Up @@ -504,6 +554,20 @@ pub async fn get_cbor(
Ok(result)
}

/// Get parameters for the given pools.
pub async fn get_stake_pool_params(
client: &mut Client,
era: u16,
pool_ids: PoolIds,
) -> Result<BTreeMap<Bytes, PoolParams>, ClientError> {
let query = BlockQuery::GetStakePoolParams(pool_ids);
let query = LedgerQuery::BlockQuery(era, query);
let query = Request::LedgerQuery(query);
let result: (_,) = client.query(query).await?;

Ok(result.0)
}

/// Get the genesis configuration for the given era.
pub async fn get_genesis_config(
client: &mut Client,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Material brought from `pallas-primitives`
// TODO: Refactor in order to avoid repetition.
pub use pallas_codec::utils::{Bytes, Nullable};
pub use pallas_crypto::hash::Hash;

use pallas_codec::minicbor::{self, Decode, Encode};

#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
pub struct PoolMetadata {
#[n(0)]
pub url: String,

#[n(1)]
pub hash: PoolMetadataHash,
}

pub type PoolMetadataHash = Hash<32>;

pub type Port = u32;

pub type IPv4 = Bytes;

pub type IPv6 = Bytes;

pub type DnsName = String;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Relay {
SingleHostAddr(Nullable<Port>, Nullable<IPv4>, Nullable<IPv6>),
SingleHostName(Nullable<Port>, DnsName),
MultiHostName(DnsName),
}

// Move to `codec.rs`?
impl<'b, C> minicbor::decode::Decode<'b, C> for Relay {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
d.array()?;
let variant = d.u16()?;

match variant {
0 => Ok(Relay::SingleHostAddr(
d.decode_with(ctx)?,
d.decode_with(ctx)?,
d.decode_with(ctx)?,
)),
1 => Ok(Relay::SingleHostName(
d.decode_with(ctx)?,
d.decode_with(ctx)?,
)),
2 => Ok(Relay::MultiHostName(d.decode_with(ctx)?)),
_ => Err(minicbor::decode::Error::message(
"invalid variant id for Relay",
)),
}
}
}

impl<C> minicbor::encode::Encode<C> for Relay {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
Relay::SingleHostAddr(a, b, c) => {
e.array(4)?;
e.encode_with(0, ctx)?;
e.encode_with(a, ctx)?;
e.encode_with(b, ctx)?;
e.encode_with(c, ctx)?;

Ok(())
}
Relay::SingleHostName(a, b) => {
e.array(3)?;
e.encode_with(1, ctx)?;
e.encode_with(a, ctx)?;
e.encode_with(b, ctx)?;

Ok(())
}
Relay::MultiHostName(a) => {
e.array(2)?;
e.encode_with(2, ctx)?;
e.encode_with(a, ctx)?;

Ok(())
}
}
}
}
Loading

0 comments on commit 9133163

Please sign in to comment.