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:
- Use poorly typed JSON like approach, where we have a
PPValue
like type and we represent PParamsUpdate as aMap String PPValue
, wherePPValue
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
- 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.
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.