@@ -7,14 +7,17 @@ module Cardano.Wallet.Kernel.Restore
7
7
8
8
import Universum
9
9
10
- import Control.Concurrent (threadDelay )
11
10
import Control.Concurrent.Async (async , cancel )
12
11
import Control.Concurrent.MVar (modifyMVar_ )
13
12
import Data.Acid (update )
14
13
import qualified Data.Map.Merge.Strict as M
15
14
import qualified Data.Map.Strict as M
16
15
import Data.Time.Clock (NominalDiffTime , UTCTime , diffUTCTime ,
17
16
getCurrentTime )
17
+ import Formatting (bprint , build , formatToString , (%) )
18
+ import qualified Formatting.Buildable
19
+
20
+ import qualified Prelude
18
21
19
22
import Cardano.Wallet.API.Types.UnitOfMeasure
20
23
import Cardano.Wallet.Kernel (walletLogMessage )
@@ -25,6 +28,7 @@ import Cardano.Wallet.Kernel.DB.AcidState (ApplyHistoricalBlock (..),
25
28
import Cardano.Wallet.Kernel.DB.BlockContext
26
29
import qualified Cardano.Wallet.Kernel.DB.HdWallet as HD
27
30
import Cardano.Wallet.Kernel.DB.HdWallet.Create (CreateHdRootError )
31
+ import qualified Cardano.Wallet.Kernel.DB.Spec.Update as Spec
28
32
import Cardano.Wallet.Kernel.DB.TxMeta.Types
29
33
import Cardano.Wallet.Kernel.Decrypt (WalletDecrCredentialsKey (.. ),
30
34
decryptAddress , keyToWalletDecrCredentials )
@@ -42,17 +46,16 @@ import Cardano.Wallet.Kernel.Types (WalletId (..))
42
46
import Cardano.Wallet.Kernel.Util.Core (utxoBalance )
43
47
import Cardano.Wallet.Kernel.Wallets (createWalletHdRnd )
44
48
45
- import Pos.Chain.Block (Blund , HeaderHash , MainBlock , headerHash ,
46
- mainBlockSlot )
49
+ import Pos.Chain.Block (Block , Blund , HeaderHash , MainBlock , Undo ,
50
+ headerHash , mainBlockSlot )
47
51
import Pos.Chain.Txp (GenesisUtxo (.. ), Utxo , genesisUtxo )
48
52
import Pos.Core (BlockCount (.. ), Coin , SlotId , flattenSlotIdExplicit ,
49
- mkCoin , unsafeIntegerToCoin )
53
+ genesisHash , mkCoin , unsafeIntegerToCoin )
50
54
import Pos.Core.Txp (TxIn (.. ), TxOut (.. ), TxOutAux (.. ))
51
55
import Pos.Crypto (EncryptedSecretKey )
52
- import Pos.DB.Block (getFirstGenesisBlockHash , getUndo ,
53
- resolveForwardLink )
56
+ import Pos.DB.Block (getUndo , resolveForwardLink )
54
57
import Pos.DB.Class (getBlock )
55
- import Pos.Util.Trace (Severity (Debug , Error ))
58
+ import Pos.Util.Trace (Severity (Error ))
56
59
57
60
-- | Restore a wallet
58
61
--
@@ -188,97 +191,94 @@ getWalletInitInfo wKey@(wId, wdc) lock = do
188
191
return (addrId ^. HD. hdAddressIdParent, M. singleton inp (out, addrId))
189
192
190
193
-- | Restore a wallet's transaction history.
194
+ --
195
+ -- TODO: Think about what we should do if a 'RestorationException' is thrown.
191
196
restoreWalletHistoryAsync :: Kernel. PassiveWallet
192
197
-> HD. HdRootId
193
198
-> HeaderHash
194
199
-> SlotId
195
200
-> (Blund -> IO (Map HD. HdAccountId PrefilteredBlock , [TxMeta ]))
196
201
-> IO ()
197
202
restoreWalletHistoryAsync wallet rootId target tgtSlot prefilter = do
198
-
199
- say " sleeping 10 seconds before starting restoration, so that the tip has a chance to move"
200
- pause 10
201
-
202
- -- TODO (@mn): should filter genesis utxo for the first wallet
203
- withNode (getFirstGenesisBlockHash >>= getBlock) >>= \ case
204
- Nothing -> say " failed to find genesis block's successor!!" >> finish
205
- Just gbs -> restore (headerHash gbs) NoTimingData
206
-
203
+ startingPoint <- withNode $ return genesisHash
204
+ restore startingPoint NoTimingData
207
205
where
206
+ wId :: WalletId
208
207
wId = WalletIdHdRnd rootId
209
- pause = when True . threadDelay . (* 1000000 )
210
-
211
- say = (wallet ^. walletLogMessage) Debug . (" mnoonan: " <> )
212
208
213
209
-- Process the restoration of the block with the given 'HeaderHash'.
214
- -- The (UTCTime, Int) pair is used t
215
210
restore :: HeaderHash -> TimingData -> IO ()
216
211
restore hh timing = do
217
-
218
- -- Increment our timing counter, producing an average rate
219
- -- every 5 blocks.
212
+ -- Updating the average rate every 5 blocks.
220
213
(rate, timing') <- tickTiming 5 timing
221
214
222
215
-- Update each account's historical checkpoints
223
- (block, undo) <- withNode ((,) <$> getBlock hh <*> getUndo hh)
224
-
225
- whenJust ((,) <$> block <*> undo) $ \ blund ->
226
- whenRight (fst blund) $ \ mb -> do
227
-
228
- -- Gather the information we will need to decide if we are within K blocks of the tip.
229
- slotCount <- getSlotCount (wallet ^. walletNode)
230
- let flat = flattenSlotIdExplicit slotCount
231
-
232
- -- Filter the blocks by account
233
- (prefilteredBlocks, txMetas) <- prefilter blund
234
-
235
- let slotId = mb ^. mainBlockSlot
236
- k <- getSecurityParameter (wallet ^. walletNode)
237
- ctxt <- withNodeState (wallet ^. walletNode) $ \ _lock ->
238
- mainBlockContext mb
239
- mErr <- update (wallet ^. wallets) $
240
- ApplyHistoricalBlock k ctxt prefilteredBlocks
241
-
242
- case mErr of
243
- Left err -> error $ " restore: unexpected error during applyHistoricalBlock: " <> pretty err
244
- Right () -> return ()
245
-
246
- -- Update our progress
247
- let blockPerSec = MeasuredIn . BlockCount . perSecond <$> rate
248
- throughputUpdate = maybe identity (set wriThroughput) blockPerSec
249
-
250
- updateRestorationInfo wallet wId ( (wriCurrentSlot .~ flat slotId)
251
- . (wriTargetSlot .~ flat tgtSlot)
252
- . throughputUpdate )
253
-
254
- -- Store the TxMetas
255
- forM_ txMetas (putTxMeta (wallet ^. walletMeta))
256
-
257
- -- MN TEMPORARY: slow your roll! Add an artificial 5 second delay whenever
258
- -- the throughput rate was updated, so we can look for it in the `wallets`
259
- -- endpoint results.
260
- case rate of
261
- Nothing -> return ()
262
- Just _ -> pause 5
216
+ block <- getBlockOrThrow hh
217
+
218
+ -- Skip EBBs
219
+ whenRight block $ \ mb -> do
220
+ -- Filter the blocks by account
221
+ blund <- (Right mb, ) <$> getUndoOrThrow hh
222
+ (prefilteredBlocks, txMetas) <- prefilter blund
223
+
224
+ -- Apply the block
225
+ k <- getSecurityParameter (wallet ^. walletNode)
226
+ ctxt <- withNode $ mainBlockContext mb
227
+ mErr <- update (wallet ^. wallets) $
228
+ ApplyHistoricalBlock k ctxt prefilteredBlocks
229
+ case mErr of
230
+ Left err -> throwM $ RestorationApplyHistoricalBlockFailed err
231
+ Right () -> return ()
232
+
233
+ -- Update our progress
234
+ slotCount <- getSlotCount (wallet ^. walletNode)
235
+ let flat = flattenSlotIdExplicit slotCount
236
+ blockPerSec = MeasuredIn . BlockCount . perSecond <$> rate
237
+ throughputUpdate = maybe identity (set wriThroughput) blockPerSec
238
+ slotId = mb ^. mainBlockSlot
239
+ updateRestorationInfo wallet wId ( (wriCurrentSlot .~ flat slotId)
240
+ . (wriTargetSlot .~ flat tgtSlot)
241
+ . throughputUpdate )
242
+
243
+ -- Store the TxMetas
244
+ forM_ txMetas (putTxMeta (wallet ^. walletMeta))
263
245
264
246
-- Get the next block from the node and recurse.
265
- if target == hh
266
- then say " made it to target!" >> finish
267
- else nextBlock hh >>= \ case
268
- Nothing -> say " failed to find next block!!" >> finish
247
+ if target == hh then
248
+ finish
249
+ else
250
+ nextBlock hh >>= \ case
251
+ Nothing -> throwM $ RestorationFinishUnreachable target hh
269
252
Just header' -> restore header' timing'
270
253
271
- -- Step forward to the successor of the given block.
272
- nextBlock :: HeaderHash -> IO (Maybe HeaderHash )
273
- nextBlock hh = withNode (resolveForwardLink hh)
274
-
275
254
-- TODO (@mn): probably should use some kind of bracket to ensure this cleanup happens.
276
255
finish :: IO ()
277
256
finish = do
278
257
k <- getSecurityParameter (wallet ^. walletNode)
279
258
update (wallet ^. wallets) $ RestorationComplete k rootId
280
259
modifyMVar_ (wallet ^. walletRestorationTask) (pure . M. delete wId)
281
260
261
+ -- Step forward to the successor of the given block.
262
+ nextBlock :: HeaderHash -> IO (Maybe HeaderHash )
263
+ nextBlock hh = withNode (resolveForwardLink hh)
264
+
265
+ -- Get a block
266
+ getBlockOrThrow :: HeaderHash -> IO Block
267
+ getBlockOrThrow hh = do
268
+ mBlock <- withNode $ getBlock hh
269
+ case mBlock of
270
+ Nothing -> throwM $ RestorationBlockNotFound hh
271
+ Just b -> return b
272
+
273
+ -- Get undo for a mainblock
274
+ -- NOTE: We use this undo information only for input resolution.
275
+ getUndoOrThrow :: HeaderHash -> IO Undo
276
+ getUndoOrThrow hh = do
277
+ mBlock <- withNode $ getUndo hh
278
+ case mBlock of
279
+ Nothing -> throwM $ RestorationUndoNotFound hh
280
+ Just b -> return b
281
+
282
282
withNode :: forall a . (NodeConstraints => WithNodeState IO a ) -> IO a
283
283
withNode action = withNodeState (wallet ^. walletNode) (\ _lock -> action)
284
284
@@ -290,6 +290,10 @@ updateRestorationInfo :: Kernel.PassiveWallet
290
290
updateRestorationInfo wallet wId upd =
291
291
modifyMVar_ (wallet ^. walletRestorationTask) (pure . M. adjust upd wId)
292
292
293
+ {- ------------------------------------------------------------------------------
294
+ Timing information (for throughput calculations)
295
+ -------------------------------------------------------------------------------}
296
+
293
297
-- | Keep track of how many events have happened since a given start time.
294
298
data TimingData
295
299
= NoTimingData
@@ -312,3 +316,29 @@ tickTiming k' (Timing k start)
312
316
-- | Convert a rate to a number of events per second.
313
317
perSecond :: Rate -> Word64
314
318
perSecond (Rate n dt) = fromInteger $ round (toRational n / toRational dt)
319
+
320
+ {- ------------------------------------------------------------------------------
321
+ Exceptions
322
+ -------------------------------------------------------------------------------}
323
+
324
+ -- | Exception during restoration
325
+ data RestorationException =
326
+ RestorationBlockNotFound HeaderHash
327
+ | RestorationUndoNotFound HeaderHash
328
+ | RestorationApplyHistoricalBlockFailed Spec. ApplyBlockFailed
329
+ | RestorationFinishUnreachable HeaderHash HeaderHash
330
+
331
+ instance Buildable RestorationException where
332
+ build (RestorationBlockNotFound hash) =
333
+ bprint (" RestorationBlockNotFound " % build) hash
334
+ build (RestorationUndoNotFound hash) =
335
+ bprint (" RestorationUndoNotFound " % build) hash
336
+ build (RestorationApplyHistoricalBlockFailed err) =
337
+ bprint (" RestorationApplyHistoricalBlockFailed " % build) err
338
+ build (RestorationFinishUnreachable target final) =
339
+ bprint (" RestorationFinishUnreachable " % build % " " % build) target final
340
+
341
+ instance Show RestorationException where
342
+ show = formatToString build
343
+
344
+ instance Exception RestorationException
0 commit comments