Cardano-client-lib fail to evaluate Ex units when send the locked fund

Hello!

I was failed to calculate Ex units when try to unlock fund. It throws below error:
evalResults = Result{successful=false, response='{"ScriptFailures":{}}', code=500, value=[]}

First I create a smart contract and build it to plutus.json.

use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{
  OutputReference, ScriptContext, Spend, Transaction, TransactionId,
}
use aiken/transaction/credential.{VerificationKey}

type Datum {
  data: Hash<Blake2b_224, VerificationKey>,
}

type Redeemer {
  message: ByteArray,
}

validator {
  fn validateTransaction(
    datum: Datum,
    redeemer: Redeemer,
    context: ScriptContext,
  ) {
    let signed = list.has(context.transaction.extra_signatories, datum.data)
    let validMessage = redeemer.message == "I am groot!"
    signed && validMessage
  }
}

Then, I lock some ADA with datum is payment credential hash.

private void lockAda(double ada) throws CborSerializationException, ApiException {
        String compileCode = "58ef010000323232323232323222232325333008323232533300b002100114a066e3cdd7180118040032450b4920616d2067726f6f7421003322323300100100322533301000114a026464a66601e66e3c00801452889980200200098098011bae30110013758601a601c601c601c601c601c601c601c601c600e6002600e0086eb8c004c01c0188c034004526136563253330083370e900000089919299980698078010a4c2c6eb8c034004c01801058c01800cc94ccc01ccdc3a400000226464a666018601c0042930b1bae300c0013005004163005003230053754002460066ea80055cd2ab9d5573caae7d5d0aba21";
        PlutusScript lockScript = getPlutusScriptFromCompiledCode(compileCode, PlutusVersion.v2);
        String scriptAddress = AddressProvider.getEntAddress(lockScript, Networks.preprod()).toBech32();
        Log.d("quan5.nguyen", "scriptAddress = " + scriptAddress);
        mAccount.getEnterpriseAddress().getPaymentCredentialHash().ifPresent(keyHash -> {
            mDatum = BytesPlutusData.of(keyHash);
            PlutusData datum = new ConstrPlutusData(0, ListPlutusData.of(mDatum));
            Output scriptReferenceOutput = Output.builder()
                    .address(scriptAddress)
                    .qty(ADAConversionUtil.adaToLovelace(ada))
                    .assetName(CardanoConstants.LOVELACE)
                    .scriptRef(lockScript)
                    .datum(datum)
                    .inlineDatum(true)
                    .build();
            TxBuilder scriptRefTxBuilder = scriptReferenceOutput.outputBuilder()
                    .buildInputs(InputBuilders.createFromSender(mSenderAddress, mSenderAddress))
                    .andThen(BalanceTxBuilders.balanceTx(mSenderAddress, 1));
            Transaction signedTx = TxBuilderContext.init(mUtxoSupplier, mProtocolParamsSupplier)
                    .buildAndSign(scriptRefTxBuilder, signerFrom(mAccount));
            Result<String> result = null;
            try {
                result = mBackendService.getTransactionService().submitTransaction(signedTx.serialize());
            } catch (ApiException | CborSerializationException e) {
                throw new RuntimeException(e);
            }
            Log.d("quan5.nguyen", "lock result = " + result);
            waitForTransactionHash(result);
        });
    }

After lock done, I continue to unlock ADA by tx hash but it fail when estimate Ex unit.

