Skip to content

Commit

Permalink
Add XPrv mgmt in REST interface
Browse files Browse the repository at this point in the history
  • Loading branch information
paolino committed Nov 15, 2024
1 parent c454a87 commit 987ef79
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 188 deletions.
3 changes: 3 additions & 0 deletions lib/customer-deposit-wallet/customer-deposit-wallet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ library
, async
, base
, base58-bytestring
, base16-bytestring
, bech32
, bech32-th
, bytestring
, cardano-addresses
, cardano-balance-tx
, cardano-crypto
, cardano-ledger-api
Expand Down Expand Up @@ -223,6 +225,7 @@ test-suite unit
, openapi3
, pretty-simple
, QuickCheck
, serialise
, temporary
, time
, transformers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import Cardano.Wallet.Deposit.HTTP.Types.JSON
import Cardano.Wallet.Deposit.IO
( WalletBootEnv
)
import Cardano.Wallet.Deposit.Pure.State.Creation
( credentialsFromEncodedXPub
, credentialsFromMnemonics
)
import Cardano.Wallet.Deposit.REST
( WalletResource
, WalletResourceM
Expand All @@ -34,8 +38,6 @@ import Cardano.Wallet.Deposit.REST.Catch
import Cardano.Wallet.Deposit.REST.Wallet.Create
( PostWalletViaMenmonic (..)
, PostWalletViaXPub (..)
, decodeXPub
, xpubFromMnemonics
)
import Control.Tracer
( Tracer
Expand Down Expand Up @@ -88,16 +90,16 @@ createWalletViaMnemonic
dir
boot
resource
(PostWalletViaMenmonic mnemonics' users') =
(PostWalletViaMenmonic mnemonics' passphrase' users') =
onlyOnWalletIntance resource initWallet $> NoContent
where
initWallet :: WalletResourceM ()
initWallet =
REST.initXPubWallet
REST.initWallet
tracer
boot
dir
(xpubFromMnemonics mnemonics')
(credentialsFromMnemonics mnemonics' passphrase')
(fromIntegral users')

createWalletViaXPub
Expand All @@ -119,17 +121,16 @@ createWalletViaXPub
Right () -> pure NoContent
where
initWallet :: WalletResourceM (Either String ())
initWallet = case decodeXPub xpubText of
Left e -> pure $ Left e
Right (Just xpub') ->
initWallet = case credentialsFromEncodedXPub xpubText of
Left e -> pure $ Left $ show e
Right xpub' ->
Right
<$> REST.initXPubWallet
<$> REST.initWallet
tracer
boot
dir
xpub'
(fromIntegral users')
Right Nothing -> pure $ Left "Invalid XPub"

listCustomerH
:: WalletResource
Expand Down
90 changes: 60 additions & 30 deletions lib/customer-deposit-wallet/rest/Cardano/Wallet/Deposit/REST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- |
-- Copyright: © 2024 Cardano Foundation
Expand All @@ -26,7 +27,7 @@ module Cardano.Wallet.Deposit.REST
-- * Operations

-- ** Initialization
, initXPubWallet
, initWallet
, loadWallet

-- ** Mapping between customers and addresses
Expand Down Expand Up @@ -57,11 +58,15 @@ module Cardano.Wallet.Deposit.REST
import Prelude

import Cardano.Address.Derivation
( xpubFromBytes
, xpubToBytes
( xpubToBytes
)
import Cardano.Crypto.Wallet
( XPub (..)
( XPrv
, XPub (..)
, unXPrv
, unXPub
, xprv
, xpub
)
import Cardano.Wallet.Address.BIP32
( BIP32Path
Expand All @@ -74,20 +79,25 @@ import Cardano.Wallet.Deposit.IO.Resource
, ErrResourceMissing (..)
)
import Cardano.Wallet.Deposit.Pure
( Customer
( Credentials
, Customer
, ErrCreatePayment
, Word31
, fromXPubAndGenesis
, fromCredentialsAndGenesis
)
import Cardano.Wallet.Deposit.Pure.API.TxHistory
( ByCustomer
, ByTime
)
import Cardano.Wallet.Deposit.Pure.State.Creation
( xpubFromCredentials
)
import Cardano.Wallet.Deposit.Read
( Address
)
import Codec.Serialise
( deserialise
( Serialise (..)
, deserialise
, serialise
)
import Control.DeepSeq
Expand Down Expand Up @@ -120,6 +130,9 @@ import Data.ByteArray.Encoding
( Base (..)
, convertToBase
)
import Data.ByteString
( ByteString
)
import Data.List
( isPrefixOf
)
Expand Down Expand Up @@ -245,52 +258,69 @@ findTheDepositWalletOnDisk dir action = do
ds <- scanDirectoryForDepositPrefix dir
case ds of
[d] -> do
(xpub, users) <- deserialise <$> BL.readFile (dir </> d)
case xpubFromBytes xpub of
Nothing -> action $ Left $ ErrDatabaseCorrupted (dir </> d)
Just identity -> do
let state =
fromXPubAndGenesis
identity
(fromIntegral @Int users)
Read.mockGenesisDataMainnet
store <- newStore
writeS store state
action $ Right store
(credentials, users) <-
deserialise <$> BL.readFile (dir </> d)
let state =
fromCredentialsAndGenesis
credentials
(fromIntegral @Int users)
Read.mockGenesisDataMainnet
store <- newStore
writeS store state
action $ Right store
[] -> action $ Left $ ErrDatabaseNotFound dir
ds' -> action $ Left $ ErrMultipleDatabases ((dir </>) <$> ds')

instance Serialise XPub where
encode = encode . unXPub
decode = do
b <- decode
case xpub b of
Right x -> pure x
Left e -> fail e

instance Serialise XPrv where
encode = encode . unXPrv
decode = do
b :: ByteString <- decode
case xprv b of
Right x -> pure x
Left e -> fail e

instance Serialise Credentials

-- | Try to create a new wallet
createTheDepositWalletOnDisk
:: Tracer IO String
-- ^ Tracer for logging
-> FilePath
-- ^ Path to the wallet database directory
-> XPub
-> Credentials
-- ^ Id of the wallet
-> Word31
-- ^ Max number of users ?
-> (Maybe WalletIO.WalletStore -> IO a)
-- ^ Action to run if the wallet is created
-> IO a
createTheDepositWalletOnDisk _tr dir identity users action = do
createTheDepositWalletOnDisk _tr dir credentials users action = do
ds <- scanDirectoryForDepositPrefix dir
case ds of
[] -> do
let fp = dir </> depositPrefix <> hashWalletId identity
let fp = dir </> depositPrefix <> hashWalletId credentials
BL.writeFile fp
$ serialise (xpubToBytes identity, fromIntegral users :: Int)
$ serialise (credentials, fromIntegral users :: Int)
store <- newStore
action $ Just store
_ -> do
action Nothing
where
hashWalletId :: XPub -> String
hashWalletId :: Credentials -> String
hashWalletId =
B8.unpack
. convertToBase Base16
. blake2b160
. xpubPublicKey
. xpubToBytes
. xpubFromCredentials

-- | Load an existing wallet from disk.
loadWallet
Expand All @@ -316,27 +346,27 @@ loadWallet bootEnv dir = do
<$> Resource.putResource action resource

-- | Initialize a new wallet from an 'XPub'.
initXPubWallet
initWallet
:: Tracer IO String
-- ^ Tracer for logging
-> WalletIO.WalletBootEnv IO
-- ^ Environment for the wallet
-> FilePath
-- ^ Path to the wallet database directory
-> XPub
-> Credentials
-- ^ Id of the wallet
-> Word31
-- ^ Max number of users ?
-> WalletResourceM ()
initXPubWallet tr bootEnv dir xpub users = do
initWallet tr bootEnv dir credentials users = do
let action
:: (WalletIO.WalletInstance -> IO b) -> IO (Either ErrDatabase b)
action f = createTheDepositWalletOnDisk tr dir xpub users $ \case
action f = createTheDepositWalletOnDisk tr dir credentials users $ \case
Just wallet -> do
fmap Right
$ WalletIO.withWalletInit
(WalletIO.WalletEnv bootEnv wallet)
xpub
credentials
users
$ \i -> do
addresses <- map snd <$> WalletIO.listCustomers i
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,22 @@
module Cardano.Wallet.Deposit.REST.Wallet.Create
( PostWalletViaMenmonic (..)
, PostWalletViaXPub (..)
, decodeXPub
, xpubFromMnemonics
, encodeXPub
)
where

import Prelude

import Cardano.Address.Derivation
( XPub
, generate
, toXPub
, xpubFromBytes
, xpubToBytes
)
import Data.ByteArray.Encoding
( Base (Base64)
, convertFromBase
, convertToBase
)
import Data.ByteString.Char8
( ByteString
)
import Data.Text
( Text
)
import GHC.Generics
( Generic
)

import qualified Data.Text.Encoding as T

-- | Data for a request to create a wallet via a mnemonic.
data PostWalletViaMenmonic = PostWalletViaMenmonic
{ mnemonics :: Text
, password :: Text
, trackedCustomers :: Int
}
deriving (Generic)
Expand All @@ -49,19 +30,3 @@ data PostWalletViaXPub = PostWalletViaXPub
, trackedCustomers :: Int
}
deriving (Generic)

unBase64 :: ByteString -> Either String ByteString
unBase64 = convertFromBase Base64

-- | Decode an extended public key from a base64-encoded text.
decodeXPub :: Text -> Either String (Maybe XPub)
decodeXPub = fmap xpubFromBytes . unBase64 . T.encodeUtf8

-- | Encode an extended public key to a base64-encoded text.
encodeXPub :: XPub -> Text
encodeXPub = T.decodeUtf8 . convertToBase Base64 . xpubToBytes

-- | Generate an extended public key from a mnemonic.
-- this is not what one wants to use in production
xpubFromMnemonics :: Text -> XPub
xpubFromMnemonics = toXPub . generate . T.encodeUtf8
15 changes: 8 additions & 7 deletions lib/customer-deposit-wallet/src/Cardano/Wallet/Deposit/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,15 @@ module Cardano.Wallet.Deposit.IO

import Prelude

import Cardano.Crypto.Wallet
( XPub
)
import Cardano.Wallet.Address.BIP32
( BIP32Path
)
import Cardano.Wallet.Deposit.IO.Network.Type
( NetworkEnv (slotToUTCTime)
)
import Cardano.Wallet.Deposit.Pure
( Customer
( Credentials
, Customer
, ValueTransfer
, WalletPublicIdentity (..)
, WalletState
Expand Down Expand Up @@ -170,7 +168,7 @@ readWalletState WalletInstance{walletState} =
-- | Initialize a new wallet in the given environment.
withWalletInit
:: WalletEnv IO
-> XPub
-> Credentials
-> Word31
-> (WalletInstance -> IO a)
-> IO a
Expand All @@ -179,12 +177,15 @@ withWalletInit
{ bootEnv = WalletBootEnv{genesisData}
, ..
}
xpub
credentials
knownCustomerCount
action = do
walletState <-
DBVar.initDBVar store
$ Wallet.fromXPubAndGenesis xpub knownCustomerCount genesisData
$ Wallet.fromCredentialsAndGenesis
credentials
knownCustomerCount
genesisData
withWalletDBVar env walletState action

-- | Load an existing wallet from the given environment.
Expand Down
Loading

0 comments on commit 987ef79

Please sign in to comment.