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. Feb 1, 2023 at 7:28
  • Yup, that was a typo 👍 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. Feb 1, 2023 at 7:27
  • Thanks! That would be helpful 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
    Feb 1, 2023 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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