private void sendLockedAda() throws ApiException, CborSerializationException {
        Log.d("quan5.nguyen", "sendLockedAda: ");
        String compileCode = "58ef010000323232323232323222232325333008323232533300b002100114a066e3cdd7180118040032450b4920616d2067726f6f7421003322323300100100322533301000114a026464a66601e66e3c00801452889980200200098098011bae30110013758601a601c601c601c601c601c601c601c601c600e6002600e0086eb8c004c01c0188c034004526136563253330083370e900000089919299980698078010a4c2c6eb8c034004c01801058c01800cc94ccc01ccdc3a400000226464a666018601c0042930b1bae300c0013005004163005003230053754002460066ea80055cd2ab9d5573caae7d5d0aba21";
        PlutusScript lockScript = getPlutusScriptFromCompiledCode(compileCode, PlutusVersion.v2);
        mAccount.getEnterpriseAddress().getPaymentCredentialHash().ifPresent(keyHash -> {
                    mDatum = BytesPlutusData.of(keyHash);
        });
        PlutusData datumKeyHash = new ConstrPlutusData(0, ListPlutusData.of(mDatum));
        String scriptAddress = AddressProvider.getEntAddress(lockScript, Networks.preprod()).toBech32();
        Log.d("quan5.nguyen", "scriptAddress = " + scriptAddress);
        PlutusData datum = BytesPlutusData.of("I'm groot!");
        Utxo scriptUtxo = ScriptUtxoFinders.findFirstByDatumHashUsingDatum(mUtxoSupplier, scriptAddress, datumKeyHash).orElseThrow();
        BigInteger claimAmount = scriptUtxo.getAmount().stream().filter(amount -> CardanoConstants.LOVELACE.equals(amount.getUnit()))
                .findFirst()
                .orElseThrow()
                .getQuantity();
        UtxoSelectionStrategy utxoSelectionStrategy = new DefaultUtxoSelectionStrategyImpl(mUtxoSupplier);
        Set<Utxo> collateralUtxos = utxoSelectionStrategy
                .select(mSenderAddress, new Amount(CardanoConstants.LOVELACE, ADAConversionUtil.adaToLovelace(5)), Collections.emptySet());
        Output output = Output.builder()
                .address(mSenderAddress)
                .assetName(CardanoConstants.LOVELACE)
                .qty(claimAmount)
                .build();
        ScriptCallContext scriptCallContext = ScriptCallContext.builder()
                .script(lockScript)
                .exUnits(ExUnits.builder()
                                .mem(BigInteger.valueOf(0))
                                .steps(BigInteger.valueOf(0))
                                .build()
                )
                .redeemer(datum)
                .redeemerTag(RedeemerTag.Spend)
                .build();
        Utxo refScriptUtxo = Utxo.builder()
                .txHash("7f8512e9aa6977f036cac1299bc637c731fb6758dd8f80aa1d71671cfc197681")
                .outputIndex(0)
                .build();
        List<Utxo> utxoList = new ArrayList<>(collateralUtxos);
        List<Utxo> scriptList = new ArrayList<>();
        scriptList.add(refScriptUtxo);
        List<Utxo> refScriptUtxoList = new ArrayList<>();
        refScriptUtxoList.add(refScriptUtxo);
        TxBuilder contractTxBuilder = output.outputBuilder()
                .buildInputs(InputBuilders.createFromUtxos(scriptList))
                .andThen(InputBuilders.referenceInputsFromUtxos(refScriptUtxoList))
                .andThen(CollateralBuilders.collateralOutputs(mSenderAddress, utxoList))
                .andThen(ScriptCallContextProviders.createFromScriptCallContext(scriptCallContext))
                .andThen((context, txn) -> {
                    ExUnits estimatedExUnits;
                    try {
                        estimatedExUnits = evaluateExUnits(txn);
                        Log.d("quan5.nguyen", "estimatedExUnits = " + estimatedExUnits);
                        txn.getWitnessSet().getRedeemers().get(0).setExUnits(estimatedExUnits);
                    } catch (Exception e) {
                        throw new ApiRuntimeException("Script cost evaluation failed", e);
                    }
                    //Remove script from witnessset as we are using reference script input
                    txn.getWitnessSet().getPlutusV2Scripts().clear();
                })
                .andThen(BalanceTxBuilders.balanceTx(mSenderAddress, 2));
        TxBuilderContext txBuilderContext = TxBuilderContext.init(mUtxoSupplier, mProtocolParamsSupplier);
        TxSigner signer = signerFrom(mAccount);
        Transaction signedTx = txBuilderContext.buildAndSign(contractTxBuilder, signer);
        Result<String> result = mBackendService.getTransactionService().submitTransaction(signedTx.serialize());
        Log.d("quan5.nguyen", "sendLockedAda: " + result);
    }

I don’t know what wrong. Could you help me?
Thank you.

@monoQ,

It seems you have not set “requiredSigners” field in the transaction.

Here’s a working example of the similar contract using QuickTX api of Cardano Client Lib.

Thanks

1 Like