3

I ran into an error while trying to autobalance a transaction body:

module Foo.Offchain where
import Plutus.V1.Ledger.Scripts (MintingPolicy)
import Plutus.V1.Ledger.Address (Address)

import Hydra.Cardano.API
import Hydra.Prelude
import Test.Hydra.Prelude
import Cardano.Api.UTxO qualified as UTxO

import Foo.Onchain(fooPolicy)

userAddress :: Address ShelleyAddr
...

txFoo = do
  let policyScript = fromPlutusScript @PlutusScriptV2 
        $ getMintingPolicy fooPolicy
      policyId = PolicyId $ hashScript $ PlutusScript policyScript
      mintingWitness = BuildTxWith . Map.singleton policyId
        $ mkScriptWitness policyScript NoScriptDatumForMint (toScriptData ())
      mintingValue = valueFromList [(AssetId policyId "", 1)]
  ...
  pparams <- queryProtocolParameters networkId nodeSocket QueryTip
  systemStart <- querySystemStart networkId nodeSocket QueryTip
  eraHistory <- queryEraHistory networkId nodeSocket QueryTip
  stakePools <- queryStakePools networkId nodeSocket QueryTip
  utxoSet <- ...
  someUtxo <- ...
  someTxOut <- ...
  ...
  let unbalancedBody =
        TxBodyContent
          (withWitness <$> toList (UTxO.inputSet utxoSet))
          (TxInsCollateral [someUtxo])
          TxInsReferenceNone
          [someTxOut]
          TxTotalCollateralNone
          TxReturnCollateralNone
          (TxFeeExplicit 0)
          (TxValidityNoLowerBound, TxValidityNoUpperBound)
          TxMetadataNone
          TxAuxScriptsNone
          TxExtraKeyWitnessesNone
          (BuildTxWith $ Just pparams)
          TxWithdrawalsNone
          TxCertificatesNone
          TxUpdateProposalNone
          (TxMintValue mintingValue mintingWitness)
          TxScriptValidityNone
  let balancedBody = either (error . show) balancedTxBody $
        makeTransactionBodyAutoBalance
          BabbageEraInCardanoMode
          systemStart
          eraHistory
          pparams
          stakePools
          (UTxO.toApi utxoSet)
          preBody
          (ShelleyAddressInEra userAddress)
          Nothing

I got the following error:

TxBodyScriptExecutionError
  [ ( ScriptWitnessIndexMint 0
    , ScriptErrorEvaluationFailed
        (CekError An error has occurred:
           User error:
             The machine terminated because of an error, either from a built-in
             function or from an explicit use of 'error'.
           Caused by: (force headList [])
        )
        []
    )
  ]

The strange thing is that I still get this error even if I reduce fooPolicy down to a trivial check that depends only the ScriptContext:

module Foo.Onchain where

import PlutusTx.Prelude
import Plutus.V1.Ledger.Api (BuiltinData(..), Data(..), MintingPolicy, ScriptContext, UnsafeFromData (unsafeFromBuiltinData), mkMintingPolicyScript)
import Plutus.V1.Ledger.Contexts (ScriptPurpose(..), scriptContextPurpose)
import PlutusTx qualified

{-# INLINEABLE fooPolicyValidator #-}
fooPolicyValidator :: BuiltinData -> BuiltinData -> ScriptContext -> Bool
fooPolicyValidator _ _ ctx =
  case scriptContextPurpose ctx of
    Minting _cSymbol ->
      traceIfTrue "Here" True
    _other ->
      traceIfTrue "There" True

fooPolicy :: MintingPolicy
fooPolicy =
  mkMintingPolicyScript $
    $$(PlutusTx.compile
      [||\y z -> checkBool 
        (fooPolicyValidator
          (BuiltinData (I 0))
          (BuiltinData (I 0)) 
          (unsafeFromBuiltinData z)
        )||])

{-# INLINEABLE checkBool #-}
checkBool :: Bool -> ()
checkBool True = ()
checkBool False = traceError "Check error"

How can it be possible to get an on-chain script execution error when all that my on-chain code does is look at the script context?

2
  • The import is wrong, you likely were using Hydra.Cardano.Api from hydra-cardano-api package. Cardano.Hydra.API does not exist to my knowledge. Commented Feb 1, 2023 at 7:28
  • Yup, that was a typo 👍 Commented Feb 1, 2023 at 8:21

1 Answer 1

2

The problem was that I was mixing on-chain code written in Plutus V1 with off-chain code that expects Plutus V2. In particular, the off-chain code utilities in the hydra-cardano-api library assume that all scripts are written in Plutus V2.

The strange error manifests because:

  • The Plutus V1 on-chain code compiles and serializes just fine.
  • The Plutus V2 off-chain code is happy to put the Plutus V1 compiled on-chain code into a transaction if you tag it as Plutus V2 code (i.e. fromPlutusScript @PlutusScriptV2)
  • When the Cardano node tries to validate your transaction, it fails at constructing the ScriptContext because of a mismatch between Plutus V1 and V2.
  • Unfortunately, when ScriptContext construction fails, the library is not able to give a more informative error than Caused by: (force headList []).

Solution: make sure that your on-chain code and off-chain code match on the Plutus version. For hydra-cardano-api, use Plutus V2 for all on-chain code.

3
  • Yes. The documentation of hydra-cardano-api was not explicit about this. We will add it to the package README and the module documentation of Hydra.Cardano.API. Commented Feb 1, 2023 at 7:27
  • Thanks! That would be helpful Commented Feb 1, 2023 at 8:22
  • Another hint to let you know you are mixing V1 and V2 code is looking at the serialised version of the on-chain code - the .plutus file. If the file has "type": "PlutusScriptV1", then the on-chain code complied as V1 code. Modify the imports.
    – Skelli
    Commented Feb 1, 2023 at 13:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.