Skip to content

Commit

Permalink
tx-generator: load DRep SigningKeys from genesis into environment
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeier committed Sep 27, 2024
1 parent 73ff84c commit 75f7a71
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 15 deletions.
6 changes: 6 additions & 0 deletions bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ compileToScript = do
pure
tc <- askNixOption _nix_cardanoTracerSocket
emit $ StartProtocol nc tc

isDrepVoting <- fromMaybe False <$> askNixOption _nix_drep_voting
when isDrepVoting $ do
emit $ ReadDRepKeys nc
logMsg "Importing DRep SigningKeys. Done."

genesisWallet <- importGenesisFunds
collateralWallet <- addCollaterals genesisWallet
splitWallet <- splittingPhase genesisWallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ action a = case a of
SetProtocolParameters p -> setProtocolParameters p
StartProtocol configFile cardanoTracerSocket -> startProtocol configFile cardanoTracerSocket
ReadSigningKey name filePath -> readSigningKey name filePath
ReadDRepKeys filepath -> readDRepKeys filepath
DefineSigningKey name descr -> defineSigningKey name descr
AddFund era wallet txIn lovelace keyName -> addFund era wallet txIn lovelace keyName
Delay t -> delay t
Expand Down
15 changes: 15 additions & 0 deletions bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import Cardano.TxGenerator.Fund as Fund
import qualified Cardano.TxGenerator.FundQueue as FundQueue
import qualified Cardano.TxGenerator.Genesis as Genesis
import Cardano.TxGenerator.PlutusContext
import Cardano.TxGenerator.Setup.NodeConfig
import Cardano.TxGenerator.Setup.Plutus as Plutus
import Cardano.TxGenerator.Setup.SigningKey
import Cardano.TxGenerator.Tx
Expand All @@ -63,6 +64,7 @@ import "contra-tracer" Control.Tracer (Tracer (..))
import Data.ByteString.Lazy.Char8 as BSL (writeFile)
import Data.Ratio ((%))
import qualified Data.Text as Text (unpack)
import System.FilePath ((</>))

import Streaming
import qualified Streaming.Prelude as Streaming
Expand Down Expand Up @@ -98,6 +100,19 @@ readSigningKey name filePath =
defineSigningKey :: String -> SigningKey PaymentKey -> ActionM ()
defineSigningKey = setEnvKeys

readDRepKeys :: FilePath -> ActionM ()
readDRepKeys ncFile = do
genesis <- liftIO (mkNodeConfig ncFile) >>= either liftTxGenError (pure . getGenesisDirectory)
case genesis of
Nothing -> liftTxGenError $ TxGenError "readDRepKeys: no genesisDirectory could be retrieved from the node config"
-- "cache-entry" is a link or copy of the actual genesis folder created by "create-testnet-data"
-- in the workbench's run directory structure, this link or copy is created for each run - by workbench
Just d -> liftIO (Genesis.genesisLoadDRepKeys (d </> "cache-entry")) >>= \case
Left err -> liftTxGenError err
Right ks -> do
setEnvDRepKeys ks
traceDebug $ "DRep SigningKeys loaded: " ++ show (length ks) ++ " from: " ++ d

