Skip to content

Commit

Permalink
Direct CLI plutus testing
Browse files Browse the repository at this point in the history
  • Loading branch information
newhoggy committed Jul 7, 2021
1 parent 2e25bee commit cbab29c
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 8 deletions.
5 changes: 4 additions & 1 deletion cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ package cardano-node
package cardano-node-chairman
tests: True

package cardano-testnet
tests: True

-- The following is needed because Nix is doing something crazy.
package byron-spec-ledger
tests: False
Expand Down Expand Up @@ -107,7 +110,7 @@ package cardano-ledger-alonzo-test
source-repository-package
type: git
location: https://github.com/input-output-hk/hedgehog-extras
tag: 2f28e62f1508f07bb628963ee9bb23dc19ec0e03
tag: 7ffbd217bb969d7c01f1e70cf58535df06e5d9a8
--sha256: 0sjlk19v0dg63v8dawg844y3wzm1nmq0qxpvhip81x5yi091jjmm

source-repository-package
Expand Down
11 changes: 9 additions & 2 deletions cardano-testnet/cardano-testnet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,22 @@ test-suite cardano-testnet-tests

type: exitcode-stdio-1.0

build-depends: cardano-testnet
build-depends: aeson
, cardano-testnet
, directory
, hedgehog
, hedgehog-extras
, filepath
, lens
, lens-aeson
, tasty
, tasty-hedgehog
, text
, unordered-containers

other-modules: Spec.Plutus
other-modules: Spec.Extras
Spec.Plutus.Direct.TxInLockingPlutus
Spec.Plutus.Script.TxInLockingPlutus

ghc-options: -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T

Expand Down
10 changes: 10 additions & 0 deletions cardano-testnet/src/Test/Process.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Test.Process
( execCli
, execCli'
, procCli
, procNode
, procChairman
Expand All @@ -11,6 +12,7 @@ import Data.Function
import Data.String
import GHC.Stack (HasCallStack)
import Hedgehog (MonadTest)
import Hedgehog.Extras.Test.Process (ExecConfig)
import System.Process (CreateProcess)

import qualified GHC.Stack as GHC
Expand All @@ -23,6 +25,14 @@ execCli
-> m String
execCli = GHC.withFrozenCallStack $ H.execFlex "cardano-cli" "CARDANO_CLI"

-- | Run cardano-cli, returning the stdout
execCli'
:: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack)
=> ExecConfig
-> [String]
-> m String
execCli' execConfig = GHC.withFrozenCallStack $ H.execFlex' execConfig "cardano-cli" "CARDANO_CLI"

-- | Create a 'CreateProcess' describing how to start the cardano-cli process
-- and an argument list.
procCli
Expand Down
6 changes: 4 additions & 2 deletions cardano-testnet/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import qualified Test.Tasty as T
import qualified Test.Tasty.Ingredients as T
import qualified Test.Tasty.Hedgehog as H

import qualified Spec.Plutus
import qualified Spec.Plutus.Direct.TxInLockingPlutus
import qualified Spec.Plutus.Script.TxInLockingPlutus

