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

Commit

Permalink
Merge pull request #3132 from input-output-hk/matt/cb-254-wallet-id
Browse files Browse the repository at this point in the history
[CBR-254] Implement wallet ID in error
  • Loading branch information
KtorZ committed Jun 27, 2018
2 parents 7a7c74d + 4f16c38 commit 2646ebd
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 47 deletions.
6 changes: 4 additions & 2 deletions wallet-new/integration/WalletSpecs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ walletSpecs _ wc = do
}
-- First wallet creation/restoration should succeed
result <- postWallet wc newWallet1
void $ result `shouldPrism` _Right
wallet <- fmap wrData (result `shouldPrism` _Right)
-- Second wallet creation/restoration should rise WalletAlreadyExists
eresp <- postWallet wc newWallet2
clientError <- eresp `shouldPrism` _Left
clientError `shouldBe` ClientWalletError WalletAlreadyExists
clientError
`shouldBe`
ClientWalletError (WalletAlreadyExists (walId wallet))
12 changes: 6 additions & 6 deletions wallet-new/src/Cardano/Wallet/API/V1/Errors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import qualified Pos.Data.Attributes as Core

import Cardano.Wallet.API.Response.JSend (ResponseStatus (ErrorStatus))
import Cardano.Wallet.API.V1.Generic (gparseJsend, gtoJsend)
import Cardano.Wallet.API.V1.Types (SyncPercentage, SyncProgress (..), V1 (..),
mkEstimatedCompletionTime, mkSyncPercentage,
mkSyncThroughput)
import Cardano.Wallet.API.V1.Types (SyncPercentage, SyncProgress (..), V1 (..), WalletId,
exampleWalletId, mkEstimatedCompletionTime,
mkSyncPercentage, mkSyncThroughput)

--
-- Error handling
Expand Down Expand Up @@ -65,7 +65,7 @@ data WalletError =
| InvalidAddressFormat { weMsg :: !Text }
| WalletNotFound
-- FIXME(akegalj): https://iohk.myjetbrains.com/youtrack/issue/CSL-2496
| WalletAlreadyExists
| WalletAlreadyExists { weWalletId :: WalletId }
| AddressNotFound
| TxFailedToStabilize
| TxRedemptionDepleted
Expand Down Expand Up @@ -152,7 +152,7 @@ sample =
, UnknownError "Unknown error"
, InvalidAddressFormat "Invalid base58 representation."
, WalletNotFound
, WalletAlreadyExists
, WalletAlreadyExists exampleWalletId
, AddressNotFound
, MissingRequiredParams (("wallet_id", "walletId") :| [])
, WalletIsNotReadyToProcessPayments sampleSyncProgress
Expand All @@ -177,7 +177,7 @@ describe = \case
"Provided address format is not valid."
WalletNotFound ->
"Reference to an unexisting wallet was given."
WalletAlreadyExists ->
WalletAlreadyExists _ ->
"Can't create or restore a wallet. The wallet already exists."
AddressNotFound ->
"Reference to an unexisting address was given."
Expand Down
25 changes: 10 additions & 15 deletions wallet-new/src/Cardano/Wallet/API/V1/LegacyHandlers/Wallets.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import Pos.Update.Configuration ()

import Pos.Util (HasLens (..))
import qualified Pos.Wallet.WalletMode as V0
import qualified Pos.Wallet.Web.Error.Types as V0
import Pos.Wallet.Web.Methods.Logic (MonadWalletLogic, MonadWalletLogicRead)
import Pos.Wallet.Web.Tracking.Types (SyncQueue)
import Servant
Expand Down Expand Up @@ -79,27 +78,23 @@ newWallet NewWallet{..} = do
-- is still catching up.
unless (isNodeSufficientlySynced spV0) $ throwM (NodeIsStillSyncing syncPercentage)

