convex-testing-interface
Safe HaskellSafe-Inferred
LanguageHaskell2010

Convex.ThreatModel.Cardano.Api

Synopsis

Types

TxOut accessors

datumOfTxOut :: TxOut ctx Era -> TxOutDatum ctx Era Source #

Get the datum from a transaction output.

txBodyContentOf :: Tx Era -> TxBodyContent ViewTx Era Source #

The transaction's body content. Rebuilding this deserialises every output's datum and reference script, so a caller that needs more than one projection of the same transaction should take the body content once and use the bodyContent* accessors (see ThreatModelEnv, which caches it per env).

Redeemer and script data

mintedPlutusPolicies :: Tx Era -> UTxO Era -> [(PolicyId, PolicyAssets, ScriptInAnyLang, ScriptData)] Source #

Plutus minting policies (with their minted/burned assets and the redeemer used) that the given transaction already exercises. Each policy's script is resolved either from the transaction's own witness set or, for reference-script mints, from a UTxO among the chain state passed in. Native-script policies, and policies whose script can't be resolved, are omitted.

recomputeScriptData :: Maybe Word32 -> (Word32 -> Word32) -> TxBodyScriptData Era -> TxBodyScriptData Era Source #

Re-key the Spending redeemers after the set of spend inputs changed: optionally drop the redeemer of a removed input, then apply the index shift to the remaining ones. Redeemers of every other purpose are indexed against their own item sets, which this change does not touch, so they pass through unchanged - shifting them would leave e.g. a withdrawal's redeemer pointing at the wrong (or a missing) reward account, and the ledger would reject the transaction in phase 1 with MissingRedeemer/ExtraRedeemers.

updateRedeemer :: Word32 -> (Data (ShelleyLedgerEra Era), ExUnits) -> TxBodyScriptData Era -> TxBodyScriptData Era Source #

Update only the redeemer for a spending input (does not modify TxDats) Use this when the original UTxO has an inline datum to avoid adding orphaned datums

addMintingRedeemer :: Word32 -> (Data (ShelleyLedgerEra Era), ExUnits) -> TxBodyScriptData Era -> TxBodyScriptData Era Source #

Add a minting redeemer to the script data (no datum needed for minting)

recomputeScriptDataForMint :: Maybe Word32 -> (Word32 -> Word32) -> TxBodyScriptData Era -> TxBodyScriptData Era Source #

Re-key the Minting redeemers after the set of minted policies changed.

toMaryAssetName :: AssetName -> AssetName Source #

Convert cardano-api AssetName to ledger Mary.AssetName

Address utilities

scriptAddressAny :: ScriptHash -> AddressAny Source #

Construct a script address.

keyAddressAny :: Hash PaymentKey -> AddressAny Source #

Construct a public key address.

isKeyAddressAny :: AddressAny -> Bool Source #

Check if an address is a public key address — i.e. has no script payment credential. Byron addresses count as key addresses, as they have no script credentials at all.

Datum/Redeemer conversion

txOutDatum :: ScriptData -> TxOutDatum CtxTx Era Source #

Convert ScriptData to a Datum.

toScriptData :: ToData a => a -> ScriptData Source #

Convert a Haskell value to ScriptData for use as a Redeemer or convert to a Datum with txOutDatum.

Transaction utilities

dummyTxId :: TxId Source #

Used for new inputs.

detectSigningWallet :: Tx Era -> Either String Wallet Source #

Detect which mock wallet signed a transaction by examining its witnesses. Returns an error message if no known mock wallet is found among the signers.

txRequiredSigners :: Tx Era -> [Hash PaymentKey] Source #

Get the required signers from the transaction body (not witnesses).

txRunsPlutusScript :: Tx Era -> Bool Source #

Does this transaction run at least one Plutus script? See needsCollateral. | Does any Plutus script run in this transaction? runningPlutusScriptHashes answers which ones, and is empty exactly when this is False.

runningPlutusScriptHashes :: UTxO LedgerEra -> Tx Era -> Set ScriptHash Source #

The hashes of the Plutus scripts that run in this transaction.

Built from the ledger's own notion of which scripts a transaction must satisfy (getScriptsNeeded), so it covers every purpose - spending, minting, rewarding, certifying, voting, proposing - and cannot drift from the ledger rules the way a hand-rolled redeemer walk would.