tests :: IO T.TestTree
tests = do
pure $ T.testGroup "test/Spec.hs"
[ T.testGroup "Spec"
[ H.testProperty "Plutus" Spec.Plutus.hprop_plutus
[ H.testProperty "Plutus" Spec.Plutus.Direct.TxInLockingPlutus.hprop_plutus
, H.testProperty "Plutus" Spec.Plutus.Script.TxInLockingPlutus.hprop_plutus
]
]

Expand Down
3 changes: 3 additions & 0 deletions cardano-testnet/test/Spec/Extras.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Spec.Extras
(
) where
225 changes: 225 additions & 0 deletions cardano-testnet/test/Spec/Plutus/Direct/TxInLockingPlutus.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Spec.Plutus.Direct.TxInLockingPlutus
( hprop_plutus
) where

import Control.Applicative
import Control.Monad
import Control.Lens
import Data.Aeson (Value)
import Data.Bool (not)
import Data.Function
import Data.Int
import Data.List ((!!))
import Data.Maybe
import Data.Monoid
import Data.String
import GHC.Real
import GHC.Num
import Hedgehog (Property, (===))
import Prelude (head)
import System.FilePath ((</>))
import Text.Show (Show(..))

import qualified Control.Lens as CL
import qualified Data.Aeson.Lens as CL
import qualified Data.List as L
import qualified Data.HashMap.Lazy as HM
import qualified Data.Text as T
import qualified Hedgehog.Extras.Stock.IO.Network.Sprocket as IO
import qualified Hedgehog.Extras.Test.Base as H
import qualified Hedgehog.Extras.Test.Concurrent as H
import qualified Hedgehog.Extras.Test.File as H
import qualified Hedgehog.Extras.Test.Process as H
import qualified System.Directory as IO
import qualified Test.Base as H
import qualified Test.Process as H
import qualified Testnet.Cardano as H
import qualified Testnet.Conf as H

{- HLINT ignore "Redundant return" -}
{- HLINT ignore "Use let" -}

hprop_plutus :: Property
hprop_plutus = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAbsBasePath' -> do
projectBase <- H.note =<< H.noteIO . IO.canonicalizePath =<< H.getProjectBase
conf@H.Conf { H.tempBaseAbsPath, H.tempAbsPath } <- H.noteShowM $ H.mkConf tempAbsBasePath' Nothing

H.TestnetRuntime { H.bftSprockets, H.testnetMagic } <- H.testnet H.defaultTestnetOptions conf

-- path <- H.noteIO $ fromMaybe "" <$> IO.lookupEnv "PATH"

execConfig <- H.noteShow H.ExecConfig
{ H.execConfigEnv = Last $ Just
[ ("CARDANO_NODE_SOCKET_PATH", IO.sprocketArgumentName (head bftSprockets))
]
, H.execConfigCwd = Last $ Just tempBaseAbsPath
}

base <- H.note projectBase
work <- H.note tempAbsPath
utxoVKeyFile <- H.note $ tempAbsPath </> "shelley/utxo-keys/utxo1.vkey"
utxoSKeyFile <- H.note $ tempAbsPath </> "shelley/utxo-keys/utxo1.skey"

-- ("CARDANO_NODE_SOCKET_PATH", IO.sprocketArgumentName (head bftSprockets))

plutusScriptFileInUse <- H.note $ base </> "scripts/plutus/scripts/untyped-always-succeeds-txin.plutus"

-- This datum hash is the hash of the untyped 42
scriptDatumHash <- pure "9e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b"
plutusRequiredSpace <- pure @_ @Integer 70000000
plutusRequiredTime <- pure @_ @Integer 70000000
datumFile <- H.note $ base </> "scripts/plutus/data/42.datum"
redeemerFile <- H.note $ base </> "scripts/plutus/data/42.redeemer"

-- Always succeeds Plutus script in use. Any datum and redeemer combination will succeed.
-- Script at: $plutusscriptinuse

-- Step 1: Create a tx ouput with a datum hash at the script address. In order for a tx ouput to be locked
-- by a plutus script, it must have a datahash. We also need collateral tx inputs so we split the utxo
-- in order to accomodate this.

plutusScriptAddr <- H.execCli
[ "address", "build"
, "--payment-script-file", plutusScriptFileInUse
, "--testnet-magic", show @Int testnetMagic
]

-- mkdir -p $WORK

utxoAddr <- H.execCli
[ "address", "build"
, "--testnet-magic", show @Int testnetMagic
, "--payment-verification-key-file", utxoVKeyFile
]

void $ H.execCli' execConfig
[ "query", "utxo"
, "--address", utxoAddr
, "--cardano-mode"
, "--testnet-magic", show @Int testnetMagic
, "--out-file", work </> "utxo-1.json"
]

H.cat $ work </> "utxo-1.json"

utxo1Json <- H.leftFailM . H.readJsonFile $ work </> "utxo-1.json"
txin <- H.noteShow $ utxo1Json ^. CL._Object . CL.to HM.keys . CL.ix 0 . to T.unpack
lovelaceAtTxin <- H.nothingFailM . H.noteShow $ utxo1Json ^? CL.key (T.pack txin) . CL.key "value" . CL.key "lovelace" . CL._Integer
lovelaceAtTxinDiv2 <- H.noteShow $ lovelaceAtTxin `div` 2

void $ H.execCli
[ "transaction", "build-raw"
, "--alonzo-era"
, "--fee", "0"
, "--tx-in", txin
, "--tx-out", plutusScriptAddr <> "+" <> show @Integer lovelaceAtTxinDiv2
, "--tx-out-datum-hash", scriptDatumHash
, "--tx-out", utxoAddr <> "+" <> show @Integer lovelaceAtTxinDiv2
, "--out-file", work </> "create-datum-output.body"
]

void $ H.execCli
[ "transaction", "sign"
, "--tx-body-file", work </> "create-datum-output.body"
, "--testnet-magic", show @Int testnetMagic
, "--signing-key-file", utxoSKeyFile
, "--out-file", work </> "create-datum-output.tx"
]

void $ H.execCli' execConfig
[ "transaction", "submit"
, "--tx-file", work </> "create-datum-output.tx"
, "--testnet-magic", show @Int testnetMagic
]

H.threadDelay 5000000

-- After "locking" the tx output at the script address, we can now can attempt to spend
-- the "locked" tx output below.

void $ H.execCli' execConfig
[ "query", "utxo"
, "--address", plutusScriptAddr
, "--testnet-magic", show @Int testnetMagic
, "--out-file", work </> "plutusutxo.json"
]

H.cat $ work </> "plutusutxo.json"

plutusUtxoJson <- H.leftFailM . H.readJsonFile $ work </> "plutusutxo.json"

plutusUtxoTxIn <- H.noteShow $ plutusUtxoJson ^. CL._Object . CL.to HM.keys . CL.ix 0 . to T.unpack

void $ H.execCli' execConfig
[ "query", "utxo"
, "--address", utxoAddr
, "--cardano-mode"
, "--testnet-magic", show @Int testnetMagic
, "--out-file", work </> "utxo-2.json"
]

H.cat $ work </> "utxo-2.json"

utxo2Json :: Value <- H.leftFailM $ H.readJsonFile $ work </> "utxo-2.json"

txinCollateral <- H.noteShow $ utxo2Json ^. CL._Object . CL.to HM.keys . CL.ix 0 . to T.unpack

void $ H.execCli' execConfig
[ "query", "protocol-parameters"
, "--testnet-magic", show @Int testnetMagic
, "--out-file", work </> "pparams.json"
]

let dummyaddress = "addr_test1vpqgspvmh6m2m5pwangvdg499srfzre2dd96qq57nlnw6yctpasy4"

lovelaceAtplutusScriptAddr <- H.nothingFailM . H.noteShow $ plutusUtxoJson ^? CL.key (T.pack plutusUtxoTxIn) . CL.key "value" . CL.key "lovelace" . CL._Integer

txFee <- H.noteShow $ plutusRequiredTime + plutusRequiredSpace
spendable <- H.noteShow $ lovelaceAtplutusScriptAddr - plutusRequiredTime - plutusRequiredSpace

void $ H.execCli
[ "transaction", "build-raw"
, "--alonzo-era"
, "--fee", show @Integer txFee
, "--tx-in", plutusUtxoTxIn
, "--tx-in-collateral", txinCollateral
, "--tx-out", dummyaddress <> "+" <> show @Integer spendable
, "--tx-in-script-file", plutusScriptFileInUse
, "--tx-in-datum-file", datumFile
, "--protocol-params-file", work </> "pparams.json"
, "--tx-in-redeemer-file", redeemerFile
, "--tx-in-execution-units", show @(Integer, Integer) (plutusRequiredTime, plutusRequiredSpace)
, "--out-file", work </> "test-alonzo.body"
]

void $ H.execCli
[ "transaction", "sign"
, "--tx-body-file", work </> "test-alonzo.body"
, "--testnet-magic", show @Int testnetMagic
, "--signing-key-file", utxoSKeyFile
, "--out-file", work </> "alonzo.tx"
]

void $ H.execCli' execConfig
[ "transaction", "submit"
, "--tx-file", work </> "alonzo.tx"
, "--testnet-magic", show @Int testnetMagic
]

H.threadDelay 5000000

-- Querying UTxO at $dummyaddress. If there is ADA at the address the Plutus script successfully executed!

result <- T.pack <$> H.execCli' execConfig
[ "query", "utxo"
, "--address", dummyaddress
, "--testnet-magic", show @Int testnetMagic
]

L.filter (not . T.null) (T.splitOn " " (T.lines result !! 2)) !! 2 === "360000000"
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}

