Skip to content

Commit

Permalink
Utxo handler is tested and works
Browse files Browse the repository at this point in the history
  • Loading branch information
berewt committed Mar 27, 2023
1 parent 050c923 commit aefd2c7
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 52 deletions.
Expand Up @@ -19,7 +19,7 @@ import Data.Set (Set)
import Data.Set qualified as Set
import GHC.Generics (Generic)
import Gen.Cardano.Api.Typed qualified as CGen
import Gen.Marconi.ChainIndex.Mockchain (MockBlock (MockBlock), MockBlockHeader (MockBlockHeader), genMockchain)
import Gen.Marconi.ChainIndex.Mockchain (BlockHeader (BlockHeader), MockBlock (MockBlock), genMockchain)
import Hedgehog (Gen)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
Expand Down Expand Up @@ -57,7 +57,7 @@ genUtxoEventsWithTxs' txOutToUtxo = do
fmap (\block -> (getStorableEventFromBlock block, block)) <$> genMockchain
where
getStorableEventFromBlock :: MockBlock C.BabbageEra -> StorableEvent UtxoHandle
getStorableEventFromBlock (MockBlock (MockBlockHeader slotNo blockHeaderHash _blockNo) txs) =
getStorableEventFromBlock (MockBlock (BlockHeader slotNo blockHeaderHash _blockNo) txs) =
let (TxOutBalance utxos spentTxOuts) = foldMap txOutBalanceFromTx txs
utxoMap = foldMap getUtxosFromTx txs
resolvedUtxos = Set.fromList
Expand Down
16 changes: 5 additions & 11 deletions marconi-chain-index/test-lib/Gen/Marconi/ChainIndex/Mockchain.hs
Expand Up @@ -3,7 +3,7 @@

module Gen.Marconi.ChainIndex.Mockchain
( Mockchain
, MockBlockHeader(..)
, C.BlockHeader(..)
, MockBlock(..)
, genMockchain
)
Expand All @@ -22,29 +22,23 @@ import Helpers (emptyTxBodyContent)

type Mockchain era = [MockBlock era]

data MockBlockHeader = MockBlockHeader
{ mockBlockHeaderSlotNo :: !C.SlotNo
, mockBlockHeaderHash :: !(C.Hash C.BlockHeader)
, mockBlockHeaderBlockNo :: !C.BlockNo
} deriving (Show)

data MockBlock era = MockBlock
{ mockBlockChainPoint :: !MockBlockHeader
{ mockBlockChainPoint :: !C.BlockHeader
, mockBlockTxs :: ![C.Tx era]
} deriving (Show)
}

genMockchain :: Gen (Mockchain C.BabbageEra)
genMockchain = do
maxSlots <- Gen.word64 (Range.linear 1 5)
blockHeaderHash <- genHashBlockHeader
let blockHeaders =
fmap (\s -> MockBlockHeader (C.SlotNo s) blockHeaderHash (C.BlockNo s))
fmap (\s -> C.BlockHeader (C.SlotNo s) blockHeaderHash (C.BlockNo s))
[0..maxSlots]
txIns <- Set.singleton <$> CGen.genTxIn
snd <$> foldM f (txIns, []) blockHeaders
where
f :: (Set C.TxIn, Mockchain C.BabbageEra)
-> MockBlockHeader
-> C.BlockHeader
-> Gen (Set C.TxIn, Mockchain C.BabbageEra)
f (utxoSet, mockchain) bh = do
utxosAsTxInput <- nonEmptySubset utxoSet
Expand Down
7 changes: 5 additions & 2 deletions plutus-chain-index-core/plutus-chain-index-core.cabal
Expand Up @@ -164,6 +164,7 @@ test-suite plutus-chain-index-test
, cardano-node-emulator >=1.2.0
, freer-extras >=1.2.0
, marconi-chain-index:{marconi-chain-index, marconi-chain-index-test-lib} >=1.2.0
, marconi-core >=1.2.0
, plutus-chain-index-core >=1.2.0
, plutus-ledger >=1.2.0
, plutus-script-utils >=1.2.0
Expand All @@ -172,8 +173,9 @@ test-suite plutus-chain-index-test
-- Other IOG dependencies
--------------------------
build-depends:
, plutus-ledger-api >=1.0.0
, plutus-tx >=1.0.0
, cardano-api:{cardano-api, gen} >=1.35
, plutus-ledger-api >=1.0.0
, plutus-tx >=1.0.0

------------------------
-- Non-IOG dependencies
Expand All @@ -196,3 +198,4 @@ test-suite plutus-chain-index-test
, stm
, tasty
, tasty-hedgehog
, transformers
Expand Up @@ -32,7 +32,13 @@ toCardanoPoint (Point slot blockId) =
tipFromCardanoBlock
:: BlockInMode CardanoMode
-> Tip
tipFromCardanoBlock (BlockInMode (Block (BlockHeader slot hash block) _) _) =
tipFromCardanoBlock (BlockInMode (Block header _) _) =
tipFromCardanoBlockHeader header

tipFromCardanoBlockHeader
:: BlockHeader
-> Tip
tipFromCardanoBlockHeader (BlockHeader slot hash block) =
fromCardanoTip $ ChainTip slot hash block

