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 ProtocolVersion::Testnet4 => Self::testnet(),
41 }
42 }
43
44 pub fn mainnet() -> Self {
46 Self {
47 initial_subsidy: 50_0000_0000, halving_interval: 210_000,
49 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(), }
57 }
58
59 pub fn testnet() -> Self {
61 Self {
62 initial_subsidy: 50_0000_0000,
63 halving_interval: 210_000,
64 max_money_supply: 21_0000_0000_0000_0000,
65 coinbase_maturity: 100,
66 dust_limit: 546,
67 min_fee_rate: 1,
68 max_fee_rate: 1_000_000,
69 min_relay_fee: 1000,
70 subsidy_schedule: Vec::new(),
71 }
72 }
73
74 pub fn regtest() -> Self {
76 Self {
77 initial_subsidy: 50_0000_0000,
78 halving_interval: 150, max_money_supply: 21_0000_0000_0000_0000,
80 coinbase_maturity: 100,
81 dust_limit: 546,
82 min_fee_rate: 0, max_fee_rate: 1_000_000,
84 min_relay_fee: 0, subsidy_schedule: Vec::new(),
86 }
87 }
88
89 pub fn signet() -> Self {
91 Self {
92 initial_subsidy: 50_0000_0000,
93 halving_interval: 210_000,
94 max_money_supply: 21_0000_0000_0000_0000,
95 coinbase_maturity: 100,
96 dust_limit: 546,
97 min_fee_rate: 1,
98 max_fee_rate: 1_000_000,
99 min_relay_fee: 1000,
100 subsidy_schedule: Vec::new(),
101 }
102 }
103
104 pub fn get_block_subsidy(&self, height: u64) -> u64 {
106 if !self.subsidy_schedule.is_empty() {
108 for (schedule_height, subsidy) in self.subsidy_schedule.iter().rev() {
109 if height >= *schedule_height {
110 return *subsidy;
111 }
112 }
113 return 0;
114 }
115
116 let halving_period = height / self.halving_interval;
118
119 if halving_period >= 64 {
121 return 0;
122 }
123
124 self.initial_subsidy >> halving_period
126 }
127
128 pub fn total_supply_at_height(&self, height: u64) -> u64 {
130 let mut total = 0u64;
131
132 for h in 0..=height {
133 total = total.saturating_add(self.get_block_subsidy(h));
134 }
135
136 total
137 }
138
139 pub fn is_dust(&self, value: u64) -> bool {
141 value < self.dust_limit
142 }
143
144 pub fn is_valid_fee_rate(&self, fee_rate: u64) -> bool {
146 fee_rate >= self.min_fee_rate && fee_rate <= self.max_fee_rate
147 }
148
149 pub fn calculate_fee(&self, size_vbytes: usize, fee_rate_sat_per_vbyte: u64) -> u64 {
151 if !self.is_valid_fee_rate(fee_rate_sat_per_vbyte) {
152 return 0;
153 }
154
155 (size_vbytes as u64).saturating_mul(fee_rate_sat_per_vbyte)
156 }
157
158 pub fn exceeds_max_supply(&self, height: u64) -> bool {
160 self.total_supply_at_height(height) > self.max_money_supply
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_mainnet_economic_parameters() {
170 let params = EconomicParameters::mainnet();
171
172 assert_eq!(params.initial_subsidy, 50_0000_0000);
173 assert_eq!(params.halving_interval, 210_000);
174 assert_eq!(params.max_money_supply, 21_0000_0000_0000_0000);
175 assert_eq!(params.coinbase_maturity, 100);
176 assert_eq!(params.dust_limit, 546);
177 }
178
179 #[test]
180 fn test_block_subsidy_halving() {
181 let params = EconomicParameters::mainnet();
182
183 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
185 assert_eq!(params.get_block_subsidy(209_999), 50_0000_0000);
186
187 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
189 assert_eq!(params.get_block_subsidy(419_999), 25_0000_0000);
190
191 assert_eq!(params.get_block_subsidy(420_000), 12_5000_0000);
193
194 assert_eq!(params.get_block_subsidy(13_440_000), 0);
196 assert_eq!(params.get_block_subsidy(20_000_000), 0);
197 }
198
199 #[test]
200 fn test_total_supply_calculation() {
201 let params = EconomicParameters::mainnet();
202
203 assert_eq!(params.total_supply_at_height(0), 50_0000_0000);
205
206 assert_eq!(params.total_supply_at_height(9), 10 * 50_0000_0000);
208
209 let first_halving_height = 210_000;
211 let _before_halving_subsidy = first_halving_height * 50_0000_0000;
212 assert!(params.total_supply_at_height(first_halving_height) > 0);
214 }
215
216 #[test]
217 fn test_dust_limit() {
218 let params = EconomicParameters::mainnet();
219
220 assert!(params.is_dust(545));
221 assert!(!params.is_dust(546));
222 assert!(!params.is_dust(1000));
223 }
224
225 #[test]
226 fn test_fee_rate_validation() {
227 let params = EconomicParameters::mainnet();
228
229 assert!(params.is_valid_fee_rate(1));
231 assert!(params.is_valid_fee_rate(100));
232 assert!(params.is_valid_fee_rate(1_000_000));
233
234 assert!(!params.is_valid_fee_rate(0));
236 assert!(!params.is_valid_fee_rate(1_000_001));
237 }
238
239 #[test]
240 fn test_fee_calculation() {
241 let params = EconomicParameters::mainnet();
242
243 assert_eq!(params.calculate_fee(250, 10), 2500);
245
246 assert_eq!(params.calculate_fee(250, 0), 0);
248 assert_eq!(params.calculate_fee(250, 2_000_000), 0);
249 }
250
251 #[test]
252 fn test_regtest_relaxed_parameters() {
253 let params = EconomicParameters::regtest();
254
255 assert_eq!(params.halving_interval, 150);
257
258 assert_eq!(params.min_fee_rate, 0);
260 assert_eq!(params.min_relay_fee, 0);
261
262 assert!(params.is_valid_fee_rate(0));
264 }
265
266 #[test]
267 fn test_regtest_faster_halving() {
268 let params = EconomicParameters::regtest();
269
270 assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
272 assert_eq!(params.get_block_subsidy(149), 50_0000_0000);
273 assert_eq!(params.get_block_subsidy(150), 25_0000_0000);
274 assert_eq!(params.get_block_subsidy(299), 25_0000_0000);
275 assert_eq!(params.get_block_subsidy(300), 12_5000_0000);
276 }
277
278 #[test]
279 fn test_testnet_same_as_mainnet() {
280 let mainnet = EconomicParameters::mainnet();
281 let testnet = EconomicParameters::testnet();
282
283 assert_eq!(mainnet.initial_subsidy, testnet.initial_subsidy);
284 assert_eq!(mainnet.halving_interval, testnet.halving_interval);
285 assert_eq!(mainnet.max_money_supply, testnet.max_money_supply);
286 assert_eq!(mainnet.coinbase_maturity, testnet.coinbase_maturity);
287 assert_eq!(mainnet.dust_limit, testnet.dust_limit);
288 }
289
290 #[test]
291 fn test_custom_subsidy_schedule() {
292 let mut params = EconomicParameters::mainnet();
293 params.subsidy_schedule = vec![
294 (0, 100_0000_0000), (1000, 50_0000_0000), (210_000, 25_0000_0000), ];
298
299 assert_eq!(params.get_block_subsidy(0), 100_0000_0000);
300 assert_eq!(params.get_block_subsidy(999), 100_0000_0000);
301 assert_eq!(params.get_block_subsidy(1000), 50_0000_0000);
302 assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
303 }
304
305 #[test]
306 fn test_max_supply_check() {
307 let params = EconomicParameters::mainnet();
308
309 assert!(!params.exceeds_max_supply(100_000));
311 assert!(!params.exceeds_max_supply(1_000_000));
312
313 }
316
317 #[test]
318 fn test_economic_parameters_serialization() {
319 let mainnet = EconomicParameters::mainnet();
320 let json = serde_json::to_string(&mainnet).unwrap();
321 let deserialized: EconomicParameters = serde_json::from_str(&json).unwrap();
322
323 assert_eq!(mainnet.initial_subsidy, deserialized.initial_subsidy);
324 assert_eq!(mainnet.halving_interval, deserialized.halving_interval);
325 assert_eq!(mainnet.max_money_supply, deserialized.max_money_supply);
326 assert_eq!(mainnet.coinbase_maturity, deserialized.coinbase_maturity);
327 assert_eq!(mainnet.dust_limit, deserialized.dust_limit);
328 }
329
330 #[test]
331 fn test_economic_parameters_equality() {
332 let mainnet1 = EconomicParameters::mainnet();
333 let mainnet2 = EconomicParameters::mainnet();
334 let testnet = EconomicParameters::testnet();
335
336 assert_eq!(mainnet1, mainnet2);
337 assert_eq!(mainnet1, testnet); }
339}