1use std::collections::BTreeMap;
4use std::fmt;
5use std::io::{self, Read};
6use std::num::NonZeroU64;
7
8use namada_macros::BorshDeserializer;
9#[cfg(feature = "migrations")]
10use namada_migrations::*;
11use serde::{Deserialize, Serialize};
12
13use super::address::Address;
14use super::hash::Hash;
15use super::time::DurationSecs;
16use super::token;
17use crate::borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
18
19#[derive(
21 Clone,
22 Debug,
23 PartialEq,
24 Eq,
25 PartialOrd,
26 Ord,
27 Hash,
28 BorshSerialize,
29 BorshDeserialize,
30 BorshDeserializer,
31 BorshSchema,
32)]
33pub struct Parameters {
34 pub max_tx_bytes: u32,
36 pub epoch_duration: EpochDuration,
38 pub max_proposal_bytes: ProposalBytes,
40 pub max_block_gas: u64,
42 pub vp_allowlist: Vec<String>,
44 pub tx_allowlist: Vec<String>,
46 pub implicit_vp_code_hash: Option<Hash>,
48 pub epochs_per_year: u64,
50 pub masp_epoch_multiplier: u64,
53 pub masp_fee_payment_gas_limit: u64,
55 pub gas_scale: u64,
57 pub minimum_gas_price: BTreeMap<Address, token::Amount>,
59 pub is_native_token_transferable: bool,
61}
62
63#[derive(
66 Clone,
67 Debug,
68 PartialEq,
69 Eq,
70 PartialOrd,
71 Ord,
72 Hash,
73 BorshSerialize,
74 BorshDeserialize,
75 BorshDeserializer,
76 BorshSchema,
77)]
78pub struct EpochDuration {
79 pub min_num_of_blocks: u64,
81 pub min_duration: DurationSecs,
83}
84
85impl Default for Parameters {
86 fn default() -> Self {
87 Parameters {
88 max_tx_bytes: 1024 * 1024,
89 epoch_duration: EpochDuration {
90 min_num_of_blocks: 1,
91 min_duration: DurationSecs(3600),
92 },
93 max_proposal_bytes: Default::default(),
94 max_block_gas: 100,
95 vp_allowlist: vec![],
96 tx_allowlist: vec![],
97 implicit_vp_code_hash: Default::default(),
98 epochs_per_year: 365,
99 masp_epoch_multiplier: 2,
100 masp_fee_payment_gas_limit: 0,
101 gas_scale: 100_000_000,
102 minimum_gas_price: Default::default(),
103 is_native_token_transferable: true,
104 }
105 }
106}
107
108#[derive(
111 Copy,
112 Clone,
113 Eq,
114 PartialEq,
115 Ord,
116 PartialOrd,
117 Hash,
118 Debug,
119 BorshSerialize,
120 BorshDeserializer,
121)]
122#[repr(transparent)]
123pub struct ProposalBytes {
124 inner: NonZeroU64,
125}
126
127impl BorshDeserialize for ProposalBytes {
128 fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
129 let value: u64 = BorshDeserialize::deserialize_reader(reader)?;
130 Self::new(value).ok_or_else(|| {
131 io::Error::new(
132 io::ErrorKind::InvalidInput,
133 format!(
134 "ProposalBytes value must be in the range 1 - {}",
135 Self::RAW_MAX.get()
136 ),
137 )
138 })
139 }
140}
141
142impl Serialize for ProposalBytes {
143 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
144 where
145 S: serde::Serializer,
146 {
147 s.serialize_u64(self.inner.get())
148 }
149}
150
151impl<'de> Deserialize<'de> for ProposalBytes {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::Deserializer<'de>,
155 {
156 struct Visitor;
157
158 impl serde::de::Visitor<'_> for Visitor {
159 type Value = ProposalBytes;
160
161 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(
163 f,
164 "a u64 in the range 1 - {}",
165 ProposalBytes::RAW_MAX.get()
166 )
167 }
168
169 fn visit_u64<E>(self, size: u64) -> Result<Self::Value, E>
170 where
171 E: serde::de::Error,
172 {
173 ProposalBytes::new(size).ok_or_else(|| {
174 serde::de::Error::invalid_value(
175 serde::de::Unexpected::Unsigned(size),
176 &self,
177 )
178 })
179 }
180
181 fn visit_i64<E>(self, size: i64) -> Result<Self::Value, E>
187 where
188 E: serde::de::Error,
189 {
190 let max_bytes = u64::try_from(size).map_err(|_e| {
191 serde::de::Error::invalid_value(
192 serde::de::Unexpected::Signed(size),
193 &self,
194 )
195 })?;
196 ProposalBytes::new(max_bytes).ok_or_else(|| {
197 serde::de::Error::invalid_value(
198 serde::de::Unexpected::Signed(size),
199 &self,
200 )
201 })
202 }
203 }
204
205 deserializer.deserialize_u64(Visitor)
206 }
207}
208
209impl BorshSchema for ProposalBytes {
210 fn add_definitions_recursively(
211 definitions: &mut std::collections::BTreeMap<
212 borsh::schema::Declaration,
213 borsh::schema::Definition,
214 >,
215 ) {
216 let fields = borsh::schema::Fields::NamedFields(vec![(
217 "inner".into(),
218 u64::declaration(),
219 )]);
220 let definition = borsh::schema::Definition::Struct { fields };
221 definitions.insert(Self::declaration(), definition);
222 }
223
224 fn declaration() -> borsh::schema::Declaration {
225 std::any::type_name::<Self>().into()
226 }
227}
228
229impl Default for ProposalBytes {
230 #[inline]
231 fn default() -> Self {
232 Self {
233 inner: Self::RAW_DEFAULT,
234 }
235 }
236}
237
238impl ProposalBytes {
240 pub const MAX: ProposalBytes = ProposalBytes {
242 inner: Self::RAW_MAX,
243 };
244 const RAW_DEFAULT: NonZeroU64 = Self::RAW_MAX;
248 const RAW_MAX: NonZeroU64 = NonZeroU64::new(6 * 1024 * 1024).unwrap();
256}
257
258impl ProposalBytes {
259 #[inline]
261 pub const fn get(self) -> u64 {
262 self.inner.get()
263 }
264
265 #[inline]
271 pub fn new(max_bytes: u64) -> Option<Self> {
272 NonZeroU64::new(max_bytes)
273 .map(|inner| Self { inner })
274 .and_then(|value| {
275 if value.get() > Self::RAW_MAX.get() {
276 None
277 } else {
278 Some(value)
279 }
280 })
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use proptest::prelude::*;
287
288 use super::*;
289
290 proptest! {
291 #[test]
293 fn test_proposal_size_serialize_roundtrip(s in 1u64..=ProposalBytes::MAX.get()) {
294 let size = ProposalBytes::new(s).expect("Test failed");
295 assert_eq!(size.get(), s);
296 let json = serde_json::to_string(&size).expect("Test failed");
297 let deserialized: ProposalBytes =
298 serde_json::from_str(&json).expect("Test failed");
299 assert_eq!(size, deserialized);
300 }
301 }
302}