fromCardanoSlot :: SlotNo -> Slot
Expand Down
9 changes: 4 additions & 5 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Marconi.hs
Expand Up @@ -138,11 +138,10 @@ getUtxoEvents
-> C.ChainPoint
-> StorableEvent UtxoHandle -- ^ UtxoEvents are stored in storage after conversion to UtxoRow
getUtxoEvents txs cp =
let
utxosFromCardanoTx (CardanoTx c _) = getUtxos Nothing c
inputsFromCardanoTx (CardanoTx c _) = getInputs c
utxos = Set.fromList $ concatMap utxosFromCardanoTx txs
ins = foldl' Set.union Set.empty $ inputsFromCardanoTx <$> txs
let utxosFromCardanoTx (CardanoTx c _) = getUtxos Nothing c
inputsFromCardanoTx (CardanoTx c _) = getInputs c
utxos = Set.fromList $ concatMap utxosFromCardanoTx txs
ins = foldl' Set.union Set.empty $ inputsFromCardanoTx <$> txs
in UtxoEvent utxos ins cp

-- | The required arguments to run the chain index effects.
Expand Down
5 changes: 4 additions & 1 deletion plutus-chain-index-core/test/Generators.hs
Expand Up @@ -5,6 +5,7 @@
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
Expand Down Expand Up @@ -153,6 +154,8 @@ deleteInputs spent = do
modify (over txgsUtxoSet (\s -> s `Set.difference` Map.keysSet spent))
modify (over txgsStxoSet (\s -> s <> spent))

-- toCardanoScript :: ReferenceScript -> Maybe (cardano-api-1.35.4:Cardano.Api.Script.ReferenceScript C.BabbageEra)

-- | Generate a valid 'Tx' that spends some UTXOs and creates some new ones
genTx ::
forall effs.
Expand All @@ -164,7 +167,7 @@ genTx = do
newOutputs <-
let outputGen = ChainIndexTxOut <$> genAddress <*> genNonZeroAdaValue <*> pure NoOutputDatum <*> pure ReferenceScriptNone in
sendM (Gen.list (Range.linear 1 5) outputGen)
outputs <- sendM (Gen.element [ValidTx newOutputs, InvalidTx (listToMaybe newOutputs)])
outputs <- sendM $ Gen.element [ValidTx newOutputs, InvalidTx (listToMaybe newOutputs)]
inputs <- availableInputs

allInputs <-
Expand Down
141 changes: 111 additions & 30 deletions plutus-chain-index-core/test/Plutus/ChainIndex/MarconiSpec.hs
@@ -1,53 +1,134 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
module Plutus.ChainIndex.MarconiSpec (tests) where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.IO.Class (MonadIO, liftIO)

import Generators qualified as Gen
import Hedgehog.Internal.Property (Property, forAll, property)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testPropertyNamed)

import Control.Lens (filtered, folded, to, (^..), (^?))
import Cardano.Api qualified as C
import Control.Concurrent (readMVar)
import Control.Lens (_1, folded, to, toListOf, (^.), (^..), (^?))
import Control.Monad (void)
import Control.Tracer (nullTracer)
import Data.Default (def)
import Gen.Marconi.ChainIndex.Mockchain (MockBlock (MockBlock), genMockchain)
import Hedgehog (Gen)
import Hedgehog qualified
import Ledger (TxIn)
import Marconi.ChainIndex.Indexers.Utxo (StorableEvent (..), UtxoHandle)
import Marconi.ChainIndex.Indexers.Utxo qualified as Utxo
import Plutus.ChainIndex (ChainSyncBlock (Block), Tip (TipAtGenesis), appendBlocks, citoAddress, citxOutputs, pageItems,
pageOf, utxoSetAtAddress)
import Plutus.ChainIndex.Api (UtxosResponse (UtxosResponse), page)
import Marconi.Core.Storable qualified as Storable
import Plutus.ChainIndex (ChainIndexTxOut, ChainSyncBlock (Block), appendBlocks, citoAddress, citxInputs, citxOutputs,
utxoSetAtAddress)
import Plutus.ChainIndex.Compatibility (tipFromCardanoBlockHeader)
import Plutus.ChainIndex.Marconi (ChainIndexIndexers (ChainIndexIndexers), ChainIndexIndexersMVar,
RunRequirements (RunRequirements), boxChainIndexIndexers, runChainIndexEffects)
import Plutus.ChainIndex.Types (_ValidTx, chainIndexTxOutputs)
RunRequirements (RunRequirements), boxChainIndexIndexers, runChainIndexEffects,
utxosIndexerMVar)
import Plutus.ChainIndex.Types (ChainSyncBlock (blockTxs), chainIndexTxOutputs)
import Plutus.Contract.CardanoAPI (fromCardanoTx)

