This repository has been archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 630
/
Kernel.hs
226 lines (205 loc) · 10.8 KB
/
Kernel.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Cardano.Wallet.WalletLayer.Kernel
( bracketPassiveWallet
, bracketActiveWallet
) where
import Universum hiding (for_)
import qualified Control.Concurrent.STM as STM
import Control.Monad.IO.Unlift (MonadUnliftIO)
import qualified Data.ByteString.Char8 as BS
import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T
import Data.Foldable (for_)
import Formatting ((%))
import qualified Formatting as F
import Pos.Chain.Block (Blund, blockHeader, headerHash, prevBlockL)
import Pos.Chain.Genesis (Config (..))
import Pos.Chain.Txp (TxIn, TxOutAux)
import Pos.Chain.Txp (toaOut, txOutAddress, txOutValue)
import Pos.Chain.Update (HasUpdateConfiguration)
import Pos.Core.Chrono (OldestFirst (..))
import Pos.Core.Common (Address, Coin, addrToBase58, mkCoin,
unsafeAddCoin)
import Pos.Core.NetworkMagic (makeNetworkMagic)
import Pos.Crypto (EncryptedSecretKey, ProtocolMagic)
import Pos.Infra.InjectFail (FInjects)
import Pos.Util.CompileInfo (HasCompileInfo)
import Pos.Util.Wlog (Severity (Debug, Warning))
import Cardano.Wallet.API.V1.Types (V1 (V1), WalletBalance (..))
import qualified Cardano.Wallet.Kernel as Kernel
import qualified Cardano.Wallet.Kernel.Actions as Actions
import qualified Cardano.Wallet.Kernel.BListener as Kernel
import Cardano.Wallet.Kernel.DB.AcidState (dbHdWallets)
import Cardano.Wallet.Kernel.DB.HdWallet (eskToHdRootId, getHdRootId,
hdAccountRestorationState, hdRootId, hdWalletsRoots)
import Cardano.Wallet.Kernel.DB.InDb (fromDb)
import qualified Cardano.Wallet.Kernel.DB.Read as Kernel
import qualified Cardano.Wallet.Kernel.DB.Util.IxSet as IxSet
import Cardano.Wallet.Kernel.Decrypt (WalletDecrCredentials,
decryptAddress, eskToWalletDecrCredentials)
import Cardano.Wallet.Kernel.Diffusion (WalletDiffusion (..))
import Cardano.Wallet.Kernel.Internal (walletNode,
walletProtocolMagic)
import Cardano.Wallet.Kernel.Keystore (Keystore)
import Cardano.Wallet.Kernel.NodeStateAdaptor
import qualified Cardano.Wallet.Kernel.Read as Kernel
import qualified Cardano.Wallet.Kernel.Restore as Kernel
import Cardano.Wallet.WalletLayer (ActiveWalletLayer (..),
PassiveWalletLayer (..))
import qualified Cardano.Wallet.WalletLayer.Kernel.Accounts as Accounts
import qualified Cardano.Wallet.WalletLayer.Kernel.Active as Active
import qualified Cardano.Wallet.WalletLayer.Kernel.Addresses as Addresses
import qualified Cardano.Wallet.WalletLayer.Kernel.Info as Info
import qualified Cardano.Wallet.WalletLayer.Kernel.Internal as Internal
import qualified Cardano.Wallet.WalletLayer.Kernel.Settings as Settings
import qualified Cardano.Wallet.WalletLayer.Kernel.Transactions as Transactions
import qualified Cardano.Wallet.WalletLayer.Kernel.Wallets as Wallets
-- | Initialize the passive wallet.
-- The passive wallet cannot send new transactions.
bracketPassiveWallet
:: forall m n a. (MonadIO n, MonadUnliftIO m, MonadMask m)
=> ProtocolMagic
-> Kernel.DatabaseMode
-> (Severity -> Text -> IO ())
-> Keystore
-> NodeStateAdaptor IO
-> FInjects IO
-> (PassiveWalletLayer n -> Kernel.PassiveWallet -> m a) -> m a
bracketPassiveWallet pm mode logFunction keystore node fInjects f = do
Kernel.bracketPassiveWallet pm mode logFunction keystore node fInjects $ \w -> do
-- For each wallet in a restoration state, re-start the background
-- restoration tasks.
liftIO $ do
snapshot <- Kernel.getWalletSnapshot w
let wallets = snapshot ^. dbHdWallets . hdWalletsRoots
for_ wallets $ \root -> do
let accts = Kernel.accountsByRootId snapshot (root ^. hdRootId)
restoring = IxSet.findWithEvidence hdAccountRestorationState accts
whenJust restoring $ \(src, tgt) -> do
(w ^. Kernel.walletLogMessage) Warning $
F.sformat ("bracketPassiveWallet: continuing restoration of " %
F.build %
" from checkpoint " % F.build %
" with target " % F.build)
(root ^. hdRootId) (maybe "(genesis)" pretty src) (pretty tgt)
Kernel.continueRestoration w root src tgt
-- Start the wallet worker
let wai = Actions.WalletActionInterp
{ Actions.applyBlocks = \blunds -> do
ls <- mapM (Wallets.blundToResolvedBlock node)
(toList (getOldestFirst blunds))
let mp = catMaybes ls
mapM_ (Kernel.applyBlock w) mp
, Actions.switchToFork = \_ (OldestFirst blunds) -> do
-- Get the hash of the last main block before this fork.
let almostOldest = fst (NE.head blunds)
gh <- configGenesisHash <$> getCoreConfig node
oldest <- withNodeState node $ \_lock ->
mostRecentMainBlock gh
(almostOldest ^. blockHeader . prevBlockL)
bs <- catMaybes <$> mapM (Wallets.blundToResolvedBlock node)
(NE.toList blunds)
Kernel.switchToFork w (headerHash <$> oldest) bs
, Actions.emit = logFunction Debug
}
Actions.withWalletWorker wai $ \invoke -> do
f (passiveWalletLayer w invoke) w
where
passiveWalletLayer :: Kernel.PassiveWallet
-> (Actions.WalletAction Blund -> STM ())
-> PassiveWalletLayer n
passiveWalletLayer w invoke = PassiveWalletLayer
{ -- Operations that modify the wallet
createWallet = Wallets.createWallet w
, updateWallet = Wallets.updateWallet w
, updateWalletPassword = Wallets.updateWalletPassword w
, deleteWallet = Wallets.deleteWallet w
, createAccount = Accounts.createAccount w
, updateAccount = Accounts.updateAccount w
, deleteAccount = Accounts.deleteAccount w
, createAddress = Addresses.createAddress w
, importAddresses = Addresses.importAddresses w
, addUpdate = Internal.addUpdate w
, nextUpdate = Internal.nextUpdate w
, applyUpdate = Internal.applyUpdate w
, postponeUpdate = Internal.postponeUpdate w
, waitForUpdate = Internal.waitForUpdate w
, resetWalletState = Internal.resetWalletState w
, importWallet = Internal.importWallet w
, applyBlocks = invokeIO . Actions.ApplyBlocks
, rollbackBlocks = invokeIO . Actions.RollbackBlocks . length
-- Read-only operations
, getWallets = join (ro $ Wallets.getWallets w)
, getWallet = \wId -> join (ro $ Wallets.getWallet w wId)
, getUtxos = \wId -> ro $ Wallets.getWalletUtxos wId
, getAccounts = \wId -> ro $ Accounts.getAccounts wId
, getAccount = \wId acc -> ro $ Accounts.getAccount wId acc
, getAccountBalance = \wId acc -> ro $ Accounts.getAccountBalance wId acc
, getAccountAddresses = \wId acc rp fo -> ro $ Accounts.getAccountAddresses wId acc rp fo
, getAddresses = \rp -> ro $ Addresses.getAddresses rp
, validateAddress = \txt -> ro $ Addresses.validateAddress txt
, getTransactions = Transactions.getTransactions w
, getTxFromMeta = Transactions.toTransaction w
, getNodeSettings = Settings.getNodeSettings w
, queryWalletBalance = xqueryWalletBalance w
}
where
-- Read-only operations
ro :: (Kernel.DB -> x) -> n x
ro g = g <$> liftIO (Kernel.getWalletSnapshot w)
invokeIO :: forall m'. MonadIO m' => Actions.WalletAction Blund -> m' ()
invokeIO = liftIO . STM.atomically . invoke
-- a variant of isOurs, that accepts a pre-derived WalletDecrCredentials, to save cpu cycles
fastIsOurs :: WalletDecrCredentials -> Address -> Bool
fastIsOurs wdc addr = case decryptAddress wdc addr of
Just _ -> True
Nothing -> False
-- takes a WalletDecrCredentials and transaction, and returns the Coin output, if its ours
maybeReadcoin :: WalletDecrCredentials -> (TxIn, TxOutAux) -> Maybe Coin
maybeReadcoin wdc (_, txout) = case fastIsOurs wdc (txOutAddress . toaOut $ txout) of
True -> Just $ (txOutValue . toaOut) txout
False -> Nothing
-- take a EncryptedSecretKey and return the sum of all utxo in the state, and the walletid
xqueryWalletBalance :: Kernel.PassiveWallet -> EncryptedSecretKey -> IO WalletBalance
xqueryWalletBalance w esk = do
let
nm = makeNetworkMagic (w ^. walletProtocolMagic)
let
rootid = eskToHdRootId nm esk
wdc = eskToWalletDecrCredentials nm esk
let
withNode :: (HasCompileInfo, HasUpdateConfiguration) => Lock (WithNodeState IO) -> WithNodeState IO [Coin]
withNode _lock = filterUtxo (maybeReadcoin wdc)
my_coins <- withNodeState (w ^. walletNode) withNode
let
balance :: Coin
balance = foldl' unsafeAddCoin (mkCoin 0) my_coins
walletid = addrToBase58 $ view fromDb (getHdRootId rootid)
pure $ WalletBalance (V1 balance) (T.pack $ BS.unpack walletid)
-- | Initialize the active wallet.
-- The active wallet is allowed to send transactions, as it has the full
-- 'WalletDiffusion' layer in scope.
bracketActiveWallet
:: forall m n a. (MonadIO m, MonadMask m, MonadIO n)
=> PassiveWalletLayer n
-> Kernel.PassiveWallet
-> WalletDiffusion
-> (ActiveWalletLayer n -> Kernel.ActiveWallet -> m a) -> m a
bracketActiveWallet walletPassiveLayer passiveWallet walletDiffusion runActiveLayer =
Kernel.bracketActiveWallet passiveWallet walletDiffusion $ \w -> do
bracket
(return (activeWalletLayer w))
(\_ -> return ())
(flip runActiveLayer w)
where
activeWalletLayer :: Kernel.ActiveWallet -> ActiveWalletLayer n
activeWalletLayer w = ActiveWalletLayer {
walletPassiveLayer = walletPassiveLayer
, pay = Active.pay w
, estimateFees = Active.estimateFees w
, redeemAda = Active.redeemAda w
, getNodeInfo = Info.getNodeInfo w
}