IntersectMBO / plutus

The Plutus language implementation and tools

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PParamsUpdate in PlutusV3 TxInfo ScriptContext

lehins opened this issue · comments

Describe the feature you'd like

Current implementation of ParameterChange governance action does not include any PParamsUpdates, because we have not converged on concrete solution on how we want to translate PParamsUpdates.
https://github.com/input-output-hk/plutus/blob/78b710d275ec88ec237aba848836883fc5e94ce4/plutus-ledger-api/src/PlutusLedgerApi/V3/Contexts.hs#L300-L302

For reference, here is a full list of ConwayPParamsUpdate:

data ConwayPParamsUpdate = ConwayPParamsUpdate
  { minFeeA :: Maybe Coin
  , minFeeB :: Maybe Coin
  , maxBBSize :: Maybe Natural
  , maxTxSize :: Maybe Natural
  , maxBHSize :: Maybe Natural
  , keyDeposit :: Maybe Coin
  , poolDeposit :: Maybe Coin
  , eMax :: Maybe EpochNo
  , nOpt :: Maybe Natural
  , a0 :: Maybe NonNegativeInterval
  , rho :: Maybe UnitInterval
  , tau :: Maybe UnitInterval
  , minPoolCost :: Maybe Coin
  , coinsPerUTxOByte :: Maybe CoinPerByte
  , costModels :: Maybe CostModels
  , prices :: Maybe Prices
  , maxTxExUnits :: Maybe OrdExUnits
  , maxBlockExUnits :: Maybe OrdExUnits
  , maxValSize :: Maybe Natural
  , collateralPercentage :: Maybe Natural
  , maxCollateralInputs :: Maybe Natural
  , poolVotingThresholds :: Maybe PoolVotingThresholds
  , dRepVotingThresholds :: Maybe DRepVotingThresholds
  , minCommitteeSize :: Maybe Natural
  , committeeTermLimit :: Maybe Natural
  , govActionExpiration :: Maybe Natural
  , govActionDeposit :: Maybe Coin
  , dRepDeposit :: Maybe Coin
  , dRepActivity :: Maybe EpochNo
  }

data PoolVotingThresholds = PoolVotingThresholds
  { motionNoConfidence :: UnitInterval
  , committeeNormal :: UnitInterval
  , committeeNoConfidence :: UnitInterval
  , hardForkInitiation :: UnitInterval
  }
data DRepVotingThresholds = DRepVotingThresholds
  { motionNoConfidence :: UnitInterval
  , committeeNormal :: UnitInterval
  , committeeNoConfidence :: UnitInterval
  , updateToConstitution :: UnitInterval
  , hardForkInitiation :: UnitInterval
  , networkGroup :: UnitInterval
  , economicGroup :: UnitInterval
  , technicalGroup :: UnitInterval
  , govGroup :: UnitInterval
  , treasuryWithdrawal :: UnitInterval
  }

I can see two potential solutions:

  1. Use poorly typed JSON like approach, where we have a PPValue like type and we represent PParamsUpdate as a Map String PPValue, where PPValue would have to enumerate all possible types for each PParam, eg. Integer, Rational, EpochNo, [PPValue], Map String PPValue, etc.

Downside of this approach are:

  • dynamic typing, thus less safety in code that will be written by Plutus users
  • performance. Map lookups aren't exactly cheap in Plutus. Plus the translation costs

Upside is

  • forward compatibility
  1. Create a matching type for ConwayPParams in plutus context, like we do for all other types. It would look very close to how it is represented here in the ticket. Then we simply translate ConwayPParamsUpdate to the Plutus context

Cons and pros are inverted from previous solution.

Upsides are:

  • better type safety
  • better performance both on translation and during Plutus execution

Downside is:

  • For every era that adds or removes PParams, we will have to implement a new Plutus version. This is true in general for anything that goes into transaction. If anything is added to a transaction in a new era we do need a new Plutus version to accommodate those changes. So this might not be too big of a deal.

There might be other solutions that I didn't think of.

Describe alternatives you've considered

No response

dynamic typing, thus less safety in code that will be written by Plutus users

Also PPValue will not be fun to work with.

better performance both on translation and during Plutus execution

I guess you mean the translation step in the ledger? I think it might end up being a wash on the Plutus side, since in both cases we end up transferring things inefficiently through Data...

Downside [of the second approach] is:

The other downside is more type definitions and more Plutus Tx code for dealing with them. But I think it's not too bad and we should probably just accept it.

For every era that adds or removes PParams, we will have to implement a new Plutus version. This is true in general for anything that goes into transaction. If anything is added to a transaction in a new era we do need a new Plutus version to accommodate those changes. So this might not be too big of a deal.

This was my worry too, and I guess you've allayed it. It makes the coupling between a Plutus ledger language and a ledger era more obvious... but perhaps it was pretty tight in general.

For this to cost us, we'd have to have a situation where there was a new ledger era that a) had new protocol parameters, and b) didn't make us want to change the rest of the TxInfo. I can only see b) happening because:

  • There were no changes to the transaction format (pparams only?)
  • We didn't want to expose the transaction format changes to scripts

Both of which seem unusual. So yeah, I think this is probably fine.


A design question: are we okay with scripts only seeing the new protocol parameters, or are there reasons why they might want to see the old ones? I guess if you wanted to implement a policy like "parameter X cannot be raised by more than Y% in one proposal" then you would need something like that.

Of course, that would complicate things even more since a) we'd need to pass the old parameters too; or pass a list of deltas rather than just the new values, and b) we would need to include all the current protocol parameters in the pparamsHash, since script execution could depend on any of them.

Actually, if we took the approach of having the parameter update be a list of deltas, we might not need to do the pparamsHash thing. Then the deltas could state, for each individual parameter changed, what it goes from and what it goes to, and it could be a phase 1 failure if the source value is not what was stated. Then that pins down the values enough.

A design question: are we okay with scripts only seeing the new protocol parameters, or are there reasons why they might want to see the old ones?

I don't know. I brought up this question as well. We could actually solve it easily, since ParameterChange governance action must include previous governance action id for the last action that changed PParams. So we could easily keep it around in the ledger state and pass it to plutus. It is guaranteed to be the same as the on on chain, since we do validate previous governance action id as phase1 validation.

So, in a sense we could do this for all governance action that are required to have previous govenance action id. This way plutus script could always figure out the delta.

#5481

Updated

data GovernanceAction
    = ParameterChange` GovernanceActionId
    | <...>

to

data GovernanceAction
    = ParameterChange (Haskell.Maybe GovernanceActionId) ChangedParameters
    | <...>

where

newtype ChangedParameters = ChangedParameters {getChangedParameters :: PlutusTx.BuiltinData}

I.e. we chose the dynamic typing approach, if I understood it correctly.

Hence I'm closing the issue as resolved.