penumbra_sdk_stake/component/action_handler/
validator_definition.rs1use crate::{
2 component::{
3 action_handler::ActionHandler, validator_handler::ValidatorDataRead,
4 validator_handler::ValidatorManager,
5 },
6 rate::RateData,
7 validator,
8};
9use anyhow::{ensure, Context, Result};
10use async_trait::async_trait;
11use cnidarium::StateWrite;
12use decaf377_rdsa::VerificationKey;
13use penumbra_sdk_proto::DomainType;
14
15#[async_trait]
16impl ActionHandler for validator::Definition {
17 type CheckStatelessContext = ();
18 async fn check_stateless(&self, _context: ()) -> Result<()> {
19 if self.validator.website.len() > 70 {
23 anyhow::bail!("validator website field must be less than 70 characters")
24 }
25
26 if self.validator.name.len() > 140 {
27 anyhow::bail!("validator name must be less than 140 characters")
28 }
29
30 if self.validator.description.len() > 280 {
31 anyhow::bail!("validator description must be less than 280 characters")
32 }
33
34 if self.validator.funding_streams.len() > 8 {
35 anyhow::bail!("validators can declare at most 8 funding streams")
36 }
37
38 if self.validator.sequence_number == u32::MAX && self.validator.enabled {
44 anyhow::bail!("validators must be disabled when their lifetime is over")
45 }
46
47 let definition_bytes = self.validator.encode_to_vec();
49 VerificationKey::try_from(self.validator.identity_key.0)
50 .and_then(|vk| vk.verify(&definition_bytes, &self.auth_sig))
51 .context("validator definition signature failed to verify")?;
52
53 let total_funding_bps = self
54 .validator
55 .funding_streams
56 .iter()
57 .map(|fs| fs.rate_bps() as u64)
58 .sum::<u64>();
59
60 if total_funding_bps > 10_000 {
61 anyhow::bail!(
62 "validator defined {} bps of funding streams, greater than 10000bps (= 100%)",
63 total_funding_bps
64 );
65 }
66
67 Ok(())
68 }
69
70 async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
71 let new_validator = &self.validator;
75
76 let prev_definition = state
79 .get_validator_definition(&new_validator.identity_key)
80 .await?;
81
82 if let Some(prev_validator) = &prev_definition {
83 let old_seq = prev_validator.sequence_number;
87 let new_seq = new_validator.sequence_number;
88 ensure!(
89 new_seq > old_seq,
90 "definition sequence number must increase (given {}, but previous definition sequence number is {})",
91 new_seq,
92 old_seq,
93 );
94 }
95
96 if let Some(ck_owner) = state
99 .get_validator_definition_by_consensus_key(&new_validator.consensus_key)
100 .await?
101 {
102 ensure!(
112 ck_owner.identity_key == new_validator.identity_key,
113 "consensus key {:?} is already in use by validator {}",
114 new_validator.consensus_key,
115 ck_owner.identity_key,
116 );
117 }
118
119 if prev_definition.is_some() {
123 state
124 .update_validator_definition(new_validator.clone())
125 .await
126 .context(
127 "should be able to update validator during validator definition execution",
128 )?;
129 } else {
130 let validator_key = new_validator.identity_key;
131
132 let initial_rate_data = RateData {
135 identity_key: validator_key,
136 validator_reward_rate: 0u128.into(),
137 validator_exchange_rate: 1_0000_0000u128.into(),
138 };
139
140 state
141 .add_validator(new_validator.clone(), initial_rate_data)
142 .await
143 .context("should be able to add validator during validator definition execution")?;
144 }
145
146 Ok(())
147 }
148}