From 8f9dc9136a913cb44fe3e8d0f89c08419161262d Mon Sep 17 00:00:00 2001 From: Alexey Kuleshevich Date: Wed, 14 Aug 2024 16:11:13 -0600 Subject: [PATCH 1/3] Expand haddock of `txNonDistinctRefScriptsSize`. Fix #4539 --- eras/conway/impl/src/Cardano/Ledger/Conway/UTxO.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eras/conway/impl/src/Cardano/Ledger/Conway/UTxO.hs b/eras/conway/impl/src/Cardano/Ledger/Conway/UTxO.hs index 072bd6dd9c1..5f9a14ef8d0 100644 --- a/eras/conway/impl/src/Cardano/Ledger/Conway/UTxO.hs +++ b/eras/conway/impl/src/Cardano/Ledger/Conway/UTxO.hs @@ -149,6 +149,9 @@ getConwayMinFeeTxUtxo pparams tx utxo = -- | Calculate the total size of reference scripts used by the transactions. Duplicate -- scripts will be counted as many times as they occur, since there is never a reason to -- include an input with the same reference script. +-- +-- Any input that appears in both regular inputs and reference inputs of a transaction is +-- only used once in this computation. txNonDistinctRefScriptsSize :: (EraTx era, BabbageEraTxBody era) => UTxO era -> Tx era -> Int txNonDistinctRefScriptsSize utxo tx = getSum $ foldMap (Sum . originalBytesSize . snd) refScripts where From e474358542f73689bbe6399b8e9729d6e9889c9a Mon Sep 17 00:00:00 2001 From: Alexey Kuleshevich Date: Wed, 14 Aug 2024 16:19:39 -0600 Subject: [PATCH 2/3] Add ADR describing changes to the fee calculation due to ref scripts --- .../2024-08-14_009-refscripts-fee-change.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/adr/2024-08-14_009-refscripts-fee-change.md diff --git a/docs/adr/2024-08-14_009-refscripts-fee-change.md b/docs/adr/2024-08-14_009-refscripts-fee-change.md new file mode 100644 index 00000000000..2c6fbf5b961 --- /dev/null +++ b/docs/adr/2024-08-14_009-refscripts-fee-change.md @@ -0,0 +1,79 @@ +--- +slug: 9 +title: | + 9. Changes to the fee calculation due to Reference Scripts +authors: [Alexey Kuleshevich] +tags: [Accepted] +--- + +## Status + +Accepted + +## Context + +It was identified a while ago that there is an overhead associated with deserializing scripts, so much so that it would be possible to create a very large script that was fairly expensive to deserialize, but very cheap to execute. This opened up an attack vector when such a Plutus script would be used as a reference script. This problem was exacerbated by the fact that there was no real limit on the total size of reference scripts that could be used in a transaction, thus being limited only by the size of the transaction itself. Therefore this opened up Cardano to a DDoS attack where an attacker could submit many such transactions that would cost very little, but would be expensive for a `cardano-node` to validate. + +In order to prevent such an attack a `"minFeeRefScriptCostPerByte"` protocol parameter was introduced in the Conway era. The idea was fairly simple: we would calculate the total size of reference scripts used by a transaction and multiply it by the value specified by this protocol parameter. Result would be added to the transaction fee. However, in order for this approach to be a definite deterrent of such attacks this parameter would have to be set to a fairly high value. Community was not very keen on having this value set to a high enough value, since that could make reference scripts almost as expensive to use as regular scripts, thus significantly reducing their usability. Taking this fact into consideration, a decision was made to set this parameter to a fairly moderate value to at least deter potential attacks like that by at least making it pretty expensive, instead of prohibitively expensive. This would allow us to fine tune the parameter or potentially change the pricing algorithm at a later point after we were already in the Conway era. This reasoning was coming from the fact that at that point we would be able to reveal to a wider audience the true nature of this issue. + +Unfortunately things did not go exactly as planned, because on [June 25th 2024 an attack like this actually took place](https://cardanospot.io/news/ddos-attack-on-the-cardano-blockchain-mRIKAzZTNnzq5NGd). This attack forced us to make a quick decision on implementing a definite prevention of such attacks, while trying to not have a drastic impact on the common use case that DApp developers rely on so much. + +## Decision + +Linear pricing was either too expensive when the multiplier was set too high or was an inadequate deterrent when the multiplier was set too low. Therefore, we needed to implement a pricing mechanism that would be very expensive for usage with large quantities of large plutus scripts, while keeping the pricing reasonably low for the most common use case of a total size of reference scripts of at most 25KiB per transaction. One of the constraints we had to operate under was inability to add any new protocol parameters, since that was a bit too late in the release cycle of the Conway era. In other words we had to hard code some values, which will be turned into proper protocol parameters in the next era. + +### Reference scripts total size + +When compared to fee calculation of the Babbage era we only add this extra amount that depends on the reference scripts. At a high level it works in this way: + +1. We combine all of the regular inputs and reference inputs from the transaction +2. Lookup the outputs from the UTxO map that correspond to those inputs +3. Calculate the size of every reference script in all of those outputs. Outputs that don't have a reference script do not affect this calculation +4. Sum all of the computed sizes. + +Here are some important properties of this calculation: + +* Any input that appears in both regular inputs and reference inputs of a transaction is only used once in this computation. +* Duplicate reference scripts that appear in any of the outputs are not removed for the purpose of the fee calculation and will contribute their size as many times as they appear. +* All Plutus scripts contribute to this calculation, regardless if they are being used or not within the transaction. +* Native scripts that are used as reference scripts also contribute their size to this calculation. + +### Formula for the cost due to reference script usage + +Once we have the total size of reference scripts used in a transaction we can proceed to computing the amount of Lovelace that will be added to the fee of a transaction. Instead of using the same linear cost for the whole size we split this total size into `25KiB` chunks and each subsequent chunk will get a linear pricing cost that is higher than the previous one by a multiplier of `1.2`. In other words pricing for the first `25KiB` will be as with the initial approach, just the value of `minFeeRefScriptCostPerByte`. The following `25KiB` will have the price of `minFeeRefScriptCostPerByte * multiplier` and so on. These are the two new hardcoded values in the fee computation: + +* Size increment: `25KiB` (or 25,600 bytes) +* Multiplier: `1.2` +* minFeeRefScriptCostPerByte: `15` (supplied in Conway genesis) + +This tiered pricing for reference scripts is defined by this recursive function: + +```haskell +tierRefScriptFee :: Integer -> Integer +tierRefScriptFee = go 0 minFeeRefScriptCostPerByte + where + go acc curTierPrice n + | n < sizeIncrement = + floor (acc + (n % 1) * curTierPrice) + | otherwise = + let acc' = acc + curTierPrice * (sizeIncrement % 1) + in go acc' (multiplier * curTierPrice) (n - sizeIncrement) + sizeIncrement = 25600 + multiplier = 1.2 + minFeeRefScriptCostPerByte = 15 +``` + +The result of applying this function to the total reference script size used by a transaction will be the increase to the fee amount when compared to the fee calculation that was done in the Babbage era. + +### Reference script size limit + +In order to further increase the resilience to this sort of attacks we added hard limits on the total size of reference scripts that can be used per transaction and per block. + +Hard caps that are currently hard coded, but will be turned into actual protocol parameters in the next era after Conway: + +* Limit per transaction: `200KiB` (or `204800` bytes) +* Limit per block: `1MiB` (or `1048576` bytes) + +## Consequences + +Unlike previous eras, inclusion of any inputs in the transaction containing reference scripts is no longer free. Overhead of using reference scripts was not properly accounted for when this feature was introduced in the Babbage era, which is now fixed in the Conwya era. From 56d40d3e32180010a127ea8b4aafa866eef73aef Mon Sep 17 00:00:00 2001 From: Alexey Kuleshevich Date: Tue, 20 Aug 2024 15:06:45 -0600 Subject: [PATCH 3/3] Add a plot with reference script pricing --- .../adr/2024-08-14_009-refscripts-fee-change.md | 5 ++++- ...4_009-refscripts-fee-change.pricing-plot.png | Bin 0 -> 14173 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/adr/2024-08-14_009-refscripts-fee-change.pricing-plot.png diff --git a/docs/adr/2024-08-14_009-refscripts-fee-change.md b/docs/adr/2024-08-14_009-refscripts-fee-change.md index 2c6fbf5b961..b7589a94fad 100644 --- a/docs/adr/2024-08-14_009-refscripts-fee-change.md +++ b/docs/adr/2024-08-14_009-refscripts-fee-change.md @@ -63,7 +63,10 @@ tierRefScriptFee = go 0 minFeeRefScriptCostPerByte minFeeRefScriptCostPerByte = 15 ``` -The result of applying this function to the total reference script size used by a transaction will be the increase to the fee amount when compared to the fee calculation that was done in the Babbage era. +The result of applying this function to the total reference script size used by a transaction will be the increase to the fee amount when compared to the fee calculation that was done in the Babbage era. Below is the plot with the cost in ADA for reference scripts used in a transaction per kilobyte for current parameters: + +![Plot with the cost in ADA for reference scripts used in a transaction per kilobyte](2024-08-14_009-refscripts-fee-change.pricing-plot.png "RefScript Pricing") + ### Reference script size limit diff --git a/docs/adr/2024-08-14_009-refscripts-fee-change.pricing-plot.png b/docs/adr/2024-08-14_009-refscripts-fee-change.pricing-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2ac1395ac0cf306ea40f1bc235a06766354c5a GIT binary patch literal 14173 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 PO5| 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 ThbiA(Ij{#)h! literal 0 HcmV?d00001