Add plutus-contract tests for required signer and... (#624)
...moved some test specs into "TxConstraints directory.
...moved some test specs into "TxConstraints directory.
Spec.Contract
Spec.Emulator
Spec.ErrorChecking
Spec.MustSpendAtLeast
Spec.Plutus.Contract.Oracle
Spec.Plutus.Contract.Wallet
Spec.Rows
Spec.Secrets
Spec.State
Spec.ThreadToken
Spec.TimeValidity
Spec.TxConstraints.MustSpendAtLeast
Spec.TxConstraints.RequiredSigner
Spec.TxConstraints.TimeValidity
--------------------
-- Local components
, plutus-chain-index-core
, plutus-contract
, plutus-ledger
, plutus-ledger-api
, plutus-ledger-constraints
, plutus-script-utils
, plutus-tx-constraints
build-depends:
, cardano-api:{cardano-api, gen}
, plutus-core
, plutus-ledger-api
, plutus-tx
if !(impl(ghcjs) || os(ghcjs))
import Spec.Contract qualified
import Spec.Emulator qualified
import Spec.ErrorChecking qualified
import Spec.MustSpendAtLeast qualified
import Spec.Plutus.Contract.Oracle qualified
import Spec.Plutus.Contract.Wallet qualified
import Spec.Rows qualified
import Spec.Secrets qualified
import Spec.State qualified
import Spec.ThreadToken qualified
import Spec.TimeValidity qualified
import Spec.TxConstraints.MustSpendAtLeast qualified
import Spec.TxConstraints.RequiredSigner qualified
import Spec.TxConstraints.TimeValidity qualified
import Test.Tasty (TestTree, defaultMain, testGroup)
main :: IO ()
Spec.State.tests,
Spec.Rows.tests,
Spec.ThreadToken.tests,
Spec.TimeValidity.tests,
Spec.TxConstraints.MustSpendAtLeast.tests,
Spec.TxConstraints.RequiredSigner.tests,
Spec.TxConstraints.TimeValidity.tests,
Spec.Secrets.tests,
Spec.ErrorChecking.tests,
Spec.Plutus.Contract.Wallet.tests,
Spec.Plutus.Contract.Oracle.tests,
Spec.Balancing.tests,
Spec.MustSpendAtLeast.tests
Spec.Balancing.tests
]
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Spec.MustSpendAtLeast(tests) where
module Spec.TxConstraints.MustSpendAtLeast(tests) where
import Control.Monad (void)
import Test.Tasty (TestTree, testGroup)
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Spec.TxConstraints.RequiredSigner(tests) where
import Control.Monad (void)
import Data.Void (Void)
import Test.Tasty (TestTree, testGroup)
import Data.List (isInfixOf)
import Data.Maybe (fromJust)
import Data.String (fromString)
import Ledger qualified
import Ledger.Ada qualified as Ada
import Ledger.CardanoWallet as CW
import Ledger.Constraints.OffChain qualified as Constraints (paymentPubKey, plutusV1TypedValidatorLookups,
unspentOutputs)
import Ledger.Constraints.OnChain.V1 qualified as Constraints
import Ledger.Constraints.TxConstraints qualified as Constraints (collectFromTheScript, mustBeSignedBy,
mustIncludeDatum, mustPayToTheScript,
requiredSignatories)
import Ledger.Tx qualified as Tx
import Ledger.Typed.Scripts qualified as Scripts
import Plutus.Contract as Con
import Plutus.Contract.Test (assertFailedTransaction, assertValidatedTransactionCount, checkPredicateOptions,
defaultCheckOptions, mockWalletPaymentPubKey, mockWalletPaymentPubKeyHash, w1, w2)
import Plutus.Trace qualified as Trace
import Plutus.V1.Ledger.Scripts (ScriptError (EvaluationError), unitDatum)
import PlutusTx qualified
import Prelude
import Wallet.Emulator.Wallet (signPrivateKeys, walletToMockWallet)
tests :: TestTree
tests =
testGroup "required signer"
[
ownWallet
, otherWallet
, otherWalletNoSigningProcess
, withoutOffChainMustBeSignedBy
, phase2FailureMustBeSignedBy
]
mustBeSignedByContract :: Ledger.PaymentPubKey -> Ledger.PaymentPubKeyHash -> Contract () Empty ContractError ()
mustBeSignedByContract pk pkh = do
let lookups1 = Constraints.plutusV1TypedValidatorLookups mustBeSignedByTypedValidator
tx1 = Constraints.mustPayToTheScript () (Ada.lovelaceValueOf 25_000_000)
ledgerTx1 <- submitTxConstraintsWith lookups1 tx1
awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx1
utxos <- utxosAt (Ledger.scriptHashAddress $ Scripts.validatorHash mustBeSignedByTypedValidator)
let lookups2 =
Constraints.plutusV1TypedValidatorLookups mustBeSignedByTypedValidator
<> Constraints.unspentOutputs utxos
<> Constraints.paymentPubKey pk
tx2 =
Constraints.collectFromTheScript utxos pkh
<> Constraints.mustIncludeDatum unitDatum
<> Constraints.mustBeSignedBy pkh
logInfo @String $ "Required Signatories: " ++ show (Constraints.requiredSignatories tx2)
ledgerTx2 <- submitTxConstraintsWith @UnitTest lookups2 tx2
awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx2
withoutOffChainMustBeSignedByContract :: Ledger.PaymentPubKey -> Ledger.PaymentPubKeyHash -> Contract () Empty ContractError ()
withoutOffChainMustBeSignedByContract pk pkh = do
let lookups1 = Constraints.plutusV1TypedValidatorLookups mustBeSignedByTypedValidator
tx1 = Constraints.mustPayToTheScript () (Ada.lovelaceValueOf 25_000_000)
ledgerTx1 <- submitTxConstraintsWith lookups1 tx1
awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx1
utxos <- utxosAt (Ledger.scriptHashAddress $ Scripts.validatorHash mustBeSignedByTypedValidator)
let lookups2 =
Constraints.plutusV1TypedValidatorLookups mustBeSignedByTypedValidator
<> Constraints.unspentOutputs utxos
<> Constraints.paymentPubKey pk
tx2 =
Constraints.collectFromTheScript utxos pkh
<> Constraints.mustIncludeDatum unitDatum
logInfo @String $ "Required Signatories: " ++ show (Constraints.requiredSignatories tx2)
ledgerTx2 <- submitTxConstraintsWith @UnitTest lookups2 tx2
awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx2
ownWallet :: TestTree
ownWallet =
let pk = mockWalletPaymentPubKey w1
pkh = mockWalletPaymentPubKeyHash w1
trace = do
void $ Trace.activateContractWallet w1 $ mustBeSignedByContract pk pkh
void $ Trace.waitNSlots 1
in checkPredicateOptions defaultCheckOptions "own wallet's signature passes on-chain mustBeSignedBy validation" (assertValidatedTransactionCount 2) (void trace)
otherWallet :: TestTree -- must use Trace.setSigningProcess for w2
otherWallet =
let pk = mockWalletPaymentPubKey w2
pkh = mockWalletPaymentPubKeyHash w2
trace = do
Trace.setSigningProcess w1 (Just $ signPrivateKeys [paymentPrivateKey $ fromJust $ walletToMockWallet w1, paymentPrivateKey $ fromJust $ walletToMockWallet w2])
void $ Trace.activateContractWallet w1 $ mustBeSignedByContract pk pkh
void $ Trace.waitNSlots 1
in checkPredicateOptions defaultCheckOptions "other wallet's signature passes on-chain mustBeSignedBy validation" (assertValidatedTransactionCount 2) (void trace)
otherWalletNoSigningProcess :: TestTree
otherWalletNoSigningProcess =
let pk = mockWalletPaymentPubKey w2
pkh = mockWalletPaymentPubKeyHash w2
trace = do
void $ Trace.activateContractWallet w1 $ mustBeSignedByContract pk pkh
void $ Trace.waitNSlots 1
in checkPredicateOptions defaultCheckOptions "without Trace.setSigningProcess fails phase-1 validation"
(assertFailedTransaction (\_ err _ -> case err of {Ledger.CardanoLedgerValidationError str -> isInfixOf "MissingRequiredSigners" str; _ -> False }))
(void trace)
withoutOffChainMustBeSignedBy :: TestTree -- there's no "required signer" in the txbody logs but still passes phase-2 so it must be there. Raised https://github.com/input-output-hk/plutus-apps/issues/645. It'd be good to check log output for expected required signer pubkey in these tests.
withoutOffChainMustBeSignedBy =
let pk = mockWalletPaymentPubKey w1
pkh = mockWalletPaymentPubKeyHash w1
trace = do
void $ Trace.activateContractWallet w1 $ withoutOffChainMustBeSignedByContract pk pkh
void $ Trace.waitNSlots 1
in checkPredicateOptions defaultCheckOptions "without mustBeSignedBy off-chain constraint passes mustBeSignedBy on-chain validation because required signer is still included in txbody"
(assertValidatedTransactionCount 2)
(void trace)
phase2FailureMustBeSignedBy :: TestTree
phase2FailureMustBeSignedBy =
let pk = mockWalletPaymentPubKey w1
pkh = Ledger.PaymentPubKeyHash $ fromString "76aaef06f38cc98ed08ceb168ddb55bab2ea5df43a6847a99f086fc9" :: Ledger.PaymentPubKeyHash
trace = do
void $ Trace.activateContractWallet w1 $ withoutOffChainMustBeSignedByContract pk pkh
void $ Trace.waitNSlots 1
in checkPredicateOptions defaultCheckOptions "with wrong pubkey fails on-chain mustBeSignedBy constraint validation"
(assertFailedTransaction (\_ err _ -> case err of {Ledger.ScriptFailure (EvaluationError ("L4":_) _) -> True; _ -> False }))
(void trace)
{-
validator using mustBeSignedBy constraint
-}
data UnitTest
instance Scripts.ValidatorTypes UnitTest where
type instance DatumType UnitTest = ()
type instance RedeemerType UnitTest = Ledger.PaymentPubKeyHash
{-# INLINEABLE mustBeSignedByValidator #-}
mustBeSignedByValidator :: () -> Ledger.PaymentPubKeyHash -> Ledger.ScriptContext -> Bool
mustBeSignedByValidator _ pkh ctx = Constraints.checkScriptContext @Void @Void (Constraints.mustBeSignedBy pkh) ctx
mustBeSignedByTypedValidator :: Scripts.TypedValidator UnitTest
mustBeSignedByTypedValidator = Scripts.mkTypedValidator @UnitTest
$$(PlutusTx.compile [||mustBeSignedByValidator||])
$$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.mkUntypedValidator
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Spec.TimeValidity(tests) where
module Spec.TxConstraints.TimeValidity(tests) where
import Cardano.Api.Shelley (protocolParamProtocolVersion)
import Control.Lens hiding (contains, from, (.>))
This patch fixes a bug where results `TVar` was never initialised if all local root peers were provided with their IP addresses, and thus the node didn't try to connect to any of its local root peers. We add `LocalRootPeersResult` to `TestTraceEvent` which is used to track values committed to the `TVar` which holds local root peers with resolved dns names. It is more robust to use `traceTVarIO` than to relay on `TraceLocalRootPeers` events, as we can force the latter to be traced even if there's no dns name to be resolved. This captures the case where local root peers contains only IP addresses.
It's no longer necessary to worry about exporting the default constructor for `TokenMap`. The original concern was that exporting the default constructor would allow callers to break the invariant that there are no `mempty` values in the map. However, this is no longer possible, as the `MonoidMap` type handles this invariant automatically. Therefore, it's no longer necessary to have a warning. Nevertheless, this commit doesn't export the default constructor, as there currently isn't any need to do so.
The `TokenMap` type has an invariant that: - no `mempty` values appear within the internal data structure. - no `mempty` values appear within any encoding of a `TokenMap`. The `MonoidMap` type already guarantees to handle this invariant, and that type is covered by a comprehensive test suite. Therefore, there's no need to repeat those tests for the `TokenMap` type.
This patch fixes a bug where results `TVar` was never initialised if all local root peers were provided with their IP addresses, and thus the node didn't try to connect to any of its local root peers. We add `LocalRootPeersResult` to `TestTraceEvent` which is used to track values committed to the `TVar` which holds local root peers with resolved dns names. It is more robust to use `traceTVarIO` than to relay on `TraceLocalRootPeers` events, as we can force the latter to be traced even if there's no dns name to be resolved. This captures the case where local root peers contains only IP addresses.