module Spec.Plutus
module Spec.Plutus.Script.TxInLockingPlutus
( hprop_plutus
) where

Expand All @@ -11,6 +11,7 @@ import Data.Function
import Data.Functor ((<$>))
import Data.Int
import Data.Maybe
import Data.Monoid
import Hedgehog (Property)
import Prelude (head)
import System.FilePath ((</>))
Expand Down Expand Up @@ -38,7 +39,7 @@ hprop_plutus = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAb
path <- H.evalIO $ fromMaybe "" <$> IO.lookupEnv "PATH"

let execConfig = H.ExecConfig
{ H.execConfigEnv = Just
{ H.execConfigEnv = Last $ Just
[ ("CARDANO_CLI", cardanoCli)
, ("BASE", projectBase)
, ("WORK", tempAbsPath)
Expand All @@ -48,7 +49,7 @@ hprop_plutus = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAb
, ("TESTNET_MAGIC", show @Int testnetMagic)
, ("PATH", path)
]
, H.execConfigCwd = Just tempBaseAbsPath
, H.execConfigCwd = Last $ Just tempBaseAbsPath
}

scriptPath <- H.eval $ projectBase </> "scripts/plutus/example-txin-locking-plutus-script.sh"
Expand Down

0 comments on commit cbab29c

Please sign in to comment.