Skip to content

Commit

Permalink
Implement new fee structure for reference scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
klntsky committed Aug 29, 2024
1 parent 8225bee commit 35502bd
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 63 deletions.
46 changes: 23 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"license": "MIT",
"dependencies": {
"@mlabs-haskell/cardano-message-signing": "^1.0.1",
"@mlabs-haskell/cardano-serialization-lib-gc": "12.0.0-alpha.31",
"@mlabs-haskell/cardano-serialization-lib-gc": "12.0.0",
"@mlabs-haskell/json-bigint": "2.0.0",
"@mlabs-haskell/uplc-apply-args": "1.0.29-alpha",
"@noble/secp256k1": "^1.7.0",
Expand Down
4 changes: 2 additions & 2 deletions packages.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ let additions =
]
, repo =
"https://github.com/mlabs-haskell/purescript-cardano-serialization-lib"
, version = "v1.0.0"
, version = "0062d27a87b2d27ddd87f2bf4aaf34e6413e5e7c"
}
, cardano-plutus-data-schema =
{ dependencies = [ "prelude" ]
Expand Down Expand Up @@ -372,7 +372,7 @@ let additions =
, "unsafe-coerce"
]
, repo = "https://github.com/mlabs-haskell/purescript-cardano-types"
, version = "v2.0.1"
, version = "8f9e5d0e0a465990024fac7c8e8db36fcd56ab8c"
}
, cardano-message-signing =
{ dependencies =
Expand Down
12 changes: 6 additions & 6 deletions spago-packages.nix

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Internal/BalanceTx/Error.purs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Ctl.Internal.BalanceTx.Error
, UtxoLookupFailedFor
, UtxoMinAdaValueCalculationFailed
, NumericOverflowError
, CouldNotComputeRefScriptsFee
)
, Expected(Expected)
, printTxEvaluationFailure
Expand Down Expand Up @@ -92,6 +93,7 @@ data BalanceTxError
| UtxoLookupFailedFor TransactionInput UtxoMap
| UtxoMinAdaValueCalculationFailed
| NumericOverflowError (Maybe Val)
| CouldNotComputeRefScriptsFee TransactionInput

derive instance Generic BalanceTxError _

Expand Down Expand Up @@ -147,6 +149,9 @@ explainBalanceTxError = case _ of
NumericOverflowError mbVal ->
"Could not compute output value due to numeric overflow. Decrease the quantity of assets. "
<> fold (prettyVal "Value:" <$> mbVal)
CouldNotComputeRefScriptsFee input ->
"Could not compute reference script size fees introduced in Conway. Ensure that all reference scripts are present in a UtxoMap provided to the balancer. "
<> pprintTagSet "Missing input: " (pprintTransactionInput input)
where
prettyVal :: String -> Val -> String
prettyVal str = pprintVal >>> pprintTagSet str
Expand Down
44 changes: 38 additions & 6 deletions src/Internal/BalanceTx/ExUnitsAndMinFee.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Ctl.Internal.BalanceTx.ExUnitsAndMinFee

import Prelude

