saito_core/core/consensus/
block.rs

1use ahash::AHashMap;
2use log::{debug, error, info, trace, warn};
3use num_derive::FromPrimitive;
4use num_traits::Zero;
5use rayon::prelude::*;
6use serde::{Deserialize, Serialize};
7use std::convert::TryInto;
8use std::fmt::{Display, Formatter};
9use std::io::{Error, ErrorKind};
10use std::ops::Rem;
11use std::{i128, mem};
12
13use crate::core::consensus::blockchain::Blockchain;
14use crate::core::consensus::burnfee::BurnFee;
15use crate::core::consensus::golden_ticket::GoldenTicket;
16use crate::core::consensus::hop::HOP_SIZE;
17use crate::core::consensus::merkle::MerkleTree;
18use crate::core::consensus::slip::{Slip, SlipType, SLIP_SIZE};
19use crate::core::consensus::transaction::{Transaction, TransactionType, TRANSACTION_SIZE};
20use crate::core::defs::{
21    BlockId, Currency, PeerIndex, PrintForLog, SaitoHash, SaitoPrivateKey, SaitoPublicKey,
22    SaitoSignature, SaitoUTXOSetKey, Timestamp, UtxoSet, BLOCK_FILE_EXTENSION,
23};
24use crate::core::io::storage::Storage;
25use crate::core::util::configuration::Configuration;
26use crate::core::util::crypto::{hash, sign, verify_signature};
27use crate::iterate;
28
29pub const BLOCK_HEADER_SIZE: usize = 389;
30
31//
32// ConsensusValues is an object that is generated that contains all of the
33// variables that a block SHOULD contain given its position in the chain
34// and its set of transactions.
35//
36// This object is returned by the functino generate_consensus_values() and
37// used to populate the variables in the block. It is also used to validate
38// the information in blocks. We strive to keep the names of the variables
39// in this object consistent with the names of the variables in the blocks
40// themselves so it is easy to know which values are used to check which
41// values in the block.
42//
43#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
44pub struct ConsensusValues {
45    // expected transaction containing outbound payments
46    pub fee_transaction: Option<Transaction>,
47
48    // number of staking transactions if exist
49    pub st_num: u8,
50    // index of staking transaction if exists
51    pub st_index: Option<usize>,
52    // number of issuance transactions if exists
53    pub it_num: u8,
54    // index of issuance transactions if exists
55    pub it_index: Option<usize>,
56    // number of FEE in transactions if exists
57    pub ft_num: u8,
58    // index of FEE in transactions if exists
59    pub ft_index: Option<usize>,
60    // number of GT in transactions if exists
61    pub gt_num: u8,
62    // index of GT in transactions if exists
63    pub gt_index: Option<usize>,
64
65    // total fees -- new and atr transactions
66    pub total_fees: Currency,
67    // total fees -- only new transactions
68    pub total_fees_new: Currency,
69    // total fees -- only atr transactions
70    pub total_fees_atr: Currency,
71    // total fees -- only fees from txs in block
72    pub total_fees_cumulative: Currency,
73
74    // smoothed avg fees -- new and atr transactions
75    pub avg_total_fees: Currency,
76    // smoothed avg fees -- only new transactions
77    pub avg_total_fees_new: Currency,
78    // smoothed avg fees -- only atr transactions
79    pub avg_total_fees_atr: Currency,
80
81    // total size in bytes
82    pub total_bytes_new: u64,
83
84    // total amount paid to routers
85    pub total_payout_routing: Currency,
86    // total amount paid to routers
87    pub total_payout_mining: Currency,
88    // total amount paid to treasury
89    pub total_payout_treasury: Currency,
90    // total amount paid to graveyard
91    pub total_payout_graveyard: Currency,
92    // total amount paid to atr/utxo
93    pub total_payout_atr: Currency,
94
95    // smoothed avg routing payout
96    pub avg_payout_routing: Currency,
97    // smoothed avg mining payout
98    pub avg_payout_mining: Currency,
99    // smoothed avg treasury payout
100    pub avg_payout_treasury: Currency,
101    // smoothed avg graveyard payout
102    pub avg_payout_graveyard: Currency,
103    // smoothed avg atr payout
104    pub avg_payout_atr: Currency,
105
106    // smoothed avg fee per byte (last epoch)
107    pub avg_fee_per_byte: Currency,
108
109    // avg byte fees paid by all txs (single block)
110    pub fee_per_byte: Currency,
111
112    // expected burnfee
113    pub burnfee: Currency,
114    // expected difficulty
115    pub difficulty: u64,
116
117    // number of rebroadcast slips
118    pub total_rebroadcast_slips: u64,
119    // total inputs rebroadcast
120    pub total_rebroadcast_nolan: Currency,
121    // rebroadcast txs
122    pub rebroadcasts: Vec<Transaction>,
123    // all ATR txs hashed together
124    pub rebroadcast_hash: [u8; 32],
125    // average of SAITO rebroadcast (inputs) each block
126    pub avg_nolan_rebroadcast_per_block: Currency,
127
128    pub total_rebroadcast_fees_nolan: Currency,
129
130    pub total_rebroadcast_staking_payouts_nolan: Currency,
131
132    pub total_fees_paid_by_nonrebroadcast_atr_transactions: Currency,
133
134    pub expected_difficulty: u64,
135}
136
137impl Display for ConsensusValues {
138    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
139        writeln!(
140            f,
141            "ConsensusValues {{ fee_transaction: {:?}, st_num: {}, st_index: {:?}, it_num: {}, it_index: {:?}, ft_num: {}, ft_index: {:?}, gt_num: {}, gt_index: {:?}, total_fees: {}, total_fees_new: {}, total_fees_atr: {}, total_fees_cumulative: {}, avg_total_fees: {}, avg_total_fees_new: {}, avg_total_fees_atr: {}, total_bytes_new: {}, total_payout_routing: {}, total_payout_mining: {}, total_payout_treasury: {}, total_payout_graveyard: {}, total_payout_atr: {}, avg_payout_routing: {}, avg_payout_mining: {}, avg_payout_treasury: {}, avg_payout_graveyard: {}, avg_payout_atr: {}, avg_fee_per_byte: {}, fee_per_byte: {}, burnfee: {}, difficulty: {}, rebroadcasts: {:?}, total_rebroadcast_slips: {}, total_rebroadcast_nolan: {}, rebroadcast_hash: {:?}, avg_nolan_rebroadcast_per_block: {}, total_rebroadcast_fees_nolan: {}, total_rebroadcast_staking_payouts_nolan: {}, total_fees_paid_by_nonrebroadcast_atr_transactions: {}, expected_difficulty: {} }}",
142            self.fee_transaction,
143            self.st_num,
144            self.st_index,
145            self.it_num,
146            self.it_index,
147            self.ft_num,
148            self.ft_index,
149            self.gt_num,
150            self.gt_index,
151            self.total_fees,
152            self.total_fees_new,
153            self.total_fees_atr,
154            self.total_fees_cumulative,
155            self.avg_total_fees,
156            self.avg_total_fees_new,
157            self.avg_total_fees_atr,
158            self.total_bytes_new,
159            self.total_payout_routing,
160            self.total_payout_mining,
161            self.total_payout_treasury,
162            self.total_payout_graveyard,
163            self.total_payout_atr,
164            self.avg_payout_routing,
165            self.avg_payout_mining,
166            self.avg_payout_treasury,
167            self.avg_payout_graveyard,
168            self.avg_payout_atr,
169            self.avg_fee_per_byte,
170            self.fee_per_byte,
171            self.burnfee,
172            self.difficulty,
173            self.rebroadcasts.len(),
174            self.total_rebroadcast_slips,
175            self.total_rebroadcast_nolan,
176            self.rebroadcast_hash.to_hex(),
177            self.avg_nolan_rebroadcast_per_block,
178            self.total_rebroadcast_fees_nolan,
179            self.total_rebroadcast_staking_payouts_nolan,
180            self.total_fees_paid_by_nonrebroadcast_atr_transactions,
181            self.expected_difficulty)
182    }
183}
184
185impl ConsensusValues {
186    #[allow(clippy::too_many_arguments)]
187    pub fn new() -> ConsensusValues {
188        ConsensusValues {
189            fee_transaction: None,
190
191            st_num: 0,
192            st_index: None,
193            it_num: 0,
194            it_index: None,
195            ft_num: 0,
196            ft_index: None,
197            gt_num: 0,
198            gt_index: None,
199
200            total_fees: 5000,
201            total_fees_new: 0,
202            total_fees_atr: 0,
203            total_fees_cumulative: 0,
204
205            avg_total_fees: 0,
206            avg_total_fees_new: 0,
207            avg_total_fees_atr: 0,
208
209            total_bytes_new: 0,
210
211            total_payout_routing: 0,
212            total_payout_mining: 0,
213            total_payout_treasury: 0,
214            total_payout_graveyard: 0,
215            total_payout_atr: 0,
216
217            avg_payout_routing: 0,
218            avg_payout_mining: 0,
219            avg_payout_treasury: 0,
220            avg_payout_graveyard: 0,
221            avg_payout_atr: 0,
222
223            avg_fee_per_byte: 0,
224            fee_per_byte: 0,
225
226            burnfee: 1,
227            difficulty: 1,
228
229            rebroadcasts: vec![],
230            total_rebroadcast_slips: 0,
231            total_rebroadcast_nolan: 0,
232            rebroadcast_hash: [0; 32],
233            avg_nolan_rebroadcast_per_block: 0,
234
235            total_rebroadcast_fees_nolan: 0,
236
237            total_rebroadcast_staking_payouts_nolan: 0,
238
239            total_fees_paid_by_nonrebroadcast_atr_transactions: 0,
240
241            expected_difficulty: 0,
242        }
243    }
244}
245
246impl Default for ConsensusValues {
247    fn default() -> ConsensusValues {
248        ConsensusValues {
249            fee_transaction: None,
250
251            st_num: 0,
252            st_index: None,
253            it_num: 0,
254            it_index: None,
255            ft_num: 0,
256            ft_index: None,
257            gt_num: 0,
258            gt_index: None,
259
260            total_fees: 0,
261            total_fees_new: 0,
262            total_fees_atr: 0,
263            total_fees_cumulative: 0,
264
265            avg_total_fees: 0,
266            avg_total_fees_new: 0,
267            avg_total_fees_atr: 0,
268
269            total_bytes_new: 0,
270
271            total_payout_routing: 0,
272            total_payout_mining: 0,
273            total_payout_treasury: 0,
274            total_payout_graveyard: 0,
275            total_payout_atr: 0,
276
277            avg_payout_routing: 0,
278            avg_payout_mining: 0,
279            avg_payout_treasury: 0,
280            avg_payout_graveyard: 0,
281            avg_payout_atr: 0,
282
283            avg_fee_per_byte: 0,
284            fee_per_byte: 0,
285
286            burnfee: 1,
287            difficulty: 1,
288
289            rebroadcasts: vec![],
290            total_rebroadcast_slips: 0,
291            total_rebroadcast_nolan: 0,
292            rebroadcast_hash: [0; 32],
293            avg_nolan_rebroadcast_per_block: 0,
294            total_rebroadcast_fees_nolan: 0,
295
296            total_rebroadcast_staking_payouts_nolan: 0,
297
298            total_fees_paid_by_nonrebroadcast_atr_transactions: 0,
299
300            expected_difficulty: 0,
301        }
302    }
303}
304
305///
306/// BlockType is a human-readable indicator of the state of the block
307/// with particular attention to its state of pruning and the amount of
308/// data that is available. It is used by some functions to fetch blocks
309/// that require certain types of data, such as the full set of transactions
310/// or the UTXOSet
311///
312/// Hash - a ghost block sent to lite-clients primarily for SPV mode
313/// Header - the header of the block without transaction data
314/// Full - the full block including transactions and signatures
315///
316#[derive(Serialize, Deserialize, Debug, Copy, PartialEq, Clone, FromPrimitive)]
317pub enum BlockType {
318    Ghost = 0,
319    Header = 1,
320    Pruned = 2,
321    Full = 3,
322}
323
324#[serde_with::serde_as]
325#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
326pub struct Block {
327    /// Consensus Level Variables
328    ///
329    /// these are the variables that are serialized into the block header
330    /// and distributed with every block. validating a block requires
331    /// confirming that all of these values are correct given the content
332    /// in the block itself.
333    ///
334    pub id: BlockId,
335    pub timestamp: Timestamp,
336    pub previous_block_hash: [u8; 32],
337    #[serde_as(as = "[_; 33]")]
338    pub creator: [u8; 33],
339    pub merkle_root: [u8; 32],
340    #[serde_as(as = "[_; 64]")]
341    pub signature: [u8; 64],
342    pub graveyard: Currency,
343    pub treasury: Currency,
344
345    pub total_fees: Currency,
346    pub total_fees_new: Currency,
347    pub total_fees_atr: Currency,
348    pub total_fees_cumulative: Currency,
349    pub avg_total_fees: Currency,
350    pub avg_total_fees_new: Currency,
351    pub avg_total_fees_atr: Currency,
352    pub total_payout_routing: Currency,
353    pub total_payout_mining: Currency,
354    pub total_payout_treasury: Currency,
355    pub total_payout_graveyard: Currency,
356    pub total_payout_atr: Currency,
357    pub avg_payout_routing: Currency,
358    pub avg_payout_mining: Currency,
359    pub avg_payout_treasury: Currency,
360    pub avg_payout_graveyard: Currency,
361    pub avg_payout_atr: Currency,
362    pub avg_fee_per_byte: Currency,
363    pub fee_per_byte: Currency,
364    pub avg_nolan_rebroadcast_per_block: Currency,
365    pub burnfee: Currency,
366    pub difficulty: u64,
367    pub previous_block_unpaid: Currency,
368
369    /// Transactions
370    ///
371    /// these are all of the transactions that are found a full-block.
372    /// lite-blocks may only contain subsets of these transactions, which
373    /// can be validated independently.
374    ///
375    pub transactions: Vec<Transaction>,
376
377    /// Non-Consensus Values
378    ///
379    /// these values are needed when creating or validating a block but are
380    /// generated from the block-data and are not included in the block-header
381    /// and must be created by running block.generate() which fills in most
382    /// of these values.
383    ///
384    /// the pre_hash is the hash created from all of the contents of this
385    /// block. it is then hashed with the previous_block_hash (in header)
386    /// to generate the unique hash for this block. this hash is not incl.
387    /// in the consensus variables as it can be independently generated.
388    ///
389    pub pre_hash: SaitoHash,
390    /// hash of block, combines pre_hash and previous_block_hash
391    pub hash: SaitoHash,
392
393    /// total routing work in block for block creator
394    pub total_work: Currency,
395    /// is block on longest chain
396    pub in_longest_chain: bool,
397    // has golden ticket
398    pub has_golden_ticket: bool,
399    // has issuance transaction
400    pub has_issuance_transaction: bool,
401    // issuance transaction index
402    pub issuance_transaction_index: u64,
403    // has fee transaction
404    pub has_fee_transaction: bool,
405    pub has_staking_transaction: bool,
406    // golden ticket index
407    pub golden_ticket_index: u64,
408    // fee transaction index
409    pub fee_transaction_index: u64,
410    // number of rebroadcast slips
411    pub total_rebroadcast_slips: u64,
412    // number of rebroadcast txs
413    pub total_rebroadcast_nolan: Currency,
414    // all ATR txs hashed together
415    pub rebroadcast_hash: [u8; 32],
416    // the state of the block w/ pruning etc
417    pub block_type: BlockType,
418    pub cv: ConsensusValues,
419    // vector of staker slips spent this block - used to prevent withdrawals and payouts same block
420    #[serde(skip)]
421    pub slips_spent_this_block: AHashMap<SaitoUTXOSetKey, u64>,
422    #[serde(skip)]
423    pub created_hashmap_of_slips_spent_this_block: bool,
424    #[serde(skip)]
425    pub routed_from_peer: Option<PeerIndex>,
426    #[serde(skip)]
427    pub transaction_map: AHashMap<SaitoPublicKey, bool>,
428    #[serde(skip)]
429    pub force_loaded: bool,
430    // used for checking, before pruning txs from block on downgrade
431    pub safe_to_prune_transactions: bool,
432    /// this block has a checkpoint. therefore we cannot reorg past this block.
433    pub has_checkpoint: bool,
434}
435
436impl Display for Block {
437    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
438        writeln!(
439            f,
440            "Block {{ id: {}, timestamp: {}, previous_block_hash: {:?}, creator: {:?}, merkle_root: {:?}, signature: {:?}, graveyard: {}, treasury: {}, total_fees: {}, total_fees_new: {}, total_fees_atr: {}, avg_total_fees: {}, avg_total_fees_new: {}, avg_total_fees_atr: {}, total_payout_routing: {}, total_payout_mining: {}, total_payout_treasury: {}, total_payout_graveyard: {}, total_payout_atr: {}, avg_payout_routing: {}, avg_payout_mining: {}, avg_payout_treasury: {}, avg_payout_graveyard: {}, avg_payout_atr: {}, avg_fee_per_byte: {}, fee_per_byte: {}, avg_nolan_rebroadcast_per_block: {}, burnfee: {}, difficulty: {}, previous_block_unpaid: {}, hash: {:?}, total_work: {}, in_longest_chain: {}, has_golden_ticket: {}, has_issuance_transaction: {}, issuance_transaction_index: {}, has_fee_transaction: {}, has_staking_transaction: {}, golden_ticket_index: {}, fee_transaction_index: {}, total_rebroadcast_slips: {}, total_rebroadcast_nolan: {}, rebroadcast_hash: {}, block_type: {:?}, cv: {}, routed_from_peer: {:?} ",
441            self.id,
442            self.timestamp,
443            self.previous_block_hash.to_hex(),
444            self.creator.to_base58(),
445            self.merkle_root.to_hex(),
446            self.signature.to_hex(),
447            self.graveyard,
448            self.treasury,
449            self.total_fees,
450            self.total_fees_new,
451            self.total_fees_atr,
452            self.avg_total_fees,
453            self.avg_total_fees_new,
454            self.avg_total_fees_atr,
455            self.total_payout_routing,
456            self.total_payout_mining,
457            self.total_payout_treasury,
458            self.total_payout_graveyard,
459            self.total_payout_atr,
460            self.avg_payout_routing,
461            self.avg_payout_mining,
462            self.avg_payout_treasury,
463            self.avg_payout_graveyard,
464            self.avg_payout_atr,
465            self.avg_fee_per_byte,
466            self.fee_per_byte,
467            self.avg_nolan_rebroadcast_per_block,
468            self.burnfee,
469            self.difficulty,
470            self.previous_block_unpaid,
471            self.hash.to_hex(),
472            self.total_work,
473            self.in_longest_chain,
474            self.has_golden_ticket,
475            self.has_issuance_transaction,
476            self.issuance_transaction_index,
477            self.has_fee_transaction,
478            self.has_staking_transaction,
479            self.golden_ticket_index,
480            self.fee_transaction_index,
481            self.total_rebroadcast_slips,
482            self.total_rebroadcast_nolan,
483            self.rebroadcast_hash.to_hex(),
484            self.block_type,
485            self.cv,
486            self.routed_from_peer,
487        ).unwrap();
488        // writeln!(f, " transactions : ").unwrap();
489        // for (index, tx) in self.transactions.iter().enumerate() {
490        //     writeln!(f, "tx {} : {}", index, tx).unwrap();
491        // }
492        writeln!(f, "}}")
493    }
494}
495
496impl Block {
497    #[allow(clippy::new_without_default)]
498    pub fn new() -> Block {
499        Block {
500            id: 0,
501            timestamp: 0,
502            previous_block_hash: [0; 32],
503            creator: [0; 33],
504            merkle_root: [0; 32],
505            signature: [0; 64],
506            graveyard: 0,
507            treasury: 0,
508            previous_block_unpaid: 0,
509            total_fees: 0,
510            total_fees_new: 0,
511            total_fees_atr: 0,
512            total_fees_cumulative: 0,
513            avg_total_fees: 0,
514            avg_total_fees_new: 0,
515            avg_total_fees_atr: 0,
516            total_payout_routing: 0,
517            total_payout_mining: 0,
518            total_payout_treasury: 0,
519            total_payout_graveyard: 0,
520            total_payout_atr: 0,
521            avg_payout_routing: 0,
522            avg_payout_mining: 0,
523            avg_payout_treasury: 0,
524            avg_payout_graveyard: 0,
525            avg_payout_atr: 0,
526            avg_fee_per_byte: 0,
527            fee_per_byte: 0,
528            burnfee: 0,
529            difficulty: 0,
530            avg_nolan_rebroadcast_per_block: 0,
531
532            transactions: vec![],
533            pre_hash: [0; 32],
534            hash: [0; 32],
535            total_work: 0,
536            in_longest_chain: false,
537            has_golden_ticket: false,
538            has_fee_transaction: false,
539            has_staking_transaction: false,
540            has_issuance_transaction: false,
541            issuance_transaction_index: 0,
542            golden_ticket_index: 0,
543            fee_transaction_index: 0,
544            total_rebroadcast_slips: 0,
545            total_rebroadcast_nolan: 0,
546            // must be initialized zeroed-out for proper hashing
547            rebroadcast_hash: [0; 32],
548            //filename: String::new(),
549            block_type: BlockType::Full,
550            // hashmap of all SaitoUTXOSetKeys of the slips in the block
551            slips_spent_this_block: AHashMap::new(),
552            created_hashmap_of_slips_spent_this_block: false,
553            routed_from_peer: None,
554            transaction_map: Default::default(),
555            cv: ConsensusValues::default(),
556            force_loaded: false,
557            safe_to_prune_transactions: false,
558            has_checkpoint: false,
559        }
560    }
561
562    pub fn add_transaction(&mut self, tx: Transaction) {
563        self.transactions.push(tx);
564    }
565
566    //
567    // returns valid block
568    //
569    pub async fn create(
570        transactions: &mut AHashMap<SaitoSignature, Transaction>,
571        previous_block_hash: SaitoHash,
572        blockchain: &Blockchain,
573        current_timestamp: Timestamp,
574        public_key: &SaitoPublicKey,
575        private_key: &SaitoPrivateKey,
576        golden_ticket: Option<Transaction>,
577        configs: &(dyn Configuration + Send + Sync),
578        storage: &Storage,
579    ) -> Result<Block, Error> {
580        debug!(
581            "Block::create : previous block hash : {:?}",
582            previous_block_hash.to_hex()
583        );
584
585        let mut previous_block_id = 0;
586        let mut _previous_block_timestamp = 0;
587        let mut previous_block_graveyard = 0;
588        let mut previous_block_treasury = 0;
589        let mut previous_block_total_fees = 0;
590        let mut _previous_block_avg_total_fees = 0;
591        let mut _previous_block_avg_total_fees_new = 0;
592        let mut _previous_block_avg_total_fees_atr = 0;
593        let mut _previous_block_avg_payout_routing = 0;
594        let mut _previous_block_avg_payout_mining = 0;
595        let mut _previous_block_avg_payout_treasury = 0;
596        let mut _previous_block_avg_payout_graveyard = 0;
597        let mut _previous_block_avg_payout_atr = 0;
598        let mut _previous_block_avg_fee_per_byte = 0;
599
600        if let Some(previous_block) = blockchain.blocks.get(&previous_block_hash) {
601            previous_block_id = previous_block.id;
602            previous_block_total_fees = previous_block.total_fees;
603            _previous_block_timestamp = previous_block.timestamp;
604            previous_block_graveyard = previous_block.graveyard;
605            previous_block_treasury = previous_block.treasury;
606            _previous_block_avg_total_fees = previous_block.avg_total_fees;
607            _previous_block_avg_total_fees_new = previous_block.avg_total_fees_new;
608            _previous_block_avg_total_fees_atr = previous_block.avg_total_fees_atr;
609            _previous_block_avg_payout_routing = previous_block.avg_payout_routing;
610            _previous_block_avg_payout_mining = previous_block.avg_payout_mining;
611            _previous_block_avg_payout_treasury = previous_block.avg_payout_treasury;
612            _previous_block_avg_payout_graveyard = previous_block.avg_payout_graveyard;
613            _previous_block_avg_payout_atr = previous_block.avg_payout_atr;
614            _previous_block_avg_fee_per_byte = previous_block.avg_fee_per_byte;
615        }
616
617        //
618        // create block
619        //
620        let mut block = Block::new();
621
622        //
623        // fill in default values
624        //
625        block.id = previous_block_id + 1;
626        block.previous_block_hash = previous_block_hash;
627        block.timestamp = current_timestamp;
628        block.creator = *public_key;
629
630        //
631        // previous block unpaid
632        //
633        if golden_ticket.is_some() {
634            block.previous_block_unpaid = 0;
635        } else {
636            block.previous_block_unpaid = previous_block_total_fees;
637        }
638
639        //
640        // golden ticket
641        //
642        if let Some(gt) = golden_ticket {
643            block.transactions.push(gt);
644        }
645
646        //
647        // normal transactions
648        //
649        block.transactions.reserve(transactions.len());
650        let iter = transactions.drain().map(|(_, tx)| tx);
651        block.transactions.extend(iter);
652        transactions.clear();
653
654        //
655        // consensus values
656        //
657        let mut cv: ConsensusValues = block
658            .generate_consensus_values(blockchain, storage, configs)
659            .await;
660        block.cv = cv.clone();
661
662        //
663        // total fees new
664        //
665        block.total_fees_new = cv.total_fees_new;
666
667        //
668        // total fees atr
669        //
670        block.total_fees_atr = cv.total_fees_atr;
671
672        //
673        // total fees cumulative
674        //
675        block.total_fees_cumulative = cv.total_fees_cumulative;
676
677        //
678        // total fees
679        //
680        block.total_fees = block.total_fees_new + block.total_fees_atr;
681
682        //
683        // avg total fees
684        //
685        block.avg_total_fees = cv.avg_total_fees;
686
687        //
688        // avg total fees new
689        //
690        block.avg_total_fees_new = cv.avg_total_fees_new;
691
692        //
693        // avg total fees atr
694        //
695        block.avg_total_fees_atr = cv.avg_total_fees_atr;
696
697        //
698        // payout routing
699        //
700        block.total_payout_routing = cv.total_payout_routing;
701
702        //
703        // avg payout mining
704        //
705        block.total_payout_mining = cv.total_payout_mining;
706
707        //
708        // avg payout treasury
709        //
710        block.total_payout_treasury = cv.total_payout_treasury;
711
712        //
713        // avg payout graveyard
714        //
715        block.total_payout_graveyard = cv.total_payout_graveyard;
716
717        //
718        // avg payout atr
719        //
720        block.total_payout_atr = cv.total_payout_atr;
721
722        //
723        // avg payout routing
724        //
725        block.avg_payout_routing = cv.avg_payout_routing;
726
727        //
728        // avg payout mining
729        //
730        block.avg_payout_mining = cv.avg_payout_mining;
731
732        //
733        // avg payout treasury
734        //
735        block.avg_payout_treasury = cv.avg_payout_treasury;
736
737        //
738        // avg payout graveyard
739        //
740        block.avg_payout_graveyard = cv.avg_payout_graveyard;
741
742        //
743        // avg payout atr
744        //
745        block.avg_payout_atr = cv.avg_payout_atr;
746
747        //
748        // fee per byte
749        //
750        block.avg_fee_per_byte = cv.avg_fee_per_byte;
751
752        //
753        // fee per byte
754        //
755        block.fee_per_byte = cv.fee_per_byte;
756
757        //
758        // nolan rebroadcast per block
759        //
760        block.avg_nolan_rebroadcast_per_block = cv.avg_nolan_rebroadcast_per_block;
761
762        //
763        // burn fee
764        //
765        block.burnfee = cv.burnfee;
766
767        //
768        // difficulty
769        //
770        block.difficulty = cv.difficulty;
771
772        //
773        // treasury
774        //
775        block.treasury = previous_block_treasury + cv.total_payout_treasury - cv.total_payout_atr;
776
777        //
778        // graveyard
779        //
780        block.graveyard = previous_block_graveyard + cv.total_payout_graveyard;
781
782        //
783        // ATR transactions
784        //
785        let rlen = cv.rebroadcasts.len();
786
787        //
788        // TODO - can we remove this section and skip right ahead to appending the atr txs? we run
789        // generate() down below at the end of this function, so why are we generating the hash and
790        // the merkle-root here?
791        //
792        // and why do we run it below AFTER we sign the block? if we can eliminate the need for this
793        // loop through the transaction set and the redundant creation of the tx hashes and the
794        // block merkle-root that would be an improvement to the efficiency of block creation.
795        //
796        let _tx_hashes_generated = cv.rebroadcasts[0..rlen]
797            .iter_mut()
798            .enumerate()
799            .all(|(index, tx)| tx.generate(public_key, index as u64, block.id));
800
801        //
802        // add ATR transactions
803        //
804        if rlen > 0 {
805            block.transactions.append(&mut cv.rebroadcasts);
806        }
807
808        //
809        // fee transaction
810        //
811        if cv.fee_transaction.is_some() {
812            let mut fee_tx = cv.fee_transaction.unwrap();
813            let hash_for_signature: SaitoHash = hash(&fee_tx.serialize_for_signature());
814            fee_tx.hash_for_signature = Some(hash_for_signature);
815            fee_tx.sign(private_key);
816            block.add_transaction(fee_tx);
817        }
818
819        //
820        // create hashmap of slips_spent_this_block (used to avoid doublespends)
821        //
822        if !block.created_hashmap_of_slips_spent_this_block {
823            debug!(
824                "creating hashmap of slips spent this block : {}...",
825                block.id
826            );
827
828            for transaction in &block.transactions {
829                if transaction.transaction_type != TransactionType::Fee {
830                    for input in transaction.from.iter() {
831                        if input.amount == 0 {
832                            continue;
833                        }
834
835                        let value = block
836                            .slips_spent_this_block
837                            .entry(input.get_utxoset_key())
838                            .and_modify(|e| *e += 1)
839                            .or_insert(1);
840
841                        if *value > 1 && input.amount > 0 {
842                            warn!(
843                                "double-spend detected in block {} : {} in block.create()",
844                                block.id, input
845                            );
846                            return Err(Error::new(
847                                ErrorKind::InvalidData,
848                                "double-spend detected",
849                            ));
850                        }
851                    }
852                }
853                block.created_hashmap_of_slips_spent_this_block = true;
854            }
855        }
856        block.created_hashmap_of_slips_spent_this_block = true;
857
858        //
859        // generate merkle root
860        //
861        block.merkle_root = block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
862
863        //
864        // the "pre-hash" is a hash value created from all of the consensus-data and is
865        // signed by the block creator to generate the signature. the signature is then
866        // part of the block data that is used to generate the final block hash.
867        //
868        block.generate_pre_hash();
869        block.sign(private_key);
870
871        //
872        // finally run generate()
873        //
874        block.generate()?;
875
876        Ok(block)
877    }
878
879    //
880    // runs when block deleted
881    //
882    pub async fn delete(&self, utxoset: &mut UtxoSet) -> bool {
883        for tx in &self.transactions {
884            tx.delete(utxoset).await;
885        }
886        true
887    }
888
889    /// Deserialize from bytes to a Block.
890    /// [len of transactions - 4 bytes - u32]
891    /// [id - 8 bytes - u64]
892    /// [timestamp - 8 bytes - u64]
893    /// [previous_block_hash - 32 bytes - SHA 256 hash]
894    /// [creator - 33 bytes - Secp25k1 pubkey compact format]
895    /// [merkle_root - 32 bytes - SHA 256 hash
896    /// [signature - 64 bytes - Secp25k1 sig]
897    /// [graveyard - 8 bytes - u64]
898    /// [treasury - 8 bytes - u64]
899    /// [burnfee - 8 bytes - u64]
900    /// [difficulty - 8 bytes - u64]
901    /// [avg_total_fees - 8 bytes - u64]
902    /// [avg_fee_per_byte - 8 bytes - u64]
903    /// [avg_nolan_rebroadcast_per_block - 8 bytes - u64]
904    /// [previous_block_unpaid - 8 bytes - u64]
905
906    /// [avg_total_fees - 8 bytes - u64]
907    /// [avg_total_fees_new - 8 bytes - u64]
908    /// [avg_total_fees_atr - 8 bytes - u64]
909    /// [avg_payout_routing - 8 bytes - u64]
910    /// [avg_payout_mining - 8 bytes - u64]
911    /// [avg_payout_treasury - 8 bytes - u64]
912    /// [avg_payout_graveyard - 8 bytes - u64]
913    /// [avg_payout_atr - 8 bytes - u64]
914    /// [total_payout_routing - 8 bytes - u64]
915    /// [total_payout_mining - 8 bytes - u64]
916    /// [total_payout_treasury - 8 bytes - u64]
917    /// [total_payout_graveyard - 8 bytes - u64]
918    /// [total_payout_atr - 8 bytes - u64]
919    /// [total_fees - 8 bytes - u64]
920    /// [total_fees_new - 8 bytes - u64]
921    /// [total_fees_atr - 8 bytes - u64]
922    /// [fee_per_byte - 8 bytes - u64]
923    /// [total_fees_cumulative - 8 bytes - u64]
924
925    /// [transaction][transaction][transaction]...
926    pub fn deserialize_from_net(bytes: &[u8]) -> Result<Block, Error> {
927        if bytes.len() < BLOCK_HEADER_SIZE {
928            warn!(
929                "block buffer is smaller than header length. length : {:?}",
930                bytes.len()
931            );
932            return Err(Error::from(ErrorKind::InvalidData));
933        }
934        let transactions_len: u32 = u32::from_be_bytes(
935            bytes[0..4]
936                .try_into()
937                .or(Err(Error::from(ErrorKind::InvalidData)))?,
938        );
939        let id: u64 = u64::from_be_bytes(
940            bytes[4..12]
941                .try_into()
942                .or(Err(Error::from(ErrorKind::InvalidData)))?,
943        );
944        let timestamp: Timestamp = Timestamp::from_be_bytes(
945            bytes[12..20]
946                .try_into()
947                .or(Err(Error::from(ErrorKind::InvalidData)))?,
948        );
949        let previous_block_hash: SaitoHash = bytes[20..52]
950            .try_into()
951            .or(Err(Error::from(ErrorKind::InvalidData)))?;
952        let creator: SaitoPublicKey = bytes[52..85]
953            .try_into()
954            .or(Err(Error::from(ErrorKind::InvalidData)))?;
955        let merkle_root: SaitoHash = bytes[85..117]
956            .try_into()
957            .or(Err(Error::from(ErrorKind::InvalidData)))?;
958        let signature: SaitoSignature = bytes[117..181]
959            .try_into()
960            .or(Err(Error::from(ErrorKind::InvalidData)))?;
961
962        let graveyard: Currency = Currency::from_be_bytes(bytes[181..189].try_into().unwrap());
963        let treasury: Currency = Currency::from_be_bytes(bytes[189..197].try_into().unwrap());
964        let burnfee: Currency = Currency::from_be_bytes(bytes[197..205].try_into().unwrap());
965        let difficulty: u64 = u64::from_be_bytes(bytes[205..213].try_into().unwrap());
966        let avg_total_fees: Currency = Currency::from_be_bytes(bytes[213..221].try_into().unwrap()); // dupe below
967        let avg_fee_per_byte: Currency =
968            Currency::from_be_bytes(bytes[221..229].try_into().unwrap());
969        let avg_nolan_rebroadcast_per_block: Currency =
970            Currency::from_be_bytes(bytes[229..237].try_into().unwrap());
971        let previous_block_unpaid: Currency =
972            Currency::from_be_bytes(bytes[237..245].try_into().unwrap());
973        let avg_total_fees: Currency = Currency::from_be_bytes(bytes[245..253].try_into().unwrap());
974        let avg_total_fees_new: Currency =
975            Currency::from_be_bytes(bytes[253..261].try_into().unwrap());
976        let avg_total_fees_atr: Currency =
977            Currency::from_be_bytes(bytes[261..269].try_into().unwrap());
978        let avg_payout_routing: Currency =
979            Currency::from_be_bytes(bytes[269..277].try_into().unwrap());
980        let avg_payout_mining: Currency =
981            Currency::from_be_bytes(bytes[277..285].try_into().unwrap());
982        let avg_payout_treasury: Currency =
983            Currency::from_be_bytes(bytes[285..293].try_into().unwrap());
984        let avg_payout_graveyard: Currency =
985            Currency::from_be_bytes(bytes[293..301].try_into().unwrap());
986        let avg_payout_atr: Currency = Currency::from_be_bytes(bytes[301..309].try_into().unwrap());
987        let total_payout_routing: Currency =
988            Currency::from_be_bytes(bytes[309..317].try_into().unwrap());
989        let total_payout_mining: Currency =
990            Currency::from_be_bytes(bytes[317..325].try_into().unwrap());
991        let total_payout_treasury: Currency =
992            Currency::from_be_bytes(bytes[325..333].try_into().unwrap());
993        let total_payout_graveyard: Currency =
994            Currency::from_be_bytes(bytes[333..341].try_into().unwrap());
995        let total_payout_atr: Currency =
996            Currency::from_be_bytes(bytes[341..349].try_into().unwrap());
997        let total_fees: Currency = Currency::from_be_bytes(bytes[349..357].try_into().unwrap());
998        let total_fees_new: Currency = Currency::from_be_bytes(bytes[357..365].try_into().unwrap());
999        let total_fees_atr: Currency = Currency::from_be_bytes(bytes[365..373].try_into().unwrap());
1000        let fee_per_byte: Currency = Currency::from_be_bytes(bytes[373..381].try_into().unwrap());
1001        let total_fees_cumulative: Currency =
1002            Currency::from_be_bytes(bytes[381..389].try_into().unwrap());
1003
1004        let mut transactions = vec![];
1005        let mut start_of_transaction_data = BLOCK_HEADER_SIZE;
1006        for _n in 0..transactions_len {
1007            if bytes.len() < start_of_transaction_data + 16 {
1008                warn!(
1009                    "block buffer is invalid to read transaction metadata. length : {:?}, end_of_tx_data : {:?}",
1010                    bytes.len(),
1011                    start_of_transaction_data+16
1012                );
1013                return Err(Error::from(ErrorKind::InvalidData));
1014            }
1015            let inputs_len: u32 = u32::from_be_bytes(
1016                bytes[start_of_transaction_data..start_of_transaction_data + 4]
1017                    .try_into()
1018                    .or(Err(Error::from(ErrorKind::InvalidData)))?,
1019            );
1020            let outputs_len: u32 = u32::from_be_bytes(
1021                bytes[start_of_transaction_data + 4..start_of_transaction_data + 8]
1022                    .try_into()
1023                    .or(Err(Error::from(ErrorKind::InvalidData)))?,
1024            );
1025            let message_len: usize = u32::from_be_bytes(
1026                bytes[start_of_transaction_data + 8..start_of_transaction_data + 12]
1027                    .try_into()
1028                    .or(Err(Error::from(ErrorKind::InvalidData)))?,
1029            ) as usize;
1030            let path_len: usize = u32::from_be_bytes(
1031                bytes[start_of_transaction_data + 12..start_of_transaction_data + 16]
1032                    .try_into()
1033                    .or(Err(Error::from(ErrorKind::InvalidData)))?,
1034            ) as usize;
1035            let total_len = inputs_len
1036                .checked_add(outputs_len)
1037                .ok_or(Error::from(ErrorKind::InvalidData))?;
1038            let end_of_transaction_data = start_of_transaction_data
1039                + TRANSACTION_SIZE
1040                + (total_len as usize * SLIP_SIZE)
1041                + message_len
1042                + path_len * HOP_SIZE;
1043
1044            if bytes.len() < end_of_transaction_data {
1045                warn!(
1046                    "block buffer is invalid to read transaction data. length : {:?}, end of tx data : {:?}, tx_count : {:?}",
1047                    bytes.len(), end_of_transaction_data, transactions_len
1048                );
1049                return Err(Error::from(ErrorKind::InvalidData));
1050            }
1051            let transaction = Transaction::deserialize_from_net(
1052                &bytes[start_of_transaction_data..end_of_transaction_data].to_vec(),
1053            )?;
1054            transactions.push(transaction);
1055            start_of_transaction_data = end_of_transaction_data;
1056        }
1057
1058        let mut block = Block::new();
1059        block.id = id;
1060        block.timestamp = timestamp;
1061        block.previous_block_hash = previous_block_hash;
1062        block.creator = creator;
1063        block.merkle_root = merkle_root;
1064        block.signature = signature;
1065        block.graveyard = graveyard;
1066        block.treasury = treasury;
1067        block.burnfee = burnfee;
1068        block.difficulty = difficulty;
1069        block.avg_total_fees = avg_total_fees;
1070        block.avg_fee_per_byte = avg_fee_per_byte;
1071        block.avg_nolan_rebroadcast_per_block = avg_nolan_rebroadcast_per_block;
1072        block.previous_block_unpaid = previous_block_unpaid;
1073        block.avg_total_fees = avg_total_fees;
1074        block.avg_total_fees_new = avg_total_fees_new;
1075        block.avg_total_fees_atr = avg_total_fees_atr;
1076        block.avg_payout_routing = avg_payout_routing;
1077        block.avg_payout_mining = avg_payout_mining;
1078        block.avg_payout_treasury = avg_payout_treasury;
1079        block.avg_payout_graveyard = avg_payout_graveyard;
1080        block.avg_payout_atr = avg_payout_atr;
1081        block.total_payout_routing = total_payout_routing;
1082        block.total_payout_mining = total_payout_mining;
1083        block.total_payout_treasury = total_payout_treasury;
1084        block.total_payout_graveyard = total_payout_graveyard;
1085        block.total_payout_atr = total_payout_atr;
1086        block.total_fees = total_fees;
1087        block.total_fees_new = total_fees_new;
1088        block.total_fees_atr = total_fees_atr;
1089        block.fee_per_byte = fee_per_byte;
1090        block.total_fees_cumulative = total_fees_cumulative;
1091
1092        block.transactions = transactions.to_vec();
1093
1094        // trace!("block.deserialize tx length = {:?}", transactions_len);
1095        if transactions_len == 0 && !(block.id == 1 && previous_block_hash == [0; 32]) {
1096            block.block_type = BlockType::Header;
1097        }
1098
1099        Ok(block)
1100    }
1101
1102    /// downgrade block
1103    pub async fn downgrade_block_to_block_type(
1104        &mut self,
1105        block_type: BlockType,
1106        _is_spv: bool,
1107    ) -> bool {
1108        debug!(
1109            "downgrading BLOCK_ID {:?} to type : {:?}",
1110            self.id, block_type
1111        );
1112
1113        if self.block_type == block_type {
1114            return true;
1115        }
1116
1117        // if the block type needed is full and we are not,
1118        // load the block if it exists on disk.
1119        //
1120        if block_type == BlockType::Pruned {
1121            self.transactions = vec![];
1122            self.block_type = BlockType::Pruned;
1123            return true;
1124        }
1125
1126        false
1127    }
1128
1129    //
1130    // find winning router in block path
1131    //
1132    pub fn find_winning_router(&self, random_number: SaitoHash) -> SaitoPublicKey {
1133        let winner_pubkey: SaitoPublicKey;
1134
1135        // find winning nolan
1136        let x = primitive_types::U256::from_big_endian(&random_number);
1137        // fee calculation should be the same used in block when
1138        // generating the fee transaction.
1139        let y = self.total_fees;
1140
1141        // if there are no fees, payout to burn address
1142        if y == 0 {
1143            winner_pubkey = [0; 33];
1144            return winner_pubkey;
1145        }
1146
1147        let z = primitive_types::U256::from_big_endian(&y.to_be_bytes());
1148        let zy = x.rem(z);
1149        let winning_nolan: Currency = std::cmp::max(zy.low_u64(), 1);
1150
1151        // we may need function-timelock object if we need to recreate
1152        // an ATR transaction to pick the winning routing node.
1153        let winning_tx_placeholder: Transaction;
1154
1155        //
1156        // winning TX contains the winning nolan
1157        //
1158        // either a fee-paying transaction or an ATR transaction
1159        let mut tx: Option<&Transaction> = None;
1160        for transaction in &self.transactions {
1161            if transaction.cumulative_fees >= winning_nolan {
1162                tx = Some(transaction);
1163                break;
1164            }
1165        }
1166
1167        // If no valid transaction is found, payout to burn address and return early
1168        if tx.is_none() {
1169            winner_pubkey = [0; 33];
1170            return winner_pubkey;
1171        }
1172
1173        // Safe to unwrap since we know it's `Some`
1174        let mut winning_tx = tx.unwrap();
1175
1176        //
1177        // if winner is atr, we take inside TX
1178        //
1179
1180        if winning_tx.transaction_type == TransactionType::ATR {
1181            let tmptx = winning_tx.data.to_vec();
1182            winning_tx_placeholder =
1183                Transaction::deserialize_from_net(&tmptx).expect("buffer to be valid");
1184            winning_tx = &winning_tx_placeholder;
1185        } else {
1186            assert_ne!(
1187                winning_tx.cumulative_fees,
1188                Currency::zero(),
1189                "winning tx doesn't have fees {}",
1190                winning_tx
1191            );
1192        }
1193        // hash random number to pick routing node
1194        winner_pubkey = winning_tx.get_winning_routing_node(hash(random_number.as_ref()));
1195        winner_pubkey
1196    }
1197
1198    // generate
1199    //
1200    // this function generates all of the ancillary data needed to process or
1201    // validate blocks. this includes the various hashes and other dynamic
1202    // values that are not provided on the creation of the block object itself
1203    // but must be calculated from information such as the set of transactions
1204    // and the presence / absence of golden tickets, etc.
1205    //
1206    // - transaction_hashmap - used to identify which addresses have txs in block
1207    // - merkle_root - root of tx tree
1208
1209    // we first calculate as much information as we can in parallel before
1210    // sweeping through the transactions to find out what percentage of the
1211    // cumulative block fees they contain.
1212    //
1213    pub fn generate(&mut self) -> Result<(), Error> {
1214        let creator_public_key = &self.creator;
1215
1216        //self.total_rebroadcast_nolan = 0;
1217        self.total_rebroadcast_slips = 0;
1218        self.rebroadcast_hash = [0; 32];
1219
1220        //
1221        // allow transactions to generate themselves
1222        //
1223        let mut tx_index: u64 = 0;
1224        for tx in self.transactions.iter_mut() {
1225            tx.generate(creator_public_key, tx_index, self.id);
1226            if let TransactionType::SPV = tx.transaction_type {
1227                tx_index += tx.txs_replacements as u64;
1228            } else {
1229                tx_index += 1;
1230            }
1231        }
1232
1233        self.generate_transaction_hashmap();
1234
1235        if self.merkle_root == [0; 32] {
1236            self.merkle_root = self.generate_merkle_root(false, false);
1237        }
1238
1239        self.generate_pre_hash();
1240        self.generate_hash();
1241
1242        // trace!(" ... block.prevalid - pst hash:  {:?}", create_timestamp());
1243
1244        // we need to calculate the cumulative figures AFTER the
1245        // original figures.
1246        let mut cumulative_fees = 0;
1247        let mut total_work = 0;
1248
1249        let mut has_golden_ticket = false;
1250        let mut has_fee_transaction = false;
1251
1252        let mut has_issuance_transaction = false;
1253        let mut issuance_transaction_index = 0;
1254        let mut golden_ticket_index = 0;
1255        let mut fee_transaction_index = 0;
1256
1257        // we have to do a single sweep through all of the transactions in
1258        // non-parallel to do things like generate the cumulative order of the
1259        // transactions in the block for things like work and fee calculations
1260        // for the lottery.
1261        //
1262        // we take advantage of the sweep to perform other pre-validation work
1263        // like counting up our ATR transactions and generating the hash
1264        // commitment for all of our rebroadcasts.
1265        for i in 0..self.transactions.len() {
1266            let transaction = &mut self.transactions[i];
1267
1268            cumulative_fees = transaction.generate_cumulative_fees(cumulative_fees);
1269
1270            total_work += transaction.total_work_for_me;
1271
1272            // update slips_spent_this_block so that we have a record of
1273            // how many times input slips are spent in this block. we will
1274            // use this later to ensure there are no duplicates.
1275            //
1276            // we skip the fee transaction as otherwise we have trouble
1277            // validating the staker slips if we have received a block from
1278            // someone else -- i.e. we will think the slip is spent in the
1279            // block when generating the FEE TX to check against the in-block
1280            // fee tx.
1281            if !self.created_hashmap_of_slips_spent_this_block
1282                && transaction.transaction_type != TransactionType::Fee
1283            {
1284                for input in transaction.from.iter() {
1285                    if input.amount == 0 {
1286                        continue;
1287                    }
1288
1289                    let value = self
1290                        .slips_spent_this_block
1291                        .entry(input.get_utxoset_key())
1292                        .and_modify(|e| *e += 1)
1293                        .or_insert(1);
1294                    if *value > 1 && input.amount > 0 {
1295                        warn!(
1296                            "double-spend detected in block {} : {} in block.generate()",
1297                            self.id, input
1298                        );
1299                        return Err(Error::new(ErrorKind::InvalidData, "double-spend detected"));
1300                    }
1301                }
1302                self.created_hashmap_of_slips_spent_this_block = true;
1303            }
1304
1305            // also check the transactions for golden ticket and fees
1306            match transaction.transaction_type {
1307                TransactionType::Issuance => {
1308                    has_issuance_transaction = true;
1309                    issuance_transaction_index = i as u64;
1310                }
1311                TransactionType::Fee => {
1312                    has_fee_transaction = true;
1313                    fee_transaction_index = i as u64;
1314                }
1315                TransactionType::GoldenTicket => {
1316                    has_golden_ticket = true;
1317                    golden_ticket_index = i as u64;
1318                }
1319                TransactionType::ATR => {
1320                    let mut vbytes: Vec<u8> = vec![];
1321                    vbytes.extend(&self.rebroadcast_hash);
1322                    vbytes.extend(&transaction.serialize_for_signature());
1323                    self.rebroadcast_hash = hash(&vbytes);
1324
1325                    for slip in transaction.to.iter() {
1326                        if matches!(slip.slip_type, SlipType::ATR) {
1327                            self.total_rebroadcast_slips += 1;
1328                            // deprecated
1329                            //self.total_rebroadcast_nolan += slip.amount;
1330                        }
1331                    }
1332                }
1333                TransactionType::BlockStake => {
1334                    self.has_staking_transaction = true;
1335                }
1336                _ => {}
1337            };
1338        }
1339        self.has_fee_transaction = has_fee_transaction;
1340        self.has_golden_ticket = has_golden_ticket;
1341        self.has_issuance_transaction = has_issuance_transaction;
1342        self.fee_transaction_index = fee_transaction_index;
1343        self.golden_ticket_index = golden_ticket_index;
1344        self.issuance_transaction_index = issuance_transaction_index;
1345        self.total_work = total_work;
1346
1347        Ok(())
1348    }
1349
1350    pub fn generate_hash(&mut self) -> SaitoHash {
1351        let hash_for_hash = hash(&self.serialize_for_hash());
1352        self.hash = hash_for_hash;
1353        hash_for_hash
1354    }
1355
1356    pub fn generate_merkle_root(&self, is_browser: bool, is_spv: bool) -> SaitoHash {
1357        if self.transactions.is_empty() && (is_browser || is_spv) {
1358            return self.merkle_root;
1359        }
1360
1361        let merkle_root_hash: SaitoHash;
1362        if let Some(tree) = MerkleTree::generate(&self.transactions) {
1363            merkle_root_hash = tree.get_root_hash();
1364        } else {
1365            merkle_root_hash = [0; 32];
1366        }
1367
1368        debug!(
1369            "generated the merkle root, Tx Count: {:?}, root = {:?}",
1370            self.transactions.len(),
1371            merkle_root_hash.to_hex()
1372        );
1373
1374        merkle_root_hash
1375    }
1376
1377    //
1378    // generate_consensus_values examines a block in the context of the blockchain
1379    // in order to determine the dynamic values that need to be inserted into the
1380    // block or validated.
1381    //
1382    pub async fn generate_consensus_values(
1383        &self,
1384        blockchain: &Blockchain,
1385        storage: &Storage,
1386        configs: &(dyn Configuration + Send + Sync),
1387    ) -> ConsensusValues {
1388        //
1389        // we will reference these variables
1390        //
1391        let mut cv = ConsensusValues::new();
1392        let mut previous_block_treasury: Currency = 0;
1393        let mut previous_block_avg_nolan_rebroadcast_per_block: Currency = 0;
1394        let mut previous_block_avg_fee_per_byte: Currency = 0;
1395        let mut previous_block_avg_total_fees: Currency = 0;
1396        let mut previous_block_avg_total_fees_new: Currency = 0;
1397        let mut previous_block_avg_total_fees_atr: Currency = 0;
1398        let mut previous_block_avg_payout_routing: Currency = 0;
1399        let mut previous_block_avg_payout_mining: Currency = 0;
1400        let previous_block_avg_payout_treasury: Currency = 0;
1401        let previous_block_avg_payout_graveyard: Currency = 0;
1402        let previous_block_avg_payout_atr: Currency = 0;
1403        let mut total_number_of_non_fee_transactions = 0;
1404
1405        //
1406        // tx fees and sizes and indices
1407        //
1408        for (index, transaction) in self.transactions.iter().enumerate() {
1409            //
1410            // Normal
1411            // Fee
1412            // GoldenTicket
1413            // ATR
1414            // SPV
1415            // Issuance
1416            // BlockStake
1417            //
1418
1419            if transaction.is_fee_transaction() {
1420                cv.ft_num += 1;
1421                cv.ft_index = Some(index);
1422            } else {
1423                //
1424                // the fee transaction is the last transaction in the block, so we
1425                // count the number of non-fee transactions in order to know the
1426                // tx_ordinal of the fee transaction, which is needed in order to
1427                // make the slips in that transaction spendable when we insert them
1428                // into the hashmap.
1429                //
1430                total_number_of_non_fee_transactions += 1;
1431            }
1432
1433            if (transaction.is_golden_ticket() || transaction.is_normal_transaction())
1434                && !transaction.is_atr_transaction()
1435            {
1436                cv.total_bytes_new += transaction.get_serialized_size() as u64;
1437                cv.total_fees_new += transaction.total_fees;
1438            }
1439
1440            if transaction.is_golden_ticket() {
1441                cv.gt_num += 1;
1442                cv.gt_index = Some(index);
1443            }
1444
1445            if transaction.is_staking_transaction() {
1446                cv.st_num += 1;
1447                cv.st_index = Some(index);
1448            }
1449
1450            if transaction.is_issuance_transaction() {
1451                cv.it_num += 1;
1452                cv.it_index = Some(index);
1453            }
1454        }
1455
1456        //
1457        // burn fee and difficulty
1458        //
1459        if let Some(previous_block) = blockchain.blocks.get(&self.previous_block_hash) {
1460            //
1461            // reference variables
1462            //
1463            previous_block_treasury = previous_block.treasury;
1464            previous_block_avg_nolan_rebroadcast_per_block =
1465                previous_block.avg_nolan_rebroadcast_per_block;
1466            previous_block_avg_fee_per_byte = previous_block.avg_fee_per_byte;
1467            previous_block_avg_total_fees = previous_block.avg_total_fees;
1468            previous_block_avg_total_fees_new = previous_block.avg_total_fees_new;
1469            previous_block_avg_total_fees_atr = previous_block.avg_total_fees_atr;
1470            previous_block_avg_payout_routing = previous_block.avg_payout_routing;
1471            previous_block_avg_payout_mining = previous_block.avg_payout_mining;
1472
1473            //
1474            // burn fee
1475            //
1476            cv.burnfee = BurnFee::calculate_burnfee_for_block(
1477                previous_block.burnfee,
1478                self.timestamp,
1479                previous_block.timestamp,
1480                configs.get_consensus_config().unwrap().heartbeat_interval,
1481            );
1482
1483            if cv.burnfee == 0 {
1484                cv.burnfee = 1;
1485            }
1486
1487            //
1488            // difficulty
1489            //
1490            cv.difficulty = previous_block.difficulty;
1491            if previous_block.has_golden_ticket {
1492                if cv.gt_num > 0 {
1493                    cv.difficulty += 1;
1494                }
1495            } else if cv.gt_num == 0 && cv.difficulty > 0 {
1496                cv.difficulty -= 1;
1497            }
1498        } else {
1499            cv.burnfee = self.burnfee;
1500            cv.difficulty = self.difficulty;
1501        }
1502
1503        //
1504        // total fees cumulative
1505        //
1506        // cumulative fees are set as the total number of new fees, unless atr transactions
1507        // exist in which case we will update this value to include the fees paid by the
1508        // subset of ATR transactions which rebroadcast, etc.
1509        cv.total_fees_cumulative = cv.total_fees_new;
1510
1511        //
1512        // automatic transaction rebroadcasts / atr
1513        //
1514        // note that we do not rebroadcast the block that is "falling off" the genesis-loop
1515        // but the block that precedes this. we make this decision to avoid the need to
1516        // track whether the ATR transactions were included in this block, since anything
1517        // more than a genesis-period old is unspendable.
1518        //
1519        if self.id > (configs.get_consensus_config().unwrap().genesis_period + 1) {
1520            if let Some(pruned_block_hash) = blockchain
1521                .blockring
1522                .get_longest_chain_block_hash_at_block_id(
1523                    self.id - (configs.get_consensus_config().unwrap().genesis_period + 1),
1524                )
1525            {
1526                if let Some(pruned_block) = blockchain.blocks.get(&pruned_block_hash) {
1527                    if let Ok(mut atr_block) = storage
1528                        .load_block_from_disk(
1529                            storage.generate_block_filepath(pruned_block).as_str(),
1530                        )
1531                        .await
1532                    {
1533                        atr_block.generate().unwrap();
1534                        assert_ne!(
1535                            atr_block.block_type,
1536                            BlockType::Pruned,
1537                            "block should be fetched fully before this"
1538                        );
1539
1540                        //
1541                        // estimate amount looping around chain
1542                        //
1543                        let total_utxo_staked =
1544                            configs.get_consensus_config().unwrap().genesis_period
1545                                * previous_block_avg_nolan_rebroadcast_per_block;
1546
1547                        //
1548                        // divide the treasury
1549                        //
1550                        let expected_atr_payout = if total_utxo_staked > 0 {
1551                            previous_block_treasury / total_utxo_staked
1552                        } else {
1553                            0
1554                        };
1555
1556                        //
1557                        // +1 gives us payout multiplier
1558                        //
1559                        let expected_atr_multiplier = 1 + expected_atr_payout;
1560
1561                        //
1562                        // loop through block to find eligible transactions
1563                        //
1564                        for transaction in &atr_block.transactions {
1565                            let mut outputs = vec![];
1566                            let mut total_nolan_eligible_for_atr_payout: Currency = 0;
1567
1568                            //
1569                            // Collect eligible slips from transaction outputs
1570                            //
1571                            // Scan through each slip in transaction.to[], looking for either:
1572                            // 1) An NFT-bound triple: [Bound, Normal, Bound], which we validate and
1573                            //    collect as a group (only the middle "payload" slip counts toward payout).
1574                            //
1575                            // 2) A regular slip, which we validate and collect individually.
1576                            // We accumulate total_nolan_eligible_for_atr_payout as we go.
1577                            //
1578                            let mut i = 0;
1579
1580                            while i < transaction.to.len() {
1581                                let slip = &transaction.to[i];
1582
1583                                //
1584                                // Check for an NFT-bound group: [Bound, Normal, Bound]
1585                                //
1586                                if slip.slip_type == SlipType::Bound && i + 2 < transaction.to.len()
1587                                {
1588                                    let slip1 = &transaction.to[i];
1589                                    let slip2 = &transaction.to[i + 1];
1590                                    let slip3 = &transaction.to[i + 2];
1591
1592                                    if slip2.slip_type != SlipType::Bound
1593                                        && slip3.slip_type == SlipType::Bound
1594                                    {
1595                                        //
1596                                        // Validate each slip in the group
1597                                        //
1598                                        if slip1.validate(&blockchain.utxoset)
1599                                            && slip2.validate(&blockchain.utxoset)
1600                                            && slip3.validate(&blockchain.utxoset)
1601                                        {
1602                                            trace!(
1603                                                "NFT group eligible: {}, {}, {}",
1604                                                slip1,
1605                                                slip2,
1606                                                slip3
1607                                            );
1608                                            outputs.push(slip1);
1609                                            outputs.push(slip2);
1610                                            outputs.push(slip3);
1611
1612                                            //
1613                                            // Only the middle "payload" slip counts toward payout
1614                                            //
1615                                            total_nolan_eligible_for_atr_payout += slip2.amount;
1616                                        }
1617
1618                                        //
1619                                        // Skip past the triple nft group
1620                                        //
1621                                        i += 3;
1622                                        continue;
1623                                    }
1624                                }
1625
1626                                //
1627                                // Fallback to single-slip case
1628                                //
1629                                if slip.validate(&blockchain.utxoset) {
1630                                    trace!("Regular slip eligible: {}", slip);
1631                                    outputs.push(slip);
1632                                    total_nolan_eligible_for_atr_payout += slip.amount;
1633                                }
1634
1635                                //
1636                                // skip single slip
1637                                //
1638                                i += 1;
1639                            }
1640
1641                            //
1642                            //  Process collected slips for ATR rebroadcast
1643                            //
1644                            // Iterate through the 'outputs', handling each item as either:
1645                            //
1646                            // - An NFT-bound triple (three slips): we unpack them, compute payout on the
1647                            //   middle slip, and create a special rebroadcast transaction.
1648                            //
1649                            // - A single slip: we compute payout and call create_rebroadcast_transaction.
1650                            //
1651                            if !outputs.is_empty() {
1652                                let tx_size = transaction.get_serialized_size() as u64;
1653                                let atr_fee = tx_size * previous_block_avg_fee_per_byte;
1654
1655                                let mut j = 0;
1656                                while j < outputs.len() {
1657                                    let output = outputs[j];
1658
1659                                    //
1660                                    // NFT-bound triple detection
1661                                    // Check if this and the next two slips form [Bound, Normal, Bound]
1662                                    //
1663                                    let is_nft_triple = output.slip_type == SlipType::Bound
1664                                        && j + 2 < outputs.len()
1665                                        && outputs[j + 1].slip_type != SlipType::Bound
1666                                        && outputs[j + 2].slip_type == SlipType::Bound;
1667
1668                                    if is_nft_triple {
1669                                        //
1670                                        // Unpack the three slips
1671                                        //
1672                                        let slip1 = outputs[j];
1673                                        let slip2 = outputs[j + 1];
1674                                        let slip3 = outputs[j + 2];
1675
1676                                        //
1677                                        // Compute payout based on the payload slip2
1678                                        //
1679                                        let atr_payout_for_slip =
1680                                            slip2.amount * expected_atr_multiplier;
1681                                        let surplus_payout_to_subtract_from_treasury =
1682                                            atr_payout_for_slip - slip2.amount;
1683                                        let atr_fee_for_slip = atr_fee;
1684
1685                                        if atr_payout_for_slip > atr_fee {
1686                                            cv.total_rebroadcast_nolan += slip2.amount;
1687                                            cv.total_rebroadcast_slips += 1;
1688
1689                                            //
1690                                            // Prepare input slips
1691                                            //
1692                                            let mut input1 = slip1.clone();
1693                                            let mut input2 = slip2.clone();
1694                                            let mut input3 = slip3.clone();
1695
1696                                            //
1697                                            // for fee accounting of payload
1698                                            //
1699                                            input2.amount = atr_payout_for_slip;
1700
1701                                            //
1702                                            // Prepare output slips, only payload slip carries ATR amount
1703                                            //
1704                                            let mut output1 = slip1.clone();
1705                                            let mut output2 = slip2.clone();
1706                                            let mut output3 = slip3.clone();
1707
1708                                            output2.slip_type = SlipType::ATR;
1709                                            output2.amount = atr_payout_for_slip - atr_fee;
1710
1711                                            //
1712                                            // Create a special rebroadcast for triple NFT group
1713                                            //
1714                                            let rebroadcast_tx =
1715                                                Transaction::create_rebroadcast_bound_transaction(
1716                                                    transaction,
1717                                                    output1,
1718                                                    input2.clone(),
1719                                                    output3,
1720                                                );
1721
1722                                            cv.total_payout_atr +=
1723                                                surplus_payout_to_subtract_from_treasury;
1724                                            cv.total_fees_atr += atr_fee;
1725
1726                                            //
1727                                            // Update cumulative ATR hash
1728                                            //
1729                                            let mut vbytes = Vec::new();
1730                                            vbytes.extend(&cv.rebroadcast_hash);
1731                                            vbytes
1732                                                .extend(&rebroadcast_tx.serialize_for_signature());
1733                                            cv.rebroadcast_hash = hash(&vbytes);
1734
1735                                            cv.rebroadcasts.push(rebroadcast_tx);
1736                                        } else {
1737                                            //
1738                                            // Payload slip didn't cover fee
1739                                            //
1740                                            cv.total_rebroadcast_nolan += slip2.amount;
1741                                            cv.total_fees_atr += slip2.amount;
1742                                            cv.total_fees_paid_by_nonrebroadcast_atr_transactions +=
1743                                                slip2.amount;
1744                                            trace!("we don't rebroadcast slip in tx - {:?} since atr_payout_for_slip = {:?} atr_fee = {:?} \n{}",transaction.hash_for_signature.unwrap().to_hex(),atr_payout_for_slip,atr_fee,output);
1745                                        }
1746
1747                                        //
1748                                        // Skip past the entire NFT group
1749                                        //
1750                                        j += 3;
1751                                    } else {
1752                                        //
1753                                        //  Single-slip case
1754                                        //
1755                                        let atr_payout_for_slip =
1756                                            output.amount * expected_atr_multiplier;
1757                                        let surplus_payout_to_subtract_from_treasury =
1758                                            atr_payout_for_slip - output.amount;
1759                                        let atr_fee_for_slip = atr_fee;
1760
1761                                        if atr_payout_for_slip > atr_fee {
1762                                            cv.total_rebroadcast_nolan += output.amount;
1763                                            cv.total_rebroadcast_slips += 1;
1764
1765                                            //
1766                                            // clone the slip, update the amount
1767                                            //
1768                                            let mut slip = output.clone();
1769                                            slip.slip_type = SlipType::ATR;
1770                                            slip.amount = atr_payout_for_slip - atr_fee_for_slip;
1771
1772                                            //
1773                                            // we update the "input" slip so that it
1774                                            // will result in cumulative fees being
1775                                            // calculated correctly when the TX is
1776                                            // examined....
1777                                            //
1778                                            let mut from_slip = output.clone();
1779                                            from_slip.amount = atr_payout_for_slip;
1780
1781                                            //
1782                                            // track payouts and fees
1783                                            //
1784                                            cv.total_payout_atr +=
1785                                                surplus_payout_to_subtract_from_treasury;
1786                                            cv.total_fees_atr += atr_fee_for_slip;
1787
1788                                            //
1789                                            // create our ATR rebroadcast transaction
1790                                            //
1791                                            let rebroadcast_tx =
1792                                                Transaction::create_rebroadcast_transaction(
1793                                                    transaction,
1794                                                    slip,
1795                                                    from_slip,
1796                                                );
1797
1798                                            //
1799                                            // update rebroadcast_hash (all ATRs)
1800                                            //
1801                                            let mut vbytes: Vec<u8> = vec![];
1802                                            vbytes.extend(&cv.rebroadcast_hash);
1803                                            vbytes
1804                                                .extend(&rebroadcast_tx.serialize_for_signature());
1805                                            cv.rebroadcast_hash = hash(&vbytes);
1806                                            cv.rebroadcasts.push(rebroadcast_tx);
1807                                        } else {
1808                                            //
1809                                            // Slip didn't cover fee
1810                                            //
1811
1812                                            //
1813                                            // this UTXO will be worth less than zero if the atr_payout is
1814                                            // added and then the atr_fee is deducted. so we do not rebroadcast
1815                                            // it but collect the dust as a fee paid to the blockchain by the
1816                                            // utxo with gratitude for its release.
1817                                            //
1818                                            cv.total_rebroadcast_nolan += output.amount;
1819                                            cv.total_fees_atr += output.amount;
1820                                            cv.total_fees_paid_by_nonrebroadcast_atr_transactions +=
1821                                                output.amount;
1822                                            trace!("we don't rebroadcast slip in tx - {:?} since atr_payout_for_slip = {:?} atr_fee = {:?} \n{}",transaction.hash_for_signature.unwrap().to_hex(),atr_payout_for_slip,atr_fee,output);
1823                                        }
1824
1825                                        //
1826                                        // skip one slip
1827                                        //
1828                                        j += 1;
1829                                    }
1830                                }
1831                            }
1832                        }
1833
1834                        //
1835                        // total fees cumulative
1836                        //
1837                        // cumulative fees are set as the total number of new fees, unless atr transactions
1838                        // exist in which case we will update this value to include the fees paid by the
1839                        // subset of ATR transactions which rebroadcast, etc.
1840                        cv.total_fees_cumulative = cv.total_fees_new + cv.total_fees_atr
1841                            - cv.total_fees_paid_by_nonrebroadcast_atr_transactions;
1842
1843                        //
1844                        // if ATR payouts are too large, adjust payout downwards
1845                        //
1846                        // because we are approximating the amount of the treasury to pay based on our
1847                        // expectation of how many UTXO are looping through the chain, we can hit a problem
1848                        // if this block has a very large amount of SAITO -- enough to blow out the payout
1849                        // to a much larger portion of the treasury than desireable.
1850                        //
1851                        // we handle this by limiting the amount of the treasury that we will issue each
1852                        // block to no more than 5% of the amount in the treasury. this prevents attackers
1853                        // from flushing the treasury out to their own wallet by massively increasing the
1854                        // amount of SAITO being rebroadcast in a single block.
1855                        //
1856                        if cv.total_payout_atr > (self.treasury as f64 * 0.05) as u64 {
1857                            let max_total_payout = (self.treasury as f64 * 0.05) as u64;
1858                            let unadjusted_total_nolan = cv.total_rebroadcast_nolan;
1859                            let adjusted_atr_payout_multiplier =
1860                                max_total_payout / unadjusted_total_nolan;
1861                            let adjusted_output_multiplier = 1 + adjusted_atr_payout_multiplier;
1862                            let _adjusted_total_rebroadcast_staking_payouts_nolan: Currency = 0;
1863                            let _adjusted_total_rebroadcast_fees_nolan: Currency = 0;
1864
1865                            //
1866                            // we re-determine our multiplier for the ATR payout based on our
1867                            // max_total_payout divided by the unadjusted_total_nolan that we
1868                            // are rebroadcasting.
1869                            //
1870                            // TODO - fee handling is complicated with _atr and _cumulative
1871                            //
1872                            cv.total_payout_atr = 0;
1873
1874                            for rebroadcast_tx in &mut cv.rebroadcasts {
1875                                //
1876                                // update the amount that is in the output transaction according
1877                                // to the amount in the input transaction. since this isn't a common
1878                                // edge-case and cannot be systematically abused we're going to forgo
1879                                // the rebroadcast fee in this case, and assume it is covered by the
1880                                // reduced payout.
1881                                //
1882
1883                                //
1884                                // Determine whether this is an NFT‐group ATR (3 slips) or a single‐slip ATR.
1885                                // [Bound, Normal, Bound]
1886                                //
1887                                if rebroadcast_tx.to.len() == 3
1888                                    && rebroadcast_tx.from.len() == 3
1889                                    && rebroadcast_tx.from[0].slip_type == SlipType::Bound
1890                                    && rebroadcast_tx.from[1].slip_type != SlipType::Bound
1891                                    && rebroadcast_tx.from[2].slip_type == SlipType::Bound
1892                                {
1893                                    let input_amount = rebroadcast_tx.from[1].amount;
1894
1895                                    //
1896                                    // Calculate the new output amount
1897                                    //
1898                                    let new_output_amount =
1899                                        input_amount * adjusted_output_multiplier;
1900
1901                                    //
1902                                    // Update the ATR output slip
1903                                    //
1904                                    rebroadcast_tx.to[1].amount = new_output_amount;
1905
1906                                    cv.total_payout_atr += rebroadcast_tx.to[1].amount;
1907                                    cv.total_payout_atr -= input_amount;
1908                                } else {
1909                                    //
1910                                    // Single‐slip ATR: payload is the only slip at index 0
1911                                    //
1912                                    let input_amount = rebroadcast_tx.from[0].amount;
1913                                    let new_output_amount =
1914                                        input_amount * adjusted_output_multiplier;
1915                                    rebroadcast_tx.to[0].amount = new_output_amount;
1916
1917                                    cv.total_payout_atr += rebroadcast_tx.to[0].amount;
1918                                    cv.total_payout_atr -= input_amount;
1919                                }
1920                            }
1921
1922                            cv.total_fees_atr = 0;
1923                        }
1924                    } else {
1925                        error!(
1926                            "couldn't load block for ATR from disk. block hash : {:?}",
1927                            pruned_block.hash.to_hex()
1928                        );
1929                    }
1930                } // block
1931            }
1932        } // if at least 1 genesis period deep
1933
1934        //
1935        // total fees
1936        //
1937        cv.total_fees = cv.total_fees_new + cv.total_fees_atr;
1938
1939        //
1940        // fee_per_byte
1941        //
1942        if cv.total_bytes_new > 0 {
1943            cv.fee_per_byte = cv.total_fees_new / cv.total_bytes_new as Currency;
1944        }
1945
1946        //
1947        // avg_fee_per_byte
1948        //
1949        let adjustment = (previous_block_avg_fee_per_byte as i128 - cv.fee_per_byte as i128)
1950            / configs.get_consensus_config().unwrap().genesis_period as i128;
1951        cv.avg_fee_per_byte = (previous_block_avg_fee_per_byte as i128 - adjustment) as Currency;
1952
1953        //
1954        // avg_total_fees
1955        //
1956        let adjustment = (previous_block_avg_total_fees as i128 - cv.total_fees as i128)
1957            / configs.get_consensus_config().unwrap().genesis_period as i128;
1958        cv.avg_total_fees = (previous_block_avg_total_fees as i128 - adjustment) as Currency;
1959
1960        //
1961        // avg_total_fees_new
1962        //
1963        let adjustment = (previous_block_avg_total_fees_new as i128 - cv.total_fees_new as i128)
1964            / configs.get_consensus_config().unwrap().genesis_period as i128;
1965        cv.avg_total_fees_new =
1966            (previous_block_avg_total_fees_new as i128 - adjustment) as Currency;
1967
1968        //
1969        // avg_total_fees_atr
1970        //
1971        let adjustment = (previous_block_avg_total_fees_atr as i128 - cv.total_fees_atr as i128)
1972            / configs.get_consensus_config().unwrap().genesis_period as i128;
1973        cv.avg_total_fees_atr =
1974            (previous_block_avg_total_fees_atr as i128 - adjustment) as Currency;
1975
1976        //
1977        // average nolan rebroadcast per block
1978        //
1979        // note that we cannot move this above the ATR section as we use the
1980        // value of this variable (from the last block) to figure out what the
1981        // ATR payout should be in this block.
1982        //
1983        let adjustment = (previous_block_avg_nolan_rebroadcast_per_block as i128
1984            - cv.total_rebroadcast_nolan as i128)
1985            / configs.get_consensus_config().unwrap().genesis_period as i128;
1986        cv.avg_nolan_rebroadcast_per_block =
1987            (previous_block_avg_nolan_rebroadcast_per_block as i128 - adjustment) as Currency;
1988
1989        //
1990        // calculate payouts
1991        //
1992        let mut miner_publickey: SaitoPublicKey = [0; 33];
1993        let mut miner_payout: Currency = 0;
1994        let mut router1_payout: Currency = 0;
1995        let mut router1_publickey: SaitoPublicKey = [0; 33];
1996        let mut router2_payout: Currency = 0;
1997        let mut router2_publickey: SaitoPublicKey = [0; 33];
1998        let mut treasury_contribution: Currency = 0;
1999        let mut graveyard_contribution: Currency = 0;
2000
2001        //
2002        // if this block contains a golden ticket
2003        //
2004        if let Some(gt_index) = cv.gt_index {
2005            //
2006            // golden ticket needed to process payout
2007            //
2008            let golden_ticket: GoldenTicket =
2009                GoldenTicket::deserialize_from_net(&self.transactions[gt_index].data);
2010            let mut next_random_number = hash(golden_ticket.random.as_ref());
2011
2012            //
2013            // load the previous block
2014            //
2015            if let Some(previous_block) = blockchain.blocks.get(&self.previous_block_hash) {
2016                debug!(
2017                    "previous block : {:?}-{:?} exists!",
2018                    previous_block.id,
2019                    previous_block.hash.to_hex()
2020                );
2021
2022                //
2023                // half to miner (capped @ 1.5x)
2024                //
2025                let expected_miner_payout = previous_block.total_fees / 2;
2026                let maximum_miner_payout = (previous_block.avg_total_fees as f64 * 1.5) as u64;
2027                if expected_miner_payout > maximum_miner_payout {
2028                    graveyard_contribution += expected_miner_payout - maximum_miner_payout;
2029                    miner_payout = maximum_miner_payout;
2030                    debug!(
2031                        "block : {} miner payout set as maximum payout : {}. expected payout : {}",
2032                        self.id, miner_payout, expected_miner_payout
2033                    );
2034                } else {
2035                    miner_payout = expected_miner_payout;
2036                    debug!(
2037                        "block : {} miner payout set as expected payout : {}. maximum payout : {}",
2038                        self.id, miner_payout, maximum_miner_payout
2039                    );
2040                }
2041                miner_publickey = golden_ticket.public_key;
2042
2043                //
2044                // half to router (capped @ 1.5x)
2045                //
2046                let expected_router_payout = previous_block.total_fees - expected_miner_payout;
2047                let maximum_router_payout = (previous_block.avg_total_fees as f64 * 1.5) as u64;
2048                if expected_router_payout > maximum_router_payout {
2049                    graveyard_contribution += expected_router_payout - maximum_router_payout;
2050                    router1_payout = maximum_router_payout;
2051                    debug!(
2052                        "block : {} router1 payout set as maximum payout : {}. expected payout : {}",
2053                        self.id, router1_payout, expected_router_payout
2054                    );
2055                } else {
2056                    router1_payout = expected_router_payout;
2057                    debug!(
2058                        "block : {} router1 payout set as expected payout : {}. maximum payout : {}",
2059                        self.id, router1_payout, maximum_router_payout
2060                    );
2061                }
2062                router1_publickey = previous_block.find_winning_router(next_random_number);
2063
2064                //
2065                // finding a router consumes 2 hashes
2066                //
2067                next_random_number = hash(next_random_number.as_ref());
2068                next_random_number = hash(next_random_number.as_ref());
2069
2070                //
2071                // if the previous block ALSO HAD a golden ticket there is no need for further
2072                // action as that previous block has already contributed its tokens to the
2073                // treasury and routing node.
2074                //
2075                if previous_block.has_golden_ticket {
2076                    //
2077                    // do nothing
2078                    //
2079                } else {
2080                    //
2081                    // recurse and payout previous block
2082                    //
2083                    if let Some(previous_previous_block) =
2084                        blockchain.blocks.get(&previous_block.previous_block_hash)
2085                    {
2086                        //
2087                        // half to treasury (capped)
2088                        //
2089                        // we sanity check that the fees in the block are not greater than 1.5x the total
2090                        // average fees processed by the blockchain. this is a sanity-check against
2091                        // attackers who might try to create extremely deep-hop routing chains in order
2092                        // to increase their chance of payout.
2093                        //
2094                        let expected_treasury_contribution2 =
2095                            previous_previous_block.total_fees / 2;
2096                        let maximum_treasury_contribution2 =
2097                            (previous_block.avg_total_fees as f64 * 1.5) as u64;
2098                        if expected_treasury_contribution2 > maximum_treasury_contribution2 {
2099                            treasury_contribution += maximum_treasury_contribution2;
2100                            graveyard_contribution +=
2101                                expected_treasury_contribution2 - maximum_treasury_contribution2;
2102                        } else {
2103                            treasury_contribution += expected_treasury_contribution2;
2104                        }
2105
2106                        //
2107                        // half to router (capped @ 1.5)
2108                        //
2109                        let expected_router2_payout =
2110                            previous_previous_block.total_fees - expected_treasury_contribution2;
2111                        // note avg used is previous block for consistency with state
2112                        let maximum_router2_payout =
2113                            (previous_block.avg_total_fees as f64 * 1.5) as u64;
2114                        if expected_router2_payout > maximum_router2_payout {
2115                            graveyard_contribution +=
2116                                expected_router2_payout - maximum_router2_payout;
2117                            router2_payout = maximum_router2_payout;
2118                        } else {
2119                            router2_payout = expected_router2_payout;
2120                        }
2121                        router2_publickey =
2122                            previous_previous_block.find_winning_router(next_random_number);
2123
2124                        //
2125                        // finding a router consumes 2 hashes
2126                        //
2127                        next_random_number = hash(next_random_number.as_slice());
2128                        next_random_number = hash(next_random_number.as_slice());
2129                    }
2130                }
2131            } else {
2132                info!(
2133                    "previous block : {:?} not found for block : {:?} at index : {:?}",
2134                    self.previous_block_hash.to_hex(),
2135                    self.hash.to_hex(),
2136                    self.id
2137                );
2138            }
2139
2140            //
2141            // now create fee transactions
2142            //
2143            let mut slip_index = 0;
2144            let mut transaction = Transaction::default();
2145            transaction.transaction_type = TransactionType::Fee;
2146            transaction.timestamp = self.timestamp;
2147
2148            if miner_publickey != [0; 33] && miner_payout > 0 {
2149                let mut output = Slip::default();
2150                output.public_key = miner_publickey;
2151                output.amount = miner_payout;
2152                output.slip_type = SlipType::MinerOutput;
2153                output.slip_index = slip_index;
2154                output.tx_ordinal = total_number_of_non_fee_transactions + 1;
2155                output.block_id = self.id;
2156                transaction.add_to_slip(output.clone());
2157                slip_index += 1;
2158            } else {
2159                debug!(
2160                    "miner_publickey is not set or payout is zero. Not adding to fee transaction"
2161                );
2162            }
2163
2164            if router1_payout > 0 {
2165                if router1_publickey != [0; 33] {
2166                    let mut output = Slip::default();
2167                    output.public_key = router1_publickey;
2168                    output.amount = router1_payout;
2169                    output.slip_type = SlipType::RouterOutput;
2170                    output.slip_index = slip_index;
2171                    output.tx_ordinal = total_number_of_non_fee_transactions + 1;
2172                    output.block_id = self.id;
2173                    transaction.add_to_slip(output.clone());
2174                    slip_index += 1;
2175                } else {
2176                    graveyard_contribution += router1_payout;
2177                }
2178            } else {
2179                debug!(
2180                    "router1_publickey is not set or payout is zero. Not adding to fee transaction"
2181                );
2182            }
2183
2184            if router2_payout > 0 {
2185                if router2_publickey != [0; 33] {
2186                    let mut output = Slip::default();
2187                    output.public_key = router2_publickey;
2188                    output.amount = router2_payout;
2189                    output.slip_type = SlipType::RouterOutput;
2190                    output.slip_index = slip_index;
2191                    output.tx_ordinal = total_number_of_non_fee_transactions + 1;
2192                    output.block_id = self.id;
2193                    transaction.add_to_slip(output.clone());
2194                    slip_index += 1;
2195                } else {
2196                    graveyard_contribution += router2_payout;
2197                }
2198            } else {
2199                debug!(
2200                    "router2_publickey is not set or payout is zero. Not adding to fee transaction"
2201                );
2202            }
2203
2204            cv.total_payout_mining = miner_payout;
2205            cv.total_payout_routing = router1_payout + router2_payout;
2206            cv.fee_transaction = Some(transaction);
2207        } else {
2208            //
2209            // if there is no golden ticket AND the previous block was not paid out then
2210            // we should collect the funds that are lost and add them to our graveyard.
2211            //
2212            if let Some(previous_block) = blockchain.blocks.get(&self.previous_block_hash) {
2213                if previous_block.has_golden_ticket {
2214                    // do nothing, already paid out
2215                } else {
2216                    // our previous_previous_block is about to disappear, which means
2217                    // we should make note that these funds are slipping into our graveyard
2218                    if let Some(previous_previous_block) =
2219                        blockchain.blocks.get(&previous_block.previous_block_hash)
2220                    {
2221                        graveyard_contribution += previous_block.previous_block_unpaid;
2222                    }
2223                }
2224            }
2225        }
2226
2227        //
2228        // treasury and graveyard
2229        //
2230        cv.total_payout_treasury = treasury_contribution;
2231        cv.total_payout_graveyard = graveyard_contribution;
2232
2233        //
2234        // average routing payout
2235        //
2236        let adjustment = (previous_block_avg_payout_routing as i128
2237            - cv.total_payout_routing as i128)
2238            / configs.get_consensus_config().unwrap().genesis_period as i128;
2239        cv.avg_payout_routing =
2240            (previous_block_avg_payout_routing as i128 - adjustment) as Currency;
2241
2242        //
2243        // average mining payout
2244        //
2245        let adjustment = (previous_block_avg_payout_mining as i128
2246            - cv.total_payout_mining as i128)
2247            / configs.get_consensus_config().unwrap().genesis_period as i128;
2248        cv.avg_payout_mining = (previous_block_avg_payout_mining as i128 - adjustment) as Currency;
2249
2250        //
2251        // average treasury payout
2252        //
2253        let adjustment = (previous_block_avg_payout_treasury as i128
2254            - cv.total_payout_treasury as i128)
2255            / configs.get_consensus_config().unwrap().genesis_period as i128;
2256        cv.avg_payout_treasury =
2257            (previous_block_avg_payout_treasury as i128 - adjustment) as Currency;
2258
2259        //
2260        // average graveyard payout
2261        //
2262        let adjustment = (previous_block_avg_payout_graveyard as i128
2263            - cv.total_payout_graveyard as i128)
2264            / configs.get_consensus_config().unwrap().genesis_period as i128;
2265        cv.avg_payout_graveyard =
2266            (previous_block_avg_payout_graveyard as i128 - adjustment) as Currency;
2267
2268        //
2269        // average atr payout
2270        //
2271        let adjustment = (previous_block_avg_payout_atr as i128 - cv.total_payout_atr as i128)
2272            / configs.get_consensus_config().unwrap().genesis_period as i128;
2273        cv.avg_payout_atr = (previous_block_avg_payout_atr as i128 - adjustment) as Currency;
2274
2275        cv
2276    }
2277
2278    pub fn generate_pre_hash(&mut self) {
2279        self.pre_hash = hash(&self.serialize_for_signature());
2280    }
2281
2282    pub fn on_chain_reorganization(&mut self, utxoset: &mut UtxoSet, longest_chain: bool) -> bool {
2283        debug!(
2284            "block : on chain reorg : {:?} - {:?}",
2285            self.id,
2286            self.hash.to_hex()
2287        );
2288        for tx in &self.transactions {
2289            tx.on_chain_reorganization(utxoset, longest_chain);
2290        }
2291        self.in_longest_chain = longest_chain;
2292        true
2293    }
2294
2295    // we may want to separate the signing of the block from the setting of the necessary hash
2296    // we do this together out of convenience only
2297
2298    pub fn sign(&mut self, private_key: &SaitoPrivateKey) {
2299        // we set final data
2300        self.signature = sign(&self.serialize_for_signature(), private_key);
2301    }
2302
2303    // serialize the pre_hash and the signature_for_source into a
2304    // bytes array that can be hashed and then have the hash set.
2305    pub fn serialize_for_hash(&self) -> Vec<u8> {
2306        [
2307            self.previous_block_hash.as_slice(),
2308            self.pre_hash.as_slice(),
2309        ]
2310        .concat()
2311    }
2312
2313    // serialize major block components for block signature
2314    // this will manually calculate the merkle_root if necessary
2315    // but it is advised that the merkle_root be already calculated
2316    // to avoid speed issues.
2317    pub fn serialize_for_signature(&self) -> Vec<u8> {
2318        [
2319            self.id.to_be_bytes().as_slice(),
2320            self.timestamp.to_be_bytes().as_slice(),
2321            self.previous_block_hash.as_slice(),
2322            self.creator.as_slice(),
2323            self.merkle_root.as_slice(),
2324            self.graveyard.to_be_bytes().as_slice(),
2325            self.treasury.to_be_bytes().as_slice(),
2326            self.burnfee.to_be_bytes().as_slice(),
2327            self.difficulty.to_be_bytes().as_slice(),
2328            self.avg_fee_per_byte.to_be_bytes().as_slice(),
2329            self.avg_nolan_rebroadcast_per_block
2330                .to_be_bytes()
2331                .as_slice(),
2332            self.previous_block_unpaid.to_be_bytes().as_slice(),
2333            self.avg_total_fees.to_be_bytes().as_slice(),
2334            self.avg_total_fees_new.to_be_bytes().as_slice(),
2335            self.avg_total_fees_atr.to_be_bytes().as_slice(),
2336            self.avg_payout_routing.to_be_bytes().as_slice(),
2337            self.avg_payout_mining.to_be_bytes().as_slice(),
2338        ]
2339        .concat()
2340    }
2341
2342    /// Serialize a Block for transport or disk.
2343    /// [len of transactions - 4 bytes - u32]
2344    /// [id - 8 bytes - u64]
2345    /// [timestamp - 8 bytes - u64]
2346    /// [previous_block_hash - 32 bytes - SHA 256 hash]
2347    /// [creator - 33 bytes - Secp25k1 pubkey compact format]
2348    /// [merkle_root - 32 bytes - SHA 256 hash
2349    /// [signature - 64 bytes - Secp25k1 sig]
2350    /// [graveyard - 8 bytes - u64]
2351    /// [treasury - 8 bytes - u64]
2352    /// [burnfee - 8 bytes - u64]
2353    /// [difficulty - 8 bytes - u64]
2354    /// [avg_total_fees - 8 bytes - u64]
2355    /// [avg_fee_per_byte - 8 bytes - u64]
2356    /// [avg_nolan_rebroadcast_per_block - 8 bytes - u64]
2357    /// [previous_block_unpaid - 8 bytes - u64]
2358    /// [avg_total_fees - 8 bytes - u64]		// note the duplicate here, is because added in group
2359    /// [avg_total_fees_new - 8 bytes - u64]
2360    /// [avg_total_fees_atr - 8 bytes - u64]
2361    /// [avg_payout_routing - 8 bytes - u64]
2362    /// [avg_payout_mining - 8 bytes - u64]
2363    /// [avg_payout_treasury - 8 bytes - u64]
2364    /// [avg_payout_graveyard - 8 bytes - u64]
2365    /// [avg_payout_atr - 8 bytes - u64]
2366    /// [total_fees - 8 bytes - u64]
2367    /// [total_fees_new - 8 bytes - u64]
2368    /// [total_fees_atr - 8 bytes - u64]
2369    /// [fee_per_byte - 8 bytes - u64]
2370    /// [total_fees_cumulative - 8 bytes - u64]
2371
2372    /// [transaction][transaction][transaction]...
2373    pub fn serialize_for_net(&self, block_type: BlockType) -> Vec<u8> {
2374        let mut tx_len_buffer: Vec<u8> = vec![];
2375
2376        // block headers do not get tx data
2377        if block_type == BlockType::Header {
2378            tx_len_buffer.extend(&0_u32.to_be_bytes());
2379        } else {
2380            tx_len_buffer.extend(&(self.transactions.iter().len() as u32).to_be_bytes());
2381        }
2382        let mut tx_buf = vec![];
2383        if block_type != BlockType::Header {
2384            // block headers do not get tx data
2385            tx_buf = iterate!(self.transactions, 10)
2386                .map(|transaction| transaction.serialize_for_net())
2387                .collect::<Vec<_>>()
2388                .concat();
2389        }
2390
2391        let buffer = [
2392            tx_len_buffer.as_slice(),
2393            self.id.to_be_bytes().as_slice(),
2394            self.timestamp.to_be_bytes().as_slice(),
2395            self.previous_block_hash.as_slice(),
2396            self.creator.as_slice(),
2397            self.merkle_root.as_slice(),
2398            self.signature.as_slice(),
2399            self.graveyard.to_be_bytes().as_slice(),
2400            self.treasury.to_be_bytes().as_slice(),
2401            self.burnfee.to_be_bytes().as_slice(),
2402            self.difficulty.to_be_bytes().as_slice(),
2403            self.avg_total_fees.to_be_bytes().as_slice(),
2404            self.avg_fee_per_byte.to_be_bytes().as_slice(),
2405            self.avg_nolan_rebroadcast_per_block
2406                .to_be_bytes()
2407                .as_slice(),
2408            self.previous_block_unpaid.to_be_bytes().as_slice(),
2409            self.avg_total_fees.to_be_bytes().as_slice(),
2410            self.avg_total_fees_new.to_be_bytes().as_slice(),
2411            self.avg_total_fees_atr.to_be_bytes().as_slice(),
2412            self.avg_payout_routing.to_be_bytes().as_slice(),
2413            self.avg_payout_mining.to_be_bytes().as_slice(),
2414            self.avg_payout_treasury.to_be_bytes().as_slice(),
2415            self.avg_payout_graveyard.to_be_bytes().as_slice(),
2416            self.avg_payout_atr.to_be_bytes().as_slice(),
2417            self.total_payout_routing.to_be_bytes().as_slice(),
2418            self.total_payout_mining.to_be_bytes().as_slice(),
2419            self.total_payout_treasury.to_be_bytes().as_slice(),
2420            self.total_payout_graveyard.to_be_bytes().as_slice(),
2421            self.total_payout_atr.to_be_bytes().as_slice(),
2422            self.total_fees.to_be_bytes().as_slice(),
2423            self.total_fees_new.to_be_bytes().as_slice(),
2424            self.total_fees_atr.to_be_bytes().as_slice(),
2425            self.fee_per_byte.to_be_bytes().as_slice(),
2426            self.total_fees_cumulative.to_be_bytes().as_slice(),
2427            tx_buf.as_slice(),
2428        ]
2429        .concat();
2430
2431        buffer
2432    }
2433
2434    pub async fn update_block_to_block_type(
2435        &mut self,
2436        block_type: BlockType,
2437        storage: &Storage,
2438        is_browser: bool,
2439    ) -> bool {
2440        if self.block_type == block_type {
2441            return true;
2442        }
2443
2444        if block_type == BlockType::Full {
2445            return self
2446                .upgrade_block_to_block_type(block_type, storage, is_browser)
2447                .await;
2448        }
2449
2450        if block_type == BlockType::Pruned {
2451            return self
2452                .downgrade_block_to_block_type(block_type, is_browser)
2453                .await;
2454        }
2455
2456        false
2457    }
2458
2459    // if the block is not at the proper type, try to upgrade it to have the
2460    // data that is necessary for blocks of that type if possible. if this is
2461    // not possible, return false. if it is possible, return true once upgraded.
2462    pub async fn upgrade_block_to_block_type(
2463        &mut self,
2464        block_type: BlockType,
2465        storage: &Storage,
2466        is_spv: bool,
2467    ) -> bool {
2468        debug!(
2469            "upgrading block : {:?}-{:?} of type : {:?} to type : {:?}",
2470            self.id,
2471            self.hash.to_hex(),
2472            self.block_type,
2473            block_type
2474        );
2475        if self.block_type == block_type {
2476            trace!("block type is already {:?}", self.block_type);
2477            return true;
2478        }
2479
2480        //
2481        // TODO - if the block does not exist on disk, we have to
2482        //  attempt a remote fetch.
2483        //
2484
2485        //
2486        // if the block type needed is full and we are not,
2487        // load the block if it exists on disk.
2488        //
2489        if block_type == BlockType::Full {
2490            if is_spv {
2491                debug!("cannot upgrade block to full in spv mode");
2492                return false;
2493            }
2494            let new_block = storage
2495                .load_block_from_disk(storage.generate_block_filepath(self).as_str())
2496                .await;
2497            if new_block.is_err() {
2498                error!(
2499                    "block not found in disk to upgrade : {:?}",
2500                    self.hash.to_hex()
2501                );
2502                return false;
2503            }
2504            let mut new_block = new_block.unwrap();
2505
2506            debug!(
2507                "upgraded tx counts : {:?} vs {:?}",
2508                self.transactions.len(),
2509                new_block.transactions.len()
2510            );
2511            // in-memory swap copying txs in block from mempool
2512            mem::swap(&mut new_block.transactions, &mut self.transactions);
2513
2514            // transactions need hashes
2515            if self.generate().is_err() {
2516                error!("failed to generate block after upgrade");
2517                return false;
2518            }
2519            self.block_type = BlockType::Full;
2520
2521            return true;
2522        }
2523
2524        false
2525    }
2526
2527    pub fn generate_lite_block(&self, keylist: Vec<SaitoPublicKey>) -> Block {
2528        debug!(
2529            "generating lite block for keys : {:?} for block : {:?}-{:?}",
2530            keylist.iter().map(hex::encode).collect::<Vec<String>>(),
2531            self.id,
2532            self.hash.to_hex()
2533        );
2534
2535        let mut pruned_txs: Vec<Transaction> = iterate!(&self.transactions, 10)
2536            .map(|tx| {
2537                if tx
2538                    .from
2539                    .iter()
2540                    .any(|slip| keylist.contains(&slip.public_key))
2541                    || tx.to.iter().any(|slip| keylist.contains(&slip.public_key))
2542                    || tx.is_golden_ticket()
2543                {
2544                    tx.clone()
2545                } else {
2546                    Transaction {
2547                        timestamp: tx.timestamp,
2548                        from: vec![],
2549                        to: vec![],
2550                        data: vec![],
2551                        transaction_type: TransactionType::SPV,
2552                        txs_replacements: 1,
2553                        signature: tx.signature,
2554                        path: vec![],
2555                        hash_for_signature: tx.hash_for_signature,
2556                        total_in: 0,
2557                        total_out: 0,
2558                        total_fees: 0,
2559                        total_work_for_me: 0,
2560                        cumulative_fees: 0,
2561                    }
2562                }
2563            })
2564            .collect();
2565
2566        let mut i = 0;
2567        while i + 1 < pruned_txs.len() {
2568            if pruned_txs[i].transaction_type == TransactionType::SPV
2569                && pruned_txs[i + 1].transaction_type == TransactionType::SPV
2570                && pruned_txs[i].txs_replacements == pruned_txs[i + 1].txs_replacements
2571            {
2572                pruned_txs[i].txs_replacements *= 2;
2573                let combined_hash = hash(
2574                    &[
2575                        pruned_txs[i].hash_for_signature.unwrap(),
2576                        pruned_txs[i + 1].hash_for_signature.unwrap(),
2577                    ]
2578                    .concat(),
2579                );
2580                pruned_txs[i].hash_for_signature = Some(combined_hash);
2581                pruned_txs.remove(i + 1);
2582            } else {
2583                i += 2;
2584            }
2585        }
2586
2587        // Create the block with pruned transactions
2588        let mut block = Block::new();
2589
2590        block.transactions = pruned_txs;
2591        block.id = self.id;
2592        block.timestamp = self.timestamp;
2593        block.previous_block_hash = self.previous_block_hash;
2594        block.creator = self.creator;
2595        block.burnfee = self.burnfee;
2596        block.difficulty = self.difficulty;
2597        block.graveyard = self.graveyard;
2598        block.treasury = self.treasury;
2599        block.signature = self.signature;
2600        block.avg_fee_per_byte = self.avg_fee_per_byte;
2601        block.avg_nolan_rebroadcast_per_block = self.avg_nolan_rebroadcast_per_block;
2602        block.previous_block_unpaid = self.previous_block_unpaid;
2603        block.avg_total_fees = self.avg_total_fees;
2604        block.avg_total_fees_new = self.avg_total_fees_new;
2605        block.avg_total_fees_atr = self.avg_total_fees_atr;
2606        block.avg_payout_routing = self.avg_payout_routing;
2607        block.avg_payout_mining = self.avg_payout_mining;
2608        block.avg_payout_treasury = self.avg_payout_treasury;
2609        block.avg_payout_graveyard = self.avg_payout_graveyard;
2610        block.avg_payout_atr = self.avg_payout_atr;
2611        block.total_payout_routing = self.total_payout_routing;
2612        block.total_payout_mining = self.total_payout_mining;
2613        block.total_payout_treasury = self.total_payout_treasury;
2614        block.total_payout_graveyard = self.total_payout_graveyard;
2615        block.total_payout_atr = self.total_payout_atr;
2616        block.total_fees = self.total_fees;
2617        block.total_fees_new = self.total_fees_new;
2618        block.total_fees_atr = self.total_fees_atr;
2619        block.fee_per_byte = self.fee_per_byte;
2620        block.hash = self.hash;
2621        block.total_fees_cumulative = self.total_fees_cumulative;
2622
2623        block.merkle_root = self.generate_merkle_root(true, true);
2624
2625        block
2626    }
2627
2628    pub async fn validate(
2629        &self,
2630        blockchain: &Blockchain,
2631        utxoset: &UtxoSet,
2632        configs: &(dyn Configuration + Send + Sync),
2633        storage: &Storage,
2634        validate_against_utxo: bool,
2635    ) -> bool {
2636        //
2637        // TODO SYNC : Add the code to check whether this is the genesis block and skip validations
2638        //
2639        assert!(self.id > 0);
2640        if configs.is_spv_mode() {
2641            self.generate_consensus_values(blockchain, storage, configs)
2642                .await;
2643            return true;
2644        }
2645
2646        //
2647        // "ghost blocks" are blocks that simply contain the block hash, they are used
2648        // by lite-clients to sync the chain without validating all of the transactions
2649        // in situations where users want to make that trade-off. for that reasons, if
2650        // we have a Ghost Block we automatically validate it.
2651        //
2652        if let BlockType::Ghost = self.block_type {
2653            return true;
2654        }
2655
2656        //
2657        // all valid blocks with ID > 1 must have at least one transaction
2658        //
2659        if self.transactions.is_empty() && self.id != 1 && !blockchain.blocks.is_empty() {
2660            error!("ERROR 424342: block does not validate as it has no transactions",);
2661            return false;
2662        }
2663
2664        //
2665        // all valid blocks must be signed by their creator
2666        //
2667        if !verify_signature(&self.pre_hash, &self.signature, &self.creator) {
2668            error!("ERROR 582039: block is not signed by creator or signature does not validate",);
2669            return false;
2670        }
2671
2672        info!("validate block : {:?}-{:?}", self.id, self.hash.to_hex());
2673
2674        //
2675        // generate "consensus values"
2676        //
2677        let cv = self
2678            .generate_consensus_values(blockchain, storage, configs)
2679            .await;
2680        trace!("consensus values generated : {}", cv);
2681
2682        if validate_against_utxo {
2683            //
2684            // total_fees
2685            //
2686            if cv.total_fees != self.total_fees {
2687                error!(
2688                    "total_fees actual: {:?} expected : {:?}",
2689                    self.total_fees, cv.total_fees
2690                );
2691                return false;
2692            }
2693
2694            //
2695            // total_fees_new
2696            //
2697            if cv.total_fees_new != self.total_fees_new {
2698                error!(
2699                    "total_fees_new actual: {:?} expected : {:?}",
2700                    self.total_fees_new, cv.total_fees_new
2701                );
2702                return false;
2703            }
2704
2705            //
2706            // total_fees_atr
2707            //
2708            if cv.total_fees_atr != self.total_fees_atr {
2709                error!(
2710                    "total_fees_atr actual: {:?} expected : {:?}",
2711                    self.total_fees_atr, cv.total_fees_atr
2712                );
2713                return false;
2714            }
2715
2716            //
2717            // total_fees_cumulative
2718            //
2719            if cv.total_fees_cumulative != self.total_fees_cumulative {
2720                error!(
2721                    "total_fees_cumulative actual: {:?} expected : {:?}",
2722                    self.total_fees_cumulative, cv.total_fees_cumulative
2723                );
2724                return false;
2725            }
2726
2727            //
2728            // avg_total_fees
2729            //
2730            if cv.avg_total_fees != self.avg_total_fees {
2731                error!(
2732                    "avg_total_fees actual: {:?} expected : {:?}",
2733                    self.avg_total_fees, cv.avg_total_fees
2734                );
2735                return false;
2736            }
2737
2738            //
2739            // avg_total_fees_new
2740            //
2741            if cv.avg_total_fees_new != self.avg_total_fees_new {
2742                error!(
2743                    "avg_total_fees_new actual: {:?} expected : {:?}",
2744                    self.avg_total_fees_new, cv.avg_total_fees_new
2745                );
2746                return false;
2747            }
2748
2749            //
2750            // avg_total_fees_atr
2751            //
2752            if cv.avg_total_fees_atr != self.avg_total_fees_atr {
2753                error!(
2754                    "avg_total_fees_atr error: {:?} expected : {:?}",
2755                    self.avg_total_fees_atr, cv.avg_total_fees_atr
2756                );
2757                return false;
2758            }
2759
2760            //
2761            // total_payout_routing
2762            //
2763            if cv.total_payout_routing != self.total_payout_routing {
2764                error!(
2765                    "total_payout_routing error: {:?} expected : {:?}",
2766                    self.total_payout_routing, cv.total_payout_routing
2767                );
2768                return false;
2769            }
2770
2771            //
2772            // total_payout_mining
2773            //
2774            if cv.total_payout_mining != self.total_payout_mining {
2775                error!(
2776                    "total_payout_mining actual: {:?} expected : {:?}",
2777                    self.total_payout_mining, cv.total_payout_mining
2778                );
2779                return false;
2780            }
2781
2782            //
2783            // total_payout_treasury
2784            //
2785            if cv.total_payout_treasury != self.total_payout_treasury {
2786                error!(
2787                    "total_payout_treasury actual: {:?} expected : {:?}",
2788                    self.total_payout_treasury, cv.total_payout_treasury
2789                );
2790                return false;
2791            }
2792
2793            //
2794            // total_payout_graveyard
2795            //
2796            if cv.total_payout_graveyard != self.total_payout_graveyard {
2797                error!(
2798                    "total_payout_graveyard actual: {:?} expected : {:?}",
2799                    self.total_payout_graveyard, cv.total_payout_graveyard
2800                );
2801                return false;
2802            }
2803
2804            //
2805            // total_payout_atr
2806            //
2807            if cv.total_payout_atr != self.total_payout_atr {
2808                error!(
2809                    "total_payout_atr actual: {:?} expected : {:?}",
2810                    self.total_payout_atr, cv.total_payout_atr
2811                );
2812                return false;
2813            }
2814
2815            //
2816            // avg_payout_routing
2817            //
2818            if cv.avg_payout_routing != self.avg_payout_routing {
2819                error!(
2820                    "avg_payout_routing actual: {:?} expected : {:?}",
2821                    self.avg_payout_routing, cv.avg_payout_routing
2822                );
2823                return false;
2824            }
2825
2826            //
2827            // avg_payout_mining
2828            //
2829            if cv.avg_payout_mining != self.avg_payout_mining {
2830                error!(
2831                    "avg_payout_mining actual: {:?} expected : {:?}",
2832                    self.avg_payout_mining, cv.avg_payout_mining
2833                );
2834                return false;
2835            }
2836
2837            //
2838            // total_payout_treasury
2839            //
2840            if cv.avg_payout_treasury != self.avg_payout_treasury {
2841                error!(
2842                    "avg_payout_treasury actual: {:?} expected : {:?}",
2843                    self.avg_payout_treasury, cv.avg_payout_treasury
2844                );
2845                return false;
2846            }
2847
2848            //
2849            // avg_payout_graveyard
2850            //
2851            if cv.avg_payout_graveyard != self.avg_payout_graveyard {
2852                error!(
2853                    "avg_payout_graveyard actual: {:?} expected : {:?}",
2854                    self.avg_payout_graveyard, cv.avg_payout_graveyard
2855                );
2856                return false;
2857            }
2858
2859            //
2860            // total_payout_atr
2861            //
2862            if cv.avg_payout_atr != self.avg_payout_atr {
2863                error!(
2864                    "avg_payout_atr actual: {:?} expected : {:?}",
2865                    self.avg_payout_atr, cv.avg_payout_atr
2866                );
2867                return false;
2868            }
2869
2870            //
2871            // avg_fee_per_byte
2872            //
2873            if cv.avg_fee_per_byte != self.avg_fee_per_byte {
2874                error!(
2875                    "ERROR 202392: avg_fee_per_byte is invalid. expected: {:?} vs actual : {:?}",
2876                    cv.avg_fee_per_byte, self.avg_fee_per_byte
2877                );
2878                return false;
2879            }
2880
2881            //
2882            // fee_per_byte
2883            //
2884            if cv.fee_per_byte != self.fee_per_byte {
2885                error!(
2886                    "ERROR 202392: fee_per_byte is invalid. expected: {:?} vs actual : {:?}",
2887                    cv.fee_per_byte, self.fee_per_byte
2888                );
2889                return false;
2890            }
2891
2892            //
2893            // consensus values -> difficulty (mining/payout unlock difficulty)
2894            //
2895            if cv.avg_nolan_rebroadcast_per_block != self.avg_nolan_rebroadcast_per_block {
2896                error!(
2897                "ERROR 202392: avg_nolan_rebroadcast_per_block is invalid. expected: {:?} vs actual : {:?}",
2898                cv.avg_nolan_rebroadcast_per_block, self.avg_nolan_rebroadcast_per_block
2899            );
2900                return false;
2901            }
2902        }
2903
2904        //
2905        // burnfee
2906        //
2907        if cv.burnfee != self.burnfee {
2908            error!(
2909                "block is misreporting its burnfee. current : {:?} expected : {:?}",
2910                self.burnfee, cv.burnfee
2911            );
2912            return false;
2913        }
2914
2915        //
2916        // difficulty
2917        //
2918        if cv.difficulty != self.difficulty {
2919            error!(
2920                "ERROR 202392: difficulty is invalid. expected: {:?} vs actual : {:?}",
2921                cv.difficulty, self.difficulty
2922            );
2923            return false;
2924        }
2925
2926        //
2927        // issuance transactions (only possible in block #1)
2928        //
2929        if cv.it_num > 0 && self.id > 1 {
2930            error!("ERROR: blockchain contains issuance after block 1 in chain",);
2931            return false;
2932        }
2933
2934        //
2935        // social staking transactions (if required)
2936        //
2937        if blockchain.social_stake_requirement != 0 && cv.st_num != 1 && self.id > 1 {
2938            error!(
2939                "block : {:?} does not have a staking transaction",
2940                self.hash.to_hex()
2941            );
2942            return false;
2943        }
2944
2945        //
2946        // validation of the following requires a previous-block to exist
2947        //
2948        if let Some(previous_block) = blockchain.blocks.get(&self.previous_block_hash) {
2949            //
2950            // ghost blocks
2951            //
2952            if let BlockType::Ghost = previous_block.block_type {
2953                return true;
2954            }
2955
2956            //
2957            // treasury
2958            //
2959            let mut expected_treasury = previous_block.treasury;
2960            expected_treasury += cv.total_payout_treasury;
2961            expected_treasury -= cv.total_payout_atr;
2962            if validate_against_utxo && self.treasury != expected_treasury {
2963                error!(
2964                    "ERROR 820391: treasury does not validate: {} expected versus {} found",
2965                    expected_treasury, self.treasury,
2966                );
2967                return false;
2968            }
2969
2970            //
2971            // graveyard
2972            //
2973            let mut expected_graveyard = previous_block.graveyard;
2974            expected_graveyard += cv.total_payout_graveyard;
2975            if validate_against_utxo && self.graveyard != expected_graveyard {
2976                error!(
2977                    "ERROR 123243: graveyard does not validate: {} expected versus {} found",
2978                    expected_graveyard, self.graveyard,
2979                );
2980                return false;
2981            }
2982
2983            //
2984            // validate routing work required
2985            //
2986            let amount_of_routing_work_needed: Currency =
2987                BurnFee::return_routing_work_needed_to_produce_block_in_nolan(
2988                    previous_block.burnfee,
2989                    self.timestamp,
2990                    previous_block.timestamp,
2991                    configs.get_consensus_config().unwrap().heartbeat_interval,
2992                );
2993            if self.total_work < amount_of_routing_work_needed {
2994                error!("Error 510293: block lacking adequate routing work from creator. actual : {:?} expected : {:?}",self.total_work, amount_of_routing_work_needed);
2995                return false;
2996            }
2997
2998            //
2999            // validate golden ticket
3000            //
3001            // the golden ticket is a special kind of transaction that stores the
3002            // solution to the network-payment lottery in the transaction message
3003            // field. it targets the hash of the previous block, which is why we
3004            // tackle it's validation logic here.
3005            //
3006            // first we reconstruct the ticket, then calculate that the solution
3007            // meets our consensus difficulty criteria. note that by this point in
3008            // the validation process we have already examined the fee transaction
3009            // which was generated using this solution. If the solution is invalid
3010            // we find that out now, and it invalidates the block.
3011            //
3012            if let Some(gt_index) = cv.gt_index {
3013                let golden_ticket: GoldenTicket =
3014                    GoldenTicket::deserialize_from_net(&self.transactions[gt_index].data);
3015
3016                //
3017                // we already have a golden ticket, but create a new one pulling the
3018                // target hash from our previous block to ensure that this ticket is
3019                // actually valid in the context of our blockchain, and not just
3020                // internally consistent in the blockchain of the sender.
3021                //
3022                let gt = GoldenTicket::create(
3023                    previous_block.hash,
3024                    golden_ticket.random,
3025                    golden_ticket.public_key,
3026                );
3027
3028                //
3029                // if there is a golden ticket, our previous_block_unpaid should be
3030                // zero, as we will have issued payment in this block.
3031                //
3032                if self.previous_block_unpaid != 0 {
3033                    error!("ERROR 720351: golden ticket but previous block incorrect");
3034                    return false;
3035                }
3036
3037                //
3038                // we confirm that the golden ticket is targetting the block hash
3039                // of the previous block. the solution is invalid if it is not
3040                // current with the state of the chain..
3041                trace!("validating gt...");
3042                if !gt.validate(previous_block.difficulty) {
3043                    error!(
3044                        "ERROR 801923: Golden Ticket solution does not validate against previous_block_hash : {:?}, difficulty : {:?}, random : {:?}, public_key : {:?} target : {:?}",
3045                        previous_block.hash.to_hex(),
3046                        previous_block.difficulty,
3047                        gt.random.to_hex(),
3048                        gt.public_key.to_base58(),
3049                        gt.target.to_hex()
3050                    );
3051                    let solution = hash(&gt.serialize_for_net());
3052                    let solution_num = primitive_types::U256::from_big_endian(&solution);
3053
3054                    error!(
3055                        "solution : {:?} leading zeros : {:?}",
3056                        solution.to_hex(),
3057                        solution_num.leading_zeros()
3058                    );
3059                    return false;
3060                }
3061                trace!("gt validated !");
3062            } else {
3063                //
3064                // if there is no golden ticket, our previous block's total_fees will
3065                // be stored in this block as previous_block_unpaid. this simplifies
3066                // smoothing payouts, and assists with monitoring that the total token
3067                // supply has not changed.
3068                //
3069                if self.previous_block_unpaid != previous_block.total_fees {
3070                    error!("ERROR 572983: previous_block_unpaid value incorrect");
3071                    return false;
3072                }
3073            }
3074            // trace!(" ... golden ticket: (validated)  {:?}", create_timestamp());
3075        }
3076
3077        //
3078        // validate atr
3079        //
3080        // Automatic Transaction Rebroadcasts are removed programmatically from
3081        // an earlier block in the blockchain and rebroadcast into the latest
3082        // block, with a fee being deducted to keep the data on-chain. In order
3083        // to validate ATR we need to make sure we have the correct number of
3084        // transactions (and ONLY those transactions!) included in our block.
3085        //
3086        // we do this by comparing the total number of ATR slips and nolan
3087        // which we counted in the generate_metadata() function, with the
3088        // expected number given the consensus values we calculated earlier.
3089        //
3090        if validate_against_utxo && cv.total_rebroadcast_slips != self.total_rebroadcast_slips {
3091            error!(
3092                "ERROR 624442: rebroadcast slips total incorrect. expected : {:?} actual : {:?}",
3093                cv.total_rebroadcast_slips, self.total_rebroadcast_slips
3094            );
3095            return false;
3096        }
3097        // deprecated -- Jan 20, 2025
3098        //if cv.total_rebroadcast_nolan != self.total_rebroadcast_nolan {
3099        //    error!(
3100        //        "ERROR 294018: rebroadcast nolan amount incorrect. expected : {:?} actual : {:?}",
3101        //        cv.total_rebroadcast_nolan, self.total_rebroadcast_nolan
3102        //    );
3103        //    return false;
3104        //}
3105        if validate_against_utxo && cv.rebroadcast_hash != self.rebroadcast_hash {
3106            error!("ERROR 123422: hash of rebroadcast transactions incorrect. expected : {:?} actual : {:?}",cv.rebroadcast_hash.to_hex(), self.rebroadcast_hash.to_hex());
3107            return false;
3108        }
3109
3110        //
3111        // merkle root
3112        //
3113        if self.merkle_root == [0; 32]
3114            && self.merkle_root
3115                != self.generate_merkle_root(configs.is_browser(), configs.is_spv_mode())
3116        {
3117            error!("merkle root is unset or is invalid false 1");
3118            return false;
3119        }
3120
3121        //
3122        // fee transaction
3123        //
3124        // because the fee transaction that is created by generate_consensus_values is
3125        // produced without knowledge of the block in which it will be put, we need to
3126        // update that transaction with this information prior to hashing it in order
3127        // for the hash-comparison to work.
3128        //
3129        if cv.ft_num > 0 {
3130            if let (Some(ft_index), Some(fee_transaction_expected)) =
3131                (cv.ft_index, cv.fee_transaction)
3132            {
3133                if cv.gt_index.is_none() {
3134                    error!("ERROR 48203: block has fee transaction but no golden ticket");
3135                    return false;
3136                }
3137
3138                //
3139                // the fee transaction is hashed to compare it with the one in the block
3140                //
3141                let fee_transaction_in_block = self.transactions.get(ft_index).unwrap();
3142                let hash1 = hash(&fee_transaction_expected.serialize_for_signature());
3143                let hash2 = hash(&fee_transaction_in_block.serialize_for_signature());
3144
3145                if validate_against_utxo && hash1 != hash2 {
3146                    error!(
3147                        "ERROR 892032: block {} fee transaction doesn't match cv-expected fee transaction",
3148                        self.id
3149                    );
3150                    error!(
3151                        "expected = {:?}",
3152                        &fee_transaction_expected.serialize_for_signature()
3153                    );
3154                    error!(
3155                        "actual   = {:?}",
3156                        &fee_transaction_in_block.serialize_for_signature()
3157                    );
3158                    if let Some(gt_index) = cv.gt_index {
3159                        let golden_ticket: GoldenTicket =
3160                            GoldenTicket::deserialize_from_net(&self.transactions[gt_index].data);
3161                        error!("gt.publickey = {:?}", golden_ticket.public_key.to_hex());
3162                    }
3163
3164                    return false;
3165                }
3166            }
3167        }
3168
3169        //
3170        // validate transactions
3171        //
3172        // validating transactions requires checking that the signatures are valid,
3173        // the routing paths are valid, and all of the input slips are pointing
3174        // to spendable tokens that exist in our UTXOSET. this logic is separate
3175        // from the validation of block-level variables, so is handled in the
3176        // transaction objects.
3177        //
3178        // this is one of the most computationally intensive parts of processing a
3179        // block which is why we handle it in parallel. the exact logic needed to
3180        // examine a transaction may depend on the transaction itself, as we have
3181        // some specific types (Fee / ATR / etc.) that are generated automatically
3182        // and may have different requirements.
3183        //
3184        // the validation logic for transactions is contained in the transaction
3185        // class, and the validation logic for slips is contained in the slips
3186        // class. Note that we are passing in a read-only copy of our UTXOSet so
3187        // as to determine spendability.
3188
3189        trace!(
3190            "validating transactions ... count : {:?}",
3191            self.transactions.len()
3192        );
3193        let mut new_slips_map = std::collections::HashMap::new();
3194        let transactions_valid = self.transactions.iter().all(|tx: &Transaction| -> bool {
3195            let valid_tx = tx.validate(utxoset, blockchain, validate_against_utxo);
3196            // validate double-spend inputs
3197            if valid_tx && tx.transaction_type != TransactionType::Fee {
3198                for input in tx.from.iter() {
3199                    if input.amount == 0 || input.slip_type == SlipType::Bound {
3200                        continue;
3201                    }
3202                    let utxo_key = input.get_utxoset_key();
3203
3204                    if new_slips_map.contains_key(&utxo_key) {
3205                        error!(
3206                            "double-spend detected in block {} : {} in block.validate()",
3207                            self.id,
3208                            Slip::parse_slip_from_utxokey(&utxo_key).unwrap()
3209                        );
3210                        return false;
3211                    }
3212
3213                    new_slips_map.insert(utxo_key, 1);
3214                }
3215            }
3216            true
3217        });
3218
3219        if !transactions_valid {
3220            error!("ERROR 579128: Invalid transactions found, block validation failed");
3221        }
3222        trace!("transactions validation complete");
3223
3224        transactions_valid
3225    }
3226
3227    pub fn generate_transaction_hashmap(&mut self) {
3228        if !self.transaction_map.is_empty() {
3229            return;
3230        }
3231        for tx in self.transactions.iter() {
3232            for slip in tx.from.iter() {
3233                self.transaction_map.insert(slip.public_key, true);
3234            }
3235            for slip in tx.to.iter() {
3236                self.transaction_map.insert(slip.public_key, true);
3237            }
3238        }
3239    }
3240    pub fn has_keylist_txs(&self, keylist: &Vec<SaitoPublicKey>) -> bool {
3241        for key in keylist {
3242            if self.transaction_map.contains_key(key) {
3243                return true;
3244            }
3245        }
3246        false
3247    }
3248
3249    pub fn get_file_name(&self) -> String {
3250        let timestamp = self.timestamp;
3251        let block_hash = self.hash;
3252
3253        timestamp.to_string() + "-" + block_hash.to_hex().as_str() + BLOCK_FILE_EXTENSION
3254    }
3255    pub fn print_all(&self) {
3256        info!(
3257            "Block {{ id: {}, timestamp: {}, previous_block_hash: {:?}, creator: {:?}, merkle_root: {:?}, signature: {:?}, graveyard: {}, treasury: {}, total_fees: {}, total_fees_new: {}, total_fees_atr: {}, avg_total_fees: {}, avg_total_fees_new: {}, avg_total_fees_atr: {}, total_payout_routing: {}, total_payout_mining: {}, total_payout_treasury: {}, total_payout_graveyard: {}, total_payout_atr: {}, avg_payout_routing: {}, avg_payout_mining: {}, avg_payout_treasury: {}, avg_payout_graveyard: {}, avg_payout_atr: {}, avg_fee_per_byte: {}, fee_per_byte: {}, avg_nolan_rebroadcast_per_block: {}, burnfee: {}, difficulty: {}, previous_block_unpaid: {}, hash: {:?}, total_work: {}, in_longest_chain: {}, has_golden_ticket: {}, has_issuance_transaction: {}, issuance_transaction_index: {}, has_fee_transaction: {}, has_staking_transaction: {}, golden_ticket_index: {}, fee_transaction_index: {}, total_rebroadcast_slips: {}, total_rebroadcast_nolan: {}, rebroadcast_hash: {}, block_type: {:?}, cv: {}, routed_from_peer: {:?} ",
3258            self.id,
3259            self.timestamp,
3260            self.previous_block_hash.to_hex(),
3261            self.creator.to_base58(),
3262            self.merkle_root.to_hex(),
3263            self.signature.to_hex(),
3264            self.graveyard,
3265            self.treasury,
3266            self.total_fees,
3267            self.total_fees_new,
3268            self.total_fees_atr,
3269            self.avg_total_fees,
3270            self.avg_total_fees_new,
3271            self.avg_total_fees_atr,
3272            self.total_payout_routing,
3273            self.total_payout_mining,
3274            self.total_payout_treasury,
3275            self.total_payout_graveyard,
3276            self.total_payout_atr,
3277            self.avg_payout_routing,
3278            self.avg_payout_mining,
3279            self.avg_payout_treasury,
3280            self.avg_payout_graveyard,
3281            self.avg_payout_atr,
3282            self.avg_fee_per_byte,
3283            self.fee_per_byte,
3284            self.avg_nolan_rebroadcast_per_block,
3285            self.burnfee,
3286            self.difficulty,
3287            self.previous_block_unpaid,
3288            self.hash.to_hex(),
3289            self.total_work,
3290            self.in_longest_chain,
3291            self.has_golden_ticket,
3292            self.has_issuance_transaction,
3293            self.issuance_transaction_index,
3294            self.has_fee_transaction,
3295            self.has_staking_transaction,
3296            self.golden_ticket_index,
3297            self.fee_transaction_index,
3298            self.total_rebroadcast_slips,
3299            self.total_rebroadcast_nolan,
3300            self.rebroadcast_hash.to_hex(),
3301            self.block_type,
3302            self.cv,
3303            self.routed_from_peer,
3304        );
3305        info!(" transactions : ");
3306        for (index, tx) in self.transactions.iter().enumerate() {
3307            info!("tx {} : {}", index, tx);
3308        }
3309    }
3310}
3311
3312#[cfg(test)]
3313mod tests {
3314    use ahash::AHashMap;
3315    use futures::future::join_all;
3316    use log::info;
3317
3318    use crate::core::consensus::block::{Block, BlockType};
3319
3320    use crate::core::consensus::merkle::MerkleTree;
3321    use crate::core::consensus::slip::{Slip, SlipType};
3322    use crate::core::consensus::transaction::{Transaction, TransactionType};
3323    use crate::core::consensus::wallet::Wallet;
3324    use crate::core::defs::{
3325        Currency, PrintForLog, SaitoHash, SaitoPrivateKey, SaitoPublicKey, NOLAN_PER_SAITO,
3326    };
3327    use crate::core::io::storage::Storage;
3328    use crate::core::util::crypto::{generate_keys, verify_signature};
3329    use crate::core::util::test::node_tester::test::NodeTester;
3330    use crate::core::util::test::test_manager::test::TestManager;
3331
3332    #[test]
3333    fn block_new_test() {
3334        let block = Block::new();
3335        assert_eq!(block.id, 0);
3336        assert_eq!(block.timestamp, 0);
3337        assert_eq!(block.previous_block_hash, [0; 32]);
3338        assert_eq!(block.creator, [0; 33]);
3339        assert_eq!(block.merkle_root, [0; 32]);
3340        assert_eq!(block.signature, [0; 64]);
3341        assert_eq!(block.graveyard, 0);
3342        assert_eq!(block.burnfee, 0);
3343        assert_eq!(block.difficulty, 0);
3344        assert_eq!(block.transactions, vec![]);
3345        assert_eq!(block.pre_hash, [0; 32]);
3346        assert_eq!(block.hash, [0; 32]);
3347        assert_eq!(block.total_fees, 0);
3348        assert_eq!(block.total_work, 0);
3349        assert_eq!(block.in_longest_chain, false);
3350        assert_eq!(block.has_golden_ticket, false);
3351        assert_eq!(block.has_issuance_transaction, false);
3352        assert_eq!(block.issuance_transaction_index, 0);
3353        assert_eq!(block.has_fee_transaction, false);
3354        assert_eq!(block.fee_transaction_index, 0);
3355        assert_eq!(block.golden_ticket_index, 0);
3356        assert_eq!(block.total_rebroadcast_slips, 0);
3357        assert_eq!(block.total_rebroadcast_nolan, 0);
3358        assert_eq!(block.rebroadcast_hash, [0; 32]);
3359        assert_eq!(block.block_type, BlockType::Full);
3360        assert_eq!(block.slips_spent_this_block, AHashMap::new());
3361        assert_eq!(block.created_hashmap_of_slips_spent_this_block, false);
3362        assert_eq!(block.routed_from_peer, None);
3363    }
3364
3365    #[test]
3366    fn block_generate_test() {
3367        let mut block = Block::new();
3368        block.generate().unwrap();
3369
3370        // block hashes should have updated
3371        assert_ne!(block.pre_hash, [0; 32]);
3372        assert_ne!(block.hash, [0; 32]);
3373        assert_ne!(block.pre_hash, [0; 32]);
3374        assert_ne!(block.hash, [0; 32]);
3375        assert_eq!(block.pre_hash, block.pre_hash);
3376        assert_eq!(block.hash, block.hash);
3377    }
3378
3379    #[test]
3380    fn block_signature_test() {
3381        let mut block = Block::new();
3382
3383        block.id = 10;
3384        block.timestamp = 1637034582;
3385        block.previous_block_hash = <SaitoHash>::from_hex(
3386            "bcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8b",
3387        )
3388        .unwrap();
3389        block.merkle_root = <SaitoHash>::from_hex(
3390            "ccf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8b",
3391        )
3392        .unwrap();
3393        block.creator = <SaitoPublicKey>::from_hex(
3394            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
3395        )
3396        .unwrap();
3397        block.burnfee = 50000000;
3398        block.difficulty = 0;
3399        block.graveyard = 0;
3400        block.treasury = 0;
3401        block.signature = <[u8; 64]>::from_hex("c9a6c2d0bf884be6933878577171a3c8094c2bf6e0bc1b4ec3535a4a55224d186d4d891e254736cae6c0d2002c8dfc0ddfc7fcdbe4bc583f96fa5b273b9d63f4").unwrap();
3402
3403        let serialized_body = block.serialize_for_signature();
3404        assert_eq!(serialized_body.len(), 209);
3405
3406        block.creator = <SaitoPublicKey>::from_hex(
3407            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
3408        )
3409        .unwrap();
3410
3411        block.sign(
3412            &<SaitoHash>::from_hex(
3413                "854702489d49c7fb2334005b903580c7a48fe81121ff16ee6d1a528ad32f235d",
3414            )
3415            .unwrap(),
3416        );
3417
3418        assert_eq!(block.signature.len(), 64);
3419    }
3420
3421    #[test]
3422    fn block_serialization_and_deserialization_test() {
3423        // pretty_env_logger::init();
3424        let mock_input = Slip::default();
3425        let mock_output = Slip::default();
3426
3427        let mut mock_tx = Transaction::default();
3428        mock_tx.timestamp = 0;
3429        mock_tx.add_from_slip(mock_input.clone());
3430        mock_tx.add_to_slip(mock_output.clone());
3431        mock_tx.data = vec![104, 101, 108, 111];
3432        mock_tx.transaction_type = TransactionType::Normal;
3433        mock_tx.signature = [1; 64];
3434
3435        let mut mock_tx2 = Transaction::default();
3436        mock_tx2.timestamp = 0;
3437        mock_tx2.add_from_slip(mock_input);
3438        mock_tx2.add_to_slip(mock_output);
3439        mock_tx2.data = vec![];
3440        mock_tx2.transaction_type = TransactionType::Normal;
3441        mock_tx2.signature = [2; 64];
3442
3443        let timestamp = 0;
3444
3445        let mut block = Block::new();
3446        block.id = 1;
3447        block.timestamp = timestamp;
3448        block.previous_block_hash = [1; 32];
3449        block.creator = [2; 33];
3450        block.merkle_root = [0; 32];
3451        block.signature = [4; 64];
3452        block.graveyard = 1_000_000;
3453        block.burnfee = 2;
3454        block.difficulty = 3;
3455        block.transactions = vec![mock_tx, mock_tx2];
3456        block.generate().unwrap();
3457
3458        let serialized_block = block.serialize_for_net(BlockType::Full);
3459        let deserialized_block = Block::deserialize_from_net(&serialized_block).unwrap();
3460
3461        let serialized_block_header = block.serialize_for_net(BlockType::Header);
3462        let deserialized_block_header =
3463            Block::deserialize_from_net(&serialized_block_header).unwrap();
3464
3465        assert_eq!(
3466            block.serialize_for_net(BlockType::Full),
3467            deserialized_block.serialize_for_net(BlockType::Full)
3468        );
3469
3470        assert_eq!(deserialized_block.id, 1);
3471        assert_eq!(deserialized_block.timestamp, timestamp);
3472        assert_eq!(deserialized_block.previous_block_hash, [1; 32]);
3473        assert_eq!(deserialized_block.creator, [2; 33]);
3474        assert_ne!(deserialized_block.merkle_root, [0; 32]);
3475        assert_eq!(deserialized_block.signature, [4; 64]);
3476        assert_eq!(deserialized_block.graveyard, 1_000_000);
3477        assert_eq!(deserialized_block.burnfee, 2);
3478        assert_eq!(deserialized_block.difficulty, 3);
3479
3480        assert_eq!(
3481            deserialized_block_header.serialize_for_net(BlockType::Full),
3482            deserialized_block.serialize_for_net(BlockType::Header)
3483        );
3484
3485        assert_eq!(deserialized_block_header.id, 1);
3486        assert_eq!(deserialized_block_header.timestamp, timestamp);
3487        assert_eq!(deserialized_block_header.previous_block_hash, [1; 32]);
3488        assert_eq!(deserialized_block_header.creator, [2; 33]);
3489        assert_ne!(deserialized_block_header.merkle_root, [0; 32]);
3490        assert_eq!(deserialized_block_header.signature, [4; 64]);
3491        assert_eq!(deserialized_block_header.graveyard, 1_000_000);
3492        assert_eq!(deserialized_block_header.burnfee, 2);
3493        assert_eq!(deserialized_block_header.difficulty, 3);
3494
3495        let mut lite_block = block.generate_lite_block(vec![]);
3496        lite_block.generate().unwrap();
3497        assert_eq!(block.id, lite_block.id);
3498        assert_eq!(block.timestamp, lite_block.timestamp);
3499        assert_eq!(
3500            block.previous_block_hash.to_hex(),
3501            lite_block.previous_block_hash.to_hex()
3502        );
3503        assert_eq!(block.creator, lite_block.creator);
3504        assert_eq!(block.graveyard, lite_block.graveyard);
3505        assert_eq!(block.treasury, lite_block.treasury);
3506        assert_eq!(block.burnfee, lite_block.burnfee);
3507        assert_eq!(block.difficulty, lite_block.difficulty);
3508        assert_eq!(block.avg_total_fees, lite_block.avg_total_fees);
3509        assert_eq!(block.avg_fee_per_byte, lite_block.avg_fee_per_byte);
3510        assert_eq!(
3511            block.avg_nolan_rebroadcast_per_block,
3512            lite_block.avg_nolan_rebroadcast_per_block
3513        );
3514        assert_eq!(
3515            block.previous_block_unpaid,
3516            lite_block.previous_block_unpaid
3517        );
3518        assert_eq!(block.merkle_root.to_hex(), lite_block.merkle_root.to_hex());
3519
3520        assert_eq!(block.pre_hash.to_hex(), lite_block.pre_hash.to_hex());
3521        assert_eq!(block.hash.to_hex(), lite_block.hash.to_hex());
3522    }
3523
3524    #[test]
3525    fn block_sign_and_verify_test() {
3526        let keys = generate_keys();
3527
3528        let wallet = Wallet::new(keys.1, keys.0);
3529        let mut block = Block::new();
3530        block.creator = wallet.public_key;
3531        block.generate().unwrap();
3532        block.sign(&wallet.private_key);
3533        block.generate_hash();
3534
3535        assert_eq!(block.creator, wallet.public_key);
3536        assert!(verify_signature(
3537            &block.pre_hash,
3538            &block.signature,
3539            &block.creator,
3540        ));
3541        assert_ne!(block.hash, [0; 32]);
3542        assert_ne!(block.signature, [0; 64]);
3543    }
3544
3545    #[test]
3546    fn block_merkle_root_test() {
3547        let mut block = Block::new();
3548        let keys = generate_keys();
3549        let wallet = Wallet::new(keys.1, keys.0);
3550
3551        let transactions: Vec<Transaction> = (0..5)
3552            .map(|_| {
3553                let mut transaction = Transaction::default();
3554                transaction.sign(&wallet.private_key);
3555                transaction
3556            })
3557            .collect();
3558
3559        block.transactions = transactions;
3560        block.merkle_root = block.generate_merkle_root(false, false);
3561
3562        assert_eq!(block.merkle_root.len(), 32);
3563        assert_ne!(block.merkle_root, [0; 32]);
3564    }
3565
3566    #[tokio::test]
3567    #[serial_test::serial]
3568    // downgrade and upgrade a block with transactions
3569    async fn block_downgrade_upgrade_test() {
3570        let mut t = TestManager::default();
3571        let wallet_lock = t.wallet_lock.clone();
3572        let mut block = Block::new();
3573        let transactions = join_all((0..5).map(|_| async {
3574            let mut transaction = Transaction::default();
3575            let wallet = wallet_lock.read().await;
3576
3577            transaction.sign(&wallet.private_key);
3578            transaction
3579        }))
3580        .await
3581        .to_vec();
3582
3583        block.transactions = transactions;
3584        block.generate().unwrap();
3585
3586        // save to disk
3587        t.storage.write_block_to_disk(&mut block).await;
3588
3589        assert_eq!(block.transactions.len(), 5);
3590        assert_eq!(block.block_type, BlockType::Full);
3591
3592        let serialized_full_block = block.serialize_for_net(BlockType::Full);
3593        block
3594            .update_block_to_block_type(BlockType::Pruned, &t.storage, false)
3595            .await;
3596
3597        assert_eq!(block.transactions.len(), 0);
3598        assert_eq!(block.block_type, BlockType::Pruned);
3599
3600        block
3601            .update_block_to_block_type(BlockType::Full, &t.storage, false)
3602            .await;
3603
3604        assert_eq!(block.transactions.len(), 5);
3605        assert_eq!(block.block_type, BlockType::Full);
3606        assert_eq!(
3607            serialized_full_block,
3608            block.serialize_for_net(BlockType::Full)
3609        );
3610    }
3611
3612    #[tokio::test]
3613    #[serial_test::serial]
3614    async fn generate_lite_block_test() {
3615        let mut t = TestManager::default();
3616
3617        // test blocks with transactions
3618        // Block 1
3619        // perform transaction to wallet public key
3620        t.initialize(100, 200_000_000_000_000).await;
3621        let block1 = t.get_latest_block().await;
3622        let public_key: SaitoPublicKey;
3623        {
3624            let wallet = t.wallet_lock.read().await;
3625            public_key = wallet.public_key;
3626        }
3627        let lite_block = block1.generate_lite_block(vec![public_key]);
3628        assert_eq!(lite_block.hash, block1.hash);
3629        assert_eq!(lite_block.signature, block1.signature);
3630
3631        // Second Block
3632        // perform a transaction to public key
3633        let public_key =
3634            Storage::decode_str("s8oFPjBX97NC2vbm9E5Kd2oHWUShuSTUuZwSB1U4wsPR").unwrap();
3635        let mut to_public_key: SaitoPublicKey = [0u8; 33];
3636        to_public_key.copy_from_slice(&public_key);
3637        t.transfer_value_to_public_key(to_public_key, 500, block1.timestamp + 120000)
3638            .await
3639            .unwrap();
3640        let block2 = t.get_latest_block().await;
3641        let mut lite_block2 = block2.generate_lite_block(vec![to_public_key]);
3642        assert_eq!(lite_block2.signature, block2.clone().signature);
3643        assert_eq!(lite_block2.hash, block2.hash);
3644        assert_eq!(lite_block2.merkle_root, block2.merkle_root);
3645        assert_eq!(lite_block2.difficulty, block2.difficulty);
3646        assert_eq!(lite_block2.id, block2.id);
3647        assert_eq!(lite_block2.block_type, BlockType::Full);
3648
3649        lite_block2.generate().unwrap();
3650        assert_eq!(lite_block2.signature, block2.clone().signature);
3651        assert_eq!(lite_block2.hash, block2.hash);
3652
3653        let buffer = lite_block2.serialize_for_net(BlockType::Pruned);
3654        let mut block2 = Block::deserialize_from_net(&buffer).unwrap();
3655        block2.generate().unwrap();
3656        assert_eq!(lite_block2.signature, block2.clone().signature);
3657        assert_eq!(lite_block2.hash, block2.hash);
3658
3659        // block 3
3660        // Perform no transaction
3661        let block2_hash = block2.hash;
3662        let mut block3 = t
3663            .create_block(
3664                block2_hash,               // hash of parent block
3665                block2.timestamp + 120000, // timestamp
3666                0,                         // num transactions
3667                0,                         // amount
3668                0,                         // fee
3669                true,                      // mine golden ticket
3670            )
3671            .await;
3672        block3.generate().unwrap(); // generate hashes
3673        dbg!(block3.id);
3674    }
3675
3676    #[tokio::test]
3677    #[serial_test::serial]
3678    async fn verify_spv_transaction_in_lite_block_test() {
3679        let mut t = TestManager::default();
3680
3681        // Initialize the test manager
3682        t.initialize(100, 100000).await;
3683
3684        // Retrieve the latest block
3685        let mut block = t.get_latest_block().await;
3686
3687        // Get the wallet's keys
3688        let private_key: SaitoPrivateKey;
3689        let public_key: SaitoPublicKey;
3690        {
3691            let wallet = t.wallet_lock.read().await;
3692
3693            public_key = wallet.public_key;
3694            private_key = wallet.private_key;
3695        }
3696
3697        // Set up the recipient's public key
3698        let _public_key = "27UK2MuBTdeARhYp97XBnCovGkEquJjkrQntCgYoqj6GC";
3699        let mut to_public_key: SaitoPublicKey = [0u8; 33];
3700
3701        // Create VIP transactions
3702        for _ in 0..50 {
3703            let public_key_ = Storage::decode_str(_public_key).unwrap();
3704            to_public_key.copy_from_slice(&public_key_);
3705            let mut tx = Transaction::default();
3706            tx.transaction_type = TransactionType::Normal;
3707            let mut output = Slip::default();
3708            output.public_key = to_public_key;
3709            output.amount = 0;
3710            output.slip_type = SlipType::Normal;
3711            tx.add_to_slip(output);
3712            tx.generate(&public_key, 0, 0);
3713            tx.sign(&private_key);
3714            // dbg!(&tx.hash_for_signature);
3715            block.add_transaction(tx);
3716        }
3717
3718        {
3719            let configs = t.config_lock.read().await;
3720            block.merkle_root =
3721                block.generate_merkle_root(configs.is_browser(), configs.is_spv_mode());
3722        }
3723
3724        // Generate and sign the block
3725        block.generate().unwrap();
3726        block.sign(&private_key);
3727
3728        // Generate a lite block from the full block, using the public keys for SPV transactions
3729        let lite_block = block.generate_lite_block(vec![public_key]);
3730
3731        // Find the SPV transaction in the lite block
3732        let _spv_tx = lite_block
3733            .transactions
3734            .iter()
3735            .find(|&tx| tx.transaction_type == TransactionType::SPV)
3736            .expect("No SPV transaction found")
3737            .clone();
3738
3739        // Generate a Merkle tree from the block transactions
3740        let _merkle_tree = MerkleTree::generate(&lite_block.transactions)
3741            .expect("Failed to generate Merkle tree for block");
3742
3743        dbg!(
3744            lite_block.generate_merkle_root(false, false),
3745            block.generate_merkle_root(false, false)
3746        );
3747    }
3748
3749    #[tokio::test]
3750    #[ignore]
3751    #[serial_test::serial]
3752    async fn avg_fee_per_byte_test() {
3753        // pretty_env_logger::init();
3754        let mut t = TestManager::default();
3755
3756        // Initialize the test manager
3757        t.initialize(250, 200_000_000_000_000).await;
3758
3759        let latest_block = t.get_latest_block().await;
3760
3761        let mut block = t
3762            .create_block(
3763                latest_block.hash,
3764                latest_block.timestamp + 40000,
3765                100,
3766                1000,
3767                1_000_000,
3768                true,
3769            )
3770            .await;
3771
3772        block.generate().unwrap();
3773
3774        let mut tx_size = 0;
3775        let mut total_fees = 0;
3776
3777        for tx in &block.transactions {
3778            if !tx.is_fee_transaction() {
3779                tx_size += tx.serialize_for_net().len();
3780                total_fees += tx.total_fees;
3781            }
3782        }
3783
3784        info!(
3785            "avg fee per byte 1: {:?} total fees = {:?} tx size = {:?} tx count = {:?}",
3786            block.avg_fee_per_byte,
3787            total_fees,
3788            tx_size,
3789            block.transactions.len()
3790        );
3791        assert_eq!(block.avg_fee_per_byte, total_fees / tx_size as Currency);
3792
3793        let mut block = t
3794            .create_block(
3795                latest_block.hash,
3796                latest_block.timestamp + 140000,
3797                100,
3798                1000,
3799                1_000_000,
3800                false,
3801            )
3802            .await;
3803
3804        block.generate().unwrap();
3805
3806        let mut tx_size = 0;
3807        let mut total_fees = 0;
3808
3809        for tx in &block.transactions {
3810            if !tx.is_fee_transaction() {
3811                tx_size += tx.serialize_for_net().len();
3812                total_fees += tx.total_fees;
3813            }
3814        }
3815        info!(
3816            "avg fee per byte 2: {:?} total fees = {:?} tx size = {:?} tx count = {:?}",
3817            block.avg_fee_per_byte,
3818            total_fees,
3819            tx_size,
3820            block.transactions.len()
3821        );
3822        assert_eq!(block.avg_fee_per_byte, total_fees / tx_size as Currency);
3823    }
3824
3825    #[ignore]
3826    #[tokio::test]
3827    #[serial_test::serial]
3828    async fn atr_test() {
3829        // pretty_env_logger::init();
3830
3831        // create test manager
3832        let mut t = TestManager::default();
3833
3834        t.initialize_with_timestamp(100, 10000, 0).await;
3835        let genesis_period = t
3836            .config_lock
3837            .read()
3838            .await
3839            .get_consensus_config()
3840            .unwrap()
3841            .genesis_period;
3842        // check if epoch length is 10
3843        assert_eq!(genesis_period, 100, "Genesis period is not 10");
3844
3845        // create 10 blocks
3846        for _i in 0..genesis_period {
3847            let mut block = t
3848                .create_block(
3849                    t.latest_block_hash,
3850                    t.get_latest_block().await.timestamp + 10_000,
3851                    10,
3852                    100,
3853                    10,
3854                    true,
3855                )
3856                .await;
3857            block.generate().unwrap();
3858            t.add_block(block).await;
3859        }
3860
3861        // check consensus values for 10th block
3862        t.check_blockchain().await;
3863        t.check_utxoset().await;
3864        t.check_token_supply().await;
3865
3866        let latest_block = t.get_latest_block().await;
3867        let cv = latest_block.cv;
3868
3869        println!("cv : {:?} \n", cv);
3870
3871        assert_eq!(cv.rebroadcasts.len(), 0);
3872        assert_eq!(cv.avg_nolan_rebroadcast_per_block, 0);
3873
3874        // add 11th block
3875        let mut block = t
3876            .create_block(
3877                t.latest_block_hash,
3878                t.get_latest_block().await.timestamp + 10_000,
3879                10,
3880                100,
3881                10,
3882                true,
3883            )
3884            .await;
3885        block.generate().unwrap();
3886        t.add_block(block).await;
3887
3888        // check consensus values for 11th block
3889        t.check_blockchain().await;
3890        t.check_utxoset().await;
3891        t.check_token_supply().await;
3892
3893        let latest_block = t.get_latest_block().await;
3894        let cv = latest_block.cv;
3895
3896        println!("cv2 : {:?}", cv);
3897
3898        // TODO : check the values in the below asserts
3899        // assert_eq!(cv.avg_total_fees, 3471);
3900        // assert_eq!(cv.total_fees, 5100);
3901        // assert_eq!(cv.burnfee, 1104854);
3902        assert_eq!(cv.rebroadcasts.len(), 1);
3903        assert_eq!(cv.avg_nolan_rebroadcast_per_block, 10);
3904    }
3905
3906    #[tokio::test]
3907    #[serial_test::serial]
3908    async fn atr_test_2() {
3909        pretty_env_logger::init();
3910        NodeTester::delete_data().await.unwrap();
3911        let mut tester = NodeTester::default();
3912
3913        let public_key = tester.get_public_key().await;
3914        tester
3915            .set_staking_requirement(2_000_000 * NOLAN_PER_SAITO, 60)
3916            .await;
3917        let issuance = vec![
3918            (public_key.to_base58(), 100 * 2_000_000 * NOLAN_PER_SAITO),
3919            (public_key.to_base58(), 100_000 * NOLAN_PER_SAITO),
3920            (
3921                "27UK2MuBTdeARhYp97XBnCovGkEquJjkrQntCgYoqj6GC".to_string(),
3922                50_000 * NOLAN_PER_SAITO,
3923            ),
3924        ];
3925        tester.set_issuance(issuance).await.unwrap();
3926
3927        tester.init().await.unwrap();
3928        // atr rebroadcasts on genesis_period + 1
3929        let genesis_period = (tester
3930            .routing_thread
3931            .config_lock
3932            .read()
3933            .await
3934            .get_consensus_config()
3935            .unwrap()
3936            .genesis_period)
3937            + 1;
3938        tester.wait_till_block_id(1).await.unwrap();
3939
3940        for i in 1..genesis_period {
3941            let tx = tester.create_transaction(10, 0, public_key).await.unwrap();
3942            tester.add_transaction(tx).await;
3943            tester.wait_till_block_id(i + 1).await.unwrap();
3944        }
3945
3946        let wallet = tester.consensus_thread.wallet_lock.read().await;
3947        let have_atr_slips = wallet
3948            .slips
3949            .iter()
3950            .any(|(_, slip)| slip.slip_type == SlipType::ATR);
3951        assert!(!have_atr_slips);
3952        drop(wallet);
3953
3954        tester.wait_till_block_id(genesis_period).await.unwrap();
3955        {
3956            let wallet = tester.consensus_thread.wallet_lock.read().await;
3957            let atr_slip_count = wallet
3958                .slips
3959                .iter()
3960                .filter(|(_, slip)| matches!(slip.slip_type, SlipType::ATR))
3961                .count();
3962            assert_eq!(atr_slip_count, 0);
3963        }
3964
3965        {
3966            let tx = tester.create_transaction(10, 0, public_key).await.unwrap();
3967            tester.add_transaction(tx).await;
3968
3969            tester.wait_till_block_id(genesis_period + 1).await.unwrap();
3970
3971            let wallet = tester.consensus_thread.wallet_lock.read().await;
3972            let atr_slip_count = wallet
3973                .slips
3974                .iter()
3975                .filter(|(_, slip)| matches!(slip.slip_type, SlipType::ATR))
3976                .count();
3977            // wallet only has slips owned by that public key
3978            assert_eq!(atr_slip_count, 0);
3979        }
3980        {
3981            let blockchain = tester.consensus_thread.blockchain_lock.read().await;
3982            let block = blockchain.get_latest_block().unwrap();
3983            assert_eq!(blockchain.get_latest_block_id(), genesis_period + 1);
3984            assert_eq!(block.id, genesis_period + 1);
3985            let mut have_atr_tx = false;
3986            for tx in block.transactions.iter() {
3987                if tx.transaction_type == TransactionType::ATR {
3988                    have_atr_tx = true;
3989                }
3990            }
3991            assert!(have_atr_tx);
3992        }
3993
3994        {
3995            let tx = tester.create_transaction(10, 0, public_key).await.unwrap();
3996            tester.add_transaction(tx).await;
3997
3998            tester.wait_till_block_id(genesis_period + 2).await.unwrap();
3999
4000            let wallet = tester.consensus_thread.wallet_lock.read().await;
4001            let atr_slip_count = wallet
4002                .slips
4003                .iter()
4004                .filter(|(_, slip)| matches!(slip.slip_type, SlipType::ATR))
4005                .count();
4006            assert_eq!(atr_slip_count, 1);
4007        }
4008        {
4009            let blockchain = tester.consensus_thread.blockchain_lock.read().await;
4010            let block = blockchain.get_latest_block().unwrap();
4011            assert_eq!(blockchain.get_latest_block_id(), genesis_period + 2);
4012            assert_eq!(block.id, genesis_period + 2);
4013            let mut have_atr_tx = false;
4014            for tx in block.transactions.iter() {
4015                if tx.transaction_type == TransactionType::ATR {
4016                    have_atr_tx = true;
4017                }
4018            }
4019            assert!(have_atr_tx);
4020        }
4021    }
4022}