Skip to content

Commit a64958f

Browse files
authored
Merge pull request #1181 from IntersectMBO/issue-1363-build-raw-script-data-hash
makeUnsignedTx: error when Plutus scripts present without protocol params
2 parents 62293e4 + 259d2ce commit a64958f

7 files changed

Lines changed: 161 additions & 92 deletions

File tree

cardano-api/src/Cardano/Api/Experimental.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Cardano.Api.Experimental
1212
-- ** Transaction-related
1313
UnsignedTx (..)
1414
, SignedTx (..)
15+
, MakeUnsignedTxError (..)
1516
, makeUnsignedTx
1617
, makeKeyWitness
1718
, signTx

cardano-api/src/Cardano/Api/Experimental/Tx.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ module Cardano.Api.Experimental.Tx
7878
-- 'TxBodyContent' that we defined earlier:
7979
--
8080
-- @
81-
-- let (Right unsignedTx) = Exp.makeUnsignedTx era txBodyContent
81+
-- case Exp.makeUnsignedTx era txBodyContent of
82+
-- Left err -> error (show err)
83+
-- Right unsignedTx -> ...
8284
-- @
8385
--
8486
-- Next, use the key witness to sign the unsigned transaction with the 'makeKeyWitness' function:
@@ -117,6 +119,7 @@ module Cardano.Api.Experimental.Tx
117119
-- * Contents
118120
UnsignedTx (..)
119121
, SignedTx (..)
122+
, MakeUnsignedTxError (..)
120123
, makeUnsignedTx
121124
, makeKeyWitness
122125
, signTx

cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module Cardano.Api.Experimental.Tx.Internal.BodyContent.New
2424
, TxWithdrawals (..)
2525
, TxBodyContent (..)
2626
, Datum (..)
27+
, MakeUnsignedTxError (..)
2728
, defaultTxBodyContent
2829
, extractDatumsAndHashes
2930
, getDatums
@@ -106,6 +107,7 @@ import Cardano.Api.Governance.Internal.Action.VotingProcedure
106107
import Cardano.Api.Key.Internal
107108
import Cardano.Api.Ledger.Internal.Reexport (StrictMaybe (..))
108109
import Cardano.Api.Ledger.Internal.Reexport qualified as L
110+
import Cardano.Api.Monad.Error (liftMaybe)
109111
import Cardano.Api.Plutus.Internal.Script
110112
( PlutusScript (..)
111113
, PlutusScriptVersion (..)
@@ -170,11 +172,27 @@ import Data.Typeable (cast)
170172
import GHC.Exts (IsList (..))
171173
import Lens.Micro
172174

175+
-- | Error that can occur when constructing an unsigned transaction.
176+
data MakeUnsignedTxError
177+
= -- | Plutus scripts are present in the transaction but no protocol
178+
-- parameters were provided. Protocol parameters are required to
179+
-- compute the script integrity hash (script_data_hash).
180+
MakeUnsignedTxMissingProtocolParams
181+
deriving (Eq, Show)
182+
183+
instance Error MakeUnsignedTxError where
184+
prettyError MakeUnsignedTxMissingProtocolParams =
185+
mconcat
186+
[ "Transaction uses Plutus scripts but no protocol parameters were provided. "
187+
, "Protocol parameters are required to compute the script integrity hash "
188+
, "(script_data_hash) from the cost models."
189+
]
190+
173191
makeUnsignedTx
174192
:: forall era
175193
. Era era
176194
-> TxBodyContent (LedgerEra era)
177-
-> UnsignedTx (LedgerEra era)
195+
-> Either MakeUnsignedTxError (UnsignedTx (LedgerEra era))
178196
makeUnsignedTx DijkstraEra _ = error "makeUnsignedTx: Dijkstra era not supported yet"
179197
makeUnsignedTx era@ConwayEra bc = obtainCommonConstraints era $ do
180198
let TxScriptWitnessRequirements languages scripts datums redeemers = collectTxBodyScriptWitnessRequirements bc
@@ -197,12 +215,13 @@ makeUnsignedTx era@ConwayEra bc = obtainCommonConstraints era $ do
197215
totCollateral = unTxTotalCollateral <$> txTotalCollateral bc
198216
txAuxData = toAuxiliaryData (txMetadata bc) (txAuxScripts bc)
199217
scriptValidity = scriptValidityToIsValid $ txScriptValidity bc
200-
scriptIntegrityHash =
201-
convPParamsToScriptIntegrityHash
202-
protocolParameters
203-
redeemers
204-
datums
205-
languages
218+
219+
scriptIntegrityHash <-
220+
convPParamsToScriptIntegrityHash
221+
protocolParameters
222+
redeemers
223+
datums
224+
languages
206225

207226
let setMint = convMintValue apiMintValue
208227
setReqSignerHashes = convExtraKeyWitnesses apiExtraKeyWitnesses
@@ -235,11 +254,12 @@ makeUnsignedTx era@ConwayEra bc = obtainCommonConstraints era $ do
235254
& L.rdmrsTxWitsL .~ redeemers
236255

237256
let eraSpecificTxBody = eraSpecificLedgerTxBody era ledgerTxBody bc
238-
UnsignedTx $
239-
L.mkBasicTx eraSpecificTxBody
240-
& L.witsTxL .~ scriptWitnesses
241-
& L.auxDataTxL .~ L.maybeToStrictMaybe (toAuxiliaryData (txMetadata bc) (txAuxScripts bc))
242-
& L.isValidTxL .~ scriptValidity
257+
Right $
258+
UnsignedTx $
259+
L.mkBasicTx eraSpecificTxBody
260+
& L.witsTxL .~ scriptWitnesses
261+
& L.auxDataTxL .~ L.maybeToStrictMaybe (toAuxiliaryData (txMetadata bc) (txAuxScripts bc))
262+
& L.isValidTxL .~ scriptValidity
243263

244264
convTxIns :: [(TxIn, AnyWitness era)] -> Set L.TxIn
245265
convTxIns inputs =
@@ -283,19 +303,23 @@ convPParamsToScriptIntegrityHash
283303
-> L.Redeemers (LedgerEra era)
284304
-> L.TxDats (LedgerEra era)
285305
-> Set Plutus.Language
286-
-> StrictMaybe L.ScriptIntegrityHash
306+
-> Either MakeUnsignedTxError (StrictMaybe L.ScriptIntegrityHash)
287307
convPParamsToScriptIntegrityHash mTxProtocolParams redeemers datums languages = obtainCommonConstraints (useEra @era) $ do
288-
pp <- L.maybeToStrictMaybe mTxProtocolParams
289308
-- This logic is copied from ledger, because their code is not reusable
290309
-- c.f. https://github.com/IntersectMBO/cardano-ledger/commit/5a975d9af507c9ee835a86d3bb77f3e2670ad228#diff-8236dfec9688f22550b91fc9a87af9915523ab9c5bd817218ecceec8ca7a789bR282
291310
let shouldCalculateHash =
292311
not $
293312
null (redeemers ^. L.unRedeemersL)
294313
&& null (datums ^. L.unTxDatsL)
295314
&& null languages
296-
guard shouldCalculateHash
297-
let scriptIntegrity = L.ScriptIntegrity redeemers datums (Set.map (L.getLanguageView pp) languages)
298-
pure $ L.hashScriptIntegrity scriptIntegrity
315+
if shouldCalculateHash
316+
then do
317+
pp <- liftMaybe MakeUnsignedTxMissingProtocolParams mTxProtocolParams
318+
pure $
319+
SJust $
320+
L.hashScriptIntegrity $
321+
L.ScriptIntegrity redeemers datums (Set.map (L.getLanguageView pp) languages)
322+
else pure SNothing
299323

300324
convProposalProcedures
301325
:: forall era

cardano-api/src/Cardano/Api/Experimental/Tx/Internal/Fee.hs

Lines changed: 58 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ data TxBodyErrorAutoBalance era
138138
-- ^ Total deposits
139139
L.MaryValue
140140
-- ^ Balance
141+
| TxBodyErrorMakeUnsignedTx MakeUnsignedTxError
141142

142143
deriving instance Show (TxBodyErrorAutoBalance era)
143144

@@ -203,6 +204,8 @@ instance Error (TxBodyErrorAutoBalance era) where
203204
, "\nBalance (UTxO value - deposits): "
204205
, pshow balance
205206
]
207+
TxBodyErrorMakeUnsignedTx err ->
208+
prettyError err
206209