let newWalletHandler CreateWallet = V0.newWallet
newWalletHandler RestoreWallet = V0.restoreWalletFromSeed
let newWalletHandler CreateWallet = V0.newWalletNoThrow
newWalletHandler RestoreWallet = V0.restoreWalletFromSeedNoThrow
(V1 spendingPassword) = fromMaybe (V1 mempty) newwalSpendingPassword
(V1 backupPhrase) = newwalBackupPhrase
initMeta <- V0.CWalletMeta <$> pure newwalName
<*> migrate newwalAssuranceLevel
<*> pure 0
let walletInit = V0.CWalletInit initMeta (V0.CBackupPhrase backupPhrase)
single <$> do
v0wallet <- newWalletHandler newwalOperation spendingPassword walletInit
`catch` rethrowDuplicateMnemonic
ss <- V0.askWalletSnapshot
addWalletInfo ss v0wallet
where
-- NOTE: this is temporary solution until we get rid of V0 error handling and/or we lift error handling into types:
-- https://github.com/input-output-hk/cardano-sl/pull/2811#discussion_r183469153
-- https://github.com/input-output-hk/cardano-sl/pull/2811#discussion_r183472103
rethrowDuplicateMnemonic (e :: V0.WalletError) =
case e of
V0.RequestError "Wallet with that mnemonics already exists" -> throwM WalletAlreadyExists
_ -> throwM e
ev0wallet <- newWalletHandler newwalOperation spendingPassword walletInit
case ev0wallet of
Left cidWal -> do
walletId <- migrate cidWal
throwM (WalletAlreadyExists walletId)
Right v0wallet -> do
ss <- V0.askWalletSnapshot
addWalletInfo ss v0wallet

