diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a147f2ef9e..9dda6cf0d0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,8 +8,7 @@ Pre-submit checklist: - [ ] Tests are provided (if possible) - [ ] Commit sequence broadly makes sense - [ ] Key commits have useful messages - - [ ] Relevant tickets are mentioned in commit messages - - [ ] Formatting, materialized Nix files, PNG optimization, etc. are updated + - [ ] Formatting, PNG optimization, etc. are updated - PR - [ ] Self-reviewed the diff - [ ] Useful pull request description diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a10e60b0b..47bfe4fef6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - uses: nixbuild/nix-quick-install-action@v13 + - uses: nixbuild/nix-quick-install-action@v14 - run: nix-instantiate release.nix --arg supportedSystems '[ builtins.currentSystem ]' --restrict-eval -I . --allowed-uris 'https://github.com/NixOS/nixpkgs https://github.com/input-output-hk https://github.com/NixOS/nixpkgs-channels' --option trusted-public-keys "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" --option substituters "https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/" --show-trace nix-tests: strategy: @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - uses: nixbuild/nix-quick-install-action@v13 + - uses: nixbuild/nix-quick-install-action@v14 - run: nix-build -A tests.nixpkgsFmt -A tests.purs-tidy -A tests.shellcheck -A tests.stylishHaskell -A tests.generated --arg supportedSystems '[ builtins.currentSystem ]' --restrict-eval -I . --allowed-uris 'https://github.com/NixOS/nixpkgs https://github.com/input-output-hk https://github.com/NixOS/nixpkgs-channels' --option trusted-public-keys "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" --option substituters "https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/" check-for-updates: strategy: @@ -27,7 +27,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - uses: nixbuild/nix-quick-install-action@v13 + - uses: nixbuild/nix-quick-install-action@v14 - run: | nix --extra-experimental-features 'nix-command flakes' --extra-experimental-features flakes flake lock --option trusted-public-keys "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" --option substituters "https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/" nix-shell --extra-experimental-features 'nix-command flakes' --command "cd plutus-playground-client && (update-client-deps || update-client-deps)" --option trusted-public-keys "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" --option substituters "https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/" # Double-call to work around bug in spago2nix on first fetch diff --git a/cabal.project b/cabal.project index 9ead6933fe..53227fc3a8 100644 --- a/cabal.project +++ b/cabal.project @@ -16,6 +16,7 @@ packages: doc plutus-playground-server plutus-script-utils plutus-use-cases + plutus-streaming quickcheck-dynamic web-ghc @@ -315,3 +316,9 @@ source-repository-package location: https://github.com/haskell-works/hw-aeson tag: d99d2f3e39a287607418ae605b132a3deb2b753f --sha256: 1vxqcwjg9q37wbwi27y9ba5163lzfz51f1swbi0rp681yg63zvn4 + +-- Temporary indexing +source-repository-package + type: git + location: https://github.com/raduom/hysterical-screams + tag: f3bbd38a19f99de5c8ddc650c94330b2d09a865b diff --git a/doc/plutus/tutorials/Escrow6.hs b/doc/plutus/tutorials/Escrow6.hs index baeec70b0e..c41e1175fd 100644 --- a/doc/plutus/tutorials/Escrow6.hs +++ b/doc/plutus/tutorials/Escrow6.hs @@ -275,6 +275,6 @@ prop_CrashTolerance = CM.propRunActions_ check_propEscrowWithCoverage :: IO () check_propEscrowWithCoverage = do - cr <- CM.quickCheckWithCoverage stdArgs (set coverageIndex covIdx defaultCoverageOptions) $ \covopts -> + cr <- CM.quickCheckWithCoverage stdArgs (set coverageIndex covIdx $ defaultCoverageOptions) $ \covopts -> withMaxSuccess 1000 $ CM.propRunActionsWithOptions @EscrowModel CM.defaultCheckOptionsContractModel covopts (const (pure True)) writeCoverageReport "Escrow" covIdx cr diff --git a/doc/plutus/tutorials/EscrowImpl.hs b/doc/plutus/tutorials/EscrowImpl.hs index 8ce7fbc476..2a2bd691b9 100644 --- a/doc/plutus/tutorials/EscrowImpl.hs +++ b/doc/plutus/tutorials/EscrowImpl.hs @@ -69,8 +69,9 @@ import Plutus.V1.Ledger.Api (Datum (Datum), DatumHash, ValidatorHash) import Plutus.V1.Ledger.Contexts (ScriptContext (ScriptContext, scriptContextTxInfo), TxInfo (txInfoValidRange)) import Plutus.Contract (AsContractError (_ContractError), Contract, ContractError, Endpoint, HasEndpoint, Promise, - awaitTime, currentTime, endpoint, mapError, mkTxConstraints, ownPaymentPubKeyHash, promiseMap, - selectList, submitUnbalancedTx, type (.\/), utxosAt, waitNSlots) + adjustUnbalancedTx, awaitTime, currentTime, endpoint, mapError, mkTxConstraints, + ownPaymentPubKeyHash, promiseMap, selectList, submitUnbalancedTx, type (.\/), utxosAt, + waitNSlots) import Plutus.Contract.Typed.Tx qualified as Typed import PlutusTx qualified {- START imports -} @@ -80,7 +81,7 @@ import PlutusTx.Coverage qualified as PlutusTx import PlutusTx.Prelude (Bool (False), Either (Left, Right), all, either, foldl, id, mempty, traceIfFalse, ($), (&&), (+), (-), (.), (<$>), (==), (>=)) -import Prelude (Semigroup ((<>)), foldMap) +import Prelude (Semigroup ((<>)), foldMap, (>>=)) import Prelude qualified as Haskell type EscrowSchema = @@ -269,8 +270,8 @@ pay inst escrow vl = do pk <- ownPaymentPubKeyHash let tx = Constraints.mustPayToTheScript pk vl <> Constraints.mustValidateIn (Ledger.interval 1 (escrowDeadline escrow)) - utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx - getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx >>= adjustUnbalancedTx + getCardanoTxId <$> submitUnbalancedTx utx newtype RedeemSuccess = RedeemSuccess TxId deriving (Haskell.Eq, Haskell.Show) @@ -312,8 +313,8 @@ redeem inst escrow = mapError (review _EscrowError) $ do else do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx - RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx >>= adjustUnbalancedTx + RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx utx newtype RefundSuccess = RefundSuccess TxId deriving newtype (Haskell.Eq, Haskell.Show, Generic) @@ -344,8 +345,8 @@ refund inst escrow = do then do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx' - RefundSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx' >>= adjustUnbalancedTx + RefundSuccess . getCardanoTxId <$> submitUnbalancedTx utx else throwing _RefundFailed () -- | Pay some money into the escrow contract. Then release all funds to their diff --git a/nix/pkgs/haskell/default.nix b/nix/pkgs/haskell/default.nix index 7f3da461fb..aa7cedf06e 100644 --- a/nix/pkgs/haskell/default.nix +++ b/nix/pkgs/haskell/default.nix @@ -25,10 +25,8 @@ let in parseIndexState (builtins.readFile ../../../cabal.project); - # The compiler that we are using. We are using a patched version so we need to specify it explicitly. - # This version has the experimental core interface files patch, and a fix for unboxed tuples in - # GHCi, which helps with HLS. - compiler-nix-name = "ghc810420210212"; + # The compiler that we are using + compiler-nix-name = "ghc8107"; # The haskell project created by haskell-nix.stackProject' baseProject = diff --git a/nix/pkgs/haskell/extra.nix b/nix/pkgs/haskell/extra.nix index 7adb549e40..317a02d9a8 100644 --- a/nix/pkgs/haskell/extra.nix +++ b/nix/pkgs/haskell/extra.nix @@ -44,8 +44,6 @@ let "https://github.com/haskell/lsp.git"."ef59c28b41ed4c5775f0ab0c1e985839359cec96" = "1whcgw4hhn2aplrpy9w8q6rafwy7znnp0rczgr6py15fqyw2fwb5"; }; modules = [{ - # for compatibility with the GHC patch for extensible interfaces, not needed on mainline GHC. - packages.ghcide.patches = [ ../../patches/ghcide_partial_iface.patch ]; # Workaround for https://github.com/haskell/haskell-language-server/issues/1160 packages.haskell-language-server.patches = lib.mkIf stdenv.isDarwin [ ../../patches/haskell-language-server-dynamic.patch ]; # See https://github.com/haskell/haskell-language-server/pull/1382#issuecomment-780472005 diff --git a/nix/pkgs/haskell/haskell.nix b/nix/pkgs/haskell/haskell.nix index a9a49e495a..9fee51389c 100644 --- a/nix/pkgs/haskell/haskell.nix +++ b/nix/pkgs/haskell/haskell.nix @@ -61,6 +61,7 @@ let plutus-pab-executables.package.buildable = false; plutus-playground-server.package.buildable = false; # Would also require libpq plutus-script-utils.package.buildable = false; + plutus-streaming.package.buildable = false; plutus-tx-plugin.package.buildable = false; plutus-use-cases.package.buildable = false; plutus-example.package.buildable = false; diff --git a/nix/pkgs/haskell/sha256map.nix b/nix/pkgs/haskell/sha256map.nix index 22e1c581e0..1a907e6f85 100644 --- a/nix/pkgs/haskell/sha256map.nix +++ b/nix/pkgs/haskell/sha256map.nix @@ -19,4 +19,5 @@ "https://github.com/input-output-hk/Win32-network"."3825d3abf75f83f406c1f7161883c438dac7277d" = "19wahfv726fa3mqajpqdqhnl9ica3xmf68i254q45iyjcpj1psqx"; "https://github.com/Quid2/flat"."ee59880f47ab835dbd73bea0847dab7869fc20d8" = "1lrzknw765pz2j97nvv9ip3l1mcpf2zr4n56hwlz0rk7wq7ls4cm"; "https://github.com/vshabanov/ekg-json"."00ebe7211c981686e65730b7144fbf5350462608" = "sha256-VT8Ur585TCn03P2TVi6t92v2Z6tl8vKijICjse6ocv8="; + "https://github.com/raduom/hysterical-screams"."f3bbd38a19f99de5c8ddc650c94330b2d09a865b" = "1sy9l3sz1vxw7si9sdhh30p3zzadxh346856kdk4qg45j0m0gf1l"; } diff --git a/plutus-chain-index/app/Indexer.hs b/plutus-chain-index/app/Indexer.hs new file mode 100644 index 0000000000..0672156355 --- /dev/null +++ b/plutus-chain-index/app/Indexer.hs @@ -0,0 +1,146 @@ +{-# LANGUAGE ExplicitForAll #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TupleSections #-} + +module Main where + +import Cardano.Api (Block (Block), BlockHeader (BlockHeader), BlockInMode (BlockInMode), BlockNo (BlockNo), CardanoMode, + ChainPoint (ChainPoint), Hash, NetworkId (Mainnet), SlotNo (SlotNo), chainPointToSlotNo, + deserialiseFromRawBytesHex, proxyToAsType) +import Cardano.Api qualified as C +import Cardano.BM.Trace (nullTracer) +import Cardano.Protocol.Socket.Client (ChainSyncEvent (Resume, RollBackward, RollForward), runChainSync) +import Control.Concurrent (threadDelay) +import Control.Lens.Operators ((^.)) +import Control.Monad (forever, when) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.List (findIndex) +import Data.Map (assocs) +import Data.Maybe (fromJust, fromMaybe) +import Data.Proxy (Proxy (Proxy)) +import Data.Text (pack) +import Data.Text.Encoding (encodeUtf8) +import Index.VSplit qualified as Ix +import Ledger.TimeSlot (SlotConfig (..)) +import Marconi.Index.Datum (DatumIndex) +import Marconi.Index.Datum qualified as Ix +import Options.Applicative (Parser, execParser, fullDesc, header, help, helper, info, long, metavar, progDesc, + strOption, (<**>)) +import Plutus.ChainIndex.Tx (ChainIndexTx (..)) +import Plutus.Contract.CardanoAPI (fromCardanoTx) +import Plutus.Script.Utils.V1.Scripts (Datum, DatumHash) + +{- | This executable is meant to exercise a set of indexers (for now datumhash -> datum) + against the mainnet (meant to be used for testing). + + In case you want to access the results of the datumhash indexer you need to query + the resulting database: + $ sqlite3 datums.sqlite + > select slotNo, datumHash, datum from kv_datumhsh_datum where slotNo = 39920450; + 39920450|679a55b523ff8d61942b2583b76e5d49498468164802ef1ebe513c685d6fb5c2|X(002f9787436835852ea78d3c45fc3d436b324184 +-} + +-- Options +data Options = Options + { socketPath :: FilePath + , dbPath :: FilePath + } + +options :: Parser Options +options = Options + <$> strOption + ( long "socket" + <> metavar "SOCKET" + <> help "Path to node socket." ) + <*> strOption + ( long "database" + <> metavar "DATABASE" + <> help "Path to database." ) + +-- We only care about the mainnet +slotConfig :: SlotConfig +slotConfig = + SlotConfig + { scSlotZeroTime = 1596059091000 + , scSlotLength = 1000 + } + +networkId :: NetworkId +networkId = Mainnet + +-- We don't generally need to sync blocks earlier than the Goguen era (other than +-- testing for memory leaks) so we may want to start synchronising from a slot that +-- is closer to Goguen era. +closeToGoguen :: ChainPoint +closeToGoguen = + ChainPoint + (SlotNo 39795032) + (fromJust $ parseHash "3e6f6450f85962d651654ee66091980b2332166f5505fd10b97b0520c9efac90") + +parseHash :: String -> Maybe (Hash BlockHeader) +parseHash hash = + deserialiseFromRawBytesHex (proxyToAsType Proxy) (encodeUtf8 $ pack hash) + +getDatums :: BlockInMode CardanoMode -> [(SlotNo, (DatumHash, Datum))] +getDatums (BlockInMode (Block (BlockHeader slotNo _ _) txs) era) = + case era of + C.ByronEraInCardanoMode -> concatMap (go era) txs + C.ShelleyEraInCardanoMode -> concatMap (go era) txs + C.AllegraEraInCardanoMode -> concatMap (go era) txs + C.MaryEraInCardanoMode -> concatMap (go era) txs + C.AlonzoEraInCardanoMode -> concatMap (go era) txs + C.BabbageEraInCardanoMode -> concatMap (go era) txs + where + go :: C.IsCardanoEra era + => C.EraInMode era C.CardanoMode + -> C.Tx era + -> [(SlotNo, (DatumHash, Datum))] + go era' tx = + let hashes = either (const []) (assocs . _citxData) $ fromCardanoTx era' tx + in map (slotNo,) hashes + +processBlock :: IORef DatumIndex -> ChainSyncEvent -> IO () +processBlock ixref = \case + -- Not really supported + Resume point -> putStrLn ("resume " <> show point) >> pure () + RollForward blk@(BlockInMode (Block (BlockHeader slotNo _ blockNo@(BlockNo b)) _txs) _era) _tip -> do + when (b `rem` 1000 == 0) $ + putStrLn $ show slotNo <> " / " <> show blockNo + ix <- readIORef ixref + ix' <- Ix.insert (getDatums blk) ix + writeIORef ixref ix' + RollBackward point tip -> do + putStrLn ("rollback to " <> show tip) + rollbackToPoint point ixref + +rollbackToPoint + :: ChainPoint -> IORef DatumIndex -> IO () +rollbackToPoint point ixref = do + ix <- readIORef ixref + events <- Ix.getEvents (ix ^. Ix.storage) + let ix' = fromMaybe ix $ rollbackOffset events ix + writeIORef ixref ix' + where + rollbackOffset :: [Ix.Event] -> DatumIndex -> Maybe DatumIndex + rollbackOffset events ix = do + slot <- chainPointToSlotNo point + offset <- findIndex (any (\(s, _) -> s < slot)) events + Ix.rewind offset ix + +main :: IO () +main = do + options' <- execParser opts + tix <- Ix.open (dbPath options') (Ix.Depth 2160) >>= newIORef + _ <- runChainSync (socketPath options') + nullTracer + slotConfig + networkId + [closeToGoguen] + (processBlock tix) + forever $ threadDelay 1000000000 + where + opts = info (options <**> helper) + ( fullDesc + <> progDesc "Synchronise datums with mainnet" + <> header "indexer - an indexing proof of concept" ) diff --git a/plutus-chain-index/plutus-chain-index.cabal b/plutus-chain-index/plutus-chain-index.cabal index 7931e3e0f5..6472d487e6 100644 --- a/plutus-chain-index/plutus-chain-index.cabal +++ b/plutus-chain-index/plutus-chain-index.cabal @@ -37,7 +37,9 @@ library Plutus.ChainIndex.Lib Plutus.ChainIndex.Logging Plutus.ChainIndex.SyncStats - other-modules: Control.Concurrent.STM.TBMQueue + Marconi.Index.Datum + other-modules: + Control.Concurrent.STM.TBMQueue hs-source-dirs: src build-depends: plutus-ledger -any, @@ -65,6 +67,9 @@ library stm -any, time-units -any, yaml -any, + serialise -any, + hysterical-screams -any, + bytestring -any, executable plutus-chain-index main-is: Main.hs @@ -78,3 +83,26 @@ executable plutus-chain-index build-depends: base >=4.9 && <5, plutus-chain-index -any + +executable plutus-indexer + main-is: Indexer.hs + hs-source-dirs: app + default-language: Haskell2010 + default-extensions: ImportQualifiedPost + ghc-options: + -threaded -rtsopts -with-rtsopts=-N -Wall -Wcompat + -Wincomplete-uni-patterns -Wincomplete-record-updates + -Wno-missing-import-lists -Wredundant-constraints -O0 + build-depends: + base >=4.9 && <5, + plutus-chain-index -any, + iohk-monitoring -any, + cardano-api -any, + plutus-ledger -any, + plutus-chain-index-core -any, + plutus-script-utils -any, + containers -any, + lens -any, + hysterical-screams -any, + text -any, + optparse-applicative -any, diff --git a/plutus-chain-index/src/Marconi/Index/Datum.hs b/plutus-chain-index/src/Marconi/Index/Datum.hs new file mode 100644 index 0000000000..f8fedaa182 --- /dev/null +++ b/plutus-chain-index/src/Marconi/Index/Datum.hs @@ -0,0 +1,110 @@ +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Marconi.Index.Datum + ( -- * DatumIndex + DatumIndex + , Event + , Query + , Result + , Notification + , Depth(..) + , open + ) where + +import Codec.Serialise (deserialiseOrFail, serialise) +import Control.Applicative ((<|>)) +import Control.Lens.Operators ((^.)) +import Data.ByteString.Lazy (toStrict) +import Data.Foldable (find) +import Data.Maybe (fromJust, listToMaybe) +import Data.String (fromString) +import Database.SQLite.Simple (Only (Only), SQLData (SQLBlob, SQLInteger, SQLText)) +import Database.SQLite.Simple qualified as SQL +import Database.SQLite.Simple.FromField (FromField (fromField), ResultError (ConversionFailed), returnError) +import Database.SQLite.Simple.ToField (ToField (toField)) + +import Cardano.Api (SlotNo (SlotNo)) +import Index.VSqlite (SqliteIndex) +import Index.VSqlite qualified as Ix +import Ledger.Scripts (Datum, DatumHash) + +type Event = [(SlotNo, (DatumHash, Datum))] +type Query = DatumHash +type Result = Maybe Datum +type Notification = () + +type DatumIndex = SqliteIndex Event Notification Query Result + +newtype Depth = Depth Int + +instance FromField DatumHash where + fromField f = fromString <$> fromField f + +instance ToField DatumHash where + toField = SQLText . fromString . show + +instance FromField Datum where + fromField f = fromField f >>= + either (const $ returnError ConversionFailed f "Cannot deserialise datum.") + pure + . deserialiseOrFail + +instance ToField Datum where + toField = SQLBlob . toStrict . serialise + +instance FromField SlotNo where + fromField f = SlotNo <$> fromField f + +instance ToField SlotNo where + toField (SlotNo s) = SQLInteger $ fromIntegral s + +open + :: FilePath + -> Depth + -> IO DatumIndex +open dbPath (Depth k) = do + ix <- fromJust <$> Ix.newBoxed query store onInsert k ((k + 1) * 2) dbPath + let c = ix ^. Ix.handle + SQL.execute_ c "CREATE TABLE IF NOT EXISTS kv_datumhsh_datum (datumHash TEXT PRIMARY KEY, datum BLOB, slotNo INT)" + pure ix + +-- | This function is used to query the data stored in the indexer as a whole: +-- data that can still change (through rollbacks), buffered data and stored data. +-- +-- Here is a query that takes into account all received events: +-- > getEvents (ix ^. storage) >>= query ix +-- +-- Here is a query that only takes into account stored events: +-- > query ix [] +query + :: DatumIndex -- ^ The indexer + -> Query -- ^ The query is a `DatumHash` + -> [Event] -- ^ The list of events that we want to query on top of whatever is settled. + -> IO Result -- ^ The result is an optional datum. +query ix hsh es = memoryResult <|> sqliteResult + where + -- TODO: Consider buffered events + memoryResult :: IO Result + memoryResult = do + bufferedEvents <- Ix.getBuffer (ix ^. Ix.storage) + pure $ snd . snd <$> find ((== hsh) . fst . snd) (concat $ es ++ bufferedEvents) + sqliteResult :: IO Result + sqliteResult = do + result <- SQL.query (ix ^. Ix.handle) "SELECT datum from kv_datumhsh_datum WHERE datumHash = ?" (Only hsh) + pure $ head <$> listToMaybe result + +store :: DatumIndex -> IO () +store ix = do + let c = ix ^. Ix.handle + SQL.execute_ c "BEGIN" + Ix.getBuffer (ix ^. Ix.storage) >>= + mapM_ (SQL.execute c "INSERT INTO kv_datumhsh_datum (slotNo, datumHash, datum) VALUES (?,?,?) ON CONFLICT(datumHash) DO UPDATE SET slotNo = ?") . map unpack . concat + SQL.execute_ c "COMMIT" + where + unpack :: (SlotNo, (DatumHash, Datum)) -> (SlotNo, DatumHash, Datum, SlotNo) + unpack (s, (h, d)) = (s, h, d, s) + +onInsert :: DatumIndex -> Event -> IO [Notification] +onInsert _ _ = pure [] diff --git a/plutus-contract/plutus-contract.cabal b/plutus-contract/plutus-contract.cabal index 1768912c94..d481880d35 100644 --- a/plutus-contract/plutus-contract.cabal +++ b/plutus-contract/plutus-contract.cabal @@ -112,6 +112,7 @@ library build-depends: cardano-api -any, cardano-crypto -any, + cardano-ledger-core -any, build-depends: aeson >= 1.5.2, aeson-pretty -any, @@ -168,6 +169,7 @@ library Plutus.Contract.Test.ContractModel.Internal Plutus.Contract.Test.ContractModel.Symbolics Plutus.Contract.Test.ContractModel.CrashTolerance + Plutus.Contract.Test.MissingLovelace build-depends: tasty -any, tasty-hunit -any, @@ -194,6 +196,7 @@ test-suite plutus-contract-test Spec.Rows Spec.State Spec.ThreadToken + Spec.TimeValidity Spec.Secrets Spec.Plutus.Contract.Wallet Spec.Plutus.Contract.Oracle diff --git a/plutus-contract/src/Plutus/Contract.hs b/plutus-contract/src/Plutus/Contract.hs index 8182c1ae38..2c859dec61 100644 --- a/plutus-contract/src/Plutus/Contract.hs +++ b/plutus-contract/src/Plutus/Contract.hs @@ -71,6 +71,7 @@ module Plutus.Contract( , tell -- * Transactions , WalletAPIError + , Request.adjustUnbalancedTx , Request.submitTx , Request.submitTxConfirmed , Request.submitTxConstraints diff --git a/plutus-contract/src/Plutus/Contract/Effects.hs b/plutus-contract/src/Plutus/Contract/Effects.hs index 6a1ee78953..017873b80b 100644 --- a/plutus-contract/src/Plutus/Contract/Effects.hs +++ b/plutus-contract/src/Plutus/Contract/Effects.hs @@ -8,6 +8,7 @@ module Plutus.Contract.Effects( -- TODO: Move to Requests.Internal -- * Plutus application backend request effect types PABReq(..), + _AdjustUnbalancedTxReq, _AwaitSlotReq, _AwaitTimeReq, _AwaitUtxoSpentReq, @@ -40,6 +41,7 @@ module Plutus.Contract.Effects( -- TODO: Move to Requests.Internal _GetTip, -- * Plutus application backend response effect types PABResp(..), + _AdjustUnbalancedTxResp, _AwaitSlotResp, _AwaitTimeResp, _AwaitUtxoSpentResp, @@ -102,16 +104,18 @@ import Plutus.ChainIndex.Api (IsUtxoResponse (IsUtxoResponse), TxosResponse (Txo UtxosResponse (UtxosResponse)) import Plutus.ChainIndex.Tx (ChainIndexTx (_citxTxId)) import Plutus.ChainIndex.Types (Tip, TxOutStatus, TxStatus) +import Plutus.Contract.CardanoAPI (ToCardanoError) import Plutus.V1.Ledger.Api (Address, Datum, DatumHash, MintingPolicy, MintingPolicyHash, Redeemer, RedeemerHash, StakeValidator, StakeValidatorHash, TxId, TxOutRef, ValidatorHash) import Plutus.V1.Ledger.Value (AssetClass) import Prettyprinter (Pretty (pretty), hsep, indent, viaShow, vsep, (<+>)) -import Wallet.API (WalletAPIError) +import Wallet.Error (WalletAPIError) import Wallet.Types (ContractInstanceId, EndpointDescription, EndpointValue) -- | Requests that 'Contract's can make data PABReq = - AwaitSlotReq Slot + AdjustUnbalancedTxReq UnbalancedTx + | AwaitSlotReq Slot | AwaitTimeReq POSIXTime | AwaitUtxoSpentReq TxOutRef | AwaitUtxoProducedReq Address @@ -132,6 +136,7 @@ data PABReq = instance Pretty PABReq where pretty = \case + AdjustUnbalancedTxReq utx -> "Adjust unbalanced tx:" <+> pretty utx AwaitSlotReq s -> "Await slot:" <+> pretty s AwaitTimeReq s -> "Await time:" <+> pretty s AwaitUtxoSpentReq utxo -> "Await utxo spent:" <+> pretty utxo @@ -151,7 +156,8 @@ instance Pretty PABReq where -- | Responses that 'Contract's receive data PABResp = - AwaitSlotResp Slot + AdjustUnbalancedTxResp (Either ToCardanoError UnbalancedTx) + | AwaitSlotResp Slot | AwaitTimeResp POSIXTime | AwaitUtxoSpentResp ChainIndexTx | AwaitUtxoProducedResp (NonEmpty ChainIndexTx) @@ -172,6 +178,7 @@ data PABResp = instance Pretty PABResp where pretty = \case + AdjustUnbalancedTxResp utx -> "Adjusted unbalanced tx: " <+> pretty utx AwaitSlotResp s -> "Slot:" <+> pretty s AwaitTimeResp s -> "Time:" <+> pretty s AwaitUtxoSpentResp utxo -> "Utxo spent:" <+> pretty utxo @@ -191,6 +198,7 @@ instance Pretty PABResp where matches :: PABReq -> PABResp -> Bool matches a b = case (a, b) of + (AdjustUnbalancedTxReq{}, AdjustUnbalancedTxResp{}) -> True (AwaitSlotReq{}, AwaitSlotResp{}) -> True (AwaitTimeReq{}, AwaitTimeResp{}) -> True (AwaitUtxoSpentReq{}, AwaitUtxoSpentResp{}) -> True @@ -352,7 +360,7 @@ data WriteBalancedTxResponse = instance Pretty WriteBalancedTxResponse where pretty = \case WriteBalancedTxFailed e -> "WriteBalancedTxFailed:" <+> pretty e - WriteBalancedTxSuccess tx -> "WriteBalancedTxFailed:" <+> pretty (getCardanoTxId tx) + WriteBalancedTxSuccess tx -> "WriteBalancedTxSuccess:" <+> pretty (getCardanoTxId tx) writeBalancedTxResponse :: Iso' WriteBalancedTxResponse (Either WalletAPIError CardanoTx) writeBalancedTxResponse = iso f g where diff --git a/plutus-contract/src/Plutus/Contract/Error.hs b/plutus-contract/src/Plutus/Contract/Error.hs index b504fdfdc9..3fbc69e499 100644 --- a/plutus-contract/src/Plutus/Contract/Error.hs +++ b/plutus-contract/src/Plutus/Contract/Error.hs @@ -27,6 +27,7 @@ import Prettyprinter (Pretty (pretty), viaShow, (<+>)) import Data.Aeson qualified as JSON import Ledger.Constraints.OffChain (MkTxError) +import Plutus.Contract.CardanoAPI (ToCardanoError) import Plutus.Contract.Checkpoint (AsCheckpointError (_CheckpointError), CheckpointError) import Plutus.Contract.Effects (ChainIndexResponse) import Wallet.Error (WalletAPIError) @@ -61,6 +62,7 @@ data ContractError = | ChainIndexContractError T.Text ChainIndexResponse | EmulatorAssertionContractError AssertionError -- TODO: Why do we need this constructor | ConstraintResolutionContractError MkTxError + | TxToCardanoConvertContractError ToCardanoError | ResumableContractError MatchingError | CCheckpointContractError CheckpointError | EndpointDecodeContractError @@ -86,6 +88,7 @@ instance Pretty ContractError where <+> pretty actualResp EmulatorAssertionContractError a -> "Emulator assertion error:" <+> pretty a ConstraintResolutionContractError e -> "Constraint resolution error:" <+> pretty e + TxToCardanoConvertContractError e -> "To Cardano transaction conversation error:" <+> pretty e ResumableContractError e -> "Resumable error:" <+> pretty e CCheckpointContractError e -> "Checkpoint error:" <+> pretty e EndpointDecodeContractError (EndpointDescription ed) (EndpointValue ev) err diff --git a/plutus-contract/src/Plutus/Contract/Request.hs b/plutus-contract/src/Plutus/Contract/Request.hs index 46dc216783..c89efc9e32 100644 --- a/plutus-contract/src/Plutus/Contract/Request.hs +++ b/plutus-contract/src/Plutus/Contract/Request.hs @@ -79,6 +79,7 @@ module Plutus.Contract.Request( -- ** Public key hashes , ownPaymentPubKeyHash -- ** Submitting transactions + , adjustUnbalancedTx , submitUnbalancedTx , submitBalancedTx , balanceTx @@ -129,7 +130,7 @@ import Plutus.V1.Ledger.Api (Address, Datum, DatumHash, MintingPolicy, MintingPo import PlutusTx qualified import Plutus.Contract.Effects (ActiveEndpoint (ActiveEndpoint, aeDescription, aeMetadata), - PABReq (AwaitSlotReq, AwaitTimeReq, AwaitTxOutStatusChangeReq, AwaitTxStatusChangeReq, AwaitUtxoProducedReq, AwaitUtxoSpentReq, BalanceTxReq, ChainIndexQueryReq, CurrentSlotReq, CurrentTimeReq, ExposeEndpointReq, OwnContractInstanceIdReq, OwnPaymentPublicKeyHashReq, WriteBalancedTxReq, YieldUnbalancedTxReq), + PABReq (AdjustUnbalancedTxReq, AwaitSlotReq, AwaitTimeReq, AwaitTxOutStatusChangeReq, AwaitTxStatusChangeReq, AwaitUtxoProducedReq, AwaitUtxoSpentReq, BalanceTxReq, ChainIndexQueryReq, CurrentSlotReq, CurrentTimeReq, ExposeEndpointReq, OwnContractInstanceIdReq, OwnPaymentPublicKeyHashReq, WriteBalancedTxReq, YieldUnbalancedTxReq), PABResp (ExposeEndpointResp)) import Plutus.Contract.Effects qualified as E import Plutus.Contract.Logging (logDebug) @@ -140,7 +141,7 @@ import Wallet.Types (ContractInstanceId, EndpointDescription (EndpointDescriptio import Plutus.ChainIndex (ChainIndexTx, Page (nextPageQuery, pageItems), PageQuery, txOutRefs) import Plutus.ChainIndex.Api (IsUtxoResponse, TxosResponse, UtxosResponse (page), paget) import Plutus.ChainIndex.Types (RollbackState (Unknown), Tip, TxOutStatus, TxStatus) -import Plutus.Contract.Error (AsContractError (_ChainIndexContractError, _ConstraintResolutionContractError, _EndpointDecodeContractError, _ResumableContractError, _WalletContractError)) +import Plutus.Contract.Error (AsContractError (_ChainIndexContractError, _ConstraintResolutionContractError, _EndpointDecodeContractError, _ResumableContractError, _TxToCardanoConvertContractError, _WalletContractError)) import Plutus.Contract.Resumable (prompt) import Plutus.Contract.Types (Contract (Contract), MatchingError (WrongVariantError), Promise (Promise), mapError, runError, throwError) @@ -171,6 +172,17 @@ pabReq req prism = Contract $ do $ WrongVariantError $ "unexpected answer: " <> tshow x +-- | Adjust the unbalanced tx +adjustUnbalancedTx :: + forall w s e. + ( AsContractError e + ) + => UnbalancedTx + -> Contract w s e UnbalancedTx +adjustUnbalancedTx utx = + let req = pabReq (AdjustUnbalancedTxReq utx) E._AdjustUnbalancedTxResp in + req >>= either (throwError . review _TxToCardanoConvertContractError) pure + -- | Wait until the slot awaitSlot :: forall w s e. diff --git a/plutus-contract/src/Plutus/Contract/StateMachine.hs b/plutus-contract/src/Plutus/Contract/StateMachine.hs index 7adb2e7dc1..88e029e7f8 100644 --- a/plutus-contract/src/Plutus/Contract/StateMachine.hs +++ b/plutus-contract/src/Plutus/Contract/StateMachine.hs @@ -81,9 +81,9 @@ import Ledger.Typed.Tx qualified as Typed import Ledger.Value qualified as Value import Plutus.ChainIndex (ChainIndexTx (_citxInputs)) import Plutus.Contract (AsContractError (_ConstraintResolutionContractError, _ContractError), Contract, ContractError, - Promise, awaitPromise, isSlot, isTime, logWarn, mapError, never, ownPaymentPubKeyHash, - promiseBind, select, submitTxConfirmed, utxoIsProduced, utxoIsSpent, utxosAt, - utxosTxOutTxFromTx) + Promise, adjustUnbalancedTx, awaitPromise, isSlot, isTime, logWarn, mapError, never, + ownPaymentPubKeyHash, promiseBind, select, submitTxConfirmed, utxoIsProduced, utxoIsSpent, + utxosAt, utxosTxOutTxFromTx) import Plutus.Contract.Request (mkTxContract) import Plutus.Contract.StateMachine.MintingPolarity (MintingPolarity (Burn, Mint)) import Plutus.Contract.StateMachine.OnChain (State (State, stateData, stateValue), @@ -437,7 +437,7 @@ runInitialiseWith customLookups customConstraints StateMachineClient{scInstance} <> Constraints.unspentOutputs utxo <> customLookups utx <- mapError (review _ConstraintResolutionContractError) (mkTxContract lookups constraints) - let adjustedUtx = Constraints.adjustUnbalancedTx utx + adjustedUtx <- adjustUnbalancedTx utx unless (utx == adjustedUtx) $ logWarn @Text $ "Plutus.Contract.StateMachine.runInitialise: " <> "Found a transaction output value with less than the minimum amount of Ada. Adjusting ..." @@ -487,7 +487,7 @@ runGuardedStepWith userLookups userConstraints smc input guard = mapError (revie utx <- either (throwing _ConstraintResolutionContractError) pure (Constraints.mkTx (lookups <> userLookups) (smtConstraints <> userConstraints)) - let adjustedUtx = Constraints.adjustUnbalancedTx utx + adjustedUtx <- adjustUnbalancedTx utx unless (utx == adjustedUtx) $ logWarn @Text $ "Plutus.Contract.StateMachine.runStep: " <> "Found a transaction output value with less than the minimum amount of Ada. Adjusting ..." diff --git a/plutus-contract/src/Plutus/Contract/Test.hs b/plutus-contract/src/Plutus/Contract/Test.hs index 83b0059453..8cdca1a91b 100644 --- a/plutus-contract/src/Plutus/Contract/Test.hs +++ b/plutus-contract/src/Plutus/Contract/Test.hs @@ -71,6 +71,7 @@ module Plutus.Contract.Test( , minLogLevel , emulatorConfig , changeInitialWalletValue + , allowBigTransactions -- * Etc , goldenPir ) where @@ -130,8 +131,9 @@ import Plutus.V1.Ledger.Scripts qualified as PV1 import Data.IORef import Plutus.Contract.Test.Coverage +import Plutus.Contract.Test.MissingLovelace (calculateDelta) import Plutus.Contract.Trace as X -import Plutus.Trace.Emulator (EmulatorConfig (..), EmulatorTrace, runEmulatorStream) +import Plutus.Trace.Emulator (EmulatorConfig (..), EmulatorTrace, params, runEmulatorStream) import Plutus.Trace.Emulator.Types (ContractConstraints, ContractInstanceLog, ContractInstanceState (..), ContractInstanceTag, UserThreadMsg) import PlutusTx.Coverage @@ -183,6 +185,11 @@ defaultCheckOptions = changeInitialWalletValue :: Wallet -> (Value -> Value) -> CheckOptions -> CheckOptions changeInitialWalletValue wallet = over (emulatorConfig . initialChainState . _Left . ix wallet) +-- | Set higher limits on transaction size and execution units. +-- This can be used to work around @MaxTxSizeUTxO@ and @ExUnitsTooBigUTxO@ errors. +-- Note that if you need this your Plutus script will probably not validate on Mainnet. +allowBigTransactions :: CheckOptions -> CheckOptions +allowBigTransactions = over (emulatorConfig . params) Ledger.allowBigTransactions -- | Check if the emulator trace meets the condition checkPredicate :: @@ -565,11 +572,12 @@ walletFundsExactChange :: Wallet -> Value -> TracePredicate walletFundsExactChange = walletFundsChangeImpl True walletFundsChangeImpl :: Bool -> Wallet -> Value -> TracePredicate -walletFundsChangeImpl exact w dlt = TracePredicate $ - flip postMapM (L.generalize $ (,) <$> Folds.walletFunds w <*> Folds.walletFees w) $ \(finalValue', fees) -> do +walletFundsChangeImpl exact w dlt' = TracePredicate $ + flip postMapM (L.generalize $ (,,) <$> Folds.walletFunds w <*> Folds.walletFees w <*> Folds.walletsAdjustedTxEvents) $ \(finalValue', fees, allWalletsTxOutCosts) -> do dist <- ask @InitialDistribution let initialValue = fold (dist ^. at w) finalValue = finalValue' P.+ if exact then mempty else fees + dlt = calculateDelta dlt' (Ada.fromValue initialValue) (Ada.fromValue finalValue) allWalletsTxOutCosts result = initialValue P.+ dlt == finalValue unless result $ do tell @(Doc Void) $ vsep $ diff --git a/plutus-contract/src/Plutus/Contract/Test/ContractModel/Internal.hs b/plutus-contract/src/Plutus/Contract/Test/ContractModel/Internal.hs index d05f1e158e..93743b452a 100644 --- a/plutus-contract/src/Plutus/Contract/Test/ContractModel/Internal.hs +++ b/plutus-contract/src/Plutus/Contract/Test/ContractModel/Internal.hs @@ -186,7 +186,7 @@ import Data.Aeson qualified as JSON import Data.Data import Data.Foldable import Data.IORef -import Data.List +import Data.List as List import Data.Map (Map) import Data.Map qualified as Map import Data.Maybe @@ -195,8 +195,9 @@ import Data.Row.Records (labels') import Data.Set (Set) import Data.Set qualified as Set import Data.Text qualified as Text - import Ledger.Ada qualified as Ada +import Ledger.Params () + import Ledger.Index import Ledger.Slot import Ledger.Value (AssetClass) @@ -219,6 +220,7 @@ import Test.QuickCheck.StateModel hiding (Action, Actions (..), actionName, arbi nextState, pattern Actions, perform, precondition, shrinkAction, stateAfter) import Test.QuickCheck.StateModel qualified as StateModel +import Plutus.Contract.Test.MissingLovelace (calculateDelta) import Test.QuickCheck hiding (ShrinkState, checkCoverage, getSize, (.&&.), (.||.)) import Test.QuickCheck qualified as QC import Test.QuickCheck.Monadic (PropertyM, monadic) @@ -478,6 +480,7 @@ class ( Typeable state -- | The initial handles initialInstances :: [StartContract state] + initialInstances = [] -- | The `precondition` function decides if a given action is valid in a given state. Typically -- actions generated by `arbitraryAction` will satisfy the precondition, but if they don't @@ -1608,7 +1611,7 @@ checkBalances s envOuter = Map.foldrWithKey (\ w sval p -> walletFundsChange w s walletFundsChange w sval = TracePredicate $ -- see Note [The Env contract] flip postMapM ((,) <$> Folds.instanceOutcome @() (toContract getEnvContract) envContractInstanceTag - <*> L.generalize ((,) <$> Folds.walletFunds w <*> Folds.walletFees w)) $ \(outcome, (finalValue', fees)) -> do + <*> L.generalize ((,,) <$> Folds.walletFunds w <*> Folds.walletFees w <*> Folds.walletsAdjustedTxEvents)) $ \(outcome, (finalValue', fees, allWalletsTxOutCosts)) -> do dist <- Freer.ask @InitialDistribution case outcome of Done envInner -> do @@ -1619,18 +1622,25 @@ checkBalances s envOuter = Map.foldrWithKey (\ w sval p -> walletFundsChange w s lookup st = case lookupMaybe st of Nothing -> error $ "Trying to look up unknown symbolic token: " ++ show st ++ ",\nare you using a custom implementation of getAllSymtokens? If not, please report this as a bug." Just tok -> tok - dlt = toValue lookup sval + invert m = Map.fromList [(v, k) | (k, v) <- Map.toList m] + invLookup ac = do + (innerVar, idx) <- listToMaybe [ (innerVar, idx) | (innerVar, tm) <- Map.toList envInner + , idx <- maybeToList $ Map.lookup ac (invert tm) ] + outerVar <- invertLookupVarMaybe envOuter innerVar + return (SymToken outerVar idx) initialValue = fold (dist ^. at w) + dlt' = toValue lookup sval finalValue = finalValue' P.+ fees + dlt = calculateDelta dlt' (Ada.fromValue initialValue) (Ada.fromValue finalValue) allWalletsTxOutCosts result = initialValue P.+ dlt == finalValue unless result $ do tell @(Doc Void) $ vsep $ [ "Expected funds of" <+> pretty w <+> "to change by" - , " " <+> viaShow dlt] ++ + , " " <+> viaShow sval] ++ if initialValue == finalValue then ["but they did not change"] - else ["but they changed by", " " <+> viaShow (finalValue P.- initialValue), - "a discrepancy of", " " <+> viaShow (finalValue P.- initialValue P.- dlt)] + else ["but they changed by", " " <+> viaShow (toSymVal invLookup (finalValue P.- initialValue)), + "a discrepancy of", " " <+> viaShow (toSymVal invLookup (finalValue P.- initialValue P.- dlt))] pure result _ -> error "I am the pope" diff --git a/plutus-contract/src/Plutus/Contract/Test/ContractModel/Symbolics.hs b/plutus-contract/src/Plutus/Contract/Test/ContractModel/Symbolics.hs index de1de9c724..119689df46 100644 --- a/plutus-contract/src/Plutus/Contract/Test/ContractModel/Symbolics.hs +++ b/plutus-contract/src/Plutus/Contract/Test/ContractModel/Symbolics.hs @@ -3,7 +3,7 @@ module Plutus.Contract.Test.ContractModel.Symbolics where import Ledger.Ada qualified as Ada -import Ledger.Value (AssetClass, Value, assetClassValue, assetClassValueOf, isZero, leq) +import Ledger.Value (AssetClass, Value, assetClass, assetClassValue, assetClassValueOf, flattenValue, isZero, leq) import PlutusTx.Monoid qualified as PlutusTx import Data.Aeson qualified as JSON @@ -11,6 +11,7 @@ import Data.Data import Data.Foldable import Data.Map (Map) import Data.Map qualified as Map +import Data.Maybe import Test.QuickCheck.StateModel hiding (Action, Actions, arbitraryAction, initialState, monitoring, nextState, perform, precondition, shrinkAction, stateAfter) @@ -60,6 +61,14 @@ symLeq (SymValue m v) (SymValue m' v') = v `leq` v' && all (<=0) (Map.unionWith toValue :: (SymToken -> AssetClass) -> SymValue -> Value toValue symTokenMap (SymValue m v) = v <> fold [ assetClassValue (symTokenMap t) v | (t, v) <- Map.toList m ] +-- | Invert a sym token mapping to turn a Value into a SymValue, +-- useful for error reporting +toSymVal :: (AssetClass -> Maybe SymToken) -> Value -> SymValue +toSymVal invSymTokenMap v = + let acMap = [ (assetClass cs tn, i) | (cs, tn, i) <- flattenValue v ] + in SymValue (Map.fromList [ (tn, i) | (ac, i) <- acMap, tn <- maybeToList $ invSymTokenMap ac ]) + (fold [ assetClassValue ac i | (ac, i) <- acMap, invSymTokenMap ac == Nothing ]) + -- Negate a symbolic value inv :: SymValue -> SymValue inv (SymValue m v) = SymValue (negate <$> m) (PlutusTx.inv v) diff --git a/plutus-contract/src/Plutus/Contract/Test/MissingLovelace.hs b/plutus-contract/src/Plutus/Contract/Test/MissingLovelace.hs new file mode 100644 index 0000000000..38a9d8ee7f --- /dev/null +++ b/plutus-contract/src/Plutus/Contract/Test/MissingLovelace.hs @@ -0,0 +1,62 @@ +{-# LANGUAGE ViewPatterns #-} +module Plutus.Contract.Test.MissingLovelace + ( calculateDelta + ) where + +import Ledger.Ada qualified as Ada +import Ledger.Value (Value, noAdaValue) +import PlutusTx.Prelude qualified as P + +-- | Returns the calculated delta between initial and final values. Might be false positive. +-- +-- The tests check if a wallet's funds are equal to some expected value at the end. +-- Unfortunately, because of the adjustion of transactions, the outputs' costs change +-- and it's hard to track these changes in the tests layer. +-- +-- This function tries to check if the difference between final and initial values ('realDelta') +-- is a result of combination of operations between output's costs and the expected delta. +-- +-- There is a risk when expected delta has only ada part and expected delta /= realDelta +-- and realDelta is divisible by some delta from deltas, then we will return realDelta's ada. +-- Which means that the test will pass but without strong confidence in wallets' funds consistency. +-- For example, we expected -n, but there is n among deltas and realDelta is n, +-- it is divisible by n, then the test will pass. So please be careful. +calculateDelta + :: Value + -- ^ Expected delta of the test + -> Ada.Ada + -- ^ Initial value of the wallet before the test + -> Ada.Ada + -- ^ Final value of the wallet after the test + -> [Ada.Ada] + -- ^ Missing lovelace costs of outputs from 'AdjustingUnbalancedTx' logs + -> Value +calculateDelta expectedDelta initialValue finalValue allWalletsTxOutCosts = + let + expectedAda = Ada.fromValue expectedDelta + + -- the list of deltas: combinations (+/-) between outputs' costs, + -- the expected delta and the wallet's output costs. + deltas = map P.abs $ concat + [ [ P.abs val P.- P.abs wCost + , P.abs val P.+ P.abs wCost ] | val <- [expectedAda, 0] ++ allWalletsTxOutCosts + , wCost <- allWalletsTxOutCosts ] + + realDelta = finalValue P.- initialValue + + missingDelta = + -- We check if 'realDelta' is a result of combination of operations between initial delta and outputs' costs + -- by checking if 'realDelta''s is divisible by any delta without a reminder. + if or [(P.abs realDelta) `mod` d == 0 | d <- deltas, d /= 0] then + -- if yes, we return a sum of 'realDelta''s ada with non-ada value of the expected delta + let missingAda = Ada.toValue realDelta + missingNonAda = noAdaValue expectedDelta + in missingAda <> missingNonAda + -- otherwise we just return the expected delta + else expectedDelta + in + -- if ada in the expected delta is the same as the real delta, then we don't need to check anything + -- and can just return it + if expectedAda == realDelta then expectedDelta + -- otherwise we return the missing delta that is needed to pass the test + else missingDelta diff --git a/plutus-contract/src/Plutus/Contract/Trace.hs b/plutus-contract/src/Plutus/Contract/Trace.hs index b1c7fc5ca8..ffead19aba 100644 --- a/plutus-contract/src/Plutus/Contract/Trace.hs +++ b/plutus-contract/src/Plutus/Contract/Trace.hs @@ -24,6 +24,7 @@ module Plutus.Contract.Trace , AsTraceError(..) , toNotifyError -- * Handle contract requests + , handleAdjustUnbalancedTx , handleSlotNotifications , handleTimeNotifications , handleOwnPaymentPubKeyHashQueries @@ -201,6 +202,18 @@ handleYieldedUnbalancedTx = E.YieldUnbalancedTxResp RequestHandler.handleYieldedUnbalancedTx +handleAdjustUnbalancedTx :: + ( Member (LogObserve (LogMessage Text)) effs + , Member (LogMsg RequestHandlerLogMsg) effs + , Member NodeClientEffect effs + ) + => RequestHandler effs PABReq PABResp +handleAdjustUnbalancedTx = + generalise + (preview E._AdjustUnbalancedTxReq) + E.AdjustUnbalancedTxResp + RequestHandler.handleAdjustUnbalancedTx + defaultDist :: InitialDistribution defaultDist = defaultDistFor EM.knownWallets diff --git a/plutus-contract/src/Plutus/Contract/Trace/RequestHandler.hs b/plutus-contract/src/Plutus/Contract/Trace/RequestHandler.hs index 1856850a95..9c4359b1e0 100644 --- a/plutus-contract/src/Plutus/Contract/Trace/RequestHandler.hs +++ b/plutus-contract/src/Plutus/Contract/Trace/RequestHandler.hs @@ -17,6 +17,7 @@ module Plutus.Contract.Trace.RequestHandler( , maybeToHandler , generalise -- * handlers for common requests + , handleAdjustUnbalancedTx , handleOwnPaymentPubKeyHash , handleSlotNotifications , handleCurrentSlot @@ -42,15 +43,15 @@ import Control.Monad.Freer.NonDet qualified as NonDet import Control.Monad.Freer.Reader (Reader, ask) import Data.Monoid (Alt (Alt), Ap (Ap)) import Data.Text (Text) - +import Data.Traversable (forM) import Plutus.Contract.Resumable (Request (Request, itID, rqID, rqRequest), Response (Response, rspItID, rspResponse, rspRqID)) import Control.Monad.Freer.Extras.Log (LogMessage, LogMsg, LogObserve, logDebug, logWarn, surroundDebug) -import Ledger (POSIXTime, POSIXTimeRange, PaymentPubKeyHash, Slot, SlotRange) -import Ledger.Constraints.OffChain (UnbalancedTx) +import Ledger (POSIXTime, POSIXTimeRange, Params (..), PaymentPubKeyHash, Slot, SlotRange) +import Ledger.Constraints.OffChain (UnbalancedTx, adjustUnbalancedTx) import Ledger.TimeSlot qualified as TimeSlot -import Ledger.Tx (CardanoTx) +import Ledger.Tx (CardanoTx, ToCardanoError) import Plutus.ChainIndex (ChainIndexQueryEffect) import Plutus.ChainIndex.Effects qualified as ChainIndexEff import Plutus.Contract.Effects (ChainIndexQuery (..), ChainIndexResponse (..)) @@ -58,7 +59,7 @@ import Plutus.Contract.Wallet qualified as Wallet import Wallet.API (WalletAPIError) import Wallet.Effects (NodeClientEffect, WalletEffect) import Wallet.Effects qualified -import Wallet.Emulator.LogMessages (RequestHandlerLogMsg (HandleTxFailed, SlotNoticationTargetVsCurrent)) +import Wallet.Emulator.LogMessages (RequestHandlerLogMsg (AdjustingUnbalancedTx, HandleTxFailed, SlotNoticationTargetVsCurrent)) import Wallet.Types (ContractInstanceId) -- | Request handlers that can choose whether to handle an effect (using @@ -147,11 +148,11 @@ handleTimeNotifications = RequestHandler $ \targetTime_ -> surroundDebug @Text "handleTimeNotifications" $ do currentSlot <- Wallet.Effects.getClientSlot - slotConfig <- Wallet.Effects.getClientSlotConfig - let targetSlot_ = TimeSlot.posixTimeToEnclosingSlot slotConfig targetTime_ + Params { pSlotConfig } <- Wallet.Effects.getClientParams + let targetSlot_ = TimeSlot.posixTimeToEnclosingSlot pSlotConfig targetTime_ logDebug $ SlotNoticationTargetVsCurrent targetSlot_ currentSlot guard (currentSlot >= targetSlot_) - pure $ TimeSlot.slotToEndPOSIXTime slotConfig currentSlot + pure $ TimeSlot.slotToEndPOSIXTime pSlotConfig currentSlot handleCurrentSlot :: forall effs a. @@ -173,8 +174,8 @@ handleCurrentTime :: handleCurrentTime = RequestHandler $ \_ -> surroundDebug @Text "handleCurrentTime" $ do - slotConfig <- Wallet.Effects.getClientSlotConfig - TimeSlot.slotToEndPOSIXTime slotConfig <$> Wallet.Effects.getClientSlot + Params { pSlotConfig } <- Wallet.Effects.getClientParams + TimeSlot.slotToEndPOSIXTime pSlotConfig <$> Wallet.Effects.getClientSlot handleTimeToSlotConversions :: forall effs. @@ -185,8 +186,8 @@ handleTimeToSlotConversions :: handleTimeToSlotConversions = RequestHandler $ \poxisTimeRange -> surroundDebug @Text "handleTimeToSlotConversions" $ do - slotConfig <- Wallet.Effects.getClientSlotConfig - pure $ TimeSlot.posixTimeRangeToContainedSlotRange slotConfig poxisTimeRange + Params { pSlotConfig } <- Wallet.Effects.getClientParams + pure $ TimeSlot.posixTimeRangeToContainedSlotRange pSlotConfig poxisTimeRange handleUnbalancedTransactions :: forall effs. @@ -256,3 +257,18 @@ handleYieldedUnbalancedTx = RequestHandler $ \utx -> surroundDebug @Text "handleYieldedUnbalancedTx" $ do Wallet.yieldUnbalancedTx utx + +handleAdjustUnbalancedTx :: + forall effs. + ( Member (LogObserve (LogMessage Text)) effs + , Member (LogMsg RequestHandlerLogMsg) effs + , Member NodeClientEffect effs + ) + => RequestHandler effs UnbalancedTx (Either ToCardanoError UnbalancedTx) +handleAdjustUnbalancedTx = + RequestHandler $ \utx -> + surroundDebug @Text "handleAdjustUnbalancedTx" $ do + params <- Wallet.Effects.getClientParams + forM (adjustUnbalancedTx params utx) $ \(missingAdaCosts, adjusted) -> do + logDebug $ AdjustingUnbalancedTx missingAdaCosts + pure adjusted diff --git a/plutus-contract/src/Plutus/Contract/Wallet.hs b/plutus-contract/src/Plutus/Contract/Wallet.hs index ce3c9a4ba6..cab430555a 100644 --- a/plutus-contract/src/Plutus/Contract/Wallet.hs +++ b/plutus-contract/src/Plutus/Contract/Wallet.hs @@ -50,7 +50,7 @@ import Ledger qualified as P import Ledger.Ada qualified as Ada import Ledger.Constraints (mustPayToPubKey) import Ledger.Constraints.OffChain (UnbalancedTx (UnbalancedTx, unBalancedTxRequiredSignatories, unBalancedTxTx, unBalancedTxUtxoIndex), - adjustUnbalancedTx, mkTx) + mkTx) import Ledger.Constraints.OffChain qualified as U import Ledger.TimeSlot (SlotConfig, posixTimeRangeToContainedSlotRange) import Ledger.Tx (CardanoTx, TxId (TxId), TxOutRef, getCardanoTxInputs, txInRef) @@ -117,7 +117,7 @@ getUnspentOutput = do ownPkh <- Contract.ownPaymentPubKeyHash let constraints = mustPayToPubKey ownPkh (Ada.lovelaceValueOf 1) utx <- either (throwing _ConstraintResolutionContractError) pure (mkTx @Void mempty constraints) - tx <- Contract.balanceTx (adjustUnbalancedTx utx) + tx <- Contract.adjustUnbalancedTx utx >>= Contract.balanceTx case Set.lookupMin (getCardanoTxInputs tx) of Just inp -> pure $ txInRef inp Nothing -> throwing _OtherContractError "Balanced transaction has no inputs" diff --git a/plutus-contract/src/Plutus/Trace/Effects/EmulatedWalletAPI.hs b/plutus-contract/src/Plutus/Trace/Effects/EmulatedWalletAPI.hs index fe4f331653..d07a192471 100644 --- a/plutus-contract/src/Plutus/Trace/Effects/EmulatedWalletAPI.hs +++ b/plutus-contract/src/Plutus/Trace/Effects/EmulatedWalletAPI.hs @@ -18,17 +18,19 @@ import Control.Monad.Freer.Error (Error) import Control.Monad.Freer.Extras (raiseEnd) import Control.Monad.Freer.Extras.Log (LogMsg) import Control.Monad.Freer.TH (makeEffect) +import Data.Default (def) import Data.Text (Text) import Ledger.Tx (TxId, getCardanoTxId) import Ledger.Value (Value) import Wallet.API (WalletAPIError, defaultSlotRange, payToPaymentPublicKeyHash) import Wallet.Effects (WalletEffect) import Wallet.Emulator qualified as EM +import Wallet.Emulator.LogMessages (RequestHandlerLogMsg) import Wallet.Emulator.MultiAgent (MultiAgentEffect, walletAction) import Wallet.Emulator.Wallet (Wallet) data EmulatedWalletAPI r where - LiftWallet :: Wallet -> Eff '[WalletEffect, Error WalletAPIError, LogMsg Text] a -> EmulatedWalletAPI a + LiftWallet :: Wallet -> Eff '[WalletEffect, Error WalletAPIError, LogMsg Text, LogMsg RequestHandlerLogMsg] a -> EmulatedWalletAPI a makeEffect ''EmulatedWalletAPI @@ -43,7 +45,7 @@ payToWallet :: -> Eff effs TxId payToWallet source target amount = do ctx <- liftWallet source - $ payToPaymentPublicKeyHash defaultSlotRange amount (EM.mockWalletPaymentPubKeyHash target) + $ payToPaymentPublicKeyHash def defaultSlotRange amount (EM.mockWalletPaymentPubKeyHash target) pure $ getCardanoTxId ctx -- | Handle the 'EmulatedWalletAPI' effect using the emulator's @@ -59,4 +61,5 @@ handleEmulatedWalletAPI = \case $ subsume $ subsume $ subsume + $ subsume $ raiseEnd action diff --git a/plutus-contract/src/Plutus/Trace/Emulator.hs b/plutus-contract/src/Plutus/Trace/Emulator.hs index 6524edb973..4776ae3910 100644 --- a/plutus-contract/src/Plutus/Trace/Emulator.hs +++ b/plutus-contract/src/Plutus/Trace/Emulator.hs @@ -58,7 +58,7 @@ module Plutus.Trace.Emulator( -- * Running traces , EmulatorConfig(..) , initialChainState - , slotConfig + , params , runEmulatorStream , TraceConfig(..) , runEmulatorTrace @@ -94,8 +94,8 @@ import Wallet.Emulator.MultiAgent (EmulatorEvent, EmulatorEvent' (InstanceEvent, SchedulerEvent, UserThreadEvent, WalletEvent), EmulatorState (_chainState, _walletStates), MultiAgentControlEffect, MultiAgentEffect, _eteEmulatorTime, _eteEvent, schedulerEvent) -import Wallet.Emulator.Stream (EmulatorConfig (_initialChainState), EmulatorErr, _slotConfig, foldEmulatorStreamM, - initialChainState, initialDist, runTraceStream, slotConfig) +import Wallet.Emulator.Stream (EmulatorConfig (_initialChainState, _params), EmulatorErr, foldEmulatorStreamM, + initialChainState, initialDist, params, runTraceStream) import Wallet.Emulator.Stream qualified import Wallet.Emulator.Wallet (Entity, balances) import Wallet.Emulator.Wallet qualified as Wallet @@ -123,6 +123,7 @@ import Streaming (Stream) import Streaming.Prelude (Of ((:>))) import Data.Aeson qualified as A +import Ledger.Params (Params (..)) import Ledger.Slot (getSlot) import Ledger.TimeSlot (SlotConfig) import Plutus.V1.Ledger.Value (Value, flattenValue) @@ -208,7 +209,7 @@ interpretEmulatorTrace conf action = $ runThreads $ do raise $ launchSystemThreads wallets - handleEmulatorTrace (_slotConfig conf) action' + handleEmulatorTrace (pSlotConfig $ _params conf) action' -- | Options for how to set up and print the trace. data TraceConfig = TraceConfig diff --git a/plutus-contract/src/Plutus/Trace/Emulator/ContractInstance.hs b/plutus-contract/src/Plutus/Trace/Emulator/ContractInstance.hs index d5e190de94..d7b49b242b 100644 --- a/plutus-contract/src/Plutus/Trace/Emulator/ContractInstance.hs +++ b/plutus-contract/src/Plutus/Trace/Emulator/ContractInstance.hs @@ -259,7 +259,7 @@ handleBlockchainQueries = <> RequestHandler.handleCurrentTimeQueries <> RequestHandler.handleTimeToSlotConversions <> RequestHandler.handleYieldedUnbalancedTx - + <> RequestHandler.handleAdjustUnbalancedTx decodeEvent :: forall effs. diff --git a/plutus-contract/src/Plutus/Trace/Emulator/Extract.hs b/plutus-contract/src/Plutus/Trace/Emulator/Extract.hs index 84c6dcedf7..4990af530c 100644 --- a/plutus-contract/src/Plutus/Trace/Emulator/Extract.hs +++ b/plutus-contract/src/Plutus/Trace/Emulator/Extract.hs @@ -23,10 +23,11 @@ import Data.Monoid (Sum (..)) import Flat (flat) import Ledger.Constraints.OffChain (UnbalancedTx (..)) import Ledger.Index (ScriptValidationEvent (..), ValidatorMode (..), getScript) +import Ledger.Params (Params (..)) import Ledger.TimeSlot (SlotConfig) import Plutus.Contract.Request (MkTxLog) import Plutus.Contract.Wallet (export) -import Plutus.Trace.Emulator (EmulatorConfig (_slotConfig), EmulatorTrace) +import Plutus.Trace.Emulator (EmulatorConfig (_params), EmulatorTrace) import Plutus.Trace.Emulator qualified as Trace import Plutus.V1.Ledger.Api (ExBudget (..)) import Plutus.V1.Ledger.Scripts (Script (..)) @@ -68,7 +69,7 @@ writeScriptsTo -> IO (Sum Int64, ExBudget) -- Total size and 'ExBudget' of extracted scripts writeScriptsTo ScriptsConfig{scPath, scCommand} prefix trace emulatorCfg = do let stream = Trace.runEmulatorStream emulatorCfg trace - slotCfg = _slotConfig emulatorCfg + slotCfg = pSlotConfig $ _params emulatorCfg getEvents :: Folds.EmulatorEventFold a -> a getEvents theFold = S.fst' $ run $ foldEmulatorStreamM (L.generalize theFold) stream createDirectoryIfMissing True scPath diff --git a/plutus-contract/src/Plutus/Trace/Playground.hs b/plutus-contract/src/Plutus/Trace/Playground.hs index ddc1fe832b..d8976757ce 100644 --- a/plutus-contract/src/Plutus/Trace/Playground.hs +++ b/plutus-contract/src/Plutus/Trace/Playground.hs @@ -38,6 +38,7 @@ import Data.Map (Map) import Data.Map qualified as Map import Data.Maybe (fromMaybe) +import Ledger.Params (pSlotConfig) import Plutus.Contract (Contract (..)) import Plutus.Trace.Effects.ContractInstanceId (ContractInstanceIdEff, handleDeterministicIds) import Plutus.Trace.Effects.EmulatedWalletAPI (EmulatedWalletAPI, handleEmulatedWalletAPI) @@ -110,7 +111,7 @@ handlePlaygroundTrace :: handlePlaygroundTrace conf contract action = do _ <- flip handleError (throwError . EmulatedWalletError) . reinterpret handleEmulatedWalletAPI - . interpret (handleWaiting @_ @effs (_slotConfig conf)) + . interpret (handleWaiting @_ @effs (pSlotConfig $ _params conf)) . subsume . interpret (handleRunContractPlayground @w @s @e @_ @effs contract) $ raiseEnd action diff --git a/plutus-contract/src/Wallet/API.hs b/plutus-contract/src/Wallet/API.hs index 15a8b6f238..ebe77e7582 100644 --- a/plutus-contract/src/Wallet/API.hs +++ b/plutus-contract/src/Wallet/API.hs @@ -29,13 +29,14 @@ module Wallet.API( NodeClientEffect, publishTx, getClientSlot, - getClientSlotConfig, + getClientParams, PubKey(..), PubKeyHash(..), signTxAndSubmit, signTxAndSubmit_, payToPaymentPublicKeyHash, payToPaymentPublicKeyHash_, + Params(..), -- * Slot ranges Interval(..), Slot, @@ -59,18 +60,20 @@ module Wallet.API( import Control.Monad (unless, void) import Control.Monad.Freer (Eff, Member) import Control.Monad.Freer.Error (Error, throwError) -import Control.Monad.Freer.Extras.Log (LogMsg, logWarn) +import Control.Monad.Freer.Extras.Log (LogMsg, logDebug, logWarn) import Data.Default (Default (def)) import Data.Text (Text) import Data.Void (Void) -import Ledger (CardanoTx, Interval (Interval, ivFrom, ivTo), PaymentPubKeyHash, PubKey (PubKey, getPubKey), +import Ledger (CardanoTx, Interval (Interval, ivFrom, ivTo), Params (..), PaymentPubKeyHash, PubKey (PubKey, getPubKey), PubKeyHash (PubKeyHash, getPubKeyHash), Slot, SlotRange, Value, after, always, before, contains, interval, isEmpty, member, singleton, width) import Ledger.Constraints qualified as Constraints +import Ledger.Constraints.OffChain (adjustUnbalancedTx) import Ledger.TimeSlot qualified as TimeSlot -import Wallet.Effects (NodeClientEffect, WalletEffect, balanceTx, getClientSlot, getClientSlotConfig, - ownPaymentPubKeyHash, publishTx, submitTxn, walletAddSignature, yieldUnbalancedTx) -import Wallet.Error (WalletAPIError (PaymentMkTxError)) +import Wallet.Effects (NodeClientEffect, WalletEffect, balanceTx, getClientParams, getClientSlot, ownPaymentPubKeyHash, + publishTx, submitTxn, walletAddSignature, yieldUnbalancedTx) +import Wallet.Emulator.LogMessages (RequestHandlerLogMsg (AdjustingUnbalancedTx)) +import Wallet.Error (WalletAPIError (PaymentMkTxError, ToCardanoError)) import Wallet.Error qualified -- | Transfer some funds to an address locked by a public key, returning the @@ -78,20 +81,22 @@ import Wallet.Error qualified -- -- Note: Due to a constraint in the Cardano ledger, each tx output must have a -- minimum amount of Ada. Therefore, the funds to transfer will be adjusted --- to satisfy that constraint. See 'Ledger.Constraints.OffChain.adjustUnbalancedTx. +-- to satisfy that constraint. See 'adjustUnbalancedTx'. payToPaymentPublicKeyHash :: ( Member WalletEffect effs , Member (Error WalletAPIError) effs , Member (LogMsg Text) effs + , Member (LogMsg RequestHandlerLogMsg) effs ) - => SlotRange -> Value -> PaymentPubKeyHash -> Eff effs CardanoTx -payToPaymentPublicKeyHash range v pk = do + => Params -> SlotRange -> Value -> PaymentPubKeyHash -> Eff effs CardanoTx +payToPaymentPublicKeyHash params range v pk = do let constraints = Constraints.mustPayToPubKey pk v <> Constraints.mustValidateIn (TimeSlot.slotRangeToPOSIXTimeRange def range) utx <- either (throwError . PaymentMkTxError) pure (Constraints.mkTx @Void mempty constraints) - let adjustedUtx = Constraints.adjustUnbalancedTx utx + (missingAdaCosts, adjustedUtx) <- either (throwError . ToCardanoError) pure (adjustUnbalancedTx params utx) + logDebug $ AdjustingUnbalancedTx missingAdaCosts unless (utx == adjustedUtx) $ logWarn @Text $ "Wallet.API.payToPublicKeyHash: " <> "Adjusted a transaction output value which has less than the minimum amount of Ada." @@ -103,9 +108,10 @@ payToPaymentPublicKeyHash_ :: ( Member WalletEffect effs , Member (Error WalletAPIError) effs , Member (LogMsg Text) effs + , Member (LogMsg RequestHandlerLogMsg) effs ) - => SlotRange -> Value -> PaymentPubKeyHash -> Eff effs () -payToPaymentPublicKeyHash_ r v = void . payToPaymentPublicKeyHash r v + => Params -> SlotRange -> Value -> PaymentPubKeyHash -> Eff effs () +payToPaymentPublicKeyHash_ params r v = void . payToPaymentPublicKeyHash params r v -- | Add the wallet's signature to the transaction and submit it. Returns -- the transaction with the wallet's signature. diff --git a/plutus-contract/src/Wallet/Effects.hs b/plutus-contract/src/Wallet/Effects.hs index 5e2f73c940..da8398f45d 100644 --- a/plutus-contract/src/Wallet/Effects.hs +++ b/plutus-contract/src/Wallet/Effects.hs @@ -22,13 +22,12 @@ module Wallet.Effects( , NodeClientEffect(..) , publishTx , getClientSlot - , getClientSlotConfig + , getClientParams ) where import Control.Monad.Freer.TH (makeEffect) -import Ledger (CardanoTx, PaymentPubKeyHash, Slot, Value) +import Ledger (CardanoTx, Params, PaymentPubKeyHash, Slot, Value) import Ledger.Constraints.OffChain (UnbalancedTx) -import Ledger.TimeSlot (SlotConfig) import Wallet.Error (WalletAPIError) data WalletEffect r where @@ -44,5 +43,5 @@ makeEffect ''WalletEffect data NodeClientEffect r where PublishTx :: CardanoTx -> NodeClientEffect () GetClientSlot :: NodeClientEffect Slot - GetClientSlotConfig :: NodeClientEffect SlotConfig + GetClientParams :: NodeClientEffect Params makeEffect ''NodeClientEffect diff --git a/plutus-contract/src/Wallet/Emulator/Chain.hs b/plutus-contract/src/Wallet/Emulator/Chain.hs index fb7afb822a..32050f36f0 100644 --- a/plutus-contract/src/Wallet/Emulator/Chain.hs +++ b/plutus-contract/src/Wallet/Emulator/Chain.hs @@ -31,12 +31,11 @@ import Data.Maybe (mapMaybe) import Data.Monoid (Ap (Ap)) import Data.Traversable (for) import GHC.Generics (Generic) -import Ledger (Block, Blockchain, CardanoTx (..), OnChainTx (..), ScriptValidationEvent, Slot (..), +import Ledger (Block, Blockchain, CardanoTx (..), OnChainTx (..), Params (..), ScriptValidationEvent, Slot (..), SomeCardanoApiTx (SomeTx), Tx (..), TxId, TxIn (txInRef), TxOut (txOutValue), Value, eitherTx, getCardanoTxId, mergeCardanoTxWith, onCardanoTx) import Ledger.Index qualified as Index import Ledger.Interval qualified as Interval -import Ledger.TimeSlot (SlotConfig) import Ledger.Validation qualified as Validation import Plutus.Contract.Util (uncurry3) import Prettyprinter @@ -79,7 +78,7 @@ data ChainControlEffect r where data ChainEffect r where QueueTx :: CardanoTx -> ChainEffect () GetCurrentSlot :: ChainEffect Slot - GetSlotConfig :: ChainEffect SlotConfig + GetParams :: ChainEffect Params -- | Make a new block processBlock :: Member ChainControlEffect effs => Eff effs Block @@ -92,23 +91,23 @@ modifySlot = send . ModifySlot queueTx :: Member ChainEffect effs => CardanoTx -> Eff effs () queueTx tx = send (QueueTx tx) -getSlotConfig :: Member ChainEffect effs => Eff effs SlotConfig -getSlotConfig = send GetSlotConfig +getParams :: Member ChainEffect effs => Eff effs Params +getParams = send GetParams getCurrentSlot :: Member ChainEffect effs => Eff effs Slot getCurrentSlot = send GetCurrentSlot type ChainEffs = '[State ChainState, LogMsg ChainEvent] -handleControlChain :: Members ChainEffs effs => SlotConfig -> ChainControlEffect ~> Eff effs -handleControlChain slotCfg = \case +handleControlChain :: Members ChainEffs effs => Params -> ChainControlEffect ~> Eff effs +handleControlChain params = \case ProcessBlock -> do st <- get let pool = st ^. txPool slot = st ^. currentSlot idx = st ^. index ValidatedBlock block events rest = - validateBlock slotCfg slot idx pool + validateBlock params slot idx pool let st' = st & txPool .~ rest & addBlock block @@ -125,11 +124,11 @@ logEvent e = case e of TxnValidationFail{} -> logWarn e TxnValidate{} -> logInfo e -handleChain :: (Members ChainEffs effs) => SlotConfig -> ChainEffect ~> Eff effs -handleChain slotConfig = \case +handleChain :: (Members ChainEffs effs) => Params -> ChainEffect ~> Eff effs +handleChain params = \case QueueTx tx -> modify $ over txPool (addTxToPool tx) GetCurrentSlot -> gets _currentSlot - GetSlotConfig -> pure slotConfig + GetParams -> pure params -- | The result of validating a block. data ValidatedBlock = ValidatedBlock @@ -145,8 +144,8 @@ data ValidatedBlock = ValidatedBlock -- | Validate a block given the current slot and UTxO index, returning the valid -- transactions, success/failure events, remaining transactions and the -- updated UTxO set. -validateBlock :: SlotConfig -> Slot -> Index.UtxoIndex -> TxPool -> ValidatedBlock -validateBlock slotCfg slot@(Slot s) idx txns = +validateBlock :: Params -> Slot -> Index.UtxoIndex -> TxPool -> ValidatedBlock +validateBlock params slot@(Slot s) idx txns = let -- Select those transactions that can be validated in the -- current slot @@ -154,8 +153,8 @@ validateBlock slotCfg slot@(Slot s) idx txns = -- Validate eligible transactions, updating the UTXO index each time processed = - flip S.evalState (Index.ValidationCtx idx slotCfg) $ for eligibleTxns $ \tx -> do - (err, events_) <- validateEm slot cUtxoIndex tx + flip S.evalState (Index.ValidationCtx idx params) $ for eligibleTxns $ \tx -> do + (err, events_) <- validateEm params slot cUtxoIndex tx pure (tx, err, events_) -- The new block contains all transaction that were validated @@ -174,7 +173,7 @@ validateBlock slotCfg slot@(Slot s) idx txns = nextSlot = Slot (s + 1) events = (uncurry3 (mkValidationEvent idx) <$> processed) ++ [SlotAdd nextSlot] - cUtxoIndex = either (error . show) id $ Validation.fromPlutusIndex idx + cUtxoIndex = either (error . show) id $ Validation.fromPlutusIndex params idx in ValidatedBlock block events rest @@ -198,22 +197,23 @@ mkValidationEvent idx t result events = -- | Validate a transaction in the current emulator state. validateEm :: S.MonadState Index.ValidationCtx m - => Slot + => Params + -> Slot -> Validation.UTxO Index.EmulatorEra -> CardanoTx -> m (Maybe Index.ValidationErrorInPhase, [ScriptValidationEvent]) -validateEm h cUtxoIndex txn = do +validateEm params h cUtxoIndex txn = do ctx@(Index.ValidationCtx idx _) <- S.get let ((e, idx'), events) = txn & mergeCardanoTxWith (\tx -> Index.runValidation (Index.validateTransaction h tx) ctx) - (\tx -> ((validateL h cUtxoIndex tx, idx), [])) + (\tx -> ((validateL params h cUtxoIndex tx, idx), [])) (\((e1, utxo), sve1) ((e2, _), sve2) -> ((e1 <|> e2, utxo), sve1 ++ sve2)) _ <- S.put ctx{Index.vctxIndex=idx'} pure (e, events) -validateL :: Slot -> Validation.UTxO Index.EmulatorEra -> SomeCardanoApiTx -> Maybe Index.ValidationErrorInPhase -validateL slot idx (SomeTx tx AlonzoEraInCardanoMode) = Validation.hasValidationErrors (fromIntegral slot) idx tx -validateL _ _ _ = Nothing +validateL :: Params -> Slot -> Validation.UTxO Index.EmulatorEra -> SomeCardanoApiTx -> Maybe Index.ValidationErrorInPhase +validateL params slot idx (SomeTx tx AlonzoEraInCardanoMode) = Validation.hasValidationErrors params (fromIntegral slot) idx tx +validateL _ _ _ _ = Nothing -- | Adds a block to ChainState, without validation. addBlock :: Block -> ChainState -> ChainState diff --git a/plutus-contract/src/Wallet/Emulator/Folds.hs b/plutus-contract/src/Wallet/Emulator/Folds.hs index a6ed2f4927..f41c174a6c 100644 --- a/plutus-contract/src/Wallet/Emulator/Folds.hs +++ b/plutus-contract/src/Wallet/Emulator/Folds.hs @@ -36,6 +36,7 @@ module Wallet.Emulator.Folds ( , walletFunds , walletFees , walletTxBalanceEvents + , walletsAdjustedTxEvents -- * Folds that are used in the Playground , annotatedBlockchain , blockchain @@ -60,8 +61,10 @@ import Data.Aeson qualified as JSON import Data.Foldable (toList) import Data.Map qualified as Map import Data.Maybe (fromMaybe, mapMaybe) +import Data.Set qualified as Set import Data.Text (Text) import Ledger (Block, OnChainTx (Invalid, Valid), TxId) +import Ledger.Ada qualified as Ada import Ledger.Address (Address) import Ledger.AddressMap (UtxoMap) import Ledger.AddressMap qualified as AM @@ -82,11 +85,11 @@ import Plutus.Trace.Emulator.Types (ContractInstanceLog, ContractInstanceMsg (Co import Prettyprinter (Pretty (..), defaultLayoutOptions, layoutPretty, vsep) import Prettyprinter.Render.Text (renderStrict) import Wallet.Emulator.Chain (ChainEvent (SlotAdd, TxnValidate, TxnValidationFail), _TxnValidate, _TxnValidationFail) -import Wallet.Emulator.LogMessages (_BalancingUnbalancedTx, _ValidationFailed) +import Wallet.Emulator.LogMessages (_AdjustingUnbalancedTx, _BalancingUnbalancedTx, _ValidationFailed) import Wallet.Emulator.MultiAgent (EmulatorEvent, EmulatorTimeEvent, chainEvent, eteEvent, instanceEvent, userThreadEvent, walletClientEvent, walletEvent') import Wallet.Emulator.NodeClient (_TxSubmit) -import Wallet.Emulator.Wallet (Wallet, _TxBalanceLog, mockWalletAddress) +import Wallet.Emulator.Wallet (Wallet, _RequestHandlerLog, _TxBalanceLog, mockWalletAddress) import Wallet.Rollup qualified as Rollup import Wallet.Rollup.Types (AnnotatedTx) @@ -121,6 +124,10 @@ scriptEvents = preMapMaybe (preview (eteEvent . chainEvent) >=> getEvent) (conca walletTxBalanceEvents :: EmulatorEventFold [UnbalancedTx] walletTxBalanceEvents = preMapMaybe (preview (eteEvent . walletEvent' . _2 . _TxBalanceLog . _BalancingUnbalancedTx)) L.list +-- | Min lovelace of 'txOut's from adjusted unbalanced transactions for all wallets +walletsAdjustedTxEvents :: EmulatorEventFold [Ada.Ada] +walletsAdjustedTxEvents = (Set.toList . Set.fromList . concat) <$> preMapMaybe (preview (eteEvent . walletEvent' . _2 . _RequestHandlerLog . _AdjustingUnbalancedTx)) L.list + mkTxLogs :: EmulatorEventFold [MkTxLog] mkTxLogs = let getTxLogEvent :: ContractInstanceMsg -> Maybe MkTxLog diff --git a/plutus-contract/src/Wallet/Emulator/LogMessages.hs b/plutus-contract/src/Wallet/Emulator/LogMessages.hs index 7967fdd2cb..f3818c75db 100644 --- a/plutus-contract/src/Wallet/Emulator/LogMessages.hs +++ b/plutus-contract/src/Wallet/Emulator/LogMessages.hs @@ -8,6 +8,7 @@ module Wallet.Emulator.LogMessages( RequestHandlerLogMsg(..) , TxBalanceMsg(..) + , _AdjustingUnbalancedTx , _BalancingUnbalancedTx , _ValidationFailed ) where @@ -16,6 +17,7 @@ import Control.Lens.TH (makePrisms) import Data.Aeson (FromJSON, ToJSON) import GHC.Generics (Generic) import Ledger (Address, CardanoTx, TxId, getCardanoTxId) +import Ledger.Ada qualified as Ada import Ledger.Constraints.OffChain (UnbalancedTx) import Ledger.Index (ScriptValidationEvent, ValidationError, ValidationPhase) import Ledger.Slot (Slot) @@ -28,9 +30,12 @@ data RequestHandlerLogMsg = | StartWatchingContractAddresses | HandleTxFailed WalletAPIError | UtxoAtFailed Address + | AdjustingUnbalancedTx [Ada.Ada] deriving stock (Eq, Show, Generic) deriving anyclass (ToJSON, FromJSON) +makePrisms ''RequestHandlerLogMsg + instance Pretty RequestHandlerLogMsg where pretty = \case SlotNoticationTargetVsCurrent target current -> @@ -38,6 +43,7 @@ instance Pretty RequestHandlerLogMsg where StartWatchingContractAddresses -> "Start watching contract addresses" HandleTxFailed e -> "handleTx failed:" <+> viaShow e UtxoAtFailed addr -> "UtxoAt failed:" <+> pretty addr + AdjustingUnbalancedTx vl -> "Adjusting an unbalanced transaction:" <+> pretty vl data TxBalanceMsg = BalancingUnbalancedTx UnbalancedTx diff --git a/plutus-contract/src/Wallet/Emulator/NodeClient.hs b/plutus-contract/src/Wallet/Emulator/NodeClient.hs index 8ee1e30786..d74675a49f 100644 --- a/plutus-contract/src/Wallet/Emulator/NodeClient.hs +++ b/plutus-contract/src/Wallet/Emulator/NodeClient.hs @@ -70,6 +70,6 @@ handleNodeClient :: (Members NodeClientEffs effs) => Eff (NodeClientEffect ': effs) ~> Eff effs handleNodeClient = interpret $ \case - PublishTx tx -> queueTx tx >> logInfo (TxSubmit (getCardanoTxId tx) (getCardanoTxFee tx)) - GetClientSlot -> gets _clientSlot - GetClientSlotConfig -> getSlotConfig + PublishTx tx -> queueTx tx >> logInfo (TxSubmit (getCardanoTxId tx) (getCardanoTxFee tx)) + GetClientSlot -> gets _clientSlot + GetClientParams -> getParams diff --git a/plutus-contract/src/Wallet/Emulator/Stream.hs b/plutus-contract/src/Wallet/Emulator/Stream.hs index cba96db83c..3de16e2e29 100644 --- a/plutus-contract/src/Wallet/Emulator/Stream.hs +++ b/plutus-contract/src/Wallet/Emulator/Stream.hs @@ -16,7 +16,7 @@ module Wallet.Emulator.Stream( , initialChainState , initialDist , initialState - , slotConfig + , params , runTraceStream -- * Stream manipulation , takeUntilSlot @@ -60,8 +60,7 @@ import Wallet.Emulator.MultiAgent (EmulatorState, EmulatorTimeEvent (EmulatorTim MultiAgentEffect, chainEvent, eteEvent) import Wallet.Emulator.Wallet (Wallet, mockWalletAddress) --- TODO: Move these two to 'Wallet.Emulator.XXX'? -import Ledger.TimeSlot (SlotConfig) +import Ledger.Params (Params) import Plutus.Contract.Trace (InitialDistribution, defaultDist, knownWallets) import Plutus.Trace.Emulator.ContractInstance (EmulatorRuntimeError) @@ -119,7 +118,7 @@ runTraceStream :: forall effs. , Error EmulatorRuntimeError ] () -> Stream (Of (LogMessage EmulatorEvent)) (Eff effs) (Maybe EmulatorErr, EmulatorState) -runTraceStream conf@EmulatorConfig{_slotConfig} = +runTraceStream conf@EmulatorConfig{_params} = fmap (first (either Just (const Nothing))) . S.hoist (pure . run) . runStream @(LogMessage EmulatorEvent) @_ @'[] @@ -131,7 +130,7 @@ runTraceStream conf@EmulatorConfig{_slotConfig} = . wrapError ChainIndexErr . wrapError AssertionErr . wrapError InstanceErr - . EM.processEmulated _slotConfig + . EM.processEmulated _params . subsume . subsume @(State EmulatorState) . raiseEnd @@ -139,7 +138,7 @@ runTraceStream conf@EmulatorConfig{_slotConfig} = data EmulatorConfig = EmulatorConfig { _initialChainState :: InitialChainState -- ^ State of the blockchain at the beginning of the simulation. Can be given as a map of funds to wallets, or as a block of transactions. - , _slotConfig :: SlotConfig -- ^ Set the start time of slot 0 and the length of one slot + , _params :: Params -- ^ Set the protocol parameters, network ID and slot configuration for the emulator. } deriving (Eq, Show) type InitialChainState = Either InitialDistribution [Tx] @@ -156,7 +155,7 @@ initialDist = either id (walletFunds . map Valid) where instance Default EmulatorConfig where def = EmulatorConfig { _initialChainState = Left defaultDist - , _slotConfig = def + , _params = def } initialState :: EmulatorConfig -> EM.EmulatorState diff --git a/plutus-contract/src/Wallet/Emulator/Types.hs b/plutus-contract/src/Wallet/Emulator/Types.hs index 90fb62005c..854682f2c0 100644 --- a/plutus-contract/src/Wallet/Emulator/Types.hs +++ b/plutus-contract/src/Wallet/Emulator/Types.hs @@ -73,12 +73,11 @@ import Control.Monad.Freer.Extras qualified as Eff import Control.Monad.Freer.Extras.Log (LogMsg, mapLog) import Control.Monad.Freer.State (State) -import Ledger (addSignature) +import Ledger (Params, addSignature) import Plutus.ChainIndex (ChainIndexError) import Wallet.API (WalletAPIError) import Ledger.CardanoWallet qualified -import Ledger.TimeSlot (SlotConfig) import Plutus.Contract.Error (AssertionError) import Plutus.Contract.Error qualified import Wallet.Emulator.Chain (ChainControlEffect, ChainEffect, ChainEvent, ChainState, handleChain, handleControlChain) @@ -98,16 +97,16 @@ processEmulated :: forall effs. , Member (State EmulatorState) effs , Member (LogMsg EmulatorEvent') effs ) - => SlotConfig + => Params -> Eff (MultiAgentEffect ': MultiAgentControlEffect ': ChainEffect ': ChainControlEffect ': effs) ~> Eff effs -processEmulated slotCfg act = +processEmulated params act = act & handleMultiAgent & handleMultiAgentControl - & reinterpret2 @ChainEffect @(State ChainState) @(LogMsg ChainEvent) (handleChain slotCfg) + & reinterpret2 @ChainEffect @(State ChainState) @(LogMsg ChainEvent) (handleChain params) & interpret (Eff.handleZoomedState chainState) & interpret (mapLog (review chainEvent)) - & reinterpret2 @ChainControlEffect @(State ChainState) @(LogMsg ChainEvent) (handleControlChain slotCfg) + & reinterpret2 @ChainControlEffect @(State ChainState) @(LogMsg ChainEvent) (handleControlChain params) & interpret (Eff.handleZoomedState chainState) & interpret (mapLog (review chainEvent)) diff --git a/plutus-contract/src/Wallet/Emulator/Wallet.hs b/plutus-contract/src/Wallet/Emulator/Wallet.hs index aa273f186b..76da47b406 100644 --- a/plutus-contract/src/Wallet/Emulator/Wallet.hs +++ b/plutus-contract/src/Wallet/Emulator/Wallet.hs @@ -22,6 +22,7 @@ module Wallet.Emulator.Wallet where import Cardano.Api (EraInMode (AlonzoEraInCardanoMode)) +import Cardano.Api.Shelley (protocolParamCollateralPercent) import Cardano.Crypto.Wallet qualified as Crypto import Cardano.Wallet.Primitive.Types qualified as Cardano.Wallet import Control.Lens (makeLenses, makePrisms, over, view, (&), (.~), (^.)) @@ -45,7 +46,8 @@ import Data.String (IsString (fromString)) import Data.Text qualified as T import Data.Text.Class (fromText, toText) import GHC.Generics (Generic) -import Ledger (CardanoTx, ChainIndexTxOut, PaymentPrivateKey (PaymentPrivateKey, unPaymentPrivateKey), +import Ledger (Address (addressCredential), CardanoTx, ChainIndexTxOut, Params (..), + PaymentPrivateKey (PaymentPrivateKey, unPaymentPrivateKey), PaymentPubKey (PaymentPubKey, unPaymentPubKey), PaymentPubKeyHash (PaymentPubKeyHash, unPaymentPubKeyHash), SomeCardanoApiTx, StakePubKey, Tx (txFee, txMint), UtxoIndex (..)) @@ -66,7 +68,7 @@ import Plutus.ChainIndex.Emulator (ChainIndexEmulatorState, ChainIndexQueryEffec import Plutus.Contract (WalletAPIError) import Plutus.Contract.Checkpoint (CheckpointLogMsg) import Plutus.Contract.Wallet (finalize) -import Plutus.V1.Ledger.Api (Address (addressCredential), PubKeyHash, TxOutRef, ValidatorHash, Value) +import Plutus.V1.Ledger.Api (PubKeyHash, TxOutRef, ValidatorHash, Value) import Plutus.V1.Ledger.Tx (TxIn (TxIn, txInRef)) import PlutusTx.Prelude qualified as PlutusTx import Prettyprinter (Pretty (pretty)) @@ -81,7 +83,6 @@ import Wallet.Emulator.LogMessages (RequestHandlerLogMsg, TxBalanceMsg (AddingCollateralInputsFor, AddingInputsFor, AddingPublicKeyOutputFor, BalancingUnbalancedTx, FinishedBalancing, NoCollateralInputsAdded, NoInputsAdded, NoOutputsAdded, SigningTx, SubmittingTx, ValidationFailed)) import Wallet.Emulator.NodeClient (NodeClientState, emptyNodeClientState) - newtype SigningProcess = SigningProcess { unSigningProcess :: forall effs. (Member (Error WAPI.WalletAPIError) effs) => [PaymentPubKeyHash] -> CardanoTx -> Eff effs CardanoTx } @@ -313,14 +314,14 @@ handleBalance :: -> Eff effs CardanoTx handleBalance utx' = do utxo <- get >>= ownOutputs - slotConfig <- WAPI.getClientSlotConfig - let utx = finalize slotConfig utx' + params@Params { pSlotConfig } <- WAPI.getClientParams + let utx = finalize pSlotConfig utx' let requiredSigners = Set.toList (U.unBalancedTxRequiredSignatories utx) - cUtxoIndex <- handleError (view U.tx utx) $ fromPlutusIndex $ UtxoIndex $ U.unBalancedTxUtxoIndex utx <> fmap Tx.toTxOut utxo + cUtxoIndex <- handleError (view U.tx utx) $ fromPlutusIndex params $ UtxoIndex $ U.unBalancedTxUtxoIndex utx <> fmap Tx.toTxOut utxo -- Find the fixed point of fee calculation, trying maximally n times to prevent an infinite loop let calcFee n fee = do tx <- handleBalanceTx utxo (utx & U.tx . Ledger.fee .~ fee) - newFee <- handleError tx $ evaluateTransactionFee cUtxoIndex requiredSigners tx + newFee <- handleError tx $ evaluateTransactionFee params cUtxoIndex requiredSigners tx if newFee /= fee then if n == (0 :: Int) -- If we don't reach a fixed point, pick the larger fee @@ -330,7 +331,7 @@ handleBalance utx' = do -- Start with a relatively high fee, bigger chance that we get the number of inputs right the first time. theFee <- calcFee 5 $ Ada.lovelaceValueOf 300000 tx' <- handleBalanceTx utxo (utx & U.tx . Ledger.fee .~ theFee) - cTx <- handleError tx' $ fromPlutusTx cUtxoIndex requiredSigners tx' + cTx <- handleError tx' $ fromPlutusTx params cUtxoIndex requiredSigners tx' pure $ Tx.Both tx' (Tx.SomeTx cTx AlonzoEraInCardanoMode) where handleError tx (Left (Left (ph, ve))) = do @@ -409,7 +410,8 @@ lookupValue outputRef@TxIn {txInRef} = do -- | Balance an unbalanced transaction by adding missing inputs and outputs handleBalanceTx :: forall effs. - ( Member (State WalletState) effs + ( Member NodeClientEffect effs + , Member (State WalletState) effs , Member ChainIndexQueryEffect effs , Member (Error WAPI.WalletAPIError) effs , Member (LogMsg TxBalanceMsg) effs @@ -418,6 +420,7 @@ handleBalanceTx :: -> UnbalancedTx -> Eff effs Tx handleBalanceTx utxo UnbalancedTx{unBalancedTxTx} = do + Params { pProtocolParams } <- WAPI.getClientParams let filteredUnbalancedTxTx = removeEmptyOutputs unBalancedTxTx let txInputs = Set.toList $ Tx.txInputs filteredUnbalancedTxTx ownPaymentPubKey <- gets ownPaymentPublicKey @@ -427,7 +430,8 @@ handleBalanceTx utxo UnbalancedTx{unBalancedTxTx} = do let fees = txFee filteredUnbalancedTxTx left = txMint filteredUnbalancedTxTx <> fold inputValues right = fees <> foldMap (view Tx.outValue) (filteredUnbalancedTxTx ^. Tx.outputs) - remainingFees = fees PlutusTx.- fold collateral -- TODO: add collateralPercent + collFees = Ada.toValue $ (Ada.fromValue fees * maybe 100 fromIntegral (protocolParamCollateralPercent pProtocolParams)) `Ada.divide` 100 + remainingCollFees = collFees PlutusTx.- fold collateral balance = left PlutusTx.- right (neg, pos) = adjustBalanceWithMissingLovelace $ Value.split balance @@ -451,13 +455,13 @@ handleBalanceTx utxo UnbalancedTx{unBalancedTxTx} = do txOutRef `notElem` inputsOutRefs addInputs filteredUtxo ownPaymentPubKey ownStakePubKey neg tx' - if remainingFees `Value.leq` PlutusTx.zero + if remainingCollFees `Value.leq` PlutusTx.zero then do logDebug NoCollateralInputsAdded pure tx'' else do - logDebug $ AddingCollateralInputsFor remainingFees - addCollateral utxo remainingFees tx'' + logDebug $ AddingCollateralInputsFor remainingCollFees + addCollateral utxo remainingCollFees tx'' -- | Adjust the left and right balance of an unbalanced 'Tx' with the missing -- lovelace considering the minimum lovelace per transaction output constraint @@ -487,7 +491,7 @@ splitOffAdaOnlyValue vl = if Value.isAdaOnlyValue vl || ada < Ledger.minAdaTxOut ada = Ada.fromValue vl - Ledger.minAdaTxOut addOutput :: PaymentPubKey -> Maybe StakePubKey -> Value -> Tx -> Tx -addOutput pk sk vl tx = tx & over Tx.outputs (pkos ++) where +addOutput pk sk vl tx = tx & over Tx.outputs (++ pkos) where pkos = (\v -> Tx.pubKeyTxOut v pk sk) <$> splitOffAdaOnlyValue vl addCollateral diff --git a/plutus-contract/test/Spec.hs b/plutus-contract/test/Spec.hs index 2f42a5a734..5982248f74 100644 --- a/plutus-contract/test/Spec.hs +++ b/plutus-contract/test/Spec.hs @@ -11,6 +11,7 @@ import Spec.Rows qualified import Spec.Secrets qualified import Spec.State qualified import Spec.ThreadToken qualified +import Spec.TimeValidity qualified import Test.Tasty (TestTree, defaultMain, testGroup) main :: IO () @@ -23,6 +24,7 @@ tests = testGroup "plutus-contract" [ Spec.State.tests, Spec.Rows.tests, Spec.ThreadToken.tests, + Spec.TimeValidity.tests, Spec.Secrets.tests, Spec.ErrorChecking.tests, Spec.Plutus.Contract.Wallet.tests, diff --git a/plutus-contract/test/Spec/Balancing.hs b/plutus-contract/test/Spec/Balancing.hs index c188a9994d..36b65bfacd 100644 --- a/plutus-contract/test/Spec/Balancing.hs +++ b/plutus-contract/test/Spec/Balancing.hs @@ -55,7 +55,7 @@ balanceTxnMinAda = constraints2 = Constraints.mustSpendScriptOutput txOutRef unitRedeemer <> Constraints.mustPayToOtherScript vHash unitDatum (Value.scale 200 ee) lookups2 = Constraints.unspentOutputs utxo <> Constraints.otherScript someValidator - utx2 = Constraints.adjustUnbalancedTx $ either (error . show) id $ Constraints.mkTx @Void lookups2 constraints2 + utx2 <- Con.adjustUnbalancedTx $ either (error . show) id $ Constraints.mkTx @Void lookups2 constraints2 submitTxConfirmed utx2 trace = do @@ -74,14 +74,14 @@ balanceTxnMinAda2 = & changeInitialWalletValue w1 (<> vA 1 <> vB 2) vHash = Scripts.validatorHash someValidator payToWallet w = Constraints.mustPayToPubKey (EM.mockWalletPaymentPubKeyHash w) - mkTx lookups constraints = Constraints.adjustUnbalancedTx . either (error . show) id $ Constraints.mkTx @Void lookups constraints + mkTx lookups constraints = Con.adjustUnbalancedTx . either (error . show) id $ Constraints.mkTx @Void lookups constraints setupContract :: Contract () EmptySchema ContractError () setupContract = do -- Make sure there is a utxo with 1 A, 1 B, and 4 ada at w2 - submitTxConfirmed $ mkTx mempty (payToWallet w2 (vA 1 <> vB 1 <> Value.scale 2 (Ada.toValue Ledger.minAdaTxOut))) + submitTxConfirmed =<< mkTx mempty (payToWallet w2 (vA 1 <> vB 1 <> Value.scale 2 (Ada.toValue Ledger.minAdaTxOut))) -- Make sure there is a UTxO with 1 B and datum () at the script - submitTxConfirmed $ mkTx mempty (Constraints.mustPayToOtherScript vHash unitDatum (vB 1)) + submitTxConfirmed =<< mkTx mempty (Constraints.mustPayToOtherScript vHash unitDatum (vB 1)) -- utxo0 @ wallet2 = 1 A, 1 B, 4 Ada -- utxo1 @ script = 1 B, 2 Ada @@ -96,7 +96,7 @@ balanceTxnMinAda2 = <> Constraints.mustPayToOtherScript vHash unitDatum (vB 1) -- 2 ada and 1 B to script <> Constraints.mustPayToOtherScript vHash (Datum $ PlutusTx.toBuiltinData (0 :: Integer)) (vB 1) -- 2 ada and 1 B to script (different datum) <> Constraints.mustMintValue (vL 1) -- 1 L and 2 ada to wallet2 - submitTxConfirmed $ mkTx lookups constraints + submitTxConfirmed =<< mkTx lookups constraints trace = do void $ Trace.activateContractWallet w1 setupContract diff --git a/plutus-contract/test/Spec/Contract.hs b/plutus-contract/test/Spec/Contract.hs index 2b8b6616cf..2f18edb935 100644 --- a/plutus-contract/test/Spec/Contract.hs +++ b/plutus-contract/test/Spec/Contract.hs @@ -217,7 +217,7 @@ tests = tx <- submitTx payment let txOuts = fmap fst $ Ledger.getCardanoTxOutRefs tx -- tell the tx out' datum hash that was specified by 'mustPayWithDatumToPubKey' - tell [txOutDatumHash (txOuts !! 1)] + tell [txOutDatumHash (head txOuts)] datum = Datum $ PlutusTx.toBuiltinData (23 :: Integer) isExpectedDatumHash [Just hash] = hash == datumHash datum @@ -256,9 +256,9 @@ tests = let payment = Constraints.mustPayToPubKey w2PubKeyHash (Ada.adaValueOf 10) tx <- submitTx payment - -- There should be 2 utxos. We suppose the first belongs to the - -- wallet calling the contract and the second one to W2. - let utxo = head $ fmap snd $ Ledger.getCardanoTxOutRefs tx + -- There should be 2 utxos. We suppose the first belongs to W2 + -- and the second one belongs to the wallet calling the contract. + let utxo = (fmap snd $ Ledger.getCardanoTxOutRefs tx) !! 1 -- We wait for W1's utxo to change status. It should be of -- status confirmed unspent. s <- awaitTxOutStatusChange utxo diff --git a/plutus-contract/test/Spec/Emulator.hs b/plutus-contract/test/Spec/Emulator.hs index ccd83180bb..76fedec07f 100644 --- a/plutus-contract/test/Spec/Emulator.hs +++ b/plutus-contract/test/Spec/Emulator.hs @@ -32,6 +32,7 @@ import Ledger.Ada qualified as Ada import Ledger.Generators (Mockchain (Mockchain)) import Ledger.Generators qualified as Gen import Ledger.Index qualified as Index +import Ledger.Params () import Ledger.Value qualified as Value import Plutus.Contract.Test hiding (not) import Plutus.Script.Utils.V1.Scripts (mkUntypedValidator) @@ -140,7 +141,7 @@ txnValidFrom = .&&. walletFundsChange wallet2 five ) $ do - Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ range five pubKey2 + Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ def range five pubKey2 void $ Trace.waitUntilSlot 6 selectCoinProp :: Property @@ -289,24 +290,24 @@ payToPaymentPubKeyScript2 = pubKeyTransactions :: EmulatorTrace () pubKeyTransactions = do let five = Ada.adaValueOf 5 - Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ W.always five pubKey2 + Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ def W.always five pubKey2 _ <- Trace.nextSlot - Trace.liftWallet wallet2 $ payToPaymentPublicKeyHash_ W.always five pubKey3 + Trace.liftWallet wallet2 $ payToPaymentPublicKeyHash_ def W.always five pubKey3 _ <- Trace.nextSlot - Trace.liftWallet wallet3 $ payToPaymentPublicKeyHash_ W.always five pubKey1 + Trace.liftWallet wallet3 $ payToPaymentPublicKeyHash_ def W.always five pubKey1 void Trace.nextSlot pubKeyTransactions2 :: EmulatorTrace () pubKeyTransactions2 = do let payment1 = initialBalance P.- Ada.adaValueOf 10 payment2 = initialBalance P.+ Ada.adaValueOf 10 - Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ W.always payment1 pubKey2 + Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ def W.always payment1 pubKey2 _ <- Trace.nextSlot - Trace.liftWallet wallet2 $ payToPaymentPublicKeyHash_ W.always payment2 pubKey3 + Trace.liftWallet wallet2 $ payToPaymentPublicKeyHash_ def W.always payment2 pubKey3 _ <- Trace.nextSlot - Trace.liftWallet wallet3 $ payToPaymentPublicKeyHash_ W.always payment2 pubKey1 + Trace.liftWallet wallet3 $ payToPaymentPublicKeyHash_ def W.always payment2 pubKey1 _ <- Trace.nextSlot - Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ W.always (Ada.adaValueOf 20) pubKey2 + Trace.liftWallet wallet1 $ payToPaymentPublicKeyHash_ def W.always (Ada.adaValueOf 20) pubKey2 void Trace.nextSlot genChainTxn :: Hedgehog.MonadGen m => m (Mockchain, Tx) diff --git a/plutus-contract/test/Spec/TimeValidity.hs b/plutus-contract/test/Spec/TimeValidity.hs new file mode 100644 index 0000000000..972cb61a62 --- /dev/null +++ b/plutus-contract/test/Spec/TimeValidity.hs @@ -0,0 +1,120 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +module Spec.TimeValidity(tests) where + +import Cardano.Api.Shelley (protocolParamProtocolVersion) +import Control.Lens hiding (contains, from, (.>)) +import Control.Monad (void) +import Data.Map qualified as Map +import Data.Void (Void) +import Test.Tasty (TestTree, testGroup) + +import Ledger qualified +import Ledger.Ada qualified as Ada +import Ledger.Constraints qualified as Constraints +import Ledger.Tx qualified as Tx +import Ledger.Typed.Scripts qualified as Scripts +import Plutus.Contract as Con +import Plutus.Contract.Test (assertFailedTransaction, assertValidatedTransactionCount, checkPredicateOptions, + defaultCheckOptions, emulatorConfig, w1) +import Plutus.Script.Utils.V1.Scripts (ValidatorHash) +import Plutus.Trace qualified as Trace +import Plutus.V1.Ledger.Api (POSIXTime, TxInfo, Validator) +import Plutus.V1.Ledger.Api qualified as P +import Plutus.V1.Ledger.Interval (contains, from) +import Plutus.V1.Ledger.Scripts (ScriptError (EvaluationError), unitDatum, unitRedeemer) +import PlutusTx qualified +import PlutusTx.Prelude qualified as P +import Prelude hiding (not) +import Wallet.Emulator.Stream (params) + +tests :: TestTree +tests = + testGroup "time validity" + [ protocolV5 + , protocolV6 + , defaultProtocolParams + ] + +contract :: Contract () Empty ContractError () +contract = do + now <- Con.currentTime + logInfo @String $ "now: " ++ show now + let lookups1 = Constraints.typedValidatorLookups $ typedValidator deadline + tx1 = Constraints.mustPayToTheScript () (Ada.lovelaceValueOf 25000000) + ledgerTx1 <- submitTxConstraintsWith lookups1 tx1 + awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx1 + utxos <- utxosAt scrAddress + let orefs = fst <$> Map.toList utxos + lookups2 = + Constraints.otherScript (validatorScript deadline) + <> Constraints.unspentOutputs utxos + tx2 = + foldMap (\oref -> Constraints.mustSpendScriptOutput oref unitRedeemer) orefs + <> Constraints.mustIncludeDatum unitDatum + <> Constraints.mustValidateIn (from $ now + 1000) + ledgerTx2 <- submitTxConstraintsWith @Void lookups2 tx2 + awaitTxConfirmed $ Tx.getCardanoTxId ledgerTx2 + +trace :: Trace.EmulatorTrace () +trace = do + void $ Trace.activateContractWallet w1 contract + void $ Trace.waitNSlots 3 + +protocolV5 :: TestTree +protocolV5 = checkPredicateOptions + (defaultCheckOptions & over (emulatorConfig . params . Ledger.protocolParamsL) (\pp -> pp { protocolParamProtocolVersion = (5, 0) })) + "tx valid time interval is not supported in protocol v5" + (assertFailedTransaction (\_ err _ -> case err of {Ledger.ScriptFailure (EvaluationError ("Invalid range":_) _) -> True; _ -> False })) + (void trace) + +protocolV6 :: TestTree +protocolV6 = checkPredicateOptions + (defaultCheckOptions & over (emulatorConfig . params . Ledger.protocolParamsL) (\pp -> pp { protocolParamProtocolVersion = (6, 0) })) + "tx valid time interval is supported in protocol v6" + (assertValidatedTransactionCount 2) + (void trace) + +defaultProtocolParams :: TestTree +defaultProtocolParams = checkPredicateOptions + defaultCheckOptions + "tx valid time interval is supported in protocol v6+" + (assertValidatedTransactionCount 2) + (void trace) + +deadline :: POSIXTime +deadline = 1596059092000 -- (milliseconds) transaction's valid range must be after this + +{-# INLINEABLE mkValidator #-} +mkValidator :: P.POSIXTime -> () -> () -> P.ScriptContext -> Bool +mkValidator dl _ _ ctx = (from dl `contains` range) || P.traceError "Invalid range" + where + info :: TxInfo + info = P.scriptContextTxInfo ctx + + range :: P.POSIXTimeRange + range = P.txInfoValidRange info + + +data UnitTest +instance Scripts.ValidatorTypes UnitTest + +typedValidator :: POSIXTime -> Scripts.TypedValidator UnitTest +typedValidator = Scripts.mkTypedValidatorParam @UnitTest + $$(PlutusTx.compile [||mkValidator||]) + $$(PlutusTx.compile [|| wrap ||]) + where + wrap = Scripts.mkUntypedValidator + +validatorScript :: P.POSIXTime -> Validator +validatorScript = Scripts.validatorScript . typedValidator + +valHash :: ValidatorHash +valHash = Scripts.validatorHash $ typedValidator deadline + +scrAddress :: Ledger.Address +scrAddress = Ledger.scriptHashAddress valHash diff --git a/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions.txt b/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions.txt index ae7e8d68f8..087d95845a 100644 --- a/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions.txt +++ b/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions.txt @@ -10,45 +10,45 @@ Slot 00001: W[10]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360 Slot 00001: W[9]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. Slot 00001: W[3]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. Slot 00001: W[5]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. -Slot 00001: W[1]: TxSubmit: 5ef3a61605e8ecaef31668d54c442883b65e124be318191e70605cff129ecc95 -Slot 00001: TxnValidate 5ef3a61605e8ecaef31668d54c442883b65e124be318191e70605cff129ecc95 +Slot 00001: W[1]: TxSubmit: 165c7aef32c0e8604e3ada906f2700466720bf36b6c157809041a70df22727a4 +Slot 00001: TxnValidate 165c7aef32c0e8604e3ada906f2700466720bf36b6c157809041a70df22727a4 Slot 00001: SlotAdd Slot 2 -Slot 00002: W[7]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[8]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[6]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[4]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[2]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[1]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[10]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[9]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[3]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[5]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 7981c7489e08ce70845757bfb7e5fdadfe7618903042bdd40b177b9c051d0dfa, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[2]: TxSubmit: 7fb18127cd3159acc57b32cdd8b3bca990509f8a0aa096d44b8fd88eca0d7660 -Slot 00002: TxnValidate 7fb18127cd3159acc57b32cdd8b3bca990509f8a0aa096d44b8fd88eca0d7660 +Slot 00002: W[7]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[8]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[6]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[4]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[2]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[1]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[10]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[9]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[3]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[5]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 899b44ac5d0db5922935d70a36349305785346c7b0227bb15f90121e67b2cf82, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[2]: TxSubmit: 6e86de5a04500195e26f7849da798a877d3e5db5c61663c6650303aa6f814282 +Slot 00002: TxnValidate 6e86de5a04500195e26f7849da798a877d3e5db5c61663c6650303aa6f814282 Slot 00002: SlotAdd Slot 3 -Slot 00003: W[7]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[8]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[6]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[4]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[2]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[1]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[10]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[9]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[3]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[5]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 422a6b6ea9dede253f8341cff4e7ea7a8773a154d0f8027074b055cec0d6019b, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[3]: TxSubmit: d347f3baf2ffc122c5b6104c2358a25d4ef2d7ca186900fad7a52c2d148c96d4 -Slot 00003: TxnValidate d347f3baf2ffc122c5b6104c2358a25d4ef2d7ca186900fad7a52c2d148c96d4 +Slot 00003: W[7]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[8]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[6]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[4]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[2]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[1]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[10]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[9]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[3]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[5]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 77bc0c8d6a835301d372111894f647f32c88fdddf232eb82b37ecd0b9898ed4c, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[3]: TxSubmit: af6027ffc2be2f8ba4df4682ab45eea8d8902cac5dff80fd91d65f28a7d3a932 +Slot 00003: TxnValidate af6027ffc2be2f8ba4df4682ab45eea8d8902cac5dff80fd91d65f28a7d3a932 Slot 00003: SlotAdd Slot 4 -Slot 00004: W[7]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[8]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[6]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[4]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[2]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[1]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[10]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[9]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[3]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[5]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 371a94483de77e79cc8f6a8405c2e467b87e75be3ccfe8389444d9b0043c9b03, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[7]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[8]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[6]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[4]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[2]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[1]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[10]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[9]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[3]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[5]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 50185f064293479a24925d63f107aecd722bea85a1e3221f5c46b35bdc06f45b, BlockNumber 3). UTxO state was added to the end. Slot 00004: SlotAdd Slot 5 Slot 00005: W[7]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 76be8b528d0075f7aae98d6fa57a6d3c83ae480a8469e668d7b0af968995ac71, BlockNumber 4). UTxO state was added to the end. Slot 00005: W[8]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 76be8b528d0075f7aae98d6fa57a6d3c83ae480a8469e668d7b0af968995ac71, BlockNumber 4). UTxO state was added to the end. @@ -70,14 +70,14 @@ Wallet 6: Wallet 4: {, ""}: 100000000 Wallet 2: - {, ""}: 99999497 + {, ""}: 99822399 Wallet 1: - {, ""}: 99999632 + {, ""}: 99828339 Wallet 10: {, ""}: 100000000 Wallet 9: {, ""}: 100000000 Wallet 3: - {, ""}: 99999497 + {, ""}: 99822399 Wallet 5: {, ""}: 100000000 diff --git a/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions2.txt b/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions2.txt index 182bf1f97c..bfc8d34827 100644 --- a/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions2.txt +++ b/plutus-contract/test/Spec/golden/traceOutput - pubKeyTransactions2.txt @@ -10,58 +10,58 @@ Slot 00001: W[10]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360 Slot 00001: W[9]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. Slot 00001: W[3]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. Slot 00001: W[5]: InsertionSuccess: New tip is Tip(Slot 1, BlockId 3c376d383360ddc44271308f02ddb2a12f1cef21beb994afca459a5b1adeb877, BlockNumber 0). UTxO state was added to the end. -Slot 00001: W[1]: TxSubmit: 6017e4db9196ec7808bf0b3be12852a32a3db288e03fd29a478424def26a80a6 -Slot 00001: TxnValidate 6017e4db9196ec7808bf0b3be12852a32a3db288e03fd29a478424def26a80a6 +Slot 00001: W[1]: TxSubmit: 1b5ce8edfe6a6e41e866434a0c835d2bd78bccdaff6b8002af35de7df03e7b6c +Slot 00001: TxnValidate 1b5ce8edfe6a6e41e866434a0c835d2bd78bccdaff6b8002af35de7df03e7b6c Slot 00001: SlotAdd Slot 2 -Slot 00002: W[7]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[8]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[6]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[4]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[2]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[1]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[10]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[9]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[3]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[5]: InsertionSuccess: New tip is Tip(Slot 2, BlockId c6c4ca2e3bcea9c9db525892606b07d7d2d56f007e5b544c3603fce472ea35d8, BlockNumber 1). UTxO state was added to the end. -Slot 00002: W[2]: TxSubmit: 20b0b18165e50ac735d74aef5ea70b38c8ba59573a04a57cdd5fb6fa9bfa1ce8 -Slot 00002: TxnValidate 20b0b18165e50ac735d74aef5ea70b38c8ba59573a04a57cdd5fb6fa9bfa1ce8 +Slot 00002: W[7]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[8]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[6]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[4]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[2]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[1]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[10]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[9]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[3]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[5]: InsertionSuccess: New tip is Tip(Slot 2, BlockId 4ac275c9db9fdfbdda6bd47e23e7aef1f1792a7abea425f718320859f23bcfc9, BlockNumber 1). UTxO state was added to the end. +Slot 00002: W[2]: TxSubmit: b7d4a17df04ce0e1ed5f2903b65a06245ca3d6aa882a01ac0a907503c35695c5 +Slot 00002: TxnValidate b7d4a17df04ce0e1ed5f2903b65a06245ca3d6aa882a01ac0a907503c35695c5 Slot 00002: SlotAdd Slot 3 -Slot 00003: W[7]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[8]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[6]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[4]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[2]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[1]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[10]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[9]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[3]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[5]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 159692c64c740290e11d18614c22130ecaeaf13af8f1ba73fbb565b6b6d99c91, BlockNumber 2). UTxO state was added to the end. -Slot 00003: W[3]: TxSubmit: 61c067fe6e021db55153888c91118bc58a0fe8788a93f31208f2a56837e37a8b -Slot 00003: TxnValidate 61c067fe6e021db55153888c91118bc58a0fe8788a93f31208f2a56837e37a8b +Slot 00003: W[7]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[8]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[6]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[4]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[2]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[1]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[10]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[9]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[3]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[5]: InsertionSuccess: New tip is Tip(Slot 3, BlockId 5ed77f308b98ff60326fbaf849d6db446242938ccedd417df5f13c5b59cb4769, BlockNumber 2). UTxO state was added to the end. +Slot 00003: W[3]: TxSubmit: 3208381e135ab1f466bf6ea0221b9d52f349237ee9ce3330dfa9605c7dfd838d +Slot 00003: TxnValidate 3208381e135ab1f466bf6ea0221b9d52f349237ee9ce3330dfa9605c7dfd838d Slot 00003: SlotAdd Slot 4 -Slot 00004: W[7]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[8]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[6]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[4]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[2]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[1]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[10]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[9]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[3]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[5]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 353dd49a02cd5f38dc13aa37b3be2d59d5dbfbee1e83c27d7263564bf280532b, BlockNumber 3). UTxO state was added to the end. -Slot 00004: W[1]: TxSubmit: de1fa2d32393f6971f0235c516cc2a02535b11b036f6a94f5f55dea0bb8ac442 -Slot 00004: TxnValidate de1fa2d32393f6971f0235c516cc2a02535b11b036f6a94f5f55dea0bb8ac442 +Slot 00004: W[7]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[8]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[6]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[4]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[2]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[1]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[10]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[9]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[3]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[5]: InsertionSuccess: New tip is Tip(Slot 4, BlockId 2b3746ebdfbb5ce21e37f402be9327676f5e7add7ab77182d13232e3695cd6bb, BlockNumber 3). UTxO state was added to the end. +Slot 00004: W[1]: TxSubmit: 8a8718e38277a729cc3ecf1f28b487ba9a46d30939e3ed8551b62e6940f1846c +Slot 00004: TxnValidate 8a8718e38277a729cc3ecf1f28b487ba9a46d30939e3ed8551b62e6940f1846c Slot 00004: SlotAdd Slot 5 -Slot 00005: W[7]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[8]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[6]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[4]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[2]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[1]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[10]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[9]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[3]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. -Slot 00005: W[5]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 88ba7ab20413fa9aa4e3da8c65e86728c53bdadde8469095012a48fe8ce8f2ab, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[7]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[8]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[6]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[4]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[2]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[1]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[10]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[9]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[3]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. +Slot 00005: W[5]: InsertionSuccess: New tip is Tip(Slot 5, BlockId 177ee7cfa492ccce6ebed475b10a9b561509eceb381b80b84d9e16092bb97b19, BlockNumber 4). UTxO state was added to the end. Slot 00005: SlotAdd Slot 6 Slot 00006: W[7]: InsertionSuccess: New tip is Tip(Slot 6, BlockId 76be8b528d0075f7aae98d6fa57a6d3c83ae480a8469e668d7b0af968995ac71, BlockNumber 5). UTxO state was added to the end. Slot 00006: W[8]: InsertionSuccess: New tip is Tip(Slot 6, BlockId 76be8b528d0075f7aae98d6fa57a6d3c83ae480a8469e668d7b0af968995ac71, BlockNumber 5). UTxO state was added to the end. @@ -83,14 +83,14 @@ Wallet 6: Wallet 4: {, ""}: 100000000 Wallet 2: - {, ""}: 99999223 + {, ""}: 99810343 Wallet 1: - {, ""}: 99997887 + {, ""}: 99596090 Wallet 10: {, ""}: 100000000 Wallet 9: {, ""}: 100000000 Wallet 3: - {, ""}: 99999497 + {, ""}: 99822399 Wallet 5: {, ""}: 100000000 diff --git a/plutus-example/test/Test/PlutusExample/Direct/CertifyingAndWithdrawingPlutus.hs b/plutus-example/test/Test/PlutusExample/Direct/CertifyingAndWithdrawingPlutus.hs index 17c0933ee2..8fb9e968f0 100644 --- a/plutus-example/test/Test/PlutusExample/Direct/CertifyingAndWithdrawingPlutus.hs +++ b/plutus-example/test/Test/PlutusExample/Direct/CertifyingAndWithdrawingPlutus.hs @@ -209,7 +209,7 @@ hprop_plutus_certifying_withdrawing = H.integration . H.runFinallies . H.workspa -- Things take long on non-linux machines if isLinux - then H.threadDelay 5000000 + then H.threadDelay 8000000 else H.threadDelay 10000000 -- Check to see if pledge's stake address was registered @@ -358,7 +358,7 @@ hprop_plutus_certifying_withdrawing = H.integration . H.runFinallies . H.workspa ] if isLinux - then H.threadDelay 5000000 + then H.threadDelay 10000000 else H.threadDelay 20000000 void $ H.execCli' execConfig @@ -530,8 +530,8 @@ hprop_plutus_certifying_withdrawing = H.integration . H.runFinallies . H.workspa , "--testnet-magic", show @Int testnetMagic ] - -- Wait 5 seconds - H.threadDelay 5000000 + -- Wait 8 seconds + H.threadDelay 8000000 H.note_ "Check to see if staking script was delegated" @@ -693,7 +693,7 @@ hprop_plutus_certifying_withdrawing = H.integration . H.runFinallies . H.workspa -- Things take long on non-linux machines if isLinux - then H.threadDelay 5000000 + then H.threadDelay 8000000 else H.threadDelay 10000000 H.note_ "Check UTxO at script staking address to see if withdrawal was successful" diff --git a/plutus-example/test/plutus-example-test.hs b/plutus-example/test/plutus-example-test.hs index 3f6ed44f4d..319669ec79 100644 --- a/plutus-example/test/plutus-example-test.hs +++ b/plutus-example/test/plutus-example-test.hs @@ -19,8 +19,7 @@ main = do plutusExampleTests :: TestTree plutusExampleTests = testGroup "plutus-example" - [ -- Fails to meet deadline on MacOS for an unknown reason - -- Also, fails intermittently for some reason. Investigation is needed. + [ -- Flaky test: -- testProperty "Plutus.Direct.CertifyingAndWithdrawingPlutus" Test.PlutusExample.Direct.CertifyingAndWithdrawingPlutus.hprop_plutus_certifying_withdrawing testProperty "prop_TxId_Api_Ledger_Plutus_Roundtrip" Test.PlutusExample.Plutus.prop_TxId_Api_Ledger_Plutus_Roundtrip , testProperty "prop_TxId_Api_Ledger_Roundtrip" Test.PlutusExample.Plutus.prop_TxId_Api_Ledger_Roundtrip diff --git a/plutus-ledger-constraints/src/Ledger/Constraints/OffChain.hs b/plutus-ledger-constraints/src/Ledger/Constraints/OffChain.hs index c19153dbe2..ac30a78b6e 100644 --- a/plutus-ledger-constraints/src/Ledger/Constraints/OffChain.hs +++ b/plutus-ledger-constraints/src/Ledger/Constraints/OffChain.hs @@ -47,7 +47,7 @@ module Ledger.Constraints.OffChain( , missingValueSpent ) where -import Control.Lens (At (at), iforM_, makeLensesFor, over, use, view, (%=), (.=), (<>=)) +import Control.Lens (At (at), alaf, iforM_, makeLensesFor, use, view, (%=), (.=), (<>=)) import Control.Monad (forM_) import Control.Monad.Except (MonadError (catchError, throwError), runExcept, unless) import Control.Monad.Reader (MonadReader (ask), ReaderT (runReaderT), asks) @@ -55,6 +55,8 @@ import Control.Monad.State (MonadState (get, put), execStateT, gets) import Data.Aeson (FromJSON, ToJSON) import Data.Foldable (traverse_) +import Data.Functor ((<&>)) +import Data.Functor.Compose (Compose (Compose)) import Data.List (elemIndex) import Data.Map (Map) import Data.Map qualified as Map @@ -69,7 +71,6 @@ import PlutusTx.Numeric qualified as N import Data.Semigroup (First (First, getFirst)) import Data.Set (Set) -import Ledger qualified import Ledger.Ada qualified as Ada import Ledger.Address (PaymentPubKey (PaymentPubKey), PaymentPubKeyHash (PaymentPubKeyHash), StakePubKeyHash, pubKeyHashAddress) @@ -82,6 +83,7 @@ import Ledger.Constraints.TxConstraints (ScriptInputConstraint (ScriptInputConst TxConstraints (TxConstraints, txConstraintFuns, txConstraints, txOwnInputs, txOwnOutputs)) import Ledger.Crypto (pubKeyHash) import Ledger.Orphans () +import Ledger.Params (Params) import Ledger.Tx (ChainIndexTxOut, RedeemerPtr (RedeemerPtr), ScriptTag (Mint), Tx, TxOut (txOutAddress, txOutDatumHash, txOutValue), TxOutRef) import Ledger.Tx qualified as Tx @@ -89,6 +91,7 @@ import Ledger.Typed.Scripts (Any, TypedValidator, ValidatorTypes (DatumType, Red import Ledger.Typed.Scripts qualified as Scripts import Ledger.Typed.Tx (ConnectionError) import Ledger.Typed.Tx qualified as Typed +import Ledger.Validation (evaluateMinLovelaceOutput, fromPlutusTxOutUnsafe) import Plutus.Script.Utils.V1.Scripts (datumHash, mintingPolicyHash, validatorHash) import Plutus.Script.Utils.V1.Tx (scriptAddressTxOut) import Plutus.V1.Ledger.Api (Datum (Datum), DatumHash, MintingPolicy, MintingPolicyHash, Redeemer, Validator, @@ -399,15 +402,14 @@ mkTx lookups txc = mkSomeTx [SomeLookupsAndConstraints lookups txc] -- | Each transaction output should contain a minimum amount of Ada (this is a -- restriction on the real Cardano network). --- --- TODO: In the future, the minimum Ada value should be configurable. -adjustUnbalancedTx :: UnbalancedTx -> UnbalancedTx -adjustUnbalancedTx = over (tx . Tx.outputs . traverse) adjustTxOut +adjustUnbalancedTx :: Params -> UnbalancedTx -> Either Tx.ToCardanoError ([Ada.Ada], UnbalancedTx) +adjustUnbalancedTx params = alaf Compose (tx . Tx.outputs . traverse) adjustTxOut where - adjustTxOut :: TxOut -> TxOut - adjustTxOut txOut = - let missingLovelace = max 0 (Ledger.minAdaTxOut - Ada.fromValue (txOutValue txOut)) - in txOut { txOutValue = txOutValue txOut <> Ada.toValue missingLovelace } + adjustTxOut :: TxOut -> Either Tx.ToCardanoError ([Ada.Ada], TxOut) + adjustTxOut txOut = fromPlutusTxOutUnsafe params txOut <&> \txOut' -> + let minAdaTxOut' = evaluateMinLovelaceOutput params txOut' + missingLovelace = max 0 (minAdaTxOut' - Ada.fromValue (txOutValue txOut)) + in ([missingLovelace], txOut { txOutValue = txOutValue txOut <> Ada.toValue missingLovelace }) -- | Add the remaining balance of the total value that the tx must spend. -- See note [Balance of value spent] diff --git a/plutus-ledger-constraints/test/Spec.hs b/plutus-ledger-constraints/test/Spec.hs index a5e6089938..06e9d631b4 100644 --- a/plutus-ledger-constraints/test/Spec.hs +++ b/plutus-ledger-constraints/test/Spec.hs @@ -29,6 +29,7 @@ import Ledger.Constraints.OffChain qualified as OC import Ledger.Credential (Credential (PubKeyCredential, ScriptCredential), StakingCredential (StakingHash)) import Ledger.Crypto (PubKeyHash (PubKeyHash)) import Ledger.Generators qualified as Gen +import Ledger.Params () import Ledger.Tx (Tx (txOutputs), TxOut (TxOut, txOutAddress)) import Ledger.Typed.Scripts qualified as Scripts import Ledger.Value (CurrencySymbol, Value (Value)) diff --git a/plutus-ledger/plutus-ledger.cabal b/plutus-ledger/plutus-ledger.cabal index 62886539f0..7e4e16a45f 100644 --- a/plutus-ledger/plutus-ledger.cabal +++ b/plutus-ledger/plutus-ledger.cabal @@ -58,11 +58,13 @@ library Ledger.Crypto.Orphans Ledger.Fee Ledger.Generators - Ledger.Orphans Ledger.Index + Ledger.Index.Internal Ledger.Scripts Ledger.Scripts.Orphans Ledger.Slot + Ledger.Orphans + Ledger.Params Ledger.TimeSlot Ledger.Tokens Ledger.Tx diff --git a/plutus-ledger/src/Ledger.hs b/plutus-ledger/src/Ledger.hs index 6da0b9890e..2cf3738987 100644 --- a/plutus-ledger/src/Ledger.hs +++ b/plutus-ledger/src/Ledger.hs @@ -13,6 +13,7 @@ import Ledger.Blockchain as Export import Ledger.Crypto as Export import Ledger.Index as Export import Ledger.Orphans () +import Ledger.Params as Export import Ledger.Scripts as Export import Ledger.Slot as Export import Ledger.Tx as Export diff --git a/plutus-ledger/src/Ledger/Generators.hs b/plutus-ledger/src/Ledger/Generators.hs index 5faeb4a66d..fe7b6caee5 100644 --- a/plutus-ledger/src/Ledger/Generators.hs +++ b/plutus-ledger/src/Ledger/Generators.hs @@ -84,6 +84,7 @@ import Ledger.Ada qualified as Ada import Ledger.CardanoWallet qualified as CW import Ledger.Fee (FeeConfig (fcScriptsFeeFactor), calcFees) import Ledger.Index qualified as Index +import Ledger.Params (Params (pSlotConfig)) import Ledger.TimeSlot (SlotConfig) import Ledger.TimeSlot qualified as TimeSlot import Ledger.Value qualified as Value @@ -129,7 +130,7 @@ constantFee = def { fcScriptsFeeFactor = 0 } data Mockchain = Mockchain { mockchainInitialTxPool :: [Tx], mockchainUtxo :: Map TxOutRef TxOut, - mockchainSlotConfig :: SlotConfig + mockchainParams :: Params } deriving Show -- | The empty mockchain. @@ -149,7 +150,7 @@ genMockchain' gm = do pure Mockchain { mockchainInitialTxPool = [txn], mockchainUtxo = Map.fromList $ first (TxOutRef tid) <$> zip [0..] ot, - mockchainSlotConfig = slotCfg + mockchainParams = def { pSlotConfig = slotCfg } } -- | Generate a mockchain using the default 'GeneratorModel'. @@ -379,10 +380,10 @@ assertValid tx mc = Hedgehog.assert $ isNothing $ validateMockchain mc tx -- | Validate a transaction in a mockchain. validateMockchain :: Mockchain -> Tx -> Maybe Index.ValidationError -validateMockchain (Mockchain txPool _ slotCfg) tx = result where +validateMockchain (Mockchain txPool _ params) tx = result where h = 1 idx = Index.initialise [map Valid txPool] - result = fmap snd $ fst $ fst $ Index.runValidation (Index.validateTransaction h tx) (ValidationCtx idx slotCfg) + result = fmap snd $ fst $ fst $ Index.runValidation (Index.validateTransaction h tx) (ValidationCtx idx params) {- | Split a value into max. n positive-valued parts such that the sum of the parts equals the original value. Each part should contain the required @@ -410,8 +411,8 @@ genTxInfo :: MonadGen m => Mockchain -> m TxInfo genTxInfo chain = do tx <- genValidTransaction chain let idx = UtxoIndex $ mockchainUtxo chain - let slotCfg = mockchainSlotConfig chain - let (res, _) = runWriter $ runExceptT $ runReaderT (_runValidation (Index.mkTxInfo tx)) (ValidationCtx idx slotCfg) + let params = mockchainParams chain + let (res, _) = runWriter $ runExceptT $ runReaderT (_runValidation (Index.mkTxInfo tx)) (ValidationCtx idx params) either (const Gen.discard) pure res genScriptPurposeSpending :: MonadGen m => TxInfo -> m Contexts.ScriptPurpose diff --git a/plutus-ledger/src/Ledger/Index.hs b/plutus-ledger/src/Ledger/Index.hs index 6781829fd6..73e0f13ba8 100644 --- a/plutus-ledger/src/Ledger/Index.hs +++ b/plutus-ledger/src/Ledger/Index.hs @@ -34,6 +34,7 @@ module Ledger.Index( minFee, maxFee, minAdaTxOut, + minLovelaceTxOut, mkTxInfo, -- * Actual validation validateTransaction, @@ -50,13 +51,9 @@ module Ledger.Index( getScript ) where +import Cardano.Api (Lovelace (..)) import Prelude hiding (lookup) -import Cardano.Ledger.Alonzo (AlonzoEra) -import Cardano.Ledger.Crypto (StandardCrypto) - -import Codec.Serialise (Serialise) -import Control.DeepSeq (NFData) import Control.Lens (toListOf, view, (^.)) import Control.Lens.Indexed (iforM_) import Control.Monad @@ -65,8 +62,8 @@ import Control.Monad.Reader (MonadReader (..), ReaderT (..), ask) import Control.Monad.Writer (MonadWriter, Writer, runWriter, tell) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Foldable (asum, fold, foldl', for_, traverse_) +import Data.Functor ((<&>)) import Data.Map qualified as Map -import Data.OpenApi.Schema qualified as OpenApi import Data.Set qualified as Set import Data.Text (Text) import GHC.Generics (Generic) @@ -74,10 +71,13 @@ import Ledger.Ada (Ada) import Ledger.Ada qualified as Ada import Ledger.Blockchain import Ledger.Crypto +import Ledger.Index.Internal import Ledger.Orphans () +import Ledger.Params (Params (pSlotConfig)) import Ledger.Slot qualified as Slot import Ledger.TimeSlot qualified as TimeSlot import Ledger.Tx +import Ledger.Validation (evaluateMinLovelaceOutput, fromPlutusTxOutUnsafe) import Plutus.Script.Utils.V1.Scripts import Plutus.Script.Utils.V1.Scripts qualified as PV1 import Plutus.V1.Ledger.Address @@ -91,20 +91,12 @@ import Plutus.V1.Ledger.Scripts qualified as Scripts import Plutus.V1.Ledger.Value qualified as V import PlutusTx (toBuiltinData) import PlutusTx.Numeric qualified as P -import Prettyprinter (Pretty) -import Prettyprinter.Extras (PrettyShow (..)) -- | Context for validating transactions. We need access to the unspent -- transaction outputs of the blockchain, and we can throw 'ValidationError's. type ValidationMonad m = (MonadReader ValidationCtx m, MonadError ValidationError m, MonadWriter [ScriptValidationEvent] m) -data ValidationCtx = ValidationCtx { vctxIndex :: UtxoIndex, vctxSlotConfig :: TimeSlot.SlotConfig } - --- | The UTxOs of a blockchain indexed by their references. -newtype UtxoIndex = UtxoIndex { getIndex :: Map.Map TxOutRef TxOut } - deriving stock (Show, Generic) - deriving newtype (Eq, Semigroup, OpenApi.ToSchema, Monoid, Serialise) - deriving anyclass (FromJSON, ToJSON, NFData) +data ValidationCtx = ValidationCtx { vctxIndex :: UtxoIndex, vctxParams :: Params } -- | Create an index of all UTxOs on the chain. initialise :: Blockchain -> UtxoIndex @@ -128,54 +120,6 @@ lookup i index = case Map.lookup i $ getIndex index of Just t -> pure t Nothing -> throwError $ TxOutRefNotFound i -type EmulatorEra = AlonzoEra StandardCrypto - --- | A reason why a transaction is invalid. -data ValidationError = - InOutTypeMismatch TxIn TxOut - -- ^ A pay-to-pubkey output was consumed by a pay-to-script input or vice versa, or the 'TxIn' refers to a different public key than the 'TxOut'. - | TxOutRefNotFound TxOutRef - -- ^ The transaction output consumed by a transaction input could not be found (either because it was already spent, or because - -- there was no transaction with the given hash on the blockchain). - | InvalidScriptHash Validator ValidatorHash - -- ^ For pay-to-script outputs: the validator script provided in the transaction input does not match the hash specified in the transaction output. - | InvalidDatumHash Datum DatumHash - -- ^ For pay-to-script outputs: the datum provided in the transaction input does not match the hash specified in the transaction output. - | MissingRedeemer RedeemerPtr - -- ^ For scripts that take redeemers: no redeemer was provided for this script. - | InvalidSignature PubKey Signature - -- ^ For pay-to-pubkey outputs: the signature of the transaction input does not match the public key of the transaction output. - | ValueNotPreserved V.Value V.Value - -- ^ The amount spent by the transaction differs from the amount consumed by it. - | NegativeValue Tx - -- ^ The transaction produces an output with a negative value. - | ValueContainsLessThanMinAda Tx TxOut - -- ^ The transaction produces an output with a value containing less than the minimum required Ada. - | NonAdaFees Tx - -- ^ The fee is not denominated entirely in Ada. - | ScriptFailure ScriptError - -- ^ For pay-to-script outputs: evaluation of the validator script failed. - | CurrentSlotOutOfRange Slot.Slot - -- ^ The current slot is not covered by the transaction's validity slot range. - | SignatureMissing PubKeyHash - -- ^ The transaction is missing a signature - | MintWithoutScript Scripts.MintingPolicyHash - -- ^ The transaction attempts to mint value of a currency without running - -- the currency's minting policy. - | TransactionFeeTooLow V.Value V.Value - -- ^ The transaction fee is lower than the minimum acceptable fee. - | CardanoLedgerValidationError String - -- ^ An error from Cardano.Ledger validation - deriving (Eq, Show, Generic) - -instance FromJSON ValidationError -instance ToJSON ValidationError -deriving via (PrettyShow ValidationError) instance Pretty ValidationError - -data ValidationPhase = Phase1 | Phase2 deriving (Eq, Show, Generic, FromJSON, ToJSON) -deriving via (PrettyShow ValidationPhase) instance Pretty ValidationPhase -type ValidationErrorInPhase = (ValidationPhase, ValidationError) - -- | A monad for running transaction validation inside, which is an instance of 'ValidationMonad'. newtype Validation a = Validation { _runValidation :: (ReaderT ValidationCtx (ExceptT ValidationError (Writer [ScriptValidationEvent]))) a } deriving newtype (Functor, Applicative, Monad, MonadReader ValidationCtx, MonadError ValidationError, MonadWriter [ScriptValidationEvent]) @@ -385,22 +329,30 @@ checkPositiveValues t = {-# INLINABLE minAdaTxOut #-} -- Minimum required Ada for each tx output. -- --- TODO: In the future, make the value configurable. +-- TODO: Should be removed. minAdaTxOut :: Ada -minAdaTxOut = Ada.lovelaceOf 2_000_000 +minAdaTxOut = Ada.lovelaceOf minTxOut --- | Check if each transaction outputs produced at least two Ada (this is a --- restriction on the real Cardano network). +{-# INLINABLE minTxOut #-} +minTxOut :: Integer +minTxOut = 2_000_000 + +-- Minimum required Lovelace for each tx output. -- --- Normally, the minimum is 1 Ada, but transaction outputs that have datum are --- slightly more expensive than 1 Ada. So, to be on the safe side, we set the --- minimum Ada of each transaction output to 2 Ada. +minLovelaceTxOut :: Lovelace +minLovelaceTxOut = Lovelace minTxOut + +-- | Check if each transaction outputs produced a minimum lovelace output. checkMinAdaInTxOutputs :: ValidationMonad m => Tx -> m () -checkMinAdaInTxOutputs t@Tx { txOutputs } = - for_ txOutputs $ \txOut -> - if Ada.fromValue (txOutValue txOut) >= minAdaTxOut +checkMinAdaInTxOutputs t@Tx { txOutputs } = do + params <- vctxParams <$> ask + for_ txOutputs $ \txOut -> do + let + minAdaTxOut' = either (const minAdaTxOut) id $ + fromPlutusTxOutUnsafe params txOut <&> \txOut' -> evaluateMinLovelaceOutput params txOut' + if Ada.fromValue (txOutValue txOut) >= minAdaTxOut' then pure () - else throwError $ ValueContainsLessThanMinAda t txOut + else throwError $ ValueContainsLessThanMinAda t txOut (Ada.toValue minAdaTxOut') -- | Check if the fees are paid exclusively in Ada. checkFeeIsAda :: ValidationMonad m => Tx -> m () @@ -429,7 +381,7 @@ checkTransactionFee tx = -- | Create the data about the transaction which will be passed to a validator script. mkTxInfo :: ValidationMonad m => Tx -> m TxInfo mkTxInfo tx = do - slotCfg <- vctxSlotConfig <$> ask + slotCfg <- pSlotConfig . vctxParams <$> ask txins <- traverse mkIn $ Set.toList $ view inputs tx let ptx = TxInfo { txInfoInputs = txins diff --git a/plutus-ledger/src/Ledger/Index/Internal.hs b/plutus-ledger/src/Ledger/Index/Internal.hs new file mode 100644 index 0000000000..56ef90688b --- /dev/null +++ b/plutus-ledger/src/Ledger/Index/Internal.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Ledger.Index.Internal where + +import Cardano.Ledger.Alonzo (AlonzoEra) +import Cardano.Ledger.Crypto (StandardCrypto) + +import Prelude hiding (lookup) + +import Codec.Serialise (Serialise) +import Control.DeepSeq (NFData) +import Data.Aeson (FromJSON (..), ToJSON (..)) +import Data.Map qualified as Map +import Data.OpenApi.Schema qualified as OpenApi +import GHC.Generics (Generic) +import Ledger.Crypto +import Ledger.Orphans () +import Ledger.Slot qualified as Slot +import Ledger.Tx +import Plutus.V1.Ledger.Scripts qualified as Scripts +import Plutus.V1.Ledger.Value qualified as V +import Prettyprinter (Pretty) +import Prettyprinter.Extras (PrettyShow (..)) + +-- | The UTxOs of a blockchain indexed by their references. +newtype UtxoIndex = UtxoIndex { getIndex :: Map.Map TxOutRef TxOut } + deriving stock (Show, Generic) + deriving newtype (Eq, Semigroup, OpenApi.ToSchema, Monoid, Serialise) + deriving anyclass (FromJSON, ToJSON, NFData) + +type EmulatorEra = AlonzoEra StandardCrypto + + +-- | A reason why a transaction is invalid. +data ValidationError = + InOutTypeMismatch TxIn TxOut + -- ^ A pay-to-pubkey output was consumed by a pay-to-script input or vice versa, or the 'TxIn' refers to a different public key than the 'TxOut'. + | TxOutRefNotFound TxOutRef + -- ^ The transaction output consumed by a transaction input could not be found (either because it was already spent, or because + -- there was no transaction with the given hash on the blockchain). + | InvalidScriptHash Scripts.Validator Scripts.ValidatorHash + -- ^ For pay-to-script outputs: the validator script provided in the transaction input does not match the hash specified in the transaction output. + | InvalidDatumHash Scripts.Datum Scripts.DatumHash + -- ^ For pay-to-script outputs: the datum provided in the transaction input does not match the hash specified in the transaction output. + | MissingRedeemer RedeemerPtr + -- ^ For scripts that take redeemers: no redeemer was provided for this script. + | InvalidSignature PubKey Signature + -- ^ For pay-to-pubkey outputs: the signature of the transaction input does not match the public key of the transaction output. + | ValueNotPreserved V.Value V.Value + -- ^ The amount spent by the transaction differs from the amount consumed by it. + | NegativeValue Tx + -- ^ The transaction produces an output with a negative value. + | ValueContainsLessThanMinAda Tx TxOut V.Value + -- ^ The transaction produces an output with a value containing less than the minimum required Ada. + | NonAdaFees Tx + -- ^ The fee is not denominated entirely in Ada. + | ScriptFailure Scripts.ScriptError + -- ^ For pay-to-script outputs: evaluation of the validator script failed. + | CurrentSlotOutOfRange Slot.Slot + -- ^ The current slot is not covered by the transaction's validity slot range. + | SignatureMissing PubKeyHash + -- ^ The transaction is missing a signature + | MintWithoutScript Scripts.MintingPolicyHash + -- ^ The transaction attempts to mint value of a currency without running + -- the currency's minting policy. + | TransactionFeeTooLow V.Value V.Value + -- ^ The transaction fee is lower than the minimum acceptable fee. + | CardanoLedgerValidationError String + -- ^ An error from Cardano.Ledger validation + deriving (Eq, Show, Generic) + +instance FromJSON ValidationError +instance ToJSON ValidationError +deriving via (PrettyShow ValidationError) instance Pretty ValidationError + +data ValidationPhase = Phase1 | Phase2 deriving (Eq, Show, Generic, FromJSON, ToJSON) +deriving via (PrettyShow ValidationPhase) instance Pretty ValidationPhase +type ValidationErrorInPhase = (ValidationPhase, ValidationError) diff --git a/plutus-ledger/src/Ledger/Params.hs b/plutus-ledger/src/Ledger/Params.hs new file mode 100644 index 0000000000..2468880bc9 --- /dev/null +++ b/plutus-ledger/src/Ledger/Params.hs @@ -0,0 +1,78 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -Wno-orphans #-} +-- | The set of parameters, like protocol parameters and slot configuration. +module Ledger.Params( + Params(..), + slotConfigL, + protocolParamsL, + networkIdL, + allowBigTransactions +) where + +import Cardano.Api.Shelley +import Control.Lens (makeLensesFor, over) +import Data.Default (Default (def)) +import Data.Map (fromList) +import Data.Maybe (fromMaybe) +import Data.Ratio ((%)) +import Ledger.TimeSlot (SlotConfig) +import PlutusCore (defaultCostModelParams) + +data Params = Params + { pSlotConfig :: SlotConfig + , pProtocolParams :: ProtocolParameters + , pNetworkId :: NetworkId + } + deriving (Eq, Show) + +makeLensesFor + [ ("pSlotConfig", "slotConfigL") + , ("pProtocolParams", "protocolParamsL") + , ("pNetworkId", "networkIdL") ] + ''Params + +-- | Set higher limits on transaction size and execution units. +-- This can be used to work around @MaxTxSizeUTxO@ and @ExUnitsTooBigUTxO@ errors. +-- Note that if you need this your Plutus script will probably not validate on Mainnet. +allowBigTransactions :: Params -> Params +allowBigTransactions = over protocolParamsL fixParams + where + fixParams pp = pp + { protocolParamMaxTxSize = 256 * 1024 + , protocolParamMaxTxExUnits = Just (ExecutionUnits {executionSteps = 100000000000, executionMemory = 100000000}) + } + +instance Default Params where + def = Params def def (Testnet $ NetworkMagic 1) + +instance Default ProtocolParameters where + -- The protocol parameters as they are in the Alonzo era. + def = ProtocolParameters + { protocolParamProtocolVersion = (6,0) + , protocolParamDecentralization = Just (3 % 5) + , protocolParamExtraPraosEntropy = Nothing + , protocolParamMaxBlockHeaderSize = 1100 + , protocolParamMaxBlockBodySize = 65536 + , protocolParamMaxTxSize = 16384 + , protocolParamTxFeeFixed = 155381 + , protocolParamTxFeePerByte = 44 + , protocolParamMinUTxOValue = Nothing + , protocolParamStakeAddressDeposit = Lovelace 2000000 + , protocolParamStakePoolDeposit = Lovelace 500000000 + , protocolParamMinPoolCost = Lovelace 340000000 + , protocolParamPoolRetireMaxEpoch = EpochNo 18 + , protocolParamStakePoolTargetNum = 150 + , protocolParamPoolPledgeInfluence = 3 % 10 + , protocolParamMonetaryExpansion = 3 % 1000 + , protocolParamTreasuryCut = 1 % 5 + , protocolParamUTxOCostPerWord = Just (Lovelace 34482) + , protocolParamCostModels = fromList + [ (AnyPlutusScriptVersion PlutusScriptV1, CostModel $ fromMaybe (error "Ledger.Params: defaultCostModelParams is broken") defaultCostModelParams) ] + , protocolParamPrices = Just (ExecutionUnitPrices {priceExecutionSteps = 721 % 10000000, priceExecutionMemory = 577 % 10000}) + , protocolParamMaxTxExUnits = Just (ExecutionUnits {executionSteps = 10000000000, executionMemory = 10000000}) + , protocolParamMaxBlockExUnits = Just (ExecutionUnits {executionSteps = 40000000000, executionMemory = 50000000}) + , protocolParamMaxValueSize = Just 5000 + , protocolParamCollateralPercent = Just 150 + , protocolParamMaxCollateralInputs = Just 3 + } diff --git a/plutus-ledger/src/Ledger/Scripts.hs b/plutus-ledger/src/Ledger/Scripts.hs index fa3aae6ade..5c8f346639 100644 --- a/plutus-ledger/src/Ledger/Scripts.hs +++ b/plutus-ledger/src/Ledger/Scripts.hs @@ -2,5 +2,6 @@ module Ledger.Scripts ( module Export ) where +import Ledger.Scripts.Orphans () import Plutus.Script.Utils.V1.Scripts as Export import Plutus.V1.Ledger.Scripts as Export diff --git a/plutus-ledger/src/Ledger/TimeSlot.hs b/plutus-ledger/src/Ledger/TimeSlot.hs index 330badea22..92f2a2f538 100644 --- a/plutus-ledger/src/Ledger/TimeSlot.hs +++ b/plutus-ledger/src/Ledger/TimeSlot.hs @@ -18,6 +18,10 @@ module Ledger.TimeSlot( , posixTimeRangeToContainedSlotRange , posixTimeToEnclosingSlot , currentSlot +, utcTimeToPOSIXTime +, posixTimeToUTCTime +, nominalDiffTimeToPOSIXTime +, posixTimeToNominalDiffTime ) where import Codec.Serialise (Serialise) @@ -138,8 +142,24 @@ currentSlot :: SlotConfig -> IO Slot currentSlot sc = timeToSlot <$> Time.getPOSIXTime where timeToSlot = posixTimeToEnclosingSlot sc - . POSIXTime - . (* 1000) -- Convert to ms - . Haskell.floor - . Time.nominalDiffTimeToSeconds - + . nominalDiffTimeToPOSIXTime + +utcTimeToPOSIXTime :: Time.UTCTime -> POSIXTime +utcTimeToPOSIXTime = nominalDiffTimeToPOSIXTime . Time.utcTimeToPOSIXSeconds + +posixTimeToUTCTime :: POSIXTime -> Time.UTCTime +posixTimeToUTCTime = Time.posixSecondsToUTCTime . posixTimeToNominalDiffTime + +nominalDiffTimeToPOSIXTime :: Time.NominalDiffTime -> POSIXTime +nominalDiffTimeToPOSIXTime + = POSIXTime + . Haskell.truncate + . (Haskell.* 1000) -- Convert to ms + . Time.nominalDiffTimeToSeconds + +posixTimeToNominalDiffTime :: POSIXTime -> Time.NominalDiffTime +posixTimeToNominalDiffTime + = Time.secondsToNominalDiffTime + . (Haskell./ 1000) + . Haskell.fromInteger + . getPOSIXTime diff --git a/plutus-ledger/src/Ledger/Tx.hs b/plutus-ledger/src/Ledger/Tx.hs index cfc16799ef..7daac30003 100644 --- a/plutus-ledger/src/Ledger/Tx.hs +++ b/plutus-ledger/src/Ledger/Tx.hs @@ -34,6 +34,7 @@ module Ledger.Tx , getCardanoTxUnspentOutputsTx , getCardanoTxFee , SomeCardanoApiTx(..) + , ToCardanoError(..) -- * Transactions , addSignature , addSignature' @@ -62,7 +63,7 @@ import GHC.Generics (Generic) import Ledger.Address (Address, PaymentPubKey, StakePubKey, pubKeyAddress) import Ledger.Crypto (Passphrase, signTx, signTx', toPublicKey) import Ledger.Orphans () -import Ledger.Tx.CardanoAPI (SomeCardanoApiTx (SomeTx)) +import Ledger.Tx.CardanoAPI (SomeCardanoApiTx (SomeTx), ToCardanoError (..)) import Ledger.Tx.CardanoAPI qualified as CardanoAPI import Ledger.Tx.Internal as Export import Plutus.Script.Utils.V1.Scripts (datumHash) diff --git a/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs b/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs index b217a68108..0424242187 100644 --- a/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs +++ b/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs @@ -43,6 +43,7 @@ module Ledger.Tx.CardanoAPI( , toCardanoTxInsCollateral , toCardanoTxInWitness , toCardanoTxOut + , toCardanoTxOutUnsafe , toCardanoTxOutDatumHash , toCardanoAddress , toCardanoMintValue @@ -497,6 +498,17 @@ toCardanoTxOut networkId fromHash (PV1.TxOut addr value datumHash) = <*> fromHash datumHash <*> pure C.ReferenceScriptNone +toCardanoTxOutUnsafe + :: C.NetworkId + -> (Maybe P.DatumHash -> Either ToCardanoError (C.TxOutDatum ctx C.AlonzoEra)) + -> PV1.TxOut + -> Either ToCardanoError (C.TxOut ctx C.AlonzoEra) +toCardanoTxOutUnsafe networkId fromHash (PV1.TxOut addr value datumHash) = + C.TxOut <$> toCardanoAddress networkId addr + <*> toCardanoTxOutValueUnsafe value + <*> fromHash datumHash + <*> pure C.ReferenceScriptNone + lookupDatum :: Map P.DatumHash P.Datum -> Maybe P.DatumHash -> Either ToCardanoError (C.TxOutDatum C.CtxTx C.AlonzoEra) lookupDatum datums datumHash = case flip Map.lookup datums =<< datumHash of @@ -583,6 +595,9 @@ toCardanoTxOutValue value = do when (Ada.fromValue value == mempty) (Left OutputHasZeroAda) C.TxOutValue C.MultiAssetInAlonzoEra <$> toCardanoValue value +toCardanoTxOutValueUnsafe :: PV1.Value -> Either ToCardanoError (C.TxOutValue C.AlonzoEra) +toCardanoTxOutValueUnsafe value = C.TxOutValue C.MultiAssetInAlonzoEra <$> toCardanoValue value + fromCardanoTxOutDatumHash :: C.TxOutDatum C.CtxTx era -> Maybe P.DatumHash fromCardanoTxOutDatumHash C.TxOutDatumNone = Nothing fromCardanoTxOutDatumHash (C.TxOutDatumHash _ h) = Just $ P.DatumHash $ PlutusTx.toBuiltin (C.serialiseToRawBytes h) diff --git a/plutus-ledger/src/Ledger/Validation.hs b/plutus-ledger/src/Ledger/Validation.hs index 8ffc9248e2..9fb5484bf1 100644 --- a/plutus-ledger/src/Ledger/Validation.hs +++ b/plutus-ledger/src/Ledger/Validation.hs @@ -30,6 +30,9 @@ module Ledger.Validation( -- * Conversion from Plutus types fromPlutusTx, fromPlutusIndex, + fromPlutusTxOut, + fromPlutusTxOutUnsafe, + fromPlutusTxOutRef, -- * Lenses ledgerEnv, memPoolState, @@ -39,32 +42,25 @@ module Ledger.Validation( emulatorGlobals ) where -import Cardano.Api.Shelley (ExecutionUnitPrices (ExecutionUnitPrices, priceExecutionMemory, priceExecutionSteps), - ExecutionUnits (ExecutionUnits, executionMemory, executionSteps), - ShelleyBasedEra (ShelleyBasedEraAlonzo), makeSignedTransaction, shelleyGenesisDefaults, +import Cardano.Api.Shelley (ShelleyBasedEra (ShelleyBasedEraAlonzo), makeSignedTransaction, shelleyGenesisDefaults, toShelleyTxId, toShelleyTxOut) import Cardano.Api.Shelley qualified as C.Api import Cardano.Crypto.Wallet qualified as Crypto import Cardano.Ledger.Alonzo (TxBody, TxOut) -import Cardano.Ledger.Alonzo.Genesis (prices) -import Cardano.Ledger.Alonzo.Genesis qualified as Alonzo -import Cardano.Ledger.Alonzo.Language qualified as Alonzo -import Cardano.Ledger.Alonzo.PParams (PParams' (..)) +import Cardano.Ledger.Alonzo.PParams (PParams' (..), retractPP) import Cardano.Ledger.Alonzo.Rules.Utxos (constructValidated) import Cardano.Ledger.Alonzo.Scripts (ExUnits (ExUnits), unCostModels) -import Cardano.Ledger.Alonzo.Scripts qualified as Alonzo import Cardano.Ledger.Alonzo.Tools qualified as C.Ledger import Cardano.Ledger.Alonzo.Tx (ValidatedTx (..)) import Cardano.Ledger.Alonzo.TxBody (TxBody (TxBody, reqSignerHashes)) import Cardano.Ledger.Alonzo.TxWitness (RdmrPtr, txwitsVKey) import Cardano.Ledger.BaseTypes (Globals (..), mkTxIxPartial) -import Cardano.Ledger.BaseTypes qualified as BT import Cardano.Ledger.Core (PParams, Tx) import Cardano.Ledger.Crypto (StandardCrypto) -import Cardano.Ledger.Shelley.API (Coin (..), LedgerEnv (..), MempoolEnv, MempoolState, NewEpochState, TxId, - TxIn (TxIn), UTxO (UTxO), Validated, epochInfo, esPp, mkShelleyGlobals, nesEs) +import Cardano.Ledger.Shelley.API (Coin (..), LedgerEnv (..), MempoolEnv, MempoolState, TxId, TxIn (TxIn), UTxO (UTxO), + Validated, epochInfo, mkShelleyGlobals) import Cardano.Ledger.Shelley.API qualified as C.Ledger -import Cardano.Ledger.Shelley.LedgerState (lsUTxOState, smartUTxOState) +import Cardano.Ledger.Shelley.LedgerState (LedgerState (..), smartUTxOState) import Cardano.Slotting.EpochInfo (fixedEpochInfo) import Cardano.Slotting.Slot (EpochSize (..), SlotNo (..)) import Cardano.Slotting.Time (mkSlotLength) @@ -75,21 +71,19 @@ import Data.Bitraversable (bitraverse) import Data.Default (def) import Data.Functor.Identity (runIdentity) import Data.Map qualified as Map -import Data.Maybe (fromMaybe) -import Data.Ratio ((%)) import Data.Set qualified as Set import GHC.Records (HasField (..)) import Ledger.Ada qualified as P import Ledger.Address qualified as P -import Ledger.Index (EmulatorEra) -import Ledger.Index qualified as P +import Ledger.Index.Internal (EmulatorEra) +import Ledger.Index.Internal qualified as P +import Ledger.Params qualified as P +import Ledger.TimeSlot qualified as P import Ledger.Tx qualified as P import Ledger.Tx.CardanoAPI qualified as P import Ledger.Value qualified as P -import Numeric.Natural (Natural) import Plutus.V1.Ledger.Api qualified as P import Plutus.V1.Ledger.Scripts qualified as P -import PlutusCore qualified as Plutus import PlutusTx.Builtins qualified as Builtins import PlutusTx.ErrorCodes (checkHasFailedError) @@ -176,140 +170,132 @@ makeBlock state = {-| Initial ledger state for a distribution -} -initialState :: EmulatedLedgerState -initialState = EmulatedLedgerState - { _ledgerEnv = C.Ledger.mkMempoolEnv emulatorNes 0 - , _memPoolState = C.Ledger.mkMempoolState emulatorNes +initialState :: P.Params -> EmulatedLedgerState +initialState params = EmulatedLedgerState + { _ledgerEnv = C.Ledger.LedgerEnv + { C.Ledger.ledgerSlotNo = 0 + , C.Ledger.ledgerIx = minBound + , C.Ledger.ledgerPp = C.Api.toLedgerPParams ShelleyBasedEraAlonzo $ P.pProtocolParams params + , C.Ledger.ledgerAccount = C.Ledger.AccountState (Coin 0) (Coin 0) + } + , _memPoolState = LedgerState + { lsUTxOState = smartUTxOState (UTxO mempty) (Coin 0) (Coin 0) def + , lsDPState = C.Ledger.DPState def def + } , _currentBlock = [] , _previousBlocks = [] } -utxoEnv :: SlotNo -> C.Ledger.UtxoEnv EmulatorEra -utxoEnv slotNo = C.Ledger.UtxoEnv slotNo emulatorPParams mempty (C.Ledger.GenDelegs mempty) + +utxoEnv :: P.Params -> SlotNo -> C.Ledger.UtxoEnv EmulatorEra +utxoEnv params slotNo = C.Ledger.UtxoEnv slotNo (emulatorPParams params) mempty (C.Ledger.GenDelegs mempty) applyTx :: + P.Params -> EmulatedLedgerState -> Tx EmulatorEra -> Either P.ValidationError (EmulatedLedgerState, Validated (Tx EmulatorEra)) -applyTx oldState@EmulatedLedgerState{_ledgerEnv, _memPoolState} tx = do - (newMempool, vtx) <- first (P.CardanoLedgerValidationError . show) (C.Ledger.applyTx emulatorGlobals _ledgerEnv _memPoolState tx) +applyTx params oldState@EmulatedLedgerState{_ledgerEnv, _memPoolState} tx = do + (newMempool, vtx) <- first (P.CardanoLedgerValidationError . show) (C.Ledger.applyTx (emulatorGlobals params) _ledgerEnv _memPoolState tx) return (oldState & memPoolState .~ newMempool & over currentBlock ((:) vtx), vtx) -emulatorNetworkId :: C.Api.NetworkId -emulatorNetworkId = C.Api.Testnet $ C.Api.NetworkMagic 1 - --- TODO: the larger maxTxSize should only be used when needed. -genesisDefaultsWithBigMaxTxSize :: C.Ledger.ShelleyGenesis EmulatorEra -genesisDefaultsWithBigMaxTxSize = shelleyGenesisDefaults { - C.Ledger.sgProtocolParams = (C.Ledger.sgProtocolParams shelleyGenesisDefaults) - { -- Is is mandatory to specify the protocol version in order to use - -- builtin functions in a Plutus script. Protocol major version 5 is - -- for Alonzo. If omitted, the validation of a transaction will fail - -- with something like: "Forbidden builtin function". - C.Ledger._protocolVersion = BT.ProtVer 5 0 - , C.Ledger._maxTxSize = 256 * 1024 - } -} +genesisDefaultsFromParams :: P.Params -> C.Ledger.ShelleyGenesis EmulatorEra +genesisDefaultsFromParams P.Params { P.pSlotConfig, P.pProtocolParams, P.pNetworkId } = shelleyGenesisDefaults + { C.Ledger.sgSystemStart = P.posixTimeToUTCTime $ P.scSlotZeroTime pSlotConfig + , C.Ledger.sgNetworkMagic = case pNetworkId of C.Api.Testnet (C.Api.NetworkMagic nm) -> nm; _ -> 0 + , C.Ledger.sgNetworkId = case pNetworkId of C.Api.Testnet _ -> C.Ledger.Testnet; C.Api.Mainnet -> C.Ledger.Mainnet + , C.Ledger.sgProtocolParams = retractPP (Coin 0) $ C.Api.toLedgerPParams ShelleyBasedEraAlonzo pProtocolParams + } {-| A sensible default 'Globals' value for the emulator -} -emulatorGlobals :: Globals -emulatorGlobals = mkShelleyGlobals - genesisDefaultsWithBigMaxTxSize - (fixedEpochInfo (EpochSize 432000) (mkSlotLength 8)) -- Not sure about these values, probably not relevant for the emulator. - 2 -- maxMajorPV (maximum major protocol version) - -emulatorNes :: NewEpochState EmulatorEra -emulatorNes = C.Ledger.initialState genesisDefaultsWithBigMaxTxSize alonzoGenesisDefaults { - prices = fromMaybe (error "emulatorNes: invalid ExecutionUnitPrices") (C.Api.toAlonzoPrices alonzoGenesisDefaultExecutionPrices) -} - where - alonzoGenesisDefaultExecutionPrices :: C.Api.ExecutionUnitPrices - alonzoGenesisDefaultExecutionPrices = - C.Api.ExecutionUnitPrices { - C.Api.priceExecutionSteps = 721 % 10000000, - C.Api.priceExecutionMemory = 577 % 10000 - } - -emulatorPParams :: PParams EmulatorEra -emulatorPParams = esPp $ nesEs emulatorNes +emulatorGlobals :: P.Params -> Globals +emulatorGlobals params@P.Params { P.pSlotConfig, P.pProtocolParams } = mkShelleyGlobals + (genesisDefaultsFromParams params) + (fixedEpochInfo (EpochSize 432000) (mkSlotLength $ P.posixTimeToNominalDiffTime $ P.POSIXTime $ P.scSlotLength pSlotConfig)) + (fst $ C.Api.protocolParamProtocolVersion pProtocolParams) -emulatorProtocolParameters :: C.Api.ProtocolParameters -emulatorProtocolParameters = C.Api.fromLedgerPParams ShelleyBasedEraAlonzo emulatorPParams +emulatorPParams :: P.Params -> PParams EmulatorEra +emulatorPParams P.Params { P.pProtocolParams } = C.Api.toLedgerPParams ShelleyBasedEraAlonzo pProtocolParams -hasValidationErrors :: SlotNo -> UTxO EmulatorEra -> C.Api.Tx C.Api.AlonzoEra -> Maybe P.ValidationErrorInPhase -hasValidationErrors slotNo utxo (C.Api.ShelleyTx _ tx) = +hasValidationErrors :: P.Params -> SlotNo -> UTxO EmulatorEra -> C.Api.Tx C.Api.AlonzoEra -> Maybe P.ValidationErrorInPhase +hasValidationErrors params slotNo utxo (C.Api.ShelleyTx _ tx) = case res of Left e -> Just (P.Phase1, e) Right _ -> Nothing where - state = setSlot slotNo $ setUtxo utxo initialState + state = setSlot slotNo $ setUtxo utxo $ initialState params res = do - vtx <- first (P.CardanoLedgerValidationError . show) (constructValidated emulatorGlobals (utxoEnv slotNo) (lsUTxOState $ _memPoolState state) tx) - applyTx state vtx + vtx <- first (P.CardanoLedgerValidationError . show) (constructValidated (emulatorGlobals params) (utxoEnv params slotNo) (lsUTxOState (_memPoolState state)) tx) + applyTx params state vtx -getTxExUnits :: UTxO EmulatorEra -> C.Api.Tx C.Api.AlonzoEra -> Either CardanoLedgerError (Map.Map RdmrPtr ExUnits) -getTxExUnits utxo (C.Api.ShelleyTx _ tx) = - case runIdentity $ C.Ledger.evaluateTransactionExecutionUnits emulatorPParams tx utxo ei ss costmdls of +getTxExUnits :: P.Params -> UTxO EmulatorEra -> C.Api.Tx C.Api.AlonzoEra -> Either CardanoLedgerError (Map.Map RdmrPtr ExUnits) +getTxExUnits params utxo (C.Api.ShelleyTx _ tx) = + case runIdentity $ C.Ledger.evaluateTransactionExecutionUnits (emulatorPParams params) tx utxo ei ss costmdls of Left e -> Left . Left . (P.Phase1,) . P.CardanoLedgerValidationError . show $ e Right rdmrs -> traverse (either toCardanoLedgerError Right) rdmrs where - ss = systemStart emulatorGlobals - ei = epochInfo emulatorGlobals - costmdls = array (minBound, maxBound) . Map.toList $ unCostModels $ getField @"_costmdls" emulatorPParams + eg = emulatorGlobals params + ss = systemStart eg + ei = epochInfo eg + costmdls = array (minBound, maxBound) . Map.toList $ unCostModels $ getField @"_costmdls" $ emulatorPParams params -- Failing transactions throw a checkHasFailedError error, but we don't want to deal with those yet. -- We might be able to do that in the future. - -- But for now just return a huge execution budget so it will run later where we do handle failing transactions. + -- But for now just return a zero execution cost so it will run later where we do handle failing transactions. toCardanoLedgerError (C.Ledger.ValidationFailedV1 (P.CekError _) logs@(_:_)) | last logs == Builtins.fromBuiltin checkHasFailedError = - Right $ ExUnits 10000000 10000000000 + Right $ ExUnits 0 0 toCardanoLedgerError (C.Ledger.ValidationFailedV1 (P.CekError _) logs) = Left $ Left (P.Phase2, P.ScriptFailure (P.EvaluationError logs "CekEvaluationFailure")) toCardanoLedgerError e = Left $ Left (P.Phase2, P.CardanoLedgerValidationError (show e)) makeTransactionBody - :: UTxO EmulatorEra + :: P.Params + -> UTxO EmulatorEra -> C.Api.TxBodyContent C.Api.BuildTx C.Api.AlonzoEra -> Either CardanoLedgerError (C.Api.TxBody C.Api.AlonzoEra) -makeTransactionBody utxo txBodyContent = do +makeTransactionBody params utxo txBodyContent = do txTmp <- first Right $ makeSignedTransaction [] <$> P.makeTransactionBody mempty txBodyContent - exUnits <- getTxExUnits utxo txTmp + exUnits <- getTxExUnits params utxo txTmp first Right $ P.makeTransactionBody exUnits txBodyContent evaluateTransactionFee - :: UTxO EmulatorEra + :: P.Params + -> UTxO EmulatorEra -> [P.PaymentPubKeyHash] -> P.Tx -> Either CardanoLedgerError P.Value -evaluateTransactionFee utxo requiredSigners tx = do - txBodyContent <- first Right $ plutusTxToTxBodyContent requiredSigners tx +evaluateTransactionFee params utxo requiredSigners tx = do + txBodyContent <- first Right $ plutusTxToTxBodyContent params requiredSigners tx let nkeys = C.Api.estimateTransactionKeyWitnessCount txBodyContent - txBody <- makeTransactionBody utxo txBodyContent - case C.Api.evaluateTransactionFee emulatorProtocolParameters txBody nkeys 0 of + txBody <- makeTransactionBody params utxo txBodyContent + case C.Api.evaluateTransactionFee (P.pProtocolParams params) txBody nkeys 0 of C.Api.Lovelace fee -> pure $ P.lovelaceValueOf fee -evaluateMinLovelaceOutput :: TxOut EmulatorEra -> P.Value -evaluateMinLovelaceOutput = toPlutusValue . C.Ledger.evaluateMinLovelaceOutput emulatorPParams - -toPlutusValue :: Coin -> P.Value -toPlutusValue (Coin c) = P.lovelaceValueOf c +evaluateMinLovelaceOutput :: P.Params -> TxOut EmulatorEra -> P.Ada +evaluateMinLovelaceOutput params = toPlutusValue . C.Ledger.evaluateMinLovelaceOutput (emulatorPParams params) + where + toPlutusValue :: Coin -> P.Ada + toPlutusValue (Coin c) = P.lovelaceOf c fromPlutusTx - :: UTxO EmulatorEra + :: P.Params + -> UTxO EmulatorEra -> [P.PaymentPubKeyHash] -> P.Tx -> Either CardanoLedgerError (C.Api.Tx C.Api.AlonzoEra) -fromPlutusTx utxo requiredSigners tx = do - txBodyContent <- first Right $ plutusTxToTxBodyContent requiredSigners tx - makeSignedTransaction [] <$> makeTransactionBody utxo txBodyContent +fromPlutusTx params utxo requiredSigners tx = do + txBodyContent <- first Right $ plutusTxToTxBodyContent params requiredSigners tx + makeSignedTransaction [] <$> makeTransactionBody params utxo txBodyContent plutusTxToTxBodyContent - :: [P.PaymentPubKeyHash] + :: P.Params + -> [P.PaymentPubKeyHash] -> P.Tx -> Either P.ToCardanoError (C.Api.TxBodyContent C.Api.BuildTx C.Api.AlonzoEra) -plutusTxToTxBodyContent requiredSigners = - P.toCardanoTxBodyContent requiredSigners (Just emulatorProtocolParameters) emulatorNetworkId +plutusTxToTxBodyContent params requiredSigners = + P.toCardanoTxBodyContent requiredSigners (Just $ P.pProtocolParams params) (P.pNetworkId params) getRequiredSigners :: C.Api.Tx C.Api.AlonzoEra -> [P.PaymentPubKeyHash] getRequiredSigners (C.Api.ShelleyTx _ (ValidatedTx TxBody { reqSignerHashes = rsq } _ _ _)) = @@ -327,9 +313,9 @@ addSignature privKey (C.Api.ShelleyTx shelleyBasedEra (ValidatedTx body wits isV C.Api.ShelleyKeyWitness _ wit -> Set.singleton wit _ -> Set.empty -fromPlutusIndex :: P.UtxoIndex -> Either CardanoLedgerError (UTxO EmulatorEra) -fromPlutusIndex (P.UtxoIndex m) = first Right $ - UTxO . Map.fromList <$> traverse (bitraverse fromPlutusTxOutRef fromPlutusTxOut) (Map.toList m) +fromPlutusIndex :: P.Params -> P.UtxoIndex -> Either CardanoLedgerError (UTxO EmulatorEra) +fromPlutusIndex params (P.UtxoIndex m) = first Right $ + UTxO . Map.fromList <$> traverse (bitraverse fromPlutusTxOutRef (fromPlutusTxOutUnsafe params)) (Map.toList m) fromPlutusTxOutRef :: P.TxOutRef -> Either P.ToCardanoError (TxIn StandardCrypto) fromPlutusTxOutRef (P.TxOutRef txId i) = TxIn <$> fromPlutusTxId txId <*> pure (mkTxIxPartial i) @@ -337,8 +323,13 @@ fromPlutusTxOutRef (P.TxOutRef txId i) = TxIn <$> fromPlutusTxId txId <*> pure ( fromPlutusTxId :: P.TxId -> Either P.ToCardanoError (TxId StandardCrypto) fromPlutusTxId = fmap toShelleyTxId . P.toCardanoTxId -fromPlutusTxOut :: P.TxOut -> Either P.ToCardanoError (TxOut EmulatorEra) -fromPlutusTxOut = fmap (toShelleyTxOut ShelleyBasedEraAlonzo) . P.toCardanoTxOut emulatorNetworkId P.toCardanoTxOutDatumHash +fromPlutusTxOut :: P.Params -> P.TxOut -> Either P.ToCardanoError (TxOut EmulatorEra) +fromPlutusTxOut params = fmap (toShelleyTxOut ShelleyBasedEraAlonzo) . P.toCardanoTxOut (P.pNetworkId params) P.toCardanoTxOutDatumHash + + +-- | Like 'fromPlutusTxOut', but ignores the check for zeros in txOuts. +fromPlutusTxOutUnsafe :: P.Params -> P.TxOut -> Either P.ToCardanoError (TxOut EmulatorEra) +fromPlutusTxOutUnsafe params = fmap (toShelleyTxOut ShelleyBasedEraAlonzo) . P.toCardanoTxOutUnsafe (P.pNetworkId params) P.toCardanoTxOutDatumHash fromPaymentPrivateKey :: Crypto.XPrv -> TxBody EmulatorEra -> C.Api.KeyWitness C.Api.AlonzoEra fromPaymentPrivateKey xprv txBody @@ -347,68 +338,3 @@ fromPaymentPrivateKey xprv txBody (C.Api.WitnessPaymentExtendedKey (C.Api.PaymentExtendedSigningKey xprv)) where notUsed = undefined -- hack so we can reuse code from cardano-api - --- | Reasonable starting defaults for constructing an 'AlonzoGenesis'. --- --- Copied from cardano-node (https://github.com/input-output-hk/cardano-node/commit/eb0975b7119e07871e3bcca8c6c390d3c796ce06) --- because it was deleted. --- --- TODO: We may need to find an alternative implementation. -alonzoGenesisDefaults :: Alonzo.AlonzoGenesis -alonzoGenesisDefaults = - let cModel = case Plutus.defaultCostModelParams of - Just costModelParams -> - if Alonzo.isCostModelParamsWellFormed costModelParams - then - case Alonzo.mkCostModel Alonzo.PlutusV1 costModelParams of - Left err -> error $ "alonzoGenesisDefaults: " ++ err - Right m -> Alonzo.CostModels $ Map.singleton Alonzo.PlutusV1 m - else error "alonzoGenesisDefaults: defaultCostModel is invalid" - Nothing -> - error "alonzoGenesisDefaults: Could not extract cost model \ - \params from defaultCostModel" - --TODO: we need a better validation story. We also ought to wrap the - -- genesis type in the API properly. - prices' = case C.Api.toAlonzoPrices alonzoGenesisDefaultExecutionPrices of - Nothing -> error "alonzoGenesisDefaults: invalid prices" - Just p -> p - in Alonzo.AlonzoGenesis - { Alonzo.coinsPerUTxOWord = C.Api.toShelleyLovelace $ C.Api.Lovelace 34482 - , Alonzo.costmdls = cModel - , Alonzo.prices = prices' - , Alonzo.maxTxExUnits = C.Api.toAlonzoExUnits alonzoGenesisDefaultMaxTxExecutionUnits - , Alonzo.maxBlockExUnits = C.Api.toAlonzoExUnits alonzoGenesisDefaultMaxBlockExecutionUnits - , Alonzo.maxValSize = alonzoGenesisDefaultMaxValueSize - , Alonzo.collateralPercentage = alonzoGenesisDefaultCollateralPercent - , Alonzo.maxCollateralInputs = alonzoGenesisDefaultMaxCollateralInputs - } - where - alonzoGenesisDefaultExecutionPrices :: ExecutionUnitPrices - alonzoGenesisDefaultExecutionPrices = - ExecutionUnitPrices { - priceExecutionSteps = 1 % 10, - priceExecutionMemory = 1 % 10 - } - - alonzoGenesisDefaultMaxTxExecutionUnits :: ExecutionUnits - alonzoGenesisDefaultMaxTxExecutionUnits = - ExecutionUnits { - executionSteps = 500_000_000_000, - executionMemory = 500_000_000_000 - } - - alonzoGenesisDefaultMaxBlockExecutionUnits :: ExecutionUnits - alonzoGenesisDefaultMaxBlockExecutionUnits = - ExecutionUnits { - executionSteps = 500_000_000_000, - executionMemory = 500_000_000_000 - } - - alonzoGenesisDefaultMaxValueSize :: Natural - alonzoGenesisDefaultMaxValueSize = 4000 - - alonzoGenesisDefaultCollateralPercent :: Natural - alonzoGenesisDefaultCollateralPercent = 1 - - alonzoGenesisDefaultMaxCollateralInputs :: Natural - alonzoGenesisDefaultMaxCollateralInputs = 5 diff --git a/plutus-ledger/test/Spec.hs b/plutus-ledger/test/Spec.hs index e59717a910..0167fe3e01 100644 --- a/plutus-ledger/test/Spec.hs +++ b/plutus-ledger/test/Spec.hs @@ -92,9 +92,10 @@ tests = testGroup "all tests" [ testProperty "slotRange to timeRange inverse property" slotToTimeInverseProp, testProperty "timeRange to slotRange inverse property" timeToSlotInverseProp, testProperty "slot to time range inverse to slot range" - slotToTimeRangeBoundsInverseProp, + slotToTimeRangeBoundsInverseProp, testProperty "slot to time range has lower bound <= upper bound" - slotToTimeRangeHasLowerAndUpperBoundsProp + slotToTimeRangeHasLowerAndUpperBoundsProp, + testProperty "POSIX time to UTC time inverse property" posixTimeToUTCTimeInverseProp ], -- TODO: Reenable once we update `cardano-node` with the following PR merged: -- https://github.com/input-output-hk/cardano-node/pull/3837 @@ -290,6 +291,16 @@ timeToSlotInverseProp = property $ do timeRange (TimeSlot.slotRangeToPOSIXTimeRange sc (TimeSlot.posixTimeRangeToContainedSlotRange sc timeRange)) +-- | Left inverse property between 'posixTimeToUTCTime' and +-- 'utcTimeToPOSIXTime' from a 'POSIXTime'. +posixTimeToUTCTimeInverseProp :: Property +posixTimeToUTCTimeInverseProp = property $ do + sc <- forAll Gen.genSlotConfig + posixTime <- forAll $ Gen.genPOSIXTime sc + let posixTime' = TimeSlot.utcTimeToPOSIXTime (TimeSlot.posixTimeToUTCTime posixTime) + Hedgehog.footnoteShow (posixTime, posixTime') + Hedgehog.assert $ posixTime' == posixTime + -- | 'POSIXTimeRange' from 'Slot' should have lower bound lower or equal than upper bound slotToTimeRangeHasLowerAndUpperBoundsProp :: Property slotToTimeRangeHasLowerAndUpperBoundsProp = property $ do diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index.purs index aec160adfd..c4d25476af 100644 --- a/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index.purs +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index.purs @@ -9,28 +9,18 @@ import Data.Argonaut.Decode (class DecodeJson) import Data.Argonaut.Decode.Aeson ((), (), ()) import Data.Argonaut.Encode (class EncodeJson) import Data.Argonaut.Encode.Aeson ((>$<), (>/\<)) -import Data.Bounded.Generic (genericBottom, genericTop) import Data.Either (Either) -import Data.Enum (class Enum) -import Data.Enum.Generic (genericPred, genericSucc) import Data.Generic.Rep (class Generic) import Data.Lens (Iso', Lens', Prism', iso, prism') import Data.Lens.Iso.Newtype (_Newtype) import Data.Lens.Record (prop) -import Data.Map (Map) import Data.Maybe (Maybe(..)) -import Data.Newtype (class Newtype, unwrap) +import Data.Newtype (unwrap) import Data.RawJson (RawJson) import Data.Show.Generic (genericShow) import Data.Tuple (Tuple) import Data.Tuple.Nested ((/\)) -import Ledger.Crypto (PubKey, Signature) -import Ledger.Slot (Slot) -import Ledger.Tx.Internal (Tx) -import Plutus.V1.Ledger.Crypto (PubKeyHash) -import Plutus.V1.Ledger.Scripts (DatumHash, MintingPolicy, ScriptError, Validator) -import Plutus.V1.Ledger.Tx (RedeemerPtr, TxIn, TxOut, TxOutRef) -import Plutus.V1.Ledger.Value (Value) +import Plutus.V1.Ledger.Scripts (MintingPolicy, ScriptError, Validator) import Type.Proxy (Proxy(Proxy)) import Data.Argonaut.Decode.Aeson as D import Data.Argonaut.Encode.Aeson as E @@ -130,221 +120,3 @@ _ScriptValidationResultOnlyEvent :: Prism' ScriptValidationEvent { sveResult :: _ScriptValidationResultOnlyEvent = prism' ScriptValidationResultOnlyEvent case _ of (ScriptValidationResultOnlyEvent a) -> Just a _ -> Nothing - --------------------------------------------------------------------------------- - -newtype UtxoIndex = UtxoIndex { getIndex :: Map TxOutRef TxOut } - -derive instance Eq UtxoIndex - -instance Show UtxoIndex where - show a = genericShow a - -instance EncodeJson UtxoIndex where - encodeJson = defer \_ -> E.encode $ unwrap >$< - ( E.record - { getIndex: (E.dictionary E.value E.value) :: _ (Map TxOutRef TxOut) } - ) - -instance DecodeJson UtxoIndex where - decodeJson = defer \_ -> D.decode $ (UtxoIndex <$> D.record "UtxoIndex" { getIndex: (D.dictionary D.value D.value) :: _ (Map TxOutRef TxOut) }) - -derive instance Generic UtxoIndex _ - -derive instance Newtype UtxoIndex _ - --------------------------------------------------------------------------------- - -_UtxoIndex :: Iso' UtxoIndex { getIndex :: Map TxOutRef TxOut } -_UtxoIndex = _Newtype - --------------------------------------------------------------------------------- - -data ValidationError - = InOutTypeMismatch TxIn TxOut - | TxOutRefNotFound TxOutRef - | InvalidScriptHash Validator String - | InvalidDatumHash String DatumHash - | MissingRedeemer RedeemerPtr - | InvalidSignature PubKey Signature - | ValueNotPreserved Value Value - | NegativeValue Tx - | ValueContainsLessThanMinAda Tx TxOut - | NonAdaFees Tx - | ScriptFailure ScriptError - | CurrentSlotOutOfRange Slot - | SignatureMissing PubKeyHash - | MintWithoutScript String - | TransactionFeeTooLow Value Value - | CardanoLedgerValidationError String - -derive instance Eq ValidationError - -instance Show ValidationError where - show a = genericShow a - -instance EncodeJson ValidationError where - encodeJson = defer \_ -> case _ of - InOutTypeMismatch a b -> E.encodeTagged "InOutTypeMismatch" (a /\ b) (E.tuple (E.value >/\< E.value)) - TxOutRefNotFound a -> E.encodeTagged "TxOutRefNotFound" a E.value - InvalidScriptHash a b -> E.encodeTagged "InvalidScriptHash" (a /\ b) (E.tuple (E.value >/\< E.value)) - InvalidDatumHash a b -> E.encodeTagged "InvalidDatumHash" (a /\ b) (E.tuple (E.value >/\< E.value)) - MissingRedeemer a -> E.encodeTagged "MissingRedeemer" a E.value - InvalidSignature a b -> E.encodeTagged "InvalidSignature" (a /\ b) (E.tuple (E.value >/\< E.value)) - ValueNotPreserved a b -> E.encodeTagged "ValueNotPreserved" (a /\ b) (E.tuple (E.value >/\< E.value)) - NegativeValue a -> E.encodeTagged "NegativeValue" a E.value - ValueContainsLessThanMinAda a b -> E.encodeTagged "ValueContainsLessThanMinAda" (a /\ b) (E.tuple (E.value >/\< E.value)) - NonAdaFees a -> E.encodeTagged "NonAdaFees" a E.value - ScriptFailure a -> E.encodeTagged "ScriptFailure" a E.value - CurrentSlotOutOfRange a -> E.encodeTagged "CurrentSlotOutOfRange" a E.value - SignatureMissing a -> E.encodeTagged "SignatureMissing" a E.value - MintWithoutScript a -> E.encodeTagged "MintWithoutScript" a E.value - TransactionFeeTooLow a b -> E.encodeTagged "TransactionFeeTooLow" (a /\ b) (E.tuple (E.value >/\< E.value)) - CardanoLedgerValidationError a -> E.encodeTagged "CardanoLedgerValidationError" a E.value - -instance DecodeJson ValidationError where - decodeJson = defer \_ -> D.decode - $ D.sumType "ValidationError" - $ Map.fromFoldable - [ "InOutTypeMismatch" /\ D.content (D.tuple $ InOutTypeMismatch D.value D.value) - , "TxOutRefNotFound" /\ D.content (TxOutRefNotFound <$> D.value) - , "InvalidScriptHash" /\ D.content (D.tuple $ InvalidScriptHash D.value D.value) - , "InvalidDatumHash" /\ D.content (D.tuple $ InvalidDatumHash D.value D.value) - , "MissingRedeemer" /\ D.content (MissingRedeemer <$> D.value) - , "InvalidSignature" /\ D.content (D.tuple $ InvalidSignature D.value D.value) - , "ValueNotPreserved" /\ D.content (D.tuple $ ValueNotPreserved D.value D.value) - , "NegativeValue" /\ D.content (NegativeValue <$> D.value) - , "ValueContainsLessThanMinAda" /\ D.content (D.tuple $ ValueContainsLessThanMinAda D.value D.value) - , "NonAdaFees" /\ D.content (NonAdaFees <$> D.value) - , "ScriptFailure" /\ D.content (ScriptFailure <$> D.value) - , "CurrentSlotOutOfRange" /\ D.content (CurrentSlotOutOfRange <$> D.value) - , "SignatureMissing" /\ D.content (SignatureMissing <$> D.value) - , "MintWithoutScript" /\ D.content (MintWithoutScript <$> D.value) - , "TransactionFeeTooLow" /\ D.content (D.tuple $ TransactionFeeTooLow D.value D.value) - , "CardanoLedgerValidationError" /\ D.content (CardanoLedgerValidationError <$> D.value) - ] - -derive instance Generic ValidationError _ - --------------------------------------------------------------------------------- - -_InOutTypeMismatch :: Prism' ValidationError { a :: TxIn, b :: TxOut } -_InOutTypeMismatch = prism' (\{ a, b } -> (InOutTypeMismatch a b)) case _ of - (InOutTypeMismatch a b) -> Just { a, b } - _ -> Nothing - -_TxOutRefNotFound :: Prism' ValidationError TxOutRef -_TxOutRefNotFound = prism' TxOutRefNotFound case _ of - (TxOutRefNotFound a) -> Just a - _ -> Nothing - -_InvalidScriptHash :: Prism' ValidationError { a :: Validator, b :: String } -_InvalidScriptHash = prism' (\{ a, b } -> (InvalidScriptHash a b)) case _ of - (InvalidScriptHash a b) -> Just { a, b } - _ -> Nothing - -_InvalidDatumHash :: Prism' ValidationError { a :: String, b :: DatumHash } -_InvalidDatumHash = prism' (\{ a, b } -> (InvalidDatumHash a b)) case _ of - (InvalidDatumHash a b) -> Just { a, b } - _ -> Nothing - -_MissingRedeemer :: Prism' ValidationError RedeemerPtr -_MissingRedeemer = prism' MissingRedeemer case _ of - (MissingRedeemer a) -> Just a - _ -> Nothing - -_InvalidSignature :: Prism' ValidationError { a :: PubKey, b :: Signature } -_InvalidSignature = prism' (\{ a, b } -> (InvalidSignature a b)) case _ of - (InvalidSignature a b) -> Just { a, b } - _ -> Nothing - -_ValueNotPreserved :: Prism' ValidationError { a :: Value, b :: Value } -_ValueNotPreserved = prism' (\{ a, b } -> (ValueNotPreserved a b)) case _ of - (ValueNotPreserved a b) -> Just { a, b } - _ -> Nothing - -_NegativeValue :: Prism' ValidationError Tx -_NegativeValue = prism' NegativeValue case _ of - (NegativeValue a) -> Just a - _ -> Nothing - -_ValueContainsLessThanMinAda :: Prism' ValidationError { a :: Tx, b :: TxOut } -_ValueContainsLessThanMinAda = prism' (\{ a, b } -> (ValueContainsLessThanMinAda a b)) case _ of - (ValueContainsLessThanMinAda a b) -> Just { a, b } - _ -> Nothing - -_NonAdaFees :: Prism' ValidationError Tx -_NonAdaFees = prism' NonAdaFees case _ of - (NonAdaFees a) -> Just a - _ -> Nothing - -_ScriptFailure :: Prism' ValidationError ScriptError -_ScriptFailure = prism' ScriptFailure case _ of - (ScriptFailure a) -> Just a - _ -> Nothing - -_CurrentSlotOutOfRange :: Prism' ValidationError Slot -_CurrentSlotOutOfRange = prism' CurrentSlotOutOfRange case _ of - (CurrentSlotOutOfRange a) -> Just a - _ -> Nothing - -_SignatureMissing :: Prism' ValidationError PubKeyHash -_SignatureMissing = prism' SignatureMissing case _ of - (SignatureMissing a) -> Just a - _ -> Nothing - -_MintWithoutScript :: Prism' ValidationError String -_MintWithoutScript = prism' MintWithoutScript case _ of - (MintWithoutScript a) -> Just a - _ -> Nothing - -_TransactionFeeTooLow :: Prism' ValidationError { a :: Value, b :: Value } -_TransactionFeeTooLow = prism' (\{ a, b } -> (TransactionFeeTooLow a b)) case _ of - (TransactionFeeTooLow a b) -> Just { a, b } - _ -> Nothing - -_CardanoLedgerValidationError :: Prism' ValidationError String -_CardanoLedgerValidationError = prism' CardanoLedgerValidationError case _ of - (CardanoLedgerValidationError a) -> Just a - _ -> Nothing - --------------------------------------------------------------------------------- - -data ValidationPhase - = Phase1 - | Phase2 - -derive instance Eq ValidationPhase - -derive instance Ord ValidationPhase - -instance Show ValidationPhase where - show a = genericShow a - -instance EncodeJson ValidationPhase where - encodeJson = defer \_ -> E.encode E.enum - -instance DecodeJson ValidationPhase where - decodeJson = defer \_ -> D.decode D.enum - -derive instance Generic ValidationPhase _ - -instance Enum ValidationPhase where - succ = genericSucc - pred = genericPred - -instance Bounded ValidationPhase where - bottom = genericBottom - top = genericTop - --------------------------------------------------------------------------------- - -_Phase1 :: Prism' ValidationPhase Unit -_Phase1 = prism' (const Phase1) case _ of - Phase1 -> Just unit - _ -> Nothing - -_Phase2 :: Prism' ValidationPhase Unit -_Phase2 = prism' (const Phase2) case _ of - Phase2 -> Just unit - _ -> Nothing diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index/Internal.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index/Internal.purs new file mode 100644 index 0000000000..4066e91cbe --- /dev/null +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Ledger/Index/Internal.purs @@ -0,0 +1,250 @@ +-- File auto generated by purescript-bridge! -- +module Ledger.Index.Internal where + +import Prelude + +import Control.Lazy (defer) +import Data.Argonaut (encodeJson, jsonNull) +import Data.Argonaut.Decode (class DecodeJson) +import Data.Argonaut.Decode.Aeson ((), (), ()) +import Data.Argonaut.Encode (class EncodeJson) +import Data.Argonaut.Encode.Aeson ((>$<), (>/\<)) +import Data.Bounded.Generic (genericBottom, genericTop) +import Data.Enum (class Enum) +import Data.Enum.Generic (genericPred, genericSucc) +import Data.Generic.Rep (class Generic) +import Data.Lens (Iso', Lens', Prism', iso, prism') +import Data.Lens.Iso.Newtype (_Newtype) +import Data.Lens.Record (prop) +import Data.Map (Map) +import Data.Maybe (Maybe(..)) +import Data.Newtype (class Newtype, unwrap) +import Data.Show.Generic (genericShow) +import Data.Tuple.Nested ((/\)) +import Ledger.Crypto (PubKey, Signature) +import Ledger.Slot (Slot) +import Ledger.Tx.Internal (Tx) +import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Plutus.V1.Ledger.Scripts (DatumHash, ScriptError, Validator) +import Plutus.V1.Ledger.Tx (RedeemerPtr, TxIn, TxOut, TxOutRef) +import Plutus.V1.Ledger.Value (Value) +import Type.Proxy (Proxy(Proxy)) +import Data.Argonaut.Decode.Aeson as D +import Data.Argonaut.Encode.Aeson as E +import Data.Map as Map + +newtype UtxoIndex = UtxoIndex { getIndex :: Map TxOutRef TxOut } + +derive instance Eq UtxoIndex + +instance Show UtxoIndex where + show a = genericShow a + +instance EncodeJson UtxoIndex where + encodeJson = defer \_ -> E.encode $ unwrap >$< + ( E.record + { getIndex: (E.dictionary E.value E.value) :: _ (Map TxOutRef TxOut) } + ) + +instance DecodeJson UtxoIndex where + decodeJson = defer \_ -> D.decode $ (UtxoIndex <$> D.record "UtxoIndex" { getIndex: (D.dictionary D.value D.value) :: _ (Map TxOutRef TxOut) }) + +derive instance Generic UtxoIndex _ + +derive instance Newtype UtxoIndex _ + +-------------------------------------------------------------------------------- + +_UtxoIndex :: Iso' UtxoIndex { getIndex :: Map TxOutRef TxOut } +_UtxoIndex = _Newtype + +-------------------------------------------------------------------------------- + +data ValidationError + = InOutTypeMismatch TxIn TxOut + | TxOutRefNotFound TxOutRef + | InvalidScriptHash Validator String + | InvalidDatumHash String DatumHash + | MissingRedeemer RedeemerPtr + | InvalidSignature PubKey Signature + | ValueNotPreserved Value Value + | NegativeValue Tx + | ValueContainsLessThanMinAda Tx TxOut Value + | NonAdaFees Tx + | ScriptFailure ScriptError + | CurrentSlotOutOfRange Slot + | SignatureMissing PubKeyHash + | MintWithoutScript String + | TransactionFeeTooLow Value Value + | CardanoLedgerValidationError String + +derive instance Eq ValidationError + +instance Show ValidationError where + show a = genericShow a + +instance EncodeJson ValidationError where + encodeJson = defer \_ -> case _ of + InOutTypeMismatch a b -> E.encodeTagged "InOutTypeMismatch" (a /\ b) (E.tuple (E.value >/\< E.value)) + TxOutRefNotFound a -> E.encodeTagged "TxOutRefNotFound" a E.value + InvalidScriptHash a b -> E.encodeTagged "InvalidScriptHash" (a /\ b) (E.tuple (E.value >/\< E.value)) + InvalidDatumHash a b -> E.encodeTagged "InvalidDatumHash" (a /\ b) (E.tuple (E.value >/\< E.value)) + MissingRedeemer a -> E.encodeTagged "MissingRedeemer" a E.value + InvalidSignature a b -> E.encodeTagged "InvalidSignature" (a /\ b) (E.tuple (E.value >/\< E.value)) + ValueNotPreserved a b -> E.encodeTagged "ValueNotPreserved" (a /\ b) (E.tuple (E.value >/\< E.value)) + NegativeValue a -> E.encodeTagged "NegativeValue" a E.value + ValueContainsLessThanMinAda a b c -> E.encodeTagged "ValueContainsLessThanMinAda" (a /\ b /\ c) (E.tuple (E.value >/\< E.value >/\< E.value)) + NonAdaFees a -> E.encodeTagged "NonAdaFees" a E.value + ScriptFailure a -> E.encodeTagged "ScriptFailure" a E.value + CurrentSlotOutOfRange a -> E.encodeTagged "CurrentSlotOutOfRange" a E.value + SignatureMissing a -> E.encodeTagged "SignatureMissing" a E.value + MintWithoutScript a -> E.encodeTagged "MintWithoutScript" a E.value + TransactionFeeTooLow a b -> E.encodeTagged "TransactionFeeTooLow" (a /\ b) (E.tuple (E.value >/\< E.value)) + CardanoLedgerValidationError a -> E.encodeTagged "CardanoLedgerValidationError" a E.value + +instance DecodeJson ValidationError where + decodeJson = defer \_ -> D.decode + $ D.sumType "ValidationError" + $ Map.fromFoldable + [ "InOutTypeMismatch" /\ D.content (D.tuple $ InOutTypeMismatch D.value D.value) + , "TxOutRefNotFound" /\ D.content (TxOutRefNotFound <$> D.value) + , "InvalidScriptHash" /\ D.content (D.tuple $ InvalidScriptHash D.value D.value) + , "InvalidDatumHash" /\ D.content (D.tuple $ InvalidDatumHash D.value D.value) + , "MissingRedeemer" /\ D.content (MissingRedeemer <$> D.value) + , "InvalidSignature" /\ D.content (D.tuple $ InvalidSignature D.value D.value) + , "ValueNotPreserved" /\ D.content (D.tuple $ ValueNotPreserved D.value D.value) + , "NegativeValue" /\ D.content (NegativeValue <$> D.value) + , "ValueContainsLessThanMinAda" /\ D.content (D.tuple $ ValueContainsLessThanMinAda D.value D.value D.value) + , "NonAdaFees" /\ D.content (NonAdaFees <$> D.value) + , "ScriptFailure" /\ D.content (ScriptFailure <$> D.value) + , "CurrentSlotOutOfRange" /\ D.content (CurrentSlotOutOfRange <$> D.value) + , "SignatureMissing" /\ D.content (SignatureMissing <$> D.value) + , "MintWithoutScript" /\ D.content (MintWithoutScript <$> D.value) + , "TransactionFeeTooLow" /\ D.content (D.tuple $ TransactionFeeTooLow D.value D.value) + , "CardanoLedgerValidationError" /\ D.content (CardanoLedgerValidationError <$> D.value) + ] + +derive instance Generic ValidationError _ + +-------------------------------------------------------------------------------- + +_InOutTypeMismatch :: Prism' ValidationError { a :: TxIn, b :: TxOut } +_InOutTypeMismatch = prism' (\{ a, b } -> (InOutTypeMismatch a b)) case _ of + (InOutTypeMismatch a b) -> Just { a, b } + _ -> Nothing + +_TxOutRefNotFound :: Prism' ValidationError TxOutRef +_TxOutRefNotFound = prism' TxOutRefNotFound case _ of + (TxOutRefNotFound a) -> Just a + _ -> Nothing + +_InvalidScriptHash :: Prism' ValidationError { a :: Validator, b :: String } +_InvalidScriptHash = prism' (\{ a, b } -> (InvalidScriptHash a b)) case _ of + (InvalidScriptHash a b) -> Just { a, b } + _ -> Nothing + +_InvalidDatumHash :: Prism' ValidationError { a :: String, b :: DatumHash } +_InvalidDatumHash = prism' (\{ a, b } -> (InvalidDatumHash a b)) case _ of + (InvalidDatumHash a b) -> Just { a, b } + _ -> Nothing + +_MissingRedeemer :: Prism' ValidationError RedeemerPtr +_MissingRedeemer = prism' MissingRedeemer case _ of + (MissingRedeemer a) -> Just a + _ -> Nothing + +_InvalidSignature :: Prism' ValidationError { a :: PubKey, b :: Signature } +_InvalidSignature = prism' (\{ a, b } -> (InvalidSignature a b)) case _ of + (InvalidSignature a b) -> Just { a, b } + _ -> Nothing + +_ValueNotPreserved :: Prism' ValidationError { a :: Value, b :: Value } +_ValueNotPreserved = prism' (\{ a, b } -> (ValueNotPreserved a b)) case _ of + (ValueNotPreserved a b) -> Just { a, b } + _ -> Nothing + +_NegativeValue :: Prism' ValidationError Tx +_NegativeValue = prism' NegativeValue case _ of + (NegativeValue a) -> Just a + _ -> Nothing + +_ValueContainsLessThanMinAda :: Prism' ValidationError { a :: Tx, b :: TxOut, c :: Value } +_ValueContainsLessThanMinAda = prism' (\{ a, b, c } -> (ValueContainsLessThanMinAda a b c)) case _ of + (ValueContainsLessThanMinAda a b c) -> Just { a, b, c } + _ -> Nothing + +_NonAdaFees :: Prism' ValidationError Tx +_NonAdaFees = prism' NonAdaFees case _ of + (NonAdaFees a) -> Just a + _ -> Nothing + +_ScriptFailure :: Prism' ValidationError ScriptError +_ScriptFailure = prism' ScriptFailure case _ of + (ScriptFailure a) -> Just a + _ -> Nothing + +_CurrentSlotOutOfRange :: Prism' ValidationError Slot +_CurrentSlotOutOfRange = prism' CurrentSlotOutOfRange case _ of + (CurrentSlotOutOfRange a) -> Just a + _ -> Nothing + +_SignatureMissing :: Prism' ValidationError PubKeyHash +_SignatureMissing = prism' SignatureMissing case _ of + (SignatureMissing a) -> Just a + _ -> Nothing + +_MintWithoutScript :: Prism' ValidationError String +_MintWithoutScript = prism' MintWithoutScript case _ of + (MintWithoutScript a) -> Just a + _ -> Nothing + +_TransactionFeeTooLow :: Prism' ValidationError { a :: Value, b :: Value } +_TransactionFeeTooLow = prism' (\{ a, b } -> (TransactionFeeTooLow a b)) case _ of + (TransactionFeeTooLow a b) -> Just { a, b } + _ -> Nothing + +_CardanoLedgerValidationError :: Prism' ValidationError String +_CardanoLedgerValidationError = prism' CardanoLedgerValidationError case _ of + (CardanoLedgerValidationError a) -> Just a + _ -> Nothing + +-------------------------------------------------------------------------------- + +data ValidationPhase + = Phase1 + | Phase2 + +derive instance Eq ValidationPhase + +derive instance Ord ValidationPhase + +instance Show ValidationPhase where + show a = genericShow a + +instance EncodeJson ValidationPhase where + encodeJson = defer \_ -> E.encode E.enum + +instance DecodeJson ValidationPhase where + decodeJson = defer \_ -> D.decode D.enum + +derive instance Generic ValidationPhase _ + +instance Enum ValidationPhase where + succ = genericSucc + pred = genericPred + +instance Bounded ValidationPhase where + bottom = genericBottom + top = genericTop + +-------------------------------------------------------------------------------- + +_Phase1 :: Prism' ValidationPhase Unit +_Phase1 = prism' (const Phase1) case _ of + Phase1 -> Just unit + _ -> Nothing + +_Phase2 :: Prism' ValidationPhase Unit +_Phase2 = prism' (const Phase2) case _ of + Phase2 -> Just unit + _ -> Nothing diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Effects.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Effects.purs index b9ed90199b..a387be1fa9 100644 --- a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Effects.purs +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Effects.purs @@ -26,6 +26,7 @@ import Ledger.Constraints.OffChain (UnbalancedTx) import Ledger.Slot (Slot) import Ledger.TimeSlot (SlotConversionError) import Ledger.Tx (CardanoTx, ChainIndexTxOut) +import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.ChainIndex.Api (IsUtxoResponse, TxosResponse, UtxosResponse) import Plutus.ChainIndex.Tx (ChainIndexTx) import Plutus.ChainIndex.Types (RollbackState, Tip, TxOutState) @@ -387,7 +388,8 @@ _GetTipResponse = prism' GetTipResponse case _ of -------------------------------------------------------------------------------- data PABReq - = AwaitSlotReq Slot + = AdjustUnbalancedTxReq UnbalancedTx + | AwaitSlotReq Slot | AwaitTimeReq POSIXTime | AwaitUtxoSpentReq TxOutRef | AwaitUtxoProducedReq Address @@ -411,6 +413,7 @@ instance Show PABReq where instance EncodeJson PABReq where encodeJson = defer \_ -> case _ of + AdjustUnbalancedTxReq a -> E.encodeTagged "AdjustUnbalancedTxReq" a E.value AwaitSlotReq a -> E.encodeTagged "AwaitSlotReq" a E.value AwaitTimeReq a -> E.encodeTagged "AwaitTimeReq" a E.value AwaitUtxoSpentReq a -> E.encodeTagged "AwaitUtxoSpentReq" a E.value @@ -432,7 +435,8 @@ instance DecodeJson PABReq where decodeJson = defer \_ -> D.decode $ D.sumType "PABReq" $ Map.fromFoldable - [ "AwaitSlotReq" /\ D.content (AwaitSlotReq <$> D.value) + [ "AdjustUnbalancedTxReq" /\ D.content (AdjustUnbalancedTxReq <$> D.value) + , "AwaitSlotReq" /\ D.content (AwaitSlotReq <$> D.value) , "AwaitTimeReq" /\ D.content (AwaitTimeReq <$> D.value) , "AwaitUtxoSpentReq" /\ D.content (AwaitUtxoSpentReq <$> D.value) , "AwaitUtxoProducedReq" /\ D.content (AwaitUtxoProducedReq <$> D.value) @@ -454,6 +458,11 @@ derive instance Generic PABReq _ -------------------------------------------------------------------------------- +_AdjustUnbalancedTxReq :: Prism' PABReq UnbalancedTx +_AdjustUnbalancedTxReq = prism' AdjustUnbalancedTxReq case _ of + (AdjustUnbalancedTxReq a) -> Just a + _ -> Nothing + _AwaitSlotReq :: Prism' PABReq Slot _AwaitSlotReq = prism' AwaitSlotReq case _ of (AwaitSlotReq a) -> Just a @@ -537,7 +546,8 @@ _YieldUnbalancedTxReq = prism' YieldUnbalancedTxReq case _ of -------------------------------------------------------------------------------- data PABResp - = AwaitSlotResp Slot + = AdjustUnbalancedTxResp (Either ToCardanoError UnbalancedTx) + | AwaitSlotResp Slot | AwaitTimeResp POSIXTime | AwaitUtxoSpentResp ChainIndexTx | AwaitUtxoProducedResp (NonEmptyList ChainIndexTx) @@ -561,6 +571,7 @@ instance Show PABResp where instance EncodeJson PABResp where encodeJson = defer \_ -> case _ of + AdjustUnbalancedTxResp a -> E.encodeTagged "AdjustUnbalancedTxResp" a (E.either E.value E.value) AwaitSlotResp a -> E.encodeTagged "AwaitSlotResp" a E.value AwaitTimeResp a -> E.encodeTagged "AwaitTimeResp" a E.value AwaitUtxoSpentResp a -> E.encodeTagged "AwaitUtxoSpentResp" a E.value @@ -582,7 +593,8 @@ instance DecodeJson PABResp where decodeJson = defer \_ -> D.decode $ D.sumType "PABResp" $ Map.fromFoldable - [ "AwaitSlotResp" /\ D.content (AwaitSlotResp <$> D.value) + [ "AdjustUnbalancedTxResp" /\ D.content (AdjustUnbalancedTxResp <$> (D.either D.value D.value)) + , "AwaitSlotResp" /\ D.content (AwaitSlotResp <$> D.value) , "AwaitTimeResp" /\ D.content (AwaitTimeResp <$> D.value) , "AwaitUtxoSpentResp" /\ D.content (AwaitUtxoSpentResp <$> D.value) , "AwaitUtxoProducedResp" /\ D.content (AwaitUtxoProducedResp <$> D.value) @@ -604,6 +616,11 @@ derive instance Generic PABResp _ -------------------------------------------------------------------------------- +_AdjustUnbalancedTxResp :: Prism' PABResp (Either ToCardanoError UnbalancedTx) +_AdjustUnbalancedTxResp = prism' AdjustUnbalancedTxResp case _ of + (AdjustUnbalancedTxResp a) -> Just a + _ -> Nothing + _AwaitSlotResp :: Prism' PABResp Slot _AwaitSlotResp = prism' AwaitSlotResp case _ of (AwaitSlotResp a) -> Just a diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Error.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Error.purs index f62a5a04c8..841f1f8e45 100644 --- a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Error.purs +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/Contract/Error.purs @@ -19,6 +19,7 @@ import Data.RawJson (RawJson) import Data.Show.Generic (genericShow) import Data.Tuple.Nested ((/\)) import Ledger.Constraints.OffChain (MkTxError) +import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.Contract.Checkpoint (CheckpointError) import Plutus.Contract.Effects (ChainIndexResponse) import Type.Proxy (Proxy(Proxy)) @@ -60,6 +61,7 @@ data ContractError | ChainIndexContractError String ChainIndexResponse | EmulatorAssertionContractError AssertionError | ConstraintResolutionContractError MkTxError + | TxToCardanoConvertContractError ToCardanoError | ResumableContractError MatchingError | CCheckpointContractError CheckpointError | EndpointDecodeContractError @@ -80,6 +82,7 @@ instance EncodeJson ContractError where ChainIndexContractError a b -> E.encodeTagged "ChainIndexContractError" (a /\ b) (E.tuple (E.value >/\< E.value)) EmulatorAssertionContractError a -> E.encodeTagged "EmulatorAssertionContractError" a E.value ConstraintResolutionContractError a -> E.encodeTagged "ConstraintResolutionContractError" a E.value + TxToCardanoConvertContractError a -> E.encodeTagged "TxToCardanoConvertContractError" a E.value ResumableContractError a -> E.encodeTagged "ResumableContractError" a E.value CCheckpointContractError a -> E.encodeTagged "CCheckpointContractError" a E.value EndpointDecodeContractError { eeEndpointDescription, eeEndpointValue, eeErrorMessage } -> encodeJson @@ -98,6 +101,7 @@ instance DecodeJson ContractError where , "ChainIndexContractError" /\ D.content (D.tuple $ ChainIndexContractError D.value D.value) , "EmulatorAssertionContractError" /\ D.content (EmulatorAssertionContractError <$> D.value) , "ConstraintResolutionContractError" /\ D.content (ConstraintResolutionContractError <$> D.value) + , "TxToCardanoConvertContractError" /\ D.content (TxToCardanoConvertContractError <$> D.value) , "ResumableContractError" /\ D.content (ResumableContractError <$> D.value) , "CCheckpointContractError" /\ D.content (CCheckpointContractError <$> D.value) , "EndpointDecodeContractError" /\ @@ -134,6 +138,11 @@ _ConstraintResolutionContractError = prism' ConstraintResolutionContractError ca (ConstraintResolutionContractError a) -> Just a _ -> Nothing +_TxToCardanoConvertContractError :: Prism' ContractError ToCardanoError +_TxToCardanoConvertContractError = prism' TxToCardanoConvertContractError case _ of + (TxToCardanoConvertContractError a) -> Just a + _ -> Nothing + _ResumableContractError :: Prism' ContractError MatchingError _ResumableContractError = prism' ResumableContractError case _ of (ResumableContractError a) -> Just a diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/PAB/Webserver/Types.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/PAB/Webserver/Types.purs index 1ad90dc4da..76d0971d7f 100644 --- a/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/PAB/Webserver/Types.purs +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Plutus/PAB/Webserver/Types.purs @@ -21,7 +21,7 @@ import Data.RawJson (RawJson) import Data.Show.Generic (genericShow) import Data.Tuple (Tuple) import Data.Tuple.Nested ((/\)) -import Ledger.Index (UtxoIndex) +import Ledger.Index.Internal (UtxoIndex) import Ledger.Slot (Slot) import Ledger.Tx.Internal (Tx) import Playground.Types (FunctionSchema) diff --git a/plutus-pab-executables/demo/pab-nami/client/generated/Wallet/Emulator/Error.purs b/plutus-pab-executables/demo/pab-nami/client/generated/Wallet/Emulator/Error.purs index 822e5e168a..c627e87f10 100644 --- a/plutus-pab-executables/demo/pab-nami/client/generated/Wallet/Emulator/Error.purs +++ b/plutus-pab-executables/demo/pab-nami/client/generated/Wallet/Emulator/Error.purs @@ -20,7 +20,7 @@ import Data.Tuple.Nested ((/\)) import Ledger.Ada (Ada) import Ledger.Address (PaymentPubKeyHash) import Ledger.Constraints.OffChain (MkTxError) -import Ledger.Index (ValidationError) +import Ledger.Index.Internal (ValidationError) import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.V1.Ledger.Value (Value) import Type.Proxy (Proxy(Proxy)) diff --git a/plutus-pab-executables/demo/pab-nami/pab/app/DemoContract.hs b/plutus-pab-executables/demo/pab-nami/pab/app/DemoContract.hs index 2c73c0c002..8c5c7c99d8 100644 --- a/plutus-pab-executables/demo/pab-nami/pab/app/DemoContract.hs +++ b/plutus-pab-executables/demo/pab-nami/pab/app/DemoContract.hs @@ -21,9 +21,10 @@ import Data.Void (Void) import GHC.Generics (Generic) import Language.PureScript.Bridge (argonaut, equal, genericShow, mkSumType, order) import Ledger (PaymentPubKeyHash, StakePubKeyHash, Value) -import Ledger.Constraints (adjustUnbalancedTx, mustPayToPubKeyAddress) +import Ledger.Constraints (mustPayToPubKeyAddress) import Playground.Types (FunctionSchema) -import Plutus.Contract (ContractError, Endpoint, Promise, endpoint, logInfo, mkTxConstraints, yieldUnbalancedTx) +import Plutus.Contract (ContractError, Endpoint, Promise, adjustUnbalancedTx, endpoint, logInfo, mkTxConstraints, + yieldUnbalancedTx) import Plutus.PAB.Effects.Contract.Builtin (HasDefinitions, SomeBuiltin (SomeBuiltin)) import Plutus.PAB.Effects.Contract.Builtin qualified as Builtin import Plutus.PAB.Run.PSGenerator (HasPSTypes (..)) @@ -72,5 +73,5 @@ payToWallet :: Promise () PayToWalletSchema ContractError () payToWallet = endpoint @"PayToWallet" $ \PayToWalletParams{amount, pkh, skh} -> do utx <- mkTxConstraints @Void mempty (mustPayToPubKeyAddress pkh skh amount) logInfo @String $ show utx - yieldUnbalancedTx $ adjustUnbalancedTx utx + adjustUnbalancedTx utx >>= yieldUnbalancedTx diff --git a/plutus-pab-executables/examples/ContractExample/PayToWallet.hs b/plutus-pab-executables/examples/ContractExample/PayToWallet.hs index fc6427f092..c4f2f7bbfa 100644 --- a/plutus-pab-executables/examples/ContractExample/PayToWallet.hs +++ b/plutus-pab-executables/examples/ContractExample/PayToWallet.hs @@ -19,8 +19,9 @@ import GHC.Generics (Generic) import Schema (ToSchema) import Ledger (PaymentPubKeyHash, Value) -import Ledger.Constraints (adjustUnbalancedTx, mustPayToPubKey) -import Plutus.Contract (ContractError, Endpoint, Promise, endpoint, logInfo, mkTxConstraints, yieldUnbalancedTx) +import Ledger.Constraints (mustPayToPubKey) +import Plutus.Contract (ContractError, Endpoint, Promise, adjustUnbalancedTx, endpoint, logInfo, mkTxConstraints, + yieldUnbalancedTx) data PayToWalletParams = PayToWalletParams @@ -37,5 +38,5 @@ payToWallet = endpoint @"PayToWallet" $ \PayToWalletParams{amount, pkh} -> do logInfo @String "Calling PayToWallet endpoint" utx <- mkTxConstraints @Void mempty (mustPayToPubKey pkh amount) logInfo @String $ "Yielding the unbalanced transaction " <> show utx - yieldUnbalancedTx $ adjustUnbalancedTx utx + adjustUnbalancedTx utx >>= yieldUnbalancedTx diff --git a/plutus-pab-executables/plutus-pab-executables.cabal b/plutus-pab-executables/plutus-pab-executables.cabal index ab2077d9f0..6ea6c8ac71 100644 --- a/plutus-pab-executables/plutus-pab-executables.cabal +++ b/plutus-pab-executables/plutus-pab-executables.cabal @@ -372,6 +372,7 @@ executable tx-inject default-language: Haskell2010 build-depends: base >=4.9 && <5, + data-default -any, plutus-pab, containers -any, clock -any, diff --git a/plutus-pab-executables/test/full/Plutus/PAB/Simulator/Test.hs b/plutus-pab-executables/test/full/Plutus/PAB/Simulator/Test.hs index 3a6ded283b..21167c5a03 100644 --- a/plutus-pab-executables/test/full/Plutus/PAB/Simulator/Test.hs +++ b/plutus-pab-executables/test/full/Plutus/PAB/Simulator/Test.hs @@ -9,6 +9,7 @@ module Plutus.PAB.Simulator.Test(runSimulation) where import Control.Monad.Freer (interpret) import Data.Default (Default (def)) +import Ledger.Params (Params (..), allowBigTransactions) import Ledger.TimeSlot (SlotConfig (..)) import Plutus.PAB.Core (EffectHandlers) import Plutus.PAB.Effects.Contract.Builtin (Builtin, BuiltinHandler (contractHandler), handleBuiltin) @@ -24,6 +25,6 @@ runSimulation = runSimulationWith simulatorHandlers -- | 'EffectHandlers' for running the PAB as a simulator (no connectivity to -- out-of-process services such as wallet backend, node, etc.) simulatorHandlers :: EffectHandlers (Builtin TestContracts) (SimulatorState (Builtin TestContracts)) -simulatorHandlers = mkSimulatorHandlers def { scSlotLength = 1 } handler where +simulatorHandlers = mkSimulatorHandlers (allowBigTransactions $ def { pSlotConfig = def { scSlotLength = 1 } }) handler where handler :: SimulatorContractHandler (Builtin TestContracts) handler = interpret (contractHandler handleBuiltin) diff --git a/plutus-pab-executables/tx-inject/TxInject/RandomTx.hs b/plutus-pab-executables/tx-inject/TxInject/RandomTx.hs index f33617c54d..bc361b45ec 100644 --- a/plutus-pab-executables/tx-inject/TxInject/RandomTx.hs +++ b/plutus-pab-executables/tx-inject/TxInject/RandomTx.hs @@ -10,6 +10,7 @@ module TxInject.RandomTx( ) where import Control.Monad.Primitive (PrimMonad, PrimState) +import Data.Default (def) import Data.List.NonEmpty (NonEmpty (..)) import Data.Map qualified as Map import Data.Maybe (fromMaybe) @@ -23,6 +24,7 @@ import Ledger.Address qualified as Address import Ledger.CardanoWallet qualified as CW import Ledger.Generators qualified as Generators import Ledger.Index (UtxoIndex (..), ValidationCtx (..), runValidation, validateTransaction) +import Ledger.Params (Params (pSlotConfig)) import Ledger.Slot (Slot (..)) import Ledger.Tx (Tx, TxOut (..)) import Ledger.Tx qualified as Tx @@ -88,7 +90,7 @@ generateTx gen slot (UtxoIndex utxo) = do Generators.genValidTransactionSpending sourceTxIns sourceAda slotCfg <- Gen.sample Generators.genSlotConfig let ((validationResult, _), _) = - runValidation (validateTransaction slot tx) (ValidationCtx (UtxoIndex utxo) slotCfg) + runValidation (validateTransaction slot tx) (ValidationCtx (UtxoIndex utxo) (def { pSlotConfig = slotCfg })) case validationResult of Nothing -> pure tx Just _ -> generateTx gen slot (UtxoIndex utxo) diff --git a/plutus-pab/plutus-pab.cabal b/plutus-pab/plutus-pab.cabal index 44773e8d60..f57eb3ce30 100644 --- a/plutus-pab/plutus-pab.cabal +++ b/plutus-pab/plutus-pab.cabal @@ -54,7 +54,6 @@ library ghc-options: -Wunused-packages exposed-modules: Cardano.Api.NetworkId.Extra - Cardano.Api.ProtocolParameters Cardano.BM.Data.Tracer.Extras Cardano.Chain Cardano.ChainIndex.ChainIndex @@ -63,6 +62,7 @@ library Cardano.Node.API Cardano.Node.Client Cardano.Node.Mock + Cardano.Node.Params Cardano.Node.Server Cardano.Node.Types Cardano.Protocol.Socket.Mock.Client @@ -147,6 +147,7 @@ library cardano-wallet-core -any, cardano-wallet-core-integration -any, cardano-wallet-launcher -any, + cardano-ledger-core -any, containers -any, contra-tracer -any, cryptonite -any, diff --git a/plutus-pab/src/Cardano/Api/ProtocolParameters.hs b/plutus-pab/src/Cardano/Api/ProtocolParameters.hs deleted file mode 100644 index ed0fd7ce88..0000000000 --- a/plutus-pab/src/Cardano/Api/ProtocolParameters.hs +++ /dev/null @@ -1,128 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -Wno-orphans #-} -module Cardano.Api.ProtocolParameters (ProtocolParameters) where - -import Cardano.Api.Shelley -import Data.Default (Default, def) -import Data.Map (fromList) -import Data.Ratio ((%)) - -instance Default ProtocolParameters where - def = ProtocolParameters - { protocolParamProtocolVersion = (5,0) - , protocolParamDecentralization = Just $ 3 % 5 - , protocolParamExtraPraosEntropy = Nothing - , protocolParamMaxBlockHeaderSize = 1100 - , protocolParamMaxBlockBodySize = 65536 - , protocolParamMaxTxSize = 16384 - , protocolParamTxFeeFixed = 155381 - , protocolParamTxFeePerByte = 44 - , protocolParamMinUTxOValue = Nothing - , protocolParamStakeAddressDeposit = Lovelace 2000000 - , protocolParamStakePoolDeposit = Lovelace 500000000 - , protocolParamMinPoolCost = Lovelace 340000000 - , protocolParamPoolRetireMaxEpoch = EpochNo 18 - , protocolParamStakePoolTargetNum = 150 - , protocolParamPoolPledgeInfluence = 3 % 10 - , protocolParamMonetaryExpansion = 3 % 1000 - , protocolParamTreasuryCut = 1 % 5 - , protocolParamUTxOCostPerWord = Just (Lovelace 1) - , protocolParamCostModels = fromList - [ (AnyPlutusScriptVersion PlutusScriptV1, CostModel (fromList - [("add_integer-cpu-arguments-intercept",235735) - ,("add_integer-cpu-arguments-slope",1722) - ,("add_integer-memory-arguments-intercept",1) - ,("add_integer-memory-arguments-slope",1) - ,("cek_apply_cost-_ex_budget_cpu",39000) - ,("cek_apply_cost-_ex_budget_memory",10) - ,("cek_builtin_cost-_ex_budget_cpu",39000) - ,("cek_builtin_cost-_ex_budget_memory",10) - ,("cek_const_cost-_ex_budget_cpu",39000) - ,("cek_const_cost-_ex_budget_memory",10) - ,("cek_delay_cost-_ex_budget_cpu",39000) - ,("cek_delay_cost-_ex_budget_memory",10) - ,("cek_force_cost-_ex_budget_cpu",39000) - ,("cek_force_cost-_ex_budget_memory",10) - ,("cek_lam_cost-_ex_budget_cpu",39000) - ,("cek_lam_cost-_ex_budget_memory",10) - ,("cek_startup_cost-_ex_budget_cpu",1000000) - ,("cek_startup_cost-_ex_budget_memory",0) - ,("cek_var_cost-_ex_budget_cpu",39000) - ,("cek_var_cost-_ex_budget_memory",10) - ,("concatenate-cpu-arguments-intercept",420084) - ,("concatenate-cpu-arguments-slope",515) - ,("concatenate-memory-arguments-intercept",0) - ,("concatenate-memory-arguments-slope",1) - ,("divide_integer-cpu-arguments-model_split_const_intercept",330895) - ,("divide_integer-cpu-arguments-model_split_const_slope",427) - ,("divide_integer-memory-arguments-intercept",0) - ,("divide_integer-memory-arguments-minimum",1) - ,("divide_integer-memory-arguments-slope",1) - ,("drop_byte_string-cpu-arguments",3418326) - ,("drop_byte_string-memory-arguments",2) - ,("eq_byte_string-cpu-arguments-intercept",188562) - ,("eq_byte_string-cpu-arguments-slope",246) - ,("eq_byte_string-memory-arguments",1) - ,("eq_integer-cpu-arguments-intercept",211716) - ,("eq_integer-cpu-arguments-slope",867) - ,("eq_integer-memory-arguments",1) - ,("greater_than_eq_integer-cpu-arguments-intercept",216015) - ,("greater_than_eq_integer-cpu-arguments-slope",699) - ,("greater_than_eq_integer-memory-arguments",1) - ,("greater_than_integer-cpu-arguments-intercept",187278) - ,("greater_than_integer-cpu-arguments-slope",980) - ,("greater_than_integer-memory-arguments",1) - ,("gt_byte_string-cpu-arguments-intercept",204537) - ,("gt_byte_string-cpu-arguments-slope",227) - ,("gt_byte_string-memory-arguments",1) - ,("if_then_else-cpu-arguments",0) - ,("if_then_else-memory-arguments",0) - ,("less_than_eq_integer-cpu-arguments-intercept",216015) - ,("less_than_eq_integer-cpu-arguments-slope",699) - ,("less_than_eq_integer-memory-arguments",1) - ,("less_than_integer-cpu-arguments-intercept",187278) - ,("less_than_integer-cpu-arguments-slope",980) - ,("less_than_integer-memory-arguments",1) - ,("lt_byte_string-cpu-arguments-intercept",204537) - ,("lt_byte_string-cpu-arguments-slope",227) - ,("lt_byte_string-memory-arguments",1) - ,("mod_integer-cpu-arguments-model_split_const_intercept",330895) - ,("mod_integer-cpu-arguments-model_split_const_slope",427) - ,("mod_integer-memory-arguments-intercept",0) - ,("mod_integer-memory-arguments-minimum",1) - ,("mod_integer-memory-arguments-slope",1) - ,("multiply_integer-cpu-arguments-intercept",78642) - ,("multiply_integer-cpu-arguments-slope",11464) - ,("multiply_integer-memory-arguments-intercept",0) - ,("multiply_integer-memory-arguments-slope",1) - ,("quotient_integer-cpu-arguments-model_split_const_intercept",330895) - ,("quotient_integer-cpu-arguments-model_split_const_slope",427) - ,("quotient_integer-memory-arguments-intercept",0) - ,("quotient_integer-memory-arguments-slope",1) - ,("remainder_integer-cpu-arguments-model_split_const_intercept",330895) - ,("remainder_integer-cpu-arguments-model_split_const_slope",427) - ,("remainder_integer-memory-arguments-intercept",0) - ,("remainder_integer-memory-arguments-slope",1) - ,("sha2-cpu-arguments-intercept",2267819) - ,("sha2-cpu-arguments-slope",28904) - ,("sha2-memory-arguments",4) - ,("sha3-cpu-arguments-intercept",1260296) - ,("sha3-cpu-arguments-slope",81356) - ,("sha3-memory-arguments",4) - ,("subtract_integer-cpu-arguments-intercept",251934) - ,("subtract_integer-cpu-arguments-slope",1194) - ,("subtract_integer-memory-arguments-intercept",1) - ,("subtract_integer-memory-arguments-slope",1) - ,("take_byte_string-cpu-arguments",3420288) - ,("take_byte_string-memory-arguments",20) - ,("verify_signature-cpu-arguments",5082989) - ,("verify_signature-memory-arguments",1) - ])) - ] - , protocolParamPrices = Just (ExecutionUnitPrices {priceExecutionSteps = 1 % 1, priceExecutionMemory = 1 % 1}) - , protocolParamMaxTxExUnits = Just (ExecutionUnits {executionSteps = 500000000000, executionMemory = 500000000000}) - , protocolParamMaxBlockExUnits = Just (ExecutionUnits {executionSteps = 500000000000, executionMemory = 500000000000}) - , protocolParamMaxValueSize = Just 5000 - , protocolParamCollateralPercent = Just 1 - , protocolParamMaxCollateralInputs = Just 5 - } diff --git a/plutus-pab/src/Cardano/Chain.hs b/plutus-pab/src/Cardano/Chain.hs index a0d654fe80..292db69b13 100644 --- a/plutus-pab/src/Cardano/Chain.hs +++ b/plutus-pab/src/Cardano/Chain.hs @@ -23,9 +23,8 @@ import Data.Foldable (traverse_) import Data.Functor (void) import Data.Maybe (listToMaybe) import GHC.Generics (Generic) -import Ledger (Block, CardanoTx, Slot (..)) +import Ledger (Block, CardanoTx, Params, Slot (..)) import Ledger.Index qualified as Index -import Ledger.TimeSlot (SlotConfig) import Wallet.Emulator.Chain qualified as EC type TxPool = [CardanoTx] @@ -83,15 +82,15 @@ handleControlChain :: , Member (LogMsg EC.ChainEvent) effs , LastMember m effs , MonadIO m ) - => SlotConfig -> EC.ChainControlEffect ~> Eff effs -handleControlChain slotCfg = \case + => Params -> EC.ChainControlEffect ~> Eff effs +handleControlChain params = \case EC.ProcessBlock -> do st <- get let pool = st ^. txPool slot = st ^. currentSlot idx = st ^. index EC.ValidatedBlock block events rest = - EC.validateBlock slotCfg slot idx pool + EC.validateBlock params slot idx pool let st' = st & txPool .~ rest & tip ?~ block @@ -106,12 +105,12 @@ handleControlChain slotCfg = \case handleChain :: ( Member (State MockNodeServerChainState) effs ) - => SlotConfig + => Params -> EC.ChainEffect ~> Eff effs -handleChain slotCfg = \case +handleChain params = \case EC.QueueTx tx -> modify $ over txPool (addTxToPool tx) EC.GetCurrentSlot -> gets _currentSlot - EC.GetSlotConfig -> pure slotCfg + EC.GetParams -> pure params logEvent :: Member (LogMsg EC.ChainEvent) effs => EC.ChainEvent -> Eff effs () logEvent e = case e of diff --git a/plutus-pab/src/Cardano/Node/Client.hs b/plutus-pab/src/Cardano/Node/Client.hs index 0ba010d4f5..d6da592db8 100644 --- a/plutus-pab/src/Cardano/Node/Client.hs +++ b/plutus-pab/src/Cardano/Node/Client.hs @@ -13,8 +13,7 @@ import Control.Monad.Freer.Error (Error, throwError) import Control.Monad.Freer.Reader (Reader, ask) import Control.Monad.IO.Class import Data.Proxy (Proxy (Proxy)) -import Ledger (onCardanoTx) -import Ledger.TimeSlot (SlotConfig) +import Ledger (Params, onCardanoTx) import Servant (NoContent, (:<|>) (..)) import Servant.Client (ClientM, client) @@ -45,10 +44,10 @@ handleNodeClientClient :: , Member (Reader (Maybe MockClient.TxSendHandle)) effs , Member (Reader ChainSyncHandle) effs ) - => SlotConfig + => Params -> NodeClientEffect ~> Eff effs -handleNodeClientClient slotCfg e = do +handleNodeClientClient params e = do txSendHandle <- ask @(Maybe MockClient.TxSendHandle) chainSyncHandle <- ask @ChainSyncHandle case e of @@ -62,7 +61,7 @@ handleNodeClientClient slotCfg e = do Just handle -> liftIO $ onCardanoTx (MockClient.queueTx handle) (const $ error "Cardano.Node.Client: Expecting a mock tx, not an Alonzo tx when publishing it.") tx GetClientSlot -> either (liftIO . MockClient.getCurrentSlot) (liftIO . Client.getCurrentSlot) chainSyncHandle - GetClientSlotConfig -> pure slotCfg + GetClientParams -> pure params -- | This does not seem to support resuming so it means that the slot tick will -- be behind everything else. This is due to having 2 connections to the node diff --git a/plutus-pab/src/Cardano/Node/Mock.hs b/plutus-pab/src/Cardano/Node/Mock.hs index 8848867dd8..5910add475 100644 --- a/plutus-pab/src/Cardano/Node/Mock.hs +++ b/plutus-pab/src/Cardano/Node/Mock.hs @@ -31,7 +31,7 @@ import Cardano.Chain (handleChain, handleControlChain) import Cardano.Node.Types import Cardano.Protocol.Socket.Mock.Client qualified as Client import Cardano.Protocol.Socket.Mock.Server qualified as Server -import Ledger (Tx) +import Ledger (Params (..), Tx) import Ledger.TimeSlot (SlotConfig (SlotConfig, scSlotLength), currentSlot) import Plutus.PAB.Arbitrary () import Plutus.PAB.Monitoring.Monitoring qualified as LM @@ -68,12 +68,12 @@ addTx tx = do -- | Run all chain effects in the IO Monad runChainEffects :: Trace IO PABServerLogMsg - -> SlotConfig + -> Params -> Maybe Client.TxSendHandle -> MVar AppState -> Eff (NodeServerEffects IO) a -> IO ([LogMessage PABServerLogMsg], a) -runChainEffects trace slotCfg clientHandler stateVar eff = do +runChainEffects trace params clientHandler stateVar eff = do oldAppState <- liftIO $ takeMVar stateVar ((a, events), newState) <- liftIO $ processBlock eff @@ -89,9 +89,9 @@ runChainEffects trace slotCfg clientHandler stateVar eff = do processBlock e = e >>= \r -> Chain.processBlock >> pure r runChain = interpret (mapLog ProcessingChainEvent) - . reinterpret (handleChain slotCfg) + . reinterpret (handleChain params) . interpret (mapLog ProcessingChainEvent) - . reinterpret (handleControlChain slotCfg) + . reinterpret (handleControlChain params) mergeState = interpret (handleZoomedState chainState) @@ -101,13 +101,13 @@ runChainEffects trace slotCfg clientHandler stateVar eff = do processChainEffects :: Trace IO PABServerLogMsg - -> SlotConfig + -> Params -> Maybe Client.TxSendHandle -> MVar AppState -> Eff (NodeServerEffects IO) a -> IO a -processChainEffects trace slotCfg clientHandler stateVar eff = do - (events, result) <- liftIO $ runChainEffects trace slotCfg clientHandler stateVar eff +processChainEffects trace params clientHandler stateVar eff = do + (events, result) <- liftIO $ runChainEffects trace params clientHandler stateVar eff LM.runLogEffects trace $ traverse_ (\(LogMessage _ chainEvent) -> logDebug chainEvent) events liftIO $ modifyMVar_ diff --git a/plutus-pab/src/Cardano/Node/Params.hs b/plutus-pab/src/Cardano/Node/Params.hs new file mode 100644 index 0000000000..0717121e08 --- /dev/null +++ b/plutus-pab/src/Cardano/Node/Params.hs @@ -0,0 +1,30 @@ +{-# LANGUAGE NamedFieldPuns #-} +module Cardano.Node.Params where + +import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (..)) +import Cardano.Api.Shelley (ProtocolParameters) +import Cardano.Node.Types +import Data.Aeson (eitherDecode) +import Data.ByteString.Lazy qualified as BSL +import Data.Default (def) +import Ledger.Params + +fromPABServerConfig :: PABServerConfig -> IO Params +fromPABServerConfig PABServerConfig{pscSlotConfig, pscNetworkId, pscProtocolParametersJsonPath} = do + let NetworkIdWrapper networkId = pscNetworkId + protocolParameters <- readProtocolParameters pscProtocolParametersJsonPath + pure $ Params + { pSlotConfig = pscSlotConfig + , pProtocolParams = protocolParameters + , pNetworkId = networkId + } + +readProtocolParameters :: Maybe FilePath -> IO ProtocolParameters +readProtocolParameters = maybe (pure def) readPP + where + readPP path = do + bs <- BSL.readFile path + case eitherDecode bs of + Left err -> error $ "Error reading protocol parameters JSON file: " + ++ show path ++ " (" ++ err ++ ")" + Right params -> pure params diff --git a/plutus-pab/src/Cardano/Node/Server.hs b/plutus-pab/src/Cardano/Node/Server.hs index b62586c0b2..ab65a911d9 100644 --- a/plutus-pab/src/Cardano/Node/Server.hs +++ b/plutus-pab/src/Cardano/Node/Server.hs @@ -11,6 +11,7 @@ module Cardano.Node.Server import Cardano.BM.Data.Trace (Trace) import Cardano.Node.API (API) import Cardano.Node.Mock +import Cardano.Node.Params qualified as Params import Cardano.Node.Types import Cardano.Protocol.Socket.Mock.Client qualified as Client import Cardano.Protocol.Socket.Mock.Server qualified as Server @@ -26,6 +27,7 @@ import Data.Proxy (Proxy (Proxy)) import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Data.Time.Units (Millisecond, Second) import Ledger.Ada qualified as Ada +import Ledger.Params (Params (..)) import Ledger.TimeSlot (SlotConfig (SlotConfig, scSlotLength, scSlotZeroTime)) import Network.Wai.Handler.Warp qualified as Warp import Plutus.PAB.Arbitrary () @@ -36,15 +38,15 @@ import Wallet.Emulator.Wallet (fromWalletNumber) app :: Trace IO PABServerLogMsg - -> SlotConfig + -> Params -> Client.TxSendHandle -> MVar AppState -> Application -app trace slotCfg clientHandler stateVar = +app trace params clientHandler stateVar = serve (Proxy @API) $ hoistServer (Proxy @API) - (liftIO . processChainEffects trace slotCfg (Just clientHandler) stateVar) + (liftIO . processChainEffects trace params (Just clientHandler) stateVar) (healthcheck :<|> consumeEventHistory stateVar) data Ctx = Ctx { serverHandler :: Server.ServerHandler @@ -54,9 +56,9 @@ data Ctx = Ctx { serverHandler :: Server.ServerHandler } main :: Trace IO PABServerLogMsg -> PABServerConfig -> Availability -> IO () -main trace PABServerConfig { pscBaseUrl - , pscKeptBlocks +main trace nodeServerConfig@PABServerConfig { pscBaseUrl , pscSlotConfig + , pscKeptBlocks , pscInitialTxWallets , pscSocketPath } availability = LM.runLogEffects trace $ do @@ -67,7 +69,8 @@ main trace PABServerConfig { pscBaseUrl { _chainState = initialState , _eventHistory = mempty } - serverHandler <- liftIO $ Server.runServerNode trace pscSocketPath pscKeptBlocks (_chainState appState) pscSlotConfig + params <- liftIO $ Params.fromPABServerConfig nodeServerConfig + serverHandler <- liftIO $ Server.runServerNode trace pscSocketPath pscKeptBlocks (_chainState appState) params serverState <- liftIO $ newMVar appState handleDelayEffect $ delayThread (2 :: Second) clientHandler <- liftIO $ Client.runTxSender pscSocketPath @@ -81,7 +84,7 @@ main trace PABServerConfig { pscBaseUrl runSlotCoordinator ctx logInfo $ StartingPABServer $ baseUrlPort pscBaseUrl - liftIO $ Warp.runSettings warpSettings $ app trace pscSlotConfig clientHandler serverState + liftIO $ Warp.runSettings warpSettings $ app trace params clientHandler serverState where warpSettings = Warp.defaultSettings & Warp.setPort (baseUrlPort pscBaseUrl) & Warp.setBeforeMainLoop (available availability) diff --git a/plutus-pab/src/Cardano/Protocol/Socket/Mock/Server.hs b/plutus-pab/src/Cardano/Protocol/Socket/Mock/Server.hs index c0e7e32de4..9a17630389 100644 --- a/plutus-pab/src/Cardano/Protocol/Socket/Mock/Server.hs +++ b/plutus-pab/src/Cardano/Protocol/Socket/Mock/Server.hs @@ -49,8 +49,7 @@ import Cardano.Protocol.Socket.Type import Cardano.Chain (MockNodeServerChainState (..), addTxToPool, chainNewestFirst, channel, currentSlot, getChannel, getTip, handleControlChain, tip, txPool) -import Ledger (Block, CardanoTx (..), Slot (..), Tx (..)) -import Ledger.TimeSlot (SlotConfig) +import Ledger (Block, CardanoTx (..), Params, Slot (..), Tx (..)) import Wallet.Emulator.Chain qualified as Chain data CommandChannel = CommandChannel @@ -146,16 +145,16 @@ handleCommand :: => Trace IO PABServerLogMsg -> CommandChannel -> MVar MockNodeServerChainState - -> SlotConfig + -> Params -> m () -handleCommand trace CommandChannel {ccCommand, ccResponse} mvChainState slotCfg = +handleCommand trace CommandChannel {ccCommand, ccResponse} mvChainState params = liftIO (atomically $ readTQueue ccCommand) >>= \case AddTx tx -> do liftIO $ modifyMVar_ mvChainState (pure . over txPool (EmulatorTx tx :)) ModifySlot f -> liftIO $ do state <- liftIO $ takeMVar mvChainState (s, nextState') <- liftIO $ Chain.modifySlot f - & interpret (handleControlChain slotCfg) + & interpret (handleControlChain params) & interpret (LM.handleLogMsgTraceMap ProcessingChainEvent trace) & runState state & runM @@ -165,7 +164,7 @@ handleCommand trace CommandChannel {ccCommand, ccResponse} mvChainState slotCfg ProcessBlock -> liftIO $ do state <- liftIO $ takeMVar mvChainState (block, nextState') <- liftIO $ Chain.processBlock - & interpret (handleControlChain slotCfg) + & interpret (handleControlChain params) & interpret (LM.handleLogMsgTraceMap ProcessingChainEvent trace) & runState state & runM @@ -181,14 +180,14 @@ runServerNode :: -> FilePath -> Integer -> MockNodeServerChainState - -> SlotConfig + -> Params -> m ServerHandler -runServerNode trace shSocketPath k initialState slotCfg = liftIO $ do +runServerNode trace shSocketPath k initialState params = liftIO $ do serverState <- newMVar initialState shCommandChannel <- CommandChannel <$> newTQueueIO <*> newTQueueIO globalChannel <- getChannel serverState void $ forkIO . void $ protocolLoop shSocketPath serverState - void $ forkIO . forever $ handleCommand trace shCommandChannel serverState slotCfg + void $ forkIO . forever $ handleCommand trace shCommandChannel serverState params void $ pruneChain k globalChannel pure $ ServerHandler { shSocketPath, shCommandChannel } diff --git a/plutus-pab/src/Cardano/Wallet/LocalClient.hs b/plutus-pab/src/Cardano/Wallet/LocalClient.hs index 6aedf5ef84..4d2ef85c95 100644 --- a/plutus-pab/src/Cardano/Wallet/LocalClient.hs +++ b/plutus-pab/src/Cardano/Wallet/LocalClient.hs @@ -10,9 +10,9 @@ module Cardano.Wallet.LocalClient where import Cardano.Api qualified -import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (NetworkIdWrapper)) import Cardano.Api.Shelley qualified as Cardano.Api -import Cardano.Node.Types (PABServerConfig (pscNetworkId, pscPassphrase)) +import Cardano.Node.Params qualified as Params +import Cardano.Node.Types (PABServerConfig (pscPassphrase)) import Cardano.Wallet.Api qualified as C import Cardano.Wallet.Api.Client qualified as C import Cardano.Wallet.Api.Types (ApiVerificationKeyShelley (getApiVerificationKey), ApiWallet (assets, balance)) @@ -37,7 +37,7 @@ import Data.Proxy (Proxy (Proxy)) import Data.Quantity (Quantity (Quantity)) import Data.Text (pack) import Data.Text.Class (fromText) -import Ledger (CardanoTx (..)) +import Ledger (CardanoTx (..), Params (..)) import Ledger qualified import Ledger.Ada qualified as Ada import Ledger.Constraints.OffChain (UnbalancedTx) @@ -66,7 +66,6 @@ handleWalletClient , Member (Error ClientError) effs , Member (Error WalletAPIError) effs , Member (Reader ClientEnv) effs - , Member (Reader Cardano.Api.ProtocolParameters) effs , Member (LogMsg WalletClientMsg) effs ) => PABServerConfig -- TODO: Rename. Not mock @@ -74,10 +73,9 @@ handleWalletClient -> WalletEffect ~> Eff effs handleWalletClient config (Wallet _ (WalletId walletId)) event = do - let NetworkIdWrapper networkId = pscNetworkId config + Params{pNetworkId = networkId, pProtocolParams = protocolParams} <- liftIO $ Params.fromPABServerConfig config let mpassphrase = pscPassphrase config clientEnv <- ask @ClientEnv - protocolParams <- ask @Cardano.Api.ProtocolParameters let runClient :: ClientM a -> Eff effs a runClient a = do @@ -111,8 +109,8 @@ handleWalletClient config (Wallet _ (WalletId walletId)) event = do balanceTxH :: UnbalancedTx -> Eff effs (Either WalletAPIError CardanoTx) balanceTxH utx = do - slotConfig <- WAPI.getClientSlotConfig - case export protocolParams networkId slotConfig utx of + Params { pSlotConfig } <- WAPI.getClientParams + case export protocolParams networkId pSlotConfig utx of Left err -> do logWarn $ BalanceTxError $ show $ pretty err throwOtherError $ pretty err diff --git a/plutus-pab/src/Cardano/Wallet/Mock/Handlers.hs b/plutus-pab/src/Cardano/Wallet/Mock/Handlers.hs index 2112091011..a552c44dea 100644 --- a/plutus-pab/src/Cardano/Wallet/Mock/Handlers.hs +++ b/plutus-pab/src/Cardano/Wallet/Mock/Handlers.hs @@ -50,7 +50,7 @@ import Ledger.Ada qualified as Ada import Ledger.Address (PaymentPubKeyHash) import Ledger.CardanoWallet (MockWallet) import Ledger.CardanoWallet qualified as CW -import Ledger.TimeSlot (SlotConfig) +import Ledger.Params (Params) import Ledger.Tx (CardanoTx) import Plutus.ChainIndex (ChainIndexQueryEffect) import Plutus.ChainIndex.Client qualified as ChainIndex @@ -64,7 +64,7 @@ import Servant.Server (err500) import Wallet.API (WalletAPIError (..)) import Wallet.API qualified as WAPI import Wallet.Effects (NodeClientEffect) -import Wallet.Emulator.LogMessages (TxBalanceMsg) +import Wallet.Emulator.LogMessages (RequestHandlerLogMsg, TxBalanceMsg) import Wallet.Emulator.Wallet qualified as Wallet newtype Seed = Seed ScrubbedBytes @@ -88,11 +88,13 @@ distributeNewWalletFunds :: forall effs. ( Member WAPI.WalletEffect effs , Member (Error WalletAPIError) effs , Member (LogMsg Text) effs + , Member (LogMsg RequestHandlerLogMsg) effs ) - => Maybe Ada.Ada + => Params + -> Maybe Ada.Ada -> PaymentPubKeyHash -> Eff effs CardanoTx -distributeNewWalletFunds funds = WAPI.payToPaymentPublicKeyHash WAPI.defaultSlotRange +distributeNewWalletFunds params funds = WAPI.payToPaymentPublicKeyHash params WAPI.defaultSlotRange (maybe (Ada.adaValueOf 10_000) Ada.toValue funds) newWallet :: forall m effs. (LastMember m effs, MonadIO m) => Eff effs MockWallet @@ -112,8 +114,8 @@ handleMultiWallet :: forall m effs. , LastMember m effs , MonadIO m ) - => MultiWalletEffect ~> Eff effs -handleMultiWallet = \case + => Params -> MultiWalletEffect ~> Eff effs +handleMultiWallet params = \case MultiWallet (Wallet.Wallet _ walletId) action -> do wallets <- get @Wallets case Map.lookup walletId wallets of @@ -139,8 +141,9 @@ handleMultiWallet = \case let sourceWallet = Wallet.fromMockWallet (CW.knownMockWallet 2) _ <- evalState sourceWallet $ interpret (mapLog @TxBalanceMsg @WalletMsg Balancing) + $ interpret (mapLog @RequestHandlerLogMsg @WalletMsg RequestHandling) $ interpret Wallet.handleWallet - $ distributeNewWalletFunds funds pkh + $ distributeNewWalletFunds params funds pkh return $ WalletInfo{wiWallet = Wallet.toMockWallet mockWallet, wiPaymentPubKeyHash = pkh} GetWalletInfo wllt -> do wallets <- get @Wallets @@ -155,17 +158,17 @@ processWalletEffects :: -> ChainSyncHandle -- ^ node client -> ClientEnv -- ^ chain index client -> MVar Wallets -- ^ wallets state - -> SlotConfig + -> Params -> Eff (WalletEffects IO) a -- ^ wallet effect -> m a -processWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv mVarState slotCfg action = do +processWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv mVarState params action = do oldState <- liftIO $ takeMVar mVarState result <- liftIO $ runWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv oldState - slotCfg + params action case result of Left e -> do @@ -182,13 +185,13 @@ runWalletEffects :: -> ChainSyncHandle -- ^ node client -> ClientEnv -- ^ chain index client -> Wallets -- ^ current state - -> SlotConfig + -> Params -> Eff (WalletEffects IO) a -- ^ wallet effect -> IO (Either ServerError (a, Wallets)) -runWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv wallets slotCfg action = - reinterpret handleMultiWallet action +runWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv wallets params action = + reinterpret (handleMultiWallet params) action & interpret (LM.handleLogMsgTrace trace) - & reinterpret2 (NodeClient.handleNodeClientClient slotCfg) + & reinterpret2 (NodeClient.handleNodeClientClient params) & runReader chainSyncHandle & runReader (Just txSendHandle) & reinterpret ChainIndex.handleChainIndexClient diff --git a/plutus-pab/src/Cardano/Wallet/Mock/Server.hs b/plutus-pab/src/Cardano/Wallet/Mock/Server.hs index 3fd317e163..ac374c13d4 100644 --- a/plutus-pab/src/Cardano/Wallet/Mock/Server.hs +++ b/plutus-pab/src/Cardano/Wallet/Mock/Server.hs @@ -33,7 +33,7 @@ import Data.Map.Strict qualified as Map import Data.Proxy (Proxy (Proxy)) import Ledger.Ada qualified as Ada import Ledger.CardanoWallet qualified as CW -import Ledger.TimeSlot (SlotConfig) +import Ledger.Params (Params (..)) import Network.HTTP.Client (defaultManagerSettings, newManager) import Network.Wai.Handler.Warp qualified as Warp import Plutus.PAB.Arbitrary () @@ -49,13 +49,13 @@ app :: Trace IO WalletMsg -> ChainSyncHandle -> ClientEnv -> MVar Wallets - -> SlotConfig + -> Params -> Application -app trace txSendHandle chainSyncHandle chainIndexEnv mVarState slotCfg = +app trace txSendHandle chainSyncHandle chainIndexEnv mVarState params = serve (Proxy @(API WalletId)) $ hoistServer (Proxy @(API WalletId)) - (processWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv mVarState slotCfg) $ + (processWalletEffects trace txSendHandle chainSyncHandle chainIndexEnv mVarState params) $ (\funds -> createWallet (Ada.lovelaceOf <$> funds)) :<|> (\w tx -> multiWallet (Wallet Nothing w) (submitTxn tx) >>= const (pure NoContent)) :<|> (getWalletInfo >=> maybe (throwError err404) pure ) :<|> @@ -63,13 +63,13 @@ app trace txSendHandle chainSyncHandle chainIndexEnv mVarState slotCfg = (\w -> multiWallet (Wallet Nothing w) totalFunds) :<|> (\w tx -> multiWallet (Wallet Nothing w) (walletAddSignature tx)) -main :: Trace IO WalletMsg -> LocalWalletSettings -> FilePath -> SlotConfig -> ChainIndexUrl -> Availability -> IO () -main trace LocalWalletSettings { baseUrl } serverSocket slotCfg (ChainIndexUrl chainUrl) availability = LM.runLogEffects trace $ do +main :: Trace IO WalletMsg -> LocalWalletSettings -> FilePath -> Params -> ChainIndexUrl -> Availability -> IO () +main trace LocalWalletSettings { baseUrl } serverSocket params (ChainIndexUrl chainUrl) availability = LM.runLogEffects trace $ do chainIndexEnv <- buildEnv chainUrl defaultManagerSettings let knownWallets = Map.fromList $ zip (Wallet.getWalletId <$> Wallet.knownWallets) (Wallet.fromMockWallet <$> CW.knownMockWallets) mVarState <- liftIO $ newMVar knownWallets txSendHandle <- liftIO $ MockClient.runTxSender serverSocket - chainSyncHandle <- Left <$> (liftIO $ MockClient.runChainSync' serverSocket slotCfg) + chainSyncHandle <- Left <$> (liftIO $ MockClient.runChainSync' serverSocket $ pSlotConfig params) logInfo $ StartingWallet (Port servicePort) liftIO $ Warp.runSettings warpSettings $ app trace @@ -77,7 +77,7 @@ main trace LocalWalletSettings { baseUrl } serverSocket slotCfg (ChainIndexUrl c chainSyncHandle chainIndexEnv mVarState - slotCfg + params where servicePort = baseUrlPort (coerce baseUrl) warpSettings = Warp.defaultSettings & Warp.setPort servicePort & Warp.setBeforeMainLoop (available availability) diff --git a/plutus-pab/src/Cardano/Wallet/Mock/Types.hs b/plutus-pab/src/Cardano/Wallet/Mock/Types.hs index e15c04165b..e22eb87dda 100644 --- a/plutus-pab/src/Cardano/Wallet/Mock/Types.hs +++ b/plutus-pab/src/Cardano/Wallet/Mock/Types.hs @@ -57,7 +57,7 @@ import Servant.Client (ClientError) import Servant.Client.Internal.HttpClient (ClientEnv) import Wallet.Effects (NodeClientEffect, WalletEffect) import Wallet.Emulator.Error (WalletAPIError) -import Wallet.Emulator.LogMessages (TxBalanceMsg) +import Wallet.Emulator.LogMessages (RequestHandlerLogMsg, TxBalanceMsg) import Wallet.Emulator.Wallet (Wallet, WalletId, WalletState (WalletState, _mockWallet), mockWalletPaymentPubKeyHash, toMockWallet) @@ -105,6 +105,7 @@ newtype Port = Port Int data WalletMsg = StartingWallet Port | ChainClientMsg Text | Balancing TxBalanceMsg + | RequestHandling RequestHandlerLogMsg deriving stock (Show, Generic) deriving anyclass (ToJSON, FromJSON) @@ -113,9 +114,11 @@ instance Pretty WalletMsg where StartingWallet port -> "Starting wallet server on port" <+> pretty port ChainClientMsg m -> "Chain Client: " <+> pretty m Balancing m -> pretty m + RequestHandling m -> pretty m instance ToObject WalletMsg where toObject _ = \case StartingWallet port -> mkObjectStr "Starting wallet server" (Tagged @"port" port) ChainClientMsg m -> mkObjectStr "Chain Client: " (Tagged @"msg" m) Balancing m -> mkObjectStr "Balancing" (Tagged @"msg" m) + RequestHandling m -> mkObjectStr "RequestHandling" (Tagged @"msg" m) diff --git a/plutus-pab/src/Cardano/Wallet/RemoteClient.hs b/plutus-pab/src/Cardano/Wallet/RemoteClient.hs index 5a9b75cde2..6f65b19ca4 100644 --- a/plutus-pab/src/Cardano/Wallet/RemoteClient.hs +++ b/plutus-pab/src/Cardano/Wallet/RemoteClient.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} @@ -10,15 +11,15 @@ module Cardano.Wallet.RemoteClient ( handleWalletClient ) where -import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (NetworkIdWrapper)) -import Cardano.Api.Shelley qualified as Cardano.Api -import Cardano.Node.Types (PABServerConfig (pscNetworkId)) +import Cardano.Node.Params qualified as Params +import Cardano.Node.Types (PABServerConfig) import Control.Concurrent.STM qualified as STM import Control.Monad.Freer (Eff, LastMember, Member, type (~>)) import Control.Monad.Freer.Error (Error, throwError) import Control.Monad.Freer.Reader (Reader, ask) import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Text qualified as Text +import Ledger.Params (Params (..)) import Plutus.Contract.Wallet (export) import Plutus.PAB.Core.ContractInstance.STM (InstancesState) import Plutus.PAB.Core.ContractInstance.STM qualified as Instances @@ -39,7 +40,6 @@ handleWalletClient , MonadIO m , Member WAPI.NodeClientEffect effs , Member (Error WalletAPIError) effs - , Member (Reader Cardano.Api.ProtocolParameters) effs , Member (Reader InstancesState) effs ) => PABServerConfig @@ -47,8 +47,7 @@ handleWalletClient -> WalletEffect ~> Eff effs handleWalletClient config cidM event = do - let NetworkIdWrapper networkId = pscNetworkId config - protocolParams <- ask @Cardano.Api.ProtocolParameters + Params{pNetworkId = networkId, pProtocolParams = protocolParams} <- liftIO $ Params.fromPABServerConfig config case event of OwnPaymentPubKeyHash -> do throwError $ RemoteClientFunctionNotYetSupported "Cardano.Wallet.RemoteClient.OwnPaymentPubKeyHash" @@ -66,8 +65,8 @@ handleWalletClient config cidM event = do throwError $ RemoteClientFunctionNotYetSupported "Cardano.Wallet.RemoteClient.BalanceTx" YieldUnbalancedTx utx -> do - slotConfig <- WAPI.getClientSlotConfig - case export protocolParams networkId slotConfig utx of + WAPI.Params { WAPI.pSlotConfig } <- WAPI.getClientParams + case export protocolParams networkId pSlotConfig utx of Left err -> throwOtherError $ Text.pack $ show err Right ex -> do case cidM of diff --git a/plutus-pab/src/Plutus/PAB/App.hs b/plutus-pab/src/Plutus/PAB/App.hs index 62f8beaaca..9f70fe6a6d 100644 --- a/plutus-pab/src/Plutus/PAB/App.hs +++ b/plutus-pab/src/Plutus/PAB/App.hs @@ -29,14 +29,12 @@ module Plutus.PAB.App( handleContractDefinition ) where -import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (NetworkIdWrapper)) -import Cardano.Api.ProtocolParameters () -import Cardano.Api.Shelley (ProtocolParameters) import Cardano.BM.Trace (Trace, logDebug) import Cardano.ChainIndex.Types qualified as ChainIndex import Cardano.Node.Client (handleNodeClientClient, runChainSyncWithCfg) +import Cardano.Node.Params qualified as Params import Cardano.Node.Types (ChainSyncHandle, NodeMode (AlonzoNode, MockNode), - PABServerConfig (PABServerConfig, pscBaseUrl, pscNetworkId, pscNodeMode, pscProtocolParametersJsonPath, pscSlotConfig, pscSocketPath)) + PABServerConfig (PABServerConfig, pscBaseUrl, pscNodeMode, pscSocketPath)) import Cardano.Protocol.Socket.Mock.Client qualified as MockClient import Cardano.Wallet.LocalClient qualified as LocalWalletClient import Cardano.Wallet.Mock.Client qualified as WalletMockClient @@ -50,10 +48,8 @@ import Control.Monad.Freer.Extras.Beam (handleBeam) import Control.Monad.Freer.Extras.Log (LogMsg, mapLog) import Control.Monad.Freer.Reader (Reader, ask, runReader) import Control.Monad.IO.Class (MonadIO (liftIO)) -import Data.Aeson (FromJSON, ToJSON, eitherDecode) -import Data.ByteString.Lazy qualified as BSL +import Data.Aeson (FromJSON, ToJSON) import Data.Coerce (coerce) -import Data.Default (def) import Data.Pool (Pool) import Data.Pool qualified as Pool import Data.Text (Text, pack, unpack) @@ -62,7 +58,8 @@ import Database.Beam.Migrate.Simple (autoMigrate) import Database.Beam.Sqlite qualified as Sqlite import Database.Beam.Sqlite.Migrate qualified as Sqlite import Database.SQLite.Simple qualified as Sqlite -import Network.HTTP.Client (managerModifyRequest, newManager, setRequestIgnoreStatus) +import Network.HTTP.Client (ManagerSettings (managerResponseTimeout), managerModifyRequest, newManager, + responseTimeoutMicro, setRequestIgnoreStatus) import Network.HTTP.Client.TLS (tlsManagerSettings) import Plutus.ChainIndex.Client qualified as ChainIndex import Plutus.PAB.Core (EffectHandlers (EffectHandlers), PABAction) @@ -81,10 +78,9 @@ import Plutus.PAB.Monitoring.PABLogMsg (PABLogMsg (SMultiAgent), PABMultiAgentMs WalletClientMsg) import Plutus.PAB.Timeout (Timeout (Timeout)) import Plutus.PAB.Types (Config (Config), DbConfig (..), - DevelopmentOptions (DevelopmentOptions, pabResumeFrom, pabRollbackHistory), PABError (BeamEffectError, ChainIndexError, NodeClientError, RemoteWalletWithMockNodeError, WalletClientError, WalletError), - WebserverConfig (WebserverConfig), chainIndexConfig, dbConfig, developmentOptions, - endpointTimeout, nodeServerConfig, pabWebserverConfig, walletServerConfig) + WebserverConfig (WebserverConfig), chainIndexConfig, dbConfig, endpointTimeout, + nodeServerConfig, pabWebserverConfig, walletServerConfig) import Servant.Client (ClientEnv, ClientError, mkClientEnv) import Wallet.API (NodeClientEffect) import Wallet.Effects (WalletEffect) @@ -106,7 +102,6 @@ data AppEnv a = , appConfig :: Config , appTrace :: Trace IO (PABLogMsg (Builtin a)) , appInMemContractStore :: InMemInstances (Builtin a) - , protocolParameters :: ProtocolParameters } appEffectHandlers @@ -125,10 +120,8 @@ appEffectHandlers storageBackend config trace BuiltinHandler{contractHandler} = EffectHandlers { initialiseEnvironment = do env <- liftIO $ mkEnv trace config - let Config { nodeServerConfig = PABServerConfig{pscSocketPath, pscSlotConfig, pscNodeMode, pscNetworkId = NetworkIdWrapper networkId} - , developmentOptions = DevelopmentOptions{pabRollbackHistory, pabResumeFrom} } = config instancesState <- liftIO $ STM.atomically Instances.emptyInstancesState - blockchainEnv <- liftIO $ BlockchainEnv.startNodeClient pscSocketPath pscNodeMode pabRollbackHistory pscSlotConfig networkId pabResumeFrom instancesState + blockchainEnv <- liftIO $ BlockchainEnv.startNodeClient config instancesState pure (instancesState, blockchainEnv, env) , handleLogMessages = @@ -173,7 +166,10 @@ appEffectHandlers storageBackend config trace BuiltinHandler{contractHandler} = . reinterpret (Core.handleMappedReader @(AppEnv a) @(Maybe MockClient.TxSendHandle) txSendHandle) . interpret (Core.handleUserEnvReader @(Builtin a) @(AppEnv a)) . reinterpret (Core.handleMappedReader @(AppEnv a) @ClientEnv nodeClientEnv) - . reinterpretN @'[_, _, _, _] (handleNodeClientClient @IO $ pscSlotConfig $ nodeServerConfig config) + . reinterpretN @'[_, _, _, _] + (\nodeClientEffect -> do + params <- liftIO $ Params.fromPABServerConfig $ nodeServerConfig config + handleNodeClientClient @IO params nodeClientEffect) -- handle 'ChainIndexEffect' . flip handleError (throwError . ChainIndexError) @@ -188,7 +184,6 @@ appEffectHandlers storageBackend config trace BuiltinHandler{contractHandler} = . interpret (Core.handleUserEnvReader @(Builtin a) @(AppEnv a)) . reinterpret (Core.handleMappedReader @(AppEnv a) @(Maybe ClientEnv) walletClientEnv) . interpret (Core.handleUserEnvReader @(Builtin a) @(AppEnv a)) - . reinterpret (Core.handleMappedReader @(AppEnv a) @ProtocolParameters protocolParameters) . interpret (Core.handleInstancesStateReader @(Builtin a) @(AppEnv a)) . reinterpretN @'[_, _, _, _, _, _] (handleWalletEffect (nodeServerConfig config) cidM wallet) @@ -205,7 +200,6 @@ handleWalletEffect , Member (Error WalletAPIError) effs , Member (Error PABError) effs , Member (Reader (Maybe ClientEnv)) effs - , Member (Reader ProtocolParameters) effs , Member (LogMsg WalletClientMsg) effs , Member (Reader InstancesState) effs ) @@ -254,7 +248,7 @@ data StorageBackend = BeamSqliteBackend | InMemoryBackend mkEnv :: Trace IO (PABLogMsg (Builtin a)) -> Config -> IO (AppEnv a) mkEnv appTrace appConfig@Config { dbConfig - , nodeServerConfig = PABServerConfig{pscBaseUrl, pscSocketPath, pscProtocolParametersJsonPath, pscNodeMode} + , nodeServerConfig = PABServerConfig{pscBaseUrl, pscSocketPath, pscNodeMode} , walletServerConfig , chainIndexConfig } = do @@ -270,22 +264,14 @@ mkEnv appTrace appConfig@Config { dbConfig -- This is for access to the slot number in the interpreter chainSyncHandle <- runChainSyncWithCfg $ nodeServerConfig appConfig appInMemContractStore <- liftIO initialInMemInstances - protocolParameters <- maybe (pure def) readPP pscProtocolParametersJsonPath pure AppEnv {..} where clientEnv baseUrl = mkClientEnv <$> liftIO mkManager <*> pure (coerce baseUrl) mkManager = newManager $ - tlsManagerSettings {managerModifyRequest = pure . setRequestIgnoreStatus} - - readPP path = do - bs <- BSL.readFile path - case eitherDecode bs of - Left err -> error $ "Error reading protocol parameters JSON file: " - ++ show pscProtocolParametersJsonPath ++ " (" ++ err ++ ")" - Right params -> pure params - + tlsManagerSettings { managerModifyRequest = pure . setRequestIgnoreStatus + , managerResponseTimeout = responseTimeoutMicro 60_000_000 } logDebugString :: Trace IO (PABLogMsg t) -> Text -> IO () logDebugString trace = logDebug trace . SMultiAgent . UserLog diff --git a/plutus-pab/src/Plutus/PAB/Core.hs b/plutus-pab/src/Plutus/PAB/Core.hs index ab76532133..ba6d4c1b25 100644 --- a/plutus-pab/src/Plutus/PAB/Core.hs +++ b/plutus-pab/src/Plutus/PAB/Core.hs @@ -39,7 +39,7 @@ module Plutus.PAB.Core , EffectHandlers(..) , runPAB , runPAB' - , PABEnvironment(appEnv) + , PABEnvironment(..) -- * Contracts and instances , reportContractState , activateContract @@ -67,7 +67,6 @@ module Plutus.PAB.Core , activeContracts , finalResult , waitUntilFinished - , blockchainEnv , valueAt , askUserEnv , askBlockchainEnv @@ -104,7 +103,7 @@ import Data.Maybe (catMaybes) import Data.Proxy (Proxy (Proxy)) import Data.Set (Set) import Data.Text (Text) -import Ledger (Address (addressCredential), TxOutRef) +import Ledger (Address (addressCredential), Params, TxOutRef) import Ledger.Address (PaymentPubKeyHash) import Ledger.Tx (CardanoTx, TxId, ciTxOutValue) import Ledger.Value (Value) @@ -335,11 +334,11 @@ callEndpointOnInstance' instanceID ep value = do $ Instances.callEndpointOnInstance state (EndpointDescription ep) (JSON.toJSON value) instanceID -- | Make a payment to a payment public key. -payToPaymentPublicKey :: ContractInstanceId -> Wallet -> PaymentPubKeyHash -> Value -> PABAction t env CardanoTx -payToPaymentPublicKey cid source target amount = +payToPaymentPublicKey :: Params -> ContractInstanceId -> Wallet -> PaymentPubKeyHash -> Value -> PABAction t env CardanoTx +payToPaymentPublicKey params cid source target amount = handleAgentThread source (Just cid) $ Modify.wrapError WalletError - $ WAPI.payToPaymentPublicKeyHash WAPI.defaultSlotRange amount target + $ WAPI.payToPaymentPublicKeyHash params WAPI.defaultSlotRange amount target -- | Effects available to contract instances with access to external services. type ContractInstanceEffects t env effs = diff --git a/plutus-pab/src/Plutus/PAB/Core/ContractInstance.hs b/plutus-pab/src/Plutus/PAB/Core/ContractInstance.hs index 86f2f03d25..389647a2d3 100644 --- a/plutus-pab/src/Plutus/PAB/Core/ContractInstance.hs +++ b/plutus-pab/src/Plutus/PAB/Core/ContractInstance.hs @@ -269,6 +269,7 @@ stmRequestHandler = fmap sequence (wrapHandler (fmap pure nonBlockingRequests) < <> RequestHandler.handleCurrentSlotQueries @effs <> RequestHandler.handleCurrentTimeQueries @effs <> RequestHandler.handleYieldedUnbalancedTx @effs + <> RequestHandler.handleAdjustUnbalancedTx @effs -- requests that wait for changes to happen blockingRequests = diff --git a/plutus-pab/src/Plutus/PAB/Core/ContractInstance/BlockchainEnv.hs b/plutus-pab/src/Plutus/PAB/Core/ContractInstance/BlockchainEnv.hs index 3fa02da68c..5f9fe55869 100644 --- a/plutus-pab/src/Plutus/PAB/Core/ContractInstance/BlockchainEnv.hs +++ b/plutus-pab/src/Plutus/PAB/Core/ContractInstance/BlockchainEnv.hs @@ -10,8 +10,10 @@ module Plutus.PAB.Core.ContractInstance.BlockchainEnv( , processChainSyncEvent ) where -import Cardano.Api (BlockInMode (..), ChainPoint (..), NetworkId) +import Cardano.Api (BlockInMode (..), ChainPoint (..)) import Cardano.Api qualified as C +import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (NetworkIdWrapper)) +import Cardano.Node.Params qualified as Params import Cardano.Node.Types (NodeMode (..)) import Cardano.Protocol.Socket.Client (ChainSyncEvent (..)) import Cardano.Protocol.Socket.Client qualified as Client @@ -25,6 +27,10 @@ import Plutus.PAB.Core.ContractInstance.STM (BlockchainEnv (..), InstanceClientE import Plutus.PAB.Core.ContractInstance.STM qualified as S import Plutus.Trace.Emulator.ContractInstance (IndexedBlock (..), indexBlock) +import Plutus.PAB.Types (Config (Config), DevelopmentOptions (DevelopmentOptions, pabResumeFrom, pabRollbackHistory), + developmentOptions, nodeServerConfig) + +import Cardano.Node.Types (PABServerConfig (PABServerConfig, pscNetworkId, pscNodeMode, pscSlotConfig, pscSocketPath)) import Control.Concurrent.STM (STM) import Control.Concurrent.STM qualified as STM import Control.Lens @@ -32,7 +38,6 @@ import Control.Monad (forM_, void, when) import Control.Tracer (nullTracer) import Data.Foldable (foldl') import Data.Maybe (catMaybes, maybeToList) -import Ledger.TimeSlot (SlotConfig) import Plutus.ChainIndex (BlockNumber (..), ChainIndexTx (..), ChainIndexTxOutputs (..), Depth (..), InsertUtxoFailed (..), InsertUtxoSuccess (..), Point (..), ReduceBlockCountResult (..), RollbackFailed (..), RollbackResult (..), Tip (..), TxConfirmedState (..), TxIdState (..), @@ -48,17 +53,15 @@ import System.Random -- | Connect to the node and write node updates to the blockchain -- env. startNodeClient :: - FilePath -- ^ Socket to connect to node - -> NodeMode -- ^ Whether to connect to real node or mock node - -> Maybe Int -- ^ How much history do we remember for rollbacks - -> SlotConfig -- ^ Slot config used by the node - -> NetworkId -- ^ Cardano network ID - -> Point + Config -- ^ PAB's config -> InstancesState -- ^ In-memory state of running contract instances -> IO BlockchainEnv -startNodeClient socket mode rollbackHistory slotConfig networkId resumePoint instancesState = do - env <- STM.atomically $ emptyBlockchainEnv rollbackHistory slotConfig - case mode of +startNodeClient config instancesState = do + let Config { nodeServerConfig = PABServerConfig{pscSocketPath = socket, pscSlotConfig = slotConfig, pscNodeMode, pscNetworkId = NetworkIdWrapper networkId} + , developmentOptions = DevelopmentOptions{pabRollbackHistory, pabResumeFrom = resumePoint} } = config + params <- Params.fromPABServerConfig $ nodeServerConfig config + env <- STM.atomically $ emptyBlockchainEnv pabRollbackHistory params + case pscNodeMode of MockNode -> do void $ MockClient.runChainSync socket slotConfig (\block slot -> handleSyncAction $ processMockBlock instancesState env block slot) diff --git a/plutus-pab/src/Plutus/PAB/Core/ContractInstance/STM.hs b/plutus-pab/src/Plutus/PAB/Core/ContractInstance/STM.hs index fd64967af4..525b071f59 100644 --- a/plutus-pab/src/Plutus/PAB/Core/ContractInstance/STM.hs +++ b/plutus-pab/src/Plutus/PAB/Core/ContractInstance/STM.hs @@ -59,7 +59,7 @@ import Data.List.NonEmpty (NonEmpty) import Data.Map (Map) import Data.Map qualified as Map import Data.Set (Set) -import Ledger (Address, Slot, TxId, TxOutRef) +import Ledger (Address, Params (pSlotConfig), Slot, TxId, TxOutRef) import Ledger.Time (POSIXTime) import Ledger.TimeSlot qualified as TimeSlot import Plutus.ChainIndex (BlockNumber (BlockNumber), ChainIndexTx, TxIdState, TxOutBalance, TxOutStatus, TxStatus, @@ -153,18 +153,18 @@ data BlockchainEnv = , beTxChanges :: TVar (UtxoIndex TxIdState) -- ^ Map holding metadata which determines the status of transactions. , beTxOutChanges :: TVar (UtxoIndex TxOutBalance) -- ^ Map holding metadata which determines the status of transaction outputs. , beCurrentBlock :: TVar BlockNumber -- ^ Current block. - , beSlotConfig :: TimeSlot.SlotConfig + , beParams :: Params -- ^ The set of parameters, like protocol parameters and slot configuration. } -- | Initialise an empty 'BlockchainEnv' value -emptyBlockchainEnv :: Maybe Int -> TimeSlot.SlotConfig -> STM BlockchainEnv -emptyBlockchainEnv rollbackHistory slotConfig = +emptyBlockchainEnv :: Maybe Int -> Params -> STM BlockchainEnv +emptyBlockchainEnv rollbackHistory params = BlockchainEnv rollbackHistory <$> STM.newTVar 0 <*> STM.newTVar mempty <*> STM.newTVar mempty <*> STM.newTVar (BlockNumber 0) - <*> pure slotConfig + <*> pure params -- | Wait until the current slot is greater than or equal to the -- target slot, then return the current slot. @@ -177,9 +177,10 @@ awaitSlot targetSlot BlockchainEnv{beCurrentSlot} = do -- | Wait until the current time is greater than or equal to the -- target time, then return the current time. awaitTime :: POSIXTime -> BlockchainEnv -> STM POSIXTime -awaitTime targetTime be@BlockchainEnv{beSlotConfig} = do - let targetSlot = TimeSlot.posixTimeToEnclosingSlot beSlotConfig targetTime - TimeSlot.slotToEndPOSIXTime beSlotConfig <$> awaitSlot targetSlot be +awaitTime targetTime be@BlockchainEnv{beParams} = do + let slotConfig = pSlotConfig beParams + let targetSlot = TimeSlot.posixTimeToEnclosingSlot slotConfig targetTime + TimeSlot.slotToEndPOSIXTime slotConfig <$> awaitSlot targetSlot be -- | Wait for an endpoint response. awaitEndpointResponse :: Request ActiveEndpoint -> InstanceState -> STM (EndpointValue Value) diff --git a/plutus-pab/src/Plutus/PAB/Run/Cli.hs b/plutus-pab/src/Plutus/PAB/Run/Cli.hs index 384f45aed4..0c3247da2d 100644 --- a/plutus-pab/src/Plutus/PAB/Run/Cli.hs +++ b/plutus-pab/src/Plutus/PAB/Run/Cli.hs @@ -21,10 +21,11 @@ module Plutus.PAB.Run.Cli (ConfigCommandArgs(..), runConfigCommand) where ----------------------------------------------------------------------------------------------------------------------- import Cardano.Api qualified as C -import Cardano.Api.NetworkId.Extra (unNetworkIdWrapper) +import Cardano.Api.NetworkId.Extra (NetworkIdWrapper (..)) import Cardano.BM.Configuration (Configuration) import Cardano.BM.Data.Trace (Trace) import Cardano.ChainIndex.Server qualified as ChainIndex +import Cardano.Node.Params qualified as Params import Cardano.Node.Server qualified as NodeServer import Cardano.Node.Types (NodeMode (AlonzoNode, MockNode), PABServerConfig (pscNetworkId, pscNodeMode, pscSlotConfig, pscSocketPath), _AlonzoNode) @@ -108,12 +109,13 @@ runConfigCommand _ ConfigCommandArgs{ccaTrace, ccaPABConfig=Config{dbConfig}} Mi App.migrate (toPABMsg ccaTrace) dbConfig -- Run mock wallet service -runConfigCommand _ ConfigCommandArgs{ccaTrace, ccaPABConfig = Config {nodeServerConfig, chainIndexConfig, walletServerConfig = LocalWalletConfig ws},ccaAvailability} MockWallet = +runConfigCommand _ ConfigCommandArgs{ccaTrace, ccaPABConfig = Config {nodeServerConfig, chainIndexConfig, walletServerConfig = LocalWalletConfig ws},ccaAvailability} MockWallet = do + params <- liftIO $ Params.fromPABServerConfig nodeServerConfig liftIO $ WalletServer.main (toWalletLog ccaTrace) ws (pscSocketPath nodeServerConfig) - (pscSlotConfig nodeServerConfig) + params (ChainIndex.ciBaseUrl chainIndexConfig) ccaAvailability diff --git a/plutus-pab/src/Plutus/PAB/Simulator.hs b/plutus-pab/src/Plutus/PAB/Simulator.hs index 966f995888..5897716ecf 100644 --- a/plutus-pab/src/Plutus/PAB/Simulator.hs +++ b/plutus-pab/src/Plutus/PAB/Simulator.hs @@ -98,8 +98,8 @@ import Data.Text (Text) import Data.Text qualified as Text import Data.Text.IO qualified as Text import Data.Time.Units (Millisecond) -import Ledger (Address, Blockchain, CardanoTx, PaymentPubKeyHash, TxId, TxOut (TxOut, txOutAddress, txOutValue), - eitherTx, getCardanoTxFee, getCardanoTxId, txId) +import Ledger (Address, Blockchain, CardanoTx, Params (..), PaymentPubKeyHash, TxId, + TxOut (TxOut, txOutAddress, txOutValue), eitherTx, getCardanoTxFee, getCardanoTxId, txId) import Ledger.Ada qualified as Ada import Ledger.CardanoWallet (MockWallet) import Ledger.CardanoWallet qualified as CW @@ -113,7 +113,7 @@ import Plutus.ChainIndex.Emulator qualified as ChainIndex import Plutus.PAB.Core (EffectHandlers (EffectHandlers, handleContractDefinitionEffect, handleContractEffect, handleContractStoreEffect, handleLogMessages, handleServicesEffects, initialiseEnvironment, onShutdown, onStartup)) import Plutus.PAB.Core qualified as Core import Plutus.PAB.Core.ContractInstance.BlockchainEnv qualified as BlockchainEnv -import Plutus.PAB.Core.ContractInstance.STM (Activity, BlockchainEnv, OpenEndpoint) +import Plutus.PAB.Core.ContractInstance.STM (Activity, BlockchainEnv (beParams), OpenEndpoint) import Plutus.PAB.Core.ContractInstance.STM qualified as Instances import Plutus.PAB.Effects.Contract (ContractStore) import Plutus.PAB.Effects.Contract qualified as Contract @@ -127,7 +127,7 @@ import Plutus.V1.Ledger.Tx (TxOutRef) import Prettyprinter (Pretty (pretty), defaultLayoutOptions, layoutPretty) import Prettyprinter.Render.Text qualified as Render import Wallet.API qualified as WAPI -import Wallet.Effects (NodeClientEffect (GetClientSlot, GetClientSlotConfig, PublishTx), WalletEffect) +import Wallet.Effects (NodeClientEffect (GetClientParams, GetClientSlot, PublishTx), WalletEffect) import Wallet.Emulator qualified as Emulator import Wallet.Emulator.Chain (ChainControlEffect, ChainState) import Wallet.Emulator.Chain qualified as Chain @@ -204,21 +204,21 @@ mkSimulatorHandlers :: ( Pretty (Contract.ContractDef t) , HasDefinitions (Contract.ContractDef t) ) - => SlotConfig + => Params -> SimulatorContractHandler t -- ^ Making calls to the contract (see 'Plutus.PAB.Effects.Contract.ContractTest.handleContractTest' for an example) -> SimulatorEffectHandlers t -mkSimulatorHandlers slotCfg handleContractEffect = +mkSimulatorHandlers params handleContractEffect = EffectHandlers { initialiseEnvironment = (,,) <$> liftIO (STM.atomically Instances.emptyInstancesState ) - <*> liftIO (STM.atomically $ Instances.emptyBlockchainEnv Nothing def) + <*> liftIO (STM.atomically $ Instances.emptyBlockchainEnv Nothing params) <*> liftIO (initialState @t) , handleContractStoreEffect = interpret handleContractStore , handleContractEffect , handleLogMessages = handleLogSimulator @t - , handleServicesEffects = handleServicesSimulator @t slotCfg + , handleServicesEffects = handleServicesSimulator @t params , handleContractDefinitionEffect = interpret $ \case Contract.AddDefinition _ -> pure () -- not supported @@ -236,7 +236,7 @@ mkSimulatorHandlers slotCfg handleContractEffect = $ interpret (Core.handleUserEnvReader @t @(SimulatorState t)) $ interpret (Core.handleInstancesStateReader @t @(SimulatorState t)) $ interpret (Core.handleBlockchainEnvReader @t @(SimulatorState t)) - $ advanceClock @t slotCfg + $ advanceClock @t Core.waitUntilSlot 1 , onShutdown = do handleDelayEffect $ delayThread (500 :: Millisecond) -- need to wait a little to avoid garbled terminal output in GHCi. @@ -260,12 +260,12 @@ handleServicesSimulator :: , LastMember IO effs , Member (Error PABError) effs ) - => SlotConfig + => Params -> Wallet -> Maybe ContractInstanceId -> Eff (WalletEffect ': ChainIndexQueryEffect ': NodeClientEffect ': effs) ~> Eff effs -handleServicesSimulator slotCfg wallet _ = +handleServicesSimulator params wallet _ = let makeTimedChainIndexEvent wllt = interpret (mapLog @_ @(PABMultiAgentMsg t) EmulatorMsg) . reinterpret (Core.timed @EmulatorEvent') @@ -280,10 +280,10 @@ handleServicesSimulator slotCfg wallet _ = makeTimedChainEvent . interpret (Core.handleBlockchainEnvReader @t @(SimulatorState t)) . interpret (Core.handleUserEnvReader @t @(SimulatorState t)) - . reinterpretN @'[Reader (SimulatorState t), Reader BlockchainEnv, LogMsg _] (handleChainEffect @t slotCfg) + . reinterpretN @'[Reader (SimulatorState t), Reader BlockchainEnv, LogMsg _] (handleChainEffect @t params) . interpret (Core.handleUserEnvReader @t @(SimulatorState t)) - . reinterpret2 (handleNodeClient @t slotCfg wallet) + . reinterpret2 (handleNodeClient @t params wallet) -- handle 'ChainIndexQueryEffect' . makeTimedChainIndexEvent wallet @@ -351,10 +351,11 @@ makeBlock :: , Member DelayEffect effs , Member TimeEffect effs ) - => SlotConfig - -> Eff effs () -makeBlock slotCfg@SlotConfig { scSlotLength } = do - let makeTimedChainEvent = + => Eff effs () +makeBlock = do + env <- ask @BlockchainEnv + let Params { pSlotConfig = SlotConfig { scSlotLength } } = beParams env + makeTimedChainEvent = interpret (logIntoTQueue @_ @(SimulatorState t) (view logMessages)) . reinterpret (mapLog @_ @(PABMultiAgentMsg t) EmulatorMsg) . reinterpret (Core.timed @EmulatorEvent') @@ -368,7 +369,7 @@ makeBlock slotCfg@SlotConfig { scSlotLength } = do void $ makeTimedChainEvent $ makeTimedChainIndexEvent - $ interpret (handleChainControl @t slotCfg) + $ interpret (handleChainControl @t) $ Chain.processBlock >> Chain.modifySlot succ -- | Get the current state of the contract instance. @@ -447,24 +448,25 @@ handleChainControl :: , Member (LogMsg Chain.ChainEvent) effs , Member (LogMsg ChainIndexLog) effs ) - => SlotConfig - -> ChainControlEffect + => ChainControlEffect ~> Eff effs -handleChainControl slotCfg = \case - Chain.ProcessBlock -> do - blockchainEnv <- ask @BlockchainEnv - instancesState <- ask @Instances.InstancesState - (txns, slot) <- runChainEffects @t @_ slotCfg ((,) <$> Chain.processBlock <*> Chain.getCurrentSlot) +handleChainControl eff = do + blockchainEnv <- ask @BlockchainEnv + let params = beParams blockchainEnv + case eff of + Chain.ProcessBlock -> do + instancesState <- ask @Instances.InstancesState + (txns, slot) <- runChainEffects @t @_ params ((,) <$> Chain.processBlock <*> Chain.getCurrentSlot) - -- Adds a new tip on the chain index given the block and slot number - runChainIndexEffects @t $ do - currentTip <- getTip - appendNewTipBlock currentTip txns slot + -- Adds a new tip on the chain index given the block and slot number + runChainIndexEffects @t $ do + currentTip <- getTip + appendNewTipBlock currentTip txns slot - void $ liftIO $ STM.atomically $ BlockchainEnv.processMockBlock instancesState blockchainEnv txns slot + void $ liftIO $ STM.atomically $ BlockchainEnv.processMockBlock instancesState blockchainEnv txns slot - pure txns - Chain.ModifySlot f -> runChainEffects @t @_ slotCfg (Chain.modifySlot f) + pure txns + Chain.ModifySlot f -> runChainEffects @t @_ params (Chain.modifySlot f) runChainEffects :: forall t a effs. @@ -472,10 +474,10 @@ runChainEffects :: , Member (LogMsg Chain.ChainEvent) effs , LastMember IO effs ) - => SlotConfig + => Params -> Eff (Chain.ChainEffect ': Chain.ChainControlEffect ': Chain.ChainEffs) a -> Eff effs a -runChainEffects slotCfg action = do +runChainEffects params action = do SimulatorState{_chainState} <- ask @(SimulatorState t) (a, logs) <- liftIO $ STM.atomically $ do oldState <- STM.readTVar _chainState @@ -484,8 +486,8 @@ runChainEffects slotCfg action = do $ runWriter @[LogMessage Chain.ChainEvent] $ reinterpret @(LogMsg Chain.ChainEvent) @(Writer [LogMessage Chain.ChainEvent]) (handleLogWriter _singleton) $ runState oldState - $ interpret (Chain.handleControlChain slotCfg) - $ interpret (Chain.handleChain slotCfg) action + $ interpret (Chain.handleControlChain params) + $ interpret (Chain.handleChain params) action STM.writeTVar _chainState newState pure (a, logs) traverse_ (send . LMessage) logs @@ -527,11 +529,11 @@ handleNodeClient :: , Member Chain.ChainEffect effs , Member (Reader (SimulatorState t)) effs ) - => SlotConfig + => Params -> Wallet -> NodeClientEffect ~> Eff effs -handleNodeClient slotCfg wallet = \case +handleNodeClient params wallet = \case PublishTx tx -> do Chain.queueTx tx SimulatorState{_agentStates} <- ask @(SimulatorState t) @@ -545,7 +547,7 @@ handleNodeClient slotCfg wallet = \case let newState = s' & submittedFees . at (getCardanoTxId tx) ?~ getCardanoTxFee tx STM.writeTVar _agentStates (Map.insert wallet newState mp) GetClientSlot -> Chain.getCurrentSlot - GetClientSlotConfig -> pure slotCfg + GetClientParams -> pure params -- | Handle the 'Chain.ChainEffect' using the 'SimulatorState'. handleChainEffect :: @@ -554,13 +556,13 @@ handleChainEffect :: , Member (Reader (SimulatorState t)) effs , Member (LogMsg Chain.ChainEvent) effs ) - => SlotConfig + => Params -> Chain.ChainEffect ~> Eff effs -handleChainEffect slotCfg = \case - Chain.QueueTx tx -> runChainEffects @t slotCfg $ Chain.queueTx tx - Chain.GetCurrentSlot -> runChainEffects @t slotCfg Chain.getCurrentSlot - Chain.GetSlotConfig -> pure slotCfg +handleChainEffect params = \case + Chain.QueueTx tx -> runChainEffects @t params $ Chain.queueTx tx + Chain.GetCurrentSlot -> runChainEffects @t params Chain.getCurrentSlot + Chain.GetParams -> pure params handleChainIndexEffect :: forall t effs. @@ -606,9 +608,8 @@ advanceClock :: , Member DelayEffect effs , Member TimeEffect effs ) - => SlotConfig - -> Eff effs () -advanceClock slotCfg = forever (makeBlock @t slotCfg) + => Eff effs () +advanceClock = forever (makeBlock @t) -- | Handle the 'ContractStore' effect by writing the state to the -- TVar in 'SimulatorState' @@ -749,9 +750,10 @@ addWalletWith funds = do currentWallets <- STM.readTVar _agentStates let newWallets = currentWallets & at (Wallet.toMockWallet mockWallet) ?~ AgentState (Wallet.fromMockWallet mockWallet) mempty STM.writeTVar _agentStates newWallets + Instances.BlockchainEnv{beParams} <- Core.askBlockchainEnv @t @(SimulatorState t) _ <- handleAgentThread (knownWallet 2) Nothing $ Modify.wrapError WalletError - $ MockWallet.distributeNewWalletFunds funds (CW.paymentPubKeyHash mockWallet) + $ MockWallet.distributeNewWalletFunds beParams funds (CW.paymentPubKeyHash mockWallet) pure (Wallet.toMockWallet mockWallet, CW.paymentPubKeyHash mockWallet) -- | Retrieve the balances of all the entities in the simulator. @@ -782,8 +784,9 @@ payToWallet :: forall t. Wallet -> Wallet -> Value -> Simulation t CardanoTx payToWallet source target = payToPaymentPublicKeyHash source (Emulator.mockWalletPaymentPubKeyHash target) -- | Make a payment from one wallet to a public key address -payToPaymentPublicKeyHash :: forall t. Wallet -> PaymentPubKeyHash -> Value -> Simulation t CardanoTx -payToPaymentPublicKeyHash source target amount = +payToPaymentPublicKeyHash :: forall t. Wallet -> PaymentPubKeyHash -> Value -> Simulation t CardanoTx +payToPaymentPublicKeyHash source target amount = do + Instances.BlockchainEnv{beParams} <- Core.askBlockchainEnv @t @(SimulatorState t) handleAgentThread source Nothing $ flip (handleError @WAPI.WalletAPIError) (throwError . WalletError) - $ WAPI.payToPaymentPublicKeyHash WAPI.defaultSlotRange amount target + $ WAPI.payToPaymentPublicKeyHash beParams WAPI.defaultSlotRange amount target diff --git a/plutus-pab/test/light/Cardano/Wallet/RemoteClientSpec.hs b/plutus-pab/test/light/Cardano/Wallet/RemoteClientSpec.hs index 11993d3fd9..2f16519093 100644 --- a/plutus-pab/test/light/Cardano/Wallet/RemoteClientSpec.hs +++ b/plutus-pab/test/light/Cardano/Wallet/RemoteClientSpec.hs @@ -10,7 +10,6 @@ module Cardano.Wallet.RemoteClientSpec ( tests ) where -import Cardano.Api.ProtocolParameters (ProtocolParameters) import Cardano.Wallet.RemoteClient (handleWalletClient) import Control.Concurrent.STM qualified as STM import Control.Monad.Freer (Eff, interpret, runM, type (~>)) @@ -22,7 +21,7 @@ import Data.List qualified as List import Gen.Cardano.Api.Typed qualified as Gen import Hedgehog (Property, (===)) import Hedgehog qualified -import Ledger (Slot) +import Ledger (Params (..), Slot) import Ledger.Constraints.OffChain (emptyUnbalancedTx) import Ledger.Generators qualified as Gen import Ledger.TimeSlot (SlotConfig) @@ -32,8 +31,7 @@ import Plutus.PAB.Core.ContractInstance.STM (InstancesState, emptyInstanceState, import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary) import Test.Tasty (TestTree, testGroup) import Test.Tasty.Hedgehog (testProperty) -import Wallet.Effects (NodeClientEffect (GetClientSlot, GetClientSlotConfig, PublishTx), - WalletEffect (YieldUnbalancedTx)) +import Wallet.Effects (NodeClientEffect (GetClientParams, GetClientSlot, PublishTx), WalletEffect (YieldUnbalancedTx)) import Wallet.Emulator.Error (WalletAPIError (OtherError)) import Wallet.Types (ContractInstanceId, randomID) @@ -51,6 +49,7 @@ yieldToInstanceState :: Property yieldToInstanceState = Hedgehog.property $ do pp <- Hedgehog.forAll Gen.genProtocolParameters sc <- Hedgehog.forAll Gen.genSlotConfig + let params = def { pProtocolParams = pp, pSlotConfig = sc } sl <- Hedgehog.forAll Gen.genSlot cid <- liftIO randomID @@ -61,7 +60,8 @@ yieldToInstanceState = Hedgehog.property $ do is <- emptyInstanceState insertInstance cid is iss pure iss - yieldedRes <- runRemoteWalletEffects pp sc sl iss (Just cid) (YieldUnbalancedTx utx) + + yieldedRes <- runRemoteWalletEffects params sl iss (Just cid) (YieldUnbalancedTx utx) pure $ fmap (,iss) yieldedRes case result of @@ -75,34 +75,34 @@ yieldNoCid :: Property yieldNoCid = Hedgehog.property $ do pp <- Hedgehog.forAll Gen.genProtocolParameters sc <- Hedgehog.forAll Gen.genSlotConfig + let params = def { pProtocolParams = pp, pSlotConfig = sc } sl <- Hedgehog.forAll Gen.genSlot result <- liftIO $ do iss <- STM.atomically emptyInstancesState - runRemoteWalletEffects pp sc sl iss Nothing (YieldUnbalancedTx emptyUnbalancedTx) + runRemoteWalletEffects params sl iss Nothing (YieldUnbalancedTx emptyUnbalancedTx) case result of Left (OtherError _) -> Hedgehog.assert True _ -> Hedgehog.assert False -- | Run the wallet effects in a remote wallet scenario. runRemoteWalletEffects - :: ProtocolParameters - -> SlotConfig + :: Params -> Slot -> InstancesState -> Maybe ContractInstanceId -> WalletEffect () -> IO (Either WalletAPIError ()) -runRemoteWalletEffects protocolParams slotConfig slot is cidM action = do +runRemoteWalletEffects params slot is cidM action = do runM $ runError @WalletAPIError - $ runReader protocolParams + $ runReader (pProtocolParams params) $ runReader is - $ interpret (handleNodeClient slotConfig slot) + $ interpret (handleNodeClient params slot) $ handleWalletClient def cidM action -- | Handle NodeClientEffect for testing purposes. -handleNodeClient :: SlotConfig -> Slot -> NodeClientEffect ~> Eff effs -handleNodeClient slotConfig slot = pure . \case - PublishTx _ -> () -- Do nothing - GetClientSlot -> slot - GetClientSlotConfig -> slotConfig +handleNodeClient :: Params -> Slot -> NodeClientEffect ~> Eff effs +handleNodeClient params slot = pure . \case + PublishTx _ -> () -- Do nothing + GetClientSlot -> slot + GetClientParams -> params diff --git a/plutus-playground-client/generated/Ledger/Index.purs b/plutus-playground-client/generated/Ledger/Index.purs index aec160adfd..c4d25476af 100644 --- a/plutus-playground-client/generated/Ledger/Index.purs +++ b/plutus-playground-client/generated/Ledger/Index.purs @@ -9,28 +9,18 @@ import Data.Argonaut.Decode (class DecodeJson) import Data.Argonaut.Decode.Aeson ((), (), ()) import Data.Argonaut.Encode (class EncodeJson) import Data.Argonaut.Encode.Aeson ((>$<), (>/\<)) -import Data.Bounded.Generic (genericBottom, genericTop) import Data.Either (Either) -import Data.Enum (class Enum) -import Data.Enum.Generic (genericPred, genericSucc) import Data.Generic.Rep (class Generic) import Data.Lens (Iso', Lens', Prism', iso, prism') import Data.Lens.Iso.Newtype (_Newtype) import Data.Lens.Record (prop) -import Data.Map (Map) import Data.Maybe (Maybe(..)) -import Data.Newtype (class Newtype, unwrap) +import Data.Newtype (unwrap) import Data.RawJson (RawJson) import Data.Show.Generic (genericShow) import Data.Tuple (Tuple) import Data.Tuple.Nested ((/\)) -import Ledger.Crypto (PubKey, Signature) -import Ledger.Slot (Slot) -import Ledger.Tx.Internal (Tx) -import Plutus.V1.Ledger.Crypto (PubKeyHash) -import Plutus.V1.Ledger.Scripts (DatumHash, MintingPolicy, ScriptError, Validator) -import Plutus.V1.Ledger.Tx (RedeemerPtr, TxIn, TxOut, TxOutRef) -import Plutus.V1.Ledger.Value (Value) +import Plutus.V1.Ledger.Scripts (MintingPolicy, ScriptError, Validator) import Type.Proxy (Proxy(Proxy)) import Data.Argonaut.Decode.Aeson as D import Data.Argonaut.Encode.Aeson as E @@ -130,221 +120,3 @@ _ScriptValidationResultOnlyEvent :: Prism' ScriptValidationEvent { sveResult :: _ScriptValidationResultOnlyEvent = prism' ScriptValidationResultOnlyEvent case _ of (ScriptValidationResultOnlyEvent a) -> Just a _ -> Nothing - --------------------------------------------------------------------------------- - -newtype UtxoIndex = UtxoIndex { getIndex :: Map TxOutRef TxOut } - -derive instance Eq UtxoIndex - -instance Show UtxoIndex where - show a = genericShow a - -instance EncodeJson UtxoIndex where - encodeJson = defer \_ -> E.encode $ unwrap >$< - ( E.record - { getIndex: (E.dictionary E.value E.value) :: _ (Map TxOutRef TxOut) } - ) - -instance DecodeJson UtxoIndex where - decodeJson = defer \_ -> D.decode $ (UtxoIndex <$> D.record "UtxoIndex" { getIndex: (D.dictionary D.value D.value) :: _ (Map TxOutRef TxOut) }) - -derive instance Generic UtxoIndex _ - -derive instance Newtype UtxoIndex _ - --------------------------------------------------------------------------------- - -_UtxoIndex :: Iso' UtxoIndex { getIndex :: Map TxOutRef TxOut } -_UtxoIndex = _Newtype - --------------------------------------------------------------------------------- - -data ValidationError - = InOutTypeMismatch TxIn TxOut - | TxOutRefNotFound TxOutRef - | InvalidScriptHash Validator String - | InvalidDatumHash String DatumHash - | MissingRedeemer RedeemerPtr - | InvalidSignature PubKey Signature - | ValueNotPreserved Value Value - | NegativeValue Tx - | ValueContainsLessThanMinAda Tx TxOut - | NonAdaFees Tx - | ScriptFailure ScriptError - | CurrentSlotOutOfRange Slot - | SignatureMissing PubKeyHash - | MintWithoutScript String - | TransactionFeeTooLow Value Value - | CardanoLedgerValidationError String - -derive instance Eq ValidationError - -instance Show ValidationError where - show a = genericShow a - -instance EncodeJson ValidationError where - encodeJson = defer \_ -> case _ of - InOutTypeMismatch a b -> E.encodeTagged "InOutTypeMismatch" (a /\ b) (E.tuple (E.value >/\< E.value)) - TxOutRefNotFound a -> E.encodeTagged "TxOutRefNotFound" a E.value - InvalidScriptHash a b -> E.encodeTagged "InvalidScriptHash" (a /\ b) (E.tuple (E.value >/\< E.value)) - InvalidDatumHash a b -> E.encodeTagged "InvalidDatumHash" (a /\ b) (E.tuple (E.value >/\< E.value)) - MissingRedeemer a -> E.encodeTagged "MissingRedeemer" a E.value - InvalidSignature a b -> E.encodeTagged "InvalidSignature" (a /\ b) (E.tuple (E.value >/\< E.value)) - ValueNotPreserved a b -> E.encodeTagged "ValueNotPreserved" (a /\ b) (E.tuple (E.value >/\< E.value)) - NegativeValue a -> E.encodeTagged "NegativeValue" a E.value - ValueContainsLessThanMinAda a b -> E.encodeTagged "ValueContainsLessThanMinAda" (a /\ b) (E.tuple (E.value >/\< E.value)) - NonAdaFees a -> E.encodeTagged "NonAdaFees" a E.value - ScriptFailure a -> E.encodeTagged "ScriptFailure" a E.value - CurrentSlotOutOfRange a -> E.encodeTagged "CurrentSlotOutOfRange" a E.value - SignatureMissing a -> E.encodeTagged "SignatureMissing" a E.value - MintWithoutScript a -> E.encodeTagged "MintWithoutScript" a E.value - TransactionFeeTooLow a b -> E.encodeTagged "TransactionFeeTooLow" (a /\ b) (E.tuple (E.value >/\< E.value)) - CardanoLedgerValidationError a -> E.encodeTagged "CardanoLedgerValidationError" a E.value - -instance DecodeJson ValidationError where - decodeJson = defer \_ -> D.decode - $ D.sumType "ValidationError" - $ Map.fromFoldable - [ "InOutTypeMismatch" /\ D.content (D.tuple $ InOutTypeMismatch D.value D.value) - , "TxOutRefNotFound" /\ D.content (TxOutRefNotFound <$> D.value) - , "InvalidScriptHash" /\ D.content (D.tuple $ InvalidScriptHash D.value D.value) - , "InvalidDatumHash" /\ D.content (D.tuple $ InvalidDatumHash D.value D.value) - , "MissingRedeemer" /\ D.content (MissingRedeemer <$> D.value) - , "InvalidSignature" /\ D.content (D.tuple $ InvalidSignature D.value D.value) - , "ValueNotPreserved" /\ D.content (D.tuple $ ValueNotPreserved D.value D.value) - , "NegativeValue" /\ D.content (NegativeValue <$> D.value) - , "ValueContainsLessThanMinAda" /\ D.content (D.tuple $ ValueContainsLessThanMinAda D.value D.value) - , "NonAdaFees" /\ D.content (NonAdaFees <$> D.value) - , "ScriptFailure" /\ D.content (ScriptFailure <$> D.value) - , "CurrentSlotOutOfRange" /\ D.content (CurrentSlotOutOfRange <$> D.value) - , "SignatureMissing" /\ D.content (SignatureMissing <$> D.value) - , "MintWithoutScript" /\ D.content (MintWithoutScript <$> D.value) - , "TransactionFeeTooLow" /\ D.content (D.tuple $ TransactionFeeTooLow D.value D.value) - , "CardanoLedgerValidationError" /\ D.content (CardanoLedgerValidationError <$> D.value) - ] - -derive instance Generic ValidationError _ - --------------------------------------------------------------------------------- - -_InOutTypeMismatch :: Prism' ValidationError { a :: TxIn, b :: TxOut } -_InOutTypeMismatch = prism' (\{ a, b } -> (InOutTypeMismatch a b)) case _ of - (InOutTypeMismatch a b) -> Just { a, b } - _ -> Nothing - -_TxOutRefNotFound :: Prism' ValidationError TxOutRef -_TxOutRefNotFound = prism' TxOutRefNotFound case _ of - (TxOutRefNotFound a) -> Just a - _ -> Nothing - -_InvalidScriptHash :: Prism' ValidationError { a :: Validator, b :: String } -_InvalidScriptHash = prism' (\{ a, b } -> (InvalidScriptHash a b)) case _ of - (InvalidScriptHash a b) -> Just { a, b } - _ -> Nothing - -_InvalidDatumHash :: Prism' ValidationError { a :: String, b :: DatumHash } -_InvalidDatumHash = prism' (\{ a, b } -> (InvalidDatumHash a b)) case _ of - (InvalidDatumHash a b) -> Just { a, b } - _ -> Nothing - -_MissingRedeemer :: Prism' ValidationError RedeemerPtr -_MissingRedeemer = prism' MissingRedeemer case _ of - (MissingRedeemer a) -> Just a - _ -> Nothing - -_InvalidSignature :: Prism' ValidationError { a :: PubKey, b :: Signature } -_InvalidSignature = prism' (\{ a, b } -> (InvalidSignature a b)) case _ of - (InvalidSignature a b) -> Just { a, b } - _ -> Nothing - -_ValueNotPreserved :: Prism' ValidationError { a :: Value, b :: Value } -_ValueNotPreserved = prism' (\{ a, b } -> (ValueNotPreserved a b)) case _ of - (ValueNotPreserved a b) -> Just { a, b } - _ -> Nothing - -_NegativeValue :: Prism' ValidationError Tx -_NegativeValue = prism' NegativeValue case _ of - (NegativeValue a) -> Just a - _ -> Nothing - -_ValueContainsLessThanMinAda :: Prism' ValidationError { a :: Tx, b :: TxOut } -_ValueContainsLessThanMinAda = prism' (\{ a, b } -> (ValueContainsLessThanMinAda a b)) case _ of - (ValueContainsLessThanMinAda a b) -> Just { a, b } - _ -> Nothing - -_NonAdaFees :: Prism' ValidationError Tx -_NonAdaFees = prism' NonAdaFees case _ of - (NonAdaFees a) -> Just a - _ -> Nothing - -_ScriptFailure :: Prism' ValidationError ScriptError -_ScriptFailure = prism' ScriptFailure case _ of - (ScriptFailure a) -> Just a - _ -> Nothing - -_CurrentSlotOutOfRange :: Prism' ValidationError Slot -_CurrentSlotOutOfRange = prism' CurrentSlotOutOfRange case _ of - (CurrentSlotOutOfRange a) -> Just a - _ -> Nothing - -_SignatureMissing :: Prism' ValidationError PubKeyHash -_SignatureMissing = prism' SignatureMissing case _ of - (SignatureMissing a) -> Just a - _ -> Nothing - -_MintWithoutScript :: Prism' ValidationError String -_MintWithoutScript = prism' MintWithoutScript case _ of - (MintWithoutScript a) -> Just a - _ -> Nothing - -_TransactionFeeTooLow :: Prism' ValidationError { a :: Value, b :: Value } -_TransactionFeeTooLow = prism' (\{ a, b } -> (TransactionFeeTooLow a b)) case _ of - (TransactionFeeTooLow a b) -> Just { a, b } - _ -> Nothing - -_CardanoLedgerValidationError :: Prism' ValidationError String -_CardanoLedgerValidationError = prism' CardanoLedgerValidationError case _ of - (CardanoLedgerValidationError a) -> Just a - _ -> Nothing - --------------------------------------------------------------------------------- - -data ValidationPhase - = Phase1 - | Phase2 - -derive instance Eq ValidationPhase - -derive instance Ord ValidationPhase - -instance Show ValidationPhase where - show a = genericShow a - -instance EncodeJson ValidationPhase where - encodeJson = defer \_ -> E.encode E.enum - -instance DecodeJson ValidationPhase where - decodeJson = defer \_ -> D.decode D.enum - -derive instance Generic ValidationPhase _ - -instance Enum ValidationPhase where - succ = genericSucc - pred = genericPred - -instance Bounded ValidationPhase where - bottom = genericBottom - top = genericTop - --------------------------------------------------------------------------------- - -_Phase1 :: Prism' ValidationPhase Unit -_Phase1 = prism' (const Phase1) case _ of - Phase1 -> Just unit - _ -> Nothing - -_Phase2 :: Prism' ValidationPhase Unit -_Phase2 = prism' (const Phase2) case _ of - Phase2 -> Just unit - _ -> Nothing diff --git a/plutus-playground-client/generated/Ledger/Index/Internal.purs b/plutus-playground-client/generated/Ledger/Index/Internal.purs new file mode 100644 index 0000000000..4066e91cbe --- /dev/null +++ b/plutus-playground-client/generated/Ledger/Index/Internal.purs @@ -0,0 +1,250 @@ +-- File auto generated by purescript-bridge! -- +module Ledger.Index.Internal where + +import Prelude + +import Control.Lazy (defer) +import Data.Argonaut (encodeJson, jsonNull) +import Data.Argonaut.Decode (class DecodeJson) +import Data.Argonaut.Decode.Aeson ((), (), ()) +import Data.Argonaut.Encode (class EncodeJson) +import Data.Argonaut.Encode.Aeson ((>$<), (>/\<)) +import Data.Bounded.Generic (genericBottom, genericTop) +import Data.Enum (class Enum) +import Data.Enum.Generic (genericPred, genericSucc) +import Data.Generic.Rep (class Generic) +import Data.Lens (Iso', Lens', Prism', iso, prism') +import Data.Lens.Iso.Newtype (_Newtype) +import Data.Lens.Record (prop) +import Data.Map (Map) +import Data.Maybe (Maybe(..)) +import Data.Newtype (class Newtype, unwrap) +import Data.Show.Generic (genericShow) +import Data.Tuple.Nested ((/\)) +import Ledger.Crypto (PubKey, Signature) +import Ledger.Slot (Slot) +import Ledger.Tx.Internal (Tx) +import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Plutus.V1.Ledger.Scripts (DatumHash, ScriptError, Validator) +import Plutus.V1.Ledger.Tx (RedeemerPtr, TxIn, TxOut, TxOutRef) +import Plutus.V1.Ledger.Value (Value) +import Type.Proxy (Proxy(Proxy)) +import Data.Argonaut.Decode.Aeson as D +import Data.Argonaut.Encode.Aeson as E +import Data.Map as Map + +newtype UtxoIndex = UtxoIndex { getIndex :: Map TxOutRef TxOut } + +derive instance Eq UtxoIndex + +instance Show UtxoIndex where + show a = genericShow a + +instance EncodeJson UtxoIndex where + encodeJson = defer \_ -> E.encode $ unwrap >$< + ( E.record + { getIndex: (E.dictionary E.value E.value) :: _ (Map TxOutRef TxOut) } + ) + +instance DecodeJson UtxoIndex where + decodeJson = defer \_ -> D.decode $ (UtxoIndex <$> D.record "UtxoIndex" { getIndex: (D.dictionary D.value D.value) :: _ (Map TxOutRef TxOut) }) + +derive instance Generic UtxoIndex _ + +derive instance Newtype UtxoIndex _ + +-------------------------------------------------------------------------------- + +_UtxoIndex :: Iso' UtxoIndex { getIndex :: Map TxOutRef TxOut } +_UtxoIndex = _Newtype + +-------------------------------------------------------------------------------- + +data ValidationError + = InOutTypeMismatch TxIn TxOut + | TxOutRefNotFound TxOutRef + | InvalidScriptHash Validator String + | InvalidDatumHash String DatumHash + | MissingRedeemer RedeemerPtr + | InvalidSignature PubKey Signature + | ValueNotPreserved Value Value + | NegativeValue Tx + | ValueContainsLessThanMinAda Tx TxOut Value + | NonAdaFees Tx + | ScriptFailure ScriptError + | CurrentSlotOutOfRange Slot + | SignatureMissing PubKeyHash + | MintWithoutScript String + | TransactionFeeTooLow Value Value + | CardanoLedgerValidationError String + +derive instance Eq ValidationError + +instance Show ValidationError where + show a = genericShow a + +instance EncodeJson ValidationError where + encodeJson = defer \_ -> case _ of + InOutTypeMismatch a b -> E.encodeTagged "InOutTypeMismatch" (a /\ b) (E.tuple (E.value >/\< E.value)) + TxOutRefNotFound a -> E.encodeTagged "TxOutRefNotFound" a E.value + InvalidScriptHash a b -> E.encodeTagged "InvalidScriptHash" (a /\ b) (E.tuple (E.value >/\< E.value)) + InvalidDatumHash a b -> E.encodeTagged "InvalidDatumHash" (a /\ b) (E.tuple (E.value >/\< E.value)) + MissingRedeemer a -> E.encodeTagged "MissingRedeemer" a E.value + InvalidSignature a b -> E.encodeTagged "InvalidSignature" (a /\ b) (E.tuple (E.value >/\< E.value)) + ValueNotPreserved a b -> E.encodeTagged "ValueNotPreserved" (a /\ b) (E.tuple (E.value >/\< E.value)) + NegativeValue a -> E.encodeTagged "NegativeValue" a E.value + ValueContainsLessThanMinAda a b c -> E.encodeTagged "ValueContainsLessThanMinAda" (a /\ b /\ c) (E.tuple (E.value >/\< E.value >/\< E.value)) + NonAdaFees a -> E.encodeTagged "NonAdaFees" a E.value + ScriptFailure a -> E.encodeTagged "ScriptFailure" a E.value + CurrentSlotOutOfRange a -> E.encodeTagged "CurrentSlotOutOfRange" a E.value + SignatureMissing a -> E.encodeTagged "SignatureMissing" a E.value + MintWithoutScript a -> E.encodeTagged "MintWithoutScript" a E.value + TransactionFeeTooLow a b -> E.encodeTagged "TransactionFeeTooLow" (a /\ b) (E.tuple (E.value >/\< E.value)) + CardanoLedgerValidationError a -> E.encodeTagged "CardanoLedgerValidationError" a E.value + +instance DecodeJson ValidationError where + decodeJson = defer \_ -> D.decode + $ D.sumType "ValidationError" + $ Map.fromFoldable + [ "InOutTypeMismatch" /\ D.content (D.tuple $ InOutTypeMismatch D.value D.value) + , "TxOutRefNotFound" /\ D.content (TxOutRefNotFound <$> D.value) + , "InvalidScriptHash" /\ D.content (D.tuple $ InvalidScriptHash D.value D.value) + , "InvalidDatumHash" /\ D.content (D.tuple $ InvalidDatumHash D.value D.value) + , "MissingRedeemer" /\ D.content (MissingRedeemer <$> D.value) + , "InvalidSignature" /\ D.content (D.tuple $ InvalidSignature D.value D.value) + , "ValueNotPreserved" /\ D.content (D.tuple $ ValueNotPreserved D.value D.value) + , "NegativeValue" /\ D.content (NegativeValue <$> D.value) + , "ValueContainsLessThanMinAda" /\ D.content (D.tuple $ ValueContainsLessThanMinAda D.value D.value D.value) + , "NonAdaFees" /\ D.content (NonAdaFees <$> D.value) + , "ScriptFailure" /\ D.content (ScriptFailure <$> D.value) + , "CurrentSlotOutOfRange" /\ D.content (CurrentSlotOutOfRange <$> D.value) + , "SignatureMissing" /\ D.content (SignatureMissing <$> D.value) + , "MintWithoutScript" /\ D.content (MintWithoutScript <$> D.value) + , "TransactionFeeTooLow" /\ D.content (D.tuple $ TransactionFeeTooLow D.value D.value) + , "CardanoLedgerValidationError" /\ D.content (CardanoLedgerValidationError <$> D.value) + ] + +derive instance Generic ValidationError _ + +-------------------------------------------------------------------------------- + +_InOutTypeMismatch :: Prism' ValidationError { a :: TxIn, b :: TxOut } +_InOutTypeMismatch = prism' (\{ a, b } -> (InOutTypeMismatch a b)) case _ of + (InOutTypeMismatch a b) -> Just { a, b } + _ -> Nothing + +_TxOutRefNotFound :: Prism' ValidationError TxOutRef +_TxOutRefNotFound = prism' TxOutRefNotFound case _ of + (TxOutRefNotFound a) -> Just a + _ -> Nothing + +_InvalidScriptHash :: Prism' ValidationError { a :: Validator, b :: String } +_InvalidScriptHash = prism' (\{ a, b } -> (InvalidScriptHash a b)) case _ of + (InvalidScriptHash a b) -> Just { a, b } + _ -> Nothing + +_InvalidDatumHash :: Prism' ValidationError { a :: String, b :: DatumHash } +_InvalidDatumHash = prism' (\{ a, b } -> (InvalidDatumHash a b)) case _ of + (InvalidDatumHash a b) -> Just { a, b } + _ -> Nothing + +_MissingRedeemer :: Prism' ValidationError RedeemerPtr +_MissingRedeemer = prism' MissingRedeemer case _ of + (MissingRedeemer a) -> Just a + _ -> Nothing + +_InvalidSignature :: Prism' ValidationError { a :: PubKey, b :: Signature } +_InvalidSignature = prism' (\{ a, b } -> (InvalidSignature a b)) case _ of + (InvalidSignature a b) -> Just { a, b } + _ -> Nothing + +_ValueNotPreserved :: Prism' ValidationError { a :: Value, b :: Value } +_ValueNotPreserved = prism' (\{ a, b } -> (ValueNotPreserved a b)) case _ of + (ValueNotPreserved a b) -> Just { a, b } + _ -> Nothing + +_NegativeValue :: Prism' ValidationError Tx +_NegativeValue = prism' NegativeValue case _ of + (NegativeValue a) -> Just a + _ -> Nothing + +_ValueContainsLessThanMinAda :: Prism' ValidationError { a :: Tx, b :: TxOut, c :: Value } +_ValueContainsLessThanMinAda = prism' (\{ a, b, c } -> (ValueContainsLessThanMinAda a b c)) case _ of + (ValueContainsLessThanMinAda a b c) -> Just { a, b, c } + _ -> Nothing + +_NonAdaFees :: Prism' ValidationError Tx +_NonAdaFees = prism' NonAdaFees case _ of + (NonAdaFees a) -> Just a + _ -> Nothing + +_ScriptFailure :: Prism' ValidationError ScriptError +_ScriptFailure = prism' ScriptFailure case _ of + (ScriptFailure a) -> Just a + _ -> Nothing + +_CurrentSlotOutOfRange :: Prism' ValidationError Slot +_CurrentSlotOutOfRange = prism' CurrentSlotOutOfRange case _ of + (CurrentSlotOutOfRange a) -> Just a + _ -> Nothing + +_SignatureMissing :: Prism' ValidationError PubKeyHash +_SignatureMissing = prism' SignatureMissing case _ of + (SignatureMissing a) -> Just a + _ -> Nothing + +_MintWithoutScript :: Prism' ValidationError String +_MintWithoutScript = prism' MintWithoutScript case _ of + (MintWithoutScript a) -> Just a + _ -> Nothing + +_TransactionFeeTooLow :: Prism' ValidationError { a :: Value, b :: Value } +_TransactionFeeTooLow = prism' (\{ a, b } -> (TransactionFeeTooLow a b)) case _ of + (TransactionFeeTooLow a b) -> Just { a, b } + _ -> Nothing + +_CardanoLedgerValidationError :: Prism' ValidationError String +_CardanoLedgerValidationError = prism' CardanoLedgerValidationError case _ of + (CardanoLedgerValidationError a) -> Just a + _ -> Nothing + +-------------------------------------------------------------------------------- + +data ValidationPhase + = Phase1 + | Phase2 + +derive instance Eq ValidationPhase + +derive instance Ord ValidationPhase + +instance Show ValidationPhase where + show a = genericShow a + +instance EncodeJson ValidationPhase where + encodeJson = defer \_ -> E.encode E.enum + +instance DecodeJson ValidationPhase where + decodeJson = defer \_ -> D.decode D.enum + +derive instance Generic ValidationPhase _ + +instance Enum ValidationPhase where + succ = genericSucc + pred = genericPred + +instance Bounded ValidationPhase where + bottom = genericBottom + top = genericTop + +-------------------------------------------------------------------------------- + +_Phase1 :: Prism' ValidationPhase Unit +_Phase1 = prism' (const Phase1) case _ of + Phase1 -> Just unit + _ -> Nothing + +_Phase2 :: Prism' ValidationPhase Unit +_Phase2 = prism' (const Phase2) case _ of + Phase2 -> Just unit + _ -> Nothing diff --git a/plutus-playground-client/generated/Playground/Usecases.purs b/plutus-playground-client/generated/Playground/Usecases.purs index d0bec7bb29..982d337a5e 100644 --- a/plutus-playground-client/generated/Playground/Usecases.purs +++ b/plutus-playground-client/generated/Playground/Usecases.purs @@ -155,7 +155,7 @@ vestFundsC vestFundsC vesting = do let txn = payIntoContract (totalAmount vesting) mkTxConstraints (Constraints.typedValidatorLookups $ typedValidator vesting) txn - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx data Liveness = Alive | Dead @@ -199,7 +199,7 @@ retrieveFundsC vesting payment = do -- transaction. mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs) txn - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx return liveness endpoints :: Contract () VestingSchema T.Text () @@ -546,7 +546,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do tx = Constraints.mustPayToTheScript contributor contribValue <> Constraints.mustValidateIn (Interval.to (campaignDeadline cmp)) txid <- fmap getCardanoTxId $ mkTxConstraints (Constraints.typedValidatorLookups inst) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx utxo <- watchAddressUntilTime (Scripts.validatorAddress inst) (campaignCollectionDeadline cmp) @@ -563,7 +563,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do logInfo @Text "Claiming refund" void $ mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs utxo) tx' - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx else pure () -- | The campaign owner's branch of the contract for a given 'Campaign'. It @@ -1542,7 +1542,7 @@ contractDemos = }, "warnings": [] }, - "contractDemoEditorContents": "-- Vesting scheme as a PLC contract\nimport Control.Lens (view)\nimport Control.Monad (void, when)\nimport Data.Default (Default (def))\nimport Data.Map qualified as Map\nimport Data.Text qualified as T\n\nimport Ledger (PaymentPubKeyHash (unPaymentPubKeyHash))\nimport Ledger.Ada qualified as Ada\nimport Ledger.Constraints (TxConstraints, mustBeSignedBy, mustPayToTheScript, mustValidateIn)\nimport Ledger.Constraints qualified as Constraints\nimport Ledger.Interval qualified as Interval\nimport Ledger.TimeSlot qualified as TimeSlot\nimport Ledger.Tx qualified as Tx\nimport Ledger.Typed.Scripts qualified as Scripts\nimport Ledger.Value (Value)\nimport Ledger.Value qualified as Value\nimport Playground.Contract\nimport Plutus.Contract\nimport Plutus.Contract.Test\nimport Plutus.Contract.Typed.Tx qualified as Typed\nimport Plutus.V1.Ledger.Api (Address, POSIXTime, POSIXTimeRange, Validator)\nimport Plutus.V1.Ledger.Contexts (ScriptContext (..), TxInfo (..))\nimport Plutus.V1.Ledger.Contexts qualified as Validation\nimport PlutusTx qualified\nimport PlutusTx.Prelude hiding (Semigroup (..), fold)\nimport Prelude as Haskell (Semigroup (..), show)\n\n{- |\n A simple vesting scheme. Money is locked by a contract and may only be\n retrieved after some time has passed.\n\n This is our first example of a contract that covers multiple transactions,\n with a contract state that changes over time.\n\n In our vesting scheme the money will be released in two _tranches_ (parts):\n A smaller part will be available after an initial number of time has\n passed, and the entire amount will be released at the end. The owner of the\n vesting scheme does not have to take out all the money at once: They can\n take out any amount up to the total that has been released so far. The\n remaining funds stay locked and can be retrieved later.\n\n Let's start with the data types.\n\n-}\n\ntype VestingSchema =\n Endpoint \"vest funds\" ()\n .\\/ Endpoint \"retrieve funds\" Value\n\n-- | Tranche of a vesting scheme.\ndata VestingTranche = VestingTranche {\n vestingTrancheDate :: POSIXTime,\n vestingTrancheAmount :: Value\n } deriving Generic\n\nPlutusTx.makeLift ''VestingTranche\n\n-- | A vesting scheme consisting of two tranches. Each tranche defines a date\n-- (POSIX time) after which an additional amount can be spent.\ndata VestingParams = VestingParams {\n vestingTranche1 :: VestingTranche,\n vestingTranche2 :: VestingTranche,\n vestingOwner :: PaymentPubKeyHash\n } deriving Generic\n\nPlutusTx.makeLift ''VestingParams\n\n{-# INLINABLE totalAmount #-}\n-- | The total amount vested\ntotalAmount :: VestingParams -> Value\ntotalAmount VestingParams{vestingTranche1,vestingTranche2} =\n vestingTrancheAmount vestingTranche1 + vestingTrancheAmount vestingTranche2\n\n{-# INLINABLE availableFrom #-}\n-- | The amount guaranteed to be available from a given tranche in a given time range.\navailableFrom :: VestingTranche -> POSIXTimeRange -> Value\navailableFrom (VestingTranche d v) range =\n -- The valid range is an open-ended range starting from the tranche vesting date\n let validRange = Interval.from d\n -- If the valid range completely contains the argument range (meaning in particular\n -- that the start time of the argument range is after the tranche vesting date), then\n -- the money in the tranche is available, otherwise nothing is available.\n in if validRange `Interval.contains` range then v else zero\n\navailableAt :: VestingParams -> POSIXTime -> Value\navailableAt VestingParams{vestingTranche1, vestingTranche2} sl =\n let f VestingTranche{vestingTrancheDate, vestingTrancheAmount} =\n if sl >= vestingTrancheDate then vestingTrancheAmount else mempty\n in foldMap f [vestingTranche1, vestingTranche2]\n\n{-# INLINABLE remainingFrom #-}\n-- | The amount that has not been released from this tranche yet\nremainingFrom :: VestingTranche -> POSIXTimeRange -> Value\nremainingFrom t@VestingTranche{vestingTrancheAmount} range =\n vestingTrancheAmount - availableFrom t range\n\n{-# INLINABLE validate #-}\nvalidate :: VestingParams -> () -> () -> ScriptContext -> Bool\nvalidate VestingParams{vestingTranche1, vestingTranche2, vestingOwner} () () ctx@ScriptContext{scriptContextTxInfo=txInfo@TxInfo{txInfoValidRange}} =\n let\n remainingActual = Validation.valueLockedBy txInfo (Validation.ownHash ctx)\n\n remainingExpected =\n remainingFrom vestingTranche1 txInfoValidRange\n + remainingFrom vestingTranche2 txInfoValidRange\n\n in remainingActual `Value.geq` remainingExpected\n -- The policy encoded in this contract\n -- is \"vestingOwner can do with the funds what they want\" (as opposed\n -- to \"the funds must be paid to vestingOwner\"). This is enforcey by\n -- the following condition:\n && Validation.txSignedBy txInfo (unPaymentPubKeyHash vestingOwner)\n -- That way the recipient of the funds can pay them to whatever address they\n -- please, potentially saving one transaction.\n\ndata Vesting\ninstance Scripts.ValidatorTypes Vesting where\n type instance RedeemerType Vesting = ()\n type instance DatumType Vesting = ()\n\nvestingScript :: VestingParams -> Validator\nvestingScript = Scripts.validatorScript . typedValidator\n\ntypedValidator :: VestingParams -> Scripts.TypedValidator Vesting\ntypedValidator = Scripts.mkTypedValidatorParam @Vesting\n $$(PlutusTx.compile [|| validate ||])\n $$(PlutusTx.compile [|| wrap ||])\n where\n wrap = Scripts.mkUntypedValidator\n\ncontractAddress :: VestingParams -> Address\ncontractAddress = Scripts.validatorAddress . typedValidator\n\nvestingContract :: VestingParams -> Contract () VestingSchema T.Text ()\nvestingContract vesting = selectList [vest, retrieve]\n where\n vest = endpoint @\"vest funds\" $ \\_ -> vestFundsC vesting\n retrieve = endpoint @\"retrieve funds\" $ \\payment -> do\n liveness <- retrieveFundsC vesting payment\n case liveness of\n Alive -> awaitPromise retrieve\n Dead -> pure ()\n\npayIntoContract :: Value -> TxConstraints () ()\npayIntoContract = mustPayToTheScript ()\n\nvestFundsC\n :: VestingParams\n -> Contract () s T.Text ()\nvestFundsC vesting = do\n let txn = payIntoContract (totalAmount vesting)\n mkTxConstraints (Constraints.typedValidatorLookups $ typedValidator vesting) txn\n >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx\n\ndata Liveness = Alive | Dead\n\nretrieveFundsC\n :: VestingParams\n -> Value\n -> Contract () s T.Text Liveness\nretrieveFundsC vesting payment = do\n let inst = typedValidator vesting\n addr = Scripts.validatorAddress inst\n nextTime <- awaitTime 0\n unspentOutputs <- utxosAt addr\n let\n currentlyLocked = foldMap (view Tx.ciTxOutValue) (Map.elems unspentOutputs)\n remainingValue = currentlyLocked - payment\n mustRemainLocked = totalAmount vesting - availableAt vesting nextTime\n maxPayment = currentlyLocked - mustRemainLocked\n\n when (remainingValue `Value.lt` mustRemainLocked)\n $ throwError\n $ T.unwords\n [ \"Cannot take out\"\n , T.pack (show payment) `T.append` \".\"\n , \"The maximum is\"\n , T.pack (show maxPayment) `T.append` \".\"\n , \"At least\"\n , T.pack (show mustRemainLocked)\n , \"must remain locked by the script.\"\n ]\n\n let liveness = if remainingValue `Value.gt` mempty then Alive else Dead\n remainingOutputs = case liveness of\n Alive -> payIntoContract remainingValue\n Dead -> mempty\n txn = Typed.collectFromScript unspentOutputs ()\n <> remainingOutputs\n <> mustValidateIn (Interval.from nextTime)\n <> mustBeSignedBy (vestingOwner vesting)\n -- we don't need to add a pubkey output for 'vestingOwner' here\n -- because this will be done by the wallet when it balances the\n -- transaction.\n mkTxConstraints (Constraints.typedValidatorLookups inst\n <> Constraints.unspentOutputs unspentOutputs) txn\n >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx\n return liveness\n\nendpoints :: Contract () VestingSchema T.Text ()\nendpoints = vestingContract vestingParams\n where\n vestingOwner = mockWalletPaymentPubKeyHash w1\n vestingParams =\n VestingParams {vestingTranche1, vestingTranche2, vestingOwner}\n vestingTranche1 =\n VestingTranche\n {vestingTrancheDate = TimeSlot.scSlotZeroTime def + 20000, vestingTrancheAmount = Ada.lovelaceValueOf 50_000_000}\n vestingTranche2 =\n VestingTranche\n {vestingTrancheDate = TimeSlot.scSlotZeroTime def + 40000, vestingTrancheAmount = Ada.lovelaceValueOf 30_000_000}\n\nmkSchemaDefinitions ''VestingSchema\n\n$(mkKnownCurrencies [])\n", + "contractDemoEditorContents": "-- Vesting scheme as a PLC contract\nimport Control.Lens (view)\nimport Control.Monad (void, when)\nimport Data.Default (Default (def))\nimport Data.Map qualified as Map\nimport Data.Text qualified as T\n\nimport Ledger (PaymentPubKeyHash (unPaymentPubKeyHash))\nimport Ledger.Ada qualified as Ada\nimport Ledger.Constraints (TxConstraints, mustBeSignedBy, mustPayToTheScript, mustValidateIn)\nimport Ledger.Constraints qualified as Constraints\nimport Ledger.Interval qualified as Interval\nimport Ledger.TimeSlot qualified as TimeSlot\nimport Ledger.Tx qualified as Tx\nimport Ledger.Typed.Scripts qualified as Scripts\nimport Ledger.Value (Value)\nimport Ledger.Value qualified as Value\nimport Playground.Contract\nimport Plutus.Contract\nimport Plutus.Contract.Test\nimport Plutus.Contract.Typed.Tx qualified as Typed\nimport Plutus.V1.Ledger.Api (Address, POSIXTime, POSIXTimeRange, Validator)\nimport Plutus.V1.Ledger.Contexts (ScriptContext (..), TxInfo (..))\nimport Plutus.V1.Ledger.Contexts qualified as Validation\nimport PlutusTx qualified\nimport PlutusTx.Prelude hiding (Semigroup (..), fold)\nimport Prelude as Haskell (Semigroup (..), show)\n\n{- |\n A simple vesting scheme. Money is locked by a contract and may only be\n retrieved after some time has passed.\n\n This is our first example of a contract that covers multiple transactions,\n with a contract state that changes over time.\n\n In our vesting scheme the money will be released in two _tranches_ (parts):\n A smaller part will be available after an initial number of time has\n passed, and the entire amount will be released at the end. The owner of the\n vesting scheme does not have to take out all the money at once: They can\n take out any amount up to the total that has been released so far. The\n remaining funds stay locked and can be retrieved later.\n\n Let's start with the data types.\n\n-}\n\ntype VestingSchema =\n Endpoint \"vest funds\" ()\n .\\/ Endpoint \"retrieve funds\" Value\n\n-- | Tranche of a vesting scheme.\ndata VestingTranche = VestingTranche {\n vestingTrancheDate :: POSIXTime,\n vestingTrancheAmount :: Value\n } deriving Generic\n\nPlutusTx.makeLift ''VestingTranche\n\n-- | A vesting scheme consisting of two tranches. Each tranche defines a date\n-- (POSIX time) after which an additional amount can be spent.\ndata VestingParams = VestingParams {\n vestingTranche1 :: VestingTranche,\n vestingTranche2 :: VestingTranche,\n vestingOwner :: PaymentPubKeyHash\n } deriving Generic\n\nPlutusTx.makeLift ''VestingParams\n\n{-# INLINABLE totalAmount #-}\n-- | The total amount vested\ntotalAmount :: VestingParams -> Value\ntotalAmount VestingParams{vestingTranche1,vestingTranche2} =\n vestingTrancheAmount vestingTranche1 + vestingTrancheAmount vestingTranche2\n\n{-# INLINABLE availableFrom #-}\n-- | The amount guaranteed to be available from a given tranche in a given time range.\navailableFrom :: VestingTranche -> POSIXTimeRange -> Value\navailableFrom (VestingTranche d v) range =\n -- The valid range is an open-ended range starting from the tranche vesting date\n let validRange = Interval.from d\n -- If the valid range completely contains the argument range (meaning in particular\n -- that the start time of the argument range is after the tranche vesting date), then\n -- the money in the tranche is available, otherwise nothing is available.\n in if validRange `Interval.contains` range then v else zero\n\navailableAt :: VestingParams -> POSIXTime -> Value\navailableAt VestingParams{vestingTranche1, vestingTranche2} sl =\n let f VestingTranche{vestingTrancheDate, vestingTrancheAmount} =\n if sl >= vestingTrancheDate then vestingTrancheAmount else mempty\n in foldMap f [vestingTranche1, vestingTranche2]\n\n{-# INLINABLE remainingFrom #-}\n-- | The amount that has not been released from this tranche yet\nremainingFrom :: VestingTranche -> POSIXTimeRange -> Value\nremainingFrom t@VestingTranche{vestingTrancheAmount} range =\n vestingTrancheAmount - availableFrom t range\n\n{-# INLINABLE validate #-}\nvalidate :: VestingParams -> () -> () -> ScriptContext -> Bool\nvalidate VestingParams{vestingTranche1, vestingTranche2, vestingOwner} () () ctx@ScriptContext{scriptContextTxInfo=txInfo@TxInfo{txInfoValidRange}} =\n let\n remainingActual = Validation.valueLockedBy txInfo (Validation.ownHash ctx)\n\n remainingExpected =\n remainingFrom vestingTranche1 txInfoValidRange\n + remainingFrom vestingTranche2 txInfoValidRange\n\n in remainingActual `Value.geq` remainingExpected\n -- The policy encoded in this contract\n -- is \"vestingOwner can do with the funds what they want\" (as opposed\n -- to \"the funds must be paid to vestingOwner\"). This is enforcey by\n -- the following condition:\n && Validation.txSignedBy txInfo (unPaymentPubKeyHash vestingOwner)\n -- That way the recipient of the funds can pay them to whatever address they\n -- please, potentially saving one transaction.\n\ndata Vesting\ninstance Scripts.ValidatorTypes Vesting where\n type instance RedeemerType Vesting = ()\n type instance DatumType Vesting = ()\n\nvestingScript :: VestingParams -> Validator\nvestingScript = Scripts.validatorScript . typedValidator\n\ntypedValidator :: VestingParams -> Scripts.TypedValidator Vesting\ntypedValidator = Scripts.mkTypedValidatorParam @Vesting\n $$(PlutusTx.compile [|| validate ||])\n $$(PlutusTx.compile [|| wrap ||])\n where\n wrap = Scripts.mkUntypedValidator\n\ncontractAddress :: VestingParams -> Address\ncontractAddress = Scripts.validatorAddress . typedValidator\n\nvestingContract :: VestingParams -> Contract () VestingSchema T.Text ()\nvestingContract vesting = selectList [vest, retrieve]\n where\n vest = endpoint @\"vest funds\" $ \\_ -> vestFundsC vesting\n retrieve = endpoint @\"retrieve funds\" $ \\payment -> do\n liveness <- retrieveFundsC vesting payment\n case liveness of\n Alive -> awaitPromise retrieve\n Dead -> pure ()\n\npayIntoContract :: Value -> TxConstraints () ()\npayIntoContract = mustPayToTheScript ()\n\nvestFundsC\n :: VestingParams\n -> Contract () s T.Text ()\nvestFundsC vesting = do\n let txn = payIntoContract (totalAmount vesting)\n mkTxConstraints (Constraints.typedValidatorLookups $ typedValidator vesting) txn\n >>= adjustUnbalancedTx >>= void . submitUnbalancedTx\n\ndata Liveness = Alive | Dead\n\nretrieveFundsC\n :: VestingParams\n -> Value\n -> Contract () s T.Text Liveness\nretrieveFundsC vesting payment = do\n let inst = typedValidator vesting\n addr = Scripts.validatorAddress inst\n nextTime <- awaitTime 0\n unspentOutputs <- utxosAt addr\n let\n currentlyLocked = foldMap (view Tx.ciTxOutValue) (Map.elems unspentOutputs)\n remainingValue = currentlyLocked - payment\n mustRemainLocked = totalAmount vesting - availableAt vesting nextTime\n maxPayment = currentlyLocked - mustRemainLocked\n\n when (remainingValue `Value.lt` mustRemainLocked)\n $ throwError\n $ T.unwords\n [ \"Cannot take out\"\n , T.pack (show payment) `T.append` \".\"\n , \"The maximum is\"\n , T.pack (show maxPayment) `T.append` \".\"\n , \"At least\"\n , T.pack (show mustRemainLocked)\n , \"must remain locked by the script.\"\n ]\n\n let liveness = if remainingValue `Value.gt` mempty then Alive else Dead\n remainingOutputs = case liveness of\n Alive -> payIntoContract remainingValue\n Dead -> mempty\n txn = Typed.collectFromScript unspentOutputs ()\n <> remainingOutputs\n <> mustValidateIn (Interval.from nextTime)\n <> mustBeSignedBy (vestingOwner vesting)\n -- we don't need to add a pubkey output for 'vestingOwner' here\n -- because this will be done by the wallet when it balances the\n -- transaction.\n mkTxConstraints (Constraints.typedValidatorLookups inst\n <> Constraints.unspentOutputs unspentOutputs) txn\n >>= adjustUnbalancedTx >>= void . submitUnbalancedTx\n return liveness\n\nendpoints :: Contract () VestingSchema T.Text ()\nendpoints = vestingContract vestingParams\n where\n vestingOwner = mockWalletPaymentPubKeyHash w1\n vestingParams =\n VestingParams {vestingTranche1, vestingTranche2, vestingOwner}\n vestingTranche1 =\n VestingTranche\n {vestingTrancheDate = TimeSlot.scSlotZeroTime def + 20000, vestingTrancheAmount = Ada.lovelaceValueOf 50_000_000}\n vestingTranche2 =\n VestingTranche\n {vestingTrancheDate = TimeSlot.scSlotZeroTime def + 40000, vestingTrancheAmount = Ada.lovelaceValueOf 30_000_000}\n\nmkSchemaDefinitions ''VestingSchema\n\n$(mkKnownCurrencies [])\n", "contractDemoName": "Vesting", "contractDemoSimulations": [ { @@ -1730,7 +1730,7 @@ contractDemos = }, "warnings": [] }, - "contractDemoEditorContents": "-- Crowdfunding contract implemented using the [[Plutus]] interface.\n-- This is the fully parallel version that collects all contributions\n-- in a single transaction.\n--\n-- Note [Transactions in the crowdfunding campaign] explains the structure of\n-- this contract on the blockchain.\n\nimport Control.Applicative (Applicative (pure))\nimport Control.Monad (void)\nimport Data.Default (Default (def))\nimport Data.Text (Text)\nimport Ledger (POSIXTime, POSIXTimeRange, PaymentPubKeyHash (unPaymentPubKeyHash), ScriptContext (..), TxInfo (..),\n getCardanoTxId)\nimport Ledger qualified\nimport Ledger.Interval qualified as Interval\nimport Ledger.TimeSlot qualified as TimeSlot\nimport Ledger.Typed.Scripts qualified as Scripts hiding (validatorHash)\nimport Ledger.Value (Value)\nimport Playground.Contract\nimport Plutus.Contract\nimport Plutus.Contract.Constraints qualified as Constraints\nimport Plutus.Contract.Typed.Tx qualified as Typed\nimport Plutus.Script.Utils.V1.Scripts qualified as PV1\nimport Plutus.V1.Ledger.Contexts qualified as PV1\nimport PlutusTx qualified\nimport PlutusTx.Prelude hiding (Applicative (..), Semigroup (..))\nimport Prelude (Semigroup (..))\nimport Prelude qualified as Haskell\nimport Wallet.Emulator qualified as Emulator\n\n-- | A crowdfunding campaign.\ndata Campaign = Campaign\n { campaignDeadline :: POSIXTime\n -- ^ The date by which the campaign funds can be contributed.\n , campaignCollectionDeadline :: POSIXTime\n -- ^ The date by which the campaign owner has to collect the funds\n , campaignOwner :: PaymentPubKeyHash\n -- ^ Public key of the campaign owner. This key is entitled to retrieve the\n -- funds if the campaign is successful.\n } deriving (Generic, ToJSON, FromJSON, ToSchema)\n\nPlutusTx.makeLift ''Campaign\n\n-- | Action that can be taken by the participants in this contract. A value of\n-- `CampaignAction` is provided as the redeemer. The validator script then\n-- checks if the conditions for performing this action are met.\n--\ndata CampaignAction = Collect | Refund\n\nPlutusTx.unstableMakeIsData ''CampaignAction\nPlutusTx.makeLift ''CampaignAction\n\ntype CrowdfundingSchema =\n Endpoint \"schedule collection\" ()\n .\\/ Endpoint \"contribute\" Contribution\n\nnewtype Contribution = Contribution\n { contribValue :: Value\n -- ^ how much to contribute\n } deriving stock (Haskell.Eq, Show, Generic)\n deriving anyclass (ToJSON, FromJSON, ToSchema, ToArgument)\n\n-- | Construct a 'Campaign' value from the campaign parameters,\n-- using the wallet's public key.\nmkCampaign :: POSIXTime -> POSIXTime -> Wallet -> Campaign\nmkCampaign ddl collectionDdl ownerWallet =\n Campaign\n { campaignDeadline = ddl\n , campaignCollectionDeadline = collectionDdl\n , campaignOwner = Emulator.mockWalletPaymentPubKeyHash ownerWallet\n }\n\n-- | The 'POSIXTimeRange' during which the funds can be collected\ncollectionRange :: Campaign -> POSIXTimeRange\ncollectionRange cmp =\n Interval.interval (campaignDeadline cmp) (campaignCollectionDeadline cmp - 1)\n\n-- | The 'POSIXTimeRange' during which a refund may be claimed\nrefundRange :: Campaign -> POSIXTimeRange\nrefundRange cmp =\n Interval.from (campaignCollectionDeadline cmp)\n\ndata Crowdfunding\ninstance Scripts.ValidatorTypes Crowdfunding where\n type instance RedeemerType Crowdfunding = CampaignAction\n type instance DatumType Crowdfunding = PaymentPubKeyHash\n\ntypedValidator :: Campaign -> Scripts.TypedValidator Crowdfunding\ntypedValidator = Scripts.mkTypedValidatorParam @Crowdfunding\n $$(PlutusTx.compile [|| mkValidator ||])\n $$(PlutusTx.compile [|| wrap ||])\n where\n wrap = Scripts.mkUntypedValidator\n\n{-# INLINABLE validRefund #-}\nvalidRefund :: Campaign -> PaymentPubKeyHash -> TxInfo -> Bool\nvalidRefund campaign contributor txinfo =\n -- Check that the transaction falls in the refund range of the campaign\n (refundRange campaign `Interval.contains` txInfoValidRange txinfo)\n -- Check that the transaction is signed by the contributor\n && (txinfo `PV1.txSignedBy` unPaymentPubKeyHash contributor)\n\nvalidCollection :: Campaign -> TxInfo -> Bool\nvalidCollection campaign txinfo =\n -- Check that the transaction falls in the collection range of the campaign\n (collectionRange campaign `Interval.contains` txInfoValidRange txinfo)\n -- Check that the transaction is signed by the campaign owner\n && (txinfo `PV1.txSignedBy` unPaymentPubKeyHash (campaignOwner campaign))\n\n-- | The validator script is of type 'CrowdfundingValidator', and is\n-- additionally parameterized by a 'Campaign' definition. This argument is\n-- provided by the Plutus client, using 'PlutusTx.applyCode'.\n-- As a result, the 'Campaign' definition is part of the script address,\n-- and different campaigns have different addresses.\nmkValidator :: Campaign -> PaymentPubKeyHash -> CampaignAction -> ScriptContext -> Bool\nmkValidator c con act p = case act of\n -- the \"refund\" branch\n Refund -> validRefund c con (scriptContextTxInfo p)\n -- the \"collection\" branch\n Collect -> validCollection c (scriptContextTxInfo p)\n\n-- | The validator script that determines whether the campaign owner can\n-- retrieve the funds or the contributors can claim a refund.\n--\ncontributionScript :: Campaign -> PV1.Validator\ncontributionScript = Scripts.validatorScript . typedValidator\n\n-- | The address of a [[Campaign]]\ncampaignAddress :: Campaign -> Ledger.ValidatorHash\ncampaignAddress = PV1.validatorHash . contributionScript\n\n-- | The crowdfunding contract for the 'Campaign'.\ncrowdfunding :: AsContractError e => Campaign -> Contract () CrowdfundingSchema e ()\ncrowdfunding c = selectList [contribute c, scheduleCollection c]\n\n-- | A sample campaign\ntheCampaign :: POSIXTime -> Campaign\ntheCampaign startTime = Campaign\n { campaignDeadline = startTime + 40000\n , campaignCollectionDeadline = startTime + 60000\n , campaignOwner = Emulator.mockWalletPaymentPubKeyHash (Emulator.knownWallet 1)\n }\n\n-- | The \"contribute\" branch of the contract for a specific 'Campaign'. Exposes\n-- an endpoint that allows the user to enter their public key and the\n-- contribution. Then waits until the campaign is over, and collects the\n-- refund if the funding was not collected.\ncontribute :: AsContractError e => Campaign -> Promise () CrowdfundingSchema e ()\ncontribute cmp = endpoint @\"contribute\" $ \\Contribution{contribValue} -> do\n contributor <- ownPaymentPubKeyHash\n let inst = typedValidator cmp\n tx = Constraints.mustPayToTheScript contributor contribValue\n <> Constraints.mustValidateIn (Interval.to (campaignDeadline cmp))\n txid <- fmap getCardanoTxId $ mkTxConstraints (Constraints.typedValidatorLookups inst) tx\n >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx\n\n utxo <- watchAddressUntilTime (Scripts.validatorAddress inst) (campaignCollectionDeadline cmp)\n\n -- 'utxo' is the set of unspent outputs at the campaign address at the\n -- collection deadline. If 'utxo' still contains our own contribution\n -- then we can claim a refund.\n\n let flt Ledger.TxOutRef{txOutRefId} _ = txid Haskell.== txOutRefId\n tx' = Typed.collectFromScriptFilter flt utxo Refund\n <> Constraints.mustValidateIn (refundRange cmp)\n <> Constraints.mustBeSignedBy contributor\n if Constraints.modifiesUtxoSet tx'\n then do\n logInfo @Text \"Claiming refund\"\n void $ mkTxConstraints (Constraints.typedValidatorLookups inst\n <> Constraints.unspentOutputs utxo) tx'\n >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx\n else pure ()\n\n-- | The campaign owner's branch of the contract for a given 'Campaign'. It\n-- watches the campaign address for contributions and collects them if\n-- the funding goal was reached in time.\nscheduleCollection :: AsContractError e => Campaign -> Promise () CrowdfundingSchema e ()\nscheduleCollection cmp =\n -- Expose an endpoint that lets the user fire the starting gun on the\n -- campaign. (This endpoint isn't technically necessary, we could just\n -- run the 'trg' action right away)\n endpoint @\"schedule collection\" $ \\() -> do\n let inst = typedValidator cmp\n\n _ <- awaitTime $ campaignDeadline cmp\n unspentOutputs <- utxosAt (Scripts.validatorAddress inst)\n\n let tx = Typed.collectFromScript unspentOutputs Collect\n <> Constraints.mustValidateIn (collectionRange cmp)\n void $ submitTxConstraintsSpending inst unspentOutputs tx\n\n{- note [Transactions in the crowdfunding campaign]\n\nAssume there is a campaign `c :: Campaign` with two contributors\n(identified by public key `pc_1` and `pc_2`) and one campaign owner (pco).\nEach contributor creates a transaction, `t_1` and `t_2`, whose outputs are\nlocked by the scripts `contributionScript c pc_1` and `contributionScript\nc pc_1` respectively.\n\nThere are two outcomes for the campaign.\n\n1. Campaign owner collects the funds from both contributors. In this case\n the owner creates a single transaction with two inputs, referring to\n `t_1` and `t_2`. Each input contains the script `contributionScript c`\n specialised to a contributor. The redeemer script of this transaction\n contains the value `Collect`, prompting the validator script to check the\n branch for `Collect`.\n\n2. Refund. In this case each contributor creates a transaction with a\n single input claiming back their part of the funds. This case is\n covered by the `Refund` branch, and its redeemer script is the\n `Refund` action.\n\nIn both cases, the validator script is run twice. In the first case\nthere is a single transaction consuming both inputs. In the second case there\nare two different transactions that may happen at different times.\n\n-}\n\n{- note [PendingTx]\n\nThis part of the API (the PendingTx argument) is experimental and subject\nto change.\n\n-}\n\nendpoints :: AsContractError e => Contract () CrowdfundingSchema e ()\nendpoints = crowdfunding (theCampaign $ TimeSlot.scSlotZeroTime def)\n\nmkSchemaDefinitions ''CrowdfundingSchema\n\n$(mkKnownCurrencies [])\n", + "contractDemoEditorContents": "-- Crowdfunding contract implemented using the [[Plutus]] interface.\n-- This is the fully parallel version that collects all contributions\n-- in a single transaction.\n--\n-- Note [Transactions in the crowdfunding campaign] explains the structure of\n-- this contract on the blockchain.\n\nimport Control.Applicative (Applicative (pure))\nimport Control.Monad (void)\nimport Data.Default (Default (def))\nimport Data.Text (Text)\nimport Ledger (POSIXTime, POSIXTimeRange, PaymentPubKeyHash (unPaymentPubKeyHash), ScriptContext (..), TxInfo (..),\n getCardanoTxId)\nimport Ledger qualified\nimport Ledger.Interval qualified as Interval\nimport Ledger.TimeSlot qualified as TimeSlot\nimport Ledger.Typed.Scripts qualified as Scripts hiding (validatorHash)\nimport Ledger.Value (Value)\nimport Playground.Contract\nimport Plutus.Contract\nimport Plutus.Contract.Constraints qualified as Constraints\nimport Plutus.Contract.Typed.Tx qualified as Typed\nimport Plutus.Script.Utils.V1.Scripts qualified as PV1\nimport Plutus.V1.Ledger.Contexts qualified as PV1\nimport PlutusTx qualified\nimport PlutusTx.Prelude hiding (Applicative (..), Semigroup (..))\nimport Prelude (Semigroup (..))\nimport Prelude qualified as Haskell\nimport Wallet.Emulator qualified as Emulator\n\n-- | A crowdfunding campaign.\ndata Campaign = Campaign\n { campaignDeadline :: POSIXTime\n -- ^ The date by which the campaign funds can be contributed.\n , campaignCollectionDeadline :: POSIXTime\n -- ^ The date by which the campaign owner has to collect the funds\n , campaignOwner :: PaymentPubKeyHash\n -- ^ Public key of the campaign owner. This key is entitled to retrieve the\n -- funds if the campaign is successful.\n } deriving (Generic, ToJSON, FromJSON, ToSchema)\n\nPlutusTx.makeLift ''Campaign\n\n-- | Action that can be taken by the participants in this contract. A value of\n-- `CampaignAction` is provided as the redeemer. The validator script then\n-- checks if the conditions for performing this action are met.\n--\ndata CampaignAction = Collect | Refund\n\nPlutusTx.unstableMakeIsData ''CampaignAction\nPlutusTx.makeLift ''CampaignAction\n\ntype CrowdfundingSchema =\n Endpoint \"schedule collection\" ()\n .\\/ Endpoint \"contribute\" Contribution\n\nnewtype Contribution = Contribution\n { contribValue :: Value\n -- ^ how much to contribute\n } deriving stock (Haskell.Eq, Show, Generic)\n deriving anyclass (ToJSON, FromJSON, ToSchema, ToArgument)\n\n-- | Construct a 'Campaign' value from the campaign parameters,\n-- using the wallet's public key.\nmkCampaign :: POSIXTime -> POSIXTime -> Wallet -> Campaign\nmkCampaign ddl collectionDdl ownerWallet =\n Campaign\n { campaignDeadline = ddl\n , campaignCollectionDeadline = collectionDdl\n , campaignOwner = Emulator.mockWalletPaymentPubKeyHash ownerWallet\n }\n\n-- | The 'POSIXTimeRange' during which the funds can be collected\ncollectionRange :: Campaign -> POSIXTimeRange\ncollectionRange cmp =\n Interval.interval (campaignDeadline cmp) (campaignCollectionDeadline cmp - 1)\n\n-- | The 'POSIXTimeRange' during which a refund may be claimed\nrefundRange :: Campaign -> POSIXTimeRange\nrefundRange cmp =\n Interval.from (campaignCollectionDeadline cmp)\n\ndata Crowdfunding\ninstance Scripts.ValidatorTypes Crowdfunding where\n type instance RedeemerType Crowdfunding = CampaignAction\n type instance DatumType Crowdfunding = PaymentPubKeyHash\n\ntypedValidator :: Campaign -> Scripts.TypedValidator Crowdfunding\ntypedValidator = Scripts.mkTypedValidatorParam @Crowdfunding\n $$(PlutusTx.compile [|| mkValidator ||])\n $$(PlutusTx.compile [|| wrap ||])\n where\n wrap = Scripts.mkUntypedValidator\n\n{-# INLINABLE validRefund #-}\nvalidRefund :: Campaign -> PaymentPubKeyHash -> TxInfo -> Bool\nvalidRefund campaign contributor txinfo =\n -- Check that the transaction falls in the refund range of the campaign\n (refundRange campaign `Interval.contains` txInfoValidRange txinfo)\n -- Check that the transaction is signed by the contributor\n && (txinfo `PV1.txSignedBy` unPaymentPubKeyHash contributor)\n\nvalidCollection :: Campaign -> TxInfo -> Bool\nvalidCollection campaign txinfo =\n -- Check that the transaction falls in the collection range of the campaign\n (collectionRange campaign `Interval.contains` txInfoValidRange txinfo)\n -- Check that the transaction is signed by the campaign owner\n && (txinfo `PV1.txSignedBy` unPaymentPubKeyHash (campaignOwner campaign))\n\n-- | The validator script is of type 'CrowdfundingValidator', and is\n-- additionally parameterized by a 'Campaign' definition. This argument is\n-- provided by the Plutus client, using 'PlutusTx.applyCode'.\n-- As a result, the 'Campaign' definition is part of the script address,\n-- and different campaigns have different addresses.\nmkValidator :: Campaign -> PaymentPubKeyHash -> CampaignAction -> ScriptContext -> Bool\nmkValidator c con act p = case act of\n -- the \"refund\" branch\n Refund -> validRefund c con (scriptContextTxInfo p)\n -- the \"collection\" branch\n Collect -> validCollection c (scriptContextTxInfo p)\n\n-- | The validator script that determines whether the campaign owner can\n-- retrieve the funds or the contributors can claim a refund.\n--\ncontributionScript :: Campaign -> PV1.Validator\ncontributionScript = Scripts.validatorScript . typedValidator\n\n-- | The address of a [[Campaign]]\ncampaignAddress :: Campaign -> Ledger.ValidatorHash\ncampaignAddress = PV1.validatorHash . contributionScript\n\n-- | The crowdfunding contract for the 'Campaign'.\ncrowdfunding :: AsContractError e => Campaign -> Contract () CrowdfundingSchema e ()\ncrowdfunding c = selectList [contribute c, scheduleCollection c]\n\n-- | A sample campaign\ntheCampaign :: POSIXTime -> Campaign\ntheCampaign startTime = Campaign\n { campaignDeadline = startTime + 40000\n , campaignCollectionDeadline = startTime + 60000\n , campaignOwner = Emulator.mockWalletPaymentPubKeyHash (Emulator.knownWallet 1)\n }\n\n-- | The \"contribute\" branch of the contract for a specific 'Campaign'. Exposes\n-- an endpoint that allows the user to enter their public key and the\n-- contribution. Then waits until the campaign is over, and collects the\n-- refund if the funding was not collected.\ncontribute :: AsContractError e => Campaign -> Promise () CrowdfundingSchema e ()\ncontribute cmp = endpoint @\"contribute\" $ \\Contribution{contribValue} -> do\n contributor <- ownPaymentPubKeyHash\n let inst = typedValidator cmp\n tx = Constraints.mustPayToTheScript contributor contribValue\n <> Constraints.mustValidateIn (Interval.to (campaignDeadline cmp))\n txid <- fmap getCardanoTxId $ mkTxConstraints (Constraints.typedValidatorLookups inst) tx\n >>= adjustUnbalancedTx >>= submitUnbalancedTx\n\n utxo <- watchAddressUntilTime (Scripts.validatorAddress inst) (campaignCollectionDeadline cmp)\n\n -- 'utxo' is the set of unspent outputs at the campaign address at the\n -- collection deadline. If 'utxo' still contains our own contribution\n -- then we can claim a refund.\n\n let flt Ledger.TxOutRef{txOutRefId} _ = txid Haskell.== txOutRefId\n tx' = Typed.collectFromScriptFilter flt utxo Refund\n <> Constraints.mustValidateIn (refundRange cmp)\n <> Constraints.mustBeSignedBy contributor\n if Constraints.modifiesUtxoSet tx'\n then do\n logInfo @Text \"Claiming refund\"\n void $ mkTxConstraints (Constraints.typedValidatorLookups inst\n <> Constraints.unspentOutputs utxo) tx'\n >>= adjustUnbalancedTx >>= submitUnbalancedTx\n else pure ()\n\n-- | The campaign owner's branch of the contract for a given 'Campaign'. It\n-- watches the campaign address for contributions and collects them if\n-- the funding goal was reached in time.\nscheduleCollection :: AsContractError e => Campaign -> Promise () CrowdfundingSchema e ()\nscheduleCollection cmp =\n -- Expose an endpoint that lets the user fire the starting gun on the\n -- campaign. (This endpoint isn't technically necessary, we could just\n -- run the 'trg' action right away)\n endpoint @\"schedule collection\" $ \\() -> do\n let inst = typedValidator cmp\n\n _ <- awaitTime $ campaignDeadline cmp\n unspentOutputs <- utxosAt (Scripts.validatorAddress inst)\n\n let tx = Typed.collectFromScript unspentOutputs Collect\n <> Constraints.mustValidateIn (collectionRange cmp)\n void $ submitTxConstraintsSpending inst unspentOutputs tx\n\n{- note [Transactions in the crowdfunding campaign]\n\nAssume there is a campaign `c :: Campaign` with two contributors\n(identified by public key `pc_1` and `pc_2`) and one campaign owner (pco).\nEach contributor creates a transaction, `t_1` and `t_2`, whose outputs are\nlocked by the scripts `contributionScript c pc_1` and `contributionScript\nc pc_1` respectively.\n\nThere are two outcomes for the campaign.\n\n1. Campaign owner collects the funds from both contributors. In this case\n the owner creates a single transaction with two inputs, referring to\n `t_1` and `t_2`. Each input contains the script `contributionScript c`\n specialised to a contributor. The redeemer script of this transaction\n contains the value `Collect`, prompting the validator script to check the\n branch for `Collect`.\n\n2. Refund. In this case each contributor creates a transaction with a\n single input claiming back their part of the funds. This case is\n covered by the `Refund` branch, and its redeemer script is the\n `Refund` action.\n\nIn both cases, the validator script is run twice. In the first case\nthere is a single transaction consuming both inputs. In the second case there\nare two different transactions that may happen at different times.\n\n-}\n\n{- note [PendingTx]\n\nThis part of the API (the PendingTx argument) is experimental and subject\nto change.\n\n-}\n\nendpoints :: AsContractError e => Contract () CrowdfundingSchema e ()\nendpoints = crowdfunding (theCampaign $ TimeSlot.scSlotZeroTime def)\n\nmkSchemaDefinitions ''CrowdfundingSchema\n\n$(mkKnownCurrencies [])\n", "contractDemoName": "Crowd Funding", "contractDemoSimulations": [ { diff --git a/plutus-playground-client/generated/Plutus/Contract/Effects.purs b/plutus-playground-client/generated/Plutus/Contract/Effects.purs index b9ed90199b..a387be1fa9 100644 --- a/plutus-playground-client/generated/Plutus/Contract/Effects.purs +++ b/plutus-playground-client/generated/Plutus/Contract/Effects.purs @@ -26,6 +26,7 @@ import Ledger.Constraints.OffChain (UnbalancedTx) import Ledger.Slot (Slot) import Ledger.TimeSlot (SlotConversionError) import Ledger.Tx (CardanoTx, ChainIndexTxOut) +import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.ChainIndex.Api (IsUtxoResponse, TxosResponse, UtxosResponse) import Plutus.ChainIndex.Tx (ChainIndexTx) import Plutus.ChainIndex.Types (RollbackState, Tip, TxOutState) @@ -387,7 +388,8 @@ _GetTipResponse = prism' GetTipResponse case _ of -------------------------------------------------------------------------------- data PABReq - = AwaitSlotReq Slot + = AdjustUnbalancedTxReq UnbalancedTx + | AwaitSlotReq Slot | AwaitTimeReq POSIXTime | AwaitUtxoSpentReq TxOutRef | AwaitUtxoProducedReq Address @@ -411,6 +413,7 @@ instance Show PABReq where instance EncodeJson PABReq where encodeJson = defer \_ -> case _ of + AdjustUnbalancedTxReq a -> E.encodeTagged "AdjustUnbalancedTxReq" a E.value AwaitSlotReq a -> E.encodeTagged "AwaitSlotReq" a E.value AwaitTimeReq a -> E.encodeTagged "AwaitTimeReq" a E.value AwaitUtxoSpentReq a -> E.encodeTagged "AwaitUtxoSpentReq" a E.value @@ -432,7 +435,8 @@ instance DecodeJson PABReq where decodeJson = defer \_ -> D.decode $ D.sumType "PABReq" $ Map.fromFoldable - [ "AwaitSlotReq" /\ D.content (AwaitSlotReq <$> D.value) + [ "AdjustUnbalancedTxReq" /\ D.content (AdjustUnbalancedTxReq <$> D.value) + , "AwaitSlotReq" /\ D.content (AwaitSlotReq <$> D.value) , "AwaitTimeReq" /\ D.content (AwaitTimeReq <$> D.value) , "AwaitUtxoSpentReq" /\ D.content (AwaitUtxoSpentReq <$> D.value) , "AwaitUtxoProducedReq" /\ D.content (AwaitUtxoProducedReq <$> D.value) @@ -454,6 +458,11 @@ derive instance Generic PABReq _ -------------------------------------------------------------------------------- +_AdjustUnbalancedTxReq :: Prism' PABReq UnbalancedTx +_AdjustUnbalancedTxReq = prism' AdjustUnbalancedTxReq case _ of + (AdjustUnbalancedTxReq a) -> Just a + _ -> Nothing + _AwaitSlotReq :: Prism' PABReq Slot _AwaitSlotReq = prism' AwaitSlotReq case _ of (AwaitSlotReq a) -> Just a @@ -537,7 +546,8 @@ _YieldUnbalancedTxReq = prism' YieldUnbalancedTxReq case _ of -------------------------------------------------------------------------------- data PABResp - = AwaitSlotResp Slot + = AdjustUnbalancedTxResp (Either ToCardanoError UnbalancedTx) + | AwaitSlotResp Slot | AwaitTimeResp POSIXTime | AwaitUtxoSpentResp ChainIndexTx | AwaitUtxoProducedResp (NonEmptyList ChainIndexTx) @@ -561,6 +571,7 @@ instance Show PABResp where instance EncodeJson PABResp where encodeJson = defer \_ -> case _ of + AdjustUnbalancedTxResp a -> E.encodeTagged "AdjustUnbalancedTxResp" a (E.either E.value E.value) AwaitSlotResp a -> E.encodeTagged "AwaitSlotResp" a E.value AwaitTimeResp a -> E.encodeTagged "AwaitTimeResp" a E.value AwaitUtxoSpentResp a -> E.encodeTagged "AwaitUtxoSpentResp" a E.value @@ -582,7 +593,8 @@ instance DecodeJson PABResp where decodeJson = defer \_ -> D.decode $ D.sumType "PABResp" $ Map.fromFoldable - [ "AwaitSlotResp" /\ D.content (AwaitSlotResp <$> D.value) + [ "AdjustUnbalancedTxResp" /\ D.content (AdjustUnbalancedTxResp <$> (D.either D.value D.value)) + , "AwaitSlotResp" /\ D.content (AwaitSlotResp <$> D.value) , "AwaitTimeResp" /\ D.content (AwaitTimeResp <$> D.value) , "AwaitUtxoSpentResp" /\ D.content (AwaitUtxoSpentResp <$> D.value) , "AwaitUtxoProducedResp" /\ D.content (AwaitUtxoProducedResp <$> D.value) @@ -604,6 +616,11 @@ derive instance Generic PABResp _ -------------------------------------------------------------------------------- +_AdjustUnbalancedTxResp :: Prism' PABResp (Either ToCardanoError UnbalancedTx) +_AdjustUnbalancedTxResp = prism' AdjustUnbalancedTxResp case _ of + (AdjustUnbalancedTxResp a) -> Just a + _ -> Nothing + _AwaitSlotResp :: Prism' PABResp Slot _AwaitSlotResp = prism' AwaitSlotResp case _ of (AwaitSlotResp a) -> Just a diff --git a/plutus-playground-client/generated/Plutus/Contract/Error.purs b/plutus-playground-client/generated/Plutus/Contract/Error.purs index f62a5a04c8..841f1f8e45 100644 --- a/plutus-playground-client/generated/Plutus/Contract/Error.purs +++ b/plutus-playground-client/generated/Plutus/Contract/Error.purs @@ -19,6 +19,7 @@ import Data.RawJson (RawJson) import Data.Show.Generic (genericShow) import Data.Tuple.Nested ((/\)) import Ledger.Constraints.OffChain (MkTxError) +import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.Contract.Checkpoint (CheckpointError) import Plutus.Contract.Effects (ChainIndexResponse) import Type.Proxy (Proxy(Proxy)) @@ -60,6 +61,7 @@ data ContractError | ChainIndexContractError String ChainIndexResponse | EmulatorAssertionContractError AssertionError | ConstraintResolutionContractError MkTxError + | TxToCardanoConvertContractError ToCardanoError | ResumableContractError MatchingError | CCheckpointContractError CheckpointError | EndpointDecodeContractError @@ -80,6 +82,7 @@ instance EncodeJson ContractError where ChainIndexContractError a b -> E.encodeTagged "ChainIndexContractError" (a /\ b) (E.tuple (E.value >/\< E.value)) EmulatorAssertionContractError a -> E.encodeTagged "EmulatorAssertionContractError" a E.value ConstraintResolutionContractError a -> E.encodeTagged "ConstraintResolutionContractError" a E.value + TxToCardanoConvertContractError a -> E.encodeTagged "TxToCardanoConvertContractError" a E.value ResumableContractError a -> E.encodeTagged "ResumableContractError" a E.value CCheckpointContractError a -> E.encodeTagged "CCheckpointContractError" a E.value EndpointDecodeContractError { eeEndpointDescription, eeEndpointValue, eeErrorMessage } -> encodeJson @@ -98,6 +101,7 @@ instance DecodeJson ContractError where , "ChainIndexContractError" /\ D.content (D.tuple $ ChainIndexContractError D.value D.value) , "EmulatorAssertionContractError" /\ D.content (EmulatorAssertionContractError <$> D.value) , "ConstraintResolutionContractError" /\ D.content (ConstraintResolutionContractError <$> D.value) + , "TxToCardanoConvertContractError" /\ D.content (TxToCardanoConvertContractError <$> D.value) , "ResumableContractError" /\ D.content (ResumableContractError <$> D.value) , "CCheckpointContractError" /\ D.content (CCheckpointContractError <$> D.value) , "EndpointDecodeContractError" /\ @@ -134,6 +138,11 @@ _ConstraintResolutionContractError = prism' ConstraintResolutionContractError ca (ConstraintResolutionContractError a) -> Just a _ -> Nothing +_TxToCardanoConvertContractError :: Prism' ContractError ToCardanoError +_TxToCardanoConvertContractError = prism' TxToCardanoConvertContractError case _ of + (TxToCardanoConvertContractError a) -> Just a + _ -> Nothing + _ResumableContractError :: Prism' ContractError MatchingError _ResumableContractError = prism' ResumableContractError case _ of (ResumableContractError a) -> Just a diff --git a/plutus-playground-client/generated/Wallet/Emulator/Chain.purs b/plutus-playground-client/generated/Wallet/Emulator/Chain.purs index aa5570be49..aeaa7aef94 100644 --- a/plutus-playground-client/generated/Wallet/Emulator/Chain.purs +++ b/plutus-playground-client/generated/Wallet/Emulator/Chain.purs @@ -17,7 +17,8 @@ import Data.Maybe (Maybe(..)) import Data.Newtype (unwrap) import Data.Show.Generic (genericShow) import Data.Tuple.Nested ((/\)) -import Ledger.Index (ScriptValidationEvent, ValidationError, ValidationPhase) +import Ledger.Index (ScriptValidationEvent) +import Ledger.Index.Internal (ValidationError, ValidationPhase) import Ledger.Slot (Slot) import Ledger.Tx (CardanoTx) import Plutus.V1.Ledger.Tx (TxId) diff --git a/plutus-playground-client/generated/Wallet/Emulator/Error.purs b/plutus-playground-client/generated/Wallet/Emulator/Error.purs index 822e5e168a..c627e87f10 100644 --- a/plutus-playground-client/generated/Wallet/Emulator/Error.purs +++ b/plutus-playground-client/generated/Wallet/Emulator/Error.purs @@ -20,7 +20,7 @@ import Data.Tuple.Nested ((/\)) import Ledger.Ada (Ada) import Ledger.Address (PaymentPubKeyHash) import Ledger.Constraints.OffChain (MkTxError) -import Ledger.Index (ValidationError) +import Ledger.Index.Internal (ValidationError) import Ledger.Tx.CardanoAPI (ToCardanoError) import Plutus.V1.Ledger.Value (Value) import Type.Proxy (Proxy(Proxy)) diff --git a/plutus-playground-client/generated/Wallet/Emulator/LogMessages.purs b/plutus-playground-client/generated/Wallet/Emulator/LogMessages.purs index a20dc31b92..fbf5032789 100644 --- a/plutus-playground-client/generated/Wallet/Emulator/LogMessages.purs +++ b/plutus-playground-client/generated/Wallet/Emulator/LogMessages.purs @@ -17,8 +17,10 @@ import Data.Maybe (Maybe(..)) import Data.Newtype (unwrap) import Data.Show.Generic (genericShow) import Data.Tuple.Nested ((/\)) +import Ledger.Ada (Ada) import Ledger.Constraints.OffChain (UnbalancedTx) -import Ledger.Index (ScriptValidationEvent, ValidationError, ValidationPhase) +import Ledger.Index (ScriptValidationEvent) +import Ledger.Index.Internal (ValidationError, ValidationPhase) import Ledger.Slot (Slot) import Ledger.Tx (CardanoTx) import Plutus.V1.Ledger.Address (Address) @@ -35,6 +37,7 @@ data RequestHandlerLogMsg | StartWatchingContractAddresses | HandleTxFailed WalletAPIError | UtxoAtFailed Address + | AdjustingUnbalancedTx (Array Ada) instance Show RequestHandlerLogMsg where show a = genericShow a @@ -45,6 +48,7 @@ instance EncodeJson RequestHandlerLogMsg where StartWatchingContractAddresses -> encodeJson { tag: "StartWatchingContractAddresses", contents: jsonNull } HandleTxFailed a -> E.encodeTagged "HandleTxFailed" a E.value UtxoAtFailed a -> E.encodeTagged "UtxoAtFailed" a E.value + AdjustingUnbalancedTx a -> E.encodeTagged "AdjustingUnbalancedTx" a E.value instance DecodeJson RequestHandlerLogMsg where decodeJson = defer \_ -> D.decode @@ -54,6 +58,7 @@ instance DecodeJson RequestHandlerLogMsg where , "StartWatchingContractAddresses" /\ pure StartWatchingContractAddresses , "HandleTxFailed" /\ D.content (HandleTxFailed <$> D.value) , "UtxoAtFailed" /\ D.content (UtxoAtFailed <$> D.value) + , "AdjustingUnbalancedTx" /\ D.content (AdjustingUnbalancedTx <$> D.value) ] derive instance Generic RequestHandlerLogMsg _ @@ -80,6 +85,11 @@ _UtxoAtFailed = prism' UtxoAtFailed case _ of (UtxoAtFailed a) -> Just a _ -> Nothing +_AdjustingUnbalancedTx :: Prism' RequestHandlerLogMsg (Array Ada) +_AdjustingUnbalancedTx = prism' AdjustingUnbalancedTx case _ of + (AdjustingUnbalancedTx a) -> Just a + _ -> Nothing + -------------------------------------------------------------------------------- data TxBalanceMsg diff --git a/plutus-playground-client/src/Playground/Lenses.purs b/plutus-playground-client/src/Playground/Lenses.purs index ce61e065d6..1a19f2ed8a 100644 --- a/plutus-playground-client/src/Playground/Lenses.purs +++ b/plutus-playground-client/src/Playground/Lenses.purs @@ -8,8 +8,8 @@ import Data.Lens.Iso.Newtype (_Newtype) import Data.Lens.Record (prop) import Data.Newtype (class Newtype) import Type.Proxy (Proxy(..)) -import Ledger.Index (UtxoIndex, _UtxoIndex) -import Plutus.V1.Ledger.Tx (TxOut, TxOutRef, TxId) +import Ledger.Index.Internal (UtxoIndex, _UtxoIndex) +import Plutus.V1.Ledger.Tx (TxId, TxOut, TxOutRef) import Plutus.V1.Ledger.Value (CurrencySymbol, TokenName, _CurrencySymbol, _TokenName) _currencySymbol :: Lens' CurrencySymbol String diff --git a/plutus-playground-server/usecases/Crowdfunding.hs b/plutus-playground-server/usecases/Crowdfunding.hs index 499f882310..0e6f9505bd 100644 --- a/plutus-playground-server/usecases/Crowdfunding.hs +++ b/plutus-playground-server/usecases/Crowdfunding.hs @@ -168,7 +168,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do tx = Constraints.mustPayToTheScript contributor contribValue <> Constraints.mustValidateIn (Interval.to (campaignDeadline cmp)) txid <- fmap getCardanoTxId $ mkTxConstraints (Constraints.typedValidatorLookups inst) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx utxo <- watchAddressUntilTime (Scripts.validatorAddress inst) (campaignCollectionDeadline cmp) @@ -185,7 +185,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do logInfo @Text "Claiming refund" void $ mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs utxo) tx' - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx else pure () -- | The campaign owner's branch of the contract for a given 'Campaign'. It diff --git a/plutus-playground-server/usecases/Vesting.hs b/plutus-playground-server/usecases/Vesting.hs index e4036b5e19..67040a8c96 100644 --- a/plutus-playground-server/usecases/Vesting.hs +++ b/plutus-playground-server/usecases/Vesting.hs @@ -166,7 +166,7 @@ vestFundsC vestFundsC vesting = do let txn = payIntoContract (totalAmount vesting) mkTxConstraints (Constraints.typedValidatorLookups $ typedValidator vesting) txn - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx data Liveness = Alive | Dead @@ -210,7 +210,7 @@ retrieveFundsC vesting payment = do -- transaction. mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs) txn - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx return liveness endpoints :: Contract () VestingSchema T.Text () diff --git a/plutus-streaming/README.md b/plutus-streaming/README.md new file mode 100644 index 0000000000..fab406ce3f --- /dev/null +++ b/plutus-streaming/README.md @@ -0,0 +1,42 @@ +# Plutus Streaming + +plutus-streaming is a simple library that wraps cardano-api giving a +streaming interface to the chain-sync protocol. This means you can use this +library to stream blocks from a locally running node. + +The blocks are presented as received from the chain-sync protocol so you +will still need to use function from `Cardano.Api` (in cardano-node) or +`Plutus.Ledger`(in plutus-ledger) to extract information from blocks. + +# Example applications + +## Example 1 + +This application simply streams to stdout events in JSON format. + +## Example 2 + +This application uses plutus-ledger to extract transactions (transaction +id, ins and outs) and datums. Then it prints them to stdout in JSON format. + +## Example 3 + +This application uses folds the stream into a UTxoState as defined in +plutus-chain-index-core. + +# Running the example applications + +You can run the example applications with cabal. Remember you need to have +a local node running and reachable through the provided socket. + +From Genesis + +``` +$ cabal run -- plutus-streaming-example-1 --socket-path /tmp/node.socket --mainnet +``` + +Passing a starting point + +``` +$ cabal run -- plutus-streaming-example-1 --socket-path /tmp/node.socket --slot-no 53427524 --block-hash 5e2bde4e504a9888a4f218dafc79a7619083f97d48684fcdba9dc78190df8f99 +``` diff --git a/plutus-streaming/examples/Common.hs b/plutus-streaming/examples/Common.hs new file mode 100644 index 0000000000..fda74c185f --- /dev/null +++ b/plutus-streaming/examples/Common.hs @@ -0,0 +1,77 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +module Common where + +import Cardano.Api qualified +import Data.Aeson qualified +import Data.ByteString.Lazy.Char8 qualified +import Options.Applicative (Alternative ((<|>)), Parser, auto, execParser, flag', help, helper, info, long, metavar, + option, str, strOption, (<**>)) +import Orphans () +import Streaming.Prelude qualified as S + +-- +-- Options parsing +-- + +data Options = Options + { optionsSocketPath :: String, + optionsNetworkId :: Cardano.Api.NetworkId, + optionsChainPoint :: Cardano.Api.ChainPoint + } + deriving (Show) + +optionsParser :: Parser Options +optionsParser = + Options + <$> strOption (long "socket-path" <> help "Node socket path") + <*> networkIdParser + <*> chainPointParser + +networkIdParser :: Parser Cardano.Api.NetworkId +networkIdParser = + pMainnet' <|> fmap Cardano.Api.Testnet testnetMagicParser + where + pMainnet' :: Parser Cardano.Api.NetworkId + pMainnet' = + flag' + Cardano.Api.Mainnet + ( long "mainnet" + <> help "Use the mainnet magic id." + ) + +testnetMagicParser :: Parser Cardano.Api.NetworkMagic +testnetMagicParser = + Cardano.Api.NetworkMagic + <$> option + auto + ( long "testnet-magic" + <> metavar "NATURAL" + <> help "Specify a testnet magic id." + ) + +chainPointParser :: Parser Cardano.Api.ChainPoint +chainPointParser = + pure Cardano.Api.ChainPointAtGenesis + <|> ( Cardano.Api.ChainPoint + <$> option (Cardano.Api.SlotNo <$> auto) (long "slot-no" <> metavar "SLOT-NO") + <*> option str (long "block-hash" <> metavar "BLOCK-HASH") + ) + +parseOptions :: IO Options +parseOptions = execParser $ info (optionsParser <**> helper) mempty + +printJson :: Data.Aeson.ToJSON a => S.Stream (S.Of a) IO r -> IO r +printJson = S.mapM_ Data.ByteString.Lazy.Char8.putStrLn . S.map Data.Aeson.encode + +-- https://github.com/input-output-hk/cardano-node/pull/3665 +workaround :: + (Cardano.Api.IsCardanoEra era => Cardano.Api.EraInMode era Cardano.Api.CardanoMode -> a) -> + Cardano.Api.EraInMode era Cardano.Api.CardanoMode -> + a +workaround k Cardano.Api.ByronEraInCardanoMode = k Cardano.Api.ByronEraInCardanoMode +workaround k Cardano.Api.ShelleyEraInCardanoMode = k Cardano.Api.ShelleyEraInCardanoMode +workaround k Cardano.Api.AllegraEraInCardanoMode = k Cardano.Api.AllegraEraInCardanoMode +workaround k Cardano.Api.MaryEraInCardanoMode = k Cardano.Api.MaryEraInCardanoMode +workaround k Cardano.Api.AlonzoEraInCardanoMode = k Cardano.Api.AlonzoEraInCardanoMode + diff --git a/plutus-streaming/examples/Example1.hs b/plutus-streaming/examples/Example1.hs new file mode 100644 index 0000000000..0a107eb4ad --- /dev/null +++ b/plutus-streaming/examples/Example1.hs @@ -0,0 +1,27 @@ +module Main where + +import Cardano.Api qualified +import Common (Options (Options, optionsChainPoint, optionsNetworkId, optionsSocketPath), parseOptions) +import Data.Aeson.Text qualified as Aeson +import Data.Text.Lazy qualified as TL +import Orphans () +import Plutus.Streaming (ChainSyncEvent (RollBackward, RollForward), withChainSyncEventStream) +import Streaming.Prelude qualified as S + +-- +-- Main +-- + +main :: IO () +main = do + Options {optionsSocketPath, optionsNetworkId, optionsChainPoint} <- parseOptions + + withChainSyncEventStream optionsSocketPath optionsNetworkId optionsChainPoint $ + S.stdoutLn + . S.map + ( \case + RollForward (Cardano.Api.BlockInMode (Cardano.Api.Block header _txs) _era) _ct -> + "RollForward, header: " <> TL.unpack (Aeson.encodeToLazyText header) + RollBackward cp _ct -> + "RollBackward, point: " <> TL.unpack (Aeson.encodeToLazyText cp) + ) diff --git a/plutus-streaming/examples/Example2.hs b/plutus-streaming/examples/Example2.hs new file mode 100644 index 0000000000..7360687c64 --- /dev/null +++ b/plutus-streaming/examples/Example2.hs @@ -0,0 +1,56 @@ +module Main where + +import Cardano.Api qualified +import Common (Options (Options, optionsChainPoint, optionsNetworkId, optionsSocketPath), parseOptions, printJson, + workaround) +import Data.Foldable (toList) +import Ledger qualified +import Orphans () +import Plutus.Script.Utils.V1.Scripts qualified +import Plutus.Streaming (withChainSyncEventStream) +import Streaming.Prelude qualified as S + +-- +-- Accessory functions +-- + +transactions :: + Cardano.Api.BlockInMode Cardano.Api.CardanoMode -> + [Ledger.CardanoTx] +transactions (Cardano.Api.BlockInMode (Cardano.Api.Block _ txs) eim) = + map (\tx -> Ledger.CardanoApiTx (workaround (Ledger.SomeTx tx) eim)) txs + +txInsAndOuts :: + Ledger.CardanoTx -> + (Ledger.TxId, [Ledger.TxOutRef], [Ledger.TxOutRef]) +txInsAndOuts tx = (txId, inRefs, outRefs) + where + txId = Ledger.getCardanoTxId tx + inRefs = map Ledger.txInRef $ toList $ Ledger.getCardanoTxInputs tx + outRefs = map snd $ Ledger.getCardanoTxOutRefs tx + +datums :: + Ledger.CardanoTx -> + [(Plutus.Script.Utils.V1.Scripts.DatumHash, Plutus.Script.Utils.V1.Scripts.Datum)] +datums tx = do + let txIns = toList $ Ledger.getCardanoTxInputs tx + (Ledger.TxIn _ (Just (Ledger.ConsumeScriptAddress _validator _redeemer datum))) <- txIns + pure (Plutus.Script.Utils.V1.Scripts.datumHash datum, datum) + +-- +-- Main +-- + +main :: IO () +main = do + Options {optionsSocketPath, optionsNetworkId, optionsChainPoint} <- parseOptions + + withChainSyncEventStream optionsSocketPath optionsNetworkId optionsChainPoint $ + printJson + . S.map -- Each ChainSyncEvent + ( fmap -- Inside the payload of RollForward events + ( fmap -- Each transaction + (\tx -> (txInsAndOuts tx, datums tx)) + . transactions + ) + ) diff --git a/plutus-streaming/examples/Example3.hs b/plutus-streaming/examples/Example3.hs new file mode 100644 index 0000000000..f9bcb73e0f --- /dev/null +++ b/plutus-streaming/examples/Example3.hs @@ -0,0 +1,53 @@ +module Main where + +import Cardano.Api qualified +import Common (Options (Options, optionsChainPoint, optionsNetworkId, optionsSocketPath), parseOptions) +import Plutus.ChainIndex (TxUtxoBalance) +import Plutus.ChainIndex.Compatibility qualified as CI +import Plutus.ChainIndex.TxUtxoBalance qualified as TxUtxoBalance +import Plutus.ChainIndex.UtxoState (UtxoIndex, UtxoState) +import Plutus.ChainIndex.UtxoState qualified as UtxoState +import Plutus.Streaming (ChainSyncEvent (RollBackward, RollForward), withChainSyncEventStream) +import Streaming (Of, Stream) +import Streaming.Prelude qualified as S + +utxoState :: + Monad m => + Stream (Of (ChainSyncEvent (Cardano.Api.BlockInMode Cardano.Api.CardanoMode))) m r -> + Stream (Of (ChainSyncEvent (Cardano.Api.BlockInMode Cardano.Api.CardanoMode), UtxoState TxUtxoBalance)) m r +utxoState = + S.scanned step initial projection + where + step index (RollForward block _) = + case CI.fromCardanoBlock block of + Left err -> error ("FromCardanoError: " <> show err) + Right txs -> + let tip = CI.tipFromCardanoBlock block + balance = TxUtxoBalance.fromBlock tip txs + in case UtxoState.insert balance index of + Left err -> + error (show err) + Right (UtxoState.InsertUtxoSuccess newIndex _insertPosition) -> + newIndex + step index (RollBackward cardanoPoint _) = + let point = CI.fromCardanoPoint cardanoPoint + in case TxUtxoBalance.rollback point index of + Left err -> error (show err) + Right (UtxoState.RollbackResult _newTip rolledBackIndex) -> + rolledBackIndex + + initial :: UtxoIndex TxUtxoBalance + initial = mempty + + projection = UtxoState.utxoState + +-- +-- Main +-- + +main :: IO () +main = do + Options {optionsSocketPath, optionsNetworkId, optionsChainPoint} <- parseOptions + + withChainSyncEventStream optionsSocketPath optionsNetworkId optionsChainPoint $ + S.print . utxoState diff --git a/plutus-streaming/examples/Orphans.hs b/plutus-streaming/examples/Orphans.hs new file mode 100644 index 0000000000..571e4ba18e --- /dev/null +++ b/plutus-streaming/examples/Orphans.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module Orphans where + +import Cardano.Api (BlockHeader (BlockHeader), BlockNo, ChainPoint (ChainPoint, ChainPointAtGenesis), + HasTypeProxy (proxyToAsType), Hash, SerialiseAsRawBytes (deserialiseFromRawBytes), ToJSON) +import Data.ByteString.Base16 qualified as Base16 +import Data.ByteString.Char8 qualified as C8 +import Data.Proxy (Proxy (Proxy)) +import Data.String (IsString (fromString)) +import GHC.Generics (Generic) +import Plutus.Streaming (ChainSyncEvent) + +deriving instance Generic ChainPoint + +instance ToJSON ChainPoint + +instance ToJSON BlockNo + +deriving instance Generic BlockHeader + +instance ToJSON BlockHeader + +instance ToJSON a => ToJSON (ChainSyncEvent a) diff --git a/plutus-streaming/plutus-streaming.cabal b/plutus-streaming/plutus-streaming.cabal new file mode 100644 index 0000000000..52ac8d8922 --- /dev/null +++ b/plutus-streaming/plutus-streaming.cabal @@ -0,0 +1,93 @@ +cabal-version: 2.4 +name: plutus-streaming +version: 0.1.0.0 +author: Andrea Bedini +maintainer: andrea.bedini@iohk.io +extra-source-files: CHANGELOG.md + +common lang + default-language: Haskell2010 + default-extensions: + DeriveFoldable + DeriveFunctor + DeriveGeneric + DeriveLift + DeriveTraversable + ExplicitForAll + GeneralizedNewtypeDeriving + ImportQualifiedPost + LambdaCase + NamedFieldPuns + ScopedTypeVariables + StandaloneDeriving + ghc-options: + -Wall + -Widentities + -Wincomplete-record-updates + -Wincomplete-uni-patterns + -Wmissing-import-lists + -Wnoncanonical-monad-instances + -Wredundant-constraints + -Wunused-packages + +library + import: lang + hs-source-dirs: src + exposed-modules: + Plutus.Streaming + build-depends: + base >=4.9 && <5, + async, + cardano-api, + ouroboros-network, + stm, + streaming, + +executable plutus-streaming-example-1 + import: lang + hs-source-dirs: examples + main-is: Example1.hs + other-modules: Common, Orphans + build-depends: + plutus-streaming, + base >=4.9 && <5, + aeson, + base16-bytestring, + bytestring, + cardano-api, + optparse-applicative, + streaming, + text + +executable plutus-streaming-example-2 + import: lang + hs-source-dirs: examples + main-is: Example2.hs + other-modules: Common, Orphans + build-depends: + plutus-streaming, + base >=4.9 && <5, + aeson, + base16-bytestring, + bytestring, + cardano-api, + optparse-applicative, + plutus-ledger, + plutus-script-utils, + streaming, + +executable plutus-streaming-example-3 + import: lang + hs-source-dirs: examples + main-is: Example3.hs + other-modules: Common, Orphans + build-depends: + plutus-streaming, + base >=4.9 && <5, + aeson, + base16-bytestring, + bytestring, + cardano-api, + optparse-applicative, + plutus-chain-index-core, + streaming, diff --git a/plutus-streaming/src/Plutus/Streaming.hs b/plutus-streaming/src/Plutus/Streaming.hs new file mode 100644 index 0000000000..6acaa6eaa7 --- /dev/null +++ b/plutus-streaming/src/Plutus/Streaming.hs @@ -0,0 +1,129 @@ +module Plutus.Streaming + ( withChainSyncEventStream, + ChainSyncEvent (..), + ChainSyncEventException (..), + ) +where + +import Cardano.Api (BlockInMode, CardanoMode, ChainPoint, ChainSyncClient (ChainSyncClient), ChainTip, + ConsensusModeParams (CardanoModeParams), EpochSlots (EpochSlots), + LocalChainSyncClient (LocalChainSyncClient), + LocalNodeClientProtocols (LocalNodeClientProtocols, localChainSyncClient, localStateQueryClient, localTxSubmissionClient), + LocalNodeConnectInfo (LocalNodeConnectInfo, localConsensusModeParams, localNodeNetworkId, localNodeSocketPath), + NetworkId, connectToLocalNode) +import Cardano.Api.ChainSync.Client (ClientStIdle (SendMsgFindIntersect, SendMsgRequestNext), + ClientStIntersect (ClientStIntersect, recvMsgIntersectFound, recvMsgIntersectNotFound), + ClientStNext (ClientStNext, recvMsgRollBackward, recvMsgRollForward)) +import Control.Concurrent.Async (link, withAsync) +import Control.Concurrent.STM (TChan, atomically, dupTChan, newBroadcastTChanIO, readTChan, writeTChan) +import Control.Exception (Exception, throw) +import GHC.Generics (Generic) +import Streaming (Of, Stream) +import Streaming.Prelude qualified as S + +data ChainSyncEvent a + = RollForward a ChainTip + | RollBackward ChainPoint ChainTip + deriving (Show, Functor, Generic) + +data ChainSyncEventException + = NoIntersectionFound + deriving (Show) + +instance Exception ChainSyncEventException + +-- | `withChainSyncEventStream` uses the chain-sync mini-protocol to +-- connect to a locally running node and fetch blocks from the given +-- starting point. +withChainSyncEventStream :: + -- | Path to the node socket + FilePath -> + NetworkId -> + -- | The point on the chain to start streaming from + ChainPoint -> + -- | Stream consumer + (Stream (Of (ChainSyncEvent (BlockInMode CardanoMode))) IO r -> IO b) -> + IO b +withChainSyncEventStream socketPath networkId point consumer = do + -- The chain-sync client runs in a different thread and it will send us + -- block through this channel. + + -- By using newBroadcastTChan, messages can be garbage collected after + -- clients have seen them, preventing pile up. The only way to read a + -- broadcast channel is to duplicate it with dupTChan. See note at + -- `newBroadcastTChan`. + chan <- newBroadcastTChanIO + readerChannel <- atomically $ dupTChan chan + + let client = chainSyncStreamingClient point chan + + localNodeClientProtocols = + LocalNodeClientProtocols + { localChainSyncClient = LocalChainSyncClient client, + localTxSubmissionClient = Nothing, + localStateQueryClient = Nothing + } + + connectInfo = + LocalNodeConnectInfo + { localConsensusModeParams = CardanoModeParams epochSlots, + localNodeNetworkId = networkId, + localNodeSocketPath = socketPath + } + + -- FIXME this comes from the config file but Cardano.Api does not expose readNetworkConfig! + epochSlots = EpochSlots 40 + + clientThread = do + connectToLocalNode connectInfo localNodeClientProtocols + -- the only reason connectToLocalNode can terminate successfully is if it + -- doesn't find an intersection, we report that case to the + -- consumer as an exception + throw NoIntersectionFound + + withAsync clientThread $ \a -> do + -- Make sure all exceptions in the client thread are passed to the consumer thread + link a + -- Run the consumer + consumer $ S.repeatM $ atomically (readTChan readerChannel) + +-- | `chainSyncStreamingClient` is the client that connects to a local node +-- and runs the chain-sync mini-protocol. This client is fire-and-forget +-- and does not require any control. +-- +-- Blocks obtained from the chain-sync mini-protocol are passed to a +-- consumer through a channel. +-- +-- If the starting point is such that an intersection cannot be found, this +-- client will throw a NoIntersectionFound exception. +chainSyncStreamingClient :: + ChainPoint -> + TChan (ChainSyncEvent e) -> + ChainSyncClient e ChainPoint ChainTip IO () +chainSyncStreamingClient point chan = + ChainSyncClient $ pure $ SendMsgFindIntersect [point] onIntersect + where + onIntersect = + ClientStIntersect + { recvMsgIntersectFound = \_ _ -> + ChainSyncClient sendRequestNext, + recvMsgIntersectNotFound = \_ -> + ChainSyncClient $ + -- There is nothing we can do here + throw NoIntersectionFound + } + + sendRequestNext = + pure $ SendMsgRequestNext onNext (pure onNext) + where + onNext = + ClientStNext + { recvMsgRollForward = \bim ct -> + ChainSyncClient $ do + atomically $ writeTChan chan (RollForward bim ct) + sendRequestNext, + recvMsgRollBackward = \cp ct -> + ChainSyncClient $ do + atomically $ writeTChan chan (RollBackward cp ct) + sendRequestNext + } diff --git a/plutus-use-cases/plutus-use-cases.cabal b/plutus-use-cases/plutus-use-cases.cabal index 4dd6f915c6..1b8cdfea4b 100644 --- a/plutus-use-cases/plutus-use-cases.cabal +++ b/plutus-use-cases/plutus-use-cases.cabal @@ -78,6 +78,7 @@ library mtl -any, plutus-core >= 1.0.0, plutus-tx >= 1.0.0, + plutus-tx-plugin >= 1.0.0, plutus-contract -any, playground-common -any, plutus-ledger -any, @@ -90,6 +91,7 @@ library prettyprinter -any, hashable -any, freer-simple -any, + streaming -any, semigroups -any, openapi3 -any diff --git a/plutus-use-cases/src/Plutus/Contracts/Crowdfunding.hs b/plutus-use-cases/src/Plutus/Contracts/Crowdfunding.hs index 935c7b817a..9aacb05a7b 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Crowdfunding.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Crowdfunding.hs @@ -209,7 +209,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do tx = Constraints.mustPayToTheScript contributor contribValue <> Constraints.mustValidateIn (Interval.to (campaignDeadline cmp)) txid <- fmap getCardanoTxId $ mkTxConstraints (Constraints.typedValidatorLookups inst) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx utxo <- watchAddressUntilTime (Scripts.validatorAddress inst) $ campaignCollectionDeadline cmp @@ -226,7 +226,7 @@ contribute cmp = endpoint @"contribute" $ \Contribution{contribValue} -> do logInfo @Text "Claiming refund" void $ mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs utxo) tx' - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx else pure () -- | The campaign owner's branch of the contract for a given 'Campaign'. It @@ -250,7 +250,7 @@ scheduleCollection cmp = endpoint @"schedule collection" $ \() -> do logInfo @Text "Collecting funds" void $ mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx -- | Call the "schedule collection" endpoint and instruct the campaign owner's -- wallet (wallet 1) to start watching the campaign address. diff --git a/plutus-use-cases/src/Plutus/Contracts/Escrow.hs b/plutus-use-cases/src/Plutus/Contracts/Escrow.hs index aa3d5e9ea5..e860eb3bde 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Escrow.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Escrow.hs @@ -261,8 +261,10 @@ pay inst escrow vl = do pk <- ownPaymentPubKeyHash let tx = Constraints.mustPayToTheScript pk vl <> Constraints.mustValidateIn (Ledger.interval 1 (escrowDeadline escrow)) - utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx - getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + mkTxConstraints (Constraints.typedValidatorLookups inst) tx + >>= adjustUnbalancedTx + >>= submitUnbalancedTx + >>= return . getCardanoTxId newtype RedeemSuccess = RedeemSuccess TxId deriving (Haskell.Eq, Haskell.Show) @@ -305,7 +307,8 @@ redeem inst escrow = mapError (review _EscrowError) $ do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs ) tx - RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + adjusted <- adjustUnbalancedTx utx + RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx adjusted newtype RefundSuccess = RefundSuccess TxId deriving newtype (Haskell.Eq, Haskell.Show, Generic) @@ -337,7 +340,8 @@ refund inst escrow = do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs ) tx' - RefundSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + adjusted <- adjustUnbalancedTx utx + RefundSuccess . getCardanoTxId <$> submitUnbalancedTx adjusted else throwing _RefundFailed () -- | Pay some money into the escrow contract. Then release all funds to their diff --git a/plutus-use-cases/src/Plutus/Contracts/Game.hs b/plutus-use-cases/src/Plutus/Contracts/Game.hs index 836c424431..66959d3532 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Game.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Game.hs @@ -51,8 +51,8 @@ import Ledger.Constraints qualified as Constraints import Ledger.Tx (ChainIndexTxOut (..)) import Ledger.Typed.Scripts qualified as Scripts import Playground.Contract (ToSchema) -import Plutus.Contract (AsContractError, Contract, Endpoint, Promise, collectFromScript, endpoint, fundsAtAddressGeq, - logInfo, mkTxConstraints, selectList, type (.\/), yieldUnbalancedTx) +import Plutus.Contract (AsContractError, Contract, Endpoint, Promise, adjustUnbalancedTx, collectFromScript, endpoint, + fundsAtAddressGeq, logInfo, mkTxConstraints, selectList, type (.\/), yieldUnbalancedTx) import Plutus.Script.Utils.V1.Address (mkValidatorAddress) import Plutus.V1.Ledger.Scripts (Datum (Datum), Validator) import PlutusTx qualified @@ -161,8 +161,7 @@ lock = endpoint @"lock" $ \LockArgs { lockArgsGameParam, lockArgsSecret, lockArg logInfo @Haskell.String $ "Pay " <> Haskell.show lockArgsValue <> " to the script" let lookups = Constraints.typedValidatorLookups (gameInstance lockArgsGameParam) tx = Constraints.mustPayToTheScript (hashString lockArgsSecret) lockArgsValue - unbalancedTx <- mkTxConstraints lookups tx - yieldUnbalancedTx $ Constraints.adjustUnbalancedTx unbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= yieldUnbalancedTx -- | The "guess" contract endpoint. See note [Contract endpoints] guess :: AsContractError e => Promise () GameSchema e () diff --git a/plutus-use-cases/src/Plutus/Contracts/GameStateMachine.hs b/plutus-use-cases/src/Plutus/Contracts/GameStateMachine.hs index 784603d07f..f0fccae05d 100644 --- a/plutus-use-cases/src/Plutus/Contracts/GameStateMachine.hs +++ b/plutus-use-cases/src/Plutus/Contracts/GameStateMachine.hs @@ -63,6 +63,7 @@ import Schema (ToSchema) import Prelude qualified as Haskell + -- | Datatype for creating a parameterized validator. data GameParam = GameParam { gameParamPayeePkh :: PaymentPubKeyHash diff --git a/plutus-use-cases/src/Plutus/Contracts/MultiSig.hs b/plutus-use-cases/src/Plutus/Contracts/MultiSig.hs index 838d8fed71..9ab4f4261b 100644 --- a/plutus-use-cases/src/Plutus/Contracts/MultiSig.hs +++ b/plutus-use-cases/src/Plutus/Contracts/MultiSig.hs @@ -81,7 +81,7 @@ lock = endpoint @"lock" $ \(ms, vl) -> do let tx = Constraints.mustPayToTheScript () vl lookups = Constraints.typedValidatorLookups inst mkTxConstraints lookups tx - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx -- | The @"unlock"@ endpoint, unlocking some funds with a list -- of signatures. @@ -94,4 +94,4 @@ unlock = endpoint @"unlock" $ \(ms, pks) -> do lookups = Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs utx mkTxConstraints lookups tx - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx diff --git a/plutus-use-cases/src/Plutus/Contracts/Prism/Mirror.hs b/plutus-use-cases/src/Plutus/Contracts/Prism/Mirror.hs index e9526902f7..c6b9357122 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Prism/Mirror.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Prism/Mirror.hs @@ -80,7 +80,7 @@ createTokens authority = endpoint @"issue" $ \CredentialOwnerReference{coTokenNa <> Constraints.mustPayToPubKey pk (Ada.lovelaceValueOf 1) -- Add self-spend to force an input _ <- mapError CreateTokenTxError $ do mkTxConstraints @Scripts.Any lookups constraints - >>= submitTxConfirmed . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitTxConfirmed let stateMachine = StateMachine.mkMachineClient authority (mockWalletPaymentPubKeyHash coOwner) coTokenName void $ mapError StateMachineError $ SM.runInitialise stateMachine Active theToken @@ -98,7 +98,7 @@ revokeToken authority = endpoint @"revoke" $ \CredentialOwnerReference{coTokenNa Left{} -> return () -- Ignore invalid transitions Right StateMachineTransition{smtConstraints=constraints, smtLookups=lookups'} -> do mkTxConstraints (lookups <> lookups') constraints - >>= submitTxConfirmed . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitTxConfirmed --- -- Errors and Logging diff --git a/plutus-use-cases/src/Plutus/Contracts/PubKey.hs b/plutus-use-cases/src/Plutus/Contracts/PubKey.hs index f94a84ec84..98aa7a72e5 100644 --- a/plutus-use-cases/src/Plutus/Contracts/PubKey.hs +++ b/plutus-use-cases/src/Plutus/Contracts/PubKey.hs @@ -74,7 +74,7 @@ pubKeyContract pk vl = mapError (review _PubKeyError ) $ do tx = Constraints.mustPayToTheScript () vl ledgerTx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx _ <- awaitTxConfirmed (getCardanoTxId ledgerTx) let refs = Map.keys diff --git a/plutus-use-cases/src/Plutus/Contracts/SimpleEscrow.hs b/plutus-use-cases/src/Plutus/Contracts/SimpleEscrow.hs index 81b863ba6c..fbf4e7a72f 100644 --- a/plutus-use-cases/src/Plutus/Contracts/SimpleEscrow.hs +++ b/plutus-use-cases/src/Plutus/Contracts/SimpleEscrow.hs @@ -130,7 +130,7 @@ lockEp = endpoint @"lock" $ \params -> do tx = Constraints.mustPayToTheScript params (paying params) <> Constraints.mustValidateIn valRange void $ mkTxConstraints (Constraints.typedValidatorLookups escrowInstance) tx - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx -- | Attempts to redeem the 'Value' locked into this script by paying in from -- the callers address to the payee. @@ -153,10 +153,9 @@ redeemEp = endpoint @"redeem" redeem if time >= deadline params then throwing _RedeemFailed DeadlinePassed else do - utx <- Constraints.adjustUnbalancedTx - <$> mkTxConstraints ( Constraints.typedValidatorLookups escrowInstance + utx <- mkTxConstraints ( Constraints.typedValidatorLookups escrowInstance <> Constraints.unspentOutputs unspentOutputs - ) tx + ) tx >>= adjustUnbalancedTx RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx utx -- | Refunds the locked amount back to the 'payee'. @@ -171,10 +170,9 @@ refundEp = endpoint @"refund" refund if Constraints.modifiesUtxoSet tx then do - utx <- Constraints.adjustUnbalancedTx - <$> mkTxConstraints ( Constraints.typedValidatorLookups escrowInstance + utx <- mkTxConstraints ( Constraints.typedValidatorLookups escrowInstance <> Constraints.unspentOutputs unspentOutputs - ) tx + ) tx >>= adjustUnbalancedTx RefundSuccess . getCardanoTxId <$> submitUnbalancedTx utx else throwing _RefundFailed () diff --git a/plutus-use-cases/src/Plutus/Contracts/TokenAccount.hs b/plutus-use-cases/src/Plutus/Contracts/TokenAccount.hs index 9099dd4b5e..a530c9af16 100644 --- a/plutus-use-cases/src/Plutus/Contracts/TokenAccount.hs +++ b/plutus-use-cases/src/Plutus/Contracts/TokenAccount.hs @@ -44,8 +44,9 @@ import Data.Map qualified as Map import GHC.Generics (Generic) import Prettyprinter (Pretty) -import Plutus.Contract (AsContractError (_ContractError), Contract, ContractError, Endpoint, HasEndpoint, endpoint, - logInfo, mapError, mkTxConstraints, selectList, submitUnbalancedTx, type (.\/), utxosAt) +import Plutus.Contract (AsContractError (_ContractError), Contract, ContractError, Endpoint, HasEndpoint, + adjustUnbalancedTx, endpoint, logInfo, mapError, mkTxConstraints, selectList, + submitUnbalancedTx, type (.\/), utxosAt) import Plutus.Contract.Constraints (ScriptLookups, TxConstraints) import PlutusTx qualified @@ -163,7 +164,7 @@ pay account vl = do <> show account mapError (review _TAContractError) $ mkTxConstraints (Constraints.typedValidatorLookups inst) (payTx vl) - >>= submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitUnbalancedTx -- | Create a transaction that spends all outputs belonging to the 'Account'. redeemTx :: forall w s e. @@ -202,8 +203,7 @@ redeem -> Contract w s e CardanoTx redeem pk account = mapError (review _TokenAccountError) $ do (constraints, lookups) <- redeemTx account pk - utx <- mkTxConstraints lookups constraints - submitUnbalancedTx $ Constraints.adjustUnbalancedTx utx + mkTxConstraints lookups constraints >>= adjustUnbalancedTx >>= submitUnbalancedTx -- | @balance account@ returns the value of all unspent outputs that can be -- unlocked with @accountToken account@ diff --git a/plutus-use-cases/src/Plutus/Contracts/Tutorial/Escrow.hs b/plutus-use-cases/src/Plutus/Contracts/Tutorial/Escrow.hs index 8ed242e20f..4e29eff63a 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Tutorial/Escrow.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Tutorial/Escrow.hs @@ -239,8 +239,8 @@ pay :: pay inst _escrow vl = do pk <- ownPaymentPubKeyHash let tx = Constraints.mustPayToTheScript pk vl - utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx - getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx >>= adjustUnbalancedTx + getCardanoTxId <$> submitUnbalancedTx utx newtype RedeemSuccess = RedeemSuccess TxId deriving (Haskell.Eq, Haskell.Show) @@ -277,8 +277,8 @@ redeem inst escrow = mapError (review _EscrowError) $ do else do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx - RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx >>= adjustUnbalancedTx + RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx utx newtype RefundSuccess = RefundSuccess TxId deriving newtype (Haskell.Eq, Haskell.Show, Generic) @@ -308,8 +308,8 @@ refund inst _escrow = do then do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx' - RefundSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx' >>= adjustUnbalancedTx + RefundSuccess . getCardanoTxId <$> submitUnbalancedTx utx else throwing _RefundFailed () covIdx :: CoverageIndex diff --git a/plutus-use-cases/src/Plutus/Contracts/Tutorial/EscrowStrict.hs b/plutus-use-cases/src/Plutus/Contracts/Tutorial/EscrowStrict.hs index 58251efbbf..693065e8ba 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Tutorial/EscrowStrict.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Tutorial/EscrowStrict.hs @@ -246,8 +246,8 @@ pay :: pay inst _escrow vl = do pk <- ownPaymentPubKeyHash let tx = Constraints.mustPayToTheScript pk vl - utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx - getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + utx <- mkTxConstraints (Constraints.typedValidatorLookups inst) tx >>= adjustUnbalancedTx + getCardanoTxId <$> submitUnbalancedTx utx newtype RedeemSuccess = RedeemSuccess TxId deriving (Haskell.Eq, Haskell.Show) @@ -284,8 +284,8 @@ redeem inst escrow = mapError (review _EscrowError) $ do else do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx - RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx >>= adjustUnbalancedTx + RedeemSuccess . getCardanoTxId <$> submitUnbalancedTx utx newtype RefundSuccess = RefundSuccess TxId deriving newtype (Haskell.Eq, Haskell.Show, Generic) @@ -315,7 +315,7 @@ refund inst _escrow = do then do utx <- mkTxConstraints ( Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs - ) tx' - RefundSuccess . getCardanoTxId <$> submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + ) tx' >>= adjustUnbalancedTx + RefundSuccess . getCardanoTxId <$> submitUnbalancedTx utx else throwing _RefundFailed () diff --git a/plutus-use-cases/src/Plutus/Contracts/Uniswap/OffChain.hs b/plutus-use-cases/src/Plutus/Contracts/Uniswap/OffChain.hs index b32fc963ce..86ee76d1ea 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Uniswap/OffChain.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Uniswap/OffChain.hs @@ -44,7 +44,7 @@ import Data.Text (Text, pack) import Data.Void (Void, absurd) import Ledger (ChainIndexTxOut (PublicKeyChainIndexTxOut, ScriptChainIndexTxOut, _ciTxOutDatum), ciTxOutValue, pubKeyHashAddress) -import Ledger.Constraints as Constraints +import Ledger.Constraints as Constraints hiding (adjustUnbalancedTx) import Ledger.Typed.Scripts qualified as Scripts import Playground.Contract import Plutus.Contract as Contract @@ -200,7 +200,7 @@ start = do tx = mustPayToTheScript (Factory []) $ unitValue c mkTxConstraints (Constraints.typedValidatorLookups inst) tx - >>= submitTxConfirmed . adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitTxConfirmed void $ waitNSlots 1 logInfo @String $ printf "started Uniswap %s at address %s" (show us) (show $ uniswapAddress us) @@ -233,7 +233,7 @@ create us CreateParams{..} = do Constraints.mustMintValue (unitValue psC <> valueOf lC liquidity) <> Constraints.mustSpendScriptOutput oref (Redeemer $ PlutusTx.toBuiltinData $ Create lp) - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "created liquidity pool: " ++ show lp @@ -265,7 +265,7 @@ close us CloseParams{..} = do Constraints.mustSpendScriptOutput oref2 redeemer <> Constraints.mustIncludeDatum (Datum $ PlutusTx.toBuiltinData $ Pool lp liquidity) - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "closed liquidity pool: " ++ show lp @@ -299,7 +299,7 @@ remove us RemoveParams{..} = do Constraints.mustMintValue (negate lVal) <> Constraints.mustSpendScriptOutput oref redeemer - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "removed liquidity from pool: " ++ show lp @@ -343,7 +343,7 @@ add us AddParams{..} = do logInfo $ show lookups logInfo $ show tx - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "added liquidity to pool: " ++ show lp @@ -378,7 +378,7 @@ swap us SwapParams{..} = do tx = mustSpendScriptOutput oref (Redeemer $ PlutusTx.toBuiltinData Swap) <> Constraints.mustPayToTheScript (Pool lp liquidity) val - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "swapped with: " ++ show lp diff --git a/plutus-use-cases/src/Plutus/Contracts/Uniswap/Trace.hs b/plutus-use-cases/src/Plutus/Contracts/Uniswap/Trace.hs index 977652b1d7..9ccc014f0b 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Uniswap/Trace.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Uniswap/Trace.hs @@ -21,7 +21,7 @@ import Data.Semigroup qualified as Semigroup import Data.Void (Void) import Ledger import Ledger.Ada (adaSymbol, adaToken) -import Ledger.Constraints +import Ledger.Constraints hiding (adjustUnbalancedTx) import Ledger.Value qualified as Value import Plutus.Contract as Contract hiding (throwError) import Plutus.Contracts.Currency qualified as Currency @@ -74,7 +74,7 @@ setupTokens = do let pkh = mockWalletPaymentPubKeyHash w when (pkh /= ownPK) $ do mkTxConstraints @Void mempty (mustPayToPubKey pkh v) - >>= submitTxConfirmed . adjustUnbalancedTx + >>= adjustUnbalancedTx >>= submitTxConfirmed tell $ Just $ Semigroup.Last cur diff --git a/plutus-use-cases/src/Plutus/Contracts/Vesting.hs b/plutus-use-cases/src/Plutus/Contracts/Vesting.hs index 008082fbd1..3aa0a2b423 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Vesting.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Vesting.hs @@ -189,7 +189,7 @@ vestFundsC vestFundsC vesting = mapError (review _VestingError) $ do let tx = payIntoContract (totalAmount vesting) mkTxConstraints (Constraints.typedValidatorLookups $ typedValidator vesting) tx - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx data Liveness = Alive | Dead @@ -227,5 +227,5 @@ retrieveFundsC vesting payment = mapError (review _VestingError) $ do -- transaction. mkTxConstraints (Constraints.typedValidatorLookups inst <> Constraints.unspentOutputs unspentOutputs) tx - >>= void . submitUnbalancedTx . Constraints.adjustUnbalancedTx + >>= adjustUnbalancedTx >>= void . submitUnbalancedTx return liveness diff --git a/plutus-use-cases/test/Spec/Auction.hs b/plutus-use-cases/test/Spec/Auction.hs index 0130c0fd5b..adcaca971e 100644 --- a/plutus-use-cases/test/Spec/Auction.hs +++ b/plutus-use-cases/test/Spec/Auction.hs @@ -42,7 +42,6 @@ import Streaming.Prelude qualified as S import Wallet.Emulator.Folds qualified as Folds import Wallet.Emulator.Stream qualified as Stream -import Ledger qualified import Ledger.TimeSlot (SlotConfig) import Ledger.TimeSlot qualified as TimeSlot import Plutus.Contract.Test.ContractModel @@ -77,6 +76,7 @@ theToken = options :: CheckOptions options = defaultCheckOptionsContractModel & changeInitialWalletValue w1 ((<>) theToken) + & allowBigTransactions seller :: Contract AuctionOutput SellerSchema AuctionError () seller = auctionSeller (apAsset params) (apEndTime params) @@ -213,7 +213,7 @@ instance ContractModel AuctionModel where where p = s ^. contractState . phase b = s ^. contractState . currentBid - validBid = choose ((b+1) `max` Ada.getLovelace Ledger.minAdaTxOut, + validBid = choose ((b+1) `max` Ada.getLovelace (Ada.adaOf 2), b + Ada.getLovelace (Ada.adaOf 100)) precondition s Init = s ^. contractState . phase == NotStarted @@ -221,7 +221,7 @@ instance ContractModel AuctionModel where -- In order to place a bid, we need to satisfy the constraint where -- each tx output must have at least N Ada. s ^. contractState . phase /= NotStarted && - bid >= Ada.getLovelace (Ledger.minAdaTxOut) && + bid >= Ada.getLovelace (Ada.adaOf 2) && bid > s ^. contractState . currentBid nextReactiveState slot' = do @@ -231,7 +231,7 @@ instance ContractModel AuctionModel where w <- viewContractState winner bid <- viewContractState currentBid phase .= AuctionOver - deposit w $ Ada.toValue Ledger.minAdaTxOut <> theToken + deposit w theToken deposit w1 $ Ada.lovelaceValueOf bid {- w1change <- viewModelState $ balanceChange w1 -- since the start of the test @@ -244,7 +244,7 @@ instance ContractModel AuctionModel where case cmd of Init -> do phase .= Bidding - withdraw w1 $ Ada.toValue Ledger.minAdaTxOut <> theToken + withdraw w1 theToken wait 3 Bid w bid -> do currentPhase <- viewContractState phase @@ -340,8 +340,8 @@ tests = (assertDone seller (Trace.walletInstanceTag w1) (const True) "seller should be done" .&&. assertDone (buyer threadToken) (Trace.walletInstanceTag w2) (const True) "buyer should be done" .&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w2) ((==) trace1FinalState ) "wallet 2 final state should be OK" - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.toValue trace1WinningBid <> inv theToken) - .&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> inv (Ada.toValue trace1WinningBid) <> theToken)) + .&&. walletFundsChange w1 (Ada.toValue trace1WinningBid <> inv theToken) + .&&. walletFundsChange w2 (inv (Ada.toValue trace1WinningBid) <> theToken)) auctionTrace1 , checkPredicateOptions options "run an auction with multiple bids" (assertDone seller (Trace.walletInstanceTag w1) (const True) "seller should be done" @@ -349,8 +349,8 @@ tests = .&&. assertDone (buyer threadToken) (Trace.walletInstanceTag w3) (const True) "3rd party should be done" .&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w2) ((==) trace2FinalState) "wallet 2 final state should be OK" .&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w3) ((==) trace2FinalState) "wallet 3 final state should be OK" - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.toValue trace2WinningBid <> inv theToken) - .&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> inv (Ada.toValue trace2WinningBid) <> theToken) + .&&. walletFundsChange w1 (Ada.toValue trace2WinningBid <> inv theToken) + .&&. walletFundsChange w2 (inv (Ada.toValue trace2WinningBid) <> theToken) .&&. walletFundsChange w3 mempty) auctionTrace2 , testProperty "QuickCheck property" $ diff --git a/plutus-use-cases/test/Spec/Crowdfunding.hs b/plutus-use-cases/test/Spec/Crowdfunding.hs index feee981bf1..5342eade59 100644 --- a/plutus-use-cases/test/Spec/Crowdfunding.hs +++ b/plutus-use-cases/test/Spec/Crowdfunding.hs @@ -78,7 +78,7 @@ tests = testGroup "crowdfunding" slotCfg <- Trace.getSlotConfig void (Trace.activateContractWallet w1 $ theContract $ TimeSlot.scSlotZeroTime slotCfg) - , checkPredicateOptions defaultCheckOptions "make contribution" + , checkPredicate "make contribution" (walletFundsChange w1 (Ada.adaValueOf (-10))) $ let contribution = Ada.adaValueOf 10 in makeContribution w1 contribution >> void Trace.nextSlot diff --git a/plutus-use-cases/test/Spec/Escrow.hs b/plutus-use-cases/test/Spec/Escrow.hs index 4ff248d0b2..35ef75f879 100644 --- a/plutus-use-cases/test/Spec/Escrow.hs +++ b/plutus-use-cases/test/Spec/Escrow.hs @@ -48,6 +48,9 @@ makeLenses ''EscrowModel modelParams :: EscrowParams d modelParams = escrowParams $ TimeSlot.scSlotZeroTime def +options :: CheckOptions +options = defaultCheckOptionsContractModel & allowBigTransactions + deriving instance Eq (ContractInstanceKey EscrowModel w s e params) deriving instance Show (ContractInstanceKey EscrowModel w s e params) @@ -152,7 +155,7 @@ testWallets :: [Wallet] testWallets = [w1, w2, w3, w4, w5] -- removed five to increase collisions (, w6, w7, w8, w9, w10]) prop_Escrow :: Actions EscrowModel -> Property -prop_Escrow = propRunActions_ +prop_Escrow = propRunActionsWithOptions options defaultCoverageOptions (\ _ -> pure True) finishEscrow :: DL EscrowModel () finishEscrow = do @@ -177,13 +180,13 @@ noLockProof = defaultNLFP , nlfpWalletStrategy = finishingStrategy . (==) } prop_NoLockedFunds :: Property -prop_NoLockedFunds = checkNoLockedFundsProof noLockProof +prop_NoLockedFunds = checkNoLockedFundsProofWithOptions options noLockProof tests :: TestTree tests = testGroup "escrow" [ let con = void $ payEp @() @EscrowSchema @EscrowError (escrowParams startTime) in - checkPredicate "can pay" + checkPredicateOptions options "can pay" ( assertDone con (Trace.walletInstanceTag w1) (const True) "escrow pay not done" .&&. walletFundsChange w1 (Ada.adaValueOf (-10)) ) @@ -197,7 +200,7 @@ tests = testGroup "escrow" @EscrowError (escrowParams startTime)) (redeemEp (escrowParams startTime)) in - checkPredicate "can redeem" + checkPredicateOptions options "can redeem" ( assertDone con (Trace.walletInstanceTag w3) (const True) "escrow redeem not done" .&&. walletFundsChange w1 (Ada.adaValueOf (-10)) .&&. walletFundsChange w2 (Ada.adaValueOf 10) @@ -205,7 +208,7 @@ tests = testGroup "escrow" ) redeemTrace - , checkPredicate "can redeem even if more money than required has been paid in" + , checkPredicateOptions options "can redeem even if more money than required has been paid in" -- in this test case we pay in a total of 40 lovelace (10 more than required), for -- the same contract as before, requiring 10 lovelace to go to wallet 1 and 20 to @@ -235,7 +238,7 @@ tests = testGroup "escrow" @EscrowError (escrowParams startTime)) <> void (refundEp (escrowParams startTime)) in - checkPredicate "can refund" + checkPredicateOptions options "can refund" ( walletFundsChange w1 mempty .&&. assertDone con (Trace.walletInstanceTag w1) (const True) "refund should succeed") refundTrace diff --git a/plutus-use-cases/test/Spec/Escrow/Endpoints.hs b/plutus-use-cases/test/Spec/Escrow/Endpoints.hs index 407df2804d..f7378da92a 100644 --- a/plutus-use-cases/test/Spec/Escrow/Endpoints.hs +++ b/plutus-use-cases/test/Spec/Escrow/Endpoints.hs @@ -56,5 +56,5 @@ badRefund inst pk = do <> Constraints.unspentOutputs unspentOutputs ) tx' handleError (\err -> logError $ "Caught error: " ++ unpack err) $ - void $ submitUnbalancedTx (Constraints.adjustUnbalancedTx utx) + adjustUnbalancedTx utx >>= void . submitUnbalancedTx diff --git a/plutus-use-cases/test/Spec/Future.hs b/plutus-use-cases/test/Spec/Future.hs index 6c0178c3e3..0f1c1c49dc 100644 --- a/plutus-use-cases/test/Spec/Future.hs +++ b/plutus-use-cases/test/Spec/Future.hs @@ -48,6 +48,7 @@ options :: CheckOptions options = defaultCheckOptions & changeInitialWalletValue w1 (const $ Ada.adaValueOf 1000) & changeInitialWalletValue w2 (const $ Ada.adaValueOf 1000) + & allowBigTransactions tests :: TestTree tests = @@ -58,12 +59,10 @@ tests = $ void F.setupTokensTrace , checkPredicateOptions options "can initialise and obtain tokens" - ( walletFundsChange w1 ( Ada.toValue (-Ledger.minAdaTxOut) - <> scale (-1) (F.initialMargin $ theFuture startTime) + ( walletFundsChange w1 ( scale (-1) (F.initialMargin $ theFuture startTime) <> F.tokenFor Short testAccounts ) - .&&. walletFundsChange w2 ( Ada.toValue Ledger.minAdaTxOut - <> scale (-1) (F.initialMargin $ theFuture startTime) + .&&. walletFundsChange w2 ( scale (-1) (F.initialMargin $ theFuture startTime) <> F.tokenFor Long testAccounts ) ) diff --git a/plutus-use-cases/test/Spec/GameStateMachine.hs b/plutus-use-cases/test/Spec/GameStateMachine.hs index a987500475..12e41babd2 100644 --- a/plutus-use-cases/test/Spec/GameStateMachine.hs +++ b/plutus-use-cases/test/Spec/GameStateMachine.hs @@ -17,7 +17,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Spec.GameStateMachine - ( tests, successTrace, successTrace2, traceLeaveOneAdaInScript, failTrace + ( tests, successTrace, successTrace2, traceLeaveTwoAdaInScript, failTrace , runTestsWithCoverage , prop_Game, propGame', prop_GameWhitelist , check_prop_Game_with_coverage @@ -61,6 +61,9 @@ import PlutusTx.Coverage gameParam :: G.GameParam gameParam = G.GameParam (mockWalletPaymentPubKeyHash w1) (TimeSlot.scSlotZeroTime def) +options :: CheckOptions +options = defaultCheckOptionsContractModel & allowBigTransactions + -- -- * QuickCheck model @@ -144,8 +147,6 @@ instance ContractModel GameModel where nextState (GiveToken w) = do w0 <- fromJust <$> viewContractState hasToken - withdraw w0 $ Ada.toValue Ledger.minAdaTxOut - deposit w $ Ada.toValue Ledger.minAdaTxOut transfer w0 w guessTokenVal hasToken .= Just w wait 1 @@ -201,7 +202,7 @@ instance CrashTolerance GameModel where -- | The main property. 'propRunActions_' checks that balances match the model after each test. prop_Game :: Actions GameModel -> Property -prop_Game = propRunActions_ +prop_Game = propRunActionsWithOptions options defaultCoverageOptions (\ _ -> pure True) prop_GameWhitelist :: Actions GameModel -> Property prop_GameWhitelist = checkErrorWhitelist defaultWhitelist @@ -230,7 +231,7 @@ propGame' l = propRunActionsWithOptions (\ _ -> pure True) prop_GameCrashTolerance :: Actions (WithCrashTolerance GameModel) -> Property -prop_GameCrashTolerance = propRunActions_ +prop_GameCrashTolerance = propRunActionsWithOptions options defaultCoverageOptions (\ _ -> pure True) wallets :: [Wallet] wallets = [w1, w2, w3] @@ -277,7 +278,7 @@ noLockedFunds = do w <- forAllQ $ elementsQ wallets secret <- viewContractState currentSecret val <- viewContractState gameValue - when (val > Ada.getLovelace Ledger.minAdaTxOut) $ do + when (val >= Ada.getLovelace Ledger.minAdaTxOut) $ do monitor $ label "Unlocking funds" action $ GiveToken w action $ Guess w secret "" val @@ -307,35 +308,35 @@ noLockProof = defaultNLFP { when hasTok $ action (Guess w secret "" val) prop_CheckNoLockedFundsProof :: Property -prop_CheckNoLockedFundsProof = checkNoLockedFundsProof noLockProof +prop_CheckNoLockedFundsProof = checkNoLockedFundsProofWithOptions options noLockProof -- * Unit tests tests :: TestTree tests = testGroup "game state machine with secret arguments tests" - [ checkPredicate "run a successful game trace" - (walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> Ada.adaValueOf 3 <> guessTokenVal) + [ checkPredicateOptions options "run a successful game trace" + (walletFundsChange w2 (Ada.adaValueOf 3 <> guessTokenVal) .&&. valueAtAddress (Scripts.validatorAddress $ G.typedValidator gameParam) (Ada.adaValueOf 5 ==) - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.adaValueOf (-8))) + .&&. walletFundsChange w1 (Ada.adaValueOf (-8))) successTrace - , checkPredicate "run a 2nd successful game trace" + , checkPredicateOptions options "run a 2nd successful game trace" (walletFundsChange w2 (Ada.adaValueOf 3) .&&. valueAtAddress (Scripts.validatorAddress $ G.typedValidator gameParam) (Ada.adaValueOf 0 ==) - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.adaValueOf (-8)) - .&&. walletFundsChange w3 (Ada.toValue Ledger.minAdaTxOut <> Ada.adaValueOf 5 <> guessTokenVal)) + .&&. walletFundsChange w1 (Ada.adaValueOf (-8)) + .&&. walletFundsChange w3 (Ada.adaValueOf 5 <> guessTokenVal)) successTrace2 - , checkPredicate "run a successful game trace where we try to leave 1 Ada in the script address" - (walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> guessTokenVal) - .&&. valueAtAddress (Scripts.validatorAddress $ G.typedValidator gameParam) (Ada.toValue Ledger.minAdaTxOut ==)) - traceLeaveOneAdaInScript + , checkPredicateOptions options "run a successful game trace where we try to leave 1 Ada in the script address" + (walletFundsChange w1 (Ada.toValue (-2_000_000) <> guessTokenVal) + .&&. valueAtAddress (Scripts.validatorAddress $ G.typedValidator gameParam) (Ada.toValue 2_000_000 ==)) + traceLeaveTwoAdaInScript - , checkPredicate "run a failed trace" - (walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> guessTokenVal) + , checkPredicateOptions options "run a failed trace" + (walletFundsChange w2 (Ada.toValue 2_000_000 <> guessTokenVal) .&&. valueAtAddress (Scripts.validatorAddress $ G.typedValidator gameParam) (Ada.adaValueOf 8 ==) - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.adaValueOf (-8))) + .&&. walletFundsChange w1 (Ada.toValue (-2_000_000) <> Ada.adaValueOf (-8))) failTrace , goldenPir "test/Spec/gameStateMachine.pir" $$(PlutusTx.compile [|| mkValidator ||]) @@ -419,13 +420,13 @@ successTrace2 = do void $ Trace.waitNSlots 1 -- | Tests whether the contract correctly handles the case where we leave less --- than 2 Ada in the script address after guessing. -traceLeaveOneAdaInScript :: EmulatorTrace () -traceLeaveOneAdaInScript = do +-- than 3 Ada in the script address after guessing. +traceLeaveTwoAdaInScript :: EmulatorTrace () +traceLeaveTwoAdaInScript = do hdl <- Trace.activateContractWallet w1 G.contract Trace.callEndpoint @"lock" hdl LockArgs { lockArgsGameParam = gameParam , lockArgsSecret = secretArg "hello" - , lockArgsValue = Ada.adaValueOf 8 + , lockArgsValue = Ada.adaValueOf 9 } _ <- Trace.waitNSlots 2 _ <- Trace.callEndpoint @"guess" hdl GuessArgs { guessArgsGameParam = gameParam @@ -445,7 +446,7 @@ failTrace = do , lockArgsValue = Ada.adaValueOf 8 } _ <- Trace.waitNSlots 2 - _ <- Trace.payToWallet w1 w2 guessTokenVal + _ <- Trace.payToWallet w1 w2 (Ada.toValue 2_000_000 <> guessTokenVal) _ <- Trace.waitNSlots 1 hdl2 <- Trace.activateContractWallet w2 G.contract _ <- Trace.callEndpoint @"guess" hdl2 GuessArgs { guessArgsGameParam = gameParam diff --git a/plutus-use-cases/test/Spec/Governance.hs b/plutus-use-cases/test/Spec/Governance.hs index aba001539c..49fe313bbb 100644 --- a/plutus-use-cases/test/Spec/Governance.hs +++ b/plutus-use-cases/test/Spec/Governance.hs @@ -9,7 +9,7 @@ {-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} module Spec.Governance(tests, doVoting) where -import Control.Lens (view) +import Control.Lens (view, (&)) import Control.Monad (void) import Data.Foldable (traverse_) import Data.Maybe (listToMaybe) @@ -31,17 +31,17 @@ import Test.Tasty.HUnit qualified as HUnit tests :: TestTree tests = testGroup "governance tests" - [ checkPredicate "vote all in favor, 2 rounds - SUCCESS" + [ checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "vote all in favor, 2 rounds - SUCCESS" (assertNoFailedTransactions .&&. dataAtAddress (Scripts.validatorAddress $ Gov.typedValidator params) (maybe False ((== lawv3) . Gov.law) . listToMaybe)) (doVoting 10 0 2) - , checkPredicate "vote 60/40, accepted - SUCCESS" + , checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "vote 60/40, accepted - SUCCESS" (assertNoFailedTransactions .&&. dataAtAddress (Scripts.validatorAddress $ Gov.typedValidator params) (maybe False ((== lawv2) . Gov.law) . listToMaybe)) (doVoting 6 4 1) - , checkPredicate "vote 50/50, rejected - SUCCESS" + , checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "vote 50/50, rejected - SUCCESS" (assertNoFailedTransactions .&&. dataAtAddress (Scripts.validatorAddress $ Gov.typedValidator params) (maybe False ((== lawv1) . Gov.law) . listToMaybe )) (doVoting 5 5 1) diff --git a/plutus-use-cases/test/Spec/Prism.hs b/plutus-use-cases/test/Spec/Prism.hs index eaea9f9eed..9539da9584 100644 --- a/plutus-use-cases/test/Spec/Prism.hs +++ b/plutus-use-cases/test/Spec/Prism.hs @@ -22,7 +22,6 @@ import Control.Monad import Data.Data import Data.Map (Map) import Data.Map qualified as Map -import Ledger (minAdaTxOut) import Ledger.Ada qualified as Ada import Ledger.Value (TokenName) import Plutus.Contract.Test hiding (not) @@ -164,12 +163,9 @@ instance ContractModel PrismModel where wait waitSlots case cmd of Revoke w -> do - issued <- use (isIssued w) - when (issued == Issued) $ deposit mirror minAdaTxOut isIssued w %= doRevoke Issue w -> do wait 1 - withdraw mirror minAdaTxOut isIssued w .= Issued Call w -> do iss <- (== Issued) <$> viewContractState (isIssued w) diff --git a/plutus-use-cases/test/Spec/SealedBidAuction.hs b/plutus-use-cases/test/Spec/SealedBidAuction.hs index e28a1c6a20..90d3c47273 100644 --- a/plutus-use-cases/test/Spec/SealedBidAuction.hs +++ b/plutus-use-cases/test/Spec/SealedBidAuction.hs @@ -17,7 +17,6 @@ import Data.Data import Data.Default (Default (def)) import Ledger (Slot (..), Value) -import Ledger qualified import Ledger.Ada qualified as Ada import Ledger.TimeSlot qualified as TimeSlot import Plutus.Contract.Secrets @@ -98,9 +97,9 @@ instance ContractModel AuctionModel where arbitraryAction s | p /= NotStarted = - frequency$[ (40, Bid <$> elements [w2, w3, w4] <*> choose (Ada.getLovelace Ledger.minAdaTxOut, 100_000_000)) + frequency$[ (40, Bid <$> elements [w2, w3, w4] <*> choose (2_000_000, 100_000_000)) -- Random reveal - , (20, Reveal <$> elements [w2, w3, w4] <*> choose (Ada.getLovelace Ledger.minAdaTxOut, 100_000_000)) + , (20, Reveal <$> elements [w2, w3, w4] <*> choose (2_000_000, 100_000_000)) ] ++ -- Correct reveal [ (20, uncurry Reveal <$> elements [ (w,i) | (i,w) <- s ^. contractState . currentBids ]) @@ -128,10 +127,10 @@ instance ContractModel AuctionModel where Bid w v -> s ^. contractState . phase == Bidding && w `notElem` fmap snd (s ^. contractState . currentBids) - && v >= Ada.getLovelace Ledger.minAdaTxOut + && v >= 2_000_000 Reveal _ v -> s ^. contractState . phase == AwaitingPayout - && v >= Ada.getLovelace Ledger.minAdaTxOut + && v >= 2_000_000 Payout _ -> s ^. contractState . phase == PayoutTime @@ -168,7 +167,7 @@ instance ContractModel AuctionModel where Init ebS pS -> do endBidSlot .= ebS payoutSlot .= pS - withdraw w1 $ Ada.toValue Ledger.minAdaTxOut <> theToken + withdraw w1 theToken phase .= Bidding wait 3 @@ -197,11 +196,10 @@ instance ContractModel AuctionModel where mwinningBid <- viewContractState currentWinningBid case mwinningBid of Just (bid, winner) -> do - deposit winner $ Ada.toValue Ledger.minAdaTxOut <> theToken + deposit winner theToken deposit w1 $ Ada.lovelaceValueOf bid - Nothing -> do - deposit w1 $ Ada.toValue Ledger.minAdaTxOut <> theToken + Nothing -> deposit w1 theToken wait 1 phase $= AuctionOver diff --git a/plutus-use-cases/test/Spec/Stablecoin.hs b/plutus-use-cases/test/Spec/Stablecoin.hs index 6e96e0ad2c..81372ba674 100644 --- a/plutus-use-cases/test/Spec/Stablecoin.hs +++ b/plutus-use-cases/test/Spec/Stablecoin.hs @@ -12,7 +12,7 @@ module Spec.Stablecoin( ) where -import Control.Lens (preview) +import Control.Lens (preview, (&)) import Control.Monad (void) import Data.Maybe (listToMaybe, mapMaybe) import Prelude hiding (Rational, negate) @@ -76,14 +76,14 @@ initialFee = Ada.lovelaceValueOf 100_000 -- Defined as 1% of initialDeposit tests :: TestTree tests = testGroup "Stablecoin" - [ checkPredicate "mint reservecoins" + [ checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "mint reservecoins" (valueAtAddress stablecoinAddress (== (initialDeposit <> initialFee)) .&&. assertNoFailedTransactions .&&. walletFundsChange user (Stablecoin.reserveCoins coin 10_000_000 <> negate (initialDeposit <> initialFee)) ) $ initialise >>= mintReserveCoins (RC 10_000_000) one - , checkPredicate "mint reservecoins and stablecoins" + , checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "mint reservecoins and stablecoins" (valueAtAddress stablecoinAddress (== (initialDeposit <> initialFee <> Ada.lovelaceValueOf 5_050_000)) .&&. assertNoFailedTransactions .&&. walletFundsChange user (Stablecoin.stableCoins coin 5_000_000 <> Stablecoin.reserveCoins coin 10_000_000 <> negate (initialDeposit <> initialFee <> Ada.lovelaceValueOf 5_050_000)) @@ -94,7 +94,7 @@ tests = testGroup "Stablecoin" -- Mint 50 stablecoins at a rate of 1 Ada: 1 USD void $ mintStableCoins (SC 5_000_000) one hdl - , checkPredicate "mint reservecoins, stablecoins and redeem stablecoin at a different price" + , checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "mint reservecoins, stablecoins and redeem stablecoin at a different price" (valueAtAddress stablecoinAddress (== (initialDeposit <> initialFee <> Ada.lovelaceValueOf 1_090_000)) .&&. assertNoFailedTransactions .&&. walletFundsChange user (Stablecoin.stableCoins coin 3_000_000 <> Stablecoin.reserveCoins coin 10_000_000 <> negate (initialDeposit <> initialFee <> Ada.lovelaceValueOf 1_090_000)) @@ -102,7 +102,7 @@ tests = testGroup "Stablecoin" stablecoinTrace , let expectedLogMsg = "New state is invalid: MaxReserves {allowed = BC {unBC = Rational 20000000 1}, actual = BC {unBC = Rational 20173235 1}}. The transition is not allowed." in - checkPredicate "Cannot exceed the maximum reserve ratio" + checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "Cannot exceed the maximum reserve ratio" (valueAtAddress stablecoinAddress (== (initialDeposit <> initialFee <> Ada.lovelaceValueOf 5_050_000)) .&&. assertNoFailedTransactions .&&. assertInstanceLog (Trace.walletInstanceTag w1) ((==) (Just expectedLogMsg) . listToMaybe . reverse . mapMaybe (preview (eteEvent . cilMessage . _ContractLog))) diff --git a/plutus-use-cases/test/Spec/TokenAccount.hs b/plutus-use-cases/test/Spec/TokenAccount.hs index dbee30a865..d990f3e553 100644 --- a/plutus-use-cases/test/Spec/TokenAccount.hs +++ b/plutus-use-cases/test/Spec/TokenAccount.hs @@ -10,7 +10,6 @@ import Control.Monad (void) import Control.Monad.Freer (run) import Control.Monad.Freer.Error (runError) import Data.Default (Default (..)) -import Ledger qualified import Ledger.Ada qualified as Ada import Ledger.Value (TokenName, Value) import Plutus.Contract (Contract) @@ -48,8 +47,8 @@ tests = testGroup "token account" , checkPredicate "Transfer & redeem all funds" (assertNoFailedTransactions .&&. assertNotDone contract (Trace.walletInstanceTag w1) "contract should not have any errors" - .&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.adaValueOf (-10)) - .&&. walletFundsChange w2 (theToken <> Ada.toValue Ledger.minAdaTxOut <> Ada.adaValueOf 10) + .&&. walletFundsChange w1 (Ada.adaValueOf (-10)) + .&&. walletFundsChange w2 (theToken <> Ada.adaValueOf 10) ) tokenAccountTrace diff --git a/plutus-use-cases/test/Spec/Uniswap.hs b/plutus-use-cases/test/Spec/Uniswap.hs index dfb56d6057..656c60a638 100644 --- a/plutus-use-cases/test/Spec/Uniswap.hs +++ b/plutus-use-cases/test/Spec/Uniswap.hs @@ -90,7 +90,10 @@ open :: Getter PoolModel Bool open = to $ \ p -> p ^. coinAAmount > 0 -- If one is bigger than zero the other one is too prop_Uniswap :: Actions UniswapModel -> Property -prop_Uniswap = propRunActions_ +prop_Uniswap = propRunActionsWithOptions + (defaultCheckOptionsContractModel & allowBigTransactions) + defaultCoverageOptions + (\ _ -> pure True) deriving instance Eq (ContractInstanceKey UniswapModel w s e params) deriving instance Show (ContractInstanceKey UniswapModel w s e params) @@ -149,7 +152,7 @@ setupTokens = do let pkh = mockWalletPaymentPubKeyHash w when (pkh /= ownPK) $ do cs <- mkTxConstraints @Void mempty (mustPayToPubKey pkh v) - submitTxConfirmed . adjustUnbalancedTx $ cs + Contract.adjustUnbalancedTx cs >>= submitTxConfirmed tell $ Just $ Semigroup.Last cur where @@ -617,7 +620,7 @@ prop_Whitelist = checkErrorWhitelist defaultWhitelist tests :: TestTree tests = testGroup "uniswap" [ - checkPredicate "can create a liquidity pool and add liquidity" + checkPredicateOptions (defaultCheckOptions & allowBigTransactions) "can create a liquidity pool and add liquidity" (assertNotDone Uniswap.setupTokens (Trace.walletInstanceTag w1) "setupTokens contract should be still running" diff --git a/plutus-use-cases/test/Spec/Uniswap/Endpoints.hs b/plutus-use-cases/test/Spec/Uniswap/Endpoints.hs index 734e29026b..6783a8fb83 100644 --- a/plutus-use-cases/test/Spec/Uniswap/Endpoints.hs +++ b/plutus-use-cases/test/Spec/Uniswap/Endpoints.hs @@ -77,7 +77,7 @@ badRemove us BadRemoveParams{..} = do Constraints.mustMintValue (negate lVal) <> Constraints.mustSpendScriptOutput oref redeemer - mkTxConstraints lookups tx >>= submitTxConfirmed . adjustUnbalancedTx + mkTxConstraints lookups tx >>= Contract.adjustUnbalancedTx >>= submitTxConfirmed logInfo $ "removed liquidity from pool: " ++ show lp diff --git a/plutus-use-cases/test/Spec/crowdfundingEmulatorTestOutput.txt b/plutus-use-cases/test/Spec/crowdfundingEmulatorTestOutput.txt index ad9e2a7036..581b77650e 100644 --- a/plutus-use-cases/test/Spec/crowdfundingEmulatorTestOutput.txt +++ b/plutus-use-cases/test/Spec/crowdfundingEmulatorTestOutput.txt @@ -17,6 +17,8 @@ Slot 1: 00000000-0000-4000-8000-000000000002 {Wallet W[3]}: Receive endpoint call on 'contribute' for Object (fromList [("contents",Array [Object (fromList [("getEndpointDescription",String "contribute")]),Object (fromList [("unEndpointValue",Object (fromList [("contribValue",Object (fromList [("getValue",Array [Array [Object (fromList [("unCurrencySymbol",String "")]),Array [Array [Object (fromList [("unTokenName",String "")]),Number 1.0e7]]]])]))]))])]),("tag",String "ExposeEndpointResp")]) Slot 1: 00000000-0000-4000-8000-000000000002 {Wallet W[3]}: Contract log: String "Contributing Value (Map [(,Map [(\"\",10000000)])])" +Slot 1: 00000000-0000-4000-8000-000000000003 {Wallet W[4]}: + Contract instance started Slot 1: W[2]: Balancing an unbalanced transaction: Tx: Tx e5d62bd3972b09291f5f53899b24b71a2c2af83f747ea629e8dbbe6f950ce524: @@ -37,7 +39,7 @@ Slot 1: W[2]: Balancing an unbalanced transaction: Validity range: (-∞ , POSIXTime 1596059111000 ] Slot 1: W[2]: Finished balancing: - Tx 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035: + Tx cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6: {inputs: - ef0ca0fb043642529818003be5a6cac88aac499e4f8f1cbc3bdb35db2b7f6958!20 @@ -47,26 +49,24 @@ Slot 1: W[2]: Finished balancing: - ef0ca0fb043642529818003be5a6cac88aac499e4f8f1cbc3bdb35db2b7f6958!20 outputs: - - Value (Map [(,Map [("",9999394)])]) addressed to - PubKeyCredential: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c087f250b86193ca7 (no staking credential) - Value (Map [(,Map [("",10000000)])]) addressed to ScriptCredential: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f (no staking credential) + - Value (Map [(,Map [("",9817867)])]) addressed to + PubKeyCredential: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c087f250b86193ca7 (no staking credential) mint: Value (Map []) - fee: Value (Map [(,Map [("",606)])]) + fee: Value (Map [(,Map [("",182133)])]) mps: signatures: validity range: Interval {ivFrom = LowerBound NegInf True, ivTo = UpperBound (Finite (Slot {getSlot = 20})) False} data: "\128\164\244[V\184\141\DC19\218#\188L\222\136\166\254\253 ="} -Slot 1: W[4]: Signing tx: 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de -Slot 1: W[4]: Submitting tx: 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de -Slot 1: W[4]: TxSubmit: 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de -Slot 1: TxnValidate 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de -Slot 1: TxnValidate c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85 -Slot 1: TxnValidate 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035 +Slot 1: W[4]: Signing tx: 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 +Slot 1: W[4]: Submitting tx: 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 +Slot 1: W[4]: TxSubmit: 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 +Slot 1: TxnValidate 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 +Slot 1: TxnValidate b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee +Slot 1: TxnValidate cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6 Slot 20: 00000000-0000-4000-8000-000000000000 {Wallet W[1]}: Contract log: String "Collecting funds" Slot 20: W[1]: Balancing an unbalanced transaction: Tx: - Tx 1b206dcca023dced8b2a18a9417042dbad0d49ac9c9c48334a23a44bad5282df: + Tx 4ba237be3ec4dd86316c6c491aaca9f1470729fb9b93f503afe03e713824f882: {inputs: - - 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035!1 + - 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4!0 <> - - 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de!1 + - b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee!0 <> - - c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85!1 + - cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6!0 <> collateral inputs: outputs: @@ -178,41 +178,41 @@ Slot 20: W[1]: Balancing an unbalanced transaction: data:} Requires signatures: Utxo index: - ( 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035!1 - , - Value (Map [(,Map [("",10000000)])]) addressed to - ScriptCredential: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f (no staking credential) ) - ( 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de!1 + ( 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4!0 , - Value (Map [(,Map [("",2500000)])]) addressed to ScriptCredential: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f (no staking credential) ) - ( c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85!1 + ( b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee!0 + , - Value (Map [(,Map [("",10000000)])]) addressed to + ScriptCredential: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f (no staking credential) ) + ( cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6!0 , - Value (Map [(,Map [("",10000000)])]) addressed to ScriptCredential: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f (no staking credential) ) Validity range: [ POSIXTime 1596059111000 , POSIXTime 1596059120999 ] Slot 20: W[1]: Finished balancing: - Tx 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186: + Tx 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616: {inputs: - - 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035!1 + - 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4!0 <> - - 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de!1 + - b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee!0 <> - - c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85!1 + - cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6!0 <> collateral inputs: - ef0ca0fb043642529818003be5a6cac88aac499e4f8f1cbc3bdb35db2b7f6958!50 outputs: - - Value (Map [(,Map [("",18602664)])]) addressed to + - Value (Map [(,Map [("",22199419)])]) addressed to PubKeyCredential: a2c20c77887ace1cd986193e4e75babd8993cfd56995cd5cfce609c2 (no staking credential) mint: Value (Map []) - fee: Value (Map [(,Map [("",3897336)])]) + fee: Value (Map [(,Map [("",300581)])]) mps: signatures: validity range: Interval {ivFrom = LowerBound (Finite (Slot {getSlot = 20})) True, ivTo = UpperBound (Finite (Slot {getSlot = 29})) True} data:} -Slot 20: W[1]: Signing tx: 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186 -Slot 20: W[1]: Submitting tx: 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186 -Slot 20: W[1]: TxSubmit: 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186 +Slot 20: W[1]: Signing tx: 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616 +Slot 20: W[1]: Submitting tx: 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616 +Slot 20: W[1]: TxSubmit: 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616 Slot 20: 00000000-0000-4000-8000-000000000000 {Wallet W[1]}: Contract instance stopped (no errors) -Slot 20: TxnValidate 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186 \ No newline at end of file +Slot 20: TxnValidate 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616 \ No newline at end of file diff --git a/plutus-use-cases/test/Spec/gameStateMachine.pir b/plutus-use-cases/test/Spec/gameStateMachine.pir index 579e91dbcc..d95f25ac41 100644 --- a/plutus-use-cases/test/Spec/gameStateMachine.pir +++ b/plutus-use-cases/test/Spec/gameStateMachine.pir @@ -3914,7 +3914,7 @@ ) (termbind (strict) - (vardecl minAdaTxOut (con integer)) + (vardecl minTxOut (con integer)) (con integer 2000000) ) (termbind @@ -7891,7 +7891,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] @@ -14767,7 +14767,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] diff --git a/plutus-use-cases/test/Spec/governance.pir b/plutus-use-cases/test/Spec/governance.pir index ff9344d23c..0c5b52c7cb 100644 --- a/plutus-use-cases/test/Spec/governance.pir +++ b/plutus-use-cases/test/Spec/governance.pir @@ -4604,7 +4604,7 @@ ) (termbind (strict) - (vardecl minAdaTxOut (con integer)) + (vardecl minTxOut (con integer)) (con integer 2000000) ) (termbind @@ -8879,7 +8879,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] @@ -15937,7 +15937,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] diff --git a/plutus-use-cases/test/Spec/multisigStateMachine.pir b/plutus-use-cases/test/Spec/multisigStateMachine.pir index 5b9e2e8ddf..6de571ec50 100644 --- a/plutus-use-cases/test/Spec/multisigStateMachine.pir +++ b/plutus-use-cases/test/Spec/multisigStateMachine.pir @@ -2461,9 +2461,7 @@ ) ) (termbind - (strict) - (vardecl minAdaTxOut (con integer)) - (con integer 2000000) + (strict) (vardecl minTxOut (con integer)) (con integer 2000000) ) (termbind (strict) @@ -8641,7 +8639,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] @@ -15637,7 +15635,7 @@ emptyByteString ] ] - minAdaTxOut + minTxOut ] ] ] diff --git a/plutus-use-cases/test/Spec/renderCrowdfunding.txt b/plutus-use-cases/test/Spec/renderCrowdfunding.txt index 2b1788a410..104074a960 100644 --- a/plutus-use-cases/test/Spec/renderCrowdfunding.txt +++ b/plutus-use-cases/test/Spec/renderCrowdfunding.txt @@ -551,11 +551,11 @@ Balances Carried Forward: Ada: Lovelace: 100000000 ==== Slot #1, Tx #0 ==== -TxId: 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de -Fee: Ada: Lovelace: 469 +TxId: 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 +Fee: Ada: Lovelace: 176105 Mint: - Signatures PubKey: c0a4b02f44c212ba6c1197df5a5cf8bd1a3dceef... - Signature: 5840aa6435bc909694691187be8e9aea0de70f77... + Signature: 58402a9d8396014485fc698b511a530c78503f7c... Inputs: ---- Input 0 ---- Destination: PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) @@ -569,14 +569,14 @@ Inputs: Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) + Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f Value: - Ada: Lovelace: 7499531 + Ada: Lovelace: 2500000 ---- Output 1 ---- - Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f + Destination: PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) Value: - Ada: Lovelace: 2500000 + Ada: Lovelace: 7323895 Balances Carried Forward: @@ -586,7 +586,7 @@ Balances Carried Forward: PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) Value: - Ada: Lovelace: 97499531 + Ada: Lovelace: 97323895 PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: @@ -625,11 +625,11 @@ Balances Carried Forward: Ada: Lovelace: 2500000 ==== Slot #1, Tx #1 ==== -TxId: c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85 -Fee: Ada: Lovelace: 606 +TxId: b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee +Fee: Ada: Lovelace: 182133 Mint: - Signatures PubKey: 4cdc632449cde98d811f78ad2e2d15a278731bc5... - Signature: 58403eb360882186bc0ff9e2ef2ea38a81f02f58... + Signature: 584089177d0a58482d00a384a42b9a452ae8f579... Inputs: ---- Input 0 ---- Destination: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) @@ -652,24 +652,24 @@ Inputs: Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) + Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f Value: - Ada: Lovelace: 9999394 + Ada: Lovelace: 10000000 ---- Output 1 ---- - Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f + Destination: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) Value: - Ada: Lovelace: 10000000 + Ada: Lovelace: 9817867 Balances Carried Forward: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) Value: - Ada: Lovelace: 89999394 + Ada: Lovelace: 89817867 PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) Value: - Ada: Lovelace: 97499531 + Ada: Lovelace: 97323895 PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: @@ -708,11 +708,11 @@ Balances Carried Forward: Ada: Lovelace: 12500000 ==== Slot #1, Tx #2 ==== -TxId: 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035 -Fee: Ada: Lovelace: 606 +TxId: cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6 +Fee: Ada: Lovelace: 182133 Mint: - Signatures PubKey: 98c77c40ccc536e0d433874dae97d4a0787b10b3... - Signature: 5840dcbbdaea0660a640bc5a25ae5075cc201584... + Signature: 5840031e4e0f06c73bc7039cd9966714870b202f... Inputs: ---- Input 0 ---- Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) @@ -735,28 +735,28 @@ Inputs: Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) + Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f Value: - Ada: Lovelace: 9999394 + Ada: Lovelace: 10000000 ---- Output 1 ---- - Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f + Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 10000000 + Ada: Lovelace: 9817867 Balances Carried Forward: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) Value: - Ada: Lovelace: 89999394 + Ada: Lovelace: 89817867 PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) Value: - Ada: Lovelace: 97499531 + Ada: Lovelace: 97323895 PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 89999394 + Ada: Lovelace: 89817867 PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) Value: @@ -791,28 +791,28 @@ Balances Carried Forward: Ada: Lovelace: 22500000 ==== Slot #2, Tx #0 ==== -TxId: 646651358b6c449be7b1735a7a53c3fe38d112b11f35cdb36e5b644ba5f57186 -Fee: Ada: Lovelace: 3897336 +TxId: 8f696523e027acdd20bcb1553236c928e30eaa0e259af28f4465cd59df6a7616 +Fee: Ada: Lovelace: 300581 Mint: - Signatures PubKey: 8d9de88fbf445b7f6c3875a14daba94caee2ffcb... - Signature: 584028e6ebf4a6a085050f50a94d3f618d145cc4... + Signature: 58402a3e78ffe24d389ea779ab0b73e21817a2b6... Inputs: ---- Input 0 ---- Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f Value: - Ada: Lovelace: 10000000 + Ada: Lovelace: 2500000 Source: - Tx: 73860734a9982d8a5887fb2fb20c0afea8a1a0b88f7cb94848b0027b0a45a035 - Output #1 + Tx: 31f38006481bbd0f6fa4ba7f2fb29856988990f264be0c35eee91dff98e879a4 + Output #0 Script: 590b0c0100003323232323232323232323232323... ---- Input 1 ---- Destination: Script: 8ff252c8c5423de7925e0c556871c90ed904ec3beb3dab519e9bfd9f Value: - Ada: Lovelace: 2500000 + Ada: Lovelace: 10000000 Source: - Tx: 917490a243478a79d9c76348a1d029cb7e5f7f468d9d726ff25387bda1bf09de - Output #1 + Tx: b130ba8f7233f5f15573b4831e1abd74fbea6f090c5f04619c34190ee4ca9dee + Output #0 Script: 590b0c0100003323232323232323232323232323... ---- Input 2 ---- @@ -820,8 +820,8 @@ Inputs: Value: Ada: Lovelace: 10000000 Source: - Tx: c13cfba1fda682eb3843a908b36782553743d2dbb42e1846ad697a29a6ffee85 - Output #1 + Tx: cb11f60209ec0a31a6f1fdbd2f30c24a23d8aec19c6647c57958eba563e6fcb6 + Output #0 Script: 590b0c0100003323232323232323232323232323... @@ -829,21 +829,21 @@ Outputs: ---- Output 0 ---- Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - Ada: Lovelace: 18602664 + Ada: Lovelace: 22199419 Balances Carried Forward: PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) Value: - Ada: Lovelace: 89999394 + Ada: Lovelace: 89817867 PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) Value: - Ada: Lovelace: 97499531 + Ada: Lovelace: 97323895 PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 89999394 + Ada: Lovelace: 89817867 PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) Value: @@ -855,7 +855,7 @@ Balances Carried Forward: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - Ada: Lovelace: 118602664 + Ada: Lovelace: 122199419 PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) Value: diff --git a/plutus-use-cases/test/Spec/renderGuess.txt b/plutus-use-cases/test/Spec/renderGuess.txt index b13225a8d0..09cbfddec3 100644 --- a/plutus-use-cases/test/Spec/renderGuess.txt +++ b/plutus-use-cases/test/Spec/renderGuess.txt @@ -551,11 +551,11 @@ Balances Carried Forward: Ada: Lovelace: 100000000 ==== Slot #1, Tx #0 ==== -TxId: 25d8a223540540f22a2482b77dae0027d42836f5ccef2e6381f39c4c3e6cda1a -Fee: Ada: Lovelace: 651 +TxId: 1734cabe3557c975b112e5d03ea10cbf46dcaf8f9ba0fc6f4ccc1235469fe81a +Fee: Ada: Lovelace: 184113 Mint: - Signatures PubKey: 8d9de88fbf445b7f6c3875a14daba94caee2ffcb... - Signature: 58409cd894e23cf42ea6e42e07297de608573065... + Signature: 5840baed5dc38d73afee8db85e59ebef1486a727... Inputs: ---- Input 0 ---- Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) @@ -578,104 +578,14 @@ Inputs: Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 11999349 - - ---- Output 1 ---- - Destination: Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 - - -Balances Carried Forward: - PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 97add5c3ca491534a1d81165f637d338e072d47e... (Wallet 4e76ce6b3f12c6cc5a6a2545f6770d2bcb360648) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 91999349 - - PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: bf342ddd3b1a6191d4ce936c92d29834d6879edf... (Wallet d3eddd0d37989746b029a0e050386bc425363901) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: c605888d3c40386d7c323a4679c767e5a0a7b683... (Wallet 1bc5f27d7b4e20083977418e839e429d00cc87f3) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: dfe12ac160d354b690385751a144e3010610fe5e... (Wallet c19599f22890ced15c6a87222302109e83b78bdf) - Value: - Ada: Lovelace: 100000000 - - Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 - -==== Slot #2, Tx #0 ==== -TxId: 65934fd993279a82f06e26985e9c46fae2be76ff90463472f3842de5b43e0eda -Fee: Ada: Lovelace: 293189 -Mint: 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 -Signatures PubKey: 8d9de88fbf445b7f6c3875a14daba94caee2ffcb... - Signature: 584050a05fca2ee6cf2ea19c8ff6ec5e6936d19b... -Inputs: - ---- Input 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 11999349 - Source: - Tx: 25d8a223540540f22a2482b77dae0027d42836f5ccef2e6381f39c4c3e6cda1a - Output #0 - - - ---- Input 1 ---- - Destination: Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d + Destination: Script: edd5ff978c24088b6945345fcec249c2da0bf22a0963514de7543d74 Value: Ada: Lovelace: 8000000 - Source: - Tx: 25d8a223540540f22a2482b77dae0027d42836f5ccef2e6381f39c4c3e6cda1a - Output #1 - Script: 59d72c0100003323232332232323232323232323... - - -Outputs: - ---- Output 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 9706160 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: - ---- Output 1 ---- Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - Ada: Lovelace: 2000000 - - ---- Output 2 ---- - Destination: Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 + Ada: Lovelace: 11815887 Balances Carried Forward: @@ -701,205 +611,7 @@ Balances Carried Forward: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - Ada: Lovelace: 91706160 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - - PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: bf342ddd3b1a6191d4ce936c92d29834d6879edf... (Wallet d3eddd0d37989746b029a0e050386bc425363901) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: c605888d3c40386d7c323a4679c767e5a0a7b683... (Wallet 1bc5f27d7b4e20083977418e839e429d00cc87f3) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: dfe12ac160d354b690385751a144e3010610fe5e... (Wallet c19599f22890ced15c6a87222302109e83b78bdf) - Value: - Ada: Lovelace: 100000000 - - Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 - -==== Slot #3, Tx #0 ==== -TxId: 11d0a6374009f1cb7f4130beb16085adcdcc095e9727d7a9d083718265da605c -Fee: Ada: Lovelace: 543 -Mint: - -Signatures PubKey: 8d9de88fbf445b7f6c3875a14daba94caee2ffcb... - Signature: 58407ead46b65cee151d450592931583c0914347... -Inputs: - ---- Input 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 9706160 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: - - Source: - Tx: 65934fd993279a82f06e26985e9c46fae2be76ff90463472f3842de5b43e0eda - Output #0 - - - ---- Input 1 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - Ada: Lovelace: 2000000 - Source: - Tx: 65934fd993279a82f06e26985e9c46fae2be76ff90463472f3842de5b43e0eda - Output #1 - - - -Outputs: - ---- Output 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 9705617 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 0 - - ---- Output 1 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - Ada: Lovelace: 2000000 - - -Balances Carried Forward: - PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - Ada: Lovelace: 102000000 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - - PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 97add5c3ca491534a1d81165f637d338e072d47e... (Wallet 4e76ce6b3f12c6cc5a6a2545f6770d2bcb360648) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 89705617 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 0 - - PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: bf342ddd3b1a6191d4ce936c92d29834d6879edf... (Wallet d3eddd0d37989746b029a0e050386bc425363901) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: c605888d3c40386d7c323a4679c767e5a0a7b683... (Wallet 1bc5f27d7b4e20083977418e839e429d00cc87f3) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: dfe12ac160d354b690385751a144e3010610fe5e... (Wallet c19599f22890ced15c6a87222302109e83b78bdf) - Value: - Ada: Lovelace: 100000000 - - Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 - -==== Slot #4, Tx #0 ==== -TxId: 3e64bb280eca2a99dee85cd2678fad79ec5b39f2037c1b49f1f72b769d55dfc2 -Fee: Ada: Lovelace: 352560 -Mint: - -Signatures PubKey: 98c77c40ccc536e0d433874dae97d4a0787b10b3... - Signature: 584098411d18e63fefed9bb28856695a71df2169... -Inputs: - ---- Input 0 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - Ada: Lovelace: 2000000 - Source: - Tx: 11d0a6374009f1cb7f4130beb16085adcdcc095e9727d7a9d083718265da605c - Output #1 - - - ---- Input 1 ---- - Destination: Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 8000000 - Source: - Tx: 65934fd993279a82f06e26985e9c46fae2be76ff90463472f3842de5b43e0eda - Output #2 - Script: 59d72c0100003323232332232323232323232323... - - ---- Input 2 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - Ada: Lovelace: 10000000 - Source: - Tx: ef0ca0fb043642529818003be5a6cac88aac499e4f8f1cbc3bdb35db2b7f6958 - Output #20 - - - -Outputs: - ---- Output 0 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 0 - Ada: Lovelace: 10647440 - - ---- Output 1 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - Ada: Lovelace: 2000000 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: - - - ---- Output 2 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - Ada: Lovelace: 2000000 - - ---- Output 3 ---- - Destination: Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d - Value: - Ada: Lovelace: 5000000 - - -Balances Carried Forward: - PaymentPubKeyHash: 2e0ad60c3207248cecd47dbde3d752e0aad141d6... (Wallet c30efb78b4e272685c1f9f0c93787fd4b6743154) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 557d23c0a533b4d295ac2dc14b783a7efc293bc2... (Wallet 5f5a4f5f465580a5500b9a9cede7f4e014a37ea8) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) - Value: - Ada: Lovelace: 104647440 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 1 - - PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: 97add5c3ca491534a1d81165f637d338e072d47e... (Wallet 4e76ce6b3f12c6cc5a6a2545f6770d2bcb360648) - Value: - Ada: Lovelace: 100000000 - - PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) - Value: - Ada: Lovelace: 89705617 - 8541cb8a39a09710d91d3c99a938135e803ad5d333974604450d19ce: guess: 0 + Ada: Lovelace: 91815887 PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) Value: @@ -917,6 +629,6 @@ Balances Carried Forward: Value: Ada: Lovelace: 100000000 - Script: c7f9f77b664ad503be56c8422e4b797f1920aa94aa6a0cbadb736d1d + Script: edd5ff978c24088b6945345fcec249c2da0bf22a0963514de7543d74 Value: - Ada: Lovelace: 5000000 \ No newline at end of file + Ada: Lovelace: 8000000 \ No newline at end of file diff --git a/plutus-use-cases/test/Spec/renderVesting.txt b/plutus-use-cases/test/Spec/renderVesting.txt index cb9dc6c370..5890c39b28 100644 --- a/plutus-use-cases/test/Spec/renderVesting.txt +++ b/plutus-use-cases/test/Spec/renderVesting.txt @@ -551,11 +551,11 @@ Balances Carried Forward: Ada: Lovelace: 100000000 ==== Slot #1, Tx #0 ==== -TxId: b01ea2cb09e58d1742302e980ac2dc366c79d7063afc22a3b010e333c8e31f7a -Fee: Ada: Lovelace: 1265 +TxId: ebaf00b674f5445f6c226ba6a210d3009a552c9fb8c5f09180efed1bd25b1aa3 +Fee: Ada: Lovelace: 211129 Mint: - Signatures PubKey: 98c77c40ccc536e0d433874dae97d4a0787b10b3... - Signature: 5840e9b98d0ea344f4c184b8a4f16c47391a89b4... + Signature: 58403e2531f531b24a60fb9391f466555c7227e7... Inputs: ---- Input 0 ---- Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) @@ -623,14 +623,14 @@ Inputs: Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) + Destination: Script: 40cfe3534a528c1b067dcea891c23024caa3cba0a080e327b9173366 Value: - Ada: Lovelace: 9998735 + Ada: Lovelace: 60000000 ---- Output 1 ---- - Destination: Script: 40cfe3534a528c1b067dcea891c23024caa3cba0a080e327b9173366 + Destination: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 60000000 + Ada: Lovelace: 9788871 Balances Carried Forward: @@ -644,7 +644,7 @@ Balances Carried Forward: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 39998735 + Ada: Lovelace: 39788871 PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) Value: @@ -679,32 +679,32 @@ Balances Carried Forward: Ada: Lovelace: 60000000 ==== Slot #2, Tx #0 ==== -TxId: 1415d7aa947df9c0e7f076200157c75d22ad3e112b0d8886876a4aea88ac150d -Fee: Ada: Lovelace: 1302220 +TxId: 09c74f30000710efcd5db926f6ca5af2ad6cf62d6586eda53374f23e0838ea12 +Fee: Ada: Lovelace: 452002 Mint: - Signatures PubKey: 8d9de88fbf445b7f6c3875a14daba94caee2ffcb... - Signature: 5840ecae543c6ceb328e32c9420974939e370738... + Signature: 5840938764f3411095cede88e88768c041baa0e3... Inputs: ---- Input 0 ---- Destination: Script: 40cfe3534a528c1b067dcea891c23024caa3cba0a080e327b9173366 Value: Ada: Lovelace: 60000000 Source: - Tx: b01ea2cb09e58d1742302e980ac2dc366c79d7063afc22a3b010e333c8e31f7a - Output #1 + Tx: ebaf00b674f5445f6c226ba6a210d3009a552c9fb8c5f09180efed1bd25b1aa3 + Output #0 Script: 590e820100003323232323232323232323232323... Outputs: ---- Output 0 ---- - Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) + Destination: Script: 40cfe3534a528c1b067dcea891c23024caa3cba0a080e327b9173366 Value: - Ada: Lovelace: 8697780 + Ada: Lovelace: 50000000 ---- Output 1 ---- - Destination: Script: 40cfe3534a528c1b067dcea891c23024caa3cba0a080e327b9173366 + Destination: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - Ada: Lovelace: 50000000 + Ada: Lovelace: 9547998 Balances Carried Forward: @@ -718,7 +718,7 @@ Balances Carried Forward: PaymentPubKeyHash: 80a4f45b56b88d1139da23bc4c3c75ec6d32943c... (Wallet 7ce812d7a4770bbf58004067665c3a48f28ddd58) Value: - Ada: Lovelace: 39998735 + Ada: Lovelace: 39788871 PaymentPubKeyHash: 8952ed1aff55f5b7674b122804a3c0a96f4e2863... (Wallet 3a4778247ad35117d7c3150d194da389f3148f4a) Value: @@ -730,7 +730,7 @@ Balances Carried Forward: PaymentPubKeyHash: a2c20c77887ace1cd986193e4e75babd8993cfd5... (Wallet 872cb83b5ee40eb23bfdab1772660c822a48d491) Value: - Ada: Lovelace: 108697780 + Ada: Lovelace: 109547998 PaymentPubKeyHash: a96a668ed7be83e332c872f51da7925b4472ca98... (Wallet bdf8dbca0cadeb365480c6ec29ec746a2b85274f) Value: diff --git a/quickcheck-dynamic/src/Test/QuickCheck/DynamicLogic.hs b/quickcheck-dynamic/src/Test/QuickCheck/DynamicLogic.hs index 25047a10d8..b2c597bce7 100644 --- a/quickcheck-dynamic/src/Test/QuickCheck/DynamicLogic.hs +++ b/quickcheck-dynamic/src/Test/QuickCheck/DynamicLogic.hs @@ -146,7 +146,7 @@ bracket (first:rest) = [" ["++first++", "] ++ -- properties at controlled times, so they are likely to fail if -- invoked at other times. -class StateModel s => DynLogicModel s where +class (Typeable s, StateModel s) => DynLogicModel s where restricted :: Action s a -> Bool restricted _ = False diff --git a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs index debf3d50b1..4963e15bcb 100644 --- a/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs +++ b/quickcheck-dynamic/src/Test/QuickCheck/StateModel.hs @@ -28,7 +28,8 @@ module Test.QuickCheck.StateModel( , runActionsInState , lookUpVar , lookUpVarMaybe -) where + , invertLookupVarMaybe + ) where import Control.Monad @@ -40,8 +41,7 @@ import Test.QuickCheck.Monadic class (forall a. Show (Action state a), Monad (ActionMonad state), - Show state, - Typeable state) => + Show state) => StateModel state where data Action state a type ActionMonad state :: * -> * @@ -79,11 +79,19 @@ lookUpVarMaybe ((v' :== a) : env) v = case cast (v',a) of Just (v'',a') | v==v'' -> Just a' _ -> lookUpVarMaybe env v + lookUpVar :: Typeable a => Env -> Var a -> a lookUpVar env v = case lookUpVarMaybe env v of Nothing -> error $ "Variable "++show v++" is not bound!" Just a -> a +invertLookupVarMaybe :: (Typeable a, Eq a) => Env -> a -> Maybe (Var a) +invertLookupVarMaybe [] _ = Nothing +invertLookupVarMaybe ((v :== a) : env) a' = + case cast (v, a) of + Just (v', a'') | a' == a'' -> Just v' + _ -> invertLookupVarMaybe env a' + data Any f where Some :: (Show a, Typeable a, Eq (f a)) => f a -> Any f Error :: String -> Any f @@ -99,7 +107,7 @@ instance Eq (Any f) where _ == _ = False data Step state where - (:=) :: (Show a, Typeable a, Eq (Action state a), Typeable (Action state a), Show (Action state a)) => + (:=) :: (Show a, Typeable a, Eq (Action state a), Show (Action state a)) => Var a -> Action state a -> Step state infix 5 := @@ -111,7 +119,7 @@ newtype Var a = Var Int instance Eq (Step state) where (Var i := act) == (Var j := act') = - (i==j) && Some act == Some act' + (i == j) && Some act == Some act' -- Action sequences use Smart shrinking, but this is invisible to -- client code because the extra Smart constructor is concealed by a @@ -142,7 +150,7 @@ instance (forall a. Show (Action state a)) => Show (Actions state) where foldr (.) (showsPrec 0 (last as) . ("]"++)) [showsPrec 0 a . (",\n "++) | a <- init as] -instance (Typeable state, StateModel state) => Arbitrary (Actions state) where +instance (StateModel state) => Arbitrary (Actions state) where arbitrary = do (as,rejected) <- arbActions initialState 1 return $ Actions_ rejected (Smart 0 as) where @@ -198,7 +206,7 @@ stateAfter (Actions actions) = loop initialState actions loop s ((var := act) : as) = loop (nextState s act var) as runActions :: StateModel state => - Actions state -> PropertyM (ActionMonad state) (state,Env) + Actions state -> PropertyM (ActionMonad state) (state, Env) runActions = runActionsInState initialState runActionsInState :: StateModel state => diff --git a/release.nix b/release.nix index 22158fdda0..2151a20675 100644 --- a/release.nix +++ b/release.nix @@ -20,7 +20,7 @@ let # ci.nix is a set of attributes that work fine as jobs (albeit in a slightly different structure, the platform comes # first), but we mainly just need to get rid of some extra attributes. ciJobsets = stripAttrsForHydra (filterDerivations ci); - # Don't require darwin jobs to succeed for now, until the mac builders are fixed - requiredJobsets = builtins.removeAttrs ciJobsets [ "darwin" ]; + # Don't filter anything out of required for now + requiredJobsets = ciJobsets; in traceNames "" (ciJobsets // { required = derivationAggregate "required-plutus-apps" requiredJobsets; })