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 }
40 }
41
42 pub fn mainnet() -> Self {
44 Self {
45 initial_subsidy: 50_0000_0000, halving_interval: 210_000,
47 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(), }
55 }
56
57 pub fn testnet() -> Self {
59 Self {
60 initial_subsidy: 50_0000_0000,
61 halving_interval: 210_000,
62 max_money_supply: 21_0000_0000_0000_0000,
63 coinbase_maturity: 100,
64 dust_limit: 546,
65 min_fee_rate: 1,
66 max_fee_rate: 1_000_000,
67 min_relay_fee: 1000,
68 subsidy_schedule: Vec::new(),
69 }
70 }
71
72 pub fn regtest() -> Self {
74 Self {
75 initial_subsidy: 50_0000_0000,
76 halving_interval: 150, max_money_supply: 21_0000_0000_0000_0000,
78 coinbase_maturity: 100,
79 dust_limit: 546,
80 min_fee_rate: 0, max_fee_rate: 1_000_000,
82 min_relay_fee: 0, subsidy_schedule: Vec::new(),
84 }
85 }
86
87 pub fn get_block_subsidy(&self, height: u64) -> u64 {
89 if !self.subsidy_schedule.is_empty() {
91 for (schedule_height, subsidy) in self.subsidy_schedule.iter().rev() {
92 if height >= *schedule_height {
93 return *subsidy;
94 }
95 }
96 return 0;
97 }
98
99 let halving_period = height / self.halving_interval;
101
102 if halving_period >= 64 {
104 return 0;
105 }
106
107 self.initial_subsidy >> halving_period
109 }
110
111 pub fn total_supply_at_height(&self, height: u64) -> u64 {
113 let mut total = 0u64;
114
115 for h in 0..=height {
116 total = total.saturating_add(self.get_block_subsidy(h));
117 }
118
119 total
120 }
121
122 pub fn is_dust(&self, value: u64) -> bool {
124 value < self.dust_limit
125 }
126
127 pub fn is_valid_fee_rate(&self, fee_rate: u64) -> bool {
129 fee_rate >= self.min_fee_rate && fee_rate <= self.max_fee_rate
130 }
131
132 pub fn calculate_fee(&self, size_vbytes: usize, fee_rate_sat_per_vbyte: u64) -> u64 {
134 if !self.is_valid_fee_rate(fee_rate_sat_per_vbyte) {
135 return 0;
136 }
137
138 (size_vbytes as u64).saturating_mul(fee_rate_sat_per_vbyte)
139 }
140
141 pub fn exceeds_max_supply(&self, height: u64) -> bool {
143 self.total_supply_at_height(height) > self.max_money_supply
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_mainnet_economic_parameters() {
153 let params = EconomicParameters::mainnet();
154
155 assert_eq!(params.initial_subsidy, 50_0000_0000);
156 assert_eq!(params.halving_interval, 210_000);
157 assert_eq!(params.max_money_supply, 21_0000_0000_0000_0000);
158 assert_eq!(params.coinbase_maturity, 100);
159 assert_eq!(params.dust_limit, 546);
160 }
161
162 #[test]
163 fn test_block_subsidy_halving() {
164 let params = EconomicParameters::mainnet();
165
166 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
168 assert_eq!(params.get_block_subsidy(209_999), 50_0000_0000);
169
170 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
172 assert_eq!(params.get_block_subsidy(419_999), 25_0000_0000);
173
174 assert_eq!(params.get_block_subsidy(420_000), 12_5000_0000);
176
177 assert_eq!(params.get_block_subsidy(13_440_000), 0);
179 assert_eq!(params.get_block_subsidy(20_000_000), 0);
180 }
181
182 #[test]
183 fn test_total_supply_calculation() {
184 let params = EconomicParameters::mainnet();
185
186 assert_eq!(params.total_supply_at_height(0), 50_0000_0000);
188
189 assert_eq!(params.total_supply_at_height(9), 10 * 50_0000_0000);
191
192 let first_halving_height = 210_000;
194 let _before_halving_subsidy = first_halving_height * 50_0000_0000;
195 assert!(params.total_supply_at_height(first_halving_height) > 0);
197 }
198
199 #[test]
200 fn test_dust_limit() {
201 let params = EconomicParameters::mainnet();
202
203 assert!(params.is_dust(545));
204 assert!(!params.is_dust(546));
205 assert!(!params.is_dust(1000));
206 }
207
208 #[test]
209 fn test_fee_rate_validation() {
210 let params = EconomicParameters::mainnet();
211
212 assert!(params.is_valid_fee_rate(1));
214 assert!(params.is_valid_fee_rate(100));
215 assert!(params.is_valid_fee_rate(1_000_000));
216
217 assert!(!params.is_valid_fee_rate(0));
219 assert!(!params.is_valid_fee_rate(1_000_001));
220 }
221
222 #[test]
223 fn test_fee_calculation() {
224 let params = EconomicParameters::mainnet();
225
226 assert_eq!(params.calculate_fee(250, 10), 2500);
228
229 assert_eq!(params.calculate_fee(250, 0), 0);
231 assert_eq!(params.calculate_fee(250, 2_000_000), 0);
232 }
233
234 #[test]
235 fn test_regtest_relaxed_parameters() {
236 let params = EconomicParameters::regtest();
237
238 assert_eq!(params.halving_interval, 150);
240
241 assert_eq!(params.min_fee_rate, 0);
243 assert_eq!(params.min_relay_fee, 0);
244
245 assert!(params.is_valid_fee_rate(0));
247 }
248
249 #[test]
250 fn test_regtest_faster_halving() {
251 let params = EconomicParameters::regtest();
252
253 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
255 assert_eq!(params.get_block_subsidy(149), 50_0000_0000);
256 assert_eq!(params.get_block_subsidy(150), 25_0000_0000);
257 assert_eq!(params.get_block_subsidy(299), 25_0000_0000);
258 assert_eq!(params.get_block_subsidy(300), 12_5000_0000);
259 }
260
261 #[test]
262 fn test_testnet_same_as_mainnet() {
263 let mainnet = EconomicParameters::mainnet();
264 let testnet = EconomicParameters::testnet();
265
266 assert_eq!(mainnet.initial_subsidy, testnet.initial_subsidy);
267 assert_eq!(mainnet.halving_interval, testnet.halving_interval);
268 assert_eq!(mainnet.max_money_supply, testnet.max_money_supply);
269 assert_eq!(mainnet.coinbase_maturity, testnet.coinbase_maturity);
270 assert_eq!(mainnet.dust_limit, testnet.dust_limit);
271 }
272
273 #[test]
274 fn test_custom_subsidy_schedule() {
275 let mut params = EconomicParameters::mainnet();
276 params.subsidy_schedule = vec![
277 (0, 100_0000_0000), (1000, 50_0000_0000), (210_000, 25_0000_0000), ];
281
282 assert_eq!(params.get_block_subsidy(0), 100_0000_0000);
283 assert_eq!(params.get_block_subsidy(999), 100_0000_0000);
284 assert_eq!(params.get_block_subsidy(1000), 50_0000_0000);
285 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
286 }
287
288 #[test]
289 fn test_max_supply_check() {
290 let params = EconomicParameters::mainnet();
291
292 assert!(!params.exceeds_max_supply(100_000));
294 assert!(!params.exceeds_max_supply(1_000_000));
295
296 }
299
300 #[test]
301 fn test_economic_parameters_serialization() {
302 let mainnet = EconomicParameters::mainnet();
303 let json = serde_json::to_string(&mainnet).unwrap();
304 let deserialized: EconomicParameters = serde_json::from_str(&json).unwrap();
305
306 assert_eq!(mainnet.initial_subsidy, deserialized.initial_subsidy);
307 assert_eq!(mainnet.halving_interval, deserialized.halving_interval);
308 assert_eq!(mainnet.max_money_supply, deserialized.max_money_supply);
309 assert_eq!(mainnet.coinbase_maturity, deserialized.coinbase_maturity);
310 assert_eq!(mainnet.dust_limit, deserialized.dust_limit);
311 }
312
313 #[test]
314 fn test_economic_parameters_equality() {
315 let mainnet1 = EconomicParameters::mainnet();
316 let mainnet2 = EconomicParameters::mainnet();
317 let testnet = EconomicParameters::testnet();
318
319 assert_eq!(mainnet1, mainnet2);
320 assert_eq!(mainnet1, testnet); }
322}