-- | Returns the full (paginated) list of wallets.
listWallets :: ( MonadThrow m
Expand Down
8 changes: 5 additions & 3 deletions wallet-new/src/Cardano/Wallet/API/V1/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module Cardano.Wallet.API.V1.Types (
, NewWallet (..)
, WalletUpdate (..)
, WalletId (..)
, exampleWalletId
, WalletOperation (..)
, SpendingPassword
-- * Addresses
Expand Down Expand Up @@ -420,6 +421,9 @@ instance BuildableSafeGen AssuranceLevel where
-- | A Wallet ID.
newtype WalletId = WalletId Text deriving (Show, Eq, Ord, Generic)

exampleWalletId :: WalletId
exampleWalletId = WalletId "J7rQqaLLHBFPrgJXwpktaMB1B1kQBXAyc2uRSfRPzNVGiv6TdxBzkPNBUWysZZZdhFG9gRy3sQFfX5wfpLbi4XTFGFxTg"

deriveJSON Serokell.defaultOptions ''WalletId

instance ToSchema WalletId where
Expand All @@ -428,9 +432,7 @@ instance ToSchema WalletId where
instance ToJSONKey WalletId

instance Arbitrary WalletId where
arbitrary =
let wid = "J7rQqaLLHBFPrgJXwpktaMB1B1kQBXAyc2uRSfRPzNVGiv6TdxBzkPNBUWysZZZdhFG9gRy3sQFfX5wfpLbi4XTFGFxTg"
in WalletId <$> elements [wid]
arbitrary = elements [exampleWalletId]

deriveSafeBuildable ''WalletId
instance BuildableSafeGen WalletId where
Expand Down
93 changes: 72 additions & 21 deletions wallet/src/Pos/Wallet/Web/Methods/Restore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

module Pos.Wallet.Web.Methods.Restore
( newWallet
, newWalletNoThrow
, importWallet
, restoreWalletFromSeed
, restoreWalletFromSeedNoThrow
, restoreWalletFromBackup
, addInitialRichAccount

Expand All @@ -18,6 +20,7 @@ import Universum
import qualified Control.Exception.Safe as E
import Control.Lens (each, ix, traversed)
import Data.Default (Default (def))
import Data.Traversable (for)
import Formatting (build, sformat, (%))
import System.IO.Error (isDoesNotExistError)
import System.Wlog (logDebug)
Expand Down Expand Up @@ -55,47 +58,95 @@ import UnliftIO (MonadUnliftIO)
initialAccAddrIdxs :: Word32
initialAccAddrIdxs = firstHardened

mnemonicExists :: Text
mnemonicExists = "Wallet with that mnemonics already exists"

-- | Creates a wallet. If the wallet with the given passphrase already exists,
-- then we return 'Left' of the wallet's ID.
mkWallet
:: L.MonadWalletLogic ctx m
=> PassPhrase -> CWalletInit -> Bool -> m (EncryptedSecretKey, CId Wal)
=> PassPhrase
-> CWalletInit
-> Bool
-> m (Either (CId Wal) (EncryptedSecretKey, CId Wal))
mkWallet passphrase CWalletInit {..} isReady = do
let CWalletMeta {..} = cwInitMeta

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

CWallet{..} <- L.createWalletSafe cAddr cwInitMeta isReady
-- can't return this result, since balances can change
eresult <- fmap Right (L.createWalletSafe cAddr cwInitMeta isReady)
`catch` \(e :: WalletError) ->
case e of
RequestError msg | msg == mnemonicExists ->
pure (Left cAddr)
_ ->
throwM e

for eresult $ \CWallet{..} -> do

let accMeta = CAccountMeta { caName = "Initial account" }
accInit = CAccountInit { caInitWId = cwId, caInitMeta = accMeta }
() <$ L.newAccountIncludeUnready True (DeterminedSeed initialAccAddrIdxs) passphrase accInit
-- can't return this result, since balances can change

let accMeta = CAccountMeta { caName = "Initial account" }
accInit = CAccountInit { caInitWId = cwId, caInitMeta = accMeta }
() <$ L.newAccountIncludeUnready True (DeterminedSeed initialAccAddrIdxs) passphrase accInit

return (skey, cAddr)

return (skey, cAddr)

newWallet :: L.MonadWalletLogic ctx m => PassPhrase -> CWalletInit -> m CWallet
newWallet passphrase cwInit = do
newWallet = throwMnemonicExists ... newWalletNoThrow

newWalletNoThrow
:: L.MonadWalletLogic ctx m
=> PassPhrase
-> CWalletInit
-> m (Either (CId Wal) CWallet)
newWalletNoThrow passphrase cwInit = do
db <- askWalletDB
-- A brand new wallet doesn't need any syncing, so we mark isReady=True
(_, wId) <- mkWallet passphrase cwInit True
removeHistoryCache db wId
-- BListener checks current syncTip before applying update,
-- thus setting it up to date manually here
withStateLockNoMetrics HighPriority $ \tip -> setWalletSyncTip db wId tip
L.getWallet wId
eresult <- mkWallet passphrase cwInit True
for eresult $ \(_, wId) -> do
removeHistoryCache db wId
-- BListener checks current syncTip before applying update,
-- thus setting it up to date manually here
withStateLockNoMetrics HighPriority $ \tip -> setWalletSyncTip db wId tip
L.getWallet wId


{- | Restores a wallet from a seed. The process is conceptually divided into
-- two parts:
-- 1. Recover this wallet balance from the global Utxo (fast, and synchronous);
-- 2. Recover the full transaction history from the blockchain (slow, asynchronous).
-}
restoreWalletFromSeed :: ( L.MonadWalletLogic ctx m
, MonadUnliftIO m
, HasLens SyncQueue ctx SyncQueue
) => PassPhrase -> CWalletInit -> m CWallet
restoreWalletFromSeed passphrase cwInit = do
(sk, _) <- mkWallet passphrase cwInit False
restoreWallet sk
restoreWalletFromSeed
:: ( L.MonadWalletLogic ctx m
, MonadUnliftIO m
, HasLens SyncQueue ctx SyncQueue
)
=> PassPhrase
-> CWalletInit
-> m CWallet
restoreWalletFromSeed = throwMnemonicExists ... restoreWalletFromSeedNoThrow

throwMnemonicExists :: MonadThrow m => m (Either (CId Wal) a) -> m a
throwMnemonicExists m = m >>= \case
Left _ -> throwM (RequestError mnemonicExists)
Right a -> pure a

-- | Restores a wallet without throwing an exception if the wallet already
-- exists.
restoreWalletFromSeedNoThrow
:: ( L.MonadWalletLogic ctx m
, MonadUnliftIO m
, HasLens SyncQueue ctx SyncQueue
)
=> PassPhrase
-> CWalletInit
-> m (Either (CId Wal) CWallet)
restoreWalletFromSeedNoThrow passphrase cwInit = do
eresult <- mkWallet passphrase cwInit False
for eresult $ \(sk, _) -> restoreWallet sk

restoreWallet :: ( L.MonadWalletLogic ctx m
, MonadUnliftIO m
Expand Down

0 comments on commit 2646ebd

Please sign in to comment.