11{-# LANGUAGE NamedFieldPuns #-}
22{-# LANGUAGE NumericUnderscores #-}
3+ {-# LANGUAGE OverloadedStrings #-}
4+ {-# LANGUAGE ScopedTypeVariables #-}
35
46module Cardano.Testnet.Test.Cli.Plutus.CostCalculation
57 ( hprop_ref_plutus_cost_calculation
68 , hprop_included_plutus_cost_calculation
9+ , hprop_included_simple_script_cost_calculation
710 -- | Execute tests in this module with:
811 -- @DISABLE_RETRIES=1 cabal run cardano-testnet-test -- -p "/Spec.hs.Spec.Ledger Events.Plutus.Cost Calc/"@
912 )
1013where
1114
1215import Cardano.Api (AnyCardanoEra (AnyCardanoEra ),
13- AnyShelleyBasedEra (AnyShelleyBasedEra ), File (File ), MonadIO (liftIO ),
14- ShelleyBasedEra (ShelleyBasedEraConway ), ToCardanoEra (toCardanoEra ), renderTxIn ,
15- unFile )
16+ AnyShelleyBasedEra (AnyShelleyBasedEra ), ExceptT , File (File ), MonadIO (liftIO ),
17+ ShelleyBasedEra (ShelleyBasedEraConway ), ToCardanoEra (toCardanoEra ),
18+ deserialiseAnyVerificationKey , liftEither , mapSomeAddressVerificationKey ,
19+ renderTxIn , serialiseToRawBytesHex , unFile , verificationKeyHash )
1620import Cardano.Api.Experimental (Some (Some ))
1721import Cardano.Api.Ledger (EpochInterval (EpochInterval ), unCoin )
1822
@@ -21,8 +25,17 @@ import Cardano.Testnet
2125import Prelude
2226
2327import Control.Monad (void )
28+ import Control.Monad.Except (runExceptT )
29+ import Data.Aeson (Value , encodeFile )
30+ import qualified Data.Aeson.KeyMap as KeyMap
31+ import Data.Aeson.Types (Value (.. ), object )
32+ import Data.Bifunctor (first )
33+ import qualified Data.ByteString as BS
2434import Data.Default.Class (Default (def ))
35+ import Data.Text (Text )
2536import qualified Data.Text as Text
37+ import Data.Text.Encoding (decodeLatin1 )
38+ import qualified Data.Vector as Vector
2639import System.Directory (makeAbsolute )
2740import System.FilePath ((</>) )
2841import qualified System.Info as SYS
@@ -41,7 +54,8 @@ import Testnet.Process.Cli.Transaction (TxOutAddress (..), mkSpendOutp
4154import Testnet.Process.Run (execCli' , mkExecConfig )
4255import Testnet.Property.Util (integrationRetryWorkspace )
4356import Testnet.Start.Types (eraToString )
44- import Testnet.Types (PaymentKeyInfo (paymentKeyInfoAddr ), paymentKeyInfoPair )
57+ import Testnet.Types (PaymentKeyInfo (paymentKeyInfoAddr ), paymentKeyInfoPair ,
58+ verificationKey )
4559
4660-- @DISABLE_RETRIES=1 cabal run cardano-testnet-test -- -p "/Spec.hs.Spec.Ledger Events.Plutus.Cost Calc.Ref Script/"@
4761hprop_ref_plutus_cost_calculation :: Property
@@ -287,7 +301,7 @@ hprop_included_plutus_cost_calculation = integrationRetryWorkspace 2 "included p
287301 submitTx execConfig cEra signedIncludedScript
288302
289303 -- Calculate cost of the transaction
290- let includedScriptCostOutput = File $ includedScriptUnlock </> " unsigned-tx.tx "
304+ let includedScriptCostOutput = File $ includedScriptUnlock </> " scriptCost.json "
291305 H. noteM_ $
292306 execCli'
293307 execConfig
@@ -300,3 +314,142 @@ hprop_included_plutus_cost_calculation = integrationRetryWorkspace 2 "included p
300314 H. diffFileVsGoldenFile
301315 (unFile includedScriptCostOutput)
302316 " test/cardano-testnet-test/files/calculatePlutusScriptCost.json"
317+
318+ -- @DISABLE_RETRIES=1 cabal run cardano-testnet-test -- -p "/Spec.hs.Spec.Ledger Events.Plutus.Cost Calc.Simple Script/"@
319+ hprop_included_simple_script_cost_calculation :: Property
320+ hprop_included_simple_script_cost_calculation = integrationRetryWorkspace 2 " included simple script" $ \ tempAbsBasePath' -> H. runWithDefaultWatchdog_ $ do
321+ H. note_ SYS. os
322+ conf@ Conf {tempAbsPath} <- mkConf tempAbsBasePath'
323+ let tempAbsPath' = unTmpAbsPath tempAbsPath
324+ work <- H. createDirectoryIfMissing $ tempAbsPath' </> " work"
325+
326+ let
327+ sbe = ShelleyBasedEraConway
328+ era = toCardanoEra sbe
329+ cEra = AnyCardanoEra era
330+ eraName = eraToString era
331+ tempBaseAbsPath = makeTmpBaseAbsPath $ TmpAbsolutePath tempAbsPath'
332+ options = def{cardanoNodeEra = AnyShelleyBasedEra sbe}
333+
334+ TestnetRuntime
335+ { configurationFile
336+ , testnetMagic
337+ , testnetNodes
338+ , wallets = wallet0 : wallet1 : _
339+ } <-
340+ cardanoTestnetDefault options def conf
341+
342+ poolNode1 <- H. headM testnetNodes
343+ poolSprocket1 <- H. noteShow $ nodeSprocket poolNode1
344+ execConfig <- mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic
345+ epochStateView <- getEpochStateView configurationFile (nodeSocketPath poolNode1)
346+
347+ -- We write a simple script that allows any of the two payment keys to spend the money
348+
349+ addrHash1 <- H. evalEitherM $ liftIO $ runExceptT $ paymentKeyInfoHash wallet0
350+ addrHash2 <- H. evalEitherM $ liftIO $ runExceptT $ paymentKeyInfoHash wallet1
351+
352+ simpleScriptLockWork <- H. createDirectoryIfMissing $ work </> " simple-script-lock"
353+ let simpleScript = File $ simpleScriptLockWork </> " simple-script.json"
354+ liftIO $ encodeFile (unFile simpleScript) $ generateSimpleAnyKeyScript [addrHash1, addrHash2]
355+
356+ -- We now submit a transaction to the script address
357+ let lockedAmount = 10_000_000
358+ enoughAmountForFees = 2_000_000 -- Needs to be more than min ada
359+
360+ txBodySimpleScriptLock <-
361+ mkSpendOutputsOnlyTx
362+ execConfig
363+ epochStateView
364+ sbe
365+ simpleScriptLockWork
366+ " tx-body"
367+ wallet0
368+ [(ScriptAddress simpleScript, lockedAmount, Nothing )]
369+
370+ signedTxSimpleScriptLock <-
371+ signTx
372+ execConfig
373+ cEra
374+ simpleScriptLockWork
375+ " signed-tx"
376+ txBodySimpleScriptLock
377+ [Some $ paymentKeyInfoPair wallet0]
378+ submitTx execConfig cEra signedTxSimpleScriptLock
379+
380+ -- Wait until transaction is on chain and obtain transaction identifier
381+ txIdSimpleScriptLock <- retrieveTransactionId execConfig signedTxSimpleScriptLock
382+ txIxSimpleScriptLock <-
383+ H. evalMaybeM $
384+ watchEpochStateUpdate
385+ epochStateView
386+ (EpochInterval 2 )
387+ (getTxIx sbe txIdSimpleScriptLock lockedAmount)
388+
389+ -- Create transaction that unlocks the simple script UTxO we just created
390+ simpleScriptUnlockWork <- H. createDirectoryIfMissing $ work </> " simple-script-unlock"
391+ let unsignedUnlockSimpleScript = File $ simpleScriptUnlockWork </> " unsigned-tx.tx"
392+
393+ void $
394+ execCli'
395+ execConfig
396+ [ eraName
397+ , " transaction" , " build"
398+ , " --change-address" , Text. unpack $ paymentKeyInfoAddr wallet1
399+ , " --tx-in" , txIdSimpleScriptLock <> " #" <> show txIxSimpleScriptLock
400+ , " --tx-in-script-file" , unFile simpleScript
401+ , " --tx-out" , Text. unpack (paymentKeyInfoAddr wallet1) <> " +" <> show (unCoin (lockedAmount - enoughAmountForFees))
402+ , " --witness-override" , " 2"
403+ , " --out-file" , unFile unsignedUnlockSimpleScript
404+ ]
405+
406+ signedScriptUnlock <-
407+ signTx
408+ execConfig
409+ cEra
410+ simpleScriptUnlockWork
411+ " signed-tx"
412+ unsignedUnlockSimpleScript
413+ [Some $ paymentKeyInfoPair wallet1]
414+
415+ submitTx execConfig cEra signedScriptUnlock
416+
417+ -- Calculate cost of the transaction
418+
419+ output <-
420+ H. noteM $
421+ execCli'
422+ execConfig
423+ [ eraName
424+ , " transaction" , " calculate-plutus-script-cost"
425+ , " --tx-file" , unFile signedScriptUnlock
426+ ]
427+
428+ H. diffVsGoldenFile output " test/cardano-testnet-test/files/calculateSimpleScriptCost.json"
429+
430+ where
431+ generateSimpleAnyKeyScript :: [Text ] -> Value
432+ generateSimpleAnyKeyScript keyHashes =
433+ object
434+ [ (" type" , " any" )
435+ ,
436+ ( " scripts"
437+ , Array $
438+ Vector. fromList
439+ [ Object $
440+ KeyMap. fromList
441+ [ (" type" , " sig" )
442+ , (" keyHash" , String keyHash)
443+ ]
444+ | keyHash <- keyHashes
445+ ]
446+ )
447+ ]
448+
449+ paymentKeyInfoHash :: PaymentKeyInfo -> ExceptT String IO Text
450+ paymentKeyInfoHash wallet = do
451+ vkBs <- liftIO $ BS. readFile (unFile $ verificationKey $ paymentKeyInfoPair wallet)
452+ svk <- liftEither $ first show $ deserialiseAnyVerificationKey vkBs
453+ return $
454+ decodeLatin1 $
455+ mapSomeAddressVerificationKey (serialiseToRawBytesHex . verificationKeyHash) svk
0 commit comments