Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 19e8040

Browse files
committed
[DEVOPS-1250] backport RCD-47 fix about 'hasSpendingPassword' metadata with extra sanity check
Since a few users have already ran the migration, they might have migrated wallet with incorrect metadata, wrongly stating that their wallet has no spending password, and as a consequence, preventing them from doing anything with their wallet. So, we do now run an extra sanity check upon every start to make sure that the wallet acid state metadata matches what we find in the keystore.
1 parent 35e8f14 commit 19e8040

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

wallet/src/Cardano/Wallet/Action.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import Cardano.Wallet.Kernel (PassiveWallet)
2222
import qualified Cardano.Wallet.Kernel as Kernel
2323
import qualified Cardano.Wallet.Kernel.Internal as Kernel.Internal
2424
import qualified Cardano.Wallet.Kernel.Keystore as Keystore
25-
import Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer)
25+
import Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer,
26+
sanityCheckSpendingPassword)
2627
import qualified Cardano.Wallet.Kernel.Mode as Kernel.Mode
2728
import qualified Cardano.Wallet.Kernel.NodeStateAdaptor as NodeStateAdaptor
2829
import Cardano.Wallet.Server.CLI (WalletBackendParams,
@@ -68,6 +69,7 @@ actionWithWallet params genesisConfig walletConfig txpConfig ntpConfig nodeParam
6869
let pm = configProtocolMagic genesisConfig
6970
WalletLayer.Kernel.bracketPassiveWallet pm dbMode logMessage' keystore nodeState (npFInjects nodeParams) $ \walletLayer passiveWallet -> do
7071
migrateLegacyDataLayer passiveWallet dbPath (getFullMigrationFlag params)
72+
sanityCheckSpendingPassword passiveWallet
7173

7274
let plugs = plugins (walletLayer, passiveWallet) dbMode
7375

wallet/src/Cardano/Wallet/Kernel/Migration.hs

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
module Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer) where
1+
{-# LANGUAGE LambdaCase #-}
2+
3+
module Cardano.Wallet.Kernel.Migration
4+
( migrateLegacyDataLayer
5+
, sanityCheckSpendingPassword
6+
) where
27

38
import Universum
49

10+
import Data.Acid.Advanced (update')
511
import Data.Text (pack)
612
import Data.Time (defaultTimeLocale, formatTime, getCurrentTime,
713
iso8601DateFormat)
14+
import Pos.Crypto.Signing (checkPassMatches)
815
import System.Directory (doesDirectoryExist, makeAbsolute, renamePath)
916

1017
import Formatting ((%))
@@ -15,10 +22,15 @@ import Pos.Crypto (EncryptedSecretKey)
1522
import Pos.Util.Wlog (Severity (..))
1623

1724
import qualified Cardano.Wallet.Kernel as Kernel
25+
import Cardano.Wallet.Kernel.DB.AcidState (UpdateHdRootPassword (..))
1826
import qualified Cardano.Wallet.Kernel.DB.HdWallet as HD
27+
import Cardano.Wallet.Kernel.DB.InDb (InDb (..))
28+
import qualified Cardano.Wallet.Kernel.DB.Read as Kernel
1929
import qualified Cardano.Wallet.Kernel.Internal as Kernel
2030
import Cardano.Wallet.Kernel.Keystore as Keystore
31+
import qualified Cardano.Wallet.Kernel.Read as Kernel
2132
import Cardano.Wallet.Kernel.Restore (restoreWallet)
33+
import Cardano.Wallet.Kernel.Util.Core (getCurrentTimestamp)
2234

2335
{-------------------------------------------------------------------------------
2436
Pure helper functions for migration.
@@ -105,7 +117,7 @@ restore pw forced esk = do
105117

106118
let -- DEFAULTS for wallet restoration
107119
-- we don't have a spending password during migration
108-
hasSpendingPassword = False
120+
hasSpendingPassword = isNothing $ checkPassMatches mempty esk
109121
-- we cannot derive an address without a spending password
110122
defaultAddress = Nothing
111123
defaultWalletName = HD.WalletName "<Migrated Wallet>"
@@ -132,3 +144,42 @@ restore pw forced esk = do
132144
True -> do
133145
logMsg Error ("Migration failed! " <> msg <> " You are advised to delete the newly created db and try again.")
134146
exitFailure
147+
148+
-- | Verify that the spending password metadata are correctly set. We mistakenly
149+
-- forgot to port a fix from RCD-47 done on 2.0.x onto 3.0.0 and, for a few
150+
-- users, have migrated / restored their wallet with a wrong spending password
151+
-- metadata (arbitrarily set to `False`), making their wallet completely
152+
-- unusable.
153+
--
154+
-- This checks makes sure that the 'hasSpendingPassword' metadata correctly
155+
-- reflects the wallet condition. To be run on each start-up, unfortunately.
156+
sanityCheckSpendingPassword
157+
:: Kernel.PassiveWallet
158+
-> IO ()
159+
sanityCheckSpendingPassword pw = do
160+
let nm = makeNetworkMagic (pw ^. Kernel.walletProtocolMagic)
161+
wKeys <- Keystore.getKeys (pw ^. Kernel.walletKeystore)
162+
db <- Kernel.getWalletSnapshot pw
163+
lastUpdateNow <- InDb <$> getCurrentTimestamp
164+
forM_ wKeys $ \esk -> do
165+
let hasSpendingPassword = case checkPassMatches mempty esk of
166+
Nothing -> HD.NoSpendingPassword
167+
Just _ -> HD.HasSpendingPassword lastUpdateNow
168+
let rootId = HD.eskToHdRootId nm esk
169+
whenDiscrepancy db rootId hasSpendingPassword restoreTruth >>= \case
170+
Left (HD.UnknownHdRoot _) ->
171+
logMsg Error "Failed to update spending password status, HDRoot is gone?"
172+
Right _ ->
173+
return ()
174+
where
175+
logMsg = pw ^. Kernel.walletLogMessage
176+
whenDiscrepancy db rootId hasSpendingPassword action = do
177+
case (hasSpendingPassword, Kernel.lookupHdRootId db rootId) of
178+
(_, Left e) ->
179+
return $ Left e
180+
(HD.HasSpendingPassword _, Right root) | root ^. HD.hdRootHasPassword == HD.NoSpendingPassword ->
181+
action rootId hasSpendingPassword
182+
_ -> -- Avoid making a DB update when there's no need
183+
return $ Right ()
184+
restoreTruth rootId hasSpendingPassword =
185+
void <$> update' (pw ^. Kernel.wallets) (UpdateHdRootPassword rootId hasSpendingPassword)

0 commit comments

Comments
 (0)