import Cardano.AsCbor (encodeCbor)
import Cardano.Types
( Coin
, CostModel
Expand All @@ -19,7 +20,9 @@ import Cardano.Types
, TransactionWitnessSet
, UtxoMap
, _body
, _inputs
, _isValid
, _referenceInputs
, _witnessSet
)
import Cardano.Types.BigNum as BigNum
Expand All @@ -30,7 +33,11 @@ import Control.Monad.Error.Class (throwError)
import Control.Monad.Except.Trans (except)
import Ctl.Internal.BalanceTx.Constraints (_additionalUtxos, _collateralUtxos) as Constraints
import Ctl.Internal.BalanceTx.Error
( BalanceTxError(UtxoLookupFailedFor, ExUnitsEvaluationFailed)
( BalanceTxError
( UtxoLookupFailedFor
, ExUnitsEvaluationFailed
, CouldNotComputeRefScriptsFee
)
)
import Ctl.Internal.BalanceTx.Types
( BalanceTxM
Expand All @@ -40,7 +47,7 @@ import Ctl.Internal.BalanceTx.Types
)
import Ctl.Internal.Contract.MinFee (calculateMinFee) as Contract.MinFee
import Ctl.Internal.Contract.Monad (getQueryHandle)
import Ctl.Internal.Helpers (unsafeFromJust)
import Ctl.Internal.Helpers (liftEither, unsafeFromJust)
import Ctl.Internal.QueryM.Ogmios
( AdditionalUtxoSet
, TxEvaluationFailure(AdditionalUtxoOverlap)
Expand All @@ -54,18 +61,26 @@ import Ctl.Internal.TxOutput
)
import Data.Array (catMaybes)
import Data.Array (fromFoldable, notElem) as Array
import Data.Bifunctor (bimap)
import Data.Bifunctor (bimap, lmap)
import Data.ByteArray as ByteArray
import Data.Either (Either(Left, Right), note)
import Data.Foldable (foldMap)
import Data.Lens ((.~))
import Data.Lens.Getter ((^.))
import Data.Map (Map)
import Data.Map (empty, filterKeys, fromFoldable, lookup, toUnfoldable, union) as Map
import Data.Maybe (Maybe(Just, Nothing), fromMaybe)
import Data.Map
( empty
, filterKeys
, fromFoldable
, lookup
, toUnfoldable
, union
) as Map
import Data.Maybe (Maybe(Just, Nothing), fromMaybe, maybe)
import Data.Newtype (unwrap, wrap)
import Data.Set (Set)
import Data.Set as Set
import Data.Traversable (for)
import Data.Traversable (for, sum)
import Data.Tuple (snd)
import Data.Tuple.Nested (type (/\), (/\))
import Data.UInt as UInt
Expand Down Expand Up @@ -127,10 +142,27 @@ evalExUnitsAndMinFee transaction allUtxos = do
additionalUtxos <- asksConstraints Constraints._additionalUtxos
collateralUtxos <- fromMaybe Map.empty
<$> asksConstraints Constraints._collateralUtxos
refScriptsTotalSize <- liftEither $ lmap CouldNotComputeRefScriptsFee $
calculateRefScriptsTotalSize finalizedTx allUtxos
minFee <- liftContract $ Contract.MinFee.calculateMinFee finalizedTx
(Map.union additionalUtxos collateralUtxos)
(UInt.fromInt refScriptsTotalSize)
pure $ txWithExUnits /\ minFee

calculateRefScriptsTotalSize
:: Transaction -> UtxoMap -> Either TransactionInput Int
calculateRefScriptsTotalSize tx utxoMap = do
let
refInputs = tx ^. _body <<< _referenceInputs
inputs = tx ^. _body <<< _inputs
allInputs = refInputs <> inputs
outputs <- for allInputs \input ->
note input $ Map.lookup input utxoMap
let
refScriptSizes = outputs <#> \(TransactionOutput { scriptRef }) ->
maybe zero (ByteArray.byteLength <<< unwrap <<< encodeCbor) scriptRef
pure $ sum refScriptSizes

-- | Attaches datums and redeemers, sets the script integrity hash,
-- | for use after reindexing.
finalizeTransaction
Expand Down
9 changes: 5 additions & 4 deletions src/Internal/Contract/MinFee.purs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import Ctl.Internal.Contract (getProtocolParameters)
import Ctl.Internal.Contract.Monad (Contract, getQueryHandle)
import Ctl.Internal.Contract.Wallet (getWalletAddresses)
import Ctl.Internal.Helpers (liftM, liftedM)
import Ctl.Internal.Serialization.MinFee (calculateMinFeeCsl)
import Ctl.Internal.MinFee (calculateMinFeeCsl)
import Data.Array (fromFoldable, mapMaybe)
import Data.Array as Array
import Data.Either (hush)
Expand All @@ -63,15 +63,16 @@ import Data.Set
, union
) as Set
import Data.Traversable (for)
import Data.UInt (UInt)
import Effect.Aff (error)
import Effect.Aff.Class (liftAff)

-- | Calculate the minimum transaction fee.
calculateMinFee :: Transaction -> UtxoMap -> Contract Coin
calculateMinFee tx additionalUtxos = do
calculateMinFee :: Transaction -> UtxoMap -> UInt -> Contract Coin
calculateMinFee tx additionalUtxos refScriptsSize = do
selfSigners <- getSelfSigners tx additionalUtxos
pparams <- getProtocolParameters
calculateMinFeeCsl pparams selfSigners tx
calculateMinFeeCsl pparams selfSigners tx refScriptsSize

