1use crate::ProtocolVersion;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct EconomicParameters {
12 pub initial_subsidy: u64,
14 pub halving_interval: u64,
16 pub max_money_supply: u64,
18 pub coinbase_maturity: u64,
20 pub dust_limit: u64,
22 pub min_fee_rate: u64,
24 pub max_fee_rate: u64,
26 pub min_relay_fee: u64,
28 pub subsidy_schedule: Vec<(u64, u64)>, }
31
32impl EconomicParameters {
33 pub fn for_protocol(version: ProtocolVersion) -> Self {
35 match version {
36 ProtocolVersion::BitcoinV1 => Self::mainnet(),
37 ProtocolVersion::Testnet3 => Self::testnet(),
38 ProtocolVersion::Regtest => Self::regtest(),
39 ProtocolVersion::Signet => Self::signet(),
40 }
41 }
42
43 pub fn mainnet() -> Self {
45 Self {
46 initial_subsidy: 50_0000_0000, halving_interval: 210_000,
48 max_money_supply: 21_0000_0000_0000_0000, coinbase_maturity: 100, dust_limit: 546, min_fee_rate: 1, max_fee_rate: 1_000_000, min_relay_fee: 1000, subsidy_schedule: Vec::new(), }
56 }
57
58 pub fn testnet() -> Self {
60 Self {
61 initial_subsidy: 50_0000_0000,
62 halving_interval: 210_000,
63 max_money_supply: 21_0000_0000_0000_0000,
64 coinbase_maturity: 100,
65 dust_limit: 546,
66 min_fee_rate: 1,
67 max_fee_rate: 1_000_000,
68 min_relay_fee: 1000,
69 subsidy_schedule: Vec::new(),
70 }
71 }
72
73 pub fn regtest() -> Self {
75 Self {
76 initial_subsidy: 50_0000_0000,
77 halving_interval: 150, max_money_supply: 21_0000_0000_0000_0000,
79 coinbase_maturity: 100,
80 dust_limit: 546,
81 min_fee_rate: 0, max_fee_rate: 1_000_000,
83 min_relay_fee: 0, subsidy_schedule: Vec::new(),
85 }
86 }
87
88 pub fn signet() -> Self {
90 Self {
91 initial_subsidy: 50_0000_0000,
92 halving_interval: 210_000,
93 max_money_supply: 21_0000_0000_0000_0000,
94 coinbase_maturity: 100,
95 dust_limit: 546,
96 min_fee_rate: 1,
97 max_fee_rate: 1_000_000,
98 min_relay_fee: 1000,
99 subsidy_schedule: Vec::new(),
100 }
101 }
102
103 pub fn get_block_subsidy(&self, height: u64) -> u64 {
105 if !self.subsidy_schedule.is_empty() {
107 for (schedule_height, subsidy) in self.subsidy_schedule.iter().rev() {
108 if height >= *schedule_height {
109 return *subsidy;
110 }
111 }
112 return 0;
113 }
114
115 let halving_period = height / self.halving_interval;
117
118 if halving_period >= 64 {
120 return 0;
121 }
122
123 self.initial_subsidy >> halving_period
125 }
126
127 pub fn total_supply_at_height(&self, height: u64) -> u64 {
129 let mut total = 0u64;
130
131 for h in 0..=height {
132 total = total.saturating_add(self.get_block_subsidy(h));
133 }
134
135 total
136 }
137
138 pub fn is_dust(&self, value: u64) -> bool {
140 value < self.dust_limit
141 }
142
143 pub fn is_valid_fee_rate(&self, fee_rate: u64) -> bool {
145 fee_rate >= self.min_fee_rate && fee_rate <= self.max_fee_rate
146 }
147
148 pub fn calculate_fee(&self, size_vbytes: usize, fee_rate_sat_per_vbyte: u64) -> u64 {
150 if !self.is_valid_fee_rate(fee_rate_sat_per_vbyte) {
151 return 0;
152 }
153
154 (size_vbytes as u64).saturating_mul(fee_rate_sat_per_vbyte)
155 }
156
157 pub fn exceeds_max_supply(&self, height: u64) -> bool {
159 self.total_supply_at_height(height) > self.max_money_supply
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_mainnet_economic_parameters() {
169 let params = EconomicParameters::mainnet();
170
171 assert_eq!(params.initial_subsidy, 50_0000_0000);
172 assert_eq!(params.halving_interval, 210_000);
173 assert_eq!(params.max_money_supply, 21_0000_0000_0000_0000);
174 assert_eq!(params.coinbase_maturity, 100);
175 assert_eq!(params.dust_limit, 546);
176 }
177
178 #[test]
179 fn test_block_subsidy_halving() {
180 let params = EconomicParameters::mainnet();
181
182 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
184 assert_eq!(params.get_block_subsidy(209_999), 50_0000_0000);
185
186 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
188 assert_eq!(params.get_block_subsidy(419_999), 25_0000_0000);
189
190 assert_eq!(params.get_block_subsidy(420_000), 12_5000_0000);
192
193 assert_eq!(params.get_block_subsidy(13_440_000), 0);
195 assert_eq!(params.get_block_subsidy(20_000_000), 0);
196 }
197
198 #[test]
199 fn test_total_supply_calculation() {
200 let params = EconomicParameters::mainnet();
201
202 assert_eq!(params.total_supply_at_height(0), 50_0000_0000);
204
205 assert_eq!(params.total_supply_at_height(9), 10 * 50_0000_0000);
207
208 let first_halving_height = 210_000;
210 let _before_halving_subsidy = first_halving_height * 50_0000_0000;
211 assert!(params.total_supply_at_height(first_halving_height) > 0);
213 }
214
215 #[test]
216 fn test_dust_limit() {
217 let params = EconomicParameters::mainnet();
218
219 assert!(params.is_dust(545));
220 assert!(!params.is_dust(546));
221 assert!(!params.is_dust(1000));
222 }
223
224 #[test]
225 fn test_fee_rate_validation() {
226 let params = EconomicParameters::mainnet();
227
228 assert!(params.is_valid_fee_rate(1));
230 assert!(params.is_valid_fee_rate(100));
231 assert!(params.is_valid_fee_rate(1_000_000));
232
233 assert!(!params.is_valid_fee_rate(0));
235 assert!(!params.is_valid_fee_rate(1_000_001));
236 }
237
238 #[test]
239 fn test_fee_calculation() {
240 let params = EconomicParameters::mainnet();
241
242 assert_eq!(params.calculate_fee(250, 10), 2500);
244
245 assert_eq!(params.calculate_fee(250, 0), 0);
247 assert_eq!(params.calculate_fee(250, 2_000_000), 0);
248 }
249
250 #[test]
251 fn test_regtest_relaxed_parameters() {
252 let params = EconomicParameters::regtest();
253
254 assert_eq!(params.halving_interval, 150);
256
257 assert_eq!(params.min_fee_rate, 0);
259 assert_eq!(params.min_relay_fee, 0);
260
261 assert!(params.is_valid_fee_rate(0));
263 }
264
265 #[test]
266 fn test_regtest_faster_halving() {
267 let params = EconomicParameters::regtest();
268
269 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
271 assert_eq!(params.get_block_subsidy(149), 50_0000_0000);
272 assert_eq!(params.get_block_subsidy(150), 25_0000_0000);
273 assert_eq!(params.get_block_subsidy(299), 25_0000_0000);
274 assert_eq!(params.get_block_subsidy(300), 12_5000_0000);
275 }
276
277 #[test]
278 fn test_testnet_same_as_mainnet() {
279 let mainnet = EconomicParameters::mainnet();
280 let testnet = EconomicParameters::testnet();
281
282 assert_eq!(mainnet.initial_subsidy, testnet.initial_subsidy);
283 assert_eq!(mainnet.halving_interval, testnet.halving_interval);
284 assert_eq!(mainnet.max_money_supply, testnet.max_money_supply);
285 assert_eq!(mainnet.coinbase_maturity, testnet.coinbase_maturity);
286 assert_eq!(mainnet.dust_limit, testnet.dust_limit);
287 }
288
289 #[test]
290 fn test_custom_subsidy_schedule() {
291 let mut params = EconomicParameters::mainnet();
292 params.subsidy_schedule = vec![
293 (0, 100_0000_0000), (1000, 50_0000_0000), (210_000, 25_0000_0000), ];
297
298 assert_eq!(params.get_block_subsidy(0), 100_0000_0000);
299 assert_eq!(params.get_block_subsidy(999), 100_0000_0000);
300 assert_eq!(params.get_block_subsidy(1000), 50_0000_0000);
301 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
302 }
303
304 #[test]
305 fn test_max_supply_check() {
306 let params = EconomicParameters::mainnet();
307
308 assert!(!params.exceeds_max_supply(100_000));
310 assert!(!params.exceeds_max_supply(1_000_000));
311
312 }
315
316 #[test]
317 fn test_economic_parameters_serialization() {
318 let mainnet = EconomicParameters::mainnet();
319 let json = serde_json::to_string(&mainnet).unwrap();
320 let deserialized: EconomicParameters = serde_json::from_str(&json).unwrap();
321
322 assert_eq!(mainnet.initial_subsidy, deserialized.initial_subsidy);
323 assert_eq!(mainnet.halving_interval, deserialized.halving_interval);
324 assert_eq!(mainnet.max_money_supply, deserialized.max_money_supply);
325 assert_eq!(mainnet.coinbase_maturity, deserialized.coinbase_maturity);
326 assert_eq!(mainnet.dust_limit, deserialized.dust_limit);
327 }
328
329 #[test]
330 fn test_economic_parameters_equality() {
331 let mainnet1 = EconomicParameters::mainnet();
332 let mainnet2 = EconomicParameters::mainnet();
333 let testnet = EconomicParameters::testnet();
334
335 assert_eq!(mainnet1, mainnet2);
336 assert_eq!(mainnet1, testnet); }
338}