Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR3: Tweaks around inputs and outputs restructured, with an additional tweak #458

Merged
merged 14 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@
be used on a redeemer to manually attach a reference input (which does not
necessarily have to contain the right reference script).
- Capability to test the result of a mockchain run based on the log entries.
- `txOutRefToTxSkelOut` helper to query the mock chain for recreating a
`TxSkelOut` from a `TxOutRef`. This is very useful when using Tweaks that need
to pay back an existing output with a slight modification.
- A new tweak `modifySpendRedeemersOfTypeTweak` to apply an optional
modification of all redeemers of a certain type within the skeleton inputs.

### Removed

- `positivePart` and `negativePart` in `ValueUtils.hs`. Replaced by `Api.split`.
- Redundant logging of errors in mockchain runs.
- Useless minting of non-ADA value in the dummy initial transaction.

### Changed

Expand All @@ -57,6 +63,8 @@
* it is not visible from outside of `cooked-validators`
- Dependency to cardano-api bumped to 8.46.
- The whole testing API has been revamped
- File `AddInputsAndOutputs.hs` has been split into `Inputs.hs`, `Outputs.hs`
and `Mint.hs`.

### Fixed

Expand All @@ -66,6 +74,8 @@
in the excess.
- Transactions that do not involve script are now properly generated without any
- All kinds of scripts can now be used as reference scripts.
- A bug where scripts being paid to in the initial distribution would not be
stored in the MockChain.

## [[4.0.0]](https://github.com/tweag/cooked-validators/releases/tag/v4.0.0) - 2024-06-28

Expand Down
4 changes: 3 additions & 1 deletion cooked-validators.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ library
Cooked.ShowBS
Cooked.Skeleton
Cooked.Tweak
Cooked.Tweak.AddInputsAndOutputs
Cooked.Tweak.Common
Cooked.Tweak.Inputs
Cooked.Tweak.Labels
Cooked.Tweak.Mint
Cooked.Tweak.OutPermutations
Cooked.Tweak.Outputs
Cooked.Tweak.Signers
Cooked.Tweak.TamperDatum
Cooked.Tweak.ValidityRange
Expand Down
37 changes: 10 additions & 27 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
zlib
xz
glibcLocales
openssl_3_3
postgresql # For pg_config
## We change the way 'blst' is built so that it takes into
## account the current architecture of the processor. This
Expand All @@ -65,6 +66,7 @@
libsodium
zlib
xz
openssl_3_3
postgresql # For cardano-node-emulator
openldap # For freer-extras‽
];
Expand Down
4 changes: 4 additions & 0 deletions src/Cooked/Conversion/ToCredential.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import PlutusLedgerApi.V3 qualified as Api
class ToCredential a where
toCredential :: a -> Api.Credential

instance (ToCredential a, ToCredential b) => ToCredential (Either a b) where
toCredential (Left l) = toCredential l
toCredential (Right r) = toCredential r

instance ToCredential Api.Credential where
toCredential = id

Expand Down
30 changes: 29 additions & 1 deletion src/Cooked/MockChain/BlockChain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ module Cooked.MockChain.BlockChain
validateTxSkel_,
txSkelProposalsDeposit,
govActionDeposit,
txOutRefToTxSkelOut,
)
where

Expand Down Expand Up @@ -79,7 +80,7 @@ import Ledger.Index qualified as Ledger
import Ledger.Slot qualified as Ledger
import Ledger.Tx qualified as Ledger
import Ledger.Tx.CardanoAPI qualified as Ledger
import ListT
import ListT hiding (null)
import Optics.Core
import Plutus.Script.Utils.Scripts qualified as Script
import PlutusLedgerApi.V3 qualified as Api
Expand Down Expand Up @@ -381,6 +382,33 @@ txSkelInputDataAsHashes skel = do
(Map.elems -> inputTxOuts) <- txSkelInputUtxos skel
catMaybes <$> mapM outputToDatumHashM inputTxOuts

