diff --git a/CHANGELOG.md b/CHANGELOG.md index fa741207c20..c1389efc3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## Cardano SL 3.0.1 + +### Fixes + +- Fix inconsistent 'hasSpendingPassword' resolution from legacy data layer migration ([DEVOPS-1250](https://iohk.myjetbrains.com/youtrack/issue/DEVOPS-1250) [#4112](https://github.com/input-output-hk/cardano-sl/pull/4112)) + ## Cardano SL 3.0.0 ### Fixes diff --git a/lib/configuration.yaml b/lib/configuration.yaml index 198b9c4371b..8776dca318d 100644 --- a/lib/configuration.yaml +++ b/lib/configuration.yaml @@ -14851,7 +14851,7 @@ mainnet_wallet_win64: &mainnet_wallet_win64 <<: *mainnet_full update: applicationName: csl-daedalus - applicationVersion: 13 + applicationVersion: 14 lastKnownBlockVersion: bvMajor: 0 bvMinor: 1 @@ -14861,7 +14861,7 @@ mainnet_wallet_macos64: &mainnet_wallet_macos64 <<: *mainnet_full update: applicationName: csl-daedalus - applicationVersion: 13 + applicationVersion: 14 lastKnownBlockVersion: bvMajor: 0 bvMinor: 1 @@ -14871,7 +14871,7 @@ mainnet_wallet_linux64: &mainnet_wallet_linux64 <<: *mainnet_full update: applicationName: csl-daedalus - applicationVersion: 13 + applicationVersion: 14 lastKnownBlockVersion: bvMajor: 0 bvMinor: 1 @@ -14945,7 +14945,7 @@ testnet_wallet: &testnet_wallet <<: *testnet_full update: &testnet_wallet_update applicationName: csl-daedalus - applicationVersion: 9 + applicationVersion: 10 lastKnownBlockVersion: bvMajor: 0 bvMinor: 0 @@ -14984,7 +14984,7 @@ mainnet_dryrun_wallet_win64: &mainnet_dryrun_wallet_win64 <<: *mainnet_dryrun_full update: applicationName: csl-daedalus - applicationVersion: 22 + applicationVersion: 23 lastKnownBlockVersion: bvMajor: 0 bvMinor: 2 @@ -14994,7 +14994,7 @@ mainnet_dryrun_wallet_macos64: &mainnet_dryrun_wallet_macos64 <<: *mainnet_dryrun_full update: applicationName: csl-daedalus - applicationVersion: 22 + applicationVersion: 23 lastKnownBlockVersion: bvMajor: 0 bvMinor: 2 @@ -15004,7 +15004,7 @@ mainnet_dryrun_wallet_linux64: &mainnet_dryrun_wallet_linux64 <<: *mainnet_dryrun_full update: applicationName: csl-daedalus - applicationVersion: 22 + applicationVersion: 23 lastKnownBlockVersion: bvMajor: 0 bvMinor: 2 diff --git a/wallet/src/Cardano/Wallet/Action.hs b/wallet/src/Cardano/Wallet/Action.hs index 6c03908697d..16f6cf1d18f 100644 --- a/wallet/src/Cardano/Wallet/Action.hs +++ b/wallet/src/Cardano/Wallet/Action.hs @@ -22,7 +22,8 @@ import Cardano.Wallet.Kernel (PassiveWallet) import qualified Cardano.Wallet.Kernel as Kernel import qualified Cardano.Wallet.Kernel.Internal as Kernel.Internal import qualified Cardano.Wallet.Kernel.Keystore as Keystore -import Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer) +import Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer, + sanityCheckSpendingPassword) import qualified Cardano.Wallet.Kernel.Mode as Kernel.Mode import qualified Cardano.Wallet.Kernel.NodeStateAdaptor as NodeStateAdaptor import Cardano.Wallet.Server.CLI (WalletBackendParams, @@ -68,6 +69,7 @@ actionWithWallet params genesisConfig walletConfig txpConfig ntpConfig nodeParam let pm = configProtocolMagic genesisConfig WalletLayer.Kernel.bracketPassiveWallet pm dbMode logMessage' keystore nodeState (npFInjects nodeParams) $ \walletLayer passiveWallet -> do migrateLegacyDataLayer passiveWallet dbPath (getFullMigrationFlag params) + sanityCheckSpendingPassword passiveWallet let plugs = plugins (walletLayer, passiveWallet) dbMode diff --git a/wallet/src/Cardano/Wallet/Kernel/Migration.hs b/wallet/src/Cardano/Wallet/Kernel/Migration.hs index 83c3c5f4130..d1c46966d08 100644 --- a/wallet/src/Cardano/Wallet/Kernel/Migration.hs +++ b/wallet/src/Cardano/Wallet/Kernel/Migration.hs @@ -1,10 +1,17 @@ -module Cardano.Wallet.Kernel.Migration (migrateLegacyDataLayer) where +{-# LANGUAGE LambdaCase #-} + +module Cardano.Wallet.Kernel.Migration + ( migrateLegacyDataLayer + , sanityCheckSpendingPassword + ) where import Universum +import Data.Acid.Advanced (update') import Data.Text (pack) import Data.Time (defaultTimeLocale, formatTime, getCurrentTime, iso8601DateFormat) +import Pos.Crypto.Signing (checkPassMatches) import System.Directory (doesDirectoryExist, makeAbsolute, renamePath) import Formatting ((%)) @@ -15,10 +22,15 @@ import Pos.Crypto (EncryptedSecretKey) import Pos.Util.Wlog (Severity (..)) import qualified Cardano.Wallet.Kernel as Kernel +import Cardano.Wallet.Kernel.DB.AcidState (UpdateHdRootPassword (..)) import qualified Cardano.Wallet.Kernel.DB.HdWallet as HD +import Cardano.Wallet.Kernel.DB.InDb (InDb (..)) +import qualified Cardano.Wallet.Kernel.DB.Read as Kernel import qualified Cardano.Wallet.Kernel.Internal as Kernel import Cardano.Wallet.Kernel.Keystore as Keystore +import qualified Cardano.Wallet.Kernel.Read as Kernel import Cardano.Wallet.Kernel.Restore (restoreWallet) +import Cardano.Wallet.Kernel.Util.Core (getCurrentTimestamp) {------------------------------------------------------------------------------- Pure helper functions for migration. @@ -104,9 +116,7 @@ restore pw forced esk = do rootId = HD.eskToHdRootId nm esk let -- DEFAULTS for wallet restoration - -- we don't have a spending password during migration - hasSpendingPassword = False - -- we cannot derive an address without a spending password + hasSpendingPassword = isNothing $ checkPassMatches mempty esk defaultAddress = Nothing defaultWalletName = HD.WalletName "" defaultAssuranceLevel = HD.AssuranceLevelStrict @@ -132,3 +142,42 @@ restore pw forced esk = do True -> do logMsg Error ("Migration failed! " <> msg <> " You are advised to delete the newly created db and try again.") exitFailure + +-- | Verify that the spending password metadata are correctly set. We mistakenly +-- forgot to port a fix from RCD-47 done on 2.0.x onto 3.0.0 and, for a few +-- users, have migrated / restored their wallet with a wrong spending password +-- metadata (arbitrarily set to `False`), making their wallet completely +-- unusable. +-- +-- This checks makes sure that the 'hasSpendingPassword' metadata correctly +-- reflects the wallet condition. To be run on each start-up, unfortunately. +sanityCheckSpendingPassword + :: Kernel.PassiveWallet + -> IO () +sanityCheckSpendingPassword pw = do + let nm = makeNetworkMagic (pw ^. Kernel.walletProtocolMagic) + wKeys <- Keystore.getKeys (pw ^. Kernel.walletKeystore) + db <- Kernel.getWalletSnapshot pw + lastUpdateNow <- InDb <$> getCurrentTimestamp + forM_ wKeys $ \esk -> do + let hasSpendingPassword = case checkPassMatches mempty esk of + Nothing -> HD.HasSpendingPassword lastUpdateNow + Just _ -> HD.NoSpendingPassword + let rootId = HD.eskToHdRootId nm esk + whenDiscrepancy db rootId hasSpendingPassword restoreTruth >>= \case + Left (HD.UnknownHdRoot _) -> + logMsg Error "Failed to update spending password status, HDRoot is gone?" + Right _ -> + return () + where + logMsg = pw ^. Kernel.walletLogMessage + whenDiscrepancy db rootId hasSpendingPassword action = do + case (hasSpendingPassword, Kernel.lookupHdRootId db rootId) of + (_, Left e) -> + return $ Left e + (HD.HasSpendingPassword _, Right root) | root ^. HD.hdRootHasPassword == HD.NoSpendingPassword -> + action rootId hasSpendingPassword + _ -> -- Avoid making a DB update when there's no need + return $ Right () + restoreTruth rootId hasSpendingPassword = + void <$> update' (pw ^. Kernel.wallets) (UpdateHdRootPassword rootId hasSpendingPassword)