Narrowed to scripts *provided* as Plutus: a native script runs no Plutus code and cannot inspect outputs at all, so it never guards anything. Reference scripts are included, since getScriptsProvided resolves them from the UTxO set rather than the witness set.

Empty exactly when txRunsPlutusScript is False, which is checked first both to short-circuit the UTxO walk and to keep that equivalence true by construction - guardedScriptOutputs relies on it to subsume requireScriptExecution.

scriptHashOfAddressAny :: AddressAny -> Maybe ScriptHash Source #

The payment credential's script hash, or Nothing for a key or Byron address. See guardedScriptOutputs for what matching this against runningPlutusScriptHashes does and does not establish.

Value utilities

leqValue :: Value -> Value -> Bool Source #

Check if a value is less or equal than another value.

projectAda :: Value -> Value Source #

Keep only the Ada part of a value.

Validation

data TxValidity Source #

The outcome of validating a transaction, mirroring the ledger's two-phase validation: Phase 2 (script execution) only runs when Phase 1 (structural ledger rules) passes, so no other combinations exist.

Constructors

Valid

Phase 1 and Phase 2 both passed

Phase1Invalid

Rejected by Phase 1 ledger rules; scripts were never executed

Phase2Invalid

Phase 1 passed, but a script rejected the transaction

validateTx :: LedgerProtocolParameters Era -> Tx Era -> UTxO Era -> ValidityReport Source #

Validate a transaction using Phase 2 (script execution) validation only.

This uses evaluateTransactionExecutionUnits to check if Plutus scripts would accept or reject the transaction. It does NOT validate Phase 1 ledger rules (fees, signatures, value preservation, etc.) because threat model modifications alter the transaction body, invalidating signatures and fee calculations.

The purpose of threat models is to test script logic, not transaction construction.

validateTxM Source #

Arguments

:: MonadMockchain Era m 
=> NodeParams Era 
-> MockChainState Era

The state the original transaction validated against (see currentChainState); its slot and UTxO set are replaced below.

-> Tx Era 
-> UTxO Era 
-> m (ValidityReport, CoverageData) 

Validate a transaction with full Phase 1 + Phase 2 validation inside MockchainT.

This uses applyTransaction which performs complete ledger validation including: - Fee adequacy - Signature verification - UTxO existence - Value preservation - Validity intervals - Collateral requirements - Script execution (Phase 2)

buildMockState :: MockChainState Era -> SlotNo -> UTxO Era -> MockChainState Era Source #

Build a MockChainState for validation: the given base state - typically the state the original transaction validated against, so its certificate state (stake registrations, deposits, DRep and pool state) is intact - with the slot and the UTxO set replaced, and the accumulated coverage data blanked. The base state carries the coverage of every honest transaction replayed to reach it; blanking it makes the coverage read back after applying a modified transaction exactly that transaction's own delta, instead of honest-run coverage with the attack's mixed in.

chainStateUTxO :: MockChainState Era -> UTxO Era Source #

The UTxO set of a chain state.

chainStateLedgerUTxO :: MockChainState Era -> UTxO LedgerEra Source #

The chain state's UTxO set in ledger form, which is how it is actually stored. chainStateUTxO converts it to the api type; anything that only feeds it back to a ledger function should take this instead and skip the round trip.

chainStatePParams :: MockChainState Era -> LedgerProtocolParameters Era Source #

The protocol parameters a chain state validates with.

Rebalancing

rebalanceAndSign Source #

Arguments

:: MonadMockchain Era m 
=> MockChainState Era

The state the original transaction validated against (see currentChainState): deposits looked up for the value balance below must come from the same state the modified transaction is re-validated against.

-> Wallet 
-> Tx Era

The original, unmodified transaction: its outputs identify which wallet output is the change output (see adjustOriginalChangeOutput).

-> Tx Era

The modified transaction to rebalance

-> UTxO Era 
-> m (Either String (Tx Era)) 

Re-balance fees, recalculate execution units, and re-sign a modified transaction.

After applying TxModifier operations, the transaction body changes which: 1. Invalidates the original signatures (body hash changed) 2. May require different fees (outputs changed) 3. May have invalid execution units (for added scripts)

