diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs b/bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs index e6c3f146927..6d45da31d2e 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs @@ -49,8 +49,8 @@ compileToScript = do genesisWallet <- newWallet "genesis_wallet" importGenesisFunds genesisWallet splitWallet <- splittingPhase genesisWallet - addCollaterals genesisWallet splitWallet - benchmarkingPhase splitWallet + collateralWallet <- addCollaterals genesisWallet + benchmarkingPhase splitWallet collateralWallet initConstants :: Compiler () initConstants = do @@ -72,16 +72,18 @@ importGenesisFunds wallet = do emit $ ImportGenesisFund era wallet LocalSocket (KeyName "pass-partout") (KeyName "pass-partout") delay -addCollaterals :: SrcWallet -> DstWallet -> Compiler () -addCollaterals src dest = do +addCollaterals :: SrcWallet -> Compiler (Maybe WalletName) +addCollaterals src = do era <- askNixOption _nix_era isAnyPlutusMode >>= \case - False -> return () - True -> do + False -> return Nothing + True -> do tx_fee <- askNixOption _nix_tx_fee safeCollateral <- _safeCollateral <$> evilFeeMagic + collateralWallet <- newWallet "collaeral_wallet" emit $ CreateChange era src src LocalSocket (PayToAddr $ KeyName "pass-partout") (safeCollateral + tx_fee) 1 - emit $ CreateChange era src dest LocalSocket (PayToCollateral $ KeyName "pass-partout") safeCollateral 1 + emit $ CreateChange era src collateralWallet LocalSocket (PayToCollateral $ KeyName "pass-partout") safeCollateral 1 + return $ Just collateralWallet splittingPhase :: SrcWallet -> Compiler DstWallet splittingPhase srcWallet = do @@ -104,10 +106,17 @@ splittingPhase srcWallet = do createChangePlutus :: AnyCardanoEra -> SplitStep -> Compiler DstWallet createChangePlutus era (src, dst, value, count) = do autoMode <- isPlutusAutoMode - plutusTarget <- if autoMode - then PayToScript <$> askNixOption _nix_plutusLoopScript <*> pure (ScriptDataNumber 0) - else PayToScript <$> askNixOption _nix_plutusScript <*> (ScriptDataNumber <$> askNixOption _nix_plutusData) - emit $ CreateChange era src dst LocalSocket plutusTarget value count + scriptSpec <- if autoMode + then ScriptSpec <$> askNixOption _nix_plutusLoopScript <*> pure AutoScript + else do + executionUnits <- ExecutionUnits <$> askNixOption _nix_executionMemory <*> askNixOption _nix_executionSteps + debugMode <- askNixOption _nix_debugMode + budget <- (if debugMode then CheckScriptBudget else StaticScriptBudget) + <$> (ScriptDataNumber <$> askNixOption _nix_plutusData) + <*> (ScriptDataNumber <$> askNixOption _nix_plutusRedeemer) + <*> pure executionUnits + ScriptSpec <$> askNixOption _nix_plutusScript <*> pure budget + emit $ CreateChange era src dst LocalSocket (PayToScript scriptSpec) value count delay return dst @@ -138,29 +147,15 @@ unfoldSplitSequence fee value count -- todo: this must be in sync with Scipt/Core.hs maxOutputs = 30 -benchmarkingPhase :: WalletName -> Compiler () -benchmarkingPhase wallet = do +benchmarkingPhase :: WalletName -> Maybe WalletName -> Compiler () +benchmarkingPhase wallet collateralWallet = do debugMode <- askNixOption _nix_debugMode - plutusMode <- askNixOption _nix_plutusMode - plutusAutoMode <- askNixOption _nix_plutusAutoMode targetNodes <- askNixOption _nix_targetNodes extraArgs <- evilValueMagic tps <- askNixOption _nix_tps era <- askNixOption _nix_era let target = if debugMode then LocalSocket else NodeToNode targetNodes - spendMode <- case (plutusAutoMode, plutusMode) of - ( True, _ ) -> SpendAutoScript <$> askNixOption _nix_plutusLoopScript - (False, True ) -> do - executionUnits <- ExecutionUnits <$> askNixOption _nix_executionMemory <*> askNixOption _nix_executionSteps - scriptBudget <- if debugMode - then return $ CheckScriptBudget executionUnits - else return $ StaticScriptBudget executionUnits - SpendScript <$> askNixOption _nix_plutusScript - <*> pure scriptBudget - <*> (ScriptDataNumber <$> askNixOption _nix_plutusData) - <*> (ScriptDataNumber <$> askNixOption _nix_plutusRedeemer) - (False,False) -> return SpendOutput - emit $ RunBenchmark era wallet target spendMode (ThreadName "tx-submit-benchmark") extraArgs tps + emit $ RunBenchmark era wallet target (ThreadName "tx-submit-benchmark") extraArgs collateralWallet tps unless debugMode $ do emit $ WaitBenchmark $ ThreadName "tx-submit-benchmark" diff --git a/bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs b/bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs index 515b47d97cc..8db545c1464 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs @@ -5,6 +5,8 @@ {-# Language GeneralizedNewtypeDeriving #-} {-# Language MultiParamTypeClasses #-} {-# Language RankNTypes #-} +{-# Language TypeApplications #-} +{-# Language ScopedTypeVariables #-} module Cardano.Benchmarking.FundSet where @@ -21,6 +23,7 @@ import Cardano.Api as Api data FundInEra era = FundInEra { _fundTxIn :: !TxIn + , _fundWitness :: Witness WitCtxTxIn era , _fundVal :: !(TxOutValue era) , _fundSigningKey :: !(Maybe (SigningKey PaymentKey)) , _fundVariant :: !Variant @@ -30,7 +33,7 @@ data FundInEra era = FundInEra { data Variant = PlainOldFund -- maybe better use the script itself instead of the filePath - | PlutusScriptFund !FilePath !ScriptData + | PlutusScriptFund -- A collateralFund is just a regular (PlainOldFund) on the chain, -- but tagged in the wallet so that it is not selected for spending. | CollateralFund @@ -66,6 +69,22 @@ getFundLovelace (Fund (InAnyCardanoEra _ a)) = case _fundVal a of TxOutAdaOnly _era l -> l TxOutValue _era v -> selectLovelace v + +-- This effectively rules out era-transitions for transactions ! +-- This is not what we want !! +getFundWitness :: forall era. IsShelleyBasedEra era => Fund -> Witness WitCtxTxIn era +getFundWitness fund = case (cardanoEra @ era, fund) of + (ByronEra , Fund (InAnyCardanoEra ByronEra a)) -> _fundWitness a + (ShelleyEra , Fund (InAnyCardanoEra ShelleyEra a)) -> _fundWitness a + (AllegraEra , Fund (InAnyCardanoEra AllegraEra a)) -> _fundWitness a + (MaryEra , Fund (InAnyCardanoEra MaryEra a)) -> _fundWitness a + (AlonzoEra , Fund (InAnyCardanoEra AlonzoEra a)) -> _fundWitness a + (BabbageEra , Fund (InAnyCardanoEra BabbageEra a)) -> _fundWitness a +-- This effectively rules out era-transitions for transactions ! +-- This is not what we want !! +-- It should be possible to cast KeyWitnesses from one era to an other ! + (_ , _) -> error "getFundWitness: era mismatch" + data IsConfirmed = IsConfirmed | IsNotConfirmed deriving (Show, Eq, Ord) @@ -227,7 +246,7 @@ selectInputs allowRecycle count minTotalValue variant targetNode fs selectToBuffer :: Int -> Lovelace - -> Variant + -> Maybe Variant -> FundSet -> Either String [Fund] selectToBuffer count minValue variant fs @@ -239,8 +258,9 @@ selectToBuffer count minValue variant fs ] else Right coins where - coins = take count $ toAscList ( Proxy :: Proxy Lovelace) (fs @=variant @= IsConfirmed @>= minValue) - + coins = case variant of + Just v -> take count $ toAscList ( Proxy :: Proxy Lovelace) (fs @=v @= IsConfirmed @>= minValue) + Nothing -> take count $ toAscList ( Proxy :: Proxy Lovelace) (fs @= IsConfirmed @>= minValue) -- Todo: check sufficient funds and minimumValuePerUtxo inputsToOutputsWithFee :: Lovelace -> Int -> [Lovelace] -> [Lovelace] inputsToOutputsWithFee fee count inputs = map (quantityToLovelace . Quantity) outputs diff --git a/bench/tx-generator/src/Cardano/Benchmarking/ListBufferedSelector.hs b/bench/tx-generator/src/Cardano/Benchmarking/ListBufferedSelector.hs index 2529e95c833..6179d06d559 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/ListBufferedSelector.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/ListBufferedSelector.hs @@ -16,7 +16,7 @@ mkBufferedSource :: WalletRef -> Int -> Lovelace - -> Variant + -> Maybe Variant -> Int -> IO (Either String FundSource) mkBufferedSource walletRef count minValue variant munch diff --git a/bench/tx-generator/src/Cardano/Benchmarking/PlutusExample.hs b/bench/tx-generator/src/Cardano/Benchmarking/PlutusExample.hs index 11eedfc2d69..2a1570d63a0 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/PlutusExample.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/PlutusExample.hs @@ -12,47 +12,13 @@ import qualified Data.ByteString.Char8 as BSC import Cardano.CLI.Shelley.Script (readFileScriptInAnyLang) import Cardano.Api -import Cardano.Api.Shelley ( ProtocolParameters(..), PlutusScript(..), ReferenceScript(..) +import Cardano.Api.Shelley ( ProtocolParameters(..), PlutusScript(..) , fromAlonzoExUnits, protocolParamCostModels, toPlutusData) import Cardano.Ledger.Alonzo.TxInfo (exBudgetToExUnits) -import Cardano.Benchmarking.FundSet -import Cardano.Benchmarking.Wallet import qualified Plutus.V1.Ledger.Api as Plutus import Plutus.V1.Ledger.Contexts (ScriptContext(..), ScriptPurpose(..), TxInfo(..), TxOutRef(..)) -mkUtxoScript :: - NetworkId - -> (FilePath, Script PlutusScriptV1, ScriptData) - -> Validity - -> ToUTxO AlonzoEra -mkUtxoScript networkId (scriptFile, script, txOutDatum) validity values - = ( map mkTxOut values - , newFunds - ) - where - mkTxOut v = TxOut - plutusScriptAddr - (lovelaceToTxOutValue v) - (TxOutDatumHash ScriptDataInAlonzoEra $ hashScriptData txOutDatum) - ReferenceScriptNone - - plutusScriptAddr = makeShelleyAddressInEra - networkId - (PaymentCredentialByScript $ hashScript script) - NoStakeAddress - - newFunds txId = zipWith (mkNewFund txId) [TxIx 0 ..] values - - mkNewFund :: TxId -> TxIx -> Lovelace -> Fund - mkNewFund txId txIx val = Fund $ InAnyCardanoEra AlonzoEra $ FundInEra { - _fundTxIn = TxIn txId txIx - , _fundVal = lovelaceToTxOutValue val - , _fundSigningKey = Nothing - , _fundValidity = validity - , _fundVariant = PlutusScriptFund scriptFile txOutDatum - } - readScript :: FilePath -> IO (Script PlutusScriptV1) readScript fp = do res <- runExceptT $ readFileScriptInAnyLang fp diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Action.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Action.hs index 3b88f09606d..8078776d4e9 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Action.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Action.hs @@ -22,7 +22,8 @@ action a = case a of Delay t -> delay t ImportGenesisFund era wallet submitMode genesisKey fundKey -> importGenesisFund era wallet submitMode genesisKey fundKey CreateChange era sourceWallet dstWallet payMode submitMode value count -> createChange era sourceWallet dstWallet payMode submitMode value count - RunBenchmark era sourceWallet submitMode spendMode thread auxArgs tps -> runBenchmark era sourceWallet submitMode spendMode thread auxArgs tps + RunBenchmark era sourceWallet submitMode thread auxArgs collateralWallet tps + -> runBenchmark era sourceWallet submitMode thread auxArgs collateralWallet tps WaitBenchmark thread -> waitBenchmark thread CancelBenchmark thread -> cancelBenchmark thread WaitForEra era -> waitForEra era diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs index 685bd756eb9..9413e0a0f48 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs @@ -73,16 +73,16 @@ instance ToJSON PayMode where instance FromJSON PayMode where parseJSON = genericParseJSON jsonOptionsUnTaggedSum -instance ToJSON SpendMode where +instance ToJSON ScriptBudget where toJSON = genericToJSON jsonOptionsUnTaggedSum toEncoding = genericToEncoding jsonOptionsUnTaggedSum -instance FromJSON SpendMode where +instance FromJSON ScriptBudget where parseJSON = genericParseJSON jsonOptionsUnTaggedSum -instance ToJSON ScriptBudget where +instance ToJSON ScriptSpec where toJSON = genericToJSON jsonOptionsUnTaggedSum toEncoding = genericToEncoding jsonOptionsUnTaggedSum -instance FromJSON ScriptBudget where +instance FromJSON ScriptSpec where parseJSON = genericParseJSON jsonOptionsUnTaggedSum instance ToJSON (DSum Tag Identity) where diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs index 4b50548ee5e..0d43d00a371 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs @@ -32,7 +32,9 @@ import Ouroboros.Network.Protocol.LocalTxSubmission.Type (SubmitResult import Cardano.Benchmarking.FundSet (FundInEra (..), Validity (..), Variant (..), liftAnyEra) + import qualified Cardano.Benchmarking.FundSet as FundSet +import Cardano.Benchmarking.FundSet as FundSet (getFundTxIn) import Cardano.Benchmarking.GeneratorTx as GeneratorTx (AsyncBenchmarkControl, TxGenError) import qualified Cardano.Benchmarking.GeneratorTx as GeneratorTx (readSigningKey, secureGenesisFund, waitBenchmark, walletBenchmark) @@ -42,7 +44,6 @@ import Cardano.Benchmarking.GeneratorTx.NodeToNode (ConnectClient, import Cardano.Benchmarking.GeneratorTx.SizedMetadata (mkMetadata) import Cardano.Benchmarking.GeneratorTx.Tx as Core (keyAddress, mkFee, txInModeCardano) -import Cardano.Benchmarking.FundSet as FundSet (getFundTxIn) import Cardano.Benchmarking.ListBufferedSelector import Cardano.Benchmarking.OuroborosImports as Core (LocalSubmitTx, SigningKeyFile, makeLocalConnectInfo, protocolToCodecConfig) @@ -116,6 +117,7 @@ addFundToWallet wallet txIn outVal skey = do where mkFund = liftAnyEra $ \value -> FundInEra { _fundTxIn = txIn + , _fundWitness = KeyWitness KeyWitnessForSpending , _fundVal = value , _fundSigningKey = Just skey , _fundValidity = Confirmed @@ -240,23 +242,28 @@ makeMetadata = do Right m -> return m Left err -> throwE $ MetadataError err -runBenchmark :: AnyCardanoEra -> WalletName -> SubmitMode -> SpendMode -> ThreadName -> RunBenchmarkAux -> TPSRate -> ActionM () -runBenchmark era sourceWallet submitMode spendMode threadName extraArgs tps - = case spendMode of - SpendOutput -> withEra era $ runBenchmarkInEra sourceWallet submitMode threadName extraArgs tps - SpendScript scriptFile scriptBudget scriptData scriptRedeemer - -> withEra era $ runPlutusBenchmark sourceWallet submitMode scriptFile scriptBudget scriptData scriptRedeemer threadName extraArgs tps - SpendAutoScript scriptFile -> withEra era $ spendAutoScript sourceWallet submitMode scriptFile threadName extraArgs tps +runBenchmark :: + AnyCardanoEra + -> WalletName + -> SubmitMode + -> ThreadName + -> RunBenchmarkAux + -> Maybe WalletName + -> TPSRate + -> ActionM () +runBenchmark era sourceWallet submitMode threadName extraArgs collateralWallet tps + = withEra era $ runBenchmarkInEra sourceWallet submitMode threadName extraArgs collateralWallet tps runBenchmarkInEra :: forall era. IsShelleyBasedEra era => WalletName -> SubmitMode -> ThreadName -> RunBenchmarkAux + -> Maybe WalletName -> TPSRate -> AsType era -> ActionM () -runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape tps era = do +runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape collateralWallet tps era = do tracers <- get BenchTracers networkId <- getUser TNetworkId fundKey <- getName $ KeyName "pass-partout" -- should be walletkey @@ -268,21 +275,23 @@ runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape tps era fundSource <- liftIO (mkBufferedSource walletRefSrc (auxInputs shape) (auxMinValuePerUTxO shape) - PlainOldFund + Nothing (auxInputsPerTx shape) ) >>= \case Right a -> return a Left err -> throwE $ WalletError err + collaterals <- selectCollateralFunds collateralWallet + let inToOut :: [Lovelace] -> [Lovelace] inToOut = FundSet.inputsToOutputsWithFee (auxFee shape) (auxOutputsPerTx shape) - txGenerator = genTx protocolParameters (TxInsCollateralNone, []) (mkFee (auxFee shape)) metadata (KeyWitness KeyWitnessForSpending) - - toUTxO :: FundSet.Target -> FundSet.SeqNumber -> ToUTxO era - toUTxO target seqNumber = Wallet.mkUTxO networkId fundKey (InFlight target seqNumber) + txGenerator = genTx protocolParameters collaterals (mkFee (auxFee shape)) metadata + toUTxO :: FundSet.Target -> FundSet.SeqNumber -> [ToUTxO era] + toUTxO target seqNumber = repeat $ Wallet.mkUTxOVariant PlainOldFund networkId fundKey (InFlight target seqNumber) + fundToStore = mkWalletFundStore walletRefDst walletScript :: FundSet.Target -> WalletScript era @@ -301,128 +310,19 @@ runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape tps era Right ctl -> setName (ThreadName threadName) ctl _otherwise -> runWalletScriptInMode submitMode $ walletScript $ FundSet.Target "alternate-submit-mode" -runPlutusBenchmark :: forall era. IsShelleyBasedEra era - => WalletName - -> SubmitMode - -> FilePath - -> ScriptBudget - -> ScriptData - -> ScriptRedeemer - -> ThreadName - -> RunBenchmarkAux - -> TPSRate - -> AsType era - -> ActionM () -runPlutusBenchmark sourceWallet submitMode scriptFile scriptBudget scriptData scriptRedeemer (ThreadName threadName) extraArgs tps era = do - tracers <- get BenchTracers - networkId <- getUser TNetworkId - protocolParameters <- getProtocolParameters - executionUnitPrices <- case protocolParamPrices protocolParameters of - Just x -> return x - Nothing -> throwE $ WalletError "unexpected protocolParamPrices == Nothing in runPlutusBenchmark" - walletRefSrc <- getName sourceWallet - let - -- runBenchmark reads and write from the single sourceWallet. - walletRefDst = walletRefSrc - walletRefCollateral = walletRefSrc - fundKey <- getName $ KeyName "pass-partout" - script <- liftIO $ PlutusExample.readScript scriptFile - -- This does not remove the collateral from the wallet, i.e. same collateral is uses for everything. - -- This is fine unless a script ever fails. - collateralFunds <- liftIO ( askWalletRef walletRefCollateral (FundSet.selectCollateral . walletFunds)) >>= \case +selectCollateralFunds :: forall era. IsShelleyBasedEra era + => Maybe WalletName + -> ActionM (TxInsCollateral era, [FundSet.Fund]) +selectCollateralFunds Nothing = return (TxInsCollateralNone, []) +selectCollateralFunds (Just walletName) = do + cw <- getName walletName + collateralFunds <- liftIO ( askWalletRef cw (FundSet.selectCollateral . walletFunds)) >>= \case Right c -> return c Left err -> throwE $ WalletError err - baseFee <- getUser TFee - metadata <- makeMetadata - - let costsPreRun = preExecuteScript protocolParameters script scriptData scriptRedeemer - executionUnits <- case (scriptBudget, costsPreRun) of - (_, Left err) -> throwE $ WalletError ("Cannot pre-execute Plutus script." ++ err) - (StaticScriptBudget exUnits, _) -> return exUnits - (PreExecuteScript, Right preRun) -> return preRun - (CheckScriptBudget want, Right preRun) - -> if want == preRun then return preRun - else throwE $ WalletError $ concat [ - " Stated execution Units do not match result of pre execution. " - , " Stated value : ", show want - , " PreExecution result : ", show preRun - ] - - let msg = mconcat [ "Plutus Benchmark :" - , " Script: ", scriptFile - , ", Datum: ", show scriptData - , ", Redeemer: ", show scriptRedeemer - , ", StatedBudget: ", show executionUnits - ] - traceDebug msg - - let - -- TODO -- Cardano.Ledger.Alonzo.Scripts.txscriptfee :: Prices -> ExUnits -> Coin - scriptFee = quantityToLovelace $ Quantity $ ceiling f - where - f :: Rational - f = (executionSteps e `times` priceExecutionSteps p) + (executionMemory e `times` priceExecutionMemory p) - e = executionUnits - p = executionUnitPrices - times w c = fromIntegral w % 1 * c - - totalFee = baseFee + fromIntegral (auxInputsPerTx extraArgs) * scriptFee - (Quantity minValue) = lovelaceToQuantity $ fromIntegral (auxOutputsPerTx extraArgs) * auxMinValuePerUTxO extraArgs + totalFee - -- this is not totally correct: - -- beware of rounding errors ! - minValuePerInput = quantityToLovelace $ fromIntegral (if m==0 then d else d+1) - where - (d, m) = minValue `divMod` fromIntegral (auxInputsPerTx extraArgs) - - fundSource <- liftIO (mkBufferedSource walletRefSrc - (auxInputs extraArgs) - minValuePerInput - (PlutusScriptFund scriptFile scriptData) - (auxInputsPerTx extraArgs)) >>= \case - Right a -> return a - Left err -> throwE $ WalletError err - - let - inToOut :: [Lovelace] -> [Lovelace] - inToOut = FundSet.inputsToOutputsWithFee totalFee (auxOutputsPerTx extraArgs) --- inToOut = FundSet.inputsToOutputsWithFee totalFee 1 - - PlutusScript PlutusScriptV1 script' = script - scriptWitness :: ScriptWitness WitCtxTxIn era - scriptWitness = case scriptLanguageSupportedInEra (cardanoEra @ era) (PlutusScriptLanguage PlutusScriptV1) of - Nothing -> error $ "runPlutusBenchmark: Plutus V1 scriptlanguage not supported : in era" ++ show (cardanoEra @ era) - Just scriptLang -> PlutusScriptWitness - scriptLang - PlutusScriptV1 - (PScript script') - (ScriptDatumForTxIn scriptData) - scriptRedeemer - executionUnits - - collateral = case collateralSupportedInEra (cardanoEra @ era) of - Nothing -> error $ "runPlutusBenchmark: collateral: era not supported :" ++ show (cardanoEra @ era) - Just p -> (TxInsCollateral p $ map getFundTxIn collateralFunds, collateralFunds) - - txGenerator = genTx protocolParameters collateral (mkFee totalFee) metadata (ScriptWitness ScriptWitnessForSpending scriptWitness) - - fundToStore = mkWalletFundStore walletRefDst - - toUTxO :: FundSet.Target -> FundSet.SeqNumber -> ToUTxO era - toUTxO target seqNumber = Wallet.mkUTxO networkId fundKey (InFlight target seqNumber) - - walletScript :: FundSet.Target -> WalletScript era - walletScript = benchmarkWalletScript walletRefSrc txGenerator (NumberOfTxs $ auxTxCount extraArgs) (const fundSource) inToOut toUTxO fundToStore - - case submitMode of - NodeToNode targetNodes -> do - connectClient <- getConnectClient - ret <- liftIO $ runExceptT $ GeneratorTx.walletBenchmark (btTxSubmit_ tracers) (btN2N_ tracers) connectClient - threadName targetNodes tps LogErrors era (NumberOfTxs $ auxTxCount extraArgs) walletScript - case ret of - Left err -> liftTxGenError err - Right ctl -> setName (ThreadName threadName) ctl - _otherwise -> runWalletScriptInMode submitMode $ walletScript $ FundSet.Target "alternate-submit-mode" - + case collateralSupportedInEra (cardanoEra @ era) of + Nothing -> throwE $ WalletError $ "selectCollateralFunds: collateral: era not supported :" ++ show (cardanoEra @ era) + Just p -> return (TxInsCollateral p $ map getFundTxIn collateralFunds, collateralFunds) + dumpToFile :: FilePath -> TxInMode CardanoMode -> ActionM () dumpToFile filePath tx = liftIO $ dumpToFileIO filePath tx @@ -464,74 +364,50 @@ initWallet :: WalletName -> ActionM () initWallet name = liftIO Wallet.initWallet >>= setName name createChange :: AnyCardanoEra -> WalletName -> WalletName -> SubmitMode -> PayMode -> Lovelace -> Int -> ActionM () -createChange era sourceWallet dstWallet submitMode payMode value count = case payMode of - PayToAddr keyName -> withEra era $ createChangeInEra sourceWallet dstWallet submitMode PlainOldFund keyName value count - -- Problem here: PayToCollateral will create an output marked as collateral - -- and also return any change to a collateral, which makes the returned change unusable. - PayToCollateral keyName -> withEra era $ createChangeInEra sourceWallet dstWallet submitMode CollateralFund keyName value count - PayToScript scriptFile scriptData -> createChangeScriptFunds sourceWallet dstWallet submitMode scriptFile scriptData value count - -createChangeScriptFunds :: WalletName -> WalletName -> SubmitMode -> FilePath -> ScriptData -> Lovelace -> Int -> ActionM () -createChangeScriptFunds sourceWallet dstWallet submitMode scriptFile scriptData value count = do - walletRef <- getName dstWallet - networkId <- getUser TNetworkId - protocolParameters <- getProtocolParameters - _fundKey <- getName $ KeyName "pass-partout" - fee <- getUser TFee - script <- liftIO $ PlutusExample.readScript scriptFile --TODO: this should throw a file-not-found-error ! - let - createCoins fundSource coins = do - let --- selector :: FundSet.FundSource --- selector = mkWalletFundSource walletRef $ FundSet.selectMinValue $ sum coins + fee - inOut :: [Lovelace] -> [Lovelace] - inOut = Wallet.includeChange fee coins - toUTxO = PlutusExample.mkUtxoScript networkId (scriptFile, script, scriptData) Confirmed - fundToStore = mkWalletFundStore walletRef - - tx <- liftIO $ sourceToStoreTransaction - (genTx protocolParameters (TxInsCollateralNone, []) - (mkFee fee) TxMetadataNone (KeyWitness KeyWitnessForSpending)) - fundSource inOut toUTxO fundToStore - return $ fmap txInModeCardano tx - addressMsg = Text.unpack $ serialiseAddress $ makeShelleyAddress networkId (PaymentCredentialByScript $ hashScript script) NoStakeAddress - createChangeGeneric sourceWallet submitMode createCoins addressMsg value count +createChange era sourceWallet dstWallet submitMode payMode value count + = withEra era $ createChangeInEra sourceWallet dstWallet submitMode payMode value count createChangeInEra :: forall era. IsShelleyBasedEra era => WalletName -> WalletName -> SubmitMode - -> Variant - -> KeyName + -> PayMode -> Lovelace -> Int -> AsType era -> ActionM () -createChangeInEra sourceWallet dstWallet submitMode variant keyName value count _proxy = do - networkId <- getUser TNetworkId +createChangeInEra sourceWallet dstWallet submitMode payMode value count _era = do walletRef <- getName dstWallet fee <- getUser TFee protocolParameters <- getProtocolParameters - fundKey <- getName keyName + (toUTxO, addressMsg) <- interpretPayMode payMode let createCoins :: FundSet.FundSource -> [Lovelace] -> ActionM (Either String (TxInMode CardanoMode)) createCoins fundSource coins = do - let --- selector :: FundSet.FundSource --- selector = mkWalletFundSource walletRef $ FundSet.selectMinValue $ sum coins + fee - inOut :: [Lovelace] -> [Lovelace] - inOut = Wallet.includeChange fee coins - toUTxO = Wallet.mkUTxOVariant variant networkId fundKey Confirmed - fundToStore = mkWalletFundStore walletRef - (tx :: Either String (Tx era)) <- liftIO $ sourceToStoreTransaction (genTx protocolParameters (TxInsCollateralNone, []) - (mkFee fee) TxMetadataNone (KeyWitness KeyWitnessForSpending)) - fundSource inOut toUTxO fundToStore + (mkFee fee) TxMetadataNone ) + fundSource (Wallet.includeChange fee coins) (repeat toUTxO) (mkWalletFundStore walletRef) return $ fmap txInModeCardano tx - addressMsg = Text.unpack $ serialiseAddress $ keyAddress @ era networkId fundKey createChangeGeneric sourceWallet submitMode createCoins addressMsg value count +interpretPayMode :: forall era. IsShelleyBasedEra era => PayMode -> ActionM (ToUTxO era, String) +interpretPayMode payMode = do + networkId <- getUser TNetworkId + case payMode of + PayToAddr keyName -> do + fundKey <- getName keyName + return ( Wallet.mkUTxOVariant PlainOldFund networkId fundKey Confirmed + , Text.unpack $ serialiseAddress $ keyAddress @ era networkId fundKey) + PayToCollateral keyName -> do + fundKey <- getName keyName + return ( Wallet.mkUTxOVariant CollateralFund networkId fundKey Confirmed + , Text.unpack $ serialiseAddress $ keyAddress @ era networkId fundKey) + PayToScript scriptSpec -> do + (witness, script, scriptData, _scriptFee) <- makePlutusContext scriptSpec + return ( mkUTxOScript networkId (script, scriptData) witness Confirmed + , Text.unpack $ serialiseAddress $ makeShelleyAddress networkId (PaymentCredentialByScript $ hashScript script) NoStakeAddress ) + createChangeGeneric :: WalletName -> SubmitMode @@ -555,7 +431,7 @@ createChangeGeneric sourceWallet submitMode createCoins addressMsg value count = , " address: ", addressMsg ] traceDebug msg - fundSource <- liftIO (mkBufferedSource walletRef txCount txValue PlainOldFund 1) >>= \case + fundSource <- liftIO (mkBufferedSource walletRef txCount txValue (Just PlainOldFund) 1) >>= \case Right a -> return a Left err -> throwE $ WalletError err @@ -581,30 +457,23 @@ It is intended to be used with the the loop script from cardano-node/plutus-exam loopScriptFile is the FilePath to the Plutus script that implements the delay loop. (for example in /nix/store/). spendAutoScript relies on a particular calling convention of the loop script. -} -spendAutoScript :: forall era. IsShelleyBasedEra era - => WalletName - -> SubmitMode - -> FilePath - -> ThreadName - -> RunBenchmarkAux - -> TPSRate - -> AsType era - -> ActionM () -spendAutoScript sourceWallet submitMode loopScriptFile threadName extraArgs tps era = do - protocolParameters <- getProtocolParameters + +spendAutoScript :: + ProtocolParameters + -> Script PlutusScriptV1 + -> ActionM (ScriptData, ScriptRedeemer) +spendAutoScript protocolParameters script = do perTxBudget <- case protocolParamMaxTxExUnits protocolParameters of Nothing -> throwE $ ApiError "Cannot determine protocolParamMaxTxExUnits" Just b -> return b traceDebug $ "Plutus auto mode : Available budget per TX: " ++ show perTxBudget let - numInputs = fromIntegral $ auxInputsPerTx extraArgs budget = ExecutionUnits - (executionSteps perTxBudget `div` numInputs) - (executionMemory perTxBudget `div` numInputs) + (executionSteps perTxBudget `div` 2) -- TODO FIX + (executionMemory perTxBudget `div` 2) traceDebug $ "Plutus auto mode : Available budget per script run: " ++ show budget - script <- liftIO $ readScript loopScriptFile let isInLimits :: Integer -> Either String Bool isInLimits n = case preExecuteScript protocolParameters script (ScriptDataNumber 0) (toLoopArgument n) of @@ -614,7 +483,7 @@ spendAutoScript sourceWallet submitMode loopScriptFile threadName extraArgs tps redeemer <- case startSearch isInLimits 0 searchUpperBound of Left err -> throwE $ ApiError $ "cannot find fitting redeemer :" ++ err Right n -> return $ toLoopArgument n - runPlutusBenchmark sourceWallet submitMode loopScriptFile PreExecuteScript (ScriptDataNumber 0) redeemer threadName extraArgs tps era + return (ScriptDataNumber 0, redeemer) where -- This is the hardcoded calling convention of the loop.plutus script. -- To loop n times one has to pass n + 1_000_000 as redeemer. @@ -631,6 +500,81 @@ spendAutoScript sourceWallet submitMode loopScriptFile threadName extraArgs tps test <- f m if test then search f m b else search f a m +makePlutusContext :: forall era. IsShelleyBasedEra era + => ScriptSpec + -> ActionM (Witness WitCtxTxIn era, Script PlutusScriptV1, ScriptData, Lovelace) +makePlutusContext scriptSpec = do + protocolParameters <- getProtocolParameters + script <- liftIO $ PlutusExample.readScript $ scriptSpecFile scriptSpec + + executionUnitPrices <- case protocolParamPrices protocolParameters of + Just x -> return x + Nothing -> throwE $ WalletError "unexpected protocolParamPrices == Nothing in runPlutusBenchmark" + + perTxBudget <- case protocolParamMaxTxExUnits protocolParameters of + Nothing -> throwE $ ApiError "Cannot determine protocolParamMaxTxExUnits" + Just b -> return b + traceDebug $ "Plutus auto mode : Available budget per TX: " ++ show perTxBudget + + (scriptData, scriptRedeemer, executionUnits) <- case scriptSpecBudget scriptSpec of + StaticScriptBudget sdata redeemer units -> return (sdata, redeemer, units) + CheckScriptBudget sdata redeemer unitsWant -> do + unitsPreRun <- preExecuteScriptAction protocolParameters script sdata redeemer + if unitsWant == unitsPreRun + then return (sdata, redeemer, unitsWant ) + else throwE $ WalletError $ concat [ + " Stated execution Units do not match result of pre execution. " + , " Stated value : ", show unitsWant + , " PreExecution result : ", show unitsPreRun + ] + AutoScript -> do + (sdata, redeemer) <- spendAutoScript protocolParameters script + preRun <- preExecuteScriptAction protocolParameters script sdata redeemer + return (sdata, redeemer, preRun) + + let msg = mconcat [ "Plutus Benchmark :" + , " Script: ", scriptSpecFile scriptSpec + , ", Datum: ", show scriptData + , ", Redeemer: ", show scriptRedeemer + , ", StatedBudget: ", show executionUnits + ] + traceDebug msg + + let + -- TODO -- Cardano.Ledger.Alonzo.Scripts.txscriptfee :: Prices -> ExUnits -> Coin + scriptFee = quantityToLovelace $ Quantity $ ceiling f + where + f :: Rational + f = (executionSteps e `times` priceExecutionSteps p) + (executionMemory e `times` priceExecutionMemory p) + e = executionUnits + p = executionUnitPrices + times w c = fromIntegral w % 1 * c + + PlutusScript PlutusScriptV1 script' = script + scriptWitness :: ScriptWitness WitCtxTxIn era + scriptWitness = case scriptLanguageSupportedInEra (cardanoEra @ era) (PlutusScriptLanguage PlutusScriptV1) of + Nothing -> error $ "runPlutusBenchmark: Plutus V1 scriptlanguage not supported : in era" ++ show (cardanoEra @ era) + Just scriptLang -> PlutusScriptWitness + scriptLang + PlutusScriptV1 + (PScript script') + (ScriptDatumForTxIn scriptData) + scriptRedeemer + executionUnits + + return (ScriptWitness ScriptWitnessForSpending scriptWitness, script, scriptData, scriptFee) + +preExecuteScriptAction :: + ProtocolParameters + -> Script PlutusScriptV1 + -> ScriptData + -> ScriptData + -> ActionM ExecutionUnits +preExecuteScriptAction protocolParameters script scriptData redeemer + = case preExecuteScript protocolParameters script scriptData redeemer of + Left err -> throwE $ WalletError ( "makePlutusContext preExecuteScript failed : " ++ show err ) + Right costs -> return costs + traceTxGeneratorVersion :: ActionM () traceTxGeneratorVersion = traceBenchTxSubmit TraceTxGeneratorVersion Version.txGeneratorVersion diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Selftest.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Selftest.hs index 3d0cb67bc8b..14a37b10ea9 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Selftest.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Selftest.hs @@ -60,8 +60,7 @@ testScript protocolFile submitMode = , createChange 2300000000 9000 , RunBenchmark era wallet submitMode - SpendOutput - (ThreadName "walletBasedBenchmark") extraArgs (TPSRate 10.0) + (ThreadName "walletBasedBenchmark") extraArgs Nothing (TPSRate 10.0) ] where era = AnyCardanoEra AllegraEra diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs index 725a7ba319a..263bfb5e44c 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs @@ -35,7 +35,7 @@ data Action where AddFund :: !AnyCardanoEra -> !WalletName -> !TxIn -> !Lovelace -> !KeyName -> Action ImportGenesisFund :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !KeyName -> !KeyName -> Action CreateChange :: !AnyCardanoEra -> !WalletName -> !WalletName -> !SubmitMode -> !PayMode -> !Lovelace -> !Int -> Action - RunBenchmark :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !SpendMode -> !ThreadName -> !RunBenchmarkAux -> !TPSRate -> Action + RunBenchmark :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !ThreadName -> !RunBenchmarkAux -> Maybe WalletName -> !TPSRate -> Action WaitBenchmark :: !ThreadName -> Action CancelBenchmark :: !ThreadName -> Action Reserved :: [String] -> Action @@ -61,24 +61,25 @@ deriving instance Generic SubmitMode data PayMode where PayToAddr :: !KeyName -> PayMode PayToCollateral :: !KeyName -> PayMode - PayToScript :: !FilePath -> !ScriptData -> PayMode + PayToScript :: !ScriptSpec -> PayMode deriving (Show, Eq) deriving instance Generic PayMode -data SpendMode where - SpendOutput :: SpendMode - SpendScript :: !FilePath -> ScriptBudget -> !ScriptData -> !ScriptRedeemer -> SpendMode - SpendAutoScript :: !FilePath -> SpendMode - deriving (Show, Eq) -deriving instance Generic SpendMode - data ScriptBudget where - StaticScriptBudget :: !ExecutionUnits -> ScriptBudget - PreExecuteScript :: ScriptBudget - CheckScriptBudget :: !ExecutionUnits -> ScriptBudget + StaticScriptBudget :: !ScriptData -> !ScriptRedeemer -> !ExecutionUnits -> ScriptBudget + CheckScriptBudget :: !ScriptData -> !ScriptRedeemer -> !ExecutionUnits -> ScriptBudget + AutoScript :: ScriptBudget --todo: add fraction of total available budget to use (==2 with 2 inputs !) deriving (Show, Eq) deriving instance Generic ScriptBudget +data ScriptSpec = ScriptSpec + { + scriptSpecFile :: !FilePath + , scriptSpecBudget :: !ScriptBudget + } + deriving (Show, Eq) +deriving instance Generic ScriptSpec + data RunBenchmarkAux = RunBenchmarkAux { auxTxCount :: Int , auxFee :: Lovelace diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Wallet.hs b/bench/tx-generator/src/Cardano/Benchmarking/Wallet.hs index ca38790cf36..9363bc239d1 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Wallet.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Wallet.hs @@ -18,7 +18,8 @@ import Cardano.Api.Shelley (ProtocolParameters, ReferenceScript(..)) type WalletRef = MVar Wallet type TxGenerator era = [Fund] -> [TxOut CtxTx era] -> Either String (Tx era, TxId) -type ToUTxO era = [Lovelace] -> ([TxOut CtxTx era], TxId -> [Fund]) + +type ToUTxO era = Lovelace -> (TxOut CtxTx era, TxIx -> TxId -> Fund) data Wallet = Wallet { walletSeqNumber :: !SeqNumber @@ -78,7 +79,7 @@ sourceToStoreTransaction :: TxGenerator era -> FundSource -> ([Lovelace] -> [Lovelace]) - -> ToUTxO era + -> [ToUTxO era] -> FundToStore -> IO (Either String (Tx era)) sourceToStoreTransaction txGenerator fundSource inToOut mkTxOut fundToStore = do @@ -89,11 +90,14 @@ sourceToStoreTransaction txGenerator fundSource inToOut mkTxOut fundToStore = do work inputFunds = do let outValues = inToOut $ map getFundLovelace inputFunds - (outputs, toFunds) = mkTxOut outValues - case txGenerator inputFunds outputs of + outs = zipWith ($) mkTxOut outValues + case txGenerator inputFunds $ map fst outs of Left err -> return $ Left err Right (tx, txId) -> do - fundToStore $ toFunds txId + let + fkt :: (a, TxIx -> TxId -> Fund) -> TxIx -> Fund + fkt a txIx = snd a txIx txId + fundToStore $ zipWith fkt outs [TxIx 0 ..] return $ Right tx includeChange :: Lovelace -> [Lovelace] -> [Lovelace] -> [Lovelace] @@ -103,45 +107,72 @@ includeChange fee spend have = case compare changeValue 0 of LT -> error "genTX: Bad transaction: insufficient funds" where changeValue = sum have - sum spend - fee -mkUTxO :: forall era. IsShelleyBasedEra era - => NetworkId - -> SigningKey PaymentKey - -> Validity - -> ToUTxO era -mkUTxO = mkUTxOVariant PlainOldFund - mkUTxOVariant :: forall era. IsShelleyBasedEra era => Variant -> NetworkId -> SigningKey PaymentKey -> Validity -> ToUTxO era -mkUTxOVariant variant networkId key validity values - = ( map mkTxOut values - , newFunds +mkUTxOVariant variant networkId key validity value + = ( mkTxOut value + , mkNewFund value ) where mkTxOut v = TxOut (keyAddress @ era networkId key) (lovelaceToTxOutValue v) TxOutDatumNone ReferenceScriptNone - newFunds txId = zipWith (mkNewFund txId) [TxIx 0 ..] values - - mkNewFund :: TxId -> TxIx -> Lovelace -> Fund - mkNewFund txId txIx val = Fund $ InAnyCardanoEra (cardanoEra @ era) $ FundInEra { + mkNewFund :: Lovelace -> TxIx -> TxId -> Fund + mkNewFund val txIx txId = Fund $ InAnyCardanoEra (cardanoEra @ era) $ FundInEra { _fundTxIn = TxIn txId txIx + , _fundWitness = KeyWitness KeyWitnessForSpending , _fundVal = lovelaceToTxOutValue val , _fundSigningKey = Just key , _fundValidity = validity , _fundVariant = variant } +-- to be merged with mkUTxOVariant +mkUTxOScript :: forall era. + IsShelleyBasedEra era + => NetworkId + -> (Script PlutusScriptV1, ScriptData) + -> Witness WitCtxTxIn era + -> Validity + -> ToUTxO era +mkUTxOScript networkId (script, txOutDatum) witness validity value + = ( mkTxOut value + , mkNewFund value + ) + where + plutusScriptAddr = makeShelleyAddressInEra + networkId + (PaymentCredentialByScript $ hashScript script) + NoStakeAddress + + mkTxOut v = case scriptDataSupportedInEra (cardanoEra @ era) of + Nothing -> error " mkUtxOScript scriptDataSupportedInEra==Nothing" + Just tag -> TxOut + plutusScriptAddr + (lovelaceToTxOutValue v) + (TxOutDatumHash tag $ hashScriptData txOutDatum) + ReferenceScriptNone + + mkNewFund :: Lovelace -> TxIx -> TxId -> Fund + mkNewFund val txIx txId = Fund $ InAnyCardanoEra (cardanoEra @ era) $ FundInEra { + _fundTxIn = TxIn txId txIx + , _fundWitness = witness + , _fundVal = lovelaceToTxOutValue val + , _fundSigningKey = Nothing + , _fundValidity = validity + , _fundVariant = PlutusScriptFund + } + genTx :: forall era. IsShelleyBasedEra era => ProtocolParameters -> (TxInsCollateral era, [Fund]) -> TxFee era -> TxMetadataInEra era - -> Witness WitCtxTxIn era -> TxGenerator era -genTx protocolParameters (collateral, collFunds) fee metadata witness inFunds outputs +genTx protocolParameters (collateral, collFunds) fee metadata inFunds outputs = case makeTransactionBody txBodyContent of Left err -> error $ show err Right b -> Right ( signShelleyTransaction b $ map WitnessPaymentKey allKeys @@ -150,7 +181,7 @@ genTx protocolParameters (collateral, collFunds) fee metadata witness inFunds ou where allKeys = mapMaybe getFundKey $ inFunds ++ collFunds txBodyContent = TxBodyContent { - txIns = map (\f -> (getFundTxIn f, BuildTxWith witness)) inFunds + txIns = map (\f -> (getFundTxIn f, BuildTxWith $ getFundWitness f)) inFunds , txInsCollateral = collateral , txInsReference = TxInsReferenceNone , txOuts = outputs @@ -194,7 +225,7 @@ benchmarkWalletScript :: forall era . -> NumberOfTxs -> (Target -> FundSource) -> ([Lovelace] -> [Lovelace]) - -> (Target -> SeqNumber -> ToUTxO era) + -> ( Target -> SeqNumber -> [ToUTxO era]) -> FundToStore -> Target -> WalletScript era