-- | This creates a payment from an existing TxOut. This is not trivial because
-- we need to reconstruct a bunch of information using data withing the blockchain
txOutRefToTxSkelOut :: (MonadBlockChainBalancing m) => Api.TxOutRef -> m TxSkelOut
txOutRefToTxSkelOut oRef = do
Just txOut@(Api.TxOut (Api.Address cred _) _ dat refS) <- txOutByRef oRef
target <- case cred of
Api.PubKeyCredential pkh -> return $ Left pkh
Api.ScriptCredential (Api.ScriptHash sh) -> do
Just val <- validatorFromHash (Script.ValidatorHash sh)
return $ Right val
datum <- case dat of
Api.NoOutputDatum -> return TxSkelOutNoDatum
Api.OutputDatumHash hash -> do
Just (Api.Datum dat') <- datumFromHash hash
return $ TxSkelOutDatum dat' -- TODO: investigate between this and TxSkelOutDatum
Api.OutputDatum (Api.Datum dat') -> return $ TxSkelOutInlineDatum dat'
refScript <- case refS of
Nothing -> return Nothing
Just (Api.ScriptHash sh) -> validatorFromHash (Script.ValidatorHash sh)
return $
Pays $
(fromAbstractOutput txOut)
{ concreteOutputOwner = target,
concreteOutputDatum = datum,
concreteOutputReferenceScript = refScript
}

-- ** Slot and Time Management

-- $slotandtime
Expand Down
31 changes: 21 additions & 10 deletions src/Cooked/MockChain/MockChainSt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import Cooked.Output
import Cooked.Skeleton
import Data.Bifunctor (bimap)
import Data.Default
import Data.Either.Combinators (mapLeft)
import Data.List (foldl')
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
Expand Down Expand Up @@ -163,24 +162,38 @@ mockChainSt0 = MockChainSt def utxoIndex0 Map.empty Map.empty 0
-- * Initial `MockChainSt` from an initial distribution

mockChainSt0From :: InitialDistribution -> MockChainSt
mockChainSt0From i0 = MockChainSt def (utxoIndex0From i0) (datumMap0From i0) (referenceScriptMap0From i0) 0
mockChainSt0From i0 = MockChainSt def (utxoIndex0From i0) (datumMap0From i0) (referenceScriptMap0From i0 <> scriptMap0From i0) 0

-- | Reference scripts from initial distributions should be accounted for in the
-- `MockChainSt` which is done using this function.
referenceScriptMap0From :: InitialDistribution -> Map Script.ValidatorHash (Script.Versioned Script.Validator)
referenceScriptMap0From (InitialDistribution initDist) =
referenceScriptMap0From =
-- This builds a map of entries from the reference scripts contained in the
-- initial distribution
Map.fromList $ mapMaybe unitMaybeFrom initDist
Map.fromList . mapMaybe unitMaybeFrom . unInitialDistribution
where
-- This takes a single output and returns a possible map entry when it
-- contains a reference script
unitMaybeFrom :: TxSkelOut -> Maybe (Script.ValidatorHash, Script.Versioned Script.Validator)
unitMaybeFrom (Pays output) = do
refScript <- view outputReferenceScriptL output
let vScript@(Script.Versioned script version) = toVersionedScript refScript
Api.ScriptHash scriptHash = toScriptHash vScript
return (Script.ValidatorHash scriptHash, Script.Versioned (Script.Validator script) version)
return (Script.ValidatorHash $ Api.getScriptHash $ toScriptHash vScript, Script.Versioned (Script.Validator script) version)

-- | Scripts from initial distributions should be accounted for in the
-- `MockChainSt` which is done using this function.
scriptMap0From :: InitialDistribution -> Map Script.ValidatorHash (Script.Versioned Script.Validator)
scriptMap0From =
-- This builds a map of entries from the scripts contained in the initial
-- distribution
Map.fromList . mapMaybe unitMaybeFrom . unInitialDistribution
where
-- This takes a single output and returns a possible map entry when it
-- contains a script
unitMaybeFrom :: TxSkelOut -> Maybe (Script.ValidatorHash, Script.Versioned Script.Validator)
unitMaybeFrom txSkelOut = do
val <- txSkelOutValidator txSkelOut
return (Script.ValidatorHash $ Api.getScriptHash $ toScriptHash val, val)

-- | Datums from initial distributions should be accounted for in the
-- `MockChainSt` which is done using this function.
Expand Down Expand Up @@ -222,15 +235,13 @@ utxoIndex0From (InitialDistribution initDist) = case mkBody of
where
mkBody :: Either GenerateTxError (Cardano.TxBody Cardano.ConwayEra)
mkBody = do
value <- mapLeft (ToCardanoError "Value error") $ Ledger.toCardanoValue (foldl' (\v -> (v <>) . view txSkelOutValueL) mempty initDist)
let mintValue = flip (Cardano.TxMintValue Cardano.MaryEraOnwardsConway) (Cardano.BuildTxWith mempty) . Cardano.filterValue (/= Cardano.AdaAssetId) $ value
theNetworkId = Cardano.Testnet $ Cardano.NetworkMagic 42
let theNetworkId = Cardano.Testnet $ Cardano.NetworkMagic 42
genesisKeyHash = Cardano.GenesisUTxOKeyHash $ Shelley.KeyHash "23d51e91ae5adc7ae801e9de4cd54175fb7464ec2680b25686bbb194"
inputs = [(Cardano.genesisUTxOPseudoTxIn theNetworkId genesisKeyHash, Cardano.BuildTxWith $ Cardano.KeyWitness Cardano.KeyWitnessForSpending)]
outputs <- mapM (generateTxOut theNetworkId) initDist
left (TxBodyError "Body error") $
Cardano.createAndValidateTransactionBody Cardano.ShelleyBasedEraConway $
Ledger.emptyTxBodyContent {Cardano.txMintValue = mintValue, Cardano.txOuts = outputs, Cardano.txIns = inputs}
Ledger.emptyTxBodyContent {Cardano.txOuts = outputs, Cardano.txIns = inputs}

utxoIndex0 :: Ledger.UtxoIndex
utxoIndex0 = utxoIndex0From def
11 changes: 11 additions & 0 deletions src/Cooked/Skeleton.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module Cooked.Skeleton
withStakingCredential,
TxSkelRedeemer (..),
Redeemer (..),
RedeemerConstrs,
withReferenceInput,
TxParameterChange (..),
TxGovAction (..),
Expand Down Expand Up @@ -98,6 +99,7 @@ module Cooked.Skeleton
txSkelReferenceTxOutRefs,
someTxSkelRedeemer,
emptyTxSkelRedeemer,
toTypedRedeemer,
)
where

Expand All @@ -122,6 +124,7 @@ import Data.Map.NonEmpty qualified as NEMap
import Data.Maybe
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Typeable (cast)
import Ledger.Slot qualified as Ledger
import Optics.Core
import Optics.TH
Expand Down Expand Up @@ -430,6 +433,11 @@ data TxSkelRedeemer = TxSkelRedeemer
}
deriving (Show, Eq)

-- Attempts to case a redeemer to a certain type
toTypedRedeemer :: (Typeable a) => Redeemer -> Maybe a
toTypedRedeemer (SomeRedeemer red) = cast red
toTypedRedeemer EmptyRedeemer = Nothing

-- Two helpers to create skeleton redeemers
someTxSkelRedeemer :: (RedeemerConstrs redeemer) => redeemer -> TxSkelRedeemer
someTxSkelRedeemer a = TxSkelRedeemer (SomeRedeemer a) Nothing
Expand Down Expand Up @@ -762,6 +770,9 @@ instance IsTxSkelOutAllowedOwner Api.PubKeyHash where
instance IsTxSkelOutAllowedOwner (Script.TypedValidator a) where
toPKHOrValidator = Right . Script.tvValidator

instance IsTxSkelOutAllowedOwner (Either Api.PubKeyHash (Script.Versioned Script.Validator)) where
toPKHOrValidator = id

-- | Transaction outputs. The 'Pays' constructor is really general, and you'll
-- probably want to use one of the smart constructors like 'paysScript' or
-- 'paysPK' in most cases.
Expand Down
4 changes: 3 additions & 1 deletion src/Cooked/Tweak.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
-- time using `Cooked.Ltl`
module Cooked.Tweak (module X) where

import Cooked.Tweak.AddInputsAndOutputs as X
import Cooked.Tweak.Common as X hiding
( Tweak,
UntypedTweak,
runTweakInChain,
runTweakInChain',
)
import Cooked.Tweak.Inputs as X
import Cooked.Tweak.Labels as X
import Cooked.Tweak.Mint as X
import Cooked.Tweak.OutPermutations as X hiding (distinctPermutations)
import Cooked.Tweak.Outputs as X
import Cooked.Tweak.Signers as X
import Cooked.Tweak.TamperDatum as X
import Cooked.Tweak.ValidityRange as X
Loading
Loading