Skip to content

Commit

Permalink
community-pool: add initial_balance to content message (#4666)
Browse files Browse the repository at this point in the history
This adds an `initial_balance` value to the genesis content message for the community-pool component.
  • Loading branch information
erwanor committed Jun 25, 2024
1 parent 97505dc commit d58b327
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 22 deletions.
28 changes: 21 additions & 7 deletions crates/core/app/tests/app_can_disable_community_pool_spends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use {
server::consensus::Consensus,
CommunityPoolStateReadExt as _,
},
penumbra_asset::STAKING_TOKEN_ASSET_ID,
penumbra_community_pool::{
CommunityPoolDeposit, CommunityPoolOutput, CommunityPoolSpend, StateReadExt as _,
},
Expand All @@ -17,10 +18,11 @@ use {
},
penumbra_keys::{
keys::{SpendKey, SpendKeyBytes},
test_keys::{self},
test_keys,
},
penumbra_mock_client::MockClient,
penumbra_mock_consensus::TestNode,
penumbra_num::Amount,
penumbra_proto::{
core::keys::v1::{GovernanceKey, IdentityKey},
penumbra::core::component::stake::v1::Validator as PenumbraValidator,
Expand Down Expand Up @@ -124,6 +126,7 @@ async fn app_can_disable_community_pool_spends() -> anyhow::Result<()> {
// Disable community spend proposals.
community_pool_spend_proposals_enabled: false,
},
..Default::default()
},
..Default::default()
};
Expand Down Expand Up @@ -309,15 +312,23 @@ async fn app_can_disable_community_pool_spends() -> anyhow::Result<()> {

// At the outset, the pool should be empty.
assert_eq!(
original_pool_balance,
BTreeMap::default(),
original_pool_balance.len(),
1,
"fresh community pool only track the staking token"
);
assert_eq!(
*original_pool_balance
.get(&STAKING_TOKEN_ASSET_ID)
.expect("CP tracks staking token, even with no balance"),
Amount::zero(),
"the community pool should be empty at the beginning of the chain"
);

// After we deposit a note into the community pool, we should see the original pool contents,
// plus the amount that we deposited.
assert_eq!(
[(note.asset_id(), note.amount())]
[(note.asset_id(), note.amount()),
(*STAKING_TOKEN_ASSET_ID, Amount::zero())]
.into_iter()
.collect::<BTreeMap<_, _>>(),
post_deposit_pool_balance,
Expand Down Expand Up @@ -354,9 +365,12 @@ async fn app_can_disable_community_pool_spends() -> anyhow::Result<()> {
// After any possible voting period, we should see the same pool balance.
assert_eq!(
post_voting_period_pool_balance,
[(note.asset_id(), note.amount())]
.into_iter()
.collect::<BTreeMap<_, _>>(),
[
(note.asset_id(), note.amount()),
(*STAKING_TOKEN_ASSET_ID, Amount::zero())
]
.into_iter()
.collect::<BTreeMap<_, _>>(),
"a rejected proposal should not decrease the funds of the community pool"
);
assert_eq!(
Expand Down
28 changes: 21 additions & 7 deletions crates/core/app/tests/app_can_propose_community_pool_spends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use {
server::consensus::Consensus,
CommunityPoolStateReadExt as _,
},
penumbra_asset::STAKING_TOKEN_ASSET_ID,
penumbra_community_pool::{
CommunityPoolDeposit, CommunityPoolOutput, CommunityPoolSpend, StateReadExt as _,
},
Expand All @@ -17,10 +18,11 @@ use {
},
penumbra_keys::{
keys::{SpendKey, SpendKeyBytes},
test_keys::{self},
test_keys,
},
penumbra_mock_client::MockClient,
penumbra_mock_consensus::TestNode,
penumbra_num::Amount,
penumbra_proto::{
core::keys::v1::{GovernanceKey, IdentityKey},
penumbra::core::component::stake::v1::Validator as PenumbraValidator,
Expand Down Expand Up @@ -303,17 +305,27 @@ async fn app_can_propose_community_pool_spends() -> anyhow::Result<()> {

// At the outset, the pool should be empty.
assert_eq!(
original_pool_balance,
BTreeMap::default(),
original_pool_balance.len(),
1,
"fresh community pool only track the staking token"
);
assert_eq!(
*original_pool_balance
.get(&STAKING_TOKEN_ASSET_ID)
.expect("CP tracks the staking token"),
Amount::zero(),
"the community pool should be empty at the beginning of the chain"
);

// After we deposit a note into the community pool, we should see the original pool contents,
// plus the amount that we deposited.
assert_eq!(
[(note.asset_id(), note.amount())]
.into_iter()
.collect::<BTreeMap<_, _>>(),
[
(note.asset_id(), note.amount()),
(*STAKING_TOKEN_ASSET_ID, Amount::zero())
]
.into_iter()
.collect::<BTreeMap<_, _>>(),
post_deposit_pool_balance,
"a community pool deposit should be reflected in the visible balance"
);
Expand Down Expand Up @@ -353,7 +365,9 @@ async fn app_can_propose_community_pool_spends() -> anyhow::Result<()> {
// After the proposal passes, we should see the balance decrease by the amount proposed.
assert_eq!(
post_voting_period_pool_balance,
BTreeMap::default(),
[(*STAKING_TOKEN_ASSET_ID, Amount::zero())]
.into_iter()
.collect::<BTreeMap<_, _>>(),
"the successful proposal should decrease the funds of the community pool"
);
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions crates/core/component/community-pool/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl Component for CommunityPool {
match app_state {
Some(genesis) => {
state.put_community_pool_params(genesis.community_pool_params.clone());
state.community_pool_deposit(genesis.initial_balance).await;
}
None => {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ impl ActionHandler for CommunityPoolDeposit {
}

async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
state.community_pool_deposit(self.value).await
Ok(state.community_pool_deposit(self.value).await)
}
}
9 changes: 6 additions & 3 deletions crates/core/component/community-pool/src/component/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ pub trait StateWriteExt: StateWrite {
self.put(state_key::community_pool_params().into(), params)
}

async fn community_pool_deposit(&mut self, value: Value) -> Result<()> {
async fn community_pool_deposit(&mut self, value: Value) {
let key = state_key::balance_for_asset(value.asset_id);
let current = self.get(&key).await?.unwrap_or_else(|| Amount::from(0u64));
let current = self
.get(&key)
.await
.expect("no deserialization errors")
.unwrap_or_else(|| Amount::from(0u64));
self.put(key, current + value.amount);
Ok(())
}

async fn community_pool_withdraw(&mut self, value: Value) -> Result<()> {
Expand Down
26 changes: 23 additions & 3 deletions crates/core/component/community-pool/src/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
use anyhow::Context;
use penumbra_asset::{Value, STAKING_TOKEN_ASSET_ID};
use penumbra_proto::{penumbra::core::component::community_pool::v1 as pb, DomainType};
use serde::{Deserialize, Serialize};

use crate::params::CommunityPoolParameters;

#[derive(Deserialize, Serialize, Debug, Clone, Default)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(try_from = "pb::GenesisContent", into = "pb::GenesisContent")]
pub struct Content {
/// The initial configuration parameters for the Community Pool component.
pub community_pool_params: CommunityPoolParameters,
/// The initial balance of the Community Pool.
pub initial_balance: Value,
}

impl From<Content> for pb::GenesisContent {
fn from(value: Content) -> Self {
fn from(genesis: Content) -> Self {
pb::GenesisContent {
community_pool_params: Some(value.community_pool_params.into()),
community_pool_params: Some(genesis.community_pool_params.into()),
initial_balance: Some(genesis.initial_balance.into()),
}
}
}
Expand All @@ -24,6 +28,10 @@ impl TryFrom<pb::GenesisContent> for Content {

fn try_from(msg: pb::GenesisContent) -> Result<Self, Self::Error> {
Ok(Content {
initial_balance: msg
.initial_balance
.context("Initial balance not present in protobuf message")?
.try_into()?,
community_pool_params: msg
.community_pool_params
.context("Community Pool params not present in protobuf message")?
Expand All @@ -35,3 +43,15 @@ impl TryFrom<pb::GenesisContent> for Content {
impl DomainType for Content {
type Proto = pb::GenesisContent;
}

impl Default for Content {
fn default() -> Self {
Content {
community_pool_params: CommunityPoolParameters::default(),
initial_balance: Value {
amount: 0u128.into(),
asset_id: *STAKING_TOKEN_ASSET_ID,
},
}
}
}
2 changes: 1 addition & 1 deletion crates/core/component/funding/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl Component for Funding {
amount: reward_amount_for_stream.into(),
asset_id: *STAKING_TOKEN_ASSET_ID,
})
.await?;
.await;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct GenesisContent {
/// CommunityPool parameters.
#[prost(message, optional, tag = "1")]
pub community_pool_params: ::core::option::Option<CommunityPoolParameters>,
/// The initial balance of the Community Pool.
#[prost(message, optional, tag = "2")]
pub initial_balance: ::core::option::Option<super::super::super::asset::v1::Value>,
}
impl ::prost::Name for GenesisContent {
const NAME: &'static str = "GenesisContent";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,16 @@ impl serde::Serialize for GenesisContent {
if self.community_pool_params.is_some() {
len += 1;
}
if self.initial_balance.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.community_pool.v1.GenesisContent", len)?;
if let Some(v) = self.community_pool_params.as_ref() {
struct_ser.serialize_field("communityPoolParams", v)?;
}
if let Some(v) = self.initial_balance.as_ref() {
struct_ser.serialize_field("initialBalance", v)?;
}
struct_ser.end()
}
}
Expand All @@ -312,11 +318,14 @@ impl<'de> serde::Deserialize<'de> for GenesisContent {
const FIELDS: &[&str] = &[
"community_pool_params",
"communityPoolParams",
"initial_balance",
"initialBalance",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
CommunityPoolParams,
InitialBalance,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand All @@ -340,6 +349,7 @@ impl<'de> serde::Deserialize<'de> for GenesisContent {
{
match value {
"communityPoolParams" | "community_pool_params" => Ok(GeneratedField::CommunityPoolParams),
"initialBalance" | "initial_balance" => Ok(GeneratedField::InitialBalance),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand All @@ -360,6 +370,7 @@ impl<'de> serde::Deserialize<'de> for GenesisContent {
V: serde::de::MapAccess<'de>,
{
let mut community_pool_params__ = None;
let mut initial_balance__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::CommunityPoolParams => {
Expand All @@ -368,13 +379,20 @@ impl<'de> serde::Deserialize<'de> for GenesisContent {
}
community_pool_params__ = map_.next_value()?;
}
GeneratedField::InitialBalance => {
if initial_balance__.is_some() {
return Err(serde::de::Error::duplicate_field("initialBalance"));
}
initial_balance__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(GenesisContent {
community_pool_params: community_pool_params__,
initial_balance: initial_balance__,
})
}
}
Expand Down
Binary file modified crates/proto/src/gen/proto_descriptor.bin.no_lfs
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ message CommunityPoolParameters {
message GenesisContent {
// CommunityPool parameters.
CommunityPoolParameters community_pool_params = 1;
// The initial balance of the Community Pool.
core.asset.v1.Value initial_balance = 2;
}

// Query operations for the community_pool component.
Expand Down

0 comments on commit d58b327

Please sign in to comment.