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

Commit f6086a3

Browse files
parsonsmattKtorZ
authored andcommitted
[CBR-254] Implement wallet ID in error
1 parent 58414fb commit f6086a3

File tree

5 files changed

+96
-45
lines changed

5 files changed

+96
-45
lines changed

wallet-new/integration/WalletSpecs.hs

+4-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ walletSpecs _ wc = do
6363
}
6464
-- First wallet creation/restoration should succeed
6565
result <- postWallet wc newWallet1
66-
void $ result `shouldPrism` _Right
66+
wallet <- fmap wrData (result `shouldPrism` _Right)
6767
-- Second wallet creation/restoration should rise WalletAlreadyExists
6868
eresp <- postWallet wc newWallet2
6969
clientError <- eresp `shouldPrism` _Left
70-
clientError `shouldBe` ClientWalletError WalletAlreadyExists
70+
clientError
71+
`shouldBe`
72+
ClientWalletError (WalletAlreadyExists (walId wallet))

wallet-new/src/Cardano/Wallet/API/V1/Errors.hs

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import Cardano.Wallet.API.Response.JSend
2323
(ResponseStatus (ErrorStatus))
2424
import Cardano.Wallet.API.V1.Generic (gparseJsend, gtoJsend)
2525
import Cardano.Wallet.API.V1.Types (SyncPercentage, SyncProgress (..),
26-
V1 (..), mkEstimatedCompletionTime, mkSyncPercentage,
26+
V1 (..), WalletId, exampleWalletId,
27+
mkEstimatedCompletionTime, mkSyncPercentage,
2728
mkSyncThroughput)
2829

2930
--
@@ -66,7 +67,7 @@ data WalletError =
6667
| InvalidAddressFormat { weMsg :: !Text }
6768
| WalletNotFound
6869
-- FIXME(akegalj): https://iohk.myjetbrains.com/youtrack/issue/CSL-2496
69-
| WalletAlreadyExists
70+
| WalletAlreadyExists { weWalletId :: WalletId }
7071
| AddressNotFound
7172
| TxFailedToStabilize
7273
| TxRedemptionDepleted
@@ -153,7 +154,7 @@ sample =
153154
, UnknownError "Unknown error"
154155
, InvalidAddressFormat "Invalid base58 representation."
155156
, WalletNotFound
156-
, WalletAlreadyExists
157+
, WalletAlreadyExists exampleWalletId
157158
, AddressNotFound
158159
, MissingRequiredParams (("wallet_id", "walletId") :| [])
159160
, WalletIsNotReadyToProcessPayments sampleSyncProgress
@@ -178,7 +179,7 @@ describe = \case
178179
"Provided address format is not valid."
179180
WalletNotFound ->
180181
"Reference to an unexisting wallet was given."
181-
WalletAlreadyExists ->
182+
WalletAlreadyExists _ ->
182183
"Can't create or restore a wallet. The wallet already exists."
183184
AddressNotFound ->
184185
"Reference to an unexisting address was given."

wallet-new/src/Cardano/Wallet/API/V1/LegacyHandlers/Wallets.hs

+10-15
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import Pos.Update.Configuration ()
2626

2727
import Pos.Util (HasLens (..))
2828
import qualified Pos.Wallet.WalletMode as V0
29-
import qualified Pos.Wallet.Web.Error.Types as V0
3029
import Pos.Wallet.Web.Methods.Logic (MonadWalletLogic,
3130
MonadWalletLogicRead)
3231
import Pos.Wallet.Web.Tracking.Types (SyncQueue)
@@ -80,27 +79,23 @@ newWallet NewWallet{..} = do
8079
-- is still catching up.
8180
unless (isNodeSufficientlySynced spV0) $ throwM (NodeIsStillSyncing syncPercentage)
8281

