IntersectMBO / cardano-node

The core component that is used to participate in a Cardano decentralised blockchain.
https://cardano.org
Apache License 2.0
3.06k stars 721 forks source link

[BUG] - Simple txSignedBy minting policy fails if tx-out count > 1 #3738

Closed grim-i-am closed 2 years ago

grim-i-am commented 2 years ago

External Plutus

Summary My simple parameterized minting policy script that checks for a single key fails unexpectedly when tx contains more than one tx-out. Succeeds otherwise.

A clear and specific description of what the bug is.

I have a simple parameterized minting policy:

  {-# INLINABLE mkPolicy #-}
  mkPolicy :: PubKeyHash -> BuiltinData -> ScriptContext -> Bool
  mkPolicy pkh _ ctx = traceIfFalse "Signature missing" $ txSignedBy (scriptContextTxInfo ctx) pkh

  policy :: PubKeyHash -> Scripts.MintingPolicy
  policy pkh = mkMintingPolicyScript $
      $$(PlutusTx.compile [|| Scripts.wrapMintingPolicy . mkPolicy ||])
      `PlutusTx.applyCode` (PlutusTx.liftCode pkh)

Minting transactions with a single tx-out succeed.

However, transactions with 2 (or more) tx-outs, whether to the same address or not, fail.

When I replace my script with the always_succeeds policy, the multiple tx-out transaction succeeds.

Steps to reproduce (I have provided all the required files to reproduce the test - refer to the file list below)

Create a simple parameterised minting policy check_sig.plutus, that checks for a single signature. (provided in zip file)

  1. submit a minting transaction for always_succeeds.plutus with 2 tx-outs. This succeeds proving that cardano supports such a txn.
  2. submit a minting transaction for check_sig.plutus with 1 tx-out. This succeeds proving that the script is valid.
  3. submit a minting transaction for check_sig.plutus with 2 tx-outs. This fails unexpectedly.

    Expected behavior As long as the transaction is balanced and signed with the expected keys, it should succeed, regardless of how many tx-outs it contains.

    System info (please complete the following information):

    • OS Name: Linux Mint
    • OS Version: 20.3
    • Node version: 1.34.1 - linux-x86_64 - ghc-8.10
    • CLI version: 1.34.1 - linux-x86_64 - ghc-8.10

    Screenshots and attachments The attached files.zip contains the following files:

    1. payment.addr - test address pre-funded with 3 utxos having same hash and indices 0 - 2 and value 4000000 lovelace
    2. payment.skey - used to sign all txs below
    3. payment.vkey
    4. always_succeeds.plutus
    5. check_sig.plutus - only succeeds if tx signed by payment.skey
    6. test_always_succeeds_2_tx_out.sign - tx spending 1st utxo with always_succeeds and 2 outputs - tx submits successfully
    7. test_check_sig_1_tx_out.sign - tx spending 2nd utxo with check_sig and 1 output - tx submits successfully
    8. test_check_sig_2_tx_out.sign - tx spending 3rd utxo with check_sig and 2 outputs - fails
    9. create_test_txs.sh - script to create all 3 test txns. (After submitting the provided txs, this can be used to recreate the test txs, after the wallet is funded again and the value of utxo_hash in the script is updated accordingly.)
    10. submit-test-txs.sh - script to submit the txns.
    11. MintTest.hs - the source file for my parameterised minting script

files.zip

Log file of my test showing first 2 txns succeeding and 3rd failing: test.log

Additional context

KtorZ commented 2 years ago

From your logs, it seems that it is a budget issue. The script fails to validate because it runs out of execution budget in the case of 2 outputs. This is because the script context is more expensive to build and grows with the size of the transaction... The more outputs, the more expensive it becomes.

Try increasing the allocated budget for your script policy.

grim-i-am commented 2 years ago

Thank you for your feedback. Regardless of how many times I added the reported budget deficit to the txn the failure never went away. It doesn't seem like its a budget issue.

Also, the additional expense to build the context would be the same for the AlwaysSucceeds transaction but that succeeds without issue.

And another thing: wouldn't the additional expense of the larger transaction and context setup be covered by the larger transaction fee, and not require an increase in the memory and cpu budget of a script? Otherwise one would need to calculate the memory and step cost for every transaction and not once at script compile time.

grim-i-am commented 2 years ago

I increased the budget by a factor of 10 and the txn went through. How can I accurately calculate the budget in the future?

It's frustrating. I've had the budget error be reported in the past when it was not a budget issue. This time I increased the budget a number of times, each time adding the amount that it was complaining about. So for example, if I started with a budget of say, 1000 and it failed with balance of -200, I would run again with a budget of 1200 (I'm just making up the numbers for the sake of the example). I'd submit again, and it would complain once more with -200. So I'd add 200 again, rinse and repeat. Having done that a few times, and having encountered the situation in the past where it was a misleading error message, I came to the conclusion that the error message was misleading once again.

KtorZ commented 2 years ago

The error reported in case the script runs out of budget is utterly bad. This should be (if not already) raised to the Plutus. I agree it's hard to debug as it usually just stops the evaluator right after running out of budget and simply report about the missing budget of the ongoing operation. Sadly, we can't expect the node / ledger to report on what the expected budget should be (as it would require to actually run the evaluation until the end!), but at the very least, I don't think the error should say anything about the amount of execution units that's missing because it is just misleading.

Regarding how to calculate the execution budget, this is something the cardano-cli should actually be doing for you if I recall correctly. But you may not be using the CLI at all. To do so programmatically, there's a good thread on the Plutus repository: https://github.com/input-output-hk/plutus-apps/issues/249

grim-i-am commented 2 years ago

Thanks for the link. I just read across it quickly but will look into it and its links more thoroughly after I post this. Although they seem to be talking the need for wallet implementations to be able to create balanced transactions. I only use the light wallets to retrieve the address. I perform all the utxo lookup, selection and transaction balancing on my back-end.

I have tried a number of different approaches to calculating a correct fee. cardano-cli's fee calculation for a plutus transaction is wrong. The node complains about insufficient fees, so submitting a transaction from the command line has to be done twice. Basically the node needs to be used to perform the calculate fee step.

Emurgo's serialisation lib doesn't even support plutus scripts so that's another headache to get around.

Calculating the memory and budget requirements of a script after compilation is meaningless since, as we see in this bug report the number of inputs / outputs affects that. Increasing the budget after node errors causes the fee to need to be increased too. In any case,

To come up with a generic fee calculation for my dApp I tried the approach of manually generating a "base fee" for my script transaction. I created a single-input, single-output transaction with cardano-cli and then used it to calculate the fee. I then used serialization-lib to calculate the fee_for_input and fee_for_output, which I subtracted from the cardano-cli fee.

The idea was to use this base fee as a starting point for the transactions that I create in the dapp. Then I would calculate the fee for inputs and outputs that my actual transaction would use, and add those to my base fee. However this fell apart for a number of reasons. Firstly, as I mentioned above, cardano-cli's calcualted fee is incorrect for my plutus script.

I tried re-implementing the fee estimation from https://input-output-hk.github.io/cardano-node/cardano-api/lib/src/Cardano.Api.Fees.html

I can't even calculate a correct fee estimation for my simple script. There goes my plan to create a generic platform for interacting with plutus scripts.

It seems like the next thing to try would be to build a haskell web-service that uses https://input-output-hk.github.io/cardano-node/cardano-api/lib/src/Cardano.Api.Fees.html#evaluateTransactionExecutionUnits to perform the fee calculation. However this would be yet another significant set-back.

KtorZ commented 2 years ago

Maybe have a look at: https://ogmios.dev/mini-protocols/local-tx-submission/#evaluatetx

grim-i-am commented 2 years ago

Yeah, that was also on my list. Thanks. Ogmios has the added benefit of being able to track when the tx actually gets processed.

Hey, I appreciate your time.