|
| 1 | +# Status |
| 2 | + |
| 3 | +-[] Accepted ??-??-??? |
| 4 | + |
| 5 | +# Context |
| 6 | + |
| 7 | +The current script witness api (and by extention the witness api) has been long overdue for a refactor. The main objectives for the introduction of the new witness api are as follows: |
| 8 | +- Use as much as possible (within reason) `cardano-ledger` types directly. This reduces the maintenance burden and allows us to leverage `cardano-ledger` type classes. |
| 9 | +- Treat simple script witnesses separately from plutus script witnesses. This is a QOL improvement that makes the code more easily understandable and nicer to work with. |
| 10 | +- Use `cardano-ledger`'s `PlutusRunnable` type in order to leverage it's serialization instances that check the validity of the plutus script at the point of disk read. |
| 11 | +- The new api needs to be compatible with the old api |
| 12 | + |
| 13 | +# Decision |
| 14 | + |
| 15 | +## Summary |
| 16 | + |
| 17 | +Currently we have several functions that generate indicies in order to construct the redeemer pointer map. For example: |
| 18 | + |
| 19 | +```haskell |
| 20 | + |
| 21 | +indexTxWithdrawals |
| 22 | + :: TxWithdrawals BuildTx era |
| 23 | + -> [(ScriptWitnessIndex, StakeAddress, L.Coin, Witness WitCtxStake era)] |
| 24 | +indexTxWithdrawals TxWithdrawalsNone = [] |
| 25 | +indexTxWithdrawals (TxWithdrawals _ withdrawals) = |
| 26 | + [ (ScriptWitnessIndexWithdrawal ix, addr, coin, witness) |
| 27 | + | (ix, (addr, coin, BuildTxWith witness)) <- zip [0 ..] (orderStakeAddrs withdrawals) |
| 28 | + ] |
| 29 | + |
| 30 | +... |
| 31 | + |
| 32 | +indexTxMintValue |
| 33 | + :: TxMintValue build era |
| 34 | + -> [ ( ScriptWitnessIndex |
| 35 | + , PolicyId |
| 36 | + , AssetName |
| 37 | + , Quantity |
| 38 | + , BuildTxWith build (ScriptWitness WitCtxMint era) |
| 39 | + ) |
| 40 | + ] |
| 41 | +indexTxMintValue TxMintNone = [] |
| 42 | +indexTxMintValue (TxMintValue _ policiesWithAssets) = |
| 43 | + [ (ScriptWitnessIndexMint ix, policyId', assetName', quantity, witness) |
| 44 | + | (ix, (policyId', assets)) <- zip [0 ..] $ toList policiesWithAssets |
| 45 | + , (assetName', quantity, witness) <- assets |
| 46 | + ] |
| 47 | + |
| 48 | +... |
| 49 | + |
| 50 | +indexTxIns |
| 51 | + :: TxIns BuildTx era |
| 52 | + -> [(ScriptWitnessIndex, TxIn, Witness WitCtxTxIn era)] |
| 53 | +indexTxIns txins = |
| 54 | + [ (ScriptWitnessIndexTxIn ix, txIn, witness) |
| 55 | + | (ix, (txIn, BuildTxWith witness)) <- zip [0 ..] $ orderTxIns txins |
| 56 | + ] |
| 57 | + |
| 58 | +...etc |
| 59 | + |
| 60 | +``` |
| 61 | + |
| 62 | +There is a repeating pattern here that basically has something that is _witnessable_ (tx input, mint value, certificate, etc) accompanied by its index and the corresponding witness. Therefore the following types are introduced to capture this pattern: |
| 63 | + |
| 64 | +```haskell |
| 65 | +data Witnessable thing era where |
| 66 | + WitTxIn :: L.AlonzoEraScript era => TxIn -> Witnessable TxIn era |
| 67 | + WitTxCert :: L.AlonzoEraScript era => Cert -> Witnessable Cert era |
| 68 | + WitMint :: L.AlonzoEraScript era => Mint -> Witnessable Mint era |
| 69 | + WitWithdrawal |
| 70 | + :: L.AlonzoEraScript era => Withdrawal -> Witnessable Withdrawal era |
| 71 | + WitVote |
| 72 | + :: L.ConwayEraScript era |
| 73 | + => Voter -> Witnessable Voter era |
| 74 | + WitProposal :: L.ConwayEraScript era => Proposal -> Witnessable Proposal era |
| 75 | + |
| 76 | +data AnyWitness era where |
| 77 | + AnyKeyWitness :: AnyWitness era |
| 78 | + AnySimpleScriptWitness :: SimpleScriptOrReferenceInput era -> AnyWitness era |
| 79 | + AnyPlutusScriptWitness :: PlutusScriptWitness lang purpose era -> AnyWitness era |
| 80 | +``` |
| 81 | + |
| 82 | +And the upshot is once we have `[(Witnessable thing era, AnyWitness era)]` these can be uniformly treated to construct the redeemer pointer map (`Redeemers era`): |
| 83 | + |
| 84 | +```haskell |
| 85 | + |
| 86 | +data TxScriptWitnessRequirements era |
| 87 | + = TxScriptWitnessRequirements |
| 88 | + (Set L.Language) |
| 89 | + [L.Script era] |
| 90 | + (L.TxDats era) |
| 91 | + (L.Redeemers era) -- Redeemer pointer map |
| 92 | + |
| 93 | +getTxScriptWitnessesRequirements |
| 94 | + :: AlonzoEraOnwards era |
| 95 | + -> [(Witnessable witnessable (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era))] |
| 96 | + -> TxScriptWitnessRequirements (ShelleyLedgerEra era) |
| 97 | +getTxScriptWitnessesRequirements eon wits = |
| 98 | + obtainMonoidConstraint eon $ mconcat $ map (getTxScriptWitnessRequirements eon) wits |
| 99 | +``` |
| 100 | + |
| 101 | +## New `IndexedPlutusScriptWitness` types |
| 102 | + |
| 103 | +We are only concerned with the index of something that is plutus script witnessed. Therefore we introduce `IndexedPlutusScriptWitness`: |
| 104 | + |
| 105 | +```haskell |
| 106 | +data IndexedPlutusScriptWitness witnessable (lang :: L.Language) (purpose :: PlutusScriptPurpose) era where |
| 107 | + IndexedPlutusScriptWitness |
| 108 | + :: Witnessable witnessable era |
| 109 | + -> (L.PlutusPurpose L.AsIx era) |
| 110 | + -> (PlutusScriptWitness lang purpose era) |
| 111 | + -> IndexedPlutusScriptWitness witnessable lang purpose era |
| 112 | +``` |
| 113 | + |
| 114 | +We do away with `cardano-api`'s `ScriptWitnessIndex` and directly use `cardano-ledger`'s `PlutusPurpose AsIx era` which is essentially the same thing. |
| 115 | +The benefit here is we get to use ledger type classes to calculate the index of the plutus script witness thing we are interested in: |
| 116 | + |
| 117 | +```haskell |
| 118 | +class GetPlutusScriptPurpose era where |
| 119 | + toPlutusScriptPurpose |
| 120 | + :: Word32 |
| 121 | + -> Witnessable thing era |
| 122 | + -> L.PlutusPurpose L.AsIx era |
| 123 | + |
| 124 | +instance GetPlutusScriptPurpose era where |
| 125 | + toPlutusScriptPurpose index WitTxIn{} = L.mkSpendingPurpose (L.AsIx index) |
| 126 | + toPlutusScriptPurpose index WitWithdrawal{} = L.mkRewardingPurpose (L.AsIx index) |
| 127 | + toPlutusScriptPurpose index WitMint{} = L.mkMintingPurpose (L.AsIx index) |
| 128 | + toPlutusScriptPurpose index WitTxCert{} = L.mkCertifyingPurpose (L.AsIx index) |
| 129 | + toPlutusScriptPurpose index WitVote{} = L.mkVotingPurpose (L.AsIx index) |
| 130 | + toPlutusScriptPurpose index WitProposal{} = L.mkProposingPurpose (L.AsIx index) |
| 131 | +``` |
| 132 | + |
| 133 | +This ultimately allows us to collapse all of the different index calculation functions into a single function: |
| 134 | + |
| 135 | +```haskell |
| 136 | +createIndexedPlutusScriptWitness |
| 137 | + :: Word32 |
| 138 | + -> Witnessable witnessable era |
| 139 | + -> PlutusScriptWitness lang purpose era |
| 140 | + -> IndexedPlutusScriptWitness witnessable lang purpose era |
| 141 | +createIndexedPlutusScriptWitness index witnessable = |
| 142 | + IndexedPlutusScriptWitness witnessable (toPlutusScriptPurpose index witnessable) |
| 143 | + |
| 144 | +createIndexedPlutusScriptWitnesses |
| 145 | + :: [(Witnessable witnessable era, AnyWitness era)] |
| 146 | + -> [AnyIndexedPlutusScriptWitness era] |
| 147 | +createIndexedPlutusScriptWitnesses witnessableThings = |
| 148 | + [ AnyIndexedPlutusScriptWitness $ createIndexedPlutusScriptWitness index thing sWit |
| 149 | + | (index, (thing, AnyPlutusScriptWitness sWit)) <- zip [0 ..] witnessableThings |
| 150 | + ] |
| 151 | +``` |
| 152 | +## New `PlutusScriptInEra` type |
| 153 | + |
| 154 | + |
| 155 | +```haskell |
| 156 | +data PlutusScriptInEra (lang :: L.Language) era where |
| 157 | + PlutusScriptInEra :: PlutusRunnable lang -> PlutusScriptInEra lang era |
| 158 | +``` |
| 159 | + |
| 160 | +Why PlutusRunnable? Mainly for deserialization benefits. The deserialization of this type looks at the major protocol version and the script language to determine if indeed the script is runnable. |
| 161 | + |
| 162 | +## New `PlutusScriptDatum` type |
| 163 | + |
| 164 | +```haskell |
| 165 | +data PlutusScriptDatum (lang :: L.Language) (purpose :: PlutusScriptPurpose) where |
| 166 | + SpendingScriptDatum |
| 167 | + :: PlutusScriptDatumF lang SpendingScript -> PlutusScriptDatum lang SpendingScript |
| 168 | + InlineDatum :: PlutusScriptDatum lang purpose |
| 169 | + NoScriptDatum |
| 170 | + :: PlutusScriptDatum lang purpose |
| 171 | + |
| 172 | +data NoScriptDatum = NoScriptDatumAllowed deriving Show |
| 173 | + |
| 174 | +-- | The PlutusScriptDatum type family is used to determine if a script datum is allowed |
| 175 | +-- for a given plutus script purpose and version. This change was proposed in CIP-69 |
| 176 | +-- https://github.com/cardano-foundation/CIPs/tree/master/CIP-0069 |
| 177 | +type family PlutusScriptDatumF (lang :: L.Language) (purpose :: PlutusScriptPurpose) where |
| 178 | + PlutusScriptDatumF L.PlutusV1 SpendingScript = HashableScriptData |
| 179 | + PlutusScriptDatumF L.PlutusV2 SpendingScript = HashableScriptData |
| 180 | + PlutusScriptDatumF L.PlutusV3 SpendingScript = Maybe HashableScriptData -- CIP-69 |
| 181 | + PlutusScriptDatumF L.PlutusV1 MintingScript = NoScriptDatum |
| 182 | + PlutusScriptDatumF L.PlutusV2 MintingScript = NoScriptDatum |
| 183 | + PlutusScriptDatumF L.PlutusV3 MintingScript = NoScriptDatum |
| 184 | + PlutusScriptDatumF L.PlutusV1 WithdrawingScript = NoScriptDatum |
| 185 | + PlutusScriptDatumF L.PlutusV2 WithdrawingScript = NoScriptDatum |
| 186 | + PlutusScriptDatumF L.PlutusV3 WithdrawingScript = NoScriptDatum |
| 187 | + PlutusScriptDatumF L.PlutusV1 CertifyingScript = NoScriptDatum |
| 188 | + PlutusScriptDatumF L.PlutusV2 CertifyingScript = NoScriptDatum |
| 189 | + PlutusScriptDatumF L.PlutusV3 CertifyingScript = NoScriptDatum |
| 190 | + PlutusScriptDatumF L.PlutusV1 ProposingScript = NoScriptDatum |
| 191 | + PlutusScriptDatumF L.PlutusV2 ProposingScript = NoScriptDatum |
| 192 | + PlutusScriptDatumF L.PlutusV3 ProposingScript = NoScriptDatum |
| 193 | + PlutusScriptDatumF L.PlutusV1 VotingScript = NoScriptDatum |
| 194 | + PlutusScriptDatumF L.PlutusV2 VotingScript = NoScriptDatum |
| 195 | + PlutusScriptDatumF L.PlutusV3 VotingScript = NoScriptDatum |
| 196 | + |
| 197 | +``` |
| 198 | +The `PlutusScriptDatum` GADT and `PlutusScriptDatumF` type family are defined to simply capture CIP-69 i.e spending script datums are optional in PlutusV3. |
| 199 | + |
| 200 | +## New `SimpleScript` type |
| 201 | + |
| 202 | +```haskell |
| 203 | +data SimpleScript era where |
| 204 | + SimpleScript :: Ledger.NativeScript era -> SimpleScript era |
| 205 | +``` |
| 206 | +A wrapper around ledger's `NativeScript` type. We opt for this type because it restricts the allowable scripts to simple scripts. |
| 207 | + |
| 208 | +## Misc |
| 209 | + |
| 210 | +Future work would include creating a `TxBodyContents` successor that will use `[(Witnessable witnessable era, AnyWitness era)]` directly. |
| 211 | + |
| 212 | +# Consequences |
| 213 | + |
| 214 | +Acceptance of this ADR will: |
| 215 | +- Improve the clarity of the witness and script apis |
| 216 | +- Make clearer the relationship between the redeemer pointers and the script witnesses |
| 217 | +- Allow us to use the new api "under the hood" of the old api reducing the maintenance burden of maintaining the old vs new api. |
0 commit comments