83-
let newWalletHandler CreateWallet = V0.newWallet
84-
newWalletHandler RestoreWallet = V0.restoreWalletFromSeed
82+
let newWalletHandler CreateWallet = V0.newWalletNoThrow
83+
newWalletHandler RestoreWallet = V0.restoreWalletFromSeedNoThrow
8584
(V1 spendingPassword) = fromMaybe (V1 mempty) newwalSpendingPassword
8685
(V1 backupPhrase) = newwalBackupPhrase
8786
initMeta <- V0.CWalletMeta <$> pure newwalName
8887
<*> migrate newwalAssuranceLevel
8988
<*> pure 0
9089
let walletInit = V0.CWalletInit initMeta (V0.CBackupPhrase backupPhrase)
9190
single <$> do
92-
v0wallet <- newWalletHandler newwalOperation spendingPassword walletInit
93-
`catch` rethrowDuplicateMnemonic
94-
ss <- V0.askWalletSnapshot
95-
addWalletInfo ss v0wallet
96-
where
97-
-- NOTE: this is temporary solution until we get rid of V0 error handling and/or we lift error handling into types:
98-
-- https://github.com/input-output-hk/cardano-sl/pull/2811#discussion_r183469153
99-
-- https://github.com/input-output-hk/cardano-sl/pull/2811#discussion_r183472103
100-
rethrowDuplicateMnemonic (e :: V0.WalletError) =
101-
case e of
102-
V0.RequestError "Wallet with that mnemonics already exists" -> throwM WalletAlreadyExists
103-
_ -> throwM e
91+
ev0wallet <- newWalletHandler newwalOperation spendingPassword walletInit
92+
case ev0wallet of
93+
Left cidWal -> do
94+
walletId <- migrate cidWal
95+
throwM (WalletAlreadyExists walletId)
96+
Right v0wallet -> do
97+
ss <- V0.askWalletSnapshot
98+
addWalletInfo ss v0wallet
10499

