@@ -9,7 +9,9 @@ module Convex.ThreatModel.Cardano.Api where
99import Cardano.Api
1010
1111import Cardano.Ledger.Allegra.Scripts (ValidityInterval (.. ))
12+ import Cardano.Ledger.Alonzo.PParams (getLanguageView )
1213import Cardano.Ledger.Alonzo.Scripts qualified as Ledger
14+ import Cardano.Ledger.Alonzo.Tx (hashScriptIntegrity )
1315import Cardano.Ledger.Alonzo.TxBody qualified as Ledger
1416import Cardano.Ledger.Alonzo.TxWits qualified as Ledger
1517import Cardano.Ledger.Api.Era qualified as Ledger (eraProtVerLow )
@@ -18,6 +20,7 @@ import Cardano.Ledger.Binary qualified as CBOR
1820import Cardano.Ledger.Conway.Scripts qualified as Conway
1921import Cardano.Ledger.Conway.TxBody qualified as Conway
2022import Cardano.Ledger.Keys (WitVKey (.. ), coerceKeyRole , hashKey )
23+ import Cardano.Ledger.Plutus.Language qualified as Plutus
2124import Cardano.Slotting.Slot ()
2225import Cardano.Slotting.Time (SlotLength , mkSlotLength )
2326import Control.Lens ((&) , (.~) , _1 )
@@ -41,6 +44,7 @@ import Data.Maybe (listToMaybe)
4144import Data.Maybe.Strict
4245import Data.SOP.NonEmpty (NonEmpty (NonEmptyOne ))
4346import Data.Sequence.Strict qualified as Seq
47+ import Data.Set qualified as Set
4448import Data.Time.Clock.POSIX (posixSecondsToUTCTime )
4549import Data.Word
4650import GHC.Exts (toList )
@@ -354,9 +358,6 @@ This function:
3543581. Calculates the new required fee
3553592. Adjusts the change output (last output to wallet address) to compensate
3563603. Re-signs the transaction with the wallet's key
357-
358- Note: This works at the ledger level to preserve the transaction structure
359- created by TxModifier operations.
360361-}
361362rebalanceAndSign
362363 :: (MonadMockchain Era m , MonadFail m )
@@ -392,13 +393,66 @@ rebalanceAndSign wallet tx utxo = do
392393 adjustedOutputs <- adjustChangeOutput walletAddr feeDiff currentOuts
393394
394395 -- Apply the changes: new fee and adjusted outputs
395- let finalTx = setTxOutputsList adjustedOutputs $ setTxFeeCoin newFee tx
396+ let modifiedTx = setTxOutputsList adjustedOutputs $ setTxFeeCoin newFee tx
397+
398+ -- Recalculate script integrity hash
399+ let finalTx = recalculateScriptIntegrityHash pparams modifiedTx
396400
397401 -- Re-sign (strip old signatures and add new one)
398402 let Tx finalBody _ = finalTx
399403 unsignedTx = makeSignedTransaction [] finalBody
400404 pure $ Wallet. signTx wallet unsignedTx
401405
406+ {- | Recalculate and update the script integrity hash in a transaction.
407+
408+ The script integrity hash commits to:
409+ - The redeemers in the transaction
410+ - The datums in the witness set
411+ - The cost models for languages used (from protocol parameters)
412+
413+ After modifying a transaction (adding/removing inputs, changing redeemers/datums),
414+ this hash becomes stale and must be recalculated.
415+ -}
416+ recalculateScriptIntegrityHash :: LedgerProtocolParameters Era -> Tx Era -> Tx Era
417+ recalculateScriptIntegrityHash pparams (Tx (ShelleyTxBody era body scripts scriptData auxData validity) wits) =
418+ let
419+ -- Extract redeemers and datums from scriptData
420+ (redeemers, datums) = case scriptData of
421+ TxBodyNoScriptData -> (Ledger. Redeemers mempty , Ledger. TxDats mempty )
422+ TxBodyScriptData _ dats rdmrs -> (rdmrs, dats)
423+
424+ -- Get the protocol parameters
425+ pp = unLedgerProtocolParameters pparams
426+
427+ -- Determine which languages are used by examining the scripts in the transaction
428+ usedLangs =
429+ Set. fromList
430+ [ lang
431+ | script <- scripts
432+ , Just lang <- [getScriptLanguage script]
433+ ]
434+
435+ -- Get LangDepView for each used language
436+ langs =
437+ Set. fromList
438+ [ getLanguageView pp lang
439+ | lang <- Set. toList usedLangs
440+ ]
441+
442+ -- Compute new script integrity hash
443+ newHash = hashScriptIntegrity langs redeemers datums
444+
445+ -- Update the body with new hash
446+ body' = body{Conway. ctbScriptIntegrityHash = newHash}
447+ in
448+ Tx (ShelleyTxBody era body' scripts scriptData auxData validity) wits
449+
450+ -- | Extract the Plutus language from a ledger script, if it's a Plutus script
451+ getScriptLanguage :: Ledger. AlonzoScript LedgerEra -> Maybe Plutus. Language
452+ getScriptLanguage script = case script of
453+ Ledger. TimelockScript {} -> Nothing
454+ Ledger. PlutusScript ps -> Just $ Ledger. plutusScriptLanguage ps
455+
402456-- | Get the fee from a transaction
403457getTxFeeCoin :: Tx Era -> Coin
404458getTxFeeCoin (Tx (ShelleyTxBody _ body _ _ _ _) _) = Conway. ctbTxfee body
0 commit comments