tests :: TestTree
tests = testGroup "Plutus.ChainIndex.MarconiSpec"
[ testGroup "testSetAtAddress"
[ testPropertyNamed "appendQueryLoop" "setAtAddressRoundtripProperty"
setAtAddressRoundtripProperty
[ testPropertyNamed "Indexer do store blocks txOuts" "checkTxOutStorage"
checkTxOutStorage
, testPropertyNamed "Indexer do store blocks txIn" "checkTxInStorage"
checkTxInStorage
]
]

genBlocks :: Gen [ChainSyncBlock]
genBlocks = fmap fromMockBlock <$> genMockchain
where
fromMockBlock :: MockBlock C.BabbageEra -> ChainSyncBlock
fromMockBlock (MockBlock header txs) =
Block
(tipFromCardanoBlockHeader header)
((,def) . fromCardanoTx C.BabbageEraInCardanoMode <$> txs)

newChainIndexIndexers :: IO ChainIndexIndexersMVar
newChainIndexIndexers = do
indexers <- ChainIndexIndexers
<$> Utxo.open ":memory:" (Utxo.Depth 10)
boxChainIndexIndexers indexers

setAtAddressRoundtripProperty :: Property
setAtAddressRoundtripProperty = property $ do
(tip, block) <- forAll $ Gen.evalTxGenState Gen.genNonEmptyBlock
indexers <- liftIO newChainIndexIndexers
let fstTxAddr = block ^? folded . citxOutputs . chainIndexTxOutputs . to citoAddress
let _originalTxOuts = block ^.. folded . citxOutputs
_txouts <- maybe
(pure $ Right $ UtxosResponse TipAtGenesis $ pageOf def mempty)
(\addr -> do
liftIO $ runChainIndexEffects (RunRequirements nullTracer indexers) $ do
appendBlocks [Block tip (map (, def) block)]
utxoSetAtAddress def addr
)
fstTxAddr
-- TODO Check TxOut once we have a working indexer
Hedgehog.success
indexers <- ChainIndexIndexers
<$> Utxo.open ":memory:" (Utxo.Depth 10)
boxChainIndexIndexers indexers

getUtxoEvents :: MonadIO m => ChainIndexIndexersMVar -> m [StorableEvent UtxoHandle]
getUtxoEvents indexers =
liftIO $ readMVar (indexers ^. utxosIndexerMVar) >>= Storable.getEvents

allTxOuts :: ChainSyncBlock -> [ChainIndexTxOut]
allTxOuts =
toListOf (to blockTxs . folded . _1 . citxOutputs . chainIndexTxOutputs)

allTxIns :: ChainSyncBlock -> [TxIn]
allTxIns =
toListOf (to blockTxs . folded . _1 . citxInputs . folded)


checkTxOutStorage :: Property
checkTxOutStorage = property $ do
blocks <- forAll genBlocks
indexers <- liftIO newChainIndexIndexers
let txOutAddr = blocks ^? folded . to blockTxs . folded . _1
. citxOutputs . chainIndexTxOutputs . to citoAddress

maybe
Hedgehog.success
(\addr -> do
void $ liftIO $ runChainIndexEffects (RunRequirements nullTracer indexers) $ do
appendBlocks blocks
utxoSetAtAddress def addr
events <- getUtxoEvents indexers
let originalTxOuts = blocks >>= allTxOuts
Hedgehog.annotateShow events
Hedgehog.annotateShow originalTxOuts
let eventUtxos = events ^.. folded . to ueUtxos . folded
length eventUtxos Hedgehog.=== length originalTxOuts
)
txOutAddr

checkTxInStorage :: Property
checkTxInStorage = property $ do
blocks <- forAll genBlocks
indexers <- liftIO newChainIndexIndexers
let txOutAddr = blocks ^? folded . to blockTxs . folded . _1
. citxOutputs . chainIndexTxOutputs . to citoAddress
maybe
Hedgehog.success
(\addr -> do
void $ liftIO $ runChainIndexEffects (RunRequirements nullTracer indexers) $ do
appendBlocks blocks
utxoSetAtAddress def addr
events <- getUtxoEvents indexers
let originalTxIns = blocks >>= allTxIns
Hedgehog.annotateShow events
Hedgehog.annotateShow originalTxIns
let eventTxIns = events ^.. folded . to ueInputs . folded
length eventTxIns Hedgehog.=== length originalTxIns
)
txOutAddr
{-
let blocksTxs = blocks ^.. folded . to blockTxs . folded . _1
lastTx = blocksTxs ^? _last
lastTxIn = lastTx ^? folded . citxInputs . _last . to txInRef
lastTxInAddr = lastTxIn >>= flip findTxOutRefAddr blocksTxs
txouts <- maybe
(pure $ Right $ UtxosResponse TipAtGenesis $ pageOf def mempty)
(\addr -> do
liftIO $ runChainIndexEffects (RunRequirements nullTracer indexers) $ do
appendBlocks blocks
utxoSetAtAddress def addr
)
lastTxInAddr
let indexedTxOuts = pageItems . page <$> txouts
events <- getUtxoEvents indexers
Hedgehog.annotateShow events
Hedgehog.annotateShow lastTxIn
Hedgehog.annotateShow indexedTxOuts
Hedgehog.assert $ either (const False) (not . maybe (const True) elem lastTxIn) indexedTxOuts
-}

0 comments on commit aefd2c7

Please sign in to comment.