1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

use std::ops::{Deref, DerefMut};

use super::OverlayAccount;

#[derive(Debug)]
#[cfg_attr(test, derive(Clone))]
pub struct AccountEntryWithWarm {
    pub warm: bool,
    pub entry: AccountEntry,
}

impl Deref for AccountEntryWithWarm {
    type Target = AccountEntry;

    fn deref(&self) -> &Self::Target { &self.entry }
}

impl DerefMut for AccountEntryWithWarm {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.entry }
}

/// Entry object in cache and checkpoint layers, adding additional markers
/// like dirty bits to the `OverlayAccount` structure.
#[derive(Debug)]
#[cfg_attr(test, derive(Clone))]
pub enum AccountEntry {
    /// Represents an account that is confirmed to be absent from the database.
    DbAbsent,
    /// An in-memory cached account paired with a dirty bit to indicate
    /// modifications.
    Cached(OverlayAccount, bool),
}

use cfx_parameters::genesis::GENESIS_ACCOUNT_ADDRESS;
use cfx_types::AddressWithSpace;
use primitives::Account;
use AccountEntry::*;

impl AccountEntry {
    pub fn new_dirty(account: OverlayAccount) -> AccountEntry {
        Cached(account, true)
    }

    /// Contruct `AccountEntry` from account loaded from statedb.
    pub fn new_loaded(account: Option<Account>) -> AccountEntry {
        match account {
            Some(acc) => Cached(
                OverlayAccount::from_loaded(&acc.address().clone(), acc),
                false,
            ),
            None => DbAbsent,
        }
    }

    #[allow(unused)]
    pub fn is_dirty(&self) -> bool { matches!(self, Cached(_, true)) }

    pub fn is_db_absent(&self) -> bool { matches!(self, DbAbsent) }

    pub fn account(&self) -> Option<&OverlayAccount> {
        match self {
            DbAbsent => None,
            Cached(acc, _) => Some(acc),
        }
    }

    pub fn account_mut(&mut self) -> Option<&mut OverlayAccount> {
        match self {
            DbAbsent => None,
            Cached(acc, _) => Some(acc),
        }
    }

    pub fn dirty_account_mut(&mut self) -> Option<&mut OverlayAccount> {
        match self {
            Cached(acc, true) => Some(acc),
            _ => None,
        }
    }

    pub fn into_to_commit_account(self) -> Option<OverlayAccount> {
        // Due to an existing bug, the genesis account is very special. It is
        // always considered to be committed even if it is not dirty.
        const SPECIAL_ADDRESS: AddressWithSpace = AddressWithSpace {
            address: GENESIS_ACCOUNT_ADDRESS,
            space: cfx_types::Space::Native,
        };

        match self {
            Cached(acc, true) => Some(acc),
            Cached(acc, _) if acc.address == SPECIAL_ADDRESS => Some(acc),
            _ => None,
        }
    }

    pub fn clone_cache_entry_for_checkpoint(
        &self, checkpoint_id: usize,
    ) -> AccountEntry {
        match self {
            DbAbsent => DbAbsent,
            Cached(acc, dirty_bit) => Cached(
                acc.clone_account_for_checkpoint(checkpoint_id),
                *dirty_bit,
            ),
        }
    }

    pub fn clone_from_committed_cache(&self) -> AccountEntry {
        match self {
            DbAbsent => DbAbsent,
            Cached(acc, dirty_bit) => {
                Cached(acc.clone_from_committed_cache(), *dirty_bit)
            }
        }
    }

    pub fn clone_account(&self) -> AccountEntry {
        match self {
            DbAbsent => DbAbsent,
            Cached(acc, dirty_bit) => Cached(acc.clone_account(), *dirty_bit),
        }
    }

    pub fn with_warm(self, warm: bool) -> AccountEntryWithWarm {
        AccountEntryWithWarm { warm, entry: self }
    }
}