This function: 1. Recalculates execution units for all scripts 2. Calculates the new required fee 3. Adjusts the change output (the original transaction's change output, located in the modified one by adjustOriginalChangeOutput) to compensate 4. Re-signs the transaction with the wallet's key

A Left means the modification cannot be realized as a well-formed transaction on this particular input (e.g. "No change output found", or no usable collateral input) - a limitation of this function, not a verdict on the transaction. The threat-model runners all treat it as a skipped test rather than an error.

The steps below have a load-bearing order: each one's comment explains what it needs to see from the ones before it. In particular, everything that can change the transaction's *size* has to be reflected in the shape the fee is estimated from - topUpUnderfundedOutputs and ensureCollateralInputShape run before it, and the change output's absorption of the residual is solved *together with* the fee as a fixed point (settle below), because each determines the other. Reordering or interleaving a new corrective step would still compile - it would only show up as an intermittent property-test failure (a wrong fee, or BabbageOutputTooSmallUTxO), so check each step's comment before moving anything.

updateExecutionUnits :: LedgerProtocolParameters Era -> SystemStart -> EraHistory -> UTxO Era -> Tx Era -> Either String (Tx Era) Source #

Update execution units in a transaction by evaluating all scripts.

This computes the actual execution units required for each script and updates the redeemers in the transaction with those values. This is necessary because TxModifier operations like addPlutusScriptMint use ExecutionUnits 0 0 as placeholders.

A script that runs and *rejects* the transaction shows up here as ScriptErrorEvaluationFailed; its redeemer keeps its previous execution units and the rejection is reported by the Phase 2 validation later - the very outcome shouldNotValidate attacks look for. Every other evaluation error is structural (a missing input, datum, script, or cost model, a redeemer pointing nowhere, an execution-units overflow): the transaction the modifier built cannot be meaningfully executed at all, so it is returned as a Left and the run is skipped as environmental. Silently keeping the placeholder units instead would fail Phase 2 on budget and be miscounted as the validator rejecting the attack - a false negative.

updateTxRedeemersWithExUnits :: Map ScriptWitnessIndex ExecutionUnits -> Tx Era -> Tx Era Source #

Update the execution units in a transaction's redeemers.

This function takes a map from ScriptWitnessIndex to ExecutionUnits and updates the corresponding redeemers in the transaction.

updateScriptDataExUnits :: Map ScriptWitnessIndex ExecutionUnits -> TxBodyScriptData Era -> TxBodyScriptData Era Source #

Update execution units in TxBodyScriptData based on ScriptWitnessIndex map.

recalculateScriptIntegrityHash :: UTxO Era -> LedgerProtocolParameters Era -> Tx Era -> Tx Era Source #

Recalculate and update the script integrity hash in a transaction.

The script integrity hash commits to: - The redeemers in the transaction - The datums in the witness set - The cost models for languages used (from protocol parameters)

After modifying a transaction (addingremoving inputs, changing redeemersdatums), this hash becomes stale and must be recalculated.

recalculateTotalCollateral :: LedgerProtocolParameters Era -> UTxO Era -> Tx Era -> Either String (Tx Era) Source #

Recalculate the total collateral and collateral return based on the new fee.

Total collateral = ceiling(fee * collateralPercentage / 100) Collateral return = collateral input value - total collateral

This is needed because cardano-ledger is strict about collateral matching the fee. When the fee increases (e.g., due to bloated datum), we need to: 1. Increase the total collateral field 2. Decrease the collateral return (to provide more collateral)

If the transaction runs a Plutus script (spending, minting, or otherwise) but doesn't have any collateral inputs yet - e.g. a TxModifier introduced a new Plutus script, such as a minting policy, into a transaction that previously ran no scripts at all - a key-address input already present in the transaction is reused as the collateral input. The candidates are tried in the order collateralInputCandidates ranks them (richest first) and the first one that yields a buildable collateral arrangement wins, so a small input that happens to sort first in TxIn order cannot mask a sufficient one further down. The same UTxO can appear in both the regular input set and the collateral input set: on a successful script run the collateral fields are simply ignored by the ledger, so this "double duty" is safe and is what a real wallet without a dedicated collateral reserve would do too.

Collateral inputs that carry native tokens are supported: the ledger's collateral balance (inputs minus return output) must be pure ADA, so the return output is given exactly the inputs' tokens along with the leftover lovelace.

Returns Left if no suitable collateral input is available, or if the chosen collateral inputs don't have enough value to cover the required collateral. The latter can happen when a TxModifier significantly increases the transaction size (and thus the fee) - the original collateral may no longer be sufficient. Also returns Left for token-carrying collateral whose leftover lovelace can't fund the token-returning return output the tokens require.

getScriptLanguage :: AlonzoScript LedgerEra -> Maybe Language Source #

Extract the Plutus language from a ledger script, if it's a Plutus script

setTxFeeCoin :: Coin -> Tx Era -> Tx Era Source #

Set the fee in a transaction

setTxOutputsList :: [TxOut CtxTx Era] -> Tx Era -> Tx Era Source #

Set transaction outputs (helper that works at the Tx level)

mkSizedShelleyTxOut :: TxOut CtxTx Era -> Sized (TxOut LedgerEra) Source #

Convert a TxOut into a sized ledger TxOut, as stored in a tx body.

adjustChangeOutput Source #

Arguments

:: LedgerProtocolParameters Era 
-> AddressInEra Era

Wallet address to find change output

-> Value

Value delta to apply to the change output

-> [TxOut CtxTx Era]

Transaction outputs

-> Either String [TxOut CtxTx Era] 

Adjust the last output going to wallet address by a value delta (see adjustOutputAt).

Taking the last wallet output as the change output is only a heuristic. It assumes the transaction was balanced with trailing change (TrailingChange; with LeadingChange the change output comes first), and it goes wrong as soon as a TxModifier appends an output to the wallet itself, since addOutput puts it after the real change output. rebalanceAndSign therefore uses adjustOriginalChangeOutput, which only falls back to this when it can't identify the change output from the original transaction.

adjustOriginalChangeOutput Source #

Arguments

:: LedgerProtocolParameters Era 
-> AddressInEra Era

Wallet address to find change output

-> [TxOut CtxTx Era]

The original (unmodified) transaction's outputs

-> Value

Value delta to apply to the change output

-> [TxOut CtxTx Era]

Transaction outputs

-> Either String [TxOut CtxTx Era] 

Adjust the change output by a value delta, identifying it through the original transaction: its change output is taken to be its last wallet output (see adjustChangeOutput), and that output is then located in the modified outputs, trying in order:

  1. the original change output, still unchanged (the modifier may have shifted it by removing an earlier output);
  2. whatever wallet output sits at the original change output's index (the modifier rewrote the change output in place, e.g. with changeValueOf);
  3. the last wallet output left unchanged by the modifier (the change output itself was removed);
  4. adjustChangeOutput's choice, the last wallet output.

Going straight to the last wallet output of the modified transaction goes wrong whenever a TxModifier adds an output to the wallet itself. Double satisfaction, for one, redirects a victim's output to the signer with addOutput, which appends it after the real change output; that new output typically sits at its minimum ADA, so charging the fee to it fails with "Change output would fall below the minimum required ADA" even though the real change output could easily cover it. Conversely, looking only for unchanged wallet outputs goes wrong when the modifier rewrote the change output in place (as token forgery does): the fee would then be charged to an earlier wallet output, such as an exact payment the validator checks.

replaceAt :: Int -> a -> [a] -> [a] Source #

Replace element at index in a list

Validity interval

UTxO utilities

restrictUTxO :: Tx Era -> UTxO Era -> UTxO Era Source #

Keep only UTxOs mentioned in the given transaction.

Coverage

extractCoverageFromValidationError :: String -> CoverageData Source #

Extract coverage data from a ValidationError string containing CovLoc annotations. Handles the format found in Phase2 script evaluation errors where coverage annotations appear as "CoverLocation (CovLoc {...})" or "CoverBool (CovLoc {...}) Bool"

unescapeHaskellString :: String -> String Source #

Unescape common Haskell string escapes (backslash-quote to quote, backslash-backslash to backslash)

extractCoverageAnnotations :: String -> [String] Source #

Extract all "CoverLocation (...)" and "CoverBool (...)" substrings from text. Uses bracket counting to properly match nested parentheses.