Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Automated drep (Scenario 1) #5815

Merged
merged 29 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3bea234
Create scafolding for "Predefined Abstain DRep" test
palas Apr 17, 2024
1c6e476
Test predefined always abstain DRep
palas Apr 17, 2024
a855a05
Add comment to `hprop_check_predefined_abstain_drep` explaining what …
palas Apr 30, 2024
f222b9e
Add comment to `getDesiredPoolNumberValue` explaining what the `desir…
palas Apr 30, 2024
05bc5ec
Used a type synonym for `votes` and added comments to `voteChangeProp…
palas Apr 30, 2024
93af416
Add `HasCallStack` to several functions
palas May 1, 2024
2cb5a18
Remove unneccessary `fromIntegral` usages
palas May 1, 2024
73af6e9
Added Haddocks to arguments for several functions
palas May 1, 2024
7f19ce7
Used `getGovState` instead of calling `cardano-clì`
palas May 1, 2024
232baa7
Refactor out and generalize `waitAndCheck` function
palas May 3, 2024
4e2eed9
Change witness to be `ConwayEraOnwards`
palas May 3, 2024
55c368d
Added comments to the new `waitAndCheckNewEpochState` function
palas May 3, 2024
e63437c
Modify `desiredPoolNumberProposalTest` to use `waitAndCheckNewEpochSt…
palas May 3, 2024
4fc4523
Apply watchdog
palas May 3, 2024
935c809
Remove `startLedgerNewEpochStateLogging`
palas May 3, 2024
0bd7d6c
Improve comment in `hprop_check_predefined_abstain_drep`
palas May 3, 2024
23b9a30
Fix typos in `getDesiredPoolNumberValue`
palas May 3, 2024
b7107f4
Abstract out era in `delegateToAlwaysAbstain`
palas May 3, 2024
ed50065
Remove waiting bit from `waitAndCheckNewEpochState` and use `watchEpo…
palas May 14, 2024
b17f09a
Modify `waitForEpochs` to use `watchEpochStateView`
palas May 15, 2024
76866df
Replace some usages of `waitUntilEpoch` with `waitForEpochs`
palas May 15, 2024
c53a5a1
Kick watchdog in middle of `DRep Activity` test
palas May 15, 2024
aa05e61
Remove `findCondition` function
palas May 15, 2024
7e32694
Fix issue with vote counting
palas May 15, 2024
d401f36
Adjust `cardanoEpochLength` to avoid flakiness
palas May 16, 2024
5b62ae6
Disable tests that don't work
palas May 16, 2024
342c405
Fix delay in PlutusV3 test
palas May 16, 2024
1d715dc
Remove unnecessary "$"
palas May 16, 2024
5b16cd1
Increase polling frequency
palas May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cardano-testnet/cardano-testnet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ test-suite cardano-testnet-test
Cardano.Testnet.Test.Gov.TreasuryGrowth
Cardano.Testnet.Test.Gov.TreasuryWithdrawal
Cardano.Testnet.Test.Misc
Cardano.Testnet.Test.Gov.DRepActivity
Cardano.Testnet.Test.Gov.PredefinedAbstainDRep
Cardano.Testnet.Test.Node.Shutdown
Cardano.Testnet.Test.SanityCheck
Cardano.Testnet.Test.SubmitApi.Babbage.Transaction
Expand Down
1 change: 0 additions & 1 deletion cardano-testnet/src/Cardano/Testnet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ module Cardano.Testnet (

-- * EpochState processsing helper functions
maybeExtractGovernanceActionIndex,
findCondition,

-- * Processes
procChairman,
Expand Down
85 changes: 79 additions & 6 deletions cardano-testnet/src/Testnet/Components/Query.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Testnet.Components.Query
Expand All @@ -20,21 +23,24 @@ module Testnet.Components.Query
, findUtxosWithAddress
, findLargestUtxoWithAddress
, findLargestUtxoForPaymentKey
, assertNewEpochState
, watchEpochStateView
) where

