This repository was archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 631
/
Copy pathMempool.hs
204 lines (180 loc) · 7.99 KB
/
Mempool.hs
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | Heavy PSK processing, in-memory state and mempool-related functions.
module Pos.DB.Delegation.Logic.Mempool
(
-- * Heavyweight psks handling & mempool
getDlgMempool
, clearDlgMemPool
, clearDlgMemPoolAction
, deleteFromDlgMemPool
, modifyDlgMemPool
, PskHeavyVerdict (..)
, processProxySKHeavy
, processProxySKHeavyInternal
) where
import Universum
import Control.Lens (at, uses, (%=), (+=), (-=), (.=))
import qualified Data.Cache.LRU as LRU
import qualified Data.HashMap.Strict as HM
import UnliftIO (MonadUnliftIO)
import Pos.Binary.Class (biSize)
import Pos.Chain.Block (headerHash)
import Pos.Chain.Delegation (DlgMemPool, DlgPayload (..),
MonadDelegation, cmPskMods, dwMessageCache, dwPoolSize,
dwProxySKPool, dwTip, emptyCedeModifier, isRevokePsk,
pskToDlgEdgeAction)
import Pos.Core as Core (Config (..), addressHash,
configBlockVersionData, epochIndexL)
import Pos.Core.Conc (currentTime)
import Pos.Core.Delegation (ProxySKHeavy)
import Pos.Core.Update (bvdMaxBlockSize)
import Pos.Crypto (ProxySecretKey (..), PublicKey)
import Pos.DB (MonadDBRead, MonadGState)
import qualified Pos.DB as DB
import Pos.DB.Delegation.Cede.Holders (evalMapCede)
import Pos.DB.Delegation.Cede.Logic (CheckForCycle (..),
dlgVerifyPskHeavy)
import Pos.DB.Delegation.Logic.Common (DelegationStateAction,
runDelegationStateAction)
import Pos.DB.GState.Lock (StateLock, withStateLockNoMetrics)
import Pos.DB.Lrc (HasLrcContext, getDlgRichmen)
import Pos.Util (HasLens', microsecondsToUTC)
import Pos.Util.Concurrent.PriorityLock (Priority (..))
----------------------------------------------------------------------------
-- Delegation mempool
----------------------------------------------------------------------------
-- | Retrieves current mempool of heavyweight psks plus undo part.
getDlgMempool
:: (MonadIO m, MonadDelegation ctx m)
=> m DlgPayload
getDlgMempool = UnsafeDlgPayload <$> (runDelegationStateAction $ uses dwProxySKPool HM.elems)
-- | Clears delegation mempool.
clearDlgMemPool
:: (MonadIO m, MonadDelegation ctx m)
=> m ()
clearDlgMemPool = runDelegationStateAction clearDlgMemPoolAction
clearDlgMemPoolAction :: DelegationStateAction ()
clearDlgMemPoolAction = do
dwProxySKPool .= mempty
dwPoolSize .= 1
-- Put value into Proxy SK Pool. Value must not exist in pool.
-- Caller must ensure it.
-- Caller must also ensure that size limit allows to put more data.
putToDlgMemPool :: PublicKey -> ProxySKHeavy -> DelegationStateAction ()
putToDlgMemPool pk psk = do
dwProxySKPool . at pk .= Just psk
dwPoolSize += biSize pk + biSize psk
deleteFromDlgMemPool :: PublicKey -> DelegationStateAction ()
deleteFromDlgMemPool pk =
use (dwProxySKPool . at pk) >>= \case
Nothing -> pass
Just psk -> do
dwProxySKPool . at pk .= Nothing
dwPoolSize -= biSize pk + biSize psk
-- Caller must ensure that there won't be too much data (more than limit) as
-- a result of transformation.
modifyDlgMemPool :: (DlgMemPool -> DlgMemPool) -> DelegationStateAction ()
modifyDlgMemPool f = do
memPool <- use dwProxySKPool
let newPool = f memPool
let newSize = biSize newPool
dwProxySKPool .= newPool
dwPoolSize .= newSize
----------------------------------------------------------------------------
-- Heavyweight PSKs processing
----------------------------------------------------------------------------
-- | Datatypes representing a verdict of heavy PSK processing.
data PskHeavyVerdict
= PHExists -- ^ If we have exactly the same cert in psk mempool
| PHInvalid Text -- ^ Can't accept PSK though it's most probably user's error
| PHCached -- ^ Message is cached
| PHTipMismatch -- ^ Verdict can't be made at the moment, mempool tip is different from db one
| PHExhausted -- ^ Memory pool is exhausted and can't accept more data
| PHRemoved -- ^ Revoked previous psk from the mempool
| PHAdded -- ^ Successfully processed/added to psk mempool
deriving (Show,Eq)
type ProcessHeavyConstraint ctx m =
( MonadIO m
, MonadUnliftIO m
, MonadDBRead m
, MonadGState m
, MonadDelegation ctx m
, MonadReader ctx m
, HasLrcContext ctx
)
-- | Processes heavyweight psk. Puts it into the mempool
-- depending on issuer's stake, overrides if exists, checks
-- validity and cachemsg state.
processProxySKHeavy
:: forall ctx m.
( ProcessHeavyConstraint ctx m
, HasLens' ctx StateLock
, MonadMask m
)
=> Core.Config -> ProxySKHeavy -> m PskHeavyVerdict
processProxySKHeavy coreConfig psk =
withStateLockNoMetrics LowPriority $ \_stateLockHeader ->
processProxySKHeavyInternal coreConfig psk
-- | Main logic of heavy psk processing, doesn't have
-- synchronization. Should be called __only__ if you are sure that
-- 'StateLock' is taken already.
processProxySKHeavyInternal ::
forall ctx m. (ProcessHeavyConstraint ctx m)
=> Core.Config
-> ProxySKHeavy
-> m PskHeavyVerdict
processProxySKHeavyInternal coreConfig psk = do
curTime <- microsecondsToUTC <$> currentTime
dbTip <- DB.getTipHeader
let dbTipHash = headerHash dbTip
let headEpoch = dbTip ^. epochIndexL
richmen <- getDlgRichmen (configBlockVersionData coreConfig)
"Delegation.Logic#processProxySKHeavy"
headEpoch
maxBlockSize <- bvdMaxBlockSize <$> DB.gsAdoptedBVData
let iPk = pskIssuerPk psk
-- Retrieve psk pool and perform another db check. It's
-- guaranteed that pool is not changed when we're under
-- 'withStateLock' lock.
cyclePool <- runDelegationStateAction $ use dwProxySKPool
-- This is inefficient. Consider supporting this map
-- in-memory or changing mempool key to stakeholderId.
let pskMods = HM.fromList $
map (bimap addressHash pskToDlgEdgeAction) $
HM.toList cyclePool
-- We don't use postedThisEpoch since we can't have more than
-- one psk per issuer in mempool and "has posted this epoch"
-- is fully backed up by the database.
let cedeModifier = emptyCedeModifier & cmPskMods .~ pskMods
(verificationError, pskValid) <-
fmap (either (,False)
(const (error "processProxySKHeavyInternal:can't happen",True))) $
evalMapCede cedeModifier $
runExceptT $
dlgVerifyPskHeavy (configProtocolMagic coreConfig)
richmen
(CheckForCycle True)
headEpoch
psk
-- Here the memory state is the same.
runDelegationStateAction $ do
memPoolSize <- use dwPoolSize
posted <- uses dwProxySKPool (\m -> isJust $ HM.lookup iPk m)
existsSameMempool <- uses dwProxySKPool $ \m -> HM.lookup iPk m == Just psk
cached <- isJust . snd . LRU.lookup psk <$> use dwMessageCache
let isRevoke = isRevokePsk psk
coherent <- uses dwTip $ (==) dbTipHash
dwMessageCache %= LRU.insert psk curTime
let -- TODO: This is a rather arbitrary limit, we should
-- revisit it (see CSL-1664)
exhausted = memPoolSize >= maxBlockSize * 2
let res = if | cached -> PHCached
| not coherent -> PHTipMismatch
| existsSameMempool -> PHExists
| not pskValid -> PHInvalid verificationError
| exhausted -> PHExhausted
| posted && isRevoke -> PHRemoved
| otherwise -> PHAdded
when (res == PHAdded) $ putToDlgMemPool iPk psk
when (res == PHRemoved) $ deleteFromDlgMemPool iPk
pure res