-
Notifications
You must be signed in to change notification settings - Fork 312
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: new approach to declaring parameter changes in governance
- Loading branch information
1 parent
db773d8
commit 313a8eb
Showing
7 changed files
with
428 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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
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,248 @@ | ||
use std::str::FromStr; | ||
|
||
use anyhow::Context; | ||
use penumbra_proto::{core::component::governance::v1 as pb, DomainType}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// An encoded parameter. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(try_from = "pb::EncodedParameter", into = "pb::EncodedParameter")] | ||
pub struct EncodedParameter { | ||
pub component: String, | ||
pub key: String, | ||
pub value: String, | ||
} | ||
|
||
impl DomainType for EncodedParameter { | ||
type Proto = pb::EncodedParameter; | ||
} | ||
|
||
impl TryFrom<pb::EncodedParameter> for EncodedParameter { | ||
type Error = anyhow::Error; | ||
fn try_from(value: pb::EncodedParameter) -> Result<Self, Self::Error> { | ||
Ok(EncodedParameter { | ||
component: value.component, | ||
key: value.key, | ||
value: value.value, | ||
}) | ||
} | ||
} | ||
|
||
impl From<EncodedParameter> for pb::EncodedParameter { | ||
fn from(value: EncodedParameter) -> Self { | ||
pb::EncodedParameter { | ||
component: value.component, | ||
key: value.key, | ||
value: value.value, | ||
} | ||
} | ||
} | ||
|
||
/// Generates a set of encoded parameters for the given object. | ||
/// | ||
/// This is useful for generating template changes. | ||
pub fn encode_parameters(parameters: serde_json::Value) -> Vec<EncodedParameter> { | ||
let mut encoded_parameters = Vec::new(); | ||
for (component, value) in parameters.as_object().into_iter().flatten() { | ||
for (key, value) in value.as_object().into_iter().flatten() { | ||
encoded_parameters.push(EncodedParameter { | ||
component: component.to_string(), | ||
key: key.to_string(), | ||
value: value.to_string(), | ||
}); | ||
} | ||
} | ||
encoded_parameters | ||
} | ||
|
||
/// Applies a set of changes to the app parameters. | ||
/// | ||
/// The app parameters are input as a [`serde_json::Value`] object, so that the | ||
/// parameter change code does not need to know about the structure of the entire | ||
/// application. | ||
/// | ||
/// If the changes can be successfully applied, the new app parameters are returned. | ||
/// By taking ownership of the input `app_parameters`, we ensure that the caller cannot | ||
/// access any partially-mutated app parameters. | ||
pub fn apply_changes( | ||
mut app_parameters: serde_json::Value, | ||
changes: &[EncodedParameter], | ||
) -> Result<serde_json::Value, anyhow::Error> { | ||
for change in changes { | ||
let component = app_parameters | ||
.get_mut(&change.component) | ||
.ok_or_else(|| { | ||
anyhow::anyhow!("component {} not found in app parameters", change.component) | ||
})? | ||
.as_object_mut() | ||
.ok_or_else(|| { | ||
anyhow::anyhow!( | ||
"expected component {} to be an object in app parameters", | ||
change.component | ||
) | ||
})?; | ||
|
||
dbg!(&change); | ||
|
||
let new_value = serde_json::Value::from_str(&change.value) | ||
.context("could not decode new value as JSON value")?; | ||
|
||
dbg!(&new_value); | ||
dbg!(component.get(&change.key)); | ||
|
||
// We want to insert into the map to handle the case where the existing value | ||
// is missing (e.g., it had a default value and so was not encoded) | ||
component.insert(change.key.clone(), new_value); | ||
} | ||
Ok(app_parameters) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use penumbra_num::Amount; | ||
|
||
use crate::params::GovernanceParameters; | ||
|
||
const SAMPLE_JSON_PARAMETERS: &'static str = r#" | ||
{ | ||
"chainId": "penumbra-testnet-deimos-6-b295771a", | ||
"sctParams": { | ||
"epochDuration": "719" | ||
}, | ||
"communityPoolParams": { | ||
"communityPoolSpendProposalsEnabled": true | ||
}, | ||
"governanceParams": { | ||
"proposalVotingBlocks": "17280", | ||
"proposalDepositAmount": { | ||
"lo": "10000000" | ||
}, | ||
"proposalValidQuorum": "40/100", | ||
"proposalPassThreshold": "50/100", | ||
"proposalSlashThreshold": "80/100" | ||
}, | ||
"ibcParams": { | ||
"ibcEnabled": true, | ||
"inboundIcs20TransfersEnabled": true, | ||
"outboundIcs20TransfersEnabled": true | ||
}, | ||
"stakeParams": { | ||
"activeValidatorLimit": "80", | ||
"baseRewardRate": "30000", | ||
"slashingPenaltyMisbehavior": "10000000", | ||
"slashingPenaltyDowntime": "10000", | ||
"signedBlocksWindowLen": "10000", | ||
"missedBlocksMaximum": "9500", | ||
"minValidatorStake": { | ||
"lo": "1000000" | ||
}, | ||
"unbondingDelay": "2158" | ||
}, | ||
"feeParams": { | ||
"fixedGasPrices": {} | ||
}, | ||
"distributionsParams": { | ||
"stakingIssuancePerBlock": "1" | ||
}, | ||
"fundingParams": {}, | ||
"shieldedPoolParams": { | ||
"fixedFmdParams": { | ||
"asOfBlockHeight": "1" | ||
} | ||
}, | ||
"dexParams": { | ||
"isEnabled": true, | ||
"fixedCandidates": [ | ||
{ | ||
"inner": "KeqcLzNx9qSH5+lcJHBB9KNW+YPrBk5dKzvPMiypahA=" | ||
}, | ||
{ | ||
"inner": "reum7wQmk/owgvGMWMZn/6RFPV24zIKq3W6In/WwZgg=" | ||
}, | ||
{ | ||
"inner": "HW2Eq3UZVSBttoUwUi/MUtE7rr2UU7/UH500byp7OAc=" | ||
}, | ||
{ | ||
"inner": "nwPDkQq3OvLnBwGTD+nmv1Ifb2GEmFCgNHrU++9BsRE=" | ||
}, | ||
{ | ||
"inner": "ypUT1AOtjfwMOKMATACoD9RSvi8jY/YnYGi46CZ/6Q8=" | ||
}, | ||
{ | ||
"inner": "pmpygqUf4DL+z849rGPpudpdK/+FAv8qQ01U2C73kAw=" | ||
}, | ||
{ | ||
"inner": "o2gZdbhCH70Ry+7iBhkSeHC/PB1LZhgkn7LHC2kEhQc=" | ||
} | ||
], | ||
"maxHops": 4, | ||
"maxPositionsPerPair": 10 | ||
}, | ||
"auctionParams": {} | ||
} | ||
"#; | ||
|
||
#[test] | ||
fn dump_encoded_parameters() { | ||
let parameters = serde_json::from_str(SAMPLE_JSON_PARAMETERS).unwrap(); | ||
dbg!(¶meters); | ||
let encoded_parameters = super::encode_parameters(parameters); | ||
for encoded_parameter in encoded_parameters.iter() { | ||
println!("{}", serde_json::to_string(&encoded_parameter).unwrap()); | ||
} | ||
} | ||
|
||
#[test] | ||
fn apply_changes_to_gov_params() { | ||
let old_parameters_raw: serde_json::Value = | ||
serde_json::from_str(SAMPLE_JSON_PARAMETERS).unwrap(); | ||
|
||
// Make changes to the gov parameters specifically since they're | ||
// local to this crate so we can also inspect the decoded parameters. | ||
let changes = vec![ | ||
super::EncodedParameter { | ||
component: "governanceParams".to_string(), | ||
key: "proposalVotingBlocks".to_string(), | ||
value: r#""17281""#.to_string(), | ||
}, | ||
super::EncodedParameter { | ||
component: "governanceParams".to_string(), | ||
key: "proposalDepositAmount".to_string(), | ||
value: r#"{"lo":"10000001"}"#.to_string(), | ||
}, | ||
]; | ||
let new_parameters_raw = | ||
super::apply_changes(old_parameters_raw.clone(), &changes).unwrap(); | ||
|
||
println!( | ||
"{}", | ||
serde_json::to_string_pretty(&old_parameters_raw).unwrap() | ||
); | ||
println!( | ||
"{}", | ||
serde_json::to_string_pretty(&new_parameters_raw).unwrap() | ||
); | ||
|
||
let old_gov_parameters_raw = old_parameters_raw["governanceParams"].clone(); | ||
let new_gov_parameters_raw = new_parameters_raw["governanceParams"].clone(); | ||
|
||
let old_gov_parameters: GovernanceParameters = | ||
serde_json::value::from_value(old_gov_parameters_raw).unwrap(); | ||
let new_gov_parameters: GovernanceParameters = | ||
serde_json::value::from_value(new_gov_parameters_raw).unwrap(); | ||
|
||
dbg!(&old_gov_parameters); | ||
dbg!(&new_gov_parameters); | ||
|
||
assert_eq!(old_gov_parameters.proposal_voting_blocks, 17280); | ||
assert_eq!( | ||
old_gov_parameters.proposal_deposit_amount, | ||
Amount::from(10_000_000u64) | ||
); | ||
assert_eq!(new_gov_parameters.proposal_voting_blocks, 17281); | ||
assert_eq!( | ||
new_gov_parameters.proposal_deposit_amount, | ||
Amount::from(10_000_001u64) | ||
); | ||
} | ||
} |
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 |
---|---|---|
|
@@ -57,3 +57,5 @@ pub use vote::Vote; | |
|
||
pub mod genesis; | ||
pub mod params; | ||
|
||
pub mod change; |
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
Oops, something went wrong.