207210
-- | Use when you do not have access to the UTxOs you intend to spend
208211
estimateBalancedTxBody
@@ -254,12 +257,14 @@ estimateBalancedTxBody
254257
data TxFeeEstimationError era
255258
= TxFeeEstimationScriptExecutionError (TxBodyErrorAutoBalance (LedgerEra era))
256259
| TxFeeEstimationBalanceError (TxBodyErrorAutoBalance (LedgerEra era))
260+
| TxFeeEstimationMakeUnsignedTxError MakeUnsignedTxError
257261
deriving Show
258262

259263
instance Error (TxFeeEstimationError era) where
260264
prettyError = \case
261265
TxFeeEstimationScriptExecutionError e -> prettyError e
262266
TxFeeEstimationBalanceError e -> prettyError e
267+
TxFeeEstimationMakeUnsignedTxError e -> prettyError e
263268

264269
-- | Use when you do not have access to the UTxOs you intend to spend
265270
estimateBalancedTxBody'
@@ -365,17 +370,18 @@ estimateBalancedTxBody'
365370
-- Step 3. Create a tx body with out max lovelace fee. This is strictly for
366371
-- calculating our fee with evaluateTransactionFee.
367372
let maxLovelaceFee = L.Coin (2 ^ (32 :: Integer) - 1)
368-
let txbody1ForFeeEstimateOnly =
369-
makeUnsignedTx
370-
useEra
371-
txbodycontent1
372-
{ txFee = maxLovelaceFee
373-
, txOuts =
374-
obtainCommonConstraints (useEra @era) (TxOut changeTxOut)
375-
: txOuts txbodycontent
376-
, txReturnCollateral = mDummyReturnCollateral
377-
, txTotalCollateral = mDummyTotalCollateral
378-
}
373+
txbody1ForFeeEstimateOnly <-
374+
first TxFeeEstimationMakeUnsignedTxError $
375+
makeUnsignedTx
376+
useEra
377+
txbodycontent1
378+
{ txFee = maxLovelaceFee
379+
, txOuts =
380+
obtainCommonConstraints (useEra @era) (TxOut changeTxOut)
381+
: txOuts txbodycontent
382+
, txReturnCollateral = mDummyReturnCollateral
383+
, txTotalCollateral = mDummyTotalCollateral
384+
}
379385
let fee =
380386
evaluateTransactionFee
381387
pparams
@@ -400,8 +406,8 @@ estimateBalancedTxBody'
400406
-- 1. The original outputs
401407
-- 2. Tx fee
402408
-- 3. Return and total collateral
403-
let
404-
txbody2 =
409+
txbody2 <-
410+
first TxFeeEstimationMakeUnsignedTxError $
405411
makeUnsignedTx
406412
useEra
407413
txbodycontent1
@@ -1420,10 +1426,11 @@ makeTransactionBodyAutoBalance
14201426
-- 3. update tx with fees
14211427
-- 4. balance the transaction and update tx change output
14221428

1423-
let txbodyForChange =
1424-
makeUnsignedTx
1425-
useEra
1426-
txbodycontent
1429+
txbodyForChange <-
1430+
first TxBodyErrorMakeUnsignedTx $
1431+
makeUnsignedTx
1432+
useEra
1433+
txbodycontent
14271434

