diff --git a/lib/launcher/src/Cardano/Launcher/Wallet.hs b/lib/launcher/src/Cardano/Launcher/Wallet.hs new file mode 100644 index 00000000000..99a0233793a --- /dev/null +++ b/lib/launcher/src/Cardano/Launcher/Wallet.hs @@ -0,0 +1,94 @@ +-- | +-- Copyright: © 2023-2023 IOHK +-- License: Apache-2.0 +-- +-- Provides a function to launch @cardano-wallet@. +module Cardano.Launcher.Wallet + ( -- * Startup + withCardanoWallet + , CardanoWalletConfig (..) + + , CardanoWalletConn + , getWalletPort + ) where + +import Prelude + +import Cardano.Launcher + ( LauncherLog, ProcessHasExited, withBackendCreateProcess ) +import Cardano.Launcher.Node + ( CardanoNodeConn, nodeSocketFile ) +import Control.Tracer + ( Tracer (..) ) +import Data.Bifunctor + ( first ) +import Data.List + ( isPrefixOf ) +import Data.Maybe + ( maybeToList ) +import Data.Text.Class + ( FromText (..), TextDecodingError (..), ToText (..) + , toTextFromBoundedEnum, fromTextToBoundedEnum ) +import System.Environment + ( getEnvironment ) +import System.FilePath + ( isValid, takeFileName, () ) +import Text.Read + ( readMaybe ) +import Data.Network + ( PortNumber ) +import UnliftIO.Process + ( CreateProcess (..), proc ) + +import qualified Data.Text as T + +{----------------------------------------------------------------------------- + Startup +------------------------------------------------------------------------------} +-- | Parameters for connecting to the running wallet process. +newtype CardanoWalletConn = CardanoWalletConn { getWalletPort :: PortNumber } + deriving (Show, Eq) + +instance ToText CardanoWalletConn where + toText = toText . fromEnum . getWalletPort + +instance FromText CardanoWalletConn where + fromText = fmap (CardanoWalletConn . toEnum) . fromText + +-- | A subset of the @cardano-wallet@ CLI parameters, +-- used for starting the process. +data CardanoWalletConfig = CardanoWalletConfig + { walletPort :: PortNumber -- Good default: 8090 + , walletDatabaseDir :: FilePath + , mainnetOrTestnet + , nodeByronGenesis :: FilePath + } deriving (Show, Eq) + +-- | Spawns a @cardano-wallet@ process. +-- +-- IMPORTANT: @cardano-wallet@ must be available on the current path. +withCardanoWallet + :: Tracer IO LauncherLog + -- ^ Trace for subprocess control logging + -> CardanoWalletConfig + -> CardanoNodeConn + -> (CardanoWalletConn -> IO a) + -- ^ Callback function with a socket filename and genesis params + -> IO (Either ProcessHasExited a) +withCardanoWallet tr cfg@CardanoWalletConfig{..} node action = + withBackendProcess tr (cardanoWallet cfg node) $ + \_ _ -> action $ CardanoWalletConn walletPort + +cardanoWallet :: CardanoWalletConfig -> CardanoNodeConn -> Command +cardanoWalletCommand CardanoWalletConfig{..} node = + Command "cardano-wallet" + [ "server" + , "--node-socket", nodeSocketFile node + , "--testnet", "config/preview/byron-genesis.json" + -- FIXME: Take testnet genesis from node config + , "--database", walletDatabaseDir + , "--port", show walletPort + ] + (pure ()) + Inherit + Inherit diff --git a/lib/wallet-membench/bench/memory-bench.hs b/lib/wallet-membench/bench/memory-bench.hs new file mode 100644 index 00000000000..aa35b69ab69 --- /dev/null +++ b/lib/wallet-membench/bench/memory-bench.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE NamedFieldPuns #-} + +import Prelude + +import qualified Cardano.Launcher as C + +{----------------------------------------------------------------------------- + Parameters +------------------------------------------------------------------------------} +testNodeSnapshot = "cardano-node-db-snapshot.tar.gz" +testBlockHeight = 7285 :: Int +walletPort = "8090" + +{----------------------------------------------------------------------------- + Main +------------------------------------------------------------------------------} +main = do + withSystemTempDirectory $ \tmp -> do + cfg <- copyNodeSnapshot tmp + withCardanoNode cfg $ \node -> do + withCardanoWallet cfg node $ \wallet -> do + waitUntilSynchronized wallet + +waitUntilSynchronized wallet = do + height <- getLatestBlockHeight wallet + when (height < testBlockHeight) $ do + sleep 1 + waitUntilSynchronized wallet + +copyNodeSnapshot :: FilePath -> IO BenchmarkConfig +copyNodeSnapshot tmp = do + copyFile (dataDir testNodeSnapshot) tmp + gunzip (tmp testNodeSnapshot) + let dir = tmp filename testNodeSnapshot + pure $ BenchmarkConfig + { nodeConfigDir = dir "db-node" + } + +{----------------------------------------------------------------------------- + Cardano commands +------------------------------------------------------------------------------} +getLatestBlockHeight wallet = do + pure 0 + +{- + [| + curl -X GET http://localhost:8090/v2/wallets + | jq ".[0].tip.height.quantity" + |] +-} + +-- Launch the wallet, but we need to create a memory profile, too! +createWallet wallet = do + pure () +{- +createWallet wallet = do + [| + curl -X POST http://localhost:8090/v2/wallets \ + -d '{"mnemonic_sentence":["slab","praise","suffer","rabbit","during","dream","arch","harvest","culture","book","owner","loud","wool","salon","table","animal","vivid","arrow","dirt","divide","humble","tornado","solution","jungle"], + "passphrase":"Secure Passphrase", + "name":"My Test Wallet", + "address_pool_gap":20}' \ + -H "Content-Type: application/json" + |] +-} + +data BenchmarkConfig = BenchmarkConfig + { nodeConfigDir :: FilePath + , nodeDatabaseDir :: FilePath + } + +withCardanoWallet + :: BenchmarkConfig + -> C.CardanoNodeConn + -> (Wallet -> IO r) + -> IO r +withCardanoWallet BenchmarkConfig{..} node action = do + action undefined + -- C.nodeSocketFile node + +-- | Start a `cardano-node` process on the benchmark configuration. +withCardanoNode + :: BenchmarkConfig + -> (C.CardanoNodeConn -> IO r) + -> IO (Either C.ProcessHasExited r) +withCardanoNode BenchmarkConfig{..} = + C.withCardanoNode nullTracer $ + C.CardanoNodeConfig + { C.nodeDir = "" + , C.nodeConfigFile = nodeConfigDir "config.json" + , C.nodeTopologyFile = nodeConfigDir "topology.json" + , C.nodeDatabaseDir = nodeDatabaseDir + , C.nodeDlgCertFile = Nothing + , C.nodeSignKeyFile = Nothing + , C.nodeOpCertFile = Nothing + , C.nodeKesKeyFile = Nothing + , C.nodeVrfKeyFile = Nothing + , C.nodePort = Nothing + , C.nodeLoggingHostname = Nothing + } + +{----------------------------------------------------------------------------- + Utilities +------------------------------------------------------------------------------} +sleep :: Int -> IO () +sleep seconds = threadDelay (seconds * 1000 * 1000) + +copyFile :: FilePath -> Filepath -> IO () +copyFile _ _ = pure () + +gunzip :: FilePath -> IO () +gunzip _ = pure () diff --git a/lib/wallet-membench/cardano-wallet-memory-benchmark.cabal b/lib/wallet-membench/cardano-wallet-memory-benchmark.cabal new file mode 100644 index 00000000000..af01e01a189 --- /dev/null +++ b/lib/wallet-membench/cardano-wallet-memory-benchmark.cabal @@ -0,0 +1,36 @@ +name: cardano-wallet-memory-benchmark +version: 2023.4.14 +synopsis: Memory benchmark for cardano-wallet. +homepage: https://github.com/cardano-foundation/cardano-wallet +author: High Assurance Lab at Cardano Foundation +maintainer: cardanofoundation.org +copyright: 2023 Cardano Foundation +license: Apache-2.0 +category: Web +build-type: Simple +cabal-version: >=1.10 + +common language + default-language: Haskell2010 + default-extensions: + NamedFieldPuns + NoImplicitPrelude + OverloadedStrings + +benchmark memory + import: language + type: exitcode-stdio-1.0 + hs-source-dirs: bench + main-is: memory-bench.hs + build-depends: + , base + , cardano-wallet-launcher + , containers + , contra-tracer + , deepseq + , filepath + , fmt + , iohk-monitoring + , say + , text + , transformers diff --git a/lib/wallet-membench/data/cardano-node-db-snapshot.tar.gz b/lib/wallet-membench/data/cardano-node-db-snapshot.tar.gz new file mode 100644 index 00000000000..4ae87e27d2f Binary files /dev/null and b/lib/wallet-membench/data/cardano-node-db-snapshot.tar.gz differ