105100
-- | Returns the full (paginated) list of wallets.
106101
listWallets :: ( MonadThrow m

wallet-new/src/Cardano/Wallet/API/V1/Types.hs

+5-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module Cardano.Wallet.API.V1.Types (
3131
, NewWallet (..)
3232
, WalletUpdate (..)
3333
, WalletId (..)
34+
, exampleWalletId
3435
, WalletOperation (..)
3536
, SpendingPassword
3637
-- * Addresses
@@ -430,6 +431,9 @@ instance BuildableSafeGen AssuranceLevel where
430431
-- | A Wallet ID.
431432
newtype WalletId = WalletId Text deriving (Show, Eq, Ord, Generic)
432433

434+
exampleWalletId :: WalletId
435+
exampleWalletId = WalletId "J7rQqaLLHBFPrgJXwpktaMB1B1kQBXAyc2uRSfRPzNVGiv6TdxBzkPNBUWysZZZdhFG9gRy3sQFfX5wfpLbi4XTFGFxTg"
436+
433437
deriveJSON Serokell.defaultOptions ''WalletId
434438

435439
instance ToSchema WalletId where
@@ -438,9 +442,7 @@ instance ToSchema WalletId where
438442
instance ToJSONKey WalletId
439443

440444
instance Arbitrary WalletId where
441-
arbitrary =
442-
let wid = "J7rQqaLLHBFPrgJXwpktaMB1B1kQBXAyc2uRSfRPzNVGiv6TdxBzkPNBUWysZZZdhFG9gRy3sQFfX5wfpLbi4XTFGFxTg"
443-
in WalletId <$> elements [wid]
445+
arbitrary = elements [exampleWalletId]
444446

445447
deriveSafeBuildable ''WalletId
446448
instance BuildableSafeGen WalletId where

wallet/src/Pos/Wallet/Web/Methods/Restore.hs

+72-21
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
module Pos.Wallet.Web.Methods.Restore
66
( newWallet
7+
, newWalletNoThrow
78
, importWallet
89
, restoreWalletFromSeed
10+
, restoreWalletFromSeedNoThrow
911
, restoreWalletFromBackup
1012
, addInitialRichAccount
1113

@@ -21,6 +23,7 @@ import Data.Default (Default (def))
2123
import Formatting (build, sformat, (%))
2224
import System.IO.Error (isDoesNotExistError)
2325
import System.Wlog (logDebug)
26+
import Data.Traversable (for)
2427

2528
import qualified Data.HashMap.Strict as HM
2629
import Pos.Client.KeyStorage (addSecretKey)
@@ -58,47 +61,95 @@ import UnliftIO (MonadUnliftIO)
5861
initialAccAddrIdxs :: Word32
5962
initialAccAddrIdxs = firstHardened
6063

64+
mnemonicExists :: Text
65+
mnemonicExists = "Wallet with that mnemonics already exists"
66+
67+
-- | Creates a wallet. If the wallet with the given passphrase already exists,
68+
-- then we return 'Left' of the wallet's ID.
6169
mkWallet
6270
:: L.MonadWalletLogic ctx m
63-
=> PassPhrase -> CWalletInit -> Bool -> m (EncryptedSecretKey, CId Wal)
71+
=> PassPhrase
72+
-> CWalletInit
73+
-> Bool
74+
-> m (Either (CId Wal) (EncryptedSecretKey, CId Wal))
6475
mkWallet passphrase CWalletInit {..} isReady = do
6576
let CWalletMeta {..} = cwInitMeta
6677

6778
skey <- genSaveRootKey passphrase (bpToList cwBackupPhrase)
6879
let cAddr = encToCId skey
6980

70-
CWallet{..} <- L.createWalletSafe cAddr cwInitMeta isReady
71-
-- can't return this result, since balances can change
81+
eresult <- fmap Right (L.createWalletSafe cAddr cwInitMeta isReady)
82+
`catch` \(e :: WalletError) ->
83+
case e of
84+
RequestError msg | msg == mnemonicExists ->
85+
pure (Left cAddr)
86+
_ ->
87+
throwM e
88+
89+
for eresult $ \CWallet{..} -> do
7290

73-
let accMeta = CAccountMeta { caName = "Initial account" }
74-
accInit = CAccountInit { caInitWId = cwId, caInitMeta = accMeta }
75-
() <$ L.newAccountIncludeUnready True (DeterminedSeed initialAccAddrIdxs) passphrase accInit
91+
-- can't return this result, since balances can change
92+
93+
let accMeta = CAccountMeta { caName = "Initial account" }
94+
accInit = CAccountInit { caInitWId = cwId, caInitMeta = accMeta }
95+
() <$ L.newAccountIncludeUnready True (DeterminedSeed initialAccAddrIdxs) passphrase accInit
96+
97+
return (skey, cAddr)
7698

77-
return (skey, cAddr)
7899

79100
newWallet :: L.MonadWalletLogic ctx m => PassPhrase -> CWalletInit -> m CWallet
80-
newWallet passphrase cwInit = do
101+
newWallet = throwMnemonicExists ... newWalletNoThrow
102+
103+
newWalletNoThrow
104+
:: L.MonadWalletLogic ctx m
105+
=> PassPhrase
106+
-> CWalletInit
107+
-> m (Either (CId Wal) CWallet)
108+
newWalletNoThrow passphrase cwInit = do
81109
db <- askWalletDB
82110
-- A brand new wallet doesn't need any syncing, so we mark isReady=True
83-
(_, wId) <- mkWallet passphrase cwInit True
84-
removeHistoryCache db wId
85-
-- BListener checks current syncTip before applying update,
86-
-- thus setting it up to date manually here
87-
withStateLockNoMetrics HighPriority $ \tip -> setWalletSyncTip db wId tip
88-
L.getWallet wId
111+
eresult <- mkWallet passphrase cwInit True
112+
for eresult $ \(_, wId) -> do
113+
removeHistoryCache db wId
114+
-- BListener checks current syncTip before applying update,
115+
-- thus setting it up to date manually here
116+
withStateLockNoMetrics HighPriority $ \tip -> setWalletSyncTip db wId tip
117+
L.getWallet wId
118+
89119

90120
{- | Restores a wallet from a seed. The process is conceptually divided into
91121
-- two parts:
92122
-- 1. Recover this wallet balance from the global Utxo (fast, and synchronous);
93123
-- 2. Recover the full transaction history from the blockchain (slow, asynchronous).
94124
-}
95-
restoreWalletFromSeed :: ( L.MonadWalletLogic ctx m
96-
, MonadUnliftIO m
97-
, HasLens SyncQueue ctx SyncQueue
98-
) => PassPhrase -> CWalletInit -> m CWallet
99-
restoreWalletFromSeed passphrase cwInit = do
100-
(sk, _) <- mkWallet passphrase cwInit False
101-
restoreWallet sk
125+
restoreWalletFromSeed
126+
:: ( L.MonadWalletLogic ctx m
127+
, MonadUnliftIO m
128+
, HasLens SyncQueue ctx SyncQueue
129+
)
130+
=> PassPhrase
131+
-> CWalletInit
132+
-> m CWallet
133+
restoreWalletFromSeed = throwMnemonicExists ... restoreWalletFromSeedNoThrow
134+
135+
throwMnemonicExists :: MonadThrow m => m (Either (CId Wal) a) -> m a
136+
throwMnemonicExists m = m >>= \case
137+
Left _ -> throwM (RequestError mnemonicExists)
138+
Right a -> pure a
139+
140+
-- | Restores a wallet without throwing an exception if the wallet already
141+
-- exists.
142+
restoreWalletFromSeedNoThrow
143+
:: ( L.MonadWalletLogic ctx m
144+
, MonadUnliftIO m
145+
, HasLens SyncQueue ctx SyncQueue
146+
)
147+
=> PassPhrase
148+
-> CWalletInit
149+
-> m (Either (CId Wal) CWallet)
150+
restoreWalletFromSeedNoThrow passphrase cwInit = do
151+
eresult <- mkWallet passphrase cwInit False
152+
for eresult $ \(sk, _) -> restoreWallet sk
102153

103154
restoreWallet :: ( L.MonadWalletLogic ctx m
104155
, MonadUnliftIO m

0 commit comments

Comments
 (0)