14281435
let initialChangeTxOutValue :: Ledger.Value (LedgerEra era) =
14291436
evaluateTransactionBalance pp poolids stakeDelegDeposits drepDelegDeposits utxo txbodyForChange
@@ -1441,13 +1448,14 @@ makeTransactionBodyAutoBalance
14411448
-- scripts execution costs.
14421449
-- TODO: The txbody is made (leder tx) so this
14431450
-- is where the execution units map is made
1444-
let UnsignedTx txbody =
1445-
makeUnsignedTx
1446-
useEra
1447-
( txbodycontent
1448-
& modTxOuts
1449-
(<> [initialChangeTxOut])
1450-
)
1451+
UnsignedTx txbody <-
1452+
first TxBodyErrorMakeUnsignedTx $
1453+
makeUnsignedTx
1454+
useEra
1455+
( txbodycontent
1456+
& modTxOuts
1457+
(<> [initialChangeTxOut])
1458+
)
14511459
let exUnitsMapWithLogs =
14521460
evaluateTransactionExecutionUnits
14531461
systemstart
@@ -1479,17 +1487,18 @@ makeTransactionBodyAutoBalance
14791487
let maxLovelaceFee = L.Coin (2 ^ (32 :: Integer) - 1)
14801488
-- Make a txbody that we will use for calculating the fees.
14811489
let (maybeDummyReturnTxCollateral, maybeDummyTotalTxCollateral) = maybeDummyTotalCollAndCollReturnOutput txbodycontent changeaddr
1482-
let txbody1 =
1483-
makeUnsignedTx
1484-
useEra
1485-
txbodycontent1
1486-
{ txFee = maxLovelaceFee
1487-
, txReturnCollateral = maybeDummyReturnTxCollateral
1488-
, txTotalCollateral = maybeDummyTotalTxCollateral
1489-
, txOuts =
1490-
txOuts txbodycontent
1491-
<> [initialChangeTxOut]
1492-
}
1490+
txbody1 <-
1491+
first TxBodyErrorMakeUnsignedTx $
1492+
makeUnsignedTx
1493+
useEra
1494+
txbodycontent1
1495+
{ txFee = maxLovelaceFee
1496+
, txReturnCollateral = maybeDummyReturnTxCollateral
1497+
, txTotalCollateral = maybeDummyTotalTxCollateral
1498+
, txOuts =
1499+
txOuts txbodycontent
1500+
<> [initialChangeTxOut]
1501+
}
14931502

14941503
-- NB: This has the potential to over estimate the fees because estimateTransactionKeyWitnessCount
14951504
-- makes the conservative assumption that all inputs are from distinct
@@ -1521,14 +1530,15 @@ makeTransactionBodyAutoBalance
15211530
-- does not matter, instead it's just the values of the fee and outputs.
15221531
-- Here we do not want to start with any change output, since that's what
15231532
-- we need to calculate.
1524-
let txbody2 =
1525-
makeUnsignedTx
1526-
useEra
1527-
txbodycontent1
1528-
{ txFee = fee
1529-
, txReturnCollateral = maybeReturnTxCollateral
1530-
, txTotalCollateral = maybeTotalTxCollateral
1531-
}
1533+
txbody2 <-
1534+
first TxBodyErrorMakeUnsignedTx $
1535+
makeUnsignedTx
1536+
useEra
1537+
txbodycontent1
1538+
{ txFee = fee
1539+
, txReturnCollateral = maybeReturnTxCollateral
1540+
, txTotalCollateral = maybeTotalTxCollateral
1541+
}
15321542

15331543
case useEra @era of
15341544
DijkstraEra -> error "makeTransactionBodyAutoBalance: DijkstraEra not supported"
@@ -1566,10 +1576,11 @@ makeTransactionBodyAutoBalance
15661576
, txReturnCollateral = maybeReturnTxCollateral
15671577
, txTotalCollateral = maybeTotalTxCollateral
15681578
}
1569-
let txbody3 =
1570-
makeUnsignedTx
1571-
useEra
1572-
finalTxBodyContent
1579+
txbody3 <-
1580+
first TxBodyErrorMakeUnsignedTx $
1581+
makeUnsignedTx
1582+
useEra
1583+
finalTxBodyContent
15731584
return
15741585
(txbody3, finalTxBodyContent)
15751586

cardano-api/test/cardano-api-golden/Test/Golden/Cardano/Api/Tx.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,11 @@ tx_canonical = H.propertyOnce $ do
9999
& Exp.setTxTotalCollateral txTotalColl
100100
& Exp.setTxFee (L.Coin 0)
101101

102-
let unsignedTx = Exp.makeUnsignedTx era txBodyContent'
103-
tx = Exp.signTx era [] [] unsignedTx
104-
Exp.SignedTx ledgerTx = tx
105-
oldStyleTx = OldApi.ShelleyTx sbe ledgerTx
102+
unsignedTx <- H.evalEither $ Exp.makeUnsignedTx era txBodyContent'
103+
let
104+
tx = Exp.signTx era [] [] unsignedTx
105+
Exp.SignedTx ledgerTx = tx
106+
oldStyleTx = OldApi.ShelleyTx sbe ledgerTx
106107

107108
void . H.evalIO $ OldApi.writeTxFileTextEnvelope sbe (OldApi.File outFileNonCanonical) oldStyleTx
108109
void . H.evalIO $

0 commit comments

Comments
 (0)