cardano-serialization-lib icon indicating copy to clipboard operation
cardano-serialization-lib copied to clipboard

Calculating Cost for Datum Values

Open bakon11 opened this issue 2 years ago • 10 comments

I'm an including datum values in my cbor like so witnesses.set_plutus_data(datumFields);

I am also successfully attaching the Datum hash to my output to be set on the UTXO.

However that doesn't get included in add_change_if_needed and when I try txBuilder.set_plutus_data(datumFields) I get function doesn't exist.

FYI: witnesses = CardanoWasm.TransactionWitnessSet.new()

This is the example I followed and got to work except for calculating the fee properly, I'm always 440 lovelaces short. https://cardano.stackexchange.com/questions/8224/attaching-datum-value-with-csl-10-x

I am also making sure that even the

witnesses.set_plutus_data(datumFields); is done before calling await txBuilder.add_change_if_needed( CardanoWasm.Address.from_bech32(changeAddress) );

Thank you.

bakon11 avatar May 26 '22 14:05 bakon11

I am having a similar situation, and not even ogmios/node is returning the right required fee. Any updates?

zxpectre avatar Jun 21 '22 23:06 zxpectre

At this point I'm going to start working on using version 11, which is better suited for inline datums and datums in general.

bakon11 avatar Jun 22 '22 01:06 bakon11

Hi! I'm thinking also to use v11 but it requires me to update the smart contracts to plutus v2. does someone figure out how to include the datum data for the fee?

jmagan avatar Jul 26 '22 08:07 jmagan

I still haven't figured that out as well.

But I'm pretty sure you can use version 11 with V1 plutus.

Will be trying that out this week actually.

bakon11 avatar Jul 26 '22 11:07 bakon11

Yes!, The CSL 11 works with plutus V1, the problem is that you can't use txs with inline datums in plutus V1 scripts. If you do so, you get a error message. You can find this in the CIP-32 documentation: https://cips.cardano.org/cips/cip32/

The interface for old versions of the language will not be changed. Scripts with old versions cannot be spent in transactions that include inline datums, attempting to do so will be a phase 1 transaction validation failure.

In summary, don't use inline datums with V1 script otherwise your funds will be stuck forever.

That said, I got a quasi elegant solution which I'm going to share. I hope it helps.

There are two problems; first, the extra data isn't taken into account in the fee calculation and the second one is that you can't calculate automatically the script data hash with the tx builder if you are not calling a script redeemer.

For the fee calculation, I create a dummy WitnessSet and put the datums inside. When the witness set is ready, I use it to calculate the final length and I add this "extra fee" to the transaction builder configuration.

const plutusList = CSL.PlutusList.new()
selection.outputs.forEach(output => {
    if (output.plutusData) {
        plutusList.add(output.plutusData)
    }
})

const witness = CSL.TransactionWitnessSet.new()
witness.set_vkeys(CSL.Vkeywitnesses.new())
witness.set_plutus_data(plutusList)

const txBuilder = CSL.TransactionBuilder.new(CSL.TransactionBuilderConfigBuilder.new()
    .coins_per_utxo_word(CSL.BigNum.from_str(protocolParameters.coinsPerUtxoWord.toString()))
    .fee_algo(CSL.LinearFee.new(CSL.BigNum.from_str(protocolParameters.minFeeCoefficient.toString()),
        CSL.BigNum.from_str((protocolParameters.minFeeConstant + protocolParameters.minFeeCoefficient * witness.to_bytes().length).toString())))
    .key_deposit(CSL.BigNum.from_str(protocolParameters.stakeKeyDeposit.toString()))
    .max_tx_size(protocolParameters.maxTxSize)
    .max_value_size(protocolParameters.maxValueSize)
    .pool_deposit(CSL.BigNum.from_str(protocolParameters.poolDeposit.toString()))
    .build())

In the fee_algo method, I multiply wintess.to_bytes().length by minFeeCoefficient to add the extra fee necessary. The good part of this solution is that you can continue using the transaction builder as before.

The final step is to add the script data hash at the end just before calling buildTx(). The challenge is that you can't use the method calc_script_data_hash, so you have to calculate and set it. In my case, I use this lines:

if (plutusList.len() > 0 ) {
    const scriptDataHash = CSL.hash_script_data(CSL.Redeemers.new(), CSL.TxBuilderConstants.plutus_vasil_cost_models(), plutusList)
    txBuilder.set_script_data_hash(scriptDataHash)
}

txBuilder.add_change_if_needed(CSL.Address.from_bech32(address))

