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

Commit b75da69

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 b75da69

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
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-4
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.
@@ -104,9 +116,7 @@ restore pw forced esk = do
104116
rootId = HD.eskToHdRootId nm esk
105117

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

0 commit comments

Comments
 (0)