-- | This function estimates the set of keys that must be used
-- | for signing to make the transaction valid for the network.
Expand Down
47 changes: 34 additions & 13 deletions src/Internal/Serialization/MinFee.purs → src/Internal/MinFee.purs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
-- | `min_fee` calculation using CSL.
module Ctl.Internal.Serialization.MinFee (calculateMinFeeCsl) where
module Ctl.Internal.MinFee (calculateMinFeeCsl) where

import Prelude

import Cardano.Serialization.Lib (linearFee_new, minFee, minScriptFee)
import Cardano.Serialization.Lib
( linearFee_new
, minFee
, minRefScriptFee
, minScriptFee
)
import Cardano.Types
( Coin
, Ed25519KeyHash
Expand All @@ -19,23 +24,27 @@ import Cardano.Types.ExUnitPrices as ExUnitPrices
import Cardano.Types.NativeScript (NativeScript(ScriptAll))
import Cardano.Types.PublicKey as PublicKey
import Cardano.Types.Transaction as Transaction
import Cardano.Types.UnitInterval as UnitInterval
import Contract.Prim.ByteArray (hexToRawBytes)
import Control.Monad.Error.Class (class MonadThrow)
import Ctl.Internal.Helpers (unsafeFromJust)
import Ctl.Internal.NativeScripts (getMaximumSigners)
import Ctl.Internal.Types.ProtocolParameters
( ProtocolParameters(ProtocolParameters)
)
import Ctl.Internal.Types.Rational as Rational
import Data.Array (range, replicate) as Array
import Data.Foldable (fold)
import Data.Int (hexadecimal) as Radix
import Data.Int (toStringAs) as Int
import Data.Int (toNumber, toStringAs) as Int
import Data.Lens ((.~))
import Data.Maybe (fromJust)
import Data.Newtype (unwrap, wrap)
import Data.Set (Set)
import Data.Set (fromFoldable, isEmpty, size) as Set
import Data.String (length) as String
import Data.UInt (UInt)
import Data.UInt as UInt
import Effect.Class (class MonadEffect)
import Effect.Exception (Error)
import Partial.Unsafe (unsafePartial)
Expand All @@ -47,22 +56,34 @@ calculateMinFeeCsl
=> ProtocolParameters
-> Set Ed25519KeyHash
-> Transaction
-> UInt
-> m Coin
calculateMinFeeCsl (ProtocolParameters pparams) selfSigners txNoSigs = do
let tx = addFakeSignatures selfSigners txNoSigs
let cslTx = Transaction.toCsl tx
calculateMinFeeCsl
(ProtocolParameters pparams)
selfSigners
txNoSigs
refScriptsSize = do
let
tx = addFakeSignatures selfSigners txNoSigs
cslTx = Transaction.toCsl tx
cslLinearFee = linearFee_new
(unwrap $ BigNum.fromUInt pparams.txFeePerByte)
(unwrap $ unwrap pparams.txFeeFixed)

let fee = minFee cslTx cslLinearFee
let exUnitPrices = pparams.prices
let exUnitPricesCsl = ExUnitPrices.toCsl exUnitPrices
let scriptFee = minScriptFee cslTx exUnitPricesCsl
fee = minFee cslTx cslLinearFee
exUnitPrices = pparams.prices
exUnitPricesCsl = ExUnitPrices.toCsl exUnitPrices
scriptFee = minScriptFee cslTx exUnitPricesCsl
refScriptFee =
minRefScriptFee
(Int.toNumber $ UInt.toInt refScriptsSize)
( UnitInterval.toCsl
$ unsafeFromJust "calculateMinFeeCsl: refScriptCoinsPerByte"
$ Rational.toUnitInterval pparams.refScriptCoinsPerByte
)
-- Ignore the overflow here: fees are much lower
pure $ wrap $ unsafeFromJust "calculateMinFeeCsl" $ BigNum.add (wrap fee)
(wrap scriptFee)
pure $ wrap $ unsafeFromJust "calculateMinFeeCsl" $
BigNum.add (wrap fee)
(wrap scriptFee) >>= BigNum.add (wrap refScriptFee)

-- | Adds fake signatures for each expected signature of a transaction.
addFakeSignatures :: Set Ed25519KeyHash -> Transaction -> Transaction
Expand Down
Loading

0 comments on commit 35502bd

Please sign in to comment.