Remember to set the script data hash before calling add_change_if_needed, otherwise the hash will not be included in the fee.

BR Juan

jmagan avatar Jul 26 '22 15:07 jmagan

oh wow this is brilliant.

I was able to figure out how to add the datums using the witness sets like you did, just wasn't smart enough to figure out a tricky way to calculate the fee hehe.

Thank you for this.

And does csl11 not give you an option to create a none inline datum?

I was thinking of switching over from 10.2 to 11 today and any extra info would help hehe.

Again thank you.

bakon11 avatar Jul 26 '22 15:07 bakon11

Hi! Back in the version 10.1.0 there has been added a proper support for plutus script inputs, with tx-builder functions like .add_plutus_script_input that accept and instance of PlutusWitness which includes a script, a datum, and a redeemer. Check the release description here: https://github.com/Emurgo/cardano-serialization-lib/releases/tag/10.1.0

It is also recommended to update to the latest available version, because in 10.2.0 the inputs API changes a bit and there's a new type TxInputsBuilder available, to handle collaterals, check details in the release notes here: https://github.com/Emurgo/cardano-serialization-lib/releases/tag/10.2.0. The old functions are still available on the tx-builder itself, but they are deprecated.

For the script data hash calculation you can use function txBuilder.set_script_data_hash or even better txBuilder.calc_script_data_hash, as described in the "Script data hash" section here: https://github.com/Emurgo/cardano-serialization-lib/releases/tag/10.1.0

This function will calculate the data hash automatically from all the plutus inputs you have added to the builder, but note that you must call that function at the end, after all the inputs been already added, ideally right before building the transaction.

If you use function .build_tx() instead of deprecated .build() - it will return you a full Transaction instance, and not just the TransactionBody. The main difference is that the Transaction will already contain a WitnessSet which includes all the scripts, datums, and redeemers that you have added with your plutus inputs. So you only need to add vkey witnesses with signatures to that witness set and that's it.


Also note: there's a new major version release 11.0.0 coming up right around today, which upgrades everything for the new babbage (vasil) hard-fork. If you want to use public Cardano testnet, which already were hard-forked, you have to use the latest available release-candidate, see the details here: https://github.com/Emurgo/cardano-serialization-lib/pull/478

Note that the latest available version is called 11.0.0-rc.9. Functionally it follows all the same details as described in the PR, just includes few small additional tests and fixes.

If you will use the recommended function txBuilder.calc_script_hash_data you can either provide your own instance of the cost-models, or you can use available module TxBuilderConstants. Note that in that new version 11.* for now you must be using TxBuilderConstants.default_vasil_cost_models() (and for this you must be using the latest -rc.9 or higher version, as the cost models been updated there)

vsubhuman avatar Jul 26 '22 15:07 vsubhuman

Hi! I'm thinking also to use v11 but it requires me to update the smart contracts to plutus v2. does someone figure out how to include the datum data for the fee?

Hi, @jmagan ! Switching to lib version 11.* does not require you to change your contracts in any way, you can still use PlutusV1 in the same exact way as you used them before, the changes are mostly backward compatible. For example we are upgrading our own other products to the new version 11 at the moment and we are not changing a single bit in how we used Plutus scripts and Plutus inputs before.

PlutusV2 is an optional new version that can be used if you need the new features, introduced there.

vsubhuman avatar Jul 26 '22 15:07 vsubhuman

Hi!, Actually I'm using the version 11.0.0-rc6. This solution is not intended to add data in the input script, it's for the outputs to the scripts. I know version 11, it's excelent including the witness in the scripts inputs and calculating fees. The solution that I gave is for including datum data when you send tokens to a script and you want to store the datum data on-chain. It's not the same case.

And yes, the version 11 works with Plutus v1. The problem isn't the version of the library. The problem is that plutus script v1 can't consume transactions with inline datums. So if you send the datum as inline datum to a v1 script, you won't able to spend this transaction at the script. And if you write your script in plutus v2, the script address will be changed. Therefore your tokens will be locked.

EDITED: Sorry, maybe my english isn't enough to explain such complex topic. :(

jmagan avatar Jul 26 '22 16:07 jmagan

And yes, the version 11 works with Plutus v1. The problem isn't the version of the library. The problem is that plutus script v1 can't consume transactions with inline datums. So if you send the datum as inline datum to a v1 script, you won't able to spend this transaction at the script. And if you write your script in plutus v2, the script address will be changed. Therefore your tokens will be locked.

That's a good point!

vsubhuman avatar Jul 26 '22 17:07 vsubhuman