Skip to content

Commit

Permalink
Expose MockChainState for transaction evaluation without submission i…
Browse files Browse the repository at this point in the history
…n MonadMockchain (#200)

* Expose MockChainState in MonadMockhain, Include unreg deposit

* Address review comments, fix build error
  • Loading branch information
amirmrad authored Aug 30, 2024
1 parent 8eb23c7 commit ec47335
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 136 deletions.
1 change: 1 addition & 0 deletions src/base/convex-base.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ library
Convex.Constants
Convex.MonadLog
Convex.NodeQueries
Convex.NodeParams
Convex.PlutusLedger
Convex.PlutusTx
Convex.Utils
Expand Down
5 changes: 2 additions & 3 deletions src/base/lib/Convex/BuildTx.hs
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,8 @@ instance MonadBlockchain m => MonadBlockchain (BuildTxT m) where
networkId = lift networkId

instance MonadMockchain m => MonadMockchain (BuildTxT m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

instance MonadDatumQuery m => MonadDatumQuery (BuildTxT m) where
queryDatumFromHash = lift . queryDatumFromHash
Expand Down
183 changes: 147 additions & 36 deletions src/base/lib/Convex/Class.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-| Typeclasses for blockchain operations
Expand All @@ -19,13 +20,38 @@ module Convex.Class(
-- * Monad mockchain
MonadMockchain(..),
MonadBlockchainError(..),

-- * Mockchain state & lenses
MockChainState (..),
env,
poolState,
transactions,
failedTransactions,
datums,
_Phase1Error,
_Phase2Error,

-- * Other types
ExUnitsError(..),
ValidationError(..),
_VExUnits,
_PredicateFailures,
_ApplyTxFailure,

-- * Utilities
getMockChainState,
putMockChainState,
setReward,
modifySlot,
getSlot,
setSlot,
setPOSIXTime,
nextSlot,
setTimeToValidRange,
modifyUtxo,
getUtxo,
setUtxo,
getTxs,

-- * MonadUtxoQuery
MonadUtxoQuery(..),
Expand All @@ -52,11 +78,29 @@ import Cardano.Api.Shelley (BabbageEra,
ScriptData,
SlotNo, Tx,
TxId)
import Cardano.Ledger.Shelley.API (Coin (..),
UTxO)
import qualified Cardano.Api.Shelley as C
import Cardano.Ledger.Alonzo.Plutus.Evaluate (CollectError)
import qualified Cardano.Ledger.Core as Core
import Cardano.Ledger.Crypto (StandardCrypto)
import Cardano.Ledger.Plutus.Evaluate (PlutusWithContext (..))
import Cardano.Ledger.Shelley.API (ApplyTxError,
Coin (..),
MempoolEnv,
MempoolState,
UTxO (..),
Validated, extractTx)
import Cardano.Ledger.Shelley.LedgerState (certDStateL,
dsUnifiedL,
lsCertStateL,
rewards)
import Cardano.Ledger.UMap (RDPair (..),
adjust,
compactCoinOrError)
import Cardano.Slotting.Time (SlotLength,
SystemStart)
import Control.Lens (_1, view)
import Control.Lens (_1, set, view, (^.), to)
import Control.Lens.TH (makeLensesFor,
makePrisms)
import Control.Monad.Except (MonadError,
catchError,
runExceptT,
Expand All @@ -70,18 +114,22 @@ import qualified Control.Monad.State as LazyState
import qualified Control.Monad.State.Strict as StrictState
import Control.Monad.Trans.Except (ExceptT (..))
import Control.Monad.Trans.Except.Result (ResultT)
import qualified Convex.CardanoApi.Lenses as L
import Convex.Constants (ERA)
import Convex.MonadLog (MonadLog (..),
MonadLogIgnoreT (..),
logInfoS,
logWarn,
logWarnS)
import Convex.NodeParams (NodeParams,
pParams)
import Convex.Utils (posixTimeToSlotUnsafe,
slotToUtcTime)
import Convex.Utxos (UtxoSet)
import Data.Aeson (FromJSON,
ToJSON)
import Data.Bifunctor (Bifunctor (..))
import Data.Functor ((<&>))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
Expand Down Expand Up @@ -182,39 +230,106 @@ singleUTxO txi = utxoByTxIn (Set.singleton txi) >>= \case
C.UTxO (Map.toList -> [(_, o)]) -> pure (Just o)
_ -> pure Nothing


{- Note [sendTx Failure]
It would be nice to return a more accurate error type than 'SendTxFailed',
but our two implementations of 'MonadBlockchain' (mockchain and cardano-node backend)
have different errors and it does not seem possible to find a common type.
-}

-- | Error message obtained when a transaction was not accepted by the node.
--
newtype SendTxFailed = SendTxFailed { unSendTxFailed :: String }
deriving stock (Eq, Ord, Show)

instance Pretty SendTxFailed where
pretty (SendTxFailed msg) = "sendTx: Submission failed:" <+> pretty msg

data ExUnitsError =
Phase1Error (C.TransactionValidityError BabbageEra)
| Phase2Error C.ScriptExecutionError
deriving (Show)

makePrisms ''ExUnitsError

data ValidationError =
VExUnits ExUnitsError
| PredicateFailures [CollectError ERA]
| ApplyTxFailure (ApplyTxError ERA)
deriving (Show)

makePrisms ''ValidationError
{-| State of the mockchain
-}
data MockChainState =
MockChainState
{ mcsEnv :: MempoolEnv ERA
, mcsPoolState :: MempoolState ERA
, mcsTransactions :: [(Validated (Core.Tx ERA), [PlutusWithContext StandardCrypto])] -- ^ Transactions that were submitted to the mockchain and validated
, mcsFailedTransactions :: [(Tx BabbageEra, ValidationError)] -- ^ Transactions that were submitted to the mockchain, but failed with a validation error
, mcsDatums :: Map (Hash ScriptData) HashableScriptData
}

makeLensesFor
[ ("mcsEnv", "env")
, ("mcsPoolState", "poolState")
, ("mcsTransactions", "transactions")
, ("mcsFailedTransactions", "failedTransactions")
, ("mcsDatums", "datums")
] ''MockChainState

{-| Modify the mockchain internals
-}
class MonadBlockchain m => MonadMockchain m where
setReward :: C.StakeCredential -> Coin -> m ()
modifySlot :: (SlotNo -> (SlotNo, a)) -> m a
modifyUtxo :: (UTxO ERA -> (UTxO ERA, a)) -> m a
modifyMockChainState :: (MockChainState -> (MockChainState, a)) -> m a
askNodeParams :: m NodeParams

deriving newtype instance MonadMockchain m => MonadMockchain (MonadLogIgnoreT m)

instance MonadMockchain m => MonadMockchain (ResultT m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

instance MonadMockchain m => MonadMockchain (ReaderT e m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

instance MonadMockchain m => MonadMockchain (ExceptT e m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

instance MonadMockchain m => MonadMockchain (StrictState.StateT e m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

instance MonadMockchain m => MonadMockchain (LazyState.StateT e m) where
setReward cred = lift . setReward cred
modifySlot = lift . modifySlot
modifyUtxo = lift . modifyUtxo
modifyMockChainState = lift . modifyMockChainState
askNodeParams = lift askNodeParams

getMockChainState :: MonadMockchain m => m MockChainState
getMockChainState = modifyMockChainState (\s -> (s, s))

putMockChainState :: MonadMockchain m => MockChainState -> m ()
putMockChainState s = modifyMockChainState (const (s, ()))

setReward :: MonadMockchain m => C.StakeCredential -> Coin -> m ()
setReward cred coin = do
mcs <- getMockChainState
let
dState = mcs ^. poolState . lsCertStateL . certDStateL
umap =
adjust
(\rd -> rd {rdReward=compactCoinOrError coin})
(C.toShelleyStakeCredential cred)
(rewards dState)
putMockChainState (set (poolState . lsCertStateL . certDStateL . dsUnifiedL) umap mcs)

modifySlot :: MonadMockchain m => (SlotNo -> (SlotNo, a)) -> m a
modifySlot f = modifyMockChainState $ \s ->
let (s', a) = f (s ^. env . L.slot)
in (set (env . L.slot) s' s, a)

{-| Get the current slot number
-}
Expand All @@ -226,6 +341,11 @@ getSlot = modifySlot (\s -> (s, s))
setSlot :: MonadMockchain m => SlotNo -> m ()
setSlot s = modifySlot (\_ -> (s, ()))

modifyUtxo :: MonadMockchain m => (UTxO ERA -> (UTxO ERA, a)) -> m a
modifyUtxo f = askNodeParams >>= \np -> modifyMockChainState $ \s ->
let (u', a) = f (s ^. poolState . L.utxoState . L._UTxOState (pParams np) . _1)
in (set (poolState . L.utxoState . L._UTxOState (pParams np) . _1) u' s, a)

{-| Get the UTxO set |-}
getUtxo :: MonadMockchain m => m (UTxO ERA)
getUtxo = modifyUtxo (\s -> (s, s))
Expand All @@ -234,6 +354,12 @@ getUtxo = modifyUtxo (\s -> (s, s))
setUtxo :: MonadMockchain m => UTxO ERA -> m ()
setUtxo u = modifyUtxo (const (u, ()))

{-| Return all Tx's from the ledger state -}
getTxs :: MonadMockchain m => m [Core.Tx ERA]
getTxs = getMockChainState <&> view (transactions . traverse . _1 . to ((: []) . extractTx))

{-| Return all Tx's from the ledger state -}

{-| Set the slot number to the slot that contains the given POSIX time.
-}
setPOSIXTime :: (MonadFail m, MonadMockchain m) => PV1.POSIXTime -> m ()
Expand Down Expand Up @@ -344,21 +470,6 @@ instance MonadDatumQuery m => MonadDatumQuery (MonadLogIgnoreT m) where
instance MonadDatumQuery m => MonadDatumQuery (PropertyM m) where
queryDatumFromHash= lift . queryDatumFromHash

{- Note [sendTx Failure]
It would be nice to return a more accurate error type than 'SendTxFailed',
but our two implementations of 'MonadBlockchain' (mockchain and cardano-node backend)
have different errors and it does not seem possible to find a common type.
-}

-- | Error message obtained when a transaction was not accepted by the node.
--
newtype SendTxFailed = SendTxFailed { unSendTxFailed :: String }
deriving stock (Eq, Ord, Show)

instance Pretty SendTxFailed where
pretty (SendTxFailed msg) = "sendTx: Submission failed:" <+> pretty msg

{-| 'MonadBlockchain' implementation that connects to a cardano node
-}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE TemplateHaskell #-}

module Convex.NodeParams(
NodeParams(..),
networkId,
Expand All @@ -9,6 +11,7 @@ module Convex.NodeParams(
eraHistory,
stakePools,
slotLength,
pParams,

-- * Lenses for @ProtocolParameters@
-- See https://input-output-hk.github.io/cardano-node/cardano-api/lib/Cardano-Api-Shelley.html#t:ProtocolParameters
Expand Down Expand Up @@ -37,16 +40,19 @@ module Convex.NodeParams(
maxValueSize,
collateralPercent,
maxCollateralInputs,
uTxOCostPerByte
uTxOCostPerByte,
) where

import Cardano.Api (BabbageEra)
import Cardano.Api.Shelley (EraHistory, LedgerProtocolParameters,
NetworkId (..), PoolId,
ProtocolParameters (..))
import Cardano.Slotting.Time (SlotLength, SystemStart)
import Control.Lens.TH (makeLensesFor)
import Data.Set as Set (Set)
import Cardano.Api (BabbageEra)
import Cardano.Api.Shelley (EraHistory,
LedgerProtocolParameters (..),
NetworkId (..), PoolId,
ProtocolParameters (..))
import Cardano.Ledger.Babbage (Babbage)
import Cardano.Ledger.Core (PParams)
import Cardano.Slotting.Time (SlotLength, SystemStart)
import Control.Lens.TH (makeLensesFor)
import Data.Set as Set (Set)

data NodeParams =
NodeParams
Expand Down Expand Up @@ -99,3 +105,8 @@ makeLensesFor
, ("protocolParamMaxCollateralInputs", "maxCollateralInputs")
, ("protocolParamUTxOCostPerByte", "uTxOCostPerByte")
] ''ProtocolParameters

-- | Convert `Params` to cardano-ledger `PParams`
pParams :: NodeParams -> PParams Babbage
pParams NodeParams { npProtocolParameters } = case npProtocolParameters of
LedgerProtocolParameters p -> p
Loading

0 comments on commit ec47335

Please sign in to comment.