addFund :: AnyCardanoEra -> String -> TxIn -> L.Coin -> String -> ActionM ()
addFund era wallet txIn lovelace keyName = do
fundKey <- getEnvKeys keyName
Expand Down
12 changes: 11 additions & 1 deletion bench/tx-generator/src/Cardano/Benchmarking/Script/Env.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ module Cardano.Benchmarking.Script.Env (
, traceBenchTxSubmit
, getBenchTracers
, setBenchTracers
, getEnvDRepKeys
, setEnvDRepKeys
, getEnvGenesis
, setEnvGenesis
, getEnvKeys
Expand All @@ -63,7 +65,7 @@ module Cardano.Benchmarking.Script.Env (
, setEnvSummary
) where

import Cardano.Api (File (..), SocketPath)
import Cardano.Api (File (..), DRepKey, SocketPath)

import Cardano.Benchmarking.GeneratorTx
import qualified Cardano.Benchmarking.LogTypes as Tracer
Expand Down Expand Up @@ -108,6 +110,7 @@ data Env = Env { -- | 'Cardano.Api.ProtocolParameters' is ultimately
, envKeys :: Map String (SigningKey PaymentKey)
, envWallets :: Map String WalletRef
, envSummary :: Maybe PlutusBudgetSummary
, envDRepKeys :: [SigningKey DRepKey]
}
-- | `Env` uses `Maybe` to represent values that might be uninitialized.
-- This being empty means `Nothing` is used across the board, along with
Expand All @@ -121,6 +124,7 @@ emptyEnv = Env { protoParams = Nothing
, envSocketPath = Nothing
, envWallets = Map.empty
, envSummary = Nothing
, envDRepKeys = []
}

newEnvConsts :: IOManager -> Maybe Nix.NixServiceOptions -> STM Tracer.EnvConsts
Expand Down Expand Up @@ -197,6 +201,9 @@ setEnvGenesis val = modifyEnv (\e -> e { envGenesis = Just val })
setEnvKeys :: String -> SigningKey PaymentKey -> ActionM ()
setEnvKeys key val = modifyEnv (\e -> e { envKeys = Map.insert key val (envKeys e) })

setEnvDRepKeys :: [SigningKey DRepKey] -> ActionM ()
setEnvDRepKeys val = modifyEnv (\e -> e { envDRepKeys = val })

-- | Write accessor for `envProtocol`.
setEnvProtocol :: SomeConsensusProtocol -> ActionM ()
setEnvProtocol val = modifyEnv (\e -> e { envProtocol = Just val })
Expand Down Expand Up @@ -273,6 +280,9 @@ getEnvGenesis = getEnvVal envGenesis "Genesis"
getEnvKeys :: String -> ActionM (SigningKey PaymentKey)
getEnvKeys = getEnvMap envKeys

getEnvDRepKeys :: ActionM [SigningKey DRepKey]
getEnvDRepKeys = lift $ RWS.gets envDRepKeys

-- | Read accessor for `envNetworkId`.
getEnvNetworkId :: ActionM NetworkId
getEnvNetworkId = getEnvVal envNetworkId "Genesis"
Expand Down
5 changes: 5 additions & 0 deletions bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ data Action where
-- drops it into a state variable via
-- 'Cardano.Benchmarking.Script.Env.setEnvKeys'.
ReadSigningKey :: !String -> !(SigningKeyFile In) -> Action
-- | 'ReadDRepKeys' expects the path to a node config file. This
-- configuration is supposed to refer to a genesis which has
-- been created with cardano-cli create-testnet-data, and from
-- where DRep signing keys can be loaded.
ReadDRepKeys :: !FilePath -> Action
-- | 'DefineSigningKey' is just a 'Map.insert' on the state variable.
DefineSigningKey :: !String -> !(SigningKey PaymentKey) -> Action
-- | 'AddFund' is mostly a wrapper around
Expand Down
27 changes: 27 additions & 0 deletions bench/tx-generator/src/Cardano/TxGenerator/Genesis.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
Expand All @@ -11,6 +12,7 @@
module Cardano.TxGenerator.Genesis
( genesisInitialFunds
, genesisInitialFundForKey
, genesisLoadDRepKeys
, genesisTxInput
, genesisExpenditure
, genesisSecureInitialFund
Expand All @@ -22,17 +24,22 @@ import Cardano.Api
import Cardano.Api.Shelley (ReferenceScript (..), fromShelleyPaymentCredential,
fromShelleyStakeReference)

import Cardano.CLI.Types.Common (SigningKeyFile)
import qualified Cardano.Ledger.Coin as L
import Cardano.Ledger.Shelley.API (Addr (..), sgInitialFunds)
import Cardano.TxGenerator.Fund
import Cardano.TxGenerator.Setup.SigningKey (readDRepKeyFile)
import Cardano.TxGenerator.Types
import Cardano.TxGenerator.Utils
import Ouroboros.Consensus.Shelley.Node (validateGenesis)

import Data.Bifunctor (bimap, second)
import Data.Char (isDigit)
import Data.Function ((&))
import Data.List (find)
import qualified Data.ListMap as ListMap (toList)
import System.Directory (listDirectory)
import System.FilePath ((</>))


genesisValidate :: ShelleyGenesis -> Either String ()
Expand Down Expand Up @@ -136,3 +143,23 @@ mkGenesisTransaction key ttl fee txins txouts

castKey :: SigningKey PaymentKey -> SigningKey GenesisUTxOKey
castKey (PaymentSigningKey skey) = GenesisUTxOSigningKey skey

-- | This function assumes a directory structure as created by
-- cardano-cli's create-testnet-data command.
genesisLoadDRepKeys :: FilePath -> IO (Either TxGenError [SigningKey DRepKey])
genesisLoadDRepKeys genesisDir = runExceptT $ do
dirContents <- handleIOExceptT IOError (listDirectory drepDir)
let subDirs = filter dirWellFormed dirContents
mapM loadFromDir ((drepDir </>) <$> subDirs)
where
asSigningKeyFile :: FilePath -> SigningKeyFile In
asSigningKeyFile = File

loadFromDir d = hoistEither =<< handleIOExceptT IOError
(readDRepKeyFile $ asSigningKeyFile (d </> "drep.skey"))

dirWellFormed = \case
'd':'r':'e':'p' : nr@(_:_) -> all isDigit nr
_ -> False

drepDir = genesisDir </> "drep-keys"
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Cardano.Node.Configuration.POM
import Cardano.Node.Handlers.Shutdown (ShutdownConfig (..))
import Cardano.Node.Protocol.Cardano
import Cardano.Node.Protocol.Types (SomeConsensusProtocol (..))
import Cardano.Node.Types (ConfigYamlFilePath (..), GenesisFile,
import Cardano.Node.Types (ConfigYamlFilePath (..), GenesisFile (..),
NodeProtocolConfiguration (..), NodeShelleyProtocolConfiguration (..),
ProtocolFilepaths (..))
import Cardano.TxGenerator.Types
Expand All @@ -25,6 +25,7 @@ import Control.Applicative (Const (Const), getConst)
import Control.Monad.Trans.Except (runExceptT)
import Data.Bifunctor (first)
import Data.Monoid
import System.FilePath (takeDirectory)


-- | extract genesis from a Cardano protocol
Expand All @@ -45,6 +46,9 @@ getGenesisPath nodeConfig =
NodeProtocolConfigurationCardano _ shelleyConfig _ _ _ ->
Just $ npcShelleyGenesisFile shelleyConfig

getGenesisDirectory :: NodeConfiguration -> Maybe FilePath
getGenesisDirectory nodeConfig = takeDirectory . unGenesisFile <$> getGenesisPath nodeConfig

mkConsensusProtocol :: NodeConfiguration -> IO (Either TxGenError SomeConsensusProtocol)
mkConsensusProtocol nodeConfig =
case ncProtocolConfig nodeConfig of
Expand Down
14 changes: 9 additions & 5 deletions bench/tx-generator/src/Cardano/TxGenerator/Setup/SigningKey.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@
module Cardano.TxGenerator.Setup.SigningKey
( parseSigningKeyTE
, parseSigningKeyBase16
, readDRepKeyFile
, readSigningKeyFile
, PaymentKey
, SigningKey
)
where

import Data.Bifunctor (first)
import qualified Data.ByteString as BS (ByteString)
import Data.ByteString.Base16 as Base16 (decode)

import Cardano.Api
import Cardano.CLI.Types.Common (SigningKeyFile)

import Cardano.CLI.Types.Common (SigningKeyFile)
import Cardano.TxGenerator.Types (TxGenError (..))

import Data.Bifunctor (first)
import qualified Data.ByteString as BS (ByteString)
import Data.ByteString.Base16 as Base16 (decode)


parseSigningKeyTE :: TextEnvelope -> Either TxGenError (SigningKey PaymentKey)
parseSigningKeyTE
Expand All @@ -41,6 +42,9 @@ parseSigningKeyBase16 k
readSigningKeyFile :: SigningKeyFile In -> IO (Either TxGenError (SigningKey PaymentKey))
readSigningKeyFile f = first ApiError <$> readFileTextEnvelopeAnyOf acceptedTypes f

readDRepKeyFile :: SigningKeyFile In -> IO (Either TxGenError (SigningKey DRepKey))
readDRepKeyFile f = first ApiError <$> readKeyFileTextEnvelope (AsSigningKey AsDRepKey) f

acceptedTypes :: [FromSomeType HasTextEnvelope (SigningKey PaymentKey)]
acceptedTypes =
[ FromSomeType (AsSigningKey AsGenesisUTxOKey) castSigningKey
Expand Down
5 changes: 4 additions & 1 deletion bench/tx-generator/src/Cardano/TxGenerator/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Cardano.Ledger.Crypto (StandardCrypto)
import qualified Cardano.Ledger.Shelley.API as Ledger (ShelleyGenesis)
import Cardano.TxGenerator.Fund (Fund)

import Control.Exception (IOException)
import GHC.Generics (Generic)
import GHC.Natural
import Prettyprinter
Expand Down Expand Up @@ -129,12 +130,14 @@ data TxGenError where
ProtocolError :: Cardano.Api.Error e => !e -> TxGenError
PlutusError :: Show e => !e -> TxGenError
TxGenError :: !String -> TxGenError
IOError :: !IOException -> TxGenError

instance Show TxGenError where
show (ApiError e) = docToString $ "ApiError " <> parens (prettyError e)
show (ProtocolError e) = docToString $ "ProtocolError " <> parens (prettyError e)
show (PlutusError e) = docToString $ "ProtocolError " <> parens (pshow e)
show (PlutusError e) = docToString $ "PlutusError " <> parens (pshow e)
show (TxGenError e) = docToString $ "ApiError " <> parens (pshow e)
show (IOError e) = docToString $ "IOError " <> parens (pshow e)

instance Semigroup TxGenError where
TxGenError a <> TxGenError b = TxGenError (a <> b)
Expand Down
21 changes: 15 additions & 6 deletions bench/tx-generator/test/ApiTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ main
ncFile <- hoistMaybe (TxGenError "nodeConfigFile not specified") $
getNodeConfigFile nixService
nc :: NodeConfiguration <-
hoistEither =<< handleIOExceptT (TxGenError . show) (mkNodeConfig ncFile)
hoistEither =<< handleIOExceptT IOError (mkNodeConfig ncFile)

GenesisFile sgFile <- hoistMaybe (TxGenError "npcShelleyGenesisFile not specified") $
getGenesisPath nc
Expand All @@ -95,20 +95,22 @@ main
genesisValidate genesis

sigKey :: SigningKey PaymentKey <-
hoistEither =<< handleIOExceptT (TxGenError . show) (readSigningKeyFile $ _nix_sigKey nixService)
hoistEither =<< handleIOExceptT IOError (readSigningKeyFile $ _nix_sigKey nixService)

pure (nixService, nc, genesis, sigKey)

case setup of
Left err -> die (show err)
Right (nixService, _nc, genesis, sigKey) -> do
Right (nixService, nc, genesis, sigKey) -> do
putStrLn $ "* Did I manage to extract a genesis fund?\n--> " ++ checkFund nixService genesis sigKey
putStrLn "* Can I pre-execute a plutus script?"
let plutus = _nix_plutus nixService
case plutusType <$> plutus of
Just BenchCustomCall -> checkPlutusBuiltin protoParamPath
Just{} -> checkPlutusLoop protoParamPath plutus
Nothing -> putStrLn "--> no Plutus configuration found - skipping"
Just{} -> putStrLn "* Can I pre-execute the plutus script?" >> checkPlutusLoop protoParamPath plutus
Nothing
| _nix_drep_voting nixService == Just True
-> checkLoadDReps nc
| otherwise -> putStrLn "--> no runnable test configuration found - skipping"
exitSuccess

-- The type annotations within patterns or expressions that would be
Expand Down Expand Up @@ -268,6 +270,13 @@ checkPlutusLoop _ _
= putStrLn "--> No plutus script defined."


checkLoadDReps :: NodeConfiguration -> IO ()
checkLoadDReps nc = case getGenesisDirectory nc of
Nothing -> putStrLn "--> getGenesisDirectory: no directory could be retrieved from NodeConfiguration"
Just d -> genesisLoadDRepKeys (d </> "cache-entry") >>= \case
Right keys -> putStrLn $ "--> successfully loaded " ++ show (length keys) ++ " DRep SigningKeys"
Left err -> error $ "--> error loading DRep keys: " ++ show err

--
-- helpers
--
Expand Down
5 changes: 4 additions & 1 deletion bench/tx-generator/tx-generator.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ library
, cborg >= 0.2.2 && < 0.3
, containers
, constraints-extras
, directory
, dlist
, extra
, filepath
, formatting
, generic-monoid
, ghc-prim
Expand Down Expand Up @@ -159,7 +161,8 @@ library
, yaml

default-language: Haskell2010
default-extensions: OverloadedStrings
default-extensions: LambdaCase
OverloadedStrings

executable tx-generator
import: project-config
Expand Down

0 comments on commit 75f7a71

Please sign in to comment.