1use kaspa_consensus_core::{
2 coinbase::*,
3 errors::coinbase::{CoinbaseError, CoinbaseResult},
4 subnets,
5 tx::{ScriptPublicKey, ScriptVec, Transaction, TransactionOutput},
6 BlockHashMap, BlockHashSet,
7};
8use std::convert::TryInto;
9
10use crate::{constants, model::stores::ghostdag::GhostdagData};
11
12const LENGTH_OF_BLUE_SCORE: usize = size_of::<u64>();
13const LENGTH_OF_SUBSIDY: usize = size_of::<u64>();
14const LENGTH_OF_SCRIPT_PUB_KEY_VERSION: usize = size_of::<u16>();
15const LENGTH_OF_SCRIPT_PUB_KEY_LENGTH: usize = size_of::<u8>();
16
17const MIN_PAYLOAD_LENGTH: usize =
18 LENGTH_OF_BLUE_SCORE + LENGTH_OF_SUBSIDY + LENGTH_OF_SCRIPT_PUB_KEY_VERSION + LENGTH_OF_SCRIPT_PUB_KEY_LENGTH;
19
20const SECONDS_PER_MONTH: u64 = 2629800;
23
24pub const SUBSIDY_BY_MONTH_TABLE_SIZE: usize = 426;
25pub type SubsidyByMonthTable = [u64; SUBSIDY_BY_MONTH_TABLE_SIZE];
26
27#[derive(Clone)]
28pub struct CoinbaseManager {
29 coinbase_payload_script_public_key_max_len: u8,
30 max_coinbase_payload_len: usize,
31 deflationary_phase_daa_score: u64,
32 pre_deflationary_phase_base_subsidy: u64,
33 target_time_per_block: u64,
34
35 blocks_per_month: u64,
37
38 subsidy_by_month_table: SubsidyByMonthTable,
40}
41
42struct PayloadParser<'a> {
44 remaining: &'a [u8], }
46
47impl<'a> PayloadParser<'a> {
48 fn new(data: &'a [u8]) -> Self {
49 Self { remaining: data }
50 }
51
52 fn take(&mut self, n: usize) -> &[u8] {
54 let (segment, remaining) = self.remaining.split_at(n);
55 self.remaining = remaining;
56 segment
57 }
58}
59
60impl CoinbaseManager {
61 pub fn new(
62 coinbase_payload_script_public_key_max_len: u8,
63 max_coinbase_payload_len: usize,
64 deflationary_phase_daa_score: u64,
65 pre_deflationary_phase_base_subsidy: u64,
66 target_time_per_block: u64,
67 ) -> Self {
68 assert!(1000 % target_time_per_block == 0);
69 let bps = 1000 / target_time_per_block;
70 let blocks_per_month = SECONDS_PER_MONTH * bps;
71
72 let subsidy_by_month_table: SubsidyByMonthTable = core::array::from_fn(|i| (SUBSIDY_BY_MONTH_TABLE[i] + bps - 1) / bps);
76 Self {
77 coinbase_payload_script_public_key_max_len,
78 max_coinbase_payload_len,
79 deflationary_phase_daa_score,
80 pre_deflationary_phase_base_subsidy,
81 target_time_per_block,
82 blocks_per_month,
83 subsidy_by_month_table,
84 }
85 }
86
87 #[cfg(test)]
88 #[inline]
89 pub fn bps(&self) -> u64 {
90 1000 / self.target_time_per_block
91 }
92
93 pub fn expected_coinbase_transaction<T: AsRef<[u8]>>(
94 &self,
95 daa_score: u64,
96 miner_data: MinerData<T>,
97 ghostdag_data: &GhostdagData,
98 mergeset_rewards: &BlockHashMap<BlockRewardData>,
99 mergeset_non_daa: &BlockHashSet,
100 ) -> CoinbaseResult<CoinbaseTransactionTemplate> {
101 let mut outputs = Vec::with_capacity(ghostdag_data.mergeset_blues.len() + 1); for blue in ghostdag_data.mergeset_blues.iter().filter(|h| !mergeset_non_daa.contains(h)) {
106 let reward_data = mergeset_rewards.get(blue).unwrap();
107 if reward_data.subsidy + reward_data.total_fees > 0 {
108 outputs
109 .push(TransactionOutput::new(reward_data.subsidy + reward_data.total_fees, reward_data.script_public_key.clone()));
110 }
111 }
112
113 let mut red_reward = 0u64;
116 for red in ghostdag_data.mergeset_reds.iter().filter(|h| !mergeset_non_daa.contains(h)) {
117 let reward_data = mergeset_rewards.get(red).unwrap();
118 red_reward += reward_data.subsidy + reward_data.total_fees;
119 }
120 if red_reward > 0 {
121 outputs.push(TransactionOutput::new(red_reward, miner_data.script_public_key.clone()));
122 }
123
124 let subsidy = self.calc_block_subsidy(daa_score);
126 let payload = self.serialize_coinbase_payload(&CoinbaseData { blue_score: ghostdag_data.blue_score, subsidy, miner_data })?;
127
128 Ok(CoinbaseTransactionTemplate {
129 tx: Transaction::new(constants::TX_VERSION, vec![], outputs, 0, subnets::SUBNETWORK_ID_COINBASE, 0, payload),
130 has_red_reward: red_reward > 0,
131 })
132 }
133
134 pub fn serialize_coinbase_payload<T: AsRef<[u8]>>(&self, data: &CoinbaseData<T>) -> CoinbaseResult<Vec<u8>> {
135 let script_pub_key_len = data.miner_data.script_public_key.script().len();
136 if script_pub_key_len > self.coinbase_payload_script_public_key_max_len as usize {
137 return Err(CoinbaseError::PayloadScriptPublicKeyLenAboveMax(
138 script_pub_key_len,
139 self.coinbase_payload_script_public_key_max_len,
140 ));
141 }
142 let payload: Vec<u8> = data.blue_score.to_le_bytes().iter().copied() .chain(data.subsidy.to_le_bytes().iter().copied()) .chain(data.miner_data.script_public_key.version().to_le_bytes().iter().copied()) .chain((script_pub_key_len as u8).to_le_bytes().iter().copied()) .chain(data.miner_data.script_public_key.script().iter().copied()) .chain(data.miner_data.extra_data.as_ref().iter().copied()) .collect();
149
150 Ok(payload)
151 }
152
153 pub fn modify_coinbase_payload<T: AsRef<[u8]>>(&self, mut payload: Vec<u8>, miner_data: &MinerData<T>) -> CoinbaseResult<Vec<u8>> {
154 let script_pub_key_len = miner_data.script_public_key.script().len();
155 if script_pub_key_len > self.coinbase_payload_script_public_key_max_len as usize {
156 return Err(CoinbaseError::PayloadScriptPublicKeyLenAboveMax(
157 script_pub_key_len,
158 self.coinbase_payload_script_public_key_max_len,
159 ));
160 }
161
162 payload.truncate(LENGTH_OF_BLUE_SCORE + LENGTH_OF_SUBSIDY);
165 payload.extend(
166 miner_data.script_public_key.version().to_le_bytes().iter().copied() .chain((script_pub_key_len as u8).to_le_bytes().iter().copied()) .chain(miner_data.script_public_key.script().iter().copied()) .chain(miner_data.extra_data.as_ref().iter().copied()), );
171
172 Ok(payload)
173 }
174
175 pub fn deserialize_coinbase_payload<'a>(&self, payload: &'a [u8]) -> CoinbaseResult<CoinbaseData<&'a [u8]>> {
176 if payload.len() < MIN_PAYLOAD_LENGTH {
177 return Err(CoinbaseError::PayloadLenBelowMin(payload.len(), MIN_PAYLOAD_LENGTH));
178 }
179
180 if payload.len() > self.max_coinbase_payload_len {
181 return Err(CoinbaseError::PayloadLenAboveMax(payload.len(), self.max_coinbase_payload_len));
182 }
183
184 let mut parser = PayloadParser::new(payload);
185
186 let blue_score = u64::from_le_bytes(parser.take(LENGTH_OF_BLUE_SCORE).try_into().unwrap());
187 let subsidy = u64::from_le_bytes(parser.take(LENGTH_OF_SUBSIDY).try_into().unwrap());
188 let script_pub_key_version = u16::from_le_bytes(parser.take(LENGTH_OF_SCRIPT_PUB_KEY_VERSION).try_into().unwrap());
189 let script_pub_key_len = u8::from_le_bytes(parser.take(LENGTH_OF_SCRIPT_PUB_KEY_LENGTH).try_into().unwrap());
190
191 if script_pub_key_len > self.coinbase_payload_script_public_key_max_len {
192 return Err(CoinbaseError::PayloadScriptPublicKeyLenAboveMax(
193 script_pub_key_len as usize,
194 self.coinbase_payload_script_public_key_max_len,
195 ));
196 }
197
198 if parser.remaining.len() < script_pub_key_len as usize {
199 return Err(CoinbaseError::PayloadCantContainScriptPublicKey(
200 payload.len(),
201 MIN_PAYLOAD_LENGTH + script_pub_key_len as usize,
202 ));
203 }
204
205 let script_public_key =
206 ScriptPublicKey::new(script_pub_key_version, ScriptVec::from_slice(parser.take(script_pub_key_len as usize)));
207 let extra_data = parser.remaining;
208
209 Ok(CoinbaseData { blue_score, subsidy, miner_data: MinerData { script_public_key, extra_data } })
210 }
211
212 pub fn calc_block_subsidy(&self, daa_score: u64) -> u64 {
213 if daa_score < self.deflationary_phase_daa_score {
214 return self.pre_deflationary_phase_base_subsidy;
215 }
216
217 let months_since_deflationary_phase_started =
218 ((daa_score - self.deflationary_phase_daa_score) / self.blocks_per_month) as usize;
219 if months_since_deflationary_phase_started >= self.subsidy_by_month_table.len() {
220 *(self.subsidy_by_month_table).last().unwrap()
221 } else {
222 self.subsidy_by_month_table[months_since_deflationary_phase_started]
223 }
224 }
225
226 #[cfg(test)]
227 pub fn legacy_calc_block_subsidy(&self, daa_score: u64) -> u64 {
228 if daa_score < self.deflationary_phase_daa_score {
229 return self.pre_deflationary_phase_base_subsidy;
230 }
231
232 let months_since_deflationary_phase_started = (daa_score - self.deflationary_phase_daa_score) / SECONDS_PER_MONTH;
234 assert!(months_since_deflationary_phase_started <= usize::MAX as u64);
235 let months_since_deflationary_phase_started: usize = months_since_deflationary_phase_started as usize;
236 if months_since_deflationary_phase_started >= SUBSIDY_BY_MONTH_TABLE.len() {
237 *SUBSIDY_BY_MONTH_TABLE.last().unwrap()
238 } else {
239 SUBSIDY_BY_MONTH_TABLE[months_since_deflationary_phase_started]
240 }
241 }
242}
243
244#[rustfmt::skip]
250const SUBSIDY_BY_MONTH_TABLE: [u64; 426] = [
251 44000000000, 41530469757, 39199543598, 36999442271, 34922823143, 32962755691, 31112698372, 29366476791, 27718263097, 26162556530, 24694165062, 23308188075, 22000000000, 20765234878, 19599771799, 18499721135, 17461411571, 16481377845, 15556349186, 14683238395, 13859131548, 13081278265, 12347082531, 11654094037, 11000000000,
252 10382617439, 9799885899, 9249860567, 8730705785, 8240688922, 7778174593, 7341619197, 6929565774, 6540639132, 6173541265, 5827047018, 5500000000, 5191308719, 4899942949, 4624930283, 4365352892, 4120344461, 3889087296, 3670809598, 3464782887, 3270319566, 3086770632, 2913523509, 2750000000, 2595654359,
253 2449971474, 2312465141, 2182676446, 2060172230, 1944543648, 1835404799, 1732391443, 1635159783, 1543385316, 1456761754, 1375000000, 1297827179, 1224985737, 1156232570, 1091338223, 1030086115, 972271824, 917702399, 866195721, 817579891, 771692658, 728380877, 687500000, 648913589, 612492868,
254 578116285, 545669111, 515043057, 486135912, 458851199, 433097860, 408789945, 385846329, 364190438, 343750000, 324456794, 306246434, 289058142, 272834555, 257521528, 243067956, 229425599, 216548930, 204394972, 192923164, 182095219, 171875000, 162228397, 153123217, 144529071,
255 136417277, 128760764, 121533978, 114712799, 108274465, 102197486, 96461582, 91047609, 85937500, 81114198, 76561608, 72264535, 68208638, 64380382, 60766989, 57356399, 54137232, 51098743, 48230791, 45523804, 42968750, 40557099, 38280804, 36132267, 34104319,
256 32190191, 30383494, 28678199, 27068616, 25549371, 24115395, 22761902, 21484375, 20278549, 19140402, 18066133, 17052159, 16095095, 15191747, 14339099, 13534308, 12774685, 12057697, 11380951, 10742187, 10139274, 9570201, 9033066, 8526079, 8047547,
257 7595873, 7169549, 6767154, 6387342, 6028848, 5690475, 5371093, 5069637, 4785100, 4516533, 4263039, 4023773, 3797936, 3584774, 3383577, 3193671, 3014424, 2845237, 2685546, 2534818, 2392550, 2258266, 2131519, 2011886, 1898968,
258 1792387, 1691788, 1596835, 1507212, 1422618, 1342773, 1267409, 1196275, 1129133, 1065759, 1005943, 949484, 896193, 845894, 798417, 753606, 711309, 671386, 633704, 598137, 564566, 532879, 502971, 474742, 448096,
259 422947, 399208, 376803, 355654, 335693, 316852, 299068, 282283, 266439, 251485, 237371, 224048, 211473, 199604, 188401, 177827, 167846, 158426, 149534, 141141, 133219, 125742, 118685, 112024, 105736,
260 99802, 94200, 88913, 83923, 79213, 74767, 70570, 66609, 62871, 59342, 56012, 52868, 49901, 47100, 44456, 41961, 39606, 37383, 35285, 33304, 31435, 29671, 28006, 26434, 24950,
261 23550, 22228, 20980, 19803, 18691, 17642, 16652, 15717, 14835, 14003, 13217, 12475, 11775, 11114, 10490, 9901, 9345, 8821, 8326, 7858, 7417, 7001, 6608, 6237, 5887,
262 5557, 5245, 4950, 4672, 4410, 4163, 3929, 3708, 3500, 3304, 3118, 2943, 2778, 2622, 2475, 2336, 2205, 2081, 1964, 1854, 1750, 1652, 1559, 1471, 1389,
263 1311, 1237, 1168, 1102, 1040, 982, 927, 875, 826, 779, 735, 694, 655, 618, 584, 551, 520, 491, 463, 437, 413, 389, 367, 347, 327,
264 309, 292, 275, 260, 245, 231, 218, 206, 194, 183, 173, 163, 154, 146, 137, 130, 122, 115, 109, 103, 97, 91, 86, 81, 77,
265 73, 68, 65, 61, 57, 54, 51, 48, 45, 43, 40, 38, 36, 34, 32, 30, 28, 27, 25, 24, 22, 21, 20, 19, 18,
266 17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4,
267 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
268 0,
269];
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::params::MAINNET_PARAMS;
275 use kaspa_consensus_core::{
276 config::params::{Params, TESTNET11_PARAMS},
277 constants::SOMPI_PER_KASPA,
278 network::NetworkId,
279 tx::scriptvec,
280 };
281
282 #[test]
283 fn calc_high_bps_total_rewards_delta() {
284 const SECONDS_PER_MONTH: u64 = 2629800;
285
286 let legacy_cbm = create_legacy_manager();
287 let pre_deflationary_rewards = legacy_cbm.pre_deflationary_phase_base_subsidy * legacy_cbm.deflationary_phase_daa_score;
288 let total_rewards: u64 = pre_deflationary_rewards + SUBSIDY_BY_MONTH_TABLE.iter().map(|x| x * SECONDS_PER_MONTH).sum::<u64>();
289 let testnet_11_bps = TESTNET11_PARAMS.bps();
290 let total_high_bps_rewards_rounded_up: u64 = pre_deflationary_rewards
291 + SUBSIDY_BY_MONTH_TABLE
292 .iter()
293 .map(|x| ((x + testnet_11_bps - 1) / testnet_11_bps * testnet_11_bps) * SECONDS_PER_MONTH)
294 .sum::<u64>();
295
296 let cbm = create_manager(&TESTNET11_PARAMS);
297 let total_high_bps_rewards: u64 =
298 pre_deflationary_rewards + cbm.subsidy_by_month_table.iter().map(|x| x * cbm.blocks_per_month).sum::<u64>();
299 assert_eq!(total_high_bps_rewards_rounded_up, total_high_bps_rewards, "subsidy adjusted to bps must be rounded up");
300
301 let delta = total_high_bps_rewards as i64 - total_rewards as i64;
302
303 println!("Total rewards: {} sompi => {} KAS", total_rewards, total_rewards / SOMPI_PER_KASPA);
304 println!("Total high bps rewards: {} sompi => {} KAS", total_high_bps_rewards, total_high_bps_rewards / SOMPI_PER_KASPA);
305 println!("Delta: {} sompi => {} KAS", delta, delta / SOMPI_PER_KASPA as i64);
306 }
307
308 #[test]
309 fn subsidy_by_month_table_test() {
310 let cbm = create_legacy_manager();
311 cbm.subsidy_by_month_table.iter().enumerate().for_each(|(i, x)| {
312 assert_eq!(SUBSIDY_BY_MONTH_TABLE[i], *x, "for 1 BPS, const table and precomputed values must match");
313 });
314
315 for network_id in NetworkId::iter() {
316 let cbm = create_manager(&network_id.into());
317 cbm.subsidy_by_month_table.iter().enumerate().for_each(|(i, x)| {
318 assert_eq!(
319 (SUBSIDY_BY_MONTH_TABLE[i] + cbm.bps() - 1) / cbm.bps(),
320 *x,
321 "{}: locally computed and precomputed values must match",
322 network_id
323 );
324 });
325 }
326 }
327
328 #[test]
329 fn subsidy_test() {
330 const PRE_DEFLATIONARY_PHASE_BASE_SUBSIDY: u64 = 50000000000;
331 const DEFLATIONARY_PHASE_INITIAL_SUBSIDY: u64 = 44000000000;
332 const SECONDS_PER_MONTH: u64 = 2629800;
333 const SECONDS_PER_HALVING: u64 = SECONDS_PER_MONTH * 12;
334
335 for network_id in NetworkId::iter() {
336 let params = &network_id.into();
337 let cbm = create_manager(params);
338
339 let pre_deflationary_phase_base_subsidy = PRE_DEFLATIONARY_PHASE_BASE_SUBSIDY / params.bps();
340 let deflationary_phase_initial_subsidy = DEFLATIONARY_PHASE_INITIAL_SUBSIDY / params.bps();
341 let blocks_per_halving = SECONDS_PER_HALVING * params.bps();
342
343 struct Test {
344 name: &'static str,
345 daa_score: u64,
346 expected: u64,
347 }
348
349 let tests = vec![
350 Test { name: "first mined block", daa_score: 1, expected: pre_deflationary_phase_base_subsidy },
351 Test {
352 name: "before deflationary phase",
353 daa_score: params.deflationary_phase_daa_score - 1,
354 expected: pre_deflationary_phase_base_subsidy,
355 },
356 Test {
357 name: "start of deflationary phase",
358 daa_score: params.deflationary_phase_daa_score,
359 expected: deflationary_phase_initial_subsidy,
360 },
361 Test {
362 name: "after one halving",
363 daa_score: params.deflationary_phase_daa_score + blocks_per_halving,
364 expected: deflationary_phase_initial_subsidy / 2,
365 },
366 Test {
367 name: "after 2 halvings",
368 daa_score: params.deflationary_phase_daa_score + 2 * blocks_per_halving,
369 expected: deflationary_phase_initial_subsidy / 4,
370 },
371 Test {
372 name: "after 5 halvings",
373 daa_score: params.deflationary_phase_daa_score + 5 * blocks_per_halving,
374 expected: deflationary_phase_initial_subsidy / 32,
375 },
376 Test {
377 name: "after 32 halvings",
378 daa_score: params.deflationary_phase_daa_score + 32 * blocks_per_halving,
379 expected: ((DEFLATIONARY_PHASE_INITIAL_SUBSIDY / 2_u64.pow(32)) + cbm.bps() - 1) / cbm.bps(),
380 },
381 Test {
382 name: "just before subsidy depleted",
383 daa_score: params.deflationary_phase_daa_score + 35 * blocks_per_halving,
384 expected: 1,
385 },
386 Test {
387 name: "after subsidy depleted",
388 daa_score: params.deflationary_phase_daa_score + 36 * blocks_per_halving,
389 expected: 0,
390 },
391 ];
392
393 for t in tests {
394 assert_eq!(cbm.calc_block_subsidy(t.daa_score), t.expected, "{} test '{}' failed", network_id, t.name);
395 if params.bps() == 1 {
396 assert_eq!(cbm.legacy_calc_block_subsidy(t.daa_score), t.expected, "{} test '{}' failed", network_id, t.name);
397 }
398 }
399 }
400 }
401
402 #[test]
403 fn payload_serialization_test() {
404 let cbm = create_manager(&MAINNET_PARAMS);
405
406 let script_data = [33u8, 255];
407 let extra_data = [2u8, 3];
408 let data = CoinbaseData {
409 blue_score: 56,
410 subsidy: 44000000000,
411 miner_data: MinerData {
412 script_public_key: ScriptPublicKey::new(0, ScriptVec::from_slice(&script_data)),
413 extra_data: &extra_data as &[u8],
414 },
415 };
416
417 let payload = cbm.serialize_coinbase_payload(&data).unwrap();
418 let deserialized_data = cbm.deserialize_coinbase_payload(&payload).unwrap();
419
420 assert_eq!(data, deserialized_data);
421
422 let payload_hex =
424 "b612c90100000000041a763e07000000000022202b32443ff740012157716d81216d09aebc39e5493c93a7181d92cb756c02c560ac302e31322e382f";
425 let mut payload = vec![0u8; payload_hex.len() / 2];
426 faster_hex::hex_decode(payload_hex.as_bytes(), &mut payload).unwrap();
427 let deserialized_data = cbm.deserialize_coinbase_payload(&payload).unwrap();
428
429 let expected_data = CoinbaseData {
430 blue_score: 29954742,
431 subsidy: 31112698372,
432 miner_data: MinerData {
433 script_public_key: ScriptPublicKey::new(
434 0,
435 scriptvec![
436 32, 43, 50, 68, 63, 247, 64, 1, 33, 87, 113, 109, 129, 33, 109, 9, 174, 188, 57, 229, 73, 60, 147, 167, 24,
437 29, 146, 203, 117, 108, 2, 197, 96, 172,
438 ],
439 ),
440 extra_data: &[48u8, 46, 49, 50, 46, 56, 47] as &[u8],
441 },
442 };
443 assert_eq!(expected_data, deserialized_data);
444 }
445
446 #[test]
447 fn modify_payload_test() {
448 let cbm = create_manager(&MAINNET_PARAMS);
449
450 let script_data = [33u8, 255];
451 let extra_data = [2u8, 3, 23, 98];
452 let data = CoinbaseData {
453 blue_score: 56345,
454 subsidy: 44000000000,
455 miner_data: MinerData {
456 script_public_key: ScriptPublicKey::new(0, ScriptVec::from_slice(&script_data)),
457 extra_data: &extra_data,
458 },
459 };
460
461 let data2 = CoinbaseData {
462 blue_score: data.blue_score,
463 subsidy: data.subsidy,
464 miner_data: MinerData {
465 script_public_key: ScriptPublicKey::new(0, ScriptVec::from_slice(&[33u8, 255, 33])),
467 extra_data: &[2u8, 3, 23, 98, 34, 34] as &[u8],
468 },
469 };
470
471 let mut payload = cbm.serialize_coinbase_payload(&data).unwrap();
472 payload = cbm.modify_coinbase_payload(payload, &data2.miner_data).unwrap(); let deserialized_data = cbm.deserialize_coinbase_payload(&payload).unwrap();
474
475 assert_eq!(data2, deserialized_data);
476 }
477
478 fn create_manager(params: &Params) -> CoinbaseManager {
479 CoinbaseManager::new(
480 params.coinbase_payload_script_public_key_max_len,
481 params.max_coinbase_payload_len,
482 params.deflationary_phase_daa_score,
483 params.pre_deflationary_phase_base_subsidy,
484 params.target_time_per_block,
485 )
486 }
487
488 fn create_legacy_manager() -> CoinbaseManager {
490 CoinbaseManager::new(150, 204, 15778800 - 259200, 50000000000, 1000)
491 }
492}