Skip to content

Commit 4b34a06

Browse files
committed
ADR-10 Script Witness API
1 parent 1ff0dd8 commit 4b34a06

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
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

Comments
 (0)