import Cardano.Api as Api
import Cardano.Api.Ledger (Credential, DRepState, KeyRole (DRepRole), StandardCrypto)
import Cardano.Api.Ledger (Credential, DRepState, EpochInterval (..), KeyRole (DRepRole),
StandardCrypto)
import Cardano.Api.Shelley (ShelleyLedgerEra, fromShelleyTxIn, fromShelleyTxOut)

import qualified Cardano.Ledger.Api as L
import Cardano.Ledger.BaseTypes (EpochInterval, addEpochInterval)
import qualified Cardano.Ledger.Coin as L
import qualified Cardano.Ledger.Conway.Governance as L
import qualified Cardano.Ledger.Conway.PParams as L
import qualified Cardano.Ledger.Shelley.LedgerState as L
import qualified Cardano.Ledger.UTxO as L

import Control.Exception.Safe (MonadCatch)
import Control.Monad (void)
import Control.Monad.Trans.Resource
import Control.Monad.Trans.State.Strict (put)
import Data.Bifunctor (bimap)
Expand All @@ -50,7 +56,7 @@ import qualified Data.Text as T
import Data.Type.Equality
import GHC.Exts (IsList (..))
import GHC.Stack
import Lens.Micro (to, (^.))
import Lens.Micro (Lens', to, (^.))

import Testnet.Property.Assert
import Testnet.Property.Util (runInBackground)
Expand Down Expand Up @@ -94,9 +100,9 @@ waitForEpochs
=> EpochStateView
-> EpochInterval -- ^ Number of epochs to wait
-> m EpochNo -- ^ The epoch number reached
waitForEpochs epochStateView@EpochStateView{nodeConfigPath, socketPath} interval = withFrozenCallStack $ do
currentEpoch <- getCurrentEpochNo epochStateView
waitUntilEpoch nodeConfigPath socketPath $ addEpochInterval currentEpoch interval
waitForEpochs epochStateView interval = withFrozenCallStack $ do
void $ watchEpochStateView epochStateView (const $ pure Nothing) interval
getCurrentEpochNo epochStateView

-- | A read-only mutable pointer to an epoch state, updated automatically
data EpochStateView = EpochStateView
Expand Down Expand Up @@ -353,3 +359,70 @@ getCurrentEpochNo
getCurrentEpochNo epochStateView = withFrozenCallStack $ do
AnyNewEpochState _ newEpochState <- getEpochState epochStateView
pure $ newEpochState ^. L.nesELL

-- | Assert that the value pointed by the @lens@ in the epoch state is the same as the @expected@ value
-- or it becomes the same within the @maxWait@ epochs. If the value is not reached within the time frame,
-- the test fails.
assertNewEpochState
:: forall m era value.
(Show value, MonadAssertion m, MonadTest m, MonadIO m, Eq value, HasCallStack)
=> EpochStateView -- ^ Current epoch state view. It can be obtained using the 'getEpochStateView' function.
-> ConwayEraOnwards era -- ^ The ConwayEraOnwards witness for current era.
-> value -- ^ The expected value to check in the epoch state.
-> EpochInterval -- ^ The maximum wait time in epochs.
-> Lens' (L.NewEpochState (ShelleyLedgerEra era)) value -- ^ The lens to access the specific value in the epoch state.
-> m ()
assertNewEpochState epochStateView ceo expected maxWait lens = withFrozenCallStack $ do
let sbe = conwayEraOnwardsToShelleyBasedEra ceo
mStateView <- watchEpochStateView epochStateView (checkEpochState sbe) maxWait
case mStateView of
Just () -> pure ()
Nothing -> do epochState <- getEpochState epochStateView
val <- getFromEpochState sbe epochState
if val == expected
then pure ()
else H.failMessage callStack $ unlines
[ "assertNewEpochState: expected value not reached within the time frame."
, "Expected value: " <> show expected
, "Actual value: " <> show val
]
where
checkEpochState :: HasCallStack
=> ShelleyBasedEra era -> AnyNewEpochState -> m (Maybe ())
checkEpochState sbe newEpochState = do
val <- getFromEpochState sbe newEpochState
return $ if val == expected then Just () else Nothing

getFromEpochState :: HasCallStack
=> ShelleyBasedEra era -> AnyNewEpochState -> m value
getFromEpochState sbe (AnyNewEpochState actualEra newEpochState) = do
Refl <- either error pure $ assertErasEqual sbe actualEra
return $ newEpochState ^. lens

-- | Watch the epoch state view until the guard function returns 'Just' or the timeout epoch is reached.
-- Wait for at most @maxWait@ epochs.
-- The function will return the result of the guard function if it is met, otherwise it will return @Nothing@.
watchEpochStateView
:: forall m a. (HasCallStack, MonadIO m, MonadTest m, MonadAssertion m)
=> EpochStateView -- ^ The info to access the epoch state
-> (AnyNewEpochState -> m (Maybe a)) -- ^ The guard function (@Just@ if the condition is met, @Nothing@ otherwise)
-> EpochInterval -- ^ The maximum number of epochs to wait
-> m (Maybe a)
watchEpochStateView epochStateView f (EpochInterval maxWait) = withFrozenCallStack $ do
AnyNewEpochState _ newEpochState <- getEpochState epochStateView
let EpochNo currentEpoch = L.nesEL newEpochState
go (EpochNo $ currentEpoch + fromIntegral maxWait)
where
go :: EpochNo -> m (Maybe a)
go (EpochNo timeout) = do
epochState@(AnyNewEpochState _ newEpochState') <- getEpochState epochStateView
let EpochNo currentEpoch = L.nesEL newEpochState'
condition <- f epochState
case condition of
Just result -> pure (Just result)
Nothing -> do
if currentEpoch > timeout
then pure Nothing
else do
H.threadDelay 10_000
go (EpochNo timeout)
107 changes: 42 additions & 65 deletions cardano-testnet/src/Testnet/EpochStateProcessing.hs
Original file line number Diff line number Diff line change
@@ -1,65 +1,40 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

module Testnet.EpochStateProcessing
( maybeExtractGovernanceActionIndex
, findCondition
, watchEpochStateView
, waitForGovActionVotes
) where

import Cardano.Api
import Cardano.Api.Ledger (EpochInterval (..), GovActionId (..))
import Cardano.Api.Ledger (EpochInterval, GovActionId (..))
import qualified Cardano.Api.Ledger as L
import Cardano.Api.Shelley (ShelleyLedgerEra)

import qualified Cardano.Ledger.Conway.Governance as L
import qualified Cardano.Ledger.Shelley.API as L
import Cardano.Ledger.Shelley.LedgerState (newEpochStateGovStateL)
import qualified Cardano.Ledger.Shelley.LedgerState as L

import Prelude

import Control.Monad.State.Strict (MonadState (put), StateT)
import Data.Data ((:~:) (..))
import qualified Data.Map as Map
import Data.Word (Word32)
import GHC.Exts (IsList (toList), toList)
import GHC.Stack
import Lens.Micro ((^.))
import Lens.Micro (to, (^.))

import Testnet.Components.Query (EpochStateView, getEpochState)
import Testnet.Components.Query (EpochStateView, watchEpochStateView)
import Testnet.Property.Assert (assertErasEqual)

import Hedgehog
import Hedgehog (MonadTest)
import Hedgehog.Extras (MonadAssertion)
import qualified Hedgehog.Extras as H

findCondition
:: HasCallStack
=> MonadTest m
=> MonadIO m
=> (AnyNewEpochState -> Maybe a)
-> NodeConfigFile In
-> SocketPath
-> EpochNo -- ^ The termination epoch: the condition must be found *before* this epoch
-> m (Either FoldBlocksError (Maybe a))
findCondition epochStateFoldFunc configurationFile socketPath maxEpochNo = withFrozenCallStack $ evalIO . runExceptT $ do
result <-
foldEpochState
configurationFile
socketPath
FullValidation
maxEpochNo
Nothing
(\epochState _ _ -> go epochStateFoldFunc epochState)
pure $ case result of
(ConditionMet, Just x) -> Just x
_ -> Nothing

where
go :: (AnyNewEpochState -> Maybe a) -> AnyNewEpochState -> StateT (Maybe a) IO LedgerStateCondition
go f epochState = do
case f epochState of
Just x -> put (Just x) >> pure ConditionMet
Nothing -> pure ConditionNotMet

maybeExtractGovernanceActionIndex
:: HasCallStack
=> TxId -- ^ transaction id searched for
Expand All @@ -78,31 +53,33 @@ maybeExtractGovernanceActionIndex txid (AnyNewEpochState sbe newEpochState) =
| ti1 == L.extractHash ti2 = Just gai
compareWithTxId _ x _ _ = x

-- | Watch the epoch state view until the guard function returns 'Just' or the timeout epoch is reached.
-- Wait for at most @maxWait@ epochs.
-- The function will return the result of the guard function if it is met, otherwise it will return @Nothing@.
watchEpochStateView
:: forall m a. (HasCallStack, MonadIO m, MonadTest m, MonadAssertion m)
=> EpochStateView -- ^ The info to access the epoch state
-> (AnyNewEpochState -> m (Maybe a)) -- ^ The guard function (@Just@ if the condition is met, @Nothing@ otherwise)
-> EpochInterval -- ^ The maximum number of epochs to wait
-> m (Maybe a)
watchEpochStateView epochStateView f (EpochInterval maxWait) = withFrozenCallStack $ do
AnyNewEpochState _ newEpochState <- getEpochState epochStateView
let EpochNo currentEpoch = L.nesEL newEpochState
go (EpochNo $ currentEpoch + fromIntegral maxWait)
where
go :: EpochNo -> m (Maybe a)
go (EpochNo timeout) = do
epochState@(AnyNewEpochState _ newEpochState') <- getEpochState epochStateView
let EpochNo currentEpoch = L.nesEL newEpochState'
condition <- f epochState
case condition of
Just result -> pure (Just result)
Nothing -> do
if currentEpoch > timeout
then pure Nothing
else do
H.threadDelay 100_000
go (EpochNo timeout)

-- | Wait for the last gov action proposal in the list to have DRep or SPO votes.
waitForGovActionVotes
:: forall m era.
(MonadAssertion m, MonadTest m, MonadIO m, HasCallStack)
=> EpochStateView -- ^ Current epoch state view. It can be obtained using the 'getEpochStateView' function.
-> ConwayEraOnwards era -- ^ The ConwayEraOnwards witness for current era.
-> EpochInterval -- ^ The maximum wait time in epochs.
-> m ()
waitForGovActionVotes epochStateView ceo maxWait = withFrozenCallStack $ do
mResult <- watchEpochStateView epochStateView getFromEpochState maxWait
case mResult of
Just () -> pure ()
Nothing -> H.failMessage callStack "waitForGovActionVotes: No votes appeared before timeout."
where
getFromEpochState :: HasCallStack
=> AnyNewEpochState -> m (Maybe ())
getFromEpochState (AnyNewEpochState actualEra newEpochState) = do
let sbe = conwayEraOnwardsToShelleyBasedEra ceo
Refl <- H.leftFail $ assertErasEqual sbe actualEra
let govState :: L.ConwayGovState (ShelleyLedgerEra era) = conwayEraOnwardsConstraints ceo $ newEpochState ^. newEpochStateGovStateL
proposals = govState ^. L.cgsProposalsL . L.pPropsL . to toList
if null proposals
then pure Nothing
else do
let lastProposal = last proposals
gaDRepVotes = lastProposal ^. L.gasDRepVotesL . to toList
gaSpoVotes = lastProposal ^. L.gasStakePoolVotesL . to toList
if null gaDRepVotes && null gaSpoVotes
then pure Nothing
else pure $ Just ()
11 changes: 5 additions & 6 deletions cardano-testnet/src/Testnet/Process/Cli/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module Testnet.Process.Cli.DRep
) where

import Cardano.Api hiding (Certificate, TxBody)
import Cardano.Api.Ledger (EpochInterval (EpochInterval))

import Prelude

Expand Down Expand Up @@ -248,16 +249,15 @@ delegateToDRep
=> MonadCatch m
=> H.ExecConfig -- ^ Specifies the CLI execution configuration.
-> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained
-> NodeConfigFile In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'.
-> SocketPath -- ^ Path to the cardano-node unix socket file.
-- using the 'getEpochStateView' function.
-> ShelleyBasedEra ConwayEra -- ^ The Shelley-based era (e.g., 'ConwayEra') in which the transaction will be constructed.
-> FilePath -- ^ Base directory path where generated files will be stored.
-> String -- ^ Name for the subfolder that will be created under 'work' folder.
-> PaymentKeyInfo -- ^ Wallet that will pay for the transaction.
-> KeyPair StakingKey -- ^ Staking key pair used for delegation.
-> KeyPair PaymentKey -- ^ Delegate Representative (DRep) key pair ('PaymentKeyPair') to which delegate.
-> m ()
delegateToDRep execConfig epochStateView configurationFile' socketPath sbe work prefix
delegateToDRep execConfig epochStateView sbe work prefix
payingWallet skeyPair@KeyPair{verificationKey=File vKeyFile}
KeyPair{verificationKey=File drepVKey} = do

Expand Down Expand Up @@ -287,9 +287,8 @@ delegateToDRep execConfig epochStateView configurationFile' socketPath sbe work
-- Submit transaction
submitTx execConfig cEra repRegSignedRegTx1

-- Wait two epochs
(EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView
void $ waitUntilEpoch configurationFile' socketPath (EpochNo (epochAfterProp + 2))
-- Wait one epoch
void $ waitForEpochs epochStateView (EpochInterval 1)

-- | This function obtains the identifier for the last enacted parameter update proposal
-- if any.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ module Cardano.Testnet.Test.Cli.Conway.Plutus
) where

import Cardano.Api
import qualified Cardano.Api.Ledger as L

import Cardano.Testnet

Expand Down Expand Up @@ -142,11 +141,9 @@ hprop_plutus_v3 = integrationWorkspace "all-plutus-script-purposes" $ \tempAbsBa
, "--tx-file", sendAdaToScriptAddressTx
]

_ <- waitForEpochs epochStateView (L.EpochInterval 1)

-- 2. Successfully spend conway spending script
txinCollateral <- findLargestUtxoForPaymentKey epochStateView sbe wallet1
plutusScriptTxIn <- fmap fst . H.nothingFailM $
plutusScriptTxIn <- fmap fst . waitForJustM $
findLargestUtxoWithAddress epochStateView sbe $ Text.pack plutusSpendingScriptAddr

let spendScriptUTxOTxBody = work </> "spend-script-utxo-tx-body"
Expand Down Expand Up @@ -187,4 +184,11 @@ hprop_plutus_v3 = integrationWorkspace "all-plutus-script-purposes" $ \tempAbsBa
]
H.success

waitForJustM :: (H.MonadTest m, MonadIO m) => m (Maybe a) -> m a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findLargestUtxoWithAddress should be modified to recurse if no UTxOs are found and terminate at a specified termination epoch.

waitForJustM src = do m <- src
case m of
Just a -> pure a
Nothing -> do H.threadDelay 100_000
waitForJustM src


Loading
Loading